Skip to content

refactor(status): single-source six duplicated status vocabulary constants - #4606

Merged
huangruiteng merged 4 commits into
loopx-project:mainfrom
songoow:feat/track-a-single-source-status-vocabulary
Sep 17, 2026
Merged

huangruiteng merged 4 commits into
loopx-project:mainfrom
songoow:feat/track-a-single-source-status-vocabulary

Conversation

@songoow

@songoow songoow commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

动机

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_REEXPORTS
  • loopx/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.py ok;same_runtime_forks=20/20、same_runtime_forks_semantic=13/13
tests/architecture/test_control_plane_import_boundaries.py 15/15
tests/architecture/test_semantic_vocabulary_drift.py 8/8
6 个 status/attention/monitor smoke 6/6 PASS
examples/project/project-asset-next-eye-smoke.py PASS
再导出与 owner 同对象 is 断言为 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 to from 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

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

songoow commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

动机

Track A(减少已测量的语义债)第二项。实测澄清了一个此前的隐含预期:18 个语义"分叉"里 0 个真的分叉——每一组的每条定义值都完全相同(distinct=1)。所以它们不是"同名不同值",而是同值多份独立定义:有人复制了常量而不是导入它。

本 PR 处理其中最集中的一组。六个名字同时定义在 loopx/status.py(240-296 行区间)与拥有它的 control_plane 投影模块里,值逐字相同。

代价不是当前行为错——今天两边一致——而是未来:改一处值不会被另一处发现,读者也无法从代码判断哪个是权威。RFC 第 5 节的 I14 与 multi-value 冲突规则针对的正是"同一概念多种拼写"。

更小的替代修法不足:只删 status.py 的定义会破坏外部调用方(7 个 smoke 通过它导入);只改注释不加约束,下一个人仍会复制。

改动思路

loopx/status.py 是聚合门面,本模块已有先例:SOURCE_REGISTRY_SHADOW_FINDINGS 由 control_plane/status/registry_health_projection.py 拥有、由这里再导出,且注释同样标注 Refs #4447。本 PR 沿用同一模式,没有发明新机制,也没有新增报告 schema 或校验分支。

owner 归属按投影层是否消费该值确定:monitor_display_projection、contract_projection、active_state_projection、goal_attention_projection 各自已在内部使用这些值(如 monitor_display_projection.py:57,65)。

一个区分:MONITOR_SIGNAL_WAITING_ON 同时被 status.py:1102 内部使用,所以它是普通导入而非 import-only 再导出,不进兼容白名单。

具体改动

4 文件、+55/−25:

  • loopx/status.py:6 个名字改为从 owner 导入、删除本地重复定义;5 个 import-only 的登记进 _PUBLIC_COMPAT_REEXPORTS
  • loopx/semantics/vocabulary_v0.json + examples/semantic-vocabulary-drift-smoke.py:3 个预算数字与锚点同步(same_runtime_forks 25→20、..._definitions 58→47、..._semantic 18→13)
  • 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,审计要求我删掉它。

我修的是审计的识别能力,而不是删条目。这一点值得评审注意:为通过审计而删掉有真实消费者的白名单条目,会让审计从此只反映"愿意删什么",而不是"谁在用"。

_public_import_only_bindings。判定"是否 import-only"靠"名字在文件内是否被 Load"。这条规则同时构成了负向守护:若未来有人在 status.py 重新复制一份这些常量,该名字重新变成 Load、从 import-only 集合消失,白名单与集合不再相等,test_public_facade_import_only_reexports_match_the_audited_allowlist 失败。

对主干的风险

无阻塞项。

检查 结果
examples/semantic-vocabulary-drift-smoke.py ok;same_runtime_forks=20/20、same_runtime_forks_semantic=13/13
tests/architecture/test_control_plane_import_boundaries.py 15/15
tests/architecture/test_semantic_vocabulary_drift.py 8/8
6 个 status/attention/monitor smoke 6/6 PASS
examples/project/project-asset-next-eye-smoke.py PASS
再导出与 owner 同对象 is 断言 True

负向走查(触发状态 → 观察结果):在 status.py 重新加入 STATE_EVENT_LOG_BASENAME = "events.jsonl",则该名字变为已加载、退出 import-only 集合,白名单多出一条,审计测试失败并列出该条目。最小修复是删除重复定义。

变更体量相称:净减 6 处重复定义,机制成本为零(复用既有门面模式与既有审计)。

