Skip to content

fix(turn-contract): hash canonical LF bytes so the provenance guard is checkout-independent - #6200

Merged
huangruiteng merged 1 commit into
loopx-project:mainfrom
JasonBuildAI:codex/turn-contract-lf-digest-x7m3
Oct 11, 2026
Merged

huangruiteng merged 1 commit into
loopx-project:mainfrom
JasonBuildAI:codex/turn-contract-lf-digest-x7m3

Conversation

@JasonBuildAI

Copy link
Copy Markdown
Contributor

Goal And Delivered Outcome

  • Outcome basis / optional anchor: reproduced defect ([Bug]: the Turn contract digest covers checkout bytes, so Windows CRLF checkouts fail the guard and the suggested regeneration breaks LF checkouts #6198) — on a Windows checkout with Git's default core.autocrlf=true, the Turn contract freshness guard fails on an unmodified revision, and the repair it prints writes a checkout-specific digest into the committed artifacts.
  • Goal/source and gap: scripts/generate_turn_contract.py hashed the physical checkout bytes of loopx/control_plane/turn_loop_controller_contract_v0.json, while Git stores that file with LF and checks it out with CRLF on Windows, so the recorded provenance digest differed per checkout. verified_generated_paths(), the semantic drift smoke, and loopx canary premerge --from-git-diff then reported stale generated Turn contract on a clean revision.
  • Observable before → after, with the validation row that proves it: examples/semantic-vocabulary-drift-smoke.py printed FAIL / stale generated Turn contract ... before and semantic-vocabulary-drift-smoke: ok after (row real_entrypoint); tests/architecture/test_turn_contract_generation.py went from 2 failed / 20 passed to 23 passed (row unit); running the generator on a CRLF checkout now reports up to date and leaves the committed artifacts untouched.
  • Issue/task and intended base: Closes [Bug]: the Turn contract digest covers checkout bytes, so Windows CRLF checkouts fail the guard and the suggested regeneration breaks LF checkouts #6198. Intended base main.

Author Declaration

  • Written by: model_agent — an OpenAI Codex agent working in the fork owner's worktree at the owner's request, under the owner's direction and review.

AI disclosure: this change was drafted with AI assistance at my request, under my direction and review.

Implemented against

  • Specification and revision: the digest owner rule at base 8d5a6fc17: scripts/generate_turn_contract.py must detect stale generated Turn artifacts from the contract's content; loopx/presentation/chat_bundle.py::source_digest states the repository's canonical-bytes rule for tracked text inputs ("Git may check text out with CRLF on Windows; binary assets remain exact"); .gitattributes treats core.autocrlf=true as a supported checkout mode.
  • Criteria:
Criterion (spec clause) Disposition Symbol / path Test or command
A tracked-text provenance digest must not depend on the checkout's line-ending translation implemented build_artifacts() in scripts/generate_turn_contract.py tests/architecture/test_turn_contract_generation.py::test_contract_digest_ignores_checkout_line_endings
Regenerating on a CRLF checkout must not modify the committed artifacts implemented same uv run --extra test python scripts/generate_turn_contract.py → up to date; git diff clean
A real contract edit must still change the digest and be reported stale implemented same mutation probe on a CRLF copy: changed reason string → digest bb2a296c... ≠ committed ab92e388...
The accepted digest shape and the committed artifacts do not change implemented turn_contract_generated.py / .ts git diff (no artifact change); required Linux checks on this PR
  • Self-check before submission: read the generator, its guard, and the chat_bundle precedent before changing anything; reproduced the failure on two native Windows checkouts (base 5fb256a4d, rebased head on 8d5a6fc17), then re-ran the same commands at the rebased head. Deliberately not fixed here: tests/architecture/test_semantic_production.py reads loopx/semantics/vocabulary_v0.json with the locale codec (UnicodeDecodeError on this host), and python -m mypy reports a Windows-stub os.WNOHANG error in loopx/control_plane/effect_runtime.py; neither file is in this diff.

Scope And Continuation

  • Completed scope and remaining work: complete within this scope. The digest is computed from canonical LF bytes, the regression test pins it, and no committed artifact changes.
  • Slice boundary / successor: N/A.

Validation

  • Tested revision: e604dd12a (rebased onto 8d5a6fc17); the before/after comparison also ran at 52040f653 on base 5fb256a4d.
  • Run state: finished
  • Input classes: synthetic
Check kind Result Public-safe evidence / limitation
unit passed uv run --extra test python -m pytest -q tests/architecture/test_turn_contract_generation.py → 23 passed. Before the change on the same checkout: 2 failed, 20 passed, both with stale generated Turn contract.
unit passed ... tests/architecture/test_semantic_vocabulary_drift.py → 126 passed; before: 1 failed with the same message.
real_entrypoint passed uv run --extra test python examples/semantic-vocabulary-drift-smoke.py → semantic-vocabulary-drift-smoke: ok; before: semantic-vocabulary-drift-smoke: FAIL + stale generated Turn contract: loopx\control_plane\turn_driver\turn_contract_generated.py; run scripts/generate_turn_contract.py.
regression_parity passed test_contract_digest_ignores_checkout_line_endings fails against the pre-fix generator (regenerated artifact differs) and passes with the fix; the mutation probe shows a content edit still moves the digest from ab92e388... to bb2a296c....
manual passed uv run --extra test loopx canary premerge --from-git-diff --git-diff-base upstream/main → merge_gate_passed: true, manual_holds: 0; catalog checks control-plane-maintainability-ratchet-smoke and semantic-vocabulary-drift-smoke passed; diff hygiene and py_compile passed.
unit passed ... tests/test_loop_turn_controller_contract.py → 172 passed (controller behavior unchanged).
static passed uv run --extra test python -m ruff check scripts/generate_turn_contract.py tests/architecture/test_turn_contract_generation.py → All checks passed.
manual passed uv run --extra test loopx check --scan-path scripts/generate_turn_contract.py --scan-path tests/architecture/test_turn_contract_generation.py → public boundary scan clean: 2 files, exit 0 (isolated state dir).
static failed uv run --extra test python -m mypy → 1 error, loopx/control_plane/effect_runtime.py:432: Module has no attribute "WNOHANG", a pre-existing Windows-stub issue. That file is not in this diff, and mypy's configured files list contains neither changed file.
  • Coverage and gaps: the regression test simulates the CRLF checkout hermetically, so it reproduces on the Linux CI shards where the defect cannot otherwise appear; the Windows windows-powershell lane does not run these architecture files. Effect-runtime-backed rows ran with Node.js 24.19.0 (declared minimum 22.22.3). Pre-existing, untouched Windows-only failures: tests/architecture/test_semantic_production.py (locale-codec read) and the mypy row above.

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 user-visible surface changed.

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

Shared-authority RFC fixture impact

  • Production-scale fixture schema: N/A
  • Semantic dimensions changed, or reviewed no-impact rationale: no vocabulary change; uv run python scripts/generate_semantic_inventory.py --changed-from upstream/main → No supported new vocabulary carriers were detected.
  • 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).

A Windows checkout with core.autocrlf=true keeps CRLF in the contract JSON, so the generator recorded a checkout-specific digest and the freshness guard reported the committed artifacts as stale; the suggested regeneration then wrote that CRLF digest into the artifacts, which fails on an LF checkout and would break the required Linux checks if committed.

Hash the contract's canonical LF bytes, matching the existing pattern in loopx/presentation/chat_bundle.py::source_digest, and pin the behavior with a regression test that regenerates from a CRLF copy of the contract.

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>
JasonBuildAI added a commit to JasonBuildAI/loopx that referenced this pull request Oct 11, 2026
…ject#6200 for agent-x7m3

Signed-off-by: JasonBuildAI <jasonbuildai@gmail.com>

@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,精确 head e604dd12ac05ddb66ecb2770430afdd638a7c357。未发现阻断项。原生 Windows/Git 安装环境及长期耗时未在本轮测量;本轮在 macOS 用实际 CRLF checkout 文件运行完整生产检查路径。

动机

受影响的是 Windows/Git CRLF contributors and maintainers reviewing generated-artifact changes。同一版本的 JSON 被 Git 检出为 CRLF 后,干净 checkout 被报为 stale;照提示运行生成器又重写两份 committed provenance,产生跨平台往返修复。 本轮实际结果:同一 LF/CRLF 输入经真实 generator --check 和 semantic smoke:base 在 CRLF 失败,head 通过;照打印提示修复时 base 改两份 artifact,head 不改文件;真实内容编辑或 artifact 损坏仍拒绝。 仅修复文本 provenance 的跨 checkout 稳定性;不修改 Turn 决策、运行时 quota/scheduler、权限或现有生成文件。

这不是 LF 单元测试失败,而是干净 CRLF checkout 被误判为 stale:贡献者照打印提示修复后,两份生成文件又写入平台特有 digest,随后 LF checkout 再报错。验收来源:https://github.com/loopx-project/loopx/issues/6198,`spec_revision: 2026-10-11T07:10:27Z;body-sha256:fc85ea6a6300624c066084bffdf1a71bda9391e8bf175846f8ed81c55c6e197c,criterion_id Expected behavior`。要求是同一文本 revision 在 LF/CRLF 下使用同一 provenance,并且无需修复或改动 committed artifact;真实契约改动仍由现有 freshness guard 识别。

改动思路

在既有 Turn artifact 生成 owner 内把文本 CRLF 归一化为 LF;无需新的 capability、模式、平台目录或共享哈希框架。 仅修复文本 provenance 的跨 checkout 稳定性;不修改 Turn 决策、运行时 quota/scheduler、权限或现有生成文件。 检索了既有 generator、完整 artifact 验证调用、chat_bundle.source_digest 和 .gitattributes。bundle 与 Turn 的资源/消费者不同,复用同一归一化原则即可;把两者合成平台或哈希框架会增加本次无须承担的依赖。未来变更成本检查:现有 owner 足够,未发现需要追加的同域重构。

