Repository navigation
test: guard remaining host-dependent control-plane assertions on native Windows - #6356
Conversation
Native Windows contributors running `npm run test:control-plane` get 32 host-dependent false failures and cannot use the suite as a pass/fail signal. Every class is the out_of_scope remainder declared by loopx-project#6207: - 20 self-SIGKILL process-death checks assert `child.signal === "SIGKILL"`; Windows terminates via TerminateProcess and reports an exit status, so assert a non-zero status there and keep the signal assertion on POSIX (same guard as sqlite_authority_store.test.ts). - 9 file-symlink fixtures fail with EPERM because Windows file symlink creation needs Developer Mode or admin. Skip the file-symlink variant where it is the only subject, guard the block where the test also covers other rejection rules, and use the suite's existing junction fallback for directory symlinks. - The mode-000 preference check is POSIX-only, matching the Python twin made POSIX-only in loopx-project#6007. - The simulated Linux directory-fsync boundary needs a real POSIX kernel. - The documented goal-acceptance configuration is read with a CRLF-aware fence on a core.autocrlf checkout. Production code is unchanged; POSIX behavior is unchanged. Refs loopx-project#6351 Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
f09342c to
efde83b
Compare
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; xhigh
Reviewed head: efde83b; merge-base: 71304fc.
[P2] Windows 的崩溃断言需要证明子进程已到达指定提交边界。 第一处断言以及273–275、325–327、368–370行,只要求没有 spawn 错误且退出码非零。在 Windows 分支,子进程因模块加载、配置读取或其他启动错误而提前 exit(1),也满足这两个条件。父进程随后自己执行操作,仍会得到 applied,因而“提交前崩溃恢复”被误判通过。
我用测试加载钩子只选择这四处断言的 Windows 分支,保留真实子进程启动、临时 File/SQLite 存储、生产平台判断和所有恢复断言;另一个变体让真实子进程在执行操作前 exit(1)。正常故障注入20/20通过;提前退出变体却有10/20通过,恰好是两个 provider 的全部 before 用例;after 用例10个失败,说明探针确实影响了子进程。相同提前退出在原基线20/20均被拒绝。这是断言选择实验,不是 native Windows 资格证明。最小修复:在现有子进程的实际 before/after commit 故障点写入可同步读回的阶段标记,父进程核对预期阶段,再检查退出结果;追加启动失败、正常退出、超时均不能冒充指定崩溃点的反例。
动机
受影响的是维护 Windows 测试的贡献者。维护 Windows 测试的贡献者在普通本地验证中遇到宿主信号、符号链接权限、目录同步和换行差异造成的误报,需要反复区分环境噪声与自己引入的缺陷。提案让这些既有测试按宿主能力运行,旨在恢复可靠的回归信号。实测 CRLF 文档输入已能通过;完整 Windows 成效目前只有作者报告,且上述断言会放过提前退出。本 PR 仅修改测试,不修复生产行为,也不证明 Windows 内核、ACL 或安装后的产品已验收。
改动思路
复用现有 Node 测试、实际 File/SQLite authority store 和生产拒绝规则,不新增控制面决策源。无法用 POSIX mode 或目录 fsync 表达的环境条件显式跳过;目录链接改用 Windows junction;文档仍交给原有规范化 owner 校验。进程死亡用例继续检查提交前状态、提交后 receipt、恢复及重复执行,但新增退出码判断没有独立证明子进程曾进入故障点。测试通过必须保留这个证明,不能让父进程正常首次执行代替崩溃恢复。
具体改动
十个文件的完整有效差异为 +71/-25;merge-base 是作者所述71304fcb,而 REST 当前 base 快照更靠前,不能用两点差异把其他已合并改动误算为本 PR。loopx、apps、packages、docs、scripts、skills 和 pyproject.toml 的 Git 内容在本 PR 基线与 head 相同。
关键代码讲解
canonical_task_lease_renew.test.ts四处 child 断言覆盖 acquire、renew、transfer/release、joint claim transfer。child.error === undefined能拒绝 spawn 超时;status !== 0仍不能区分指定崩溃与普通启动失败,见上面反例。生产 child 的故障点仍在canonical_task_lease_renew_process.ts::measured.commitAuthority,没有新增阶段读回。coordination_runtime_shadow.test.ts的 directory/parent/outbox 分区使用 junction,保留拒绝及原状态不变断言;cold import、shadow delivery、quota void、SQLite backup 的文件链接分支在 Windows 跳过。effect snapshot 的 mode 检查与现有生产process.getuid条件一致,摘要和长度检查继续执行。authority_archive.test.ts保留 Windows 不打开目录进行同步的验证;模拟 Linux 的目录同步错误传播用例需要真实 POSIX 内核。agent_preferences.test.ts只在非 root POSIX host 验证 mode-000 拒绝。goal_acceptance_authority.test.ts的 JSON fence 接受 LF/CRLF。使用同一真实文档和原规范化校验,仅把输入转成 CRLF:基线1 fail,head1 pass;正常 LF 也在十文件测试中通过。
规范依据是 #6351 读取版本2026-10-13T04:01:58Z 的 #6351 Expected behavior 与 #6207 的既有剩余范围。映射:LF/CRLF 配置 implemented;POSIX 行为 implemented,包含非 root mode 检查;junction 和 POSIX 信号保留 implemented(本机 POSIX 实测,junction 的真实 Windows 行为未独立核验);Windows 回归信号 not_met,具体缺口和最小修复见首条发现;全 Windows 无误报结果待独立验证。文件链接按整个平台跳过也包括有创建权限的 Windows host,不能称为“所有能表达链接的宿主都保留覆盖”。
对主干的风险
普通十文件测试在同一 Node24 环境对不可变基线与 exact head 都是236 passed、0 failed、19 skipped,跳过的19个 PostgreSQL 用例名称和原因相同,相关生产及用例主体未改变;不把这些 skips 算 PostgreSQL 通过。typecheck、diff check、语义 advisory 后的完整语义 smoke 通过,没有咨询或等待远程 CI。
主要风险是验证的假阳性:before 子进程根本没到 commit,父进程完成首次操作后报告恢复成功,后续对长期重启、身份交接和单次效果的回归就可能失去保护。需要用实际阶段证据拒绝这个反例,而非只加更多非零退出检查。符号链接跳过应清楚写明覆盖损失,真实 Windows 信号、junction、ACL 与完整平台测试仍须分别读回;本次 macOS 平台选择实验不替代它们。
我的整体评价
REQUEST_CHANGES。CRLF 修复和宿主适配有明确维护价值,减少误报可降低贡献者反复排错的成本;但当前崩溃断言对长程可靠性是负向的,会把启动失败当成已执行故障恢复。生产用户流程保持原样,完整 Windows 开发体验的净收益尚未证明,不能凭全绿计数定论。范围和代码量适当,没有发现同作者重复造 smoke 或批量低价值贡献;本地测试条件没有引入共享协议词汇。未来维护性检查建议随本次修复把四处重复的“阶段证明+退出校验”收拢为测试内 helper,保留实际 child 与存储,避免扩展生产抽象。修复并重跑正常20例、提前退出拒绝、超时/正常退出反例及 Windows 全套后再评。
English verdict: REQUEST_CHANGES. The Windows crash oracle accepts an ordinary early child exit as a successful before-commit crash/recovery. A test-local selector with real child processes and stores passes all 20 intended crashes but falsely passes all 10 before cases when the child exits before executing the operation; the baseline rejects all 20 early exits. Add independently read-back evidence that the child reached the requested fault boundary, retaining startup-error, normal-exit and timeout negatives. The CRLF change is independently useful; native Windows/ACL qualification remains separate.
The Windows branch of the process-death oracle accepted any non-zero exit, so a child that failed before the operation also satisfied "crash before commit" while the parent performed the first operation itself. The child now writes a `<config>.stage` marker at the exact before/after commit fault point before it self-terminates, and the four guards share `assertChildCrashedAtCommitBoundary`, which reads the marker back before it accepts the exit status and signal. A new negative case rejects a start-up failure, a normal exit, a spawn timeout and the other boundary stage. Refs loopx-project#6351 Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
|
回复 [P2]:崩溃断言现在先读回阶段标记,再接受退出结果。 新 head: 改动(仍仅测试面,2 个文件):
验证(native Windows,本机):
另有一条与本 diff 无关的观察:merge-base 的 English summary: Fixed at |
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent | gpt-6.1-sol | OpenAI | runtime_reported | xhigh
APPROVE — 0d26c53913b48944c7ac1ac4432e618e291622a1。旧 P2 的提前退出假阳性已修复,完整十一文件按实际基线重新判断。
动机
在没有 symlink 特权的 Windows 开发环境运行控制面测试的贡献者。过去合法的 Windows 退出形式、换行及文件权限差异会让测试误报;当前使用平台守卫或目录 junction,并要求子进程先到达指定提交故障点,才接受崩溃结果。POSIX 原有断言保留,提前退出反例现在全部拒绝,CRLF 文档配置能由真实解析器验证;生产代码树与基线相同。本次只改测试与测试子进程,不更改生产权限、租约或存储语义,也不承诺覆盖所有原生 Windows 权限组合。
改动思路
复用既有 SIGKILL 平台守卫、mode-000 非 root/POSIX 条件及目录 junction 的测试先例,直接修复现有用例。只取消 Windows signal 断言会让启动失败混进崩溃结果;当前一个测试内部 helper 在判断退出前读取实际子进程写下的故障阶段,四组调用共用同一断言,不为测试标记增加生产协议。before 标记在实际 commit hook 前写入,after 标记只在 commit 返回 applied 后写入;原租约状态、代次及联合所有权恢复断言保留。文件系统和提交的真实 owner 没有迁移。
具体改动
接受依据 issue #6351,spec_ref=https://github.com/loopx-project/loopx/issues/6351,spec_revision=issue6351-updated-2026-10-13T04:01:58Z。实际 merge base 71304fcbdd7402cfd4355fffc397079fe37d0c34 到当前十一测试文件 +111/-29;另核对旧 efde83b9aa13b4d0b882d238d498a89843483138 到当前的子进程与 oracle 修复。
- native_host_signal:实现所述守卫/平台替代。保留真正后端的故障注入;目录/parent/outbox 使用 junction,文件 symlink 以 Windows skip 或条件分支明确缩小覆盖。mode-000 不要求 Windows 提供 POSIX 权限;Linux 目录同步模拟不在真实 Windows 内核上伪装运行。
- posix_preservation:实现。相同十个完整模块在实际 POSIX 主机,基线236项通过、当前237项通过、相同19项 PostgreSQL 跳过;新增项拒绝启动失败、正常退出、超时及错误阶段。权限、摘要和长度断言仍在对应支持环境执行。
assertChildCrashedAtCommitBoundary 要求 child.error 为空、阶段精确匹配、状态非0;POSIX 继续要求 SIGKILL。测试子进程在真实 File/SQLite 提交边界写 marker,不由父进程填一个期望结果。snapshot 在可用 getuid 时做 POSIX mode 检查,digest/length 全部保留。文档提取只接受 LF 或 CRLF,仍把实际文档 JSON 交给原生产 normalizer。其余 admission/backup/coordination 测试继续使用原状态和拒绝断言,没有新增生产逃生开关。
对主干的风险
未发现当前阻塞项。最强反例保留真实子进程、生产平台判断、File/SQLite 文件与全部恢复断言,仅选取 Windows 的非 signal oracle 分支:正常故障注入旧/当前各20项通过;子进程在操作前实际退出1时,旧 oracle 误通过10项,当前全部20项拒绝。缺少阶段、阶段错误和 child.error 也由新增负向用例覆盖。这直接验证旧假阳性消失,并非用不为零的状态猜测曾经发生提交。
完整相关十模块、TypeScript typecheck 和 diff hygiene 通过;相同实际文档转换 CRLF 后基线提取失败,当前1项通过并使用真实解析器。生产 loopx/apps/packages/docs/scripts 等树与基线逐路径相同。19项 PostgreSQL 用例因未配置环境在两端同名跳过,不宣称 PG 集成通过;这不是存储后端重构。宿主为 macOS,选择器不等于原生 Windows 内核/ACL,Windows 的文件 symlink skip 也会略过有权限的 Windows 主机,需要明确保留这个覆盖边界。阶段标记只证明进程到达 commit 故障点,不证明断电持久性。CI 未查询或等待。
我的整体评价
APPROVE 当前 exact head;long_horizon 与 user_experience 均 improved:维护者反复运行测试能保留崩溃反例敏感性,Windows 贡献者按既有测试约定减少宿主噪声。此处价值来自真实假阳性对照及原断言保留,而非增加一项测试。已有覆盖和作者近期同批 PR 已按职责检索,本 PR 修既有测试而非增加重复 walkthrough;小型未来整理已落实为测试内部统一 helper,未创建推测框架。依 issue 指定的无 symlink 特权宿主接受上述有界覆盖,不把作者实机报告、平台 skip 或批准当作所有权限组合验收;无产品语义、新公共词表或权限授权变化。
English verdict: APPROVE - 0d26c53: actual premature-exit counterexample is rejected in all20 current cases versus10 old false passes; normal20 crash cases, full ten-module POSIX base/head comparison, CRLF guide and typecheck pass. Same19 optional PostgreSQL skips; native Windows kernel/ACL and privileged symlink coverage remain unverified. CI not consulted.
|
看能否在仓库的开发和review和测试的机制上,考虑好windows,让之后开发这些内容的agent不犯这些错误 |
Goal And Delivered Outcome
npm run test:control-planewas not a pass/fail signal on native Windows. At71304fcbd, 32 of 4548 tests fail for host-dependent reasons that are not product defects (fail 32, exit 1), so a Windows contributor cannot separate their own regression from known host noise. Classes: 20 self-SIGKILL exit-signal assertions, 9 file-symlink fixtures, a mode-000 read denial, a simulated Linux directory-fsync boundary, and a CRLF-sensitive doc-fence extraction.71304fcbd):ℹ tests 4548 / pass 4456 / fail 32 / skipped 60, exit 1.0d26c5391):ℹ tests 4549 / pass 4484 / fail 0 / skipped 65, exit 0. The five added skips are the primitives a Windows kernel cannot express (see the Validation table); the crash oracles read back the child's fault-stage marker before accepting the exit result.main.Author Declaration
Implemented against
71304fcbd, plus the successor row quoted from test: make control-plane path assertions host-independent on Windows #6207.canonical_task_lease_renew.test.ts(4 sites share one guard) andcanonical_task_lease_renew_process.tsstage markerquota_void_commit.test.ts,shadow_entry_delivery.test.ts,sqlite_backup.test.ts,cold_source_import.test.ts,coordination_runtime_shadow.test.ts,effect_runtime_snapshot.test.ts; directory symlinks use the suite's existing junction fallbackdirectory_symlink/parent_symlink/ qualification still passagent_preferences.test.tsauthority_archive.test.tsgoal_acceptance_authority.test.tsnpm run typecheck:control-plane;git diff --check; the semantic-inventory advisory.platform !== "win32"blocks execute, the junction argument applies only on win32,\r?\nalso matches LF); CI runs the lane.Scope And Continuation
Validation
0d26c53913b48944c7ac1ac4432e618e291622a1(the run used the identical working tree;git statuswas clean apart from an unrelated pre-existing untrackeduv.lock, which is not part of this change).regression_paritypassedtests 4548 / pass 4456 / fail 32 / skipped 60(exit 1) → aftertests 4549 / pass 4484 / fail 0 / skipped 65(exit 0); zero✖entries after.unitpassedstaticpassednpm run typecheck:control-planeexit 0;git diff --checkclean;uv run python scripts/generate_semantic_inventory.py --changed-from HEADreports no supported new vocabulary carriers.manualpassedstatus 1, signal null(hence the non-zero-status guard); a forced early child exit now fails all 10 before cases at the stage-marker check; a parent-issuedchild.kill("SIGKILL")still reportsSIGKILL— socold_source_import.test.tsneeded no change.real_backendnot_applicablelinuxdirectory-fsync row ofauthority_archive.test.tsis skipped on Windows because the kernel cannot fsync a directory handle; it still runs on POSIX. File-symlink variants are skipped only on Windows, where creating the fixture itself raisesEPERM; the remaining rejection rules in those tests still run.Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
Shared-authority RFC fixture impact
Boundary Checklist
.loopx/,.codex/goals/, and liveACTIVE_GOAL_STATE.md).none.Signed-off-bytrailer (git commit -s).