Skip to content

fix(goal-frontier): diagnose missing final-outcome claims - #4341

Merged
huangruiteng merged 7 commits into
loopx-project:mainfrom
LIHUA919:codex/4331-final-outcome-claim
Sep 15, 2026
Merged

huangruiteng merged 7 commits into
loopx-project:mainfrom
LIHUA919:codex/4331-final-outcome-claim

Conversation

@LIHUA919

@LIHUA919 LIHUA919 commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Summary

A satisfied, fresh material checkpoint with a valid continuation and evidence refs now reports final_outcome_claim_missing when only acceptance_summary is absent. The source projection supplies passed/failed components and an actionable correction to CLI, managed Turn, Goal acceptance UI, and Lark rows. Completion, evidence, identity, and quota gates remain unchanged.

The recovery hint explicitly preserves the existing durable-field gate: retain the route/evidence while recording the claim correction with path_delta.outcome=replan. The public CLI regression exercises repeated same-Turn diagnosis without quota spend and the actual accepted correction.

The latest merge refresh resolves only generated frontend conflicts against a55702c25; the automatically merged Turn-envelope and CLI-renderer source paths were also revalidated: rebuild from combined source, retain the current main asset generation, and preserve the original commits. The 203-test suite, TypeScript typecheck and 12 Turn-envelope tests, packaged browser acceptance, CLI output-budget differential, Lark reconciliation/kanban and PWA bundle smokes were rerun successfully.

Issue Or Task

Closes #4331. #4336 is outside this PR.

Validation

  • Tested revision: 0ede73b55f9d2928bf58b6e0560cd1e5a97a12b6, merging current main a55702c25 while retaining all original commits and previous merge refreshes.
  • Run state: finished
  • Input classes: synthetic, public_fixture
Check kind Result Public-safe evidence / limitation
regression_parity passed Missing-claim test failed before the fix; focused succession, checkpoint, reentry, writeback, status/CLI projection and Turn suites pass (203 tests).
real_entrypoint passed tests/control_plane/test_final_outcome_diagnostics.py: public CLI same-Turn retries preserve obligation identity/quota; real refresh_state_run accepts the documented correction.
integration passed npm run typecheck:control-plane; 12 TypeScript Turn-envelope tests; npm run build.
real_entrypoint passed Dashboard smoke:goal-acceptance-browser and smoke:goal-acceptance-packaged: actual collected synthetic state, Chinese desktop and English mobile, missing/invalid source recovery.
integration passed CLI output-budget, quota replan decision-plane, Lark projection reconciliation/kanban and PWA bundle smokes. No real Lark message was sent.
static passed Ruff on changed Python surfaces, diff checks, and premerge public-boundary scan.
integration failed Initial implementation check (before the merge refresh): loopx canary premerge --from-git-diff, 17/18 selected checks passed plus 4 direct checks. issue-fix-validated-memory-writeback-smoke.py fails with provider_sync_success_readback_absent; reproduced unchanged on clean base 6b337bcbd.
static failed Initial implementation check: explicit-path mypy traversed imports with 966 errors in 135 files on both 6b337bcbd and 7d408cea0; no added normalized diagnostics. This broad check was not rerun for the generated-asset merge refresh.

Coverage and gaps: guarded completion and the diagnosed correction are exercised through real local CLI/writeback and TypeScript transport using disposable state. The initial OpenViking smoke and mypy failures are disclosed above; full premerge was not rerun for the merge refresh. The conflict-resolution validation set covers combined source behavior and actual packaged assets; new-head remote CI and independent review remain required. No storage/provider behavior changed, and no production backend or live Lark delivery was exercised.

Future-facing pass: the existing outcome-continuity owner now derives qualification and explanation from the same component facts. Existing TypeScript Turn transport forwards the bounded diagnostic; no second decision authority or new capability/configuration was introduced.

Frontend / Visual Evidence

UI impact: changed, within the existing Goal details acceptance card. Source data: synthetic. Screenshots show the initial implementation at 7d408cea0; the same diagnostic card was revalidated through packaged-browser acceptance after the merge. These are read-only offline fixture views; the execution-service unavailable banner is expected. Packaged frontend and previous shipped asset retention are included.

View Before After
Chinese desktop Before desktop After desktop
English mobile Before mobile After mobile

Type of Change

  • Bug fix
  • Documentation update
  • Test update

LoopX Area

  • Control plane (goals, todos, quota, scheduler, registry, runtime)
  • Public docs or presentation surface
  • Host or runtime integration

Technical Direction

Shared-authority RFC fixture impact

N/A; no authority-store, runtime-routing or migration claim.

Boundary Checklist

Signed-off-by: Lihua <1017343802@qq.com>
Signed-off-by: Lihua <1017343802@qq.com>
huangruiteng
huangruiteng previously approved these changes Sep 15, 2026

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

