Skip to content

refactor(status): single-source the source-registry shadow findings vocabulary - #4517

Merged
huangruiteng merged 1 commit into
loopx-project:mainfrom
YZJF:codex/4447-string-fork-classification
Sep 16, 2026
Merged

huangruiteng merged 1 commit into
loopx-project:mainfrom
YZJF:codex/4447-string-fork-classification

Conversation

@YZJF

@YZJF YZJF commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Refs #4447 (Track A) — checklist row: "Classify the 19 historical multi-value twins and 18 semantic string forks; retain justified independent contracts."

This is one bounded sub-item, not completion of the row and not completion of the tracker. It must not close #4447.

Classification: SOURCE_REGISTRY_SHADOW_FINDINGS is same_semantics

The name has four definitions on main (a28562e97):

Location Values Verdict
loopx/status.py:289 {source_registry_missing, stale_source_registry} active — merged
loopx/control_plane/status/registry_health_projection.py:28 same active — retained as owner
deprecate/benchmark-legacy/.../benchmark_status_runner.py:218 same outside active scope, untouched
deprecate/benchmark-legacy/.../benchmark_status_compaction.py:185 same outside active scope, untouched

Evidence that the active pair is one concept rather than two contracts:

  • Same values. Both sets are exactly {source_registry_missing, stale_source_registry}.
  • Same slot. Both are passed as the source_registry_shadow_findings keyword argument.
  • Same consumer. Both reach work_items/attention_queue.py:77, which is the only place the value is read (if finding.get("kind") in source_registry_shadow_findings and live_items).
  • Same runtime, no persisted boundary. The set is an in-process argument, not a serialized field, so there is no historical-reader compatibility argument for keeping two copies.

Per the issue's own rule, only confirmed same_semantics groups become merge work — this one qualifies, so it is merged.

Change

registry_health_projection.py keeps the only definition; loopx/status.py imports and re-exports it.

  • The dependency direction stays outer → control_plane. status.py already imports .control_plane.status.collection and .control_plane.status.runtime_summaries at module level, and registry_health_projection.py has no import back into status.py, so no cycle is introduced.
  • status.SOURCE_REGISTRY_SHADOW_FINDINGS is kept as a public name on purpose: examples/control_plane/attention-queue-readmodel-smoke.py:62,95 reads it as status_module.SOURCE_REGISTRY_SHADOW_FINDINGS. Removing the re-export would be an unforced break of an existing caller, which the row's "caller parity" requirement argues against.
  • Values are unchanged, so no behavior changes.

Verification

  • New regression tests/control_plane/test_source_registry_shadow_findings.py locks three things: the admitted values are unchanged, loopx.status exposes the same object as the projection rather than a copy, and no literal can be reintroduced into loopx.status.
  • examples/control_plane/attention-queue-readmodel-smoke.py → ok (this is the caller that reads the re-exported name).
  • ruff check clean on both changed files.
  • tests/control_plane -k "status or registry or attention or queue" — no new failures versus baseline.

Out of scope

The remaining 18 semantic forks and 19 multi-value twins in that row are not classified here; each needs its own caller check. The two deprecate/benchmark-legacy/ copies are left alone. Base a28562e97, Python 3.13.12.

…ocabulary

Refs loopx-project#4447 (Track A)

SOURCE_REGISTRY_SHADOW_FINDINGS had two active definitions with identical
values: one in loopx/status.py and one in the control_plane status
projection. Both were passed as the source_registry_shadow_findings argument
and both reached the only reader, work_items/attention_queue.py:77, so the
fork is same_semantics rather than two independent contracts.

The projection keeps the only definition and loopx/status.py imports it. The
dependency direction stays outer -> control_plane: status.py already imports
sibling control_plane.status modules at module level and the projection has
no import back, so no cycle is introduced.

status.SOURCE_REGISTRY_SHADOW_FINDINGS is deliberately kept as a public name
because examples/control_plane/attention-queue-readmodel-smoke.py reads it;
removing the re-export would break an existing caller for no gain. Values are
unchanged, so no behavior changes.

The two further copies under deprecate/benchmark-legacy/ are outside the
active scan scope and are left alone. The remaining forks in this checklist
row are not classified here.

Signed-off-by: YZJF <195568136+YZJF@users.noreply.github.com>
@YZJF
YZJF requested a review from huangruiteng as a code owner September 16, 2026 09:11

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

动机

#4447 Track A 把 SOURCE_REGISTRY_SHADOW_FINDINGS 判为同名同值的 fork:loopx/status.py 与 loopx/control_plane/status/registry_health_projection.py 各自持有一份内容完全相同的两值集合,而它们喂给的是 attention-queue read model 的同一个 source_registry_shadow_findings 参数。两份字面量只要有一处以后被改动,读模型接受的 finding 就会和生产者发出的种类不一致。

改动思路

既然语义相同、且投影本来就是该 read model 输入词汇的 owner,就把定义收在投影里(registry_health_projection.py:28 保持不变),loopx/status.py 改为 re-export 同一个对象,保证既有调用方(examples/control_plane/attention-queue-readmodel-smoke.py 通过 status_module.SOURCE_REGISTRY_SHADOW_FINDINGS 取值)不需要改动。新增 3 个用例分别钉住取值集合、对象同一性(is)以及"status 里不得再出现第二份定义"。

