docs(semantics): say what each Turn kernel vocabulary value means - #4625
huangruiteng merged 3 commits into
Conversation
The registry settles who owns a vocabulary and which values are legal, but until now it said nothing about what an individual value means. Measured on the baseline: 13 of 149 registered values carry a value_notes entry, and the three existing sets of notes record disposition (legacy class, compatibility -only) rather than meaning. A reader who wants to know when the controller emits `stop` rather than `repair` has to reconstruct it from the generated rule table. Document all 32 values of the four canonical Turn kernel vocabularies against their deciding code, not against their names: - `turn_result_kind` and `loop_disposition` notes name the controller rule that produces the value, so `stop` records that it is reachable only from an `iteration_failed` receipt, and `repair` lists the five failed receipt classes that collapse into it. - `turn_route` notes carry the `_typed_route` condition, including that `blocked` projects to `wait` rather than failing, and that `contract_error` is the one route with no disposition because the controller rejects instead. - `agent_scope_frontier_action` notes separate the three lookalike verdicts by what is actually missing: `agent_scope_wait` when a blocking handoff is claimed elsewhere, `reassignment_required` when visible work is claimed elsewhere, `agent_scope_exhausted` when there is no candidate at all. Pin the coverage with a test, so a new value in one of these four sets fails the PR path until the diff that adds it also says what it means. A new value here is a new control-flow case; requiring the note in the same diff keeps that case reviewable. The test rejects an empty or whitespace-only note, and was mutation-checked by dropping one entry. No behaviour, budget or inventory count changes: value_notes is registry documentation, already validated by the drift smoke to name only registered values. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: song <22676124+songoow@users.noreply.github.com>
exact-head 复核(
|
| 词表 | 依据 |
|---|---|
turn_result_kind、loop_disposition |
TURN_CONTROLLER_CONTRACT 的 29 条规则 —— stop 记录它只能从 iteration_failed receipt 到达;repair 列出坍缩进它的五个失败 receipt 类 |
turn_route |
driver.py::_typed_route 的判定条件 —— 包括 blocked 投影到 wait 而非报错,以及 contract_error 是唯一没有处置的路由(控制器直接拒绝) |
agent_scope_frontier_action |
agent_scope.py —— 按"缺的到底是什么"区分三个长得很像的判定:阻塞交接被他人认领(agent_scope_wait)、可见推进工作被他人认领(reassignment_required)、根本没有候选(agent_scope_exhausted) |
一处我自己的更正
PR 描述初版称 external_evidence_observe、heartbeat_receipt_write_failed、runtime_user_gate_projection_repair 在 owner 之外零引用。那是错的,已更正描述。我当时只按字符串字面量搜索;按枚举成员搜,三者都有真实生产者(should_run_packet.py:895、heartbeat_receipt.py:421、stall_repair.py:220)——它们走的正是仓库要求的 owner import 写法,字面量扫描本就看不见。这恰好复现了 RFC 第 9 节记载的字面量扫描边界。
对主干的风险
零行为变更。value_notes 是注册表文档字段,drift smoke 已经校验它只能命名已注册取值(examples/semantic-vocabulary-drift-smoke.py:228)。预算与清单计数逐项与基线相同。
新增测试是一条棘轮:这四个词表将来新增取值时,不写注记就过不了 PR 路径。这里新增一个取值就是新增一条控制流分支,要求同一 diff 说清楚它意味着什么。
验证(当前 exact head)
semantic-vocabulary-drift-smoke: ok
coverage=vocabularies:26/26,owner_symbols:51/51,...
conflicting_values_semantic=0/0 unresolved_producer_sites=41 # 与基线逐项一致
pytest tests/architecture/test_semantic_vocabulary_drift.py \
tests/architecture/test_semantic_inventory.py -q -> 77 passed
变异验证:删掉 turn_route.blocked 的注记 -> 测试失败并点名 ['blocked']
环境归因:缺 node_modules 时 TypeScript 生产解析器抛错,clean origin/main 上同样 16 failed / 42 passed;补齐 Node 依赖后本分支全绿,净增 4 条通过、零新增失败。
huangruiteng
left a comment
There was a problem hiding this comment.
详细评审(exact head f349b80c0)
动机
注册表已经说清"谁拥有这个词表、哪些值合法",但没说"某个值是什么意思":作者给出的基线测量是 149 个已登记值里只有 13 个带 value_notes,而且已有的三套 notes 记的是处置(legacy class / compatibility-only),不是含义。想弄清控制器什么时候发 stop 而不是 repair,读者只能回到生成的规则表里自己重建。这个 gap 是真的,而且随每个新值持续复利——新值就是新的控制流分支。
改动思路
把含义写在值旁边,而不是新开一份文档:四个 Turn 内核词表(turn_result_kind、turn_route、loop_disposition、agent_scope_frontier_action)的 32 个值全部对着决定它们的代码写 note,并用一条参数化测试把覆盖面钉住——以后往这四套里加值,同一个 diff 里必须带上含义说明,否则 PR 路径失败。方向对:文档与它解释的注册表同源,不会各自漂移。
具体改动
loopx/semantics/vocabulary_v0.json(+36/-1):turn_result_kind(12 值,含iteration_failed是唯一停外层循环的结果、repair收拢五类失败收据)、turn_route(8 值,来自driver._typed_route,含blocked → wait与contract_error是唯一没有 disposition 的路由)、loop_disposition(8 值,来自 29 条控制器规则)、agent_scope_frontier_action(4 值,区分三个相像的 frontier:blocking handoff 被别人认领 →agent_scope_wait、可见工作被别人认领 →reassignment_required、完全没有候选 →agent_scope_exhausted)。原有 notes 全部保留。tests/architecture/test_semantic_vocabulary_drift.py(+25):test_turn_kernel_values_each_carry_a_note,四个参数各查一套词表,空串与纯空白都算缺失;用文件里既有的runpy加载器读注册表。
验证(都在 f349b80c0 上):pytest tests/architecture/test_semantic_vocabulary_drift.py -q → 62 passed(main 同环境 58 passed,正好是新增 4 条);pytest tests/architecture/test_semantic_production.py tests/capabilities/test_pr_review_contract.py -q → 65 passed;examples/semantic-vocabulary-drift-smoke.py → exit 0,conflicting_values=16/18、kernel_producer_coverage_pending 为空,说明这次的 note 没有挪动语义冲突预算或清单计数。
对主干的风险
我在本 head 上逐值核对了 note 与决定代码,没有发现与实现不符的表述:stop 在整个 TURN_CONTROLLER_CONTRACT 里只出现一次(receipt_iteration_failed),而 loop_controller.py 是契约驱动、没有手写的 STOP 路径;repair 收的五类失败收据与 failed_receipt 规则完全一致;turn_route 八个条件与 driver.py::_typed_route 第 65–109 行逐条对应;successor_replan_required 点名的五个 frontier(selected-candidate priority、monitor blocked resume、deferred resume、route continuation、cleared handoff)与 agent_scope.py 五个构造点一致,且该分支 quiet_noop_allowed=False、在 should_run_packet.py 里令 should_run=True,所以 "被调度器当作要推进的工作而不是等待" 的说法成立。
风险面很小,因为这次没有任何生产行为、值集、schema 或预算变化,回滚只需撤掉文本与那条测试。唯二需要说清的限制:(1) 测试保证的是"有 note",不是"note 正确"——正确性靠人读代码,我按上面的方式代读了一遍;(2) 其余内核词表仍未覆盖,本 PR 明确只做这四套。
另外提醒一个环境性现象,避免误判:在没有 node_modules 的干净 worktree 里,tests/architecture/test_semantic_vocabulary_drift.py 会报 16 条失败(TypeScript production parser 不可用)。同一个 worktree 的 origin/main 也是同样 16 条,而带依赖的主检出上该文件 58/58 全绿;本 head 补上依赖后 62/62 全绿。也就是说这 16 条不是本 PR 引入的,评审时不要把它记成本 PR 的回归。
我的整体评价
APPROVE。 目标明确、范围克制、文档与决定代码同源,测试把未来的新值钉在了同一个 diff 里;我用独立复算(逐值比对 + 自己抹掉一条 note 做变异验证:抹掉 turn_result_kind.wait 后恰好只有该参数失败,恢复后 4/4 通过)确认了这不是自己证明自己的空断言。剩下的(其他词表的含义说明)属于同一 Track A 的后续,不构成本 PR 的阻塞。
关键代码讲解
loopx/semantics/vocabulary_v0.json(turn_result_kind.value_notes):每个 note 都指向产生该值的收据或规则,而不是复述值名——例如validated_progress说明"重路由到新的 should-run 决定,只有预算耗尽才转 replan",对应progress_exhausted与progress_route两条规则的前后顺序。loopx/control_plane/turn_driver/driver.py::_typed_route:turn_route的 8 条 note 直接来自这里的判序(schema/signature → capability intent → replan → repair → ready_for_host;非 should_run 再分 user/wait/blocked),contract_error无 disposition 也正是这里唯一抛错的分支。loopx/control_plane/agents/agent_scope.py:四个 frontier 动作的差异被准确写成"缺什么"——agent_scope_wait缺的是被别人的 blocking handoff 占着的候选,reassignment_required缺的是被别人占着的可见工作,agent_scope_exhausted则是根本没有候选;这三条与 1235–1280 行的分支一一对应。tests/architecture/test_semantic_vocabulary_drift.py::test_turn_kernel_values_each_carry_a_note:用注册表自己的 values 列表做集合差,不做白名单,因此不存在"把新值加到豁免表里就绕过去"的退路。
English verdict: APPROVE - reviewed exact head f349b80. This documents the meaning of all 32 values across turn_result_kind, turn_route, loop_disposition and agent_scope_frontier_action against the code that produces them, and pins the coverage with a parametrized test that treats a blank note as missing. I verified every note against its decider: stop appears exactly once in TURN_CONTROLLER_CONTRACT (receipt_iteration_failed) and loop_controller.py has no hand-written STOP path; the repair note lists exactly the five failed receipt classes in the failed_receipt rule; the eight turn_route notes match driver.py::_typed_route line by line including blocked->wait and contract_error having no disposition; the five agent_scope frontiers named for successor_replan_required match the five builders and that branch sets quiet_noop_allowed=False with should_run=True downstream. Validation at this head: drift file 62 passed (main 58 in the same environment, the delta being the four new cases), tests/architecture/test_semantic_production.py plus tests/capabilities/test_pr_review_contract.py 65 passed, drift smoke exit 0 with unchanged conflicting_values and empty kernel_producer_coverage_pending. My own mutation check (blanking the wait note) fails exactly turn_result_kind and passes 4/4 after restore. Note for other reviewers: the drift file reports 16 failures in clean worktrees that lack node_modules, identically on origin/main, so those are environmental and not introduced here.
…ry-value-notes Signed-off-by: song <22676124+songoow@users.noreply.github.com>
本 PR 在 #4447 计划中的位置issue #4447 现在有一节统一协调(中英双语),把这 13 个在开 PR 作为一个计划列出:各自修什么、为何必要、以及实测出的合并顺序。 冲突实测:对全部 78 对做了试合并,9 对冲突,分四簇,每一处都是文本相邻,没有一处是语义分歧。
建议顺序(代价从低到高):#4628 → #4625、#4626 → #4627 → #4619、#4621 → #4630 → #4614 → #4631 → #4629 → #4617 → #4606 → #4608。四个棘轮 PR 放最后,因为每落地一个,下一个的数字就从估算变成确定值。 全部 13 个 PR 现已同步到 |
huangruiteng
left a comment
There was a problem hiding this comment.
复确认评审(exact head dc51d8f2d)— 分支已 rebase,内容未变
动机
与首次评审相同:注册表说清了"谁拥有词表、哪些值合法",但没说"某个值是什么意思"。我在 f349b80c0 上已给出完整评审与结论(APPROVE)。这次 head 变化的唯一原因是作者把分支合并了最新 origin/main,以便不再落后主干。
改动思路 与 具体改动
本 head 的两个文件与 f349b80c0 逐字节相同(loopx/semantics/vocabulary_v0.json 与 tests/architecture/test_semantic_vocabulary_drift.py 的 md5 一致),diff 规模同样是 2 文件 +60/-1。也就是说:四个 Turn 内核词表的 32 个值含义说明、以及那条把覆盖率钉住的参数化测试,内容没有任何变化;唯一变化来自被合并进来的主干提交。
对主干的风险
无新增风险。我在本 head 上重跑了验证:tests/architecture/test_semantic_vocabulary_drift.py、tests/architecture/test_semantic_production.py、tests/capabilities/test_pr_review_contract.py → 127 passed(drift 文件 62 条含新增 4 条,另两份 65 条),与首次评审时在旧 head 上得到的 62/65 完全一致;examples/semantic-vocabulary-drift-smoke.py 仍 exit 0。首次评审里逐值比对决定代码(TURN_CONTROLLER_CONTRACT 中 stop 仅一处、driver._typed_route 的八条路由条件、五个 agent-scope frontier 构造点)与我自己做的变异验证(抹掉 turn_result_kind.wait 后恰好该参数失败)在内容不变的前提下继续成立。
我的整体评价
APPROVE。 这是纯 rebase 的复确认:改动内容与此前被我批准的 head 逐字节一致,验证结果一致,之前的结论不变。合并仍由维护者决定;本 head 的精确评审记录即此卡片。
English verdict: APPROVE - reviewed exact head dc51d8f. Re-confirmation after a pure rebase: both PR files are byte-identical to f349b80 (md5 match) with the same 2-file +60/-1 diff, and the branch only merged origin/main. Re-run at this head: tests/architecture/test_semantic_vocabulary_drift.py plus test_semantic_production.py and test_pr_review_contract.py = 127 passed (62 including the 4 new cases, plus 65), and the drift smoke exits 0 - identical to the first pass. The value-by-value comparison against TURN_CONTROLLER_CONTRACT, driver._typed_route and the five agent-scope frontier builders, and my mutation check on the wait note, remain valid because the content did not change.
huangruiteng
left a comment
There was a problem hiding this comment.
复确认评审 — exact head dc51d8f2df65e13fee507d4a6d8356202756f62e
动机
与首次评审相同:注册表说清了"谁拥有词表、哪些值合法",但没说"某个值是什么意思"。我在 f349b80c0 上已给出完整评审与结论(APPROVE)。这次 head 变化的唯一原因是作者把分支合并了最新 origin/main,让分支不再落后主干。
改动思路
不改内容,只做 rebase:本 head 的两个文件与 f349b80c0 逐字节相同(loopx/semantics/vocabulary_v0.json 与 tests/architecture/test_semantic_vocabulary_drift.py 的 md5 一致),diff 规模同样是 2 文件 +60/-1。也就是说四个 Turn 内核词表 32 个值的含义说明、以及把覆盖率钉住的参数化测试都没有变化;变化只来自被合并进来的主干提交。
具体改动
loopx/semantics/vocabulary_v0.json:turn_result_kind、turn_route、loop_disposition、agent_scope_frontier_action的 32 个值全部对着决定它们的代码写明含义(含iteration_failed是唯一停外层循环的结果、blocked → wait、contract_error无 disposition、三个相像 frontier 的区分)。tests/architecture/test_semantic_vocabulary_drift.py:test_turn_kernel_values_each_carry_a_note,四个参数覆盖上述四套,空串与纯空白都算缺失。
本 head 上重跑:tests/architecture/test_semantic_vocabulary_drift.py、tests/architecture/test_semantic_production.py、tests/capabilities/test_pr_review_contract.py → 127 passed(drift 62 条含新增 4 条,另两份 65 条);examples/semantic-vocabulary-drift-smoke.py → exit 0。与首次评审在旧 head 上得到的 62/65 完全一致。
对主干的风险
无新增风险。首次评审里我逐值比对过决定代码(TURN_CONTROLLER_CONTRACT 中 stop 仅出现一次且 loop_controller.py 无手写 STOP 路径、driver._typed_route 八条路由条件、五个 agent-scope frontier 构造点),并做过变异验证(把 turn_result_kind.wait 的 note 抹白后恰好只有该参数失败);这些在内容逐字节不变的前提下继续成立。合并仍由维护者决定。
我的整体评价
APPROVE。 纯 rebase 的复确认:内容与此前被我批准的 head 逐字节一致,验证结果一致,结论不变。本 head 的精确评审记录即此卡片。
English verdict: APPROVE - reviewed exact head dc51d8f. Re-confirmation after a pure rebase: both PR files are byte-identical to f349b80 (md5 match) with the same 2-file +60/-1 diff, and only origin/main was merged in. Re-run at this head: tests/architecture/test_semantic_vocabulary_drift.py plus tests/architecture/test_semantic_production.py and tests/capabilities/test_pr_review_contract.py = 127 passed (62 including the four new cases, plus 65), and the drift smoke exits 0 - identical to the first pass. The value-by-value comparison against TURN_CONTROLLER_CONTRACT, driver._typed_route and the five agent-scope frontier builders, and my mutation check on the wait note, remain valid because the content did not change.
…ry-value-notes Signed-off-by: song <22676124+songoow@users.noreply.github.com>
huangruiteng
left a comment
There was a problem hiding this comment.
Reviewed exact head: e9e9aa6d1450c960e3954c4fb1637c03a0509d31 (codex/kernel-vocabulary-value-notes).
动机
#4447 的 Track A 是注册表的「文档半边」:注册表已经说清谁拥有一个词表、哪些值合法,但没说单个值是什么意思。基线实测是 149 个注册值里只有 13 个带 value_notes,而且三类既有说明记的都是「兼容状态」而不是语义。读者想知道控制器为什么发 stop 而不是 repair,只能自己去生成的规则表里倒推。
改动后四个 canonical kernel 词表的 32 个值都有了对着代码写的说明(turn_result_kind 12、turn_route 8、loop_disposition 8、agent_scope_frontier_action 4),并且加了测试:这四个词表里新增一个没有说明的值就会在同一个 diff 里失败。这是一个可独立复核的完整切片——它把 #4447 的文档半边做完,并明确把 effective_action(等你说的 M1 slot 拆分)留在后续,不是顺手带上。
改动思路
入口是 loopx/semantics/vocabulary_v0.json,经 examples/semantic-vocabulary-drift-smoke.py::load_registry 读取;说明的权威来源是决定这些值的代码:TURN_CONTROLLER_CONTRACT 的规则表、driver.py::_typed_route、agent_scope.py 的 frontier 构造。改动只扩展既有可选键 value_notes(VOCABULARY_OPTIONAL_KEYS 里本来就有,smoke 也本来就会校验它只能命名已注册的值),没有新增 key、没有新增词表、没有新增 owner,所以是一条「复用」而不是「新建权威」的路。
判定边界在新增的测试:parametrize 四个词表,对每个值要求 value_notes 非空(strip() 之后),空串/纯空白算缺失。副作用为零:value_notes 不被任何运行时读取,唯一消费者是 smoke 的键校验与这个新测试。
我另外确认了「说明不会跑到别处去」:scripts/generate_semantic_bindings.py 的 render_binding 只取枚举成员与值串,render_glossary 只读 meaning/owners/values/compatibility_only,都不读 value_notes,所以本 PR 不需要重新生成任何产物——我在这个 head 上跑了 --check,输出 semantic bindings/glossary: up to date。
具体改动
2 个文件、+60/-1:注册表 36 行 JSON(28 条新说明加标点),测试 25 行。那 1 行删除是 turn_result_kind.value_notes 原来最后一条末尾补的逗号。
我在这个 head 上独立核了对数:注册表已文档化值 13/149 → 41/149,其中 turn_result_kind 4/12 → 12/12、turn_route 0/8 → 8/8、loop_disposition 0/8 → 8/8、agent_scope_frontier_action 0/4 → 4/4;lease_action 4/4 与 effective_action 5/32 未变——也就是说只有这四个词表被动到。
关键代码讲解
turn_result_kind.value_notes(注册表 168 行起):新写的iteration_failed说明是「the only result kind that stops the outer loop」,我对照规则表验证disposition: 'stop'只出现一次(receipt_iteration_failed,turn_contract_generated.py:196-200);terminal_closeout_failed被并入failed_receipt的五个失败类 → repair,与既有四条失败类说明一致。turn_route.value_notes(224 行起):blocked说明「Projects to wait, so a blocked lane waits rather than failing」与route_projection的'blocked': 'wait'一致;contract_error说明「the one route with no disposition」与route_projection['contract_error'] = None及project_turn_route抛ValueError一致;capability_action_required说明的「malformed intent yields contract_error」对应_typed_route里对pending_capability_intent_projection_v0/goal/agent/command 的检查。loop_disposition.value_notes(269 行起):terminal的两条来源(无 receipt 的 terminal action、validated_completion声明no_followup)与initial_terminal、completion_terminal两条规则一一对应;stop的「only from an iteration_failed receipt」我也逐规则核过。agent_scope_frontier_action.value_notes(308 行起):三条形似判定按「缺什么」区分,与agent_scope.py:1251-1275的三个分支完全一致(blocking_claimants→agent_scope_wait;其他被认领的 advancement →reassignment_required;都没有 →agent_scope_exhausted);successor_replan_required说明里点的五个 frontier 正好是它在该文件里的五个生产点,而「with must_attempt the scheduler treats it as active work rather than a wait」对应scheduler/arbitration.py:92-93返回ACTIVE_WORK。test_turn_kernel_values_each_carry_a_note(测试 373 行起):4 个新用例全通过;我用它自己的谓词在内存里删掉turn_route.blocked与loop_disposition.stop,分别报出['blocked']、['stop'],把wait改成纯空白也会被报出——PR 里说的 mutation check 是真的。
对主干的风险
我跑了 pytest tests/architecture/test_semantic_vocabulary_drift.py tests/architecture/test_semantic_inventory.py -q → 80 passed(PR 正文在其较早 head 上报 77);python examples/semantic-vocabulary-drift-smoke.py → ok,计数面与 main 完全一致(conflicting_values_semantic=0/0、unresolved_producer_sites=41、coverage 26/26 … 1/1);generate_semantic_bindings.py --check → up to date。
最强回归不是崩溃而是「读者信任一条看起来穷尽的说明,却漏掉真实的产生路径」。据此一条 P3(F1,非阻塞):loop_disposition 的 repair 与 replan 说明读起来是完整触发列表,但都漏了 fresh route 投影这条路径——initial_route / progress_route / completion_successor / completion_active_goal 的 reason 表里明确写着 'repair': 'fresh decision requires repair'、'replan': 'fresh decision requires replan'。也就是说一个没有 receipt、只是 fresh route 为 repair_required/replan_required 的回合同样会得到 repair/replan。同一块里 run_now("Projected from route ready_for_host")、wait("Route wait or blocked, …")、user_action_required("The fresh route or the last receipt …")都写了 route 投影,所以这是块内不一致。最小修复就是把这条子句补进两条说明。
另一条 P3(F2,非阻塞):新测试把「canonical kernel 词表」这个集合手工写成四个名字,而注册表本身已经带着这个分类——我算过 tier == 'kernel' 有 6 个,剔除 effective_action(merge_candidate) 与 lease_action(compatibility_only) 正好是这四个。将来真出现第五个 canonical kernel 词表时,注册表会加、测试不会覆盖,而这个测试存在的意义恰恰是防这类漂移。建议直接按 tier == 'kernel' and status == 'canonical' 参数化,或断言两者相等。
语义与 CI 对齐
本 PR 属于复用既有语义面:只扩展注册表既有的 value_notes 键,未新增词表/owner/键,规则的唯一变化是「这四个词表的值必须有说明」这一覆盖约束,且它由 CI 内的架构测试执行(不是散文引导)。CI 侧不改任何生成产物,--check 已验证新鲜。需要跟进的只有上面 F1/F2 两条文档保真度问题。
我的整体评价
结论 APPROVE。这是一次范围清楚、可独立复核的文档增量:32 个控制流取值现在带着对着代码写的含义,且新增值必须带说明,把 #4447 的文档半边落地而不夹带 effective_action。我逐条把说明与 TURN_CONTROLLER_CONTRACT、_typed_route、agent-scope frontier 构造、scheduler/arbitration.py 对过,结论与作者声明一致;文档化值计数、smoke 计数面、生成产物新鲜度和 80 个架构测试我都独立复现。回退成本是一个 commit。
两条 P3 都不阻塞:一条是说明的完整性(repair/replan 漏写 fresh route 路径),一条是测试里「canonical kernel」集合的第二份表达。它们都不改变本次判断,但在这些说明被当作「什么时候会产生这个值」的权威答案之前,值得顺手补齐。
English verdict: APPROVE - exact head e9e9aa6; I verified the registry-side claims directly (documented values 13/149 -> 41/149, only the four canonical kernel vocabularies change, turn_result_kind 12/12, turn_route 8/8, loop_disposition 8/8, agent_scope_frontier_action 4/4), read each new note against TURN_CONTROLLER_CONTRACT, _typed_route, the agent_scope frontier builders and scheduler arbitration (all match), reproduced the test's mutation check against removed and whitespace-only notes, and confirmed the drift smoke, bindings/glossary freshness and 80 architecture tests at this head. Two non-blocking P3s: the loop_disposition repair and replan notes read as complete trigger lists but omit the fresh-route path that reaches the same disposition, and the new test keeps a second hand-written copy of the registry's canonical-kernel vocabulary set instead of deriving it from tier/status.
Six tracker PRs landed while this branch was open. Three touched files it also edits, in the way loopx-project#4447's merge-order note predicted: - loopx-project#4626 and loopx-project#4625 append to the end of `test_semantic_vocabulary_drift.py`; both blocks are kept, theirs first. - loopx-project#4627 replaced the Section 11 target table with a *Measured by* column and a rule that the table carries no dated values, since those belong to the tracker. This branch's row had added dated numbers, so the resolution takes loopx-project#4627's table and puts the migration surface in *Measured by* as the `--report` line that prints it. The dated table stays in Appendix A. - loopx-project#4628 memoized `python_facts`. The retirement scan needs the tree rather than the facts, so `parse_python` is factored out for one error path and left uncached: caching the trees held about two million AST nodes for the rest of the run and measured 0.7s worse overall, while slowing `check_inventory` from 2.4s to 5.4s -- the pass loopx-project#4628 had just made cheaper. Remeasured on the integrated tree: every role count is unchanged, and `dynamic_mapping_key_sites` moved 1704 to 1712 with the new code. Both mirrors carry the new number. `loopx/semantics/field_use.py` also had to stop spelling the six field names in its own docstrings. The scan reads tracked sources under `loopx/`, this module is one of them, and committing it pushed `heartbeat_recommendation` to 18 of a budget of 17 -- the check catching its own module. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: song <22676124+songoow@users.noreply.github.com>
Only conflict is the end of test_semantic_vocabulary_drift.py, where loopx-project#4625 and loopx-project#4626 append their value-notes coverage blocks and this branch appends the invariant-domain fixtures. loopx-project#4447's merge-order table calls this cluster out: keep every block, there is no overlap. Both are kept, theirs first. Revalidated on the integrated tree: drift smoke ok, docs governance ok, 99 drift tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: song <22676124+songoow@users.noreply.github.com>
Same append-at-end cluster as loopx-project#4631: loopx-project#4625 and loopx-project#4626 landed their blocks at the end of test_semantic_vocabulary_drift.py while this branch appends the ratchet-lock fixtures. Both blocks kept, theirs first. The locked values still hold on the integrated tree: conflicting_values 16/16 and conflicting_definitions 55/55, so no anchor moves in this merge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: song <22676124+songoow@users.noreply.github.com>
The kernel tier gained per-value meaning in #4625 and #4626; the 20 cross_runtime vocabularies did not, so 81 of 149 registered values were bare tokens whose meaning a reader had to recover from the generated rule table. Per-value coverage moves from 68/149 to 149/149. A note says which condition produces the value: what has to be true at runtime for the code to choose it. Not a restatement of the identifier, and not only the disposition that follows -- that was the failure mode of the three sets of notes M0 started with. Two values could not be established and say so rather than guess. settlement_failure_kind.cancelled is declared in both owners and admitted by the decoders but selected by no branch under loopx/, exercised only by tests that fabricate it, with no compatibility_only declaration marking it reserved. todo_decision_scope_kind.other is an accepted member with no producer and no fallback -- a kind outside the set is rejected, not coerced to it -- and no documented rule for when an author picks it. Both name the missing evidence. The notes also state a boundary the registry previously left implicit: several cross_runtime values are author-declared and only membership-validated, never selected by a branch. That is the whole of goal_amendment_class, todo_decision_scope_kind, todo_decision_scope_granularity, and delivery_outcome.primary_goal_outcome. Their notes name who declares the value, the criterion, where that criterion is normative, and that no code branch selects it. The ratchet is a new file rather than an addition to the tail of test_semantic_vocabulary_drift.py, where the kernel ratchet lives and where open branches already collide. It derives its population from the registry, so a new cross_runtime vocabulary is covered without editing the test, and it fails a missing note, an empty note, a note carrying no words beyond its own identifier, and an unresolved marker that does not name its missing evidence. The unresolved count is pinned at 2. The tracking issue called this remainder 117 values; that count predates #4626 and included effective_action (32) and lease_action (4), both kernel-tier and already documented. The outstanding work was 81. Refs #4447 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: song <22676124+songoow@users.noreply.github.com>
Refs #4447 — Track A / documentation half of the registry contract.
Why
The registry already settles who owns a vocabulary and which values are legal. It does not say what an individual value means. Measured on the baseline: 13 of 149 registered values carry a
value_notesentry, and all three existing sets record disposition (legacy class, compatibility-only) rather than meaning. To learn when the controller emitsstoprather thanrepair, a reader has to reconstruct it from the generated rule table.That is the gap this PR closes for the four canonical Turn kernel vocabularies — the ones that decide what a Turn did and what the outer loop does next.
What
All 32 values of
turn_result_kind,turn_route,loop_dispositionandagent_scope_frontier_actionare now documented against their deciding code, not against their names:turn_result_kind,loop_dispositionTURN_CONTROLLER_CONTRACT—stoprecords that it is reachable only from aniteration_failedreceipt;repairlists the five failed receipt classes that collapse into itturn_routedriver.py::_typed_route— including thatblockedprojects towaitrather than failing, and thatcontract_erroris the one route with no disposition because the controller rejects instead of dispositioningagent_scope_frontier_actionagent_scope.py— separating three lookalike verdicts by what is actually missing:agent_scope_wait(blocking handoff claimed elsewhere),reassignment_required(visible work claimed elsewhere),agent_scope_exhausted(no candidate at all)A test pins the coverage: a new value in one of these four sets fails the PR path until the same diff says what it means. A new value here is a new control-flow case, so requiring the note in that diff keeps the case reviewable. The test rejects empty or whitespace-only notes and was mutation-checked by dropping one entry (
turn_route.blocked→ the test fails naming it).Boundary
value_notesis registry documentation; the drift smoke already validates that it names only registered values.canonicalkernel vocabularies.effective_action(32 values,merge_candidate) is deliberately left for a follow-up: it is the overloaded slot M1 is due to split, so its per-value notes have to record which slot each value belongs to. That is review work of its own, not a rider on this diff.lease_actionalready documents all four of its values as compatibility-only, so it needs nothing.Validation
Smoke output unchanged from the baseline on every counted surface, including
conflicting_values_semantic=0/0andunresolved_producer_sites=41.Note on the environment: without
node_modulespresent the TypeScript production parser raises, and 16 tests in that file fail on a cleanorigin/maintree as well (16 failed / 42 passed). With the Node dependencies linked in, the same tree is fully green, and this branch adds 4 passing tests and no failures.🤖 Generated with Claude Code