队列前言(loopx pr-review --state open --limit 200complete=truepolicy_revision=3other-developers-first:49 个 open PR 中 28 个需要处理。本卡覆盖 rank 1 的 #4341——社区贡献者 @LIHUA919,tier 1,review_depth=runtime_behavior_review,需要全部 18 项证据。下一张待办是 rank 2 的 #4374

结论先行:在这个 exact head 上通过。 这是一次「把已经算出来但被丢弃的事实变成类型化诊断,并让 UI/Lark 与既有 frontier 判定对齐」的改动;判定本身(replan_required、obligation 身份、quota、状态)一律未动,我用真实 CLI 与真实打包前端逐条验证过。

动机

base 上 acceptance_gaps_from_outcome_checkpoint 用一条六项合取判断检查点是否完整,任何一项不满足都只返同一句 untyped 文案("a material milestone closed without a fresh evidence-linked final-outcome path decision")。这意味着 stalesatisfied=false、路径决策非法、证据引用缺失、已报告 outcome_gap、以及唯一真正需要「补一句 claim」这六种情况对操作者完全不可区分——而其中最便宜的一种修法(恢复 bounded acceptance_summary)恰恰被这句通用文案淹没了。同时 Goal acceptance 观测根本不消费 checkpoint 来源,所以个人工作台与 Lark 行里连这条 gap 都不显示。

同一个 disposable fixture 的实测对比说明了代价:base 上 loopx statusacceptance_observation.acceptance_gaps0 条vision_continuation_audit 里也没有任何 diagnostics;head 上同一个 fixture 给出 reason_code=final_outcome_claim_missing、六项 component_checks(其中 final_outcome_claim_present=false)、以及指名 vision_patch.acceptance_summary 的 bounded 修复提示。而 replan_required 在两边都是 true——也就是说 base 早就在阻塞这个 Goal,只是没告诉操作者该修哪一项。

更小的修法我都排除过:只改通用文案无法恢复「六项里哪一项失败」这一信息;只在 UI 修则 CLI、managed Turn capsule 与 Lark 仍然 untyped;新建一个诊断模块会让同一条规则出现第二个 owner。正确的最小修法是在已经持有这六个布尔值的 owner 内部完成分类,并把三个派生字段透传出去。

改动思路

入口没有变化:loopx quota should-run(JSON 与 markdown)、loopx status 里的 attach_goal_acceptance_observations、managed Turn envelope,以及 Lark 的 projection_rows_from_payload。权威输入仍是 run history 里已经存在的 per-agent outcome checkpoint 与最新 agent vision;决策 owner 仍是 acceptance_gaps_from_outcome_checkpoint(检查点完整性)与 select_goal_frontier_replan_rule(是否存在 replan 义务),本 PR 两者都没有换人。新增的 reason_codecomponent_checksresolution_hint 全部是读时派生的投影数据,不落盘、不成为新真值,因此不存在「第二份状态与 canonical 事实漂移」的风险。

正向路径:run history → latest_outcome_vision_checkpoint_from_status_payload(复用既有取用函数)→ 分类器算出 failed_components → 只有恰好失败 final_outcome_claim_present 时才给 final_outcome_claim_missing,否则给 final_outcome_checkpoint_incomplete → frontier rule 照旧选中 VISION_ACCEPTANCE_GAPoutcome_checkpoint_diagnostics 进入 compact CLI audit、markdown、Turn capsule、Lark 行,以及 Goal acceptance 卡片。负向路径:逐一改坏其它五项中的任意一项,必须落到 incomplete(测试参数化覆盖五种),空白 claim 必须判为缺失、恢复 claim 必须让 gap 整体消失,且 peer agent 不能拿到该行。

所有权代价上有一处值得记录:六项 component key 现在被复述了四遍(Python 分类器、TS capsule 过滤数组、dashboard zod schema、卡片 label map),这是本卡唯一的架构级观察,属于 P3,原因见下节。

具体改动

23 个文件 +544/−25,分类清楚:runtime/presentation 8 个文件 127 行(分类器 +38/−9、Lark 行 +27、CLI/status/TS capsule 各自数行透传)、测试 +345/−1(新增 303 行诊断测试 + 两个既有测试模块补充)、协议文档 +29、dashboard schema +9/−1 与组件 +14/−1、browser smoke +5/−2、生成的打包资产(bundle、asset-retention.jsonindex.html、4 张合成截图)。没有任何文件新增持久化字段、命令行开关或权限边界。

关键代码讲解

  1. acceptance_gaps_from_outcome_checkpointloopx/control_plane/goals/goal_frontier/outcome_continuity.py:209):把原来那条六项合取改写成先建 component_checks、再取 failed_components。关键不变量是空结果条件完全等价not failed_components ≡ 旧的六项全真),所以 base 上"完整"的检查点在 head 不可能凭空多出一条 gap;claim_only_missingfailed_components == ["final_outcome_claim_present"] 精确等值,而不是"包含 claim 失败",这正是防误判的核心。
  2. latest_outcome_vision_checkpoint_from_status_payload(同文件 :108):本次被复用而非重写。它优先读 semantic_history.agents[].latest_outcome_vision_checkpoint_run、否则回退 latest_runs,并要求 trigger 为 material_delivery_outcome、agent 身份匹配。这一点很关键——UI 侧能否拿到检查点取决于 status payload 的拼装是否保留了 run history,我没有假设它成立,而是用真实 loopx status 验证了取到值。
  3. attach_goal_acceptance_observationsacceptance_observation.py:225):新增把 checkpoint gap 追加进观测,并把三个新字段透传进行;source 也从硬编码改为取 gap 自身。实测 head 上该行的 kind=vision_outcome_checkpoint_requiredsource=latest_vision_checkpoint,base 上同一 fixture 一行都没有。
  4. build_vision_continuation_auditloopx/control_plane/goals/goal_frontier/__init__.py:443):新增 outcome_checkpoint_diagnostics(要求三个字段齐全,上限 5 条),并把 recommended_action 在"所有 compact gap 都带 hint"时替换为 hint 拼接。我特别核过 all(...) 的空列表陷阱:该分支的 rule 选择要求 acceptance_gap_count > 0,而 acceptance_gap_count 就是同一个列表长度,因此空列表不可达,不会出现 recommended_action 变空字符串。
  5. contractCapsuleloopx/control_plane/quota/turn_envelope.ts:478):capsule 侧按字段截断(reason_code 80、resolution_hint 420)并只保留 boolean 的 component 项,避免把结构化对象 stringify 进紧凑 capsule;diagnostics 为空时完全不改 capsule,所以老 payload 行为不变。

