feat(goals): derive the Goal artifact lifecycle projection - #4360
huangruiteng merged 21 commits into
Conversation
huangruiteng
left a comment
There was a problem hiding this comment.
结论:REQUEST_CHANGES
审阅绑定的 exact head:367de99e659948efc893de7a80eccfd1d8ec5a86
动机
把 Goal 的阶段、里程碑、阻塞 guard 与下一步统一投影出来,产品方向有价值:当前 operator 确实需要从 Todo、run history、quota/frontier 等分散信号中手工重建状态。但这个 PR 当前只落了 356 行 production helper 和 321 行 example smoke,正文又明确把 status markdown/dashboard 两个实际 consumer 留到后续。因此 exact head 并没有改变任何用户或 operator 的 shipped behavior,也没有建立输入完整性、失败重试与显示语义的真实 owner。
改动思路
helper 试图把已收集的 goal、user/agent Todo summary、run history、work-lane contract、acceptance gaps 合成为 goal_artifact_lifecycle_projection_v0。它把 milestone 分成 declared/evidence 两类,从 owner gates 和 acceptance gaps 生成 guards,再根据 terminal status、open_count、milestone reachability 和 lane 推导 phase/next transition。纯投影、不写状态、不授予权限的原则是正确的;问题在于实现没有沿用仓库已有的 typed delivery semantics 和依赖方向,而且没有真实 consumer 来验证这些输入是不是完整、相互一致、适合决定 closeout。
具体改动
关键代码讲解
_evidence_milestones(artifact_lifecycle.py:119)本地维护_MATERIAL_OUTCOMES,并允许delivery_batch_scale == multi_surface独立把一条 run 变成 reached milestone。随后 milestone id 又优先取delivery_outcome。_compact_text(:55)从 control-plane 向外导入presentation.public_safety,同时再维护一套 provider-token regex;这直接违反既有 import boundary,也让 public-safety classification 出现第二份规则。build_goal_artifact_lifecycle_projection(:301)导出一个新的 v0 contract,但仓库级 caller search 只找到 example smoke;status/dashboard 均未加载它。
本地 fixture smoke 能通过,但同一 exact head 上 Ruff 对 smoke 报 E402。更重要的是,远端 test-shard (1) 因 control-plane -> presentation 依赖失败,test-shard (3) 因同一 dependency debt 触发 maintainability ratchet,最终 pytest 和 merge-gate 都失败。
对主干的风险
有三个阻塞问题:
- 依赖方向错误。
loopx.control_plane.goals.artifact_lifecycle -> loopx.presentation.public_safety是仓库明令禁止的 outward dependency,现有 required checks 已经精确捕获。应把可复用 redaction primitive 放在 inward-safe owner,或让 presentation consumer 自己做最后一层适配;不能登记新 debt 来绕过。 - material outcome 语义被错误扩大。 我直接调用 exact-head helper,输入
delivery_outcome=surface_only、delivery_batch_scale=multi_surface,结果得到id=surface_only, reached=true的 milestone。batch scale 只描述交付范围,不能把非进展 outcome 升格为 Goal evidence;仓库的 typedMATERIAL_DELIVERY_OUTCOMES明确不包含surface_only。请复用 canonical typed rule,并为所有 outcome × scale 组合加负向矩阵。 - 没有 production caller。 当前只有 example smoke 调用新模块;356 行 production contract、phase vocabulary 和 v0 schema 没有任何用户入口,也无法通过真实 consumer 验证 incomplete input、gate scope、declared milestone identity 与 closeout。请要么在本 PR 中落下最小、内聚的真实 status/dashboard consumer,要么先移除/暂缓 production module,等 consumer 到来时从已有 frontier/status owner 提取被真实路径证明的最小 rule group。
我的整体评价
这个方向值得做,但当前批次把设计可能性提前冻结成了较大的未使用生产 surface,并且已经出现 typed semantics 漂移和 required CI 红灯。第二个 fix commit 解决了部分 closeout/redaction 症状,却没有解决 rule ownership、consumer demand 与依赖层级。建议先把 outcome materiality 收回 canonical typed owner、修复依赖边界,然后以真实 consumer 驱动最小投影;复审时需要看到 consumer-level readback、complete/incomplete source counterfactual 以及全绿 merge gate。
English verdict: REQUEST_CHANGES — the exact head has a forbidden control-plane-to-presentation dependency, misclassifies surface_only + multi_surface as reached evidence, and ships a 356-line production contract with no active consumer.
367de99 to
8441fcd
Compare
|
Rebased onto current main and addressed all three blocking points at new head
Mutation evidence: restoring the scale-promotion rule fails the smoke with your exact Verification at One scope note on your third point. RFC section 3 lists the dashboard as a later item and section 7's smallest slice names status markdown; I implemented the status markdown readout and left the dashboard consumer for a follow-up. If you would rather have both in this PR, say so and I will add the dashboard view on top. |
8441fcd to
55e74cc
Compare
|
CI caught a real conflict on the previous head that I had missed, now fixed at
That assertion is not stale — it came from #4248, which shipped The guard's intent is the distinction between the two contracts, not the absence of one of them, so I preserved the intent rather than deleting the assertion. The collection test now requires both projections present under their own keys with their own schema versions, and requires the phase/milestone/transition vocabulary to belong to the lifecycle projection alone. I also gave the new renderer the symmetric check #4248 established for its own: each renderer must refuse the other's schema rather than print a half-understood payload under its own heading. Mutation-verified — relaxing the lifecycle renderer's schema check to "any non-empty payload" makes it print
Verification at One unrelated red I want to name rather than leave ambiguous: |
b9d7160 to
2d0617d
Compare
songoow
left a comment
There was a problem hiding this comment.
Request changes conclusion (author-owned PR; GitHub blocks formal self-review)
审查 head:2d0617d0dd9eadc48034290d122084ec8459a1a1。结论:REQUEST_CHANGES。
动机
Goal 生命周期读出应让操作员看到真实阶段、里程碑与阻塞项。新增一个 schema 或能打印一行 markdown 还不能证明这一价值:它必须消费已有状态的真实形状,保留来源完整性,并服从现有 lane/frontier。较小且更可靠的方案是复用 acceptance observation 的数据提取、user-gate/vision owner 和既有 public-safety 边界,再增加必要的生命周期词汇;不应复制一套缺省为空的推导链。
改动思路
当前 consumer 为 collect_status → attach_goal_artifact_lifecycle_projections → pure builder → run_history.goals[].artifact_lifecycle → status markdown。相对上次评审,已去掉 control-plane → presentation 导入,materiality 也改用 canonical MATERIAL_DELIVERY_OUTCOMES,并接入真实 collection/rendering。两个投影的 schema 隔离有对称测试。这些修复有效,但新消费者的输入接线、脱敏边界与 lane 优先级仍有问题。
具体改动
整个 diff 为 6 个文件:423 行生命周期模块、47 行 renderer、collection/status markdown 两处接入、485 行 fixture smoke,以及 acceptance observation 集成测试的并存断言。没有新增持久状态或执行权。Dashboard 没有新显示消费者;这不应被描述为完成了 dashboard 功能。
关键代码讲解
attach_goal_artifact_lifecycle_projections(artifact_lifecycle.py:395-406)读取user_todo_summary/agent_todo_summary,并把整个run_history汇总对象交给 builder。_evidence_milestones实际只读取输入中的latest_runs;_guards只读取gate_open_items以及独立 gaps。_compact_text(:82-90)先用本地 regex 重写/截断,再以另一套 private-text/token 规则判断是否保留。_next_transitions(:284-310)仅在 phase 不是 closing 时服从 lane,否则直接建议 closed。
对主干的风险
[P1] 真实状态接线错误,当前 guards 与运行证据被丢失。
运行仓库现有 collect_fixture,它通过真实 refresh_state_run/collect_status 构建状态:acceptance_observation 有 1 个 user gate 和 1 个 acceptance gap,新 lifecycle 却为 starting、guards=[]、milestones=[]。真实 attention 字段为 user_todos/agent_todos,summary 的 gate 需要现有 open_user_gate_todo_items 提取;顶层 run_history 有 goals/recent_runs,没有 latest_runs。新 smoke 的 consumer fixture 手填了只有新实现才认识的字段,所以无法发现接线错位。请使用每个 Goal 未经过 display limit 裁剪的 history,复用现有 user-gate/agent-vision 提取,并在真实 collection fixture 断言 guard、gap、evidence 和 markdown 的值,而非只检查 schema/key 存在。
[P1] “public-safe”仍放行已有公共边界明确禁止的值。
用合成的 Windows 用户路径、Linux 配置/工作区绝对路径,以及 access_key=<synthetic-value> 测试:_compact_text 全部原样返回,而仓库现有 runtime.public_safety.validate_public_safe_value 全部拒绝。Windows regex 匹配的是重复反斜杠,常见路径不匹配;本地 Linux 前缀集和凭据规则也比 canonical validator 窄。应复用已经位于 control-plane 内的 public-safety owner,并在截断前校验完整输入;不要维护第三份缩减规则。negative coverage 应调用 canonical validator 校验整个输出,覆盖 label 和 evidence ref。
[P2] closing 路径覆盖现有强制 work lane。
用真实 lark_inbox_reply_due_work_lane_contract 构造 must_attempt_work=true 的待回复 lane,配合零 open Todo 和已有 material run,builder 仍返回 next=closed、reason_codes=[no_open_agent_work]。这与 PR 宣称的 lane 优先相反,也不符合 RFC 要求“从既有 frontier/lane 推导合法下一步”。这是只读建议错误,不是已经修改 Goal 完成状态;修复应服从现有权威 lane/frontier,避免另建 completion gate。
我的整体评价
本轮 fixture smoke 与 10 个 acceptance tests 均通过,以上三个独立反例也均复现。当前 CI 通过说明旧依赖错误已消除,不能证明产品读出的事实正确。423 行生产模块与 485 行 smoke 的核心成本仍应通过复用既有数据提取和安全规则降低,而非继续追加手工 payload。
PR 正文也需要按最终实现重写:它仍说 status consumer 是后续,并声称使用已被删去的 redactor。当前不建议合并;修复真实输入接线、安全合同及 lane 优先级后再做端到端复审。本轮未作 packaged dashboard 验收,也不将不完整历史等同于 Goal 已完成。
English verdict: REQUEST_CHANGES at 2d0617d0dd9eadc48034290d122084ec8459a1a1. Real status collection loses the existing user gate, acceptance gap and per-goal run evidence due to mismatched input fields; the local sanitizer retains values rejected by the canonical public-safety validator; closing overrides a mandatory existing work lane. The fixture smoke and 10 acceptance tests pass but miss these reproduced counterexamples. Reuse the existing extraction/safety owners and test actual collection output.
|
修复提交: 已修复真实字段接线、共享安全边界和强制 lane 优先级。复用 acceptance-observation 保留人工/Controller gate 与 vision gaps,读取 display trimming 前的 Goal history 和 semantic retention;补齐 quoted credentials、Windows/UNC/data-root 负例。安全值和公开 HTTPS 引用仍可使用。 验证:137 项相关测试、fixture smoke、Ruff、公私边界及 LoopX premerge 通过;独立候选复查发现的同边界遗漏也已加入回归。 已重写 PR 正文以反映当前实现与边界。全部提交带 DCO sign-off;新的 CI 已触发,尚未宣告合并或批准。请按此 head 复审。 Fix follow-up: the reproduced findings are addressed at this exact head with regression coverage and repository-native premerge validation. CI is rerunning; this is a fix report, not an approval. |
steven-kid
left a comment
There was a problem hiding this comment.
动机
REQUEST_CHANGES。 审查 head:155c20760d2755974ddb145451b8426c900c3a0c;完整 base:e743f2404d4a17d52cbf2d15db1f879ff46caea8。
RFC #4128 希望 operator 从一个 Goal 读出阶段、里程碑、阻塞 guard 与下一步,而不是手工拼接 Todo/run/quota。RFC 的最小切片允许 pure projection、fixture smoke 和 status markdown,现有 consumer 现在满足这一最小使用场景,不需要为本轮批准强制增加 dashboard。
相对旧评审 head 2d0617d0dd9eadc48034290d122084ec8459a1a1,真实字段接线、shared safety 与 mandatory lane 优先级已修复,旧 outward import 和 scale promotion 也没有回归。但是“material history”仍被当作“可关闭 Goal”的证据,越过了现有 acceptance observation 对不完整来源的明确限制。复用更多字段和全部 fixture 通过,不能证明这一步推断成立。
改动思路
当前生产链路为 collect_status → attach_goal_artifact_lifecycle_projections → builder → run_history.goals[].artifact_lifecycle → status markdown。attach 读取 display trimming 前的 per-Goal history,复用 semantic retention,真正使用 attention 的 user_todos/agent_todos。guards 与 vision gaps 通过已有 acceptance-observation owner 提取,materiality 使用 canonical DeliveryOutcome;public safety 也改为 inward-safe owner。
我比较了 base/head 的 acceptance observation、history/semantic retention、frontier/work-lane 和 shared safety。保留原 acceptance observation 的“partial、acceptance_assessed=false”是合理的不同视图;但新投影应派生相同权威事实,不能在缺少 acceptance 来源时另行推出 closeout。最小修复应收窄 closing 的证明条件并沿用已有 frontier/验收语义,不需要新存储或新的完成引擎。
正向真实 collection 测试已证明 user gate、vision gap、material run 在 display limit 0/5 下保留,markdown 可读;deferred/completed gates、foreign Goal run 和强制 Lark reply lane 有负向覆盖。新增 readout 没有执行完成或修改 quota 的权力;下面报告的是面向 operator 的错误阶段及行动建议。
具体改动
关键代码讲解
artifact_lifecycle.py::attach_goal_artifact_lifecycle_projections/build_goal_artifact_lifecycle_projection连接当前用户/agent summary 与未裁剪 Goal history,补入 retained material/vision run,并过滤 foreign Goal。已消除上一轮手填字段才能工作的问题;没有文件写入。_evidence_milestones/_lifecycle_phase/_next_transitions把 material outcome 转成 reached marker,再以零 open agent Todo 和没有 unreached marker 判定 closing,并建议 closed。这里混淆了 materiality 与验收/进展:canonicalMATERIAL_DELIVERY_OUTCOMES包含 outcome_gap,而PROGRESS_DELIVERY_OUTCOMES刻意不包含它。_guards复用 acceptance-observation 的 owner gate 和 gap,但 builder 丢掉 observation 的 coverage/missing_sources/acceptance_assessed。这使“未观察到 gap”继续被当成“没有未满足验收”,尤其在缺少 agent vision 时。runtime/public_safety.py扩大 Windows drive/UNC/data-root 与带引号凭据识别;_compact_text在截断前调用完整 validator,再保留已有 private-text/provider-token 的更严格检查。作用域是所有 shared-safe-text 调用者,PR 正文已经披露这种更严格拒绝;仍需处理下面实际失败的已有契约测试。goal_artifact_lifecycle_markdown.py只接受自身 schema,输出阶段、milestone 数、guard 与 next transition;status renderer 接入,接受观察的对称 schema 测试保留。481 行 fixture smoke 与扩展 acceptance tests 覆盖正反路径,但部分 closeout oracle 本身就允许缺少验收证据的关闭建议,不能作为该语义的独立证明。
全部 7 文件 +1107/-8 已审查:423 行 pure module、47 行 renderer、collection/render 接入、shared safety 和两份测试资产。没有新 settings、自动安装指令或 feature flag。_declared_milestones 的实际 authoring→collection 路径仍缺少证明:collect_history 的 Goal 字段选择没有 acceptance/milestones,手填 helper payload 不能证明普通用户声明会到达 readout。请接入既有真实 marker 来源或收窄未交付的声明,而不是再增加手工同步状态。
对主干的风险
[P2,功能性缺陷] 不完整验收加 outcome_gap 被显示为 closing,并建议 closed。 定位:loopx/control_plane/goals/artifact_lifecycle.py:238–240,结合 _evidence_milestones 和 _next_transitions。
使用真实 refresh_state_run 写入一条 delivery_outcome=outcome_gap、建议“Missing independent verification”的记录;Goal active,无 open Todo,也没有 agent vision。然后通过真实 collect_status 和 status markdown,在 base/head 分别运行完全相同输入,display limit 均测试 0/5:
- base 的 acceptance observation 为
coverage=partial, missing_sources=[agent_vision], acceptance_assessed=false,现有 next action 仍是继续只读检查;不存在生命周期关闭读出。 - head 保留上述 partial observation,却同时输出
lifecycle_phase=closing、outcome_gap reached=true、guards=[]、next_transitions.target_phase=closed;markdown 明确显示next: closed (record the terminal no-follow-up outcome)。
这不是 Todo 已经被关闭,也不是测试伪造新投影结果;实际 collector/backend 自己产生了矛盾的公开建议。独立 oracle 来自 canonical outcome 对 material/progress 的区分,以及已有 observation 对 missing source 的限制。将 gap 记作历史证据可以有价值,但不能据此推导验收已足够关闭。修复时保留 incomplete/unknown coverage,并从已有 frontier/验收依据决定 closeout;仅从 material 集合删除一个值还不能证明 outcome_progress 就足够完成整个 Goal。
现有 required check 仍失败。 本地复现 tests/test_loopx_turn_executor.py::test_enabled_host_result_rejects_path_shaped_opaque_refs[worker_ref-C:/workspace/private/worker.json]:期待 opaque-reference 错误,现在返回 absolute-local-path 错误。base 聚焦两个用例通过,head 同一用例失败;这与远端 test-shard(4)/pytest/merge-gate 一致。安全值仍被拒绝,并非敏感值泄漏;请明确保留旧诊断优先级,或按有意变更更新契约断言,并覆盖 shared consumers,不能将 CI 红灯当作无关。
验证:acceptance + turn executor 70 passed、1 failed;architecture/import + retained-history 24 passed;fixture smoke、按项目范围 Ruff、diff check 通过。base 相关 opaque-reference 用例 2 passed。没有运行 packaged dashboard、全仓库 suite 或真实服务;不把 smoke 和局部绿灯等同于完整产品验证。
我的整体评价
本轮确实解决了上一轮接线、安全和 lane 的具体问题,也有最小真实 consumer;但还未证明新增 closing 推断与 canonical completion/acceptance 语义一致。当前应请求修改,优先消除上述真实 readout 矛盾、修复 shared-consumer 红灯,并补齐声明来源或缩减承诺。建议把回归放在真实 collection/markdown 路径,继续以不完整数据为 unknown,而不是继续扩展手填 fixture 来匹配当前实现。未批准、未合并。
English verdict: REQUEST_CHANGES at 155c207. A real refresh_state_run → collect_status → markdown probe with only outcome_gap and no agent vision produces closing / next: closed, while the existing acceptance observation still says partial, missing agent_vision, acceptance_assessed=false. Material history is not completion evidence; preserve missing-source uncertainty and derive closeout from the existing acceptance/frontier owner. This is a read-only recommendation defect, not an executed Goal completion. Prior wiring, safety and mandatory-lane fixes are present. A shared turn-executor diagnostic contract also fails locally and in required CI: 70 related tests passed / 1 failed; 24 architecture/retention tests, lifecycle smoke, Ruff and diff checks passed. Baseline opaque-reference cases pass. No merge performed.
|
English verdict: REQUEST_CHANGES at 155c207. A real refresh_state_run → collect_status → markdown probe with only outcome_gap and no agent vision produces closing / next: closed, while the existing acceptance observation still says partial, missing agent_vision, acceptance_assessed=false. Material history is not completion evidence; preserve missing-source uncertainty and derive closeout from the existing acceptance/frontier owner. This is a read-only recommendation defect, not an executed Goal completion. Prior wiring, safety and mandatory-lane fixes are present. A shared turn-executor diagnostic contract also fails locally and in required CI: 70 related tests passed / 1 failed; 24 architecture/retention tests, lifecycle smoke, Ruff and diff checks passed. Baseline opaque-reference cases pass. No merge performed. |
155c207 to
55f9ee4
Compare
…d quoted secrets The shared local-path rule only treated `X:\Users` and `X:\Documents and Settings` as local paths, so any other drive-qualified path, every UNC share and `/data/...` roots passed the public boundary. The secret rule required the key to touch its separator, so JSON-shaped and quoted forms such as `"token": "..."` and `access_key='...'` were never classified. Both rules are widened. The lookbehind still excludes URL schemes, clock times and ratios; a boundary test pins those shapes on both sides. Behavior change, disclosed: a drive-qualified path-shaped opaque ref is now reported as an absolute local path before the opaque-reference shape check. The value was rejected before and is rejected now; only the diagnostic differs. The turn-executor contract records the intended diagnostic per shape. Split out of loopx-project#4360, which only needs the rules that already exist. Signed-off-by: song <liusongstep@gmail.com>
…d quoted secrets The shared local-path rule only treated `X:\Users` and `X:\Documents and Settings` as local paths, so any other drive-qualified path, every UNC share and `/data/...` roots passed the public boundary. The secret rule required the key to touch its separator, so JSON-shaped and quoted forms such as `"token": "..."` and `access_key='...'` were never classified. Both rules are widened. The lookbehind still excludes URL schemes, clock times and ratios; a boundary test pins those shapes on both sides. Behavior change, disclosed: a drive-qualified path-shaped opaque ref is now reported as an absolute local path before the opaque-reference shape check. The value was rejected before and is rejected now; only the diagnostic differs. The turn-executor contract records the intended diagnostic per shape. Split out of loopx-project#4360, which only needs the rules that already exist. Signed-off-by: song <liusongstep@gmail.com>
|
Reworked at P2 — On "defer to the acceptance owner". The first attempt gated Declared milestones stay readable (RFC §3.2 lists them in the smoke, §9 keeps their source open). No producer yet; not resolved here. P1 — shared turn-executor contract red. The Validation: 73 tests + lifecycle smoke, Ruff, public-boundary scan, maintainability ratchet; rebased on |
huangruiteng
left a comment
There was a problem hiding this comment.
队列前言(loopx pr-review --state open,policy_revision=3,other-developers-first):本卡是 rank 2 的 #4360(社区贡献者 @songoow,tier 0 社区反馈槽位)。我在开始审查时读出 packet 记录的 head 是 55f9ee4ac,随后远端已前进到 692c1b8f0,因此本轮全部证据都在最新 head 上重新取得,未继承 @steven-kid 上一轮的结论。
结论先行:在这个 exact head 上通过(一个非阻塞 P2)。 上一轮 REQUEST_CHANGES 的两条实质问题都已在真实路径上被修复:outcome_gap + 零 open agent Todo 不再产生 closing / next: closed;shared turn-executor 诊断契约的回归也消失了。我另提一个非阻塞 P2:终态建议分支目前没有可达的生产触发条件。
动机
RFC #4128 希望 operator 从一个 Goal 直接读出阶段、已达里程碑、阻塞 guard 与下一步,而不是自己从 Todo 计数、run 分类和 quota 里拼。改动前后是可测的:在 base cd9cfa6d4 上用同一份一次性 fixture 跑真实 loopx status,两个 Goal 都得到 artifact_lifecycle: ABSENT;在 head 上同一份 fixture 得到 lifecycle_phase、带 reached 的 milestones、带 owner/blocked 的 guards、以及带 target_phase/precondition/reason_codes 的 next_transitions,markdown 也渲染出对应区块。
这块不是「已经能工作还要再包一层」:上一轮的真实探针证明,自建推断会把 run 级 outcome_gap 读成可以关闭 Goal 的证据。所以本轮的判断标准不是「新增了字段」,而是「新增的字段在证据不足时是否还会给出错误动作」。
改动思路
链路是 collect_status → attach_goal_artifact_lifecycle_projections → build_goal_artifact_lifecycle_projection → run_history.goals[].artifact_lifecycle → status markdown。投影只读已经收集好的 payload:Goal 记录、run_history 的 latest_runs 与 collector 保留的 semantic_history、attention item 的 user_todos/agent_todos/work_lane_contract,以及 frontier 的 acceptance_gaps。它不读文件、不写状态、不花 quota。
规则的归属是这轮最值得肯定的部分:materiality 用 canonical PROGRESS_DELIVERY_OUTCOMES(而不是自建集合),里程碑可达性只由 progress outcome 决定、outcome_gap 保留为「可见但未达」的标记;guards 与 coverage 来自既有 acceptance-observation owner;lane 优先级来自 attention item 的 work_lane_contract;保留证据来自 collector 的 semantic retention,而不是放宽 display limit。_next_transitions 的顺序是固定的:open guard → 已有 lane 约束 → 未达里程碑 → 最后才是 closing,所以 lane owner 永远压过投影自己的推断。
正向路径:progress 证据把里程碑标为 reached,阶段进入 closing,此时 closeout 步骤不直接给终态,而是要求 acceptance owner 自己的裁决;不满足就把步骤留在 closing,并在 precondition 里点名读不到的来源。负向路径:outcome_gap 只产生未达标记,阶段是 qualifying,下一步是 milestone_unreached;已终结的 Goal 直接 phase=closed 且没有下一步。
具体改动
6 个文件 +1215/−3:445 行纯投影模块、47 行渲染器、两处各 1 行的接入(status collection 与 status markdown),以及 497 行 fixture smoke 与 225 行行为测试。没有新增持久状态、没有 feature flag、没有新命令。
关键代码讲解
attach_goal_artifact_lifecycle_projections(loopx/control_plane/goals/artifact_lifecycle.py:389):只把已经收集到的 payload 组装成输入,成本为零额外 IO;推导不出来的 Goal 直接没有该字段,而不是给一个占位值。_acceptance_supports_closeout(:178):终态建议必须由 acceptance owner 自己的裁决字段支撑(acceptance_assessed is True且coverage == "complete"且无未读来源),不再从「没有可点名的缺失」推断裁决。这是我下面 P2 的来源。_next_transitions(:245):用既有 lane/guard/里程碑顺序派生下一步,而不是第二套状态机;没有裁决时把步骤留在closing,并把acceptance_unverified与读不到的来源写进 precondition。_evidence_milestones(:88):只有 progress outcome 能把里程碑标为 reached;delivery_batch_scale不改变 materiality;outcome_gap保留为未达标记。append_goal_artifact_lifecycle_markdown(loopx/presentation/renderers/goal_artifact_lifecycle_markdown.py:13):schema 不匹配就完全不输出,避免把别的投影当成本投影渲染。
我的验证
- 真实路径(base 对比 head,一次性 fixture,真实
loopx status):outcome_gap+ 零 open agent Todo →phase=qualifying、outcome_gap reached=false、next=[qualifying, milestone_unreached];progress + 零 open work →phase=closing、next=[closing, acceptance_unverified],precondition 点名agent_vision, todo_projection;base 上两者都无该字段。acceptance observation 在 base 与 head 上取值完全一致,说明这不是靠改动既有投影来凑结果。 tests/control_plane/test_goal_acceptance_observation.py20 passed;tests/test_loopx_turn_executor.py -k opaque2 passed(上一轮报告的 shared-consumer 红灯已消失);goal-artifact-lifecycle-projection-smoke ok;改动文件 Ruff 通过。- 依赖方向已修正:新模块只导入
control_plane/runtime/public_safety与根级public_safe_text,不再有 control-plane → presentation 的外向依赖;rg也确认仓库里没有任何生产者写goal.acceptance.milestones,与「删除声明式里程碑读取器」的说明一致。 - 未验证:head 的完整 CI 矩阵当时仍在 queued/in progress(Sign-off 已 SUCCESS,无失败项);dashboard UI 与 periodic-report consumer 是本 PR 声明的后续;我的探针用的是合成 Goal/run 数据,没有触碰任何真实 goal。
对主干的风险
最强的回归场景不是崩溃,而是 readout 给出证据不支持的动作。第一类(把非进展 outcome 当关闭证据)和第二类(把有界观测的沉默当已验收)在 head 上都已被阻断,我在真实 collector 路径上复现了两者的输入形状,结果符合预期。
第三类是共享面:collect_status 与 status markdown 都被大量消费者使用,所以隔离不能靠读 diff。我用 base/head 同 fixture 对比确认:既有字段取值不变(acceptance observation 完全一致),新增字段只在能推导时出现,渲染器对非本 schema 静默返回。
P2(非阻塞,建议后续处理):终态建议分支目前没有可达的生产触发条件。 定位:artifact_lifecycle.py:178(guard)与 :292-303(target_phase: closed 分支)。触发条件:任何走真实 collect_status 的 active Goal,包括「无 open agent 工作且所有里程碑已达」的那一种。结果:唯一的生产者 build_goal_acceptance_observation 把 acceptance_assessed 硬编码为 False(acceptance_observation.py:198)、coverage 只会是 partial/unavailable(:199),dashboard 契约把该字段钉成 z.literal(false),本 PR 自己的测试也断言 coverage != "complete";因此 closed 建议永远不会触发,也没有任何 fixture 覆盖它,而同一个 head 刚刚以「没有生产者」为由删掉了声明式里程碑读取器。最小修复:要么删掉这个不可达分支、明确读出停在 closing 直到 acceptance 裁决 owner 出现,要么保留但在注释与测试中显式标注当前不可达(现有那条 coverage != "complete" 断言就是很好的起点)。我把它判为非阻塞,因为方向是 fail-safe、读出不误导,而且它正是上一轮要求的「不自己发明完成规则」的直接结果——但它值得在 acceptance 裁决生产者落地时一起收口。
残余风险:head 的完整 CI 矩阵在本轮审查时仍在排队;dashboard 与 periodic-report 消费面未演练;关闭建议的可达性取决于未来的 acceptance 裁决生产者。
我的整体评价
可观测语义是明确的新增,并且相对上一个 head 是有意的语义修正:base 上没有该字段,head 上同一份真实输入给出自洽的阶段/里程碑/guard/下一步,而上一轮报告的具体矛盾(gap + 无 open work → closing/next: closed)在真实路径上已不可复现。代码体量与问题相称:一个纯投影加一个渲染器与两处一行接入,没有新持久状态、没有新命令、没有第二套完成规则;规则都留在原有 owner 里。scope fit 有真实调用点(collect_status),并且这一轮真的删掉了没有生产者的声明式里程碑读取器。因此我给出通过结论;上面的 P2 不阻塞,合并仍走 loopx-pr-merge,本评论不构成合并授权。
English verdict: APPROVE at exact head 692c1b8 — the derived lifecycle readout reaches the real loopx status JSON and markdown (base: field absent, head: phase/milestones/guards/next present), and the previous round's reproduced defect is gone: on a disposable fixture whose only run is outcome_gap with zero open agent Todos the real collector now reports phase=qualifying, outcome_gap reached=false, next=[qualifying, milestone_unreached], while a progress Goal stays in closing with acceptance_unverified naming agent_vision, todo_projection. Validation on this head: 20 goal-acceptance tests, 2 shared turn-executor tests (the earlier shared-consumer failure is resolved), the lifecycle smoke, Ruff, and my base/head probe. One non-blocking P2: the target_phase: closed branch is currently unreachable because the only acceptance-observation producer hardcodes acceptance_assessed=False and never reports complete coverage, so it should either be removed or explicitly pinned as awaiting a producer. Full CI was still queued at review time; dashboard and periodic-report consumers are declared follow-ups.
Implements the smallest useful slice of the Goal artifact lifecycle RFC: one pure derivation module plus a fixture smoke. An operator can see todo counts, quota state and the latest classification for a long-running Goal but must reconstruct three answers from them: where the Goal sits in its lifecycle, which milestones it has reached, and which guard blocks the next step and who owns it. This derives those from state LoopX already owns. The projection reads no files, writes no state and grants no authority. Milestone reachability starts from markers the Goal declares and falls back to material evidence already recorded in the run history; a declared marker is a claim, not proof, so it counts only when evidence records it. Guards are open owner decisions and unmet evidence preconditions. Next transitions reuse the existing frontier/lane derivation rather than a second state machine. The fixture smoke covers the RFC's negative cases: an unreached declared milestone, a blocking owner gate that admits no other transition, an evidence guard owned by the agent, the closing/closed boundary, projection purity, and the public-safe boundary. Refs loopx-project#4128 Signed-off-by: song <liusongstep@gmail.com>
Review found two defects in the derived projection. Closeout ignored milestone reachability: a Goal whose declared acceptance marker was still unreached, with no open agent work, was reported as closing with a next transition of closed. Running out of open work is not the same as having reached acceptance, so an unreached milestone now keeps the Goal in qualifying with a milestone_unreached reason. An existing work-lane constraint also outranks this projection's own reading of remaining work. The compact label helper claimed public safety without providing it: it only collapsed whitespace and truncated, so a private absolute path in a run history reference reached the projection verbatim. It now reuses the shared redaction rule and drops a value that still matches a private-text or provider token shape. Both are covered by negative cases, and each case kills its mutant: removing the closeout guard or the redaction rule fails the smoke. Signed-off-by: song <liusongstep@gmail.com>
…consumer Three review findings, all reproduced at the previous head: 1. Forbidden dependency direction. The module imported `loopx.presentation.public_safety`, an outward dependency the control plane may not take. Redaction is now bounded and inward-safe inside the module and reuses `loopx.public_safe_text.find_private_text_match` for classification, so the shared private-text contract still has exactly one owner. 2. Material outcome semantics drifted. Evidence milestones kept a local outcome set and let `delivery_batch_scale == "multi_surface"` promote a run on its own. Driving the helper with `delivery_outcome=surface_only` plus `delivery_batch_scale=multi_surface` returned a reached milestone, but batch scale describes delivery width and the canonical typed rule (`MATERIAL_DELIVERY_OUTCOMES`) excludes `surface_only`. The module now consumes that canonical rule directly, and the smoke pins the full outcome x scale matrix in both directions. 3. No production consumer. The RFC's smallest slice requires one readout in status markdown. Status collection now attaches the projection and the presentation renderer prints phase, milestone and guard counts plus the blocking guards and the next transition. `loopx status` shows the readout without any extra IO: it derives only from payloads collection already gathered. Verified: the smoke fails when the scale-promotion rule is restored and when the presentation import is restored; the import-boundary suite is 15/15; status and architecture suites are 29/29; docs governance passes; the four status smokes pass; Ruff reports nothing new on the changed files. Signed-off-by: song <liusongstep@gmail.com>
CI `test-shard (2)` caught a real conflict I had missed: `loopx-project#4248` shipped `goal_acceptance_observation_projection_v0` as bounded historical evidence and guarded that it is *not* the full lifecycle contract, partly by asserting the collected Goal carries no `artifact_lifecycle` key at all. Attaching the real lifecycle projection made that guard fail (2100 passed, 1 failed). The guard's intent was the distinction, not the absence, so this preserves the intent under the new reality instead of deleting the assertion. The collection test now requires both projections to be present under their own keys with their own schema versions, and requires the phase/milestone/transition vocabulary to belong to the lifecycle projection alone. The smoke gains the symmetric renderer check `loopx-project#4248` established for its own renderer: each renderer must refuse the other's schema rather than print a half-understood payload under its own heading. Verified by mutation — relaxing the lifecycle renderer's schema check to "any non-empty payload" makes it print `phase=unknown milestones=0/0` for an acceptance observation, and the new assertion fails. `test_markdown_rejects_the_distinct_full_lifecycle_contract` is untouched and still passes. Signed-off-by: song <liusongstep@gmail.com>
Own the Goal status vocabulary and the public-safety boundary from their canonical modules instead of local copies, and give every emitted string the same redaction the rest of the control plane applies. The shared public-safety rule widening (drive-qualified and UNC paths, quoted secret keys, `/data/` roots) that this change originally carried is now a separate PR, because it changes a rule other owners consume and alters the turn-executor diagnostic contract. This projection only needs the rules that already exist. Signed-off-by: song <liusongstep@gmail.com>
Two corrections to the lifecycle readout, both bounded by owners this projection already consumes. Material history is not progress. `MATERIAL_DELIVERY_OUTCOMES` decides what run history retains and deliberately includes `outcome_gap`, while `PROGRESS_DELIVERY_OUTCOMES` deliberately excludes it. The evidence milestones used the material set to mark markers reached, so zero open agent Todos over a recorded gap read as closing / next: closed. A gap now stays a visible but unreached marker, and that Goal stays qualifying with `milestone_unreached`. Closing keeps the todo-completion reading this RFC adopts (no open agent work and every marker reached); it does not become an acceptance gate. `goal_acceptance_observation_projection_v0` is bounded by contract (`acceptance_assessed` is always false, coverage is never complete), so requiring its verdict would make `closing` unreachable. Instead, when that owner names sources it could not read, the closeout step carries them in its precondition with `acceptance_unverified`, and the reader keeps the decision. Declared milestones stay readable: RFC §3.2 lists them in the smoke and §9 keeps their source an open question. They have no producer yet; that is tracked there, not resolved here. The shared public-safety rule widening is a separate PR. Signed-off-by: song <liusongstep@gmail.com>
Builds on the recorded-gap fix rather than replacing it: `outcome_gap` staying an unreached marker is kept, and so is naming the acceptance sources the bounded observation could not read. Two gaps remained in that reading. An empty `missing_sources` was treated as an acceptance verdict. It only means the observation read every source it knows about; the projection still reports `acceptance_assessed=False` and a coverage that is `partial` or `unavailable`, never `complete`. So a Goal with both an attention item and agent vision present produced a bare `next: closed` with no disclosure at all -- the same defect as the reported one, moved to a fully observed input. `_acceptance_supports_closeout` now reads the verdict fields directly instead of inferring one from silence. Annotating the reason codes did not undo the recommendation. The reported defect is an actionable wrong step shown to an operator, and `target_phase: closed` remained that step even with `acceptance_unverified` attached. Closing stays reachable as the todo-completion reading, but without a verdict the step stays inside closing and asks for the acceptance the existing owner has not given. Declared acceptance markers are removed. No producer writes `goal.acceptance.milestones` or `goal.milestones` anywhere in the repository, so the reader was unreachable in production while carrying the only guard able to hold back a closeout, and the fixtures exercising it could not show that a user declaration reaches the readout. Add it back together with the producer that writes it. Signed-off-by: song <liusongstep@gmail.com>
692c1b8 to
9d04037
Compare
|
Rebased onto Conflict and resolution. One file: Validation at this head (public-safe summary).
变基到最新 |
huangruiteng
left a comment
There was a problem hiding this comment.
动机
loopx status 目前只能告诉 operator "有哪些 Goal、跑了什么、quota 还剩多少",不能回答长周期 Goal 最常被问的两个问题:这条 Goal 现在处在哪个阶段,下一步该做什么、被谁阻塞。run_history.goals[] 里没有任何生命周期读出(base 上没有 artifact_lifecycle 键),于是阶段、已达里程碑、阻塞者只能靠人工从 Todo 计数、run 分类和 quota 里推断。推断错的代价不是"少看一眼",而是按错误阶段行动——上一轮 review 的真实探针正是如此:outcome_gap(material 但未推进)被读成可以关闭 Goal 的证据。这个方向我认可:把 RFC #4128 的生命周期读出投影到既有 status 面上,并且明确它只读、不授予完成权限、不新增存储。
改动思路
- 新增
loopx/control_plane/goals/artifact_lifecycle.py(445 行):纯函数派生lifecycle_phase、带reached的milestones[]、带 owner/blocked 的guards[],以及带target_phase/precondition/reason_codes的next_transitions[]。 - 新增渲染器
goal_artifact_lifecycle_markdown.py,并在status_markdown.py接线;collection.py在既有 acceptance observation attach 之后挂上本投影。 - 用 497 行 fixture smoke 与
tests/control_plane/test_goal_acceptance_observation.py(+231/-3)把"material 不等于 progress"这条规则钉住。 - 本 head 相对上一轮受审 head
692c1b8f0的三处修正:reached改由PROGRESS_DELIVERY_OUTCOMES判定(outcome_gap保持"可见但未达");新增_acceptance_supports_closeout门;删掉没有 producer 的_declared_milestones。
具体改动
loopx/control_plane/goals/artifact_lifecycle.py(新增 445 行)::129的"reached": outcome in PROGRESS_DELIVERY_OUTCOMES、:178的_acceptance_supports_closeout、:245的_next_transitions、:361的 attention item 归一化。loopx/presentation/renderers/goal_artifact_lifecycle_markdown.py(新增 47 行):按schema_version拒绝外来 schema 后再渲染。loopx/control_plane/status/collection.py(+2,:12/:247)与loopx/presentation/renderers/status_markdown.py(+2):status JSON 与 markdown 两个消费面。examples/control_plane/goal-artifact-lifecycle-projection-smoke.py(新增 497 行)与tests/control_plane/test_goal_acceptance_observation.py(+231/-3)。
关键内容讲解
- material 不等于 progress 这条规则现在有真实探针。
:129只把 canonical progress outcome 判为已达;我把该行改回"reached": True后,smoke 在assert_outcome_gap_is_material_but_not_reached立刻失败(reached == {'outcome_gap': False}断言被打破),恢复后git status干净。这正是上一轮 review 提出的读错风险,本 head 已经堵住。 - 规则 owner 没有重复。materiality 归
control_plane/work_items/delivery_outcome,验收来源与 guard 归acceptance_observation,public safety 归runtime/public_safety;本模块只派生阶段与下一步,两个投影 schema 不同且互相拒绝,没有出现第二份完成规则或第二份持久状态(阶段不是存储枚举,注释已写明 Goal 可在阶段间移动而无 durable transition)。 - 验证证据:
pytest tests/control_plane/test_goal_acceptance_observation.py tests/test_loopx_turn_executor.py→ 77 passed;pytest tests/control_plane/test_cli_output_budget.py tests/control_plane/test_cli_output_differential.py tests/test_goal_portfolio.py→ 97 passed;maintainability ratchetunreviewed=0;lifecycle fixture smoke ok。
对主干的风险
P2(读出的"观测到什么"会少报一项,不阻塞但要修)。artifact_lifecycle.py:361 把缺失的 attention item 归一化成 {"user_todos": user_summary} 这个真值 dict。于是调用导出 builder 的路径(本 PR 的 smoke 与若干测试正是这样用)内部得到的是 coverage=partial 且不含 attention_queue,而 owner 侧的 attach_goal_acceptance_observations 对同一个 Goal 报的是 missing_sources=['attention_queue','agent_vision']、coverage='unavailable'——两条路径对"同一份输入观测到了什么"给出不同答案。我用直接调 builder 与调 attach 对同一 payload 对比复现。最小修法:始终把 attention_item 原样传下去(owner 已正确处理 None),或在 coverage 语义里显式声明"{"user_todos": ...} 等于没有 attention item";并在 smoke 的 closing 断言里补上 attention_queue。
P3(closed 分支当前不可达)。_acceptance_supports_closeout 要求 acceptance_assessed is True 且 coverage == 'complete',但 owner 侧唯一生产者 acceptance_observation.py:211 硬编码 acceptance_assessed=False、coverage 只可能是 partial/unavailable,所以 :299 的 target_phase=closed 分支在本 head 上永远走不到;smoke 也只断言 target_phase != PHASE_CLOSED。这不是错误(保守失败方向是对的),但读代码的人会以为存在可达的"建议关闭"路径。最小修法:在门函数上写明这是为 M1 预留的保守门,或在注释里点名唯一生产者当前不可能返回 True。
P3(第三份 provider-token 规则)。artifact_lifecycle.py:53 的 _TOKEN_SHAPES 与 loopx/control_plane/runtime/public_safety.py 的 SECRET_LIKE_SURFACE_PATTERN 是一组语义相近但阈值不同的规则,属于同一知识的两份副本。最小修法:要么复用 public_safety 的 owner,要么在注释里写明为什么这里的形状判定必须更严/更松。
其余残余风险:本 head 相比上一轮受审 head 已经修掉了最关键的一处读错(reached),并删掉了没有 producer 的 _declared_milestones,我认可现在的收敛方向;投影是派生只读、无状态残留,回滚面就是这一个键与两个消费面。继承性红灯(Frontstage Pages build 缺 long-horizon-control/position-en.svg/index.html、本地 [sqlite] canonical authority 测试因 Node 25.5.0 与要求 22.22.3 不符)在 base/main 上同样失败,与本 PR 无关。
我的整体评价
APPROVE。这个 PR 把"我在哪、下一步是什么、被谁挡住"做成 loopx status 上的一等读出,并且没有拿完成权限去换可读性:它只是派生投影,materiality、验收来源、public safety 各自的 owner 都没被复制;上一轮指出的真实误读(outcome_gap 被当成可达里程碑)在本 head 上已经有可复现的探针守住。剩下的三条都是局部可修的小问题——最值得顺手改的是 :361 那处 attention item 归一化,它会让"我观测到了什么"在两条路径上不一致(P2)——但都不改变这个投影该不该合并的判断,也不引入新的状态或权限。不阻塞合并。
English verdict: APPROVE at 9d04037. The PR adds a read-only Goal artifact-lifecycle projection (phase, milestones with reached, guards with owner/blocked, next transitions) to the status JSON and markdown, with no new durable state and no completion authority; the materiality, acceptance-source and public-safety rules stay with their existing owners. I re-verified the head delta against the previously reviewed 692c1b8f0: reached now uses PROGRESS_DELIVERY_OUTCOMES, so outcome_gap stays visible but unreached (mutation probe: reverting line 129 makes the fixture smoke fail at assert_outcome_gap_is_material_but_not_reached), a conservative _acceptance_supports_closeout gate was added, and the producer-less _declared_milestones was deleted. Validation at this head: 77 passed (test_goal_acceptance_observation.py + test_loopx_turn_executor.py), 97 passed (CLI output budget/differential + goal portfolio), maintainability ratchet unreviewed=0, lifecycle smoke ok. Non-blocking findings: artifact_lifecycle.py:361 normalizes a missing attention item into a truthy dict, so the exported builder reports coverage=partial without attention_queue while the owner attach path reports missing_sources=['attention_queue','agent_vision']/unavailable for the same Goal (P2); _acceptance_supports_closeout can never be True at this head because its only producer hardcodes acceptance_assessed=False, leaving target_phase=closed unreachable (P3); and _TOKEN_SHAPES is a third provider-token rule alongside public_safety's SECRET_LIKE_SURFACE_PATTERN (P3).
Merge-readiness qualification —
|
huangruiteng
left a comment
There was a problem hiding this comment.
动机
APPROVE。 审查 head:4c1f815c4106369b1816768a69bf02170d7f3d34,与当前 main(9719dc0d4)的合并基就是当前 main,合并为快进,无冲突。
这个 PR 给 status 增加了一份只读的 Goal artifact lifecycle 读出(阶段、证据里程碑、门禁、下一步),挂在 run_history.goals[].artifact_lifecycle 并在 Markdown 摘要里渲染,免去 operator 手工拼 Todo/run/quota。上一轮我提出的两点(语义 inventory 陈旧、当时被 CI 卡住的共享 turn-executor 诊断契约)与独立评审 steven-kid 的 P2(把 material history 当成完成证据)都已在本 head 修好,我用独立探针重新验证过,不是只看作者的自测。
改动思路
相对上一轮评审的 head(9d040379a),本 head 只有两个提交 + 一次上游 main 合并,主题收敛:
- 工作事实改为从真实产出者读取:新增
WorkLaneObservation/observe_work_lane(放在 lane 归属模块work_items/work_lane.py)与session_runtime_work_observation适配器(放在 session-runtime 模块),attach_goal_artifact_lifecycle_projections改为读取当前 Goal 自己的session_runtime_projection。旧代码链式查attention/asset/source["work_lane_contract"],而 status 里没有任何生产者在 attention item 顶层写这个键,所以这条 lane 事实上从未到达读出。 - 关闭判定要求验收结论:
_acceptance_supports_closeout只在acceptance_assessed is True且coverage == "complete"且无未观测来源时才给出target_phase: closed;否则留在closing,并在 precondition 中点名未观测到的来源。 - 文档披露:
docs/reference/goal-acceptance-observations.md中英双语补上新读出、工作优先于 Todo 完成、缺少工作观察不凭空产生要求、closing不等于验收通过。
具体改动
关键代码讲解
loopx/control_plane/work_items/work_lane.py:11-35:WorkLaneObservation(frozen dataclass)+observe_work_lane只做字段解码(lane/must_attempt_work/obligation或 fallbacknext_action),must_attempt只对字面True成立,非 mapping 返回None,不修改输入。它位于既有 lane 归属模块,不复制该模块里更窄的策略谓词work_lane_contract_requires_current_agent_attempt(后者只覆盖 monitor 派生义务)。loopx/control_plane/runtime/session_runtime.py:271-288:session_runtime_work_observation只在schema_version匹配且goal_id相等时返回观察,否则None;负例有 3 个参数化用例。loopx/control_plane/goals/artifact_lifecycle.py:179-198, 216-320:阶段由 Goal status、guard kind、work.must_attempt、agent Todoopen_count(必须是 int)与 milestonereached推导;outcome_gap作为未达成里程碑(milestone_unreached);未取得验收结论时target_phase留在closing并带acceptance_unverified。loopx/presentation/renderers/goal_artifact_lifecycle_markdown.py:13-47:只接受本投影 schema,输出一行阶段/里程碑/门禁与一行 next;status_markdown.py接入两行。- 全部 11 文件 +1416/-7 已审查:投影 445 行、renderer 47 行、适配与解码 48 行、接线 4 行、inventory 计数 3+3、文档 17 行、smoke 500 行、测试 355 行。没有新增 CLI、配置、存储或调度面。
独立验证(exact head 4c1f815c4,worktree /private/tmp/pr4360-mr19)
- 强制 lane 贯通:用真实
collect_status(含真实 run index、真实 session-runtime 投影、真实 Markdown)在旧 head 与新 head 跑同一 fixture(投影带must_attempt_work: true、Todo 全完成)。旧 head:item 顶层没有work_lane_contract,读出closing;新 head:qualifying,next_transitions=[{target_phase: qualifying, precondition: "Verify the remaining evidence", reason_codes: [work_lane_selected]}]。这是本轮真实修复,不只是 fixture 对齐。 - gap-only 不再读成收尾:只写一条
delivery_outcome=outcome_gap、无 agent vision、无未完成 Todo →qualifying+milestone_unreached。 - 无验收结论不推荐终态:
outcome_progress且无 agent vision →closing,Markdown 为next: closing (verify the declared acceptance with its existing owner; this readout could not observe agent_vision),不含next: closed。 - 测试与检查:
pytest -q tests/control_plane/test_goal_acceptance_observation.py tests/control_plane/test_goal_artifact_work_observation.py29 passed;tests/test_loopx_turn_executor.py57 passed(上一轮该处 1 failed);投影 smoke ok;scripts/generate_semantic_inventory.py --check在 head 与 head 并入当前 main 后均up to date;gh pr checks 4360全部通过(test-shard 1-4、pytest、merge-gate、kernel-static-checks、stage2c e2e/installed/mutants、postgresql-authority、dashboard-acceptance、windows-powershell)。本地跑整个tests/control_plane得到 158 passed / 1 failed,该失败在干净 main9719dc0d4上完全同样复现(本机 Node v25.5.0 环境问题),不归因于本 PR。
对主干的风险
[P3,结构性缺陷] closed 终态在当前代码里不可达。 _acceptance_supports_closeout(artifact_lifecycle.py:179-198)要求 acceptance_assessed is True 且 coverage == "complete",而唯一的产出者 acceptance_observation.py:211-212 把 acceptance_assessed 固定为 False、coverage 只会是 partial/unavailable,仓库里没有任何测试构造过 True/complete(两个 smoke 只断言 != PHASE_CLOSED)。结果是 target_phase: closed 与 no_open_agent_work 这条分支永远不会执行。这不影响正确性(fail-closed),但等于把"看起来能收尾、实际结构上不能收尾"的能力留在代码里,且未来某个产出者一旦开始写这两个字段就会静默改变行为而没有任何测试绑定。最小修复:删掉 closed 迁移与 _acceptance_supports_closeout(并在文档说明该读出没有终态建议),或给 acceptance owner 增加带产出侧测试的 typed 验收结论。
[P3] "未验证的收尾"只存在于散文里。 precondition 字符串是唯一区分"closing 因为验收未验证"与"closing 因为验收已验证"的地方(artifact_lifecycle.py:300-320),投影不暴露 coverage/missing_sources/acceptance_assessed。机器消费者只能做英文子串匹配,正是本仓 review 契约要求避免的 prose-only 分类。最小修复:随 next_transitions 发布一个紧凑的 typed 字段(例如验收状态或可达的最高阶段),或用 reason code 区分两种 closing。
[P3] 必需工作信号只在 session-runtime adapter 下交付。 工作事实的唯一产出者是 session_runtime_work_observation(session_runtime.py:271-288);其它 adapter 的 Goal 拿不到工作观察,仍可能读出 closing,而 quota/lane owner 认为仍有必须执行的工作。这不是回归(旧查找本来就没产出者),文档也已披露"缺少工作观察不凭空产生要求",但目前这只是产品边界,payload 上不可见。最小修复:在文档页写明哪些 adapter 类型能交付工作观察,或由 status owner 统一投影该事实。
主机侧合并门禁(非代码缺陷,但影响能否落地): mergeStateStatus=BLOCKED、reviewDecision=CHANGES_REQUESTED,来自独立评审在旧 head 155c20760d 上仍未撤回的 review。本结论不替代该评审:需要其在新 head 上重审或撤回,PR 才可能进入合并流程。我不会执行合并。
**残余风险:**本读出是只读、追加式投影,所有写入面(quota/todo/settlement/scheduler)未受影响,输入未被修改(新测试逐字节比对)。最强的缺失观测是"真实多 agent Goal 经打包 Dashboard 渲染"这一条我只在本地 renderer 上验证过,打包面由 CI 的 dashboard-acceptance job 覆盖。
我的整体评价
这是本轮两轮 review 之后收得比较干净的一个 head:上一轮的两个阻塞点(material history 被当作完成证据、共享 turn-executor 诊断契约红灯)都在真实路径上修好了,而且我复核的是"旧 head 真的会漏掉强制 lane"这一前后对照,而不是作者的自测结论;语义 inventory 同步、必需 CI 全绿、与当前 main 快进合并。剩下的三条 P3 都是"能力/合同精度"问题而非行为错误(不可达的终态分支、散文承载的合同、adapter 覆盖面),不构成阻塞。建议按最小修复至少处理第 1 条,因为它同时是死代码和未来行为漂移的风险点。批准,不合并。
English verdict: APPROVE at 4c1f815. Both previous blockers are verified closed on the real collection path: an independent probe shows the old head 9d04037 reads closing for a Goal with a mandatory session-runtime lane while the new head reads qualifying with the lane's own next action, and gap-only material history no longer reads as closing (outcome_gap stays an unreached milestone), while an unassessed acceptance keeps the reader inside closing instead of recommending closed. 29 lifecycle tests pass, the previously failing turn-executor module is green (57 passed), the projection smoke passes, the semantic inventory is up to date at the head and at the merge ref, and every required check is green at 4c1f815. Three non-blocking P3 findings remain: the closed transition is unreachable with any producer (acceptance_assessed is hard-coded False), the unverified-closeout caveat is prose-only, and the required-work signal is delivered only for session-runtime-adapter Goals. Merge additionally stays BLOCKED by the standing CHANGES_REQUESTED from an independent review at the older head 155c207. No merge performed.
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
已处理本轮三项 P3 —
|
huangruiteng
left a comment
There was a problem hiding this comment.
动机
APPROVE。 复审 head:c9efe804d4f2909a83a4926e8ef5faf2d5c343c2(分支已并入当时 origin/main 2e310d768);我上一轮在 4c1f815c4 的复审(review 5212454576)提出 3 条 P3,本 head 全部关闭。
问题依旧是真的:loopx status 以前没有任何"这个 Goal 走到哪一步"的派生读出,operator 只能手工拼 Todo/run/quota,而早期的 head 会在验收没有任何结论的情况下给出终态建议——方向正确但推论越界。本 head 不再补产出方,而是删掉那条够不着的终态分支,并把"未验证验收"降为机器可读字段加按来源的覆盖说明。这正是我上一轮建议的最小修法。
改动思路
- 删除不可达分支:
_acceptance_supports_closeout与_next_transitions里返回target_phase: closed的分支一并删除(loopx/control_plane/goals/artifact_lifecycle.py)。现在唯一的lifecycle_phase: closed来自 Goal 自身已记录的终态(_is_closed),而不是本投影的推断。 - 把提示变成机器可读:
closing的下一步恒为target_phase: closing,reason_codes固定带no_open_agent_work与acceptance_unverified;英文precondition只做解释,消费者读 code。 - 按来源写覆盖边界:
docs/reference/goal-acceptance-observations.md用表格说明哪些 status 来源能提供工作观察、哪些不能,中英同步。
具体改动
关键代码讲解
loopx/control_plane/goals/artifact_lifecycle.py:179_unobserved_acceptance_sources:只把"本读出没读到的验收来源"列进 precondition,不再据此推导完成。loopx/control_plane/goals/artifact_lifecycle.py:196-222_lifecycle_phase:outcome_gap仍是未达成的里程碑(reached=False),因此带着 gap 的 Goal 停在qualifying,不会被读成closing。loopx/control_plane/goals/artifact_lifecycle.py:272-292closing 分支:注释明确写出"v0 读出永不建议终态迁移,未来要加必须另外改契约并验证产出侧",把这条约束钉在代码里而不是只写在文档里。docs/reference/goal-acceptance-observations.md:38-56(中文88-104):never-terminal 规则、acceptance_unverified的读法、按来源的工作观察覆盖表;并说明缺少工作来源时closing或缺少work_lane_selected都不证明工作完成。- 合计 11 文件 +1460/-7(相对当前 main):投影 414 行、renderer 47 行、adapter/decoder 48 行、两份测试 401 行、fixture smoke 498 行、文档与 inventory 计数。
独立验证(exact head c9efe804d,worktree /private/tmp/pr4360-r20)
pytest -q tests/control_plane/test_goal_acceptance_observation.py tests/control_plane/test_goal_artifact_work_observation.py→ 37 passed(我本地在 exact head 重跑)。- 我自己写探针直接调用
build_goal_artifact_lifecycle_projection+append_goal_artifact_lifecycle_markdown,只喂一条 run 和零 open Todo:delivery_outcome=outcome_gap→qualifying,milestones=[(outcome_gap, reached=False)],next_transitions=[(qualifying, [milestone_unreached])],markdownnext: qualifying (...);delivery_outcome=outcome_progress(无 agent vision)→closing,next_transitions=[(closing, [no_open_agent_work, acceptance_unverified])],precondition 追加this readout could not observe agent_vision,markdownnext: closing (...)。- 两条路径都没有
target_phase: closed。这同时覆盖了steven-kid在旧 head 的 P2(gap 被读成 closing 并建议 closed)。
scripts/generate_semantic_inventory.py --check→up to date;投影 smoke 通过;分支已并入当前 main,合并无冲突。
对主干的风险
未发现合并阻塞项。 本轮我只剩残余观察,不构成 REQUEST_CHANGES:
- [P3,观察]
examples/control_plane/goal-artifact-lifecycle-projection-smoke.py498 行,正好压在仓库约 500 行的 smoke 阈值下。它目前确实在断言 shipped 行为(真实 collection + markdown),暂不需要拆分;下次再往里加断言时,应先按仓库惯例把可复用逻辑移进产品模块并收窄断言面。 - [P3,观察] 契约对消费者的要求从"读英文 precondition"变成"读
reason_codes"。这是本 head 有意为之的改进,但旧消费者若把next_transitions当终态信号,需要改读lifecycle_phase与 reason code。文档已写清楚,属于必要的行为变更披露。 - 合并门禁现状(非本 head 的缺陷):GitHub 仍显示
review_decision=CHANGES_REQUESTED,来源是steven-kid在更早 head155c20760的 review5205028401;该 review 的两条 P2(gap 被读成 closing 并建议 closed、test_loopx_turn_executor的 opaque-ref 诊断优先级)在当前 head 上第一条已由删除终态分支解决,第二条在 CI 里已不再复现。这条 stale review 需要其作者复审或由维护者 dismiss 才能解锁合并门,我不会代为处理,也不会执行合并。
我的整体评价
这一版把上一轮的问题修在了正确的位置:不是给"未验证验收"补一个产出方,而是删掉那条够不着的终态分支,并把结论降级成机器可读的 acceptance_unverified 加一份按来源的覆盖表。代码净减、约束写进注释、中英文档与实现一致,我的独立探针与 37 个测试在 exact head 上全部与文档描述相符。本 PR 已并入当前 main,合并无冲突。我同意合入这个 head;但请注意 GitHub 的合并门仍被一条指向旧 head 的 CHANGES_REQUESTED 挡住,需要其作者复审或 dismiss。
English verdict: APPROVE at c9efe80. All three P3 findings from my previous review at 4c1f815 are closed at this head: _acceptance_supports_closeout and the unreachable target_phase: closed branch are deleted (the only lifecycle_phase: closed now comes from the Goal's own recorded terminal status), the acceptance caveat is machine-readable via reason_codes: [no_open_agent_work, acceptance_unverified], and the work-observation coverage is documented by source in English and Chinese. Independently reproduced at the exact head: 37 tests pass, generate_semantic_inventory.py --check reports up to date, the projection smoke passes, and my own probe over build_goal_artifact_lifecycle_projection plus the markdown renderer shows outcome_gap -> qualifying/milestone_unreached, outcome_progress (no agent vision) -> closing with acceptance_unverified, and no path emitting target_phase: closed. That also resolves steven-kid's old-head P2 about a gap being read as closing. Residual risk is only that consumers must switch from English precondition text to reason_codes, and the 498-line projection smoke sits just under the repository's ~500-line threshold - worth watching, not blocking. The GitHub merge gate is still blocked by a stale CHANGES_REQUESTED from steven-kid at commit 155c207, which its author or a maintainer must re-review or dismiss. No merge performed.
合并门禁复核(head
|
Signed-off-by: song <liusongstep@gmail.com>
|
Maintenance update at Merged main GitHub now reports MERGEABLE. CI and reviews must qualify this exact head; earlier approval is not being reused. No merge or force push was performed. |
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com> (cherry picked from commit db58562) Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
…-lifecycle-projection Signed-off-by: song <liusongstep@gmail.com> # Conflicts: # loopx/semantics/inventory_v0.json
a805b2b to
e2a44d4
Compare
|
是每个 PR 目前都需要 agent 去改“loopx/semantics/inventory_v0.json“ 么? 如果是这样的话,感觉对开发者不太友好,看能不能,动态的内容不显式存在仓库里 |
|
已按这个方向调整,改动放在 #4494(Q9 提交: 此前不是每个 PR 都必须修改清单,但新增枚举/常量集合、或同步主干,都可能触发快照过期。这确实给没有语义问题的改动增加了维护成本。现在的实现是:
验证中特别覆盖了“新增文件与未修改文件产生分叉”:报告缺失、过期或损坏都不能绕过检查。186 项定向测试及 15 项 premerge 检查通过,提交前独立复审通过并修复了措辞建议。新提交的项目原生 review 与远端 CI 单独核验,不把本地通过视为已经合并就绪。 当前也在同步新推进的 main;结构快照的修改/删除冲突按 Q9 保持删除,其他主干改动保留,整合版本会重新验证。 |
…-lifecycle-projection Signed-off-by: song <liusongstep@gmail.com> # Conflicts: # loopx/semantics/inventory_v0.json
|
Approval conclusion (author-owned PR; GitHub blocks formal self-approval) 审查 head: 动机RFC #4128 要求 operator 能从一个 Goal 直接读出所处的生命周期阶段、已达成的里程碑、阻塞的 guard 及其 owner、以及下一步动作,而不必从 Todo 计数、quota 状态和最近一次 run 分类里手工拼装。这三件事在改动前没有任何 owner:既不在既有 acceptance observation 的返回里,也不是某个已存字段。改动把它们作为派生投影引入,而不是新增一份需要人工同步的持久状态。 受影响的调用方是读 status 的 operator 和 status markdown 的读者;没有新的执行路径、没有新的权限面。改动前 operator 只能看到碎片,改动后同一份已采集的 payload 会附带一份有界读模型。 改动思路核心设计是只读投影: 四类事实各有其既有 owner,本改动只做读取与命名转换:
与"什么都不做"相比:不做的代价是 operator 继续手工拼装,且 具体改动11 个文件、+1460/−7。生产代码 516 行(含
smoke(+498)与两份测试(+401) — 正反路径覆盖,含真实 对主干的风险未发现阻断项。 以下是我实际反驳过的点及其结果。 只读性成立。 对模块 grep 不存在第二权威。 终止建议不可达(关键反证)。 我按"当作者的测试通过时仍可能为假"的思路构造反例:直接向投影喂入自称验收完成的 observation( 即即使未来的验收产出者开始返回"已完成",v0 也不会开始建议 语义预算未放宽。 一条继承自主干的必需检查失败(非本 PR 引入)。 静态检查无法用于比对。 本地 残余证据边界。 本轮的评测包给出 我的整体评价结论:APPROVE。 证据如下:
方向与仓库既有架构一致:派生状态而非人工同步、复用既有 owner 而非第二权威、把旧协议解码留在其 owner 内。上一轮评审提出的两点( 非阻断建议(不改变结论):
English verdictEnglish verdict: APPROVE — exact head |
…-lifecycle-projection Signed-off-by: song <liusongstep@gmail.com> # Conflicts: # loopx/semantics/inventory_v0.json
aef5b02 to
aee2796
Compare
|
Pushed English — the standing 中文 — 当前 Local evidence at On the |
huangruiteng
left a comment
There was a problem hiding this comment.
动机
在此之前,status 里没有任何一处"这个 Goal 的交付物生命周期走到哪了"的读模型:接受证据(acceptance observation)和 work-lane 契约各自暴露,读者要自己把 phase、里程碑、阻塞守卫和下一步拼出来。后果是把"Todo 全完成 + 历史有进度"误读成"可以收尾"——即便接受证据尚未达到、或 owner gate 还开着。本 PR 增加一个只读派生投影来消除这种拼接。
改动思路
新增 loopx/control_plane/goals/artifact_lifecycle.py,把已有 payload 组合成一个派生读模型:phase(starting/qualifying/waiting_owner/closing/closed)、evidence milestones、guards(owner_decision / evidence_precondition)与 next_transitions。关键的架构选择是"不新建规则引擎":接受规则仍由 build_goal_acceptance_observation 拥有,work 事实的解码放在 lane 自己的 owner(work_lane.observe_work_lane),next_transitions 复用既有 lane/frontier 推导。消费侧只在 status collection 挂载、并在 Markdown 里多打一行。
具体改动
loopx/control_plane/goals/artifact_lifecycle.py(新增 414 行):build_goal_artifact_lifecycle_projection(:294)与attach_goal_artifact_lifecycle_projections(:358),以及 phase/guard 常量;_lifecycle_phase(:196)决定阶段,_next_transitions(:225)复用既有推导。loopx/control_plane/work_items/work_lane.py(+26):新增WorkLaneObservation与observe_work_lane(:14),把 legacy 字段解码留在 lane owner 内,并写明"只读、非调度决定"。loopx/control_plane/runtime/session_runtime.py(+22):session_runtime_work_observation(:271)从 session-runtime 投影里读 work 事实,schema 或 goal_id 不匹配就返回None。loopx/control_plane/status/collection.py(+2)、loopx/presentation/renderers/status_markdown.py(+2)、新渲染器goal_artifact_lifecycle_markdown.py(+47):挂载与展示。examples/control_plane/goal-artifact-lifecycle-projection-smoke.py(+498)、两个测试模块(+233/+168)、docs/reference/goal-acceptance-observations.md(+49)。
关键代码讲解
loopx/control_plane/goals/artifact_lifecycle.py:294—build_goal_artifact_lifecycle_projection:docstring 直接写明"每个输入都是调用方已经收集好的 payload,本函数自己什么都不读、也不授予任何权限",并在_evidence_milestones里刻意不读 goal 自带的里程碑声明,理由写得明白——没有任何生产路径会写入goal.acceptance.milestones,读它等于承诺一个用户声明永远到不了的读数,同时却握着唯一能挡住收尾的守卫。这是我这次最认可的一处克制度:宁可少一个特性,也不留一个永不可达的守卫。loopx/control_plane/goals/artifact_lifecycle.py:196—_lifecycle_phase:终态 goal status 优先 → owner_decision 守卫 → 任意守卫或must_attemptwork → qualifying;特别值得肯定的是open_count缺失时不当作 0,注释写明"被省略/被截断的来源不是没有工作的证据"。这正是把"没有证据"与"证据为零"区分开的正确写法。loopx/control_plane/work_items/work_lane.py:14—observe_work_lane/WorkLaneObservation:把 legacy 字段解码放在 lane owner 模块内,而不是让新消费者各自解析work_lane_contract;dataclass 的 docstring 明确"既不是调度决定,也不是执行授权"。这符合仓库"契约语义跟随 owner"的要求。loopx/presentation/renderers/goal_artifact_lifecycle_markdown.py:10—append_goal_artifact_lifecycle_markdown:schema 不匹配即静默返回;只打印 phase、reached/total 里程碑数、守卫数,并把被阻挡的守卫与 next transition 逐行列出——展示层不重算规则,只渲染派生结果。
对主干的风险
没有阻塞项。 这是纯派生、只读的读数:不写状态、不授予权限、不推荐终态转换(文档明确 closing 是验证步骤,并给出 acceptance_unverified 这个机器可读 reason code,消费者应看它而不是英文 precondition)。文档还显式列出覆盖边界:work 观测只有在上游提供了匹配 schema/goal_id 的 session-runtime 投影时可用,缺失时读数可能显示 closing 但外部 lane 仍有工作,且 lane/完成决定权仍属各自 owner。
P3(非阻断,知识归属):loopx/control_plane/goals/artifact_lifecycle.py:54 的 _TOKEN_SHAPES 用局部正则补充了 provider token 形状(gh?_…/sk-…/AKIA…)。注释解释了它为什么是局部的(共享私密文本规则没覆盖这些前缀),但没留下后续动作,因此在共享 owner(loopx/public_safe_text.py 的 find_private_text_match)里仍然不知道这些形状——我核对过,那两个公共安全模块里确实没有这些前缀定义。仓库规则要求这类补充性启发式既有书面理由、也有类型化后续;最小修复是把这些形状并入共享语料,或在注释里点名要更新的 owner。
验证(在 aee2796d 上跑):python examples/control_plane/goal-artifact-lifecycle-projection-smoke.py → goal-artifact-lifecycle-projection-smoke ok;pytest -q tests/control_plane/test_goal_artifact_work_observation.py tests/control_plane/test_goal_acceptance_observation.py → 37 passed;git merge-tree --write-tree HEAD origin/main 干净(head 本身就是把 upstream/main 合进来的 merge commit)。按本 lane 配置不拉取 CI;我没有跑真实浏览器的 dashboard acceptance smoke,这一层属残余证据缺口。
我的整体评价
APPROVE。这个 PR 的价值不在"多一个字段",而在把生命周期读数做成派生而非第二份权威:接受规则、lane 解码、公共安全都留在各自 owner,读模型只做组合与呈现,文档还把"不推荐终态、缺失观测不等于没有工作"这两条最容易被误读的边界写成明文。我特别认可两处细节——刻意不读没有生产者的 goal 里程碑(避免永不可达的守卫),以及 open_count 缺失时不推断为 0(避免把截断当成完成)。近 1500 行看起来不小,但构成是 414 行投影 + 401 行测试 + 498 行缺陷复现型 fixture smoke + 文档,属于可复审的完整包;唯一想提醒的是 smoke 已接近仓库 500 行的复审线,继续增长时应把 fixture 移入测试模块、让 smoke 退回薄渲染检查。剩下一条 P3 是 token 形状知识的归属,不影响正确性。
English verdict: APPROVE — exact head aee2796d74dcb537e5197d91d3ddd733f8a9f100 of #4360. The PR adds a read-only, derived Goal artifact lifecycle projection (phase, evidence milestones, guards, next transitions) that composes the existing acceptance observation and work-lane owners instead of creating a second rule engine: build_goal_artifact_lifecycle_projection reads nothing itself, _lifecycle_phase refuses to treat an omitted open_count as "no work remains", goal-declared milestones are deliberately not read because no producer writes them, and observe_work_lane keeps legacy field decoding next to the lane owner. Validation at this head: examples/control_plane/goal-artifact-lifecycle-projection-smoke.py prints ok, tests/control_plane/test_goal_artifact_work_observation.py plus test_goal_acceptance_observation.py pass 37 tests, and the branch merges cleanly with current main. No blocking findings; the docs state the readout never recommends a terminal transition and that a missing work observation does not prove completion. One non-blocking P3: the local _TOKEN_SHAPES secret-prefix regex supplements the shared public-safety corpus without recording a follow-up to move those shapes into loopx/public_safe_text.py.
Behavior
Adds the RFC #4128 Goal artifact lifecycle readout to status JSON and Markdown: observed phase, evidence milestones, owner/evidence guards, and next steps. Observed required work takes precedence over inferred closeout, including when completed Todos and historical progress would otherwise suggest
closing.The projection is read-only and grants no execution or completion authority. It remains distinct from the bounded acceptance-observation projection.
Implementation
WorkLaneObservationfrom existing work-lane and session-runtime owners. Legacy field decoding stays in those owners. Status reads the current Goal's nestedsession_runtime_projection, validates its schema and Goal identity, and uses the observation before display trimming. There is no new direct legacy-field reader or increased retirement budget.outcome_gapremains an unreached marker and cannot satisfy closeout. Batch size cannot promote a non-progress outcome.closingremains a verification step carrying the existing machine-readableacceptance_unverifiedreason code. An already-terminal Goal still displaysclosedwith no next steps. A future acceptance producer change cannot silently add terminal advice.Bounded refactor: separates a three-field immutable work read model from legacy protocol decoding; no new production module, persistent store, scheduling rule, or provider is introduced.
Validation
Tested revision:
d70419e42583c3ac287e4ce690638f27cbfe0affRun state: finished
d8c8988b35a40d7f9784462967d3b14391d6ce75, based on main675a9e50b: 467 Python tests plus 187 DSH plugin tests pass; Ruff, kernel mypy, plugin typecheck/build/artifact smoke, docs governance and lifecycle smoke pass.Affected entry points remain status JSON/Markdown. Session-runtime observation coverage is bounded by the supplied schema and Goal id; missing work observations do not certify completion. No new acceptance authority, permission, settings field or packaged lifecycle UI is introduced. The three semantic siblings are coordinated as #4457 → #4459 → #4480; this feature and #4482 remain independent. All source heads were composed and tested together, but no PR has been merged into main. Fresh remote CI/review remains required.