无 CI 归因问题:本 PR 的检查刚触发;dashboard-acceptance 在 main e66615d33 上为同一失败(Chat runtime picker ignored the declared steward executor: Chat Codex),与语义改动无关。

English verdict: APPROVE — exact head 7f69d2d303cea3c21f48de98f115225682b9b10f; six names carried byte-identical values in two places, and the fix follows the module's existing single-source precedent rather than inventing a mechanism. The facade audit was blind to from loopx import status as status_module, so a live consumer read as a stale entry; teaching it that import form fixes the audit rather than removing evidence. Verified at head: drift smoke ok with semantic forks 18→13, import boundaries 15/15, drift tests 8/8, seven consuming smokes pass, re-exports are the owner's own objects, and reintroducing a duplicate fails the allowlist test.

Refs #4447

🤖 Generated with Claude Code

…ource-status-vocabulary

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

songoow commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

exact-head 复核(c399315ee,合并最新 main 后)

动机

六个状态词表常量在 status.py 与 control_plane/status/* 投影模块各有一份独立定义,值相同但无导入关系。风险不在今天(到处一致),而在将来:改一处不会被别处发现,而且这种复制无法从阅读任一模块察觉——没有任何检查会失败。

改动思路

按仓库已有先例处理:SOURCE_REGISTRY_SHADOW_FINDINGS 已由 control_plane 拥有、status.py 只做再导出。本 PR 对这六组套用同一模式,status.py 变为聚合 facade 的再导出方。

具体改动

4 文件。语义 fork 18 → 13(移除 5 组);--check 与预算/锚点同步。status.py 的 MONITOR_SIGNAL_WAITING_ON 因文件内有 2 处使用,不属 import-only 再导出,已从白名单排除。

对主干的风险

改公共门面触碰审计:_PUBLIC_COMPAT_REEXPORTS 要求每个再导出条目有仓库消费者或文档证据。过程中发现审计本身有盲区——证据函数只识别 from loopx.status import X,不识别 from loopx import status as status_module 后的属性访问,导致有真实消费者的条目被报成 stale。该盲区已一并修复(test_control_plane_import_boundaries.py +25/−4)。

这一点值得评审注意:为通过审计而删掉有证据的条目,会让审计从此只反映"愿意删什么",而不是"谁在用"。

我的整体评价

observable_semantics:值对象未变(六个再导出指向 owner 同一对象),决策语义零变化;语义计数按实测下降,未放宽任何预算。消费这些名字的 6 个 smoke 全部通过。

验证:drift smoke ok、docs-governance ok、消费方 smoke 6/6、loopx canary premerge ok。CI 当前 pending,fails=0。

Residual risk: 该门面审计的别名识别仍只覆盖 from loopx import X as Y 与 import loopx.X 两种形态,未穷举所有绑定形式。

Verdict: APPROVE (self-review) — do not self-merge (control-plane change, per #4586).

…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>
songoow added a commit to songoow/loopx that referenced this pull request Sep 17, 2026
…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>
@songoow

songoow commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

exact-head 复核(6fe5cba10)— 同步 main 并解冲突

之前是 CONFLICTING/DIRTY,无法合并。冲突出在两侧各自收紧同一组棘轮,git 无法替我们选:

锚点 本分支 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" 不签名,故本地合并)。

@songoow

songoow commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

合并顺序:与 4608 共享三个棘轮锚点

我对全部 8 个在开 PR 做了两两试合并,唯一会撞的就是 #4606 × #4608(其余 27 对全部干净)。两者改的是同一组锚点的不同数字,因为每个分支只统计自己消掉的分叉:

锚点 main #4606 单独 #4608 单独 两者都合并后(已实测)
same_runtime_forks 25 20 23 18
same_runtime_fork_definitions 58 47 52 41
same_runtime_forks_semantic 18 13 16 11
conflicting_values_semantic 0 0 0 0

后合并的那个需要在它的合并提交里把这三个数改成最右列,registry 与 BUDGET_ANCHOR 两处都改(该检查是相等,不是 <=)。不改的话预算会比实测松,等于白留一截余量。

这不是估算:我在本地把两个 head 都合到 main 上,解成上表数字后跑 smoke,得到 same_runtime_forks=18/18、same_runtime_fork_definitions=41/41、same_runtime_forks_semantic=11/11,三项均恰好压线——说明两者消掉的分叉互不重叠。

…ource-status-vocabulary

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、零失败检查。

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

详细评审 — 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).

@huangruiteng
huangruiteng merged commit 0a23e1c 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
…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 deleted the feat/track-a-single-source-status-vocabulary 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.

3 participants