具体改动

完整两文件 +17/-1 都已核验。build_artifacts() 在 SHA256 前只把物理 CRLF 转 LF;read_contract() 的解析/校验、Python/TypeScript 生成结构、verified_generated_paths() 的全文 freshness 判断及 main() 的 check/显式修复分支保持。一个聚焦测试比较实际 CRLF 输入与 committed artifact,没有新增运行时 vocabulary、状态、CLI 模式或 capability。

独立实测:三组架构/控制器 suite,head 321 passed、不可变 base 8d5a6fc170422ff4ff78818898b172064b1e1591 320 passed。同一个真实文件 harness 经实际 generator 和 semantic smoke:LF 两边成功;未修改 CRLF 在 base 都退出1、head 都退出0。执行错误消息提示的真实修复命令,base 改两份 artifact,head 改零份。实际有效 reason 字符串编辑,两边 generator/semantic 都退出1;污染生成文件,两边 generator 都退出1,最后原始字节恢复、两棵 tracked checkout 干净。该旧版本反例经过公共 caller,避免仅证明新的哈希表达式自洽。

语义 advisory 先运行(无 supported 新集合候选,不把零报告当语义证明),随后当前原生 premerge 4 direct + 2 selected 全通过、0失败/警告/manual hold,完整 semantic 和 maintainability guard 都执行;覆盖两个改动路径。未查询或等待 GitHub CI。

对主干的风险

有意行为变化只有 CRLF 文本不再触发假 stale;LF artifact 与 SHA256 格式保持。JSON 中转义的换行字符不是物理 CRLF,真实内容和 schema 校验没有被绕开。--check 仍不写文件,显式生成器仍只修复真正 stale 的 artifact;不赋予运行、调度、权限或自动修复权。无 opt-in/default-off 声明,无新 frontend/Lark 配置旅程。未修改可影响 Agent 判断的义务文本,错误和恢复指令保持原 owner。

我的整体评价

这个有界变更对体验和维护效率是正向的:实际消除了错误修复及两份 artifact 的跨平台往返,同时保留对真实 stale 的拒绝。依据是同输入 base/head 完整 CLI 和副作用对照,不能由测试数量推断跨 Goal、不同领域 Agent 的长期净收益百分比。在既有 Turn artifact 生成 owner 内把文本 CRLF 归一化为 LF;无需新的 capability、模式、平台目录或共享哈希框架。 仅修复文本 provenance 的跨 checkout 稳定性;不修改 Turn 决策、运行时 quota/scheduler、权限或现有生成文件。 没有必要为它追加状态、provider 或新能力。无阻断项;合并资格与仓库维护者的合并操作独立。

English review

English verdict: APPROVE

APPROVE at e604dd12ac05ddb66ecb2770430afdd638a7c357; no blocking finding. Specification: #6198, revision 2026-10-11T07:10:27Z;body-sha256:fc85ea6a6300624c066084bffdf1a71bda9391e8bf175846f8ed81c55c6e197c, criterion Expected behavior.

The old clean CRLF checkout falsely fails the real generator/semantic guard, and the suggested repair dirties both generated provenance files. This two-file fix canonicalizes physical CRLF to LF inside the existing artifact-generation owner, retaining schema validation, complete generated-text verification, interfaces and LF artifact bytes. The existing chat-bundle digest uses the same principle for different resources; no shared platform framework or new capability is needed.

Independent source suites: head321/base320 passed. Identical real-filesystem probes at immutable base 8d5a6fc170422ff4ff78818898b172064b1e1591 and this head show LF success at both; unchanged CRLF generator/semantic exits1 at base and0 at head; explicit repair changes2 artifacts at base and0 at head. A real contract reason edit still makes both public checks fail, and corrupted generated content still fails verification. All original bytes were restored. Current vocabulary advisory precedes native premerge:4direct+2selected passed, zero failures/warnings/manual holds, including complete semantic and maintainability guards. No GitHub CI consulted.

The bounded experience and maintenance effect is positive because false repair and cross-platform artifact churn disappear without hiding real changes. No runtime Goal/lease/quota authority or optional activation contract changes. Native Windows OS/Git installation and long-run saved time are unmeasured; actual CRLF bytes and real production checks ran on macOS. A broader refactor would add cost without a required consumer. Merge readiness remains a separate maintainer decision.

@huangruiteng
huangruiteng merged commit 05edb09 into loopx-project:main Oct 11, 2026
7 checks passed
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]: the Turn contract digest covers checkout bytes, so Windows CRLF checkouts fail the guard and the suggested regeneration breaks LF checkouts

3 participants