Skip to content

test: guard remaining host-dependent control-plane assertions on native Windows - #6356

Merged
huangruiteng merged 2 commits into
loopx-project:mainfrom
JasonBuildAI:codex/control-plane-windows-host-adaption-s7e5
Oct 14, 2026
Merged

huangruiteng merged 2 commits into
loopx-project:mainfrom
JasonBuildAI:codex/control-plane-windows-host-adaption-s7e5

Conversation

@JasonBuildAI

@JasonBuildAI JasonBuildAI commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Goal And Delivered Outcome

  • Outcome basis / optional anchor: Bug report [Bug]: native Windows: remaining 32 host-dependent control-plane TypeScript test failures after #6207 #6351. The merged criteria table of test: make control-plane path assertions host-independent on Windows #6207 already named this exact remainder: "The other 34 Windows failures (symlink privileges, fsync, signals) | out_of_scope".
  • Goal/source and gap: npm run test:control-plane was not a pass/fail signal on native Windows. At 71304fcbd, 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.
  • Observable before → after, with the validation row that proves it:
    • Before (native Windows, 71304fcbd): ℹ tests 4548 / pass 4456 / fail 32 / skipped 60, exit 1.
    • After (native Windows, 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.
  • Issue/task and intended base: Closes [Bug]: native Windows: remaining 32 host-dependent control-plane TypeScript test failures after #6207 #6351. Base main.

Author Declaration

  • Written by: model_agent (OpenAI Codex, via the Codex desktop app), operated by @JasonBuildAI. AI disclosure: this change was drafted with AI assistance at the operator's request, under the operator's direction and review.

Implemented against

Criterion (spec clause) Disposition Symbol / path Test or command
The control-plane suite reports no host-dependent false failures on native Windows implemented all ten test files in the diff native Windows full-suite run below
POSIX keeps the SIGKILL signal proof; every crash oracle reads back the child's fault-stage marker implemented canonical_task_lease_renew.test.ts (4 sites share one guard) and canonical_task_lease_renew_process.ts stage marker full run plus the guard negative case
File-symlink rejection coverage is kept wherever the host can express it implemented 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 fallback full run: directory_symlink / parent_symlink / qualification still pass
Mode-000 denial stays POSIX-only, matching the Python twin fixed in #6007 implemented agent_preferences.test.ts full run
The simulated Linux directory-fsync boundary is not asserted against a Windows kernel implemented authority_archive.test.ts full run
The documented configuration is extracted from LF and CRLF checkouts implemented goal_acceptance_authority.test.ts full run
  • Self-check before submission:
    • Verified: the same native Windows host before and after; focused runs of every changed file; npm run typecheck:control-plane; git diff --check; the semantic-inventory advisory.
      • Self-SIGKILL guards pin child.error === undefined, read back the child's stage marker at the requested before/after commit fault point, and only then check the exit status (POSIX keeps the SIGKILL signal); the guard negative case rejects a start-up failure, a normal exit, a spawn timeout and a wrong-stage marker (focused rerun of the file: 73 pass / 0 fail; a forced early child exit that previously passed now fails all 10 before cases at the stage check).
    • Not verified: the Linux lane, because no Linux host was available here. On POSIX every guard is a no-op (platform !== "win32" blocks execute, the junction argument applies only on win32, \r?\n also matches LF); CI runs the lane.

Scope And Continuation

  • Completed scope and remaining work: Complete within this scope. Production code is unchanged and POSIX behavior is unchanged.
  • Slice boundary / successor: N/A.

Validation

  • Tested revision: 0d26c53913b48944c7ac1ac4432e618e291622a1 (the run used the identical working tree; git status was clean apart from an unrelated pre-existing untracked uv.lock, which is not part of this change).
  • Run state: finished
  • Input classes: synthetic
Check kind Result Public-safe evidence / limitation
regression_parity passed Same native Windows host: before tests 4548 / pass 4456 / fail 32 / skipped 60 (exit 1) → after tests 4549 / pass 4484 / fail 0 / skipped 65 (exit 0); zero ✖ entries after.
unit passed Focused run of the ten changed files: 232 pass / 0 fail / 24 skipped (PostgreSQL guards and Windows host primitives); the crash-oracle file alone: 73 pass / 0 fail.
static passed npm run typecheck:control-plane exit 0; git diff --check clean; uv run python scripts/generate_semantic_inventory.py --changed-from HEAD reports no supported new vocabulary carriers.
manual passed Windows probes feeding the assertions: a self-SIGKILL child is observed as status 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-issued child.kill("SIGKILL") still reports SIGKILL — so cold_source_import.test.ts needed no change.
real_backend not_applicable Test-only change; no provider behavior is exercised differently.
  • Coverage and gaps: the Linux lane is covered by CI, not by this host. The linux directory-fsync row of authority_archive.test.ts is 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 raises EPERM; the remaining rejection rules in those tests still run.

Frontend / Visual Evidence

  • UI impact: none
  • Before: N/A
  • After: N/A
  • States and viewports shown: N/A
  • Source data: none
  • Attention review: N/A, no UI change.
  • Craft review: N/A, no UI change.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring (no functional changes)
  • Documentation update
  • Test update

LoopX Area

  • Control plane (goals, todos, quota, scheduler, registry, runtime)
  • Benchmark boundary (adapters, runners, verifiers, scoring, evidence)
  • Capability or extension (providers, adapters, skills)
  • Public docs or presentation surface (README, protocols, dashboard)
  • Build, packaging, installer, or CI
  • Host or runtime integration

Technical Direction

  • Direction / acceptance reference, when applicable: N/A.

Shared-authority RFC fixture impact

  • Production-scale fixture schema: N/A.
  • Semantic dimensions changed, or reviewed no-impact rationale: N/A, no shared-authority contract or fixture schema changed.
  • Provider conformance arms run: N/A.
  • Read-only legacy/file/PostgreSQL three-arm rehearsal (required for promotion, runtime-routing, or compatibility-projection changes): N/A.

Boundary Checklist

  • Neither the diff nor this PR body/comments/attachments disclose private state, credentials, raw traces or verifier output, internal links, or local machine paths (including .loopx/, .codex/goals/, and live ACTIVE_GOAL_STATE.md).
  • I did not duplicate maintainer-owned benchmark work unless a maintainer split out a public issue for it.
  • I kept the change scoped to the linked issue/task.
  • I completed the visual evidence section for UI changes, or marked UI impact none.
  • Every commit includes a DCO Signed-off-by trailer (git commit -s).

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>
@JasonBuildAI
JasonBuildAI force-pushed the codex/control-plane-windows-host-adaption-s7e5 branch from f09342c to efde83b Compare October 13, 2026 04:57

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@JasonBuildAI

Copy link
Copy Markdown
Contributor Author

回复 [P2]:崩溃断言现在先读回阶段标记,再接受退出结果。 新 head:0d26c5391。

改动(仍仅测试面,2 个文件):

  1. canonical_task_lease_renew_process.ts:子进程在 measured.commitAuthority 的两个真实故障点——before-commit,以及 after-commit 且 result.status === "applied"——先同步写入阶段标记 <config>.stage,再自行 SIGKILL。标记只在故障点产生;"退出码非零"不再单独作为故障点证据。
  2. canonical_task_lease_renew.test.ts:按维护性建议把四处重复的"阶段证明+退出校验"收拢为测试内 helper assertChildCrashedAtCommitBoundary(判定顺序:spawn 错误 → 阶段标记与期望边界一致 → 非零退出 → POSIX SIGKILL),acquire、renew、transfer/release、joint claim transfer 四处统一使用。
  3. 新增 1 个反例测试:真实子进程启动失败(非零退出、无 marker)、正常退出(status 0)、spawn 超时(ETIMEDOUT)、以及另一阶段的 marker(用 "after" 冒充 "before")都会被该 guard 拒绝。

验证(native Windows,本机):

  • 提前退出反证:把子进程临时改成在执行操作前 exit(1)(你实验中的提前退出变体),聚焦文件为 63 pass / 10 fail——失败恰好是两个 provider 的全部 before 用例,失败点全部是 AssertionError: child never reached the before-commit fault point;还原真实子进程后 73 pass / 0 fail。
  • 20 个 process-death 正常用例全部通过(若子进程未到达故障点、marker 缺失,它们会全部失败)。
  • 全量 npm run test:control-plane:tests 4549 / pass 4484 / fail 0 / skipped 65,exit 0。
  • npm run typecheck:control-plane exit 0;git diff --check clean;语义 advisory 无新增词汇载体。

另有一条与本 diff 无关的观察:merge-base 的 tests/control_plane_ts/sqlite_authority_store.test.ts(来自已合并的 #4328)在 Windows 分支用 "stdout ready + 非零退出" 作判据;ready 只能证明启动完成,不能证明到达 COMMIT 边界,理论上存在同类假阳性。本 PR 未改动该文件;如评审希望收口,我可以用同样的阶段标记方式并入本 PR 或另开一个有界小改动。

English summary: Fixed at 0d26c5391. The crash child now writes a stage marker at the exact before/after commit fault point and the parent reads it back before accepting the exit status/signal; the four sites share one test-local helper; a negative case rejects start-up failure, normal exit, timeout and wrong-stage markers; a forced early exit(1) now fails all 10 before cases at the stage check. Full Windows suite tests 4549 / pass 4484 / fail 0 / skipped 65, exit 0; typecheck 0. A pre-existing sibling oracle in sqlite_authority_store.test.ts (merged #4328) is noted but outside this diff.

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@huangruiteng
huangruiteng merged commit db35c65 into loopx-project:main Oct 14, 2026
49 of 53 checks passed
@huangruiteng

Copy link
Copy Markdown
Collaborator

看能否在仓库的开发和review和测试的机制上,考虑好windows,让之后开发这些内容的agent不犯这些错误

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: native Windows: remaining 32 host-dependent control-plane TypeScript test failures after #6207

3 participants