Skip to content

test(turn): preserve recovery after repeated unknown readbacks - #7580

Merged
huangruiteng merged 1 commit into
mainfrom
fix/turn-writeback-prepare-crash-block
Oct 29, 2026
Merged

huangruiteng merged 1 commit into
mainfrom
fix/turn-writeback-prepare-crash-block

Conversation

@hhyykk

@hhyykk hhyykk commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Prepared settlement effects must remain recoverable when provider readback becomes available after repeated unknown outcomes. This revision extends the existing File-journal/provider-ledger regression across durable writeback, quota spend and terminal closeout: an initial unknown hold and an explicit failed-Turn retry must leave the effect intent and committed providers unchanged, then committed/absent readback must finish the original Turn and replay without another Host call or duplicate debit.

The earlier failed+prepared guard is removed from the proposal. Main already returns effect_outcome_unknown without executing the unresolved effect; repeated explicit calls are not an in-process infinite loop. Blocking before provider readback prevented legitimate later recovery. No new production retry policy, journal format, resolver API or identity is introduced. The prior 140-line fixture is replaced by a small extension of the existing three-effect test.

Validation at c9d0fde1ec681ad768cfdd262b8b3a6ae4168986, based on target main 652f503842609e948e8ff832ae3977f23f79f28d:

  • Python settlement recovery, recovery state machine and journal characterization: 26 passed.
  • Three TypeScript journal modules: 73 passed.
  • Reapplying the former guard to the current typed owner makes the updated regression fail with TurnRecoveryBlockedError (one expected counterexample); restoring the unchanged owner passes.
  • Ruff, diff hygiene and quick premerge passed (4 direct checks and changed-Python compile; no catalog canaries were selected for this test-only scope).
  • The original unsigned commit is replaced by this author-signed commit; every commit in the revised PR has DCO sign-off.

This is now a regression-test proposal, not a new runtime fix or a claim of complete host/backend qualification. The tests use real File journal IO and the existing TS reducer/Python interpreter with a synthetic provider ledger; they do not establish live-provider exactly-once behavior.

中文:撤回会永久挡住恢复的生产 guard。复用既有三类效果测试,核对反复 unknown 时不重复执行、原 intent 不丢失、后续 committed/absent 可按原身份恢复。当前运行时规则不变,签名已补齐,合并仍待维护者复审。

@mergify

mergify Bot commented Oct 28, 2026

Copy link
Copy Markdown

Hi @hhyykk, the DCO Sign-off check did not pass. Please inspect
its details first: checkout, fetch, timeout or infrastructure errors
need their own recovery, not a rewrite of otherwise signed commits.

If the log confirms a missing Signed-off-by trailer, amend the
affected commit with git commit --amend -s; for multiple commits,
use an interactive rebase against the current base from the correct
base-repository remote and sign off each affected commit. Push the
rewritten PR branch with git push --force-with-lease origin HEAD.

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

动机

失败 Turn 的恢复调用者会在第一次 prepared readback 得到 unknown、稍后 provider 可返回 committed/absent 时遇到该问题。原来显式重试可以核对原 effect 并完成结算;本 head 在调用 resolver 前拒绝,导致既有工作无法继续。提案意图是停止无结果的重复重试;交付仍需保留 later-known 恢复并补 DCO。完整跨版本产品资格及长期成本继续沿既有任务验收。本片不需要新建 reconciliation 状态库或更换 Turn 身份。

要求修改。发现两项阻断:新 guard 把暂时未知的 prepared effect 变成无法经原 resolver 恢复的永久拒绝;本 PR 唯一 commit 还缺少仓库要求的 DCO sign-off。避免未知副作用被盲目重复是正确目标,但失败 journal 不足以证明外部 readback 此后始终不可用。

依据 docs/reference/protocols/loopx-turn-v0.md,spec_revision 1a7234c2afcc3fd7897b63d5bab78d3e6a9f7959,criterion_id Dangling prepared effect:dangling prepared effect 继续由原 provider readback protocol 负责,recovery decision 记录下一步需要 readback,不能代替 provider 判定实际结果。

改动思路