UI 侧 goal-acceptance-observation-card.tsx 新增 hint 与六项 pass/fail 渲染,并用打包态浏览器 smoke 在中英文两种视口断言到 最终成果声明齐全: 未通过 / Final outcome claim present: Failed路径决策有效: 通过;我另外确认打包 bundle 里确实含这两条新字符串、index.html 指向新 bundle、旧 bundle 仍保留在 retention 清单里。

非阻塞观察(不影响通过):

  • (P3)六项 component key 在四处各写一遍:分类器、turn_envelope.ts 的过滤数组、zod schema、卡片 label map。加第七项时 capsule 与 dashboard 会静默丢弃(zod 默认 strip 未知键),而不是报错;建议导出单一 key 列表或改成布尔透传。
  • (P3)同一组件内混用两套 i18n 机制:新增的六项标签用 locale === "zh-CN" ? {...} : {...} 硬编码,其余文案仍走 t("acceptance.*")。该 feature 下确实有 inline 双语先例,所以不阻塞,但新文案今后改动必须动代码。
  • (P3)acceptance_observation.py{"run_history": {"goals": [goal]}} 把已持有的 goal 包成合成 payload 再喂给取用函数。复用既有 owner 优于另写查询,但若在 checkpoint 模块补一个 goal 级入口,这个包装可以去掉。

对主干的风险

最强回归场景是误判:把一个实际因 stale/unsatisfied/无证据/路径非法/已报 outcome_gap 而失败的检查点标成 final_outcome_claim_missing,操作者于是只补一句 claim,guard 看起来被满足而证据依然陈旧或缺失。触发条件正是「其它项也失败 + claim 缺失」这一组合,防护是精确等值判定(要求失败项恰好只有 claim),并有参数化负例逐项覆盖五种变体、断言失败项数量为 2 且 reason 必须为 incomplete。影响面限于派生投影:不写状态、不动 gate、不耗 quota,错了也只是误导性文案,没有状态污染需要迁移;回滚就是 revert,最小修复只需改分类器一处判定。

一个必须点明的证据边界:作者测试既覆盖分类器也覆盖真实 CLI,但它们都基于作者构造的 fixture;若某个消费端静默丢字段,这些测试仍会通过。因此我补了作者测试覆盖不到的真实路径——真实 loopx status 的拼装(历史上 goal 合并基准是 history goal,最容易在这里丢 run history)与真实打包前端的浏览器断言,两者都通过。未验证的维度是 Lark 真实投递与 development-mode 的 vite smoke(我跑的是 packaged 路径,因为本 PR 改的正是打包 bundle)。CI 在该 head 为 25 success + 5 skipped、0 失败;PR 当前 mergeStateStatus=DIRTY(与 main 有冲突),合并前需要作者先解决冲突并重跑合并门禁。

我的整体评价

可观测语义是明确且已验证的有意变化,不是漂移:我按五个消费面做了 base/head 对照——acceptance_observation 从 0 条变 1 条带类型诊断的行(有意变化)、replan_required 两边都为 true(等价)、audit 从无 diagnostics 变为一条(有意)、recommended_action 从通用文案变为 bounded hint(有意)、obligation 身份与状态文件逐字节不变(等价)。体量与问题相称:没有新命令、新持久字段、新模块或新 gate,runtime 侧只有 127 行且集中在既有 owner 与既有透传点。三条 P3 都是可维护性观察,不构成阻塞;鉴于作者是社区贡献者且这是该 PR 的首轮通过结论,我建议在合并时把「六项 key 四处复述」记为后续 follow-up。合并仍需 --check-merge-readiness 且需先解决 DIRTY 冲突,本评论不构成合并授权。

English verdict: APPROVE at exact head 0ede73b — the checkpoint owner now classifies which of its six component checks failed (final_outcome_claim_missing only when the claim is the single failure, else final_outcome_checkpoint_incomplete), and the read-only diagnosis reaches the CLI audit, managed Turn capsule, quota markdown, Lark rows, and the Goal acceptance card without changing the guard, obligation identity, quota, or stored state. Evidence on this head: 55 focused tests pass, Ruff is clean, the packaged browser smoke passes against the rebuilt bundle (which really contains the new zh/en labels), and my own base/head probes on the same disposable fixture show base replan_required=true with no diagnostics and zero acceptance gaps versus head's typed diagnostic with the six component checks and a byte-identical state file and run history. Three P3 observations only: the six component keys are restated in four places (a seventh would be silently dropped by the capsule filter and the zod schema), the card mixes inline bilingual maps with the shared t() catalog, and the observation wraps its goal in a synthetic status payload to reuse the checkpoint lookup. Merge is still blocked on conflict resolution (mergeStateStatus=DIRTY), so this approval is not merge authority.

Resolve the packaged-chat conflict by rebuilding from the merged sources.
`asset-retention.json`, `index.html`, and the hashed entry bundle are all
vite-generated, so the conflict is resolved by regenerating them instead of
merging minified output.

`npm run build:chat` produced a single new entry bundle and a retention manifest
whose second generation is the bundle currently referenced by main, which keeps
the rolling-upgrade window intact. Validation on the merged tree: 55 focused
tests, `dashboard-pwa-bundle-smoke`, and the packaged Goal acceptance browser
smoke all pass, and the rebuilt bundle still carries the new bilingual UI copy.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>

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

队列前言(loopx pr-review --state open --limit 200complete=truepolicy_revision=3other-developers-first:本卡是 #4341 在新 head a2d49206e 上的重审。上一轮我在旧 head 0ede73b55 给出的通过结论因分支更新被 GitHub dismiss(review_not_bound_to_current_head),符合契约;本轮全部证据都在新 head 上重新取得。下一张待办是 rank 2 的 #4376

结论先行:在新 head 上通过,且冲突已由维护者解决。 分支此前 DIRTY,冲突全在 vite 生成的打包 chat 产物里;我按「合并源码后重新构建」的方式解决并推回作者分支,没有手改压缩产物。

动机

base 上 acceptance_gaps_from_outcome_checkpoint 用一条六项合取判断检查点是否完整,任何一项不满足都只返同一句 untyped 文案,于是 stalesatisfied=false、路径决策非法、证据引用缺失、已报告 outcome_gap、以及唯一真正「只需补一句 claim」这六种情况对操作者完全不可区分;其中最便宜的修法恰好被通用文案淹没。同时 Goal acceptance 观测根本不消费 checkpoint 来源,个人工作台与 Lark 行里连这条 gap 都不显示。同一 fixture 的实测:base 的 acceptance_observation.acceptance_gaps 是 0 条、audit 无 diagnostics;head 给出 final_outcome_claim_missing、六项 component_checksfinal_outcome_claim_present=false)与指名 vision_patch.acceptance_summary 的受限提示,而 replan_required 两边都是 true——base 早就在阻塞这个 Goal,只是没告诉操作者该修哪一项。

改动思路

入口不变(quota JSON/markdown、status 的 Goal acceptance 观测、managed Turn envelope、Lark 投影行)。权威输入仍是 run history 里既有的 per-agent outcome checkpoint 与最新 agent vision;决策 owner 仍是 acceptance_gaps_from_outcome_checkpoint(检查点完整性)和 select_goal_frontier_replan_rule(replan 义务),本 PR 没有换人。新增的 reason_code / component_checks / resolution_hint 全部是读时派生投影,不落盘、不构成新真值,因此不存在第二份状态与 canonical 事实漂移的风险。

