refactor(status): single-source six duplicated status vocabulary constants - #4606
Conversation
…tants Track A for loopx-project#4447: the same six names were defined twice with identical values, once in the control_plane projection that owns each one and again in loopx/status.py. Nothing was contradictory yet, but a value change had two places to land and only one would be found by a reader. Export each name from its owner through the facade, following the SOURCE_REGISTRY_SHADOW_FINDINGS precedent already in this module, and register the five import-only ones in _PUBLIC_COMPAT_REEXPORTS. The sixth, MONITOR_SIGNAL_WAITING_ON, keeps a real internal use and needs no entry. The public-facade evidence audit missed live consumers that reach the facade by attribute access (from loopx import status as status_module), which made a real consumer look like a stale entry. Teach it that import form instead of weakening the audit. Semantic same-runtime forks 18 -> 13; budgets and the smoke anchor move together. Refs loopx-project#4447 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: song <song@users.noreply.github.com>
动机Track A(减少已测量的语义债)第二项。实测澄清了一个此前的隐含预期:18 个语义"分叉"里 0 个真的分叉——每一组的每条定义值都完全相同( 本 PR 处理其中最集中的一组。六个名字同时定义在 代价不是当前行为错——今天两边一致——而是未来:改一处值不会被另一处发现,读者也无法从代码判断哪个是权威。RFC 第 5 节的 I14 与 multi-value 冲突规则针对的正是"同一概念多种拼写"。 更小的替代修法不足:只删 改动思路
owner 归属按投影层是否消费该值确定: 一个区分: 具体改动4 文件、+55/−25:
关键代码讲解
我修的是审计的识别能力,而不是删条目。这一点值得评审注意:为通过审计而删掉有真实消费者的白名单条目,会让审计从此只反映"愿意删什么",而不是"谁在用"。
对主干的风险无阻塞项。
负向走查(触发状态 → 观察结果):在 变更体量相称:净减 6 处重复定义,机制成本为零(复用既有门面模式与既有审计)。 无 CI 归因问题:本 PR 的检查刚触发; English verdict: APPROVE — exact head Refs #4447 🤖 Generated with Claude Code |
…ource-status-vocabulary Signed-off-by: song <22676124+songoow@users.noreply.github.com>
exact-head 复核(
|
…ource-status-vocabulary Both sides tightened the semantic ratchets in the same two anchored places, so git could not pick one. Resolved by taking the tighter side of each, which is the only resolution the registry policy allows (budgets_only_decrease): - same_runtime_forks_semantic 18 -> 13, from this branch: single-sourcing six duplicated status constants removes five semantic forks. - conflicting_values_semantic 2 -> 0, from loopx-project#4603 on main, which lowered the budget to its measured value. Registry and BUDGET_ANCHOR carry the same pair, as the equality check requires. Measured after the merge: same_runtime_forks_semantic=13/13 and conflicting_values_semantic=0/0, both exactly at budget. Signed-off-by: song <22676124+songoow@users.noreply.github.com>
…ource-todo-task-class Both sides tightened the semantic ratchets in the same two anchored places. Resolved by taking the tighter side of each, the only resolution budgets_only_decrease allows: - same_runtime_forks_semantic 18 -> 16, from this branch: importing the Todo task-class vocabulary from its owner removes two semantic forks. - conflicting_values_semantic 2 -> 0, from loopx-project#4603 on main. Registry and BUDGET_ANCHOR carry the same pair. Measured after the merge: same_runtime_forks_semantic=16/16, conflicting_values_semantic=0/0. Note for merge order: loopx-project#4606 resolves the same anchor to 13. Whichever of the two lands second has to lower the anchor again in that merge, because each branch only counts the forks it removes. Signed-off-by: song <22676124+songoow@users.noreply.github.com>
exact-head 复核(
|
| 锚点 | 本分支 | main | 解析 |
|---|---|---|---|
same_runtime_forks_semantic |
18 → 13(单源化六个 status 常量消掉五个语义分叉) | 18 | 取 13 |
conflicting_values_semantic |
2 | 2 → 0(#4603 降到实测值) | 取 0 |
两边都取更紧的一侧——这是注册表 budgets_only_decrease 策略下唯一允许的解法。registry 与 BUDGET_ANCHOR 同步带同一对值(该检查是相等而非 <=)。
合并顺序耦合(需要注意):#4608 把同一个锚点解到 16,因为每个分支只统计自己消掉的分叉。两者谁后合并,都必须在那次合并里再降一次锚点,否则会留下一个比实测松的预算。
验证(当前 exact head):semantic-vocabulary-drift-smoke: ok,same_runtime_forks_semantic=13/13、conflicting_values_semantic=0/0,均恰好压在预算上。合并提交带 DCO 签名(web "Update branch" 不签名,故本地合并)。
合并顺序:与 4608 共享三个棘轮锚点我对全部 8 个在开 PR 做了两两试合并,唯一会撞的就是 #4606 × #4608(其余 27 对全部干净)。两者改的是同一组锚点的不同数字,因为每个分支只统计自己消掉的分叉:
后合并的那个需要在它的合并提交里把这三个数改成最右列,registry 与 这不是估算:我在本地把两个 head 都合到 main 上,解成上表数字后跑 smoke,得到 |
…ource-status-vocabulary 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 a8442a7be4f0c9c987610ae1bd507fe5210e9259
动机
Track A(#4447):六个名字在控制面投影和 loopx/status.py 里各定义一次,值虽然一致,但"改一个值要改两处、读者只会找到其中一处"是结构性隐患。作者的判断准确:这类重复不会因为读某一个模块而暴露,只有跨模块比较才能发现。
改动思路
不新增机制,复用本模块既有的 SOURCE_REGISTRY_SHADOW_FINDINGS 先例:每个名字从拥有它的投影导入,门面继续对外暴露;把五个纯再导出的名字登记进 _PUBLIC_COMPAT_REEXPORTS,第六个 MONITOR_SIGNAL_WAITING_ON 因为 status.py:1101 有真实内部使用而不需要登记。同时把 drift 预算锚点从 25/58/18 下调到 20/47/13——这是收紧方向(fork 变少),并要求 smoke 与 registry 同步。
顺带修了公共门面证据审计的一个盲点:from loopx import status as status_module 这种别名访问是真实消费者,但旧审计只统计 from-import,于是活消费者会被当成 stale 条目。作者选择教会审计识别这种导入形式,而不是删掉再导出——方向正确。
具体改动
loopx/status.py(+39/-25):删除六个字面量定义,改为从active_state_projection(STATE_EVENT_LOG_BASENAME)、contract_projection(STATUS_CONTRACT_RELOAD_HINT)、goal_attention_projection(PLANNED_CONTROLLER_OPT_IN_RECOMMENDED_ACTION)、monitor_display_projection(MONITOR_DISPLAY_STOP_CONDITION、MONITOR_DISPLAY_FALLBACK_ACTION、MONITOR_SIGNAL_WAITING_ON)导入;五个名字在_PUBLIC_COMPAT_REEXPORTS登记归属模块。tests/architecture/test_control_plane_import_boundaries.py(+29/-2):证据收集新增"门面别名 → 属性访问"路径;仅当别名绑定自公共门面时才计入,相对导入照旧跳过。examples/semantic-vocabulary-drift-smoke.py、loopx/semantics/vocabulary_v0.json(各 6 行):锚点与inventory_ratchets同步下调。
验证(都在本 head):六个常量取值为 'events.jsonl'、'monitor_signal'、monitor stop/fallback 原文、'scripts/macos-dashboard-launchagent.sh restart'、中文 operator 动作——与旧字面量逐字一致;dir(loopx.status) 242 个名字,前后零增零删;tests/architecture/test_control_plane_import_boundaries.py、test_semantic_vocabulary_drift.py、test_semantic_production.py → 106 passed;drift smoke ok 且实测 same_runtime_forks=20/20、same_runtime_fork_definitions=47/47、same_runtime_forks_semantic=13/13。
对主干的风险
行为面没有变化,我用两条独立证据锁住:值逐字一致(直接 import 打印)与导出名字集合完全一致(242 vs 242)。门槛类风险也已核:降锚点只在实测值确实下降时成立(实测正好等于新锚点),审计扩展只接受"别名绑定自公共门面"的属性访问,因此计入的证据仍是同一个导出的真实消费者,不会把 stale 条目洗成有效;它补的是漏报,不是放宽。
唯一可讨论的是审计证据来源变宽这一点:如果将来某个再导出名字只被 alias.attr 形式访问,它仍会被视为有消费者——但那确实是门面消费者,因此这是修正而非削弱。整体风险低、可逆(撤回文本即回到原状)。
我的整体评价
APPROVE。 单源化方向正确、范围克制,既有门面机制被复用而非另起一套;值一致性与导出面一致性都经我独立验证,fork 预算下降 5 个名字/11 个定义/5 个语义 fork,属于"让下一步更容易"的改动。合并权仍在维护者。
English verdict: APPROVE - reviewed exact head a8442a7. Six status vocabulary names now have one definition each in the projections that own them, with five registered in _PUBLIC_COMPAT_REEXPORTS and MONITOR_SIGNAL_WAITING_ON kept for its real internal use. I verified parity two ways: each of the six values prints identically to the deleted literal, and dir(loopx.status) is the same 242 names with zero added and zero removed. The public-facade evidence audit now also counts attribute access through an alias bound from a public facade, which fixes a false stale-entry report without weakening the guard, since that access reaches the same export. Validation at this head: 106 passed across test_control_plane_import_boundaries.py, test_semantic_vocabulary_drift.py and test_semantic_production.py, and the drift smoke reports same_runtime_forks=20/20, same_runtime_fork_definitions=47/47 and same_runtime_forks_semantic=13/13, exactly the lowered anchors (25/58/18 before).
…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>
动机
Track A(减少已测量的语义债)第二项。实测发现:18 个语义"分叉"里 0 个真的分叉——每一组的每条定义值都完全相同(
distinct=1)。所以这 18 个不是"同名不同值",而是同值多份独立定义:有人复制了常量而不是导入它。本 PR 处理其中最集中的一组:六个名字同时定义在
loopx/status.py与拥有它的control_plane投影模块里,值逐字相同。代价不是当前行为错,而是未来:改一处值不会被另一处发现,读者也无法判断哪个是权威。RFC 第 5 节的 I14 与 multi-value 冲突规则针对的正是这种"同一概念多种拼写"。
改动思路
loopx/status.py是聚合门面,本模块已有一个先例:SOURCE_REGISTRY_SHADOW_FINDINGS由control_plane/status/registry_health_projection.py拥有、由这里再导出(status.py:12-17,同样标注Refs #4447)。本 PR 沿用同一模式,而不是发明新机制。owner 归属按投影层是否消费该值确定,与
monitor_display_projection/contract_projection/active_state_projection/goal_attention_projection各自的既有用法一致。MONITOR_SIGNAL_WAITING_ON也被status.py:1102内部使用,所以它是普通导入,不是 import-only 再导出,不进兼容白名单。具体改动
4 文件、+55/−25:
loopx/status.py:6 个名字改为从 owner 导入;删除本地重复定义;5 个 import-only 的登记进_PUBLIC_COMPAT_REEXPORTSloopx/semantics/vocabulary_v0.json+examples/semantic-vocabulary-drift-smoke.py:预算与锚点同步(3 个数字)tests/architecture/test_control_plane_import_boundaries.py:证据函数修正(见下)关键代码讲解
loopx/status.py的再导出块。值不再在门面里出现,from ... import使门面属性与 owner 是同一个对象(已用is断言验证),所以任何调用方拿到的东西没有变化。_public_contract_evidence。仓库原有的公共门面审计要求每个 import-only 再导出都有仓库消费者或文档证据。它原先只识别from loopx.status import X,而真实消费者用的是from loopx import status as status_module+ 属性访问(examples/control_plane/status-contract-readmodel-smoke.py:15,23等)。结果是真实存在的消费者被报成 stale entry。本 PR 让证据函数识别该导入形态,而不是为绕过审计而删条目——后一种做法会让审计失去意义。对主干的风险
无阻塞项。
验证矩阵:
examples/semantic-vocabulary-drift-smoke.pyok;same_runtime_forks=20/20、same_runtime_forks_semantic=13/13tests/architecture/test_control_plane_import_boundaries.pytests/architecture/test_semantic_vocabulary_drift.pyexamples/project/project-asset-next-eye-smoke.pyis断言为 True负向走查:若未来有人再在
status.py复制一份这些常量,_public_import_only_bindings会把它视为已加载名字、从 import-only 集合消失,于是白名单与集合不再相等,test_public_facade_import_only_reexports_match_the_audited_allowlist失败。也就是说这条路径是被钉住的,不是靠人来记得。dashboard-acceptance的失败与 main 在e66615d33上同一条(Chat runtime picker ignored the declared steward executor: Chat Codex),属既有基线问题,与本 diff 无关。English verdict: APPROVE — exact head
7f69d2d30; six names carried identical values in two places, and the fix follows the module's existing single-source precedent rather than a new mechanism. The facade audit's evidence function was blind tofrom loopx import status as status_module, so a live consumer read as stale; teaching it that form fixes the audit instead of removing entries. Verified: drift smoke ok with semantic forks 18→13, import boundaries 15/15, drift tests 8/8, seven consuming smokes pass, re-exported names are the owner's objects.Refs #4447
🤖 Generated with Claude Code