原路径先保留 prepared intent,再由 resolve_prepared 查询原 effect:unknown 保留并失败,committed 读取既有结果,absent 才允许原身份执行未发生的效果。提案在 typed journal owner 内增加 failed+prepared 的无条件 blocked。放置 owner 本身合理,但 guard 跨过了真正拥有事实的 resolver;最小修复应在原 readback/retry 边界区分仍 unknown 和已经恢复,而不是对所有 failed journal 禁止进入。无需另建 reconciliation 状态库,也不应通过清空 journal 或换 Turn 身份绕过。

具体改动

完整审阅 2db2754a50bbe5b9a7325fc83f6a210ece99623e 两文件 154 增/0 删,其中生产改动14行,新增测试140行。

关键代码讲解

  • recoveryDecision(turn_journal.ts:537)在 preparedEffectCheck 后仅凭 journalStatus === "failed" 返回 blocked/host_outcome_unknown,因此 Python driver 在读取传入的 writeback/spend/terminal resolver 前就抛 TurnRecoveryBlockedError。这是新 P2:先一次 unknown,稍后 resolver 返回 committed 或 absent,也无法恢复原工作。
  • execute_turn_driver_settlement(settlement.py:536)原本让 provider owner 提供 prepared effect 的 readback;run_loopx_turn_once 的 recovery assessment 在它之前执行。新测试只覆盖永远没有 resolver 的负例,没有验证正常恢复至有用工作这一相反方向。

[P2] 保留 resolver 恢复失败 prepared effect 的入口。 独立执行仓库已有 tests/test_loopx_turn_settlement_recovery.py,不可变 base 为18通过;同命令在本 head 为7通过/11失败。其中9项覆盖 durable_writeback、quota_spend、terminal_closeout 的 before_effect/after_effect/before_checkpoint:第一次 unknown 后提交真实 File journal,第二次提供 committed/absent resolver,本 head 在537行拒绝,resolver 根本没有机会核对;另外两项已有无效 completion 读回测试也回归。请保留 unknown 时不重做效果的性质,同时恢复显式 retry 后原 resolver 的 committed/absent 路径;重跑该模块,核对原 Host 一次、已提交 effect 不重做、未提交效果恰好执行以及原 quota 一次。

[P2] 补齐贡献者自己的 DCO sign-off。 git log -1 --format=full 2db2754a50bbe5b9a7325fc83f6a210ece99623e 的完整 commit message 只有 Co-Authored-By,没有 Signed-off-by。仓库要求每个 PR commit 使用 git commit -s;请原作者修复 trailer 并推送新 head,再重新验证。不能由 reviewer 代签。

独立检查中,作者新增状态机/characterization 共9通过,两个 journal TS 模块44通过,Ruff及标准 premerge 的5 direct/10 selected 通过。它们没有覆盖上述相反方向,不能抵消18→7/11的真实恢复回归;严格 change-quality 记录保持阻断。没有查询或等待 CI。

对主干的风险

这是共享恢复决策,无可选开关,所有保持 prepared intent 的失败 journal 都可能受影响;并不限于一次 Host dispatch unknown 或某个业务领域。Host outcome 实际已经可知时继续显示未知,还会把合法用户指向错误的恢复对象。保留现有 journal、original effect identity 及 quota 回执,不通过替换事务或手工删 intent 修测试。当前未运行真实远端模型或线上恢复,不把合成 provider ledger 当安装采用证据;但 File journal、TS reducer、Python executor 和 provider readback 的集成失败是真实执行结果。完整跨版本产品资格及长期成本仍另行验收。

我的整体评价

English verdict: REQUEST_CHANGES — exact head 2db2754a50bbe5b9a7325fc83f6a210ece99623e。Dangling prepared effect 当前未满足:局部负例改善引入了正常恢复回归。建议在同一 owner 内修 guard、补上 unknown→committed/absent 的正向路径,并补 DCO 后提交新版本。现有绿色局部套件和小 diff 都不足以交付这个语义变化;这里没有新增人工审批或要求完成整个父级重构。

本次检查现有 CLI、原生 capture 与恢复入口,没有渲染页面改动;无需截图。

