fix(semantics): keep retired migration vocabulary out of the repository map - #4486
huangruiteng wants to merge 1 commit into
Conversation
…ry map The generated semantic inventory copied migration-only constants into the repository-wide vocabulary map. That misstated which module owns the vocabulary today and republished retired hierarchy tokens into a derived artifact outside the migration boundary, so examples/control_plane/peer-agent-hard-cut-boundary-smoke.py failed on main with LEGACY_HIERARCHY_ROLES and TODO_REMOVED_REVIEW_CONTINUATION_POLICY_VALUES. Name the retired carriers explicitly instead of matching on substrings, keep the omission visible in the rendered inventory, and pin the rule with a characterization test. Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
62be8a2 to
370ece7
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Reviewed exact head: 370ece7709739006e8ffb7f611055bab98e4beef
动机
examples/control_plane/peer-agent-hard-cut-boundary-smoke.py(及其 wrapper)在当前 main 上是红的,报的是"legacy agent hierarchy escaped migration boundary",并直接指出 inventory_v0.json 里两行:LEGACY_HIERARCHY_ROLES(loopx/control_plane/agents/legacy_migration.py)与 TODO_REMOVED_REVIEW_CONTINUATION_POLICY_VALUES(loopx/control_plane/todos/contract.py)。根因不是迁移边界被破坏,而是派生产物越界:build_inventory() 会把 loopx/** 下所有闭集常量都抄进仓库语义地图,包括那些只为"读取或拒绝"已被单向迁移淘汰的词汇而存在的常量。结果一是把 retired 的层级 token 重新发布到边界之外的产物里,二是把迁移模块写成了这些词汇的"当前所有者"。对读者来说这会把历史词汇误读成现行契约,对 CI 来说则是每个基于 main 的分支都被这条守卫挡住。
改动思路
作者没有用"名字包含 legacy/removed"这类子串规则去过滤——那会把模块里仍然有效的常量一起藏掉——而是在生成器里把retired 载体声明成精确的 (module, name) 对:RETIRED_VOCABULARY_CARRIERS。过滤发生在 build_inventory 收集完 facts、进入派生计算之前,所以排除项不会经由 multi_value_carriers 再漏回来;同时渲染出的产物里多了一节 retired_vocabulary_excluded,把"被跳过的东西"留在产物里,而不是在代码里静默消失。这个组合正好对应仓库对"状态/词汇规则要显式声明、不要用 prose 或子串启发式"的要求,也保持了失败方向是 fail-closed:将来新增一个没声明的 retired 载体,守卫会照样变红,而不是悄悄放行。
具体改动
loopx/semantics/inventory.py:新增RETIRED_VOCABULARY_CARRIERS与_is_retired_vocabulary;在build_inventory中对 enums、closed sets、literal aliases、const arrays、string constants 五类载体统一过滤;渲染结果新增retired_vocabulary_excluded。loopx/semantics/inventory_v0.json:重新生成——删除两行载体,新增排除说明,python_closed_sets从 493 变为 491。tests/architecture/test_semantic_inventory.py:新增retirement_repofixture 与test_retired_migration_vocabulary_stays_out_of_the_map,同一模块里同时放一个 live 常量(TODO_DECISION_SCOPE_KIND_VALUES),断言只有被声明的那一个被排除。
关键代码讲解
loopx/semantics/inventory.py:26 RETIRED_VOCABULARY_CARRIERS:精确元组的 frozenset,注释明确写了"用显式配对而不是名字匹配,避免把新载体意外藏起来,并且被跳过的配对会保留在渲染产物里"——这句话和实现是一致的。loopx/semantics/inventory.py:296起的五处过滤:在派生计算之前完成,因此multi_value_carriers与后续渲染都不会重新引入被排除项;新增测试正是从这个角度断言 closed sets 的最终形状。loopx/semantics/inventory.py:337 retired_vocabulary_excluded:把省略变成可见产物,评审者不必再看代码才知道地图少了什么。tests/architecture/test_semantic_inventory.py的retirement_repo用例:同一contract.py里既有 retired 又有 live 常量,断言列表里只剩 live 那个——这条负例正好排除了"按模块或按名字前缀过滤"的错误实现。
对主干的风险
我做的独立验证:python scripts/generate_semantic_inventory.py 输出 "semantic inventory up to date",且 git diff 对 inventory_v0.json 干净——也就是说提交进来的派生产物确实与生成器一致(这点很重要,因为最容易出的错是"代码改了、产物没重生成")。python examples/control_plane/peer-agent-hard-cut-boundary-smoke.py 现在 ok;pytest -q tests/architecture/test_semantic_inventory.py 13 项通过。产物只新增了一节、少了两个载体,没有运行时行为、协议字段、配额或授权面变化,回滚也只是删掉声明再重生成。仓库状态方面 GitHub 报这个分支对 main 是 DIRTY,合并前需要 rebase,这是流程事项不是代码问题。
一条非阻塞 P3:这个显式清单是每常量维护点。它的失败模式是 fail-closed(新载体被发布 → 守卫报错 → 维护者补一条并重生成),这是有意的;但目前"新增 retired 载体需要在这里加一条并重生成地图"只写在代码注释里。建议在 inventory 模块文档或语义 RFC 里补一句同样的维护规则,让下一位作者顺手就能遵守,而不是再被守卫教一次。
我的整体评价
结论是 APPROVE。这是一次范围很小但判断准确的修复:它没有去动迁移边界,也没有用子串/名字启发式去"让 CI 变绿",而是把"哪些常量是 retired 载体"变成显式、可评审、留在产物里的声明;过滤位置放在派生计算之前,避免了排除项从别的路径绕回来。最重要的一点我独立复现了——生成器重跑无 diff,说明产物与代码一致,而没有停在"测试通过"这一层。唯一值得跟进的是维护规则的可发现性(P3),以及合并前需要 rebase。没有阻塞项。
English verdict: APPROVE — exact head 370ece7709739006e8ffb7f611055bab98e4beef of #4486. The fix stops the derived semantic map from republishing retired migration vocabulary by declaring exact (module, name) carriers instead of using a name-based filter, and it keeps the omission reviewable by rendering a retired_vocabulary_excluded section. Independent validation at this head: python scripts/generate_semantic_inventory.py reports "semantic inventory up to date" with a clean git diff on inventory_v0.json (so the committed artifact really matches the generator), examples/control_plane/peer-agent-hard-cut-boundary-smoke.py prints ok, and pytest -q tests/architecture/test_semantic_inventory.py passes 13 tests including the new fixture where a live constant shares a module with a declared retired carrier. Filtering happens before the derived multi_value_carriers computation, so exclusions cannot leak back through another path, and the fail-closed direction is preserved: an undeclared retired carrier still breaks the guard instead of slipping silently into the map. One non-blocking P3: the explicit list is a per-constant maintenance point, and the rule "add one declared pair and regenerate" lives only in a code comment — mirror it in the inventory module docs or the semantic RFC. Note that GitHub reports the branch as DIRTY against main, so a rebase is required before merge. No runtime behaviour, protocol field, quota or authority surface changes.
|
Closing as superseded: on current Verified on
Why the original symptom is gone: #4494 retired the committed Why I am not re-fitting the change instead: the surviving part of this PR is a generator-level Residual, verified and left for the owner: the on-demand report still names the migration modules as carriers of the retired values ( My earlier review on |
Problem
examples/control_plane/peer-agent-hard-cut-boundary-smoke.py(and its wrapperpeer-agent-runtime-v1-smoke.py) fail on currentmain:Root cause:
build_inventory()copies every closed-set constant inloopx/.**, including constants that exist only to read or reject vocabulary retired by a one-way migration. The generated map therefore republishes retired hierarchy tokens into a derived artifact outside the migration boundary the guard protects, and it names a migration module as the current owner of a retired vocabulary.Change
(module, constant)pairs inloopx/semantics/inventory.pyinstead of matching on name substrings, so a new carrier cannot be hidden by accident.retired_vocabulary_excludedin the rendered inventory.loopx/semantics/inventory_v0.json.tests/architecture/test_semantic_inventory.py(retired vocabulary is skipped, live vocabulary in the same module is still mapped).No guard was relaxed: the peer-hierarchy boundary smoke keeps its rule and now passes because the derived artifact no longer leaks the tokens.
Validation
examples/control_plane/peer-agent-hard-cut-boundary-smoke.py— failed before,okafter.examples/control_plane/peer-agent-runtime-v1-smoke.py—ok(runs continuation state machine, task orchestration, hard-cut boundary).examples/semantic-vocabulary-drift-smoke.py—ok, all budgets reportedx/x.scripts/generate_semantic_inventory.py --check— up to date.pytest tests/architecture/test_semantic_inventory.py— 13 passed.Boundary
Public-safe only: no private artifacts, credentials, raw logs, or local paths.