Skip to content

feat(semantics): generate shared Turn contracts and controller rules (M2) - #4499

Merged
huangruiteng merged 13 commits into
loopx-project:mainfrom
songoow:codex/semantic-vocabulary-m2-final
Sep 16, 2026
Merged

huangruiteng merged 13 commits into
loopx-project:mainfrom
songoow:codex/semantic-vocabulary-m2-final

Conversation

@songoow

@songoow songoow commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Dependency and review scope

Draft continuation of #4494 (M1), related to #4447. Base is main; until #4494 lands, GitHub's full diff includes that prerequisite. Do not merge this PR before M1. The M2-only range is 2c703172174f3e43bcb0f4d371d4a9b83f86ae00..e7231c534. Rebase onto main and rerun exact-head qualification after the prerequisite merges.

Problem and resulting behavior

Turn result, route and loop-disposition sets were maintained separately, while a projection dictionary only described a slice of the actual controller decision. This change gives the three vocabularies, route projection and 29 ordered controller rules a shared JSON source. The existing Python controller consumes the generated rules; TypeScript settlement consumes the generated result set. Previous imports and every value spelling remain compatible. There is no new TypeScript decision engine or persisted-state migration.

The same result can lead to different dispositions depending on quota, predecessor and budget. The contract therefore preserves the full function and its rejection/precedence rules rather than inventing a result-string-to-route mapping. Independent qualified cases exercise actual outputs and refusals; an enum declaration or a value appearing in JSON is not production evidence.

The generated Python/TypeScript pair is excluded from independently maintained twin counts only after byte-for-byte regeneration checks. Raw physical counts remain visible. Owner coverage increases from 49 to 51 and has deletion-negative tests. Q7 reuses the existing maintainability evaluator for inventory-budget exceptions only: the reviewed map is empty, targets stay unchanged, and invalid/stale/growing exceptions fail. Other semantic gates cannot be waived through it.

Contributor workflow and costs

Edit loopx/control_plane/turn_loop_controller_contract_v0.json, then run:

uv run python scripts/generate_turn_contract.py
uv run python scripts/generate_turn_contract.py --check
uv run python scripts/generate_semantic_bindings.py
uv run python scripts/generate_semantic_inventory.py
npm ci --ignore-scripts
uv run python examples/semantic-vocabulary-drift-smoke.py --report

Only regenerate artifacts affected by the source change; inspect the resulting diff. A behavior change also needs independently specified input/output and rejection cases. Do not modify generated files directly or treat generation as a proof of arbitrary data-flow safety. Checks reuse existing jobs. The public contract and bilingual RFC explain ownership, compatibility, provenance and exception repair.

Validation and independent review

  • Final integration: 321 focused Python tests; 19 TypeScript settlement tests; control-plane typecheck; mypy on 22 files; Ruff and docs governance.
  • Independent reviewer: 351 generation/controller/production tests and 228 managed-step/executor/semantic tests. Batches overlap and are not summed. All 29 rules witnessed; prior C1/C2 and owner-floor findings closed.
  • loopx canary premerge: 11/11 selected checks plus four direct checks; no failures, warnings or manual holds.
  • Real CLI self-heal on disposable state: provider capacity failure → bounded wait → same-Turn progress, exactly one quota slot.
  • Built wheel: resource bytes match, isolated Python imports execute 28 independent controller cases, table mutation changes the live result, and packaged TypeScript settlement shares the generated array.

Touched entrypoints are the existing managed-step/Turn controller and settlement paths. Their output contract and settings are preserved, so no frontend configuration/editor change is required. Characterization, real CLI and installed-package checks cover adoption. The future-facing refactor removes independent owners and keeps one evaluator; no unused framework was added.

Remote CI for this head is pending. This PR is not merge authorization and does not complete the RFC: semantic debt consolidation, legacy-field retirement and migration-linked twin reduction remain.

中文说明

M2 把三套 Turn 词表、投影与完整决策表收敛到共享契约,保留已有拼法、导入与持久化兼容性,由原 Python 控制器继续执行。生产证据来自实际控制器案例;生成文件不能自行证明生产者存活。

