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
55 changes: 55 additions & 0 deletions docs/architecture/rfcs/monorepo-distribution-split-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -1619,6 +1619,61 @@ invalid Vision/workspace also remain refusals. The temporary measurement harness
and raw captures stay private; this recipe describes the repeated workload and
its oracles, not a distributed benchmark runner or public reproduction receipt.

### 2026-11-07 — Original-Turn closeout survives automatic Todo archival

The complete CLI comparison in #8257 exposed a lifecycle failure with 101
synthetic Todos. Fresh source-console reproductions on `4a33eb4b5` retain
`todo_archived` on both File and default SQLite after ordinary completion,
durable writeback and one quota debit. The frozen comparison remains unchanged;
these failed episodes are not accepted latency samples.

The proposed repair stays in `todo_terminal_lifecycle.ts`. Its existing
same-Turn fence and exact ordinary-completion receipt verification also qualify
an archived Todo's closeout before the active-only rejection. One local helper
owns both active and archived parent checks. Original claim/lease, task digest,
validation declaration/receipt and provider CAS checks remain in force; archive
selection and retention thresholds are unchanged. This permits only the
original completion's terminal phase, without reopening archived work.

Real File/SQLite console fixtures now complete the large-state sequence and
replay completion, writeback, quota spend and terminal closeout with one debit
and an unchanged independently verified JSON result. The durable CLI regression
uses a real validator and ordinary hard-lease claim: premature settlement,
unrelated archived work, another actor, claim or Turn, and changed completion
intent are refused. Terminal closeout retains the original passed validation
receipt, first completion time and archive state without rerunning the validator.
Existing provider conformance retains source/digest, validation, lease and replay
checks; an isolated real PostgreSQL suite qualifies its shared owner separately.