具体改动

  • loopx/status.py(+6/-4):删除本地字面量,改为从投影导入并在原位置继续使用(第 16 行导入、第 1093 行传入 read model),并加注释说明这是 re-export 而不是第二份定义。
  • tests/control_plane/test_source_registry_shadow_findings.py(新增 42 行):test_vocabulary_is_unchanged、test_status_shares_the_single_definition(is 同一性)、test_status_defines_no_second_copy(源码里不得再出现 stale_source_registry)。

关键代码讲解

  1. loopx/control_plane/status/registry_health_projection.py:28 — SOURCE_REGISTRY_SHADOW_FINDINGS = ('source_registry_missing', 'stale_source_registry'):现在是全仓唯一的活动定义,并且在同文件第 53 行直接喂给 read model,因此"owner 与消费点在同一模块"这件事成立。
  2. loopx/status.py:16 — from .control_plane.status.registry_health_projection import SOURCE_REGISTRY_SHADOW_FINDINGS:re-export 是这次改动的兼容性关键。我确认了消费点仍在 loopx/status.py:1093,且 shipped smoke examples/control_plane/attention-queue-readmodel-smoke.py:62/:95 仍以 status_module.SOURCE_REGISTRY_SHADOW_FINDINGS 读取——如果直接删掉名字而不 re-export,这个 smoke 会立刻失败。
  3. tests/control_plane/test_source_registry_shadow_findings.py:35 — status.SOURCE_REGISTRY_SHADOW_FINDINGS is registry_health_projection.SOURCE_REGISTRY_SHADOW_FINDINGS:用对象同一性而不是值相等来防 fork,这一点比"值仍然相等"强得多,因为未来的漂移往往从"又是一份字面量"开始。
  4. tests/control_plane/test_source_registry_shadow_findings.py:41 — assert "stale_source_registry" not in inspect.getsource(status):这条最直接,但也是我唯一提出的问题(见下):它检查的是源码文本,而不是赋值结构。

对主干的风险

没有阻塞项。 这是一次同语义去重:取值集合未变(('source_registry_missing', 'stale_source_registry')),loopx/status.py:1093 与 read-model smoke 的消费路径都保留;我全仓搜索确认活动代码里只剩投影这一处声明(其余命中是 deprecate/benchmark-legacy/ 下的归档副本、global_registry_health.py 里作为生产者发出的 kind、以及 example smoke 里作为数据使用的字符串,都不构成第二份词汇表)。分支与当前 main 合并干净。

P3(非阻断,断言形式):test_status_defines_no_second_copy 断言的是 "stale_source_registry" not in inspect.getsource(status)。这意味着以后有人在 loopx/status.py 的注释或 docstring 里提到这个取值(例如解释 re-export 的来源),测试会因为散文而失败,而不是因为真的出现了第二份定义——这类假阳性会诱使后来者删注释而不是复核 fork。最小修复是改成检查解析后的模块级赋值目标(或把检查范围限定在赋值语句),保留"重新引入字面量就失败"的能力。

验证(全部在 be53db7d9 上跑):pytest -q tests/control_plane/test_source_registry_shadow_findings.py → 3 passed;python examples/control_plane/attention-queue-readmodel-smoke.py → attention-queue-readmodel-smoke ok;git merge-tree --write-tree HEAD origin/main → 无冲突。按本 lane 配置不拉取 CI。

我的整体评价

APPROVE。这是"删除重复权威"这一类里最干净的一种:owner 已经在正确的模块(投影就是 read model 输入的来源),改动只是把另一份相同字面量换成 re-export,并用对象同一性测试把 fork 的可能性真正关掉;同时它照顾到了既有消费方,因此没有连带 churn。我有意把它与"值相等"型断言区分开评价:is 断言是这次最值钱的三行。唯一的 P3 是那条源码文本断言的形式,属于可随手收紧的细节,不影响正确性。

English verdict: APPROVE — exact head be53db7d9ffe1313e8999e2aeb2fb0db0d1b24d0 of #4517. The duplicate SOURCE_REGISTRY_SHADOW_FINDINGS vocabulary is merged into its existing owner (loopx/control_plane/status/registry_health_projection.py:28) and loopx/status.py now re-exports the same object, so the attention-queue read model keeps receiving identical findings while a second literal can no longer drift. Validation at this head: the 3 new tests pass (value set, is identity, no re-introduced literal), the shipped consumer smoke examples/control_plane/attention-queue-readmodel-smoke.py prints ok, and the branch merges cleanly with current main. A tree-wide search shows no remaining active declaration outside the projection. One non-blocking P3: the anti-fork assertion inspects module source text, so a future comment mentioning the finding name would fail it without a real re-fork.

@huangruiteng
huangruiteng merged commit c84d4a4 into loopx-project:main Sep 16, 2026
18 of 20 checks passed
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.

[Task]: Semantic vocabulary convergence follow-ups after RFC PR #4433 / 语义词表收敛后续任务

2 participants