From d0971830525f230d0f90692d98f73fd3023cc87e Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Sun, 11 Oct 2026 23:59:32 +0800 Subject: [PATCH 1/3] fix(todos): derive external-wait successors from source facts Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- loopx/control_plane/todos/resume_condition.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/loopx/control_plane/todos/resume_condition.ts b/loopx/control_plane/todos/resume_condition.ts index 33a6f11674..45274acf2e 100644 --- a/loopx/control_plane/todos/resume_condition.ts +++ b/loopx/control_plane/todos/resume_condition.ts @@ -119,7 +119,6 @@ interface TodoItem extends JsonObject { status?: string; task_class?: string; resume_when?: string; - resume_ready?: boolean; resume_monitor_generation?: number; material_change_generation?: number; } @@ -168,7 +167,6 @@ function todoItem(value: unknown, label: string): TodoItem { // normalized it. Lower-casing the entire token makes offset timestamps // fail strict parsing on the read path. if (resumeWhen !== undefined) item.resume_when = resumeWhen; - if (typeof raw.resume_ready === "boolean") item.resume_ready = raw.resume_ready; const resumeGeneration = nonNegativeInteger( raw.resume_monitor_generation, `${label}.resume_monitor_generation`, @@ -832,7 +830,14 @@ function externalWaitSuccessors( "external_wait_successor_not_open_advancement", ); } - if (successor.resume_when && successor.resume_ready !== true) { + // Readiness is a projection, never an authoring fact. Reuse the same + // predicate/diagnosis over this locked source snapshot. External provider + // or clock evidence is unavailable here and cannot be replaced by a flag. + const spec = parseResumeWhen(successor.resume_when); + const diagnosis = spec ? diagnoseTodoResumeCondition( + conditionFor(successor, spec, byId, [], null, null), successor.todo_id, + ) : null; + if (successor.resume_when && diagnosis?.state !== "satisfied") { throw new EffectRuntimeRequestError( "external-wait successor must be runnable, not resume-gated", "external_wait_successor_resume_gated", From 63c26ecf50c40afdd4d4801b346622dfa61d6b96 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Sun, 11 Oct 2026 23:59:33 +0800 Subject: [PATCH 2/3] test(todos): cover source-satisfied successors and forged readiness Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../test_native_todo_planning_update.py | 66 +++++++++++++++++++ .../public_todo_update.test.ts | 51 ++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/tests/control_plane/test_native_todo_planning_update.py b/tests/control_plane/test_native_todo_planning_update.py index beb0d0c55e..d69fd0822d 100644 --- a/tests/control_plane/test_native_todo_planning_update.py +++ b/tests/control_plane/test_native_todo_planning_update.py @@ -567,3 +567,69 @@ def test_native_target_lookup_keeps_public_value_error(tmp_path: Path, todo_id: role=role, agent_id="agent-a", text="Correction") assert records(registry) == before assert not state.exists() + + +@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"]) +@pytest.mark.parametrize("condition_kind", ["todo_done", "monitor_changed"]) +@pytest.mark.parametrize("satisfied", [False, True]) +def test_public_cli_external_wait_uses_current_successor_source( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str, condition_kind: str, satisfied: bool, +) -> None: + if provider == "sqlite": + isolate_sqlite_runtime(tmp_path, monkeypatch) + registry, state = fixture(tmp_path, False) + items = list(records(registry).values()) + successor = next(item for item in items if item["todo_id"] == "todo_other") + successor.update(claimed_by="agent-a", resume_when=f"{condition_kind}:todo_evidence", + resume_monitor_generation=1 if condition_kind == "monitor_changed" else None) + items.extend([ + {"schema_version": "todo_item_v0", "done": condition_kind == "todo_done" and satisfied, + "archive_state": "active", "source_section": "Agent Todo", + "todo_id": "todo_evidence", "role": "agent", "status": "done" if condition_kind == "todo_done" and satisfied else "open", + "task_class": "advancement_task" if condition_kind == "todo_done" else "continuous_monitor", + "text": "Historical source evidence", "material_change_generation": 2 if satisfied else 1, + "claimed_by": "agent-a", "cadence": "1h", "watch_only": "true"}, + {"schema_version": "todo_item_v0", "done": False, "archive_state": "active", "source_section": "Agent Todo", + "todo_id": "todo_monitor", "role": "agent", "status": "open", "text": "Await new observation", + "task_class": "continuous_monitor", "claimed_by": "agent-a", "material_change_generation": 3, + "cadence": "1h", "watch_only": "true"}, + ]) + if provider == "legacy": + rows = [] + for item in items: + metadata = {key: item[key] for key in ("todo_id", "status", "task_class", "claimed_by", + "note", "resume_when", "resume_monitor_generation", "material_change_generation", + "cadence", "watch_only") if item.get(key) is not None} + rows.append(f"- [{'x' if item['status'] == 'done' else ' '}] {item['text']}\n" + + format_todo_metadata_line(**metadata)) + state.write_text("# Goal\n\n## Agent Todo\n\n" + "\n".join(rows) + "\n", encoding="utf-8") + else: + projection = build_todo_runtime_shadow_projection(goal_id="goal-a", todos=items, handoff_mode="soft_claim") + initialize_canonical_authority(tmp_path / "runtime", "goal-a", projection, state_path=state, provider=provider) + state.unlink() + before = records(registry) + assert before["todo_other"]["resume_ready"] is satisfied + args = ["--resume-when", "monitor_changed:todo_monitor", "--successor-todo-id", "todo_other"] + if provider != "legacy": + args += ["--update-operation-id", "source-satisfied-successor"] + if not satisfied: + source = list_goal_todos(registry_path=registry, goal_id="goal-a") + display = state.read_bytes() if state.exists() else None + rejected = update(registry, *args, ok=False) + assert "external-wait successor must be runnable, not resume-gated" in json.dumps(rejected) + assert list_goal_todos(registry_path=registry, goal_id="goal-a") == source + assert (state.read_bytes() if state.exists() else None) == display + return + update(registry, *args, "--dry-run") + assert records(registry) == before + update(registry, *args) + after = records(registry) + assert after["todo_target"]["status"] == "open" + assert after["todo_target"]["resume_ready"] is False + assert after["todo_target"]["resume_monitor_generation"] == 3 + assert after["todo_target"]["successor_todo_ids"] == ["todo_other"] + assert after["todo_other"] == before["todo_other"] + assert after["todo_other"]["resume_ready"] is True + if provider != "legacy": + assert update(registry, *args)["status"] == "replayed" + assert records(registry) == after diff --git a/tests/control_plane_ts/public_todo_update.test.ts b/tests/control_plane_ts/public_todo_update.test.ts index 2514b4bad2..b534c26ef5 100644 --- a/tests/control_plane_ts/public_todo_update.test.ts +++ b/tests/control_plane_ts/public_todo_update.test.ts @@ -91,3 +91,54 @@ test("complete production-scale snapshot stays immutable and is not display-capp assert.equal((result.metadata_updates as JsonObject).resume_monitor_generation, 3); assert.deepEqual(fixture, snapshot); }); + + +test("external-wait successor readiness is derived from the locked dependency snapshot", () => { + for (const resume_ready of [undefined, false, true]) { + for (const task_class of ["advancement_task", "continuous_monitor"]) { + const dependency = {todo_id: "todo_complete", role: "agent", status: "done", task_class, + archive_state: "archived", resume_when: "todo_done:todo_missing"}; + const ready = {...successor, resume_when: "todo_done:todo_complete", resume_ready}; + const items = [todo, monitor, ready, dependency]; + const before = structuredClone(items); + const result = plan({successor_todo_ids: [successor.todo_id]}, todo, {items}); + assert.equal((result.external_wait_transition as JsonObject).state, "already_waiting"); + assert.deepEqual(items, before); + } + } +}); + +test("external-wait successor monitor fence uses source generation rather than a display flag", () => { + for (const generation of [3, 4]) { + const ready = {...successor, resume_when: "monitor_changed:todo_evidence", + resume_monitor_generation: 3, resume_ready: generation === 3}; + const items = [todo, monitor, {...monitor, todo_id: "todo_evidence", material_change_generation: generation}, ready]; + if (generation === 4) { + assert.ok(plan({successor_todo_ids: [successor.todo_id]}, todo, {items}).external_wait_transition); + } else { + assert.throws(() => plan({successor_todo_ids: [successor.todo_id]}, todo, {items}), + error => (error as {code?: string}).code === "external_wait_successor_resume_gated"); + } + } +}); + +test("forged successor readiness cannot waive pending, invalid or unavailable source evidence", () => { + for (const extra of [ + {resume_when: "todo_done:todo_missing"}, + {resume_when: "todo_done:todo_successor"}, + {resume_when: "todo_done:todo_waiting"}, + {resume_when: "todo_done:todo_monitor"}, + {resume_when: "monitor_changed:todo_monitor"}, + {resume_when: "monitor_changed:todo_monitor", resume_monitor_generation: 3}, + {resume_when: "pr_merged:owner/repo#12"}, + {resume_when: "capacity_available:shell"}, + {resume_when: "resume_at:2020-01-01T00:00:00Z"}, + {resume_when: "note_contains:approved"}, + ]) { + const ready = {...successor, ...extra, resume_ready: true}; + assert.throws(() => plan({successor_todo_ids: [successor.todo_id]}, todo, + {items: [todo, monitor, ready]}), + error => (error as {code?: string}).code === "external_wait_successor_resume_gated", + extra.resume_when); + } +}); From 2f919407ca8378ccce84588b6687ea209dbca55f Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Sun, 11 Oct 2026 23:59:33 +0800 Subject: [PATCH 3/3] docs(todos): disclose successor source-proof behavior and remaining gates Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- .../rfcs/loopx-overall-roadmap-v0.md | 2 +- .../rfcs/loopx-overall-roadmap-v0.zh-CN.md | 2 +- .../quota-blocked-causal-closeout-v0.md | 28 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/architecture/rfcs/loopx-overall-roadmap-v0.md b/docs/architecture/rfcs/loopx-overall-roadmap-v0.md index 0647fd5f97..cff4f272b3 100644 --- a/docs/architecture/rfcs/loopx-overall-roadmap-v0.md +++ b/docs/architecture/rfcs/loopx-overall-roadmap-v0.md @@ -79,7 +79,7 @@ P0 blocks correctness or continuity in the current user journey. P1 enables repe | **S4 Runtime/host/daemon · P0/P1** | Attached/managed, Turn, broker, runtime connectors and Desktop repairs exist; registration does not establish executable capacity | Qualify multi-Turn supervision for one real supported combination; restart/cancel/drain/stop retain work and fence old executors. Then expand host parity, unique service-profile ownership, clean installation and upgrades; show unsupported adapter capabilities | | **S5 Frontend, Lark and human interaction · P0/P1** | Local chat, settings, proposals and partial Goal Channel verticals exist; Goal Overview, Tasks, the W1 work map, usage, acceptance readback and lifecycle stop ship as separate regions without one page owner; shared audience/session/work readback needs qualification | One journey spans settings, work graph, handoff, blockers, cost, corrections, artifacts and return. **Page:** the [Goal workspace](goal-workspace-surface-v0.md) composes that journey per Goal over shared typed projections: a persistent masthead with scoped Stop, vitals with one meaning per figure (named window and coverage, unknown is not zero), work grouped by owner, results and an all-work rail. M1–M2 come first, after first-screen preview approval. **Cases:** reconnect/repeated-click/stale/original-route. **Realtime IM** reuses Chat/Turn: isolate independent conversations on one listener, then qualify ordinary DM onboarding, explicit role selection, busy-session admission, provisional progress, media and exact permission callbacks through [the shared operational contract](capable-manager-semantic-handoff-v0.md#10-operational-contract). **Team:** [Live team workspace](live-team-workspace-v0.md) makes exchange, revision and original-coordinator continuation visible. Its [Work-scale map track](live-team-workspace-v0.md#11-delivery-order-and-relationship-to-aggressive-r2-progress) draws each Goal's typed Todo relations first (W1, the Goal workspace's Map lens), then live state and outputs on the same nodes. Then intelligent review, keyboard accessibility and a launcher-only command palette, bilingual terminology, actionable errors and offline degradation; interrupt only for actual decisions | | **S6 Materials, evidence, memory and learning · P1** | Authority registry, material lifecycle/frontier, decision context, reward memory and turn recall exist; explicit project material packets and source-verifier gates are implemented, while private source initialization/Core binding and ordinary-conversation adoption remain unqualified; direction baseline and parts of attribution remain proposed | Connect material revision→same-Agent read→decision reference→artifact/outcome. Expose expiry/revocation/source loss and forgetting policy. Handoff preserves decision-relevant summaries and authorized artifacts; qualify OpenViking/Obelisk as optional providers. Prove causal utility with controls, not relevance alone | -| **S7 Budget, scheduling and fleet scale · P0 observation/P1–P2 expansion** | Quota/scheduler and partial usage aggregates exist; full provider cost, distributed reservations and hundred-Agent concurrency need evidence | Separate configured budget, admission, consumption and estimates; unknown is not zero and replay cannot double-charge. R7 pagination/bounded summaries and [complete-history transport](typescript-control-plane-migration-v0.md), including refresh/replay/single-debit evidence beyond the RPC limit; provider/host limits, fairness, backpressure, event wake and isolation; report registration/activity/throughput and cost per accepted outcome separately. Receipt-backed semantic progress closes its original host Turn without completing an open/waiting Todo; File/SQLite guard replay preserves verified writeback when a later dependency wait changes the frontier and covers automatic quiet writeback during a receipt-owned retry, preserving independent work and monitor clocks. [Full quota and TurnEnvelope](../../reference/protocols/turn-envelope-v0.md) retain the original executable settlement command and single debit. Follow [the quota settlement contract](../../quota-allocation.md#receipt-backed-settlement-progress). Cross-Todo continuation isolation and live host backoff/readback remain separate acceptance; this bounded repair does not qualify fleet scale or host latency. | +| **S7 Budget, scheduling and fleet scale · P0 observation/P1–P2 expansion** | Quota/scheduler and partial usage aggregates exist; full provider cost, distributed reservations and hundred-Agent concurrency need evidence | Separate configured budget, admission, consumption and estimates; unknown is not zero and replay cannot double-charge. R7 pagination/bounded summaries and [complete-history transport](typescript-control-plane-migration-v0.md), including refresh/replay/single-debit evidence beyond the RPC limit; provider/host limits, fairness, backpressure, event wake and isolation; report registration/activity/throughput and cost per accepted outcome separately. Receipt-backed semantic progress closes its original host Turn without completing an open/waiting Todo; File/SQLite guard replay preserves verified writeback when a later dependency wait changes the frontier and covers automatic quiet writeback during a receipt-owned retry, preserving independent work and monitor clocks. [Full quota and TurnEnvelope](../../reference/protocols/turn-envelope-v0.md) retain the original executable settlement command and single debit. Follow [the quota settlement contract](../../quota-allocation.md#receipt-backed-settlement-progress). External-wait authoring now derives successor readiness from the locked Todo snapshot through the existing resume owner, covering completed dependencies and monitor generation fences without trusting display flags. Source-qualified continuation and live host backoff/readback remain separate acceptance; this bounded repair does not qualify fleet scale or host latency. | | **S8 Capabilities, extensions and domain integration · P1/P2** | Capability catalog, extension lifecycle, hooks, engineering/research/content/office capabilities and computer-use contracts exist | First exercise the shared control plane with existing issue-fix/PR-review and material/research callers. Every provider has readiness/version/permissions/default-off/uninstall/rollback/isolation and real-entry evidence. New domain effects start with one simulated operation, not a marketplace or workflow DSL | | **S9 Identity, authority, privacy and trust · continuous P0/P1–P2 remote** | Public/private scope, capability gates, fencing and confirmation contracts belong to existing owners | R1/R3 cover sender/audience/artifact scope and stale authority; R6 authenticates tenant/Goal/actor/host, rotation/revocation and least privilege. Qualify credential custody, untrusted tool/document inputs, dependency supply chain, audit retention/deletion and vulnerability response through real paths; roles/messages/memory mint no write authority | | **S10 Reliability, diagnostics and operations · P0/P1** | Recovery/canary, read-only diagnostics prototype and DSH event adapter exist; C0/C1, overhead and full operations qualification are open | Failure classification→observable state→recovery drill→regression prevention; process/storage/network/delivery failures and data growth. Accepted Chat requests must settle even when runtime preparation fails before dispatch; qualify missing runtime assets, stop races and recovery without replay under [the shared conversation operational contract](capable-manager-semantic-handoff-v0.md#10-operational-contract). A context/provider read returning after the stop wait must honor the persisted Turn and exact Session claim: no late provider dispatch or handoff, even when a fresh request has completed. This bounded GQ08 repair does not certify upstream interrupt fidelity or effects already admitted elsewhere. Use [bounded repair lookup and targeted diagnostics](../../../skills/loopx-self-repair/references/targeted-diagnostics.md) to reduce redundant reads above the provider boundary; measure backend-specific cold/warm reads, writes and lock waits separately. Freeze SLO/RPO/RTO/capacity/retention boundaries and measure before qualification. Lark transport backpressure and drain retain the App consumer lease; unpersisted buffered lines are not accepted work or completion receipts. Runbooks include upgrade, restore, stop and human takeover; test counts do not prove recovery | diff --git a/docs/architecture/rfcs/loopx-overall-roadmap-v0.zh-CN.md b/docs/architecture/rfcs/loopx-overall-roadmap-v0.zh-CN.md index 5193c22a2c..7a7ee368fb 100644 --- a/docs/architecture/rfcs/loopx-overall-roadmap-v0.zh-CN.md +++ b/docs/architecture/rfcs/loopx-overall-roadmap-v0.zh-CN.md @@ -65,7 +65,7 @@ managed 与 attached 的工作对话都应能持续在 LoopX 中进行:沿用 | **S4 runtime/host/daemon · P0/P1** | attached/managed、Turn、broker、runtime connector 和 Desktop 修复存在;“registered”不等于可执行 | 选择一个真实合格组合完成多 Turn supervision;restart/cancel/drain/stop 不丢工作且旧 executor 被 fence。之后扩 host parity、service-profile 唯一 owner、干净安装与版本升级;按 adapter 能力显示不支持项 | | **S5 前端、Lark 与人机交互 · P0/P1** | 本地对话、settings、proposal 和部分 Goal Channel vertical 已有;Goal 概览、任务、W1 工作地图、用量、验收回读与生命周期停止作为分散区域已交付,但没有统一页面 owner;统一受众/会话/工作回读仍需资格 | 用一个团队旅程贯穿设置、工作图、handoff、阻塞、成本、修订、产物和回报。**页面:**[Goal 工作区](goal-workspace-surface-v0.zh-CN.md)在共享 typed projection 之上按 Goal 组合这条旅程:带限定范围停止的常驻页头、每个数字只有一种含义的指标(标明窗口与覆盖度,未知不是零)、按负责人分组的工作、成果与全部工作侧栏;先做 M1–M2,并先取得首屏预览批准。**场景:**验证重连/重复点击/stale/原路反馈。**实时 IM** 复用 Chat/Turn:先让同一 listener 下的独立会话互不阻塞,再按[共享运行契约](capable-manager-semantic-handoff-v0.zh-CN.md#10-运行契约)验收普通私聊开通、显式角色选择、忙碌会话准入、临时进度、媒体和精确权限回调。**团队:**[团队实时工作区](live-team-workspace-v0.zh-CN.md)让交换、修订与原协调员继续推进可见;其[工作尺度地图分线](live-team-workspace-v0.zh-CN.md#11-交付顺序与激进推进-r2-的关系)先画出每个 Goal 的类型化 Todo 关系(W1,即 Goal 工作区的地图镜头),再在同一节点叠加实时状态与产出。再做 intelligent review、无障碍键盘流程与只作启动器的命令面板、中英术语、错误可恢复和离线降级;只在真实决策处打断人 | | **S6 材料、证据、记忆与学习 · P1** | authority registry、material lifecycle/frontier、decision context、reward memory、turn recall 已有;方向基线和部分归因仍是提案 | 先打通“材料 revision→同 Agent 阅读→决策引用→产物/结果”;失效、撤销、来源消失与遗忘策略可回读。handoff 保存影响决策的摘要与授权 artifact;OpenViking/Obelisk 按可选 provider 资格化。utility 的因果收益另以对照证明,不把相关性当提升 | -| **S7 预算、调度与 fleet 规模 · P0 观测/P1–P2 扩展** | quota/scheduler 与部分 usage aggregate 存在;全 provider 成本、分布式资源预留及百 Agent 并发尚需证据 | 先区分配置预算、准入、消耗与估算;未知成本不记零、重复事件不双记。R7 分页/有界摘要及[完整历史传输](typescript-control-plane-migration-v0.zh-CN.md),验收超出 RPC 上限后的写回/重放/单次扣记;provider/host 限流、公平性、背压、事件唤醒与失败隔离;分别报告注册数/活跃数/吞吐量和每个验收成果成本 回执支持的语义进展已能关闭原 host Turn,保留未完成/等待中的 Todo;file/SQLite guard 回放覆盖 frontier 改变,边界见[quota 结算契约](../../quota-allocation.md#receipt-backed-settlement-progress)。这项有界修复不证明 fleet 规模或宿主延迟。 | +| **S7 预算、调度与 fleet 规模 · P0 观测/P1–P2 扩展** | quota/scheduler 与部分 usage aggregate 存在;全 provider 成本、分布式资源预留及百 Agent 并发尚需证据 | 先区分配置预算、准入、消耗与估算;未知成本不记零、重复事件不双记。R7 分页/有界摘要及[完整历史传输](typescript-control-plane-migration-v0.zh-CN.md),验收超出 RPC 上限后的写回/重放/单次扣记;provider/host 限流、公平性、背压、事件唤醒与失败隔离;分别报告注册数/活跃数/吞吐量和每个验收成果成本 回执支持的语义进展已能关闭原 host Turn,保留未完成/等待中的 Todo;file/SQLite guard 回放覆盖 frontier 改变,边界见[quota 结算契约](../../quota-allocation.md#receipt-backed-settlement-progress)。external-wait authoring 现复用 resume owner 从锁定 Todo 快照重算后继,以完成依赖/monitor 代际证明恢复,不再相信展示字段;源证据续接与真实 Host backoff/readback 仍须分别验收。这项有界修复不证明 fleet 规模或宿主延迟。 | | **S8 能力、扩展与领域集成 · P1/P2** | 已有 capability catalog、extension 生命周期、hook、工程/研究/content/office 能力及 computer-use 合同 | 优先用现有 issue-fix/PR-review 和材料/研究 caller 检验共享控制面;每个 provider 带 readiness、版本、权限、默认关闭、卸载/回滚、失败隔离与真实入口证据。新 domain effect 从模拟单操作闭环开始,不先建市场或通用工作流 DSL | | **S9 身份、权限、隐私与信任 · P0 持续/P1–P2 远端** | public/private 边界、作用域、capability gate、fence 与确认合同分布在已有 owner | 随 R1/R3 验 sender/audience/artifact scope 和 stale authority;远端 R6 必须认证 tenant/Goal/actor/host、轮换撤销与最小权限。凭据保管、非可信工具/文档输入、依赖供应链、审计留存/删除及漏洞响应纳入真实路径;角色、消息或 memory 不铸造写权限 | | **S10 可靠性、诊断与运行运营 · P0/P1** | recovery/canary、read-only diagnostics 原型及 DSH event adapter 已有;C0/C1、开销和完整运营资格仍未闭合 | 故障分类→可观察状态→恢复演练→防复发;覆盖进程/存储/网络/投递故障和数据增长。Chat 上下文或 provider 读取晚于停止等待返回时,按持久 Turn 和精确 Session claim 判断:即使新请求已完成,也不得再启动旧请求或交接迟到结果。这项有界 GQ08 修复不证明上游 interrupt 保真,也不取消其他 owner 已准入的效果;完整恢复仍遵循[共享对话运行契约](capable-manager-semantic-handoff-v0.md#10-operational-contract)。定义并冻结 SLO、RPO/RTO、容量/保留边界,实测后标 qualified;Lark 传输背压与 drain 保留 App consumer lease;未持久化的缓冲消息不算已受理工作或完成回执。运行手册含升级、备份恢复、停止与人工接管,不以测试数代替恢复结果 | diff --git a/docs/reference/protocols/quota-blocked-causal-closeout-v0.md b/docs/reference/protocols/quota-blocked-causal-closeout-v0.md index 9cb387b667..6dbcdea9fa 100644 --- a/docs/reference/protocols/quota-blocked-causal-closeout-v0.md +++ b/docs/reference/protocols/quota-blocked-causal-closeout-v0.md @@ -46,6 +46,22 @@ CLI/provider acceptance checks both dependency kinds, replay, no debit, original validator preservation and independent next-Turn selection. It does not claim live research adoption or PostgreSQL qualification. +### Registering a source-satisfied successor + +External-wait authoring recomputes a successor's `todo_done` or +`monitor_changed` condition from the same locked Todo snapshot using the +existing typed resume evaluator. A completed dependency (including archived +history) or a strictly advanced monitor generation can therefore serve as the +runnable successor without clearing its condition. A pending, missing, +self-referential or invalid dependency still rejects the whole edit without +writing. Caller-supplied `resume_ready` no longer admits work: it is a display +projection, not proof. The default change affects CLI, native planning and the +shared Chat/Lark Todo update owner; no activation switch or new authority is +added. PR, capacity and clock conditions lack evidence at this authoring +boundary and remain rejected as successor proof. Their ordinary scheduling +semantics are unchanged. Dry-run and canonical operation replay keep their +existing behavior; live host and packaged App/Lark adoption are separate checks. + ### Suspending leased work For an open, leased Agent Todo, the current claim/lease owner can use the @@ -144,6 +160,18 @@ Dashboard 已消费 canonical 等待与回执,Chat/Lark 仍复用 Todo updat 验收覆盖两种依赖、重放、零扣额、原验收器保留与下一 Turn 独立选择;不据此 宣称投研真实采用或 PostgreSQL 资格已通过。 +### 登记源证据已满足的后继 + +external-wait authoring 复用既有 typed resume evaluator,从同一份锁定的 Todo +快照重算后继的 `todo_done` 或 `monitor_changed` 条件。已完成的依赖(包括归档 +历史)或严格超过基线的 monitor generation,可合法承接工作,无须删除后继的 +等待条件。尚未满足、缺失、自引用或非法依赖仍整体拒绝且不写入。 +`resume_ready` 是展示投影,调用方提供的值不再授权后继。这项默认行为调整覆盖 +CLI、native planning 及同源 Chat/Lark Todo update owner,不新增开关或权限。 +PR、capacity 和 clock 条件在此 authoring 边界缺少证据,仍不能靠展示字段承接; +它们的普通调度语义不变。Dry-run 与 canonical 操作重放沿用原契约,本机 Host +及打包 App/Lark 采用须分别验收。 + ### 有租约任务的延期 当前 claim/lease 持有者可沿用 `todo update`,只提交 `--status deferred`、