Repository navigation
test: guard remaining host-dependent control-plane assertions on native Windows - #6356
JasonBuildAI wants to merge 1 commit into
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.
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.efde83b9a):ℹ tests 4548 / pass 4483 / fail 0 / skipped 65, exit 0. The five added skips are the primitives a Windows kernel cannot express (see the Validation table).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)quota_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
efde83b9aa13b4d0b882d238d498a89843483138(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 4548 / pass 4483 / 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), while 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).