正向路径:run history → latest_outcome_vision_checkpoint_from_status_payload(复用既有取用函数)→ 分类器算出 failed_components → 仅当恰好失败 final_outcome_claim_present 时才给 final_outcome_claim_missing,否则 final_outcome_checkpoint_incomplete → frontier rule 照旧选中 VISION_ACCEPTANCE_GAP → diagnostics 进入 compact CLI audit、markdown、Turn capsule、Lark 行与 Goal acceptance 卡片。负向路径:逐一改坏其它五项必须落到 incomplete,空白 claim 必须判缺失、恢复 claim 必须让 gap 整体消失,peer agent 不得拿到该行。

具体改动

作者侧 23 个文件 +544/−25(runtime/presentation 8 文件 127 行、测试 +345、协议文档 +29、dashboard schema 与卡片、browser smoke、生成资产)。本 head 额外多一个维护者合并提交:解决 loopx/web/chat 的冲突。

关键代码讲解

  1. acceptance_gaps_from_outcome_checkpointloopx/control_plane/goals/goal_frontier/outcome_continuity.py:209):先建 component_checks 再取 failed_components;关键不变量是空结果条件与旧六项合取完全等价,所以 base 上完整的检查点不可能凭空多出 gap;claim_only_missing 用精确等值(失败项恰好只有 claim)而非「包含 claim 失败」,这是防误判的核心。
  2. latest_outcome_vision_checkpoint_from_status_payload(同文件 :108):本轮复用而非重写,优先 semantic_history 槽位、否则回退 latest_runs,并要求 trigger 为 material_delivery_outcome、agent 身份匹配。UI 侧能否取到检查点取决于 status payload 是否保留了 run history,我用真实 loopx status 验证取到了值。
  3. attach_goal_acceptance_observationsacceptance_observation.py:225):新增把 checkpoint gap 追加进观测并透传三个新字段,source 不再硬编码。实测 head 该行 kind=vision_outcome_checkpoint_requiredsource=latest_vision_checkpoint,base 同 fixture 一行都没有。
  4. build_vision_continuation_auditgoal_frontier/__init__.py:443):新增 outcome_checkpoint_diagnostics(三字段齐全、上限 5 条),recommended_action 在「所有 compact gap 都带 hint」时改用 hint。我核过 all(...) 的空列表陷阱:该分支要求 acceptance_gap_count > 0,而它就是该列表长度,所以空列表不可达,不会把 recommended_action 变成空串。
  5. contractCapsuleloopx/control_plane/quota/turn_envelope.ts:478):按字段截断(reason_code 80、resolution_hint 420)且只保留 boolean 的 component 项,避免把结构化对象 stringify 进 capsule;diagnostics 为空时完全不改 capsule。

冲突解决方式(本 head 新增部分)

冲突文件只有三个,且都是构建产物:loopx/web/chat/asset-retention.jsonloopx/web/chat/index.html,以及入口 bundle 的 rename/rename(main 侧 index-DscLlBrK.js vs 本 PR 侧 index-cPucsEps.js)。asset-retention.jsonindex.html 都由 apps/presentation/dashboard/vite.chat.config.ts 的插件在构建时写出,所以我先取 main 侧的整个 loopx/web/chat 作为构建基线(让「上一代线上 bundle」被正确识别),再执行 npm run build:chat(含 tsc --noEmit)重新生成三件产物。结果是一个新入口 assets/index-qvBcoVSz.jsasset-retention.json 恰好两代:[新, index-DscLlBrK.js],即保留 main 当前正在服务的那一代用于滚动升级,且清单内无缺失文件。合并后的 bundle 仍含本 PR 的中英双语新文案。

非阻塞观察(与上一轮相同,不影响通过):六项 component key 在四处各写一遍(新增第七项会被 capsule 过滤与 zod schema 静默丢弃);卡片内同文件混用 inline 双语映射与 t();观测层把 goal 包成合成 payload 复用取用函数。三条 P3。

对主干的风险

最强回归仍是误判:把实际因 stale/unsatisfied/无证据/路径非法/已报 outcome_gap 失败的检查点标成 final_outcome_claim_missing,操作者于是只补一句 claim。防护是精确等值判定加参数化负例(五种变体,断言失败项数为 2 且 reason 必须 incomplete)。影响面限于派生投影:不写状态、不动 gate、不耗 quota,错了也只是误导性文案,回滚即 revert,最小修复只需改分类器一处。作者测试覆盖不到的地方我补了真实路径:真实 loopx status 的拼装、真实打包前端浏览器断言,两者在合并后的树上都通过;dashboard-pwa-bundle-smoke 也通过,确认资产三元组一致、无缺失文件。未验证维度是 Lark 真实投递与 development-mode vite smoke。

我的整体评价