Reviewer: actor_kind=model_agent; model=gpt-6.1-sol; provider=OpenAI; declaration_source=runtime_reported; reasoning_effort=max

// returned unknown and the outcome cannot be determined without external
// reconciliation. Block rather than loop on successive retries; the
// in_progress path above still returns continue for the initial recovery.
if (preparedCheck && journalStatus === "failed") {

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.

[P2] failed + prepared 不能替代当前 provider 的 readback。先一次 unknown,之后显式 retry 已提供 committed/absent resolver 时,这里在 resolver 被调用前就抛 TurnRecoveryBlockedError;原 tests/test_loopx_turn_settlement_recovery.py 在 base 18通过,在此 head 7通过/11失败,涉及三种 settlement effect。请让原 resolver 仍可核对同一 effect;unknown 保持不重复执行,已提交的读回、确实 absent 的才继续原身份结算。

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed at c9d0fde1ec681ad768cfdd262b8b3a6ae4168986 by withdrawing the blanket failed+prepared guard. The current typed settlement owner already returns effect_outcome_unknown without redoing the unresolved effect; no new retry policy is needed. The revised PR is regression-only: the existing three-effect File-journal test now covers repeated unknown holds, then committed/absent recovery with original intent, one Host call and one debit. The former guard makes the updated case fail when reapplied to the current owner. Validation: 26 Python and 73 TypeScript tests passed; Ruff and quick premerge passed. The unsigned original commit was replaced by this author-signed commit. The description and title now reflect the narrower proposal.

Signed-off-by: aihop <4408344+hhyykk@users.noreply.github.com>
@hhyykk
hhyykk force-pushed the fix/turn-writeback-prepare-crash-block branch from 2db2754 to c9d0fde Compare October 29, 2026 03:57
@hhyykk hhyykk changed the title fix(turn): block recovery on failed journal with unresolved prepared effect test(turn): preserve recovery after repeated unknown readbacks Oct 29, 2026
xiaoan17 added a commit to xiaoan17/loopx that referenced this pull request Oct 29, 2026
The C4.3 settlement obligation comparison listed a candidate slice:
vocabulary unification. It was conditional on a question that had not been
answered: do guard, lifecycle_completion and validation describe one
obligation? Guessing it would have produced either a wrong merge or a
wrong refusal to merge.

The answer is no, so the candidate is withdrawn.

- validation is not an executable effect in either runtime.
  TurnProviderStepKind excludes it, require_turn_provider_step_kind
  raises "validation is not a provider effect step", the reducer throws
  "unsupported base settlement step validation" if asked to run it, and
  the registry's own value note records that it is the one step the
  dispatcher never executes and carries no command template.
- guard and lifecycle_completion are executed steps: the adapter runs
  them through run_cli with a continue_when that decides whether the rest
  of the plan runs.
- The two value sets differ exactly where the question is asked. guard and
  lifecycle_completion occur only in A, validation only in B, and the
  repository advisory does not list them as a merge candidate.
- lifecycle_completion has no counterpart in B.
- One shipped caller already uses both vocabularies for one admission
  decision: governed_capability_execution.py classifies the admission
  snapshot with A's classify_host_guard_snapshot and then hands B a
  completed_phases that already contains validation.

guard and validation name one obligation seen from two positions, so
renaming them would hide that asymmetry and break B's "a phase is never
executed" invariant.

The entry also records what the confirmation leaves open: the vocabulary
that does exist has no typed owner, its literal is written twice (an
unexported STEP_KINDS and a private _STEP_KINDS), and the repository's
inventory sees neither carrier, so the registry's drift smoke cannot
cover it. That is a separate slice and must first reconcile loopx-project#3412,
loopx-project#5677, loopx-project#7580 and loopx-project#7523.

The derived RFC status index is regenerated, because it counts ledger
entries per RFC and the new entry makes the checked-in index stale.

No code, schema, wire, registry or default change.

Validation: mkdocs build --strict for the docs site and both developer
books; RFC bilingual check passed; docs-governance smoke passed; loopx
canary premerge --from-git-diff green.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
xiaoan17 added a commit to xiaoan17/loopx that referenced this pull request Oct 29, 2026
The C4.3 settlement obligation comparison listed a candidate slice:
vocabulary unification. It was conditional on a question that had not been
answered: do guard, lifecycle_completion and validation describe one
obligation? Guessing it would have produced either a wrong merge or a
wrong refusal to merge.

The answer is no, so the candidate is withdrawn.

- validation is not an executable effect in either runtime.
  TurnProviderStepKind excludes it, require_turn_provider_step_kind
  raises "validation is not a provider effect step", the reducer throws
  "unsupported base settlement step validation" if asked to run it, and
  the registry's own value note records that it is the one step the
  dispatcher never executes and carries no command template.
- guard and lifecycle_completion are executed steps: the adapter runs
  them through run_cli with a continue_when that decides whether the rest
  of the plan runs.
- The two value sets differ exactly where the question is asked. guard and
  lifecycle_completion occur only in A, validation only in B, and the
  repository advisory does not list them as a merge candidate.
- lifecycle_completion has no counterpart in B.
- One shipped caller already uses both vocabularies for one admission
  decision: governed_capability_execution.py classifies the admission
  snapshot with A's classify_host_guard_snapshot and then hands B a
  completed_phases that already contains validation.

guard and validation name one obligation seen from two positions, so
renaming them would hide that asymmetry and break B's "a phase is never
executed" invariant.

The entry also records what the confirmation leaves open: the vocabulary
that does exist has no typed owner, its literal is written twice (an
unexported STEP_KINDS and a private _STEP_KINDS), and the repository's
inventory sees neither carrier, so the registry's drift smoke cannot
cover it. That is a separate slice and must first reconcile loopx-project#3412,
loopx-project#5677, loopx-project#7580 and loopx-project#7523.

The derived RFC status index is regenerated, because it counts ledger
entries per RFC and the new entry makes the checked-in index stale.

No code, schema, wire, registry or default change.

Validation: mkdocs build --strict for the docs site and both developer
books; RFC bilingual check passed; docs-governance smoke passed; loopx
canary premerge --from-git-diff green.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>

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

Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · reasoning_effort=max

APPROVE — c9d0fde1ec681ad768cfdd262b8b3a6ae4168986。未发现阻断项。 原先挡住后续恢复的生产 guard 已撤回,当前是既有集成测试的一次小扩展,DCO 也已补齐。以下结论仅针对当前完整提案,不追认旧 head。

动机 / Motivation

失败 Turn 的显式恢复调用者与维护恢复测试的贡献者。

同一效果第一次读回 unknown 后,调用者再显式重试;本测试新增 retry_failed=false/true 两次等待,然后沿原身份取得 committed/absent 并完成结算。

实测两次 unknown 均不重做效果、不重调 Host、不丢 prepared intent,随后完成原三步并可原样重放。

失败后的 unknown 表示暂时无法确认效果,并不表示 provider 以后永远不能提供证据。维护者需要同时防止盲目重复执行和合法恢复被永久挡住。新增的固定两次显式调用不是生产进程内重试循环;没有重新引入另一套 retry policy。

本 PR 仅增强回归测试,不改变生产重试策略、journal 格式、Host 准入或结算身份;不认证真实外部 provider 的恰好一次及长期收益。

改动思路 / Approach

沿用原测试的三个有序效果:durable_writeback、quota_spend、terminal_closeout,以及 before/after effect 和 before/after checkpoint 四种中断。真实 File journal、TypeScript reducer 与 Python executor 保持原 owner;Host 和 provider ledger 是合成边界。unknown 时比较原 intent、提交列表和 Host 调用次数;之后由原 resolver 区分 committed 与 absent,只有真正缺失的效果可以执行。完成后仍验证三个效果各一次、原阶段完整以及 replay 不重复调用。

旧提案的 failed+prepared 无条件拒绝和另起的大 fixture 都没有进入当前 diff。比新增 reconciliation 状态或另造执行身份更小的方案,就是在现有跨模块测试中保护这条既有路径;当前一个文件的改动与这个目的相称。

具体改动 / Changes

完整 base 652f503842609e948e8ff832ae3977f23f79f28d 到 head 为 1 个测试文件,+9/-6,生产代码 0 行。

规范依据:spec_ref docs/reference/protocols/loopx-turn-v0.md,spec_revision 652f503842609e948e8ff832ae3977f23f79f28d。该段没有正式条款号,prepared-effect-readback 是本评审映射标签:dangling prepared effect 的事实仍由现有 provider readback protocol 决定,recovery decision 只能要求下一次 readback。当前 disposition 为 implemented,由原测试的 unknown→committed/absent 和 replay 路径验证;没有将未来通用 exactly-once 提升为本片义务。

关键内容讲解

  • test_file_journal_recovers_each_provider_boundary 复用原十二组输入,仅将单次 unknown hold 扩为 retry_failed 两值,新增 failed 状态及 Host 保全断言。before_hold 与持久化 effect_attempts 的比较保留,避免只看返回码。
  • 同一测试后半段仍通过 load_loopx_turn_plan_from_journal 恢复原 plan;真实执行器调用原 resolver,并核对 commits、Host 次数、完成阶段和 exact replay。没有用 provider 的幂等返回掩盖重复调用:ledger 在重复提交时直接断言失败。

上轮两项意见分别处理:错误生产 guard 已撤回;当前唯一 commit c9d0fde1ec681ad768cfdd262b8b3a6ae4168986 含作者自己的 Signed-off-by。相关现有覆盖包含底层 journal/reducer 和恢复状态机;本片复用跨 File IO 与 Python/TS 边界的 fixture,没有再复制一套同形 smoke。同作者的进程停止测试处理另一个不变量,不能替代这里的 provider 结算恢复。

对主干的风险 / Risks

独立检查 结果
settlement recovery、recovery state machine、journal characterization 三个完整 Python 模块 PASS:26 passed,零 skip
三个完整 journal TS 模块 PASS:73 passed,零 skip
不可变 base 的原 settlement recovery 模块 PASS:18 passed
隔离 source 重放原 failed+prepared guard 预期 FAIL:当前 before_effect/durable_writeback 用例抛 TurnRecoveryBlockedError;仅恢复原 owner 后同用例 PASS
原生 premerge PASS:4 direct + changed-Python compile,0 catalog 选项,0 hold
当前测试 Ruff、diff hygiene、作者 DCO PASS

反例使用真实公共 run_loopx_turn_once、File journal 和 Node/TS owner;仅在私有副本恢复旧 guard,没有改评审分支。它证明本测试能捕获已知恢复回归,不证明所有 provider 都实现了正确 readback。初始中断、两次未知、later-known 和 replay 都有独立断言,保留了只阻断盲目执行、允许可靠证据恢复的两个方向。没有查询或等待远端 CI。精确 change-quality receipt cqr_39d7a4f6e13e8a9a99c5 为 pass,并须独立读回有效性。

我的整体评价 / Verdict

本次有界测试任务为 goal_achieved;long_horizon 与 user_experience 均 preserved:生产路径与 base 相同,测试保护后续有用恢复,未新增用户步骤、状态或等待义务。维护成本是既有参数化测试内一个固定长度循环;兼容层没有变化,也无需新的 helper 或后继任务。

当前唯一差异为测试文件;检查的生产入口是 run_loopx_turn_once,没有渲染页面变化。 未验证范围:真实外部 provider、安装采用和长期稳定性;这些不作为本次测试增量的交付声明。批准仍与旧评审 closeout、当前 branch policy 和维护者合并权限分开,本文不执行合并或删除既有讨论。

English verdict: APPROVE — c9d0fde. The revised test preserves repeated-unknown holds and later evidence-based recovery without changing production behavior.

xiaoan17 added a commit to xiaoan17/loopx that referenced this pull request Oct 29, 2026
The C4.3 settlement obligation comparison listed a candidate slice:
vocabulary unification. It was conditional on a question that had not been
answered: do guard, lifecycle_completion and validation describe one
obligation? Guessing it would have produced either a wrong merge or a
wrong refusal to merge.

The answer is no, so the candidate is withdrawn.

- validation is not an executable effect in either runtime.
  TurnProviderStepKind excludes it, require_turn_provider_step_kind
  raises "validation is not a provider effect step", the reducer throws
  "unsupported base settlement step validation" if asked to run it, and
  the registry's own value note records that it is the one step the
  dispatcher never executes and carries no command template.
- guard and lifecycle_completion are executed steps: the adapter runs
  them through run_cli with a continue_when that decides whether the rest
  of the plan runs.
- The two value sets differ exactly where the question is asked. guard and
  lifecycle_completion occur only in A, validation only in B, and the
  repository advisory does not list them as a merge candidate.
- lifecycle_completion has no counterpart in B.
- One shipped caller already uses both vocabularies for one admission
  decision: governed_capability_execution.py classifies the admission
  snapshot with A's classify_host_guard_snapshot and then hands B a
  completed_phases that already contains validation.

guard and validation name one obligation seen from two positions, so
renaming them would hide that asymmetry and break B's "a phase is never
executed" invariant.

The entry also records what the confirmation leaves open: the vocabulary
that does exist has no typed owner, its literal is written twice (an
unexported STEP_KINDS and a private _STEP_KINDS), and the repository's
inventory sees neither carrier, so the registry's drift smoke cannot
cover it. That is a separate slice and must first reconcile loopx-project#3412,
loopx-project#5677, loopx-project#7580 and loopx-project#7523.

The derived RFC status index is regenerated, because it counts ledger
entries per RFC and the new entry makes the checked-in index stale.

No code, schema, wire, registry or default change.

Validation: mkdocs build --strict for the docs site and both developer
books; RFC bilingual check passed; docs-governance smoke passed; loopx
canary premerge --from-git-diff green.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
@huangruiteng
huangruiteng merged commit c944214 into main Oct 29, 2026
26 of 35 checks passed
@huangruiteng
huangruiteng deleted the fix/turn-writeback-prepare-crash-block branch October 29, 2026 09:01
xiaoan17 pushed a commit to xiaoan17/loopx that referenced this pull request Oct 29, 2026
The C4.3 settlement obligation comparison listed a candidate slice:
vocabulary unification. It was conditional on a question that had not been
answered: do guard, lifecycle_completion and validation describe one
obligation? Guessing it would have produced either a wrong merge or a
wrong refusal to merge.

The answer is no, so the candidate is withdrawn.

- validation is not an executable effect in either runtime.
  TurnProviderStepKind excludes it, require_turn_provider_step_kind
  raises "validation is not a provider effect step", the reducer throws
  "unsupported base settlement step validation" if asked to run it, and
  the registry's own value note records that it is the one step the
  dispatcher never executes and carries no command template.
- guard and lifecycle_completion are executed steps: the adapter runs
  them through run_cli with a continue_when that decides whether the rest
  of the plan runs.
- The two value sets differ exactly where the question is asked. guard and
  lifecycle_completion occur only in A, validation only in B, and the
  repository advisory does not list them as a merge candidate.
- lifecycle_completion has no counterpart in B.
- One shipped caller already uses both vocabularies for one admission
  decision: governed_capability_execution.py classifies the admission
  snapshot with A's classify_host_guard_snapshot and then hands B a
  completed_phases that already contains validation.

guard and validation name one obligation seen from two positions, so
renaming them would hide that asymmetry and break B's "a phase is never
executed" invariant.

The entry also records what the confirmation leaves open: the vocabulary
that does exist has no typed owner, its literal is written twice (an
unexported STEP_KINDS and a private _STEP_KINDS), and the repository's
inventory sees neither carrier, so the registry's drift smoke cannot
cover it. That is a separate slice and must first reconcile loopx-project#3412,
loopx-project#5677, loopx-project#7580 and loopx-project#7523.

The derived RFC status index is regenerated, because it counts ledger
entries per RFC and the new entry makes the checked-in index stale.

No code, schema, wire, registry or default change.

Validation: mkdocs build --strict for the docs site and both developer
books; RFC bilingual check passed; docs-governance smoke passed; loopx
canary premerge --from-git-diff green.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
xiaoan17 pushed a commit to xiaoan17/loopx that referenced this pull request Oct 29, 2026
The C4.3 settlement obligation comparison listed a candidate slice:
vocabulary unification. It was conditional on a question that had not been
answered: do guard, lifecycle_completion and validation describe one
obligation? Guessing it would have produced either a wrong merge or a
wrong refusal to merge.

The answer is no, so the candidate is withdrawn.

- validation is not an executable effect in either runtime.
  TurnProviderStepKind excludes it, require_turn_provider_step_kind
  raises "validation is not a provider effect step", the reducer throws
  "unsupported base settlement step validation" if asked to run it, and
  the registry's own value note records that it is the one step the
  dispatcher never executes and carries no command template.
- guard and lifecycle_completion are executed steps: the adapter runs
  them through run_cli with a continue_when that decides whether the rest
  of the plan runs.
- The two value sets differ exactly where the question is asked. guard and
  lifecycle_completion occur only in A, validation only in B, and the
  repository advisory does not list them as a merge candidate.
- lifecycle_completion has no counterpart in B.
- One shipped caller already uses both vocabularies for one admission
  decision: governed_capability_execution.py classifies the admission
  snapshot with A's classify_host_guard_snapshot and then hands B a
  completed_phases that already contains validation.

guard and validation name one obligation seen from two positions, so
renaming them would hide that asymmetry and break B's "a phase is never
executed" invariant.

The entry also records what the confirmation leaves open: the vocabulary
that does exist has no typed owner, its literal is written twice (an
unexported STEP_KINDS and a private _STEP_KINDS), and the repository's
inventory sees neither carrier, so the registry's drift smoke cannot
cover it. That is a separate slice and must first reconcile loopx-project#3412,
loopx-project#5677, loopx-project#7580 and loopx-project#7523.

The derived RFC status index is regenerated, because it counts ledger
entries per RFC and the new entry makes the checked-in index stale.

No code, schema, wire, registry or default change.

Validation: mkdocs build --strict for the docs site and both developer
books; RFC bilingual check passed; docs-governance smoke passed; loopx
canary premerge --from-git-diff green.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
huangruiteng pushed a commit that referenced this pull request Oct 29, 2026
The C4.3 settlement obligation comparison listed a candidate slice:
vocabulary unification. It was conditional on a question that had not been
answered: do guard, lifecycle_completion and validation describe one
obligation? Guessing it would have produced either a wrong merge or a
wrong refusal to merge.

The answer is no, so the candidate is withdrawn.

- validation is not an executable effect in either runtime.
  TurnProviderStepKind excludes it, require_turn_provider_step_kind
  raises "validation is not a provider effect step", the reducer throws
  "unsupported base settlement step validation" if asked to run it, and
  the registry's own value note records that it is the one step the
  dispatcher never executes and carries no command template.
- guard and lifecycle_completion are executed steps: the adapter runs
  them through run_cli with a continue_when that decides whether the rest
  of the plan runs.
- The two value sets differ exactly where the question is asked. guard and
  lifecycle_completion occur only in A, validation only in B, and the
  repository advisory does not list them as a merge candidate.
- lifecycle_completion has no counterpart in B.
- One shipped caller already uses both vocabularies for one admission
  decision: governed_capability_execution.py classifies the admission
  snapshot with A's classify_host_guard_snapshot and then hands B a
  completed_phases that already contains validation.

guard and validation name one obligation seen from two positions, so
renaming them would hide that asymmetry and break B's "a phase is never
executed" invariant.

The entry also records what the confirmation leaves open: the vocabulary
that does exist has no typed owner, its literal is written twice (an
unexported STEP_KINDS and a private _STEP_KINDS), and the repository's
inventory sees neither carrier, so the registry's drift smoke cannot
cover it. That is a separate slice and must first reconcile #3412,
#5677, #7580 and #7523.

The derived RFC status index is regenerated, because it counts ledger
entries per RFC and the new entry makes the checked-in index stale.

No code, schema, wire, registry or default change.

Validation: mkdocs build --strict for the docs site and both developer
books; RFC bilingual check passed; docs-governance smoke passed; loopx
canary premerge --from-git-diff green.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants