Skip to content
Open
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
18 changes: 18 additions & 0 deletions apps/presentation/dashboard/smoke/action-review-plan-smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,24 @@ check(isStaleActionFailure({ error_code: "action_conflict" }), "Typed conflicts
check(isStaleActionFailure({ proposal: { status: "stale" } }), "Typed stale proposal survives error wrapping");
check(!isStaleActionFailure({ error_code: "canonical_action_failed", error: "conflict with unrelated external service" }), "Error wording cannot classify source state");

for (const [action_kind, operation] of [["todo.update", "complete"], ["monitor.update", "stop"]] as const) {
for (const status of ["applying", "failed"] as const) {
const terminal = typedActionProposalSchema.parse({...proposal, action_kind, status,
normalized_parameters: {goal_id: "sample-goal", todo_id: "todo_work", operation},
canonical_update_basis: {schema_version: "loopx_chat_canonical_terminal_basis_v0",
provider_revision: "revision-1", registry_sha256: "a".repeat(64), source_authority: "file_v0"},
failure: {error_code: "canonical_update_projection_pending", message: "Display pending", retry_safe: true}});
const plan = compileActionReviewPlan(terminal);
check(plan.canApply && plan.retryOriginal === true, "Terminal recovery retries the original proposal");
check(plan.reason === "canonical_update_projection_pending", "Pending display is distinct from failed business mutation");
check(compileActionReviewPlan({...terminal, status: "stale"}).canApply === false, "A stale terminal preview must be regenerated");
check(compileActionReviewPlan({...terminal, normalized_parameters: {...terminal.normalized_parameters, operation: "edit"}}).canApply === false,
"A terminal review basis cannot enable retries of unrelated operations");
check(compileActionReviewPlan({...terminal, status: "applied", receipt: {projection_verified: true}}).interaction === "completed",
"Only current display readback completes terminal presentation");
}
}

const operationProposal = typedActionProposalSchema.parse({
...proposal,
proposal_id: "operation-1",
Expand Down
2 changes: 1 addition & 1 deletion apps/presentation/dashboard/src/data/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ export const typedActionProposalSchema = z.object({
checkpoint: z.record(z.string(), z.unknown()).nullable().optional(),
failure: z.record(z.string(), z.unknown()).nullable().optional(),
canonical_update_basis: z.object({
schema_version: z.literal("loopx_chat_canonical_update_basis_v0"),
schema_version: z.enum(["loopx_chat_canonical_update_basis_v0", "loopx_chat_canonical_terminal_basis_v0"]),
provider_revision: z.string().min(1),
source_authority: z.enum(["file_v0", "sqlite_v0"]),
registry_sha256: z.string().regex(/^[a-f0-9]{64}$/),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -694,10 +694,10 @@ const en = {
"actionReview.readback_unverified": "The action returned without verified readback. Completion is not confirmed; recheck the state.",
"actionReview.operation_group_confirmation": "This exact request can only be confirmed on its original card in the bound Feishu group. The Dashboard does not expose a local execution control.",
"actionReview.operation_result_delivery_pending": "The operation outcome was recorded, but the original group result card has not passed readback verification yet.",
"drawer.recoverEditResult": "Recover edit result",
"drawer.recoverEditResult": "Recover operation result",
"drawer.retryOriginal": "Retry original operation",
"actionReview.canonical_update_retry": "The edit is not yet verified. Retry this operation to recover its result.",
"actionReview.canonical_update_projection_pending": "The edit was committed; display delivery is pending. Retry this operation to restore the current view.",
"actionReview.canonical_update_projection_pending": "The operation was committed; display delivery is pending. Retry this operation to restore the current view.",
"actionReview.apply_failed": "Execution did not complete. Check the failure and regenerate the preview before retrying.",
"actionReview.inactive_proposal": "This proposal is no longer ready to execute. Recheck it before continuing.",
"proposal.gate.default": "Host confirmation required",
Expand Down Expand Up @@ -1782,10 +1782,10 @@ const zhCN: Record<WorkspaceMessageKey, string> = {
"actionReview.readback_unverified": "操作返回但未通过读回验证。尚不能确认完成,请重新检查状态。",
"actionReview.operation_group_confirmation": "这份精确请求只能在已绑定飞书群的原始卡片确认;Dashboard 不提供本地执行入口。",
"actionReview.operation_result_delivery_pending": "操作结果已经记录,但原群结果卡尚未通过回读核验。",
"drawer.recoverEditResult": "恢复编辑结果",
"drawer.recoverEditResult": "恢复操作结果",
"drawer.retryOriginal": "重试原操作",
"actionReview.canonical_update_retry": "编辑结果尚未确认。重试此操作以恢复原结果。",
"actionReview.canonical_update_projection_pending": "编辑已提交,展示尚未同步。重试此操作以恢复当前视图。",
"actionReview.canonical_update_projection_pending": "操作已提交,展示尚未同步。重试此操作以恢复当前视图。",
"actionReview.apply_failed": "执行未完成。请检查失败原因并重新生成预览后再试。",
"actionReview.inactive_proposal": "此提案当前不可执行。请重新检查后再继续。",
"proposal.gate.default": "需要宿主确认",
Expand Down
5 changes: 5 additions & 0 deletions docs/architecture/rfcs/loopx-overall-roadmap-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,11 @@ a parallel team-orchestration program.

### R5: TS Convergence and Local Persistence

L2/L5 terminal checkpoint: Agent completion and Monitor stop now use source-bound
TS validation and canonical receipt/display recovery. Real-provider mixed-graph
counterexamples cover concurrent changes and lost responses. [Scope and remaining
boundaries](../../reference/canonical-terminal-review.md); this does not settle R5 or D1–D3.

L3 checkpoint: standalone acquisition/takeover, atomic claim admission and maintenance share typed lease facts/rules and provider opening. Exact acquisition retry verifies current execution proof; real CLI completion can recover missing Markdown display. Full-state scope conflicts, process interruption and File/SQLite/PostgreSQL read-only rehearsal are covered. [Remaining executor and integration boundaries](../../reference/canonical-lease-renew.md); R5, D2/D3 and default qualification remain open.

- **Owner:** TS T0–T4 and shared-authority D1–D3; retain their numbering and gates.
Expand Down
4 changes: 4 additions & 0 deletions docs/architecture/rfcs/loopx-overall-roadmap-v0.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ steer;传输成功不关闭请求。复用现有 R2/R3 后继,不另开平

### R5:TS 收敛与本地持久化

L2/L5 终结检查点:Agent 完成、Monitor 停止现使用绑定来源的 TS 验证与 canonical
回执/显示恢复;真实 provider 的混合图反例覆盖并发变化和响应丢失。
[范围及剩余边界](../../reference/canonical-terminal-review.zh-CN.md),不据此结清 R5 或 D1–D3。

L3 检查点:独立领取/接管、原子 claim 准入与维护共用 typed lease facts/rules 和 provider opening;原领取重试校验当前执行 proof,真实 CLI 完成可恢复缺失 Markdown 展示。覆盖完整 scope 冲突、进程中断及 File/SQLite/PostgreSQL 只读演练。[剩余 executor 与集成边界](../../reference/canonical-lease-renew.md);R5、D2/D3 和默认化资格仍未完成。

- **Owner:** TS RFC T0–T4、shared-authority D1–D3;保留两套编号及原门禁。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@

## Current implementation checkpoint

The terminal caller family now binds review and validation to the canonical
source and recovers historical receipts independently of private argv. Agent
completion and Monitor stop share current-head display acknowledgement with
ordinary edits. [Caller and recovery contract](../../reference/canonical-terminal-review.md).
This advances L2/L5 without closing executor-held fences, D1–D3 or default
onboarding; the conditional 5–8-package estimate below remains unchanged.

The local registry witness now spans canonical create/claim/update/Monitor poll
and terminal mutations through one TS owner. File, SQLite and service-injected
PostgreSQL execute the same source checks and preserve historical receipts.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@

## 当前实现检查点

终结 caller 现将审核与验证绑定 canonical 来源,历史回执恢复不再依赖私有 argv。
Agent 完成和 Monitor 停止复用普通编辑的当前 head 显示确认。
[调用与恢复合同](../../reference/canonical-terminal-review.zh-CN.md)。此批推进 L2/L5,
未闭合 executor-held fence、D1–D3 或默认 onboarding,下文有条件的 5–8 批估算不变。

本地 registry witness 现经同一 TS owner 覆盖 canonical create/claim/update、
Monitor poll 与 terminal mutation;File、SQLite、service-injected PostgreSQL
执行相同来源检查并保留历史回执。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ Retain T0 caller/parity inventory, T1/T2 transaction/effect convergence, T3 comp

## Current implementation checkpoint

Terminal review and validation now converge in the existing TS terminal owner.
Agent completion and Monitor stop reuse Chat's canonical receipt-first recovery
and display acknowledgement; v2 binds validation continuation to its source
revision and resolves private declarations only after admission/replay. Python's
separate terminal review selection and eager declaration sequencing are removed.
This closes a T1/T2/L2 terminal caller family, not the remaining leased metadata,
executor fences or T4 retirement. [Semantics, crossings and rollback](../../reference/canonical-terminal-review.md).

Linked User completion now has one typed owner, `todos/user_completion.ts`.
The terminal transaction commits exact-target scope consumption, reject/cancel
outcomes and conditional resume with its own completion/receipt; the Markdown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@

## 当前实现检查点

终结审核与验证已收敛到既有 TS terminal owner:Agent 完成、Monitor 停止复用 Chat
先恢复 canonical 回执再确认显示的路径;v2 把验证 continuation 绑定来源 revision,
准入/回放之后才请求私有声明。删除 Python 的终结操作审核分流和提前解析声明编排。
这闭合 T1/T2/L2 的一组真实终结 caller,剩余 leased metadata、executor fence 和 T4
仍未完成。[语义、调用次数与回滚](../../reference/canonical-terminal-review.zh-CN.md)。

Canonical create/claim/update/Monitor poll/terminal 事务现共用
`coordination/authority_source.ts`;Python adapter 经 `authority_registry_source`
在注册/grant 投影前后校验来源。外部验证结束后保留原 witness,在新 effect/提交前
Expand Down
89 changes: 89 additions & 0 deletions docs/reference/canonical-terminal-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Canonical terminal review and validation

On an explicitly promoted local Goal, Agent completion and Monitor stop now use
the same reviewed recovery path as Todo edits and User completion. The initiating
Chat action binds the complete provider revision and registry digest, preserves
one operation identity, and acknowledges display only after the existing
projection outbox confirms the current view.

## Operate and recover

For ordinary CLI completion, retain the same explicit completion identity after
a lost response:

```bash
loopx todo complete --goal-id example-goal --todo-id todo_work \
--agent-id agent-a --completion-identity-key reviewed-result --no-follow-up
loopx todo list --goal-id example-goal --todo-id todo_work
loopx todo project-markdown --goal-id example-goal --execute
```

Use `--no-follow-up` only when no successor is needed. Leased work additionally
requires its current `--task-lease-idempotency-key` and
`--task-lease-expected-version`; owner confirmation is not a lease or a lifecycle
grant. Chat users retry the same failed proposal. A stale proposal requires a
fresh preview, not a replacement identity that bypasses review.

| Boundary | Observable result |
| --- | --- |
| Provider/registration changes before a fresh reviewed completion | Reject before private validation execution; Chat marks the proposal stale |
| Provider changes during validation | Reject the old validation result; Todo remains unfinished |
| Lease expires during validation | Recheck runtime time and reject stale execution proof |
| Canonical commit succeeds, display delivery fails | Business remains committed; Chat reports recoverable failure without a successful display receipt |
| Response/action receipt is lost after commit | Retry recovers the original business receipt before checking current review freshness |
| Same operation carries a changed reviewed note, evidence, reason or basis | Reject identity reuse; never silently acknowledge the changed intent |
| Private declaration is unavailable after successful completion | Business receipt can recover from its public commitment; lossless display recovery still requires restoring the original declaration |

The TypeScript terminal owner performs admission, source checks, validation
planning, lease retirement, linked effects, CAS and receipt recovery. Python
transports facts, resolves private argv only when requested, executes declared
validation and drains projection. It does not decide whether a stale validation
can complete work. The preview executes no validator. Separate user-completion
edits retain their existing combined edit/terminal semantics and old stored Chat
proposals retain their existing protocol.

## Wire and migration boundary

The current Python terminal adapter sends
`loopx_local_coordination_todo_terminal_lifecycle_request_v2`. The existing
terminal method accepts these bounded additions:

- `review_basis`, when present, contains exactly `provider_revision` and
`registry_sha256`. It binds reviewed intent and is part of receipt identity.
- `validation_source_provider_revision` is null before an issued effect and is
the returned revision on continuation. It is a freshness constraint, not new
operation identity. Both caller validation and Goal acceptance validation
require it in v2.
- `validation_declaration_sha256` carries the canonical public commitment.
Historical recovery precedes private declaration resolution. Fresh execution
still requires the matching declaration and current authorization.

The existing method may return `resolve_validation` before `execute_validation`.
Both responses bind the source revision; neither commits the business operation.
For a validated fresh completion the host crosses the runtime boundary three
times (resolve, plan effects, commit), versus two before this change. Unvalidated
completion and historical recovery remain one terminal request. This bounded
extra crossing makes receipt recovery independent of host-local argv; it can
disappear when the native host owns declaration resolution and effect execution.

v0/v1 retain their old request fingerprints and validation contract. They reject
the new fields rather than silently discarding obligations. A v2 request without
review preserves the existing CLI terminal fingerprint. Existing receipts are
not rewritten. The public completion facade rejects a reviewed canonical request
if authority has reverted to an unpromoted legacy path.

No provider default, promotion, permission, retention or storage format changes.
Rollback restores compatible code while retaining provider data, receipts and
writer fences. Older code cannot execute v2; regenerate a preview with compatible
code instead of stripping its review fields. Markdown stays a permanent display.
These changes close the terminal review/recovery family, not all leased metadata
updates, executor-held external-effect fencing, D1–D3 or whole-Goal cutover.

Shared provider conformance uses the complete production-scale fixture, both
native and imported records, stale review/validation, expired proof, lost commit
response and unchanged non-target state. Real File/SQLite Chat HTTP tests exercise
the packaged entrypoint and retry feedback. The frontend runtime decoder and shared action-review plan now recognize the
terminal basis for exactly Agent completion and Monitor stop. The packaged Chat
bundle includes the original-operation retry path and distinguishes pending
display from verified completion; no new configuration or visual control is required. Lark receives no new
command or transport in this slice.
Loading
Loading