可观测语义是明确且已验证的有意变化:五面 base/head 对照中,acceptance 观测由 0 条变 1 条带类型诊断的行(有意)、replan_required 两边皆 true(等价)、audit 从无到有(有意)、recommended_action 从通用文案变受限提示(有意)、obligation 身份与状态逐字节不变(等价);本 head 再增加一行「打包资产三元组」的有意变化。体量相称:无新命令、无新持久字段、无新模块、无新 gate,合并提交只碰生成产物。三条 P3 属可维护性观察,不阻塞。冲突已解决且校验通过,本卡在新 head 上给通过结论。

English verdict: APPROVE at exact head a2d4920 — the branch is now conflict-free, and the conflict was resolved the only correct way for vite-generated assets: merge the sources, then rebuild the chat bundle. npm run build:chat produced one new entry asset (index-qvBcoVSz.js) and a retention manifest whose second generation is exactly the bundle main currently serves, with no missing files and both new bilingual UI labels present in the rebuilt bundle. Validation re-run on this exact merged tree: 55 focused tests pass, dashboard-pwa-bundle-smoke ok, and the packaged Goal acceptance browser smoke passes. The semantic review is unchanged from the previous head because the source-side merge was automatic; the three P3 observations (four restatements of the six component keys, inline bilingual labels beside the shared t() catalog, and the synthetic status payload wrapper) remain non-blocking.

Refresh the branch onto current main so the required up-to-date status check
can run. The packaged chat bundle is vite-generated, so it is regenerated from
the merged sources rather than merged as minified output.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng
huangruiteng force-pushed the codex/4331-final-outcome-claim branch from 9060808 to 3bf602c Compare September 15, 2026 07:58

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

队列前言(loopx pr-review --state open --limit 200complete=truepolicy_revision=3other-developers-first:本卡是 #4341 在 head 3bf602cd2 上的重审。此前两轮的通过结论分别因 head 前移与 GitHub 对分支的自动更新而失效(review_not_bound_to_current_head),符合契约;本轮全部证据再次在当前 head 上重新取得。下一张待办是 rank 2 的 #4376

结论先行:在当前 head 上通过。 分支既无冲突、也不再缺 DCO 签署;本轮除语义复核外,还证明了打包 chat 产物在当前树上可复现(npm run build:chatloopx/web/chat 无 diff,与 CI 的 dashboard-acceptance 判定同一条不变量)。

动机

base 上 acceptance_gaps_from_outcome_checkpoint 用一条六项合取判断检查点是否完整,任何一项不满足都只返同一句 untyped 文案,于是 stalesatisfied=false、路径决策非法、证据引用缺失、已报告 outcome_gap、以及唯一真正「只需补一句 claim」这六种情况对操作者完全不可区分;其中最便宜的修法恰好被通用文案淹没。同时 Goal acceptance 观测根本不消费 checkpoint 来源,个人工作台与 Lark 行里连这条 gap 都不显示。同一 fixture 的实测:base 的 acceptance_observation.acceptance_gaps 是 0 条、audit 无 diagnostics;head 给出 final_outcome_claim_missing、六项 component_checksfinal_outcome_claim_present=false)与指名 vision_patch.acceptance_summary 的受限提示,而 replan_required 两边都是 true——base 早就在阻塞这个 Goal,只是没告诉操作者该修哪一项。

改动思路

入口不变(quota JSON/markdown、status 的 Goal acceptance 观测、managed Turn envelope、Lark 投影行)。权威输入仍是 run history 里既有的 per-agent outcome checkpoint 与最新 agent vision;决策 owner 仍是 acceptance_gaps_from_outcome_checkpoint(检查点完整性)和 select_goal_frontier_replan_rule(replan 义务),本 PR 没有换人。新增的 reason_code / component_checks / resolution_hint 全部是读时派生投影,不落盘、不构成新真值,因此不存在第二份状态与 canonical 事实漂移的风险。

正向路径:run history → latest_outcome_vision_checkpoint_from_status_payload(复用既有取用函数)→ 分类器算出 failed_components → 仅当恰好失败 final_outcome_claim_present 时才给 final_outcome_claim_missing,否则 final_outcome_checkpoint_incomplete → frontier rule 照旧选中 VISION_ACCEPTANCE_GAP → diagnostics 进入 compact CLI audit、markdown、Turn capsule、Lark 行与 Goal acceptance 卡片。负向路径:逐一改坏其它五项必须落到 incomplete,空白 claim 必须判缺失、恢复 claim 必须让 gap 整体消失,peer agent 不得拿到该行。

具体改动

相对当前 base 共 25 个文件 +670/−152:runtime/presentation 8 个文件 127 行、测试 +345、协议文档 +29、dashboard schema 与卡片、browser smoke、4 张合成截图,以及由本仓库自身 vite 插件生成的打包 chat 面。−152 里绝大多数是生成产物:新入口 bundle 取代 base 当前那一代,于是 diff 同时显示被替代的两个上一代资产文件。相对作者原始改动,本分支只多出维护者侧的合并提交。