The author proposal also integrates main `abf144202` (#8161), whose explicit
Goal-scoped checks do not gate local Todo completion. The archive regression
covers a failed Goal commitment alongside the local validator: ordinary and
terminal completion still preserve the original receipt and one debit, never
rerun the Goal checker, and leave the overall check failed. This is author-side
integration coverage; it does not replace independent review or reviewed-repair
wheel qualification.

Independent review of #8264 at `12627eff8` found that changing only `--evidence`
could replace the archived Todo's accepted artifact while returning the old
validation receipt. All four File/SQLite × Goal-check fixtures reproduce this
failure on that head. The revision integrates target main `10f66ec5e` and rejects
different supplied evidence before archived closeout. Its new archived-closeout
receipt also retains the accepted evidence, so a changed retry cannot bypass the
check through receipt recovery. Omitted or empty evidence with a valid completion
note keeps the original value; the CLI's completion-note requirement still applies.
Historical fingerprints and older receipts remain compatible; active
completion behavior is unchanged. The regression checks canonical evidence,
original validation and completion time, and once-only validation/writeback/debit
before and after terminal replay. The earlier reviewed head remains unaccepted;
the revised head still needs independent review.

This is a proposed lifecycle repair enabling the remaining R1/V4 journey and
large-state comparison. Independent exact-head review, reviewed-repair wheel
readback and remaining guard/parser characterization still govern the next
step. It does not upgrade an installed host or qualify interruption/restart,
split installation, full R0/V10/R1, M1/#8011, D2/D5/D6/D3, context/model,
adoption or complete-cost benefit. I3 remains at least one minor release.

## Appendix B: Decision log

| Date | Decision | Owner / approval | Alternatives | Normative sections changed |
Expand Down
45 changes: 45 additions & 0 deletions docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,51 @@ stale claim 使用普通 release 后的 lease。它们必须在 writeback/debit
Vision/workspace 同样保留拒绝。临时测量 harness 和 raw capture 保持私有;这个 recipe
描述重复的 workload/oracle,不是分发的 benchmark runner 或公共复现 receipt。

### 2026-11-07 — 原 Turn 终结在 Todo 自动归档后仍可完成

#8257 的完整 CLI 比较在 101 个合成 Todo 下暴露了生命周期故障。基于
`4a33eb4b5` 的新源码 console 复现在 File 和默认 SQLite 上均保留同一
`todo_archived`:普通完成、持久写回和一次额度扣账已提交。冻结比较保持原样,
这些失败 episode 不计入验收通过的延迟样本。

修复提案仍由 `todo_terminal_lifecycle.ts` 拥有。在仅限 active 的拒绝之前,
复用已有同 Turn fence 与精确普通 completion receipt 校验,验证已归档 Todo
的终结资格。一个文件内 helper 统一 active 和已归档路径的父 receipt 检查。
原 claim/lease、任务 digest、validation 声明与 receipt、provider CAS 校验
继续生效;归档选择与保留阈值保持原样。允许的只是原 completion 的终结阶段,
不会重新开放已归档工作。

真实 File/SQLite console fixture 现在可以完成大状态整轮,并回放 completion、
写回、扣账和 terminal closeout:只有一次扣账,独立验证的 JSON 结果保持原样。
持久 CLI 回归使用真实 validator 和普通 hard-lease claim,拒绝过早结算、
无关归档工作、其他 actor、claim 或 Turn,以及变更后的 completion intent。
终结保留原已通过的 validation receipt、首次完成时间和归档状态,不重复执行
validator。既有 provider conformance 继续检查 source/digest、validation、
lease 和 replay;隔离真实 PostgreSQL suite 单独验证其共享 owner。

作者提案还集成了 main `abf144202`(#8161):显式 Goal 范围的检查不约束局部
Todo 完成。归档回归在局部 validator 之外加入未通过的整体 Goal 承诺检查:
普通完成和终结仍保留原 receipt 与一次扣账,不重复执行 Goal checker,整体
检查继续失败。这是作者阶段的集成覆盖,不能替代独立审阅或审阅修复后的
wheel 资格验证。

#8264 `12627eff8` 的独立审阅发现:只改 `--evidence` 就能替换已归档 Todo
接受的产物,却仍返回旧 validation receipt。File/SQLite × 有无整体 Goal 检查
的四个 fixture 均在该 head 复现失败。修订集成 target main `10f66ec5e`,在归档
终结前拒绝不同的显式 evidence;新归档 closeout receipt 同时保存已接受证据,
让变更后的重试不能通过 receipt recovery 绕过检查。带有效 completion note 时,
省略或空 evidence 保留原值;CLI 原有的完成说明要求继续适用。
历史 fingerprint 和旧 receipt 继续兼容,active completion 行为保持原样。
回归在 terminal replay 前后检查 canonical evidence、原 validation 和完成时间,
以及只执行一次的 validation/writeback/debit。此前被审阅的 head 仍未获验收,
修订 head 仍需独立审阅。

这是支持后续 R1/V4 journey 和大状态比较的生命周期修复提案。下一步仍需
独立 exact-head review、审阅修复后的 wheel 回读,以及剩余 guard/parser
刻画。它不会升级 installed host,也不验收中断/重启、split installation、
完整 R0/V10/R1、M1/#8011、D2/D5/D6/D3、context/model、adoption 或完整成本
收益。I3 继续保留至少一个 minor release。

## 附录 B:决策日志

| 日期 | 决策 | 负责人 / 批准 | 备选 | 变更的规范章节 |
Expand Down
139 changes: 91 additions & 48 deletions loopx/control_plane/coordination/todo_terminal_lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,15 @@ function terminalFailure(
};
}

function completionEvidenceRejection(
input: CoordinationTodoTerminalLifecycleInput,
acceptedEvidence: unknown,
): CoordinationTodoTerminalLifecycleResult | null {
if (input.evidence === null || input.evidence === (acceptedEvidence ?? null)) return null;
return terminalFailure("terminal_completion_receipt_mismatch",
"Archived closeout must retain the evidence accepted by ordinary completion.", {}, "decision_rejection");
}

function terminalRequestSha(input: CoordinationTodoTerminalLifecycleInput): string {
const completionPolicyIdentity = input.completion_policy_request === null
? null
Expand Down Expand Up @@ -1194,6 +1203,59 @@ function semanticDuplicate(
return undefined;
}

async function readSameTurnCompletionReceipt(
store: AuthorityStore,
input: ResolvedCoordinationTodoTerminalLifecycleInput,
normalized: CoordinationTodoTerminalLifecycleInput,
todo: JsonObject,
): Promise<CoordinationTodoTerminalLifecycleResult | null> {
if (input.command !== "complete" || normalized.operation_identity.kind !== "completion_turn" ||
!input.requested_no_followup) return null;
const validationRequired = todo.completion_validation_required === true;
const validationSha256 = todo.completion_validation_sha256;
let fence;
try {
fence = evaluateTodoCompletionFence({schema_version: TODO_COMPLETION_FENCE_REQUEST_SCHEMA,
projection_source: "materialized", goal_id: input.goal_id, todo_id: input.todo_id, todo,
requested_no_followup: input.requested_no_followup,
requested_completion_turn_key: input.requested_completion_turn_key,
requested_completion_identity_source: input.requested_completion_identity_source});
} catch (error) {
return terminalFailure("invalid_todo_completion_transaction",
error instanceof Error ? error.message : "Invalid original completion identity");
}
if (fence.outcome === "continue" && fence.reason === "same_turn_terminal_upgrade") {
const originalInput = {...input,
...(normalized.claim_operation_id === undefined ? {} : {claim_operation_id: normalized.claim_operation_id,
lease_idempotency_key: normalized.lease_idempotency_key, lease_expected_version: normalized.lease_expected_version}),
requested_no_followup: false,
operation_id: completionTurnOperationId(input, false)};
const original = await terminalReceipt(originalInput, terminalRequestSha(originalInput)).read(store);
if (original === null) return terminalFailure("terminal_completion_receipt_required",
"Same-Turn closeout requires the original accepted ordinary completion receipt.", {}, "decision_rejection");
if (original.status !== "replayed") return original;
const originalValidation = original.validation_receipt as JsonObject | null;
const receiptDigest = originalValidation?.validation_declaration_sha256;
// Revision-zero receipts may omit the digest. The exact recovered
// request already binds the canonical declaration, actor and lease;
// later declaration revisions still require the receipt's own digest.
const requiresDigest = Number.isSafeInteger(todo.completion_validation_revision) &&
Number(todo.completion_validation_revision) > 0;
if (original.completion_identity_key !== input.requested_completion_turn_key ||
(validationRequired && (originalValidation === null ||
typeof originalValidation !== "object" || originalValidation.passed !== true ||
(receiptDigest != null && receiptDigest !== validationSha256) ||
(requiresDigest && receiptDigest !== validationSha256))) ||
(input.validation_receipt !== null &&
canonicalAuthoritySha256(input.validation_receipt) !== canonicalAuthoritySha256(original.validation_receipt))) {
return terminalFailure("terminal_completion_receipt_mismatch",
"Original completion validation and identity must still match the completed Todo.", {}, "decision_rejection");
}
return original;
}
return null;
}

/**
* Execute complete/supersede as one provider-neutral CAS transaction.
*
Expand Down Expand Up @@ -1239,6 +1301,12 @@ export async function executeCoordinationTodoTerminalLifecycle(
const receipt = terminalReceipt(input, requestSha);
const replay = await receipt.read(store);
if (replay !== null) {
// New archived closeout receipts preserve accepted evidence separately
// from the historical request fingerprint. Older receipts still replay.
if (Object.hasOwn(replay, "evidence")) {
const rejection = completionEvidenceRejection(input, replay.evidence);
if (rejection !== null) return rejection;
}
// Older accepted lifecycle receipts lack the original quota binding.
// Add it to this response from verified history; never rewrite the receipt.
if (replay.status === "replayed" && isLifecycleCloseout(input) &&
Expand Down Expand Up @@ -1267,7 +1335,9 @@ export async function executeCoordinationTodoTerminalLifecycle(
if (!await authoritySourcesCurrent()) return terminalFailure(AUTHORITY_SOURCE_CHANGED.code,
AUTHORITY_SOURCE_CHANGED.reason, {}, "decision_rejection");
const observation = await receipt.observe(store);
if (observation.kind === "receipt") return observation.result;
if (observation.kind === "receipt") return Object.hasOwn(observation.result, "evidence")
? completionEvidenceRejection(input, observation.result.evidence) ?? observation.result
: observation.result;
if (observation.authority.status !== "loaded") {
return {
schema_version: COORDINATION_TODO_TERMINAL_LIFECYCLE_RESULT_SCHEMA,
Expand Down Expand Up @@ -1360,13 +1430,19 @@ export async function executeCoordinationTodoTerminalLifecycle(
"Todo requirements changed after Turn admission; reread the task and run a fresh Turn",
{todo_id: input.todo_id}, "decision_rejection");
}
let terminalUpgradeReceipt: CoordinationTodoTerminalLifecycleResult | null = null;
if (todo.archive_state !== "active") {
return terminalFailure(
"todo_archived",
"Todo terminal lifecycle requires an active Todo",
{},
"decision_rejection",
);
// Ordinary completion may archive this Todo before its original Turn
// settles. Only its exact accepted parent permits this second phase;
// archive alone grants no new execution or completion authority.
terminalUpgradeReceipt = await readSameTurnCompletionReceipt(store, input, normalized, todo);
if (terminalUpgradeReceipt === null) {
return terminalFailure("todo_archived", "Todo terminal lifecycle requires an active Todo",
{}, "decision_rejection");
}
if (terminalUpgradeReceipt.status !== "replayed") return terminalUpgradeReceipt;
const rejection = completionEvidenceRejection(input, todo.evidence);
if (rejection !== null) return rejection;
}
const authorityTodo = todo;
let edit: ReturnType<typeof prepareUpdatedTodo> | null = null;
Expand Down Expand Up @@ -1607,50 +1683,13 @@ export async function executeCoordinationTodoTerminalLifecycle(
}
}
let completion: ReturnType<typeof reduceTodoCompletionTransaction> | null = null;
let terminalUpgradeReceipt: JsonObject | null = null;
if (input.command === "complete") {
const validationRequired = todo.completion_validation_required === true;
const validationSha256 = todo.completion_validation_sha256;
if (normalized.operation_identity.kind === "completion_turn" && input.requested_no_followup) {
let fence;
try {
fence = evaluateTodoCompletionFence({schema_version: TODO_COMPLETION_FENCE_REQUEST_SCHEMA,
projection_source: "materialized", goal_id: input.goal_id, todo_id: input.todo_id, todo,
requested_no_followup: input.requested_no_followup,
requested_completion_turn_key: input.requested_completion_turn_key,
requested_completion_identity_source: input.requested_completion_identity_source});
} catch (error) {
return terminalFailure("invalid_todo_completion_transaction",
error instanceof Error ? error.message : "Invalid original completion identity");
}
if (fence.outcome === "continue" && fence.reason === "same_turn_terminal_upgrade") {
const originalInput = {...input,
...(normalized.claim_operation_id === undefined ? {} : {claim_operation_id: normalized.claim_operation_id,
lease_idempotency_key: normalized.lease_idempotency_key, lease_expected_version: normalized.lease_expected_version}),
requested_no_followup: false,
operation_id: completionTurnOperationId(input, false)};
const original = await terminalReceipt(originalInput, terminalRequestSha(originalInput)).read(store);
if (original === null) return terminalFailure("terminal_completion_receipt_required",
"Same-Turn closeout requires the original accepted ordinary completion receipt.", {}, "decision_rejection");
if (original.status !== "replayed") return original;
const originalValidation = original.validation_receipt as JsonObject | null;
const receiptDigest = originalValidation?.validation_declaration_sha256;
// Revision-zero receipts may omit the digest. The exact recovered
// request already binds the canonical declaration, actor and lease;
// later declaration revisions still require the receipt's own digest.
const requiresDigest = Number.isSafeInteger(todo.completion_validation_revision) &&
Number(todo.completion_validation_revision) > 0;
if (original.completion_identity_key !== input.requested_completion_turn_key ||
(validationRequired && (originalValidation === null ||
typeof originalValidation !== "object" || originalValidation.passed !== true ||
(receiptDigest != null && receiptDigest !== validationSha256) ||
(requiresDigest && receiptDigest !== validationSha256))) ||
(input.validation_receipt !== null &&
canonicalAuthoritySha256(input.validation_receipt) !== canonicalAuthoritySha256(original.validation_receipt))) {
return terminalFailure("terminal_completion_receipt_mismatch",
"Original completion validation and identity must still match the completed Todo.", {}, "decision_rejection");
}
terminalUpgradeReceipt = original;
if (terminalUpgradeReceipt === null) {
terminalUpgradeReceipt = await readSameTurnCompletionReceipt(store, input, normalized, todo);
if (terminalUpgradeReceipt !== null && terminalUpgradeReceipt.status !== "replayed") {
return terminalUpgradeReceipt;
}
}
if (validationRequired && !implicitMonitorNoChange &&
Expand Down Expand Up @@ -1985,6 +2024,10 @@ export async function executeCoordinationTodoTerminalLifecycle(
todo_id: input.todo_id,
command: input.command,
changed,
// Freeze this archived phase's evidence for receipt-only recovery without
// adding a fresh authority read or changing legacy operation fingerprints.
...(authorityTodo.archive_state !== "active" && terminalUpgradeReceipt !== null
? {evidence: authorityTodo.evidence ?? null} : {}),
...(input.decision_outcome === null ? {} : {decision_outcome: input.decision_outcome}),
...(followthrough?.unblock_resume ? {unblock_resume: followthrough.unblock_resume} : {}),
...(followthrough?.decision_scope_resolution ? {decision_scope_resolution: followthrough.decision_scope_resolution} : {}),
Expand Down
Loading
Loading