生成孪生必须通过逐字节来源验证,才能从独立维护计数中排除。Q7 复用已有例外生命周期,当前例外表为空,没有提高预算或放宽其他守卫。上述本地验证和独立 review 已通过;远端检查与 M1 依赖尚待完成,暂保持草稿。M3/M4 和其余债务不在本阶段完成声明内。

Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
…ptions

Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
…bulary-m2-final

Signed-off-by: song <liusongstep@gmail.com>

# Conflicts:
#	docs/architecture/rfcs/semantic-vocabulary-convergence-v0.md
#	docs/architecture/rfcs/semantic-vocabulary-convergence-v0.zh-CN.md
#	docs/reference/glossary.md
#	examples/semantic-vocabulary-drift-smoke.py
#	loopx/control_plane/turn_driver/driver.py
#	loopx/control_plane/turn_driver/loop_controller.py
#	loopx/semantics/inventory_v0.json
#	loopx/semantics/production.py
#	loopx/semantics/vocabulary_v0.json
#	tests/architecture/test_semantic_production.py
#	tests/architecture/test_semantic_vocabulary_drift.py
@songoow
songoow force-pushed the codex/semantic-vocabulary-m2-final branch from 302a10b to 55d185c Compare September 16, 2026 11:13
@songoow

songoow commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

Rebased/merged upstream/main@1b6493412 into this branch; head is now 55d185c87. 12 conflicts, resolved by synthesis rather than picking a side.

English — three resolutions needed judgment, not marker deletion:

  1. loop_controller.py — took this branch. refactor(turn): derive every Turn decision from one shared owner #4496 wrote the routing rules directly into Python; this branch moves them into the generated contract (29 rules). Same behavior, one authority. generate_turn_contract.py --check → Turn contract bindings: up to date, and the contract retains main's additions (completion_terminal, capability, initial_terminal).

  2. examples/semantic-vocabulary-drift-smoke.py — genuine synthesis. Kept this branch's generated-pair accounting (twins_raw=44 generated_verified=1 independently_maintained=43/43, so the +1 twin is excluded from budget and TWIN_BUDGET_ANCHOR stays 43) and its executable input_producer verifiers, while adopting main's Q9 model: the inventory is computed per run, not compared against a committed snapshot. Dropped this branch's now-dead render_inventory == committed freshness check and registry['inventory'] requirement.

  3. loopx/semantics/vocabulary_v0.json — took this branch's loop_disposition. main registers _completion_disposition / _route_to_disposition as producer sites; on this branch those symbols no longer exist (_route_to_disposition is now an import alias), so keeping main's entries would leave the scan pointing at non-existent sites.

One test assertion was updated with the Q9 change: tests/architecture/test_semantic_inventory_exceptions.py asserted a stale committed snapshot must fail; under per-run computation that precondition no longer exists, so it now asserts the lifecycle part only.

中文 — 三处需要判断而非删标记: (1) loop_controller.py 取本分支——#4496 把规则写进 Python,本分支收归生成契约(29 条),行为等价且单一权威;生成器校验通过。(2) smoke 是真合成——保留本分支的生成对记账(43/43,生成对不计入预算)与可执行 input_producer 验证器,同时采用 main 的 Q9 按需计算,删除本分支已死的快照新鲜度检查。(3) 注册表取本分支——main 登记的 _completion_disposition/_route_to_disposition 在本分支已不存在,保留会导致扫描指向空站点。

Evidence at 55d185c87: tests/architecture/ → 0 failed (baseline upstream/main has 24 failures in the same suite, all of which pass here); generate_turn_contract.py --check and generate_semantic_bindings.py --check both up to date; drift smoke ok.

…owup

Signed-off-by: song <22676124+songoow@users.noreply.github.com>
@songoow
songoow marked this pull request as ready for review September 16, 2026 15:12
Replaces the unsigned web-UI merge ea69b88 with a byte-identical tree so
the DCO gate passes. Brings in the two baseline fixes this PR's checks
were failing on: loopx-project#4565 (refresh-state test patch targets) and loopx-project#4567
(chat_runtime reviewed module ceiling). The M2 Turn contract, its 29
ordered controller rules, and the generated Python/TypeScript bindings
are unchanged.

Signed-off-by: song <22676124+songoow@users.noreply.github.com>

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@songoow
songoow force-pushed the codex/semantic-vocabulary-m2-final branch from ea69b88 to 445d871 Compare September 16, 2026 15:20

@huangruiteng huangruiteng 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.