关键代码讲解

  1. acceptance_gaps_from_outcome_checkpointloopx/control_plane/goals/goal_frontier/outcome_continuity.py:209):先建 component_checks 再取 failed_components;关键不变量是空结果条件与旧六项合取完全等价,所以 base 上完整的检查点不可能凭空多出 gap;claim_only_missing 用精确等值(失败项恰好只有 claim)而非「包含 claim 失败」,这是防误判的核心。
  2. latest_outcome_vision_checkpoint_from_status_payload(同文件 :108):本轮复用而非重写,优先 semantic_history 槽位、否则回退 latest_runs,并要求 trigger 为 material_delivery_outcome、agent 身份匹配。UI 侧能否取到检查点取决于 status payload 是否保留了 run history,我用真实 loopx status 验证取到了值。
  3. attach_goal_acceptance_observationsacceptance_observation.py:225):新增把 checkpoint gap 追加进观测并透传三个新字段,source 不再硬编码。实测 head 该行 kind=vision_outcome_checkpoint_requiredsource=latest_vision_checkpoint,base 同 fixture 一行都没有。
  4. build_vision_continuation_auditgoal_frontier/__init__.py:443):新增 outcome_checkpoint_diagnostics(三字段齐全、上限 5 条),recommended_action 在「所有 compact gap 都带 hint」时改用 hint。我核过 all(...) 的空列表陷阱:该分支要求 acceptance_gap_count > 0,而它就是该列表长度,所以空列表不可达,不会把 recommended_action 变成空串。
  5. contractCapsuleloopx/control_plane/quota/turn_envelope.ts:478):按字段截断(reason_code 80、resolution_hint 420)且只保留 boolean 的 component 项,避免把结构化对象 stringify 进 capsule;diagnostics 为空时完全不改 capsule。

冲突与 head 刷新(本 head 前的维护者动作)

冲突文件只有三个,且都是构建产物:loopx/web/chat/asset-retention.jsonloopx/web/chat/index.html,以及入口 bundle 的 rename/rename。两者都由 apps/presentation/dashboard/vite.chat.config.ts 的插件在构建时写出,所以我先取 main 侧的整个 loopx/web/chat 作为构建基线(让「上一代线上 bundle」被正确识别),再执行 npm run build:chat(含 tsc --noEmit)重新生成产物:新入口 assets/index-qvBcoVSz.jsasset-retention.json 恰好两代 [新, index-DscLlBrK.js],即保留 main 当前正在服务的那一代用于滚动升级,清单内无缺失文件。插件本身只保留 2 代,这一点是仓库既有语义,不是我手工挑选的窗口。

随后 main 前移,GitHub 的 update-branch 生成了 head 9060808210,该提交没有 Signed-off-by trailer,Sign-off 检查因此失败。修法是在本地重做同一次 main 合并并用 git commit -s 签署(3bf602cd2),再把分支强推到作者的 codex/4331-final-outcome-claim。该合并提交的树与 9060808210/此前已评审的 a2d49206e 逐字节相同(tree 4cd2cf27),所以源码语义没有任何新变化。

非阻塞观察(与上一轮相同,不影响通过):六项 component key 在四处各写一遍(新增第七项会被 capsule 过滤与 zod schema 静默丢弃);卡片内同文件混用 inline 双语映射与 t();观测层把 goal 包成合成 payload 复用取用函数。三条 P3。

对主干的风险

最强回归仍是误判:把实际因 stale/unsatisfied/无证据/路径非法/已报 outcome_gap 失败的检查点标成 final_outcome_claim_missing,操作者于是只补一句 claim。防护是精确等值判定加参数化负例(五种变体,断言失败项数为 2 且 reason 必须 incomplete)。影响面限于派生投影:不写状态、不动 gate、不耗 quota,错了也只是误导性文案,回滚即 revert,最小修复只需改分类器一处。作者测试覆盖不到的地方我补了真实路径:真实 loopx status 的拼装、真实打包前端浏览器断言,两者在当前树上都通过;dashboard-pwa-bundle-smoke 也通过,确认资产三元组一致、无缺失文件。生成产物本身的风险由「重建必须无 diff」这条 CI 不变量兜住,我在当前树上复跑过同一条命令。未验证维度是 Lark 真实投递与 development-mode vite smoke。

我的整体评价

可观测语义是明确且已验证的有意变化:五面 base/head 对照中,acceptance 观测由 0 条变 1 条带类型诊断的行(有意)、replan_required 两边皆 true(等价)、audit 从无到有(有意)、recommended_action 从通用文案变受限提示(有意)、obligation 身份与状态逐字节不变(等价);打包资产三元组的变化是生成产物取代上一代(有意)。体量相称:无新命令、无新持久字段、无新模块、无新 gate,维护者提交只碰生成产物与其签署。三条 P3 属可维护性观察,不阻塞。分支无冲突、DCO 通过、验证在当前 head 上复跑通过,本卡给通过结论。

