Skip to content

chore(semantics): lock two inventory ratchets and disclose budget slack - #4629

Merged
huangruiteng merged 5 commits into
loopx-project:mainfrom
songoow:codex/lock-inventory-ratchets
Sep 17, 2026
Merged

huangruiteng merged 5 commits into
loopx-project:mainfrom
songoow:codex/lock-inventory-ratchets

Conversation

@songoow

@songoow songoow commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

What

  1. Lock two inventory ratchets to their measured values: conflicting_values 18→16, conflicting_definitions 59→55. Both sides of each budget (registry and BUDGET_ANCHOR) move in this one diff, so the equality anchor holds. These are the values feat(semantics): generate shared Turn contracts and controller rules (M2) #4499 earned; until now they sat 4 units below their budgets with nothing checking the gap.

  2. Disclose budget slack in the inventory report line: slack=key=N whenever budget > measured. The guard only fails on overflow (measured > budget), so a merge that reverts a tightened budget used to pass silently; now the reopened headroom is visible in the smoke output.

  3. multi_value_twins stays 19 here — the multi-value single-source batch (refactor(semantics): single-source five duplicated multi-value vocabularies #4617) owns that counter and locks it to 13, which is tighter than this branch could.

Why the slack line exists (merge-trap reproduction)

Merging two budget-tightening branches straddles the budget hunk:

<<<<<<< ours
"same_runtime_forks": 25,        # stale (theirs improves it to 20)
"conflicting_values": 16,        # this branch's lock
=======
"same_runtime_forks": 20,        # theirs' improvement
"conflicting_values": 18,        # stale (reverts this lock)
>>>>>>> theirs

A whole-hunk resolution in either direction silently reverts one side's lock and the smoke stays green — both files revert together, so the equality anchor stays satisfied, and 16 > 18 is false so the overflow check never fires. Reproduced live: take-theirs on this branch merged with #4606 yields ok with conflicting_values=16/18. With the slack line, that same resolution prints slack=conflicting_values=2,conflicting_definitions=4 in the diff of any post-merge run.

Merge-order note for reviewers

This branch textually conflicts with #4617 / #4606 / #4608 on the budget lines. The resolution is line-by-line (take theirs' fork budgets, keep this branch's 16/55), never a whole-hunk take. The slack line added here is what makes a wrong resolution visible.

Validation

  • python3.11 examples/semantic-vocabulary-drift-smoke.py — green: conflicting_values=16/16 conflicting_definitions=55/55, slack line shows only the 1 unit intentionally left for refactor(semantics): single-source five duplicated multi-value vocabularies #4617 (slack=multi_value_twins=1)
  • python3.11 -m pytest -q tests/architecture/test_semantic_vocabulary_drift.py — 59 passed (58 pre-existing + the new slack-disclosure test)
  • New test test_inventory_report_discloses_budget_slack covers both directions: pinned budgets disclose no slack; a two-file budget revert (the merge-trap shape) must disclose slack=conflicting_values=2

conflicting_values 18->16 and conflicting_definitions 59->55 pin the
measured values loopx-project#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 (loopx-project#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>
…ratchets

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

songoow commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

本 PR 在 #4447 计划中的位置

issue #4447 现在有一节统一协调(中英双语),把这 13 个在开 PR 作为一个计划列出:各自修什么、为何必要、以及实测出的合并顺序。

冲突实测:对全部 78 对做了试合并,9 对冲突,分四簇,每一处都是文本相邻,没有一处是语义分歧。

冲突簇 涉及 PR 后合并者的解法
棘轮锚点 #4606、#4608、#4617、#4629 四者全部合并后的终态已实测:same_runtime_forks 18、same_runtime_fork_definitions 41、same_runtime_forks_semantic 11、conflicting_values 16、conflicting_definitions 55、multi_value_twins 13。registry 与 BUDGET_ANCHOR 两处都要带同一组值(该检查是相等而非 <=)
漂移测试文件 #4626、#4629、#4631 三者都在文件末尾追加,全部保留即可
RFC 双镜像 #4614、#4627、#4631 附录 B 决策行,按日期先后保留两条
清单与生成器 #4614、#4630 加法式:owner 对过滤与改名不变性证据同时保留

建议顺序(代价从低到高):#4628 → #4625、#4626 → #4627 → #4619、#4621 → #4630 → #4614 → #4631 → #4629 → #4617 → #4606 → #4608。四个棘轮 PR 放最后,因为每落地一个,下一个的数字就从估算变成确定值。

全部 13 个 PR 现已同步到 main、零失败检查。

…ratchets

loopx-project#4606 and loopx-project#4617 merged while this branch was open, and both sides tightened
ratchets in the same two anchored blocks. Resolved by taking the tighter side of
each, the only resolution budgets_only_decrease allows:

- same_runtime_forks 25 -> 20 and same_runtime_fork_definitions 58 -> 47 from
  main (loopx-project#4606 single-sourced six status constants).
- conflicting_values 18 -> 16 and conflicting_definitions 59 -> 55 from this
  branch, which is what it exists to lock.

multi_value_twins takes main's 13 from loopx-project#4617, which is tighter than the 19 this
branch left in place for exactly that reason.

Registry and BUDGET_ANCHOR carry the same values, as the equality check
requires. Measured after the merge: every counter this branch owns is at
budget, conflicting_values=16/16 and conflicting_definitions=55/55.

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

songoow commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

exact-head 复核(8bbbdd055)— 同步 main 并解冲突

#4606 与 #4617 在本分支开会期间合并了,两侧在同一批锚点上各自收紧,git 无法替我们选。按 budgets_only_decrease 取更紧一侧:

锚点 本分支 main 来源 解析
same_runtime_forks 25 20(#4606 单源化六个 status 常量) 取 20
same_runtime_fork_definitions 58 47(同上) 取 47
conflicting_values 16 18 取 16(本分支存在的意义)
conflicting_definitions 55 59 取 55
multi_value_twins 19 13(#4617) 取 13

最后一行值得说明:本分支当初故意把 multi_value_twins 留在 19,并在 PR 描述里写明"归 #4617 所有,它能降到 13,比本分支能做的更紧"。现在 #4617 已合并,于是按当时的约定取它的 13。

registry 与 BUDGET_ANCHOR 同值(该检查是相等)。合并后实测:本分支负责的两个计数器 conflicting_values=16/16、conflicting_definitions=55/55,均恰好压线。合并提交带 DCO 签名。

…ratchets

loopx-project#4608 merged after the previous sync, lowering the same anchored block again.
Resolved by taking the tighter side of each, as budgets_only_decrease requires:

- same_runtime_forks 20 -> 18 and same_runtime_fork_definitions 47 -> 41 from
  main.
- conflicting_values 16 and conflicting_definitions 55 from this branch, which
  is what it exists to lock.

Measured after the merge: conflicting_values=16/16 and
conflicting_definitions=55/55, both exactly at budget.

Signed-off-by: song <22676124+songoow@users.noreply.github.com>
huangruiteng
huangruiteng previously approved these changes Sep 17, 2026

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

Reviewed exact head: 0a858ba3c59e56cc59c44ef0e1a0c5c620193daf (codex/lock-inventory-ratchets).

动机

#4447 里两个 inventory ratchet 落在测量值以下四格:conflicting_values 实测 16 却挂着 18 的预算、conflicting_definitions 实测 55 挂着 59。守卫只在超支时失败,所以把预算改回去的那种合并是「静默通过」——而 registry 与 BUDGET_ANCHOR 会一起回退,等值 anchor 依然满足。改动后两个预算锁到实测值,并且报告行会把任何残留余量作为 slack= 打出来。

改动思路

入口是 examples/semantic-vocabulary-drift-smoke.py::check_inventory:预算改动同时落在注册表与代码 anchor 两侧(这是仓库等值 anchor 的既有要求),报告侧只是把「预算 − 实测」这个派生量加进同一行。没有新机制、没有新命令,唯一新增的语义是「余量要被看见,但不构成失败」——这是对的,因为 multi_value_twins 的那 1 格余量属于另一个批次(#4617),把 slack 断言成 0 反而会误伤。

具体改动

3 个文件、+44/-5:BUDGET_ANCHOR 与注册表 inventory_ratchets 各两处(18→16、59→55),check_inventory 约 10 行 slack 汇总,测试约 31 行。

我在这个 head 上跑了 smoke → ok,且 conflicting_values=16/16 conflicting_definitions=55/55(其余计数不变);pytest tests/architecture/test_semantic_vocabulary_drift.py -q → 62 passed。我另外手工复现了 slack 路径:把一个注册表副本的 conflicting_values 抬到 18、同时把 anchor 改成同值(即「两文件一起回退」的合并陷阱形状),报告行确实输出 slack=conflicting_values=2;而在未改动的 head 上输出里没有 slack= 片段。顺带确认新测试的 monkeypatch.setitem(smoke["BUDGET_ANCHOR"], ...) 是有效的(runpy 复制的是 globals 映射,但 dict 对象是共享的,所以 setitem 是活的)。

关键代码讲解

  • BUDGET_ANCHOR(smoke 147):两个值锁到 16/55;注册表同步,所以等值 anchor 仍成立(smoke 输出即证明)。
  • check_inventory 的 slack 段(695 附近):遍历 RATCHET_KEYS,budget > measured 时累加 f"{key}={ratchets[key] - actual}" 并在非空时把 slack=... 追加到报告行;注释明确「守卫只在超支时失败,余量不失败」,所以这是披露而非门禁。
  • test_inventory_report_discloses_budget_slack:钉住两个方向——当前锁定值不产生 slack 片段;两文件同时回退必须产生 slack=conflicting_values=2。

对主干的风险

最强回归不是崩溃,而是「锁被合并流程悄悄撤掉」。改动本身把这类回退从「静默」变成「下一轮输出里可见」,方向正确;但它仍是建议性的:忽略这一行的人不会比以前更差,所以真正阻止回退的还是「冲突必须逐行解,不能整块取一侧」这条 reviewer 纪律。

一条 P3(F1,非阻塞):这次 diff 把 loopx/semantics/vocabulary_v0.json 的行尾换行删掉了(diff 里是 -} / +} 加 \ No newline at end of file)。我核过字节:本 head 的文件不以换行结尾,而 merge-base 的同一文件以换行结尾(脚本自身也仍有)。对 json.loads 无影响,但会在此后每次改动这个文件的 diff 里留下噪音,也会被会在末尾补换行的编辑器/格式化工具反复改写。最小修复:补回一个字节。

我的整体评价

结论 APPROVE。改动小但切中真实失效模式:预算未锁 + 等值 anchor 一起回退 = 静默重开债务;现在锁上了,并且把余量披露出来(multi_value_twins 留给 #4617 的那 1 格也因此可见)。我独立复现了 16/16、55/55 与 slack 路径,跑了 62 个测试,并确认没有其他地方仍引用旧预算值。回退成本是一个 commit。

唯一 P3 是文件末尾换行被删,非阻塞。

English verdict: APPROVE - exact head 0a858ba; the two ratchets are locked to their measured values in both copies (smoke prints conflicting_values=16/16 and conflicting_definitions=55/55), the new slack disclosure reproduces when a budget and its anchor are reverted together (I saw slack=conflicting_values=2) and does not appear on the shipped head, no remaining doc cites the old budgets, and 62 architecture tests pass. One non-blocking P3: the diff removes the trailing newline from loopx/semantics/vocabulary_v0.json, which the merge-base copy has.

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

songoow commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

Head moved: merged main, so the existing approval no longer covers this head (6f06f77bd).

Same append-at-end cluster as #4631: #4625 and #4626 landed their blocks at the end of tests/architecture/test_semantic_vocabulary_drift.py while this branch appends the ratchet-lock fixtures. Both blocks kept, theirs first.

The point worth re-checking is that the locked values still hold on the integrated tree: conflicting_values 16/16 and conflicting_definitions 55/55. No anchor moved in this merge, so the equality this PR introduces is still measured, not inherited.

Revalidated: semantic-vocabulary-drift-smoke ok, docs-governance-smoke ok, pytest tests/architecture/ 300 passed.

Per the exact-head rule in AGENTS.md, this needs a re-confirmation on 6f06f77bd.

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

Reviewed exact head: 6f06f77bd62e031f268a2e9838a0d3fa365bb47f (codex/lock-inventory-ratchets, re-review after the previous head 0a858ba3c was superseded by a main merge).

动机

未变:conflicting_values 与 conflicting_definitions 两个 inventory ratchet 挂在比实测值高四格的预算上(实测 16 / 55,预算却写 18 / 59),而守卫只在超支时失败。所以「整块取一侧」的冲突解法会把收紧过的锁悄悄撤掉——registry 与 BUDGET_ANCHOR 一起回退,等值 anchor 仍然成立,16 > 18 也不为真,于是 smoke 依旧绿。这个动机在本次 head 上仍然成立。

改动思路

两件事一起做:把两个预算钉到实测值(registry 与 BUDGET_ANCHOR 同步改,等值 anchor 不破),并在既有的报告行构造里加一段条件性 slack 披露——只要某个预算高于实测值就打印 slack=<key>=<delta>。这样「回退锁」这种解不再是静默的,而是在下一次运行的同一行里露出来,不需要新增模块或二级报告。

具体改动

3 个文件、+44/-5(相对当前 main;本次 head 相对上一次 review 只多了 main 合并,内容增量未变):

  • loopx/semantics/vocabulary_v0.json:conflicting_values 18→16、conflicting_definitions 59→55。
  • examples/semantic-vocabulary-drift-smoke.py:BUDGET_ANCHOR 同步为 16/55;check_inventory 的报告行在预算大于实测值时追加 slack=...。
  • tests/architecture/test_semantic_vocabulary_drift.py:新增 test_inventory_report_discloses_budget_slack,双向断言——钉住的预算不产生 slack;把预算回调 2(merger-trap 形状)必须披露 slack=conflicting_values=2。

我复核的关键点(都在这个 head 上自己跑过):

  • python examples/semantic-vocabulary-drift-smoke.py → exit 0,报告行 conflicting_values=16/16 conflicting_definitions=55/55,本 head 没有 slack 段。
  • pytest -q tests/architecture/test_semantic_vocabulary_drift.py → 68 passed。
  • 反向验证(PR 的核心主张):我在 scratch 副本里把 registry 与 BUDGET_ANCHOR 的 conflicting_values 一起改回 18,重跑 smoke 仍是绿的(conflicting_values=16/18),但报告行多出 slack=conflicting_values=2。这正面复现了「整块取一侧 → 静默回退」的解法被转成可见信号。探针已还原,git status --short 干净。
  • 与当前 main 逐键比对:conflicting_values main 18 / head 16,conflicting_definitions main 59 / head 55;multi_value_twins 与 same_runtime_forks 两边一致(13 / 18),说明这个 head 只动了它声明要动的两个键。

遗留问题(非阻塞,P3)

两条,都是文案/字节层面的:

  1. loopx/semantics/vocabulary_v0.json 仍缺行尾换行(我上一轮就提过,本次 head 未修)。实测本 head 文件末字节是 0x7d(}),而 main 的同文件末字节是 0x0a,所以 diff 里仍有 \ No newline at end of file。对 json.loads 无影响,但会让此后每次改这个文件的 diff 都带噪音,也会被补行尾的编辑器反复改写。最小修复:补一个字节。
  2. PR body 里的 slack 例子在当前 head 已不复现。body 说 slack 行只显示「为后续 PR 留的 1 个单位」(slack=multi_value_twins=1);本 head 的 smoke 完全没有 slack 段——main 后来把那个单位也锁上了(multi_value_twins 在 main 与 head 都是 13/13)。披露路径本身没坏:两文件回调后仍能打印 slack=conflicting_values=2。这只是引文过期,不是行为缺陷。

对主干的风险

最强回归不是崩溃,而是「锁被合并流程悄悄撤掉」。这次改动把回退从「静默」变成「下一轮可见」,方向正确;但它依旧只是建议性的:整块取一侧的人仍会合并成功,只是输出里多一行。真正硬性的护栏还是「冲突必须逐行解」这条 reviewer 纪律。除此之外:不涉及运行时路径、quota 规则或持久化状态;读侧也没有新增 schema 或键;回退成本是一个 commit。我这次没有只继承上一轮的结论——head 换了(main 合并),所以我重新对当前 main 比对了内容增量、重跑了 smoke 与 68 个测试,并亲手复现了 merger-trap 的反例。

我的整体评价

结论 APPROVE。改动很小但落在对的地方:预算锁到实测值,披露放在已经在做同一比较的那个函数里,双向测试把 merge-trap 形状固定下来。我在新 head 上独立复现了「钉住即无 slack」和「两文件回调即 slack=conflicting_values=2 且仍绿」,并确认相对当前 main 的内容增量与 PR 声明一致。两条 P3 都不阻塞:一个缺行尾换行,一处 body 引文过期。

English verdict: APPROVE - exact head 6f06f77 (re-review after a main merge; content delta versus current main is the same 3 files, +44/-5). The two ratchets are locked to their measured values in both the registry and BUDGET_ANCHOR (16/55), the drift smoke exits 0 and prints conflicting_values=16/16 conflicting_definitions=55/55 with no slack segment at this head, 68 architecture tests pass, and I reproduced the merge-trap case first-hand: reverting both copies to 18 keeps the run green while the new disclosure prints slack=conflicting_values=2. Two non-blocking P3 findings: loopx/semantics/vocabulary_v0.json still lacks its trailing newline, and the body's slack=multi_value_twins=1 example no longer reproduces because main has since closed that last unit.

@huangruiteng
huangruiteng merged commit 4b6bbc9 into loopx-project:main Sep 17, 2026
25 checks passed
songoow added a commit to songoow/loopx that referenced this pull request Sep 17, 2026
Five more tracker PRs landed, three of them in files this branch edits.

- loopx-project#4629 and loopx-project#4631 both append to `test_semantic_vocabulary_drift.py`; all
  fifteen of their blocks are kept beside this branch's three.
- Appendix A gains loopx-project#4631's invariant-domain entry in the same 2026-09-17
  date as this branch's B3 entry, so newest-first keeps both.
- Appendix C is a real collision, not textual adjacency: loopx-project#4631 took E21,
  E22 and E23. This branch's evidence row is renumbered E24 and its
  baseline SHA refreshed to `001c6daf2`.

Remeasured on the integrated tree: every role count is unchanged
(`goal_boundary` surface 15 of 30 carriers, `protocol_action_packet` one
reader and four writers), `dynamic_mapping_key_sites` stays 1712, and all
six migration-surface anchors hold.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: song <22676124+songoow@users.noreply.github.com>
@songoow
songoow deleted the codex/lock-inventory-ratchets branch September 28, 2026 03:00
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