From 2f5cbb19440749776a1009a4f0532d39bc6be428 Mon Sep 17 00:00:00 2001 From: xiaoan17 Date: Thu, 29 Oct 2026 00:49:30 +0800 Subject: [PATCH 1/4] docs(rfc): compare C4.3 settlement obligations before deletion C4.3 asked for an obligation-by-obligation comparison of host_adapter_settlement.py, turn_driver/host_todo_completion.ts and turn_driver/settlement.py before removing a supposedly duplicated condition/step interpreter. No such comparison was recorded, so the next PR had to guess. Reading the three owners at e19d94ca0 shows the two settlement paths are not competing policy owners: both delegate every decision to a TypeScript owner, and Python is transport in both. The step vocabularies overlap only on durable_writeback, quota_spend and terminal_closeout; A's guard and lifecycle_completion have no counterpart, and B's validation is a phase marker it refuses to execute. The decisive difference is recovery. A is single-shot with no journal; B persists a checkpoint per provider and replays receipts. At the shipped capability call site the two already compose, so deleting A into B would first require giving the MCP host a journal it does not have. The only Python-side interpreter is _condition_matches over seven declarative kinds plus _decode_provider_steps order validation; moving evaluation into TypeScript needs one runtime call per step, which the RFC says is not automatically an improvement. Records the evidence, the preconditions a deletion would need, and the overlapping PRs to reconcile. No code, schema, wire or default changes. Validation: RFC bilingual check passed; loopx canary premerge --tier quick green; public boundary scan clean. Signed-off-by: xiaoan17 --- ...9-c4-3-settlement-obligation-comparison.md | 109 ++++++++++++++++++ ...-settlement-obligation-comparison.zh-CN.md | 98 ++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md create mode 100644 docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md diff --git a/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md new file mode 100644 index 0000000000..b73a44789b --- /dev/null +++ b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md @@ -0,0 +1,109 @@ +# C4.3 settlement obligation comparison + +- Parent: [controller/executor RFC, C4.3](../2026-10-27-refactor-handoff.md#c43--settlement-adapters-over-existing-primitives). +- Baseline: `e19d94ca0`, 2026-10-29. +- Status: reading-only comparison. No deletion, runtime, schema, wire or default change. +- [Chinese mirror](2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md). + +## Why this entry exists + +C4.3 requires comparing `host_adapter_settlement.py`, `turn_driver/host_todo_completion.ts` +and `turn_driver/settlement.py` obligation by obligation *before* deleting a +supposedly duplicated condition/step interpreter. No such comparison was +recorded. This entry supplies it so the next PR can be decided rather than +guessed. + +An **obligation** here is a decision or durable effect that must occur exactly +once per transaction, and whose owner must reject, replay or recover it. + +## The three owners at this baseline + +| Where | Entry | Decision owner | Python role | +| --- | --- | --- | --- | +| A — Host Todo completion | `settle_host_todo_completion` | `host_todo_completion.ts` via `turn.host_todo_completion.evaluate` | Materializes authored vision JSON, decodes the emitted plan, runs CLI steps, evaluates `continue_when` | +| B — Turn settlement | `execute_turn_driver_settlement` | `settlement.ts` via `turn.settlement.reduce` | Invokes provider effects, writes checkpoints, returns observations | +| C — MCP host facade | `goal_mode_mcp.py` | A end-to-end | Loads A's request from the host | + +## Obligation matrix + +| Obligation | A decides | B decides | Equivalent? | +| --- | --- | --- | --- | +| Transaction identity | TS `turnInstanceId` / `expectedIdentity`, surfaced as `SettlementIdentity` | TS identity plus per-step receipt matching | Same vocabulary, different derivation inputs | +| Request validation | TS `decodeRequest`, `TODO_ID_PATTERN` | TS decode plus `receipt_missing` / `identity_mismatch` | Not equivalent: A validates shape, B validates committed receipts | +| Pre-write admission | `guard` step runs the real `quota should-run` CLI with the turn instance id | `validation` is a pre-committed phase; B throws if it would have to execute it | **Not equivalent** — different obligations sharing no code | +| Lifecycle completion | `lifecycle_completion` step with completion args | No counterpart; completion is part of writeback | A-only | +| Durable writeback | `durable_writeback` step, condition guarded | `durable_writeback` provider effect through the reducer | Same kind, different execution and recovery | +| Quota spend | `quota_spend` step | `quota_spend` provider effect | Same kind, different execution and recovery | +| Terminal closeout | `terminal_closeout` step, emitted last with `continue_when: null` | `terminal_closeout` provider effect plus `terminal_closeout_requirement` | Same kind, B owns the requirement rule | +| Replay / recovery | None: prepare → plan → finalize is single-shot, no journal | Checkpoint per step, `resolve_prepared`, receipt replay, retry ceiling | **Decisive asymmetry** | +| Persistence | Only a temporary vision file | Durable checkpoint plus source journal | B-only | + +## Findings + +1. **Neither path is a second policy owner.** Both delegate every decision to a + TypeScript owner and keep Python as transport. `host_adapter_settlement.py` + carries no lifecycle policy of its own. + +2. **The step vocabularies overlap only partially.** A emits + `guard / lifecycle_completion / durable_writeback / quota_spend / + terminal_closeout`; B's `SettlementStepKind` is + `validation / durable_writeback / quota_spend / terminal_closeout`. `guard` + and `lifecycle_completion` have no B counterpart, and B's `validation` is a + phase marker it refuses to execute rather than A's admission call. + +3. **Recovery is the real difference, not duplication.** A has no journal and no + replay; it either completes or raises. B persists a checkpoint per provider + and can resume, replay receipts and reject ambiguous attempts. Deleting A + into B would require giving the MCP host a journal it does not have today. + +4. **At the shipped capability call site they already compose.** + `extensions/governed_capability_execution.py` calls A's + `classify_host_guard_snapshot` for admission and then + `execute_turn_driver_settlement` for the transaction with its own journal. + The suspicion that these are competing settlement transactions is not + supported by this call site. + +5. **The only Python-side interpreter is small and transport-necessary.** + `_condition_matches` supports `all`, `any`, `equals`, `nullish`, `object`, + `not_object` and `normalized_string_equals`; `_decode_provider_steps` + validates that the plan has four or five steps in the fixed order. TypeScript + still authors every condition. Evaluating them in TypeScript instead needs + one runtime call per step, which the RFC explicitly says is not automatically + an improvement. + +## What a deletion would require + +None of these is established today: + +- the MCP host path gaining a journal and an effect receiver for A's steps; or +- B accepting `ordered_cli_sequence` plans with `continue_when` plus A's `guard` + and `lifecycle_completion` kinds without a per-step runtime call; or +- evidence that no shipped caller uses A end-to-end. + +## Candidate next slices + +1. **Vocabulary unification (small).** If any caller adopts both paths, give + `guard`, `lifecycle_completion` and `validation` one typed owner instead of + three names in two files. Do not unify names that describe different + obligations. +2. **Characterization of A's single-shot behavior (medium).** Pin what happens + when a step succeeds and a later step raises, before any recovery change + touches it. +3. **Not recommended now.** Merging A and B, or adding a third plan format, + would add a transaction boundary without deleting an obligation. + +## Coordination + +Active or recently merged work touching these obligations: +[#3412](https://github.com/loopx-project/loopx/pull/3412) (ambiguous settlement +recovery), [#5677](https://github.com/loopx-project/loopx/pull/5677) (caller-authored +first delivery), [#7580](https://github.com/loopx-project/loopx/pull/7580) (failed +journal with an unresolved prepared effect), [#7523](https://github.com/loopx-project/loopx/pull/7523) +(typed receipts and continuation). Any implementation slice must reconcile those +diffs first. + +## What this does not establish + +No equivalence proof, no deletion authorization, no runtime qualification, no +benefit measurement, and no claim that either path is redundant. Source facts +are read at the baseline above and may drift. diff --git a/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md new file mode 100644 index 0000000000..862a202e03 --- /dev/null +++ b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md @@ -0,0 +1,98 @@ +# C4.3 结算义务对照 + +- 上级:[controller/executor RFC,C4.3](../2026-10-27-refactor-handoff.zh-CN.md#c43--settlement-adapters-over-existing-primitives)。 +- 基线:`e19d94ca0`,2026-10-29。 +- 状态:只读对照。不删除代码,不改变运行时、schema、wire 或默认值。 +- [英文版](2026-10-29-c4-3-settlement-obligation-comparison.md)。 + +## 为什么有这条记录 + +C4.3 要求在删除所谓重复的 condition/step 解释器**之前**,逐项比对 +`host_adapter_settlement.py`、`turn_driver/host_todo_completion.ts` 与 +`turn_driver/settlement.py` 的义务。此前没有留下这样的对照。本条目补上, +使下一个 PR 可以依据证据决定,而不是靠猜测。 + +这里的**义务**指:每笔事务必须恰好发生一次的决定或持久效果,且其属主必须能够 +拒绝、重放或恢复它。 + +## 基线下的三个属主 + +| 位置 | 入口 | 决定属主 | Python 的职责 | +| --- | --- | --- | --- | +| A — Host Todo completion | `settle_host_todo_completion` | `host_todo_completion.ts`,经 `turn.host_todo_completion.evaluate` | 落盘作者提交的 vision JSON,解码产出的 plan,执行 CLI 步骤,求值 `continue_when` | +| B — Turn settlement | `execute_turn_driver_settlement` | `settlement.ts`,经 `turn.settlement.reduce` | 调用 provider effect、写 checkpoint、回传 observation | +| C — MCP host 门面 | `goal_mode_mcp.py` | 端到端走 A | 从 host 装载 A 的请求 | + +## 义务矩阵 + +| 义务 | A 的决定者 | B 的决定者 | 是否等价 | +| --- | --- | --- | --- | +| 事务身份 | TS `turnInstanceId` / `expectedIdentity`,以 `SettlementIdentity` 呈现 | TS 身份 + 逐步骤 receipt 匹配 | 词汇相同,推导输入不同 | +| 请求校验 | TS `decodeRequest`、`TODO_ID_PATTERN` | TS 解码 + `receipt_missing` / `identity_mismatch` | 不等价:A 校验形状,B 校验已提交回执 | +| 写入前准入 | `guard` 步骤携带 turn instance id 调用真实 `quota should-run` CLI | `validation` 是已提交阶段;B 若需执行它会直接抛错 | **不等价**——两者义务不同,且不共用代码 | +| 生命周期完成 | `lifecycle_completion` 步骤,带 completion 参数 | 无对应项;完成并入 writeback | A 独有 | +| 持久写回 | `durable_writeback` 步骤,受条件约束 | 经 reducer 的 `durable_writeback` provider effect | 类型相同,执行与恢复不同 | +| 配额花费 | `quota_spend` 步骤 | `quota_spend` provider effect | 类型相同,执行与恢复不同 | +| 终态收尾 | `terminal_closeout` 步骤,最后发出且 `continue_when: null` | `terminal_closeout` provider effect + `terminal_closeout_requirement` | 类型相同,B 拥有需求判定规则 | +| 重放 / 恢复 | 无:prepare → plan → finalize 是一次性,无 journal | 逐步骤 checkpoint、`resolve_prepared`、回执重放、重试上限 | **决定性差异** | +| 持久化 | 仅一个临时 vision 文件 | 持久 checkpoint 加来源 journal | B 独有 | + +## 发现 + +1. **两条路径都不是第二个策略属主。** 二者都把决定交给 TypeScript 属主, + Python 只做传输。`host_adapter_settlement.py` 自身不携带生命周期策略。 + +2. **步骤词汇只部分重叠。** A 产出 + `guard / lifecycle_completion / durable_writeback / quota_spend / + terminal_closeout`;B 的 `SettlementStepKind` 是 + `validation / durable_writeback / quota_spend / terminal_closeout`。 + `guard` 与 `lifecycle_completion` 在 B 无对应项;B 的 `validation` 是一个 + 拒绝执行的阶段标记,而不是 A 的准入调用。 + +3. **真正的差异是恢复,而不是重复。** A 没有 journal,也不能重放;它要么完成, + 要么抛错。B 逐 provider 持久化 checkpoint,可续跑、重放回执并拒绝歧义尝试。 + 要把 A 并入 B,先得给 MCP host 一个它今天并没有的 journal。 + +4. **在已发布的能力调用点上,二者已经是组合关系。** + `extensions/governed_capability_execution.py` 先用 A 的 + `classify_host_guard_snapshot` 做准入,再用 `execute_turn_driver_settlement` + 携带自己的 journal 执行事务。该调用点不支持"二者是竞争事务"的假设。 + +5. **Python 侧唯一的解释器很小,且是传输必需。** `_condition_matches` 支持 + `all`、`any`、`equals`、`nullish`、`object`、`not_object` 和 + `normalized_string_equals`;`_decode_provider_steps` 校验 plan 是四或五步的 + 固定顺序。条件全部由 TypeScript 编写。改由 TypeScript 逐步求值需要每个步骤 + 一次运行时调用,RFC 明确指出这不自动等于改进。 + +## 删除需要什么前提 + +以下今均不成立: + +- MCP host 路径获得 journal 与接收 A 各步骤效果的能力;或 +- B 接受带 `continue_when` 的 `ordered_cli_sequence` plan,并接受 A 的 `guard` + 与 `lifecycle_completion` 类型,且不引入逐步运行时调用;或 +- 证明没有已发布的调用方端到端使用 A。 + +## 候选后续切片 + +1. **词汇统一(小)。** 若确有调用方同时采用两条路径,为 `guard`、 + `lifecycle_completion` 与 `validation` 指定一个类型属主,而不是让三个名字 + 分散在两个文件中。不要合并描述不同义务的名字。 +2. **A 的一次性行为刻画(中)。** 在任何恢复改动之前,先钉住"某步成功、后续 + 步骤抛错"时的实际行为。 +3. **当前不建议。** 合并 A 与 B,或新增第三种 plan 格式,都会增加事务边界而 + 不删除任何义务。 + +## 协同 + +涉及这些义务的在研或刚合并工作: +[#3412](https://github.com/loopx-project/loopx/pull/3412)(歧义结算恢复)、 +[#5677](https://github.com/loopx-project/loopx/pull/5677)(调用方作者的首投递)、 +[#7580](https://github.com/loopx-project/loopx/pull/7580)(失败 journal 存在未了结的 +prepared effect)、[#7523](https://github.com/loopx-project/loopx/pull/7523) +(类型化回执与续跑)。任何实现切片都必须先调和对这些 diff 的理解。 + +## 本记录不成立的结论 + +不构成等价证明,不构成删除授权,不构成运行时资格,不构成收益测量,也不主张 +任何一条路径是冗余的。源码事实按上述基线读取,之后可能漂移。 From cf9c79ada11c171a40549e310dac542c0b4e5dd2 Mon Sep 17 00:00:00 2001 From: xiaoan17 Date: Thu, 29 Oct 2026 01:11:48 +0800 Subject: [PATCH 2/4] docs(rfc): fix C4.3 comparison parent link path The two new C4.3 comparison notes linked their parent handoff note as '../2026-10-27-refactor-handoff.md', but the handoff note lives in the same ledger directory. mkdocs strict resolves the '../' form against architecture/rfcs/ledger/, so the strict documentation build failed with two unresolvable-target warnings and aborted. Dropping '../' points each note at the sibling file. Both the English and the Chinese note change together, so the RFC bilingual pair stays complete. No content, schema, wire or default change. Validation: mkdocs build --strict for the docs site and both developer books; RFC bilingual check passed; loopx canary premerge --from-git-diff green; public boundary scan clean. Signed-off-by: xiaoan17 --- .../2026-10-29-c4-3-settlement-obligation-comparison.md | 2 +- .../2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md index b73a44789b..64d5465be1 100644 --- a/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md +++ b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md @@ -1,6 +1,6 @@ # C4.3 settlement obligation comparison -- Parent: [controller/executor RFC, C4.3](../2026-10-27-refactor-handoff.md#c43--settlement-adapters-over-existing-primitives). +- Parent: [controller/executor RFC, C4.3](2026-10-27-refactor-handoff.md#c43--settlement-adapters-over-existing-primitives). - Baseline: `e19d94ca0`, 2026-10-29. - Status: reading-only comparison. No deletion, runtime, schema, wire or default change. - [Chinese mirror](2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md). diff --git a/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md index 862a202e03..776b058329 100644 --- a/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md +++ b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md @@ -1,6 +1,6 @@ # C4.3 结算义务对照 -- 上级:[controller/executor RFC,C4.3](../2026-10-27-refactor-handoff.zh-CN.md#c43--settlement-adapters-over-existing-primitives)。 +- 上级:[controller/executor RFC,C4.3](2026-10-27-refactor-handoff.zh-CN.md#c43--settlement-adapters-over-existing-primitives)。 - 基线:`e19d94ca0`,2026-10-29。 - 状态:只读对照。不删除代码,不改变运行时、schema、wire 或默认值。 - [英文版](2026-10-29-c4-3-settlement-obligation-comparison.md)。 From dd4d0f32abd21078e60c2caf8b585b287f9226b9 Mon Sep 17 00:00:00 2001 From: xiaoan17 Date: Thu, 29 Oct 2026 10:31:20 +0800 Subject: [PATCH 3/4] docs(rfc): correct the C4.3 recovery baseline An earlier revision of the C4.3 comparison read the adapter's lack of a local journal as a lack of recovery on the whole MCP path, and made "give the MCP host a journal it does not have" a precondition of any deletion. Both were wrong. The MCP path does recover. turnInstanceId derives a stable identity from the same intent, the CLI persists canonical settlement receipts, and spendContinueWhen accepts idempotent_replay and receipt_repaired, so a response lost after the real commit recovers by re-entering the same intent: the durable readback reports settlement_pending or settled, a premature terminal completion is blocked, the retry succeeds and one slot is spent. tests/test_goal_mode_mcp_settlement.py::test_real_mcp_completion_recovers_a_lost_mutation_response covers it over lifecycle, writeback and spend, and passes at this head. The real difference is owner and granularity: the adapter keeps no journal, the CLI owns the durable receipts, and B recovers per provider step inside one transaction. The matrix rows, Finding 3, the deletion preconditions and the characterization candidate now say that, and the characterization candidate targets the adapter-local partial-step behavior rather than the response-loss case that is already covered. Both mirrors are corrected together. No runtime, schema, wire, registry or default change. Validation: mkdocs build --strict for the docs site and both developer books; RFC bilingual check passed; loopx canary premerge --from-git-diff green; the response-loss recovery test passes locally. Signed-off-by: xiaoan17 --- ...9-c4-3-settlement-obligation-comparison.md | 50 +++++++++++++------ ...-settlement-obligation-comparison.zh-CN.md | 37 ++++++++++---- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md index 64d5465be1..974cd0d5f1 100644 --- a/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md +++ b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.md @@ -1,8 +1,11 @@ # C4.3 settlement obligation comparison - Parent: [controller/executor RFC, C4.3](2026-10-27-refactor-handoff.md#c43--settlement-adapters-over-existing-primitives). -- Baseline: `e19d94ca0`, 2026-10-29. +- Baseline: `e19d94ca0`, 2026-10-29. The settlement owners are unchanged at `fb331f45`. - Status: reading-only comparison. No deletion, runtime, schema, wire or default change. +- Correction: an earlier revision of this entry read the adapter's lack of a + local journal as a lack of recovery on the whole MCP path. That was wrong. + The recovery baseline is corrected below, in both mirrors. - [Chinese mirror](2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md). ## Why this entry exists @@ -35,8 +38,8 @@ once per transaction, and whose owner must reject, replay or recover it. | Durable writeback | `durable_writeback` step, condition guarded | `durable_writeback` provider effect through the reducer | Same kind, different execution and recovery | | Quota spend | `quota_spend` step | `quota_spend` provider effect | Same kind, different execution and recovery | | Terminal closeout | `terminal_closeout` step, emitted last with `continue_when: null` | `terminal_closeout` provider effect plus `terminal_closeout_requirement` | Same kind, B owns the requirement rule | -| Replay / recovery | None: prepare → plan → finalize is single-shot, no journal | Checkpoint per step, `resolve_prepared`, receipt replay, retry ceiling | **Decisive asymmetry** | -| Persistence | Only a temporary vision file | Durable checkpoint plus source journal | B-only | +| Replay / recovery | The adapter keeps no journal of its own. The call chain still recovers: `turnInstanceId` derives a stable identity from the same intent, the CLI persists canonical settlement receipts, and the continue-when conditions accept `idempotent_replay` / `receipt_repaired`, so re-entering the same intent succeeds and spends exactly one slot | Checkpoint per step, `resolve_prepared`, receipt replay, retry ceiling | **Different owner and granularity** — not "recovery versus none" | +| Persistence | The adapter writes only a temporary vision file; the durable settlement receipts live in the canonical CLI/quota store, owned by the CLI | Durable checkpoint plus source journal | Different owners: CLI receipts versus transaction journal | ## Findings @@ -51,10 +54,20 @@ once per transaction, and whose owner must reject, replay or recover it. and `lifecycle_completion` have no B counterpart, and B's `validation` is a phase marker it refuses to execute rather than A's admission call. -3. **Recovery is the real difference, not duplication.** A has no journal and no - replay; it either completes or raises. B persists a checkpoint per provider - and can resume, replay receipts and reject ambiguous attempts. Deleting A - into B would require giving the MCP host a journal it does not have today. +3. **Recovery differs in owner and granularity, not in existence.** The adapter + keeps no journal, but the MCP path is not recovery-free. + `turnInstanceId` derives a stable identity from the same intent, the CLI + persists canonical settlement receipts, and `spendContinueWhen` accepts + `idempotent_replay` / `receipt_repaired`. A response lost after the real + commit therefore recovers by re-entering the same intent: the durable + readback reports `settlement_pending` or `settled`, a premature terminal + completion is blocked, the retry succeeds, and the slot is spent exactly + once — `tests/test_goal_mode_mcp_settlement.py::test_real_mcp_completion_recovers_a_lost_mutation_response`, + parametrized over `lifecycle`, `writeback` and `spend`. B recovers at + provider-step granularity inside one transaction, through its own checkpoint + and `resolve_prepared`. A consolidation must preserve the CLI-receipt replay + path; it must neither assume the MCP host needs a new journal nor assume the + CLI receipts disappear with the adapter. 4. **At the shipped capability call site they already compose.** `extensions/governed_capability_execution.py` calls A's @@ -75,9 +88,13 @@ once per transaction, and whose owner must reject, replay or recover it. None of these is established today: -- the MCP host path gaining a journal and an effect receiver for A's steps; or +- the consolidation preserving the CLI's canonical receipt replay and the + stable turn instance id that let a re-entry of the same intent succeed + without paying twice, together with an adapter-local journal *or* an + equivalent effect receiver for A's steps; or - B accepting `ordered_cli_sequence` plans with `continue_when` plus A's `guard` - and `lifecycle_completion` kinds without a per-step runtime call; or + and `lifecycle_completion` kinds without a per-step runtime call, and + adopting the CLI-receipt replay that A's path relies on today; or - evidence that no shipped caller uses A end-to-end. ## Candidate next slices @@ -86,9 +103,12 @@ None of these is established today: `guard`, `lifecycle_completion` and `validation` one typed owner instead of three names in two files. Do not unify names that describe different obligations. -2. **Characterization of A's single-shot behavior (medium).** Pin what happens - when a step succeeds and a later step raises, before any recovery change - touches it. +2. **Characterization of A's adapter-local behavior (medium).** Pin what the + adapter itself does when a step succeeds and a later step raises, before any + recovery change touches it. The end-to-end response-loss case is already + characterized by `test_real_mcp_completion_recovers_a_lost_mutation_response`; + what is not characterized is the adapter's own partial-step behavior and + where the recovery boundary between the adapter and the CLI receipts sits. 3. **Not recommended now.** Merging A and B, or adding a third plan format, would add a transaction boundary without deleting an obligation. @@ -105,5 +125,7 @@ diffs first. ## What this does not establish No equivalence proof, no deletion authorization, no runtime qualification, no -benefit measurement, and no claim that either path is redundant. Source facts -are read at the baseline above and may drift. +benefit measurement, and no claim that either path is redundant. The recovery +baseline is the CLI-receipt replay that exists today; the adapter-local +partial-step behavior is not characterized here. Source facts are read at the +baseline above and may drift. diff --git a/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md index 776b058329..049aee009a 100644 --- a/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md +++ b/docs/architecture/rfcs/ledger/controller-executor-unified-contract-v0/2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md @@ -1,8 +1,10 @@ # C4.3 结算义务对照 - 上级:[controller/executor RFC,C4.3](2026-10-27-refactor-handoff.zh-CN.md#c43--settlement-adapters-over-existing-primitives)。 -- 基线:`e19d94ca0`,2026-10-29。 +- 基线:`e19d94ca0`,2026-10-29。结算属主在 `fb331f45` 未变。 - 状态:只读对照。不删除代码,不改变运行时、schema、wire 或默认值。 +- 更正:本条目早前版本把"适配器没有本地 journal"读成"整条 MCP 路径没有恢复"。 + 该结论错误。恢复基线已在下方更正,两侧镜像同步更正。 - [英文版](2026-10-29-c4-3-settlement-obligation-comparison.md)。 ## 为什么有这条记录 @@ -34,8 +36,8 @@ C4.3 要求在删除所谓重复的 condition/step 解释器**之前**,逐项 | 持久写回 | `durable_writeback` 步骤,受条件约束 | 经 reducer 的 `durable_writeback` provider effect | 类型相同,执行与恢复不同 | | 配额花费 | `quota_spend` 步骤 | `quota_spend` provider effect | 类型相同,执行与恢复不同 | | 终态收尾 | `terminal_closeout` 步骤,最后发出且 `continue_when: null` | `terminal_closeout` provider effect + `terminal_closeout_requirement` | 类型相同,B 拥有需求判定规则 | -| 重放 / 恢复 | 无:prepare → plan → finalize 是一次性,无 journal | 逐步骤 checkpoint、`resolve_prepared`、回执重放、重试上限 | **决定性差异** | -| 持久化 | 仅一个临时 vision 文件 | 持久 checkpoint 加来源 journal | B 独有 | +| 重放 / 恢复 | 适配器自身不持有 journal。但调用链仍然能恢复:`turnInstanceId` 从同一意图推导出稳定身份,CLI 持久化规范结算回执,continue-when 条件接受 `idempotent_replay` / `receipt_repaired`,因此以同一意图重新进入可以成功且只扣一个槽位 | 逐步骤 checkpoint、`resolve_prepared`、回执重放、重试上限 | **属主与粒度不同**——不是"有恢复与无恢复"之分 | +| 持久化 | 适配器只写一个临时 vision 文件;持久结算回执存放在规范的 CLI/quota 存储中,属主是 CLI | 持久 checkpoint 加来源 journal | 属主不同:CLI 回执与事务 journal | ## 发现 @@ -49,9 +51,16 @@ C4.3 要求在删除所谓重复的 condition/step 解释器**之前**,逐项 `guard` 与 `lifecycle_completion` 在 B 无对应项;B 的 `validation` 是一个 拒绝执行的阶段标记,而不是 A 的准入调用。 -3. **真正的差异是恢复,而不是重复。** A 没有 journal,也不能重放;它要么完成, - 要么抛错。B 逐 provider 持久化 checkpoint,可续跑、重放回执并拒绝歧义尝试。 - 要把 A 并入 B,先得给 MCP host 一个它今天并没有的 journal。 +3. **恢复的差异在属主与粒度,而不在有无。** 适配器不持有 journal,但 MCP 路径 + 并非无恢复能力。`turnInstanceId` 从同一意图推导出稳定身份,CLI 持久化规范结算 + 回执,`spendContinueWhen` 接受 `idempotent_replay` / `receipt_repaired`。 + 因此真实提交之后响应丢失的场景可以靠以同一意图重新进入而恢复:持久读回报告 + `settlement_pending` 或 `settled`,过早的终态完成被拦下,重试成功,且只扣一个 + 槽位——见 `tests/test_goal_mode_mcp_settlement.py::test_real_mcp_completion_recovers_a_lost_mutation_response`, + 参数覆盖 `lifecycle`、`writeback`、`spend`。B 的恢复是单个事务内按 provider 步骤 + 的粒度,靠自己的 checkpoint 与 `resolve_prepared`。整合方案必须保留 CLI 回执 + 重放这条路径:既不能假定 MCP host 需要新增 journal,也不能假定 CLI 回执会随 + 适配器一起消失。 4. **在已发布的能力调用点上,二者已经是组合关系。** `extensions/governed_capability_execution.py` 先用 A 的 @@ -68,9 +77,12 @@ C4.3 要求在删除所谓重复的 condition/step 解释器**之前**,逐项 以下今均不成立: -- MCP host 路径获得 journal 与接收 A 各步骤效果的能力;或 +- 整合保留 CLI 的规范回执重放与稳定 turn instance id(当前正是它们让同一意图的 + 再次进入成功且不重复扣费),同时为 A 的各步骤补上适配器本地 journal *或* 等价的 + 效果接收方;或 - B 接受带 `continue_when` 的 `ordered_cli_sequence` plan,并接受 A 的 `guard` - 与 `lifecycle_completion` 类型,且不引入逐步运行时调用;或 + 与 `lifecycle_completion` 类型,且不引入逐步运行时调用,并接手 A 路径今天依赖的 + CLI 回执重放;或 - 证明没有已发布的调用方端到端使用 A。 ## 候选后续切片 @@ -78,8 +90,10 @@ C4.3 要求在删除所谓重复的 condition/step 解释器**之前**,逐项 1. **词汇统一(小)。** 若确有调用方同时采用两条路径,为 `guard`、 `lifecycle_completion` 与 `validation` 指定一个类型属主,而不是让三个名字 分散在两个文件中。不要合并描述不同义务的名字。 -2. **A 的一次性行为刻画(中)。** 在任何恢复改动之前,先钉住"某步成功、后续 - 步骤抛错"时的实际行为。 +2. **A 的适配器本地行为刻画(中)。** 在任何恢复改动之前,先钉住适配器自身在 + "某步成功、后续步骤抛错"时的实际行为。端到端的响应丢失场景已由 + `test_real_mcp_completion_recovers_a_lost_mutation_response` 刻画;尚未刻画的是 + 适配器自身的部分步骤行为,以及适配器与 CLI 回执之间的恢复边界在哪里。 3. **当前不建议。** 合并 A 与 B,或新增第三种 plan 格式,都会增加事务边界而 不删除任何义务。 @@ -95,4 +109,5 @@ prepared effect)、[#7523](https://github.com/loopx-project/loopx/pull/7523) ## 本记录不成立的结论 不构成等价证明,不构成删除授权,不构成运行时资格,不构成收益测量,也不主张 -任何一条路径是冗余的。源码事实按上述基线读取,之后可能漂移。 +任何一条路径是冗余的。恢复基线是今天已经存在的 CLI 回执重放;适配器本地的部分 +步骤行为不在本记录范围内。源码事实按上述基线读取,之后可能漂移。 From 563f08799f21c171224407ba4188c863e12333f1 Mon Sep 17 00:00:00 2001 From: xiaoan17 Date: Thu, 29 Oct 2026 10:40:05 +0800 Subject: [PATCH 4/4] docs(rfc): regenerate the RFC status index for the new ledger entry The derived status index counts ledger entries per RFC, so adding the C4.3 comparison entry makes the checked-in index stale and the docs-governance smoke fails for every later change until it is regenerated. Both index files are regenerated from the RFC headers. No prose change. Signed-off-by: xiaoan17 --- docs/architecture/rfcs/STATUS.md | 2 +- docs/architecture/rfcs/STATUS.zh-CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture/rfcs/STATUS.md b/docs/architecture/rfcs/STATUS.md index 67a340c3ce..24f907b374 100644 --- a/docs/architecture/rfcs/STATUS.md +++ b/docs/architecture/rfcs/STATUS.md @@ -31,7 +31,7 @@ appendix may keep dated history, but no dated log heading may precede it. | [RFC: Capable Agent Manager and Semantic Work Handoff (v0)](capable-manager-semantic-handoff-v0.md) | Accepted | [Personal Follow-through independent proposal](personal-follow-through-v0.md) | [2 entries](ledger/capable-manager-semantic-handoff-v0/) | | [RFC: Complete State Recovery and Controlled Reactivation (v0)](complete-state-recovery-v0.md) | Accepted | none | — | | [RFC: Composable State Machines and Recovery Verification (v0)](composable-state-machines-recovery-verification-v0.md) | Accepted | none | — | -| [RFC: Controller / Executor Sessions and One Governed Work Contract (v0)](controller-executor-unified-contract-v0.md) | Accepted | none | [1 entry](ledger/controller-executor-unified-contract-v0/) | +| [RFC: Controller / Executor Sessions and One Governed Work Contract (v0)](controller-executor-unified-contract-v0.md) | Accepted | none | [2 entries](ledger/controller-executor-unified-contract-v0/) | | [Explicit Todo continuation: Stage A](cross-session-memory-substrate-v0.md) | Accepted | none | — | | [RFC: LoopX Desktop Execution Frontends v0](desktop-execution-frontends-v0.md) | Accepted | none | — | | [RFC: External Evidence Research Capability v0](external-evidence-research-capability-v0.md) | Accepted | none | [3 entries](ledger/external-evidence-research-capability-v0/) | diff --git a/docs/architecture/rfcs/STATUS.zh-CN.md b/docs/architecture/rfcs/STATUS.zh-CN.md index 7059783a1e..039db58cdb 100644 --- a/docs/architecture/rfcs/STATUS.zh-CN.md +++ b/docs/architecture/rfcs/STATUS.zh-CN.md @@ -28,7 +28,7 @@ | [RFC:强能力 Agent 管家与语义工作交接(v0)](capable-manager-semantic-handoff-v0.zh-CN.md) | 已接受 | [个人事项持续跟进独立提案](personal-follow-through-v0.zh-CN.md) | [2 条](ledger/capable-manager-semantic-handoff-v0/) | | [RFC:完整状态恢复与受控重新激活(v0)](complete-state-recovery-v0.zh-CN.md) | 已接受 | 无 | — | | [RFC:可组合状态机与恢复验证(v0)](composable-state-machines-recovery-verification-v0.zh-CN.md) | 已接受 | 无 | — | -| [RFC:Controller / Executor 会话与统一受治理工作契约(v0)](controller-executor-unified-contract-v0.zh-CN.md) | 已接受 | 无 | [1 条](ledger/controller-executor-unified-contract-v0/) | +| [RFC:Controller / Executor 会话与统一受治理工作契约(v0)](controller-executor-unified-contract-v0.zh-CN.md) | 已接受 | 无 | [2 条](ledger/controller-executor-unified-contract-v0/) | | [显式 Todo 接续:阶段 A](cross-session-memory-substrate-v0.zh-CN.md) | 已接受 | 无 | — | | [RFC:LoopX 桌面执行前端 v0](desktop-execution-frontends-v0.zh-CN.md) | 已接受 | 无 | — | | [RFC:外部证据研究能力 v0](external-evidence-research-capability-v0.zh-CN.md) | 已接受 | 无 | [3 条](ledger/external-evidence-research-capability-v0/) |