Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/architecture/rfcs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | [4 entries](ledger/controller-executor-unified-contract-v0/) |
| [RFC: Controller / Executor Sessions and One Governed Work Contract (v0)](controller-executor-unified-contract-v0.md) | Accepted | none | [5 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/) |
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/rfcs/STATUS.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) | 已接受 | 无 | [4 条](ledger/controller-executor-unified-contract-v0/) |
| [RFC:Controller / Executor 会话与统一受治理工作契约(v0)](controller-executor-unified-contract-v0.zh-CN.md) | 已接受 | 无 | [5 条](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/) |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# C4.3 adapter partial-step characterization

- Parent: [controller/executor RFC, C4.3](2026-10-27-refactor-handoff.md#c43-settlement-adapters-over-existing-primitives).
- Baseline: `8caaf678e`, 2026-10-29. The settlement owners are unchanged at `1e3401591`.
- Status: characterization only. No runtime, schema, wire or default change.
- Follows: [C4.3 settlement obligation comparison](2026-10-29-c4-3-settlement-obligation-comparison.md).
- [Chinese mirror](2026-10-29-c4-3-adapter-partial-step-characterization.zh-CN.md).

## Why this entry exists

The comparison entry recorded the adapter's lack of a local journal as a fact
about the adapter, and left the adapter's own behavior when a step succeeds and
a later step fails uncharacterized, together with the place where the recovery
boundary between the adapter and the CLI receipts sits. A consolidation slice
that moved this path would otherwise have had to assume both.

This entry pins that behavior with executable cases, so a later recovery change
starts from measured behavior rather than from the comparison's wording.

## What is characterized now

| Surface | Adapter behavior | Owner behavior |
| --- | --- | --- |
| A step returns output the adapter cannot read | `_provider_condition_matches` treats a non-JSON output as a condition that did not match, so the plan stops after that step | The finalize phase still runs and the owner classifies the stop: `blocked`, `failed_stage: guard`, reason `quota guard returned malformed JSON` |
| A step raises | The exception propagates out of the adapter. The finalize phase never runs, so the transaction is never classified. The adapter keeps no record of the step that already succeeded | Nothing is classified, because the owner is never asked |
| A retry after a raise | The adapter re-enters the plan at its first step and never resumes mid-plan | The identity is re-derived from the same intent, so the same settlement identity is used |
| A step raises before the lifecycle completion | Nothing is spent and the Todo stays open | The guard already ran, so the `validation` receipt is committed. The readback is `open` with progress `writeback_required`; there is no lifecycle completion, writeback or spend receipt to replay |
| A step raises after the lifecycle completion | The adapter leaves nothing behind; the CLI's canonical receipt store holds the committed receipt | The readback is `settlement_pending` with progress `writeback_required`: `validation` is committed, the writeback and spend receipts are missing |
| A step raises after the durable writeback | The adapter leaves nothing behind; the CLI's canonical receipt store holds the committed receipt | The readback is `settlement_pending` with progress `spend_required`: `validation` and `durable_writeback` are committed, the spend receipt is missing |
| A step raises after the quota spend | The adapter leaves nothing behind; the CLI's canonical receipt store holds the committed receipt | The readback is `settled`; the retry replays that receipt (`idempotent_replay`), spends no second slot and reports the same settlement identity |

Three findings follow from these cases.

1. **The adapter makes no classification decision.** A stopped plan is
classified by the TypeScript owner; a raised step is not classified at all,
because the owner is never asked. The adapter is transport in both cases.
2. **The recovery boundary is the CLI receipt, not adapter state.** After a
raise, the only durable evidence is what the CLI already committed. The
retry succeeds because that receipt replays, and the adapter contributes
nothing but a re-execution of the plan.
3. **The outstanding work is named by the owner, stage by stage.** The
readback distinguishes an open settlement from a pending one and names the
step still owed — `writeback_required` after the lifecycle completion,
`spend_required` after the durable writeback, `settled` after the spend.
A raise is therefore not a single undifferentiated failure state, and the
committed receipt kinds tell which stage a retry would resume from.

## Evidence

`tests/control_plane/test_host_adapter_settlement_partial_steps.py`:

- `test_an_unreadable_step_output_stops_the_plan_and_the_owner_classifies_it`
- `test_a_raising_step_leaves_no_finalize_and_no_adapter_local_resume`
- `test_a_raised_step_after_a_durable_commit_leaves_only_that_stage_pending`
(real CLI, real quota store; parametrized over the guard, lifecycle
completion, durable writeback and quota spend stages, reading back the
committed receipt kinds, the replay phase and the settlement progress)
- `test_a_raising_step_after_a_durable_commit_recovers_through_the_cli_receipt`
(real CLI, real quota store)
- `test_a_raised_step_before_completion_commits_no_settlement` (real CLI)

The existing end-to-end recovery case stays where it was:
`tests/test_goal_mode_mcp_settlement.py::test_real_mcp_completion_recovers_a_lost_mutation_response`
covers a lost response after a commit; the new case covers the same recovery
through a raised transport instead.

## What this does not establish

- No equivalence proof, no deletion authorization, no runtime qualification, no
benefit measurement, and no claim that either settlement path is redundant.
- The characterization covers the shipped MCP host path through
`settle_host_todo_completion`. It does not characterize other callers of the
adapter, and it does not describe the TypeScript-only Turn settlement path,
which recovers per provider step inside its own transaction.
- It adds no journal, retry policy or resume capability to the adapter. Adding
one is a new design decision with its own acceptance, not a characterization.
- The observed recovery depends on the CLI's canonical receipt store. Source
facts were read at the baseline above and may drift.

## Next slices

Unchanged by this entry, with one correction to how the remaining work is
described. A deletion of the adapter-side interpreter still needs the
preconditions recorded in the comparison entry. The vocabulary candidate that
the confirmation entry withdrew — one typed owner for `guard`,
`lifecycle_completion` and `validation` — is not reopened here: that entry
already identified the shipped caller that adopts both paths and recorded the
candidate as withdrawn. The independent registry slice — the shared owner for
the ordered host-adapter step kinds — is completed in
[#7619](https://github.com/loopx-project/loopx/pull/7619). That slice was a
registry change, not a continuation of the withdrawn unification. The work
that remains open is the deletion and adoption preconditions recorded in the
comparison entry.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# C4.3 适配器部分步骤行为刻画

- 上级:[controller/executor RFC,C4.3](2026-10-27-refactor-handoff.zh-CN.md#c43-adapter)。
- 基线:`8caaf678e`,2026-10-29。结算属主自 `1e3401591` 起未变。
- 状态:只做刻画。不改变运行时、schema、wire 或默认值。
- 上游条目:[C4.3 结算义务对照](2026-10-29-c4-3-settlement-obligation-comparison.zh-CN.md)。
- [英文版](2026-10-29-c4-3-adapter-partial-step-characterization.md)。

## 为什么有这条记录

对照条目把"适配器没有本地 journal"记成关于适配器自身的事实,但适配器在
"某步成功、后续步失败"时的自身行为,以及适配器与 CLI 回执之间的恢复边界落在
哪里,当时仍没有被刻画。任何会改动这条路径的整合切片,都只能对这两点做假设。

本条目用可执行用例把该行为钉住,让后续的恢复改动从实测行为出发,而不是从对照
条目的措辞出发。

## 现在刻画了什么

| 场景 | 适配器行为 | 属主行为 |
| --- | --- | --- |
| 某一步返回适配器读不懂的输出 | `_provider_condition_matches` 把非 JSON 输出当作"条件不匹配",计划在该步之后停止 | finalize 阶段仍然运行,由属主判定这次停止:`blocked`、`failed_stage: guard`、原因 `quota guard returned malformed JSON` |
| 某一步抛错 | 异常直接冒出适配器。finalize 阶段从未运行,这笔事务从未被分类。适配器不保留已成功那一步的任何记录 | 没有分类,因为属主从未被询问 |
| 抛错后的重试 | 适配器从计划第一步重新进入,绝不在中途续跑 | 身份由同一意图重新推导,因此使用同一个 settlement identity |
| 某一步在生命周期完成之前抛错 | 没有花费,Todo 保持 open | guard 已经跑过,因此 `validation` 凭据已提交。读回为 `open`,进度为 `writeback_required`;不存在可重放的生命周期完成、写回或花费凭据 |
| 某一步在生命周期完成提交之后抛错 | 适配器什么也不留下;已提交的凭据由 CLI 的规范凭据库持有 | 读回为 `settlement_pending`,进度为 `writeback_required`:`validation` 已提交,写回与花费凭据缺失 |
| 某一步在持久写回提交之后抛错 | 适配器什么也不留下;已提交的凭据由 CLI 的规范凭据库持有 | 读回为 `settlement_pending`,进度为 `spend_required`:`validation` 与 `durable_writeback` 已提交,花费凭据缺失 |
| 某一步在配额花费提交之后抛错 | 适配器什么也不留下;已提交的凭据由 CLI 的规范凭据库持有 | 读回报告 `settled`;重试重放该凭据(`idempotent_replay`),不会再扣一个槽位,并报告同一个 settlement identity |

由此得到三条结论。

1. **适配器不做任何分类判定。** 计划停止由 TypeScript 属主分类;某一步抛错则
完全没有分类,因为属主从未被询问。两种情形下适配器都只是传输。
2. **恢复边界是 CLI 凭据,不是适配器状态。** 抛错之后,唯一持久存在的证据是
CLI 已经提交的东西。重试之所以成功,是因为那份凭据可以重放;适配器除了重跑
计划之外没有贡献任何东西。
3. **欠下的工作由属主按阶段点名。** 读回区分"结算尚未开始"与"结算待补",并指出
还欠哪一步:生命周期完成之后是 `writeback_required`,持久写回之后是
`spend_required`,配额花费之后是 `settled`。因此抛错并不是一种不加区分的
失败状态;已提交的凭据类型说明重试会从哪个阶段继续。

## 证据

`tests/control_plane/test_host_adapter_settlement_partial_steps.py`:

- `test_an_unreadable_step_output_stops_the_plan_and_the_owner_classifies_it`
- `test_a_raising_step_leaves_no_finalize_and_no_adapter_local_resume`
- `test_a_raised_step_after_a_durable_commit_leaves_only_that_stage_pending`
(真实 CLI、真实配额存储;按 guard、生命周期完成、持久写回与配额花费四个阶段
参数化,读回已提交的凭据类型、replay phase 与结算进度)
- `test_a_raising_step_after_a_durable_commit_recovers_through_the_cli_receipt`
(真实 CLI、真实配额存储)
- `test_a_raised_step_before_completion_commits_no_settlement`(真实 CLI)

既有的端到端恢复用例仍在原处:
`tests/test_goal_mode_mcp_settlement.py::test_real_mcp_completion_recovers_a_lost_mutation_response`
覆盖的是提交之后响应丢失;新增用例覆盖同一条恢复路径在传输抛错时的形态。

## 本条目没有建立什么

- 没有等价性证明,没有删除授权,没有运行时资格,没有收益测量,也没有断言任何
一条结算路径是多余的。
- 刻画覆盖的是经 `settle_host_todo_completion` 的已交付 MCP host 路径。它不覆盖
适配器的其他调用方,也不描述纯 TypeScript 的 Turn settlement 路径——后者在
自己的事务内按 provider 步骤恢复。
- 它没有给适配器增加 journal、重试策略或续跑能力。增加这些是新的设计决策,
有各自的验收,不是刻画。
- 观察到的恢复依赖 CLI 的规范凭据库。源码事实基于上述基线读取,可能漂移。

## 后续切片

本条目不改变结论,但修正了剩余工作的表述方式。删除适配器侧解释器仍需要对照条目
记录的前提条件。词汇对照条目已经撤回的候选——把 `guard`、`lifecycle_completion`
与 `validation` 归到一个 typed 属主——在此不重新打开:那条目已经指出了同时采用
两条路径的已交付调用方,并把该候选记为 withdrawn。独立的登记切片——为 host-adapter
有序步骤类型设立共享属主——已在
[#7619](https://github.com/loopx-project/loopx/pull/7619) 中完成;那是登记切片,
不是被撤回的统一化工作的延续。仍然开放的是对照条目记录的删除与采用前提。
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ That gap is a separate slice: one shared owner for the ordered host-adapter step
kinds, registered in the vocabulary registry so that the guard/validation
distinction becomes machine-readable instead of prose. It must first reconcile
the in-flight settlement work: #3412, #5677, #7580, #7523. That slice is
proposed in [#7619](https://github.com/loopx-project/loopx/pull/7619): it
merged in [#7619](https://github.com/loopx-project/loopx/pull/7619): it
registers the ordered host-adapter step kinds as their own vocabulary with both
runtimes as owners, so the two carriers are compared on every drift scan instead
of hand-kept.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ C4.3 结算义务对照列出一个候选后续切片:*词汇统一(小)
这是一个独立切片:为 host-adapter 的有序步骤类型给出同一个共享属主,并登记进
词汇 registry,让 guard/validation 的区别从散文变成机器可读。它必须先调和在途的
结算工作:#3412、#5677、#7580、#7523。该切片已在
[#7619](https://github.com/loopx-project/loopx/pull/7619) 中提出:它把 host-adapter
[#7619](https://github.com/loopx-project/loopx/pull/7619) 中合入:它把 host-adapter
的有序步骤类型登记为独立词汇,并由两个运行时共同作为属主,于是两个载体在每次
漂移扫描中都会被比较,而不再靠手工维护。

Expand Down
Loading
Loading