English verdict: APPROVE at exact head 3bf602c — the branch is conflict-free, DCO-signed, and refreshed onto current main. The conflict was resolved the only correct way for vite-generated assets: merge the sources, then rebuild the chat bundle, which produced one new entry asset (index-qvBcoVSz.js) and a retention manifest whose second generation is exactly the bundle main currently serves, with no missing files. The reviewed tree is byte-identical (tree 4cd2cf27) to the previously audited merge commit a2d49206e, so the semantic pass carries over, and every executable check was re-established here: 55 focused tests pass, dashboard-pwa-bundle-smoke ok, the packaged Goal acceptance browser smoke passes, and a clean npm run build:chat re-run leaves loopx/web/chat unchanged. The three P3 observations (four restatements of the six component keys, inline bilingual labels beside the shared t() catalog, and the synthetic status payload wrapper) remain non-blocking.

@huangruiteng
huangruiteng merged commit 6bb4131 into loopx-project:main Sep 15, 2026
26 checks passed
@huangruiteng

Copy link
Copy Markdown
Collaborator

合并决策记录(exact head 3bf602cd2

决策:合并。 合并提交 6bb413105Merge pull request #4341 from LIHUA919/codex/4331-final-outcome-claim),第二父提交即本次评审并验证的 head 3bf602cd2c52d357bae5b2bb663705e4e6b4a1ee。维护者侧的冲突解决与 DCO 修复都留在该 head 上,没有改变作者源码语义。

变更面goal_frontier 的 outcome-checkpoint 分类器与其 diagnostics 投影、Goal acceptance 观测、quota compact CLI 与 markdown 渲染、managed Turn envelope capsule、Lark 投影行、dashboard 数据 schema 与验收卡片、协议文档、browser smoke、三个测试模块,以及由仓库自身 vite 插件重建的打包 chat 产物(index.htmlasset-retention.json、入口 bundle)。

已跑检查

  • 分支 required checks:Sign-offmerge-gate 均通过;PR 上其余 31 项检查全部成功(desktop/release/pypi 类 job 按路径条件 skip)。
  • pytest tests/control_plane/test_final_outcome_diagnostics.py tests/control_plane/test_goal_acceptance_observation.py tests/control_plane/test_goal_vision_succession.py -q → 55 passed。
  • examples/dashboard-pwa-bundle-smoke.py → ok(打包资产三元组一致、清单无缺失文件)。
  • 打包路径的真实浏览器验收 smoke(Goal acceptance,packaged 模式)→ passed。
  • npm run build:chat 在合并后的树上重跑后 loopx/web/chat 无 diff,与 CI 的 packaged-dashboard 判定是同一条不变量;重建结果把「上一代线上 bundle」保留为清单第二世代,滚动升级窗口完整。
  • loopx canary premerge --from-git-diff → tier standard,selected 18 / executed 18 / failures 0,含 public/private boundary 扫描与 git diff --check

失败、跳过与人工保留

  • main push 上的 Full Public Smokes 失败与本次变更面无关:失败项为 goal-actions 顶层命令分类、install-local-smoke 的 skill 物化、PostgreSQL workflow 的 node 版本断言与 local-install 晋升边界,且同一形状的失败在合并前的 main 提交(1dc6ad8d8)上已经存在。
  • 未本地验证:Lark 真实投递,以及 development-mode 的 vite smoke。
  • 非阻塞观察(P3,不影响通过):六项 component key 在四处重复枚举、卡片内混用 inline 双语映射与 t()、观测层把 goal 包成合成 payload 复用取件函数。

门禁说明:GitHub 规则在 main 上要求 require_last_push_approval,而分支最后一次推送者与审批者是同一账号,因此 reviewDecision 无法进入 APPROVED,普通合并会被规则拦下。合并前已取得该 head 的有效 exact-head 评审结论(APPROVE),并按仓库所有者的显式授权使用规则集里的 owner bypass 完成合并;bypass 只用于这一条推送审批要求,没有用它替代评审或验证证据。

English summary: merged at exact head 3bf602cd2 (merge commit 6bb413105) after an exact-head APPROVE and green required checks. Validation on the merged tree: 55 focused tests, dashboard-pwa-bundle-smoke, the packaged Goal acceptance browser smoke, a reproducible npm run build:chat run, and loopx canary premerge (18/18, including the public/private boundary scan). The Full Public Smokes failure on main is pre-existing and unrelated to this change surface; Lark live delivery and the development-mode vite smoke were not run locally. The owner ruleset bypass was used only for require_last_push_approval, which cannot be satisfied when the last pusher is the approver.

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.

goal-frontier: distinguish missing final-outcome claim from missing path decision

2 participants