动机

评审 head:ea69b88ae4904868b2753e1dd6a82b78e9d57e42;base:main(merge-base = 43d3625323,因此本 PR 的可见 diff 已经只含 M2,M1 前置 #4494 已合入)。

在 base 上,"一个 Turn 的结论"这套词表有四处独立维护:LoopXTurnResultKind 在 transaction.py、LoopXTurnRoute 在 driver.py、LoopDisposition 在 loop_controller.py,TypeScript 的 settlement.ts 还自己写了一份 TURN_RESULT_KINDS;同时 controller 的 29 条决策优先级是 loop_controller.py 里的手写 if/elif 链,而 M1 的语义清单又是靠扫描这段源码里的字面量(LoopXTurnRoute.X: LoopDisposition.Y)来证明"这些值在生产中确实会被产出"。

代价是双向的:既有一份词表被四处维护的漂移风险,也有"规则一旦变成数据,原有静态证据就失效"的证据风险。本 PR 要做的正是把这两件事一起收口:词表、投影与规则表共用一个 JSON 源,生成 Python/TypeScript 绑定,同时给规则表换一套能站得住的产出证据。

改动思路

权威输入是新契约 loopx/control_plane/turn_loop_controller_contract_v0.json(schema loop_turn_controller_decision_contract_v0,其 SHA256 被写进两个生成件头部);scripts/generate_turn_contract.py 从它派生 turn_contract_generated.py/.ts;决策所有权仍在既有的 Python decide_loop_disposition,admission 仍由 ValidatedTurnReceipt、BoundedTurnBudget 和信封签名校验把守。

三个取舍我认为都站得住:

  1. 没有第二个决策引擎。settlement.ts 只是把本地数组换成 import { TURN_RESULT_KINDS } from "./turn_contract_generated.ts" 并原样 re-export,规则求值仍然只发生在 Python。
  2. 没有 unary 的"result → route"映射。作者明确说明同一个 validated_progress 在不同 quota/前驱/预算下可以落到 run_now / replan / user gate / capability,因此生成的是有序规则表(10 条 admission check + 19 条 return,首个匹配胜出),而不是字符串查表。这一点是这份设计里最容易被做错、也是做得对的地方。
  3. 证据换引擎:规则变成数据后,静态字面量扫描不再成立,于是新增 loopx/semantics/turn_contract_witness.py,用固定合格输入真的调用 controller 与 projection,期望值在 witness 里独立声明。文档也把边界写清楚了——证明的是有限输入的 liveness,不是"任意伪造对象安全"。

具体改动

20 个文件、+3352/-398。结构上:生成件 1285 行(283 py + 524 ts,含枚举与规则表副本)、契约 JSON 478 行、生成器 302 行、witness 295 行、loop_controller.py 的求值改写(-398/+562)、测试约 800 行、文档 123 行,以及三处很窄的兼容改动。

关键代码讲解

loop_controller.py:639 decide_loop_disposition:新的求值器遍历 _LOOP_CONTROLLER_CONTRACT["rules"],inputs.fact(name) in values 做有限分区精确匹配,命中 check 行时必须先通过(否则 raise)才继续,命中 return 行即渲染结论;全部走完仍无匹配则 raise "controller contract has no disposition for the qualified inputs"。也就是说"没有默认分支"是刻意的失败闭合,而不是遗漏。

turn_contract_generated.py:283 project_turn_route:公开的 total 投影,contract_error 从原来的 KeyError 变成带信封语义的 ValueError;wait 与 blocked 都投影为 wait。driver.LoopXTurnRoute / transaction.LoopXTurnResultKind / loop_controller.LoopDisposition 都保留为兼容 re-export,所以外部 import 路径没变。

scripts/generate_turn_contract.py:266 verified_generated_paths:孪生计数豁免的唯一入口——它先 build_artifacts() 再逐字节比对,任何过期/缺失都 raise(stale generated Turn contract: <path>),不会因为文件名就放行。smoke 里对孪生预算的判定仍然打印 twins_raw、generated_verified、independently_maintained 三个数(实测 44 / 1 / 43,预算 43)。

loopx/semantics/turn_contract_witness.py:20:28 个 controller 用例 + 8 个投影用例,期望 disposition 与 reason 片段独立写在文件顶部,不来自生成表;collect_production 只在词表声明了 decide_loop_disposition 时才追加这些行。

对主干的风险

最强回归场景是"契约 JSON 改了但生成件没重生成"或"有人直接手改生成件",导致调用方读到一套词表、controller 执行另一套。防线是 --check 与 smoke 的 verified_generated_paths(),两者都是 raise 而不是修复或豁免——我做了变异验证:分别往两个生成件尾部追加一个字节后,--check 都报 stale Turn artifacts: <path> 且 exit=1,恢复后重新干净。

第二个风险是"新证据比旧证据弱"。这个我认可作者的处理:静态扫描在规则表变成数据后确实不可用,但 witness 执行的是真函数、期望值独立声明,并且明确只声称有限 liveness;我把 base 上未修改的 tests/test_loop_turn_loop_controller.py(70 例)、tests/test_turn_loop_disposition.py + tests/capabilities/test_periodic_report_pending_intent.py(61 例)以及 tests/control_plane_ts/turn_settlement.test.ts(19 例)原样拷到 head 上运行,全部通过——这比"作者自己新写的测试通过"更能说明行为保持。

P2(非阻塞):PR 描述仍写着"Do not merge this PR before M1"并引用旧的 M2-only 区间 2c7031721..e7231c534,还写着远端 CI pending。这些文字写于 rebase 之前:当前 head 的 merge-base 就是 43d3625323,前置已合入,可见 diff 已经只含 M2。建议在合并前把依赖与验证段落更新到当前 base/head,否则后来者会去追一个已经不存在的依赖。

P3(非阻塞):规则覆盖现在依赖 witness 模块;如果以后有人裁掉 witness 用例而不同步规则,覆盖可能仍绿。当前 tests/test_loop_turn_controller_contract.py 已把"每条规则都被 witness"钉住,保持这条断言即可。

语义与 CI 对齐

受影响契约:Turn 词表与投影、loop-controller 决策契约,以及语义清单的 production / 孪生 / 预算三项规则。结论是 reuse_existing:本批次沿用了 M1 已有的生成管线与 evaluate_maintainability_findings(reviewed_exceptions=...) 例外生命周期,没有新造豁免机制;REVIEWED_SEMANTIC_INVENTORY_EXCEPTIONS 为空,预算锚点未上调,BUDGET_ANCHOR 与注册表必须同 diff 移动(smoke 里 require 强制)。我另外做了一次反向验证:给它塞一条 ceiling 高于锚点的例外,smoke 立刻 FAIL stale inventory exception: semantic_inventory_budget:multi_value_twins(exit=1),说明这不是装饰性开关。

实测结果:generate_turn_contract.py --check → up to date(exit 0);semantic-vocabulary-drift-smoke.py --report → ok(coverage 26/26、owner_symbols 51/51、twins_raw=44 / generated_verified=1 / independently_maintained=43/43);pytest 聚焦 234 例通过;base 原版测试 70+61 例、TS 19 例通过。需要说明两点环境事实:TS 解析相关用例在没有 node_modules 时会失败,运行 npm ci --ignore-scripts 后 tests/architecture/test_semantic_production.py 30 例全过(该现象在 base 上同样存在,与本 PR 无关);本机 Node/SQLite 低于仓库声明下限,[*‑sqlite] 臂未运行。

我的整体评价

baseline(43d3625323)与 head(ea69b88ae)的对比:词表 owner 从"三处声明 + TS 一份数组"变为"一个契约 + 字节校验的派生对",controller 从手写优先级链变为同一优先级的生成表,投影值、拼法与 import 路径不变,inventory 预算结论在空例外表下与改前一致。没有可观测的决策漂移。

体量与收益匹配(change_proportionality: proportionate):真正产生漂移风险的四个 owner 被收成一个,且新机制全部落在既有 owner 内(turn_driver 与 semantics),没有新增 CLI/状态/权限面。repository_reuse: reused、typed_state_rule 用有限分区 + 失败闭合、authority_semantics: aligned(文档明确 controller 输出不授予 host launch / quota spend / 状态写权限)。

结论 APPROVE,两条建议均为非阻塞:先把 PR 描述的依赖段落对齐到当前 head(P2),以及保持"每条规则都被 witness"的断言(P3)。复评只需在 head 变化时重跑 --check、drift smoke 与上述三组测试。

English verdict: APPROVE - exact head ea69b88; the M2 batch collapses four owners of the Turn vocabulary into one contract with byte-verified generated bindings, keeps the Python controller as the only evaluator, and preserves observable behavior. Validated independently: generator --check clean plus mutation probes rejecting stale bindings, drift smoke ok (owner_symbols 51/51, twins_raw=44 generated_verified=1 independently_maintained=43/43), a stale reviewed exception refused, 234 focused Python tests, main's unmodified 70+61 Python and 19 TypeScript characterization cases passing at the head, and the real TS settlement suite green after npm ci. Two non-blocking notes: the PR body still declares the merged M1 prerequisite and a superseded M2-only range, and rule coverage now rests on the witness module staying pinned by tests.

@huangruiteng huangruiteng 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.

评审 head 变更说明

我先按 ea69b88ae4904868b2753e1dd6a82b78e9d57e42 完成了完整取证并发布过一条 review;发布期间该 PR 的 head 前进到 445d871d3268dba5ca59326130e133d1b5096834(一条 "Merge main into codex/semantic-vocabulary-m2-final" 的合并提交)。两条提交的目录树完全相同(ea69b88ae^{tree} == 445d871d3^{tree} == 29561ecd25df712e84c870242f2d96ec3501c7dd,git diff 为空),merge-base 仍为 43d3625323,因此前面的取证结论对当前 head 逐字适用;我仍在 445d871d3 上重跑了全部命令(见下文实测结果)。本条 review 取代上一条同内容 head 的 结论,请只以当前 head 为准。

动机

评审 head:445d871d3268dba5ca59326130e133d1b5096834;base:main(merge-base = 43d3625323,因此本 PR 的可见 diff 已经只含 M2,M1 前置 #4494 已合入)。

在 base 上,"一个 Turn 的结论"这套词表有四处独立维护:LoopXTurnResultKind 在 transaction.py、LoopXTurnRoute 在 driver.py、LoopDisposition 在 loop_controller.py,TypeScript 的 settlement.ts 还自己写了一份 TURN_RESULT_KINDS;同时 controller 的 29 条决策优先级是 loop_controller.py 里的手写 if/elif 链,而 M1 的语义清单又是靠扫描这段源码里的字面量(LoopXTurnRoute.X: LoopDisposition.Y)来证明"这些值在生产中确实会被产出"。

代价是双向的:既有一份词表被四处维护的漂移风险,也有"规则一旦变成数据,原有静态证据就失效"的证据风险。本 PR 要做的正是把这两件事一起收口:词表、投影与规则表共用一个 JSON 源,生成 Python/TypeScript 绑定,同时给规则表换一套能站得住的产出证据。

改动思路

权威输入是新契约 loopx/control_plane/turn_loop_controller_contract_v0.json(schema loop_turn_controller_decision_contract_v0,其 SHA256 被写进两个生成件头部);scripts/generate_turn_contract.py 从它派生 turn_contract_generated.py/.ts;决策所有权仍在既有的 Python decide_loop_disposition,admission 仍由 ValidatedTurnReceipt、BoundedTurnBudget 和信封签名校验把守。

三个取舍我认为都站得住:

  1. 没有第二个决策引擎。settlement.ts 只是把本地数组换成 import { TURN_RESULT_KINDS } from "./turn_contract_generated.ts" 并原样 re-export,规则求值仍然只发生在 Python。
  2. 没有 unary 的"result → route"映射。作者明确说明同一个 validated_progress 在不同 quota/前驱/预算下可以落到 run_now / replan / user gate / capability,因此生成的是有序规则表(10 条 admission check + 19 条 return,首个匹配胜出),而不是字符串查表。这一点是这份设计里最容易被做错、也是做得对的地方。
  3. 证据换引擎:规则变成数据后,静态字面量扫描不再成立,于是新增 loopx/semantics/turn_contract_witness.py,用固定合格输入真的调用 controller 与 projection,期望值在 witness 里独立声明。文档也把边界写清楚了——证明的是有限输入的 liveness,不是"任意伪造对象安全"。

具体改动

20 个文件、+3352/-398。结构上:生成件 1285 行(283 py + 524 ts,含枚举与规则表副本)、契约 JSON 478 行、生成器 302 行、witness 295 行、loop_controller.py 的求值改写(-398/+562)、测试约 800 行、文档 123 行,以及三处很窄的兼容改动。

关键代码讲解

loop_controller.py:639 decide_loop_disposition:新的求值器遍历 _LOOP_CONTROLLER_CONTRACT["rules"],inputs.fact(name) in values 做有限分区精确匹配,命中 check 行时必须先通过(否则 raise)才继续,命中 return 行即渲染结论;全部走完仍无匹配则 raise "controller contract has no disposition for the qualified inputs"。也就是说"没有默认分支"是刻意的失败闭合,而不是遗漏。

turn_contract_generated.py:283 project_turn_route:公开的 total 投影,contract_error 从原来的 KeyError 变成带信封语义的 ValueError;wait 与 blocked 都投影为 wait。driver.LoopXTurnRoute / transaction.LoopXTurnResultKind / loop_controller.LoopDisposition 都保留为兼容 re-export,所以外部 import 路径没变。

scripts/generate_turn_contract.py:266 verified_generated_paths:孪生计数豁免的唯一入口——它先 build_artifacts() 再逐字节比对,任何过期/缺失都 raise(stale generated Turn contract: <path>),不会因为文件名就放行。smoke 里对孪生预算的判定仍然打印 twins_raw、generated_verified、independently_maintained 三个数(实测 44 / 1 / 43,预算 43)。

loopx/semantics/turn_contract_witness.py:20:28 个 controller 用例 + 8 个投影用例,期望 disposition 与 reason 片段独立写在文件顶部,不来自生成表;collect_production 只在词表声明了 decide_loop_disposition 时才追加这些行。

对主干的风险

最强回归场景是"契约 JSON 改了但生成件没重生成"或"有人直接手改生成件",导致调用方读到一套词表、controller 执行另一套。防线是 --check 与 smoke 的 verified_generated_paths(),两者都是 raise 而不是修复或豁免——我做了变异验证:分别往两个生成件尾部追加一个字节后,--check 都报 stale Turn artifacts: <path> 且 exit=1,恢复后重新干净。

第二个风险是"新证据比旧证据弱"。这个我认可作者的处理:静态扫描在规则表变成数据后确实不可用,但 witness 执行的是真函数、期望值独立声明,并且明确只声称有限 liveness;我把 base 上未修改的 tests/test_loop_turn_loop_controller.py(70 例)、tests/test_turn_loop_disposition.py + tests/capabilities/test_periodic_report_pending_intent.py(61 例)以及 tests/control_plane_ts/turn_settlement.test.ts(19 例)原样拷到 head 上运行,全部通过——这比"作者自己新写的测试通过"更能说明行为保持。

P2(非阻塞):PR 描述仍写着"Do not merge this PR before M1"并引用旧的 M2-only 区间 2c7031721..e7231c534,还写着远端 CI pending。这些文字写于 rebase 之前:当前 head 的 merge-base 就是 43d3625323,前置已合入,可见 diff 已经只含 M2。建议在合并前把依赖与验证段落更新到当前 base/head,否则后来者会去追一个已经不存在的依赖。

P3(非阻塞):规则覆盖现在依赖 witness 模块;如果以后有人裁掉 witness 用例而不同步规则,覆盖可能仍绿。当前 tests/test_loop_turn_controller_contract.py 已把"每条规则都被 witness"钉住,保持这条断言即可。

语义与 CI 对齐

受影响契约:Turn 词表与投影、loop-controller 决策契约,以及语义清单的 production / 孪生 / 预算三项规则。结论是 reuse_existing:本批次沿用了 M1 已有的生成管线与 evaluate_maintainability_findings(reviewed_exceptions=...) 例外生命周期,没有新造豁免机制;REVIEWED_SEMANTIC_INVENTORY_EXCEPTIONS 为空,预算锚点未上调,BUDGET_ANCHOR 与注册表必须同 diff 移动(smoke 里 require 强制)。我另外做了一次反向验证:给它塞一条 ceiling 高于锚点的例外,smoke 立刻 FAIL stale inventory exception: semantic_inventory_budget:multi_value_twins(exit=1),说明这不是装饰性开关。

实测结果:generate_turn_contract.py --check → up to date(exit 0);semantic-vocabulary-drift-smoke.py --report → ok(coverage 26/26、owner_symbols 51/51、twins_raw=44 / generated_verified=1 / independently_maintained=43/43);pytest 聚焦 234 例通过;base 原版测试 70+61 例、TS 19 例通过。需要说明两点环境事实:TS 解析相关用例在没有 node_modules 时会失败,运行 npm ci --ignore-scripts 后 tests/architecture/test_semantic_production.py 30 例全过(该现象在 base 上同样存在,与本 PR 无关);本机 Node/SQLite 低于仓库声明下限,[*‑sqlite] 臂未运行。

我的整体评价

baseline(43d3625323)与 head(ea69b88ae)的对比:词表 owner 从"三处声明 + TS 一份数组"变为"一个契约 + 字节校验的派生对",controller 从手写优先级链变为同一优先级的生成表,投影值、拼法与 import 路径不变,inventory 预算结论在空例外表下与改前一致。没有可观测的决策漂移。

体量与收益匹配(change_proportionality: proportionate):真正产生漂移风险的四个 owner 被收成一个,且新机制全部落在既有 owner 内(turn_driver 与 semantics),没有新增 CLI/状态/权限面。repository_reuse: reused、typed_state_rule 用有限分区 + 失败闭合、authority_semantics: aligned(文档明确 controller 输出不授予 host launch / quota spend / 状态写权限)。

结论 APPROVE,两条建议均为非阻塞:先把 PR 描述的依赖段落对齐到当前 head(P2),以及保持"每条规则都被 witness"的断言(P3)。复评只需在 head 变化时重跑 --check、drift smoke 与上述三组测试。

English verdict: APPROVE - exact head 445d871; the M2 batch collapses four owners of the Turn vocabulary into one contract with byte-verified generated bindings, keeps the Python controller as the only evaluator, and preserves observable behavior. Validated independently: generator --check clean plus mutation probes rejecting stale bindings, drift smoke ok (owner_symbols 51/51, twins_raw=44 generated_verified=1 independently_maintained=43/43), a stale reviewed exception refused, 234 focused Python tests, main's unmodified 70+61 Python and 19 TypeScript characterization cases passing at the head, and the real TS settlement suite green after npm ci. Two non-blocking notes: the PR body still declares the merged M1 prerequisite and a superseded M2-only range, and rule coverage now rests on the witness module staying pinned by tests.

@huangruiteng
huangruiteng merged commit 262923d into loopx-project:main Sep 16, 2026
24 of 28 checks passed
huangruiteng pushed a commit that referenced this pull request Sep 16, 2026
…ule (#4571)

M2 (#4499) moved `LoopXTurnResultKind` to `turn_contract_generated.py`
and kept `transaction.py` as a compatibility re-export. The Python
producer scanner attributes a site only when the owner class is imported
from the owner's own module (`python_production._qualified_bindings`),
so every `LoopXTurnResultKind.*` production site in `executor.py` became
"not proven safe" although executor.py did not change in M2.

Import the enum from the generated owner. It is the same enum object
through both paths, so behavior is unchanged; the compatibility export
stays for external readers.

`examples/semantic-vocabulary-drift-smoke.py --report` on main vs this
change: unresolved_producer_sites 52 -> 42 (distinct sites 34 -> 32),
resolving `_host_result_stage` (2), `_run_task_validator` (7) and
`_task_validation_stage` (1); no site becomes newly unresolved; smoke
still reports ok.

Refs #4447 (Track B, B2 producer pilot for the Turn kernel vocabularies).

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
huangruiteng pushed a commit that referenced this pull request Sep 17, 2026
conflicting_values 18->16 and conflicting_definitions 59->55 pin the
measured values #4499 earned; both sides of each budget move in this
one diff so the equality anchor holds.  multi_value_twins stays 19 here:
the multi-value single-source batch (#4617) owns that counter and locks
it to 13.

The inventory report line now discloses unlocked headroom
(slack=key=N).  The guard only fails on overflow, so a merge that
reverts a tightened budget (registry and anchor move back together, the
merge-trap shape) used to pass silently; now the reopened headroom is
visible in the smoke output and in the PR diff of any run after it.

Signed-off-by: song <liusongstep@gmail.com>
@songoow
songoow deleted the codex/semantic-vocabulary-m2-final branch September 28, 2026 03:01
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.

2 participants