{locale === "zh-CN"
- ? "启用 canonical 创建后,新 Goal 直接建立权威存储,并采用所选执行策略;失败时须重试原创建操作。关闭时仅固定晋升后的目标存储。已有 Goal 的升级仍需备份、停止写入和结算租约;这里不授予工具权限。"
- : "With canonical creation enabled, new Goals establish authority with the selected execution policy; retry the original operation after failure. Disabled only freezes the post-promotion target. Existing Goals still require backup, stopped writers and settled leases for upgrade; this setting grants no tool permissions."}
+ ? "2.0 新建 Goal 必须启用 canonical 创建,并采用所选执行策略。旧的关闭设置不会自动改变;启用后再重试创建。已有 Goal 需备份、停止写入并导入;这里不迁移已有数据,也不授予工具权限。"
+ : "2.0 requires canonical creation with the selected execution policy. An older disabled setting is not changed automatically; enable it before retrying creation. Back up, stop writers and import existing Goals separately. This setting neither migrates existing data nor grants tool permissions."}
) : null}
diff --git a/benchmark/tests/test_shared_codex_runtime.py b/benchmark/tests/test_shared_codex_runtime.py
index 3c9c0f1130..89a90e9186 100644
--- a/benchmark/tests/test_shared_codex_runtime.py
+++ b/benchmark/tests/test_shared_codex_runtime.py
@@ -248,6 +248,10 @@ def test_scheduler_cancel_reaps_detached_worker_host_and_keeps_receipt(tmp_path)
str(source / "scripts/external_scheduler_worker.py"),
"--cli-bin",
str(cli),
+ "--registry",
+ str(tmp_path / "registry.json"),
+ "--runtime-root",
+ str(tmp_path / "runtime"),
"--goal-id",
"fixture",
"--agent-id",
diff --git a/benchmark/tests/test_task_entry.py b/benchmark/tests/test_task_entry.py
index 8eaa32873a..8a72e5887d 100644
--- a/benchmark/tests/test_task_entry.py
+++ b/benchmark/tests/test_task_entry.py
@@ -41,17 +41,31 @@ def test_task_entry_defaults_to_planning_only_for_loopx_modes(mode):
@pytest.fixture
def planning_env(tmp_path):
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+ from tests.control_plane.canonical_authority_fixture import (
+ initialize_canonical_authority,
+ )
+
project = tmp_path / "project"
project.mkdir()
task = tmp_path / "task.md"
task.write_text("Repair the failure and preserve a regression test.\n")
state = tmp_path / "state.md"
- state.write_text("# Active Goal State\n\n## Agent Todos\n\n## User Todos\n")
+ state.write_text(
+ "# Active Goal State\n\n"
+ "## User Todo / Owner Review Reading Queue\n\n"
+ "## Agent Todo\n\n"
+ "## Completed Work Archive\n"
+ )
+ runtime = tmp_path / "runtime"
registry = tmp_path / "registry.json"
registry.write_text(
json.dumps(
{
"schema_version": "0.1",
+ "common_runtime_root": str(runtime),
"goals": [
{
"id": "planning-goal",
@@ -64,6 +78,15 @@ def planning_env(tmp_path):
}
)
)
+ initialize_canonical_authority(
+ runtime,
+ "planning-goal",
+ build_todo_runtime_shadow_projection(
+ goal_id="planning-goal", todos=[], handoff_mode="soft_claim"
+ ),
+ state_path=state,
+ provider="file",
+ )
cli = tmp_path / "loopx"
cli.write_text(
f"#!{sys.executable}\nfrom loopx.cli import main\nraise SystemExit(main())\n"
@@ -104,7 +127,7 @@ def planning_env(tmp_path):
"LOOPX_GOAL_ID": "planning-goal",
"LOOPX_AGENT_ID": "planner",
"LOOPX_REGISTRY": str(registry),
- "LOOPX_RUNTIME_ROOT": str(tmp_path / "runtime"),
+ "LOOPX_RUNTIME_ROOT": str(runtime),
"LOOPX_CLI": str(cli),
"CODEX_BIN": str(binary),
"LOOPX_PROJECT": str(project),
diff --git a/demo/auto_research/worker_runtime.py b/demo/auto_research/worker_runtime.py
index 9937201e44..fe90a20eb5 100644
--- a/demo/auto_research/worker_runtime.py
+++ b/demo/auto_research/worker_runtime.py
@@ -34,6 +34,9 @@
normalize_todo_id,
)
from loopx.control_plane.todos.handoff_mode import HANDOFF_MODE_HARD_LEASE
+from loopx.control_plane.coordination.local_authority import (
+ LocalCoordinationAuthorityUnavailable, read_canonical_todos_if_promoted,
+)
from loopx.control_plane.work_items.task_lease import (
TaskLeaseError,
acquire_task_lease,
@@ -388,28 +391,38 @@ def _maybe_add_role_successor_todos(
action=action,
)
failure_continuation = decision_summary.get("failure_continuation")
- if isinstance(failure_continuation, dict):
- next_outcome = str(failure_continuation.get("next_outcome") or "")
- successor_specs = [
- spec
- for spec in successor_specs
- if spec.get("action_kind") == next_outcome
- ]
text_source_todo_id = None
if isinstance(failure_continuation, dict):
- text_source_todo_id = str(failure_continuation.get("source_todo_id") or "") or None
- return apply_role_successor_todos(
- registry_path=registry_path,
- goal_id=goal_id,
- source_todo_id=source_todo_id,
- current_agent_id=agent_id,
- role_id=role_id,
- action=action,
- successor_specs=successor_specs,
- decision_summary=decision_summary,
- execute=execute,
- text_source_todo_id=text_source_todo_id,
- )
+ next_outcome = str(failure_continuation.get("next_outcome") or "")
+ successor_specs = [spec for spec in successor_specs if spec.get("action_kind") == next_outcome]
+ source_todo_id = str(failure_continuation.get("source_todo_id") or "") or source_todo_id
+ text_source_todo_id = source_todo_id
+ def apply_binding(parent_id: str, *, revision: str | None = None) -> dict[str, object]:
+ return apply_role_successor_todos(registry_path=registry_path, goal_id=goal_id,
+ source_todo_id=parent_id, current_agent_id=agent_id, role_id=role_id,
+ action=action, successor_specs=successor_specs, decision_summary=decision_summary,
+ execute=execute, text_source_todo_id=text_source_todo_id,
+ expected_provider_revision=revision)
+
+ try:
+ return apply_binding(source_todo_id)
+ except LocalCoordinationAuthorityUnavailable as error:
+ if error.code != 'todo_semantic_duplicate_conflict' or text_source_todo_id is None:
+ raise
+ # Native create owns normalized title/intent matching. Qualify only its
+ # exact conflicting record, without rewriting a historical parent.
+ snapshot = read_canonical_todos_if_promoted(
+ runtime_root=runtime_root_from_registry(registry_path, None), goal_id=goal_id,
+ )
+ todos = {row['todo_id']: row for row in (snapshot or {}).get('todos', [])}
+ existing = todos.get(error.payload.get('todo_id'))
+ parent_id = existing.get('unblocks_todo_id') if existing else None
+ parent = todos.get(parent_id)
+ if not (parent and parent['role'] == 'agent' and parent.get('action_kind') in SUMMARY_ACTIONS
+ and parent.get('unblocks_todo_id') == text_source_todo_id):
+ raise
+ return apply_binding(parent_id, revision=snapshot['provider_revision'])
+
def _executed_successor_todo_ids(successor_todos: dict[str, object]) -> list[str]:
diff --git a/demo/multi_agent/role_successor.py b/demo/multi_agent/role_successor.py
index 5dd13cefac..4a054582a8 100644
--- a/demo/multi_agent/role_successor.py
+++ b/demo/multi_agent/role_successor.py
@@ -173,13 +173,13 @@ def apply_role_successor_todos(
decision_summary: Mapping[str, object],
execute: bool,
text_source_todo_id: str | None = None,
+ expected_provider_revision: str | None = None,
) -> dict[str, object]:
- """Preview or create role-declared successor todos through LoopX state.
+ """Create successors, retaining qualified historical source bindings.
- When text_source_todo_id is provided, it is used for rendering the
- successor text (so the title is stable across different summary todos
- that review the same failure lineage). source_todo_id is still used
- for the unblocks_todo_id binding.
+ Normally one source supplies both title and lineage. A caller recovering
+ an existing successor may preserve its original parent while rendering
+ the same title from the independently verified evidence lineage.
"""
specs = [dict(spec) for spec in successor_specs if isinstance(spec, Mapping)]
@@ -257,6 +257,7 @@ def apply_role_successor_todos(
action_kind=action_kind,
claimed_by=claimed_by,
unblocks_todo_id=source_todo_id,
+ expected_provider_revision=expected_provider_revision,
dry_run=False,
)
successors.append(
diff --git a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.md b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.md
index 73357e4622..6caff20507 100644
--- a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.md
+++ b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.md
@@ -1,11 +1,33 @@
# Local authority: retirement cadence after integration
-- Current plan: October 7, 2026, `06b6caa07`; historical audit: `ce3862e33`; adoption follow-up: `71525ab90`, September 28, 2026; [中文](2026-09-28-retirement-cadence.zh-CN.md).
+- Current candidate checkpoint: October 15, 2026, #6396 at `5d29213d3`; historical 1.x plan: October 7, 2026, `06b6caa07`; historical audit: `ce3862e33`; adoption follow-up: `71525ab90`, September 28, 2026; [中文](2026-09-28-retirement-cadence.zh-CN.md).
- Owners: overall roadmap R3/R4/R5/R6; shared authority D1–D3; TS migration T0–T4.
- This replaces the **current inventory/estimates** in the September 27 recovery
and Host-supervision ledgers, not their historical validation results.
-## Reconciled baseline
+
+
+## Current candidate checkpoint (2026-10-15)
+
+The C1/T4 deletion package is implemented in contributor-led
+[#6396](https://github.com/loopx-project/loopx/pull/6396) at reviewed head
+`5d29213d3`, including the fork supplements and
+[absorbed #6366 obligations](../../../../reference/local-authority-provider-selection.md#retirement-proposal-reconciliation).
+This is an open 2.0 candidate, not the released additive 1.x baseline.
+
+| Boundary | Candidate fact | Remaining work / retained obligation |
+| --- | --- | --- |
+| Ordinary Markdown mutation and capture producers | Obsolete writer implementations, including `todos/legacy_mutation.py`, `todos/line_update.py` and `runtime_shadow_writer_adapter.py`, are absent. Canonical creation/mutation, team-plan, Monitor and lease callers use existing typed owners. | Integrate and qualify the adopted source; do not create another writer-deletion task from the historical table. Keep permanent document/display IO and supported import/recovery. |
+| Ownership policy | Fresh execution/configuration targets are `soft_claim` / `hard_lease`; typed runtime admission and proof inspection reject live `legacy` execution. | Historical three-value decoding, original receipts, exact release and reviewed backed-up policy migration remain. Qualify real installed migration and affected Host/App consumers separately. |
+| Source and outbox recovery | Normal producer retirement does not erase prepared/committed history. | Preserve original receipt/cursor recovery and reconcile or explicitly disposition outstanding outboxes before removing their readers. Storage conversion does not complete ownership migration. |
+| Adoption and release | Exact-head review and selected combined-tree validation are recorded with source revisions in the PR. | The reversible core-developer trial, natural Host use, Goal-instance lifetime/recovery and formal D2/D3/default evidence keep their existing owners and open exits. Retain failed/missing rows; mocks, elapsed mixed-version use or a PR merge cannot replace them. |
+
+Use this table and the [TS current frontier](../../typescript-control-plane-migration-v0.md#current-delivery-frontier-2026-10-02)
+for new work. The dated records below preserve their original source-specific
+measurements and dependency decisions; claims that old writers are reachable
+refer to those earlier baselines and must not regenerate completed candidate work.
+
+## Historical reconciled baseline
| Already merged | What no longer belongs in the remaining-work count |
| --- | --- |
@@ -27,12 +49,12 @@ reimplementing them. Their scopes are dependencies only for affected callers;
local default does not wait for unrelated cloud or hundred-Agent work.
**File as the default store factory is not File as the default authority for
-all new/existing Goals.** Unpromoted Markdown writers remain reachable. Two
+all new/existing Goals.** At this historical baseline, unpromoted Markdown writers remained reachable. Two
successful Goal migrations, or File↔SQLite transfer, do not prove their absence.
No fixed “5–8 PRs remaining” is carried forward: below are named delivery and
qualification boundaries, not a promise about defect count or merge count.
-## When and how to delete
+## Historical last-caller inventory and deletion rules
| Boundary | Actual reachable code/caller | Earliest safe deletion and retained obligation |
| --- | --- | --- |
@@ -126,9 +148,9 @@ C1, D2 and release-default adoption remain separate acceptance boundaries.
-## Current closeout: validation, migration and deletion (2026-10-07)
+## Historical closeout plan: validation, migration and deletion (2026-10-07)
-Rechecked against main `06b6caa07` and the linked merged PRs. This is the current
+Rechecked against main `06b6caa07` and the linked merged PRs. This was the
execution plan for **R5 / D1–D3 / T0–T4**, replacing the previous A–D schedule;
older measurements remain source-specific evidence. R6 is a separate successor.
Storage format, authority selection and ownership policy are three distinct
diff --git a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.zh-CN.md b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.zh-CN.md
index 5901bc2c8e..6a5ec13c22 100644
--- a/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.zh-CN.md
+++ b/docs/architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.zh-CN.md
@@ -1,11 +1,31 @@
# 合并后的本地权威退役节奏
-- 当前计划:2026-10-07,`06b6caa07`;历史核对基线:`ce3862e33`;采用后续核对:`71525ab90`,2026-09-28;[English](2026-09-28-retirement-cadence.md)。
+- 当前候选 checkpoint:2026-10-15,#6396 `5d29213d3`;历史 1.x 计划:2026-10-07,`06b6caa07`;历史核对基线:`ce3862e33`;采用后续核对:`71525ab90`,2026-09-28;[English](2026-09-28-retirement-cadence.md)。
- Owner:总 roadmap R3/R4/R5/R6、shared authority D1–D3、TS 迁移 T0–T4。
- 本记录替代 9 月 27 日 recovery、Host supervision 记录的**当前清单和估算**,
不替代其历史验证结果。
-## 重新核对的基线
+
+
+## 当前候选 checkpoint(2026-10-15)
+
+贡献者主导的 [#6396](https://github.com/loopx-project/loopx/pull/6396),已评审 head
+`5d29213d3`,已实现 C1/T4 删除包,纳入 fork 补缺及
+[#6366 已吸收义务](../../../../reference/local-authority-provider-selection.md#retirement-proposal-reconciliation)。
+这是开放的 2.0 候选,不是已发布的兼容 1.x 基线。
+
+| 边界 | 候选现状 | 剩余工作/保留义务 |
+| --- | --- | --- |
+| 普通 Markdown mutation 与 capture producer | 废弃 writer 实现已不存在,包括 `todos/legacy_mutation.py`、`todos/line_update.py` 和 `runtime_shadow_writer_adapter.py`;canonical 创建/写入、team-plan、Monitor 及 lease caller 使用既有 typed owner。 | 集成并验证采用后的源码,不从历史表再创建 writer 删除任务;保留永久文档/显示 IO 及受支持的导入/恢复。 |
+| 所有权策略 | fresh 执行/配置目标为 `soft_claim`/`hard_lease`;typed runtime 准入及 proof inspect 拒绝 live `legacy` 执行。 | 历史三值解码、原回执、精确 release 与有备份的审核策略迁移保留;真实安装迁移及受影响 Host/App 消费者分别验证。 |
+| Source/outbox 恢复 | 普通 producer 退役不抹去 prepared/committed 历史。 | 保留原 receipt/cursor 恢复;删除 reader 前对账或明确处置未完成 outbox。存储转换不等于所有权迁移完成。 |
+| 采用与发布 | PR 按源码 revision 记录精确 head 评审及选定组合 tree 验证。 | 可回退核心开发者试用、自然 Host 使用、Goal-instance 生命周期/恢复及正式 D2/D3/默认资格沿用原 owner 与开放出口;保留失败/缺失项,mock、混合版本经过时间或 PR 合并都不能替代。 |
+
+新工作以本表及 [TS 当前交付边界](../../typescript-control-plane-migration-v0.zh-CN.md#当前交付边界2026-10-02)
+为准。下方具名历史记录保留原源码上的测量与依赖判断;“旧 writer 仍可达”
+只描述当时基线,不能再次派生候选已经实现的工作。
+
+## 历史核对基线
| 已合并 | 不再计入剩余工作的内容 |
| --- | --- |
@@ -25,11 +45,11 @@
不等待无关云端或百 Agent 工作。
**File 是默认 store factory,不等于所有新旧 Goal 默认以 File 为权威。**
-未晋升的 Markdown writer 仍可达。两个 Goal 迁移成功、或 File↔SQLite 传输成功,
+在这个历史基线上,未晋升的 Markdown writer 仍可达。两个 Goal 迁移成功、或 File↔SQLite 传输成功,
都不能证明旧 writer 已没有消费者。本轮不再沿用“还剩 5–8 个 PR”;以下列出的是
交付和验证边界,不承诺缺陷数量或合并数量。
-## 什么时候删、怎么删
+## 历史最后调用方清单与删除规则
| 边界 | 真实可达的代码/调用方 | 最早删除条件及保留义务 |
| --- | --- | --- |
@@ -109,10 +129,10 @@ Host IO;C1、D2 和发布默认采用仍是独立验收边界。
-## 当前收尾:验证、迁移与删除(2026-10-07)
+## 历史收尾计划:验证、迁移与删除(2026-10-07)
按 main `06b6caa07` 和所列已合并 PR 重新核对。本节是 **R5 / D1–D3 / T0–T4**
-的当前执行计划,替代旧 A–D 排期;历史测量仍只适用于原源码和负载。R6 单独推进。
+的当时执行计划,替代旧 A–D 排期;历史测量仍只适用于原源码和负载。R6 单独推进。
存储格式、权威选择、所有权策略是三种不同迁移:有 SQLite 数据库,不代表新 Goal
已经默认使用 canonical authority,也不代表 `legacy` handoff 策略已经退役。
diff --git a/docs/architecture/rfcs/typescript-control-plane-migration-v0.md b/docs/architecture/rfcs/typescript-control-plane-migration-v0.md
index c056cf2fc6..3a9c897c5f 100644
--- a/docs/architecture/rfcs/typescript-control-plane-migration-v0.md
+++ b/docs/architecture/rfcs/typescript-control-plane-migration-v0.md
@@ -4,7 +4,7 @@
- Supersedes / closes: none
- Proposed by: LoopX maintainers
- Date: 2026-08-15
-- Last revised: 2026-10-07
+- Last revised: 2026-10-15
- Scope: an incremental, replacement-first migration of the LoopX control-plane
core from Python to TypeScript without maintaining two semantic
implementations
@@ -31,7 +31,58 @@ T0–T4, not additional provider promotion or completed migration claims.
-## Current delivery frontier (2026-10-12)
+## Current delivery frontier (2.0 candidate, 2026-10-15)
+
+Contributor-led [#6396](https://github.com/loopx-project/loopx/pull/6396),
+at reviewed head `5d29213d3`, carries the C1/T4 normal-Markdown-writer cutoff
+and live `legacy` ownership-policy retirement. It incorporates the fork
+supplements and the valuable #6366 invariants and recovery checks documented in
+[the absorption map](../../reference/local-authority-provider-selection.md#retirement-proposal-reconciliation).
+The candidate removes the obsolete writer implementations and capture producers;
+canonical mutation, creation, team-plan, Monitor and lease consumers reuse their
+existing typed owners. Do not schedule those deletions again from the older
+writer inventory below. The additive 1.x line and an open 2.0 proposal remain
+different delivery baselines; this checkpoint does not claim a released cutoff.
+
+The next integration pairs `7c0215f26` with pinned main `77d6b07fb` through a
+normal two-parent merge. Keep `2.0.0a1` as the retirement candidate while
+carrying the compatible 1.4.2 fixes; preserving source bytes in a one-parent
+snapshot would lose the main ancestry needed for a reviewable upstream diff.
+Fresh canonical-create checks retain both structured P1 and its compatibility
+display, while original receipts keep their recorded intent. Retained outbox
+recovery uses the existing native delivery/drain counterexamples after producer
+removal. The released-lease cutover check moves to backed-up cold import on
+File/SQLite, preserving the complete terminal record, later canonical work and
+original-import replay without recreating a source writer. Later main revisions
+and installed Host adoption still require their own applicable evidence.
+
+New execution/configuration targets share `ExecutionHandoffMode`, derived from
+`EXECUTION_HANDOFF_MODES` (`soft_claim` / `hard_lease`). Historical `HandoffMode`
+and receipt codecs still recognize `legacy`. The typed policy owner refuses
+fresh legacy execution, acquire/maintenance and current-proof grants, including
+delegation launch/resume; exact historical receipt recovery, release and
+reviewed backup/plan/migrate remain supported. Storage conversion and ownership
+migration remain distinct. No active Goal is migrated by this proposal.
+
+Current work is candidate integration and adoption qualification, not another
+writer extraction: retain the compatible 1.x upgrade/backup/recovery route,
+finish the bounded reversible core-developer trial and affected real Host/App
+journeys, and keep formal D2 failures/missing evidence and release-default
+qualification open. Permanent Markdown document/display IO, historical readers
+and outstanding receipt/outbox disposition are retained obligations. A passing
+conversion drill does not establish natural adoption or Goal-instance retirement.
+
+Use the [current retirement ledger](ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.md#current-candidate-checkpoint)
+for that frontier. [Exact-head review](https://github.com/loopx-project/loopx/pull/6396#pullrequestreview-5535116423)
+and [combined-tree evidence](https://github.com/loopx-project/loopx/pull/6396#issuecomment-6195855339)
+retain their source revisions; they do not certify an untested later head/base,
+a completed installed cohort or all RFC acceptance.
+
+### Historical 1.x prerequisite checkpoints (through 2026-10-12)
+
+The following records explain the prerequisites and validation at their named
+sources. Their references to reachable writers and a “next” cutoff describe
+that 1.x baseline, not remaining implementation work in the candidate above.
Rechecked at main `38cb407ed`: #5413/#5466/#5283 and the affected creation,
Host-stop and UI-history owners #5500/#5805/#5308/#5398 are merged. Reuse
diff --git a/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md b/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md
index 9b76cb9208..fae357d9cb 100644
--- a/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md
+++ b/docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md
@@ -4,7 +4,7 @@
- 替代 / 关闭:无
- Proposed by:LoopX maintainers
- Date:2026-08-15
-- Last revised:2026-10-07
+- Last revised:2026-10-15
- Scope:LoopX 控制面核心从 Python 到 TypeScript 的增量、replacement-first
迁移;不长期维护两份语义实现
- Tracking issue:[#3225](https://github.com/loopx-project/loopx/issues/3225)
@@ -29,7 +29,48 @@ R1–R3 的 TS 消费者包括 App 产品路径,不只 CLI 结算。
-## 当前交付边界(2026-10-07)
+## 当前交付边界(2.0 候选,2026-10-15)
+
+贡献者主导的 [#6396](https://github.com/loopx-project/loopx/pull/6396),
+已评审 head `5d29213d3`,承接 C1/T4 普通 Markdown writer 截止与 live `legacy`
+所有权策略退役,已纳入 fork 补缺及 #6366 中有价值的不变量和恢复检查,见
+[吸收清单](../../reference/local-authority-provider-selection.md#retirement-proposal-reconciliation)。
+候选已删除废弃 writer 实现及 capture producer;canonical mutation、创建、
+team-plan、Monitor 和 lease 消费者复用既有 typed owner。不能根据下方旧清单
+再次派生同样的删除工作。兼容的 1.x 线和仍开放的 2.0 提案是不同交付基线;
+本 checkpoint 不宣称截止行为已发布。
+
+后续整合以正常双亲 merge 连接 `7c0215f26` 与冻结主干 `77d6b07fb`。
+吸收兼容的 1.4.2 修复时保留退役候选 `2.0.0a1`;仅保留相同源码的单亲
+snapshot 会丢失主干祖先关系,破坏上游 diff 的可评审性。新 canonical 创建
+同时保留结构化 P1 和兼容显示,原回执仍保持记录时的意图。producer 删除后,
+历史 outbox 恢复复用既有 native delivery/drain 反例;已释放 lease 的 cutover
+检查迁到有备份的 File/SQLite cold import,保留完整终态记录、后续 canonical
+工作与原导入回执重放,不重建 source writer。后续主干 revision 和安装后的
+Host 采用仍需分别验证其适用证据。
+
+新执行/配置目标统一使用 `ExecutionHandoffMode`,从既有
+`EXECUTION_HANDOFF_MODES`(`soft_claim`/`hard_lease`)派生;历史 `HandoffMode`
+及回执 codec 仍识别 `legacy`。typed 策略 owner 拒绝 fresh legacy 执行、
+acquire/维护及当前 proof 授权,包括 delegation 启动/恢复;历史原回执恢复、
+精确 release 与有备份的 plan/migrate 保留。存储转换和所有权迁移仍是不同操作;
+提案不自动迁移任何活跃 Goal。
+
+当前工作是候选集成与采用验证,不是再做 writer extraction:保留兼容 1.x 的
+升级/备份/恢复路线,完成有界可回退核心开发者试用及受影响的真实 Host/App
+旅程,保持正式 D2 的失败/缺失证据与发布默认资格开放。永久 Markdown 文档/
+显示 IO、历史 reader 及未完成的 receipt/outbox 处置仍需保留。转换演练通过
+不代表自然采用或 Goal-instance 退役完成。
+
+后续以[当前退役 ledger](ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.zh-CN.md#current-candidate-checkpoint)
+为准。[精确 head 评审](https://github.com/loopx-project/loopx/pull/6396#pullrequestreview-5535116423)
+和[组合 tree 证据](https://github.com/loopx-project/loopx/pull/6396#issuecomment-6195855339)
+各自保留源码 revision,不认证未验证的后续 head/base、安装 cohort 或全部 RFC 验收。
+
+### 历史 1.x 前置 checkpoint(截至 2026-10-12)
+
+以下记录解释具名源码上的前置条件与验证。“writer 仍可达”及“下一次截止”
+描述当时的 1.x 基线,不是上方候选仍未实现的工作。
按 main `06b6caa07` 复核,#5413/#5466/#5283,以及创建、Host 停止、UI 历史
相关 owner #5500/#5805/#5308/#5398 均已合并。复用各自已交付的边界,不重开
diff --git a/docs/book/chapters/00-reading-guide.md b/docs/book/chapters/00-reading-guide.md
index de6b8068fc..3136f7cb60 100644
--- a/docs/book/chapters/00-reading-guide.md
+++ b/docs/book/chapters/00-reading-guide.md
@@ -180,7 +180,7 @@ lifecycle 选择,不是所有贡献的默认终点。
## 版本基线
-当前内容面向 LoopX release `v1.4.2`;本地命令示例已按 `loopx 1.4.2` 的
+当前内容面向 LoopX release `v2.0.0a1`;本地命令示例已按 `loopx 2.0.0a1` 的
公开 CLI 与协议表面复核。该版本要求 Python 3.11+ 与 Node.js 22.22.3+。LoopX 会自动管理
空闲后退出的 TypeScript Effect runtime,用户不需要手工维护 daemon。
diff --git a/docs/book/chapters/core-state-machines.md b/docs/book/chapters/core-state-machines.md
index a14365f283..baadf6ceb6 100644
--- a/docs/book/chapters/core-state-machines.md
+++ b/docs/book/chapters/core-state-machines.md
@@ -535,8 +535,8 @@ observation 与它请求的独立 successor**。事务先校验 actor 注册、c
这里的“原子”有明确边界:它不包含外部网络 poll、quota spend 或 Markdown/dashboard delivery。
canonical commit 成功后,projection 仍可能处于 `pending`;outbox 只重试显示投影,
-`retry_business_mutation=false`,不能再次执行已经提交的 observation/successor。没有提升 authority 的
-Goal 仍走 legacy writer,因此这项保证不能外推成“整个 Goal 的所有状态已统一迁移”。
+`retry_business_mutation=false`,不能再次执行已经提交的 observation/successor。2.0 中未迁移
+Goal 返回 `canonical_authority_required`,必须先备份并迁移,之后才能执行新的 Monitor 写入。
把 future `next_due_at` 当作 advancement frontier 是错误的:它只说明何时观察,不能替代“下一步
怎样推进 Goal”。
diff --git a/docs/book/en/chapters/00-reading-guide.md b/docs/book/en/chapters/00-reading-guide.md
index 11f80c4a41..48616f2b33 100644
--- a/docs/book/en/chapters/00-reading-guide.md
+++ b/docs/book/en/chapters/00-reading-guide.md
@@ -199,8 +199,8 @@ Do not bypass a newer permission or lifecycle check just to make an older exampl
## Version baseline
-This book targets LoopX release `v1.4.2`. Local command examples were checked against
-the public `loopx 1.4.2` CLI and protocol surface. It requires Python 3.11+ and Node.js 22.22.3+.
+This book targets LoopX release `v2.0.0a1`. Local command examples were checked against
+the public `loopx 2.0.0a1` CLI and protocol surface. It requires Python 3.11+ and Node.js 22.22.3+.
LoopX starts and reuses its managed, idle-exiting TypeScript Effect runtime automatically; users do not
operate that runtime as a manual daemon.
diff --git a/docs/book/en/chapters/core-state-machines.md b/docs/book/en/chapters/core-state-machines.md
index c122fac9e1..7f49b0ee52 100644
--- a/docs/book/en/chapters/core-state-machines.md
+++ b/docs/book/en/chapters/core-state-machines.md
@@ -576,8 +576,9 @@ observation updates observation state and cadence without creating a delivery To
“Atomic” has a precise boundary here: it excludes the external network poll, quota spend, and
Markdown/dashboard delivery. After the canonical commit, projection delivery may still be `pending`; its
outbox retries display projection only, with `retry_business_mutation=false`, and must not repeat the committed
-observation/successor mutation. Goals not promoted to shared authority still use the legacy writer, so this guarantee
-must not be generalized into “all Goal state has migrated to one store.”
+observation/successor mutation. In 2.0 an unmigrated Goal returns
+`canonical_authority_required`; it must be backed up and migrated before a new
+Monitor write.
Treating a future `next_due_at` as an advancement frontier is wrong. It says when to observe, not how to
advance the Goal.
diff --git a/docs/book/en/index.md b/docs/book/en/index.md
index 4deec65abd..82c59e4846 100644
--- a/docs/book/en/index.md
+++ b/docs/book/en/index.md
@@ -79,7 +79,7 @@ versioned or optional functionality, not the default shape of every contribution
- Source format: Markdown
- Site generator: MkDocs Material
- Hosting: GitHub Pages
-- LoopX release anchor: `v1.4.2`
+- LoopX release anchor: `v2.0.0a1`
- Runtime prerequisites: Python 3.11+ and Node.js 22.22.3+
The official public protocols remain authoritative for protocol facts. Commands that change across
diff --git a/docs/book/index.md b/docs/book/index.md
index 762026919c..abd9ec2fa7 100644
--- a/docs/book/index.md
+++ b/docs/book/index.md
@@ -70,7 +70,7 @@ Extension 只是可独立版本化和交付的一种路径。
- 正文格式:Markdown;
- 站点生成器:MkDocs Material;
- 在线发布:GitHub Pages;
-- LoopX 发布锚点:`v1.4.2`;
+- LoopX 发布锚点:`v2.0.0a1`;
- 运行时前提:Python 3.11+ 与 Node.js 22.22.3+。
协议解释以 LoopX 官方公开合同为事实源。易变化的命令仍以对应发布物、官方文档和当前
diff --git a/docs/concepts/interaction-pattern-catalog.md b/docs/concepts/interaction-pattern-catalog.md
index 6a4a967051..9f3aeca5cd 100644
--- a/docs/concepts/interaction-pattern-catalog.md
+++ b/docs/concepts/interaction-pattern-catalog.md
@@ -2756,15 +2756,11 @@ flowchart TD
**Validation**
-- `tests/control_plane/test_local_authority_shadow_cli_e2e.py` runs the real CLI
- upgrade journey: `:10` proves a retained retired setting can neither enable
- capture nor satisfy a bootstrap, `:20` asserts the rejection names
- `local_authority_shadow_retired`, and `:25` asserts status still reads
- `retired`.
-- `tests/control_plane/test_local_authority_shadow_config.py` pins both halves:
- `:49` a retired enable rejects without rewriting the registry (dry-run and
- execute), `:58` clearing preserves runtime configuration and peer
- registration.
+- `tests/control_plane/test_local_authority_shadow_config.py` exercises the real
+ `configure-goal` CLI. Retired activation returns
+ `local_authority_shadow_retired`, leaves the registry unchanged and exposes
+ no enable action. Clearing the retired key preserves peer registration and
+ only enables runtime configuration when the caller requests it explicitly.
- `tests/control_plane/test_state_migration_authority_shadow.py` keeps
`migrate-state` reporting retirement instead of seeding it, and
`tests/control_plane_ts/local_authority_shadow.test.ts` keeps the old RPC
diff --git a/docs/development/contributor-tasks-history.md b/docs/development/contributor-tasks-history.md
index 195bec1394..387e6cfe0f 100644
--- a/docs/development/contributor-tasks-history.md
+++ b/docs/development/contributor-tasks-history.md
@@ -58,7 +58,7 @@ points:
| Review quality | PR review now requires scope-fit evidence for production surface changes (#3090), the execution contract carries four self-dev review lenses (#3123), example-only PRs need durable smoke-value evidence (#3134), age-fair exact-head scheduling persists across restarts (#3317), durable projection ACK is required (#3744), and disproportionate changes are gated (#3731). | Add synthetic conformance and negative cases around scope-fit evidence, causal chains, exact-head review packets, durable projection ACKs, and durable-smoke-value claims. |
| Task leases | Typed task-lease CLI with preserved legacy error codes landed (#3095); on-disk hard leases surface in goal-channel projection (#3039); Turn fencing uses lease fences plus an append-only journal, and the OpenCode 2 goal worker fences its own live worker lease. A task-lease generation ABA fix shipped (#3393), a Pi `loopx_task_lease` facade over the shipped `task_lease_v0` CLI merged (#3559, closing #3549), task-lease settlement cut over to TypeScript (#3674), and native task-lease acquire cut over too (#3702). | Adopt the same facade in one more real host integration (for example TraeX) or add a transfer/overlap-write-scope fixture. Keep soft-claim routing and undeclared-lease authority unchanged. |
| Status, quota, monitors | Replan context is host-projected from the evidence ledger; two equivalent typed progress observations create an obligation, maintenance writes fail closed against the same full goal-frontier reducer used by quota, and an exact runnable-successor Todo carries the obligation-bound semantic receipt and turn boundary. Heartbeat todos survive capability reentry (#3321), Todo identity filtering and UTC ordering are corrected (#3311), unbound `/loopx` sessions inherit existing agent identity (#3315), declared validation gates run on user-role done updates (#3291, #3293), quota guards follow the selected Todo (#3506), and a bounded fallback action portfolio keeps quota decisions actionable (#3514). Guided start is bound to one turn (#3572), stale generated Next Actions rebind to the current todo (#3524), unknown workspace causality is repairable (#3519), malformed runtime recovery and settlement payloads fail closed (#3525), managed and queued turn creation are serialized (#3542, #3562), and guided selection packet consumption is explicit (#3707). Post-v0.5.3, `todo list` gained a bounded thin projection with an output-budget cap (#3679), the dashboard projects completed Todos with done-count run progress (#3689), guided-todo onboarding delta coverage shipped (#3716), agent-lane Next Action selection prefers the selected Todo over shared prose (#3693), typed blocker outcomes settle (#3734), Turn receipt priority wins in compact output (#3726), and CLI common command owners load lazily (#3717). Manual evidence reads, prose ACKs, and historical repair-delta claims are diagnostic only. Compact scheduler-hint and heartbeat-prompt budgets plus todo-detail cold paths remain the reference. | Extend one measured performance, detail-readback, lock-timeout, malformed-state, typed progress, or obligation-bound semantic-transition case. Keep default output bounded and cold-path detail available. |
-| Governance and productization | A synthetic visible-governance slice landed (#3086) and its Stage-2 proposal-vs-shipped refresh keeps additive `claim_work` / coordination-head truth plus a lease-as-fence negative explicit; decision-context evidence cursors settle (#3079); the React homepage rebuild (#3098) landed; a deterministic project registry (#3170) serializes global sync; per-goal handoff mode gates claim/lease authority (#3164) and hard-lease gates auto-acquire completion keys (#3198); a repository change-window gate and pending ledger ship (#3319); goal channels default to human-gate auto-notify on new channels (#3523); coordination state rules are centralized (#3410) and the Stage 2 slice ships an aggregate head, file provider, and `claim_work` executor (#3529); project repository delivery lands through capability hooks (#3570) with gitless delivery workspaces settled (#3574) and CPA/provider-routing qualification recorded (#3576, #3573, #3563); a read-only stride shadow observation M1 (#3207), a synthetic stride-boundary shadow fixture (#3290), and the hierarchical stride RFC (#3204) open the next boundary; goal-artifact lifecycle projection (#3136) proposes a read-model boundary and post-outcome memory utility attribution from RFC #3215 is implemented (#3280). Post-v0.5.3, periodic-report post-writeback hooks ship (#3691) with terminal Todo closeout dispatch (#3748), pending-intent consumption (#3749), Chinese-analysis editorial requirement (#3750), rejected-draft reopen (#3751), actual work interval (#3754), approved delivery bound to the Goal Channel Bot (#3755), and normalized card readback (#3757); the dashboard retains startup error context (#3745), keeps the default status source same-origin (#3732), and avoids an unbound lark-cli variable (#3738); doctor validates deep checks by install kind (#3736); Frontstage Pages PR validation is isolated (#3728); Luna ships as a bounded account-ring extension (#3737); and structured outbound mentions are verified (#3741). | Add one synthetic lifecycle or stride-boundary fixture, extend the visible-governance walkthrough with a missing negative case, or characterize the shipped `claim_work` executor with a file-backed parity fixture. Keep activation explicit and leave source bodies, draft bodies, review text, provider payloads, private locators, cursor state, and apply/publish authority outside public fixtures. |
+| Governance and productization | A synthetic visible-governance slice landed (#3086) and received a Stage-2 refresh (#3657); the isolated prototype and its sole smoke are now retired in the 2.0 candidate, while canonical Goal Channel/status ownership and lease coverage remain with their existing typed owner; decision-context evidence cursors settle (#3079); the React homepage rebuild (#3098) landed; a deterministic project registry (#3170) serializes global sync; per-goal handoff mode gates claim/lease authority (#3164) and hard-lease gates auto-acquire completion keys (#3198); a repository change-window gate and pending ledger ship (#3319); goal channels default to human-gate auto-notify on new channels (#3523); coordination state rules are centralized (#3410) and the Stage 2 slice ships an aggregate head, file provider, and `claim_work` executor (#3529); project repository delivery lands through capability hooks (#3570) with gitless delivery workspaces settled (#3574) and CPA/provider-routing qualification recorded (#3576, #3573, #3563); a read-only stride shadow observation M1 (#3207), a synthetic stride-boundary shadow fixture (#3290), and the hierarchical stride RFC (#3204) open the next boundary; goal-artifact lifecycle projection (#3136) proposes a read-model boundary and post-outcome memory utility attribution from RFC #3215 is implemented (#3280). Post-v0.5.3, periodic-report post-writeback hooks ship (#3691) with terminal Todo closeout dispatch (#3748), pending-intent consumption (#3749), Chinese-analysis editorial requirement (#3750), rejected-draft reopen (#3751), actual work interval (#3754), approved delivery bound to the Goal Channel Bot (#3755), and normalized card readback (#3757); the dashboard retains startup error context (#3745), keeps the default status source same-origin (#3732), and avoids an unbound lark-cli variable (#3738); doctor validates deep checks by install kind (#3736); Frontstage Pages PR validation is isolated (#3728); Luna ships as a bounded account-ring extension (#3737); and structured outbound mentions are verified (#3741). | Extend the existing canonical Goal Channel/status owner only for demonstrated gaps; retain lease, unavailable-provider and recovery coverage in the existing canonical coordination and task-lease tests. Do not restore the retired visible-governance or `claim_work` prototype. Keep activation explicit and leave source bodies, draft bodies, review text, provider payloads, private locators, cursor state, and apply/publish authority outside public fixtures. |
| Security boundaries | Four merged hardening fixes contain state-file override writes (#3140), reject shell metacharacters in launcher worker commands (#3139), validate `goal_id` in reward routes (#3138), and stop ACAO:* on unauthenticated status reads (#3137). Loopback CORS, worker-command charset, and state-file symlink containment are pinned as regression tests (#3340). GH-C90 audited the four shipped negative fixtures and added one durable mutation per boundary: path-prefix sibling containment, worker-command input redirect, absolute `goal_id` path segments, and `file://localhost` ACAO rejection (`tests/test_state_file_containment.py`, `tests/test_worker_command_validation.py`, `tests/test_feedback_goal_id_validation.py`, `tests/test_status_server_cors.py`), completing the GH-C90 slice (#3655, closing #3636). | Keep credentials, private reproduction details, and advisory coordination out of public fixtures; extend only when a new shipped boundary lacks a distinct fail-closed mutation. |
| Runtime connectors and content workflows | DeepSeek Harness Turn adapter with real e2e smoke landed (#3188); OpenCode 1 and OpenCode 2 continuous goal loops ship (#3151); content-ops gained a layout template library and dense-cover defaults (#3222, #3223); provider-neutral PR program snapshots ship (#2814); `computer_use_runtime_v0` is now a machine-checkable protocol contract (#3279); a `loopx-community-discussion` public source provider (#3299) and a `loopx-repo-health` provider (#3272) extend public source coverage; goal-channel botmux runtime integration lands with terminal dispatches preserved and uncertain dispatches persisted, desktop chat routes through a loopback service, chat-wide routing and contextual inbox replies land (#3555), and managed and queued turn creation are serialized (#3542, #3562); the desktop added workspace language settings, localized write previews, and preserved heartbeat schedule and off-hours semantics (#3594); host parity, skill-delivery, and observable-handle thin pytest now cover the expanded host list. Post-v0.5.3, bot document comment scopes read from the published app (#3722), turn-start reaction replay is bounded (#3733), and the DSH plugin is one-step ready (#3725). | Add one provider-neutral parity slice for DeepSeek Harness, OpenCode 1/2, goal-channel botmux, content-ops delivery/readback, or desktop locale parity; keep raw transcripts, provider payloads, credentials, and host-local paths out of fixtures. |
| Benchmark boundary | Benchmark research was reset around native runners (#3267): native Codex Goal connects to the real runtime (#3271), workers are isolated from the host (#3277), provider env binds to installed Goal profiles (#3276) with formally verified treatments (#3275) and live continuations (#3273), runtime evidence binds to the exact container (#3303), a post-run case analyst brief ships (#3289), an integrity qualification toolkit landed (#3241), source-env usage is distinguished from credential probes (#3298, #3278), and accountable closeout requires Todo validation (#3229). A provider-neutral four-arm study contract ships (#3516), alongside restricted task-source access (#3504), non-http git clones classified as network with loopback integrity probes allowed (#3510), namespaced public case ids (#3532), locked git-clone integrity boundaries (#3515), orchestrator runtime provenance, and fail-closed runtime closeout drift (#3503). Deterministic fixtures already cover GH-C99 without live scoring or uploads: `tests/capabilities/test_benchmark_four_arm_contract.py`, `tests/capabilities/test_benchmark_experiment_board.py` (orchestrator runtime provenance negatives), `tests/capabilities/test_benchmark_runtime_continuity.py` (fail-closed closeout drift), and integrity/network/task-source cases in `tests/capabilities/test_benchmark_toolkit.py`; the covered GH-C99 fixture task was retired (#3653). Public native Goal trajectory summaries now derive from compact lifecycle facts without retaining raw artifacts (#3327), completing the GH-C16 slice. Post-v0.5.3, an external agent phase ships (#3565), restricted-access suspicion adjudicates by causal use (#3747), the plan-role fidelity gate is removed (#3753), and retired fidelity references are cleaned up (#3756). Shared lifecycle, readiness, ledger, and reducer contracts remain the public seam; generic Effect Program conformance and replay tests harden settlement infrastructure but do not change benchmark scoring or authorize live runs. Live scored comparisons stay held until a fresh task-free runner lifecycle receipt proves readiness. | Extend synthetic setup/termination attribution, derive the public trajectory summary for a second non-SkillsBench adapter, or add a SWE adapter only when a second SWE route needs shared launch/observe/ingest behavior. Do not launch scoring, duplicate the controller, or expose raw task text, logs, trajectories, verifier tails, credentials, uploads, or local paths. |
diff --git a/docs/development/control-plane-course/05-work-graph-and-peers.md b/docs/development/control-plane-course/05-work-graph-and-peers.md
index c1931e0f15..d8a66f634c 100644
--- a/docs/development/control-plane-course/05-work-graph-and-peers.md
+++ b/docs/development/control-plane-course/05-work-graph-and-peers.md
@@ -217,11 +217,9 @@ if (rejection === "claim_owner_mismatch") {
}
```
-这是同一 `authority` 决策中的片段,不是新的调用接口。未晋升源适配器
-`mutation_authority.py::authorize_todo_lifecycle_mutation` 仍在 owner mismatch 后
-读取并规范化 registry grant,再通过 `authority_core.decide` 调用同一 TS owner;
-canonical 事务也复用该 owner。Python facts、grant 校验和 receipt 投影保留,
-不再经由无人调用的单项 grant 查询。
+这是同一 `authority` 决策中的片段,不是新的调用接口。canonical 事务从 registry
+读取并规范化 grant,将 facts 交给 TS owner 统一决策。Python 只保留 registry facts
+与 grant 配置校验,不再暴露独立的 lifecycle mutation authorizer。
这份 grant 有四个边界:
diff --git a/docs/product/release-readiness.md b/docs/product/release-readiness.md
index 2c4ae6d07a..023de0e1a7 100644
--- a/docs/product/release-readiness.md
+++ b/docs/product/release-readiness.md
@@ -881,6 +881,8 @@ path, and canary route rather than as a user-facing release baseline.
Windows artifacts were verified without native client execution. No paired
benchmark, sustained scientific continuation or isolated model causal uplift
is claimed.
+ Existing Goals retain their recorded policy and require explicit migration;
+ canonical-only retirement remains a separate 2.0 candidate.
When a new public release is promoted, add it here only after the matching tag,
release note, stable ref, update path, and focused release canary agree.
diff --git a/docs/reference/authority-observation-retirement.md b/docs/reference/authority-observation-retirement.md
index 1816ba69a6..0826cf448d 100644
--- a/docs/reference/authority-observation-retirement.md
+++ b/docs/reference/authority-observation-retirement.md
@@ -3,9 +3,11 @@
The `coordination.authority_shadow` / `file_one_way` observer is retired.
Todo, handoff-mode and task-lease writes no longer re-read the Goal after commit
or create a second observation store. Their primary semantics are unchanged.
-The transaction-bound `coordination.runtime_shadow` outbox remains the only
-writable shadow lineage; its existing bootstrap, source locks, byte witnesses,
-recovery and promotion checks remain mandatory.
+The retained `coordination.runtime_shadow` files are pre-cutoff migration and
+recovery evidence. Normal 2.0 writes require canonical authority and no longer
+feed a Markdown shadow lineage. Existing bootstrap, source locks, byte
+witnesses, recovery and promotion checks remain available for reviewed import
+and interrupted-operation recovery.
This is an intentional behavior change for Goals that opted into the old
observer. Unconfigured Goals remain default-off. A retained old setting is
@@ -94,12 +96,10 @@ Bootstrap, rollback and promotion still load their supported management adapter;
this reader exit grants no writer cutoff, import or promotion authority.
The retained Python filesystem codecs preserve the existing wire and errors;
-candidate proof and drain effects remain in their TypeScript owners. Supported
-unpromoted writes and `authority-shadow drain` still need their current producer
-and orchestration modules. This separation permits eventual producer retirement
-without discarding historical readback, but does not itself authorize deletion
-or disposition of pending originals. Code rollback restores the previous import
-layout without changing data or restoring retired write authority.
+candidate proof and drain effects remain in their TypeScript owners. Historical
+outbox drain and disposition retain their orchestration modules without
+reopening a business writer. Pending originals still require explicit
+reconciliation; historical readback does not authorize deletion.
## Why two matching reads were not a capture guarantee
diff --git a/docs/reference/canonical-terminal-review.md b/docs/reference/canonical-terminal-review.md
index d04bae8c5d..aa9be08b7a 100644
--- a/docs/reference/canonical-terminal-review.md
+++ b/docs/reference/canonical-terminal-review.md
@@ -173,9 +173,9 @@ may remain a historical relation without being reported as open agent work.
Original-operation replay returns the originally accepted completion policy,
including receipts written by earlier releases, without rewriting it against
today's head. Current Todo reads show subsequent changes independently. The
-unpromoted Markdown writer retains its source-backed compatibility path; this
-change does not require migration or retire that writer. Rollback restores the
-earlier adapter and transaction together; no stored format migration is needed.
+2.0 command requires canonical authority; an unmigrated Goal must use the
+reviewed backup-and-migrate path before a new terminal write. Historical
+receipts remain readable without rewriting their stored format.
## Retiring an original Turn with an existing successor
diff --git a/docs/reference/canonical-todo-completion-update.md b/docs/reference/canonical-todo-completion-update.md
index 2c21d6e9db..291ece5652 100644
--- a/docs/reference/canonical-todo-completion-update.md
+++ b/docs/reference/canonical-todo-completion-update.md
@@ -5,6 +5,15 @@ through the TypeScript terminal transaction. File and SQLite use the same
semantic owner; PostgreSQL exercises it through the service-owned provider.
This does not select a default provider or promote an existing Goal.
+> **2.0 cutoff:** normal Todo, task-lease, handoff-mode, Monitor, team-plan,
+> bootstrap and registration writes require canonical File, SQLite or
+> PostgreSQL authority. An unmigrated Goal returns
+> `canonical_authority_required` with backup-and-migrate recovery. The later
+> "isolation" sections record the pre-cutoff migration stages; they no longer
+> describe an available Markdown write route. Projection/readback, reviewed
+> cold import, backup/history, historical receipt replay, outbox disposition,
+> exact legacy lease release and historical `fence_close` remain supported.
+
```sh
loopx todo update --goal-id example --todo-id todo_observation \
--agent-id agent-a --status done --note 'Observed outcome verified' \
@@ -130,18 +139,18 @@ With no other edit, a whitespace-only note or direct empty facade edit reports
`Todo update requires a non-empty patch` on File/SQLite
without importing the legacy Markdown editor, committing a receipt or changing
the record. This replaces the erroneous legacy-writer/fence diagnostic. A valid
-retry with the same operation id remains available. Unpromoted Goals retain
-their existing empty-edit no-change behavior. No provider or ownership default
-changes, and removing this Python routing predicate does not retire the source
-writer, capture/recovery readers or permanent Markdown display.
+retry with the same operation id remains available. An unmigrated Goal rejects
+the edit with `canonical_authority_required`, including an empty edit. The
+retired source writer and capture producers are not loaded. Permanent Markdown
+display, reviewed cold import and historical recovery readers remain.
普通 promoted 更新 facade 均交由既有 TS decoder,包括空修改。空白 `--note` 仍表示
省略,不清除已有 note;CLI 对无字段和空字符串保留原参数检查。只有空白 note 或
直接 facade 的空修改在 File/SQLite 返回明确的非空 patch 要求,不加载旧
Markdown editor、不提交回执或修改记录。同一 operation id 仍可用于后续合法重试。
-这修正了误入旧 writer/fence 的诊断;未晋升 Goal 的空修改 no-change 行为保持。
-provider 与 ownership 默认不变,旧 writer、capture/恢复 reader 和永久 Markdown
-投影不因删除该 Python 路由判断而退役。
+这修正了误入旧 writer/fence 的诊断;未迁移 Goal 的空修改也返回
+`canonical_authority_required`。旧 writer 和 capture producer 已退役,不再加载;
+永久 Markdown 投影、审核冷导入和历史恢复 reader 继续保留。
The update decoder, authoring planner and record materializer are shared with
ordinary edits. The terminal owner checks the **original** Todo's completion
@@ -208,9 +217,9 @@ Python 只复用 OS I/O;准入、声明、期限、源/租约见证和完成
An admitted `todo complete --decision-outcome approve|reject|cancel` now commits
its exact linked Agent Todo effects in the **same** provider transaction as the
User completion and receipt. Ordinary User actions completed through update or
-Chat use the same rule. `todos/user_completion.ts` owns this decision for both
-canonical providers and the legacy Markdown adapter; Python retains locked
-snapshot extraction and writeback, not a second scope/resume implementation.
+Chat use the same rule. `todos/user_completion.ts` owns this decision for all
+canonical providers. Python retains transport and readback adapters, not a
+second scope/resume implementation.
| Decision / current target | Effect |
| --- | --- |
@@ -229,10 +238,9 @@ User gate itself. Supersede never runs approval effects. An unrelated Todo or
unlinked standing approval is outside this exact-target mutation.
This fixes canonical completion previously leaving an approved target stranded.
-It also intentionally tightens **both** legacy and canonical behavior: partial
-approval cannot resume work with unmet requirements, and late rejection cannot
-revive terminal/deferred work. Normal completion without a linked target is
-unchanged. No provider selector or capability default changes.
+Partial approval cannot resume work with unmet requirements, and late rejection
+cannot revive terminal/deferred work. Normal canonical completion without a
+linked target is unchanged. No provider selector or capability default changes.
`unblock_resume` and `decision_scope_resolution` are historical business results,
not fresh permission. Existing receipt identities remain valid. Replaying a
@@ -265,6 +273,22 @@ edit under the existing provider CAS, not an execution grant. A foreign actor,
an excluded/unregistered actor, an active lease holder or stale explicit lease
proof is not exempted. No claim, lease generation or decision scope is created.
+Fresh acquire, renewal and transfer refuse `user_action` execution with
+`todo_not_executable`, including for its bound Agent. Retained acquire receipts
+cannot supply current execution proof. Before an administrative copy edit, a
+historical active or expired lease must be released with its exact
+owner/key/version. After release, the
+registered bound Agent may edit text, notes or evidence under the ordinary
+provider CAS without lease proof; the released record remains unchanged.
+Explicit old or partial proof still rejects. Recovery never recommends a new
+execution lease for a User action.
+
+`user_action` 不领取执行租约:新 acquire、renew、transfer 即使由绑定 Agent
+发起,也返回 `todo_not_executable`;历史 acquire 回执不能提供当前执行证明。
+行政编辑前,历史 active/expired lease 须先按原 owner/key/version 精确 release。之后,
+已注册的绑定 Agent 可不带租约 proof,按原 provider CAS 编辑文本、备注或证据,
+保留 released 历史;旧 proof 或部分 proof 仍被拒绝,恢复提示不再要求申请新租约。
+
```sh
loopx todo complete --goal-id example --todo-id todo_observation \
--role user --agent-id agent-a --decision-outcome cancel \
@@ -283,20 +307,18 @@ Compatibility: canonical `todo complete` continues to accept a `user_gate`
without `--decision-outcome` as closure only, not approval, rejection or
cancellation. Its required scopes, scope outcomes and blocked dependents remain
unchanged, just as for `todo update --status done`. To record a decision and its
-linked effects, explicitly supply `approve|reject|cancel`. The legacy Markdown
-explicit-completion adapter keeps its pre-existing requirement for a decision;
-that adapter's stricter input rule is not imposed on native callers. Historical
-successful receipts remain replayable as recorded.
+linked effects, explicitly supply `approve|reject|cancel`. An unmigrated Goal
+cannot use this compatibility path; it must migrate first. Historical successful
+receipts remain replayable as recorded.
The delivered cancellation entry point is CLI/managed CLI. Existing Chat
completion continues through the shared terminal owner, and frontend/Lark
consumers read the canonical completed status; no new cancellation button,
configuration setting or chat-specific authority is introduced. Direct
frontend/Lark cancellation controls are not part of this bounded slice. The
-canonical transaction is qualified on File, SQLite and real PostgreSQL. The
-legacy Markdown adapter shares outcome validation and cancellation effects,
-but its separate hard-lease terminal fence is not changed. Historical receipts
-are replayed as recorded, not reinterpreted as a new cancellation.
+canonical transaction is qualified on File, SQLite and real PostgreSQL.
+Historical receipts are replayed as recorded, not reinterpreted as a new
+cancellation or fresh write authority.
已晋升且启用 `hard_lease` 的 Goal 中,精确绑定、已注册且未被排除的 Agent 可以
关闭普通 `user_action`,无需取得执行租约。用户事项本来不能领取执行租约;这里是
@@ -399,41 +421,19 @@ feedback、迁移和 team-plan 调用方复用同一 Host IO owner。旧编辑
三个定义,仍活跃的源编辑/读取函数保留。故障仍进入原调用方恢复契约,不切换
provider、默认值、格式或权威策略,也不强制旧 Goal 升级。
-Canonical Todo creation, update, completion, supersession and archive do not
-import the Markdown line writer or source Todo capture producers during CLI
-registration. Bootstrap loads capture producers only for a source-state write;
-canonical creation and original-operation recovery do not need them. The source
-adapter loads them only when an unpromoted operation needs them. Explicit historic imports
-from `loopx.todos` still resolve to the same functions in
-`control_plane.todos.line_update`; they do not create another decision owner.
-Provider failure still rejects the canonical operation without a source write.
-
-The absence regression runs real File/SQLite new-Goal creation and Todo
-lifecycle commands with the line writer and four source Todo capture producer
-functions physically removed from a disposable package. Prose write guards and
-lease capture evidence remain in their owning adapter and keep their live callers.
-Original creation recovery preserves later work when the display is
-missing and device defaults change; an unavailable selected provider requires
-restoration rather than rebuilding source authority. It qualifies that caller
-boundary, not deletion of the supported Markdown writer, backup readers or
-receipt recovery. Those paths retain their own last-caller migration exits.
-The same absence check covers admitted hard-lease work through material vision
-writeback, one quota settlement and exact retry. Missing leases and provider
-outages still reject; rebuilding the display does not make it fallback authority.
-
-canonical Todo 创建、更新、完成、替代和归档不再在 CLI 注册时导入 Markdown
-行写入器或旧源 Todo capture producer;bootstrap 仅在源状态写入时加载 capture,
-canonical 创建和原操作恢复不依赖它。未迁移操作实际需要旧路径时才加载。
-`loopx.todos` 的历史显式导入仍指向
-同一实现,不新增决策 owner。provider 失效仍拒绝 canonical 写入,不回退到旧源。
-缺失模块回归在一次性包中物理移除行写入器及四个旧源 Todo capture 函数,并运行真实 File/SQLite 新 Goal 创建与
-Todo 生命周期。显示源缺失且设备默认值改变后,原创建操作恢复仍保留后续工作;所选
-provider 不可用时要求恢复,不重建旧源权威。
-同一 adapter 中仍有真实调用方的 prose 写入边界和 lease capture evidence 保留。
-相同缺失模块检查覆盖 hard-lease 工作的准入、实质 vision 写回、一次额度结算与原
-Turn 重试;缺租约和 provider 失效仍拒绝,重建显示不会使它成为回退权威。
-这验证调用方隔离;仍受支持的 Markdown writer、备份读取和原回执恢复保留各自的
-迁移与最后调用方退役条件。
+Canonical Todo creation, update, completion, supersession and archive require
+File, SQLite or PostgreSQL authority. The Markdown line writer, source Todo
+capture producers and their compatibility imports are retired. Provider failure
+rejects the operation without a source write. The absence regression runs the
+real File/SQLite lifecycle and hard-lease settlement paths with those modules
+physically absent. Permanent Markdown projection, backup readers, reviewed cold
+import, historical receipt replay and bounded outbox disposition remain.
+
+canonical Todo 创建、更新、完成、替代和归档只接受 File、SQLite 或 PostgreSQL
+authority。Markdown 行写入器、旧源 Todo capture producer 及兼容导入均已退役。
+provider 失效时明确拒绝,不回退写旧源。真实 File/SQLite 生命周期和 hard-lease
+结算会在这些模块物理缺席时运行。永久 Markdown 投影、备份读取、审核冷导入、
+历史回执重放和有限 outbox 处置继续保留。
This closes the User completion-update caller within TS T1/T2 and local-default
L2. It does not close every Monitor/event caller, executor-held effect fencing,
@@ -533,62 +533,76 @@ The retired RPC rejects unsupported-method requests. Continuation selection,
contradictory-intent rejection and historical terminal recovery remain covered
through the production owner. Python normalization/cache, import-compatible
enums, original receipt values, Markdown projection, backup readers and live
-Host IO remain. This removes neither the Markdown writer nor shadow capture,
-and changes no provider default. File/SQLite CLI completion and settlement
-recovery are the real-path checks; installation qualification also runs those
-paths from a wheel with the retired entries absent. Reverting this code restores
-the internal crossing without converting data or rewriting receipts.
+Host IO remain. The Markdown writer and shadow capture producers are retired.
+File/SQLite CLI completion and settlement recovery are the real-path checks;
+installation qualification also runs those paths from a wheel with the retired
+entries absent. Reverting this code restores the internal crossing without
+converting data or rewriting receipts.
完成与 terminal update 已在现有 TS 事务中组合完成状态规则。本批只退役两个无生产
调用方的 Python 入口及其不再使用的 RPC carrier;选择 continuation、拒绝矛盾意图
和历史收尾恢复仍由原 TS owner 承担。Python 编解码/cache、兼容枚举、原回执、
Markdown 投影、备份读取及活跃 Host IO 保留。真实 File/SQLite CLI 和删除旧入口后的
-wheel 路径验证完成与结算恢复;本批不删除 Markdown writer/shadow,也不改变默认
-provider。代码回滚不需要转换数据或重写回执。
+wheel 路径验证完成与结算恢复;Markdown writer 和 shadow capture producer 已
+退役。代码回滚不需要转换数据或重写回执。
-## Canonical mutation entrypoint isolation
+### Repairing imported completion metadata / 修复导入的完成元数据
+
+An imported completed Todo may lack `completion_continuation`. Use an authorized
+Actor and its original completion identity to preview and apply the ordinary completion
+command without new note, evidence, successor, policy or validation intent.
+For a record without a stored Turn identity:
+
+```sh
+loopx todo complete --goal-id example --todo-id todo_imported --agent-id agent-a --dry-run
+loopx todo complete --goal-id example --todo-id todo_imported --agent-id agent-a
+loopx todo list --goal-id example --todo-id todo_imported
+```
+
+A stored identity requires the original Turn tuple and any required execution
+proof; do not replace it with a new identity. The TypeScript owner accepts the
+ordinary transport's read-only policy facts, including an explicit `--claimed-by`
+that matches the stored claim, but applies no policy and acquires no lease during
+this repair. A different claim is rejected. It preserves identity, completion time, content,
+claim and original receipt. Preview/refusal has no effects; retrying the same
+operation preserves newer writes. Fresh edits require their own authorized
+operation after repair.
+
+导入的已完成 Todo 若缺少 `completion_continuation`,使用已授权 Actor、原完成身份和所需
+执行证明,通过普通完成命令预览并修复;不夹带新的 note、evidence、successor、
+policy 或验证意图。上例适用于未存储 Turn 身份的记录;已有身份必须提供原 tuple,
+不能替换身份。TS owner 接受普通入口携带的只读 policy 事实,包括与已存 claim 一致的
+显式 `--claimed-by`;不同 claim 会被拒绝。修复不应用 policy、
+不领取租约,并保留原 ID、完成时间、内容、claim 与回执。预览和拒绝无副作用;
+原操作重试保留后写。后续编辑仍须单独通过原授权入口。
+
+## 2.0 canonical mutation boundary
Create, update, complete, supersede and archive share
`control_plane/todos/mutation_api.py` and the existing TypeScript transaction
-owners. The historic `loopx.todos` imports remain available lazily. The
-unpromoted writer bodies live in `control_plane/todos/legacy_mutation.py`;
-canonical CLI registration and provider operations do not import that module.
-This is a mechanical separation, with no Python deletion payoff or new decision
-owner. Shared validation, successor intent and receipt adapters remain live.
+owners. An unmigrated Goal receives `canonical_authority_required`; no mutation
+entrypoint loads a Markdown writer or capture producer. Shared validation,
+successor intent and historical receipt readers remain live.
| Caller family | Current boundary and retained duty |
| --- | --- |
| CLI and App canonical Todo mutations | Existing provider-first create/update/terminal owners; missing selected authority fails without starting a Markdown writer. |
-| Unpromoted create/update/complete/supersede/archive | Load the isolated writer only after the existing provider owner declines an unpromoted request; preserve writer fence, capture preparation, original receipts and projection. |
-| Bootstrap, handoff, team-plan and Monitor source capture | Still have supported unpromoted callers; retain producers and outbox reconciliation until each caller exits. |
-| Import-compatible `apply_todo_update_to_lines` and legacy terminal updates | The existing `todo.field_update.plan` owns supplied wait normalization and contradictory clears in the complete edit. Python retains blank-as-omission transport, locked Markdown IO and receipt formatting, with no separate resume-normalization preflight. |
-| Markdown projection, source import and backup recovery | Retain qualified readers, historical bytes and Host IO; moving writer code does not grant deletion of these paths. |
-
-The File/SQLite lifecycle and settlement oracle removes the **whole isolated
-writer module**, line editor and four capture producers from a copy of the
-installed package. It covers original creation replay, update, completion,
-supersede/archive, leased settlement, restart recovery and provider loss.
-The same source and installed-wheel arms also recover a real legacy outbox
-transaction interrupted before or after the native store commit, after those
-producer definitions are physically removed. Recovery preserves source bytes
-and original receipts, drains exactly once and rejects an unproved cursor
-without deleting pending entries. This qualifies the retained File shadow drain;
-it does not qualify cold backup import or dispose of an active Goal's outbox.
-Retained Markdown interruption/capture tests guard the compatibility arm.
-The cold-import CLI oracle also removes that whole writer and line editor
-before preparing and applying a reviewed source backup. After import, real
-File/SQLite CLI operations reject an unleased edit, acquire a fresh lease,
-update imported work and create new work. Original import recovery and Todo
-operation replay preserve the current complete records, archived evidence and
-provider revision after the source display is removed. Losing the provider
-refuses recovery without creating a replacement; restoring its original bytes
-recovers both imported and later writes. This composes the migration and writer
-isolation boundaries; the historic facade and still-live capture adapters remain
-in the post-import mutation arm.
-Reverting the code move requires no data conversion or receipt rewriting.
-Whole-writer retirement still needs the declared support cutoff, last callers
-and pending outbox disposition; this separation does not force an upgrade or
-change a provider default.
+| Unpromoted create/update/complete/supersede/archive | Reject before write and return backup-and-migrate recovery. |
+| Bootstrap, handoff, team-plan and Monitor | Require canonical authority for new writes; retain only historical receipt replay and outbox disposition. |
+| Markdown projection, reviewed source import and backup recovery | Retain qualified readers, historical bytes and Host IO without granting write authority. |
+
+The File/SQLite lifecycle and settlement oracle runs from source and installed
+packages with the retired writer, line editor and capture producers absent. It
+covers create, update, completion, supersede/archive, leased settlement, restart
+recovery and provider loss. Historical outbox tests seed persisted entries
+directly. Recovery preserves source bytes and original receipts, drains exactly
+once where disposition permits it, and rejects an unproved cursor without
+deleting pending entries.
+
+The cold-import oracle prepares and applies a reviewed source backup without
+calling a retired producer. After import, real File/SQLite operations acquire
+canonical authority and preserve later writes across original-operation replay.
+Losing the provider refuses recovery without creating a replacement.
Production callers import those same owners directly: `mutation_api` for the
five operations and archive constant, `list_readback` for complete Todo reads,
@@ -620,38 +634,30 @@ Invalid supplied conditions use the typed owner's actionable diagnostic. This
removes a duplicate decision crossing on the compatibility entrypoint; canonical
updates already use their whole transaction and gain no new route or authority.
-创建、更新、完成、替代和归档统一进入既有 `mutation_api.py` 与 TS 事务 owner,
-`loopx.todos` 保留惰性历史导入。未迁移 writer 已隔离到 `legacy_mutation.py`,
-canonical CLI 注册及 provider 操作不加载它;这只是机械隔离,不计作 Python
-删除收益,也不新增决策源。共享验证、successor intent 与原回执适配器仍有用途。
-安装包副本物理移除整个 writer、行编辑器及四个 capture producer 后,真实
-File/SQLite CLI 验证生命周期、租约结算、重启与 provider 失效恢复。未迁移
-Markdown outbox 的真实 native commit 前/后中断,在移除 producer 后由相同
-源码/安装包两臂恢复:源字节与原回执保持、恰好一次 drain,未证明的 cursor
-被拒绝且 pending 条目不被删除。这验证保留的 File shadow drain,活跃 Goal 的
-outbox 仍须逐项处置;中断/capture 反例继续保留,代码回滚不转换数据或重写回执。
-冷导入 CLI 检查在审核备份、准备及应用前也移除整个 writer 和行编辑器,随后
-真实 File/SQLite CLI 拒绝无租约编辑、取得新租约、更新导入任务并新增任务。
-删除显示源后,原导入恢复和 Todo 原操作重放保留完整的新旧记录、归档证据及
-当前 provider revision;provider 丢失时明确拒绝,不重建空库,恢复原字节后
-新旧写入仍可读。该旅程组合迁移及 writer 隔离边界;迁移后的 mutation 检查
-仍保留历史 facade 和有真实调用的 capture 适配器。
-bootstrap、handoff、team-plan 和 Monitor 的旧源调用尚须分别退役,最终删除仍须
-支持截止及 outbox 处置;本批不强制升级或更改 provider 默认值。
-保留的 `apply_todo_update_to_lines` 兼容入口与 legacy terminal 更新由既有
-`todo.field_update.plan` 在完整编辑中统一归一化等待条件、拒绝矛盾 clear;Python
-只适配空白值省略、锁内 Markdown IO 与回执格式,不再单独 preflight resume。
-时区归一化、显式清除和拒绝时源字节不变继续保持;非法条件使用 typed owner
-的可操作诊断。canonical 更新本就使用完整事务,本批不新增路由或权限。
-
-## Team-plan source-writer isolation
+创建、更新、完成、替代和归档统一进入 `mutation_api.py` 与 TS 事务 owner。
+源码和安装包验收会物理移除旧 writer、行编辑器和 capture producer,再运行
+File/SQLite 生命周期、租约结算、重启及 provider 故障恢复。历史 outbox 用持久化
+样本直接构造,不调用已退役 producer。冷导入从审核备份开始,导入后只走
+canonical 写入;provider 丢失时明确拒绝,不重建空库。
+
+## 2.0 team-plan mutation boundary
Canonical team-plan preview, confirmed batch commit and original-operation
-replay do not load `runtime_shadow_writer_adapter.py`. Source locks, reviewed
-intent, the TypeScript batch transaction and canonical projection outbox keep
-their existing owners. Only the supported unpromoted branch loads the capture
-producer and Markdown editing dependencies. Its batch-write IO seam remains
-available to existing failure/recovery consumers through a lazy adapter.
+replay use the TypeScript batch transaction and canonical projection outbox.
+An unpromoted fresh request returns `canonical_authority_required`; it cannot
+load a capture producer or Markdown editor. An exact pre-cutoff receipt may be
+read back before import through the same typed identity validator, without
+creating tasks, capture evidence or execution authority. The locked source
+binding is revalidated first; a selected but unavailable provider still fails
+rather than replaying from Markdown as fallback.
+
+A missing Markdown display does not block an exact canonical receipt replay.
+After the typed owner verifies the original plan, actor and reviewed basis,
+the existing projection owner restores the current canonical Todos, including
+later work, without another provider commit. A fresh plan still requires a
+readable source; reviewing a missing display cannot authorize new assignments.
+An undecodable display also leaves the receipt readable, but projection stays
+pending without overwriting its bytes; repair the display before retrying.
The real File/SQLite Chat oracle physically removes the capture adapter from
the package before preview and confirmation. It checks provider outage without
@@ -661,37 +667,34 @@ pre-promotion receipts remain historical readback: replay preserves newer work
and the current provider revision. Normal wheel qualification uses the same
oracle; the source checkout cannot substitute for its installed package.
-This isolates another canonical caller, without deleting the still-supported
-legacy branch, changing provider defaults or requiring existing Goals to migrate.
Projection IO and historical receipt readers remain necessary. Assignment still
grants neither receiver adoption nor execution authority.
-canonical team-plan 的预览、确认后整批提交与原操作重放不再加载 capture adapter;
-源锁、审核意图、TS batch 事务及 canonical 投影 outbox 继续复用原 owner。
-只有受支持的未迁移分支加载 capture producer 与 Markdown 编辑依赖,既有批量
-写入故障/恢复消费方通过惰性 IO adapter 保留其调用入口。
+canonical team-plan 的预览、确认后整批提交与原操作重放复用 TS batch 事务及
+canonical 投影 outbox。未迁移的新请求返回 `canonical_authority_required`,不能加载
+capture producer 或 Markdown editor。原 pre-cutoff 回执在导入前也可通过同一 TS
+身份校验读回,不创建 Todo、capture 或执行授权。先在原锁内核验来源绑定;已选定
+provider 不可用时仍明确拒绝,不能将 Markdown 历史读取当作 fallback。
+Markdown 展示文件缺失不阻断 canonical 原回执重放。TS owner 校验原计划、操作者及
+审阅依据后,既有投影 owner 恢复当前 canonical Todo(含后来工作),不增加 provider
+提交。新计划仍须可读的来源;对缺失展示的审阅不能授权新分派。
+展示内容不可解码时仍可读原回执,但投影保留 pending 且不覆盖原字节;修复展示后重试。
真实 File/SQLite Chat 检查在预览及确认前物理移除该模块,验证 provider 失效无
Markdown fallback、恢复、后续写入后的原操作重放、审核依据过期及提交后投影失败
恢复;导入前的历史回执重放保留后来写入与当前 revision。正常 wheel 复用同一
-oracle,不能以源码检查代替安装包。本批只隔离 canonical 调用,不删除仍受支持的
-旧源分支、不改变默认值或强制迁移;投影 IO、历史 reader 继续保留,分派不授予
+oracle,不能以源码检查代替安装包。投影 IO、历史 reader 继续保留,分派不授予
receiver adoption 或执行权。
-## Monitor source-writer isolation
+## 2.0 Monitor mutation boundary
-Canonical Monitor polling does not load the live `legacy_monitor_poll.py`
-writer. Immutable pre-promotion receipt codecs and replay now live in the
-scheduler's `monitor_batch_receipts.py`, shared by the router and the supported
-legacy writer. The TypeScript batch planner still validates the original
-operation and supplies its unchanged writeback. There is no new decision owner,
-receipt format, provider setting or execution authority.
+Canonical Monitor polling uses the TypeScript batch planner and provider
+writeback. `legacy_monitor_poll.py` is retired. Immutable pre-promotion receipt
+codecs and replay live in `scheduler/monitor_batch_receipts.py`. There is no new
+decision owner, receipt format, provider setting or execution authority.
-On historical replay, the router checks promotion before loading shadow IO.
-After cutover, even ambiguous retained source outbox bytes remain inert: replay
-must not drain them into the current authority. Before cutover, an ordinary
-retry still loads the existing drain owner when an outbox is present, recovering
-one markerless prepared batch after the atomic Markdown replacement. That
-valuable recovery path and the live legacy instrumentation seam remain.
+On historical replay, ambiguous retained source outbox bytes remain inert and
+cannot grant current authority. Bounded disposition reads persisted historical
+entries; it does not reopen a Monitor business writer.
Real File/SQLite CLI checks physically remove the entire Monitor writer from a
copied source package or a normally installed wheel, together with all four
@@ -699,26 +702,17 @@ shadow producer modules after CLI startup isolation. They cover no-change and
material-change polling, provider outage/restoration, lease-release replay and
recovery of an interrupted pre-promotion operation after newer canonical work.
Recovery preserves the original successor identities, current provider revision,
-state and retained outbox bytes, with one event and no quota spend. A fresh
-production provider call proves these dependencies are not loaded even when
-the modules are present. Existing malformed-receipt, intent-conflict, writer-fence and
-unpromoted outbox recovery counterexamples remain required.
-
-This qualifies the Monitor caller boundary, not removal of the still-supported
-source writer or all capture producers. Retain historical readers, backup/import
-recovery and original outbox disposition until their own exits pass. No default,
-existing Goal upgrade requirement, PostgreSQL transaction or Host lifecycle
-changes. Reverting this isolation restores imports without rewriting receipts;
-it does not restore legacy write authority after cutover.
-
-canonical Monitor poll 不再加载整个 `legacy_monitor_poll.py` 活跃 writer。
-晋升前的 immutable 回执 codec/replay 归入 scheduler 的
-`monitor_batch_receipts.py`,路由及旧 writer 共用;TS batch planner 继续校验
-原操作并返回相同 writeback,不新增决策源、回执格式、provider 配置或执行权。
-历史重放先检查晋升状态再加载 shadow IO。晋升后,含歧义的原 outbox 字节仍须
-保持静止,不导入当前 authority;晋升前的普通重试则在 outbox 存在时调用原 drain
-owner,恢复一次 Markdown 原子替换后未写 commit marker 的 prepared batch。
-保留这条有效恢复路径和现有 legacy 故障注入入口。
+state and retained outbox bytes, with one event and no quota spend. Existing
+malformed-receipt, intent-conflict, writer-fence and historical outbox
+counterexamples remain required.
+
+Historical readers, backup/import recovery and original outbox disposition
+remain. No provider default, PostgreSQL transaction or Host lifecycle changes.
+
+canonical Monitor poll 使用 TS batch planner 和 provider writeback;
+`legacy_monitor_poll.py` 已退役。晋升前的 immutable 回执 codec/replay 归入
+`monitor_batch_receipts.py`。历史 outbox 字节保持静止,有限处置只读取持久化
+历史条目,不恢复 Monitor 业务 writer。
真实 File/SQLite CLI 检查在复制源码/正常安装 wheel 中物理移除整个 Monitor
writer,并在 CLI 启动隔离后移除四个 shadow producer module;覆盖无变化/实质
@@ -727,10 +721,8 @@ writer,并在 CLI 启动隔离后移除四个 shadow producer module;覆盖
及原 outbox 字节保持,一次 event 且无 quota spend。冷 production provider 调用
另证明模块仍存在时也不加载旧 writer/drain。损坏回执、意图冲突、writer fence
和未晋升 outbox 恢复反例仍须通过。
-本批只验收 Monitor 调用边界,不删除受支持的源 writer/全部 capture producer;
-历史 reader、备份导入恢复及原 outbox 处置各保留自己的出口。不改变默认、已有
-Goal 升级要求、PostgreSQL 事务或 Host 生命周期。回滚只恢复导入关系,不改写
-回执,也不恢复晋升后的 legacy 写权限。
+历史 reader、备份导入恢复及原 outbox 处置继续保留。不改变默认、
+PostgreSQL 事务或 Host 生命周期。
## Frontier classification retirement
@@ -749,10 +741,10 @@ cache or new public packet field.
File/SQLite quota CLI checks use the same independent oracle before and after
the move. The permanent Markdown narrative projection remains required by
-status; canonical storage does not retire that document IO. Existing Markdown
-writers, source capture, old backups and original-operation recovery keep
-their live callers and separate retirement exits. Code rollback restores the
-internal classifier without migrating Goal data or changing provider defaults.
+status; canonical storage does not retire that document IO. Old backups and
+historical original-operation recovery keep their declared readers. Code
+rollback restores the internal classifier without migrating Goal data or
+changing provider defaults.
Goal frontier 的等待、fallback 与重规划调用方保留 Python import 和旧输入编码;
summary 视图优先级、认领/排除分组及诊断计数下界交给既有 TS frontier owner,
@@ -762,5 +754,4 @@ summary 视图优先级、认领/排除分组及诊断计数下界交给既有
同次 Goal context reduction 复用 fresh 计数给重规划及最终投影(含原回执恢复);
独立 helper 仍读取当前 summary,不新增跨调用缓存或公共 packet 字段。
真实 File/SQLite quota CLI 使用迁移前后同一独立断言。永久 Markdown 叙事投影、
-仍有调用方的 writer/capture、旧备份及原操作恢复继续保留。本批不转换数据或回执,
-不改变 provider 默认,也不代表全部 Python 或旧 writer 已退役。
+旧备份及历史原操作恢复继续保留。本批不转换数据或回执,不改变 provider 默认。
diff --git a/docs/reference/handoff-mode.md b/docs/reference/handoff-mode.md
index e3ba872707..b58e75a0e7 100644
--- a/docs/reference/handoff-mode.md
+++ b/docs/reference/handoff-mode.md
@@ -3,8 +3,17 @@
`soft_claim` uses the Todo assignment; `hard_lease` also requires the existing
execution lease fences. New `set` and migration intents accept these two modes.
`legacy` remains a historical source for backed-up upgrades and original receipt
-recovery; new requests cannot select it, even as a no-op. Existing Goals are not
-silently changed, and creation/Host defaults remain unchanged in this stage.
+recovery; new requests cannot select it, even as a no-op. In the 2.0 candidate,
+fresh claim/update/terminal and Monitor admission, execution fences, acquisition
+and lease maintenance reject `legacy` with `handoff_mode_retired`. Inspection
+keeps the original lease record but reports it ineffective; an original acquire
+receipt cannot be presented as current execution proof. Exact release and original
+receipt recovery remain available. Delegation checks the typed inspection's
+`execution_policy.supported` before first launch and resuming a running operation,
+including retained records declaring that no lease was needed. This readback grants
+no execution authority; hard mode still needs its original claim/lease proof.
+Source-only lease inspection is historical: it cannot report an effective execution
+and no longer asks Python for a second full Todo projection. Existing Goals are not silently changed, and creation/Host defaults remain unchanged in this stage.
This is an ownership policy, not a capability grant or a storage provider.
The App's Task list and Task details use the same reviewed completion entry.
@@ -40,9 +49,9 @@ loopx --format json handoff-mode set --goal-id example-goal --mode soft_claim --
loopx --format json handoff-mode set --goal-id example-goal --mode soft_claim --operation-id mode-change-1
```
-Before promotion, `show`/`set` use the existing frontmatter adapter and locks.
-After promotion they use the selected canonical provider, even when Markdown
-is stale or absent. Provider failure rejects the operation without fallback;
+Before import, `show` can inspect historical frontmatter; normal `set` requires
+canonical authority. Canonical operations use the selected provider even when
+Markdown is stale or absent. Provider failure rejects the operation without fallback;
leftover lease files cannot override canonical records. Only frontmatter and
compact ownership facts cross the legacy TS planning boundary; unrelated body,
metadata, line endings and the final newline remain intact.
@@ -50,9 +59,9 @@ metadata, line endings and the final newline remain intact.
A changed `set` requires no unfinished claimed active Todo and no time-active
lease in the complete snapshot. Invalid expiry/schema cannot prove quiescence;
concurrent work invalidates its CAS. Identical soft/hard mode is a no-op even
-with active work. Malformed legacy frontmatter can be repaired only when
-quiescent; duplicate fields or missing frontmatter reject, and malformed
-canonical state is never repaired through the legacy adapter.
+with active work. Historical malformed frontmatter must be resolved through the reviewed import
+boundary; it cannot create a normal writer fallback. Malformed canonical state
+is never repaired through that adapter.
Canonical `--operation-id` binds Goal and target. Even an accepted no-op seals a
receipt. Retry with the same ID/target recovers the original decision and never
@@ -61,8 +70,8 @@ restores an old mode over later work; changed intent needs a fresh ID.
Malformed receipts reject. An original `set --mode legacy --operation-id `
request still reaches receipt recovery after upgrade, including after a later
policy change. New legacy writes and dry-runs return `handoff_mode_retired`;
-`plan-migration` also refuses legacy as a target. The unpromoted writer does not
-promise durable operation replay.
+`plan-migration` also refuses legacy as a target. An unpromoted normal write returns `canonical_authority_required`; use the
+reviewed import before changing current policy.
## Migrate an assigned canonical Goal with a verified backup
@@ -127,16 +136,18 @@ unretained preview.
CLI and App share registry validation and the same TS migration owner. Lark
ordinary Todo/lease actions consume that canonical state; policy migration has
no Lark editor in this stage. The capability editor does not own this policy. Migration does not
-qualify provider defaults, D1–D3, or PostgreSQL deployment. Complete Host lease
-lifecycles and migration of remaining legacy Goals precede legacy execution and
-last-caller Python retirement; historical import/receipt readers stay at their
-migration/recovery boundary.
+qualify provider defaults, D1–D3, or PostgreSQL deployment. Complete Host lease lifecycles, reviewed migration of remaining Goals and installed
+adoption remain release qualifications. The candidate's legacy execution cutoff
+is not evidence that those qualifications passed. Historical import/receipt
+readers stay at their migration/recovery boundary.
## 中文
新增 `set` 和迁移只接受 `soft_claim`、`hard_lease`。legacy 仍可作为升级来源、
-历史回执恢复输入,不能再作为新请求的目标;已有 Goal 和创建/Host 默认值不被
-静默改写。soft 用于明确的单执行者,hard 用于本机并行 Agent/automation 和云端
+历史回执恢复输入,不能再作为新请求的目标;已有 Goal 和创建/Host 默认值不被静默改写。2.0 候选的 fresh
+claim/update/terminal、Monitor、执行 fence 及租约执行准入明确拒绝 legacy;
+inspect 保留原记录但报告无有效执行,旧 acquire 回执也不能换成当前 proof。
+精确 CAS release、历史原操作恢复及有备份的审核迁移继续可用。soft 用于明确的单执行者,hard 用于本机并行 Agent/automation 和云端
共享;默认策略切换前还需验证 Host 的 acquire、renew、release 和阻塞结算。
App 的任务列表和详情共用审核完成入口。终态 owner 要求租约或当前执行证明时,
页面明确说明此调用方无法代为完成;负责 Agent 须从原执行提交当前证明,之后刷新
@@ -178,15 +189,17 @@ App 在 **Goal 设置 → 任务所有权** 复用同一操作:选择协作认
CLI 与 App 共用注册信息校验和 TS 迁移 owner;飞书普通操作消费相同 canonical 状态,
本阶段没有飞书策略编辑器,也不新建 capability 设置项。此批不关闭 provider 默认与
-D1–D3;先完成 Host 生命周期、
-逐 Goal 备份迁移,再删除 legacy 执行分支和无调用方的 Python 业务逻辑。旧格式解析
-只保留在迁移/历史回执恢复边界。
+D1–D3;候选已拒绝 legacy 正常执行,并不证明 Host 生命周期、逐 Goal 备份迁移
+及安装采用已经完成。旧格式解析只保留在迁移/历史回执恢复边界。
## Recover a canonical Todo edit with retained lease history
-A canonical Todo can retain a released or expired lease even in `legacy` mode.
-In `legacy` and `hard_lease`, that record preserves execution lineage: `todo update`
-still requires a current active owner proof. In `soft_claim`, an open agent Todo's
+A canonical Todo can retain a released or expired lease in historical `legacy`
+mode. That mode requires a reviewed migration before normal work, even with a
+retained active proof. Release the exact old execution first, then use the existing
+backup/plan/migrate flow; never acquire another legacy execution. In `hard_lease`,
+the retained record preserves execution lineage: `todo update` requires current
+active owner proof. In `soft_claim`, an open agent Todo's
current claim owner may edit text/notes or clear those fields without proof when
its retained lease is released and held by the same owner. The transaction keeps
the lease, epoch and historical receipts unchanged and still checks actor,
@@ -202,6 +215,7 @@ under a fresh operation and current revision. Other execution fences remain:
| Observation | Recovery |
| --- | --- |
+| Historical `legacy` policy, with or without a retained lease | Release any retained execution using its exact CAS, then review and back up a migration to `soft_claim` or `hard_lease`. Historical acceptance grants no current execution. |
| Active lease held by the current claim owner; missing/stale proof | Inspect the lease and retry using its current proof. Do not acquire a competing execution. |
| Released/expired lease; current owner is eligible and acquisition passes the current mode, acceptance and scope checks | Inspect the version, acquire a short lease with a fresh key, update using the returned proof, then release it. |
| Active foreign holder, divergent claim, or absent claim | Reconcile ownership through its lifecycle; never borrow another holder's proof. |
@@ -227,7 +241,8 @@ todo_done: --reason ''` 暂停。此显式 wait
The recovery descriptor uses the standalone `loopx task-lease acquire` command.
Combined `todo claim --task-lease-idempotency-key` is restricted to `hard_lease`
-and is not the recovery route for `legacy`. Acquire uses `--owner`, a **fresh**
+and is unavailable in historical `legacy` mode. After reviewed migration,
+acquire uses `--owner`, a **fresh**
`--idempotency-key`, `--expected-version` from `task-lease inspect`, a bounded
`--ttl-seconds`, and any projected `--write-scope` values. Retry the original
update with `--task-lease-idempotency-key` and `--task-lease-expected-version`
@@ -241,8 +256,12 @@ the configured mode, promote a provider, or waive acceptance.
### 保留租约历史时的更新恢复
-canonical Todo 在 `legacy` 模式下也可能保留 released/expired lease。legacy 与
-hard_lease 仍要求当前有效 owner proof。soft_claim 下,open Agent Todo 的当前
+历史 `legacy` policy 即使保留 active lease,也不能执行新的 Todo mutation 或借原回执
+恢复当前执行资格。先按原 key/version 精确 release,再通过既有 plan/backup/migrate
+审核升级到 soft_claim 或 hard_lease;不改写旧回执、不丢弃 lease history,也不自动迁移活跃 Goal。
+
+canonical Todo 在历史 `legacy` 模式下也可能保留 released/expired lease,
+须先审核迁移;hard_lease 仍要求当前有效 owner proof。soft_claim 下,open Agent Todo 的当前
claim owner 可不带 proof 编辑文本/备注或清空这些字段,前提是保留的 lease 已释放
且同属本人。事务保留 lease、epoch 和原回执,仍核验 actor、excluded、bound 和
provider CAS;这不授权 ownership、planning、工作要求或完成变更。未释放的 active
@@ -253,8 +272,8 @@ lease 即使自然过期,也不适用。显式旧 proof 或部分 proof 仍拒
有效的本主租约应 inspect 后使用当前 proof 重试;已释放或过期的租约,只有当前
claim owner 满足相同的 acquire 准入规则时,才提示“inspect version → 用新 key
-申请短 lease → 带新 proof 更新 → release”。legacy 必须用独立 `task-lease acquire`,
-不能用仅限 hard_lease 的合并式 claim+lease。
+申请短 lease → 带新 proof 更新 → release”。legacy 下独立 acquire 与合并式
+claim+lease 均不可执行,必须先审核迁移;合并式 claim+lease 仍仅限 hard_lease。
异主有效 lease、claim 不一致、不允许执行的 Todo、验收阻塞或 scope
冲突不会得到不可执行的 acquire 建议;修改已租用工作的要求或状态须走对应 lifecycle。
diff --git a/docs/reference/local-authority-provider-selection.md b/docs/reference/local-authority-provider-selection.md
index b062eddcc9..4530d7cf7d 100644
--- a/docs/reference/local-authority-provider-selection.md
+++ b/docs/reference/local-authority-provider-selection.md
@@ -186,8 +186,9 @@ layout or responsive navigation is introduced.
-To disable future canonical creation, preview and apply the same v1 document
-with `canonical_creation=false`. Removing the preference restores the candidate
+In 2.0, `canonical_creation=false` preserves an inspectable preference but
+refuses new Goal creation. Enable canonical creation and review the preview
+before retrying. Removing the preference restores the candidate
SQLite/hard-lease default for future new Goals; removal is **not** opt-out.
To remove the whole preference:
@@ -247,7 +248,8 @@ CLI 使用上面的完整 JSON 和 preview/apply/inspect/bootstrap 命令;App
后的存储丢失须恢复完整备份,不能重新创建空库;通用 force 不能重建。原回执在
后续写入后仍可读回,重试不得丢失或重复 Todo。
-显式设置 `canonical_creation=false` 才关闭之后的新建;remove 删除偏好会恢复候选
+2.0 中,显式 `canonical_creation=false` 保留可查看的偏好,但拒绝之后的新建;
+须启用并审核配置,再重试创建。remove 删除偏好会恢复候选
SQLite/hard-lease 默认,不能用于关闭。两者均不会迁回已有数据、
删除 fence 或重新开放旧 writer。既有 Goal 升级仍需备份、停止写入、结算租约和
审核计划;反向迁移须保留新增写入。受支持的旧备份、格式和原回执恢复能力保留。
@@ -266,18 +268,18 @@ boolean and integer facts remain distinct even where Python object equality
would equate them. This uses the same encoding boundary as capture identity,
without granting a prose writer any Todo mutation or provider fallback.
-## Retirement boundaries before changing the release default
+## 2.0 retirement boundaries
-SQLite adoption and Python retirement need separate evidence. A canonical Goal
-can bypass a source writer while supported unmigrated Goals still call it.
-Changing the creation default does not migrate those Goals or settle their
-capture history. Use the existing [retirement cadence](../architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.md)
-to qualify each last caller family independently.
+Normal mutation now requires canonical authority. Python still owns supported
+Markdown projection, reviewed cold import and historical recovery adapters.
+Changing the creation default does not migrate an existing Goal or settle its
+historical obligations. The existing [retirement cadence](../architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.md)
+records how each former writer family reached this cutoff.
-| Retained boundary | Current value and owner | Evidence needed before removal |
+| Retained boundary | Current value and owner | Required evidence |
| --- | --- | --- |
-| `todos/mutation_api.py` → `todos/legacy_mutation.py`; `bootstrap.py`, `work_items/team_plan_adapter.py`, `scheduler/legacy_monitor_poll.py` | These are still reachable unpromoted mutation families. Production callers have left the historic `todos.py` re-export; canonical transactions use their concrete typed owners. | Qualify the import-first cutoff for every family below, then delete its normal source-write dispatch and implementation together. Keep Markdown display, historical imports where supported, and import/export recovery. |
-| `runtime_shadow_writer_adapter.py`, `local_authority_shadow_outbox.py` | Source writers prepare capture before the primary write and record commit afterward; outbox records preserve interrupted operations. Typed coordination owners interpret the captured mutations. | Stop the corresponding source producers, classify every pending prepared/committed entry against its source and exact receipt, and prove recovery after producer removal. Clearing configuration cannot cancel an active capture obligation. |
+| `todos.py`, `bootstrap.py`, canonical Todo and lease adapters | Public mutation entrypoints admit only File, SQLite or PostgreSQL authority. Markdown remains a display and import source, not a write authority. | Run creation, update and recovery with the removed writer modules absent. Keep Markdown projection and supported import/export. |
+| `local_authority_shadow_outbox.py` and its typed reader | New outbox production is removed. Historical prepared and committed entries remain available for bounded disposition and exact receipt replay. | Seed historical bytes directly, classify each entry against its source and receipt, and prove restart recovery without a business write. |
| `local_authority_shadow_projection.py` | Python transports full source artifacts, rejects floats and unsafe integers before Python/JS digest comparison, and checks identity, size, digest and regular-file readback. `coordination.source.project` remains the typed projection owner. | Preserve exact-byte and malformed-input behavior in the replacement, including symlink rejection, bounded transfer and ambiguous-operation errors. A language-only rewrite is insufficient. |
| `runtime_shadow.py`, `local_authority_shadow_adapter.py`; retained `configuration.py`, `authority_source_capture.py`, `local_authority_shadow_history.py`, `local_authority_shadow_readback.py` | Configuration, cold-source capture and read-only diagnostics now have concrete owners independent of producers. Explicit shadow bootstrap/drain/promotion and recovery still have live callers. | Retire obsolete producer aliases only after their supported callers exit. Preserve the retained import/history/readback owners and outbox recovery; inspect or cold import succeeding without producers does not make a live drain unused. |
| `authority_core.py` | Live Todo adapters delegate to typed mutation/ownership rules; scope overlap delegates to the typed lease owner. The lease-mode input compatibility contract is explicitly retained. | Trace direct, internal, dynamic and exported callers separately. Review registered historical input support before retiring its vocabulary; do not infer a fresh grant from old `legacy` inputs. |
@@ -288,60 +290,47 @@ process cleanup, private validation declarations and supported backup/original
receipt readers where they still serve real callers. Their I/O or recovery
value is distinct from a duplicated decision owner.
-For each proposed deletion, record the supported caller, replacement owner,
-persisted obligations and rollback. Search references and exports, then exercise
-the real CLI and installed package in a disposable runtime with the retired
-path absent. Include interrupted writes, restart/replay, malformed evidence and
-unavailable selected-provider cases. The provider must fail visibly without
-falling back to a Markdown writer. Keep historical recovery coverage; reverse
-cutover must retain writes made after the original migration.
+The cutoff qualification records the supported caller, replacement owner,
+persisted obligations and rollback. It exercises the real CLI and installed
+package with the retired writer path absent. The provider fails visibly without
+falling back to a Markdown writer. Historical recovery tests seed old evidence
+directly instead of calling a retired producer.
Useful existing regressions include `test_source_projection.py`,
-`test_source_transfer.py`, `test_local_authority_shadow_outbox.py`,
-`test_runtime_shadow_bounded_e2e.py`, `test_shadow_writer_boundaries.py` and
-`test_shadow_cursor_recovery_e2e.py` under `tests/control_plane/`. Transport
-fault tests use injected faults; source/recovery tests also run real CLI/native
-writers against disposable File stores. These are bounded regression evidence,
-not sustained SQLite, PostgreSQL, packaged App or release-default qualification.
-
-### Proposed compatibility cutoff and release sequence
-
-**Proposal, not installed behavior or a release announcement.** The current
-implementation still accepts target-only v0 settings, explicit
-`canonical_creation=false`, and normal writes to unpromoted Markdown Goals.
-Retiring that supported write path is an incompatible change; target a **2.0**
-release rather than silently removing it in a compatible 1.x update. This
-follows [Semantic Versioning's compatibility distinction](https://semver.org/).
-The implementation and release decision remain subject to the existing
+`test_source_transfer.py`, `test_shadow_writer_boundaries.py`,
+`test_cold_source_inspection.py`, `test_shadow_registry_source.py` and
+`test_shadow_management_variant_e2e.py` under `tests/control_plane/`. These
+tests cover cold source admission, historical management evidence and
+producer-free recovery. Provider conformance and installed-package tests cover
+the canonical write path separately.
+
+### 2.0 compatibility cutoff and release sequence
+
+The implementation still decodes target-only v0 settings and explicit
+`canonical_creation=false` for inspection and reviewed configuration upgrade.
+It rejects normal writes to unpromoted Markdown Goals. This is an incompatible
+change for the 2.0 release, not a compatible 1.x update. The release decision
+remains subject to the existing
[shared-authority RFC](../architecture/rfcs/shared-goal-authority-state-provider-v0.md)
and [retirement cadence](../architecture/rfcs/ledger/shared-goal-authority-state-provider-v0/2026-09-28-retirement-cadence.md).
-The proposed cutoff is **canonical authority before normal mutation**, not
-SQLite for every Goal and not removal of every Python module:
+The cutoff is **canonical authority before normal mutation**, not SQLite for
+every Goal and not removal of every Python module:
-| Existing state | Proposed major-release behavior |
+| Existing state | 2.0 behavior |
| --- | --- |
| Unpromoted Markdown authority | Retain inspection and explicit upgrade/recovery; reject normal mutation before any write or new execution grant, with an actionable supported migration route. Do not silently initialize an empty canonical store. |
| Canonical File or SQLite | Keep the recorded provider and either supported `soft_claim` or `hard_lease` policy. New unconfigured Goals default to SQLite; existing File Goals do not have to switch. Missing providers remain errors. |
-| Canonical store with `legacy` ownership policy | Review a claim-preserving policy migration to `soft_claim` or `hard_lease` before normal execution. Changing policy does not require promoting the store again or switching its provider. |
+| Canonical store with `legacy` ownership policy | Normal ownership mutations and execution proofs return `handoff_mode_retired`; old receipt recovery and exact release remain available. Review a claim-preserving policy migration to `soft_claim` or `hard_lease`. This does not require another storage promotion or provider switch. |
| Target-only v0 or v1 creation disabled | Keep decoding for inspection and reviewed configuration upgrade. Require an explicit compatible creation choice before creating a writable Goal; do not erase the preference or reinterpret disabled as enabled. |
| Historical ownership inputs, source captures and backups | Recognize them in declared migration/recovery contracts. Historical leases, receipts and a restored source do not grant new execution authority. Preserve supported original-operation recovery and permanent Markdown display. |
-There is a concrete prerequisite for the unpromoted cohort: today's reviewed
-promotion requires an enabled, bootstrapped shadow qualified by **real source
-mutations**, with the saved operation/event policy. An empty shadow cannot pass.
-If the major release first blocks the writer that produces those mutations, a
-cold Goal cannot manufacture its own migration qualification. The selected
-upgrade path is **reviewed import of the existing source**, implemented by the
-existing typed transaction and CLI/App storage settings described below,
-without requiring a compatible 1.x writer to create new captures.
-Do not lower operation counts, fabricate capture history or bypass source and
-receipt verification to hide this dependency. Implementation and bounded
-source/normal-wheel recovery evidence do not close installed adoption, complete
-Goal backup or every supported historical source. Keep those acceptance rows
-open; canonical create and isolated archive restore cannot substitute for them.
-Keep the compatible installer available for recovery until
-the declared historical support matrix has been qualified.
+Pre-cutoff promotion depended on an enabled shadow qualified by source
+mutations. A cold Goal can no longer manufacture that capture history. The 2.0
+upgrade path uses a separately qualified **reviewed import of the existing
+source**. It does not lower operation counts, fabricate capture history or ask
+a retired 1.x writer to create new evidence. Canonical create and isolated
+archive restore remain separate workflows.
The import package must close one installed user journey through the existing
typed coordination owner and Goal storage settings, shared by CLI and App:
@@ -469,16 +458,13 @@ selected command dispatcher and
the existing Goal path resolver; its File/SQLite import and original-receipt
recovery run with `todos.py`, `bootstrap.py`, `runtime_shadow_writer_adapter.py`
and `local_authority_shadow_outbox.py` physically absent in a disposable package.
-This proves that command's independence, not that other commands or supported
-writers can lose those files. The retained prose-write guard now belongs to the
-existing source/fence boundary and shares the source partition projector with
-capture. Its old import remains compatible. Configuration and the guard load
-with both capture modules absent. Pure runtime-root routing now belongs to
+The retired writer and producer modules are absent from normal and installed
+qualification. Historical outbox inspection and disposition use independent
+readers. Pure runtime-root routing belongs to
`paths.effective_runtime_root`; canonical Todo, terminal lifecycle, acceptance,
-Chat and CLI callers import that owner directly. The old adapter reexports the
-same function for supported capture callers. Source and fresh-wheel File/SQLite
-qualification runs the complete `serve_chat`
-entrypoint with all four modules physically absent. The packaged Goal settings
+Chat and CLI callers import that owner directly. Source and fresh-wheel
+File/SQLite qualification runs the complete `serve_chat` entrypoint with the
+retired modules physically absent. The packaged Goal settings
journey performs backup/preview, reload and original-operation readback before
explicit confirmation. Full current and archived requirements survive import;
subsequent reviewed App creation, editing and completion work with the explicitly
@@ -493,10 +479,8 @@ retains subsequent writes. Unreviewed v0/v1 callers keep their existing intent
and receipt identity. Import and original-receipt recovery refresh current
ownership independently on the same settings screen, including retryable read
failures; a saved receipt does not substitute for today's policy.
-Supported old APIs reexport the same functions; their real Markdown writers and bootstrap
-operations remain for actual callers. This qualifies the bounded import/loading
-journey, not retirement of every writer, full-state identity adoption, D2 or a
-released SQLite default.
+Read-only compatibility APIs retain their declared historical inputs. They do
+not reexport a Markdown business writer or grant fresh execution authority.
已审核的 canonical Todo 创建使用请求 v2,将预览的 provider revision 绑定到既有
命令身份和事务 CAS;并发修改会拒绝过期请求。原操作恢复先于当前 head 校验,保留
@@ -513,95 +497,29 @@ used by the source witness, including registries outside conventional Goal folde
App 备份使用与 source witness 一致的 registry 实际路径,也覆盖位于常规 Goal
目录之外的 registry。
-The last-writer inventory at `38cb407ed` names the next implementation frontier:
+The 2.0 cutoff was qualified in this order:
-| Caller family | Existing owner and cutoff proof |
-| --- | --- |
-| Ordinary Todo add/update/claim/complete/supersede/archive | `todos/mutation_api.py`, `provider_create.py`, `provider_update.py`, `provider_terminal_lifecycle.py`. Five legacy mutation dispatches remain; claim uses the update route. Require canonical authority before a fresh mutation, then remove `legacy_mutation.py` and writer-only line editing after tracing public imports. Preserve validators, display publication and original receipt recovery. |
-| Bootstrap/reconnect and explicit creation opt-out | `bootstrap.py` and the typed creation/default owner. Preserve existing canonical selection and interrupted initialization; an unmigrated source or disabled creation setting receives the reviewed import/configuration route before source replacement. Do not turn an old Goal into an empty new store. |
-| Confirmed team-plan commit | `work_items/team_plan_adapter.py` and `work_items.team_plan.commit`. Remove the unpromoted batch writer after canonical-required admission; recover historical confirmation receipts without applying the plan again or losing later work. |
-| Monitor observation and succession | `scheduler/monitor_poll_writeback.py`, `provider_monitor_poll.py`, `monitor_batch.ts`. Remove fresh `legacy_monitor_poll.py` writes, retaining `monitor_batch_receipts.py` recovery and exact generation/successor/once-only identity. A promoted historical replay must leave old outbox bytes inert. |
-| Ownership-policy/source lease entrypoints | `todos/handoff_mode.py`, `provider_handoff_mode.py` and the existing lease owners. Keep source inspection and settled lease history for import; refuse fresh unmigrated execution grants. An expired but unreleased lease still blocks import. This is separate from File/SQLite provider selection. |
-| Capture and active binding | `runtime_shadow_writer_adapter.py` and the existing typed source/outbox owners. Removing a configuration switch does not cancel an active binding. Classify prepared, committed and ambiguous entries against source bytes and original receipts before removing the last producer; retain recovery readers for the declared support window. |
-
-These are live call sites, not modules approved for immediate deletion. The
-implementation PR must join their shared typed admission boundary and exercise
-both fresh refusal **before any effect** and original-operation recovery. Do not
-deliver one PR per fallback. Use one cohesive normal-writer cutoff package,
-then a separate producer cleanup when its last callers and outboxes are settled.
-
-**Admission ordering for the 2.0 implementation.** The existing promotion fence
-is not itself the release cutoff. Today, a missing fence permits legacy writes;
-the Python adapter also returns early when that file is absent. Changing only
-the TypeScript missing-fence decision would leave that Python path open.
-Conversely, denying every legacy operation would block the original lease
-release required by cold import: an expired but unreleased lease is still an
-import refusal. The implementation must distinguish the following operations
-through the existing typed transaction owners, before deleting their callers:
-
-| Operation on an unmigrated source | Required cutoff behavior and retained owner |
-| --- | --- |
-| Fresh Todo, team-plan or Monitor mutation; new execution grant, renewal or ownership transfer | Refuse before source replacement, capture preparation or a new execution receipt. Route to reviewed cold import; do not enable creation settings, invent a store or fall back after a selected provider failure. Python transports the typed decision and cannot bypass it with a missing-file check. |
-| Original lease release or close of its existing execution fence, including an expired active lease | Preserve the original owner/key/version, generation and lock-token checks in `work_items/task_lease_lifecycle.ts`; allow settlement/abort without a new holder verification or execution grant. A wrong owner, stale proof or corrupt record must still refuse. Never delete a lease to make import pass. |
-| Original business receipt replay | Validate the original identity and return the recorded result before fresh-work admission. `work_items/team_plan_adapter.py` currently reads an unpromoted receipt inside its legacy write transaction; move that recovery route with the cutoff. Retain the Monitor receipt owner and keep promoted historical replay from draining old source state. A changed intent is a new operation, not recovery. |
-| Prepared or committed shadow work | Use the existing bounded drain and source/receipt reconciliation. Business replay success does not promise that its inline drain finished. Explicit catch-up must retain its budget, cursor and original identity; ambiguous source replacement remains a refusal. Do not recreate a business write to obtain a new capture. |
-| Explicit import, supported backup/format recovery and Markdown display | Keep their specific owners and source checks. `state_migration.py` and `projects/registry.py` also call the current fence/replacement helpers, so a blanket replacement of those helpers is not a complete cutoff. Recovery must not reopen normal writers or consume historical receipts as fresh authority. |
-
-The public entrypoints must preserve this distinction. CLI and packaged App
-should offer the existing reviewed import path for an unmigrated Goal; a missing
-selected provider instead requires restoration of that provider. Supported Lark
-entrypoints must return the same typed refusal and recovery boundary. A preview
-must not advertise a fresh legacy write that confirmation will reject. These
-are implementation requirements, not a shipped 2.0 switch or a new setting.
-
-The decisive combined acceptance starts with a cold source carrying an expired
-unreleased lease and an interrupted original operation: reject fresh work,
-release only the original lease, reconcile the original receipt/outbox, import,
-perform a canonical write, then restart and retry the old operation without
-losing the new write. Repeat for File and SQLite, wrong/stale release proofs,
-changed replay intent, malformed history and unavailable selected providers.
-Run it through the normal wheel and affected packaged entrypoints with the
-retired normal writers physically absent. Existing cold-import lease refusal,
-lease lifecycle retry, Monitor catch-up and team-plan replay tests characterize
-the components; they do not establish this future cutoff journey by themselves.
-
-Deliver complete, reversible PR packages in this order:
-
-1. **Direct source import and support boundary.** Qualify the existing
- reviewed cold-source import on the installed CLI and packaged App, alongside
+1. **Direct source import and support boundary.** Qualify the reviewed
+ cold-source import on the installed CLI and packaged App, alongside
existing new-Goal creation/retry and provider migration. Cover both
qualified and cold unpromoted sources, target-only settings, stopped writers,
active/expired leases, pending captures, missing providers and original-plan
- recovery. Publish the selected major-release support contract before removing
- its writer. An additive 1.x release may ship the importer and deprecation
- guidance first; using the old writer is not a prerequisite for import.
- New SQLite creation is already implemented.
-2. **Core-developer SQLite trial.** Between import qualification and the breaking
- cutoff, use the existing [reversible trial procedure](sqlite-authority-store.md#reversible-developer-trial--可逆开发者试用).
- Record actual installed source, CLI/App/Host adoption, ordinary use and recovery
- outcomes for the declared small cohort. Use disposable copies for conversion
- drills when an existing Goal is already canonical. Rollback/export starts at
- the current head and retains new writes. A local conversion exercise is not
- natural cohort observation; keep formal D2 results separately visible.
-3. **Normal writer family cutoff.** In the major-release implementation, enforce
- that boundary through the existing typed admission owner and remove the
+ recovery. The importer does not call the retired writer.
+2. **Normal writer family cutoff.** Enforce the boundary through the existing
+ typed admission owner and remove the
corresponding Markdown create/update/claim/complete/supersede/archive paths
together with their private dispatch and writer-only tests. Trace CLI,
internal, dynamic and supported import callers; keep shared rendering,
validation effects and migration/recovery readers. Run the real wheel and
packaged user journey with the retired paths physically absent, including
- refusal and recovery for an unmigrated Goal. Do not land the breaking package
- into an otherwise compatible 1.x release line.
-4. **Capture producer and duplicate decision retirement.** After the writer
- family exits, remove only producers and bridges without supported callers.
+ refusal and recovery for an unmigrated Goal.
+3. **Capture producer and duplicate decision retirement.** Remove producers and
+ bridges without supported callers.
Reconcile each prepared/committed outbox entry against source bytes and its
original receipt first. Retain the codecs/readers needed by supported old
backups and interrupted migrations. Prove restart/replay and forward recovery
without recreating a business write or losing a later canonical write.
-Publish 2.0 beta, then rc and final only as these user journeys qualify for the
-declared support matrix. Do not wait for every external Goal to have migrated;
-each affected user must have a tested route before its next normal write.
Software installation, authority-format upgrade, provider migration and
ownership-policy migration remain distinct operations. Binary updates do not
automatically switch authority. Reverse File/SQLite cutover starts from the
@@ -613,12 +531,76 @@ D2 capacity/soak qualification; missing or failed D2 evidence stays visible.
A microbenchmark failure alone does not block an unrelated, proven internal
deletion, and passing those deletions does not certify the release default.
-### 切换发布默认前的退役边界
+### Retirement proposal reconciliation
-canonical 写入绕过旧 writer,不代表未迁移 Goal 的调用方已消失。默认值修改也不
-迁移已有 Goal 或结算 capture 历史。按调用族分别验收,不把整项迁移设成所有小批
-退役的前置依赖;仍有调用的 writer/producer 在对应迁移后删,已无调用的内部桥接
-可先删,受支持的备份、格式、原回执恢复与 Markdown 展示继续保留。
+The contributor-led [#6396](https://github.com/loopx-project/loopx/pull/6396)
+and [supplement #2](https://github.com/Duang777/loopx/pull/2) carry the useful
+contracts from [#6366](https://github.com/loopx-project/loopx/pull/6366).
+Reconciliation follows supported behavior, rather than preserving every old
+module, response alias or test filename:
+
+| Contract | Current owner and regression evidence |
+| --- | --- |
+| Normal writer cutoff and deletion | Canonical mutation/creation/Monitor adapters; cold-source import and writer-isolation tests. Markdown mutation producers stay retired. |
+| Imported completion, succession and policy repair | Typed terminal/update owners; shared provider conformance and actual CLI consumer smokes in the supplement. |
+| Claim/lease ownership and atomic completion | `test_canonical_handoff_invariants.py`, canonical mutation/claim-transfer tests and provider conformance. Metadata cannot implement the retired two-write handoff. |
+| Original-operation recovery | Fixed 1.x lease, Monitor and team-plan artifacts guard exact replay, prepared matching effects, original release, lost close response and preservation of later state. Selected-provider failure remains a refusal; original receipts are checked before fresh-work admission. Fresh legacy verification/grants remain retired. |
+| Current ownership policy | Only `soft_claim` and `hard_lease` admit current work. Imported `legacy` records and original receipts cannot grant execution; reviewed backup/plan/migrate and exact release remain available. |
+| Retired lease helpers | Delete the unused Python lease writer/factory, generation/CAS helpers, owner-validation chain, and source Todo projection. The typed transaction remains the decision owner; retained JSON reading stays read-only. Release corruption/CAS assertions run through the actual native adapter with fixed 1.x records. |
+| Reviewed App creation and configuration | Chat binds the accepted operation to the reviewed provider revision and Todo identity; stale preview rejects, corrected preview can retry. Settings preserve a disabled preference and explain the required upgrade. |
+| Mutation-oracle strength and packaging | The fence oracle observes actual exclusive-open contention, including delayed startup. Existing prerelease bundle and installed-package checks retain their independent scope. |
+
+The ordinary lease CLI routes `renew`, `transfer` and `release` by the recorded
+provider. Unpromoted retries reach the retained typed lifecycle owner, which
+recognizes the original prepared/committed receipt before refusing fresh work.
+A successful historical renewal or transfer replay is not a current execution
+proof. Changed intent, ambiguous prepared effects and unavailable selected
+providers still refuse; the public adapter does not implement receipt decisions.
+
+Recovery and new execution retain separate admission paths. An unmigrated
+Monitor may finish its original quota transaction only from its matching retained
+business receipt; missing or changed evidence cannot rerun the observation,
+create a new reservation, or revive an old producer. Selected-provider failure
+never authorizes a fallback to historical Markdown. These cases run through the
+ordinary CLI as well as the retained receipt reader.
+
+For completed canonical creation and team plans, a missing or unreadable
+Markdown display is not a new-operation precondition. Exact creation/plan
+receipts remain recoverable after later Todos have been committed. Project
+registration keeps its original registry intent and frozen target; incomplete
+creation still validates its original source. Missing display recovery uses the
+current canonical projection and explicitly restores Todo sections only, never
+an empty registration template. An unreadable existing display remains intact:
+registration returns only the original creation readback, while a team-plan
+projection attempt reports its pending delivery. Creation readback does not
+certify display health.
+
+User actions retain their administrative operation through the bound Agent;
+they cannot acquire, renew or transfer an execution lease. Current proof and
+inspection use the same typed eligibility owner. An old invalid lease can still
+be released with its exact identity; the bound Agent can then resume the normal
+administrative edit or completion without creating another execution lease.
+
+The absorption does not qualify a release, contributor adoption, sustained D2
+operation or an unobserved Host. Full current-head CI and the combined adopted
+candidate retain their own acceptance. Historical output aliases and tests
+whose only subject was a deleted producer are superseded by the typed current
+contract and its supported recovery tests.
+
+Retirement removes internal Python helper exports with no shipped caller or
+registered import contract; it does not remove CLI/API lease operations, the
+historical wire vocabulary or receipt decoding. Test fixtures describe source
+records directly instead of keeping a production writer solely to construct
+fixtures. Owner eligibility remains covered at its typed owner and by real
+canonical claim/lease transactions. Historical generation checks belong to
+native recovery/release, including corrupt boolean generations and unchanged
+bytes after rejection.
+
+### 2.0 退役边界
+
+正常写入现在只接受 canonical authority;旧 writer、producer 和无调用内部桥接
+已删除。默认值修改仍不迁移已有 Goal 或自动处置历史 outbox。受支持的备份、格式、
+原回执恢复与 Markdown 展示继续保留。
上表中的精确数字与摘要校验、安全文件传输、prepare→主写入→commit 顺序、游标
恢复和 Host I/O 都有实际价值。替代实现须保留它们;Python 行数减少不能代替语义
@@ -628,65 +610,37 @@ canonical 写入绕过旧 writer,不代表未迁移 Goal 的调用方已消失
新写入。现有回归覆盖不等于长期 SQLite、真实 PostgreSQL、打包 App 或发布默认
已通过,trial 与正式默认的资格继续分别记录。
-**发布路径提案:**当前仍支持未晋升 Markdown Goal 的正常写入、v0 target-only
-和显式关闭 canonical creation;删除这条支持路径属于不兼容变更,建议在 **2.0**
-统一截止,不混入兼容 1.x 更新。要求是“正常写入前必须有 canonical authority”,
-不是所有 Goal 强制 SQLite。已有 canonical File/SQLite 和明确的两种 ownership
-policy 保持;已有 canonical store 若仍用 `legacy` policy,只需审核保留 claim 的
-policy 迁移,不重新晋升或强制换 provider。关闭/旧配置先明确审核升级,不删偏好
-或暗中启用。旧备份、格式、原回执和永久 Markdown 投影继续按其恢复契约支持。
-
-一个必须解决的死结是:当前晋升需要真实源写入形成合格 capture;冷启动、未资格化
-的旧 Goal 若先被禁止写入,就无法再产生迁移资格。选定主路径是独立验收的
-**旧源审核导入**,无需先在兼容 1.x writer 上新增 capture;不能降低计数、伪造
-capture 或把空库创建/isolated restore 当作此验收。先完整盘点和备份源,停止
-writer/Host、结算 lease 并逐项对账 outbox,再预览、明确确认、fence 旧 writer、
-导入并读回。未支持或歧义内容明确拒绝,旧 lease/回执不转为新授权;导入新回执
-与历史原回执分别保存。中断、失响应、重启须恢复同一操作,不重复副作用、不丢
-后续 canonical 新写入。此旅程与既有 canonical File/SQLite 切换及新身份 archive
-restore 分别验收。typed transaction、CLI 和 App 存储设置已实现,已有有界源码/
-正常 wheel 恢复证据;安装采用、完整 Goal 备份和全部历史源支持仍须各自验收,
-不能沿用“尚未实现”作为等待理由。历史支持矩阵验收前保留兼容安装包用于恢复。
-
-当前真实调用族包括五处 mutation dispatch(claim 走 update)、bootstrap/
-creation opt-out、team-plan batch、Monitor、ownership/source lease 和 active
-binding/capture。上述表格逐族指定现有 owner、正常写入截止及历史恢复义务;
-消费者离开 `todos.py` 或 canonical 测试通过,不代表这些 writer 已无调用。
-
-**2.0 准入顺序:**现有 promotion fence 不能直接充当发布截止开关。TS 在缺少
-fence 时允许旧写入,Python 还有文件不存在即返回的快速路径;只改 TS 会漏掉
-后者。反过来全面拒绝 legacy 操作,会同时阻止导入所需的原租约释放。即使租约
-已过期,只要仍是 active,冷导入仍须拒绝,不能删记录或伪造结算来绕过。
-
-截止包须在既有 typed transaction owner 区分新工作与原操作恢复:新 Todo、
-team-plan、Monitor 写入和新授权、续租、转移,在源写入、capture prepare 或
-新执行回执前拒绝;原租约释放及原执行 fence 关闭/中止继续核验原
-owner/key/version/generation 和 lock token,不重新 holder verify 或授予执行权。原业务回执先校验身份再读回结果,不能重新执行;team-plan 当前把未
-晋升回执读取放在 legacy 写事务内部,截止时须一起调整,Monitor 晋升后的旧回执
-重放仍不得 drain 旧源。改 intent、错 owner、过期版本或损坏历史继续明确拒绝。
-
-pending shadow 继续按原源字节和回执独立、有界地对账。业务重试成功不保证内联
-drain 已追平,也不能重做业务写入来生成新 capture。`state_migration.py` 和
-`projects/registry.py` 同样调用当前 fence/replacement helper;不能整体替换
-helper 后就宣告所有入口截止。显式导入、受支持格式/备份恢复和 Markdown 展示
-保留专属校验,同时不得重新打开正常 writer。CLI、packaged App 和受支持 Lark
-入口须区分“未迁移,走审核导入”和“已选 provider 缺失,先恢复 provider”,预览
-也不能许诺确认时会拒绝的旧写入;不新增开关或暗中开启已有创建设置。
-
-组合准出从“冷旧源+过期未释放租约+中断原操作”开始:拒绝新工作 → 仅原证明
-释放租约 → 原回执/outbox 对账 → 导入 → 新 canonical 写入 → 重启并重放旧操作,
-新写入不丢失。覆盖 File/SQLite、错误/过期释放证明、变更 intent、坏历史及
-provider 缺失;在正常 wheel/受影响 packaged 入口及旧正常 writer 物理缺席下
-验证。现有各组件测试只是这条未来截止旅程的依据,不是整条旅程已通过的证明。
-
-按上面的四个完整阶段推进:安装态 CLI/App 直接导入与支持边界 → 核心开发者
-SQLite 可逆试用 → 同边界
-create/update/claim/complete/supersede/archive 旧 writer 及私有 dispatch 删除 →
-逐项对账 outbox 后删除无调用的 capture producer/重复决策。大版本实现须有原生
-拒绝与恢复、旧路径物理缺席的真实入口验证;不要求所有外部用户已迁移。2.0
-beta→rc→final 依据明确支持矩阵的旅程验收,不将原失败/缺失 D2 改成通过,也不把
-独立微基准当所有内部退役的前置。二进制更新不自动切 authority,反向切换须保留
-当前 head 的新增写入。版本和截止仍是提案,尚未改变安装行为或宣布发布。
+普通 lease CLI 的 renew/transfer/release 按原 provider 路由;未迁移的原操作重试
+先由既有 typed lifecycle owner 核验 prepared/committed 回执,再拒绝真正的新工作。
+历史 renew/transfer 的成功回读不提供当前执行证明;改意图、prepared 效果歧义或
+已选 provider 不可用仍拒绝,Python adapter 不新增回执判断规则。
+
+恢复与新执行分开准入:先验证已选 provider 可用性,再核验原操作的 typed 回执,
+最后才判断新写入。未迁移 Monitor 只能凭原业务回执完成原 quota 事务;缺失、损坏
+或身份改变不能重新观察、创建新 reservation 或恢复旧 producer。canonical 创建和
+team plan 的原回执不依赖 Markdown 展示仍然存在;重复注册保留原 registry 意图与
+已提交的新 Todo,缺展示时只从当前 canonical head 恢复 Todo 区域,不重写空模板。
+已有坏字节保留;注册只读回原创建回执,team-plan 的投影失败如实返回 pending,
+不把创建回执当成展示健康证明。User action 仍由绑定 Agent 执行行政编辑或完成,
+不能申请、续期或转移执行租约;历史错误租约可以精确释放,再继续原行政操作。
+
+**2.0 支持矩阵:**未迁移 Markdown Goal 的正常写入返回
+`canonical_authority_required`;v0 target-only 和显式关闭 canonical creation
+只用于检查与审核升级。要求是“正常写入前必须有 canonical authority”,不是所有
+Goal 强制 SQLite。已有 canonical File/SQLite 和两种 ownership policy 保持。
+旧备份、格式、原回执和永久 Markdown 投影继续按其恢复契约支持。
+
+旧 Goal 通过独立验收的**旧源审核导入**迁移,无需调用退役 writer 新增 capture。
+流程先完整盘点和备份源,停止 Host、结算 lease 并逐项对账 outbox,再预览、明确
+确认、fence 旧 writer、导入并读回。未支持或歧义内容明确拒绝,旧 lease/回执不
+转为新授权;导入新回执与历史原回执分别保存。中断、失响应和重启恢复同一操作,
+不重复副作用,也不丢后续 canonical 新写入。
+
+实现按三个包完成:安装态 CLI/App 直接导入与支持边界;删除
+create/update/claim/complete/supersede/archive 旧 writer 及私有 dispatch;逐项
+对账 outbox 后删除无调用 capture producer 和重复决策。验收覆盖原生拒绝与恢复,
+并在旧路径物理缺席时运行真实入口。二进制更新不自动切 authority,反向切换须
+保留当前 head 的新增写入。
## Existing canonical Goal storage in the App
diff --git a/docs/reference/monitor-configuration.md b/docs/reference/monitor-configuration.md
index 748691b9f7..aa33459e70 100644
--- a/docs/reference/monitor-configuration.md
+++ b/docs/reference/monitor-configuration.md
@@ -2,8 +2,9 @@
`todo update` changes an existing Monitor's configuration. After whole-Goal
promotion it uses the same TS planning/CAS/receipt owner as other supported
-Todo updates; it does not read Markdown as authority. Before promotion it
-uses the legacy writer and the same typed configuration policy.
+Todo updates; it does not read Markdown as authority. In 2.0 an unmigrated
+Goal returns `canonical_authority_required`; back up and migrate it before
+retrying the configuration write.
```sh
loopx todo update --goal-id demo --todo-id todo_watch --agent-id agent-a \
@@ -82,10 +83,29 @@ timestamp parsing and provider IO remain live compatibility adapters.
The internal planning request uses v3 for these clock facts while retaining v2
route/handoff/closure behavior; existing v0/v1/v2 requests keep their input contracts.
+### Original observation recovery
+
+An unmigrated Goal may read its exact historical Monitor business receipt and
+finish an already admitted quota transaction. Missing business evidence never
+authorizes another observation or a new quota reservation. Fresh targeted
+mutation previews and execution still require canonical authority. A selected
+but unavailable provider must be restored; historical Markdown is no fallback.
+
+Canonical observation recovery can use internal request
+`loopx_coordination_monitor_poll_request_v4` when the display cannot be read.
+This schema is **receipt-only**: the existing typed Monitor owner validates the
+original identity and receipt, then returns it; an absent or malformed receipt
+cannot enter planning or commit. It grants no execution authority or lease.
+Older runtimes reject the new schema instead of ignoring a restrictive flag.
+Ordinary v0–v3 requests and the business receipt hash remain unchanged. Display
+delivery may still report pending when its source needs repair; recovery does
+not overwrite unreadable prose or drain historical capture work.
+
## 中文
-Monitor 配置修改复用 `todo update`。晋升后由 TS 在同一个 canonical revision 上
-校验并提交状态、事件和回执;晋升前保留 legacy writer,复用相同 typed 配置规则。
+Monitor 配置修改复用 `todo update`。TS 在同一个 canonical revision 上校验并提交
+状态、事件和回执;2.0 中未迁移 Goal 返回 `canonical_authority_required`,需先备份
+并迁移后再重试。
上面的 CLI 给出了预览、带 operation ID 的执行和回读;operation ID 只适用于已
晋升 Goal,修正或撤销使用新 ID,重试同一请求使用原 ID。
@@ -117,3 +137,11 @@ writeback 与 capability 准入保留。缺口数量先于展示截断;其 pri
内部 planning request 用 v3 承载时钟事实,保留 v2 的 route/handoff/closure 规则;
已有 v0/v1/v2 请求保持原输入合同。
+
+未迁移 Goal 可读取精确的旧 Monitor 业务回执并完成已准入的 quota 原事务;缺失
+业务证据时不能重做观察或新建 quota reservation。新的定向写入预览与执行仍须
+canonical authority;已选 provider 不可用时须恢复 provider,不能退回 Markdown。
+内部 observation 请求 `loopx_coordination_monitor_poll_request_v4` 仅用于原回执
+恢复,复用同一 typed owner 的身份与回执校验;没有合法原回执就不能规划或提交,
+也不授予执行权限或 lease。旧 runtime 拒绝该 schema;原 v0–v3 请求和业务回执
+hash 不变。损坏展示仍可报告 pending,恢复不会覆盖不可读正文或 drain 旧 capture。
diff --git a/docs/reference/protocols/active-state-structured-projection-v0.md b/docs/reference/protocols/active-state-structured-projection-v0.md
index 836df035f6..c4ea0d66a6 100644
--- a/docs/reference/protocols/active-state-structured-projection-v0.md
+++ b/docs/reference/protocols/active-state-structured-projection-v0.md
@@ -133,10 +133,12 @@ This is the permanent Python presentation/legacy-input adapter described by the
and [shared-authority RFC](../../architecture/rfcs/shared-goal-authority-state-provider-v0.md#next-delivery-and-parallel-provider-work).
It adds no business rule, RPC, provider, or authority write. Post-cutover Todo
consumers still read canonical state when Markdown is absent or malformed;
-rendering never imports Objective examples into that state. Before cutover,
-the existing legacy writer remains subject to its normal fence. Objective is
-independent Goal narrative, outside the Todo store and Todo-section recovery.
-Existing malformed documents are not automatically repaired.
+rendering never imports Objective examples into that state. For an unmigrated
+Goal, Markdown remains an inspection and reviewed-import source. Normal
+mutation fails with `canonical_authority_required`; no legacy writer fallback
+remains. Objective is independent Goal narrative, outside the Todo store and
+Todo-section recovery. Existing malformed documents are not automatically
+repaired.
Markdown is not one undifferentiated database row. Agents generate and maintain
both its structured sections and narrative through LoopX. The distinction is
@@ -147,8 +149,10 @@ canonical source can reconstruct it.
The cutover is deliberately section-sized, not document-sized:
-- before promotion, Markdown remains the authority and writes use its existing
- transaction boundary;
+- before reviewed import, Markdown remains the source for inspection and
+ backup. Normal Todo mutation and new lease acquisition are rejected. The
+ exact retained lease cleanup route can settle an existing grant without
+ creating a new one;
- after promotion, the versioned Todo records live in the canonical provider
head and Markdown's Todo section is a compatibility/workbench projection;
- other generated sections remain in Markdown until their canonical ownership
@@ -156,9 +160,9 @@ The cutover is deliberately section-sized, not document-sized:
- a provider outage after promotion fails closed and never makes stale
Markdown authoritative again.
-The first write using this boundary is Todo claim. It exercises a complete
-provider-neutral TypeScript transaction while leaving the default local path
-unchanged. On promoted `hard_lease` authority, the same claim command may
+Todo claim was the first canonical write qualified through this boundary. It
+exercises a complete provider-neutral TypeScript transaction. On canonical
+`hard_lease` authority, the same claim command may
supply a task-lease idempotency key and optional expected version so the claim,
canonical lease, and durable receipt commit in one provider transaction; the
write scopes come from the canonical Todo rather than caller input. After
diff --git a/docs/reference/protocols/auto-research-role-profile-v0.md b/docs/reference/protocols/auto-research-role-profile-v0.md
index 4d17f3d005..38566eb70e 100644
--- a/docs/reference/protocols/auto-research-role-profile-v0.md
+++ b/docs/reference/protocols/auto-research-role-profile-v0.md
@@ -105,6 +105,20 @@ public/private boundary checks before another agent can run it.
target is not met and a role-declared successor condition is satisfied, the
worker must create or link that successor instead of closing the lane.
+For failure continuation, the successor's title and `unblocks_todo_id` share
+the failed evidence Todo from `research_failure_continuation_v0.source_todo_id`.
+Several summaries of that lineage therefore create or reuse one identical
+successor without rebinding its original parent. A historical successor may
+retain its original summary parent when the complete canonical records prove
+that summary belongs to the same failed evidence lineage; all other intent
+fields still pass the native duplicate check. Each completed summary keeps
+its own `successor_todo_ids` link to that successor. Other role handoffs still
+bind the selected source Todo. These links grant no execution or claim rights;
+the canonical owner continues to reject same-title requests with different
+lineage or other intent. Historical-parent reuse binds create to the inspected
+provider revision; a concurrent change refuses without rebinding and requires
+a fresh read before retry.
+
## Resolution Order
Every visible auto-research worker should resolve its instructions in this
diff --git a/docs/reference/protocols/quota-monitor-observation-receipt-v0.md b/docs/reference/protocols/quota-monitor-observation-receipt-v0.md
index c93d5f0443..395f10e124 100644
--- a/docs/reference/protocols/quota-monitor-observation-receipt-v0.md
+++ b/docs/reference/protocols/quota-monitor-observation-receipt-v0.md
@@ -217,8 +217,19 @@ lease proof. Before downgrading to a version without this protocol, finish v1
pending settlements; older binaries cannot interpret them. Never disable a
writer fence to recover by writing an older Markdown projection.
-The bounded real-source rehearsal clones read-only source state and adds only
-synthetic records in disposable runtimes. It requires an isolated PostgreSQL URL:
+The bounded source rehearsal reads a stable Markdown snapshot, including
+unreferenced archives, and adds synthetic records only in disposable runtimes.
+Use a baseline that supports the same authority format and Monitor reactivation;
+all four arms must preserve the full source records and reach identical heads.
+The disposable cases use `soft_claim` for observation and `hard_lease` for
+execution. These fixture transitions do not qualify policy migration, whole-Goal
+recovery, or the contents of a selected canonical store. It requires an isolated
+PostgreSQL URL:
+
+有界演练读取稳定的 Markdown 快照,保留无依赖关系的完整归档;仅在临时运行目录添加
+合成记录。基线须支持相同的权威存储格式及 Monitor 重启语义,四组最终状态必须一致。
+观察使用 `soft_claim`,执行使用 `hard_lease`;样本内切换不证明实际策略迁移、完整
+Goal 恢复或已选 canonical 存储的内容验收。
```bash
LOOPX_TEST_POSTGRES_URL="$DISPOSABLE_POSTGRES_URL" \
diff --git a/examples/auto-research-worker-turn-smoke.py b/examples/auto-research-worker-turn-smoke.py
index b6bf588022..650631c766 100644
--- a/examples/auto-research-worker-turn-smoke.py
+++ b/examples/auto-research-worker-turn-smoke.py
@@ -33,6 +33,11 @@
apply_role_successor_todos,
)
from loopx.todos import add_goal_todo, complete_goal_todo # noqa: E402
+from loopx.control_plane.coordination.local_authority import ( # noqa: E402
+ LocalCoordinationAuthorityUnavailable,
+ read_canonical_todos_if_promoted,
+)
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
from loopx.control_plane.work_items.task_lease import ( # noqa: E402
TaskLeaseError,
acquire_task_lease,
@@ -173,7 +178,9 @@ def write_summary_state(path: Path) -> None:
)
-def write_registry(path: Path, *, project: Path, state_file: Path, runtime_root: Path) -> None:
+def write_registry(
+ path: Path, *, project: Path, state_file: Path, runtime_root: Path, provider: str,
+) -> None:
path.write_text(
json.dumps(
{
@@ -207,6 +214,7 @@ def write_registry(path: Path, *, project: Path, state_file: Path, runtime_root:
+ "\n",
encoding="utf-8",
)
+ import_fixture_authority(path, GOAL_ID, provider=provider)
def append_real_fixture_evidence(*, registry: Path, runtime_root: Path, temp: Path) -> None:
@@ -317,7 +325,7 @@ def append_negative_fixture_evidence(
assert_public_safe(appended)
-def main() -> int:
+def assert_seeded_worker_turns() -> None:
with tempfile.TemporaryDirectory() as temp_dir:
temp = Path(temp_dir)
supervisor = build_auto_research_demo_supervisor_plan(
@@ -533,7 +541,7 @@ def main() -> int:
successors = seeded_failure_turn["successor_todos"]["successors"]
assert len(successors) == 1, seeded_failure_turn
assert successors[0]["action_kind"] == "propose_failure_successor", seeded_failure_turn
- assert successors[0]["unblocks_todo_id"] == evaluator_seed["todo_id"], seeded_failure_turn
+ assert successors[0]["unblocks_todo_id"] == executor_seed["todo_id"], seeded_failure_turn
assert seeded_failure_turn["completion"]["successor_todo_ids"] == [
successors[0]["todo_id"]
], seeded_failure_turn
@@ -562,6 +570,9 @@ def main() -> int:
)
assert manual_lease["lease"] is None, manual_lease
+
+
+def assert_evidence_lineage(provider: str) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
temp = Path(temp_dir)
project = temp / "project"
@@ -570,7 +581,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
summary_todo = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -618,6 +629,12 @@ def main() -> int:
for successor_id in successor_ids:
assert successor_id in state_text, state_text
assert_public_safe(evaluator_summary)
+ canonical = read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID)
+ by_id = {row["todo_id"]: row for row in canonical["todos"]}
+ assert by_id[summary_todo_id]["status"] == "done", canonical
+ assert by_id[summary_todo_id]["claimed_by"] == EVALUATOR_AGENT_ID
+ assert by_id[successor_ids[0]]["claimed_by"] == CURATOR_AGENT_ID
+ assert by_id[successor_ids[0]]["unblocks_todo_id"] == summary_todo_id
with tempfile.TemporaryDirectory() as temp_dir:
temp = Path(temp_dir)
@@ -627,7 +644,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
promotion_todo = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -732,7 +749,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
first_failure_evidence = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -808,7 +825,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
retry_evidence = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -892,7 +909,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
self_referential_summary = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -948,7 +965,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
positive_evidence = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -1069,7 +1086,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
failure_evidence = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -1122,7 +1139,7 @@ def main() -> int:
failure_turn["frontier"]["frontier"]["selected"]["unblocks_todo_id"]
== failure_evidence["todo_id"]
), failure_turn
- assert successors[0]["unblocks_todo_id"] == failure_review["todo_id"], failure_turn
+ assert successors[0]["unblocks_todo_id"] == failure_evidence["todo_id"], failure_turn
assert failure_turn["completion"]["successor_todo_ids"] == [successors[0]["todo_id"]], failure_turn
state_text = state_file.read_text(encoding="utf-8")
assert f"todo_id={failure_review['todo_id']} status=done" in state_text, state_text
@@ -1153,7 +1170,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
data_gap_evidence = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -1213,7 +1230,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
retry_evidence = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -1268,7 +1285,7 @@ def main() -> int:
retry_turn["evaluation_summary"]["failure_continuation"]["source_todo_id"]
== retry_evidence["todo_id"]
), retry_turn
- assert successors[0]["unblocks_todo_id"] == retry_review["todo_id"], retry_turn
+ assert successors[0]["unblocks_todo_id"] == retry_evidence["todo_id"], retry_turn
assert retry_turn["completion"]["successor_todo_ids"] == [successors[0]["todo_id"]], retry_turn
state_text = state_file.read_text(encoding="utf-8")
assert f"todo_id={retry_review['todo_id']} status=done" in state_text, state_text
@@ -1282,7 +1299,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
generic_todo = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -1324,7 +1341,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
append_fixture_evidence(
registry=registry,
runtime_root=runtime_root,
@@ -1366,6 +1383,11 @@ def main() -> int:
assert target_reached["frontier"]["frontier"]["selected"] is None, target_reached
assert_public_safe(target_reached)
+ for historical_parent in (False, True):
+ assert_failure_successor_lineage(provider, historical_parent)
+
+
+def assert_failure_successor_lineage(provider: str, historical_parent: bool) -> None:
with tempfile.TemporaryDirectory() as temp_dir:
temp = Path(temp_dir)
project = temp / "project"
@@ -1374,7 +1396,7 @@ def main() -> int:
registry = temp / "registry.json"
runtime_root = temp / "runtime"
write_summary_state(state_file)
- write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root, provider=provider)
failure_evidence = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -1385,7 +1407,7 @@ def main() -> int:
claimed_by=EXECUTOR_AGENT_ID,
dry_run=False,
)
- add_goal_todo(
+ first_summary = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
role="agent",
@@ -1396,7 +1418,7 @@ def main() -> int:
unblocks_todo_id=failure_evidence["todo_id"],
dry_run=False,
)
- add_goal_todo(
+ second_summary = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
role="agent",
@@ -1417,6 +1439,21 @@ def main() -> int:
workspace = project / "visible-workspace"
workspace.mkdir()
+ if historical_parent:
+ # Seed the published pre-cutoff producer intent through native create;
+ # its parent must survive upgrade even when another summary retries.
+ historical = apply_role_successor_todos(registry_path=registry, goal_id=GOAL_ID,
+ source_todo_id=first_summary['todo_id'], text_source_todo_id=failure_evidence['todo_id'],
+ current_agent_id=EVALUATOR_AGENT_ID, role_id='evaluator_promoter',
+ action='summarize_evidence', execute=True,
+ successor_specs=[spec for spec in auto_research_successor_specs_for_action(
+ role_id='evaluator_promoter', action='summarize_evidence')
+ if spec['action_kind'] == 'propose_failure_successor'],
+ decision_summary={'failure_continuation': {'next_outcome': 'propose_failure_successor'}})
+ assert historical['successors'][0]['added'] is True
+ original = read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID)
+ original_source = next(row for row in original['todos'] if row['todo_id'] == failure_evidence['todo_id'])
+
first_turn = run_worker_turn(
registry=registry,
runtime_root=runtime_root,
@@ -1432,6 +1469,56 @@ def main() -> int:
first_successor_text = first_successors[0]["todo_command"]
assert ".." not in first_successor_text, f"successor text contains ..: {first_successor_text!r}"
assert_public_safe(first_turn)
+ first_snapshot = read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID)
+ successor_id = first_successors[0]['todo_id']
+ accepted = next(row for row in first_snapshot['todos'] if row['todo_id'] == successor_id)
+ assert accepted['unblocks_todo_id'] == (first_summary['todo_id'] if historical_parent else failure_evidence['todo_id'])
+
+ # An explicit attempt to rebind this existing successor remains illegal.
+ # The producer fix must not weaken the canonical duplicate-intent fence.
+ try:
+ add_goal_todo(registry_path=registry, goal_id=GOAL_ID, role='agent',
+ text=accepted['text'], task_class='advancement_task',
+ action_kind='propose_failure_successor', claimed_by=HYPOTHESIS_AGENT_ID,
+ unblocks_todo_id=second_summary['todo_id'], dry_run=False)
+ except LocalCoordinationAuthorityUnavailable as error:
+ assert error.code == 'todo_semantic_duplicate_conflict', error
+ else:
+ raise AssertionError('same-title successor must not acquire another source binding')
+ assert read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID) == first_snapshot
+ if historical_parent:
+ from loopx.control_plane.testing.canary_harness import run_json_cli
+ run_json_cli('todo', 'archive-completed', '--goal-id', GOAL_ID, '--role', 'agent',
+ '--max-active-done', '0', '--execute',
+ registry_path=registry, runtime_root=runtime_root)
+
+ if historical_parent:
+ from unittest.mock import patch
+ from demo.auto_research import worker_runtime
+ observed_after_race = None
+
+ def concurrent_read(**kwargs):
+ nonlocal observed_after_race
+ snapshot = read_canonical_todos_if_promoted(**kwargs)
+ add_goal_todo(registry_path=registry, goal_id=GOAL_ID, role='agent',
+ text='[P2] Independent concurrent planning work.', task_class='advancement_task',
+ claimed_by=CURATOR_AGENT_ID, dry_run=False)
+ observed_after_race = read_canonical_todos_if_promoted(**kwargs)
+ return snapshot
+
+ # Intervene after a real read; both the update and CAS refusal use
+ # the real provider. No mock supplies the claimed postcondition.
+ with patch.object(worker_runtime, 'read_canonical_todos_if_promoted', concurrent_read):
+ try:
+ run_auto_research_worker_turn(registry_path=registry,
+ runtime_root_arg=str(runtime_root), goal_id=GOAL_ID,
+ agent_id=EVALUATOR_AGENT_ID, workspace=workspace, execute=True,
+ complete_selected_todo=True, lane_count=4, visible_lanes_accepted=True)
+ except LocalCoordinationAuthorityUnavailable as error:
+ assert error.code == 'provider_revision_mismatch', error
+ else:
+ raise AssertionError('stale historical-lineage proof must refuse')
+ assert read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID) == observed_after_race
second_turn = run_worker_turn(
registry=registry,
@@ -1451,7 +1538,20 @@ def main() -> int:
f"second successor should have the same todo_id, got: {second_successor['todo_id']}"
)
assert_public_safe(second_turn)
+ final = read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID)
+ rows = {row['todo_id']: row for row in final['todos']}
+ assert len(final['todos']) == len(original['todos']) + 1
+ assert rows[successor_id] == accepted
+ assert rows[failure_evidence['todo_id']] == original_source
+ for summary in (first_summary, second_summary):
+ stored = rows[summary['todo_id']]
+ assert stored['status'] == 'done' and stored['claimed_by'] == EVALUATOR_AGENT_ID
+ assert stored['successor_todo_ids'] == [successor_id]
+def main() -> int:
+ assert_seeded_worker_turns()
+ for provider in ("file", "sqlite"):
+ assert_evidence_lineage(provider)
return 0
diff --git a/examples/blocker-push-runtime-smoke.py b/examples/blocker-push-runtime-smoke.py
index a4372be45a..f65396cd2e 100644
--- a/examples/blocker-push-runtime-smoke.py
+++ b/examples/blocker-push-runtime-smoke.py
@@ -217,6 +217,7 @@ def main() -> int:
# The bootstrap rule is shared from heartbeat.rules after #4201; assert the
# current compact sentence instead of the retired per-shell phrasing.
assert "reuse the value on retries" in compact_prompt, prompt
+ assert "obey projected replan/stop, never wake counts" in compact_prompt, prompt
assert "re-enter guard." in compact_prompt, prompt
assert "2 stalls->replan" not in compact_prompt, prompt
assert "Exact monitor settlement=no refresh/spend" in compact_prompt, prompt
diff --git a/examples/canary/catalog-planner-smoke.py b/examples/canary/catalog-planner-smoke.py
index 4aec465a60..53d0601ff6 100644
--- a/examples/canary/catalog-planner-smoke.py
+++ b/examples/canary/catalog-planner-smoke.py
@@ -5,6 +5,7 @@
import json
import os
+import shlex
import shutil
import subprocess
import sys
@@ -453,13 +454,28 @@ def assert_pr_release_and_refactor_profiles_select() -> None:
status_wide_commands = [
check["command"] for check in status_wide_profiles["status-read-path"]["checks"]
]
- assert "python3 examples/control_plane/run-compaction-readmodel-smoke.py" in status_wide_commands, (
+ assert "python3 examples/control_plane/run-history-readmodel-smoke.py" in status_wide_commands, (
status_wide_payload
)
assert "python3 examples/control_plane/goal-channel-readmodel-smoke.py" in status_wide_commands, (
status_wide_payload
)
+ # A bounded default plan can hide a deleted script later in the profile.
+ status_complete_payload = build_catalog_canary_plan(
+ profiles=["status-read-path"],
+ include_deep_checks=True,
+ max_checks_per_profile=100,
+ )
+ assert [profile["id"] for profile in status_complete_payload["domain_profiles"]] == [
+ "status-read-path"
+ ], status_complete_payload
+ for profile in status_complete_payload["domain_profiles"]:
+ for check in profile["checks"]:
+ command = shlex.split(check["command"])
+ assert command[0] == "python3", check
+ assert (REPO_ROOT / command[1]).is_file(), check
+
status_cache_payload = build_catalog_canary_plan(
changed_files=["loopx/control_plane/runtime/status_projection_cache.py"],
surfaces=["status_projection_cache projection-cache read-path"],
diff --git a/examples/capability-gate-smoke.py b/examples/capability-gate-smoke.py
index 9f63b59fc2..bb7a3346fc 100644
--- a/examples/capability-gate-smoke.py
+++ b/examples/capability-gate-smoke.py
@@ -367,29 +367,38 @@ def main() -> int:
"provide or authorize the missing owner-held capability: credentials "
"for todo_capability_9"
], owner_with_fallback
- unselected = owner_with_fallback["interaction_contract"]["cli_channel"]
- assert unselected["selection_required"] is True, owner_with_fallback
- assert unselected["next_cli_actions"] == [], owner_with_fallback
- assert "{todo_id}" in unselected["selection_command"]["command_args_template"], unselected
- selected_fallback = build_quota_should_run(
+ # A runnable recommendation is not a Turn binding. Keep the owner notice,
+ # but require explicit selection before projecting write/spend commands.
+ unbound = owner_with_fallback["interaction_contract"]
+ assert unbound["agent_channel"]["delivery_allowed"] is False, unbound
+ assert unbound["cli_channel"]["selection_required"] is True, unbound
+ assert unbound["cli_channel"]["next_cli_actions"] == [], unbound
+ assert unbound["cli_channel"]["spend_after_validation"] is False, unbound
+ assert "--todo-id '{todo_id}'" in unbound["cli_channel"][
+ "selection_command"
+ ]["command_args_template"], unbound
+
+ # This pure projection receives an already-bound receipt as input; real
+ # receipt acquisition and canonical recovery are covered by CLI smokes.
+ bound_fallback = build_quota_should_run(
status_payload([credentials_todo, p1_docs], registered_agent=AGENT_ID),
goal_id=GOAL_ID,
agent_id=AGENT_ID,
available_capabilities=["shell", "filesystem_write"],
scheduler_execution_context=SCHEDULER_CONTEXT,
- requested_action_todo_id=p1_docs["todo_id"],
+ receipt_bound_todo_id=p1_docs["todo_id"],
)
- assert selected_fallback["ok"] is True, selected_fallback
- assert selected_fallback["selected_todo"]["todo_id"] == p1_docs["todo_id"], selected_fallback
- assert selected_fallback["action_selection_qualification"]["state"] == "qualified", selected_fallback
- assert selected_fallback["interaction_contract"]["cli_channel"].get("selection_required") is not True, selected_fallback
+ bound = bound_fallback["interaction_contract"]
+ assert bound["agent_channel"]["delivery_allowed"] is True, bound
+ assert bound["cli_channel"]["spend_after_validation"] is True, bound
+ assert bound["user_channel"] == unbound["user_channel"], bound
assert any(
"--task-class user_gate" in action
and "--target-capability credentials" in action
and f"--blocks-agent {AGENT_ID}" in action
and "--unblocks-todo-id todo_capability_9" in action
- for action in selected_fallback["interaction_contract"]["cli_channel"]["next_cli_actions"]
- ), selected_fallback
+ for action in bound["cli_channel"]["next_cli_actions"]
+ ), bound_fallback
mixed_capability_todo = todo(
8,
@@ -401,6 +410,7 @@ def main() -> int:
status_payload([mixed_capability_todo]),
goal_id=GOAL_ID,
available_capabilities=["shell", "filesystem_write"],
+ scheduler_execution_context=SCHEDULER_CONTEXT,
)
mixed_gate = mixed_owner_gate["capability_gate"]
assert mixed_gate["action"] == "ask_owner", mixed_owner_gate
@@ -431,6 +441,7 @@ def main() -> int:
"credentials",
"external_evidence_poll",
],
+ scheduler_execution_context=SCHEDULER_CONTEXT,
)
assert mixed_ready["capability_gate"]["action"] == "run", mixed_ready
assert mixed_ready["interaction_contract"]["user_channel"]["action_required"] is False
diff --git a/examples/collaboration-delivery/demo.py b/examples/collaboration-delivery/demo.py
index 80dfd9ed89..53d0d6b7a9 100644
--- a/examples/collaboration-delivery/demo.py
+++ b/examples/collaboration-delivery/demo.py
@@ -15,6 +15,9 @@
from loopx.control_plane.collaboration.peers import returns
from loopx.capabilities.manager_context.roundtrip import drain
from loopx.chat_store import ChatSessionStore
+from loopx.control_plane.coordination.goal_storage_initialization import (
+ initialize_goal_storage_target,
+)
HERE = Path(__file__).resolve().parent
GOAL = "allocation-demo"
@@ -173,6 +176,7 @@ def prepare(root):
"goals": [
{
"id": GOAL,
+ "creation_operation_id": f"goal-create:{GOAL}",
"domain": "synthetic-allocation",
"status": "active",
"repo": str(project),
@@ -183,11 +187,27 @@ def prepare(root):
"agent_model": "peer_v1",
"registered_agents": list(ACTORS),
"write_scope": ["**"],
+ "storage_target": {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": "file",
+ "handoff_mode": "soft_claim",
+ },
},
}
],
}
- write(root / "registry.json", registry)
+ registry_path = root / "registry.json"
+ write(registry_path, registry)
+ initialized = initialize_goal_storage_target(
+ root / "runtime",
+ registry["goals"][0],
+ registry_path=registry_path,
+ )
+ if not isinstance(initialized, dict) or (
+ initialized.get("authority_initialized") is not True
+ or initialized.get("legacy_fallback_used") is not False
+ ):
+ raise RuntimeError("demo canonical authority initialization failed")
for actor in ACTORS:
workspace = worker(root, actor)
workspace.parent.mkdir(exist_ok=True)
diff --git a/examples/control_plane/authority-monitor-poll-rehearsal.py b/examples/control_plane/authority-monitor-poll-rehearsal.py
index 7f21b47867..9de8a3896d 100644
--- a/examples/control_plane/authority-monitor-poll-rehearsal.py
+++ b/examples/control_plane/authority-monitor-poll-rehearsal.py
@@ -1,8 +1,10 @@
#!/usr/bin/env python3
-"""Compare Monitor polling and observation updates on a read-only Goal snapshot.
+"""Compare current-policy Monitor recovery on a read-only Goal snapshot.
Only disposable copies receive synthetic Monitor/lease records. The report is
bounded and excludes source text, identifiers, paths and connection strings.
+The baseline must support the same Monitor recovery and authority format;
+this is a parity rehearsal, not an assertion that an older bug still exists.
"""
from __future__ import annotations
@@ -43,7 +45,7 @@
const {selectLocalSqliteAuthority}=await moduleAt(input.repo,'coordination/local_authority_provider.ts');
const {executeCanonicalTaskLeaseAcquire}=await moduleAt(input.repo,'coordination/task_lease_acquire.ts');
const {coordinationTodoReadModel}=await moduleAt(input.repo,'coordination/coordination_projection.ts');
-const {canonicalAuthoritySha256: digest}=await moduleAt(input.repo,'coordination/authority_store_codec.ts');
+const {canonicalAuthoritySha256: digest,authorityUnicodeCompare}=await moduleAt(input.repo,'coordination/authority_store_codec.ts');
const {engageLegacyCoordinationWriterFence}=await moduleAt(input.repo,'coordination/legacy_writer_fence.ts');
const goal=input.goal_id, target='todo_monitor_rehearsal';
const initial=structuredClone(input.projection);
@@ -52,9 +54,9 @@
text:'Observe isolated public changes',archive_state:'active',source_section:'Agent Todo',
claimed_by:'agent-a',excluded_agents:[],task_class:'continuous_monitor',target_key:'isolated-watch',
cadence:'1h',watch_only:'true',next_due_at:'2026-09-01T00:00:00Z'});
-initial.todos.sort((a,b)=>a.todo_idb.todo_id?1:0);
+initial.todos.sort((a,b)=>authorityUnicodeCompare(a.todo_id,b.todo_id));
initial.todo_read_model=coordinationTodoReadModel(initial.todos,initial.todo_read_model.schema_version);
-initial.handoff_mode='legacy';
+initial.handoff_mode='soft_claim';
const proof={idempotency_key:'monitor-rehearsal-execution',expected_version:3};
const lease={schema_version:'task_lease_v0',goal_id:goal,todo_id:target,owner:'agent-a',
idempotency_key:proof.idempotency_key,version:3,lease_epoch:2,status:'active',write_scopes:[],
@@ -98,6 +100,7 @@
expected_shadow_provider_revision:seed.provider_revision}})).status,'applied');
await rm(display);
const poll=arm==='baseline'?baseline:current;
+ const updateTodo=arm==='baseline'?baselineUpdate:update;
const request={schema_version:'loopx_coordination_monitor_poll_request_v0',runtime_root:runtime,goal_id:goal,
operation_id:'unchanged',actor_agent_id:'agent-a',registered_agents:['agent-a','agent-b'],dry_run:false,
observation:{todo_id:target,generated_at:'2026-09-01T00:00:00Z',result_hash:'first',material_change:false},intent:{}};
@@ -105,7 +108,7 @@
assert.equal(firstPoll.status,'applied',JSON.stringify(firstPoll));
const first=await store.loadAuthority(); assert.equal(first.status,'loaded'); compatible[arm]=first.head;
assert.equal((await poll(request,dependencies)).status,'replayed');
- const leased={...first.head,handoff_mode:'hard_lease',leases:[...first.head.leases,lease].sort((a,b)=>a.todo_idb.todo_id?1:0)};
+ const leased={...first.head,handoff_mode:'hard_lease',leases:[...first.head.leases,lease].sort((a,b)=>authorityUnicodeCompare(a.todo_id,b.todo_id))};
assert.equal((await store.commitAuthority({operation_id:'seed-execution',expected_provider_revision:first.provider_revision,
events:[],receipts:[],next_projection:leased})).status,'applied');
const before=await store.loadAuthority();
@@ -152,17 +155,11 @@
planning_intent:{status:'open',no_followup:false},monitor_observation:{generated_at:'2030-01-01T00:00:00Z',
result_hash:'second',material_change:true,monitor_effect_id:'reactivate',cadence:'1h'}};
const closed=await store.loadAuthority();
- if(arm==='baseline') {
- const unsupported=await baselineUpdate(observation,dependencies);
- assert.equal(unsupported.status,'failed'); assert.equal(unsupported.reason_code,'monitor_reactivation_lease_transition_required');
- assert.deepEqual(await store.loadAuthority(),closed);
- report[arm]={poll_parity:true,retained_lease_reactivation:'rejected_no_write'}; continue;
- }
- assert.equal((await update({...observation,dry_run:true},dependencies)).status,'planned');
+ assert.equal((await updateTodo({...observation,dry_run:true},dependencies)).status,'planned');
assert.deepEqual(await store.loadAuthority(),closed);
- assert.equal((await update({...observation,patch:{text:'mixed edit'}},dependencies)).status,'failed');
+ assert.equal((await updateTodo({...observation,patch:{text:'mixed edit'}},dependencies)).status,'failed');
assert.deepEqual(await store.loadAuthority(),closed);
- const resumed=await update(observation,dependencies); assert.equal(resumed.status,'applied',JSON.stringify(resumed));
+ const resumed=await updateTodo(observation,dependencies); assert.equal(resumed.status,'applied',JSON.stringify(resumed));
assert.equal(resumed.monitor_poll_transition.material_change_generation,2);
const afterResume=await store.loadAuthority(); assert.equal(afterResume.status,'loaded');
const resumedTodo=afterResume.head.todos.find(t=>t.todo_id===target);
@@ -177,7 +174,7 @@
assert.equal(fresh.status,'applied',JSON.stringify(fresh));
assert.equal(fresh.lease.version,4); assert.equal(fresh.lease.lease_epoch,3);
const acquired=await store.loadAuthority();
- assert.equal((await update(observation,dependencies)).status,'replayed');
+ assert.equal((await updateTodo(observation,dependencies)).status,'replayed');
assert.deepEqual(await store.loadAuthority(),acquired,'historical replay cannot release the fresh execution');
assert.deepEqual(afterResume.head.todos.filter(t=>input.projection.todos.some(old=>old.todo_id===t.todo_id)),input.projection.todos);
const closedAgain=afterResume.head.todos.map(t=>t.todo_id===target?{...t,status:'done',done:true,completed_at:'2030-01-01T01:00:00Z'}:t);
@@ -185,15 +182,15 @@
events:[],receipts:[],next_projection:{...acquired.head,todos:closedAgain,
todo_read_model:coordinationTodoReadModel(closedAgain,afterResume.head.todo_read_model.schema_version)}});
const final=await store.loadAuthority();
- assert.equal((await update(observation,dependencies)).status,'replayed');
- assert.equal((await update({...observation,operation_id:'stale-new-id'},dependencies)).status,'failed');
+ assert.equal((await updateTodo(observation,dependencies)).status,'replayed');
+ assert.equal((await updateTodo({...observation,operation_id:'stale-new-id'},dependencies)).status,'failed');
assert.deepEqual(await store.loadAuthority(),final);
heads[arm]=final.head;
report[arm]={poll_parity:true,retained_lease_reactivation:'applied',fresh_execution_epoch:3,same_hash_reactivation_generation:2,
historical_replay_keeps_completion:true,stale_observation:'rejected',non_target_unchanged:true};
}
for(const arm of ['file','sqlite','postgresql']) assert.deepEqual(compatible[arm],compatible.baseline);
- assert.deepEqual(heads.file,heads.sqlite); assert.deepEqual(heads.file,heads.postgresql);
+ for(const arm of ['file','sqlite','postgresql']) assert.deepEqual(heads[arm],heads.baseline);
process.stdout.write(JSON.stringify({schema_version:'authority_monitor_rehearsal_v0',
source_todos:input.projection.todos.length,source_leases:input.projection.leases.length,
compatible_head_sha256:digest(compatible.baseline),final_head_sha256:digest(heads.file),
@@ -227,7 +224,7 @@ def git(*command):
goal = next(g for g in registry["goals"] if g["id"] == args.goal_id)
runtime = resolve_runtime_root(registry, None, registry_path=registry_path)
_, _, state = resolve_goal_state(registry=registry, goal_id=args.goal_id, project_override=None, state_file_override=None)
- projection, snapshot = build_runtime_shadow_source_snapshot(goal=goal, runtime_root=runtime, state_path=state, registry_path=registry_path)
+ projection, snapshot = build_runtime_shadow_source_snapshot(goal=goal, runtime_root=runtime, state_path=state, registry_path=registry_path, include_all_archived_todos=True)
source_digest = hashlib.sha256(json.dumps(snapshot, sort_keys=True).encode()).hexdigest()
child = subprocess.run(["node", "--no-warnings", "--experimental-sqlite", "--experimental-strip-types", "--input-type=module", "-e", NODE_REHEARSAL],
input=json.dumps({"repo": str(REPOSITORY), "baseline_repo": str(baseline), "goal_id": args.goal_id, "projection": projection}),
@@ -239,7 +236,7 @@ def git(*command):
with os.fdopen(descriptor, "w") as stream:
stream.write(child.stderr)
raise SystemExit(f"isolated Monitor rehearsal failed (exit {child.returncode}); no qualification; diagnostics stay private")
- after_projection, after_snapshot = build_runtime_shadow_source_snapshot(goal=goal, runtime_root=runtime, state_path=state, registry_path=registry_path)
+ after_projection, after_snapshot = build_runtime_shadow_source_snapshot(goal=goal, runtime_root=runtime, state_path=state, registry_path=registry_path, include_all_archived_todos=True)
if projection != after_projection or snapshot != after_snapshot or registry_path.read_bytes() != registry_bytes:
raise SystemExit("live source changed during rehearsal; rerun from a stable snapshot")
result = json.loads(child.stdout)
diff --git a/examples/control_plane/auto-research-role-successor-completion-smoke.py b/examples/control_plane/auto-research-role-successor-completion-smoke.py
index b5931322ab..7270dea817 100644
--- a/examples/control_plane/auto-research-role-successor-completion-smoke.py
+++ b/examples/control_plane/auto-research-role-successor-completion-smoke.py
@@ -13,6 +13,7 @@
sys.path.insert(0, str(REPO_ROOT))
from loopx.control_plane.testing.canary_harness import ( # noqa: E402
+ import_fixture_authority,
run_json_cli,
run_json_cli_result,
write_fixture_registry,
@@ -71,6 +72,7 @@ def write_fixture(root: Path, *, auto_research: bool) -> tuple[Path, Path, Path]
registered_agents=[CURATOR, PROPOSER, EXECUTOR],
peer_independent_worktree_required=False,
)
+ import_fixture_authority(registry_path, GOAL_ID)
return project, runtime, registry_path
@@ -122,7 +124,7 @@ def assert_actor_attribution_fails_closed() -> None:
actor_agent_id=None,
)
assert missing_code == 1, missing
- assert "requires --agent-id" in missing["error"], missing
+ assert missing["error_code"] == "actor_required", missing
assert state_path.read_text(encoding="utf-8") == before
wrong_code, wrong = complete_proposer(
@@ -131,7 +133,7 @@ def assert_actor_attribution_fails_closed() -> None:
actor_agent_id=CURATOR,
)
assert wrong_code == 1, wrong
- assert f"claimed_by={PROPOSER!r}" in wrong["error"], wrong
+ assert wrong["error_code"] == "claim_owner_mismatch", wrong
assert state_path.read_text(encoding="utf-8") == before
@@ -142,7 +144,6 @@ def assert_auto_research_role_successor_allowed() -> None:
assert returncode == 0, completed
assert completed["ok"] is True, completed
assert completed["status"] == "done", completed
- assert completed["linked_successor_id"] == EXECUTOR_TODO, completed
assert completed["successor_todo_ids"] == [EXECUTOR_TODO], completed
assert completed["next_todos"] == [], completed
@@ -168,7 +169,6 @@ def assert_generic_peer_route_links_existing_successor() -> None:
returncode, completed = complete_proposer(registry_path, runtime)
assert returncode == 0, completed
assert completed["ok"] is True, completed
- assert completed["linked_successor_id"] == EXECUTOR_TODO, completed
assert completed["successor_todo_ids"] == [EXECUTOR_TODO], completed
diff --git a/examples/control_plane/completion-validation-accountability-smoke.py b/examples/control_plane/completion-validation-accountability-smoke.py
index e8a0efd022..8b6e926fcb 100755
--- a/examples/control_plane/completion-validation-accountability-smoke.py
+++ b/examples/control_plane/completion-validation-accountability-smoke.py
@@ -13,14 +13,15 @@
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
-from loopx.control_plane.quota.slot_accounting import (
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
+from loopx.control_plane.quota.slot_accounting import ( # noqa: E402
build_quota_slot_preview_for_decision,
)
-from loopx.control_plane.todos.active_state_todo_parser import (
+from loopx.control_plane.todos.active_state_todo_parser import ( # noqa: E402
parse_active_state_todos,
)
-from loopx.state_refresh import refresh_state_run
-from loopx.todos import add_goal_todo, complete_goal_todo
+from loopx.state_refresh import refresh_state_run # noqa: E402
+from loopx.todos import add_goal_todo, complete_goal_todo # noqa: E402
GOAL_ID = "completion-validation-accountability"
AGENT_ID = "codex-author"
@@ -71,6 +72,7 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
),
encoding="utf-8",
)
+ import_fixture_authority(registry, GOAL_ID)
return registry, runtime, state
diff --git a/examples/control_plane/control-plane-integrated-canary-smoke.py b/examples/control_plane/control-plane-integrated-canary-smoke.py
index a784e4f554..bafd9b4089 100644
--- a/examples/control_plane/control-plane-integrated-canary-smoke.py
+++ b/examples/control_plane/control-plane-integrated-canary-smoke.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-"""Bounded canary for the status -> quota -> review-packet Todo read path."""
+"""Bounded File/SQLite canary for status, quota, successor and monitor readback."""
from __future__ import annotations
@@ -17,21 +17,23 @@
sys.path.insert(0, str(REPO_ROOT))
from loopx.control_plane.testing.canary_harness import ( # noqa: E402
+ import_fixture_authority,
read_run_index,
run_json_cli,
write_fixture_registry,
)
+from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted # noqa: E402
GOAL_ID = "control-plane-integrated-canary"
MONITOR_GOAL_ID = "control-plane-integrated-monitor-canary"
-MARKDOWN_GOAL_ID = "control-plane-markdown-continuation-canary"
+CONTINUATION_GOAL_ID = "control-plane-continuation-canary"
AGENT_ID = "codex-product-capability"
PRIMARY_AGENT_ID = "codex-main-control"
CANARY_TODO_ID = "todo_integrated_canary"
CANARY_TODO_TITLE = "Design bounded status/quota/review-packet/Todo/read-path canary"
-SUCCESSOR_TODO_TITLE = "Continue integrated Markdown successor routing canary"
-MARKDOWN_CONTINUATION_TITLE = "[P1] Continue same-agent markdown continuation through status and quota."
+SUCCESSOR_TODO_TITLE = "Continue integrated canonical successor routing canary"
+CONTINUATION_TITLE = "[P1] Continue same-agent canonical continuation through status and quota."
MONITOR_TODO_ID = "todo_integrated_due_monitor"
MONITOR_TARGET_KEY = "integrated-due-monitor-watch"
VALIDATED_PROGRESS_CLASSIFICATION = "integrated_canary_validated_progress"
@@ -73,7 +75,7 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
return registry_path, state_file, runtime
-def write_monitor_fixture(root: Path) -> tuple[Path, Path]:
+def write_monitor_fixture(root: Path, *, provider: str) -> tuple[Path, Path]:
project = root / "monitor-project"
runtime = root / "monitor-runtime"
state_file = project / ".codex" / "goals" / MONITOR_GOAL_ID / "ACTIVE_GOAL_STATE.md"
@@ -103,13 +105,14 @@ def write_monitor_fixture(root: Path) -> tuple[Path, Path]:
registered_agents=[AGENT_ID],
quota_allowed_slots=10,
)
+ import_fixture_authority(registry_path, MONITOR_GOAL_ID, provider=provider)
return registry_path, runtime
-def write_markdown_continuation_fixture(root: Path) -> tuple[Path, Path, Path]:
- project = root / "markdown-project"
- runtime = root / "markdown-runtime"
- state_file = project / ".codex" / "goals" / MARKDOWN_GOAL_ID / "ACTIVE_GOAL_STATE.md"
+def write_continuation_fixture(root: Path, *, provider: str) -> tuple[Path, Path, Path]:
+ project = root / "continuation-project"
+ runtime = root / "continuation-runtime"
+ state_file = project / ".codex" / "goals" / CONTINUATION_GOAL_ID / "ACTIVE_GOAL_STATE.md"
registry_path = project / ".loopx" / "registry.json"
state_file.parent.mkdir(parents=True)
state_file.write_text(
@@ -117,9 +120,9 @@ def write_markdown_continuation_fixture(root: Path) -> tuple[Path, Path, Path]:
"status: active\n"
"updated_at: 2026-06-27T00:00:00+00:00\n"
"---\n\n"
- "# Control Plane Markdown Continuation Canary\n\n"
+ "# Control Plane Canonical Continuation Canary\n\n"
"## Next Action\n\n"
- "- Preserve same-agent markdown successor routing across status and quota.\n\n"
+ "- Preserve same-agent canonical successor routing across status and quota.\n\n"
"## Agent Todo\n\n"
"- [ ] [P2] Markdown placeholder that should lose to the claimed P1 todo.\n"
" \n"
- "- [ ] [P1] Repair a separate repository-writing control-plane task.\n"
- f" \n",
- encoding="utf-8",
+ monitor = run_cli(
+ project, "todo", "add", "--goal-id", GOAL_ID, "--role", "agent",
+ "--operation-id", "due-monitor",
+ "--text", "[P0] Monitor one due repository lifecycle without repository writes.",
+ "--task-class", "continuous_monitor", "--action-kind", "monitor_repository_lifecycle",
+ "--task-repository", PROJECT_REPOSITORY, "--claimed-by", PEER_ALPHA,
+ "--target-key", "repository-lifecycle", "--cadence", "30m",
+ "--next-due-at", "2020-01-01T00:00:00+00:00", "--expires-at", "2099-01-01T00:00:00+00:00",
+ registry_path=registry_path, runtime=runtime,
+ )
+ run_cli(
+ project, "todo", "add", "--goal-id", GOAL_ID, "--role", "agent",
+ "--operation-id", "unrelated-repair",
+ "--text", "[P1] Repair a separate repository-writing control-plane task.",
+ "--task-class", "advancement_task", "--action-kind", "repair_workspace_route",
+ "--task-repository", TASK_REPOSITORY, "--required-write-scope", "loopx/**",
+ "--claimed-by", PEER_ALPHA,
+ registry_path=registry_path, runtime=runtime,
)
monitor_selected = should_run(project, project, runtime, registry_path)
@@ -304,7 +328,7 @@ def main() -> None:
assert "workspace_guard" not in monitor_selected, monitor_selected
assert "boundary_projection_gap" not in monitor_selected, monitor_selected
assert monitor_selected["selected_todo"]["todo_id"] == (
- "todo_due_monitor"
+ monitor["todo_id"]
), monitor_selected
assert monitor_selected["selected_todo"]["task_repository"] == (
PROJECT_REPOSITORY
diff --git a/examples/control_plane/quota-agent-scoped-user-gate-smoke.py b/examples/control_plane/quota-agent-scoped-user-gate-smoke.py
index 25e5bf012f..88c1c56a61 100644
--- a/examples/control_plane/quota-agent-scoped-user-gate-smoke.py
+++ b/examples/control_plane/quota-agent-scoped-user-gate-smoke.py
@@ -13,6 +13,7 @@
from loopx.control_plane.todos.decision_scope import todo_gate_relation # noqa: E402
from loopx.control_plane.scheduler.execution_context import ( # noqa: E402
+ GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
scheduler_execution_context_for_runtime_profile,
)
from loopx.control_plane.testing.quota_fixtures import ( # noqa: E402
@@ -46,6 +47,7 @@ def todo_item(
role: str = "agent",
task_class: str = "advancement_task",
claimed_by: str | None = None,
+ bound_agent: str | None = None,
action_kind: str | None = None,
blocks_agent: str | None = None,
global_gate: bool = False,
@@ -61,6 +63,7 @@ def todo_item(
"next_due_at": next_due_at,
"target_key": target_key,
"global_gate": True if global_gate else None,
+ "bound_agent": bound_agent,
}.items()
if value is not None
}
@@ -245,7 +248,7 @@ def other_agent_gate_with_current_user_action_payload() -> dict:
role="user",
task_class="user_action",
action_kind="review_handoff_material",
- claimed_by="codex-value-explorer",
+ bound_agent=VALUE_AGENT,
priority="P1",
)
value_agent_todo = todo_item(
@@ -276,7 +279,7 @@ def assert_other_agent_user_gate_does_not_block_current_agent() -> None:
status_payload(),
goal_id=GOAL_ID,
agent_id="codex-main-control",
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["should_run"] is True, payload
assert payload["normal_delivery_allowed"] is True, payload
@@ -308,14 +311,16 @@ def assert_current_user_action_does_not_preserve_other_agent_operator_gate() ->
other_agent_gate_with_current_user_action_payload(),
goal_id=GOAL_ID,
agent_id=VALUE_AGENT,
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["decision"] == "run", payload
assert payload["state"] == "eligible", payload
assert payload["should_run"] is True, payload
assert payload["normal_delivery_allowed"] is True, payload
+ # A routed user_action is a non-blocking notice while live work proceeds;
+ # the foreign user_gate must not turn that notice into an execution gate.
assert payload["requires_user_action"] is False, payload
- assert payload["interaction_contract"]["user_channel"]["notify"] == "NOTIFY", payload
+ assert "notify_user_on_open_todo" not in payload, payload
assert "notify_user_on_gate" not in payload, payload
summary = payload["user_todo_summary"]
@@ -335,8 +340,11 @@ def assert_current_user_action_does_not_preserve_other_agent_operator_gate() ->
contract = payload["interaction_contract"]
assert contract["mode"] == "bounded_delivery", contract
assert contract["user_channel"]["action_required"] is False, contract
- assert contract["user_channel"]["notify"] == "NOTIFY", contract
assert contract["user_channel"]["non_blocking"] is True, contract
+ assert contract["user_channel"]["notify"] == "NOTIFY", contract
+ assert contract["user_channel"]["actions"] == [
+ "Review the value-lane handoff material when convenient."
+ ], contract
assert contract["agent_channel"]["must_attempt"] is True, contract
assert contract["agent_channel"]["delivery_allowed"] is True, contract
@@ -346,7 +354,7 @@ def assert_other_agent_user_gate_does_not_notify_non_due_monitor_lane() -> None:
other_agent_gate_with_non_due_monitor_payload(),
goal_id=GOAL_ID,
agent_id="codex-value-explorer",
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["decision"] == "skip", payload
assert payload["effective_action"] == "monitor_quiet_skip", payload
@@ -373,7 +381,7 @@ def assert_target_agent_still_blocks_on_its_user_gate() -> None:
status_payload(),
goal_id=GOAL_ID,
agent_id="codex-product-capability",
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["should_run"] is False, payload
assert payload["normal_delivery_allowed"] is False, payload
@@ -394,7 +402,7 @@ def assert_unscoped_user_gate_requires_projection_repair() -> None:
status_payload(blocks_agent=None),
goal_id=GOAL_ID,
agent_id="codex-main-control",
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["decision"] == "self_repair", payload
assert payload["should_run"] is True, payload
@@ -418,7 +426,7 @@ def assert_explicit_global_gate_blocks_goal() -> None:
status_payload(blocks_agent=None, global_gate=True),
goal_id=GOAL_ID,
agent_id="codex-main-control",
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["should_run"] is False, payload
assert payload["requires_user_action"] is True, payload
@@ -470,7 +478,7 @@ def assert_unrelated_user_gate_allows_feishu_fallback() -> None:
unrelated_gate_status_payload(),
goal_id=GOAL_ID,
agent_id="codex-main-control",
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["should_run"] is True, payload
assert payload["decision"] == "safe_bypass_user_gate_fallback", payload
@@ -723,7 +731,7 @@ def assert_exact_todo_gate_only_blocks_target_todo() -> None:
exact_todo_gate_status_payload(),
goal_id=GOAL_ID,
agent_id="codex-main-control",
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["should_run"] is True, payload
assert payload["decision"] == "safe_bypass_user_gate_fallback", payload
@@ -751,7 +759,7 @@ def assert_exact_todo_gate_only_blocks_target_todo() -> None:
exact_todo_gate_status_payload(include_fallback=False),
goal_id=GOAL_ID,
agent_id="codex-main-control",
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert blocked_payload["should_run"] is False, blocked_payload
assert blocked_payload["interaction_contract"]["mode"] == "user_gate", blocked_payload
@@ -801,7 +809,7 @@ def assert_exact_todo_gate_survives_decision_scope_migration() -> None:
exact_todo_gate_with_decision_scope_migration_payload(),
goal_id=GOAL_ID,
agent_id="codex-main-control",
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["should_run"] is True, payload
assert payload["decision"] == "safe_bypass_user_gate_fallback", payload
@@ -830,7 +838,7 @@ def assert_exact_todo_gate_allows_independent_due_monitor() -> None:
goal_id=GOAL_ID,
agent_id="codex-main-control",
available_capabilities=["network", "external_write"],
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["should_run"] is True, payload
assert payload["decision"] == "safe_bypass_user_gate_fallback", payload
@@ -854,7 +862,7 @@ def assert_exact_todo_gate_allows_independent_due_monitor() -> None:
goal_id=GOAL_ID,
agent_id="codex-main-control",
available_capabilities=["network"],
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert blocked_payload["should_run"] is False, blocked_payload
assert "scoped_user_gate_fallback" not in blocked_payload, blocked_payload
@@ -901,7 +909,7 @@ def assert_monitor_debt_yields_to_equal_priority_advancement() -> None:
goal_id=GOAL_ID,
agent_id="codex-main-control",
available_capabilities=["network", "external_write"],
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
arbitration = payload["monitor_debt_arbitration"]
assert arbitration["active"] is True, arbitration
@@ -923,7 +931,7 @@ def assert_monitor_debt_yields_to_equal_priority_advancement() -> None:
goal_id=GOAL_ID,
agent_id="codex-main-control",
available_capabilities=["network", "external_write"],
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert "monitor_debt_arbitration" not in terminal_transition, terminal_transition
terminal_fallback = terminal_transition["scoped_user_gate_fallback"]
@@ -974,7 +982,7 @@ def assert_decision_scope_overrides_shared_action_kind_tokens() -> None:
decision_scope_status_payload(),
goal_id=GOAL_ID,
agent_id="codex-main-control",
- scheduler_execution_context=APP_SCHEDULER_CONTEXT,
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT,
)
assert payload["should_run"] is True, payload
assert payload["decision"] == "safe_bypass_user_gate_fallback", payload
diff --git a/examples/control_plane/quota-no-git-status-smoke.py b/examples/control_plane/quota-no-git-status-smoke.py
index 6ff81a01a5..881d48b5b8 100644
--- a/examples/control_plane/quota-no-git-status-smoke.py
+++ b/examples/control_plane/quota-no-git-status-smoke.py
@@ -12,6 +12,11 @@
REPO_ROOT = Path(__file__).resolve().parents[2]
+if str(REPO_ROOT) not in sys.path:
+ sys.path.insert(0, str(REPO_ROOT))
+
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
+
GOAL_ID = "no-git-quota-fixture"
AGENT_ID = "codex-benchmark-agent"
@@ -30,7 +35,8 @@ def write_fixture(root: Path) -> Path:
"## Objective\n\n"
"Solve this no-git fixture.\n\n"
"## Next Action\n\n"
- "- Pick the seeded todo.\n",
+ "- Pick the seeded todo.\n\n"
+ "## Agent Todo\n\n",
encoding="utf-8",
)
registry_path.parent.mkdir(parents=True)
@@ -60,6 +66,7 @@ def write_fixture(root: Path) -> Path:
+ "\n",
encoding="utf-8",
)
+ import_fixture_authority(registry_path, GOAL_ID)
return registry_path
@@ -100,6 +107,7 @@ def main() -> int:
"Solve the no-git quota fixture.",
)
assert todo["ok"] is True, todo
+ before = run_cli(registry_path, "todo", "list", "--goal-id", GOAL_ID)
no_git_bin = root / "no-git-bin"
no_git_bin.mkdir()
@@ -119,6 +127,11 @@ def main() -> int:
assert quota.get("status") != "quota_collection_failed", quota
assert quota["should_run"] is True, quota
assert quota["recommended_action"] == "[P1] Solve the no-git quota fixture.", quota
+ assert quota["selected_todo"]["todo_id"] == todo["todo_id"], quota
+ after = run_cli(registry_path, "todo", "list", "--goal-id", GOAL_ID, env=env)
+ assert after["todos"] == before["todos"], after
+ assert after["authority_read"]["provider_revision"] == before["authority_read"]["provider_revision"], after
+ assert after["authority_read"]["legacy_fallback_used"] is False, after
return 0
diff --git a/examples/control_plane/runtime-handoff-status-read-path-smoke.py b/examples/control_plane/runtime-handoff-status-read-path-smoke.py
index 8f57342ea3..6b75c7b683 100644
--- a/examples/control_plane/runtime-handoff-status-read-path-smoke.py
+++ b/examples/control_plane/runtime-handoff-status-read-path-smoke.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-"""Canary the runtime handoff read path across status, quota, and review-packet."""
+"""Keep handoff history intact across fresh and rebuilt current-state routing."""
from __future__ import annotations
@@ -50,7 +50,7 @@ def run_cli(
return json.loads(result.stdout)
-def write_fixture(root: Path) -> tuple[Path, Path, Path]:
+def write_fixture(root: Path, *, stale_run: bool) -> tuple[Path, Path, Path]:
project = root / "project"
runtime_root = root / "runtime"
state_file = f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md"
@@ -59,10 +59,15 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
run_index = runtime_root / "goals" / GOAL_ID / "runs" / "index.jsonl"
state_path.parent.mkdir(parents=True)
+ # Both timestamps use the run's timezone: the original fixture accidentally
+ # made its supposedly fresh run older than the state by four hours.
+ state_updated_at = (
+ "2026-07-05T05:00:00+08:00" if stale_run else "2026-07-05T00:00:00+08:00"
+ )
state_path.write_text(
"---\n"
"status: active\n"
- "updated_at: 2026-07-05T00:00:00+00:00\n"
+ f"updated_at: {state_updated_at}\n"
"---\n\n"
"# Runtime Handoff Status Read Path Fixture\n\n"
"## Next Action\n\n"
@@ -193,6 +198,7 @@ def assert_status_read_path(
project: Path,
runtime_root: Path,
registry_path: Path,
+ stale_run: bool,
) -> None:
payload = run_cli(
"status",
@@ -206,11 +212,17 @@ def assert_status_read_path(
runtime_root=runtime_root,
)
item = status_item(payload)
- assert item["status"] == "active_state_agent_todo", item
- assert item["source"] == "active_state", item
+ if stale_run:
+ # A current Todo read replaces stale progress routing, never its history.
+ assert item["source"] == "active_state", item
+ assert item["status"] == "active_state_agent_todo", item
+ assert item["recommended_action"] == f"[P1] {TODO_TITLE}", item
+ else:
+ assert item["source"] == "latest_run", item
+ assert item["status"] == POST_HANDOFF_CLASSIFICATION, item
+ assert item["recommended_action"] == POST_HANDOFF_ACTION, item
assert item.get("stale_latest_run_warning") is None, item
assert item["waiting_on"] == "codex", item
- assert item["recommended_action"] == f"[P1] {TODO_TITLE}", item
assert item["project_asset"]["active_state_next_action"] == GOAL_NEXT_ACTION, item
assert item["project_asset"]["latest_validation"]["classification"] == POST_HANDOFF_CLASSIFICATION, item
assert item["project_asset"]["latest_validation"]["summary"] == POST_HANDOFF_ACTION, item
@@ -289,21 +301,27 @@ def assert_review_packet_handoff(
def main() -> int:
with tempfile.TemporaryDirectory(prefix="loopx-runtime-handoff-status-read-") as tmp:
- project, runtime_root, registry_path = write_fixture(Path(tmp))
- assert_status_read_path(
- project=project,
- runtime_root=runtime_root,
- registry_path=registry_path,
- )
- assert_quota_read_path(
- runtime_root=runtime_root,
- registry_path=registry_path,
- )
- assert_review_packet_handoff(
- project=project,
- runtime_root=runtime_root,
- registry_path=registry_path,
- )
+ for stale_run in (False, True):
+ root = Path(tmp) / ("stale" if stale_run else "fresh")
+ project, runtime_root, registry_path = write_fixture(root, stale_run=stale_run)
+ files = [path for path in root.rglob("*") if path.is_file()]
+ before = {path: path.read_bytes() for path in files}
+ assert_status_read_path(
+ project=project,
+ runtime_root=runtime_root,
+ registry_path=registry_path,
+ stale_run=stale_run,
+ )
+ assert_quota_read_path(
+ runtime_root=runtime_root,
+ registry_path=registry_path,
+ )
+ assert_review_packet_handoff(
+ project=project,
+ runtime_root=runtime_root,
+ registry_path=registry_path,
+ )
+ assert {path: path.read_bytes() for path in files} == before
print("runtime-handoff-status-read-path-smoke ok")
return 0
diff --git a/examples/control_plane/status-projection-cache-smoke.py b/examples/control_plane/status-projection-cache-smoke.py
index 1b81901b4e..dc3eedc8ff 100644
--- a/examples/control_plane/status-projection-cache-smoke.py
+++ b/examples/control_plane/status-projection-cache-smoke.py
@@ -17,6 +17,7 @@
sys.path.insert(0, str(REPO_ROOT))
from loopx.control_plane.runtime.status_projection_cache import write_status_projection_cache # noqa: E402
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
GOAL_ID = "status-projection-cache-fixture"
AGENT_ID = "codex-main-control"
@@ -41,7 +42,8 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
"## Objective\n\n"
"Validate opt-in status projection cache behavior.\n\n"
"## Next Action\n\n"
- "- Exercise the status/quota projection cache.\n",
+ "- Exercise the status/quota projection cache.\n\n"
+ "## Agent Todo\n\n",
encoding="utf-8",
)
public_doc.write_text("Public smoke fixture for status projection cache.\n", encoding="utf-8")
@@ -81,6 +83,7 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
+ "\n",
encoding="utf-8",
)
+ import_fixture_authority(registry_path, GOAL_ID)
return registry_path, runtime, public_doc
@@ -258,6 +261,8 @@ def main() -> int:
"agent",
"--todo-id",
initial_todo_id,
+ "--agent-id",
+ AGENT_ID,
"--claimed-by",
AGENT_ID,
"--evidence",
diff --git a/examples/control_plane/status-quota-perf-budget-smoke.py b/examples/control_plane/status-quota-perf-budget-smoke.py
index 0e07ba7eb8..e5d7c7af2e 100644
--- a/examples/control_plane/status-quota-perf-budget-smoke.py
+++ b/examples/control_plane/status-quota-perf-budget-smoke.py
@@ -14,6 +14,11 @@
REPO_ROOT = Path(__file__).resolve().parents[2]
+if str(REPO_ROOT) not in sys.path:
+ sys.path.insert(0, str(REPO_ROOT))
+
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
+
GOAL_ID = "status-quota-perf-budget-fixture"
AGENT_ID = "codex-main-control"
IGNORED_FILE_COUNT = 1_200
@@ -22,7 +27,7 @@
PRIVATE_DOC_MARKER = "https://" + "la" + "rk" + "office.example/doc"
-def write_fixture(root: Path) -> tuple[Path, Path, Path]:
+def write_fixture(root: Path, *, import_authority: bool = True) -> tuple[Path, Path, Path]:
project = root / "project"
runtime = root / "runtime"
state_rel = f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md"
@@ -40,7 +45,12 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
"## Objective\n\n"
"Keep status and quota hot paths fast without skipping public scans.\n\n"
"## Next Action\n\n"
- "- Validate the selected hot-path budget todo.\n",
+ "- Validate the selected hot-path budget todo.\n\n"
+ "## Agent Todo\n\n"
+ + ("" if import_authority else
+ "- [ ] Inspect the historical status budget fixture.\n"
+ f" \n"),
encoding="utf-8",
)
public_doc.write_text("Public smoke fixture for status/quota perf budgets.\n", encoding="utf-8")
@@ -90,6 +100,8 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
+ "\n",
encoding="utf-8",
)
+ if import_authority:
+ import_fixture_authority(registry_path, GOAL_ID)
return registry_path, project, public_doc
@@ -230,10 +242,23 @@ def main() -> int:
)
state_text = state_file.read_text(encoding="utf-8")
assert " status=open " in state_text, state_text
- state_file.write_text(
- state_text.replace(" status=open ", " status=stuck ", 1),
- encoding="utf-8",
- )
+ before, _ = run_cli(registry_path, "todo", "list", "--goal-id", GOAL_ID)
+ tampered = state_text.replace(" status=open ", " status=stuck ", 1)
+ state_file.write_text(tampered, encoding="utf-8")
+ after, _ = run_cli(registry_path, "todo", "list", "--goal-id", GOAL_ID)
+ assert after["todos"] == before["todos"], after
+ assert after["authority_read"]["provider_revision"] == before["authority_read"]["provider_revision"], after
+ assert after["authority_read"]["legacy_fallback_used"] is False, after
+ assert state_file.read_text(encoding="utf-8") == tampered
+
+ # Malformed historical source still has a read-only diagnostic/budget
+ # contract. It cannot act as the authority of the imported fixture.
+ registry_path, project, public_doc = write_fixture(root / "legacy-inspection", import_authority=False)
+ state_file = project / ".codex" / "goals" / GOAL_ID / "ACTIVE_GOAL_STATE.md"
+ state_text = state_file.read_text(encoding="utf-8")
+ assert " status=open " in state_text, state_text
+ state_file.write_text(state_text.replace(" status=open ", " status=stuck ", 1), encoding="utf-8")
+ legacy_source = state_file.read_bytes()
(
malformed_status_payload,
@@ -308,6 +333,7 @@ def main() -> int:
QUOTA_BUDGET_SECONDS,
"malformed_state",
)
+ assert state_file.read_bytes() == legacy_source
print(
"status_quota_perf_budget: "
diff --git a/examples/control_plane/task-lease-runtime-smoke.py b/examples/control_plane/task-lease-runtime-smoke.py
index 1b0ac95ac7..d15d2aac86 100644
--- a/examples/control_plane/task-lease-runtime-smoke.py
+++ b/examples/control_plane/task-lease-runtime-smoke.py
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
-"""Smoke-test the task_lease_v0 runtime and CLI contract."""
+"""Exercise canonical lease, claim handover and completion through the real CLI."""
from __future__ import annotations
import json
-import re
+import os
import subprocess
import sys
import tempfile
@@ -14,6 +14,10 @@
REPO_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(REPO_ROOT))
+sys.path.insert(0, str(REPO_ROOT / "tests" / "control_plane"))
+
+from canonical_authority_fixture import initialize_canonical_authority # noqa: E402
+from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection # noqa: E402
GOAL_ID = "task-lease-runtime-goal"
TODO_A = "todo_taskleasea"
@@ -21,7 +25,7 @@
TODO_C = "todo_taskleasec"
-def write_fixture(root: Path) -> tuple[Path, Path]:
+def write_fixture(root: Path, provider: str) -> tuple[Path, Path]:
project = root / "project"
runtime = root / "runtime"
registry_path = project / ".loopx" / "registry.json"
@@ -32,14 +36,7 @@ def write_fixture(root: Path) -> tuple[Path, Path]:
"status: active\n"
"updated_at: 2026-01-01T00:00:00+00:00\n"
"---\n\n"
- "# Active Goal State\n\n"
- "## Agent Todo\n\n"
- f"- [ ] First independently claimable todo.\n"
- f" \n"
- f"- [ ] Second independently claimable todo.\n"
- f" \n"
- f"- [ ] Conflicting write-scope todo.\n"
- f" \n",
+ "# Active Goal State\n\n## Agent Todo\n\n",
encoding="utf-8",
)
registry_path.parent.mkdir(parents=True)
@@ -76,28 +73,43 @@ def write_fixture(root: Path) -> tuple[Path, Path]:
+ "\n",
encoding="utf-8",
)
+ worker = project.parent / "worker"
+ for args in [
+ ("init", "--initial-branch", "main"),
+ ("remote", "add", "origin", "https://example.invalid/loopx/lease-fixture.git"),
+ ("-c", "user.name=LoopX Fixture", "-c", "user.email=fixture@example.invalid",
+ "commit", "--allow-empty", "-m", "fixture"),
+ ("worktree", "add", "-b", "worker", str(worker)),
+ ]:
+ subprocess.run(["git", "-C", str(project), *args], check=True, capture_output=True, text=True)
+ projection = build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, handoff_mode="hard_lease", leases=[], todos=[
+ {
+ "schema_version": "todo_item_v0", "todo_id": todo_id, "role": "agent",
+ "status": "open", "done": False, "text": text, "archive_state": "active",
+ "source_section": "Agent Todo", "index": index, "task_class": "advancement_task",
+ "action_kind": "validate", "task_domain": "validation", "claimed_by": owner,
+ "task_repository": "git:example.invalid/loopx/lease-fixture",
+ "required_write_scopes": [scope],
+ }
+ for index, (todo_id, text, owner, scope) in enumerate([
+ (TODO_A, "First independently claimable todo.", "codex-main-control", "loopx/**"),
+ (TODO_B, "Second independently claimable todo.", None, "docs/**"),
+ (TODO_C, "Conflicting write-scope todo.", None, "loopx/cli_commands/todo.py"),
+ ], 1)
+ ],
+ )
+ initialize_canonical_authority(runtime, GOAL_ID, projection, state_path=state_file, provider=provider)
return registry_path, state_file
-def set_claimed_by(state_file: Path, *, todo_id: str, owner: str | None) -> None:
- lines = state_file.read_text(encoding="utf-8").splitlines()
- for index, line in enumerate(lines):
- if f"todo_id={todo_id}" not in line:
- continue
- line = re.sub(r"\s+claimed_by=[^\s<>]+", "", line)
- if owner:
- line = line.replace(" -->", f" claimed_by={owner} -->")
- lines[index] = line
- state_file.write_text("\n".join(lines) + "\n", encoding="utf-8")
- return
- raise AssertionError(f"todo metadata not found: {todo_id}")
-
-
def command(
registry_path: Path,
*args: str,
check: bool = True,
) -> subprocess.CompletedProcess[str]:
+ env = os.environ.copy()
+ env["PYTHONPATH"] = str(REPO_ROOT)
return subprocess.run(
[
sys.executable,
@@ -109,7 +121,8 @@ def command(
"json",
*args,
],
- cwd=REPO_ROOT,
+ cwd=registry_path.parents[2] / "worker",
+ env=env,
check=check,
text=True,
capture_output=True,
@@ -119,6 +132,8 @@ def command(
def cli(
registry_path: Path, *args: str, check: bool = True
) -> subprocess.CompletedProcess[str]:
+ if args[0] == "acquire":
+ args = (*args, "--write-worktree", str(registry_path.parents[2] / "worker"))
return command(registry_path, "task-lease", *args, check=check)
@@ -158,15 +173,9 @@ def inspected_lease(registry_path: Path, todo_id: str) -> dict[str, Any]:
)
-def main() -> int:
+def exercise_provider(provider: str) -> None:
with tempfile.TemporaryDirectory(prefix="loopx-task-lease-smoke-") as tmp:
- registry_path, state_file = write_fixture(Path(tmp))
-
- set_claimed_by(
- state_file,
- todo_id=TODO_A,
- owner="codex-main-control",
- )
+ registry_path, _state_file = write_fixture(Path(tmp), provider)
first = payload(
cli(
registry_path,
@@ -190,6 +199,10 @@ def main() -> int:
assert first["lease"]["version"] == 1, first
assert first["lease"]["lease_epoch"] == 1, first
assert first["lease"]["acquire_ttl_seconds"] == 120, first
+ assert first["source_authority"] == provider + "_v0", first
+ assert first["lease"]["write_workspace"]["repository"] == (
+ "git:example.invalid/loopx/lease-fixture"
+ ), first
idempotent = payload(
cli(
@@ -296,30 +309,21 @@ def main() -> int:
assert payload(claim_blocked_transfer)["error_code"] == (
"owner_conflicts_with_claim"
)
- set_claimed_by(state_file, todo_id=TODO_A, owner=None)
- transferred = payload(
- cli(
- registry_path,
- "transfer",
- "--goal-id",
- GOAL_ID,
- "--todo-id",
- TODO_A,
- "--owner",
- "codex-main-control",
- "--idempotency-key",
- "turn-1",
- "--new-owner",
- "codex-side-bypass",
- "--new-idempotency-key",
- "side-transfer",
- "--expected-version",
- "2",
- )
+ assert inspected_lease(registry_path, TODO_A)["lease"] == renewed["lease"]
+ transfer_args = (
+ "transfer", "--goal-id", GOAL_ID, "--todo-id", TODO_A,
+ "--owner", "codex-main-control", "--idempotency-key", "turn-1",
+ "--new-owner", "codex-side-bypass", "--new-idempotency-key", "side-transfer",
+ "--expected-version", "2", "--transfer-claim",
)
+ transferred = payload(cli(registry_path, *transfer_args))
assert transferred["lease"]["owner"] == "codex-side-bypass", transferred
assert transferred["lease"]["version"] == 3, transferred
assert transferred["lease"]["lease_epoch"] == 2, transferred
+ assert transferred["claimed_by"] == "codex-side-bypass", transferred
+ current = payload(command(registry_path, "todo", "list", "--goal-id", GOAL_ID, "--todo-id", TODO_A))
+ assert current["todo"]["claimed_by"] == "codex-side-bypass", current
+ assert current["authority_read"]["legacy_fallback_used"] is False, current
stale_owner = lifecycle_cli(
registry_path,
@@ -330,9 +334,16 @@ def main() -> int:
check=False,
)
assert stale_owner.returncode == 1, stale_owner.stdout
- assert payload(stale_owner)["error_code"] == "lease_cas_mismatch"
+ # Atomic claim handover rejects the previous actor before testing its lease key.
+ assert payload(stale_owner)["error_code"] == "owner_conflicts_with_claim", payload(stale_owner)
after_stale_owner = inspected_lease(registry_path, TODO_A)
assert after_stale_owner["lease"] == transferred["lease"], after_stale_owner
+ stale_key = lifecycle_cli(
+ registry_path, "renew", "codex-side-bypass", "turn-1", 3, check=False,
+ )
+ assert stale_key.returncode == 1, stale_key.stdout
+ assert payload(stale_key)["error_code"] == "lease_cas_mismatch", payload(stale_key)
+ assert inspected_lease(registry_path, TODO_A)["lease"] == transferred["lease"]
renewed_after_transfer = payload(
lifecycle_cli(
@@ -458,6 +469,9 @@ def main() -> int:
)
assert stale_completion.returncode == 1, stale_completion.stdout
assert payload(stale_completion)["error_code"] == "lease_cas_mismatch"
+ assert inspected_lease(registry_path, TODO_A)["lease"] == next_generation["lease"]
+ pending = payload(command(registry_path, "todo", "list", "--goal-id", GOAL_ID, "--todo-id", TODO_A))
+ assert pending["todo"]["status"] == "open", pending
completed = payload(
command(
@@ -480,7 +494,23 @@ def main() -> int:
)
)
assert completed["ok"] is True, completed
- assert completed["task_lease_fence"]["lease_epoch"] == 3, completed
+ current = payload(command(registry_path, "todo", "list", "--goal-id", GOAL_ID, "--todo-id", TODO_A))
+ assert current["todo"]["status"] == "done", current
+ final_lease = inspected_lease(registry_path, TODO_A)
+ assert final_lease["lease"]["lease_epoch"] == 3, final_lease
+ assert final_lease["lease"]["status"] == "released" and not final_lease["active"], final_lease
+ historical = payload(cli(registry_path, *transfer_args))
+ assert historical["original_receipt"] == transferred["original_receipt"], historical
+ assert historical["lease"]["status"] == "active", historical
+ assert inspected_lease(registry_path, TODO_A)["lease"] == final_lease["lease"]
+ replayed = payload(command(registry_path, "todo", "list", "--goal-id", GOAL_ID, "--todo-id", TODO_A))
+ assert replayed["todo"] == current["todo"], replayed
+
+
+def main() -> int:
+ os.environ["NODE_OPTIONS"] = os.environ.get("NODE_OPTIONS", "") + " --experimental-sqlite"
+ for provider in ("file", "sqlite"):
+ exercise_provider(provider)
print("task-lease-runtime-smoke ok")
return 0
diff --git a/examples/control_plane/todo-archive-completed-smoke.py b/examples/control_plane/todo-archive-completed-smoke.py
index 70d8923454..9b676cb372 100644
--- a/examples/control_plane/todo-archive-completed-smoke.py
+++ b/examples/control_plane/todo-archive-completed-smoke.py
@@ -17,6 +17,7 @@
completed_todo_archive_warning,
completed_todo_count,
)
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
from loopx.status import parse_active_state_todos # noqa: E402
GOAL_ID = "todo-archive-completed-goal"
@@ -47,6 +48,7 @@ def state_text() -> str:
+ "- [ ] [P2] Keep monitor work after completed items visible.\n\n"
"## Completed Work Archive\n\n"
"- [x] [P1] Previously archived completed work.\n"
+ " \n"
)
@@ -80,7 +82,7 @@ def user_role_state_text() -> str:
)
-def write_fixture(root: Path, *, text: str | None = None) -> tuple[Path, Path, Path]:
+def write_fixture(root: Path, *, text: str | None = None, import_authority: bool = True) -> tuple[Path, Path, Path]:
project = root / "project"
runtime = root / "runtime"
state_file = ".codex/goals/todo-archive-completed-goal/ACTIVE_GOAL_STATE.md"
@@ -119,6 +121,8 @@ def write_fixture(root: Path, *, text: str | None = None) -> tuple[Path, Path, P
+ "\n",
encoding="utf-8",
)
+ if import_authority:
+ import_fixture_authority(registry_path, GOAL_ID)
return registry_path, runtime, state_path
@@ -312,7 +316,7 @@ def main() -> int:
assert updated.count("[P1] Completed implementation lane 1.") == 1
assert updated.count("[P1] Completed implementation lane 2.") == 1
assert "excluded_agents=codex-reviewer" in updated
- assert "updated_at: 2026-01-01T00:00:00+00:00" not in updated
+ assert "updated_at: 2026-01-01T00:00:00+00:00" in updated
status_after = run_cli(registry_path, runtime, "status")
assert status_after["ok"] is True, status_after
@@ -438,8 +442,9 @@ def main() -> int:
assert lineage_status["ok"] is True, lineage_status
assert lineage_status["contract_errors"] == [], lineage_status
- state_path.write_text(
- archived_lineage
+ before_authority = run_cli(registry_path, runtime, "todo", "list", "--goal-id", GOAL_ID)
+ invalid_rows = (
+ ""
+ "- [ ] [P1] Contradictory archived work item.\n"
+ " \n"
@@ -448,11 +453,24 @@ def main() -> int:
+ "task_class=advancement_task excluded_agents=codex-reviewer -->\n"
+ "- [x] [P1] Empty-status archived work item.\n"
+ " \n",
- encoding="utf-8",
+ + "task_class=advancement_task excluded_agents=codex-reviewer -->\n"
)
+ state_path.write_text(archived_lineage + invalid_rows, encoding="utf-8")
invalid_status = run_cli(registry_path, runtime, "status", check=False)
+ assert invalid_status["ok"] is True, invalid_status
+ after_authority = run_cli(registry_path, runtime, "todo", "list", "--goal-id", GOAL_ID)
+ assert after_authority["todos"] == before_authority["todos"]
+ assert after_authority["authority_read"]["provider_revision"] == before_authority["authority_read"]["provider_revision"]
+ # These diagnostics still belong to explicit legacy source inspection,
+ # before import. Display text outside canonical regions grants no work.
+ source_registry, source_runtime, source_state = write_fixture(
+ Path(tmp) / "invalid-source", text=state_text() + invalid_rows,
+ import_authority=False,
+ )
+ before_source = source_state.read_bytes()
+ invalid_status = run_cli(source_registry, source_runtime, "status", check=False)
assert invalid_status["ok"] is False, invalid_status
+ assert source_state.read_bytes() == before_source
assert any(
"todo_archived_open" in item and "executor exclusions" in item
for item in invalid_status["contract_errors"]
diff --git a/examples/control_plane/todo-claim-visibility-lanes-smoke.py b/examples/control_plane/todo-claim-visibility-lanes-smoke.py
index 5b757ae8f4..6290287a5e 100644
--- a/examples/control_plane/todo-claim-visibility-lanes-smoke.py
+++ b/examples/control_plane/todo-claim-visibility-lanes-smoke.py
@@ -4,6 +4,7 @@
from __future__ import annotations
import sys
+import tempfile
from pathlib import Path
@@ -17,7 +18,9 @@
from loopx.control_plane.todos.quota_selection import project_quota_planning # noqa: E402
from loopx.control_plane.todos.contract import parse_todo_metadata_line # noqa: E402
from loopx.quota import build_quota_should_run # noqa: E402
-from loopx.todos import apply_todo_update_to_lines # noqa: E402
+from loopx.control_plane.testing.canary_harness import ( # noqa: E402
+ import_fixture_authority, run_json_cli, run_json_cli_result, write_fixture_registry,
+)
from work_lane_contract_fixtures import GOAL_ID, status_payload # noqa: E402
@@ -423,40 +426,45 @@ def assert_legacy_review_handoff_write_requires_explicit_repair() -> None:
"continuation_policy=review_handoff -->",
]
- for updates in (
- {"claimed_by": CURRENT_AGENT, "claim_only": True},
- {"note": "Keep this legacy review blocked."},
- ):
- lines = list(original_lines)
- try:
- apply_todo_update_to_lines(
- lines,
- todo_id="todo_legacy_write",
- role="agent",
- updated_at="2026-07-11T06:00:00+08:00",
- **updates,
- )
- except ValueError as exc:
- assert "repair it" in str(exc), exc
- else:
- raise AssertionError("legacy review writes must fail closed")
- assert lines == original_lines
-
- repaired_lines = list(original_lines)
- repaired = apply_todo_update_to_lines(
- repaired_lines,
- todo_id="todo_legacy_write",
- role="agent",
- continuation_policy="independent_handoff",
- excluded_agents=[CURRENT_AGENT],
- updated_at="2026-07-11T06:00:00+08:00",
- )
- assert repaired["continuation_policy"] == "independent_handoff", repaired
- assert repaired["excluded_agents"] == [CURRENT_AGENT], repaired
- repaired_text = "\n".join(repaired_lines)
- assert "continuation_policy=review_handoff" not in repaired_text
- assert "continuation_policy=independent_handoff" in repaired_text
- assert f"excluded_agents={CURRENT_AGENT}" in repaired_text
+ with tempfile.TemporaryDirectory(prefix="loopx-claim-repair-") as tmp:
+ root = Path(tmp).resolve()
+ state = root / "ACTIVE_GOAL_STATE.md"
+ state.write_text("\n".join(original_lines) + "\n", encoding="utf-8")
+ registry = root / ".loopx/registry.json"
+ write_fixture_registry(
+ project=root, runtime_root=root / "runtime", registry_path=registry,
+ goal_id=GOAL_ID, domain="claim-repair-fixture",
+ adapter_kind="generic_project_goal_v0", state_file=state.name,
+ registered_agents=[CURRENT_AGENT, OTHER_AGENT],
+ )
+ import_fixture_authority(registry, GOAL_ID)
+ before = state.read_bytes()
+ prefix = ("todo", "update", "--goal-id", GOAL_ID,
+ "--todo-id", "todo_legacy_write", "--agent-id", OTHER_AGENT)
+ revision = run_json_cli("todo", "list", "--goal-id", GOAL_ID,
+ registry_path=registry)["authority_read"]["provider_revision"]
+ claim = ("todo", "claim", "--goal-id", GOAL_ID,
+ "--todo-id", "todo_legacy_write", "--agent-id", CURRENT_AGENT,
+ "--claimed-by", CURRENT_AGENT)
+ for request in (claim, (*prefix, "--note", "Keep this legacy review blocked.")):
+ code, rejected = run_json_cli_result(*request, registry_path=registry)
+ assert code == 1, rejected
+ if request is claim:
+ assert rejected["error_code"] == "removed_continuation_policy", rejected
+ else:
+ assert "repair it" in rejected["error"], rejected
+ assert state.read_bytes() == before
+ assert run_json_cli("todo", "list", "--goal-id", GOAL_ID,
+ registry_path=registry)["authority_read"]["provider_revision"] == revision
+ repaired = run_json_cli(*prefix, "--continuation-policy", "independent_handoff",
+ "--excluded-agent", CURRENT_AGENT, registry_path=registry)
+ assert repaired["source_authority"] == "file_v0", repaired
+ current = run_json_cli("todo", "list", "--goal-id", GOAL_ID,
+ "--todo-id", "todo_legacy_write", registry_path=registry)["todo"]
+ assert current["continuation_policy"] == "independent_handoff", current
+ assert current["excluded_agents"] == [CURRENT_AGENT], current
+ assert not current.get("removed_continuation_policy"), current
+ assert "continuation_policy=review_handoff" not in state.read_text()
def assert_claim_visibility_lanes_split_current_other_and_task_class() -> None:
diff --git a/examples/control_plane/todo-cli-smoke.py b/examples/control_plane/todo-cli-smoke.py
index 822b5038ab..bb0e469043 100644
--- a/examples/control_plane/todo-cli-smoke.py
+++ b/examples/control_plane/todo-cli-smoke.py
@@ -13,6 +13,7 @@
sys.path.insert(0, str(REPO_ROOT))
from loopx.control_plane.testing.canary_harness import ( # noqa: E402
+ import_fixture_authority,
run_json_cli,
run_json_cli_result,
write_fixture_registry,
@@ -34,7 +35,7 @@
MONITOR_EXPIRES_AT = "2026-01-04T00:00:00+00:00"
-def write_fixture(root: Path, *, register_agents: bool = True) -> tuple[Path, Path]:
+def write_fixture(root: Path, *, register_agents: bool = True, folded_source: bool = False) -> tuple[Path, Path]:
project = root / "project"
runtime = root / "runtime"
state_file = project / ".codex" / "goals" / GOAL_ID / "ACTIVE_GOAL_STATE.md"
@@ -48,10 +49,20 @@ def write_fixture(root: Path, *, register_agents: bool = True) -> tuple[Path, Pa
"# Active Goal State\n\n"
"## Objective\n\n"
"Keep this fixture small.\n\n"
+ "## User Todo / Owner Review Reading Queue\n\n"
+ "## Agent Todo\n\n"
+ "## Completed Work Archive\n\n"
"## Next Action\n\n"
"- Choose the next bounded step.\n",
encoding="utf-8",
)
+ if folded_source:
+ text = state_file.read_text(encoding="utf-8")
+ state_file.write_text(text.replace("## Agent Todo\n\n",
+ "## Agent Todo\n\n- [ ] [P1] Summarize the read-only evidence after the user\n"
+ " checklist is done.\n"
+ " \n\n"),
+ encoding="utf-8")
write_fixture_registry(
project=project,
runtime_root=runtime,
@@ -64,6 +75,7 @@ def write_fixture(root: Path, *, register_agents: bool = True) -> tuple[Path, Pa
else None,
quota_allowed_slots=None,
)
+ import_fixture_authority(registry_path, GOAL_ID)
return registry_path, state_file
@@ -82,11 +94,27 @@ def run_cli_error(registry_path: Path, *args: str) -> dict:
return payload
+def assert_folded_source_import_preserves_identity() -> None:
+ with tempfile.TemporaryDirectory(prefix="loopx-folded-source-") as tmp:
+ registry, state = write_fixture(Path(tmp), folded_source=True)
+ imported = run_cli(registry, "todo", "list", "--goal-id", GOAL_ID,
+ "--todo-id", "todo_folded")["todo"]
+ assert imported["text"] == AGENT_TODO, imported
+ duplicate = run_cli(registry, "todo", "add", "--goal-id", GOAL_ID,
+ "--role", "agent", "--text", AGENT_TODO, "--task-class", "advancement_task")
+ assert duplicate["already_exists"] is True and duplicate["todo_id"] == "todo_folded", duplicate
+ listing = run_cli(registry, "todo", "list", "--goal-id", GOAL_ID)
+ assert len(listing["todos"]) == 1, listing
+ assert listing["authority_read"]["legacy_fallback_used"] is False, listing
+ assert state.read_text(encoding="utf-8").count("todo_id=todo_folded") == 1
+
+
def main() -> int:
+ assert_folded_source_import_preserves_identity()
with tempfile.TemporaryDirectory(prefix="loopx-todo-cli-smoke-") as tmp:
root = Path(tmp)
registry_path, state_file = write_fixture(root)
- legacy_registry_path, _ = write_fixture(root / "legacy", register_agents=False)
+ legacy_registry_path, legacy_state_file = write_fixture(root / "legacy", register_agents=False)
claim_state_registry_path, claim_state_file = write_fixture(root / "claim-state")
original = state_file.read_text(encoding="utf-8")
@@ -119,10 +147,8 @@ def main() -> int:
"--agent-id",
"codex-main-control",
)
- assert blocked_claim["error"] == (
- f"todo claim requires status=open; todo_id '{blocked_todo['todo_id']}' "
- "is status='blocked'"
- ), blocked_claim
+ assert blocked_claim["error_code"] == "todo_not_open", blocked_claim
+ assert blocked_claim["todo_status"] == "blocked", blocked_claim
assert claim_state_file.read_text(encoding="utf-8") == before_rejected_claim
bare_user_error = run_cli_error(
@@ -213,7 +239,8 @@ def main() -> int:
assert "## User Todo / Owner Review Reading Queue" in after_user, after_user
assert f"- [ ] {USER_TODO}" in after_user, after_user
assert after_user.index("## User Todo / Owner Review Reading Queue") < after_user.index("## Next Action")
- assert "updated_at: 2026-01-01T00:00:00+00:00" not in after_user
+ # Todo provider revisions leave the Goal narrative timestamp intact.
+ assert "updated_at: 2026-01-01T00:00:00+00:00" in after_user
duplicate = run_cli(
registry_path,
@@ -251,8 +278,8 @@ def main() -> int:
"codex-side-bypass",
)
assert scoped_gate["added"] is True, scoped_gate
- assert scoped_gate["agent_id"] == "codex-side-bypass", scoped_gate
- assert scoped_gate["blocks_agent"] == "codex-side-bypass", scoped_gate
+ assert scoped_gate["todo"]["bound_agent"] == "codex-side-bypass", scoped_gate
+ assert scoped_gate["todo"]["blocks_agent"] == "codex-side-bypass", scoped_gate
agent_payload = run_cli(
registry_path,
@@ -268,7 +295,7 @@ def main() -> int:
AGENT_TODO_NOTE,
)
assert agent_payload["added"] is True, agent_payload
- assert agent_payload["note"] == AGENT_TODO_NOTE, agent_payload
+ assert agent_payload["todo"]["note"] == AGENT_TODO_NOTE, agent_payload
added_fields = parse_active_state_todos(state_file.read_text(encoding="utf-8"))
assert added_fields["agent_todos"]["items"][0]["note"] == AGENT_TODO_NOTE, added_fields
legacy_agent = run_cli(
@@ -284,7 +311,8 @@ def main() -> int:
)
# Omit-note negative (GH-C97): an add without --note must not invent a note.
assert legacy_agent["added"] is True, legacy_agent
- assert not legacy_agent.get("note"), legacy_agent
+ assert not legacy_agent["todo"].get("note"), legacy_agent
+ before_legacy_claim = legacy_state_file.read_bytes()
legacy_claim = run_cli_error(
legacy_registry_path,
"todo",
@@ -296,15 +324,10 @@ def main() -> int:
"--claimed-by",
"codex-main-control",
)
- assert "has no coordination.registered_agents" in legacy_claim["error"], legacy_claim
- assert "Register this peer identity first" in legacy_claim["error"], legacy_claim
- assert "--registered-agent codex-main-control" in legacy_claim["error"], legacy_claim
- wrapped_agent_todo = "- [ ] [P1] Summarize the read-only evidence after the user\n checklist is done."
- state_file.write_text(
- state_file.read_text(encoding="utf-8").replace(f"- [ ] {AGENT_TODO}", wrapped_agent_todo),
- encoding="utf-8",
- )
- metadata_payload = run_cli(
+ assert legacy_claim["error_code"] == "actor_not_registered", legacy_claim
+ assert legacy_state_file.read_bytes() == before_legacy_claim
+ before_metadata = state_file.read_text(encoding="utf-8")
+ metadata_conflict = run_cli_error(
registry_path,
"todo",
"add",
@@ -331,18 +354,48 @@ def main() -> int:
"--required-decision-scope",
"direction:action:read_only_evidence_review",
)
- assert metadata_payload["added"] is False, metadata_payload
- assert metadata_payload["already_exists"] is True, metadata_payload
- assert metadata_payload["metadata_updated"] is True, metadata_payload
- assert metadata_payload["task_class"] == "advancement_task", metadata_payload
- assert metadata_payload["action_kind"] == "run_eval", metadata_payload
- assert metadata_payload["note"] == UPDATED_AGENT_TODO_NOTE, metadata_payload
+ assert metadata_conflict["error_code"] == "todo_semantic_duplicate_conflict", metadata_conflict
+ assert "use Todo update" in metadata_conflict["error"], metadata_conflict
+ assert state_file.read_text(encoding="utf-8") == before_metadata
+ metadata_payload = run_cli(
+ registry_path,
+ "todo",
+ "update",
+ "--goal-id",
+ GOAL_ID,
+ "--todo-id",
+ agent_payload["todo_id"],
+ "--agent-id",
+ "codex-main-control",
+ "--task-class",
+ "advancement_task",
+ "--action-kind",
+ "run_eval",
+ "--note",
+ UPDATED_AGENT_TODO_NOTE,
+ "--continuation-policy",
+ "independent_handoff",
+ "--required-capability",
+ "shell",
+ "--required-capability",
+ "benchmark_runner",
+ "--target-capability",
+ "benchmark_runner",
+ "--required-decision-scope",
+ "direction:action:read_only_evidence_review",
+ )
+ assert metadata_payload["status"] == "applied", metadata_payload
+ metadata_todo = run_cli(registry_path, "todo", "list", "--goal-id", GOAL_ID,
+ "--todo-id", agent_payload["todo_id"])["todo"]
+ assert metadata_todo["task_class"] == "advancement_task", metadata_payload
+ assert metadata_todo["action_kind"] == "run_eval", metadata_payload
+ assert metadata_todo["note"] == UPDATED_AGENT_TODO_NOTE, metadata_payload
assert (
- metadata_payload["continuation_policy"] == "independent_handoff"
+ metadata_todo["continuation_policy"] == "independent_handoff"
), metadata_payload
- assert metadata_payload["required_capabilities"] == ["shell", "benchmark_runner"], metadata_payload
- assert metadata_payload["target_capabilities"] == ["benchmark_runner"], metadata_payload
- assert metadata_payload["required_decision_scopes"] == [
+ assert metadata_todo["required_capabilities"] == ["shell", "benchmark_runner"], metadata_payload
+ assert metadata_todo["target_capabilities"] == ["benchmark_runner"], metadata_payload
+ assert metadata_todo["required_decision_scopes"] == [
{
"schema_version": "decision_scope_v0",
"kind": "direction",
@@ -352,10 +405,7 @@ def main() -> int:
], metadata_payload
after_metadata = state_file.read_text(encoding="utf-8")
assert after_metadata.count("- [ ] [P1] Summarize the read-only evidence after the user") == 1, after_metadata
- agent_block_start = after_metadata.index("- [ ] [P1] Summarize the read-only evidence after the user")
- agent_metadata_start = after_metadata.index("\n"
for index in range(1, ARCHIVED_DONE_TODOS + 1)
)
return (
@@ -273,11 +258,7 @@ def assert_pr_resume_repository_binding() -> None:
], ambiguous
-def main() -> int:
- assert_pr_resume_repository_binding()
- parsed = parse_active_state_todos(state_text(), rollout_events=[pr_merged_event()])
- assert_parseable_agent_todos(parsed["agent_todos"])
-
+def assert_lifecycle(provider: str) -> None:
with tempfile.TemporaryDirectory(prefix="loopx-todo-durability-") as tmp:
registry_path, runtime = write_fixture(Path(tmp))
status_payload = run_cli("status", registry_path=registry_path, runtime=runtime)
@@ -329,6 +310,26 @@ def main() -> int:
assert "completed_todo_archive_warning" not in guard, guard
first_todo_id = guard["agent_todo_summary"]["first_executable_items"][0]["todo_id"]
+ # Historical inspection above is read-only. Declare the fixture's
+ # existing claim owners before the reviewed import and normal writes.
+ registry = json.loads(registry_path.read_text(encoding='utf-8'))
+ registry['goals'][0]['coordination'] = {
+ 'agent_model': 'peer_v1',
+ 'registered_agents': ['fixture-agent', 'codex-product-capability',
+ 'codex-side-bypass', 'codex-reviewer', 'codex-builder'],
+ }
+ registry_path.write_text(json.dumps(registry), encoding='utf-8')
+ import_fixture_authority(registry_path, GOAL_ID, provider=provider)
+ original = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID)
+ assert original['source_authority'] == provider + '_v0'
+ assert original['decision_read_from_provider'] is True and original['legacy_fallback_used'] is False
+ assert len(original['todos']) == 5 + ARCHIVED_DONE_TODOS
+ assert sum(row['archive_state'] == 'archive' for row in original['todos']) == ARCHIVED_DONE_TODOS
+ native_guard = run_cli('quota', 'should-run', '--goal-id', GOAL_ID,
+ '--agent-id', 'fixture-agent', '--host-surface', 'generic_cli',
+ '--scheduler-owner', 'none', '--execution-mode', 'interactive',
+ registry_path=registry_path, runtime=runtime)
+ assert native_guard['selected_todo']['todo_id'] == first_todo_id
lifecycle_result = run_cli(
"todo",
"complete",
@@ -338,6 +339,7 @@ def main() -> int:
"agent",
"--todo-id",
first_todo_id,
+ '--agent-id', 'fixture-agent',
"--evidence",
"fixture completed",
"--next-agent-todo",
@@ -350,18 +352,35 @@ def main() -> int:
runtime=runtime,
)
next_todo = lifecycle_result["next_todos"][0]
- assert next_todo["todo"].startswith("[P1] Continue lifecycle"), lifecycle_result
+ canonical = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID)
+ rows = {row['todo_id']: row for row in canonical['todos']}
+ assert rows[next_todo['todo_id']]['text'].startswith('[P1] Continue lifecycle')
+ assert rows[first_todo_id]['status'] == 'done'
+ assert rows[first_todo_id]['successor_todo_ids'] == [next_todo['todo_id']]
+ assert len(canonical['todos']) == len(original['todos']) + 1
+ for row in original['todos']:
+ if row['todo_id'] != first_todo_id:
+ assert rows[row['todo_id']] == row
post_lifecycle_status = run_cli("status", registry_path=registry_path, runtime=runtime)
post_lifecycle_item = attention_item(post_lifecycle_status)
post_agent_todos = post_lifecycle_item["agent_todos"]
assert post_agent_todos["open_count"] == 2, post_agent_todos
+ # The summary retains its documented non-open count, including deferred.
assert post_agent_todos["done_count"] == 4, post_agent_todos
assert post_agent_todos["deferred_count"] == 2, post_agent_todos
assert post_agent_todos["first_open_items"][0]["priority"] == "P1", post_agent_todos
assert post_agent_todos["first_open_items"][0]["todo_id"] == next_todo["todo_id"], post_agent_todos
assert post_agent_todos["first_open_items"][0]["action_kind"] == "fixture_follow_up", post_agent_todos
+
+
+def main() -> int:
+ assert_pr_resume_repository_binding()
+ parsed = parse_active_state_todos(state_text(), rollout_events=[pr_merged_event()])
+ assert_parseable_agent_todos(parsed["agent_todos"])
+ for provider in ('file', 'sqlite'):
+ assert_lifecycle(provider)
print("todo-durability-fixture-smoke ok")
return 0
diff --git a/examples/control_plane/todo-lifecycle-cli-smoke.py b/examples/control_plane/todo-lifecycle-cli-smoke.py
index 7903069a89..830f8581d4 100644
--- a/examples/control_plane/todo-lifecycle-cli-smoke.py
+++ b/examples/control_plane/todo-lifecycle-cli-smoke.py
@@ -112,10 +112,10 @@ def assert_peer_monitor_no_followup() -> None:
"--no-follow-up",
)
assert side_monitor_completed["changed"] is True, side_monitor_completed
- assert side_monitor_completed["self_merged"] is False, (
+ assert side_monitor_completed["completion_policy"]["self_merged"] is False, (
side_monitor_completed
)
- assert side_monitor_completed["no_followup"] is True, side_monitor_completed
+ assert side_monitor_completed["completion_continuation"] == "no_followup", side_monitor_completed
monitor_item = next(
item for item in parsed_items(state_file) if item["todo_id"] == side_monitor_id
)
@@ -166,7 +166,7 @@ def assert_peer_monitor_no_followup() -> None:
"--no-follow-up",
)
assert write_monitor_completed["changed"] is True, write_monitor_completed
- assert write_monitor_completed["no_followup"] is True, write_monitor_completed
+ assert write_monitor_completed["completion_continuation"] == "no_followup", write_monitor_completed
write_monitor_item = next(
item
for item in parsed_items(state_file)
@@ -189,6 +189,8 @@ def assert_open_parent_successor_advisory() -> None:
"agent",
"--text",
"Split a bounded aggregate into explicit successor work.",
+ "--task-class",
+ "advancement_task",
"--claimed-by",
"codex-main-control",
)
@@ -202,6 +204,8 @@ def assert_open_parent_successor_advisory() -> None:
"agent",
"--text",
"Execute the first explicit successor slice.",
+ "--task-class",
+ "advancement_task",
"--claimed-by",
"codex-side-bypass",
)
@@ -219,16 +223,16 @@ def assert_open_parent_successor_advisory() -> None:
"--successor-todo-id",
successor["todo_id"],
)
- advisory = linked["parent_successor_advisory"]
- assert linked["status"] == "open", linked
- assert advisory["reason_code"] == (
- "open_parent_remains_runnable_after_successor_link"
- ), advisory
- assert advisory["successor_semantics"] == "lineage_only", advisory
- assert advisory["parent_remains_quota_runnable"] is True, advisory
- assert advisory["automatic_transition_applied"] is False, advisory
- assert advisory["authoring_decision_required"] is True, advisory
- assert advisory["successor_todo_ids"] == [successor["todo_id"]], advisory
+ assert linked["status"] == "applied", linked
+ parent_read = run_cli(registry_path, "todo", "list", "--goal-id", GOAL_ID,
+ "--todo-id", parent["todo_id"])["todo"]
+ assert parent_read["status"] == "open", parent_read
+ assert parent_read["successor_todo_ids"] == [successor["todo_id"]], parent_read
+ quota = run_cli(registry_path, "quota", "should-run", "--goal-id", GOAL_ID,
+ "--agent-id", "codex-main-control")
+ # Linking records lineage; only an explicit lifecycle command changes
+ # the parent's availability in the actual canonical quota consumer.
+ assert quota["agent_lane_next_action"]["todo_id"] == parent["todo_id"], quota
explicitly_deferred = run_cli(
registry_path,
@@ -247,8 +251,12 @@ def assert_open_parent_successor_advisory() -> None:
"--successor-todo-id",
successor["todo_id"],
)
- assert explicitly_deferred["status"] == "deferred", explicitly_deferred
- assert "parent_successor_advisory" not in explicitly_deferred, explicitly_deferred
+ assert explicitly_deferred["status"] == "applied", explicitly_deferred
+ deferred = run_cli(registry_path, "todo", "list", "--goal-id", GOAL_ID,
+ "--todo-id", parent["todo_id"])["todo"]
+ assert deferred["status"] == "deferred", deferred
+ assert deferred["resume_when"] == f"todo_done:{successor['todo_id']}", deferred
+
def assert_nonblocking_review_continuation() -> None:
@@ -427,8 +435,7 @@ def main() -> int:
assert archive_open["moved_count"] == 0, archive_open
assert any(item["todo_id"] == run_todo_id and not item["done"] for item in parsed_items(state_file))
- completed = run_cli(
- registry_path,
+ completion_args = (
"todo",
"complete",
"--goal-id",
@@ -448,8 +455,9 @@ def main() -> int:
"--next-action-kind",
"rebuild_score",
)
+ completed = run_cli(registry_path, *completion_args)
assert completed["changed"] is True, completed
- assert completed["next_todos"][0]["added"] is True, completed
+ assert completed["next_todos"][0]["status"] == "open", completed
rebuild_todo_id = completed["next_todos"][0]["todo_id"]
assert completed["successor_todo_ids"] == [rebuild_todo_id], completed
items = parsed_items(state_file)
@@ -519,7 +527,7 @@ def main() -> int:
"--self-merged",
)
assert side_self_merged["changed"] is True, side_self_merged
- assert side_self_merged["self_merged"] is True, side_self_merged
+ assert side_self_merged["completion_policy"]["self_merged"] is True, side_self_merged
assert side_self_merged["next_todos"] == [], side_self_merged
side_continuation_added = run_cli(
@@ -563,7 +571,7 @@ def main() -> int:
"contract_refine",
)
assert side_continuation_completed["changed"] is True, side_continuation_completed
- assert side_continuation_completed["self_merged"] is True, side_continuation_completed
+ assert side_continuation_completed["completion_policy"]["self_merged"] is True, side_continuation_completed
assert side_continuation_completed["next_todos"][0]["claimed_by"] == "codex-side-bypass", (
side_continuation_completed
)
@@ -639,7 +647,7 @@ def main() -> int:
"--next-excluded-agent",
"codex-side-bypass",
)
- assert "cannot also appear in next_excluded_agents" in (
+ assert "cannot also appear in excluded_agents" in (
side_review_claim_error["error"]
), side_review_claim_error
@@ -677,7 +685,7 @@ def main() -> int:
assert side_completed["changed"] is True, side_completed
review_todo_id = side_completed["next_todos"][0]["todo_id"]
assert side_completed["next_todos"][0]["claimed_by"] == "codex-main-control", side_completed
- assert side_completed["next_todos"][0]["blocks_agent"] is None, side_completed
+ assert side_completed["next_todos"][0].get("blocks_agent") is None, side_completed
assert side_completed["next_todos"][0]["excluded_agents"] == ["codex-side-bypass"], side_completed
assert side_completed["next_todos"][0]["task_repository"] == (
SUCCESSOR_REPOSITORY
@@ -706,24 +714,15 @@ def main() -> int:
assert review_item.get("updated_at"), review_item
assert side_completed["next_todos"][0].get("updated_at") == review_item["updated_at"], side_completed
- repeated = run_cli(
- registry_path,
- "todo",
- "complete",
- "--goal-id",
- GOAL_ID,
- "--todo-id",
- run_todo_id,
- "--agent-id",
- "codex-main-control",
- "--evidence",
- "fresh-repeat-result-ready",
- "--next-agent-todo",
- REBUILD_TODO,
- "--next-action-kind",
- "rebuild_score",
+ before_retry = state_file.read_bytes()
+ conflicting_retry = run_cli_error(
+ registry_path, *completion_args, "--next-action-kind", "different_action",
)
+ assert conflicting_retry["error_code"] == "coordination_operation_identity_mismatch", conflicting_retry
+ assert state_file.read_bytes() == before_retry
+ repeated = run_cli(registry_path, *completion_args)
assert repeated["changed"] is False, repeated
+ assert state_file.read_bytes() == before_retry
assert state_file.read_text(encoding="utf-8").count(REBUILD_TODO) == 1
supersede_claimed_by_error = run_cli_error(
diff --git a/examples/control_plane/todo-user-gate-scope-smoke.py b/examples/control_plane/todo-user-gate-scope-smoke.py
index c3e022c8b0..f2cf7ff69b 100644
--- a/examples/control_plane/todo-user-gate-scope-smoke.py
+++ b/examples/control_plane/todo-user-gate-scope-smoke.py
@@ -15,10 +15,13 @@
sys.path.insert(0, str(REPO_ROOT))
from loopx.contract import check_contract # noqa: E402
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
from loopx.control_plane.todos.todo_summary import active_state_todo_attention_item # noqa: E402
from loopx.quota import build_quota_should_run # noqa: E402
from loopx.status import collect_status, parse_active_state_todos # noqa: E402
-from loopx.todos import add_goal_todo, complete_goal_todo, update_goal_todo # noqa: E402
+from loopx.todos import ( # noqa: E402
+ add_goal_todo, complete_goal_todo, list_goal_todos, update_goal_todo,
+)
GOAL_ID = "user-gate-scope-smoke"
@@ -26,7 +29,7 @@
SIDE_AGENT = "codex-product-capability"
-def write_fixture(root: Path) -> tuple[Path, Path, Path]:
+def write_fixture(root: Path, *, import_authority: bool = True) -> tuple[Path, Path, Path]:
repo = root / "repo"
repo.mkdir()
state = repo / "ACTIVE_GOAL_STATE.md"
@@ -38,6 +41,8 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
"updated_at: 2026-06-26T00:00:00+00:00",
"---",
"",
+ "## User Todo",
+ "",
"## Agent Todo",
"",
]
@@ -68,6 +73,8 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
),
encoding="utf-8",
)
+ if import_authority:
+ import_fixture_authority(registry, GOAL_ID)
return repo, state, registry
@@ -164,6 +171,128 @@ def assert_user_action_does_not_drive_active_state_attention() -> None:
assert gate_item["waiting_on"] == "controller", gate_item
+
+def read_todo(registry: Path, todo_id: str) -> dict:
+ view = list_goal_todos(registry_path=registry, goal_id=GOAL_ID, todo_id=todo_id)
+ assert view["authority_read"]["source_authority"] == "file_v0", view
+ assert view["authority_read"]["legacy_fallback_used"] is False, view
+ return view["todo"]
+
+
+def assert_legacy_scope_diagnostics() -> None:
+ # Historical source validation remains readable before import. It cannot
+ # serve as a normal writer or alter an already canonical authority.
+ with tempfile.TemporaryDirectory(prefix="loopx-legacy-gate-diagnostics-") as tmp:
+ root = Path(tmp)
+ repo, state, registry = write_fixture(root, import_authority=False)
+ insert_before_agent_section(
+ state,
+ "- [ ] Approve an intentionally unscoped gate.\n"
+ " \n"
+ "- [ ] Track an unbound non-blocking user action.\n"
+ " \n"
+ "- [ ] Resolve a mismatched gate binding.\n"
+ f" \n"
+ "- [ ] Track an intentionally untyped user todo.\n"
+ " \n"
+ "- [ ] Approve a gate with an unregistered blocked agent.\n"
+ " \n"
+ "- [ ] Approve a gate with conflicting scope.\n"
+ f" ",
+ )
+ state.write_text(
+ state.read_text(encoding="utf-8")
+ + "\n- [ ] Repair a removed continuation value.\n"
+ + " \n"
+ + "- [ ] Repair removed agent gate routing.\n"
+ + f" \n"
+ + "- [ ] Repair an unknown executor exclusion.\n"
+ + " \n"
+ + "- [ ] Repair malformed executor exclusion metadata.\n"
+ + " \n",
+ encoding="utf-8",
+ )
+ original = state.read_bytes()
+ check = check_contract(
+ registry_path=registry, runtime_root_override=str(root / "runtime"),
+ scan_roots=[repo], limit=1,
+ )
+ assert check["ok"] is False, check
+ for todo_id, diagnostic in (
+ ("todo_unscoped_gate", "requires bound_agent"),
+ ("todo_unbound_action", "requires bound_agent"),
+ ("todo_mismatched_gate_binding", "must bind to blocks_agent"),
+ ("todo_untyped_user", "requires task_class"),
+ ("todo_bad_agent", "not registered"),
+ ("todo_both_scopes", "cannot set both"),
+ ("todo_removed_continuation", "removed continuation_policy"),
+ ("todo_removed_agent_gate", "removed blocks_agent routing"),
+ ("todo_unknown_exclusion", "unregistered agents"),
+ ("todo_malformed_exclusion", "malformed excluded_agents"),
+ ):
+ assert any(todo_id in error and diagnostic in error for error in check["errors"]), check
+ assert state.read_bytes() == original
+
+
+def assert_imported_scope_repairs() -> None:
+ with tempfile.TemporaryDirectory(prefix="loopx-imported-gate-repair-") as tmp:
+ root = Path(tmp)
+ repo, state, registry = write_fixture(root, import_authority=False)
+ insert_before_agent_section(
+ state,
+ "- [ ] Approve an intentionally unscoped gate.\n"
+ " \n"
+ "- [ ] Track an unbound non-blocking user action.\n"
+ " \n"
+ "- [ ] Resolve a mismatched gate binding.\n"
+ f" \n"
+ "- [ ] Track an intentionally untyped user todo.\n"
+ " ",
+ )
+ state.write_text(
+ state.read_text(encoding="utf-8")
+ + "\n- [ ] Repair removed agent gate routing.\n"
+ + f" \n",
+ encoding="utf-8",
+ )
+ import_fixture_authority(registry, GOAL_ID)
+ for todo_id, role, changes, expected in (
+ ("todo_unscoped_gate", "user", {"status": "done"}, {"status": "done"}),
+ ("todo_unbound_action", "user", {"bound_agent": SIDE_AGENT}, {"bound_agent": SIDE_AGENT}),
+ ("todo_mismatched_gate_binding", "user", {"bound_agent": SIDE_AGENT}, {"bound_agent": SIDE_AGENT}),
+ # The caller now declares the missing type/binding explicitly;
+ # closing old source data does not infer an approval or audience.
+ ("todo_untyped_user", "user", {"status": "done", "task_class": "user_action", "goal_bound": True},
+ {"status": "done", "task_class": "user_action", "goal_bound": True}),
+ ("todo_removed_agent_gate", "agent", {"clear_blocks_agent": True}, {"blocks_agent": None}),
+ ):
+ original = read_todo(registry, todo_id)
+ source = state.read_bytes()
+ preview = update_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo_id,
+ role=role, agent_id=PRIMARY_AGENT, dry_run=True, **changes,
+ )
+ assert preview["changed"] is True, preview
+ assert read_todo(registry, todo_id) == original
+ assert state.read_bytes() == source
+ changed = update_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo_id,
+ role=role, agent_id=PRIMARY_AGENT, **changes,
+ )
+ assert changed["changed"] is True, changed
+ current = read_todo(registry, todo_id)
+ assert current["text"] == original["text"], current
+ for key, value in expected.items():
+ assert current.get(key) == value, current
+ repaired_check = check_contract(
+ registry_path=registry, runtime_root_override=str(root / "runtime"),
+ scan_roots=[repo], limit=1,
+ )
+ assert repaired_check["ok"] is True, repaired_check
+
+
def main() -> int:
assert_user_action_does_not_drive_active_state_attention()
@@ -214,9 +343,10 @@ def main() -> int:
task_class="user_action",
agent_id=SIDE_AGENT,
)
+ user_action = {**user_action, **read_todo(registry, user_action["todo_id"])}
assert user_action["task_class"] == "user_action", user_action
assert user_action["bound_agent"] == SIDE_AGENT, user_action
- assert user_action["claimed_by"] is None, user_action
+ assert user_action.get("claimed_by") is None, user_action
goal_bound_action = add_goal_todo(
registry_path=registry,
@@ -227,7 +357,9 @@ def main() -> int:
goal_bound=True,
dry_run=True,
)
- assert goal_bound_action["goal_bound"] is True, goal_bound_action
+ assert goal_bound_action["status"] == "planned", goal_bound_action
+ assert goal_bound_action["changed"] is True, goal_bound_action
+ assert len(list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]) == 1
other_lane_only_status = collect_status(
registry_path=registry,
@@ -281,6 +413,7 @@ def main() -> int:
task_class="advancement_task",
claimed_by=SIDE_AGENT,
)
+ side_agent_todo = {**side_agent_todo, **read_todo(registry, side_agent_todo["todo_id"])}
assert side_agent_todo["claimed_by"] == SIDE_AGENT, side_agent_todo
blocked_agent_todo = add_goal_todo(
@@ -301,6 +434,9 @@ def main() -> int:
bound_agent=SIDE_AGENT,
unblocks_todo_id=blocked_agent_todo["todo_id"],
)
+ approval_before = read_todo(registry, approval["todo_id"])
+ target_before = read_todo(registry, blocked_agent_todo["todo_id"])
+ source_before = state.read_bytes()
assert_raises_message(
lambda: complete_goal_todo(
registry_path=registry,
@@ -311,8 +447,11 @@ def main() -> int:
evidence="wrong-lane response attempt",
dry_run=True,
),
- "response continuation is bound",
+ "bound_agent_mismatch",
)
+ assert read_todo(registry, approval["todo_id"]) == approval_before
+ assert read_todo(registry, blocked_agent_todo["todo_id"]) == target_before
+ assert state.read_bytes() == source_before
approval_completed = complete_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -441,6 +580,7 @@ def main() -> int:
task_class="user_gate",
agent_id=SIDE_AGENT,
)
+ scoped = {**scoped, **read_todo(registry, scoped["todo_id"])}
assert scoped["blocks_agent"] == SIDE_AGENT, scoped
assert scoped["bound_agent"] == SIDE_AGENT, scoped
assert f"blocks_agent={SIDE_AGENT}" in state.read_text(encoding="utf-8")
@@ -454,6 +594,7 @@ def main() -> int:
task_class="user_gate",
global_gate=True,
)
+ global_gate = {**global_gate, **read_todo(registry, global_gate["todo_id"])}
assert global_gate["global_gate"] is True, global_gate
assert global_gate["goal_bound"] is True, global_gate
assert "global_gate=true" in state.read_text(encoding="utf-8")
@@ -467,22 +608,6 @@ def main() -> int:
task_class="advancement_task",
claimed_by=PRIMARY_AGENT,
)
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=str(agent_todo["todo_id"]),
- role="agent",
- claimed_by=PRIMARY_AGENT,
- agent_id=PRIMARY_AGENT,
- evidence="fixture validation",
- next_user_todo="Approve publishing the release notes.",
- next_user_task_class="user_gate",
- )
- state_text = state.read_text(encoding="utf-8")
- assert "Approve publishing the release notes." in state_text, state_text
- assert f"blocks_agent={PRIMARY_AGENT}" in state_text, state_text
- assert f"bound_agent={PRIMARY_AGENT}" in state_text, state_text
-
assert_raises_message(
lambda: update_goal_todo(
registry_path=registry,
@@ -496,210 +621,62 @@ def main() -> int:
"global_gate is only valid for user_gate todos",
)
- clean_check = check_contract(
- registry_path=registry,
- runtime_root_override=str(root / "runtime"),
- scan_roots=[repo],
- limit=1,
- )
- assert clean_check["ok"] is True, clean_check
-
- insert_before_agent_section(
- state,
- "- [ ] Approve an intentionally unscoped gate.\n"
- " ",
- )
- bad_check = check_contract(
- registry_path=registry,
- runtime_root_override=str(root / "runtime"),
- scan_roots=[repo],
- limit=1,
- )
- assert bad_check["ok"] is False, bad_check
- assert any("todo_unscoped_gate" in item for item in bad_check["errors"]), bad_check
-
- closed = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id="todo_unscoped_gate",
- role="user",
- agent_id=PRIMARY_AGENT,
- status="done",
- )
- assert closed["changed"] is True, closed
- repaired_check = check_contract(
- registry_path=registry,
- runtime_root_override=str(root / "runtime"),
- scan_roots=[repo],
- limit=1,
- )
- assert repaired_check["ok"] is True, repaired_check
-
- insert_before_agent_section(
- state,
- "- [ ] Track an unbound non-blocking user action.\n"
- " ",
- )
- unbound_action_check = check_contract(
- registry_path=registry,
- runtime_root_override=str(root / "runtime"),
- scan_roots=[repo],
- limit=1,
- )
- assert unbound_action_check["ok"] is False, unbound_action_check
- assert any(
- "todo_unbound_action" in item and "requires bound_agent" in item
- for item in unbound_action_check["errors"]
- ), unbound_action_check
- rebound_action = update_goal_todo(
+ complete_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
- todo_id="todo_unbound_action",
- role="user",
+ todo_id=str(agent_todo["todo_id"]),
+ role="agent",
+ claimed_by=PRIMARY_AGENT,
agent_id=PRIMARY_AGENT,
- bound_agent=SIDE_AGENT,
- )
- assert rebound_action["bound_agent"] == SIDE_AGENT, rebound_action
- rebound_check = check_contract(
- registry_path=registry,
- runtime_root_override=str(root / "runtime"),
- scan_roots=[repo],
- limit=1,
+ evidence="fixture validation",
+ next_user_todo="Approve publishing the release notes.",
+ next_user_task_class="user_gate",
)
- assert rebound_check["ok"] is True, rebound_check
+ state_text = state.read_text(encoding="utf-8")
+ assert "Approve publishing the release notes." in state_text, state_text
+ assert f"blocks_agent={PRIMARY_AGENT}" in state_text, state_text
+ assert f"bound_agent={PRIMARY_AGENT}" in state_text, state_text
- insert_before_agent_section(
- state,
- "- [ ] Resolve a mismatched gate binding.\n"
- f" ",
- )
- mismatched_binding_check = check_contract(
- registry_path=registry,
- runtime_root_override=str(root / "runtime"),
- scan_roots=[repo],
- limit=1,
- )
- assert mismatched_binding_check["ok"] is False, mismatched_binding_check
- assert any(
- "todo_mismatched_gate_binding" in item
- and "must bind to blocks_agent" in item
- for item in mismatched_binding_check["errors"]
- ), mismatched_binding_check
- repaired_gate_binding = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id="todo_mismatched_gate_binding",
- role="user",
- agent_id=PRIMARY_AGENT,
- bound_agent=SIDE_AGENT,
+ goal_bound_action = add_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, role="user",
+ text="Track a goal-wide owner note.", task_class="user_action",
+ goal_bound=True,
)
- assert repaired_gate_binding["bound_agent"] == SIDE_AGENT, repaired_gate_binding
- repaired_binding_check = check_contract(
- registry_path=registry,
- runtime_root_override=str(root / "runtime"),
- scan_roots=[repo],
- limit=1,
+ goal_bound_readback = read_todo(registry, goal_bound_action["todo_id"])
+ assert goal_bound_readback["goal_bound"] is True, goal_bound_readback
+ assert goal_bound_readback.get("bound_agent") is None, goal_bound_readback
+ assert goal_bound_readback.get("global_gate") is not True, goal_bound_readback
+ complete_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID,
+ todo_id=goal_bound_action["todo_id"], role="user", agent_id=PRIMARY_AGENT,
+ evidence="fixture owner note acknowledged",
)
- assert repaired_binding_check["ok"] is True, repaired_binding_check
+ assert read_todo(registry, goal_bound_action["todo_id"])["status"] == "done"
- insert_before_agent_section(
- state,
- "- [ ] Track an intentionally untyped user todo.\n"
- " ",
- )
- untyped_check = check_contract(
+ clean_check = check_contract(
registry_path=registry,
runtime_root_override=str(root / "runtime"),
scan_roots=[repo],
limit=1,
)
- assert untyped_check["ok"] is False, untyped_check
- assert any("todo_untyped_user" in item and "requires task_class" in item for item in untyped_check["errors"]), untyped_check
-
- closed_untyped = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id="todo_untyped_user",
- role="user",
- agent_id=PRIMARY_AGENT,
- status="done",
- )
- assert closed_untyped["changed"] is True, closed_untyped
+ assert clean_check["ok"] is True, clean_check
+ original = state.read_bytes()
+ before = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)
insert_before_agent_section(
state,
- "- [ ] Approve a gate with an unregistered blocked agent.\n"
- " \n"
- "- [ ] Approve a gate with conflicting scope.\n"
- f" ",
- )
- invalid_scope_check = check_contract(
- registry_path=registry,
- runtime_root_override=str(root / "runtime"),
- scan_roots=[repo],
- limit=1,
- )
- assert invalid_scope_check["ok"] is False, invalid_scope_check
- assert any("todo_bad_agent" in item and "not registered" in item for item in invalid_scope_check["errors"]), invalid_scope_check
- assert any("todo_both_scopes" in item and "cannot set both" in item for item in invalid_scope_check["errors"]), invalid_scope_check
-
- state.write_text(
- state.read_text(encoding="utf-8")
- + "\n- [ ] Repair a removed continuation value.\n"
- + " \n"
- + "- [ ] Repair removed agent gate routing.\n"
- + f" \n"
- + "- [ ] Repair an unknown executor exclusion.\n"
- + " \n"
- + "- [ ] Repair malformed executor exclusion metadata.\n"
- + " \n",
- encoding="utf-8",
- )
- hard_cut_check = check_contract(
- registry_path=registry,
- runtime_root_override=str(root / "runtime"),
- scan_roots=[repo],
- limit=1,
- )
- assert hard_cut_check["ok"] is False, hard_cut_check
- assert any(
- "todo_removed_continuation" in item and "removed continuation_policy" in item
- for item in hard_cut_check["errors"]
- ), hard_cut_check
- assert any(
- "todo_removed_agent_gate" in item and "removed blocks_agent routing" in item
- for item in hard_cut_check["errors"]
- ), hard_cut_check
- assert any(
- "todo_unknown_exclusion" in item and "unregistered agents" in item
- for item in hard_cut_check["errors"]
- ), hard_cut_check
- assert any(
- "todo_malformed_exclusion" in item and "malformed excluded_agents" in item
- for item in hard_cut_check["errors"]
- ), hard_cut_check
-
- repaired_agent_gate = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id="todo_removed_agent_gate",
- role="agent",
- agent_id=PRIMARY_AGENT,
- clear_blocks_agent=True,
- )
- assert repaired_agent_gate["blocks_agent"] is None, repaired_agent_gate
- repaired_hard_cut_check = check_contract(
- registry_path=registry,
- runtime_root_override=str(root / "runtime"),
- scan_roots=[repo],
- limit=1,
- )
- assert not any(
- "todo_removed_agent_gate" in item
- for item in repaired_hard_cut_check["errors"]
- ), repaired_hard_cut_check
+ "- [ ] An injected projection cannot add a gate.\n"
+ f" ",
+ )
+ after = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)
+ assert after["todos"] == before["todos"], after
+ assert after["authority_read"]["provider_revision"] == before["authority_read"]["provider_revision"], after
+ assert state.read_bytes() != original
+ state.write_bytes(original)
+
+ assert_legacy_scope_diagnostics()
+ assert_imported_scope_repairs()
print("todo-user-gate-scope-smoke ok")
return 0
diff --git a/examples/control_plane/todo-write-correctness-smoke.py b/examples/control_plane/todo-write-correctness-smoke.py
index 9dceb28031..fe36b4d3f2 100644
--- a/examples/control_plane/todo-write-correctness-smoke.py
+++ b/examples/control_plane/todo-write-correctness-smoke.py
@@ -1,9 +1,8 @@
#!/usr/bin/env python3
-"""Smoke-test todo add/update dry-run write correctness packets."""
+"""Qualify canonical Todo previews, once-only commit, actor checks and CAS."""
from __future__ import annotations
-import hashlib
import json
import subprocess
import sys
@@ -15,11 +14,10 @@
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
-from loopx.control_plane.runtime.local_state_write_correctness import ( # noqa: E402
- shadow_validate_local_state_write_correctness_packet,
-)
from loopx.control_plane.testing.canary_harness import ( # noqa: E402
+ import_fixture_authority,
run_json_cli,
+ run_json_cli_result,
write_fixture_registry,
)
@@ -55,6 +53,7 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
registered_agents=("codex-main-control", AGENT_ID),
quota_allowed_slots=None,
)
+ import_fixture_authority(registry_path, GOAL_ID)
return registry_path, state_file, project
@@ -83,216 +82,70 @@ def run_cli(registry_path: Path, *args: str, as_json: bool = True) -> dict | str
return result.stdout
-def assert_packet(
- payload: dict,
- *,
- state_file: Path,
- project: Path,
- write_class: str,
- todo_id: str,
- claimed_by: str | None,
-) -> dict:
- packet = payload.get("local_state_write_correctness")
- assert isinstance(packet, dict), payload
- assert packet["schema_version"] == "local_state_write_correctness_v0", packet
- assert str(project) not in json.dumps(packet, ensure_ascii=False), packet
-
- intent = packet["write_intent"]
- assert intent["goal_id"] == GOAL_ID, packet
- assert intent["writer_id"] == "loopx.todo", packet
- assert intent["write_class"] == write_class, packet
- assert intent["target_refs"]["state_file_ref"] == "registry.goal.state_file", packet
- assert intent["target_refs"]["todo_id"] == todo_id, packet
- assert intent["expected_revision"]["value"] == "sha256:" + hashlib.sha256(
- state_file.read_text(encoding="utf-8").encode("utf-8")
- ).hexdigest(), packet
- assert intent["idempotency_key"].startswith(f"{GOAL_ID}:{write_class}:"), packet
-
- assert packet["lock_boundary"] == {
- "kind": "per_goal",
- "lock_key": f"goal:{GOAL_ID}",
- "narrower_lock_allowed": "per_todo_when_patch_is_single_todo_and_order_independent",
- }, packet
- assert packet["preview"]["mode"] == "dry_run", packet
- assert packet["preview"]["non_destructive"] is True, packet
- assert packet["preview"]["expected_write_scopes"] == ["active_state"], packet
- assert packet["apply_result"]["status"] == "preview_only", packet
- boundary = packet["projection"]["public_boundary"]
- assert boundary == {
- "raw_logs_copied": False,
- "private_paths_copied": False,
- "credentials_copied": False,
- "production_action_authorized": False,
- }, packet
-
- if claimed_by:
- assert intent["lease_ref"]["todo_id"] == todo_id, packet
- assert intent["lease_ref"]["claimed_by"] == claimed_by, packet
- assert packet["projection"]["lease_projection"] == {
- "todo_id": todo_id,
- "claimed_by": claimed_by,
- "lease_state": "preview_only",
- }, packet
- else:
- assert intent["lease_ref"] is None, packet
- assert packet["projection"]["lease_projection"] is None, packet
- return packet
-
-
def main() -> int:
with tempfile.TemporaryDirectory(prefix="loopx-todo-write-correctness-") as tmp:
- registry_path, state_file, project = write_fixture(Path(tmp))
- original = state_file.read_text(encoding="utf-8")
-
- add_dry_run = run_cli(
- registry_path,
- "todo",
- "add",
- "--goal-id",
- GOAL_ID,
- "--role",
- "agent",
- "--text",
- TODO_TEXT,
- "--claimed-by",
- AGENT_ID,
- "--dry-run",
- )
- add_dry_run_repeat = run_cli(
- registry_path,
- "todo",
- "add",
- "--goal-id",
- GOAL_ID,
- "--role",
- "agent",
- "--text",
- TODO_TEXT,
- "--claimed-by",
- AGENT_ID,
- "--dry-run",
- )
- assert add_dry_run["dry_run"] is True, add_dry_run
- assert add_dry_run["added"] is True, add_dry_run
- assert state_file.read_text(encoding="utf-8") == original
- add_packet = assert_packet(
- add_dry_run,
- state_file=state_file,
- project=project,
- write_class="todo_add",
- todo_id=add_dry_run["todo_id"],
- claimed_by=AGENT_ID,
- )
- clean_shadow = shadow_validate_local_state_write_correctness_packet(
- add_packet,
- current_state_text=state_file.read_text(encoding="utf-8"),
- observed_lease_ref=add_packet["write_intent"]["lease_ref"],
- )
- assert clean_shadow["apply_result"]["status"] == "preview_only", clean_shadow
- assert clean_shadow["apply_result"]["conflict"] is None, clean_shadow
-
- revision_conflict = shadow_validate_local_state_write_correctness_packet(
- add_packet,
- current_state_text=(
- state_file.read_text(encoding="utf-8")
- + "\n\n"
- ),
- )
- assert (
- revision_conflict["apply_result"]["status"] == "revision_conflict"
- ), revision_conflict
- revision_conflict_detail = revision_conflict["apply_result"]["conflict"]
- assert revision_conflict_detail["kind"] == "revision_conflict", revision_conflict
- assert revision_conflict_detail["expected_revision"] == add_packet["write_intent"][
- "expected_revision"
- ], revision_conflict
- assert revision_conflict_detail["current_revision"] != add_packet["write_intent"][
- "expected_revision"
- ], revision_conflict
- assert state_file.read_text(encoding="utf-8") == original
-
- foreign_lease = dict(add_packet["write_intent"]["lease_ref"])
- foreign_lease["claimed_by"] = "codex-main-control"
- foreign_lease["lease_id"] = f"lease_{add_dry_run['todo_id']}_codex_main_control"
- lease_conflict = shadow_validate_local_state_write_correctness_packet(
- add_packet,
- current_state_text=state_file.read_text(encoding="utf-8"),
- observed_lease_ref=foreign_lease,
- )
- assert lease_conflict["apply_result"]["status"] == "lease_conflict", lease_conflict
- lease_conflict_detail = lease_conflict["apply_result"]["conflict"]
- assert lease_conflict_detail["kind"] == "lease_conflict", lease_conflict
- assert lease_conflict_detail["expected_lease_ref"] == add_packet["write_intent"][
- "lease_ref"
- ], lease_conflict
- assert lease_conflict_detail["observed_lease_ref"] == foreign_lease, lease_conflict
- assert state_file.read_text(encoding="utf-8") == original
-
- repeat_packet = add_dry_run_repeat["local_state_write_correctness"]
- assert (
- add_packet["write_intent"]["idempotency_key"]
- == repeat_packet["write_intent"]["idempotency_key"]
- )
-
- add_markdown = run_cli(
- registry_path,
- "todo",
- "add",
- "--goal-id",
- GOAL_ID,
- "--role",
- "agent",
- "--text",
- TODO_TEXT,
- "--dry-run",
- as_json=False,
- )
- assert "## Local State Write Correctness" in add_markdown, add_markdown
- assert "status: `preview_only`" in add_markdown, add_markdown
-
- real_add = run_cli(
- registry_path,
- "todo",
- "add",
- "--goal-id",
- GOAL_ID,
- "--role",
- "agent",
- "--text",
- TODO_TEXT,
- )
- assert real_add["dry_run"] is False, real_add
- assert "local_state_write_correctness" not in real_add, real_add
-
- update_dry_run = run_cli(
- registry_path,
- "todo",
- "update",
- "--goal-id",
- GOAL_ID,
- "--todo-id",
- "todo_existing",
- "--agent-id",
- AGENT_ID,
- "--claimed-by",
- AGENT_ID,
- "--note",
- "Preview update without writing state.",
- "--dry-run",
- )
- assert update_dry_run["dry_run"] is True, update_dry_run
- assert update_dry_run["changed"] is True, update_dry_run
- assert_packet(
- update_dry_run,
- state_file=state_file,
- project=project,
- write_class="todo_update",
- todo_id="todo_existing",
- claimed_by=AGENT_ID,
- )
- assert "Preview update without writing state." not in state_file.read_text(
- encoding="utf-8"
- )
+ registry, state, project = write_fixture(Path(tmp))
+
+ def readback() -> dict:
+ return run_cli(registry, "todo", "list", "--goal-id", GOAL_ID)
+
+ def unchanged(before: dict, document: bytes) -> None:
+ after = readback()
+ assert after["todos"] == before["todos"], after
+ assert after["authority_read"] == before["authority_read"], after
+ assert state.read_bytes() == document
+
+ before, document = readback(), state.read_bytes()
+ create = ("todo", "add", "--goal-id", GOAL_ID, "--role", "agent",
+ "--text", TODO_TEXT, "--claimed-by", AGENT_ID,
+ "--operation-id", "preview-create")
+ first = run_cli(registry, *create, "--dry-run")
+ repeat = run_cli(registry, *create, "--dry-run")
+ for planned in (first, repeat):
+ assert planned["status"] == "planned" and planned["dry_run"] is True, planned
+ assert planned["source_authority"] == "file_v0", planned
+ assert planned["decision_read_from_provider"] is True, planned
+ assert planned["legacy_fallback_used"] is False, planned
+ assert str(project) not in json.dumps(planned), planned
+ assert first["todo_id"] == repeat["todo_id"]
+ assert first["operation_id"] == repeat["operation_id"] == "preview-create"
+ unchanged(before, document)
+ # A preview must not consume the original operation or mint its receipt.
+ committed = run_cli(registry, *create)
+ assert committed["todo_id"] == first["todo_id"] and committed["status"] == "applied", committed
+ replay = run_cli(registry, *create)
+ assert replay["provider_revision"] == committed["provider_revision"], replay
+ assert len([t for t in readback()["todos"] if t["todo_id"] == first["todo_id"]]) == 1
+
+ before, document = readback(), state.read_bytes()
+ update = ("todo", "update", "--goal-id", GOAL_ID, "--todo-id", first["todo_id"],
+ "--agent-id", AGENT_ID, "--note", "Preview update without writing state.")
+ planned = run_cli(registry, *update, "--dry-run")
+ assert planned["status"] == "planned", planned
+ unchanged(before, document)
+
+ # The provider revision, rather than editable Markdown bytes, owns CAS.
+ revision = before["authority_read"]["provider_revision"]
+ run_cli(registry, "todo", "add", "--goal-id", GOAL_ID, "--role", "agent",
+ "--text", "A later independently committed Todo.")
+ before, document = readback(), state.read_bytes()
+ for dry_run in (("--dry-run",), ()):
+ code, stale = run_json_cli_result(
+ *update, "--update-operation-id", "stale-preview",
+ "--update-expected-provider-revision", revision, *dry_run,
+ registry_path=registry)
+ assert code == 1 and stale["error_code"] == "provider_revision_mismatch", stale
+ unchanged(before, document)
+ code, foreign = run_json_cli_result(
+ "todo", "update", "--goal-id", GOAL_ID, "--todo-id", first["todo_id"],
+ "--agent-id", "codex-main-control", "--note", "Foreign actor edit.",
+ *dry_run, registry_path=registry)
+ assert code == 1 and "claimed_by" in foreign["error"], foreign
+ unchanged(before, document)
+ applied = run_cli(registry, *update)
+ assert applied["status"] == "applied", applied
+ todo = next(t for t in readback()["todos"] if t["todo_id"] == first["todo_id"])
+ assert todo["note"] == "Preview update without writing state.", todo
print("todo-write-correctness-smoke ok")
return 0
diff --git a/examples/control_plane/todo_lifecycle_fixtures.py b/examples/control_plane/todo_lifecycle_fixtures.py
index ddbfe4982b..9ebe75d5d5 100644
--- a/examples/control_plane/todo_lifecycle_fixtures.py
+++ b/examples/control_plane/todo_lifecycle_fixtures.py
@@ -15,6 +15,9 @@
sys.path.insert(0, str(REPO_ROOT))
from loopx.cli import main as loopx_cli_main # noqa: E402
+from loopx.control_plane.coordination.goal_storage_initialization import ( # noqa: E402
+ initialize_goal_storage_target,
+)
from loopx.control_plane.testing.canary_harness import ( # noqa: E402
runtime_root_from_registry,
write_fixture_registry,
@@ -39,6 +42,7 @@
def write_fixture(root: Path) -> tuple[Path, Path]:
+ root = root.resolve()
project = root / "project"
runtime = root / "runtime"
state_file = project / ".codex" / "goals" / GOAL_ID / "ACTIVE_GOAL_STATE.md"
@@ -52,8 +56,9 @@ def write_fixture(root: Path) -> tuple[Path, Path]:
"# Active Goal State\n\n"
"## Objective\n\n"
"Exercise todo lifecycle transitions.\n\n"
+ "## User Todo / Owner Review Reading Queue\n\n"
"## Agent Todo\n\n"
- "- [ ] Legacy monitor-only placeholder.\n",
+ "## Completed Work Archive\n",
encoding="utf-8",
)
write_fixture_registry(
@@ -65,7 +70,30 @@ def write_fixture(root: Path) -> tuple[Path, Path]:
adapter_kind="generic_project_goal_v0",
registered_agents=["codex-main-control", "codex-side-bypass"],
quota_allowed_slots=None,
+ extra_goal_fields={
+ "creation_operation_id": f"goal-create:{GOAL_ID}",
+ "coordination": {
+ "agent_model": "peer_v1",
+ "registered_agents": ["codex-main-control", "codex-side-bypass"],
+ "storage_target": {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": "file",
+ "handoff_mode": "soft_claim",
+ },
+ },
+ },
+ )
+ registry = json.loads(registry_path.read_text(encoding="utf-8"))
+ initialized = initialize_goal_storage_target(
+ runtime,
+ registry["goals"][0],
+ registry_path=registry_path,
)
+ if not isinstance(initialized, dict) or (
+ initialized.get("authority_initialized") is not True
+ or initialized.get("legacy_fallback_used") is not False
+ ):
+ raise AssertionError("fixture did not initialize canonical File authority")
return registry_path, state_file
@@ -182,7 +210,7 @@ def assert_configured_peer_handoff() -> None:
assert side_handoff_completed["next_todos"][0]["claimed_by"] == "codex-side-reviewer", (
side_handoff_completed
)
- assert side_handoff_completed["next_todos"][0]["blocks_agent"] is None, side_handoff_completed
+ assert side_handoff_completed["next_todos"][0].get("blocks_agent") is None, side_handoff_completed
assert side_handoff_completed["next_todos"][0]["unblocks_todo_id"] == side_handoff_added["todo_id"], (
side_handoff_completed
)
@@ -248,7 +276,7 @@ def assert_configured_peer_handoff() -> None:
assert review_successor["action_kind"] == "review_merge", explicit_primary_review
assert review_successor["continuation_policy"] == "independent_handoff", explicit_primary_review
assert review_successor["excluded_agents"] == ["codex-side-bypass"], explicit_primary_review
- assert review_successor["blocks_agent"] is None, explicit_primary_review
+ assert review_successor.get("blocks_agent") is None, explicit_primary_review
assert review_successor["unblocks_todo_id"] == review_source_added["todo_id"], explicit_primary_review
review_successor_item = next(
item for item in parsed_items(state_file) if item["todo_id"] == review_successor["todo_id"]
@@ -301,7 +329,7 @@ def assert_configured_peer_handoff() -> None:
"--next-excluded-agent",
"codex-side-bypass",
)
- assert "cannot also appear in next_excluded_agents" in (
+ assert "cannot also appear in excluded_agents" in (
same_agent_handoff["error"]
), same_agent_handoff
@@ -358,7 +386,7 @@ def assert_configured_peer_handoff() -> None:
assert profile_handoff_completed["next_todos"][0]["claimed_by"] == "codex-main-control", (
profile_handoff_completed
)
- assert profile_handoff_completed["next_todos"][0]["blocks_agent"] is None, profile_handoff_completed
+ assert profile_handoff_completed["next_todos"][0].get("blocks_agent") is None, profile_handoff_completed
assert profile_handoff_completed["next_todos"][0]["unblocks_todo_id"] == profile_handoff_added["todo_id"], (
profile_handoff_completed
)
@@ -510,7 +538,7 @@ def assert_complete_links_existing_successor() -> None:
successor_id,
)
assert completed["changed"] is True, completed
- assert completed["self_merged"] is True, completed
+ assert completed["completion_policy"]["self_merged"] is True, completed
assert completed["next_todos"] == [], completed
assert completed["successor_todo_ids"] == [successor_id], completed
@@ -571,7 +599,7 @@ def assert_same_title_completion_creates_fresh_successor() -> None:
"state_machine_canary_refactor",
)
assert completed["changed"] is True, completed
- assert completed["next_todos"][0]["added"] is True, completed
+ assert completed["next_todos"][0]["status"] == "open", completed
successor_id = completed["next_todos"][0]["todo_id"]
assert successor_id != added["todo_id"], completed
assert completed["next_todos"][0]["unblocks_todo_id"] == added["todo_id"], completed
diff --git a/examples/custom-runtime-minimal-cli-turn-smoke.py b/examples/custom-runtime-minimal-cli-turn-smoke.py
index 76c73ba995..26b42791e2 100755
--- a/examples/custom-runtime-minimal-cli-turn-smoke.py
+++ b/examples/custom-runtime-minimal-cli-turn-smoke.py
@@ -24,6 +24,7 @@
GOAL_ID = "custom-runtime-min"
AGENT_ID = "custom-host-agent"
+TURN_ID = "custom-runtime-fixture-turn"
MARKER_NAME = "public-marker.txt"
MARKER_BODY = "ok-from-custom-runtime\n"
ADVANCED_TURN_SMOKE = (
@@ -106,8 +107,10 @@ def write_project_fixture(root: Path) -> tuple[Path, Path, Path]:
return project, registry_path, state_file
-def assert_mainstream_cli_turn() -> None:
+def assert_mainstream_cli_turn(provider: str) -> None:
from loopx.status import parse_active_state_todos
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+ from loopx.control_plane.testing.canary_harness import import_fixture_authority
assert WAKE_TRIGGER, "host must own a wake trigger"
assert "quota should-run" in REENTRY_INSTRUCTION
@@ -116,6 +119,7 @@ def assert_mainstream_cli_turn() -> None:
with tempfile.TemporaryDirectory(prefix="loopx-custom-runtime-min-") as tmp:
project, registry_path, state_file = write_project_fixture(Path(tmp))
+ import_fixture_authority(registry_path, GOAL_ID, provider=provider)
status = run_cli(registry_path, "status", "--scan-root", str(project))
assert status.get("ok") is True, status
@@ -160,6 +164,8 @@ def assert_mainstream_cli_turn() -> None:
AGENT_ID,
"--runtime-profile",
"generic_cli",
+ "--todo-id", todo_id,
+ "--turn-instance-id", TURN_ID,
"--scan-path",
str(project),
)
@@ -184,12 +190,13 @@ def assert_mainstream_cli_turn() -> None:
AGENT_ID,
"--agent-id",
AGENT_ID,
+ "--turn-instance-id", TURN_ID,
"--evidence",
"public marker written under project root",
- "--no-follow-up",
)
assert completed.get("changed") is True, completed
- assert completed.get("no_followup") is True, completed
+ assert completed["completion_continuation"] == "active_goal", completed
+ assert completed["successor_todo_ids"] == [], completed
summary = parse_active_state_todos(state_file.read_text(encoding="utf-8"))
item = next(
@@ -198,12 +205,33 @@ def assert_mainstream_cli_turn() -> None:
if row.get("todo_id") == todo_id
)
assert item.get("done") is True, item
+ canonical = read_canonical_todos_if_promoted(
+ runtime_root=Path(tmp) / "runtime", goal_id=GOAL_ID,
+ )
+ stored = next(row for row in canonical["todos"] if row["todo_id"] == todo_id)
+ assert stored["status"] == "done" and stored["claimed_by"] == AGENT_ID, stored
refreshed = run_cli(
registry_path,
"refresh-state",
"--goal-id",
GOAL_ID,
+ "--agent-id", AGENT_ID,
+ "--agent-lane", AGENT_ID,
+ "--progress-scope", "agent_lane",
+ "--todo-id", todo_id,
+ "--turn-instance-id", TURN_ID,
+ "--classification", "validated_progress",
+ "--delivery-batch-scale", "multi_surface",
+ "--delivery-outcome", "outcome_progress",
+ "--delivery-boundary", "in_flight_continuation",
+ "--progress-result-class", "advanced",
+ "--progress-surface-id", "custom-runtime-cli",
+ "--progress-work-item-id", todo_id,
+ "--progress-hypothesis-id", "marker-terminal-single-debit",
+ "--progress-probe-kind", "real-cli-artifact-readback",
+ "--progress-coverage-scope-id", "canonical-cli-turn",
+ "--progress-evidence-id", "artifact:public-marker",
"--no-global-sync",
)
assert refreshed.get("ok") is True, refreshed
@@ -217,10 +245,12 @@ def assert_mainstream_cli_turn() -> None:
GOAL_ID,
"--agent-id",
AGENT_ID,
+ "--todo-id", todo_id,
+ "--turn-instance-id", TURN_ID,
"--slots",
"1",
"--source",
- "controller",
+ "heartbeat",
"--execute",
"--scan-path",
str(project),
@@ -230,10 +260,25 @@ def assert_mainstream_cli_turn() -> None:
after = spent["quota_event"]["after"]
assert after["spent_slots"] == 1, spent
assert after["allowed_slots"] == 5, spent
+ closed = run_cli(
+ registry_path, "todo", "complete", "--goal-id", GOAL_ID,
+ "--todo-id", todo_id, "--claimed-by", AGENT_ID, "--agent-id", AGENT_ID,
+ "--turn-instance-id", TURN_ID,
+ "--evidence", "public marker written under project root", "--no-follow-up",
+ )
+ assert closed["completion_continuation"] == "no_followup", closed
+ replay = run_cli(
+ registry_path, "quota", "spend-slot", "--goal-id", GOAL_ID,
+ "--agent-id", AGENT_ID, "--todo-id", todo_id,
+ "--turn-instance-id", TURN_ID, "--slots", "1", "--source", "heartbeat",
+ "--execute", "--scan-path", str(project),
+ )
+ assert replay["appended"] is False and replay["idempotent_replay"] is True, replay
def main() -> int:
- assert_mainstream_cli_turn()
+ for provider in ("file", "sqlite"):
+ assert_mainstream_cli_turn(provider)
print("custom-runtime-minimal-cli-turn-smoke ok")
return 0
diff --git a/examples/dreaming-proposal-decision-smoke.py b/examples/dreaming-proposal-decision-smoke.py
index f368457487..f12326dd49 100644
--- a/examples/dreaming-proposal-decision-smoke.py
+++ b/examples/dreaming-proposal-decision-smoke.py
@@ -4,36 +4,24 @@
from __future__ import annotations
import json
-import subprocess
import sys
import tempfile
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
+if str(REPO_ROOT) not in sys.path:
+ sys.path.insert(0, str(REPO_ROOT))
+
+from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted # noqa: E402
+from loopx.control_plane.testing.canary_harness import import_fixture_authority, run_json_cli # noqa: E402
+
GOAL_ID = "dreaming-decision-fixture"
def run_cli(*args: str, registry_path: Path, runtime: Path) -> dict:
- result = subprocess.run(
- [
- sys.executable,
- "-m",
- "loopx.cli",
- "--registry",
- str(registry_path),
- "--runtime-root",
- str(runtime),
- "--format",
- "json",
- *args,
- ],
- cwd=REPO_ROOT,
- check=True,
- capture_output=True,
- text=True,
- )
- return json.loads(result.stdout)
+ return run_json_cli(*args, registry_path=registry_path, runtime_root=runtime,
+ cwd=REPO_ROOT, include_returncode=False)
def append_recorded_proposal(
@@ -86,7 +74,7 @@ def append_recorded_proposal(
f.write(json.dumps(record, sort_keys=True) + "\n")
-def write_fixture(root: Path) -> tuple[Path, Path, Path, Path]:
+def write_fixture(root: Path, provider: str) -> tuple[Path, Path, Path, Path]:
project = root / "project"
runtime = root / "runtime"
state_file = f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md"
@@ -139,6 +127,7 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path, Path]:
+ "\n",
encoding="utf-8",
)
+ import_fixture_authority(registry_path, GOAL_ID, provider=provider)
append_recorded_proposal(
runs_dir,
generated_at="2026-01-01T00:00:00+00:00",
@@ -162,9 +151,10 @@ def assert_no_quota_spend(runtime: Path) -> None:
assert "quota_slot_spent" not in text, text
-def main() -> int:
+def assert_decisions(provider: str) -> None:
with tempfile.TemporaryDirectory(prefix="loopx-dreaming-decision-") as tmp:
- registry_path, runtime, state_path, index_path = write_fixture(Path(tmp))
+ registry_path, runtime, state_path, index_path = write_fixture(Path(tmp), provider)
+ original = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID)
approve = run_cli(
"dreaming",
@@ -200,6 +190,15 @@ def main() -> int:
assert approve["side_effects"]["runtime_history_appended"] is True, approve
assert approve["side_effects"]["quota_spent"] is False, approve
assert_no_quota_spend(runtime)
+ approved = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID)
+ assert approved['source_authority'] == provider + '_v0'
+ assert approved['decision_read_from_provider'] is True and approved['legacy_fallback_used'] is False
+ assert len(approved['todos']) == len(original['todos']) + 1
+ promoted = next(row for row in approved['todos'] if row['todo_id'] == todo_id)
+ assert promoted['text'] == '[P1] Implement the approved dreaming follow-up as normal delivery.'
+ assert promoted['status'] == 'open' and promoted['task_class'] == 'advancement_task'
+ original_ids = {row['todo_id'] for row in original['todos']}
+ assert [row for row in approved['todos'] if row['todo_id'] in original_ids] == original['todos']
before_defer_state = state_path.read_text(encoding="utf-8")
before_defer_index = index_path.read_text(encoding="utf-8")
@@ -228,6 +227,7 @@ def main() -> int:
assert defer["side_effects"]["runtime_history_appended"] is True, defer
assert defer["side_effects"]["quota_spent"] is False, defer
assert state_path.read_text(encoding="utf-8") == before_defer_state
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID) == approved
assert index_path.read_text(encoding="utf-8") != before_defer_index
assert_no_quota_spend(runtime)
@@ -257,6 +257,12 @@ def main() -> int:
assert reject["side_effects"]["quota_spent"] is False, reject
assert state_path.read_text(encoding="utf-8") == before_reject_state
assert index_path.read_text(encoding="utf-8") == before_reject_index
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID) == approved
+
+
+def main() -> int:
+ for provider in ('file', 'sqlite'):
+ assert_decisions(provider)
print("dreaming-proposal-decision-smoke ok")
return 0
diff --git a/examples/explore-todo-result-node-linkage-smoke.py b/examples/explore-todo-result-node-linkage-smoke.py
index 1df43c0a6d..e7812529e3 100644
--- a/examples/explore-todo-result-node-linkage-smoke.py
+++ b/examples/explore-todo-result-node-linkage-smoke.py
@@ -18,6 +18,7 @@
from loopx.capabilities.explore.todo_branch_plan import ( # noqa: E402
build_explore_todo_branch_plan,
)
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
from loopx.control_plane.todos.contract import format_todo_metadata_line # noqa: E402
@@ -51,7 +52,7 @@ def run_loopx(registry: Path, *args: str) -> dict[str, object]:
return json.loads(completed.stdout)
-def write_fixture(root: Path) -> tuple[Path, Path]:
+def write_fixture(root: Path, provider: str) -> tuple[Path, Path]:
project = root / "project"
project.mkdir()
state_file = project / "ACTIVE_GOAL_STATE.md"
@@ -84,10 +85,11 @@ def write_fixture(root: Path) -> tuple[Path, Path]:
),
encoding="utf-8",
)
+ import_fixture_authority(registry, GOAL_ID, provider=provider)
return registry, state_file
-def check_todo_lifecycle() -> None:
+def check_todo_lifecycle(provider: str) -> None:
try:
format_todo_metadata_line(
todo_id="todo_invalid123",
@@ -99,7 +101,7 @@ def check_todo_lifecycle() -> None:
raise AssertionError("private path-shaped Explore node refs must fail closed")
with tempfile.TemporaryDirectory(prefix="loopx-explore-todo-link-") as tmp:
- registry, state_file = write_fixture(Path(tmp))
+ registry, state_file = write_fixture(Path(tmp), provider)
added = run_loopx(
registry,
"todo",
@@ -118,7 +120,7 @@ def check_todo_lifecycle() -> None:
"node_dead",
)
todo_id = str(added["todo_id"])
- assert added["explore_result_node_refs"] == ["node_live", "node_dead"], added
+ assert added["todo"]["explore_result_node_refs"] == ["node_live", "node_dead"], added
listed = run_loopx(
registry,
@@ -148,6 +150,10 @@ def check_todo_lifecycle() -> None:
"--no-follow-up",
)
assert completed["status"] == "done", completed
+ terminal = run_loopx(registry, "todo", "list", "--goal-id", GOAL_ID,
+ "--todo-id", todo_id)["todo"]
+ assert terminal["status"] == "done"
+ assert terminal["explore_result_node_refs"] == ["node_live", "node_dead"]
state_text = state_file.read_text(encoding="utf-8")
assert "explore_result_node_refs=node_live%2Cnode_dead" in state_text, state_text
@@ -165,6 +171,11 @@ def check_todo_lifecycle() -> None:
assert "Completed Work Archive" in archived, archived
assert "explore_result_node_refs=node_live%2Cnode_dead" in archived, archived
+ archived_item = run_loopx(registry, "todo", "list", "--goal-id", GOAL_ID,
+ "--todo-id", todo_id)["todo"]
+ assert archived_item["archive_state"] == "archive", archived_item
+ assert archived_item["explore_result_node_refs"] == ["node_live", "node_dead"]
+
second = run_loopx(
registry,
"todo",
@@ -197,7 +208,7 @@ def check_todo_lifecycle() -> None:
"--todo-id",
str(second["todo_id"]),
)["todo"]
- assert "explore_result_node_refs" not in cleared, cleared
+ assert cleared["explore_result_node_refs"] == [], cleared
def check_planner_diagnostics() -> None:
@@ -283,7 +294,8 @@ def check_planner_diagnostics() -> None:
def main() -> int:
- check_todo_lifecycle()
+ for provider in ("file", "sqlite"):
+ check_todo_lifecycle(provider)
check_planner_diagnostics()
print("explore-todo-result-node-linkage-smoke ok")
return 0
diff --git a/examples/frontstage-pages-workflow-smoke.py b/examples/frontstage-pages-workflow-smoke.py
index 20947cb22a..35eedb2b9a 100644
--- a/examples/frontstage-pages-workflow-smoke.py
+++ b/examples/frontstage-pages-workflow-smoke.py
@@ -22,6 +22,12 @@ def assert_absent(text: str, needle: str) -> None:
raise AssertionError(f"workflow must not reference {needle!r}")
+def step_source(text: str, name: str) -> str:
+ marker = f" - name: {name}\n"
+ assert text.count(marker) == 1, name
+ return text.split(marker, 1)[1].split("\n - ", 1)[0]
+
+
def _extract_paths_block(text: str, event: str) -> str:
"""Extract the paths list under a given event (pull_request or push)."""
marker = f"\n {event}:"
@@ -163,6 +169,11 @@ def main() -> int:
"npm run export:frontstage-share -- --base /loopx/ --out-dir ../../../output/frontstage-pages",
}, step
assert "permissions" not in workflow["jobs"]["build"], "build must not elevate token permissions"
+ stargazers = step_source(text, "Fetch complete GitHub stargazer history")
+ assert_contains(stargazers, "if: github.event_name != 'pull_request'")
+ assert_contains(stargazers, "GH_TOKEN: ${{ secrets.STAR_HISTORY_READ_TOKEN }}")
+ assert_contains(stargazers, "scripts/fetch-github-stargazers.sh")
+ assert_absent(stargazers, "GH_TOKEN: ${{ github.token }}")
# Detaching a non-published product must preserve its own qualification lane.
python_tests = (WORKFLOW.parent / "python-tests.yml").read_text(encoding="utf-8")
diff --git a/examples/full-public-smokes-workflow-smoke.py b/examples/full-public-smokes-workflow-smoke.py
index 486a68bf21..ca3c576675 100644
--- a/examples/full-public-smokes-workflow-smoke.py
+++ b/examples/full-public-smokes-workflow-smoke.py
@@ -49,7 +49,7 @@ def main() -> int:
"--jobs \"${SMOKE_JOBS}\"",
"--no-execute",
"--json",
- 'python -m pip install --disable-pip-version-check "jsonschema>=4.23,<5"',
+ "python -m pip install --disable-pip-version-check --editable '.[test]'",
"actions/upload-artifact@",
"$RUNNER_TEMP/loopx-smoke-results/full-public-shard-${{ matrix.shard }}.json",
"${{ runner.temp }}/loopx-smoke-results/full-public-shard-${{ matrix.shard }}.json",
diff --git a/examples/install-local-smoke.py b/examples/install-local-smoke.py
index 18779bb484..18d3aa4444 100644
--- a/examples/install-local-smoke.py
+++ b/examples/install-local-smoke.py
@@ -834,6 +834,7 @@ def main() -> int:
)
assert "```sh\nLOOPX_TURN=\n" in payload["task_body"], payload
assert "not a command-prefix assignment" in payload["task_body"], payload
+ assert "obey projected replan/stop, never wake counts" in payload["task_body"], payload
assert "re-enter guard." in payload["task_body"], payload
assert "2 stalls->replan" not in payload["task_body"], payload
assert "Exact monitor settlement=no refresh/spend" in payload["task_body"], payload
diff --git a/examples/issue-fix-maintainer-correction-smoke.py b/examples/issue-fix-maintainer-correction-smoke.py
index 9ef6bb1079..19a53ab67b 100644
--- a/examples/issue-fix-maintainer-correction-smoke.py
+++ b/examples/issue-fix-maintainer-correction-smoke.py
@@ -18,6 +18,9 @@
normalise_issue_fix_maintainer_correction_input,
)
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
+from loopx.todos import list_goal_todos # noqa: E402
+
GOAL_ID = "issue-fix-maintainer-correction-smoke"
AGENT_ID = "issue-fix-agent"
@@ -43,7 +46,7 @@ def run_cli(registry: Path, args: list[str]) -> dict[str, object]:
return json.loads(result.stdout)
-def write_fixture(root: Path) -> tuple[Path, Path, Path]:
+def write_fixture(root: Path, provider: str) -> tuple[Path, Path, Path]:
project = root / "project"
project.mkdir()
state_file = project / "ACTIVE_GOAL_STATE.md"
@@ -57,6 +60,7 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
registry.write_text(
json.dumps(
{
+ "common_runtime_root": str(root / "runtime"),
"goals": [
{
"id": GOAL_ID,
@@ -72,6 +76,7 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
),
encoding="utf-8",
)
+ import_fixture_authority(registry, GOAL_ID, provider=provider)
metadata = root / "pr.json"
metadata.write_text(
json.dumps(
@@ -107,10 +112,10 @@ def base_correction(**overrides: object) -> dict[str, object]:
return payload
-def main() -> int:
+def check_corrections(provider: str) -> None:
with tempfile.TemporaryDirectory(prefix="loopx-maintainer-correction-") as tmpdir:
root = Path(tmpdir)
- project, registry, metadata = write_fixture(root)
+ project, registry, metadata = write_fixture(root, provider)
correction = correction_file(root, "actionable.json", base_correction())
common = [
"issue-fix",
@@ -237,6 +242,14 @@ def main() -> int:
assert quiet["todo_write"]["skip_reason"] == "unchanged_monitor_quiet", quiet
assert quiet["todo_write_performed"] is False, quiet
+ native = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)
+ assert native["authority_read"]["decision_read_from_provider"] is True, native
+ assert native["authority_read"]["legacy_fallback_used"] is False, native
+ patches = [item for item in native["todos"]
+ if item.get("action_kind") == "issue_fix_maintainer_correction_patch"]
+ assert len(patches) == 1, patches
+ assert patches[0]["claimed_by"] == AGENT_ID, patches
+
for unsafe in (
base_correction(raw_comment_body="raw body must not enter the contract"),
base_correction(source_ref="https://localhost/reviews/42"),
@@ -250,6 +263,11 @@ def main() -> int:
else:
raise AssertionError(f"unsafe correction input was accepted: {unsafe}")
+
+
+def main() -> int:
+ for provider in ("file", "sqlite"):
+ check_corrections(provider)
print("issue-fix-maintainer-correction-smoke: ok")
return 0
diff --git a/examples/issue-fix-pr-lifecycle-smoke.py b/examples/issue-fix-pr-lifecycle-smoke.py
index 854faf692b..e47bf3feae 100644
--- a/examples/issue-fix-pr-lifecycle-smoke.py
+++ b/examples/issue-fix-pr-lifecycle-smoke.py
@@ -21,6 +21,8 @@
ISSUE_FIX_PR_LIFECYCLE_MONITOR_SCHEMA_VERSION,
build_issue_fix_pr_lifecycle_monitor_packet,
)
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
+from loopx.todos import list_goal_todos # noqa: E402
from loopx.domain_packs.issue_fix import ( # noqa: E402
default_issue_fix_domain_state_ledger_path,
issue_fix_pr_lifecycle_ledger_key,
@@ -122,7 +124,7 @@ def assert_packet_shape(packet: dict[str, Any]) -> None:
assert_public_safe(packet)
-def main() -> int:
+def check_lifecycle(provider: str) -> None:
merged = build_issue_fix_pr_lifecycle_monitor_packet(
url="https://github.com/huangruiteng/loopx/pull/1715",
provider_payload={
@@ -350,6 +352,8 @@ def main() -> int:
"## Agent Todo\n\n",
encoding="utf-8",
)
+ import_fixture_authority(registry_path, "example-goal", provider=provider)
+
ledger = default_issue_fix_domain_state_ledger_path(
project=project,
goal_id="example-goal",
@@ -552,8 +556,7 @@ def main() -> int:
mutation_authority = reconciled_gate["todo_completion"][
"mutation_authority"
]
- assert mutation_authority["mode"] == "registered_peer_actor", reconciled_gate
- assert mutation_authority["actor_agent_id"] == "codex-test", reconciled_gate
+ assert mutation_authority["authority_mode"] == "registered_peer_actor", reconciled_gate
assert reconciled_gate["rollout_event"]["appended"] is True, reconciled_gate
assert "status=done" in state_file.read_text(encoding="utf-8")
gate_projection = parse_active_state_todos(
@@ -786,6 +789,18 @@ def main() -> int:
material_rows
)
+ native = list_goal_todos(registry_path=registry_path, goal_id="example-goal")
+ assert native["authority_read"]["decision_read_from_provider"] is True, native
+ assert native["authority_read"]["legacy_fallback_used"] is False, native
+ rows = {item["todo_id"]: item for item in native["todos"]}
+ for todo_id in ("todo_merge_gate_1716", "todo_review_action_1716"):
+ assert rows[todo_id]["status"] == "done", rows[todo_id]
+ assert rows[todo_id]["last_actor_agent_id"] == "codex-test", rows[todo_id]
+
+
+def main() -> int:
+ for provider in ("file", "sqlite"):
+ check_lifecycle(provider)
print("issue-fix-pr-lifecycle-smoke: ok")
return 0
diff --git a/examples/issue-fix-pr-review-reconcile-smoke.py b/examples/issue-fix-pr-review-reconcile-smoke.py
index 040d4fc599..36075c0fcb 100644
--- a/examples/issue-fix-pr-review-reconcile-smoke.py
+++ b/examples/issue-fix-pr-review-reconcile-smoke.py
@@ -24,9 +24,10 @@
from loopx.capabilities.issue_fix.pr_review_ack import ( # noqa: E402
record_issue_fix_pr_review_ack,
)
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
from loopx.heartbeat_prequota import run_heartbeat_pre_quota # noqa: E402
from loopx.status import parse_active_state_todos # noqa: E402
-from loopx.todos import update_goal_todo # noqa: E402
+from loopx.todos import list_goal_todos, update_goal_todo # noqa: E402
PR_URL = "https://github.com/huangruiteng/loopx/pull/1716"
@@ -89,7 +90,7 @@ def reconcile_without_provider(
)
-def main() -> int:
+def check_reconciliation(provider: str) -> None:
with tempfile.TemporaryDirectory(
prefix="loopx-issue-fix-pr-review-reconcile-"
) as tmpdir:
@@ -142,6 +143,8 @@ def main() -> int:
encoding="utf-8",
)
+ import_fixture_authority(registry_path, "example-goal", provider=provider)
+
missing = reconcile_without_provider(
registry_path=registry_path,
runtime_root=runtime_root,
@@ -330,7 +333,18 @@ def main() -> int:
assert cli_payload["quota_spend_required"] is False, cli_payload
assert todo(state_file, "todo_other_provider")["status"] == "open"
+ native = list_goal_todos(registry_path=registry_path, goal_id="example-goal")
+ assert native["authority_read"]["decision_read_from_provider"] is True, native
+ assert native["authority_read"]["legacy_fallback_used"] is False, native
+ rows = {item["todo_id"]: item for item in native["todos"]}
+ assert rows["todo_github_review"]["status"] == "done"
+ assert rows["todo_other_provider"]["status"] == "open"
+
+
+def main() -> int:
+ for provider in ("file", "sqlite"):
+ check_reconciliation(provider)
print("issue-fix-pr-review-reconcile-smoke: ok")
return 0
diff --git a/examples/lark-goal-channel-human-gate-delivery-smoke.py b/examples/lark-goal-channel-human-gate-delivery-smoke.py
index d2f4a344eb..3faffa02e5 100644
--- a/examples/lark-goal-channel-human-gate-delivery-smoke.py
+++ b/examples/lark-goal-channel-human-gate-delivery-smoke.py
@@ -13,6 +13,8 @@
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from loopx.extensions.bundled import bundled_extension_manifest
+from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+from loopx.control_plane.testing.canary_harness import import_fixture_authority
from loopx.extensions.lark.goal_channel_contracts import (
GOAL_CHANNEL_BINDING_SCHEMA_VERSION,
read_goal_channel_binding,
@@ -296,12 +298,14 @@ def write_project(root: Path) -> tuple[Path, Path]:
return registry_path, binding_path
-def main() -> None:
+def assert_gate_delivery(provider: str) -> None:
with tempfile.TemporaryDirectory(prefix="loopx-goal-channel-human-gate-") as raw:
registry_path, binding_path = write_project(Path(raw))
bin_dir, fake_state_path = write_fake_lark_cli(Path(raw))
project = registry_path.parent.parent
runtime_root = Path(raw) / "runtime"
+ import_fixture_authority(registry_path, GOAL_ID, provider=provider)
+ before = read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID)
first_refresh = run_refresh(
registry_path=registry_path,
runtime_root=runtime_root,
@@ -350,6 +354,11 @@ def main() -> None:
== send_count
)
assert gate_update["changed"] is True, gate_update
+ after = read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID)
+ gate = next(row for row in after["todos"] if row["todo_id"] == GATE_TODO_ID)
+ assert "after reviewing the diff" in gate["text"], gate
+ assert gate["status"] == "open" and gate["task_class"] == "user_gate", gate
+ assert after["provider_revision"] != before["provider_revision"]
assert third["status"] == "sent_verified", third
third_key = third["notification"]["idempotency_key"]
assert third_key != first_key
@@ -375,5 +384,10 @@ def main() -> None:
print("lark-goal-channel-human-gate-delivery-smoke ok")
+def main() -> None:
+ for provider in ("file", "sqlite"):
+ assert_gate_delivery(provider)
+
+
if __name__ == "__main__":
main()
diff --git a/examples/loopx-chat-actions-smoke.py b/examples/loopx-chat-actions-smoke.py
index 328971c8c6..63b921ef5e 100644
--- a/examples/loopx-chat-actions-smoke.py
+++ b/examples/loopx-chat-actions-smoke.py
@@ -24,6 +24,8 @@
from loopx.chat_server import ChatHTTPServer, ChatRequestHandler # noqa: E402
from loopx.chat_store import ChatSessionStore # noqa: E402
from loopx.history import collect_history # noqa: E402
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
+from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted # noqa: E402
def preview_request(
@@ -194,6 +196,7 @@ def write_registry_fixture(root: Path) -> tuple[Path, Path]:
+ "\n",
encoding="utf-8",
)
+ import_fixture_authority(registry, "goal-one")
return registry, state
@@ -965,6 +968,8 @@ def assert_http_action_api(root: Path) -> None:
# A decision outcome is only valid on a User gate; the canonical owner
# rejects it before any proposal is stored.
+ before_wrong_role = read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id="goal-one")
+ before_wrong_role_display = state_path.read_bytes()
code, misdirected_decision = request_json(
f"{base_url}/api/actions/preview",
method="POST",
@@ -977,7 +982,12 @@ def assert_http_action_api(root: Path) -> None:
},
)
assert code == 400, misdirected_decision
- assert "decision_outcome is only valid" in misdirected_decision["error"], misdirected_decision
+ assert misdirected_decision["error_code"] == "invalid_action_preview", misdirected_decision
+ assert misdirected_decision["error"] == "Todo does not have the requested role", misdirected_decision
+ assert not any(proposal.get("idempotency_key") == "http-misdirected-decision"
+ for proposal in service.store.list())
+ assert state_path.read_bytes() == before_wrong_role_display
+ assert read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id="goal-one") == before_wrong_role
for index, (action_kind, params, gate_kind) in enumerate(
[
diff --git a/examples/loopx-chat-contract-smoke.py b/examples/loopx-chat-contract-smoke.py
index 24a95d5a2b..766e8cfa75 100644
--- a/examples/loopx-chat-contract-smoke.py
+++ b/examples/loopx-chat-contract-smoke.py
@@ -19,13 +19,16 @@
build_todo_review_preview,
parse_agent_response,
)
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
+from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted # noqa: E402
+from loopx.control_plane.todos.provider_projection import project_current_canonical_todos # noqa: E402
GOAL_ID = "loopx-chat-contract-smoke"
TODO_TEXT = "[P0] Add a reviewable Goal Studio interaction."
-def write_fixture(root: Path) -> tuple[Path, Path, Path]:
+def write_fixture(root: Path, *, todo_anchor: bool = True) -> tuple[Path, Path, Path]:
project = root / "project"
runtime = root / "runtime"
state_file = project / ".codex" / "goals" / GOAL_ID / "ACTIVE_GOAL_STATE.md"
@@ -39,7 +42,7 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
"## Objective\n\n"
"Keep LoopX Chat reviewable.\n\n"
"## Next Action\n\n"
- "- Review the next bounded proposal.\n",
+ "- Review the next bounded proposal.\n" + ("\n## Agent Todo\n" if todo_anchor else ""),
encoding="utf-8",
)
registry_path = project / ".loopx" / "registry.json"
@@ -69,6 +72,7 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path]:
+ "\n",
encoding="utf-8",
)
+ import_fixture_authority(registry_path, GOAL_ID)
return registry_path, state_file, project
@@ -174,6 +178,7 @@ def main() -> None:
assert "tool narration" not in malformed["message"], malformed
original = state_file.read_text(encoding="utf-8")
+ authority_before = read_canonical_todos_if_promoted(runtime_root=root / "runtime", goal_id=GOAL_ID)
preview = build_todo_review_preview(
registry_path=registry_path,
goal_id=GOAL_ID,
@@ -191,6 +196,7 @@ def main() -> None:
"action_kind": "loopx_chat_proposal",
}, preview
assert state_file.read_text(encoding="utf-8") == original
+ assert read_canonical_todos_if_promoted(runtime_root=root / "runtime", goal_id=GOAL_ID) == authority_before
assert "state_file" not in json.dumps(preview), preview
assert "project" not in json.dumps(preview), preview
@@ -213,6 +219,8 @@ def main() -> None:
assert str(project) not in json.dumps(no_write_receipt), no_write_receipt
else:
raise AssertionError("stale preview must be rejected")
+ assert state_file.read_text(encoding="utf-8") == original
+ assert read_canonical_todos_if_promoted(runtime_root=root / "runtime", goal_id=GOAL_ID) == authority_before
applied = apply_todo_review_preview(
registry_path=registry_path,
@@ -222,7 +230,15 @@ def main() -> None:
)
assert applied["ok"] is True, applied
assert applied["applied"] is True, applied
- assert applied["todo"]["todo_id"] == preview["todo"]["todo_id"], applied
+ # Canonical creation allocates its id at commit; preview locks the
+ # intent and provider revision, not a dry-run's provisional id.
+ for field in ("goal_id", "text", "status", "task_class", "action_kind"):
+ assert applied["todo"][field] == preview["todo"][field], applied
+ committed = read_canonical_todos_if_promoted(runtime_root=root / "runtime", goal_id=GOAL_ID)
+ assert committed["decision_read_from_provider"] is True and committed["legacy_fallback_used"] is False
+ assert len(committed["todos"]) == 1, committed
+ assert committed["todos"][0]["todo_id"] == applied["todo"]["todo_id"], committed
+ assert committed["todos"][0]["text"] == TODO_TEXT, committed
receipt = applied["receipt"]
assert receipt["schema_version"] == "loopx_chat_todo_receipt_v0", receipt
assert receipt["receipt_id"], receipt
@@ -236,6 +252,14 @@ def main() -> None:
assert str(project) not in json.dumps(receipt), receipt
assert state_file.read_text(encoding="utf-8").count(TODO_TEXT) == 1
+ # Display edits cannot replace the committed record or create a
+ # duplicate. Restore the document before another explicit write.
+ display = state_file.read_text(encoding="utf-8")
+ state_file.write_text(display.replace(TODO_TEXT, "Spoofed display proposal"), encoding="utf-8")
+ assert read_canonical_todos_if_promoted(runtime_root=root / "runtime", goal_id=GOAL_ID) == committed
+
+ state_file.write_text(display, encoding="utf-8")
+
retry_preview = build_todo_review_preview(
registry_path=registry_path,
goal_id=GOAL_ID,
@@ -254,6 +278,37 @@ def main() -> None:
assert retry_receipt["already_exists"] is True, retry_receipt
assert retry_receipt["todo_id"] == receipt["todo_id"], retry_receipt
assert state_file.read_text(encoding="utf-8").count(TODO_TEXT) == 1
+ assert read_canonical_todos_if_promoted(runtime_root=root / "runtime", goal_id=GOAL_ID) == committed
+
+ # An anchorless historical document is retained as a separate display
+ # recovery case. A successful canonical commit cannot repair its prose
+ # implicitly or turn display failure into a second business write.
+ recovery_root = root / "display-recovery"
+ recovery_registry, recovery_state, _ = write_fixture(recovery_root, todo_anchor=False)
+ original_narrative = recovery_state.read_text(encoding="utf-8")
+ recovery_preview = build_todo_review_preview(registry_path=recovery_registry, goal_id=GOAL_ID, text=TODO_TEXT)
+ recovery_applied = apply_todo_review_preview(registry_path=recovery_registry, goal_id=GOAL_ID,
+ text=TODO_TEXT, preview_id=recovery_preview["preview_id"])
+ assert recovery_applied["applied"] is True, recovery_applied
+ assert recovery_state.read_text(encoding="utf-8") == original_narrative
+ accepted = read_canonical_todos_if_promoted(runtime_root=recovery_root / "runtime", goal_id=GOAL_ID)
+ assert len(accepted["todos"]) == 1, accepted
+ try:
+ project_current_canonical_todos(registry_path=recovery_registry,
+ runtime_root=recovery_root / "runtime", goal_id=GOAL_ID)
+ except ValueError as error:
+ assert "no projection anchor" in str(error), error
+ else:
+ raise AssertionError("an anchorless document must refuse implicit projection")
+ assert recovery_state.read_text(encoding="utf-8") == original_narrative
+ recovery_state.write_text(original_narrative + "\n## Agent Todo\n", encoding="utf-8")
+ delivered = project_current_canonical_todos(registry_path=recovery_registry,
+ runtime_root=recovery_root / "runtime", goal_id=GOAL_ID,
+ expected_provider_revision=accepted["provider_revision"])
+ assert delivered["status"] == "delivered" and delivered["narrative_preserved"] is True, delivered
+ assert recovery_state.read_text(encoding="utf-8").startswith(original_narrative)
+ assert recovery_state.read_text(encoding="utf-8").count(TODO_TEXT) == 1
+ assert read_canonical_todos_if_promoted(runtime_root=recovery_root / "runtime", goal_id=GOAL_ID) == accepted
print("loopx-chat-contract-smoke: ok")
diff --git a/examples/loopx-chat-server-smoke.py b/examples/loopx-chat-server-smoke.py
index bec354db11..882e3b041f 100644
--- a/examples/loopx-chat-server-smoke.py
+++ b/examples/loopx-chat-server-smoke.py
@@ -19,6 +19,10 @@
REPO_ROOT = Path(__file__).resolve().parents[1]
+sys.path.insert(0, str(REPO_ROOT))
+
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
+
GOAL_ID = "loopx-chat-server-smoke"
SECOND_GOAL_ID = "loopx-chat-server-second-goal"
TODO_TEXT = "[P0] Implement the approved Goal Studio review card."
@@ -169,6 +173,8 @@ def write_fixture(root: Path) -> tuple[Path, Path]:
+ "\n",
encoding="utf-8",
)
+ for goal_id in (GOAL_ID, SECOND_GOAL_ID):
+ import_fixture_authority(registry, goal_id)
return registry, state_file
diff --git a/examples/loopx-turn-path-delta-acceptance-smoke.py b/examples/loopx-turn-path-delta-acceptance-smoke.py
index e0863556ea..3431108f53 100644
--- a/examples/loopx-turn-path-delta-acceptance-smoke.py
+++ b/examples/loopx-turn-path-delta-acceptance-smoke.py
@@ -16,6 +16,10 @@
sys.path.insert(0, str(REPO_ROOT))
from loopx.cli import main as cli_main # noqa: E402
+from loopx.control_plane.coordination.local_authority import ( # noqa: E402
+ read_canonical_todos_if_promoted,
+)
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
GOAL_ID = "loopx-turn-path-delta-fixture"
@@ -112,6 +116,8 @@ def _write_host(root: Path) -> Path:
mode = sys.argv[1]
marker = pathlib.Path({MARKER!r})
marker.write_text(mode, encoding="utf-8")
+with marker.with_suffix(".calls").open("a", encoding="utf-8") as calls:
+ calls.write(mode + "\\n")
material_packet = {{
"agent_id": {AGENT_ID!r},
"state": "active",
@@ -181,6 +187,8 @@ def _run_turn(
workspace: Path,
host: Path,
mode: str,
+ expect_canonical_refusal: bool = False,
+ resume_turn_key: str | None = None,
) -> dict[str, Any]:
output = io.StringIO()
with contextlib.redirect_stdout(output):
@@ -200,8 +208,10 @@ def _run_turn(
GOAL_ID,
"--agent-id",
AGENT_ID,
- "--turn-instance-id",
- f"path-delta-{mode}",
+ *(
+ ["--resume-turn-key", resume_turn_key]
+ if resume_turn_key else ["--turn-instance-id", f"path-delta-{mode}"]
+ ),
"--project",
str(workspace),
"--host-adapter-command-json",
@@ -215,6 +225,14 @@ def _run_turn(
]
)
payload = json.loads(output.getvalue())
+ if expect_canonical_refusal:
+ assert exit_code != 0, payload
+ assert payload["reason_code"] == "canonical_authority_required", payload
+ assert payload["write_admitted"] is False, payload
+ assert payload["journal_observation"]["recorded_effects"]["host_invoked"] is True, payload
+ assert payload["journal_observation"]["recorded_effects"]["quota_spent"] is False, payload
+ assert payload["journal_observation"]["recovery_decision"]["reinvoke_host"] is False, payload
+ return payload
assert exit_code == 0, payload
assert payload["status"] == "committed", payload
assert payload["validation"]["status"] == "passed", payload
@@ -226,19 +244,44 @@ def _run_records(runtime: Path) -> list[dict[str, Any]]:
return [json.loads(line) for line in index.read_text(encoding="utf-8").splitlines()]
-def main() -> int:
- with tempfile.TemporaryDirectory(prefix="loopx-turn-path-delta-") as directory:
+def assert_canonical_turn_path_delta(provider: str) -> None:
+ with tempfile.TemporaryDirectory(prefix=f"loopx-turn-path-delta-{provider}-") as directory:
root = Path(directory)
project, runtime, workspace, registry = _write_fixture(root)
host = _write_host(root)
- _run_turn(
+ state = project / ".codex" / "goals" / GOAL_ID / "ACTIVE_GOAL_STATE.md"
+ source_bytes = state.read_bytes()
+ refused = _run_turn(
+ registry=registry,
+ runtime=runtime,
+ project=project,
+ workspace=workspace,
+ host=host,
+ mode="material",
+ expect_canonical_refusal=True,
+ )
+ # The Host has returned, but no Todo write/spend is admitted. Import
+ # the complete stopped fixture and recover that original Turn.
+ assert state.read_bytes() == source_bytes
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID) is None
+ calls = (project / MARKER).with_suffix(".calls")
+ assert calls.read_text(encoding="utf-8").splitlines() == ["material"]
+ import_fixture_authority(registry, GOAL_ID, provider=provider)
+ original = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID)
+ assert original is not None
+ assert original["source_authority"] == f"{provider}_v0", original
+ assert {item["todo_id"] for item in original["todos"]} == {TODO_ID}, original
+ recovered = _run_turn(
registry=registry,
runtime=runtime,
project=project,
workspace=workspace,
host=host,
mode="material",
+ resume_turn_key=refused["resume_turn_key"],
)
+ assert recovered["resume_turn_key"] == refused["resume_turn_key"], recovered
+ assert calls.read_text(encoding="utf-8").splitlines() == ["material"]
_run_turn(
registry=registry,
runtime=runtime,
@@ -247,6 +290,19 @@ def main() -> int:
host=host,
mode="routine",
)
+ assert calls.read_text(encoding="utf-8").splitlines() == ["material", "routine"]
+ canonical = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID)
+ assert canonical is not None
+ assert canonical["source_authority"] == f"{provider}_v0", canonical
+ assert {item["todo_id"] for item in canonical["todos"]} == {TODO_ID}, canonical
+ before, after = original["todos"][0], canonical["todos"][0]
+ for field in ("todo_id", "text", "role", "status", "claimed_by",
+ "task_class", "action_kind", "priority", "successor_todo_ids"):
+ assert after.get(field) == before.get(field), (field, before, after)
+ mutable_result_fields = {"note", "evidence", "updated_at", "last_actor_agent_id"}
+ assert {k: v for k, v in after.items() if k not in mutable_result_fields} == {
+ k: v for k, v in before.items() if k not in mutable_result_fields
+ }, (before, after)
records = _run_records(runtime)
material = next(
@@ -266,6 +322,10 @@ def main() -> int:
assert "agent_vision" not in routine, routine
assert routine["vision_checkpoint"]["decision"] == "unchanged_with_reason", routine
+
+def main() -> int:
+ for provider in ("file", "sqlite"):
+ assert_canonical_turn_path_delta(provider)
print("loopx-turn-path-delta-acceptance-smoke ok")
return 0
diff --git a/examples/macos-dashboard-launchagent-status-smoke.py b/examples/macos-dashboard-launchagent-status-smoke.py
index a005124053..32ebd20062 100644
--- a/examples/macos-dashboard-launchagent-status-smoke.py
+++ b/examples/macos-dashboard-launchagent-status-smoke.py
@@ -388,10 +388,12 @@ def main() -> int:
"#!/usr/bin/env bash\n"
"if [[ \"$*\" == *\"--format json doctor\"* ]]; then\n"
" if [[ -n \"${FAKE_RUNTIME_IDENTITY:-}\" ]]; then\n"
- " printf '{\"service_runtime_identity\":%s}\\n' \"$FAKE_RUNTIME_IDENTITY\"\n"
+ " printf '{\"service_runtime_identity\":%s,\"python\":{\"executable\":%s}}\\n' \"$FAKE_RUNTIME_IDENTITY\" "
+ + shlex.quote(json.dumps(sys.executable)) + "\n"
" exit 0\n"
" fi\n"
- " printf '%s\\n' '{\"release_manifest\":{\"manifest\":{\"release_id\":\"current-release\",\"package\":{\"version\":\"0.5.3\"},\"source\":{\"git_commit\":\"current-revision\"}}}}'\n"
+ " printf '{\"release_manifest\":{\"manifest\":{\"release_id\":\"current-release\",\"package\":{\"version\":\"0.5.3\"},\"source\":{\"git_commit\":\"current-revision\"}}},\"python\":{\"executable\":%s}}\\n' "
+ + shlex.quote(json.dumps(sys.executable)) + "\n"
" exit 0\n"
"fi\n"
"echo loopx \"$@\"\n",
@@ -456,6 +458,12 @@ def main() -> int:
assert f"export CODEX_HOME={(home / '.codex').resolve()};" in default_chat_plist, default_chat_plist
assert "export LOOPX_PYTHON=" in default_plist, default_plist
assert "export LOOPX_PYTHON=" in default_chat_plist, default_chat_plist
+ # The selected console distribution owns its interpreter; another
+ # Python version on PATH must not replace this current doctor fact.
+ for plist in (status_plist, chat_plist):
+ shell = plistlib.loads(plist.read_bytes())["ProgramArguments"][2]
+ words = [word.removesuffix(";") for word in shlex.split(shell)]
+ assert f"LOOPX_PYTHON={sys.executable}" in words, shell
assert "/loopx --registry" in default_plist, default_plist
assert "/loopx-canary" not in default_plist, default_plist
assert not (home / "Library" / "LaunchAgents" / "com.loopx.dashboard.plist").exists(), "retired dashboard LaunchAgent should not be installed"
diff --git a/examples/multi-agent-role-successor-todos-smoke.py b/examples/multi-agent-role-successor-todos-smoke.py
index a65d483f1c..8b2682e3cf 100644
--- a/examples/multi-agent-role-successor-todos-smoke.py
+++ b/examples/multi-agent-role-successor-todos-smoke.py
@@ -18,6 +18,8 @@
apply_role_successor_todos,
first_successor_followup,
)
+from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted # noqa: E402
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
GOAL_ID = "multi-agent-successor-smoke"
@@ -34,7 +36,8 @@ def write_minimal_state(path: Path) -> None:
"",
"## Agent Todo",
"",
- "- [ ] [P0] Run source action. ",
+ "- [ ] [P0] Run source action.",
+ " ",
"",
]
),
@@ -92,6 +95,9 @@ def main() -> int:
runtime_root = root / "runtime"
write_minimal_state(state_file)
write_registry(registry, project=project, state_file=state_file, runtime_root=runtime_root)
+ import_fixture_authority(registry, GOAL_ID)
+ before = read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID)
+ display_before = state_file.read_bytes()
preview = apply_role_successor_todos(
registry_path=registry,
@@ -139,6 +145,8 @@ def main() -> int:
)
assert skipped["needed"] is False, skipped
assert skipped["reason"] == "no_second_candidate", skipped
+ assert read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID) == before
+ assert state_file.read_bytes() == display_before
executed = apply_role_successor_todos(
registry_path=registry,
@@ -158,6 +166,24 @@ def main() -> int:
state_text = state_file.read_text(encoding="utf-8")
assert "claimed_by=agent-b" in state_text, state_text
assert f"unblocks_todo_id={SOURCE_TODO_ID}" in state_text, state_text
+ after = read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID)
+ assert after["source_authority"] == "file_v0", after
+ records = {todo["todo_id"]: todo for todo in after["todos"]}
+ assert len(records) == 2, records
+ assert records[added["todo_id"]]["claimed_by"] == "agent-b", records
+ assert records[added["todo_id"]]["unblocks_todo_id"] == SOURCE_TODO_ID, records
+ original_source = next(todo for todo in before["todos"] if todo["todo_id"] == SOURCE_TODO_ID)
+ assert records[SOURCE_TODO_ID] == original_source, records
+ replay = apply_role_successor_todos(
+ registry_path=registry, goal_id=GOAL_ID, source_todo_id=SOURCE_TODO_ID,
+ current_agent_id="agent-a", role_id="evidence_runner", action="run_dev_eval",
+ successor_specs=[successor_spec()], decision_summary={"dev_promotion_candidate_count": 1},
+ execute=True,
+ )
+ assert replay["successors"][0]["already_exists"] is True, replay
+ assert replay["successors"][0]["todo_id"] == added["todo_id"], replay
+ assert read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID) == after
+ display_after = state_file.read_bytes()
try:
apply_role_successor_todos(
@@ -175,6 +201,8 @@ def main() -> int:
assert "successor target_agent_id 'missing-agent' is not registered" in str(exc), exc
else:
raise AssertionError("missing successor target agent should fail closed")
+ assert read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=GOAL_ID) == after
+ assert state_file.read_bytes() == display_after
print("multi-agent-role-successor-todos-smoke ok")
return 0
diff --git a/examples/personal-workspace-browser/attention-user-action.mjs b/examples/personal-workspace-browser/attention-user-action.mjs
index 23968b074b..8f8723deb5 100644
--- a/examples/personal-workspace-browser/attention-user-action.mjs
+++ b/examples/personal-workspace-browser/attention-user-action.mjs
@@ -25,19 +25,19 @@ from loopx.chat_server import ChatHTTPServer, ChatRequestHandler
from loopx.chat_actions import ChatActionService
from loopx.chat_action_store import ChatActionStore
from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
-from loopx.todos import add_goal_todo, list_goal_todos
+from loopx.todos import add_goal_todo
r=pathlib.Path(sys.argv[1]); runtime=r/'runtime'; registry=r/'registry.json'; state=r/'state.md'
state.write_text('---\\ngoal_id: product-release\\n---\\n\\n## Agent Todo\\n')
agents=['codex-review','codex-delivery'] + (['codex'] if sys.argv[2]=='true' else [])
registry.write_text(json.dumps({'common_runtime_root':str(runtime),'goals':[{'id':'product-release','repo':str(r),'state_file':'state.md','coordination':{'agent_model':'peer_v1','registered_agents':agents}}]}))
+config=json.loads(registry.read_text()); config['goals'][0]['coordination']['handoff_mode']='hard_lease'; registry.write_text(json.dumps(config))
+projection=build_todo_runtime_shadow_projection(goal_id='product-release',handoff_mode='hard_lease',todos=[])
+initialize_canonical_authority(runtime,'product-release',projection,state_path=state,provider='file')
ids={}
for operation in ['complete','cancel']:
- target=add_goal_todo(registry_path=registry,goal_id='product-release',role='agent',text='Dependent '+operation,status='blocked',task_class='advancement_task',claimed_by='codex-delivery')
- action=add_goal_todo(registry_path=registry,goal_id='product-release',role='user',text='Handle '+operation,task_class='user_action',bound_agent='codex-delivery',unblocks_todo_id=target['todo_id'])
+ target=add_goal_todo(registry_path=registry,goal_id='product-release',role='agent',text='Dependent '+operation,status='blocked',task_class='advancement_task',claimed_by='codex-delivery',operation_id='attention-user-action:'+operation+':target')
+ action=add_goal_todo(registry_path=registry,goal_id='product-release',role='user',text='Handle '+operation,task_class='user_action',bound_agent='codex-delivery',unblocks_todo_id=target['todo_id'],operation_id='attention-user-action:'+operation+':request')
ids[operation]=[action['todo_id'],target['todo_id']]
-projection=build_todo_runtime_shadow_projection(goal_id='product-release',handoff_mode='hard_lease',todos=list_goal_todos(registry_path=registry,goal_id='product-release')['todos'])
-config=json.loads(registry.read_text()); config['goals'][0]['coordination']['handoff_mode']='hard_lease'; registry.write_text(json.dumps(config))
-initialize_canonical_authority(runtime,'product-release',projection,state_path=state,provider='file')
s=ChatHTTPServer(('127.0.0.1',0),ChatRequestHandler)
s.registry_path,s.runtime_root,s.runtime_root_override,s.verbose=registry,runtime,str(runtime),False
s.selected_goal_id,s.scan_roots,s.limit='product-release',[],10
diff --git a/examples/personal-workspace-browser/goal-workspace-todo-backend.py b/examples/personal-workspace-browser/goal-workspace-todo-backend.py
index 508bdf46cf..22bf1a9256 100644
--- a/examples/personal-workspace-browser/goal-workspace-todo-backend.py
+++ b/examples/personal-workspace-browser/goal-workspace-todo-backend.py
@@ -10,6 +10,9 @@
from loopx.chat_action_store import ChatActionStore
from loopx.chat_actions import ChatActionService
from loopx.chat_server import ChatHTTPServer, ChatRequestHandler
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
from loopx.control_plane.effect_runtime import restart_effect_runtime
root = Path(sys.argv[1]).resolve()
@@ -20,8 +23,23 @@
registry, state = fixtures["write_registry_fixture"](root)
data = json.loads(registry.read_text())
data["goals"][0].update(id="progress-projection", display_name="Synthetic workspace")
+data["goals"][0]["coordination"]["handoff_mode"] = "soft_claim"
registry.write_text(json.dumps(data), encoding="utf-8")
runtime = Path(data["common_runtime_root"])
+canonical_fixtures = runpy.run_path(
+ str(Path(__file__).resolve().parents[2] / "tests/control_plane/canonical_authority_fixture.py")
+)
+canonical_fixtures["initialize_canonical_authority"](
+ runtime,
+ "progress-projection",
+ build_todo_runtime_shadow_projection(
+ goal_id="progress-projection",
+ handoff_mode="soft_claim",
+ todos=[],
+ ),
+ state_path=state,
+ provider="file",
+)
server = ChatHTTPServer(("127.0.0.1", 0), ChatRequestHandler)
server.registry_path, server.runtime_root, server.runtime_root_override = registry, runtime, str(runtime)
server.selected_goal_id, server.scan_roots, server.limit = "progress-projection", [], 20
diff --git a/examples/project/bootstrap-force-preserve-todos-smoke.py b/examples/project/bootstrap-force-preserve-todos-smoke.py
index 9a28448ded..110b28e261 100644
--- a/examples/project/bootstrap-force-preserve-todos-smoke.py
+++ b/examples/project/bootstrap-force-preserve-todos-smoke.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-"""Smoke-test canonical force refusal and explicit File-preserving reconnects."""
+"""Smoke-test canonical reconnect preservation and refusal of retired writes."""
from __future__ import annotations
@@ -56,9 +56,11 @@ def main() -> int:
assert canonical_state.read_bytes() == state_before
assert (canonical_project / ".loopx/registry.json").read_bytes() == registry_before
- # Existing force/Markdown migration assertions belong to the supported
- # explicit File creation path, not the canonical default writer.
- runtime = Path(tmp) / "file-runtime"
+ for provider in ("file", "sqlite"):
+ assert_canonical_reconnect(Path(tmp) / provider, provider)
+
+ # The old opt-out no longer authorizes new Markdown Goal writers.
+ runtime = Path(tmp) / "unpromoted-runtime"
configuration = runtime / "machine/configuration.json"
configuration.parent.mkdir(parents=True)
configuration.write_text(json.dumps({
@@ -71,81 +73,77 @@ def main() -> int:
project = Path(tmp) / "project"
project.mkdir()
- initial = run_cli(
- "--runtime-root", str(runtime), "bootstrap",
- "--project",
- str(project),
- "--goal-id",
- GOAL_ID,
- "--objective",
- "Exercise force reconnect preservation.",
- "--no-global-sync",
- )
- assert initial["ok"] is True, initial
- assert initial["state_action"] == "created", initial
- assert initial["storage_target"] == {
- "schema_version": "loopx_new_goal_storage_target_v0", "provider": "file",
- }, initial
- state_file = Path(initial["state_file"])
- assert state_file == (project / ".loopx/goals/bootstrap-preserve-fixture/ACTIVE_GOAL_STATE.md").resolve()
-
- todo_state = "# Active Goal State\n\n## Agent Todo\n\n- [ ] Preserve this during reconnect.\n"
- state_file.write_text(todo_state, encoding="utf-8")
-
- preserved = run_cli(
- "--runtime-root", str(runtime), "bootstrap",
- "--project",
- str(project),
- "--goal-id",
- GOAL_ID,
- "--objective",
- "Exercise force reconnect preservation.",
- "--write-scope",
- "src/**",
- "--force",
- "--preserve-todos",
- "--no-global-sync",
- )
- assert preserved["ok"] is True, preserved
- assert preserved["state_action"] == "kept-existing-preserve-todos", preserved
- warning = preserved["force_bootstrap_warning"]
- assert warning["will_replace_active_state"] is False, warning
- assert warning["preserve_todos_requested"] is True, warning
- assert "configure-goal --write-scope" in warning["recommended_scope_migration"], warning
- migrated_state = state_file.read_text(encoding="utf-8")
- assert "- [ ] Preserve this during reconnect." in migrated_state
- assert "## User Todo / Owner Review Reading Queue" in migrated_state
- assert preserved["todo_source_migration"] == {
- "schema_version": "todo_source_section_migration_v0",
- "added_roles": ["user"],
- "applied": True,
- }, preserved
- assert goal_from_registry(project)["coordination"]["write_scope"] == ["src/**"]
-
- replaced = run_cli(
+ refused = run_cli(
"--runtime-root", str(runtime), "bootstrap",
"--project",
str(project),
"--goal-id",
GOAL_ID,
"--objective",
- "Exercise force reconnect replacement warning.",
- "--write-scope",
- "docs/**",
- "--force",
+ "Do not revive the retired Markdown writer.",
"--no-global-sync",
+ check=False,
)
- assert replaced["ok"] is True, replaced
- assert replaced["state_action"] == "replaced", replaced
- warning = replaced["force_bootstrap_warning"]
- assert warning["will_replace_active_state"] is True, warning
- assert warning["preserve_todos_requested"] is False, warning
- assert "Preserve this during reconnect." not in state_file.read_text(encoding="utf-8")
- assert goal_from_registry(project)["coordination"]["write_scope"] == ["docs/**"]
+ assert refused["ok"] is False, refused
+ assert refused["error_code"] == "canonical_authority_required", refused
+ assert not (project / ".loopx/registry.json").exists()
+ assert not (project / f".loopx/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md").exists()
print("bootstrap-force-preserve-todos-smoke ok")
return 0
+def assert_canonical_reconnect(root: Path, provider: str) -> None:
+ runtime = root / "runtime"
+ configuration = runtime / "machine/configuration.json"
+ configuration.parent.mkdir(parents=True)
+ configuration.write_text(json.dumps({
+ "schema_version": "loopx_machine_configuration_v0", "namespaces": {
+ "goal_storage": {"schema_version": "loopx_goal_storage_defaults_v1",
+ "new_goal_provider": provider, "canonical_creation": True,
+ "new_goal_handoff_mode": "soft_claim"},
+ },
+ }), encoding="utf-8")
+ project = root / "project"
+ project.mkdir()
+ bootstrap = (
+ "--runtime-root", str(runtime), "bootstrap", "--project", str(project),
+ "--goal-id", GOAL_ID, "--objective", "Preserve canonical Todos on reconnect.",
+ "--no-global-sync",
+ )
+ initial = run_cli(*bootstrap)
+ assert initial["ok"] and initial["state_action"] == "created", initial
+ assert initial["storage_target"]["provider"] == provider, initial
+ state = Path(initial["state_file"])
+ registry = project / ".loopx/registry.json"
+ prefix = ("--runtime-root", str(runtime), "--registry", str(registry))
+ text = "[P1] Preserve canonical work — 保留任务与原始归属。"
+ added = run_cli(*prefix, "todo", "add", "--goal-id", GOAL_ID,
+ "--role", "agent", "--task-class", "advancement_task",
+ "--action-kind", "smoke", "--text", text)
+ assert added["added"] is True, added
+ read = ("todo", "list", "--goal-id", GOAL_ID)
+ before = run_cli(*prefix, *read)
+ assert before["authority_read"]["source_authority"] == f"{provider}_v0", before
+ assert before["authority_read"]["decision_read_from_provider"] is True, before
+ assert before["authority_read"]["legacy_fallback_used"] is False, before
+ stored = run_cli(*prefix, *read, "--todo-id", added["todo_id"])["todo"]
+ assert stored["text"] == text and stored["status"] == "open", stored
+ for force_args in (("--force",), ("--force", "--preserve-todos")):
+ state_before, registry_before = state.read_bytes(), registry.read_bytes()
+ refused = run_cli(*bootstrap, *force_args, check=False)
+ assert refused["ok"] is False, refused
+ assert "cannot rebuild existing state" in json.dumps(refused), refused
+ assert state.read_bytes() == state_before
+ assert registry.read_bytes() == registry_before
+ assert run_cli(*prefix, *read) == before
+ reconnected = run_cli(*bootstrap)
+ assert reconnected["ok"] is True, reconnected
+ assert reconnected["state_action"] == "kept-existing", reconnected
+ assert run_cli(*prefix, *read) == before
+ assert text in state.read_text(encoding="utf-8")
+ assert goal_from_registry(project)["coordination"]["storage_target"]["provider"] == provider
+
+
if __name__ == "__main__":
raise SystemExit(main())
diff --git a/examples/project/global-manager-command-cli-smoke.py b/examples/project/global-manager-command-cli-smoke.py
index 4703554556..7bf216d5f8 100644
--- a/examples/project/global-manager-command-cli-smoke.py
+++ b/examples/project/global-manager-command-cli-smoke.py
@@ -4,6 +4,7 @@
from __future__ import annotations
import json
+import os
import re
import subprocess
import sys
@@ -12,6 +13,17 @@
REPO_ROOT = Path(__file__).resolve().parents[2]
+sys.path.insert(0, str(REPO_ROOT))
+sys.path.insert(0, str(REPO_ROOT / "tests" / "control_plane"))
+
+from canonical_authority_fixture import initialize_canonical_authority # noqa: E402
+from loopx.control_plane.coordination.authority_source_capture import ( # noqa: E402
+ build_todo_runtime_shadow_projection,
+ capture_todo_archive_dependencies,
+)
+from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted # noqa: E402
+from loopx.control_plane.todos.goal_todo_projection import project_goal_todo_items # noqa: E402
+
GOAL_ID = "smoke-goal"
RUNNABLE_GOAL_ID = "smoke-goal-runnable"
DEFERRED_GOAL_ID = "smoke-goal-deferred"
@@ -80,7 +92,9 @@ def run_cli(
)
-def write_fixture(root: Path) -> tuple[Path, Path, Path, Path, list[Path]]:
+def write_fixture(
+ root: Path, *, provider: str | None = None,
+) -> tuple[Path, Path, Path, Path, list[Path]]:
runtime = root / "runtime"
registry = root / "registry.global.json"
projects = {
@@ -185,6 +199,21 @@ def write_fixture(root: Path) -> tuple[Path, Path, Path, Path, list[Path]]:
),
encoding="utf-8",
)
+ if provider:
+ for goal in goals:
+ state = states[goal["id"]]
+ state_text = state.read_text(encoding="utf-8")
+ todos = project_goal_todo_items(
+ goal, state_text=state_text, state_path=state, rollout_events=[],
+ )
+ # Deferred readiness depends on the completed archive, too.
+ todos = capture_todo_archive_dependencies(todos, state_text)
+ projection = build_todo_runtime_shadow_projection(
+ goal_id=goal["id"], todos=todos, handoff_mode="soft_claim",
+ )
+ initialize_canonical_authority(
+ runtime, goal["id"], projection, state_path=state, provider=provider,
+ )
run_index = runtime / "goals" / GOAL_ID / "runs" / "index.jsonl"
run_index.parent.mkdir(parents=True)
run_index.write_text(
@@ -291,7 +320,9 @@ def assert_todos_markdown(markdown: str, *, private_root: Path) -> None:
assert str(private_root) not in markdown, markdown
-def assert_risks_payload(payload: dict[str, object]) -> None:
+def assert_risks_payload(
+ payload: dict[str, object], *, unresolved_failure: bool,
+) -> None:
assert payload["ok"] is True, payload
request = payload["request"]
assert request["command"] == "/loopx-global-risks", request
@@ -320,9 +351,13 @@ def assert_risks_payload(payload: dict[str, object]) -> None:
], payload
stale_rows = payload["groups"]["stale_runs"]
- # Current Todo reads reconcile this ordinary stale progress projection.
- # Historical failures and unavailable sources retain their independent risks.
- assert stale_rows == [], payload
+ if unresolved_failure:
+ assert len(stale_rows) == 1, payload
+ assert stale_rows[0]["kind"] == "stale_latest_run_projection", stale_rows
+ assert stale_rows[0]["goal_id"] == GOAL_ID, stale_rows
+ else:
+ # A successful current-state read reconciles ordinary stale progress.
+ assert stale_rows == [], payload
assert any(
warning["reason_code"] == "global_registry_unavailable"
for warning in payload["source_warnings"]
@@ -483,12 +518,19 @@ def assert_unhealthy_status_envelope(
assert_public_safe(risks_payload)
-def main() -> int:
+def run_scenario(provider: str | None) -> None:
with tempfile.TemporaryDirectory(prefix="loopx-global-manager-smoke-") as tmp:
root = Path(tmp)
- registry, runtime, risk_input, safe_input, fixture_files = write_fixture(root)
+ registry, runtime, risk_input, safe_input, fixture_files = write_fixture(
+ root, provider=provider,
+ )
files = [registry, *fixture_files]
before = snapshot_files(files)
+ canonical_before = {
+ goal_id: read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=goal_id)
+ for goal_id in (GOAL_ID, RUNNABLE_GOAL_ID, DEFERRED_GOAL_ID)
+ }
+ assert all((value is not None) == bool(provider) for value in canonical_before.values())
summary_proc = run_cli(
registry=registry,
@@ -590,9 +632,23 @@ def main() -> int:
assert_gates_markdown(markdown_proc.stdout, private_root=root)
assert_todos_payload(json.loads(todos_proc.stdout))
assert_todos_markdown(todos_markdown_proc.stdout, private_root=root)
- assert_risks_payload(json.loads(risks_proc.stdout))
+ assert_risks_payload(json.loads(risks_proc.stdout), unresolved_failure=False)
assert_risks_markdown(risks_markdown_proc.stdout, private_root=root)
assert snapshot_files(files) == before
+
+ # Change only this synthetic historical run: real failures must not be
+ # hidden by the same current-state reconciliation that clears progress.
+ run_index = runtime / "goals" / GOAL_ID / "runs" / "index.jsonl"
+ record = json.loads(run_index.read_text(encoding="utf-8"))
+ record["classification"] = "runtime_error"
+ run_index.write_text(json.dumps(record) + "\n", encoding="utf-8")
+ failure_before = snapshot_files(files)
+ failure_proc = run_cli(
+ registry=registry, runtime=runtime, command="global-risks",
+ output_format="json", command_args=risks_args,
+ )
+ assert_risks_payload(json.loads(failure_proc.stdout), unresolved_failure=True)
+ assert snapshot_files(files) == failure_before
assert_error_envelope(root, runtime=runtime)
assert_unhealthy_status_envelope(
root,
@@ -600,7 +656,18 @@ def main() -> int:
runtime=runtime,
safe_input=safe_input,
)
+ assert snapshot_files(files) == failure_before
+ assert {
+ goal_id: read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=goal_id)
+ for goal_id in canonical_before
+ } == canonical_before
+
+def main() -> int:
+ os.environ["NODE_OPTIONS"] = os.environ.get("NODE_OPTIONS", "") + " --experimental-sqlite"
+ # Keep historical read compatibility alongside both canonical providers.
+ for provider in (None, "file", "sqlite"):
+ run_scenario(provider)
print("global-manager-command-cli-smoke ok")
return 0
diff --git a/examples/project/goal-rollout-event-log-smoke.py b/examples/project/goal-rollout-event-log-smoke.py
index 08323d4549..d5748768dc 100644
--- a/examples/project/goal-rollout-event-log-smoke.py
+++ b/examples/project/goal-rollout-event-log-smoke.py
@@ -21,6 +21,7 @@
rollout_event_log_path,
summarize_rollout_events,
)
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
def assert_public_safe_text(text: str) -> None:
@@ -347,86 +348,113 @@ def main() -> None:
assert_public_safe_text(log_path.read_text(encoding="utf-8"))
- registry_path, cli_runtime_root, project, cli_goal_id = write_smoke_project(tmp_root / "auto")
- claim_payload = run_loopx_cli(
- "--registry",
- str(registry_path),
- "--runtime-root",
- str(cli_runtime_root),
- "--format",
- "json",
- "todo",
- "claim",
- "--goal-id",
- cli_goal_id,
- "--todo-id",
- "todo_auto_rollout",
- "--claimed-by",
- "codex-main-control",
- )
- assert claim_payload["rollout_event"]["event_kind"] == "todo_claim", claim_payload
-
- refresh_payload = run_loopx_cli(
- "--registry",
- str(registry_path),
- "--runtime-root",
- str(cli_runtime_root),
- "--format",
- "json",
- "refresh-state",
- "--goal-id",
- cli_goal_id,
- "--classification",
- "automatic_rollout_logging_smoke",
- "--delivery-batch-scale",
- "implementation",
- "--delivery-outcome",
- "outcome_progress",
- "--agent-id",
- "codex-main-control",
- "--agent-lane",
- "smoke",
- "--no-global-sync",
- )
- assert refresh_payload["rollout_event"]["event_kind"] == "refresh_state", refresh_payload
+ for provider in ("file", "sqlite"):
+ assert_cli_events(tmp_root / "auto" / provider, provider)
- should_run_payload = run_loopx_cli(
- "--registry",
- str(registry_path),
- "--runtime-root",
- str(cli_runtime_root),
- "--format",
- "json",
- "quota",
- "should-run",
- "--goal-id",
- cli_goal_id,
- "--agent-id",
- "codex-main-control",
- "--scan-root",
- str(project),
- "--limit",
- "1",
- allow_failure=True,
- )
- assert should_run_payload["rollout_event"]["event_kind"] == (
- "quota_should_run"
- ), should_run_payload
-
- auto_events = load_rollout_events(rollout_event_log_path(cli_runtime_root, cli_goal_id))
- auto_kinds = [event["event_kind"] for event in auto_events]
- assert auto_kinds == [
- "todo_claim",
- "refresh_state",
- "quota_should_run",
- ], auto_kinds
- latest_event = auto_events[-1]
- assert_boundary(latest_event)
- auto_log_text = rollout_event_log_path(cli_runtime_root, cli_goal_id).read_text(
- encoding="utf-8"
- )
- assert_public_safe_text(auto_log_text)
+def assert_cli_events(root: Path, provider: str) -> None:
+ registry_path, cli_runtime_root, project, cli_goal_id = write_smoke_project(root)
+ claim_args = (
+ "--registry", str(registry_path), "--runtime-root", str(cli_runtime_root),
+ "--format", "json", "todo", "claim", "--goal-id", cli_goal_id,
+ "--todo-id", "todo_auto_rollout", "--claimed-by", "codex-main-control",
+ )
+ state = project / "ACTIVE_GOAL_STATE.md"
+ before_state, before_registry = state.read_bytes(), registry_path.read_bytes()
+ refused = run_loopx_cli(*claim_args, allow_failure=True)
+ assert refused["ok"] is False, refused
+ assert refused["error_code"] == "canonical_authority_required", refused
+ assert state.read_bytes() == before_state
+ assert registry_path.read_bytes() == before_registry
+ import_fixture_authority(registry_path, cli_goal_id, provider=provider)
+ events_before = load_rollout_events(rollout_event_log_path(cli_runtime_root, cli_goal_id))
+ claim_payload = run_loopx_cli(
+ "--registry",
+ str(registry_path),
+ "--runtime-root",
+ str(cli_runtime_root),
+ "--format",
+ "json",
+ "todo",
+ "claim",
+ "--goal-id",
+ cli_goal_id,
+ "--todo-id",
+ "todo_auto_rollout",
+ "--claimed-by",
+ "codex-main-control",
+ )
+ assert claim_payload["rollout_event"]["event_kind"] == "todo_claim", claim_payload
+ readback = run_loopx_cli(
+ "--registry", str(registry_path), "--runtime-root", str(cli_runtime_root),
+ "--format", "json", "todo", "list", "--goal-id", cli_goal_id,
+ "--todo-id", "todo_auto_rollout",
+ )
+ assert readback["authority_read"]["source_authority"] == f"{provider}_v0", readback
+ assert readback["authority_read"]["legacy_fallback_used"] is False, readback
+ assert readback["todo"]["claimed_by"] == "codex-main-control", readback
+
+ refresh_payload = run_loopx_cli(
+ "--registry",
+ str(registry_path),
+ "--runtime-root",
+ str(cli_runtime_root),
+ "--format",
+ "json",
+ "refresh-state",
+ "--goal-id",
+ cli_goal_id,
+ "--classification",
+ "automatic_rollout_logging_smoke",
+ "--delivery-batch-scale",
+ "implementation",
+ "--delivery-outcome",
+ "outcome_progress",
+ "--agent-id",
+ "codex-main-control",
+ "--agent-lane",
+ "smoke",
+ "--no-global-sync",
+ )
+ assert refresh_payload["rollout_event"]["event_kind"] == "refresh_state", refresh_payload
+
+ should_run_payload = run_loopx_cli(
+ "--registry",
+ str(registry_path),
+ "--runtime-root",
+ str(cli_runtime_root),
+ "--format",
+ "json",
+ "quota",
+ "should-run",
+ "--goal-id",
+ cli_goal_id,
+ "--agent-id",
+ "codex-main-control",
+ "--scan-root",
+ str(project),
+ "--limit",
+ "1",
+ allow_failure=True,
+ )
+ assert should_run_payload["rollout_event"]["event_kind"] == (
+ "quota_should_run"
+ ), should_run_payload
+
+ auto_events = load_rollout_events(rollout_event_log_path(cli_runtime_root, cli_goal_id))
+ assert auto_events[:len(events_before)] == events_before
+ auto_kinds = [event["event_kind"] for event in auto_events[len(events_before):]]
+ assert auto_kinds == [
+ "todo_claim",
+ "refresh_state",
+ "quota_should_run",
+ ], auto_kinds
+ latest_event = auto_events[-1]
+ assert_boundary(latest_event)
+ auto_log_text = rollout_event_log_path(cli_runtime_root, cli_goal_id).read_text(
+ encoding="utf-8"
+ )
+ assert_public_safe_text(auto_log_text)
if __name__ == "__main__":
main()
diff --git a/examples/project/goal-vision-refresh-state-budget-smoke.py b/examples/project/goal-vision-refresh-state-budget-smoke.py
index 5a3af07ffe..73de3068c9 100644
--- a/examples/project/goal-vision-refresh-state-budget-smoke.py
+++ b/examples/project/goal-vision-refresh-state-budget-smoke.py
@@ -11,6 +11,16 @@
REPO_ROOT = Path(__file__).resolve().parents[2]
+if str(REPO_ROOT) not in sys.path:
+ sys.path.insert(0, str(REPO_ROOT))
+
+from loopx.control_plane.coordination.local_authority import ( # noqa: E402
+ LocalCoordinationAuthorityUnavailable,
+ read_canonical_todos_if_promoted,
+)
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
+from loopx.control_plane.todos.mutation_api import add_goal_todo # noqa: E402
+
GOAL_ID = "goal-vision-budget-fixture"
AGENT_ID = "research-curator"
@@ -184,10 +194,27 @@ def write_json(path: Path, value: dict) -> None:
path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
-def main() -> int:
+def assert_vision_budget(provider: str) -> None:
with tempfile.TemporaryDirectory(prefix="loopx-goal-vision-budget-") as tmp:
root = Path(tmp)
registry_path, runtime, project = write_fixture(root)
+ state_path = project / ".codex" / "goals" / GOAL_ID / "ACTIVE_GOAL_STATE.md"
+ # A source fixture must be imported before any normal Todo writer.
+ source_before = state_path.read_bytes()
+ create_args = dict(registry_path=registry_path, goal_id=GOAL_ID, role="agent",
+ text="Validate the scoped delivery evidence.", task_class="advancement_task",
+ claimed_by=AGENT_ID, agent_id=AGENT_ID)
+ try:
+ add_goal_todo(**create_args)
+ except LocalCoordinationAuthorityUnavailable as error:
+ assert error.code == "canonical_authority_required", error
+ else:
+ raise AssertionError("unmigrated fixture must not admit a normal Todo write")
+ assert state_path.read_bytes() == source_before
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID) is None
+ import_fixture_authority(registry_path, GOAL_ID, provider=provider)
+ original = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID)
+ assert original["source_authority"] == provider + "_v0"
valid_path = root / "valid-vision.json"
invalid_path = root / "invalid-vision.json"
drift_path = root / "drift-vision.json"
@@ -603,10 +630,14 @@ def main() -> int:
assert "limit is 80" in long_todo["error"], long_todo
assert "suggested compact value" in long_todo["error"], long_todo
- from loopx.todos import add_goal_todo
- add_goal_todo(registry_path=registry_path, goal_id=GOAL_ID, role="agent",
- text="Validate the scoped delivery evidence.", task_class="advancement_task",
- claimed_by=AGENT_ID)
+ created = add_goal_todo(**create_args)
+ canonical = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID)
+ assert len(canonical["todos"]) == len(original["todos"]) + 1
+ rows = {row["todo_id"]: row for row in canonical["todos"]}
+ assert rows[created["todo_id"]]["claimed_by"] == AGENT_ID
+ assert rows[created["todo_id"]]["text"] == create_args["text"]
+ for row in original["todos"]:
+ assert rows[row["todo_id"]] == row
local_next_action = "/Users/example/private/raw-task-note"
private_next_action_result = payload(
run_cli(
@@ -689,6 +720,11 @@ def main() -> int:
assert "vision_summary" in invalid["error"], invalid
assert "suggested compact value" in invalid["error"], invalid
+
+
+def main() -> int:
+ for provider in ("file", "sqlite"):
+ assert_vision_budget(provider)
print("goal-vision-refresh-state-budget-smoke ok")
return 0
diff --git a/examples/project/next-action-projection-contract-smoke.py b/examples/project/next-action-projection-contract-smoke.py
index 314987dce5..75223875a1 100644
--- a/examples/project/next-action-projection-contract-smoke.py
+++ b/examples/project/next-action-projection-contract-smoke.py
@@ -8,10 +8,17 @@
import sys
import tempfile
-from loopx.control_plane.scheduler.execution_context import GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT
-from loopx.quota import build_quota_should_run
-from loopx.state_refresh import refresh_state_run
-from loopx.status import collect_status
+REPO_ROOT = Path(__file__).resolve().parents[2]
+if str(REPO_ROOT) not in sys.path:
+ sys.path.insert(0, str(REPO_ROOT))
+
+from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted # noqa: E402
+from loopx.control_plane.scheduler.execution_context import GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT # noqa: E402
+from loopx.control_plane.testing.canary_harness import import_fixture_authority, run_json_cli # noqa: E402
+from loopx.control_plane.todos.mutation_api import complete_goal_todo # noqa: E402
+from loopx.quota import build_quota_should_run # noqa: E402
+from loopx.state_refresh import refresh_state_run # noqa: E402
+from loopx.status import collect_status # noqa: E402
GOAL_ID = "next-action-projection-goal"
ACTIVE_NEXT_ACTION = "Shared compatibility guidance."
@@ -93,9 +100,18 @@ def assert_state_next_action(path: Path, expected: str) -> None:
-def main() -> None:
+def assert_bound_projection(provider: str) -> None:
with tempfile.TemporaryDirectory(prefix="loopx-bound-recommendation-") as directory:
registry, runtime, project, state = write_fixture(Path(directory))
+ import_fixture_authority(registry, GOAL_ID, provider=provider)
+ original = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID)
+ assert original["source_authority"] == provider + "_v0"
+ assert {row["todo_id"] for row in original["todos"]} == {"todo_primary", "todo_side"}
+ # Finish the import's display delivery before checking that a task
+ # step does not change the already-current Markdown projection.
+ run_json_cli("todo", "project-markdown", "--goal-id", GOAL_ID,
+ "--provider-revision", original["provider_revision"], "--execute",
+ registry_path=registry, runtime_root=runtime, cwd=REPO_ROOT)
before = state.read_bytes()
common = dict(registry_path=registry, runtime_root_override=str(runtime),
goal_id=GOAL_ID, project=project, state_file=None, classification="state_refreshed",
@@ -133,7 +149,7 @@ def main() -> None:
command = [sys.executable, "-m", "loopx.cli", "--registry", str(registry),
"--runtime-root", str(runtime), "--format", "json", "status",
"--goal-id", GOAL_ID, "--agent-id", "codex-side-bypass"]
- readback = subprocess.run(command, capture_output=True, text=True, timeout=30)
+ readback = subprocess.run(command, cwd=REPO_ROOT, capture_output=True, text=True, timeout=30)
assert readback.returncode == 0, readback.stdout + readback.stderr
item = next(item for item in json.loads(readback.stdout)["attention_queue"]["items"] if item["goal_id"] == GOAL_ID)
projected = item.get("agent_lane_next_action") or item["project_asset"]["agent_lane_next_action"]
@@ -141,14 +157,37 @@ def main() -> None:
assert projected["next_action_basis"] == lane["next_action_basis"]
assert "next_action_basis" not in item # the selected route owns the basis
markdown = subprocess.run([*command[:command.index("--format")], "--format", "markdown",
- *command[command.index("--format") + 2:]], capture_output=True, text=True, timeout=30)
+ *command[command.index("--format") + 2:]], cwd=REPO_ROOT, capture_output=True, text=True, timeout=30)
assert markdown.returncode == 0 and "next_step: " + step in markdown.stdout
- # A different selected task does not inherit the old experiment.
+ # Display metadata cannot complete the authoritative selected task.
state.write_text(state.read_text().replace("todo_side status=open", "todo_side status=done"))
+ assert state.read_bytes() != before
+ spoofed = collect_status(registry_path=registry, runtime_root_override=str(runtime), scan_roots=[project], limit=2)
+ spoofed_decision = build_quota_should_run(spoofed, goal_id=GOAL_ID, agent_id="codex-side-bypass",
+ scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT)
+ assert spoofed_decision["agent_lane_next_action"]["todo_id"] == "todo_side"
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID) == original
+ state.write_bytes(before)
+
+ # Complete through the real owner: another selected task must not
+ # inherit the original task's retained recommendation receipt.
+ complete_goal_todo(registry_path=registry, goal_id=GOAL_ID,
+ todo_id="todo_side", agent_id="codex-side-bypass", evidence="Scoped fixture work verified.")
+ completed = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID)
+ before_rows = {row["todo_id"]: row for row in original["todos"]}
+ after_rows = {row["todo_id"]: row for row in completed["todos"]}
+ assert after_rows["todo_side"]["status"] == "done"
+ assert after_rows["todo_side"]["claimed_by"] == before_rows["todo_side"]["claimed_by"]
+ assert after_rows["todo_primary"] == before_rows["todo_primary"]
changed = collect_status(registry_path=registry, runtime_root_override=str(runtime), scan_roots=[project], limit=2)
next_decision = build_quota_should_run(changed, goal_id=GOAL_ID, agent_id="codex-side-bypass",
scheduler_execution_context=GENERIC_CLI_OUTER_CONTROLLER_SCHEDULER_CONTEXT)
assert step not in next_decision["interaction_contract"]["agent_channel"]["primary_action"]
+
+
+def main() -> None:
+ for provider in ("file", "sqlite"):
+ assert_bound_projection(provider)
print("next-action-projection-contract-smoke ok")
diff --git a/examples/project/project-agent-adoption-smoke.py b/examples/project/project-agent-adoption-smoke.py
index bccc2230c1..5730d7b8a0 100644
--- a/examples/project/project-agent-adoption-smoke.py
+++ b/examples/project/project-agent-adoption-smoke.py
@@ -20,6 +20,11 @@
REPO_ROOT = Path(__file__).resolve().parents[2]
+if str(REPO_ROOT) not in sys.path:
+ sys.path.insert(0, str(REPO_ROOT))
+
+from loopx.control_plane.testing.canary_harness import import_fixture_authority # noqa: E402
+
GOAL_ID = "adoption-main-control"
USER_TODO = "[P1] Review the P0 owner-risk checklist before approving delivery."
AGENT_TODO = "Run the read-only map dry-run after the operator approval is recorded."
@@ -86,7 +91,7 @@ def write_planned_fixture(root: Path) -> Path:
return registry_path
-def run_cli(root: Path, registry_path: Path, *args: str) -> dict:
+def run_cli(root: Path, registry_path: Path, *args: str, check: bool = True) -> dict:
result = subprocess.run(
[
sys.executable,
@@ -101,7 +106,7 @@ def run_cli(root: Path, registry_path: Path, *args: str) -> dict:
*args,
],
cwd=REPO_ROOT,
- check=True,
+ check=check,
text=True,
capture_output=True,
)
@@ -135,8 +140,8 @@ def attention_item(status_payload: dict) -> dict:
return items[0]
-def main() -> int:
- with tempfile.TemporaryDirectory(prefix="loopx-project-agent-adoption-") as tmp:
+def assert_adoption(provider: str) -> None:
+ with tempfile.TemporaryDirectory(prefix=f"loopx-project-agent-adoption-{provider}-") as tmp:
root = Path(tmp)
registry_path = write_planned_fixture(root)
project = root / "project"
@@ -179,24 +184,31 @@ def main() -> int:
assert first_quota["agent_todo_summary"]["open_count"] == 1, first_quota
assert first_quota["selected_todo"]["text"] == AGENT_TODO, first_quota
- todo_payload = run_cli(
- root,
- registry_path,
- "todo",
- "add",
- "--goal-id",
- GOAL_ID,
- "--role",
- "user",
- "--task-class",
- "user_gate",
- "--global-gate",
- "--text",
- USER_TODO,
+ add_gate = (
+ "todo", "add", "--goal-id", GOAL_ID, "--role", "user",
+ "--task-class", "user_gate", "--global-gate", "--text", USER_TODO,
)
+ state = project / f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md"
+ before_state, before_registry = state.read_bytes(), registry_path.read_bytes()
+ refused = run_cli(root, registry_path, *add_gate, check=False)
+ assert refused["ok"] is False, refused
+ assert refused["error_code"] == "canonical_authority_required", refused
+ assert state.read_bytes() == before_state
+ assert registry_path.read_bytes() == before_registry
+ import_fixture_authority(registry_path, GOAL_ID, provider=provider)
+ imported = run_cli(root, registry_path, "todo", "list", "--goal-id", GOAL_ID)
+ assert imported["authority_read"]["source_authority"] == f"{provider}_v0", imported
+ assert imported["authority_read"]["legacy_fallback_used"] is False, imported
+ assert len(imported["todos"]) == 1, imported
+ original = imported["todos"][0]
+ assert original["text"] == AGENT_TODO and original["status"] == "open", original
+ todo_payload = run_cli(root, registry_path, *add_gate)
assert todo_payload["ok"] is True, todo_payload
assert todo_payload["added"] is True, todo_payload
- assert todo_payload["section"] == "User Todo / Owner Review Reading Queue", todo_payload
+ gate_id = todo_payload["todo_id"]
+ gate = run_cli(root, registry_path, "todo", "list", "--goal-id", GOAL_ID,
+ "--todo-id", gate_id)["todo"]
+ assert gate["text"] == USER_TODO and gate["global_gate"] is True, gate
status_after_todo = run_cli(root, registry_path, "status", "--scan-root", str(project))
item_after_todo = attention_item(status_after_todo)
@@ -267,6 +279,10 @@ def main() -> int:
assert "不要从旧聊天或旧 packet 拼当前状态" in packet["project_agent_handoff"], packet
assert "【用户本地 Gate 记录草稿】" not in packet["packet"], packet
+
+def main() -> int:
+ for provider in ("file", "sqlite"):
+ assert_adoption(provider)
print("project-agent-adoption-smoke ok")
return 0
diff --git a/examples/shared-goal-authority-e2e/README.md b/examples/shared-goal-authority-e2e/README.md
index fa70d29cd5..c6465f7128 100644
--- a/examples/shared-goal-authority-e2e/README.md
+++ b/examples/shared-goal-authority-e2e/README.md
@@ -1,193 +1,121 @@
# Shared-goal-authority E2E stage ladder
-One incremental end-to-end ladder for
-[RFC: LoopX shared control-plane authority and pluggable state providers v0](../../docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md).
-Every completed RFC stage claim is one row; every row drives the product
-through the real `python -m loopx.cli` (`real_cli`) or runs a retained
-store-level probe (`store_direct`). The ladder adds no product path, reads the
-candidate only through the production TypeScript `FileAuthorityStore`, and
-never reports green while a selected row is unverified.
+This ladder qualifies the current shared-authority providers described by the
+[shared control-plane authority RFC](../../docs/architecture/rfcs/shared-goal-authority-state-provider-v0.md).
+Each row uses either the public LoopX CLI or a provider's production TypeScript
+store. A selected row must pass, report an explicit environment gate, or make
+the command fail.
```bash
-python examples/shared-goal-authority-e2e/ladder.py # exit 1 here: live rows unverified, the soak row pending
-python examples/shared-goal-authority-e2e/ladder.py --allow-unverified --allow-pending
-python examples/shared-goal-authority-e2e/ladder.py --stage 2c1 --report-json ladder-report.json
-python examples/shared-goal-authority-e2e/ladder.py --stage 2c2 --allow-pending --report-json ladder-report.json
+python examples/shared-goal-authority-e2e/ladder.py
+python examples/shared-goal-authority-e2e/ladder.py --allow-unverified
+python examples/shared-goal-authority-e2e/ladder.py --stage 1 \
+ --report-json ladder-report.json
python examples/shared-goal-authority-e2e/ladder.py --list
```
-The pytest projection is `tests/control_plane/test_shared_goal_authority_e2e.py`;
-there, an unverified row skips as `unverified: ` and a POSIX-only row
-skips on Windows. The `s2c1.retired_observation_upgrade` row is also covered by
-`tests/control_plane/test_local_authority_shadow_cli_e2e.py` and is skipped in
-the default pytest projection; `LOOPX_LADDER_FULL=1` runs it there too. The
-standalone example always runs selected rows. The eleven `s2c2.*` rows run in
-the stage2c correctness job alongside real-CLI process-death and recovery suites.
+The pytest projection is
+`tests/control_plane/test_shared_goal_authority_e2e.py`. Stage 0 runs the full
+native provider suites, so the default pytest projection leaves those rows to
+the TypeScript job. Set `LOOPX_LADDER_FULL=1` to run them through pytest too.
+The standalone ladder always runs every selected row.
-The seven former `s2c1.*` observation-writer rows are retired with that writer.
-Historical reports retain their meaning; current runs validate explicit upgrade
-and the transaction-bound outbox instead of requiring a second writable history.
+## Current rows
-## Rows
-
-| Row | Stage | Path | Gate | Asserts |
+| Row | Stage | Path | Gate | Assertion |
| --- | --- | --- | --- | --- |
-| `s0.native_file_conformance` | 0 | store_direct | deterministic | Runs the complete `authority_store.test.ts` suite against the current TS FileAuthorityStore; every selected test must pass, with no skip or missing trailer |
-| `s0.native_sqlite_conformance` | 0 | store_direct | deterministic | Runs the complete `sqlite_authority_store.test.ts` suite against real SQLite files and the same shared conformance contract |
-| `s1.cli_document_decodes_through_ts_store` | 1 | real_cli | deterministic | Explicit bootstrap plus three CLI writes load at cursor `4`; paged `scanCommitted` returns four distinct transactions in source order and `readReceipt` finds the first source write |
-| `s2a.nokv_live_qualification` | 2a | store_direct | env:nokv_authority | runs the merged `examples/nokv-authority-store/live-qualification.ts --execute-live` against an existing workbench with a fresh tenant/goal pair; requires `ok=true`, the single-node store-conformance scope, every check `passed`, NoKV SDK `0.11.1` / API `1`, the two stale-incarnation fence checks (`stale_incarnation_fence_rejected`, `stale_incarnation_fence_left_generation_unchanged`), and no promotion or availability claim; evidence carries check ids, counts, the helpers' declared SDK protocol schemas, and config and workbench digest prefixes, never a configuration value or the workbench name |
-| `s2b.postgresql_conformance_live` | 2b | store_direct | env:postgresql | `postgresql_authority_store.integration.test.ts` under node's TAP reporter: `# pass >= 9`, `# fail 0`, `# skipped 0` |
-| `s2c1.retired_observation_upgrade` | 2c1 | real_cli | deterministic | Old enable rejects without writes; retained settings create no history; explicit clear/configure/bootstrap captures the next source transaction |
-| `s2c2.outbox_prepared_then_committed_entries` | 2c2 | real_cli | deterministic | with the maintenance lock held, `todo add` (Python) and `task-lease acquire` (TypeScript) report `drain_deferred/drain_lock_busy`, `status` shows one `committed_pending` entry per partition with one prepared record and one committed marker on disk; one `drain` delivers both (`delivered=2`), history holds the bootstrap plus two committed receipts from both writer runtimes, and the next write delivers inline at cursor `4` |
-| `s2c2.drain_idempotent` | 2c2 | real_cli | deterministic | three deferred entries: `drain --max-entries 1` delivers one (`pending_after=2`, `budget_exhausted`), the next `drain` delivers two, an idle `drain` reports `nothing_pending` with unchanged cursor, `head_digest` and `provider_revision`; receipts settle sequences 1..3; an idempotent same-key re-acquire carries no capture evidence and adds no transaction |
-| `s2c2.sigkill_between_primary_write_and_drain` | 2c2 | real_cli | deterministic (POSIX) | `todo add` SIGKILLed at `before_replace`, `after_replace` and `before_marker` leaves one prepared-only entry each; `drain` settles it as `abandoned` (no-op, primary unchanged) or `committed_proven_by_readback`, the projection equals the primary, and `inspect` ends `matched` |
-| `s2c2.sigkill_mid_drain` | 2c2 | real_cli | deterministic (POSIX) | `todo add` SIGKILLed at `before_commit`, `after_commit`, `after_cursor` and `between_unlinks`: the next `drain` delivers the uncommitted entry once or replays the committed one (`replayed=1, delivered=0`), history holds exactly one delivery, only the cursor remains, and a further drain is idle |
-| `s2c2.rollback_with_pending_entries` | 2c2 | real_cli | deterministic (POSIX) | with one committed-pending and one prepared-only entry, `inspect` reports `outbox_pending` at the exact revision, a rollback preview writes nothing, `rollback --execute` applies and archives the outbox with both entries, the marker, the cursor and the manifest; capture then reports `bootstrap_required` while primary writes continue, a rebootstrap starts a new lineage from the current primary (three todos), and the historical rollback replays against it |
-| `s2c2.parity_equal` | 2c2 | real_cli | deterministic | three cycles interleave Python Markdown writers (add, note update plus a no-change repeat, explicit exclusion set and clear plus a no-change repeat, complete, supersede, and a second add) with TypeScript lease writers (acquire, renew, transfer, and the fence close of a leased complete or supersede); after each cycle `inspect` is `matched`, `qualify` with every required write class is `qualified` with `operation_count` equal to the delivered mutations, `read-candidate` returns the anchor todo, and `sustained_parity_verdict` stays `not_evaluated` |
-| `s2c2.parity_divergent_detects_foreign_edit` | 2c2 | real_cli | deterministic | a direct edit of the primary makes `inspect` report `drifted/shadow_projection_drift`, `qualify` and `read-candidate` reject, a later `todo add` commits but its capture holds on `source_partition_continuity_unproved`; restoring the bytes does not requalify (`outbox_pending`), `drain` stays `stopped`, and only `rollback --execute` plus a fresh bootstrap qualifies again |
-| `s2c2.event_only_todo_source_holds` | 2c2 | real_cli | deterministic | a nonempty legacy Todo event source makes `inspect`, `qualify`, `read-candidate` and Todo writes refuse with `legacy_todo_event_source_retired`; status identifies the affected Goal while preserving healthy Goals and marks its Todo source unavailable; the log remains untouched; explicit owner export/removal restores the supported source |
-| `s2c2.migration_seeds_and_drains` | 2c2 | real_cli | deterministic | `migrate-state` previews an actively captured goal without writing, refuses `--execute` with `shadow_source_replacement_requires_rebootstrap` (also when capture is merely disabled), and executes only after `rollback`; the migrated goal carries its disabled capture configuration, plans no observation seed, requires its own `bootstrap`, then captures a write to cursor `2` and qualifies on it while the legacy archive is retained |
-| `s2c2.growth_measurement_gate` | 2c2 | real_cli | deterministic | ten fixed-size `todo add` writes: the cursor advances by one each time, `store_bytes` grows monotonically, the per-transaction delta accelerates by at most 2048 bytes (one live record), every retained transaction carries its complete projection, `retention_pressure` stays false; the report carries final and cumulative publication bytes and claims no capacity horizon (`capacity_verdict=not_evaluated`) |
-| `s2c2.archive_after_leased_completion_parity` | 2c2 | real_cli | deterministic | a leased Todo is completed through its fence and then archived by `todo archive-completed`: the archive retires the Todo from the current graph while its released lease file stays on disk as audit history, and `inspect` still reports `matched` with `parity_matches=true`, no drift reason, a qualified bounded read of a co-resident open Todo, and a `qualify` that requires the archive event kind; the released lease file remains on disk |
-
-Pending rows are declared in the report as `pending`, never counted as pass,
-and they block a green exit unless `--allow-pending` is passed. One
-declaration remains: `s2c2.sustained_parity_soak`, the >=10-day synthetic-goal
-soak of the selected local profile owned by RFC Section 7.2 (lane L). Bounded
-qualification reports `sustained_parity_verdict=not_evaluated`, and no
-`s2c2.*` row promotes a provider or completes the Stage 2C promotion.
-
-The former `s2c2.archive_after_leased_completion_parity` declaration is now an
-executable row. The gap it recorded is closed at the fold: a Todo partition
-carries the published Todo read records, and the candidate head now keeps a
-lease edge only for Todos still in the current graph
-(`archive_state === "active"`), matching the rule the source projection and the
-TypeScript source verification already apply.
-
-The `s2c2.*` rows use two scheduling-only seams outside every product decision:
-holding the stable maintenance lock, which makes a writer report
-`drain_deferred/drain_lock_busy` and leave its committed entry pending, and a
-POSIX crash worker that pauses one real CLI process at a named persistence
-window so the row can SIGKILL it there. Neither substitutes a result or edits a
-byte; every assertion still goes through `status`, `drain`, `inspect`,
-`qualify`, `read-candidate`, `rollback`, `migrate-state` and the retained
-TypeScript store read.
-
-## Native qualification and prototype retirement
-
-Stage 0 now exercises the providers used by current Goals. Run from a source
-checkout after `npm ci` and `uv sync --extra test`:
+| `s0.native_file_conformance` | 0 | `store_direct` | deterministic | The current `FileAuthorityStore` passes its complete conformance suite with no skip or incomplete TAP trailer. |
+| `s0.native_sqlite_conformance` | 0 | `store_direct` | deterministic | The current `SqliteAuthorityStore` passes the same complete conformance contract against real SQLite files. |
+| `s1.cli_document_decodes_through_ts_store` | 1 | `real_cli` | deterministic | Public CLI bootstrap creates canonical File authority. Todo add, lease acquire, and Todo update commit at cursors 2 through 4. An independent TypeScript process reads the four ordered transactions and the first business receipt from `runtime/authority/file-v0`. |
+| `s2a.nokv_live_qualification` | 2a | `store_direct` | `env:nokv_authority` | The NoKV candidate passes the live single-node qualification with SDK `0.11.1`, API `1`, and both stale-incarnation fence checks. |
+| `s2b.postgresql_conformance_live` | 2b | `store_direct` | `env:postgresql` | The PostgreSQL candidate passes the provider integration suite with at least nine TAP passes and no failures or skips. |
+
+Stage 1 checks the complete public path. It applies File creation defaults,
+bootstraps a new Goal, configures its registered agents, performs three
+business mutations, and verifies all responses report:
+
+- `source_authority=file_v0`;
+- `decision_read_from_provider=true`;
+- `legacy_fallback_used=false`;
+- no retired `coordination_runtime_shadow` write evidence.
+
+The readback process opens the same canonical provider independently. A Python
+response cannot make this row pass without the provider journal and receipt.
+
+## Legacy capture cutoff
+
+The 2.0 cutoff removed the Stage 2C rows that created new Markdown shadow
+transactions. They were tests of a producer that no longer owns business
+writes. Keeping those rows would require LoopX to restore the retired producer
+only to satisfy its qualification suite.
+
+Current Todo, handoff, Monitor, team-plan, bootstrap, project-registration, and
+lease operations require canonical authority. An unpromoted Goal returns
+`canonical_authority_required` with `migrate_goal_authority` and
+`backup_required=true`. It does not create a Markdown mutation or a shadow
+outbox entry.
+
+Existing shadow stores, outboxes, rollback archives, and receipts remain
+readable through their recovery commands. Those commands classify and settle
+pre-cutoff bytes. They do not enable a new capture lineage or qualify a new
+business authority. See [historical outbox recovery](correctness.md).
+
+## Native qualification
+
+Run Stage 0 from a prepared source checkout:
```bash
-uv run --extra test python examples/shared-goal-authority-e2e/ladder.py --stage 0
+uv run --extra test python \
+ examples/shared-goal-authority-e2e/ladder.py --stage 0
```
-These are complete suites, so allow several minutes. The regular TypeScript test
-job already runs them; the default pytest projection skips these two duplicate
-runs. Set `LOOPX_LADDER_FULL=1` for pytest to execute them too. The standalone
-ladder always executes selected rows. The test runner's successful
-exit is necessary but insufficient: all tests must pass, the selection must be
-nonempty, and failures, skips, cancellations or an incomplete TAP trailer fail
-qualification. Missing Node or source suites is explicitly `unverified`.
-Installing a wheel alone does not install the source qualification suite.
-
-The Python `CoordinationAuthorityExecutor`, head codec, `FileCoordinationProvider`
-and bootstrap bridge had no production CLI callers. Their remaining callers were
-the prototype examples, unit tests and the old ladder. They are removed together;
-current TS executors, stores, logical archives and File v0-to-v1 migration remain.
-`coordination-head-*.json` was the experimental head format, not a production
-`authority-store-*.json` journal. This retirement neither rewrites those prototype
-artifacts nor converts them into a full Goal. Historical probe records remain
-historical; `s0.file_matrix_twelve_rows` and `s0.nokv_live_matrix` are no longer
-selectable and are never aliases for current proof.
-
-Coverage follows the current semantic owner rather than reproducing prototype
-schemas or its superseded rules:
-
-| Retired prototype obligation | Retained native qualification |
-| --- | --- |
-| Same-Todo CAS winner, independent writes, stale revision and operation identity | `authority_store_conformance.ts`: CAS, replay/fencing, atomic Todo claim and lease acquisition |
-| Lost response, receipt recovery and bounded retries | `coordination_receipt_conformance.ts` and `claim_acquisition_proof_conformance.ts` |
-| Renew, release, transfer, expiry and stale ownership | `lease_lifecycle_conformance.ts`, `lease_acquisition_conformance.ts`, `claim_transfer_conformance.ts` and claim acquisition proof |
-| Completion plus successor, durable continuation and unchanged unrelated rows | Native terminal lifecycle and production-scale scenarios in `authority_store_conformance.ts` |
-| Restore lineage, corrupt records, publication and reopen | File/SQLite provider suites, authority source and promotion recovery conformance |
-| Large state and observation isolation | Existing production-scale coordination fixtures, complete snapshots and paginated authority scan conformance |
-
-Two intentional differences already belong to the current runtime: an expired
-lease may still be released by its matching holder for cleanup, and an original
-claim receipt does not authorize execution after the current lease has retired.
-Retiring the prototype must not reinstate its expired-release rejection or treat
-historical receipt replay as fresh execution authority.
-
-NoKV keeps its actual TS store and Stage 2A live qualification; removing the old
-head adapter does not remove that provider. Open #4726 touches both implementations:
-its native SDK routing/schema work remains relevant, while its prototype-only
-changes must be dropped when rebasing across this retirement. Local conformance does not certify
-SQLite D2 capacity/soak, production NoKV availability, PostgreSQL deployment or
-default cutover.
-
-## Gates and environment variables
-
-| Gate | Requirement | Unverified reason when absent |
+A successful process exit is necessary but insufficient. The ladder parses the
+TAP trailer and requires a nonempty selection where every test passed. A
+missing Node runtime or source suite reports `unverified`; it never becomes a
+pass.
+
+The retired Python `CoordinationAuthorityExecutor`, head codec, and bootstrap
+bridge had no production CLI callers. Their former prototype rows are not
+aliases for current File or NoKV evidence. The current TypeScript stores,
+logical archives, migrations, and provider conformance remain independently
+qualified.
+
+## Environment gates
+
+| Gate | Required input | Unverified result |
| --- | --- | --- |
-| `deterministic` | none (needs `node` on `PATH` for the CLI's TypeScript runtime and the read-back probe) | `node_missing` when the probe cannot run |
-| `env:postgresql` | `LOOPX_TEST_POSTGRES_URL` plus `node_modules/pg` (`npm ci`) | `postgres_url_missing`, `pg_dependency_missing`, `node_missing` |
-| `env:nokv_authority` | `LOOPX_NOKV_AUTHORITY_LIVE=1` (the probe writes durable test data), `LOOPX_NOKV_AUTHORITY_CONFIG_JSON` (absolute path to the ignored NoKV client configuration), `LOOPX_NOKV_AUTHORITY_PYTHON` (absolute path to the Python executable that resolves NoKV SDK 0.11.1), `LOOPX_NOKV_AUTHORITY_WORKBENCH` (an existing workbench); `node` on `PATH` | `nokv_authority_env_missing`, `loopx_nokv_authority_live_not_enabled`, `nokv_authority_config_missing`, `nokv_authority_python_missing`, `node_missing` |
-
-POSIX-only rows report `unverified/posix_only` on Windows.
-
-## Report and exit policy
-
-The report schema is `loopx_shared_goal_authority_e2e_report_v0`:
-`rows[]` (`status in {pass, fail, unverified}`, `reason_code`, public-safe
-`evidence`, `duration_ms`), `pending[]`, `summary{pass, fail, unverified,
-pending, executed, privacy_violations}`, `bindings{loopx_commit, loopx_tree_dirty, probe_sha256[],
-nokv_client_config_sha256, nokv_sdk_version, postgres_url_sha256_prefix,
-pg_package_version}` (`null` when unknown), and `exit_policy`.
-
-Exit code is `0` iff `fail == 0` and `privacy_violations == 0` and
-(`unverified == 0` or `--allow-unverified`) and (`pending == 0` or
-`--allow-pending`): a selected
-row that never executed, whether gated or declared pending, is an unmet
-obligation, so `--row s2c2.sustained_parity_soak` exits 1 with zero executions, and a
-mixed selection exits 1 even when its executable rows pass. `--list` only
-prints the registry and never claims verification. A privacy scan runs over
-the finished report: any occurrence of a temporary root, the home directory,
-the repository path, the PostgreSQL URL, a NoKV configuration value, or a NoKV
-authority input path rewrites that row to `fail/privacy_violation`; a leak
-confined to the `bindings` block nulls every binding, marks
-`bindings.privacy_violation`, and still exits `1` through
-`summary.privacy_violations`, which no flag relaxes. Evidence therefore
-carries counters, cursors, outcome tokens, and sha256 prefixes only.
-
-## Bounded outbox correctness and the pending ladder
-
-The independently runnable [correctness suite](correctness.md) covers the
-`file_outbox_v1` capture lineage, strict cursor recovery, mixed writers,
-source fences, and recoverable bootstrap/rollback. It uses real CLI and native
-processes, the production `FileAuthorityStore`, and process death at persistence
-boundaries. [Installed-package E2E](installed.py) repeats the public lifecycle
-outside the checkout for both wheel and sdist. [Negative controls](mutants.py)
-deliberately remove correctness checks in disposable source copies.
-
-The ten `s2c2.*` ladder rows above exercise the same lifecycle through the
-public interfaces and read history only through the retained TypeScript store.
-Sustained (elapsed-time) parity and promotion remain separate obligations: the
-soak and the archive-after-lease capture gap stay pending declarations, and a
-bounded qualification result reports `sustained_parity_verdict=not_evaluated`.
-
-Future ladder rows must use the actual product interfaces:
-
-- `authority-shadow drain` for receipt-verified replay and cursor recovery;
-- `coordination-shadow inspect / qualify / read-candidate` for comparison,
- bounded historical qualification, and a qualified read from that same head;
-- `coordination-shadow rollback` with an exact revision or unfinished bootstrap
- operation selector, followed by explicit rebootstrap;
-- the stable management lock outside the goal outbox for scheduling a management
- boundary. Rollback moves the entire goal outbox, so a lock inside that directory
- cannot coordinate it. The tests retain scheduling-only barriers around real
- persistence calls rather than depending on the retired drain lock.
-
-There is no `verify` command or generic `reset`. The independent v0 observation
-store and its runtime-root override behavior retain their original scope; its
-historical evidence is not an outbox qualification receipt.
+| `deterministic` | Node.js on `PATH` for native execution and readback | `node_missing` |
+| `env:postgresql` | `LOOPX_TEST_POSTGRES_URL` and the installed `pg` package | `postgres_url_missing`, `pg_dependency_missing`, or `node_missing` |
+| `env:nokv_authority` | `LOOPX_NOKV_AUTHORITY_LIVE=1`, absolute config and Python paths, an existing workbench, and Node.js | A specific missing-input or disabled-live reason |
+
+The NoKV probe creates a fresh tenant and Goal in the selected workbench.
+Reports contain digests and counters, never configuration values, workbench
+names, repository paths, temporary roots, or provider credentials.
+
+## Report policy
+
+The report schema is `loopx_shared_goal_authority_e2e_report_v0`. It contains
+executed rows, explicit pending declarations, aggregate counts, environment
+bindings, and the exit policy.
+
+Exit code `0` requires:
+
+- no failed row;
+- no privacy violation;
+- no unverified row unless `--allow-unverified` is set;
+- no pending declaration unless `--allow-pending` is set.
+
+There are no pending rows in the current registry. The generic pending
+mechanism remains tested so a future obligation cannot be listed as completed
+work. `--list` prints the registry and makes no verification claim.
+
+## Distribution qualification
+
+[installed.py](installed.py) builds an isolated environment for a wheel or
+source distribution, runs outside the checkout, verifies installed resource
+provenance, and exercises canonical File creation, mutations, exact replay,
+history, receipts, and legacy-bootstrap rejection.
+
+[mutants.py](mutants.py) runs negative controls in disposable source copies.
+The controls prove that removing a required assertion or authority boundary
+causes the qualification to fail.
diff --git a/examples/shared-goal-authority-e2e/correctness.md b/examples/shared-goal-authority-e2e/correctness.md
index dde1310522..91c3bd87b8 100644
--- a/examples/shared-goal-authority-e2e/correctness.md
+++ b/examples/shared-goal-authority-e2e/correctness.md
@@ -1,346 +1,167 @@
-# Bounded file outbox correctness
+# Historical outbox recovery correctness
-An active shadow captures each mutation of its bound primary once. A cursor is a
-position hint: only the actual file provider's committed transaction and receipt
-can authorize deletion of an outbox file. Qualification folds the complete
-baseline and transaction history, then compares the current Todo, handoff mode,
-and lease state. Equal final snapshots alone are insufficient.
+LoopX 2.0 retains the reader and recovery contract for shadow artifacts created
+before the Legacy Markdown authority cutoff. It does not retain a producer for
+new shadow transactions.
-This is a pre-promotion `file_outbox_v1` contract. Native canonical Todo keeps its
-existing contract. Older file-v0 mirror or mixed histories remain unchanged,
-read-only, and ineligible for this qualification; there is no automatic migration.
-The separate observation handler remains available within its original scope.
+Normal Todo, handoff, Monitor, team-plan, project, and lease operations commit
+through canonical File, SQLite, or PostgreSQL authority. An unpromoted Goal is
+held with `canonical_authority_required`; it does not change Markdown, create a
+lease grant, or append to an old outbox.
-## Operator lifecycle
+## Retained responsibilities
-Enable the existing per-goal `coordination.runtime_shadow` configuration with
-`enabled: true` and `provider: file_v0`. Select the intended common runtime root
-and state file in the registry before bootstrap. The new history binds that
-source; conflicting `--runtime-root`, `--project`, or `--state-file` overrides
-cannot establish a second authority for it. Default-off primary writes and the
-independent observation path retain their own override behavior.
-Registry-relative runtime paths also bind rollout-event logging to the registry's
-owning project, including when capture is disabled and the CLI runs elsewhere.
-This corrects a split-log defect; it does not activate capture for that Goal.
+Recovery code may:
-Use the same registry and Goal throughout:
+- inventory an existing shadow store, outbox, management journal, and rollback
+ archive without changing them;
+- decode prepared and committed records written by the retired producer;
+- compare a retained candidate with its bound source;
+- drain or disposition an existing entry from its exact source and receipt
+ evidence;
+- replay an exact historical management result;
+- archive a candidate and its complete outbox under the existing rollback
+ contract;
+- export source bytes for backup and reviewed cold import;
+- inspect and release an exact retained lease or close an exact historical
+ fence.
+
+Recovery code may not:
+
+- prepare a new outbox entry for a normal business operation;
+- enable a new continuous Markdown capture lineage;
+- treat an old candidate as current authority without reviewed migration;
+- create, renew, transfer, or broaden an execution lease;
+- clear an ambiguous artifact to make inspection pass;
+- fall back to Markdown when canonical authority is unavailable.
+
+## Operator commands
+
+Use source inspection before changing any historical material:
```bash
-loopx --registry registry.json --format json coordination-shadow bootstrap --goal-id goal-a
-loopx --registry registry.json --format json coordination-shadow bootstrap --goal-id goal-a --execute
-loopx --registry registry.json --format json authority-shadow status --goal-id goal-a
-loopx --registry registry.json --format json authority-shadow drain --goal-id goal-a
-loopx --registry registry.json --format json coordination-shadow inspect --goal-id goal-a
-loopx --registry registry.json --format json coordination-shadow qualify --goal-id goal-a
-loopx --registry registry.json --format json coordination-shadow read-candidate --goal-id goal-a --todo-id TODO_ID
+loopx --registry registry.json --format json \
+ coordination-shadow inspect-source --goal-id GOAL
+
+loopx --registry registry.json --format json \
+ authority-shadow status --goal-id GOAL
```
-Bootstrap imports the complete current Todo/handoff and lease baseline. It does
-not count as a mutation. Qualification requires three actual primary mutations
-by default; replay and proven abandonment also do not count. `read-candidate`
-uses that default policy and repeats the same validator, returning from its
-verified head rather than trusting a previous successful qualification.
-
-Turning off the configuration does not cancel an already active capture
-obligation. Todo/handoff/followup writers and native lease writers must still
-prepare before changing their bound primary. A failed durable prepare holds
-that mutation. A failure after primary replacement retains recovery evidence;
-it cannot truthfully report that the primary was unchanged. No-change, preview,
-idempotent retry, and prose-only changes produce no mutation receipt. Event-only
-Todo sources retain `event_log_writer_not_bound` and prevent qualification.
-
-To retire the candidate, obtain the exact current `provider_revision` from
-inspection and preview the target before execution. If an invalid cursor or
-outbox manifest blocks inspection, use `authority-shadow status` and its
-read-only provider proof. If the provider itself cannot be proved, preserve the
-scene and hold; do not guess a revision.
+When status proves that a pre-cutoff outbox has entries, run bounded recovery:
```bash
-loopx --registry registry.json --format json coordination-shadow rollback --goal-id goal-a --provider-revision EXACT_REVISION
-loopx --registry registry.json --format json coordination-shadow rollback --goal-id goal-a --provider-revision EXACT_REVISION --execute
+loopx --registry registry.json --format json \
+ authority-shadow drain --goal-id GOAL --max-entries 20 --budget-seconds 30
```
-To terminate a bootstrap interrupted before active publication, use its exact
-operation identity from the management result/journal:
+Drain success depends on durable provider and receipt evidence. A cursor is
+only a position hint. It cannot authorize deletion by itself. Invalid records,
+missing source proof, identity disagreement, or a lost outcome remain explicit
+holds.
+
+For an existing candidate, the retained readers remain available:
```bash
-loopx --registry registry.json --format json coordination-shadow rollback --goal-id goal-a --bootstrap-operation-id EXACT_BOOTSTRAP_OPERATION --execute
-```
+loopx --registry registry.json --format json \
+ coordination-shadow inspect --goal-id GOAL
-The selectors are mutually exclusive. Neither is an unconditional cleanup.
-Rollback preserves the candidate and the complete Goal outbox, including pending
-entries, markers, and malformed cursor bytes. It publishes `inactive` only after
-the archive matches the immutable manifest. The shared store identity and other
-Goals are unchanged. Primary writes then resume; capture reports
-`bootstrap_required`. A new bootstrap imports that current state and starts a
-new capture lineage, even if its data equals the old baseline.
-
-## Recovery and holds
-
-Management uses `bootstrapping / active / rolling_back / inactive`. The stable
-management directory is under `authority-transition/file-v0/`, outside the
-outbox being archived. Its immutable manifest and intent bind the operation,
-request, source, provider revision, and file hashes. Retrying the same operation
-reconciles actual source/archive existence and hashes; a phase string alone
-cannot prove completion. Conflicts hold without overwriting or deleting either
-copy. A delayed exact old request only returns its historical result. Reusing
-its operation ID with different request contents returns an identity mismatch.
-Cached results must match their immutable operation manifest, lineage and archive
-references. A result copied from another Goal cannot establish successful replay.
-
-During an unfinished management operation, canonical and whole-state prose
-writes return a maintenance hold before their first side effect. Management lock
-order is M → T → S → L → K. Native lease writers check under their existing K;
-ordinary writers release primary locks before drain. Legacy fence engagement
-also takes the actual source S and therefore requires its absolute state path
-in the internal RPC request.
-For source owners declared in frontmatter or registered to that state path,
-selecting another Goal cannot bypass the existing binding or fence. Unbound
-legacy shared-state writes retain their existing behavior; prose-only writes
-retain their separate maintenance boundary.
-
-### Fenced legacy write response
-
-A legacy Todo, handoff-mode, followup, registry-state, or native task-lease
-write against a goal whose durable legacy writer fence is present is rejected
-before its first primary side effect: no lease record, Markdown state,
-canonical store document, outbox entry, or lifecycle-fence receipt is written.
-The shared write check (`coordination.local_authority.legacy_write_check`) owns
-only the stable machine reason and the fence binding facts: `reason_code`
-`legacy_coordination_writer_fenced` with `authority_mode` and `fence_id` for an
-engaged fence; `legacy_writer_fence_read_failed` with the technical `reason`
-for a present but unreadable or invalid fence; every non-allowed result fails
-closed. Each caller adapter (the TypeScript
-`requireLegacyCoordinationPrimaryWriteAllowed` wrapper used by native acquire,
-renew, transfer, release, every terminal or holder verify branch, and a
-committed releasing fence-close; the Python
-`require_legacy_coordination_write_allowed` wrapper used by every legacy Todo,
-handoff, followup, registry, bootstrap, and monitor writer) maps the reason to
-`error_code`, carries the complete check result unchanged under `write_check`
-(never spread into its own envelope, so the envelope keeps its own
-`schema_version`), and renders one provider-neutral remediation for an engaged
-fence:
+loopx --registry registry.json --format json \
+ coordination-shadow qualify --goal-id GOAL
+loopx --registry registry.json --format json \
+ coordination-shadow read-candidate --goal-id GOAL --todo-id TODO_ID
```
-legacy coordination writer is fenced; use the promoted canonical authority
-() for goal ; fence ; the primary record
-was not changed
-```
-A fence that cannot be validated reports the check's technical reason
-verbatim. The profile label is opaque data from the check (`file_v0` today),
-never a provider path, table, or command, so a provider-neutral binding needs
-no template change.
-
-A native task-lease envelope keeps `schema_version: task_lease_v0` and its
-`action`; its settlement is `{effect_id, receipts: [], failure: {step:
-"validation", kind: "permission_denied", code: }}` at every
-native guard site alike: no receipt exists because no settlement step ran, and
-`permission_denied` marks the rejection terminal for this writer (RFC section
-5 `rejected` and its no-fabricated-receipt rule, section 5.6, question 11,
-Appendix C). `effect_id` follows each verb's existing rule: acquire reports
-`null` whenever no receipt exists; a lifecycle operation reports the
-settlement identity when the request carries `owner` and `idempotency_key`
-and `null` otherwise.
-
-Previews: `archive-completed` without `--execute` and `todo add --dry-run`
-write nothing. After promotion, public terminal/archive commands
-route to canonical authority instead of treating the promotion fence as an
-error. A hard-lease terminal preview therefore rejects with
-`handoff_mode_requires_lease` when its target has no lease, succeeds for a
-matching lease, and may preview the declared user-gate auto-acquire path; all
-three leave the primary record and receipts unchanged. Legacy-only writers
-such as `todo add` and `todo update` remain fenced. A fenced
-committed releasing `fence_close` releases the caller's claimed mutation lock
-in `finally` while the lease stays `active` and its `held` receipt is
-untouched; the caller's fence token is spent, a retry reports
-`fence_token_invalid`, and recovery is lease expiry or a canonical release.
-
-A promoted `archive-completed --execute` that selects no records is also a
-provider no-op: it returns `no_change` without advancing the canonical cursor,
-revision, or receipt history. Archive operation identity is bound to the
-canonical source revision, so concurrent attempts over one snapshot share the
-same logical operation. Projection recovery may create a missing machine-owned
-User, Agent, or non-empty archive region next to an existing Todo region; it
-still proves byte-for-byte preservation of all narrative outside those regions
-and remains idempotent on replay. Empty successor intent is an identity at the
-typed derivation boundary: it does not impose successor-only Agent registry or
-completion-policy admission on an otherwise unchanged legacy terminal call.
-
-The complete observable behaviour is pinned row by row in
-`tests/fixtures/control_plane/legacy_writer_fence_caller_parity_v0.json`
-(21 TypeScript entry rows, 21 real-process CLI rows; whole-object legacy
-envelopes, stable-field subsets for provider-first rows, exact exit status,
-exclusion-free effect snapshots, and declared after-state) and
-enforced by `tests/control_plane_ts/legacy_writer_fence_caller_parity.test.ts`
-and `tests/control_plane/test_shadow_fence_caller_parity_e2e.py`; the
-`baseline` entries of that fixture document earlier revisions and are never
-executed.
-
-Promoted CLI acquire, renew, transfer and release rows assert provider commits and
-independent lease readback; fresh acquisition C is separate from keyless A and
-existing-lease B so one newly working caller does not invalidate another row's intended negative precondition. Acquisition replay verifies current proof.
-The direct legacy-wire rows remain fenced. The CLI
-scenario carries the validated current owner/key/version after renewal or
-transfer, rejects the superseded completion proof, and previews with the current
-proof before release. Release follows the active-lease/quiescence checks and
-must leave completion unauthorized. Capture-enabled rows still create no second
-legacy record or shadow outbox entry. When migrating an entry point, update this
-caller matrix and its downstream proof flow together; focused transaction tests
-or generic premerge canaries do not replace it. Run the owning matrix directly:
+These commands report historical evidence. They do not grant current write
+authority. New migration uses backup-bound source inspection and reviewed cold
+import into an empty canonical provider.
+
+Rollback requires an exact retained selector:
```bash
-uv run --extra test python -m pytest -q tests/control_plane/test_shadow_fence_caller_parity_e2e.py
+loopx --registry registry.json --format json \
+ coordination-shadow rollback --goal-id GOAL \
+ --provider-revision EXACT_REVISION
+
+loopx --registry registry.json --format json \
+ coordination-shadow rollback --goal-id GOAL \
+ --provider-revision EXACT_REVISION --execute
```
-Baseline delta (0fb497af8 is the PR's diff baseline; ee1b17217 the previously
-reviewed head):
-
-| Row | 0fb497af8 | ee1b17217 | Now | Basis |
-| --- | --- | --- | --- | --- |
-| native acquire settlement | committed validation receipt, `durable_writeback` / `writeback_rejected` | `validation` / `writeback_rejected`, no receipt | `validation` / `permission_denied`, no receipt | the guard ran before `commitAcquire` on every revision; only the classifier drifted; RFC section 5 forbids a fabricated receipt for `rejected` |
-| native renew, transfer, release settlement | `validation` / `permission_denied` | same | same | unchanged; acquire now matches its siblings |
-| native error text | `legacy task-lease writer is fenced; use the canonical file authority` | `legacy coordination writer is fenced` | one rendered template | remediation belongs to the caller adapter and must not name a provider |
-| Python error text | `legacy coordination writer is fenced; use the canonical file authority` | same | one rendered template | same |
-| envelope `schema_version` | check schema overwrote `task_lease_v0`; every CLI lease rejection printed `RuntimeError` | same | `task_lease_v0` plus nested `write_check` | envelope-owned keys win; check result travels under its contract name |
-| Python Todo rejections | no `error_code` | flat check keys, `schema_version` injected | `error_code` plus `write_check` | same |
-| terminal or holder verify under a fence | fence bypassed on the held and holder branches; auto-acquire branch not consulted | auto-acquire branch guarded; held branch previews reported `ok: true` and wrote receipts | every verify branch guarded before its first receipt | a preview must not succeed for a write the fence forbids |
-| committed releasing fence-close | fence bypassed | guarded | guarded; lock released in `finally`, retry `fence_token_invalid` | declared and pinned |
-| public terminal/archive after promotion | legacy fence rejection | legacy fence rejection | canonical provider result; lease admission, preview, CAS, receipt and projection semantics come from the promoted authority | the fence blocks legacy writes; it is not a rejection of the canonical replacement path |
-| generic CLI branch of `task-lease` and `turn` | typed payload spread after envelope keys | same | payload first, envelope keys win | `LocalCoordinationAuthorityUnavailable` carries a `schema_version` |
-
-Known gap, unchanged on both revisions: `loopx quota monitor-poll --execute`
-(and the scheduler monitor writeback it wraps) re-types every non-validation
-exception to `quota_unexpected_collection_error` with reason `quota collection
-failed`, so a fenced monitor writeback loses its `error_code` and remediation
-at that boundary. The fix belongs to the quota domain's public-safety
-contract (map the typed fence exception to its own code and pass `write_check`
-through) and is not part of this batch.
-
-Canonical FileAuthorityStore Todo updates, including compatibility v0 records
-already held by that authority, use the same M and maintenance boundary as
-canonical Todo creation. They retain their own transaction receipts and do not
-produce legacy shadow captures. A waiting update rechecks management state after
-acquiring M; invalid or unfinished management state holds before any commit.
-
-Drain checks the real receipt first, persists the cursor second, and reclaims
-each individually verified file last. Missing cursors can be reconstructed from
-complete continuous history; malformed or unreadable cursors are never silently
-replaced. Unknown files, mismatched bytes, wrong identities, or incomplete proof
-preserve the scene. An interrupted primary without a marker is either proven
-under the source lock or left `unproved`; an A → B → A history cannot establish
-that the first write was abandoned. TS repeats source proof under its own locks
-after Python releases its locks to call the native commit operation.
-Cursor position includes settled no-ops; its digest is the verified head's last
-applied partition marker. It stays null through bootstrap and an abandoned-only
-prefix, even with a nonempty baseline. Recovery and qualification consume that
-same marker rather than hashing the current snapshot independently.
-If cursor persistence or file reclamation fails after a verified commit, drain
-still reports the verified candidate revision. The preserved outbox can be
-replayed after repairing filesystem access; the failure does not undo the commit.
-
-For unresolved evidence, retain the directory for inspection and use exact
-rollback when abandoning the candidate. Do not delete the cursor to bypass a
-failed history check, copy an old entry into a new lineage, or reseed implicitly.
-
-Qualification explicitly reports bounded scope and
-`sustained_parity_verdict=not_evaluated`. The current proof boundary is 10,000
-transactions including the baseline; new commits hold at that boundary while
-exact receipt replays remain valid. This is a bounded correctness limit, not a
-performance result. Drain budgets bound work between operations; they do not
-preempt an in-flight filesystem or RPC call.
-
-## Roadmap and retirement boundary
-
-This batch belongs to capture lane C in the [Shared Goal Authority RFC](../../docs/architecture/rfcs/shared-goal-authority-state-provider-v0.zh-CN.md).
-Its rollback retires a **pre-promotion candidate**. Lane F still needs a separate
-fenced export/rollback after canonical writes, following lane I's exact profile,
-import, consumer and recovery qualification and explicit maintainer approval.
-The 10,000-transaction bound here grants no sustained parity or promotion.
-Long-running local use also needs lane L: an embedded store, bounded live-state
-and receipt access, accelerated capacity tests and at least ten natural days of
-soak. L can proceed alongside native Todo callers without waiting for PostgreSQL P.
-
-Retire legacy capture only after every bound primary writer has cut over to the
-qualified authority and its replay/recovery consumers have cut over too. Retire
-the separate observation handler only with the owner's Q14 decision and verified
-replacement of its remaining callers. Python guards stay until those writers no
-longer own primary effects; remove the bridge/protocol once no production caller
-needs it. The next cutover batch must report deleted product LOC, added bridge
-LOC and happy/recovery runtime crossings under the [TS migration RFC](../../docs/architecture/rfcs/typescript-control-plane-migration-v0.zh-CN.md).
-
-## Required validation
-
-Install the repository test extra and Node dependencies. Run the long tests
-separately without skip or relaxation flags:
+An unfinished historical bootstrap uses its exact operation ID instead of a
+provider revision. The selectors are mutually exclusive. Rollback archives the
+candidate and the complete Goal outbox before it publishes an inactive result.
+It does not start another capture lineage.
-```bash
-python -m pip install -e '.[test]' 'build==1.6.0'
-npm ci --ignore-scripts
-python -m pytest -q -n 4 --dist loadfile -m stage2c_e2e --durations=20 --junitxml=stage2c-e2e.xml
-python examples/shared-goal-authority-e2e/mutants.py --output .local/stage2c-mutants
-python -m build
-python examples/shared-goal-authority-e2e/installed.py --artifact dist/*.whl --report-json .local/installed-wheel.json
-python examples/shared-goal-authority-e2e/installed.py --artifact dist/*.tar.gz --report-json .local/installed-sdist.json
-python -m pytest -q -n 2
-npm run test:control-plane
-npm run typecheck:control-plane
-python -m mypy
-python -m ruff check tests loopx/canary loopx/control_plane loopx/domain_packs loopx/presentation
-python scripts/generate_coordination_state_contract.py --check
-python examples/control_plane/cli-output-budget-regression-smoke.py
-loopx --format json canary premerge --from-git-diff
-```
+## Recovery invariants
+
+### Exact bytes
+
+Inspection and backup hash the original files. Recovery never rewrites a
+malformed, unknown, or ambiguous artifact before the operator has exported it.
+Symlinks, non-regular files, path escapes, and changed bytes fail closed.
+
+### Exact identity
+
+Every recovery action binds the Goal, runtime root, source path, store identity,
+capture lineage, operation ID, and provider revision required by its historical
+schema. A result copied from another Goal cannot establish completion.
+
+### Exact receipts
+
+A committed provider transaction and matching receipt can authorize outbox
+disposition. A prepared-only entry needs source readback to classify it as
+applied or abandoned. Missing or conflicting proof remains pending.
+
+### Idempotent replay
+
+Retrying the same operation returns the durable result. Reusing an operation ID
+with different arguments fails. A replay never grants fresh execution
+authority and never advances canonical state.
+
+### Bounded work
+
+Drain respects its entry and time budgets. It reports remaining work and can be
+resumed. A failed or interrupted batch leaves enough durable evidence for the
+next process to classify the exact operation.
+
+### Lock order
+
+Historical management keeps the established cross-runtime lock order. Recovery
+must not weaken the canonical writer's maintenance lock or add a second lock
+that permits recovery and a canonical commit to race.
+
+## Qualification matrix
-The Linux/Python 3.11 CI lanes use the exact dependency versions and hashes in
-`tests/requirements-stage2c-linux-py311.txt`, including pytest 9.1.1. It builds
-the checked-out source wheel, verifies its hash during installation, and removes
-its generated build tree before normal pytest discovery. The workflow records
-the complete installation sequence and retains normal source discovery.
-
-E2E, deliberate mutants, and independently installed wheel/sdist qualification
-run in separate jobs; the stable `stage2c-correctness-e2e` check requires all three
-to succeed. No path-based skipping or reduced case selection is used. E2E uses
-four file-grouped workers so a module's shared workspaces and ordered parity rows
-stay together. Each lane has a distinct evidence artifact. Mutants remain serial
-inside their isolated source copy: parallel edits to that copy would invalidate
-the control/mutant comparison. Single-row fence probes initialize only the
-workspace they use, without removing any baseline or mutant assertion.
-
-The full TS suite's PostgreSQL conformance requires `LOOPX_TEST_POSTGRES_URL`
-pointing to a disposable test database. Source smoke success does not replace
-installed-package evidence: the package runner creates an empty environment,
-clears source-path injection, runs outside the checkout, verifies installed
-Python/TS/JSON provenance, and reads back through an independent native process.
-
-| Obligation | Retained oracle |
+| Obligation | Primary coverage |
| --- | --- |
-| Full baseline, mixed Python/native writers, handoff, todo add, monitor successor, one receipt per mutation | `test_runtime_shadow_bounded_e2e.py`, `test_shadow_drain_e2e.py` |
-| Cursor attacks, complete proof before bounded cleanup, missing cursor writer-first, dual drainers | `test_shadow_cursor_safety.py`, `test_shadow_drain_adversarial.py`, `shadow_cursor_safety.test.ts` |
-| Todo/lease abandoned prefixes, later mutations, all cursor consumers and forged applied digests | `test_shadow_cursor_recovery_e2e.py` |
-| Full caller diagnostics, argument readback and no-effect controls with absent/disabled/enabled capture | `test_shadow_observable_e2e.py` |
-| Primary and drain process death, lost ACK, prepared-only A → B → A | `test_shadow_drain_e2e.py`, `test_shadow_management_e2e.py` |
-| Every bootstrap/rollback durable window, raw archive fidelity, late requests, cached-result binding and other-Goal isolation | `shadow_management.test.ts`, `test_shadow_management_e2e.py`, `test_shadow_management_variant_e2e.py` |
-| Fence and maintenance boundaries, source and Goal override races, whole-file durability, paragraph injection, public refresh no-write boundary | `test_shadow_writer_boundaries.py`, `test_shadow_writer_variant_e2e.py`, `shadow_native_writer_boundary.test.ts`, `test_shadow_drain_adversarial.py` |
-| Canonical native/v0 Todo updates through CLI and native RPC, real pending management, M ordering, and unchanged authority on hold | `test_shadow_native_todo_update_e2e.py` |
-| History flaws despite equal snapshots, legacy mixed profile, source drift, event-only hold, qualified reads | `coordination_runtime_shadow.test.ts`, `file_outbox_qualification.test.ts`, `test_runtime_shadow_bounded_e2e.py` |
-| Ladder parity half through the public CLI and management interfaces: deferred entries, bounded and idempotent drain, primary and drain SIGKILL windows, rollback with pending entries, sustained mixed-writer parity, drift, the event-only hold, migration refusal and rebootstrap, growth measurement | `test_shared_goal_authority_e2e.py` (`s2c2.*` rows), `ladder.py --stage 2c2` |
-| Installed lifecycle and resource provenance in wheel and sdist | `installed.py` |
-| Missing checks, lock placement, duplicate mirror, early marker, cursor regression | `mutants.py` with unchanged GREEN controls and assertion RED results |
-
-The mandatory repair set must have zero failures, skips, pending, or unverified
-cases. Broader ladder rows retain their declared pending/environment gates:
-`s2c2.archive_after_leased_completion_parity` is now an executable
-deterministic row rather than a declaration, and `s2c2.sustained_parity_soak`
-stays pending until the Section 7.2 soak exists; these tests grant neither
-production promotion nor a completed Stage 2C claim.
-
-For a caller comparison, run both `test_shadow_observable*_e2e.py` files with
-`LOOPX_SHADOW_COMPARISON_SOURCE` set to an immutable baseline checkout, then to
-the reviewed source. Set `LOOPX_SHADOW_COMPARISON_OUTPUT` to separate private
-directories to retain full diagnostics and before/after fixture bytes. The oracle
-is unchanged: the baseline must fail cases for defects this batch intentionally
-repairs. Inspect every difference; normalize only documented time, path and
-generated-identity variation, and disclose changes to rejection or receipt
-behavior. CI retains the reviewed source's synthetic observations alongside the
-crash, mutant and installed-package evidence.
+| Producer-free status and drain when no history exists | `test_shadow_drain_native_plan.py` |
+| Existing prepared/committed record decoding and exact disposition | `local_authority_shadow_outbox.test.ts`, `shadow_entry_delivery.test.ts` |
+| Cursor corruption, replay, and bounded cleanup | `test_shadow_cursor_safety.py`, `shadow_cursor_safety.test.ts` |
+| Historical management replay and rollback archive fidelity | `test_shadow_management_variant_e2e.py`, `shadow_management.test.ts` |
+| Cold source inventory, original-byte hashing, and import holds | `test_cold_source_inspection.py`, `test_cold_source_import_cli.py` |
+| Archive and receipt recovery after producer removal | `test_archive_retry_delivery.py`, `test_archive_dependency_capture.py` |
+| Canonical provider business writes and exact replay | `test_canonical_todo_writer_isolation.py`, native File/SQLite/PostgreSQL conformance |
+| Installed wheel and source distribution lifecycle | `installed.py` |
+| Negative controls for deleted authority paths | `mutants.py` |
+
+Fixtures for recovery tests create the same persisted schemas that old releases
+wrote, then invoke only retained readers and disposition owners. A recovery test
+must not call a normal business mutation to manufacture its input.
+
+## Retirement boundary
+
+The shared-authority ladder no longer registers Stage 2C capture rows or the
+long-running capture soak declaration. Those claims described the deleted
+producer, not the current provider architecture.
+
+Historical reports remain valid for the release that produced them. Current
+qualification is:
+
+1. provider conformance for File, SQLite, NoKV, and PostgreSQL;
+2. public canonical creation and mutation readback;
+3. source-free installed-package retirement tests;
+4. producer-free recovery tests over existing artifact schemas.
+
+Passing historical shadow inspection does not promote a Goal. Passing
+canonical provider qualification does not authorize deleting old backups.
diff --git a/examples/shared-goal-authority-e2e/installed.py b/examples/shared-goal-authority-e2e/installed.py
index 924f693b9c..25e8106b21 100644
--- a/examples/shared-goal-authority-e2e/installed.py
+++ b/examples/shared-goal-authority-e2e/installed.py
@@ -23,9 +23,9 @@
GOAL = "installed-authority-e2e"
REQUIRED_STAGES = {
- "installed_python_ts_json_resources", "real_baseline_bootstrap",
- "three_mutations_one_receipt_each", "default_qualification_and_exact_candidate_read",
- "rollback_inactive_write_new_lineage", "installed_fallback_dispatch",
+ "installed_python_ts_json_resources", "canonical_file_creation_and_binding",
+ "direct_canonical_mutations_and_replay", "independent_provider_history_and_receipts",
+ "legacy_shadow_bootstrap_rejected", "installed_fallback_dispatch",
}
@@ -60,38 +60,52 @@ def __init__(self, artifact: Path, workspace: Path, report: dict[str, Any]) -> N
"removed_environment_keys": sorted(removed), "node": str(self.node),
"checkout_added_to_import_path": False}
- def process(self, stage: str, argv: list[str], *, timeout: int = 60, parse_json: bool = True) -> Any:
+ def process(self, stage: str, argv: list[str], *, timeout: int = 60,
+ parse_json: bool = True, succeeds: bool = True) -> Any:
started = time.monotonic()
result = subprocess.run(argv, cwd=self.cwd, env=self.environment,
text=True, capture_output=True, timeout=timeout, check=False)
self.report["processes"].append({"stage": stage, "argv": argv, "returncode": result.returncode,
"elapsed_seconds": round(time.monotonic() - started, 3),
"stdout": result.stdout, "stderr": result.stderr})
- require(result.returncode == 0, f"{stage} exited {result.returncode}: {result.stdout}\n{result.stderr}")
+ require((result.returncode == 0) is succeeds,
+ f"{stage} exited {result.returncode}: {result.stdout}\n{result.stderr}")
return json.loads(result.stdout) if parse_json else result.stdout
def checked(self, stage: str, **evidence: Any) -> None:
self.report["checks"].append({"stage": stage, "status": "passed", **evidence})
- def cli(self, stage: str, *args: str) -> dict[str, Any]:
+ def cli(self, stage: str, *args: str, succeeds: bool = True) -> dict[str, Any]:
result = self.process(stage, [str(self.console), "--registry", str(self.registry),
- "--runtime-root", str(self.runtime), "--format", "json", *args])
- require(isinstance(result, dict) and result.get("ok") is True, f"{stage} did not return ok: {result}")
+ "--runtime-root", str(self.runtime), "--format", "json", *args],
+ succeeds=succeeds)
+ require(isinstance(result, dict) and (result.get("ok") is True) is succeeds,
+ f"{stage} returned an unexpected result: {result}")
return result
- def sdk_add(self, stage: str, text: str) -> dict[str, Any]:
+ def canonical_result(self, stage: str, value: dict[str, Any]) -> dict[str, Any]:
+ require(value.get("source_authority") == "file_v0", f"{stage} used another authority: {value}")
+ require(value.get("decision_read_from_provider") is True, f"{stage} did not read the provider")
+ require(value.get("legacy_fallback_used") is False, f"{stage} used a legacy fallback")
+ require("coordination_runtime_shadow" not in value, f"{stage} emitted retired shadow capture")
+ return value
+
+ def sdk_add(self, stage: str, text: str, operation_id: str,
+ *, added: bool) -> dict[str, Any]:
code = """
import json, sys
from pathlib import Path
from loopx.todos import add_goal_todo
value = add_goal_todo(registry_path=Path(sys.argv[1]), runtime_root_arg=sys.argv[2],
- goal_id=sys.argv[3], role='agent', text=sys.argv[4], task_class='advancement_task', action_kind='analyze')
+ goal_id=sys.argv[3], role='agent', text=sys.argv[4], task_class='advancement_task',
+ action_kind='analyze', operation_id=sys.argv[5])
print(json.dumps(value))
"""
value = self.process(stage, [str(self.python), "-I", "-c", code,
- str(self.registry), str(self.runtime), GOAL, text])
- require(value.get("ok") is True and value.get("added") is True, f"{stage} did not add one Todo")
- return value
+ str(self.registry), str(self.runtime), GOAL, text, operation_id])
+ require(value.get("ok") is True and value.get("added") is added,
+ f"{stage} returned an unexpected add result: {value}")
+ return self.canonical_result(stage, value)
def read_store(self, stage: str, package: Path) -> dict[str, Any]:
module = package / "control_plane/coordination/file_authority_store.ts"
@@ -100,7 +114,7 @@ def read_store(self, stage: str, package: Path) -> dict[str, Any]:
import {join} from 'node:path';
const [modulePath, root, goal] = process.argv.slice(1);
const {FileAuthorityStore} = await import(pathToFileURL(modulePath).href);
-const store = new FileAuthorityStore(join(root, 'authority-shadow', 'file-v0'), goal, {existingOnly: true});
+const store = new FileAuthorityStore(join(root, 'authority', 'file-v0'), goal, {existingOnly: true});
const head = await store.loadAuthority();
const history = await store.scanCommitted(null, 100);
console.log(JSON.stringify({module_path: modulePath, head, history}));
@@ -158,90 +172,117 @@ def run(self) -> None:
require(fallback.get("kind") == "resolved", "empty fallback declarations must resolve")
self.checked("installed_fallback_dispatch")
- # This lifecycle qualifies pre-promotion capture. Explicitly retain the
- # supported File target-only source instead of inheriting new-Goal
- # canonical SQLite creation, which correctly fences legacy capture.
- source_storage = {"schema_version": "loopx_goal_storage_defaults_v1",
- "new_goal_provider": "file", "canonical_creation": False,
- "new_goal_handoff_mode": "hard_lease"}
- source_storage_path = self.cwd / "shadow-source-storage.json"
- source_storage_path.write_text(json.dumps(source_storage), encoding="utf-8")
- preview = self.cli("console_source_storage_preview", "machine-config", "preview",
- "--namespace", "goal_storage", "--config-json", str(source_storage_path))
- revision = preview.get("plan_revision")
- require(isinstance(revision, str) and bool(revision), "source storage preview has no revision")
- applied = self.cli("console_source_storage_apply", "machine-config", "apply",
- "--namespace", "goal_storage", "--config-json", str(source_storage_path),
- "--expected-plan-revision", revision, "--execute")
- require(applied.get("readback_verified") is True
- and applied["machine_configuration"]["namespaces"]["goal_storage"] == source_storage,
- "target-only source configuration did not read back exactly")
+ storage_defaults = {"schema_version": "loopx_goal_storage_defaults_v1", "new_goal_provider": "file",
+ "canonical_creation": True, "new_goal_handoff_mode": "hard_lease"}
+ storage_defaults_path = self.cwd / "goal-storage-defaults.json"
+ storage_defaults_path.write_text(json.dumps(storage_defaults), encoding="utf-8")
+ storage_preview = self.cli("canonical_goal_storage_preview", "machine-config", "preview", "--namespace",
+ "goal_storage", "--config-json", str(storage_defaults_path))
+ self.cli("canonical_goal_storage_apply", "machine-config", "apply", "--namespace", "goal_storage",
+ "--config-json", str(storage_defaults_path), "--expected-plan-revision",
+ str(storage_preview["plan_revision"]), "--execute")
created = self.cli("console_project_bootstrap", "bootstrap", "--project", str(self.project),
"--goal-id", GOAL, "--objective", "Qualify installed authority transactions.",
"--no-global-sync")
- require(created.get("storage_target") == {
- "schema_version": "loopx_new_goal_storage_target_v0", "provider": "file"},
- "shadow source was not created with the target-only File contract")
- selection = created["storage_selection"]
- require(selection.get("provider") == "file" and selection.get("promotion_performed") is False
- and "authority_initialized" not in selection,
- "shadow source unexpectedly initialized canonical authority")
- # Set configuration only, before shadow bootstrap creates the real binding.
- registry = json.loads(self.registry.read_text())
- goal = next(item for item in registry["goals"] if item["id"] == GOAL)
- goal["coordination"].update({"agent_model": "peer_v1", "registered_agents": ["agent-a", "agent-b"],
- "runtime_shadow": {"schema_version": "loopx_coordination_runtime_shadow_config_v0",
- "enabled": True, "provider": "file_v0"}})
- self.registry.write_text(json.dumps(registry))
- boot = self.cli("console_shadow_bootstrap", "coordination-shadow", "bootstrap", "--goal-id", GOAL, "--execute")["bootstrap"]
- require(boot.get("status") == "applied" and bool(boot.get("capture_lineage_id")), f"bootstrap not applied: {boot}")
- self.checked("real_baseline_bootstrap", capture_lineage_id=boot["capture_lineage_id"])
+ selection = created.get("storage_selection")
+ require(isinstance(selection, dict), f"bootstrap omitted storage selection: {created}")
+ require(selection.get("provider") == "file"
+ and selection.get("handoff_mode") == "hard_lease"
+ and selection.get("authority_initialized") is True
+ and selection.get("legacy_fallback_used") is False,
+ f"bootstrap did not initialize canonical File authority: {selection}")
+ require((self.runtime / "authority" / "file-v0").is_dir(),
+ "bootstrap did not create the canonical File provider directory")
+ self.cli("console_configure_agents_and_shadow_rejection_probe", "configure-goal",
+ "--goal-id", GOAL, "--registered-agent", "agent-a", "--registered-agent", "agent-b",
+ "--coordination-runtime-shadow-file", "--execute")
+ self.checked("canonical_file_creation_and_binding",
+ provider=selection["provider"], provider_revision=selection["provider_revision"])
- first = self.sdk_add("installed_python_todo_add", "First installed package mutation.")
- require(first["coordination_runtime_shadow"]["outcome"] == "delivered", f"first Todo capture failed: {first}")
+ first_operation = "installed-todo-first"
+ first = self.sdk_add("installed_python_todo_add",
+ "First installed package mutation.", first_operation, added=True)
lease = self.cli("console_native_lease_acquire", "task-lease", "acquire", "--goal-id", GOAL,
- "--todo-id", first["todo_id"], "--owner", "agent-a", "--idempotency-key", "installed-lease-a", "--ttl-seconds", "600")
- require(lease["coordination_runtime_shadow"]["outcome"] == "delivered", f"native lease capture failed: {lease}")
- second = self.sdk_add("installed_python_second_todo_add", "Third installed package mutation.")
- require(second["coordination_runtime_shadow"]["outcome"] == "delivered", f"second Todo capture failed: {second}")
- drained = self.cli("console_drain", "authority-shadow", "drain", "--goal-id", GOAL)
- require(drained.get("pending_after") == 0 and drained.get("prepared_only_after") == 0,
- f"drain left unverified work: {drained}")
- qualified = self.cli("console_qualify_default_policy", "coordination-shadow", "qualify", "--goal-id", GOAL)["qualification"]
- require(qualified.get("status") == "qualified" and qualified.get("qualified") is True,
- f"default policy was not qualified: {qualified}")
- require(qualified["policy"]["minimum_operations"] == 3 and qualified["evidence"]["operation_count"] == 3,
- "qualification did not prove exactly three real primary mutations under the default threshold")
- candidate = self.cli("console_read_candidate", "coordination-shadow", "read-candidate", "--goal-id", GOAL,
- "--todo-id", first["todo_id"])["read_candidate"]
- require(candidate.get("status") == "matched" and candidate.get("read_candidate_qualified") is True,
- f"candidate read was not qualified: {candidate}")
+ "--todo-id", first["todo_id"], "--owner", "agent-a",
+ "--idempotency-key", "installed-lease-a", "--expected-version", "0",
+ "--ttl-seconds", "600")
+ self.canonical_result("console_native_lease_acquire", lease)
+ require(lease.get("acquired") is True and lease.get("idempotent") is False,
+ f"native lease was not acquired: {lease}")
+ second_operation = "installed-todo-second"
+ second = self.sdk_add("installed_python_second_todo_add",
+ "Second installed package mutation.", second_operation, added=True)
+ before_replay = self.cli("console_canonical_todo_list_before_replay",
+ "todo", "list", "--goal-id", GOAL)
+ self.canonical_result("console_canonical_todo_list_before_replay",
+ before_replay["authority_read"])
+ receipt_before = self.cli("console_exact_receipt_before_replay", "todo", "receipt",
+ "--goal-id", GOAL, "--operation-id", first_operation)
+ self.canonical_result("console_exact_receipt_before_replay", receipt_before)
+ require(receipt_before.get("status") == "found", f"Todo receipt missing: {receipt_before}")
+
+ replayed = self.sdk_add("installed_python_todo_add_exact_replay",
+ "First installed package mutation.", first_operation, added=False)
+ require(replayed.get("status") in {"replayed", "recovered"},
+ f"Todo retry did not replay its original operation: {replayed}")
+ lease_replay = self.cli("console_native_lease_acquire_exact_replay",
+ "task-lease", "acquire", "--goal-id", GOAL, "--todo-id", first["todo_id"],
+ "--owner", "agent-a", "--idempotency-key", "installed-lease-a",
+ "--expected-version", "0", "--ttl-seconds", "600")
+ self.canonical_result("console_native_lease_acquire_exact_replay", lease_replay)
+ require(lease_replay.get("idempotent") is True and lease_replay.get("acquired") is False,
+ f"lease retry did not replay its original operation: {lease_replay}")
+ after_replay = self.cli("console_canonical_todo_list_after_replay",
+ "todo", "list", "--goal-id", GOAL)
+ self.canonical_result("console_canonical_todo_list_after_replay",
+ after_replay["authority_read"])
+ require(after_replay["todos"] == before_replay["todos"]
+ and after_replay["authority_read"]["provider_revision"]
+ == before_replay["authority_read"]["provider_revision"],
+ "exact retries advanced or changed canonical Todo authority")
+ receipt_after = self.cli("console_exact_receipt_after_replay", "todo", "receipt",
+ "--goal-id", GOAL, "--operation-id", first_operation)
+ require(receipt_after == receipt_before, "exact retry changed its historical receipt")
+ self.checked("direct_canonical_mutations_and_replay",
+ todo_ids=[first["todo_id"], second["todo_id"]],
+ lease_version=lease["lease"]["version"],
+ provider_revision=after_replay["authority_read"]["provider_revision"])
+
independent = self.read_store("independent_installed_native_readback", package)
- require(independent["head"]["status"] == "loaded" and independent["history"]["status"] == "page", "independent provider read failed")
+ require(independent["head"]["status"] == "loaded"
+ and independent["history"]["status"] == "page",
+ "independent provider read failed")
transactions = independent["history"]["transactions"]
- require(len(transactions) == 4 and independent["head"]["cursor"] == "4", "primary writes did not map one-to-one to receipts")
- require(all(len(tx["receipts"]) == 1 for tx in transactions[1:]), "a primary mutation has missing or duplicate receipts")
- old_head = independent["head"]["head"]
- require({first["todo_id"], second["todo_id"]}.issubset({item["todo_id"] for item in old_head["todos"]}), "native readback lost a Todo")
- require(any(item["todo_id"] == first["todo_id"] and item["owner"] == "agent-a" for item in old_head["leases"]), "native readback lost the lease")
- self.checked("three_mutations_one_receipt_each", mutation_count=3, cursor="4")
- self.checked("default_qualification_and_exact_candidate_read", minimum_operations=3, todo_id=first["todo_id"])
+ require(len(transactions) == 4 and independent["head"]["cursor"] == "4",
+ "canonical writes did not map one-to-one to provider transactions")
+ require(all(len(tx["receipts"]) == 1 for tx in transactions[1:]),
+ "a canonical business mutation has missing or duplicate receipts")
+ require({first_operation, second_operation}.issubset(
+ {tx["operation_id"] for tx in transactions}),
+ "provider history omitted an installed SDK operation")
+ head = independent["head"]["head"]
+ require({first["todo_id"], second["todo_id"]}.issubset(
+ {item["todo_id"] for item in head["todos"]}),
+ "native readback lost a Todo")
+ require(any(item["todo_id"] == first["todo_id"] and item["owner"] == "agent-a"
+ for item in head["leases"]), "native readback lost the lease")
+ self.checked("independent_provider_history_and_receipts",
+ transaction_count=len(transactions), cursor=independent["head"]["cursor"],
+ operation_ids=[tx["operation_id"] for tx in transactions])
- rolled = self.cli("console_rollback", "coordination-shadow", "rollback", "--goal-id", GOAL,
- "--provider-revision", independent["head"]["provider_revision"], "--execute")["rollback"]
- require(rolled.get("status") == "applied", f"rollback not applied: {rolled}")
- absent = self.read_store("independent_read_after_rollback", package)
- require(absent["head"]["status"] == "missing", "rollback left an active candidate")
- inactive = self.sdk_add("installed_python_inactive_primary_write", "Primary remains writable after rollback.")
- require(inactive["coordination_runtime_shadow"]["reason_code"] == "bootstrap_required", "inactive write fabricated capture qualification")
- require(not (self.runtime / "authority-shadow" / "outbox" / GOAL).exists(), "inactive write created outbox without a lineage")
- again = self.cli("console_new_bootstrap", "coordination-shadow", "bootstrap", "--goal-id", GOAL, "--execute")["bootstrap"]
- require(again.get("status") == "applied" and again.get("capture_lineage_id") != boot["capture_lineage_id"], "new baseline reused the retired lineage")
- final = self.read_store("independent_new_baseline_readback", package)
- require(final["head"]["status"] == "loaded" and final["head"]["cursor"] == "1", "new baseline has an invalid initial cursor")
- require(inactive["todo_id"] in {item["todo_id"] for item in final["head"]["head"]["todos"]}, "new baseline omitted the inactive primary write")
- require(len(final["history"]["transactions"]) == 1, "new baseline pretended to retain old mutation coverage")
- self.checked("rollback_inactive_write_new_lineage", previous_lineage=boot["capture_lineage_id"], new_lineage=again["capture_lineage_id"])
+ rejected = self.cli("console_legacy_shadow_bootstrap_rejected",
+ "coordination-shadow", "bootstrap", "--goal-id", GOAL, "--execute",
+ succeeds=False)["bootstrap"]
+ require(rejected.get("status") == "failed"
+ and rejected.get("reason_code") == "legacy_authority_already_promoted",
+ f"legacy shadow bootstrap did not reject canonical authority: {rejected}")
+ final = self.read_store("independent_read_after_legacy_rejection", package)
+ require(final["head"]["provider_revision"] == independent["head"]["provider_revision"]
+ and final["history"] == independent["history"],
+ "rejected legacy bootstrap changed canonical authority")
+ self.checked("legacy_shadow_bootstrap_rejected",
+ reason_code=rejected["reason_code"],
+ provider_revision=final["head"]["provider_revision"])
def main(argv: list[str] | None = None) -> int:
diff --git a/examples/shared-goal-authority-e2e/mutants.py b/examples/shared-goal-authority-e2e/mutants.py
index 1d83c05604..b6b63402bc 100644
--- a/examples/shared-goal-authority-e2e/mutants.py
+++ b/examples/shared-goal-authority-e2e/mutants.py
@@ -7,7 +7,6 @@
from __future__ import annotations
import argparse
-import ast
import difflib
import hashlib
import json
@@ -92,39 +91,6 @@ def command(self) -> list[str]:
'["advancement_task", "user_gate", "user_action", "blocker"].includes(String(condition.target_task_class))',
'true')),),
'tests/control_plane_ts/delivery_response.test.ts', 'exact dependency identity'),
- Case('rollout_cwd_root', (('loopx/cli_rollout.py', replacement(
- 'resolve_runtime_root(registry, runtime_root_arg, registry_path=registry_path)',
- 'resolve_runtime_root(registry, runtime_root_arg)')),),
- 'tests/control_plane/test_shadow_observable_e2e.py::test_registry_relative_root_does_not_depend_on_callers_cwd[disabled]'),
- Case('native_note_dropped', ((COORDINATION + 'todo_update_intent.ts', replacement(
- ' const next: JsonObject = {...todo, ...input.patch};',
- ' const next: JsonObject = {...todo, ...input.patch};\n if ("note" in input.patch) next.note = todo.note;')),),
- 'tests/control_plane/test_shadow_observable_native_e2e.py::test_native_unclaimed_edit_and_explicit_note_clear[disabled]'),
- Case('native_unclaimed_edit_rejected', ((COORDINATION + 'todo_lifecycle_decision.ts', replacement(
- ' if (todo.claimed_by !== null && todo.claimed_by !== actor) return "claim_owner_mismatch";',
- ' if (todo.claimed_by === null || todo.claimed_by !== actor) return "claim_owner_mismatch";')),),
- 'tests/control_plane/test_shadow_observable_native_e2e.py::test_native_unclaimed_edit_and_explicit_note_clear[disabled]'),
- Case('native_diagnostic_truncated', ((COORDINATION + 'todo_update_admission.ts', replacement(
- '? "Todo update cannot edit another claim owner\'s work"',
- '? "Update rejected"')),),
- 'tests/control_plane/test_shadow_observable_native_e2e.py::test_canonical_argument_intent_and_atomic_claim[disabled]'),
- Case('cursor_baseline_digest', ((COORDINATION + 'shadow_drain_plan.ts', replacement(
- 'last_partition_digest: partitionDigest(last, r.partition)',
- 'last_partition_digest: view.head_digest')),),
- 'tests/control_plane/test_shadow_cursor_recovery_e2e.py::test_abandoned_cursor_survives_all_consumers[2-0-todos]'),
- Case('qualification_baseline_digest', ((COORDINATION + 'runtime_shadow.ts', replacement(
- 'const digest = marker === null ? null : (marker as JsonObject).partition_digest;',
- 'const digest = marker === null ? localAuthorityShadowHeadDigest(anchor.projection) : (marker as JsonObject).partition_digest;')),),
- 'tests/control_plane/test_shadow_cursor_recovery_e2e.py::test_abandoned_cursor_survives_all_consumers[2-0-leases]'),
- Case('cursor_digest_unchecked', (
- (COORDINATION + 'runtime_shadow.ts', replacement(
- 'if (digest !== cursor.last_partition_digest) throw new ShadowLineageError("outbox_cursor_unproved");',
- '// DELIBERATE MUTANT: accept any syntactically valid cursor digest.')),
- (COORDINATION + 'shadow_drain_plan.ts', replacement(
- ' anchor.provider_revision === r.cursor.last_provider_revision &&\n'
- ' partitionDigest(anchor, r.partition) === r.cursor.last_partition_digest,\n',
- ' anchor.provider_revision === r.cursor.last_provider_revision,\n'))),
- 'tests/control_plane/test_shadow_cursor_recovery_e2e.py::test_forged_applied_digest_holds_every_consumer_without_rewriting_bytes[True-todos]'),
Case('lineage', ((COORDINATION + 'local_authority_shadow.ts', replacement(' requireLineage(entry.capture_lineage_id === binding.capture_lineage_id, "stale_generation");', ' // DELIBERATE MUTANT: omit active lineage validation.')),),
'tests/control_plane_ts/local_authority_shadow_outbox.test.ts', 'self-consistent foreign'),
Case('previous_partition', ((COORDINATION + 'local_authority_shadow.ts', replacement(' requireLineage(request.entry.source.previous_partition_digest === digest, "source_partition_continuity_unproved");', ' // DELIBERATE MUTANT: omit previous partition proof.')),),
@@ -143,59 +109,6 @@ def command(self) -> list[str]:
"tests/control_plane_ts/shadow_management.test.ts", 'rollback refuses a different valid candidate lineage'),
]
-PREPARED_WRITE = ''' durable_write_json(
- self._directory / entry_file_name(seq, entry_id, "prepared"),
- record,
- )'''
-CASES.extend([
- Case("receipt_bytes", ((COORDINATION + "shadow_drain_plan.ts", replacement(
- "entry[key] === null || entry[key] === rc[key]",
- "entry[key] === null || entry[key] === entry[key]")),),
- "tests/control_plane/test_shadow_drain_adversarial.py::test_raw_residue_mismatch_preserves_every_file_before_any_cleanup"),
- Case("cursor_regression", ((COORDINATION + "shadow_drain_plan.ts", replacement(
- "{last_seq: history.size, last_entry_id: last.operation_id",
- "{last_seq: 1, last_entry_id: last.operation_id")),),
- "tests/control_plane/test_shadow_drain_e2e.py::test_public_primary_maps_one_to_one_to_receipts_and_replays_idempotently"),
- Case("early_committed", ((COORDINATION + "local_authority_shadow_outbox.py", replacement(
- PREPARED_WRITE, PREPARED_WRITE + '''
- durable_write_json(
- self._directory / entry_file_name(seq, entry_id, "committed"),
- {"schema_version": OUTBOX_COMMIT_SCHEMA, "entry_id": entry_id,
- "capture_lineage_id": self._lineage_id, "committed_at": utc_now_text()},
- )''')),),
- "tests/control_plane/test_shadow_drain_e2e.py::test_primary_sigkill_preserves_complete_bytes_and_proves_before_marker[before_replace]"),
-])
-
-# Ladder parity-half rows (s2c2.*) drive the same lifecycle through the public
-# CLI; these mutants remove one operator-visible truth each and must turn the
-# corresponding row red.
-LADDER_ROW = "tests/control_plane/test_shared_goal_authority_e2e.py::test_ladder_row_passes_or_is_declared_unverified"
-CASES.extend([
- Case("status_hides_prepared_only", ((COORDINATION + "local_authority_shadow_history.py", replacement(
- ' "committed_pending": sum(1 for entry in entries if entry.is_committed),\n'
- ' "prepared_only": sum(1 for entry in entries if not entry.is_committed),',
- ' "committed_pending": len(entries),\n'
- ' "prepared_only": 0,')),),
- LADDER_ROW + "[s2c2.sigkill_between_primary_write_and_drain]"),
- Case("qualification_ignores_drift", ((COORDINATION + "runtime_shadow.ts", replacement(
- "const matched = localAuthorityShadowHeadDigest(request.projection) === localAuthorityShadowHeadDigest(lineage.head.head);",
- "const matched = true;")),),
- LADDER_ROW + "[s2c2.parity_divergent_detects_foreign_edit]"),
- Case("replay_counted_as_delivery", ((COORDINATION + "shadow_drain.ts", replacement(
- "result.no_op += Number(noOp === true); result.entries.push(summary); result.replayed++; consumed++;",
- "result.no_op += Number(noOp === true); result.entries.push(summary); result.delivered++; consumed++;")),),
- LADDER_ROW + "[s2c2.sigkill_mid_drain]"),
-])
-
-
-def remove_fence(source: str) -> str:
- function = next(node for node in ast.parse(source).body
- if isinstance(node, ast.FunctionDef)
- and node.name == "require_legacy_coordination_write_allowed")
- lines = source.splitlines(keepends=True)
- return "".join(lines[:function.body[0].lineno - 1]) + " return\n" + "".join(lines[function.end_lineno:])
-
-
def typescript_exported_function_span(source: str, name: str) -> tuple[int, int]:
"""Bound one top-level exported function without depending on its successor."""
@@ -215,49 +128,19 @@ def remove_native_update_maintenance(source: str) -> str:
"return await withCanonicalWriter(root, goalId, input.dry_run === true, async () => {",
"return await (async () => {",
)(function)
- function = replacement(" });\n } catch (error) {", " })();\n } catch (error) {")(function)
+ function = replacement(
+ ' }, {businessWrite: "todo_update"});\n } catch (error) {',
+ " })();\n } catch (error) {",
+ )(function)
return source[:start] + function + source[end:]
-def move_todo_guard_outside_lock(source: str) -> str:
- function = next(node for node in ast.parse(source).body
- if isinstance(node, ast.FunctionDef)
- and node.name == "legacy_todo_write_transaction")
- guards = [node for node in ast.walk(function) if isinstance(node, ast.If)
- and isinstance(node.test, ast.UnaryOp) and isinstance(node.test.op, ast.Not)
- and isinstance(node.test.operand, ast.Name) and node.test.operand.id == "dry_run"
- and len(node.body) == 1 and not node.orelse
- and isinstance(node.body[0], ast.Expr) and isinstance(node.body[0].value, ast.Call)
- and isinstance(node.body[0].value.func, ast.Name)
- and node.body[0].value.func.id == "require_legacy_todo_write_allowed"]
- locks = [node for node in function.body if isinstance(node, ast.With)]
- if len(guards) != 1 or len(locks) != 1 or guards[0] not in locks[0].body:
- raise ValueError("guard locator drift; review lock protocol")
- guard, lock = guards[0], locks[0]
- lines = source.splitlines(keepends=True)
- # Both race oracles exercise the same combined admission owner. Move its
- # whole dry-run condition so the locked body remains executable.
- segment = "".join(line[4:] for line in lines[guard.lineno - 1:guard.end_lineno])
- return "".join(lines[:lock.lineno - 1]) + segment + "".join(
- lines[lock.lineno - 1:guard.lineno - 1] + lines[guard.end_lineno:]
- )
-
-
WRITER_TEST = "tests/control_plane/test_shadow_writer_boundaries.py::"
-FENCE_TEST = WRITER_TEST + "test_cli_waiting_for_todo_mutex_rechecks_fence_after_engagement"
CASES.extend([
Case("management_result_binding", ((COORDINATION + "shadow_management.ts", replacement(
" await validateReplayResult(request, manifest, prior.result, state);",
" // DELIBERATE MUTANT: trust a cached result from another operation.")),),
"tests/control_plane/test_shadow_management_variant_e2e.py::test_rollback_result_cannot_borrow_another_goals_archive_evidence[historical]"),
- Case("cross_goal_source_guard", ((COORDINATION + "legacy_writer_fence.py", replacement(
- " resolved_source = state_file.resolve(strict=False)",
- " return # DELIBERATE MUTANT: allow another goal to bypass source authority.\n resolved_source = state_file.resolve(strict=False)")),),
- "tests/control_plane/test_shadow_writer_variant_e2e.py::test_other_goal_cannot_write_a_protected_goal_source_via_state_override[active_capture]"),
- Case("cleanup_hides_verified_commit", ((COORDINATION + "shadow_drain.ts", replacement(
- " if (plan.view !== null && typeof plan.view === \"object\") observe(plan.view as JsonObject);\n",
- "")),),
- "tests/control_plane/test_shadow_drain_adversarial.py::test_cleanup_permission_failure_reports_verified_commit_and_recovers[before_commit]"),
Case("native_update_maintenance", ((COORDINATION + "local_authority_runtime.ts",
remove_native_update_maintenance),),
"tests/control_plane/test_shadow_native_todo_update_e2e.py::test_native_update_holds_before_primary_for_management[native-bootstrapping-cli]"),
@@ -265,12 +148,6 @@ def move_todo_guard_outside_lock(source: str) -> str:
"movable.slice(0, moveCount)", "movable.slice(-moveCount)")),),
"tests/control_plane_ts/todo_archive_selection.test.ts",
"archive selection preserves imported order"),
- Case("remove_fence", ((COORDINATION + "legacy_writer_fence.py", remove_fence),), FENCE_TEST),
- Case("fence_outside_lock", ((COORDINATION + "legacy_writer_fence.py",
- move_todo_guard_outside_lock),), FENCE_TEST),
- Case("source_binding_outside_lock", ((COORDINATION + "legacy_writer_fence.py",
- move_todo_guard_outside_lock),),
- WRITER_TEST + "test_waiting_override_writer_rechecks_registry_binding_inside_shared_state_lock"),
Case("remove_refresh_source_recheck", (("loopx/state_refresh.py", replacement(
" if normalized_next_action:",
" if False and normalized_next_action: # DELIBERATE MUTANT: bypass source recheck.")),),
@@ -284,65 +161,15 @@ def move_todo_guard_outside_lock(source: str) -> str:
'withFileMutationLock(statePath + ".mutant-unshared", () =>')),),
"tests/control_plane_ts/shadow_native_writer_boundary.test.ts",
"^fence engagement waits for an existing state writer before publication$"),
- Case("remove_bound_state_path", ((COORDINATION + "legacy_writer_fence.py", replacement(
- "if bound_source.resolve(strict=False) != state_file.resolve(strict=False):",
- "if False:")),),
- "tests/control_plane/test_shadow_drain_adversarial.py::test_state_file_override_cannot_attribute_an_unbound_source_to_active_lineage"),
- Case("bootstrap_unregistered_root", ((COORDINATION + "runtime_shadow.ts", replacement(
- "if (!await sameSourceRuntimeRoot(registeredRoot, request.runtime_root))",
- "if (false)")),),
- "tests/control_plane/test_runtime_shadow_bounded_e2e.py::test_controller_cannot_bind_the_registered_source_to_an_override_runtime_root"),
- Case("bootstrap_unregistered_state", ((COORDINATION + "runtime_shadow.ts", replacement(
- "if (resolve(String(snapshot.state_path)) !== resolve(String(snapshot.registered_state_path)))",
- "if (false && resolve(String(snapshot.state_path)) !== resolve(String(snapshot.registered_state_path)))")),),
- "tests/control_plane/test_runtime_shadow_bounded_e2e.py::test_controller_cannot_bind_an_alternate_state_file_before_or_after_bootstrap"),
- Case("locale_dependent_lease_order", ((COORDINATION + "runtime_shadow.ts", replacement(
- '''.sort((left, right) => {
- if (left < right) return -1;
- if (left > right) return 1;
- return 0;
- });''', ".sort((left, right) => left.localeCompare(right));")),),
- "tests/control_plane/test_runtime_shadow_bounded_e2e.py::test_source_snapshot_preserves_inventory_without_projecting_orphan_leases"),
])
-# Restore both halves of the obsolete mirror: the public CLI hook and an actual
-# second FileAuthorityStore commit. A fabricated RPC result would prove nothing.
-MIRROR_HOOK = ''' if payload.get("ok") and payload.get("added") and not payload.get("dry_run"):
- from ..control_plane.effect_runtime import effect_runtime_result as restored_mirror
- restored_mirror("coordination.runtime_shadow.commit", {
- "runtime_root": str(resolve_runtime_root(load_registry(registry_path), runtime_root_arg)),
- "goal_id": args.goal_id, "operation_id": "restored-snapshot:" + payload["todo_id"],
- })
-'''
-MIRROR_COMMIT = ''' const request = requireJsonObject(_value, "restored snapshot request");
- const root = String(request.runtime_root); const goal = String(request.goal_id);
- return await withShadowMaintenanceLock(root, goal, async () => {
- const store = new FileAuthorityStore(join(root, "authority-shadow", "file-v0"), goal, { existingOnly: true });
- const head = await store.loadAuthority();
- if (head.status !== "loaded") throw new Error("restored mirror needs a real existing baseline");
- const result = await store.commitAuthority({expected_provider_revision: head.provider_revision,
- operation_id: String(request.operation_id), next_projection: head.head,
- events: [{schema_version: "loopx_coordination_runtime_shadow_event_v0", event_kind: "todo_add"}],
- receipts: [{schema_version: "loopx_coordination_runtime_shadow_receipt_v0", operation_id: request.operation_id}]});
- return {...result};
- });'''
-CASES.append(Case("duplicate_mirror", (
- ("loopx/cli_commands/todo.py", replacement(" print_payload(\n payload,\n",
- MIRROR_HOOK + " print_payload(\n payload,\n")),
- (COORDINATION + "runtime_shadow.ts", replacement(
- ''' return { schema_version: COORDINATION_RUNTIME_SHADOW_RESULT_SCHEMA, status: "failed",
- reason_code: "legacy_lineage_read_only", primary_writeback_preserved: true, decision_read_from_shadow: false };''',
- MIRROR_COMMIT)),
-), "tests/control_plane/test_shadow_drain_e2e.py::test_public_mutation_has_no_second_snapshot_mirror"))
-
-# Fenced sibling-caller parity: each mutant is caught by exactly one parity row.
-# Promoted CLI acquisition now succeeds through canonical authority. Probe the
-# retained native legacy entrypoint for its fence diagnostic/envelope contract.
+# The retained exact-release path still observes the durable promotion fence.
CASES.append(Case("native_fence_remediation_truncated", (
(COORDINATION + "legacy_writer_fence.ts", replacement(
'export const LEGACY_WRITER_FENCED_REMEDIATION =\n "legacy coordination writer is fenced; use the promoted canonical authority ({authority_mode}) for goal {goal_id}; fence {fence_id}; the primary record was not changed";',
'export const LEGACY_WRITER_FENCED_REMEDIATION =\n "legacy coordination writer is fenced";')),
-), "tests/control_plane_ts/legacy_writer_fence_caller_parity.test.ts", pattern="^fence parity: ts-acquire-engaged$"))
+), "tests/control_plane_ts/task_lease_lifecycle.test.ts",
+ pattern="^exact legacy release honors an engaged promotion fence without writes$"))
CASES.append(Case("python_fence_remediation_truncated", (
(COORDINATION + "legacy_writer_fence.py", replacement(
'LEGACY_WRITER_FENCED_REMEDIATION = (\n "legacy coordination writer is fenced; use the promoted canonical authority "\n "({authority_mode}) for goal {goal_id}; fence {fence_id}; "\n "the primary record was not changed"\n)',
@@ -352,17 +179,15 @@ def move_todo_guard_outside_lock(source: str) -> str:
(COORDINATION + "legacy_writer_fence.ts", replacement(
" this.payload = { write_check: writeCheck };",
" this.payload = writeCheck;")),
-), "tests/control_plane_ts/legacy_writer_fence_caller_parity.test.ts", pattern="^fence parity: ts-acquire-engaged$"))
-CASES.append(Case("fence_acquire_receipt_fabricated", (
- ("loopx/control_plane/work_items/task_lease_acquire.ts", replacement(
- ' return { code: error.code, message: error.message, payload: error.payload, stage: "validation", kind: "permission_denied" };',
- ' return { code: error.code, message: error.message, payload: error.payload };')),
-), "tests/control_plane_ts/legacy_writer_fence_caller_parity.test.ts", pattern="^fence parity: ts-acquire-engaged$"))
+), "tests/control_plane_ts/task_lease_lifecycle.test.ts",
+ pattern="^exact legacy release honors an engaged promotion fence without writes$"))
CASES.append(Case("lifecycle_fence_guard_skipped", (
("loopx/control_plane/work_items/task_lease_lifecycle.ts", replacement(
- " await requireLegacyCoordinationPrimaryWriteAllowed(request!.runtime_root, request!.goal_id);\n",
- "")),
-), "tests/control_plane_ts/legacy_writer_fence_caller_parity.test.ts", pattern="^fence parity: ts-release-engaged$"))
+ " await requireLegacyCoordinationPrimaryWriteAllowed(request.runtime_root, request.goal_id);\n"
+ " const decision = retainedReleaseDecision(request, existing, at);",
+ " const decision = retainedReleaseDecision(request, existing, at);")),
+), "tests/control_plane_ts/task_lease_lifecycle.test.ts",
+ pattern="^exact legacy release honors an engaged promotion fence without writes$"))
def run(case: Case, directory: Path, log: Path) -> subprocess.CompletedProcess[str]:
diff --git a/examples/value-connectors-finance-probe-doc-smoke.py b/examples/value-connectors-finance-probe-doc-smoke.py
index 2f08cc4974..e5bf0f992e 100644
--- a/examples/value-connectors-finance-probe-doc-smoke.py
+++ b/examples/value-connectors-finance-probe-doc-smoke.py
@@ -44,17 +44,30 @@ def main() -> int:
"finance_market_snapshot",
)
assert completed.returncode == 0, completed.stderr
- payload = json.loads(completed.stdout)
+ packet = json.loads(completed.stdout)
+ assert packet["ok"] is True, packet
if command == "source-map":
- migration = payload["source_profiles"][0]["migration"]
+ profiles = packet["source_profiles"]
elif command == "install-check":
- migration = payload["checks"][0]["migration"]
+ profiles = packet["checks"]
else:
- migration = payload
- assert migration["status"] == "migrated_to_extension", payload
- assert migration["replacement_extension_id"] == "loopx-finance-value-discovery", payload
- assert migration["replacement_capability_id"] is None, payload
- assert migration["truth_contract"]["legacy_connector_executes_finance"] is False, payload
+ profiles = None
+ if profiles is not None:
+ assert len(profiles) == 1, profiles
+ profile = profiles[0]
+ assert profile["connector_id"] == "finance_market_snapshot", profile
+ assert profile["status"] == "migrated_to_extension", profile
+ migration = profile["migration"]
+ else:
+ migration = packet
+ assert migration["schema_version"] == "value_connector_extension_migration_v0", migration
+ assert migration["status"] == "migrated_to_extension", migration
+ assert migration["replacement_extension_id"] == "loopx-finance-value-discovery", migration
+ assert migration["replacement_capability_id"] is None, migration
+ truth = migration["truth_contract"]
+ assert truth["legacy_connector_executes_finance"] is False, truth
+ assert truth["external_reads_performed"] is False, truth
+ assert truth["external_writes_performed"] is False, truth
doc = " ".join(DOC.read_text(encoding="utf-8").lower().split())
for marker in (
diff --git a/examples/visible-governance-slice-smoke.py b/examples/visible-governance-slice-smoke.py
deleted file mode 100644
index d37c32b13d..0000000000
--- a/examples/visible-governance-slice-smoke.py
+++ /dev/null
@@ -1,477 +0,0 @@
-#!/usr/bin/env python3
-"""Smoke-test the ``visible_governance_slice_v0`` governance projection.
-
-Builds a synthetic multi-agent fixture with soft claims, a task lease,
-decision scopes, and a user gate, then projects the full governance slice
-and validates every section. All assertions are public-safe -- no raw
-paths, credentials, or private payloads.
-"""
-
-from __future__ import annotations
-
-import json
-import sys
-from pathlib import Path
-
-REPO_ROOT = Path(__file__).resolve().parents[1]
-if str(REPO_ROOT) not in sys.path:
- sys.path.insert(0, str(REPO_ROOT))
-
-from loopx.control_plane.testing.canary_harness import ( # noqa: E402
- run_json_cli,
- write_fixture_registry,
-)
-from loopx.quota import build_quota_should_run # noqa: E402
-from loopx.status import collect_status # noqa: E402
-from loopx.todos import ( # noqa: E402
- add_goal_todo,
- complete_goal_todo,
- list_goal_todos,
-)
-from loopx.visible_governance import ( # noqa: E402
- build_visible_governance_slice,
- render_visible_governance_markdown,
-)
-
-
-GOAL_ID = "governance-slice-smoke"
-AGENT_ALPHA = "codex-alpha"
-AGENT_BETA = "codex-beta"
-
-
-def _assert_public_safe_payload(payload: dict, *, label: str) -> None:
- """Verify a payload contains no raw paths, URLs, or credential markers."""
- text = json.dumps(payload, sort_keys=True, ensure_ascii=False)
- assert "/Users/" not in text, f"{label}: absolute path leak"
- assert "https://" not in text, f"{label}: URL leak"
- assert "pass" + "word" not in text.lower(), f"{label}: credential leak"
- assert "api_" + "key" not in text.lower(), f"{label}: API key leak"
- assert "C:\\" not in text, f"{label}: Windows path leak"
-
-
-def _write_fixture(tmp_path: Path) -> tuple[Path, Path, Path]:
- """Create a multi-agent fixture with claims, gates, and a task lease."""
- project = tmp_path / "project"
- runtime = tmp_path / "runtime"
- registry = project / ".loopx" / "registry.json"
- state = project / ".codex" / "goals" / GOAL_ID / "ACTIVE_GOAL_STATE.md"
- state.parent.mkdir(parents=True)
- state.write_text(
- "---\nstatus: active\n---\n\n"
- "# Active Goal State\n\n"
- "## Agent Todo\n\n"
- "- [ ] Alpha advancement task\n"
- "- [ ] Beta advancement task\n\n"
- "## User Todo\n\n"
- "- [ ] Review and approve the release scope\n",
- encoding="utf-8",
- )
- write_fixture_registry(
- project=project,
- runtime_root=runtime,
- registry_path=registry,
- goal_id=GOAL_ID,
- domain="loopx-platform",
- adapter_kind="harness_self_improvement",
- registered_agents=[AGENT_ALPHA, AGENT_BETA],
- quota_allowed_slots=10,
- )
- return registry, runtime, state
-
-
-def _add_agent_todo(
- registry: Path,
- *,
- text: str,
- claimed_by: str,
- task_class: str = "advancement_task",
- required_decision_scopes: list[str] | None = None,
-) -> dict:
- """Add an agent todo, optionally with required decision scopes.
-
- Scopes use the ``kind:granularity:scope_key`` string format
- (e.g. ``"release:action:governance-bridge"``).
- """
- kwargs: dict = {
- "registry_path": registry,
- "goal_id": GOAL_ID,
- "role": "agent",
- "text": text,
- "task_class": task_class,
- "claimed_by": claimed_by,
- }
- if required_decision_scopes:
- kwargs["required_decision_scopes"] = required_decision_scopes
- return add_goal_todo(**kwargs)
-
-
-def _add_user_gate(
- registry: Path,
- *,
- text: str,
- blocks_agent: str | None = None,
- unblocks_todo_id: str | None = None,
- decision_scope: str | None = None,
-) -> dict:
- """Add a user gate, optionally linked to an agent todo or scope.
-
- ``decision_scope`` uses the ``kind:granularity:scope_key`` string format.
- """
- kwargs: dict = {
- "registry_path": registry,
- "goal_id": GOAL_ID,
- "role": "user",
- "text": text,
- "task_class": "user_gate",
- }
- if blocks_agent:
- kwargs["blocks_agent"] = blocks_agent
- if unblocks_todo_id:
- kwargs["unblocks_todo_id"] = unblocks_todo_id
- if decision_scope:
- kwargs["decision_scope"] = decision_scope
- return add_goal_todo(**kwargs)
-
-
-def _add_user_action(
- registry: Path,
- *,
- text: str,
- bound_agent: str | None = None,
-) -> dict:
- """Add a non-blocking user action."""
- kwargs: dict = {
- "registry_path": registry,
- "goal_id": GOAL_ID,
- "role": "user",
- "text": text,
- "task_class": "user_action",
- }
- if bound_agent:
- kwargs["bound_agent"] = bound_agent
- return add_goal_todo(**kwargs)
-
-
-def main() -> None:
- import tempfile
-
- print("=== Visible Governance Slice Smoke ===\n")
-
- with tempfile.TemporaryDirectory() as tmp:
- tmp_path = Path(tmp)
- registry, runtime, state = _write_fixture(tmp_path)
-
- # ── 1. Create agent todos with claims ──
- alpha_todo = _add_agent_todo(
- registry,
- text="Alpha builds the core governance bridge.",
- claimed_by=AGENT_ALPHA,
- required_decision_scopes=["direction:action:governance-bridge"],
- )
- beta_todo = _add_agent_todo(
- registry,
- text="Beta reviews the integration surface.",
- claimed_by=AGENT_BETA,
- )
- print(
- f" [OK] Agent todos: alpha={alpha_todo['todo_id']}, "
- f"beta={beta_todo['todo_id']}"
- )
-
- # ── 2. Create a user gate that covers alpha's required scope ──
- gate = _add_user_gate(
- registry,
- text="Approve the governance bridge release scope.",
- blocks_agent=AGENT_ALPHA,
- decision_scope="direction:goal:*",
- )
- print(f" [OK] User gate: {gate['todo_id']} (goal-scoped, blocks alpha)")
-
- # ── 3. Create a non-blocking user action ──
- action = _add_user_action(
- registry,
- text="Review the dashboard budget projection.",
- bound_agent=AGENT_ALPHA,
- )
- print(f" [OK] User action: {action['todo_id']}")
-
- # ── 4. Acquire a task lease for alpha ──
- lease_result = run_json_cli(
- "task-lease",
- "acquire",
- "--goal-id",
- GOAL_ID,
- "--todo-id",
- alpha_todo["todo_id"],
- "--owner",
- AGENT_ALPHA,
- "--idempotency-key",
- "smoke-lease-key-001",
- "--ttl-seconds",
- "2700",
- "--write-scope",
- "governance-bridge/*",
- registry_path=registry,
- runtime_root=runtime,
- )
- assert lease_result["ok"] is True, f"lease acquire failed: {lease_result}"
- assert lease_result["acquired"] is True
- print(f" [OK] Task lease acquired for alpha on {alpha_todo['todo_id']}")
-
- # ── 5. Collect status ──
- status_payload = collect_status(
- registry_path=registry,
- runtime_root_override=str(runtime),
- scan_roots=[Path(__file__).resolve()],
- limit=20,
- )
- assert isinstance(status_payload, dict)
- print(" [OK] Status collected")
-
- # ── 6. Build quota should_run for alpha ──
- decision = build_quota_should_run(
- status_payload,
- goal_id=GOAL_ID,
- agent_id=AGENT_ALPHA,
- )
- assert isinstance(decision, dict)
- assert "effective_action" in decision
- print(f" [OK] Quota should_run built: effective={decision['effective_action']}")
-
- # ── 7. Build visible governance slice ──
- gov_slice = build_visible_governance_slice(
- status_payload,
- goal_id=GOAL_ID,
- agent_id=AGENT_ALPHA,
- include_task_leases=True,
- )
- assert gov_slice["ok"] is True, f"slice not ok: {gov_slice}"
- assert gov_slice["schema_version"] == "visible_governance_slice_v0"
- assert gov_slice["goal_id"] == GOAL_ID
- assert gov_slice["agent_id"] == AGENT_ALPHA
-
- # ── 8. Validate every section ──
-
- # claims
- claims = gov_slice["claims"]
- assert isinstance(claims, dict)
- assert claims["total_claims"] >= 2, f"expected >= 2 claims, got {claims}"
- assert claims["claiming_agent_count"] >= 2
- assert claims["current_agent_claims"] >= 1
- assert AGENT_ALPHA in claims["by_agent"]
- assert AGENT_BETA in claims["by_agent"]
- for c in claims["claims"]:
- assert "todo_id" in c
- assert "claimed_by" in c
- print(
- f" [OK] Claims: {claims['total_claims']} claims "
- f"across {claims['claiming_agent_count']} agents"
- )
-
- # leases
- leases = gov_slice["leases"]
- assert isinstance(leases, dict)
- assert leases["active_leases"] >= 1
- assert leases["current_agent_active_leases"] >= 1
- assert leases["nature"] == "optional runtime fence, not authority"
- for lease_entry in leases["leases"]:
- assert "todo_id" in lease_entry
- assert "owner" in lease_entry
- assert "expires_at" in lease_entry
- print(
- f" [OK] Leases: {leases['active_leases']} active, "
- f"nature='{leases['nature']}'"
- )
-
- # quota
- quota = gov_slice["quota"]
- assert isinstance(quota, dict)
- for key in ("should_run", "effective_action", "state"):
- assert key in quota, f"quota missing {key}"
- print(f" [OK] Quota: should_run={quota['should_run']}, action={quota['effective_action']}")
-
- # scheduler hints
- sched = gov_slice["scheduler_hints"]
- if sched is not None:
- assert isinstance(sched, dict)
- for key in ("action", "cadence_class", "arbitration"):
- assert key in sched, f"scheduler_hints missing {key}"
- print(f" [OK] Scheduler hints: cadence={sched['cadence_class']}")
- else:
- print(" [OK] Scheduler hints: None (no agent-lane context)")
-
- # decision scopes
- scopes = gov_slice["decision_scopes"]
- assert isinstance(scopes, dict)
- consistency = scopes["required_scope_consistency"]
- assert isinstance(consistency, dict)
- assert "status" in consistency
- print(
- f" [OK] Decision scopes: consistency={consistency['status']}, "
- f"checked={consistency['checked_agent_todo_count']} todos"
- )
-
- # authority boundary (implementation availability, not per-Goal promotion)
- boundary = gov_slice["authority_boundary"]
- assert isinstance(boundary, list)
- assert len(boundary) >= 5, f"expected >= 5 RFC entries, got {len(boundary)}"
- expected_shipped = {
- "3 -- State Classification": True,
- "4 -- Coordination Ledger Shape": False,
- "5.1 -- claim_work command": False,
- "7 -- Receipt Retention": True,
- "8 -- Local vs Shared Mode": True,
- "Appendix B -- handoff_mode": True,
- "9 -- Offline/Local Boundaries": False,
- }
- by_section = {entry["rfc_section"]: entry for entry in boundary}
- for section, expect_shipped in expected_shipped.items():
- assert section in by_section, f"missing RFC section: {section}"
- entry = by_section[section]
- assert "proposal" in entry
- assert "shipped_equivalent" in entry
- assert "gap" in entry
- assert entry["shipped"] is expect_shipped, (
- f"{section}: shipped={entry['shipped']!r}, "
- f"expected {expect_shipped} after native prototype retirement"
- )
- claim_work = by_section["5.1 -- claim_work command"]
- claim_text = (
- f"{claim_work['shipped_equivalent']} {claim_work['gap']}".lower()
- )
- assert "retired" in claim_text and "todo_claim.ts" in claim_text
- assert "not current execution authority" in claim_text
- shipped_count = sum(1 for e in boundary if e["shipped"])
- assert shipped_count == sum(
- 1 for value in expected_shipped.values() if value
- ), f"unexpected shipped count: {shipped_count}"
- print(
- f" [OK] Authority boundary: {len(boundary)} RFC sections, "
- f"{shipped_count} implemented, "
- f"{len(boundary) - shipped_count} original proposals not shipped"
- )
-
- # ── Negative: active lease is never write authority ──
- assert leases["active_leases"] >= 1
- assert leases["nature"] == "optional runtime fence, not authority"
- lease_blob = json.dumps(leases, sort_keys=True).lower()
- for forbidden in (
- "write authority",
- "runtime authority",
- "browser write",
- "write api",
- ):
- assert forbidden not in lease_blob, (
- f"leases projection must not present '{forbidden}'"
- )
- # The coverage-only claim_work reference must not imply lease authority.
- assert claim_work["shipped"] is False
- assert "optional runtime fence" in leases["nature"]
- print(
- " [OK] Negative: active leases remain fence-only "
- "(not write authority / no browser write API)"
- )
-
- # ── 9. Public safety scan ──
- _assert_public_safe_payload(gov_slice, label="governance-slice")
- print(" [OK] Public safety scan passes")
-
- # ── 10. include_task_leases=False ──
- no_lease_slice = build_visible_governance_slice(
- status_payload,
- goal_id=GOAL_ID,
- agent_id=AGENT_ALPHA,
- include_task_leases=False,
- )
- assert no_lease_slice["leases"] is None
- print(" [OK] include_task_leases=False omits leases section")
-
- # ── 11. Missing goal_id ──
- bad_slice = build_visible_governance_slice(status_payload, goal_id="")
- assert bad_slice["ok"] is False
- assert "error" in bad_slice
- print(" [OK] Empty goal_id returns error")
-
- # ── 12. agent_id=None works ──
- no_agent_slice = build_visible_governance_slice(
- status_payload,
- goal_id=GOAL_ID,
- agent_id=None,
- )
- assert no_agent_slice["ok"] is True
- assert no_agent_slice["agent_id"] is None
- print(" [OK] agent_id=None produces valid slice")
-
- # ── 13. Markdown rendering ──
- markdown = render_visible_governance_markdown(gov_slice)
- assert "Visible Governance Slice" in markdown
- assert GOAL_ID in markdown
- assert AGENT_ALPHA in markdown
- assert "Claims (Soft Ownership)" in markdown
- assert "Task Leases (Optional Runtime Fence)" in markdown
- assert "Quota Decision" in markdown
- assert "Scheduler Hints" in markdown
- assert "Decision Scopes" in markdown
- assert "Authority Boundary" in markdown
- assert "optional runtime fence, not authority" in markdown
- assert "Proposals shipped" in markdown
- assert "Stage-2" in markdown or "Stage 2" in markdown or "additive" in markdown
- assert "not write authority" in markdown.lower() or (
- "optional fence, not write authority" in markdown.lower()
- )
- # Verify no private markers in rendered markdown
- assert "/Users/" not in markdown
- assert "https://" not in markdown
- assert "C:\\" not in markdown
- print(" [OK] Markdown render includes all sections")
- print(" [OK] Markdown is public-safe")
- print(" [OK] Markdown keeps Stage-2 / lease-authority boundary explicit")
-
- # ── 14. Complete the gate and verify standing authority ──
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=gate["todo_id"],
- agent_id=AGENT_ALPHA,
- decision_outcome="approve",
- )
- status2 = collect_status(
- registry_path=registry,
- runtime_root_override=str(runtime),
- scan_roots=[Path(__file__).resolve()],
- limit=20,
- )
- gov_slice2 = build_visible_governance_slice(
- status2,
- goal_id=GOAL_ID,
- agent_id=AGENT_ALPHA,
- )
- standing = gov_slice2["decision_scopes"]["standing_decision_authority"]
- if standing is not None:
- assert standing["active_count"] >= 1
- print(
- f" [OK] Standing authority after gate approval: "
- f"{standing['active_count']} active receipts"
- )
- else:
- print(" [OK] Standing authority: None (gate scoped to different items)")
-
- # ── 15. Verify the slice is read-only (no mutations in builder) ──
- todos_before = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)
- _recheck = build_visible_governance_slice(
- status2,
- goal_id=GOAL_ID,
- agent_id=AGENT_BETA,
- include_task_leases=True,
- )
- todos_after = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)
- assert todos_before == todos_after, (
- "build_visible_governance_slice must not mutate todos"
- )
- print(" [OK] Slice builder is read-only (no mutations)")
-
- print("\n=== All smoke checks passed ===")
-
-
-if __name__ == "__main__":
- main()
diff --git a/loopx/__init__.py b/loopx/__init__.py
index d473ce0095..13aa310863 100644
--- a/loopx/__init__.py
+++ b/loopx/__init__.py
@@ -2,4 +2,4 @@
__all__ = ["__version__"]
-__version__ = "1.4.2"
+__version__ = "2.0.0a1"
diff --git a/loopx/bootstrap.py b/loopx/bootstrap.py
index 1ae08d3a67..e4f3871300 100644
--- a/loopx/bootstrap.py
+++ b/loopx/bootstrap.py
@@ -13,8 +13,11 @@
derive_goal_display_name as derive_goal_display_name,
)
from .registry import find_registry_goal, resolve_state_file
-from .control_plane.coordination.local_authority import read_canonical_todos_if_promoted
-from .control_plane.coordination.legacy_writer_fence import legacy_todo_write_transaction, require_legacy_state_replacement_allowed
+from .control_plane.coordination.local_authority import (
+ read_canonical_todos_if_promoted,
+ require_canonical_authority,
+)
+from .control_plane.coordination.legacy_writer_fence import require_legacy_state_replacement_allowed
from .control_plane.coordination.legacy_writer_fence import require_registry_source_write_allowed
from .control_plane.projects.registry_codec import (
load_project_registry,
@@ -393,6 +396,8 @@ def bootstrap_project(
existing_authority = (read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=goal_id)
if previous_goal is not None and not canonical_creation else None)
canonical_existing = existing_authority is not None
+ if not canonical_creation and not canonical_existing:
+ require_canonical_authority(goal_id=goal_id, operation="goal_create")
if canonical_creation and previous_goal is not None and force:
raise ValueError("Canonical creation cannot rebuild existing state; restore or migrate through its owning operation")
if canonical_existing and force:
@@ -530,7 +535,6 @@ def bootstrap_project(
"error": str(global_writability.get("error") or "global registry is not writable"),
}
storage_selection = None
- shadow_capture = None
shadow_evidence: dict[str, Any] = {}
if not dry_run:
# Reconnect enters the same typed receipt owner before compatibility
@@ -545,10 +549,7 @@ def bootstrap_project(
registry_path,
operation="bootstrap_registry",
create=dict,
- ) as registry_transaction, (nullcontext() if canonical_existing else legacy_todo_write_transaction(
- registry_path, goal_id, state_file, None, "bootstrap_state", canonical_creation,
- runtime_root=runtime_root,
- )):
+ ) as registry_transaction, nullcontext():
current_registry = registry_transaction.payload_copy()
current_goal = find_registry_goal(current_registry, goal_id)
if canonical_existing and current_goal != previous_goal:
@@ -607,20 +608,7 @@ def bootstrap_project(
if canonical_bootstrap_transport:
require_registry_source_write_allowed(registry_path=registry_path, runtime_root=runtime_root,
goal_id=goal_id, state_file=state_file)
- if not canonical_creation:
- from .control_plane.coordination.runtime_shadow_writer_adapter import (
- begin_todo_runtime_shadow_capture,
- require_runtime_shadow_capture_prepared,
- )
-
- shadow_capture = begin_todo_runtime_shadow_capture(registry_path=registry_path,
- runtime_root=runtime_root, goal_id=goal_id, state_path=state_file,
- write_class="bootstrap_state", original_text=original)
- shadow_capture.prepare(planned)
- require_runtime_shadow_capture_prepared(shadow_capture, runtime_root=runtime_root, goal_id=goal_id)
atomic_write_state_text(state_file, planned)
- if shadow_capture is not None:
- shadow_capture.committed()
if todo_source_migration is not None:
todo_source_migration["applied"] = True
current_registry.setdefault("schema_version", "0.1")
@@ -633,11 +621,6 @@ def bootstrap_project(
# retarget that Goal. The TS owner refuses replacing an existing provider.
if not canonical_existing:
storage_selection = initialize_goal_storage_target(runtime_root, find_registry_goal(registry, goal_id) or {}, registry_path=registry_path)
- if shadow_capture is not None:
- from .control_plane.coordination.runtime_shadow_writer_adapter import settle_todo_runtime_shadow_capture
-
- shadow_evidence = settle_todo_runtime_shadow_capture({}, registry_path=registry_path,
- runtime_root=runtime_root, goal_id=goal_id, capture=shadow_capture, emit_disabled=False)
if sync_global:
global_sync = sync_project_registry_to_global(
registry_path=registry_path,
diff --git a/loopx/canary/maintainability_ratchet.py b/loopx/canary/maintainability_ratchet.py
index 4ccc88748f..ebad2d40c4 100644
--- a/loopx/canary/maintainability_ratchet.py
+++ b/loopx/canary/maintainability_ratchet.py
@@ -49,13 +49,6 @@ def _exception(
REVIEWED_MAINTAINABILITY_EXCEPTIONS: dict[str, dict[str, Any]] = {
- "dependency_debt:loopx.control_plane.todos.legacy_mutation->loopx.capabilities.explore.research_frontier": _exception(
- "The unpromoted legacy Todo writer was moved out of loopx.todos without changing its "
- "explicit Explore completion evidence guard. The existing capability owner still "
- "qualifies that evidence; omitting the guard would let legacy completion bypass M3.",
- "Retire this bridge with the legacy writer, or replace it with a provider-neutral "
- "injected terminal guard before removing this exact exception.",
- ),
"compatibility_facade:loopx.quota": _exception(
"The public loopx.quota import surface remains a supported compatibility contract, "
"including presentation-owned quota event renderers.",
diff --git a/loopx/canary/planner.py b/loopx/canary/planner.py
index 4a180c1022..d2ac7ed152 100644
--- a/loopx/canary/planner.py
+++ b/loopx/canary/planner.py
@@ -432,9 +432,9 @@
),
},
{
- "command": "python3 examples/control_plane/run-compaction-readmodel-smoke.py",
+ "command": "python3 examples/control_plane/run-history-readmodel-smoke.py",
"tier": "default",
- "reason": "guards status wrapper parity for compact run read-model and summary projection attachment",
+ "reason": "guards status run-history parity, compact reward/gate privacy, and bounded history projection",
},
{
"command": "python3 examples/control_plane/goal-channel-readmodel-smoke.py",
diff --git a/loopx/capabilities/configuration_ui.py b/loopx/capabilities/configuration_ui.py
index df00f6677a..c1850581a5 100644
--- a/loopx/capabilities/configuration_ui.py
+++ b/loopx/capabilities/configuration_ui.py
@@ -100,7 +100,7 @@ def capability_configuration_editor(
options=["file", "sqlite"], required=True,
description="Fixed at creation. Existing Goals need a separate backed-up migration."),
_field("canonical_creation", "Create canonical authority", "boolean", required=True,
- description="Enabled by default for new Goals. Disabled retains the post-promotion target behavior."),
+ description="Required for new Goals in 2.0. Enable before creating a Goal; import existing Markdown Goals separately."),
_field("new_goal_handoff_mode", "New Goal execution policy", "select",
options=["soft_claim", "hard_lease"], required=True,
description="Used only with canonical creation. Agents inherit the Goal policy; this grants no tool permissions.")],
diff --git a/loopx/capabilities/connector_registry/tests/test_connector_registry.py b/loopx/capabilities/connector_registry/tests/test_connector_registry.py
index 06c4b2f7d4..29e3d63b19 100644
--- a/loopx/capabilities/connector_registry/tests/test_connector_registry.py
+++ b/loopx/capabilities/connector_registry/tests/test_connector_registry.py
@@ -71,6 +71,10 @@ def test_cli_rejects_negative_elapsed_time_without_persisting(tmp_path: Path) ->
sys.executable,
"-m",
"loopx.cli",
+ "--registry",
+ str(tmp_path / "loopx-registry.json"),
+ "--runtime-root",
+ str(tmp_path / "runtime"),
"connector",
"use",
"sina-daily",
@@ -125,6 +129,10 @@ def test_cli_path_override_reads_the_registry_it_writes(tmp_path: Path) -> None:
sys.executable,
"-m",
"loopx.cli",
+ "--registry",
+ str(tmp_path / "loopx-registry.json"),
+ "--runtime-root",
+ str(tmp_path / "runtime"),
"connector",
"register",
"probe-cli",
@@ -151,6 +159,10 @@ def test_cli_path_override_reads_the_registry_it_writes(tmp_path: Path) -> None:
sys.executable,
"-m",
"loopx.cli",
+ "--registry",
+ str(tmp_path / "loopx-registry.json"),
+ "--runtime-root",
+ str(tmp_path / "runtime"),
"connector",
"list",
"--path",
@@ -171,6 +183,10 @@ def test_cli_path_override_reads_the_registry_it_writes(tmp_path: Path) -> None:
sys.executable,
"-m",
"loopx.cli",
+ "--registry",
+ str(tmp_path / "loopx-registry.json"),
+ "--runtime-root",
+ str(tmp_path / "runtime"),
"connector",
"rank",
"--path",
diff --git a/loopx/capabilities/issue_fix/cli.py b/loopx/capabilities/issue_fix/cli.py
index 65d8ad868a..69c9caa772 100644
--- a/loopx/capabilities/issue_fix/cli.py
+++ b/loopx/capabilities/issue_fix/cli.py
@@ -1356,14 +1356,15 @@ def handle_issue_fix_command(
or todo_write.get("metadata_updated")
or todo_write.get("status_changed")
)
+ created_todo = todo_write["todo"]
payload["todo_write"] = {
"schema_version": "issue_fix_maintainer_correction_todo_write_v0",
"write_performed": todo_changed,
"already_exists": bool(todo_write.get("already_exists")),
"todo_id": todo_write.get("todo_id"),
"role": todo_write.get("role"),
- "claimed_by": todo_write.get("claimed_by"),
- "blocks_agent": todo_write.get("blocks_agent"),
+ "claimed_by": created_todo.get("claimed_by"),
+ "blocks_agent": created_todo.get("blocks_agent"),
"path_recorded": False,
}
payload["todo_write_performed"] = todo_changed
diff --git a/loopx/capabilities/machine_configuration/goal_storage.py b/loopx/capabilities/machine_configuration/goal_storage.py
index 616e2779f8..d1c6373dcf 100644
--- a/loopx/capabilities/machine_configuration/goal_storage.py
+++ b/loopx/capabilities/machine_configuration/goal_storage.py
@@ -7,7 +7,6 @@
from .contract import MachineConfigurationNamespace
from ...control_plane.effect_runtime import effect_runtime_result
-
from ...control_plane.coordination.new_goal_storage_transport import (
CANONICAL_GOAL_STORAGE_DEFAULTS_SCHEMA, NEW_GOAL_STORAGE_METHOD,
)
diff --git a/loopx/chat.py b/loopx/chat.py
index 12ec2880ec..23f63e9aee 100644
--- a/loopx/chat.py
+++ b/loopx/chat.py
@@ -710,8 +710,8 @@ def _compact_todo_payload(payload: dict[str, Any], *, applied: bool) -> dict[str
def _todo_preview_fingerprint(payload: dict[str, Any]) -> str:
compact = _compact_todo_payload(payload, applied=False)
if payload.get("decision_read_from_provider") is True:
- # A create dry-run allocates a proposed operation identity without a
- # write. Its random Todo id is not the reviewed intent or source head.
+ # Bind the transport operation to reviewed intent and provider head.
+ # The provider allocates Todo identity from that operation separately.
compact["todo"].pop("todo_id")
return _stable_digest(
{
@@ -761,7 +761,7 @@ def _todo_no_write_receipt(
"outcome": "preview_stale",
"write_attempted": write_attempted,
**({"current_preview_id": _todo_preview_fingerprint(current_preview)}
- if not write_attempted else {}),
+ if not write_attempted and state_revision is None else {}),
"state_revision": state_revision or _todo_revision(current_preview),
}
receipt["receipt_id"] = _stable_digest(receipt)
@@ -776,6 +776,7 @@ def _add_review_todo(
priority: str | None = None,
dry_run: bool,
expected_provider_revision: str | None = None,
+ operation_id: str | None = None,
) -> dict[str, Any]:
return add_goal_todo(
registry_path=registry_path,
@@ -786,11 +787,41 @@ def _add_review_todo(
task_class="advancement_task",
action_kind=CHAT_TODO_ACTION_KIND,
dry_run=dry_run,
+ **({"operation_id": operation_id} if operation_id is not None else {}),
**({"expected_provider_revision": expected_provider_revision}
if expected_provider_revision is not None else {}),
)
+def _bound_todo_review_preview(
+ *, registry_path: Path, goal_id: str, text: str, priority: str | None,
+) -> dict[str, Any]:
+ payload = _add_review_todo(
+ registry_path=registry_path, goal_id=goal_id, text=text, priority=priority, dry_run=True,
+ )
+ if payload.get("decision_read_from_provider") is not True:
+ return payload
+ # Reuse the existing canonical operation contract instead of predicting a
+ # Todo id here. Both dry-runs are read-only; the second refuses a changed
+ # provider head. Confirmation uses this same operation and revision.
+ try:
+ return _add_review_todo(
+ registry_path=registry_path, goal_id=goal_id, text=text, priority=priority, dry_run=True,
+ operation_id="chat-todo:" + _todo_preview_fingerprint(payload),
+ expected_provider_revision=_todo_revision(payload),
+ )
+ except LocalCoordinationAuthorityUnavailable as exc:
+ if exc.code != "provider_revision_mismatch":
+ raise
+ raise TodoReviewPreviewConflict(
+ "stale todo preview; preview the proposal again before applying",
+ receipt=_todo_no_write_receipt(
+ goal_id=goal_id, current_preview=payload,
+ state_revision=exc.payload.get("current_provider_revision"),
+ ),
+ ) from exc
+
+
def build_todo_review_preview(
*,
registry_path: Path,
@@ -798,12 +829,8 @@ def build_todo_review_preview(
text: str,
priority: str | None = None,
) -> dict[str, Any]:
- payload = _add_review_todo(
- registry_path=registry_path,
- goal_id=goal_id,
- text=text,
- priority=priority,
- dry_run=True,
+ payload = _bound_todo_review_preview(
+ registry_path=registry_path, goal_id=goal_id, text=text, priority=priority,
)
compact = _compact_todo_payload(payload, applied=False)
compact["preview_id"] = _todo_preview_fingerprint(payload)
@@ -818,12 +845,8 @@ def apply_todo_review_preview(
priority: str | None = None,
preview_id: str,
) -> dict[str, Any]:
- current_preview = _add_review_todo(
- registry_path=registry_path,
- goal_id=goal_id,
- text=text,
- priority=priority,
- dry_run=True,
+ current_preview = _bound_todo_review_preview(
+ registry_path=registry_path, goal_id=goal_id, text=text, priority=priority,
)
if not preview_id or preview_id != _todo_preview_fingerprint(current_preview):
raise TodoReviewPreviewConflict(
@@ -840,7 +863,8 @@ def apply_todo_review_preview(
text=text,
priority=priority,
dry_run=False,
- **({"expected_provider_revision": _todo_revision(current_preview)}
+ **({"expected_provider_revision": _todo_revision(current_preview),
+ "operation_id": current_preview["operation_id"]}
if current_preview.get("decision_read_from_provider") is True else {}),
)
except LocalCoordinationAuthorityUnavailable as exc:
diff --git a/loopx/cli_commands/project.py b/loopx/cli_commands/project.py
index d19d1f9f54..86bf6c54e9 100644
--- a/loopx/cli_commands/project.py
+++ b/loopx/cli_commands/project.py
@@ -8,6 +8,7 @@
from collections.abc import Callable, Mapping
from pathlib import Path
+from ..capabilities.machine_configuration.goal_storage import new_goal_storage_target
from ..control_plane.projects.registry import (
PROJECT_KINDS,
bind_session,
@@ -165,6 +166,7 @@ def handle_project_command(
stop_condition=args.stop_condition,
repository_bindings=args.repository,
external_locator_bindings=args.external_locator,
+ resolve_new_goal_storage_target=new_goal_storage_target,
goal_instance_profile=args.goal_instance_profile,
operation_id=args.operation_id,
registered_agents=getattr(args, "registered_agent", []),
diff --git a/loopx/cli_commands/quota_context.py b/loopx/cli_commands/quota_context.py
index 7baa0c58a2..6c8498eb24 100644
--- a/loopx/cli_commands/quota_context.py
+++ b/loopx/cli_commands/quota_context.py
@@ -5,8 +5,8 @@
from dataclasses import dataclass
from pathlib import Path
-from ..control_plane.scheduler.provider_monitor_poll import (
- require_monitor_poll_source_available,
+from ..control_plane.coordination.local_authority import (
+ read_canonical_todos_if_promoted,
)
from ..control_plane.quota.error_codes import QuotaCommandValidationError
from ..control_plane.runtime.status_projection_cache import (
@@ -291,9 +291,10 @@ def prepare_quota_command_context(
runtime_root_override=runtime_root_arg,
)
if command == "monitor-poll" and args.execute and (args.todo_id or args.target_key):
- # Canonical availability precedes unrelated status/quota preparation.
- # The eventual transaction repeats its fence check under the writer lock.
- require_monitor_poll_source_available(
+ # A selected provider outage precedes unrelated status preparation.
+ # An unmigrated source may still recover its original quota transaction;
+ # fresh-work admission remains inside that transaction's source lock.
+ read_canonical_todos_if_promoted(
runtime_root=runtime_root, goal_id=args.goal_id,
)
# Observation commands use the same scoped collector/cache as execution
diff --git a/loopx/cli_commands/todo_argument_validation.py b/loopx/cli_commands/todo_argument_validation.py
index 2f4982ebf5..eda6ec3cb6 100644
--- a/loopx/cli_commands/todo_argument_validation.py
+++ b/loopx/cli_commands/todo_argument_validation.py
@@ -499,6 +499,10 @@ def validate_todo_complete_options(args: argparse.Namespace) -> None:
)
if args.completion_identity_key and not args.no_follow_up:
raise ValueError("--completion-identity-key is only valid with --no-follow-up")
+ if args.completion_identity_key and (args.note or args.evidence):
+ raise ValueError(
+ "--completion-identity-key reentry preserves the original note and evidence"
+ )
if any(getattr(args, field) for field in ("task_repository", "bound_agent", "goal_bound", "blocks_agent", "clear_blocks_agent", "excluded_agents", "clear_excluded_agents", "global_gate", "clear_global_gate", "unblocks_todo_id", "resume_when")):
raise ValueError("todo complete does not update current todo routing metadata; use todo update first")
if any(getattr(args, field) for field in ("monitor_target_key", "cadence", "next_due_at", "expires_at")):
@@ -509,7 +513,11 @@ def validate_todo_complete_options(args: argparse.Namespace) -> None:
raise ValueError("--no-follow-up cannot be combined with successor todos")
if args.successor_todo_ids and (args.next_agent_todo or args.next_user_todo):
raise ValueError("--successor-todo-id links existing work and cannot be combined with --next-agent-todo or --next-user-todo")
- if args.no_follow_up and not (args.note or args.evidence):
+ if (
+ args.no_follow_up
+ and not args.completion_identity_key
+ and not (args.note or args.evidence)
+ ):
raise ValueError("--no-follow-up requires --note or --evidence")
if args.continuation_policy:
raise ValueError("todo complete does not update --continuation-policy; use todo update first")
diff --git a/loopx/cli_commands/todo_registration.py b/loopx/cli_commands/todo_registration.py
index fe60eefed6..ef02ecc0db 100644
--- a/loopx/cli_commands/todo_registration.py
+++ b/loopx/cli_commands/todo_registration.py
@@ -93,7 +93,8 @@ def register_todo_command(
help=(
"For todo complete --no-follow-up lifecycle reentry, reuse the "
"exact completion identity projected by LoopX. This is not a quota "
- "turn id and cannot be combined with --turn-instance-id."
+ "turn id and cannot be combined with --turn-instance-id, --note, "
+ "or --evidence."
),
)
todo_parser.add_argument(
@@ -418,8 +419,9 @@ def register_todo_command(
"--no-follow-up",
action="store_true",
help=(
- "For todo update/complete, record a structured no-follow-up rationale "
- "when a completed todo intentionally has no successor."
+ "For ordinary todo update/complete, record a structured no-follow-up "
+ "rationale when a completed todo intentionally has no successor. "
+ "Lifecycle reentry preserves the original completion prose."
),
)
register_todo_successor_creation_arguments(todo_parser)
diff --git a/loopx/cli_commands/turn_todo_writeback.py b/loopx/cli_commands/turn_todo_writeback.py
index 303255334d..15ecdf2f0e 100644
--- a/loopx/cli_commands/turn_todo_writeback.py
+++ b/loopx/cli_commands/turn_todo_writeback.py
@@ -21,8 +21,8 @@ def write_turn_repair_update(
``runtime_root_arg`` is required on purpose: the Turn settlement must
hand down the same ``--runtime-root`` override the dispatch resolved, so
- the legacy writer fence and the todo mutex of a promotion cannot split
- from the Turn writeback path.
+ the canonical provider route and its maintenance guard cannot split from
+ the Turn writeback path.
"""
update_goal_todo(
diff --git a/loopx/collaboration_mcp.py b/loopx/collaboration_mcp.py
index 2f2b997811..6994599d72 100644
--- a/loopx/collaboration_mcp.py
+++ b/loopx/collaboration_mcp.py
@@ -35,7 +35,6 @@
effect_runtime_request_scope, effect_runtime_result, EffectRuntimeRemoteError,
)
from .control_plane.coordination.local_authority import local_authority_is_promoted
-from .control_plane.todos.handoff_mode import show_goal_handoff_mode
from .control_plane.turn_driver.journal_store import (
load_turn_journal,
turn_journal_path,
@@ -1558,20 +1557,16 @@ def _acquire_delegation_lease(
) -> dict:
"""Acquire the promoted hard lease before worker or completion effects."""
- if not local_authority_is_promoted(
- runtime_root=self.root,
- goal_id=self.goal_id,
- ):
- if row.get("task_lease", {}).get("required") is True:
- raise ValueError("delegation canonical authority disappeared; reconcile the original execution")
- row["task_lease"] = {"required": False, "handoff_mode": "legacy"}
- self._fenced_write(path, row)
- return row["task_lease"]
- handoff_mode = show_goal_handoff_mode(
- registry_path=self.registry,
- runtime_root_arg=str(self.root),
- goal_id=self.goal_id,
- )["handoff_mode"]
+ from .control_plane.work_items.task_lease import inspect_task_lease
+
+ observed = inspect_task_lease(registry_path=self.registry, runtime_root=self.root,
+ goal_id=self.goal_id, todo_id=binding["todo_id"])
+ support = observed.get("execution_policy")
+ if not isinstance(support, dict) or type(support.get("supported")) is not bool:
+ raise RuntimeError("lease inspection omitted typed execution-policy support")
+ if not support["supported"]:
+ raise ValueError(str(support.get("reason") or "current execution policy is unavailable"))
+ handoff_mode = observed["handoff_mode"]
if handoff_mode != "hard_lease":
if row.get("task_lease", {}).get("required") is True:
raise ValueError("delegation authority mode changed; reconcile the original execution")
@@ -1730,9 +1725,10 @@ def _execute(self, path: Path, row: dict, binding: dict) -> None:
self._write_delegation_bootstrap(row, binding)
self._acquire_delegation_lease(path, row, binding)
self._observe(path, row, "running")
- if row.get("task_lease", {}).get("required") is True and "lease" not in row["task_lease"]:
- # Upgrade a still-current operation record by replaying its
- # original claim, not by inventing a replacement execution.
+ elif row["status"] == "running":
+ # Resumption rechecks typed policy support even for old records
+ # that said no lease was required. Hard mode replays its original
+ # claim proof; it cannot invent a replacement execution.
self._acquire_delegation_lease(path, row, binding)
if row["status"] == "running":
self._raise_if_stop_requested(path)
diff --git a/loopx/configuration_catalog.py b/loopx/configuration_catalog.py
index e8f8d3dac2..d6787bd17e 100644
--- a/loopx/configuration_catalog.py
+++ b/loopx/configuration_catalog.py
@@ -242,8 +242,8 @@ def build_goal_configuration_catalog(
"cutover to canonical local authority."
),
"effect": (
- "Captures transaction-bound Todo and task-lease mutations in "
- "the file-v0 shadow used by parity qualification."
+ "Retains inspection and bootstrap controls for pre-cutoff "
+ "file-v0 shadow lineage; normal writes no longer feed it."
),
"does_not": [
"promote the Goal or fence legacy writers",
diff --git a/loopx/control_plane/collaboration/delegation_result_use.ts b/loopx/control_plane/collaboration/delegation_result_use.ts
index 15c7a1f643..b200fdf89f 100644
--- a/loopx/control_plane/collaboration/delegation_result_use.ts
+++ b/loopx/control_plane/collaboration/delegation_result_use.ts
@@ -15,10 +15,11 @@ type Input = {operation_id: string; ref: string; sha256: string; input_ref: stri
type Node = {operation_id: string; accepted: boolean; artifacts: {ref: string; sha256: string}[]; inputs: Input[]};
export const DELEGATION_RESULT_USE_MAX_OPERATIONS = 64;
export const DELEGATION_RESULT_USE_MAX_DEPTH = 16;
+export const DELEGATION_RESULT_USE_MAX_SECONDS = 15;
export function delegationResultUseLimits(): JsonObject {
return {max_operations: DELEGATION_RESULT_USE_MAX_OPERATIONS,
- max_depth: DELEGATION_RESULT_USE_MAX_DEPTH, max_seconds: 15};
+ max_depth: DELEGATION_RESULT_USE_MAX_DEPTH, max_seconds: DELEGATION_RESULT_USE_MAX_SECONDS};
}
function requireThat(value: unknown, message: string): asserts value {
diff --git a/loopx/control_plane/coordination/goal_storage_initialization.py b/loopx/control_plane/coordination/goal_storage_initialization.py
new file mode 100644
index 0000000000..e6d113bbba
--- /dev/null
+++ b/loopx/control_plane/coordination/goal_storage_initialization.py
@@ -0,0 +1,75 @@
+"""Initialize a new Goal from its frozen canonical storage target."""
+
+from __future__ import annotations
+
+from collections.abc import Callable, Mapping
+import hashlib
+from pathlib import Path
+from typing import Any
+
+from ...agent_registry import registered_agent_ids_for_goal
+from ...registry import resolve_state_file
+from ..effect_runtime import effect_runtime_result
+from .authority_source_capture import (
+ authority_registry_source,
+ build_runtime_shadow_source_snapshot,
+)
+from .new_goal_storage_transport import NEW_GOAL_STORAGE_METHOD
+
+
+def initialize_goal_storage_target(
+ runtime_root: Path,
+ goal: Mapping[str, Any],
+ *,
+ registry_path: Path | None = None,
+ validate_source: Callable[[str], None] | None = None,
+) -> dict[str, Any] | None:
+ target = (goal.get("coordination") or {}).get("storage_target")
+ if target is None:
+ return None
+ request = {
+ "action": "initialize",
+ "runtime_root": str(runtime_root),
+ "goal_id": goal["id"],
+ "target": target,
+ }
+ if target.get("schema_version") == "loopx_new_goal_storage_target_v1":
+ if registry_path is None:
+ raise ValueError("Canonical creation requires its registered source")
+ state_path = resolve_state_file(Path(goal["repo"]), goal["state_file"])
+ with authority_registry_source(registry_path) as witness:
+ request.update(
+ creation_operation_id=goal.get("creation_operation_id"),
+ source_snapshot={
+ "state_path": str(state_path.resolve()),
+ "registry_source": {
+ **witness,
+ "registered_agents": registered_agent_ids_for_goal(dict(goal)),
+ },
+ },
+ )
+ result = effect_runtime_result(NEW_GOAL_STORAGE_METHOD, request)
+ if result.get("source_capture_required") is not True:
+ return result
+ # Only the typed owner can request unfinished creation's source. A
+ # completed receipt must remain readable without its display bytes.
+ validated_bytes = None
+ if validate_source is not None:
+ validated_bytes = state_path.read_bytes()
+ validate_source(validated_bytes.decode("utf-8"))
+ projection, snapshot = build_runtime_shadow_source_snapshot(
+ goal=goal,
+ runtime_root=runtime_root,
+ state_path=state_path,
+ registry_path=registry_path,
+ )
+ if validated_bytes is not None and snapshot["state_bytes_sha256"] != (
+ "sha256:" + hashlib.sha256(validated_bytes).hexdigest()
+ ):
+ raise ValueError("Goal creation source changed during validation; retry")
+ request.update(
+ creation_operation_id=goal.get("creation_operation_id"),
+ projection=projection,
+ source_snapshot=snapshot,
+ )
+ return effect_runtime_result(NEW_GOAL_STORAGE_METHOD, request)
diff --git a/loopx/control_plane/coordination/handoff_mode_legacy_plan.ts b/loopx/control_plane/coordination/handoff_mode_legacy_plan.ts
deleted file mode 100644
index 86e95f95a6..0000000000
--- a/loopx/control_plane/coordination/handoff_mode_legacy_plan.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-/** Typed compatibility plan. Python owns source locks/capture; TS owns the change. */
-import type {JsonObject} from "../effect_program.ts";
-import {requireJsonObject, requireStringLiteral} from "../runtime_decode.ts";
-import {HANDOFF_MODES, decideHandoffMode} from "./handoff_mode_policy.ts";
-import {handoffQuiescence, persistedHandoffMode, previousModeFields} from "./handoff_mode_facts.ts";
-
-export const LEGACY_HANDOFF_PLAN_SCHEMA = "loopx_legacy_handoff_mode_plan_request_v0";
-const RESULT_SCHEMA = "loopx_legacy_handoff_mode_plan_result_v0";
-
-/** Edit only the generated scalar field. All other bytes, including CRLF and
- * Unicode line separators inside quoted metadata, remain untouched. A duplicate
- * key has no single writable owner, so do not silently patch only one copy. */
-export function patchHandoffMode(text: string, requested: string): string {
- const opening = /^---[ \t]*\r?\n/.exec(text)?.[0];
- if (!opening) throw new Error("state_frontmatter_missing");
- const fields: {start: number; end: number; ending: string}[] = [];
- let close: number | undefined;
- // JavaScript multiline ^ also recognizes U+2028/U+2029. The generated
- // metadata protocol uses physical LF only, including inside JSON strings.
- for (let start = opening.length; start <= text.length;) {
- const newline = text.indexOf("\n", start);
- const end = newline < 0 ? text.length : newline + 1;
- const raw = text.slice(start, end);
- const line = raw.replace(/\r?\n$/, "");
- if (/^---[ \t]*$/.test(line)) { close = start; break; }
- if (/^[ \t]*handoff_mode[ \t]*:/.test(line)) {
- fields.push({start, end, ending: /\r?\n$/.exec(raw)?.[0] ?? ""});
- }
- if (newline < 0) break;
- start = end;
- }
- if (close === undefined) throw new Error("state_frontmatter_missing");
- if (fields.length > 1) throw new Error("handoff_mode_duplicate_field");
- const field = fields[0];
- if (field) return text.slice(0, field.start) + `handoff_mode: ${requested}${field.ending}` + text.slice(field.end);
- const newline = opening.endsWith("\r\n") ? "\r\n" : "\n";
- return text.slice(0, close) + `handoff_mode: ${requested}${newline}` + text.slice(close);
-}
-
-export function planLegacyHandoffMode(value: unknown): JsonObject {
- const input = requireJsonObject(value, "legacy handoff mode plan");
- if (input.schema_version !== LEGACY_HANDOFF_PLAN_SCHEMA) throw new Error("legacy handoff plan schema mismatch");
- const requested = requireStringLiteral(input.requested_mode, HANDOFF_MODES, "requested_mode");
- if (typeof input.frontmatter_text !== "string") throw new Error("frontmatter_text must be a string");
- const previous = persistedHandoffMode(input.previous_value);
- const fields = {...previousModeFields(previous), handoff_mode: requested};
- if (requested === "legacy") return {schema_version: RESULT_SCHEMA,
- ...decideHandoffMode(previous, requested, 0, 0), ...fields};
- // An identical valid mode grants no new behavior. Preserve the compatibility
- // no-op even when leases exist or the legacy state has no frontmatter.
- if (previous.kind === "valid" && previous.value === requested) {
- return {schema_version: RESULT_SCHEMA, outcome: "no_change", code: "handoff_mode_unchanged",
- changed: false, ...fields};
- }
- if (input.todos === null && input.leases === null) return {schema_version: RESULT_SCHEMA,
- outcome: "snapshot_required", ...fields, changed: false};
- if (!Array.isArray(input.todos) || !Array.isArray(input.leases)) throw new Error("complete Todo and lease facts required");
- let facts;
- try {
- facts = handoffQuiescence(input.todos.map(row => requireJsonObject(row, "Todo")),
- input.leases.map(row => requireJsonObject(row, "lease")), String(input.observed_at));
- } catch (error) {
- return {schema_version: RESULT_SCHEMA, outcome: "rejected", code: "invalid_handoff_mode_authority",
- ...fields, changed: false, reason: String(error)};
- }
- const plan = decideHandoffMode(previous, requested, facts.claimed_todos.length, facts.active_leases.length);
- if (plan.outcome === "rejected") return {schema_version: RESULT_SCHEMA, ...plan, ...fields,
- changed: false, ...facts};
- try {
- return {schema_version: RESULT_SCHEMA, ...plan, ...fields, changed: true,
- next_frontmatter_text: patchHandoffMode(input.frontmatter_text, requested)};
- } catch (error) {
- if (!(error instanceof Error) || !["state_frontmatter_missing", "handoff_mode_duplicate_field"].includes(error.message)) throw error;
- return {schema_version: RESULT_SCHEMA, outcome: "rejected", code: error.message, ...fields, changed: false};
- }
-}
diff --git a/loopx/control_plane/coordination/handoff_mode_migration.ts b/loopx/control_plane/coordination/handoff_mode_migration.ts
index ef0f96af50..5bb21118f0 100644
--- a/loopx/control_plane/coordination/handoff_mode_migration.ts
+++ b/loopx/control_plane/coordination/handoff_mode_migration.ts
@@ -11,7 +11,7 @@ import {AuthorityStoreProtocolError, canonicalAuthorityObject, canonicalAuthorit
import {exportAuthorityArchive, verifyAuthorityArchive, type AuthorityArchiveSummary} from "./authority_archive.ts";
import {syncAuthorityDirectory} from "./file_authority_store.ts";
import {CoordinationCommandReceipt} from "./command_receipt.ts";
-import {HANDOFF_MODES, EXECUTION_HANDOFF_MODES} from "./handoff_mode_policy.ts";
+import {HANDOFF_MODES, EXECUTION_HANDOFF_MODES, type ExecutionHandoffMode} from "./handoff_mode_policy.ts";
import {planHandoffPolicyMigration} from "./handoff_policy_migration.ts";
import {normalizeRegisteredTodoAgents} from "./todo_agents.ts";
@@ -29,7 +29,7 @@ interface ReviewedPlan extends JsonObject {
schema_version: typeof PLAN_SCHEMA;
goal_id: string;
runtime_root: string;
- requested_mode: typeof EXECUTION_HANDOFF_MODES[number];
+ requested_mode: ExecutionHandoffMode;
registered_agents: string[];
source: JsonObject;
}
diff --git a/loopx/control_plane/coordination/handoff_mode_policy.ts b/loopx/control_plane/coordination/handoff_mode_policy.ts
index c768eca892..74c4484126 100644
--- a/loopx/control_plane/coordination/handoff_mode_policy.ts
+++ b/loopx/control_plane/coordination/handoff_mode_policy.ts
@@ -1,13 +1,21 @@
-/** One quiescence rule for the legacy adapter and canonical mode transaction. */
+/** One policy owner: two current execution modes, historical mode decoding and migration. */
import type {JsonObject} from "../effect_program.ts";
import {requireJsonObject, requireStringArray, requireStringLiteral} from "../runtime_decode.ts";
import {HANDOFF_MODES, type HandoffMode} from "./handoff_mode_vocabulary.ts";
-export {HANDOFF_MODES, EXECUTION_HANDOFF_MODES, type HandoffMode} from "./handoff_mode_vocabulary.ts";
+export {HANDOFF_MODES, EXECUTION_HANDOFF_MODES, type HandoffMode, type ExecutionHandoffMode} from "./handoff_mode_vocabulary.ts";
export const HANDOFF_MODE_PLAN_SCHEMA = "loopx_handoff_mode_plan_request_v0";
import type {PersistedHandoffMode} from "./handoff_mode_facts.ts";
+export const HANDOFF_MODE_RETIRED_REASON = "Legacy ownership is historical only. Release retained execution, then review handoff-mode plan-migration/migrate to soft_claim or hard_lease; policy migration does not require another storage promotion.";
+
+/** Historical mode is readable and releasable, never present-tense execution.
+ * Receipt recovery runs before fresh admission; readback must not turn it into a grant. */
+export function retiredHandoffModeRejection(mode: unknown): "handoff_mode_retired" | null {
+ return mode === "legacy" ? "handoff_mode_retired" : null;
+}
+
export function decideHandoffMode(previous: PersistedHandoffMode, requested: HandoffMode,
claims: number, leases: number): JsonObject {
if (requested === "legacy") return {outcome: "rejected", code: "handoff_mode_retired",
diff --git a/loopx/control_plane/coordination/handoff_mode_runtime.ts b/loopx/control_plane/coordination/handoff_mode_runtime.ts
index 84236670e2..e63f19cf46 100644
--- a/loopx/control_plane/coordination/handoff_mode_runtime.ts
+++ b/loopx/control_plane/coordination/handoff_mode_runtime.ts
@@ -5,7 +5,7 @@ import {requireAuthorityStoreId} from "./authority_store_codec.ts";
import {openLocalAuthorityStore, localAuthorityOpenFailure} from "./local_authority_provider.ts";
import {requireLocalAuthorityRuntimeRoot as runtimeRoot} from "./local_authority_provider.ts";
import {authorityStoreSourceAuthority as sourceAuthorityFor} from "./authority_store.ts";
-import {withCanonicalWriter} from "./local_authority_write.ts";
+import {canonicalWriteRouteFailure, withCanonicalWriter} from "./local_authority_write.ts";
import {ShadowManagementError} from "./shadow_management.ts";
import {executeHandoffModeSet, HANDOFF_MODE_SET_SCHEMA} from "./handoff_mode_transaction.ts";
import {planHandoffModeMigration, executeHandoffModeMigration} from "./handoff_mode_migration.ts";
@@ -24,11 +24,12 @@ export async function setLocalHandoffMode(value: unknown): Promise {
return {...await executeHandoffModeSet(store, {goal_id: goalId,
operation_id: input.operation_id as string, requested_mode: input.requested_mode as string,
observed_at: input.observed_at as string, dry_run: input.dry_run as boolean}), ...evidence};
- });
+ }, {businessWrite: "handoff_mode_set"});
} catch (error) {
return {schema_version: "loopx_coordination_handoff_mode_set_result_v0", status: "failed", changed: false,
reason_code: error instanceof ShadowManagementError ? error.reason_code : "handoff_mode_unavailable",
- reason: error instanceof Error ? error.message : String(error), ...evidence, ...localAuthorityOpenFailure(error)};
+ reason: error instanceof Error ? error.message : String(error), ...evidence,
+ ...localAuthorityOpenFailure(error), ...canonicalWriteRouteFailure(error)};
}
}
diff --git a/loopx/control_plane/coordination/handoff_mode_vocabulary.ts b/loopx/control_plane/coordination/handoff_mode_vocabulary.ts
index b9899eb8aa..a5d40ac56e 100644
--- a/loopx/control_plane/coordination/handoff_mode_vocabulary.ts
+++ b/loopx/control_plane/coordination/handoff_mode_vocabulary.ts
@@ -2,4 +2,6 @@
export const HANDOFF_MODES = ["legacy", "soft_claim", "hard_lease"] as const;
/** New configuration targets. legacy remains readable for upgrade and receipts. */
export const EXECUTION_HANDOFF_MODES = ["soft_claim", "hard_lease"] as const;
+/** Historical state and receipt decoding, including the retired mode. */
export type HandoffMode = typeof HANDOFF_MODES[number];
+export type ExecutionHandoffMode = typeof EXECUTION_HANDOFF_MODES[number];
diff --git a/loopx/control_plane/coordination/lease_acquisition_proof.ts b/loopx/control_plane/coordination/lease_acquisition_proof.ts
index 0dd9082ea8..280888f173 100644
--- a/loopx/control_plane/coordination/lease_acquisition_proof.ts
+++ b/loopx/control_plane/coordination/lease_acquisition_proof.ts
@@ -6,7 +6,7 @@ import type {AuthorityStore, AuthorityStoreLoadResult} from "./authority_store.t
import {canonicalAuthorityObject} from "./authority_store_codec.ts";
import {indexCoordinationProjection, validateCoordinationTodoReadModel} from "./coordination_projection.ts";
import {canonicalTaskLease, canonicalTaskLeaseAcquireFacts} from "./task_lease_state.ts";
-import {HANDOFF_MODES} from "./handoff_mode_policy.ts";
+import {HANDOFF_MODES, retiredHandoffModeRejection, HANDOFF_MODE_RETIRED_REASON} from "./handoff_mode_policy.ts";
import {requireStringLiteral} from "../runtime_decode.ts";
import {leaseOwnerRejection} from "../work_items/task_lease_eligibility.ts";
import {leaseEpoch, leaseVersion, leaseIsActive} from "../work_items/task_lease_acquire.ts";
@@ -48,10 +48,11 @@ export async function currentLeaseAcquisitionProof(store: Auth
const current = facts.current;
const details = {handoff_mode: mode, original_receipt: result.original_receipt,
current_provider_revision: head.provider_revision, current_cursor: head.cursor};
- const rejection = mode === "soft_claim" ? "handoff_mode_forbids_lease"
+ const rejection = retiredHandoffModeRejection(mode) ?? (mode === "soft_claim" ? "handoff_mode_forbids_lease"
: input.required_handoff_mode !== undefined && mode !== input.required_handoff_mode ? "claim_lease_requires_hard_lease"
- : leaseOwnerRejection(facts.todo, input.owner, input.registered_agents);
- if (rejection) return failed(rejection, `current authority rejects lease acquire replay: ${rejection}`, details);
+ : leaseOwnerRejection(facts.todo, input.owner, input.registered_agents));
+ if (rejection) return failed(rejection, rejection === "handoff_mode_retired" ? HANDOFF_MODE_RETIRED_REASON
+ : `current authority rejects lease acquire replay: ${rejection}`, details);
if (!current || !leaseIsActive(current, input.now) || current.owner !== input.owner ||
current.idempotency_key !== input.idempotency_key || leaseEpoch(current) !== leaseEpoch(original) ||
leaseVersion(current) < leaseVersion(original)) {
diff --git a/loopx/control_plane/coordination/legacy_writer_fence.py b/loopx/control_plane/coordination/legacy_writer_fence.py
index f540942769..a987000e98 100644
--- a/loopx/control_plane/coordination/legacy_writer_fence.py
+++ b/loopx/control_plane/coordination/legacy_writer_fence.py
@@ -1,22 +1,21 @@
-"""Cheap Python entry guard for the TypeScript-owned coordination fence.
+"""Python guards for retained source maintenance and historical cleanup.
-Legacy Todo writes still happen in Python during Stage 2C. The durable fence
-itself and its validation semantics remain owned by the TypeScript control
-plane; this module only avoids starting that runtime while no fence exists and
-delegates every present-fence decision to the canonical handler.
+Normal Markdown Todo writes are retired. The TypeScript control plane still
+owns the durable promotion fence used by exact legacy lease release, recovery,
+and prose-only source maintenance. This module avoids starting that runtime
+while no fence exists and delegates every present-fence decision to the
+canonical handler.
"""
from __future__ import annotations
from collections.abc import Mapping
-from contextlib import contextmanager
import hashlib
import re
from pathlib import Path
-from typing import Any, Iterator
+from typing import Any
from ..effect_runtime import effect_runtime_result
-from ...file_lock import exclusive_cross_runtime_file_lock
from ..projects.registry_codec import load_registry
from ...paths import resolve_runtime_root
from ...registry import find_registry_goal, registry_goals, resolve_state_file
@@ -274,10 +273,10 @@ def require_legacy_coordination_write_allowed(
) -> None:
"""Allow the default path cheaply; delegate a present fence fail-closed.
- A future promotion transaction must acquire the legacy writer's existing
- mutation lock before engaging the fence. Calling this function while that
- lock is held then closes the check/write race without introducing a second
- Python authority for fence contents.
+ A promotion transaction acquires the retained mutation lock before
+ engaging the fence. Calling this function while that lock is held closes
+ the check/write race without introducing a second Python authority for
+ fence contents.
"""
fence_path = legacy_coordination_writer_fence_path(
@@ -337,63 +336,3 @@ def require_legacy_coordination_write_allowed(
code=code,
payload={"write_check": result},
)
-
-
-def require_legacy_todo_write_allowed(
- *, registry_path: Path, runtime_root: Path, goal_id: str, state_file: Path,
-) -> None:
- """Admit a fresh source mutation while its Todo/state locks are held.
-
- Historical receipt reads use the same locks but do not admit a new write.
- Keep source binding, management and promotion checks together for writers.
- """
- require_registry_source_write_allowed(
- registry_path=registry_path, runtime_root=runtime_root, goal_id=goal_id,
- state_file=state_file,
- )
- require_shadow_primary_write_allowed(runtime_root, goal_id)
- require_legacy_coordination_write_allowed(runtime_root=runtime_root, goal_id=goal_id)
-
-
-@contextmanager
-def legacy_todo_write_transaction(
- registry_path: Path,
- goal_id: str,
- state_file: Path,
- agent_id: str | None,
- operation: str,
- dry_run: bool,
- *,
- runtime_root: Path | None = None,
-) -> Iterator[None]:
- """Serialize promotion with one complete legacy Todo mutation.
-
- ``runtime_root`` is the effective runtime root of the CLI call (the
- ``--runtime-root`` override when given) so the mutex and the fence check
- share the same root as promotion and the observation hooks. Callers that
- omit it keep the registry-derived root.
- """
-
- resolved_runtime_root = runtime_root or resolve_runtime_root(
- load_registry(registry_path),
- None,
- registry_path=registry_path,
- )
- with exclusive_cross_runtime_file_lock(
- legacy_coordination_todo_lock_path(
- runtime_root=resolved_runtime_root,
- goal_id=goal_id,
- ),
- agent_id=agent_id,
- operation="legacy_coordination_todo_write",
- ), exclusive_cross_runtime_file_lock(
- state_file,
- agent_id=agent_id,
- operation=operation,
- ):
- if not dry_run:
- require_legacy_todo_write_allowed(
- registry_path=registry_path, runtime_root=resolved_runtime_root,
- goal_id=goal_id, state_file=state_file,
- )
- yield
diff --git a/loopx/control_plane/coordination/local_authority.py b/loopx/control_plane/coordination/local_authority.py
index 689fb1aea8..513238df44 100644
--- a/loopx/control_plane/coordination/local_authority.py
+++ b/loopx/control_plane/coordination/local_authority.py
@@ -10,7 +10,7 @@
from collections.abc import Mapping
from copy import deepcopy
from pathlib import Path
-from typing import Any
+from typing import Any, NoReturn
from uuid import uuid4
from ...agent_registry import registered_agent_ids_from_registry
@@ -27,7 +27,6 @@
TODO_ITEM_SCHEMA_VERSION,
)
from .canonical_snapshot import read_canonical_snapshot
-from .legacy_writer_fence import legacy_coordination_writer_fence_path
LOCAL_COORDINATION_TODO_LIST_TIMEOUT_SECONDS = 15.0
@@ -37,7 +36,7 @@
LOCAL_COORDINATION_TODO_CLAIM_METHOD = "coordination.local_authority.todo_claim"
-LOCAL_AUTHORITY_SOURCES = ("file_v0", "sqlite_v0")
+LOCAL_AUTHORITY_SOURCES = ("file_v0", "sqlite_v0", "postgresql_v0")
class LocalCoordinationAuthorityUnavailable(RuntimeError):
@@ -68,7 +67,63 @@ def __init__(self, message: str, *, code: str, payload: Mapping[str, Any]) -> No
super().__init__(message, code=code, payload=payload)
+class CanonicalAuthorityRequired(LocalCoordinationAuthorityRejection):
+ """A normal write targeted a Goal that has not completed authority migration."""
+
+ def __init__(
+ self,
+ *,
+ goal_id: str,
+ operation: str,
+ payload: Mapping[str, Any] | None = None,
+ ) -> None:
+ evidence = {
+ "goal_id": goal_id,
+ "operation": operation,
+ "write_admitted": False,
+ "decision_read_from_provider": False,
+ "legacy_fallback_used": False,
+ "recovery": {
+ "action": "migrate_goal_authority",
+ "backup_required": True,
+ },
+ **dict(payload or {}),
+ }
+ super().__init__(
+ "Canonical authority is required; back up and migrate this Goal before retrying",
+ code="canonical_authority_required",
+ payload=evidence,
+ )
+
+
+def raise_canonical_write_failure(
+ payload: Mapping[str, Any],
+ *,
+ goal_id: str,
+ operation: str,
+) -> None:
+ """Map native route admission once without re-reading promotion state."""
+
+ if (
+ payload.get("reason_code") == "canonical_authority_required"
+ or payload.get("error_code") == "canonical_authority_required"
+ ):
+ raise CanonicalAuthorityRequired(
+ goal_id=goal_id,
+ operation=operation,
+ payload=payload,
+ )
+
+
+def require_canonical_authority(*, goal_id: str, operation: str) -> NoReturn:
+ """Fail a compatibility-only path that has no native request to dispatch."""
+
+ raise CanonicalAuthorityRequired(goal_id=goal_id, operation=operation)
+
+
def local_authority_is_promoted(*, runtime_root: Path, goal_id: str) -> bool:
+ from .legacy_writer_fence import legacy_coordination_writer_fence_path
+
fence_path = legacy_coordination_writer_fence_path(
runtime_root=runtime_root,
goal_id=goal_id,
@@ -101,11 +156,8 @@ def claim_canonical_todo_if_promoted(
task_lease_expected_version: int | None = None,
project: Path | None = None,
state_file: Path | None = None,
-) -> dict[str, Any] | None:
- """Route a post-cutover claim to the TypeScript transaction owner."""
-
- if not local_authority_is_promoted(runtime_root=runtime_root, goal_id=goal_id):
- return None
+) -> dict[str, Any]:
+ """Route a claim to the canonical TypeScript transaction owner."""
with authority_registry_source(registry_path) as registry_source:
registered = registered_agent_ids_from_registry(registry_path, goal_id)
result = effect_runtime_result(
@@ -146,6 +198,11 @@ def claim_canonical_todo_if_promoted(
payload={"source_authority": "file_v0"},
)
payload = dict(result)
+ raise_canonical_write_failure(
+ payload,
+ goal_id=goal_id,
+ operation="todo_claim",
+ )
accepted = {"applied", "recovered", "replayed", "no_change", "planned"}
if (
payload.get("status") == "failed"
diff --git a/loopx/control_plane/coordination/local_authority_runtime.ts b/loopx/control_plane/coordination/local_authority_runtime.ts
index 4e7760e5e1..52b5b9de31 100644
--- a/loopx/control_plane/coordination/local_authority_runtime.ts
+++ b/loopx/control_plane/coordination/local_authority_runtime.ts
@@ -8,16 +8,15 @@ import {decodeTaskLeaseProof} from "./task_lease_proof.ts";
import {COORDINATION_TODO_ARCHIVE_RESULT_SCHEMA} from "./todo_archive.ts";
import {readCoordinationOwnership} from "./ownership_observation.ts";
import {executeTodoContinuation} from "./todo_continuation.ts";
-import {withCanonicalWriter} from "./local_authority_write.ts";
+import {canonicalWriteRouteFailure, withCanonicalWriter} from "./local_authority_write.ts";
import { ShadowManagementError, withShadowMaintenanceLock } from "./shadow_management.ts";
import { isAbsolute, join } from "node:path";
import {readFile, realpath} from "node:fs/promises";
import {createHash} from "node:crypto";
import type { JsonObject } from "../effect_program.ts";
-import {decodeMonitorPollObservation} from "../todos/monitor_metadata.ts";
import {decodeCompletionValidationRevision} from "../todos/completion_validation_revision.ts";
-import {executeCoordinationMonitorPoll, COORDINATION_MONITOR_POLL_REQUEST_SCHEMA, COORDINATION_LEASED_MONITOR_POLL_REQUEST_SCHEMA, COORDINATION_WITNESSED_MONITOR_POLL_REQUEST_SCHEMA, COORDINATION_GUARDED_MONITOR_POLL_REQUEST_SCHEMA,
+import {executeCoordinationMonitorPoll, COORDINATION_MONITOR_POLL_REQUEST_SCHEMA, COORDINATION_LEASED_MONITOR_POLL_REQUEST_SCHEMA, COORDINATION_WITNESSED_MONITOR_POLL_REQUEST_SCHEMA, COORDINATION_GUARDED_MONITOR_POLL_REQUEST_SCHEMA, COORDINATION_MONITOR_RECEIPT_ONLY_REQUEST_SCHEMA,
COORDINATION_MONITOR_POLL_RESULT_SCHEMA} from "./todo_monitor_poll.ts";
import { requireJsonObject, requireBoolean } from "../runtime_decode.ts";
import {
@@ -447,22 +446,23 @@ export async function pollLocalCoordinationMonitor(value: unknown,
const evidence = {source_authority: "file_v0", decision_read_from_provider: true, legacy_fallback_used: false};
try {
const input = requireJsonObject(value, "local Monitor poll request");
+ const receiptOnly = input.schema_version === COORDINATION_MONITOR_RECEIPT_ONLY_REQUEST_SCHEMA;
if (input.schema_version !== COORDINATION_MONITOR_POLL_REQUEST_SCHEMA &&
input.schema_version !== COORDINATION_LEASED_MONITOR_POLL_REQUEST_SCHEMA &&
input.schema_version !== COORDINATION_WITNESSED_MONITOR_POLL_REQUEST_SCHEMA &&
- input.schema_version !== COORDINATION_GUARDED_MONITOR_POLL_REQUEST_SCHEMA) throw new TypeError("Monitor poll schema mismatch");
+ input.schema_version !== COORDINATION_GUARDED_MONITOR_POLL_REQUEST_SCHEMA && !receiptOnly) throw new TypeError("Monitor poll schema mismatch");
if (input.lease_proof != null && input.schema_version === COORDINATION_MONITOR_POLL_REQUEST_SCHEMA) {
throw new TypeError("lease-backed Monitor poll requires request v1");
}
if (input.schema_version === COORDINATION_GUARDED_MONITOR_POLL_REQUEST_SCHEMA && input.gate_scope_guard !== true) {
throw new TypeError("guarded Monitor request v3 requires gate_scope_guard=true");
}
- if (input.gate_scope_guard === true && input.schema_version !== COORDINATION_GUARDED_MONITOR_POLL_REQUEST_SCHEMA) {
+ if (input.gate_scope_guard === true && input.schema_version !== COORDINATION_GUARDED_MONITOR_POLL_REQUEST_SCHEMA && !receiptOnly) {
throw new TypeError("commit-head Monitor gate scope requires request v3");
}
const authoritySourcesCurrent = registryAuthoritySourceCheck(input,
input.schema_version === COORDINATION_WITNESSED_MONITOR_POLL_REQUEST_SCHEMA ||
- input.schema_version === COORDINATION_GUARDED_MONITOR_POLL_REQUEST_SCHEMA);
+ input.schema_version === COORDINATION_GUARDED_MONITOR_POLL_REQUEST_SCHEMA || receiptOnly);
const proof = decodeTaskLeaseProof(input.lease_proof);
if (input.schema_version === COORDINATION_LEASED_MONITOR_POLL_REQUEST_SCHEMA && !proof) {
throw new TypeError("Monitor poll request v1 requires lease_proof");
@@ -478,17 +478,18 @@ export async function pollLocalCoordinationMonitor(value: unknown,
goal_id: goalId, operation_id: requireAuthorityStoreId(input.operation_id, "operation id"),
actor_agent_id: input.actor_agent_id == null ? null : claimAgentValue(input.actor_agent_id, "actor_agent_id"),
registered_agents: registered, dry_run: input.dry_run as boolean,
+ receipt_only: receiptOnly,
observation: requireJsonObject(input.observation, "Monitor observation"),
intent: requireJsonObject(input.intent, "Monitor successor intent"),
lease_proof: proof, gate_scope_guard: input.gate_scope_guard == null ? false :
requireBoolean(input.gate_scope_guard, "gate_scope_guard"), now: new Date(),
}, authoritySourcesCurrent), ...evidence};
- });
+ }, {businessWrite: "monitor_poll"});
} catch (error) {
return {schema_version: COORDINATION_MONITOR_POLL_RESULT_SCHEMA, status: "failed", changed: false,
reason_code: error instanceof ShadowManagementError ? error.reason_code : "invalid_local_monitor_poll_request",
reason: error instanceof Error ? error.message : String(error), ...evidence,
- ...localAuthorityOpenFailure(error)};
+ ...localAuthorityOpenFailure(error), ...canonicalWriteRouteFailure(error)};
}
}
@@ -1084,7 +1085,7 @@ export async function claimLocalCoordinationTodo(
decision_read_from_provider: true,
legacy_fallback_used: false,
};
- });
+ }, {businessWrite: "todo_claim"});
} catch (error) {
return {
schema_version: COORDINATION_TODO_CLAIM_RESULT_SCHEMA,
@@ -1095,6 +1096,7 @@ export async function claimLocalCoordinationTodo(
decision_read_from_provider: true,
legacy_fallback_used: false,
...localAuthorityOpenFailure(error),
+ ...canonicalWriteRouteFailure(error),
};
}
}
@@ -1155,7 +1157,7 @@ export async function createLocalCoordinationTodo(
...result,
...providerEvidence,
};
- });
+ }, {businessWrite: "todo_create"});
} catch (error) {
return {
schema_version: COORDINATION_TODO_CREATE_RESULT_SCHEMA,
@@ -1164,6 +1166,7 @@ export async function createLocalCoordinationTodo(
reason: error instanceof Error ? error.message : "invalid Todo create request",
...providerEvidence,
...localAuthorityOpenFailure(error),
+ ...canonicalWriteRouteFailure(error),
};
}
}
@@ -1249,7 +1252,12 @@ export async function updateLocalCoordinationTodo(
lease_expected_version: optionalNonNegativeSafeInteger(input.lease_expected_version, "lease_expected_version"),
patch: requireJsonObject(input.patch, "Todo update patch"),
planning_intent: planningIntent,
- ...(observationUpdate ? {monitor_observation: decodeMonitorPollObservation(input.monitor_observation)} : {}),
+ ...(observationUpdate ? {
+ monitor_observation: requireJsonObject(
+ input.monitor_observation,
+ "Monitor observation",
+ ),
+ } : {}),
...(completionUpdate ? {completion: requireJsonObject(input.completion, "Todo completion payload")} : {}),
...(validationRevisionUpdate ? {completion_validation_revision:
decodeCompletionValidationRevision(input.completion_validation_revision)} : {}),
@@ -1257,13 +1265,14 @@ export async function updateLocalCoordinationTodo(
dry_run: input.dry_run as boolean,
now: claimObservedAt(input.observed_at),
}, authoritySourcesCurrent), ...providerEvidence};
- });
+ }, {businessWrite: "todo_update"});
} catch (error) {
return {schema_version: COORDINATION_TODO_UPDATE_RESULT_SCHEMA, status: "failed",
changed: false, reason_code: error instanceof ShadowManagementError ? error.reason_code : "invalid_local_coordination_todo_update_request",
reason: error instanceof Error ? error.message : "invalid Todo update request",
...providerEvidence,
...localAuthorityOpenFailure(error),
+ ...canonicalWriteRouteFailure(error),
};
}
}
@@ -1373,7 +1382,7 @@ export async function terminalLifecycleLocalCoordinationTodo(
}, async () => await authoritySourcesCurrent() && researchEvidence.current(),
researchEvidence.qualify, request => verifyReentrySettlement(root, request)), ...providerEvidence};
} finally {await researchEvidence.close();}
- });
+ }, {businessWrite: "todo_terminal"});
} catch (error) {
return {schema_version: COORDINATION_TODO_TERMINAL_LIFECYCLE_RESULT_SCHEMA,
status: "failed", changed: false,
@@ -1382,6 +1391,7 @@ export async function terminalLifecycleLocalCoordinationTodo(
reason: error instanceof Error ? error.message : "invalid local Todo terminal request",
...providerEvidence,
...localAuthorityOpenFailure(error),
+ ...canonicalWriteRouteFailure(error),
};
}
}
@@ -1424,7 +1434,7 @@ export async function archiveLocalCoordinationTodos(
dry_run: input.dry_run as boolean,
now,
}), ...providerEvidence};
- });
+ }, {businessWrite: "todo_archive"});
} catch (error) {
return {schema_version: COORDINATION_TODO_ARCHIVE_RESULT_SCHEMA,
status: "failed", changed: false,
@@ -1433,6 +1443,7 @@ export async function archiveLocalCoordinationTodos(
reason: error instanceof Error ? error.message : "invalid local Todo archive request",
...providerEvidence,
...localAuthorityOpenFailure(error),
+ ...canonicalWriteRouteFailure(error),
};
}
}
@@ -1454,7 +1465,7 @@ export async function acknowledgeLocalCoordinationTodoArchive(
return await withCanonicalWriter(root, goalId, false, async () => {
const store = await openRuntimeStore(root, goalId, dependencies);
return acknowledgeLocalArchiveAttempt(store, root, goalId, role, operationId);
- });
+ }, {businessWrite: "todo_archive"});
} catch (error) {
return {
schema_version: LOCAL_TODO_ARCHIVE_ACK_RESULT_SCHEMA,
@@ -1463,6 +1474,7 @@ export async function acknowledgeLocalCoordinationTodoArchive(
"invalid_local_coordination_todo_archive_ack_request",
reason: error instanceof Error ? error.message : "invalid archive acknowledgement",
...localAuthorityOpenFailure(error),
+ ...canonicalWriteRouteFailure(error),
};
}
}
@@ -1480,17 +1492,13 @@ export async function continueLocalTodo(
return await withCanonicalWriter(root, goalId, false, async () => {
const store = await openRuntimeStore(root, goalId, dependencies);
evidence.source_authority = sourceAuthorityFor(store);
- const fence = await loadLegacyCoordinationWriterFence(root, goalId);
- if (fence.status !== "loaded") return {ok: false, status: "rejected",
- reason_code: "continuation_requires_canonical_authority",
- reason: "Use an explicitly promoted canonical authority; this command never promotes or falls back to Markdown"};
return {...await executeTodoContinuation(store, input), ...evidence};
- });
+ }, {businessWrite: "todo_update"});
} catch (error) {
return {ok: false, status: "failed",
reason_code: error instanceof ShadowManagementError ? error.reason_code : "invalid_continuation_request",
reason: error instanceof Error ? error.message : "Invalid continuation request", ...evidence,
- ...localAuthorityOpenFailure(error)};
+ ...localAuthorityOpenFailure(error), ...canonicalWriteRouteFailure(error)};
}
}
diff --git a/loopx/control_plane/coordination/local_authority_shadow_adapter.py b/loopx/control_plane/coordination/local_authority_shadow_adapter.py
index 9e304babf4..0e7e36fa6a 100644
--- a/loopx/control_plane/coordination/local_authority_shadow_adapter.py
+++ b/loopx/control_plane/coordination/local_authority_shadow_adapter.py
@@ -19,10 +19,8 @@
LOCAL_AUTHORITY_SHADOW_COMMIT_ENTRY_RESULT_SCHEMA,
LOCAL_AUTHORITY_SHADOW_READ_REQUEST_SCHEMA,
LOCAL_AUTHORITY_SHADOW_READ_RESULT_SCHEMA,
- LOCAL_AUTHORITY_SHADOW_TRANSACTION_EVIDENCE_SCHEMA,
)
from .configuration import resolve_coordination_runtime_shadow_config
-from .authority_source_capture import todo_partition_projector as todo_partition_projector
from .shadow_management import shadow_management_state_path
from .shadow_goal_scope import shadow_goal_scope
from .local_authority_shadow_readback import (
@@ -44,9 +42,6 @@
# behind instead of guessing.
# ---------------------------------------------------------------------------
-LOCAL_AUTHORITY_SHADOW_EVIDENCE_SCHEMA_V1 = (
- LOCAL_AUTHORITY_SHADOW_TRANSACTION_EVIDENCE_SCHEMA
-)
INLINE_DRAIN_MAX_ENTRIES = 16
INLINE_DRAIN_BUDGET_SECONDS = 2.0
INLINE_DRAIN_LOCK_TIMEOUT_SECONDS = 0.25
@@ -190,91 +185,16 @@ def drain_local_authority_shadow_outbox(
return result
-def capture_evidence(
- *,
- goal_id: str,
- capture: outbox.CaptureOutcome,
- drain: DrainResult | None,
-) -> dict[str, Any]:
- """Evidence v1 attached to a writer payload: capture facts plus drain facts.
-
- Every flag here is a measured fact of this write. ``source_candidate_compared``
- and ``parity_verdict`` stay negative until the verify step exists.
- """
-
- if capture.failure is not None:
- outcome = "capture_failed"
- reason_code: str | None = str(capture.failure.get("reason_code"))
- elif capture.entry_id is None:
- outcome = "no_transaction"
- reason_code = capture.skipped_reason
- elif drain is None or drain.outcome == "drain_deferred":
- outcome = "drain_deferred" if drain is not None else "pending"
- reason_code = drain.reason_code if drain is not None else None
- else:
- settled = drain.entry_outcome(capture.entry_id)
- if settled is None:
- outcome = "pending"
- reason_code = drain.reason_code
- else:
- outcome = str(settled["outcome"])
- reason_code = settled.get("reason_code")
- return {
- "schema_version": LOCAL_AUTHORITY_SHADOW_EVIDENCE_SCHEMA_V1,
- "outcome": outcome,
- "reason_code": reason_code,
- "goal_id": goal_id,
- "entry": {
- "entry_id": capture.entry_id,
- "partition": capture.partition,
- "seq": capture.seq,
- "partition_digest": capture.partition_digest,
- "source_bytes_digest": capture.source_bytes_digest,
- },
- "drain": None
- if drain is None
- else {
- "outcome": drain.outcome,
- "delivered": drain.delivered,
- "replayed": drain.replayed,
- "reclaimed_residue": drain.reclaimed_residue,
- "pending_after": drain.pending_after,
- "prepared_only_after": drain.prepared_only_after,
- "stopped_at": drain.stopped_at,
- "last_cursor": drain.last_cursor,
- "provider_revision": drain.provider_revision,
- "candidate_readback_verified": drain.candidate_readback_verified,
- },
- "capture_kind": "source_transaction_outbox",
- # Both flags are measured facts of this write: they are true only when
- # a prepared/committed entry was actually recorded for it. A disabled
- # or unchanged capture recorded nothing and claims nothing.
- "source_transaction_correlated": capture.recorded,
- "durable_source_outbox": capture.recorded,
- "source_candidate_compared": False,
- "parity_verdict": "not_evaluated",
- "primary_authority": "legacy_local",
- "candidate_provider": "file",
- "candidate_read_for_decision": False,
- "provider_to_local_writes": False,
- "primary_writeback_preserved": True,
- "store_identity": drain.store_identity if drain is not None else None,
- }
-
-
__all__ = [
"effective_runtime_root",
"INLINE_DRAIN_BUDGET_SECONDS",
"INLINE_DRAIN_LOCK_TIMEOUT_SECONDS",
"INLINE_DRAIN_MAX_ENTRIES",
"LOCAL_AUTHORITY_SHADOW_COMMIT_ENTRY_RESULT_SCHEMA",
- "LOCAL_AUTHORITY_SHADOW_EVIDENCE_SCHEMA_V1",
"LOCAL_AUTHORITY_SHADOW_READ_REQUEST_SCHEMA",
"LOCAL_AUTHORITY_SHADOW_READ_RESULT_SCHEMA",
"RETENTION_PRESSURE_BYTES",
"DrainResult",
- "capture_evidence",
- "todo_partition_projector",
"drain_local_authority_shadow_outbox",
"local_authority_shadow_status",
"read_local_authority_shadow",
diff --git a/loopx/control_plane/coordination/local_authority_shadow_outbox.py b/loopx/control_plane/coordination/local_authority_shadow_outbox.py
index b6b2297bdf..8fd656f370 100644
--- a/loopx/control_plane/coordination/local_authority_shadow_outbox.py
+++ b/loopx/control_plane/coordination/local_authority_shadow_outbox.py
@@ -1,51 +1,9 @@
-"""Durable, transaction-bound outbox for the local authority shadow.
+"""Read-only compatibility exports for retained shadow outbox history."""
-A legacy writer that changes coordination facts records, inside the lock it
-already holds, a two-phase entry for exactly the state that lock guards (one
-*partition*): a ``prepared`` file computed from the bytes about to be written,
-then a ``committed`` marker after the primary write returns. Nothing in the
-lock talks to the TypeScript runtime. A later drain (same process after the
-lock, or an operator command) turns each committed entry into exactly one
-candidate-store transaction whose ``operation_id`` is the entry id.
-
-Prepare failures return typed evidence for the transaction owner to reject an
-active-lineage write before changing primary bytes. Marker failures preserve
-prepared evidence; candidate delivery happens after releasing primary locks.
-The native drain owns cursor publication and receipt-proven cleanup; Python
-retains source capture; historical inventory/decoding has an independent IO owner.
-"""
-
-from __future__ import annotations
-
-import json
-import os
-import uuid
-from collections.abc import Callable, Mapping
-from dataclasses import dataclass
-from datetime import datetime, timezone
-from pathlib import Path
-from typing import Any
-
-from .local_authority_shadow_projection import (
- TODO_PARTITION,
- canonical_value,
- partition_digest,
- text_digest,
-)
-from .coordination_state_contract_generated import (
- LOCAL_AUTHORITY_SHADOW_READ_REQUEST_SCHEMA,
- LOCAL_AUTHORITY_SHADOW_READ_RESULT_SCHEMA,
-)
-from .shadow_management import (
- read_shadow_capture_binding,
- shadow_maintenance_lock_target,
-)
-
-
-# Retained imports preserve existing writer/tool callers; the definitions and
-# read-only CLI no longer depend on this producer.
from .local_authority_shadow_history import (
DRAIN_CURSOR_SCHEMA as DRAIN_CURSOR_SCHEMA,
+ ENTRY_ID_PREFIX as ENTRY_ID_PREFIX,
+ MAX_OUTBOX_SEQUENCE as MAX_OUTBOX_SEQUENCE,
OUTBOX_COMMIT_SCHEMA as OUTBOX_COMMIT_SCHEMA,
OUTBOX_ENTRY_SCHEMA as OUTBOX_ENTRY_SCHEMA,
SOURCE_MARKDOWN as SOURCE_MARKDOWN,
@@ -53,483 +11,42 @@
SOURCE_TASK_LEASE as SOURCE_TASK_LEASE,
WRITER_RUNTIME_PYTHON as WRITER_RUNTIME_PYTHON,
WRITER_RUNTIME_TYPESCRIPT as WRITER_RUNTIME_TYPESCRIPT,
- ENTRY_ID_PREFIX as ENTRY_ID_PREFIX,
- MAX_OUTBOX_SEQUENCE as MAX_OUTBOX_SEQUENCE,
OutboxEntry as OutboxEntry,
OutboxError as OutboxError,
- outbox_root as outbox_root,
- partition_directory as partition_directory,
- lease_directory as lease_directory,
- entry_identity as entry_identity,
+ cursor_path as cursor_path,
+ decode_cursor as decode_cursor,
entry_file_name as entry_file_name,
+ entry_identity as entry_identity,
+ lease_directory as lease_directory,
list_entries as list_entries,
- cursor_path as cursor_path,
+ outbox_root as outbox_root,
+ outbox_summary as outbox_summary,
+ partition_directory as partition_directory,
read_cursor as read_cursor,
- decode_cursor as decode_cursor,
- retired_residue as retired_residue,
record_source_ref as record_source_ref,
+ retired_residue as retired_residue,
runtime_root_digest as runtime_root_digest,
- outbox_summary as outbox_summary,
- _ENTRY_FILE,
)
-TodoPartitionProjector = Callable[[str], dict[str, Any]]
-"""Active-state Markdown text -> ``{"handoff_mode": str, "todos": [compact...]}``."""
-
-
-def utc_now_text() -> str:
- return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
-
-
-def drain_lock_target(runtime_root: Path, goal_id: str) -> Path:
- return shadow_maintenance_lock_target(runtime_root, goal_id)
-
-
-def _fsync_directory(directory: Path) -> None:
- if os.name == "nt":
- return
- descriptor = os.open(directory, os.O_RDONLY)
- try:
- os.fsync(descriptor)
- finally:
- os.close(descriptor)
-
-
-def durable_write_json(path: Path, payload: Mapping[str, Any]) -> None:
- """Temp file in the same directory, fsync, atomic replace, directory fsync."""
-
- path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
- temporary = path.with_name(f"{path.name}.tmp-{os.getpid()}-{uuid.uuid4().hex}")
- data = json.dumps(payload, ensure_ascii=False, sort_keys=True, indent=1).encode(
- "utf-8"
- )
- descriptor = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
- try:
- view = memoryview(data)
- while view:
- written = os.write(descriptor, view)
- view = view[written:]
- os.fsync(descriptor)
- finally:
- os.close(descriptor)
- try:
- os.replace(temporary, path)
- _fsync_directory(path.parent)
- finally:
- if temporary.exists():
- temporary.unlink(missing_ok=True)
-
-
-def _require_binding_goal_ref(
- binding: Mapping[str, Any],
- *,
- goal_id: str,
- goal_ref: Mapping[str, str] | None,
-) -> None:
- bound = binding.get("goal_ref")
- if goal_ref is None:
- if bound is not None:
- raise OutboxError(
- "goal_instance_id_missing",
- "exact shadow operation requires the source GoalRef",
- )
- return
- if bound is None:
- raise OutboxError(
- "legacy_goal_binding",
- "exact shadow operation cannot infer a legacy binding",
- )
- if bound != dict(goal_ref) or bound.get("goal_id") != goal_id:
- raise OutboxError(
- "stale_goal_instance",
- "shadow operation belongs to another Goal instance",
- )
-
-
-def _proved_sequence(
- runtime_root: Path,
- goal_id: str,
- partition: str,
- goal_ref: Mapping[str, str] | None,
-) -> int:
- """Read settled progress while the primary lock prevents management changes.
-
- This exceptional missing-cursor path owns neither M nor cursor writes. The
- native boundary validates the complete bounded history without initializing
- a missing store. Primary exclusion keeps its lineage stable during the read.
- """
- from ..effect_runtime import effect_runtime_result
-
- binding = read_shadow_capture_binding(runtime_root, goal_id)
- if binding["status"] != "active":
- raise OutboxError(
- "bootstrap_required", "sequence recovery needs an active lineage"
- )
- _require_binding_goal_ref(
- binding["binding"], goal_id=goal_id, goal_ref=goal_ref
- )
- view = effect_runtime_result(
- "coordination.runtime_shadow.outbox_read",
- {
- "schema_version": LOCAL_AUTHORITY_SHADOW_READ_REQUEST_SCHEMA,
- "runtime_root": str(runtime_root),
- "goal_id": goal_id,
- "scan_limit": 0,
- "read_model": "proof",
- },
- timeout=15.0,
- )
- proof = view.get("proof") if isinstance(view, dict) else None
- if (
- not isinstance(view, dict)
- or view.get("schema_version") != LOCAL_AUTHORITY_SHADOW_READ_RESULT_SCHEMA
- or view.get("status") != "loaded"
- or not isinstance(proof, dict)
- or proof.get("capture_lineage_id") != binding["binding"]["capture_lineage_id"]
- or view.get("store_identity") != binding["binding"]["store_identity"]
- or read_shadow_capture_binding(runtime_root, goal_id) != binding
- ):
- raise OutboxError(
- "outbox_sequence_unproved", "missing cursor has no stable history proof"
- )
- sequences = proof.get("last_sequences")
- seq = sequences.get(partition) if isinstance(sequences, dict) else None
- if type(seq) is not int or not 0 <= seq <= MAX_OUTBOX_SEQUENCE:
- raise OutboxError(
- "outbox_sequence_unproved", "history has no verified partition progress"
- )
- return seq
-
-
-def next_seq(
- directory: Path,
- *,
- runtime_root: Path | None = None,
- goal_id: str | None = None,
- goal_ref: Mapping[str, str] | None = None,
-) -> int:
- """Allocate after visible files and the cursor hint; drain proves continuity."""
-
- highest = 0
- if directory.is_dir():
- for path in directory.iterdir():
- match = _ENTRY_FILE.match(path.name)
- if match is not None:
- highest = max(highest, int(match.group("seq")))
- cursor = read_cursor(directory)
- if cursor is not None:
- highest = max(highest, int(cursor.get("last_seq") or 0))
- elif runtime_root is not None and goal_id is not None:
- highest = max(
- highest,
- _proved_sequence(
- runtime_root,
- goal_id,
- directory.name,
- goal_ref,
- ),
- )
- if highest >= MAX_OUTBOX_SEQUENCE:
- raise OutboxError("outbox_sequence_exhausted", "outbox sequence is exhausted")
- return highest + 1
-
-
-def _writer(
- write_class: str, *, runtime: str, operation_id: str | None
-) -> dict[str, Any]:
- return {
- "runtime": runtime,
- "write_class": write_class,
- "operation_id": operation_id,
- }
-
-
-def _entry_record(
- *,
- goal_id: str,
- partition: str,
- seq: int,
- entry_id: str,
- writer: Mapping[str, Any],
- source: Mapping[str, Any],
- source_root_digest: str,
- capture_lineage_id: str,
- projection: Mapping[str, Any] | None,
- digest: str | None,
-) -> dict[str, Any]:
- return {
- "schema_version": OUTBOX_ENTRY_SCHEMA,
- "goal_id": goal_id,
- "partition": partition,
- "seq": seq,
- "entry_id": entry_id,
- "writer": dict(writer),
- "source": dict(source),
- "source_root_digest": source_root_digest,
- "capture_lineage_id": capture_lineage_id,
- "projection": canonical_value(dict(projection))
- if projection is not None
- else None,
- "partition_digest": digest,
- "prepared_at": utc_now_text(),
- }
-
-
-@dataclass
-class CaptureOutcome:
- """What the capture did for one primary write (attached to its evidence)."""
-
- entry_id: str | None = None
- partition: str | None = None
- seq: int | None = None
- partition_digest: str | None = None
- source_bytes_digest: str | None = None
- skipped_reason: str | None = None
- failure: dict[str, Any] | None = None
-
- @property
- def recorded(self) -> bool:
- return self.entry_id is not None and self.failure is None
-
-
-class TodoPartitionCapture:
- """Two-phase capture of the todos partition inside the active-state lock.
-
- ``begin`` returns an inert capture when the goal has no shadow binding, so
- default-off writers create no directory, lock, or file.
- """
-
- def __init__(
- self,
- *,
- enabled: bool,
- registry_path: Path | None,
- runtime_root: Path | None,
- goal_id: str,
- state_path: Path | None,
- write_class: str,
- original_text: str,
- projector: TodoPartitionProjector | None,
- goal_ref: Mapping[str, str] | None = None,
- ) -> None:
- self._enabled = enabled
- self._registry_path = registry_path
- self._runtime_root = runtime_root
- self._goal_id = goal_id
- self._state_path = state_path
- self._write_class = write_class
- self._original_digest = text_digest(original_text)
- self._original_text = original_text
- self._projector = projector
- self._goal_ref = dict(goal_ref) if goal_ref is not None else None
- self._directory = (
- partition_directory(runtime_root, goal_id, TODO_PARTITION)
- if enabled and runtime_root is not None
- else None
- )
- self._seq: int | None = None
- self._entry_id: str | None = None
- self._lineage_id: str | None = None
- self.outcome = CaptureOutcome(partition=TODO_PARTITION if enabled else None)
-
- @classmethod
- def begin(
- cls,
- *,
- enabled: bool,
- registry_path: Path | None = None,
- runtime_root: Path | None,
- goal_id: str,
- state_path: Path | None,
- write_class: str,
- original_text: str,
- projector: TodoPartitionProjector | None,
- goal_ref: Mapping[str, str] | None = None,
- ) -> TodoPartitionCapture:
- """``projector`` maps active-state text to the todos partition projection.
-
- It is injected so this module stays free of the Markdown parser; the
- adapter supplies the production projector.
- """
-
- return cls(
- enabled=enabled,
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id=goal_id,
- state_path=state_path,
- write_class=write_class,
- original_text=original_text,
- projector=projector,
- goal_ref=goal_ref,
- )
-
- @property
- def enabled(self) -> bool:
- return self._enabled and self._directory is not None
-
- @property
- def goal_ref(self) -> dict[str, str] | None:
- return dict(self._goal_ref) if self._goal_ref is not None else None
-
- @property
- def registry_path(self) -> Path | None:
- return self._registry_path
-
- def skip(self, reason: str) -> None:
- """Record why this writer deliberately did not open a transaction."""
-
- if self.enabled and self.outcome.entry_id is None:
- self.outcome.skipped_reason = reason
-
- def _project(self, state_text: str) -> dict[str, Any]:
- if self._projector is None:
- raise OutboxError(
- "outbox_prepare_failed", "a todo partition projector is required"
- )
- projection = self._projector(state_text)
- if set(projection) != {"handoff_mode", "todos"}:
- raise OutboxError(
- "outbox_prepare_failed", "projector must return {handoff_mode, todos}"
- )
- return projection
-
- def _fail(self, reason_code: str, error: BaseException) -> None:
- self.outcome.failure = {
- "reason_code": reason_code,
- "error_class": error.__class__.__name__,
- }
-
- def prepare(self, new_text: str) -> None:
- """Prepare capture for the exact Markdown bytes about to be written."""
-
- if not self.enabled or self._directory is None or self._runtime_root is None:
- self.outcome.skipped_reason = "shadow_disabled"
- return
- binding_view = read_shadow_capture_binding(self._runtime_root, self._goal_id)
- if binding_view["status"] != "active":
- self.outcome.skipped_reason = str(
- binding_view.get("reason_code") or "bootstrap_required"
- )
- return
- binding = binding_view["binding"]
- try:
- _require_binding_goal_ref(
- binding,
- goal_id=self._goal_id,
- goal_ref=self._goal_ref,
- )
- except OutboxError as error:
- self._fail(
- error.reason_code,
- error,
- )
- return
- self._lineage_id = str(binding["capture_lineage_id"])
- source_root_digest = str(binding["source_root_digest"])
- try:
- projection = self._project(new_text)
- digest = partition_digest(projection)
- previous_digest = partition_digest(self._project(self._original_text))
- if digest == previous_digest:
- self.outcome.skipped_reason = "partition_unchanged"
- return
- source_ref = text_digest(new_text)
- bytes_digest = source_ref
- source_kind = SOURCE_MARKDOWN
- seq = next_seq(
- self._directory,
- runtime_root=self._runtime_root,
- goal_id=self._goal_id,
- goal_ref=self._goal_ref,
- )
- entry_id = entry_identity(
- goal_id=self._goal_id,
- partition=TODO_PARTITION,
- seq=seq,
- source_ref=source_ref,
- capture_lineage_id=self._lineage_id,
- source_root_digest=source_root_digest,
- )
- record = _entry_record(
- goal_id=self._goal_id,
- partition=TODO_PARTITION,
- seq=seq,
- entry_id=entry_id,
- writer=_writer(
- self._write_class,
- runtime=WRITER_RUNTIME_PYTHON,
- operation_id=None,
- ),
- source={
- "kind": source_kind,
- "previous_bytes_digest": self._original_digest,
- "previous_partition_digest": previous_digest,
- "bytes_digest": bytes_digest,
- "lease": None,
- "event_id": None,
- },
- source_root_digest=source_root_digest,
- capture_lineage_id=self._lineage_id,
- projection=projection,
- digest=digest,
- )
- durable_write_json(
- self._directory / entry_file_name(seq, entry_id, "prepared"),
- record,
- )
- except Exception as error: # noqa: BLE001 - the transaction owner enforces active preparation
- self._fail("outbox_prepare_failed", error)
- return
- self._seq = seq
- self._entry_id = entry_id
- self.outcome.entry_id = entry_id
- self.outcome.seq = seq
- self.outcome.partition_digest = digest
- self.outcome.source_bytes_digest = bytes_digest
-
- def committed(self) -> None:
- """Mark the prepared entry committed after the primary write returned."""
-
- if self._seq is None or self._entry_id is None or self._directory is None:
- return
- if self.outcome.failure is not None:
- return
- marker: dict[str, Any] = {
- "schema_version": OUTBOX_COMMIT_SCHEMA,
- "entry_id": self._entry_id,
- "capture_lineage_id": self._lineage_id,
- "committed_at": utc_now_text(),
- }
- try:
- durable_write_json(
- self._directory
- / entry_file_name(self._seq, self._entry_id, "committed"),
- marker,
- )
- except Exception as error: # noqa: BLE001 - the primary write already landed
- self._fail("outbox_commit_marker_failed", error)
-
-
__all__ = [
"DRAIN_CURSOR_SCHEMA",
+ "ENTRY_ID_PREFIX",
+ "MAX_OUTBOX_SEQUENCE",
"OUTBOX_COMMIT_SCHEMA",
"OUTBOX_ENTRY_SCHEMA",
"SOURCE_MARKDOWN",
"SOURCE_STATE_EVENT_LOG",
"SOURCE_TASK_LEASE",
- "CaptureOutcome",
+ "WRITER_RUNTIME_PYTHON",
+ "WRITER_RUNTIME_TYPESCRIPT",
"OutboxEntry",
"OutboxError",
- "TodoPartitionCapture",
- "TodoPartitionProjector",
- "drain_lock_target",
- "durable_write_json",
+ "cursor_path",
+ "decode_cursor",
"entry_file_name",
"entry_identity",
"lease_directory",
"list_entries",
- "next_seq",
"outbox_root",
"outbox_summary",
"partition_directory",
@@ -537,5 +54,4 @@ def committed(self) -> None:
"record_source_ref",
"retired_residue",
"runtime_root_digest",
- "utc_now_text",
]
diff --git a/loopx/control_plane/coordination/local_authority_shadow_outbox.ts b/loopx/control_plane/coordination/local_authority_shadow_outbox.ts
index 974d2de82b..0e11e77d62 100644
--- a/loopx/control_plane/coordination/local_authority_shadow_outbox.ts
+++ b/loopx/control_plane/coordination/local_authority_shadow_outbox.ts
@@ -1,19 +1,10 @@
import { createHash } from "node:crypto";
-import { readdir, readFile } from "node:fs/promises";
+import { readFile } from "node:fs/promises";
import { join } from "node:path";
import type { JsonObject } from "../effect_program.ts";
-import { durableWriteJson } from "../effect_runtime_io.ts";
-import { authorityUnicodeCompare, canonicalAuthorityBytes } from "./authority_store_codec.ts";
-import {
- requireShadowBindingGoalRef,
- requireShadowCaptureBinding,
- shadowBindingGoalRef,
- ShadowManagementError,
-} from "./shadow_management.ts";
import { parseExactGoalRef } from "../goals/goal_instance_identity.ts";
-import { outboxEntryIdentity, OUTBOX_ENTRY_FILE_PATTERN } from "./local_authority_shadow_identity.ts";
-import { readProvenShadowSequence } from "./local_authority_shadow.ts";
+import { outboxEntryIdentity } from "./local_authority_shadow_identity.ts";
import {
LOCAL_AUTHORITY_SHADOW_BINDING_SCHEMA,
LOCAL_AUTHORITY_SHADOW_EXACT_BINDING_SCHEMA,
@@ -41,9 +32,6 @@ export {
LOCAL_AUTHORITY_SHADOW_OUTBOX_COMMIT_SCHEMA,
LOCAL_AUTHORITY_SHADOW_OUTBOX_ENTRY_SCHEMA,
};
-export const LEASE_PARTITION = "leases";
-const LEASE_FILE = /^[A-Za-z0-9_.-]+\.json$/u;
-const LEASE_SOURCE_FIELDS = ["todo_id", "version", "lease_epoch", "status", "updated_at"] as const;
export interface LegacyLocalAuthorityShadowBinding {
schema_version: typeof LOCAL_AUTHORITY_SHADOW_BINDING_SCHEMA;
@@ -95,11 +83,6 @@ export function sha256Digest(input: Uint8Array | string): string {
return `sha256:${createHash("sha256").update(input).digest("hex")}`;
}
-/** Digest of the exact bytes `atomicWriteJson` persists for a lease record. */
-export function leaseRecordDigest(record: JsonObject): string {
- return sha256Digest(`${JSON.stringify(record, null, 2)}\n`);
-}
-
export function outboxPartitionDirectory(
runtimeRoot: string,
goalId: string,
@@ -171,245 +154,3 @@ export function decodeOutboxCursor(value: unknown, partition: string): JsonObjec
parsed.toISOString().slice(0, 19) !== timestamp.slice(0, 19)) throw invalid();
return record;
}
-
-async function nextSeq(
- directory: string,
- runtimeRoot: string,
- goalId: string,
- lineageId: string,
- goalRef: unknown,
-): Promise {
- let highest = 0;
- try {
- for (const name of await readdir(directory)) {
- const match = OUTBOX_ENTRY_FILE_PATTERN.exec(name);
- if (match !== null) highest = Math.max(highest, Number(match[1]));
- }
- } catch (error) {
- if (!isMissing(error)) throw error;
- }
- const cursor = await readOutboxCursor(directory, LEASE_PARTITION);
- const proved = cursor === null
- ? await readProvenShadowSequence(runtimeRoot, goalId, LEASE_PARTITION, lineageId, goalRef)
- : cursor.last_seq as number;
- highest = Math.max(highest, proved);
- if (highest >= MAX_OUTBOX_SEQUENCE) throw new Error("outbox sequence exhausted");
- return highest + 1;
-}
-
-/**
- * Read the lease partition for one capture.
- *
- * The legacy lease directory is append-retained history: archiving a Todo
- * leaves its released lease file on disk. The source projection models only
- * the current Todo graph, so it drops a lease whose Todo is no longer part of
- * that graph (see `build_todo_runtime_shadow_projection`). Capture must apply
- * the same rule, or archiving a Todo after a released lease writes an orphan
- * edge into the candidate head and parity reports `shadow_projection_drift`.
- *
- * `activeTodoIds` is the current Todo graph supplied by the caller; `null`
- * means the caller could not read it, and the capture stays strict rather
- * than guessing a projection.
- */
-async function readLeasePartition(
- leaseDirectory: string,
- plannedStem: string,
- plannedLease: JsonObject | null,
- activeTodoIds: ReadonlySet | null,
-): Promise {
- const records = new Map();
- let names: string[] = [];
- try {
- names = await readdir(leaseDirectory);
- } catch (error) {
- if (!isMissing(error)) throw error;
- }
- for (const name of names) {
- if (!LEASE_FILE.test(name) || name.startsWith(".")) continue;
- const stem = name.slice(0, -".json".length);
- if (stem === plannedStem) continue;
- if (activeTodoIds !== null && !activeTodoIds.has(stem)) continue;
- const raw: unknown = JSON.parse(await readFile(join(leaseDirectory, name), "utf8"));
- if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
- records.set(stem, raw as JsonObject);
- }
- }
- if (plannedLease !== null) records.set(plannedStem, plannedLease);
- const stems = [...records.keys()];
- stems.sort(authorityUnicodeCompare);
- return stems.map((stem) => ({ file_stem: stem, record: records.get(stem) as JsonObject }));
-}
-
-function leaseSourceFacts(record: JsonObject | null): JsonObject | null {
- if (record === null) return null;
- const facts: JsonObject = {};
- for (const field of LEASE_SOURCE_FIELDS) {
- const value = record[field];
- if (value === undefined) continue;
- if (typeof value === "string" || typeof value === "number" || value === null) {
- facts[field] = value;
- }
- }
- return facts;
-}
-
-export interface LeaseOutboxCaptureInput {
- runtime_root: string;
- goal_id: string;
- lease_directory: string;
- write_class: string;
- operation_id: string | null;
- previous_lease: JsonObject | null;
- planned_lease: JsonObject;
- /**
- * Current Todo graph for the goal, used to drop leases orphaned by an
- * archived Todo exactly as the source projection does. `null` keeps the
- * strict pre-existing behavior.
- */
- active_todo_ids: readonly string[] | null;
- goal_ref?: unknown;
-}
-
-export interface LeaseOutboxCapture {
- entry_id: string | null;
- seq: number | null;
- source_bytes_digest: string | null;
- failure: { reason_code: string; error_class: string } | null;
- skipped_reason?: string;
- /** Write the committed marker after the lease record landed. Never throws. */
- commit(): Promise;
-}
-
-function failureOf(reasonCode: string, error: unknown): { reason_code: string; error_class: string } {
- return {
- reason_code: reasonCode,
- error_class: error instanceof Error ? error.constructor.name : typeof error,
- };
-}
-
-/**
- * Record a prepared lease-partition entry for the record about to be written.
- *
- * Call inside the lease lock, right before `atomicWriteJson(leasePath, planned)`;
- * call `commit()` right after it returns. The primary owner must reject an
- * active mutation when preparation returns a failure.
- */
-export async function beginLeaseOutboxEntry(
- input: LeaseOutboxCaptureInput,
-): Promise {
- const inert: LeaseOutboxCapture = {
- entry_id: null,
- seq: null,
- source_bytes_digest: null,
- failure: null,
- async commit(): Promise {},
- };
- const plannedStem = typeof input.planned_lease.todo_id === "string"
- ? input.planned_lease.todo_id
- : "";
- if (plannedStem.length === 0) {
- return { ...inert, failure: { reason_code: "outbox_prepare_failed", error_class: "MissingTodoId" } };
- }
- const directory = outboxPartitionDirectory(input.runtime_root, input.goal_id, LEASE_PARTITION);
- try {
- const binding = await requireShadowCaptureBinding(input.runtime_root, input.goal_id);
- const boundGoalRef = shadowBindingGoalRef(binding);
- if (boundGoalRef === null && input.goal_ref !== undefined && input.goal_ref !== null) {
- throw new ShadowManagementError("legacy_goal_binding");
- }
- if (boundGoalRef !== null) {
- requireShadowBindingGoalRef(binding, input.goal_ref, input.goal_id);
- }
- if (input.previous_lease !== null &&
- canonicalAuthorityBytes(input.previous_lease).equals(canonicalAuthorityBytes(input.planned_lease))) {
- return { ...inert, skipped_reason: "partition_unchanged" };
- }
- const activeTodoIds = input.active_todo_ids === null
- ? null
- : new Set(input.active_todo_ids);
- const projection = { leases: await readLeasePartition(input.lease_directory, plannedStem, input.planned_lease, activeTodoIds) };
- const previousRecords = await readLeasePartition(input.lease_directory, plannedStem, input.previous_lease, activeTodoIds);
- for (const item of [...previousRecords, ...projection.leases]) {
- const record = item.record as JsonObject;
- if (record.goal_id !== input.goal_id || record.todo_id !== item.file_stem) {
- throw new Error("lease source identity does not match its partition");
- }
- }
- const previousPartitionDigest = sha256Digest(canonicalAuthorityBytes({
- leases: previousRecords.map((item) => item.record),
- }));
- const bytesDigest = leaseRecordDigest(input.planned_lease);
- const seq = await nextSeq(
- directory,
- input.runtime_root,
- input.goal_id,
- binding.capture_lineage_id,
- input.goal_ref ?? null,
- );
- const sourceRootDigest = binding.source_root_digest;
- const entryId = outboxEntryIdentity(input.goal_id, LEASE_PARTITION, seq, bytesDigest,
- binding.capture_lineage_id, sourceRootDigest);
- const entry: JsonObject = {
- schema_version: LOCAL_AUTHORITY_SHADOW_OUTBOX_ENTRY_SCHEMA,
- goal_id: input.goal_id,
- partition: LEASE_PARTITION,
- seq,
- entry_id: entryId,
- capture_lineage_id: binding.capture_lineage_id,
- writer: {
- runtime: "typescript",
- write_class: input.write_class,
- operation_id: input.operation_id,
- },
- source: {
- kind: "task_lease_record",
- previous_partition_digest: previousPartitionDigest,
- previous_bytes_digest: input.previous_lease === null
- ? null
- : leaseRecordDigest(input.previous_lease),
- bytes_digest: bytesDigest,
- lease: leaseSourceFacts(input.planned_lease),
- previous_lease: leaseSourceFacts(input.previous_lease),
- event_id: null,
- },
- source_root_digest: sourceRootDigest,
- projection,
- partition_digest: null,
- prepared_at: new Date().toISOString(),
- };
- await durableWriteJson(join(directory, outboxEntryFileName(seq, entryId, "prepared")), entry);
- let committed = false;
- const capture: LeaseOutboxCapture = {
- entry_id: entryId,
- seq,
- source_bytes_digest: bytesDigest,
- failure: null,
- async commit(): Promise {
- if (committed) return;
- committed = true;
- try {
- await durableWriteJson(
- join(directory, outboxEntryFileName(seq, entryId, "committed")),
- {
- schema_version: LOCAL_AUTHORITY_SHADOW_OUTBOX_COMMIT_SCHEMA,
- entry_id: entryId,
- capture_lineage_id: binding.capture_lineage_id,
- committed_at: new Date().toISOString(),
- },
- );
- } catch (error) {
- capture.failure = failureOf("outbox_commit_marker_failed", error);
- }
- },
- };
- return capture;
- } catch (error) {
- if (error instanceof ShadowManagementError && error.code === "bootstrap_required") {
- return { ...inert, skipped_reason: "bootstrap_required" };
- }
- if (error instanceof ShadowManagementError) {
- return { ...inert, failure: failureOf(error.code, error) };
- }
- return { ...inert, failure: failureOf("outbox_prepare_failed", error) };
- }
-}
diff --git a/loopx/control_plane/coordination/local_authority_write.ts b/loopx/control_plane/coordination/local_authority_write.ts
index 864da1ba9b..81333cc984 100644
--- a/loopx/control_plane/coordination/local_authority_write.ts
+++ b/loopx/control_plane/coordination/local_authority_write.ts
@@ -1,4 +1,6 @@
import {withFileMutationLock} from "../effect_runtime_io.ts";
+import type {JsonObject} from "../effect_program.ts";
+import {loadLegacyCoordinationWriterFence} from "./legacy_writer_fence.ts";
import {requireShadowPrimaryWriteAllowed, shadowMaintenanceLockPath} from "./shadow_management.ts";
// File-v0 still verifies and republishes its retained history on each write.
@@ -6,11 +8,93 @@ import {requireShadowPrimaryWriteAllowed, shadowMaintenanceLockPath} from "./sha
// section; the provider CAS and hard lease remain the decision authority.
const CANONICAL_WRITER_LOCK_TIMEOUT_MS = 30_000;
+export type CanonicalBusinessWrite =
+ | "todo_claim"
+ | "todo_create"
+ | "todo_update"
+ | "todo_terminal"
+ | "todo_archive"
+ | "monitor_poll"
+ | "team_plan"
+ | "handoff_mode_set"
+ | "task_lease_acquire"
+ | "task_lease_renew"
+ | "task_lease_transfer"
+ | "task_lease_release"
+ | "goal_create";
+
+export class CanonicalWriteRouteError extends Error {
+ readonly code: "canonical_authority_required" | "canonical_authority_fence_invalid";
+ readonly payload: JsonObject;
+
+ constructor(code: CanonicalWriteRouteError["code"], goalId: string, operation: CanonicalBusinessWrite, reason: string) {
+ super(reason);
+ this.name = "CanonicalWriteRouteError";
+ this.code = code;
+ this.payload = {
+ status: "failed",
+ reason_code: code,
+ goal_id: goalId,
+ operation,
+ write_admitted: false,
+ decision_read_from_provider: false,
+ legacy_fallback_used: false,
+ recovery: {
+ action: code === "canonical_authority_required"
+ ? "migrate_goal_authority"
+ : "repair_canonical_authority_fence",
+ backup_required: true,
+ },
+ };
+ }
+}
+
+export function canonicalWriteRouteFailure(error: unknown): JsonObject {
+ return error instanceof CanonicalWriteRouteError ? {...error.payload, reason: error.message} : {};
+}
+
+interface CanonicalWriterOptions {
+ businessWrite?: CanonicalBusinessWrite;
+}
+
+/** Call only while holding the Goal maintenance lock. */
+export async function requireCanonicalBusinessWrite(
+ root: string,
+ goalId: string,
+ operation: CanonicalBusinessWrite,
+): Promise {
+ const fence = await loadLegacyCoordinationWriterFence(root, goalId);
+ if (fence.status === "missing") {
+ throw new CanonicalWriteRouteError(
+ "canonical_authority_required",
+ goalId,
+ operation,
+ "Canonical authority is required; back up and migrate this Goal before retrying",
+ );
+ }
+ if (fence.status === "failed") {
+ throw new CanonicalWriteRouteError(
+ "canonical_authority_fence_invalid",
+ goalId,
+ operation,
+ fence.reason,
+ );
+ }
+}
+
/** Canonical command writers share the maintenance guard; provider CAS owns state. */
-export async function withCanonicalWriter(root: string, goalId: string, dryRun: boolean, write: () => Promise): Promise {
- if (dryRun) return await write();
+export async function withCanonicalWriter(
+ root: string,
+ goalId: string,
+ _dryRun: boolean,
+ write: () => Promise,
+ options: CanonicalWriterOptions = {},
+): Promise {
return await withFileMutationLock(shadowMaintenanceLockPath(root, goalId), async () => {
await requireShadowPrimaryWriteAllowed(root, goalId);
+ if (options.businessWrite !== undefined) {
+ await requireCanonicalBusinessWrite(root, goalId, options.businessWrite);
+ }
return await write();
}, CANONICAL_WRITER_LOCK_TIMEOUT_MS);
}
diff --git a/loopx/control_plane/coordination/runtime_shadow_writer_adapter.py b/loopx/control_plane/coordination/runtime_shadow_writer_adapter.py
deleted file mode 100644
index 167407d60b..0000000000
--- a/loopx/control_plane/coordination/runtime_shadow_writer_adapter.py
+++ /dev/null
@@ -1,206 +0,0 @@
-"""Transaction-capture orchestration for legacy Todo and lease writers."""
-
-from __future__ import annotations
-
-from collections.abc import Mapping
-from pathlib import Path
-from typing import Any
-
-from . import local_authority_shadow_outbox as outbox
-from .local_authority_shadow_projection import LEASE_PARTITION
-from .legacy_writer_fence import (
- ActiveStateAuthorityMutationError as ActiveStateAuthorityMutationError,
- require_prose_state_write_allowed as require_prose_state_write_allowed,
-)
-from .configuration import resolve_coordination_runtime_shadow_config
-from .shadow_goal_scope import shadow_goal_scope
-from .shadow_management import ShadowManagementError, read_shadow_capture_binding, require_shadow_primary_write_allowed
-
-
-def begin_todo_runtime_shadow_capture(
- *,
- registry_path: Path,
- runtime_root: Path,
- goal_id: str,
- state_path: Path,
- write_class: str,
- original_text: str,
-) -> outbox.TodoPartitionCapture:
- """Create the default-off transaction capture while the Todo lock is held."""
-
- active_binding = read_shadow_capture_binding(runtime_root, goal_id)["status"] == "active"
- goal_ref: dict[str, str] | None = None
- projector = None
- try:
- with shadow_goal_scope(registry_path, goal_id=goal_id) as scope:
- goal_ref = (
- dict(scope.goal_ref) if scope.goal_ref is not None else None
- )
- enabled = (
- active_binding
- or resolve_coordination_runtime_shadow_config(scope.goal).enabled
- )
- if enabled:
- from ...rollout_event_log import load_rollout_events, rollout_event_log_path
- from ..todos.todo_index import MAX_TODO_INDEX_ROLLOUT_EVENTS_PER_GOAL
- from .local_authority_shadow_adapter import todo_partition_projector
-
- events = load_rollout_events(
- rollout_event_log_path(runtime_root, goal_id),
- limit=MAX_TODO_INDEX_ROLLOUT_EVENTS_PER_GOAL,
- )
- projector = todo_partition_projector(
- scope.goal,
- state_path=state_path,
- rollout_events=events,
- )
- except Exception:
- enabled = active_binding
- projector = None
- return outbox.TodoPartitionCapture.begin(
- enabled=enabled,
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id=goal_id,
- state_path=state_path,
- write_class=write_class,
- original_text=original_text,
- projector=projector,
- goal_ref=goal_ref,
- )
-
-
-
-def require_runtime_shadow_capture_prepared(
- capture: outbox.TodoPartitionCapture, *, runtime_root: Path, goal_id: str,
-) -> None:
- """Active lineage cannot admit a primary transition without durable preparation."""
-
- if capture.outcome.failure is not None and require_shadow_primary_write_allowed(runtime_root, goal_id) is not None:
- raise ShadowManagementError(
- "shadow_capture_prepare_failed",
- "durable shadow preparation failed; the primary state was not changed",
- )
-
-
-def write_captured_todo_state(
- capture: outbox.TodoPartitionCapture,
- *,
- runtime_root: Path,
- goal_id: str,
- state_path: Path,
- text: str,
-) -> None:
- """Under the primary lock, prepare before replacement and mark only after durability."""
-
- from ..runtime.document_io import atomic_write_state_text
-
- def write() -> None:
- capture.prepare(text)
- require_runtime_shadow_capture_prepared(
- capture,
- runtime_root=runtime_root,
- goal_id=goal_id,
- )
- atomic_write_state_text(state_path, text)
- capture.committed()
-
- captured_goal_ref = capture.goal_ref
- if captured_goal_ref is None:
- write()
- return
- registry_path = capture.registry_path
- if registry_path is None:
- raise ShadowManagementError(
- "goal_instance_id_missing",
- "exact Todo capture is missing its source registry",
- )
- with shadow_goal_scope(registry_path, goal_id=goal_id) as scope:
- if scope.goal_ref != captured_goal_ref:
- raise ShadowManagementError(
- "stale_goal_instance",
- "Todo capture belongs to a retired Goal instance",
- )
- write()
-
-
-def settle_todo_runtime_shadow_capture(
- payload: dict[str, Any],
- *,
- registry_path: Path,
- runtime_root: Path,
- goal_id: str,
- capture: outbox.TodoPartitionCapture,
- emit_disabled: bool = True,
-) -> dict[str, Any]:
- """Boundedly drain one transaction capture after releasing the Todo lock."""
-
- from .local_authority_shadow_adapter import capture_evidence, drain_local_authority_shadow_outbox
-
- drain = (
- drain_local_authority_shadow_outbox(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id=goal_id,
- )
- if capture.outcome.entry_id is not None
- else None
- )
- if emit_disabled or capture.enabled:
- payload["coordination_runtime_shadow"] = capture_evidence(
- goal_id=goal_id, capture=capture.outcome, drain=drain,
- )
- return payload
-
-
-def settle_lease_runtime_shadow_capture(
- payload: dict[str, Any],
- *,
- registry_path: Path | None,
- runtime_root: Path,
- goal_id: str,
-) -> dict[str, Any]:
- """Turn the TS writer capture receipt into bounded runtime-shadow evidence."""
-
- raw = payload.pop("coordination_runtime_shadow_capture", None)
- if not isinstance(raw, Mapping) or registry_path is None:
- return payload
- capture = outbox.CaptureOutcome(
- entry_id=str(raw["entry_id"]) if isinstance(raw.get("entry_id"), str) else None,
- partition=LEASE_PARTITION,
- seq=int(raw["seq"]) if isinstance(raw.get("seq"), int) else None,
- source_bytes_digest=(
- str(raw["source_bytes_digest"])
- if isinstance(raw.get("source_bytes_digest"), str)
- else None
- ),
- failure=dict(raw["failure"]) if isinstance(raw.get("failure"), Mapping) else None,
- skipped_reason=str(raw["skipped_reason"]) if raw.get("skipped_reason") else None,
- )
- from .local_authority_shadow_adapter import (
- capture_evidence,
- drain_local_authority_shadow_outbox,
- )
-
- drain = (
- drain_local_authority_shadow_outbox(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id=goal_id,
- )
- if capture.entry_id is not None
- else None
- )
- payload["coordination_runtime_shadow"] = capture_evidence(
- goal_id=goal_id,
- capture=capture,
- drain=drain,
- )
- return payload
-
-
-__all__ = [
- "begin_todo_runtime_shadow_capture",
- "settle_lease_runtime_shadow_capture",
- "settle_todo_runtime_shadow_capture",
-]
diff --git a/loopx/control_plane/coordination/task_lease_acquire.ts b/loopx/control_plane/coordination/task_lease_acquire.ts
index f2335e7150..792f38c165 100644
--- a/loopx/control_plane/coordination/task_lease_acquire.ts
+++ b/loopx/control_plane/coordination/task_lease_acquire.ts
@@ -80,7 +80,9 @@ export async function executeCanonicalTaskLeaseAcquire(store: AuthorityStore, ra
const decision = decideTaskLeaseAcquire({handoff_mode: mode!, registered_agents: input.registered_agents,
...facts, command: input});
if (decision.outcome === "rejected" || decision.outcome === "conflict") {
- return failed(decision.code, `canonical task lease acquire rejected: ${decision.code}`, {
+ return failed(decision.code, decision.code === "handoff_mode_retired"
+ ? "Legacy ownership cannot grant new execution; release retained leases and review handoff-mode plan-migration/migrate before acquiring again."
+ : `canonical task lease acquire rejected: ${decision.code}`, {
...(decision.code === "write_scope_conflict" ? {recommended_action:
"Coordinate shared writes with the listed holders. For isolated code edits, use your own separate Git worktree and acquire with --write-worktree; file overlaps become integration advisories. A known same-checkout collision remains rejected. Never take over a foreign lease or use code-edit mode for shared runtime state."} : {}),
handoff_mode: mode, expected_version: input.expected_version, actual_version: leaseVersion(facts.current),
diff --git a/loopx/control_plane/coordination/task_lease_lifecycle.ts b/loopx/control_plane/coordination/task_lease_lifecycle.ts
index f9c1f600bc..0719fd9b13 100644
--- a/loopx/control_plane/coordination/task_lease_lifecycle.ts
+++ b/loopx/control_plane/coordination/task_lease_lifecycle.ts
@@ -11,7 +11,7 @@ import {planLeaseClaimTransfer} from "./lease_claim_transfer.ts";
import {decideTaskLeaseLifecycle, materializeTaskLeaseLifecycle,
type TaskLeaseLifecycleDecisionOperation, type TaskLeaseLifecycleDecisionCommand} from "../work_items/task_lease_lifecycle_decision.ts";
import {requireStringLiteral} from "../runtime_decode.ts";
-import {HANDOFF_MODES} from "./handoff_mode_policy.ts";
+import {HANDOFF_MODES, retiredHandoffModeRejection, HANDOFF_MODE_RETIRED_REASON} from "./handoff_mode_policy.ts";
import {leaseEpoch, leaseIsActive, leaseVersion, normalizeAgent, normalizeGoalId,
normalizeIdempotencyKey, normalizeOwner, normalizeTodoId,
normalizeTtl, utcIsoformat, TaskLeaseAcquireError, type LeaseRecord} from "../work_items/task_lease_acquire.ts";
@@ -112,6 +112,9 @@ export async function executeCanonicalTaskLeaseLifecycle(store: AuthorityStore,
const current = await store.loadAuthority();
if (current.status !== "loaded") throw new AuthorityStoreProtocolError("current lease authority unavailable");
validateCoordinationTodoReadModel(current.head, input.goal_id);
+ const retired = retiredHandoffModeRejection(current.head.handoff_mode ?? "legacy");
+ if (retired !== null) return {...failed(retired, HANDOFF_MODE_RETIRED_REASON),
+ original_receipt: result.original_receipt};
const dependency = todoExecutionDependencyRejection(
indexCoordinationProjection(current.head, input.goal_id).todos, input.todo_id, input.now);
return dependency === null ? result : {...failed(dependency.code, dependency.reason),
@@ -151,7 +154,9 @@ export async function executeCanonicalTaskLeaseLifecycle(store: AuthorityStore,
...(lease.write_repository == null ? {} : {write_repository: String(lease.write_repository)}), acquire_ttl_seconds: null} : null,
command});
if (decision.outcome === "rejected" || decision.outcome === "conflict") {
- return {...failed(decision.code, `canonical task lease ${input.operation} rejected: ${decision.code}`),
+ return {...failed(decision.code, decision.code === "handoff_mode_retired"
+ ? "Legacy ownership cannot renew or transfer execution; release retained leases and review handoff-mode plan-migration/migrate."
+ : `canonical task lease ${input.operation} rejected: ${decision.code}`),
handoff_mode: mode, expected_version: input.expected_version, actual_version: leaseVersion(lease),
...(todo ? {todo_status: todo.status, claimed_by: todo.claimed_by ?? null, excluded_agents: excluded} : {})};
}
diff --git a/loopx/control_plane/coordination/task_lease_proof.ts b/loopx/control_plane/coordination/task_lease_proof.ts
index 0489444603..8b4605f64c 100644
--- a/loopx/control_plane/coordination/task_lease_proof.ts
+++ b/loopx/control_plane/coordination/task_lease_proof.ts
@@ -1,5 +1,6 @@
/** Current nonterminal mutation proof. Admission stays in the lifecycle owner;
* this boundary never acquires, renews, releases or transfers a lease. */
+import {retiredHandoffModeRejection} from "./handoff_mode_policy.ts";
import type {JsonObject} from "../effect_program.ts";
import {requireJsonObject} from "../runtime_decode.ts";
import {EffectRuntimeRequestError} from "../effect_runtime_errors.ts";
@@ -8,7 +9,7 @@ import {indexCoordinationProjection} from "./coordination_projection.ts";
import {canonicalTaskLease, canonicalTaskLeaseAcquireFacts} from "./task_lease_state.ts";
import {coordinationTodoWriteScopes} from "./todo_write_scopes.ts";
import {decideTaskLeaseAcquire} from "../work_items/task_lease_acquire_decision.ts";
-import {leaseOwnerRejection} from "../work_items/task_lease_eligibility.ts";
+import {leaseOwnerRejection, leaseExecutionTargetRejection} from "../work_items/task_lease_eligibility.ts";
import type {CoordinationTodoUpdateInput} from "./todo_update_intent.ts";
import {deferredReopenRejection, isDeferredReopen, isOwnerDeferral} from "./todo_deferred_lifecycle.ts";
import {blockedLifecycleRejection, isBlockedLifecycleTransition} from "./todo_blocked_lifecycle.ts";
@@ -70,6 +71,10 @@ export function evaluateCanonicalTaskLeaseProof(input: {
actor_agent_id: string | null; registered_agents: readonly string[];
lease_idempotency_key: string | null; lease_expected_version: number | null; now: Date;
}) {
+ const retired = retiredHandoffModeRejection(input.handoff_mode);
+ if (retired !== null) return {outcome: "rejected", code: retired};
+ const targetRejection = leaseExecutionTargetRejection(input.todo);
+ if (targetRejection !== null) return {outcome: "rejected", code: targetRejection};
const {lease} = input;
if (!(input.now instanceof Date) || !Number.isFinite(input.now.valueOf())) {
throw new EffectRuntimeRequestError("lease proof requires a valid transaction clock");
@@ -139,6 +144,24 @@ export function todoUpdateLeaseRecovery(head: JsonObject, input: CoordinationTod
};
const todo = index.todos.get(input.todo_id)!;
const intent = input.planning_intent ?? {};
+ if (leaseExecutionTargetRejection(todo) !== null) {
+ if (!isBoundUserActionMetadataUpdate(todo, input)) {
+ return {...base, action: "resolve_lifecycle_edit", reason_code: "todo_not_executable",
+ reason: "User actions cannot acquire execution leases. Use the registered bound actor and the supported administrative text, note or evidence edit; other changes require their owning lifecycle. A new lease cannot authorize this request."};
+ }
+ if (lease === null || lease.status === "released") {
+ return {...base, action: "resolve_lifecycle_edit", reason_code: "todo_not_executable",
+ reason: "User actions do not grant execution leases. Retry this bound administrative copy edit without either lease proof flag, using a fresh update operation and current provider revision. Preserve released history; do not acquire another lease.",
+ retry: {command: "loopx todo update", goal_id: input.goal_id, todo_id: input.todo_id,
+ agent_id: input.actor_agent_id,
+ requires_flags: ["--update-operation-id", "--update-expected-provider-revision"]}};
+ }
+ return {...base, action: "reconcile_lease_owner", reason_code: "todo_not_executable",
+ reason: "The retained User-action execution must be released by its holder using the exact current owner/key/version, including after expiry. Then retry the bound administrative copy edit without lease proof under a fresh operation and current provider revision. Do not borrow the holder's proof or acquire another lease.",
+ release: {command: "loopx task-lease release",
+ requires_flags: ["--owner", "--idempotency-key", "--expected-version"],
+ proof_source: "current_owner_lease_readback"}};
+ }
if (mode === "soft_claim" && isSoftClaimReleasedCopyUpdate(todo,
index.leases.get(input.todo_id), input)) {
return {...base, action: "resolve_lifecycle_edit",
diff --git a/loopx/control_plane/coordination/task_lease_state.ts b/loopx/control_plane/coordination/task_lease_state.ts
index 5987c3b840..29b9dd5034 100644
--- a/loopx/control_plane/coordination/task_lease_state.ts
+++ b/loopx/control_plane/coordination/task_lease_state.ts
@@ -37,7 +37,11 @@ export function canonicalLeaseTodoFact(todo: JsonObject | undefined): TodoFact |
if (!todo || todo.archive_state !== "active") return null;
const excluded = todo.excluded_agents ?? [];
if (!Array.isArray(excluded)) throw new AuthorityStoreProtocolError("Todo exclusions must be an array");
+ if (todo.task_class != null && typeof todo.task_class !== "string") {
+ throw new AuthorityStoreProtocolError("Todo task_class must be a string");
+ }
return {todo_id: String(todo.todo_id), status: String(todo.status),
+ task_class: todo.task_class as string | null | undefined,
task_repository: normalizeTodoRepository(todo.task_repository),
claimed_by: todo.claimed_by == null ? null : normalizeTodoAgent(todo.claimed_by, "todo.claimed_by"),
excluded_agents: excluded.map(value => normalizeTodoAgent(value, "todo.excluded_agents"))};
diff --git a/loopx/control_plane/coordination/todo_acceptance_restoration.ts b/loopx/control_plane/coordination/todo_acceptance_restoration.ts
index 0d4df4ce2f..3d0777d158 100644
--- a/loopx/control_plane/coordination/todo_acceptance_restoration.ts
+++ b/loopx/control_plane/coordination/todo_acceptance_restoration.ts
@@ -41,7 +41,8 @@ export function acceptanceRestoration(
input.completion_validation_revision !== undefined) {
return unavailable("Exact restoration supports only the prior text/wait; ask the owner to review and rebind other work changes");
}
- const candidate = prepareUpdatedTodo(todo, input, head).next;
+ const {monitor_observation: _absentObservation, ...restorationInput} = input;
+ const candidate = prepareUpdatedTodo(todo, restorationInput, head).next;
const binding = state.bindings.find(item => item.todo_id === input.todo_id)!;
if (goalAcceptanceTodoDigest(candidate) !== binding.todo_semantic_digest) {
return unavailable("The supplied text/wait does not reconstruct the exact owner-confirmed declaration; ask the owner to review and rebind this Todo");
diff --git a/loopx/control_plane/coordination/todo_claim.ts b/loopx/control_plane/coordination/todo_claim.ts
index 2ecd77edaa..1bebb93f6c 100644
--- a/loopx/control_plane/coordination/todo_claim.ts
+++ b/loopx/control_plane/coordination/todo_claim.ts
@@ -1,3 +1,4 @@
+import {retiredHandoffModeRejection, HANDOFF_MODE_RETIRED_REASON} from "./handoff_mode_policy.ts";
import {AUTHORITY_SOURCE_CHANGED, uncheckedAuthoritySource, type AuthoritySourceCheck} from "./authority_source.ts";
import {currentLeaseAcquisitionProof} from "./lease_acquisition_proof.ts";
import {canonicalTaskLeaseAcquireFacts} from "./task_lease_state.ts";
@@ -526,6 +527,9 @@ async function executeClaimAttempt(
if (!["legacy", "soft_claim", "hard_lease"].includes(handoffMode)) {
return failure("invalid_handoff_mode", "canonical projection has an invalid handoff mode");
}
+ const retired = retiredHandoffModeRejection(handoffMode);
+ if (retired !== null) return failure(retired, HANDOFF_MODE_RETIRED_REASON,
+ {todo_id: input.todo_id, handoff_mode: handoffMode}, "decision_rejection");
if (leaseRequest !== null && handoffMode !== "hard_lease") {
return failure(
"claim_lease_requires_hard_lease",
diff --git a/loopx/control_plane/coordination/todo_lifecycle_decision.ts b/loopx/control_plane/coordination/todo_lifecycle_decision.ts
index 8f732ccdde..1a0d63bc71 100644
--- a/loopx/control_plane/coordination/todo_lifecycle_decision.ts
+++ b/loopx/control_plane/coordination/todo_lifecycle_decision.ts
@@ -27,7 +27,7 @@ export const COORDINATION_TERMINAL_FENCE_REQUEST_SCHEMA =
"loopx_coordination_terminal_fence_request_v0";
export const COORDINATION_TERMINAL_FENCE_RESULT_SCHEMA =
"loopx_coordination_terminal_fence_result_v0";
-import {HANDOFF_MODES, type HandoffMode} from "./handoff_mode_policy.ts";
+import {HANDOFF_MODES, retiredHandoffModeRejection, type HandoffMode} from "./handoff_mode_policy.ts";
const OUTCOMES = ["approve", "reject", "cancel"] as const;
const AUTHORITY_ACTIONS = ["complete", "reassign", "supersede", "update"] as const;
const EXECUTOR_RECLAIM_ACTION = "reclaim";
@@ -79,7 +79,7 @@ interface LifecycleDecisionRequest {
readonly command: LifecycleCommand;
readonly handoff_mode: HandoffMode;
readonly registered_agents: readonly string[];
- readonly lifecycle_grants: readonly LifecycleGrant[];
+ readonly resolve_lifecycle_grants: () => readonly LifecycleGrant[];
readonly todo: TodoFact;
readonly decision_target: TodoFact | null;
readonly lease: LeaseFact | null;
@@ -233,7 +233,7 @@ function lifecycleGrants(
agent_id: agentId,
actions: [...new Set(actions)],
requires_reason: requireBoolean(
- grant.requires_reason,
+ grant.requires_reason === undefined ? true : grant.requires_reason,
`lifecycle_grants[${index}].requires_reason`,
),
};
@@ -276,14 +276,22 @@ function decodeRequest(value: unknown, kind: "terminal" | "mutation" = "terminal
);
const actor = optionalAgent(request.actor_agent_id, "actor_agent_id");
const internalReclaim = kind === "mutation" && request.authority_action === EXECUTOR_RECLAIM_ACTION;
+ // The executor-only action is never a public delegation, even if unused.
+ if (!internalReclaim && Array.isArray(request.lifecycle_grants) &&
+ request.lifecycle_grants.some(grant => grant !== null && typeof grant === "object" &&
+ Array.isArray(grant.actions) && grant.actions.some((action: unknown) =>
+ typeof action === "string" && action.trim().toLowerCase() === EXECUTOR_RECLAIM_ACTION))) {
+ throw new EffectRuntimeRequestError("executor reclaim is not a public lifecycle grant");
+ }
+ const reclaimGrants = internalReclaim ? executorReclaimGrant(request, actor) : null;
const outcome = kind === "terminal" ? optionalString(request.decision_outcome, "decision_outcome") : null;
const decoded = {
command: requireStringLiteral(request.command,
kind === "terminal" ? COMMANDS : MUTATION_COMMANDS, "command"),
handoff_mode: requireStringLiteral(request.handoff_mode, HANDOFF_MODES, "handoff_mode"),
registered_agents: registeredAgents,
- lifecycle_grants: internalReclaim ? executorReclaimGrant(request, actor)
- : lifecycleGrants(request.lifecycle_grants ?? [], registeredAgents),
+ resolve_lifecycle_grants: () => reclaimGrants ??
+ lifecycleGrants(request.lifecycle_grants ?? [], registeredAgents),
todo: request.todo === null ? null : todoFact(request.todo, "todo"),
decision_target: kind !== "terminal" || request.decision_target === null || request.decision_target === undefined
? null
@@ -377,6 +385,8 @@ function authority(request: LifecycleDecisionRequest):
| { mode: string; ownershipGate: CoordinationTodoTerminalDecisionResult["ownership_gate"] }
| CoordinationTodoTerminalDecisionResult {
const { todo, actor_agent_id: actor, registered_agents: registered } = request;
+ const retired = retiredHandoffModeRejection(request.handoff_mode);
+ if (retired !== null) return result("rejected", retired);
if (registered.length <= 1) {
if (actor !== null) {
const rejection = registeredTodoMutationRejection(todo, actor, registered);
@@ -395,7 +405,9 @@ function authority(request: LifecycleDecisionRequest):
const rejection = registeredTodoMutationRejection(todo, actor, registered);
if (rejection !== null && rejection !== "claim_owner_mismatch") return result("rejected", rejection);
if (rejection === "claim_owner_mismatch") {
- const grant = request.lifecycle_grants.find((candidate) => candidate.agent_id === actor);
+ // Delegation is consulted only after ordinary actor/owner authorization.
+ // Unrelated invalid grants cannot disable the owner or exact user gate.
+ const grant = request.resolve_lifecycle_grants().find((candidate) => candidate.agent_id === actor);
if (grant === undefined) return result("rejected", "claim_owner_mismatch");
if (!grant.actions.includes(request.authority_action)) {
return result("rejected", "delegation_action_not_granted");
@@ -433,6 +445,8 @@ function terminalFence(
authorityMode: string | null,
requireActiveWhenFenceSupplied = true,
): CoordinationTodoTerminalDecisionResult {
+ const retired = retiredHandoffModeRejection(request.handoff_mode);
+ if (retired !== null) return result("rejected", retired);
const lease = request.lease;
const timeActive = lease !== null && lease.present && lease.active;
const effective = timeActive && ownerEligible(request, lease.owner);
diff --git a/loopx/control_plane/coordination/todo_monitor_cycle.ts b/loopx/control_plane/coordination/todo_monitor_cycle.ts
index c62571cac8..fd8f42516f 100644
--- a/loopx/control_plane/coordination/todo_monitor_cycle.ts
+++ b/loopx/control_plane/coordination/todo_monitor_cycle.ts
@@ -2,7 +2,7 @@
* Admission and observation validation run first; all effects join their CAS. */
import {evaluateCoordinationTodoMutationDecision, COORDINATION_TODO_MUTATION_DECISION_REQUEST_SCHEMA} from "./todo_lifecycle_decision.ts";
import {evaluateCanonicalTaskLeaseProof, type TaskLeaseProof} from "./task_lease_proof.ts";
-import {HANDOFF_MODES} from "./handoff_mode_policy.ts";
+import {HANDOFF_MODES, HANDOFF_MODE_RETIRED_REASON} from "./handoff_mode_policy.ts";
import {requireStringLiteral} from "../runtime_decode.ts";
import type {JsonObject} from "../effect_program.ts";
import type {CoordinationProjectionMutation} from "./coordination_projection.ts";
@@ -62,7 +62,8 @@ export function monitorMutationRejection(input: {
});
if (decision.outcome !== "apply") {
return reject(decision.code === "claim_owner_mismatch" ? "update_owner_mismatch" : String(decision.code),
- "Monitor observation is outside the actor's registered owner/binding scope");
+ decision.code === "handoff_mode_retired" ? HANDOFF_MODE_RETIRED_REASON
+ : "Monitor observation is outside the actor's registered owner/binding scope");
}
if (input.operation === "reactivate") {
return input.proof === null ? null : reject("monitor_reactivation_execution_proof_not_allowed",
@@ -76,7 +77,6 @@ export function monitorMutationRejection(input: {
}
return executionWait();
}
- if (lease === undefined && mode !== "hard_lease" && input.proof === null) return executionWait();
const fence = evaluateCanonicalTaskLeaseProof({todo, lease, handoff_mode: mode,
actor_agent_id: actor, registered_agents: registered, now: input.now,
lease_idempotency_key: input.proof?.idempotency_key ?? null,
diff --git a/loopx/control_plane/coordination/todo_monitor_poll.ts b/loopx/control_plane/coordination/todo_monitor_poll.ts
index 927947417c..a24fa50f65 100644
--- a/loopx/control_plane/coordination/todo_monitor_poll.ts
+++ b/loopx/control_plane/coordination/todo_monitor_poll.ts
@@ -17,6 +17,8 @@ export const COORDINATION_MONITOR_POLL_REQUEST_SCHEMA = "loopx_coordination_moni
export const COORDINATION_LEASED_MONITOR_POLL_REQUEST_SCHEMA = "loopx_coordination_monitor_poll_request_v1";
export const COORDINATION_WITNESSED_MONITOR_POLL_REQUEST_SCHEMA = "loopx_coordination_monitor_poll_request_v2";
export const COORDINATION_GUARDED_MONITOR_POLL_REQUEST_SCHEMA = "loopx_coordination_monitor_poll_request_v3";
+/** v4 can only recover an existing receipt; older runtimes reject its schema. */
+export const COORDINATION_MONITOR_RECEIPT_ONLY_REQUEST_SCHEMA = "loopx_coordination_monitor_poll_request_v4";
export const COORDINATION_MONITOR_POLL_RESULT_SCHEMA = "loopx_coordination_monitor_poll_result_v0";
const RECEIPT_SCHEMA = "loopx_coordination_monitor_poll_receipt_v0";
@@ -31,18 +33,38 @@ export interface CoordinationMonitorPollInput {
lease_proof?: TaskLeaseProof | null;
/** New auxiliary effects must qualify dependencies on the commit head. */
gate_scope_guard?: boolean;
+ /** A damaged display cannot authorize a fresh canonical observation. */
+ receipt_only?: boolean;
/** Authority clock supplied by the runtime, never observation.generated_at. */
now?: Date;
}
-function failure(reason_code: string, reason: string): JsonObject & {schema_version: typeof COORDINATION_MONITOR_POLL_RESULT_SCHEMA} {
- return {schema_version: COORDINATION_MONITOR_POLL_RESULT_SCHEMA, status: "failed", changed: false, reason_code, reason};
+type CoordinationMonitorPollFailureKind = "decision_rejection" | "protocol_failure";
+
+function failure(
+ reason_code: string,
+ reason: string,
+ kind: CoordinationMonitorPollFailureKind = "protocol_failure",
+): JsonObject & {schema_version: typeof COORDINATION_MONITOR_POLL_RESULT_SCHEMA} {
+ return {schema_version: COORDINATION_MONITOR_POLL_RESULT_SCHEMA, status: "failed",
+ changed: false, failure_kind: kind, reason_code, reason};
+}
+
+function rejection(
+ reason_code: string,
+ reason: string,
+): JsonObject & {schema_version: typeof COORDINATION_MONITOR_POLL_RESULT_SCHEMA} {
+ return failure(reason_code, reason, "decision_rejection");
}
function monitorReceipt(input: CoordinationMonitorPollInput, hash: string) {
return new CoordinationCommandReceipt({result_schema: COORDINATION_MONITOR_POLL_RESULT_SCHEMA,
identity: {schema_version: RECEIPT_SCHEMA, goal_id: input.goal_id,
- operation_id: input.operation_id, request_sha256: hash}, failure,
+ operation_id: input.operation_id, request_sha256: hash},
+ failure: (code, reason) => failure(code, reason,
+ code === "coordination_operation_identity_mismatch"
+ ? "decision_rejection"
+ : "protocol_failure"),
decode(original, phase) {
const writeback = canonicalAuthorityObject(original.writeback, "Monitor writeback");
if (writeback.schema_version !== "monitor_poll_todo_writeback_v0" ||
@@ -70,6 +92,7 @@ function normalize(raw: CoordinationMonitorPollInput): NormalizedMonitorPollInpu
dry_run: requireBoolean(raw.dry_run, "dry_run"),
...normalizeMonitorPollFields(raw.observation, raw.intent),
gate_scope_guard: raw.gate_scope_guard == null ? false : requireBoolean(raw.gate_scope_guard, "gate_scope_guard"),
+ receipt_only: raw.receipt_only == null ? false : requireBoolean(raw.receipt_only, "receipt_only"),
lease_proof: decodeTaskLeaseProof(raw.lease_proof), now: raw.now ?? new Date()};
return input;
}
@@ -96,13 +119,17 @@ export async function executeCoordinationMonitorPoll(store: AuthorityStore,
authoritySourcesCurrent: AuthoritySourceCheck = uncheckedAuthoritySource): Promise {
let input: NormalizedMonitorPollInput;
try { input = normalize(raw); }
- catch (error) { return failure("invalid_monitor_poll_request", String(error)); }
+ catch (error) { return rejection("invalid_monitor_poll_request", String(error)); }
// Original wire identity, before any normalization/default route inference.
const hash = monitorPollRequestHash(input);
const receipt = monitorReceipt(input, hash);
const previous = await receipt.read(store);
if (previous) return previous;
- if (!await authoritySourcesCurrent()) return failure(AUTHORITY_SOURCE_CHANGED.code, AUTHORITY_SOURCE_CHANGED.reason);
+ if (input.receipt_only) return rejection("monitor_poll_original_receipt_required",
+ "Original Monitor receipt is unavailable; restore the historical source before retrying recovery");
+ if (!await authoritySourcesCurrent()) {
+ return rejection(AUTHORITY_SOURCE_CHANGED.code, AUTHORITY_SOURCE_CHANGED.reason);
+ }
const observation = await receipt.observe(store);
if (observation.kind === "receipt") return observation.result;
const head = observation.authority;
@@ -112,11 +139,13 @@ export async function executeCoordinationMonitorPoll(store: AuthorityStore,
catch (error) {
// Receipt lookup succeeded and planning failed before commit. Only this
// boundary can certify no effect; outages and commit failures cannot.
- return {...failure("monitor_poll_rejected", error instanceof Error ? error.message : String(error)),
+ return {...rejection("monitor_poll_rejected", error instanceof Error ? error.message : String(error)),
no_effect: {schema_version: "monitor_poll_no_effect_v0", goal_id: input.goal_id,
operation_id: input.operation_id, request_sha256: hash}};
}
- if (!await authoritySourcesCurrent()) return failure(AUTHORITY_SOURCE_CHANGED.code, AUTHORITY_SOURCE_CHANGED.reason);
+ if (!await authoritySourcesCurrent()) {
+ return rejection(AUTHORITY_SOURCE_CHANGED.code, AUTHORITY_SOURCE_CHANGED.reason);
+ }
if (input.dry_run) return {schema_version: COORDINATION_MONITOR_POLL_RESULT_SCHEMA,
status: "planned", changed: true, writeback: plan.writeback, provider_revision: head.provider_revision};
const commit = prepareCoordinationProjectionCommit({goal_id: input.goal_id, operation_id: input.operation_id,
diff --git a/loopx/control_plane/coordination/todo_terminal_lifecycle.ts b/loopx/control_plane/coordination/todo_terminal_lifecycle.ts
index 3380de5bd5..9a5656266e 100644
--- a/loopx/control_plane/coordination/todo_terminal_lifecycle.ts
+++ b/loopx/control_plane/coordination/todo_terminal_lifecycle.ts
@@ -1,12 +1,18 @@
+import {HANDOFF_MODE_RETIRED_REASON} from "./handoff_mode_policy.ts";
import {planUserCompletion, requireCompletionDecisionOutcome} from "../todos/user_completion.ts";
import {AUTHORITY_SOURCE_CHANGED, uncheckedAuthoritySource, type AuthoritySourceCheck} from "./authority_source.ts";
+import {
+ normalizeTodoUpdateInput,
+ prepareUpdatedTodo,
+ type NormalizedCoordinationTodoUpdateInput,
+ type TodoCompletionEdit,
+} from "./todo_update_intent.ts";
/** Service-owned evidence admission, evaluated against the actual provider
* head. Public requests cannot supply this callback or an approval boolean. */
export type TodoTerminalEvidenceGuard = (context: {
goal_id: string; todo: JsonObject; todos: JsonObject[]; actor_agent_id: string | null; command: string;
}) => Promise;
-import {normalizeTodoUpdateInput, prepareUpdatedTodo, type CoordinationTodoUpdateInput, type TodoCompletionEdit} from "./todo_update_intent.ts";
import {todoUpdateAdmissionRejection} from "./todo_update_admission.ts";
import { createHash } from "node:crypto";
import {acceptanceWorkGuard, acceptanceCompletionRequirements, validateAcceptanceCompletion,
@@ -50,7 +56,12 @@ import {
reduceTodoCompletionTransaction,
TODO_COMPLETION_TRANSACTION_REQUEST_SCHEMA,
} from "../todos/completion_transaction.ts";
+import {resolveTodoCompletionPolicy} from "../todos/completion_policy.ts";
import {evaluateTodoCompletionFence, TODO_COMPLETION_FENCE_REQUEST_SCHEMA} from "../todos/completion_fence.ts";
+import {
+ selectTodoCompletionState,
+ TODO_COMPLETION_STATE_REQUEST_SCHEMA,
+} from "../todos/completion_state.ts";
import {
TASK_LEASE_SCHEMA_VERSION,
leaseEpoch,
@@ -1017,13 +1028,146 @@ function terminalTarget(
return {todo: next, clear_fields: clearFields};
}
+interface UntypedCompletionRepairPlan {
+ readonly todo: JsonObject;
+ readonly successorTodoIds: readonly string[];
+}
+
+function validateUntypedCompletionRepairPolicy(
+ request: JsonObject,
+ input: ResolvedCoordinationTodoTerminalLifecycleInput,
+ todo: JsonObject,
+): void {
+ const allowedFields = new Set([
+ "schema_version",
+ "goal_id",
+ "agent_model",
+ "claimed_by",
+ "registered_agents",
+ "next_claimed_by",
+ "next_agent_todo",
+ "next_continuation_policy",
+ "next_excluded_agents",
+ "self_merged",
+ "evidence",
+ "linked_successors",
+ ]);
+ if (Object.keys(request).some((field) => !allowedFields.has(field))) {
+ throw new AuthorityStoreProtocolError(
+ "imported completion repair must not carry completion policy extensions",
+ );
+ }
+ const policy = resolveTodoCompletionPolicy(request);
+ if (request.goal_id !== input.goal_id ||
+ canonicalAuthoritySha256(policy.registered_agents) !==
+ canonicalAuthoritySha256(input.registered_agents) ||
+ // An explicit repeat of the stored claim is a source fact, not an edit.
+ (policy.effective_claimed_by !== null &&
+ policy.effective_claimed_by !== (todo.claimed_by ?? null)) ||
+ policy.effective_next_claimed_by !== null ||
+ policy.effective_next_excluded_agents.length > 0 ||
+ policy.self_merged ||
+ policy.linked_successor_id !== null ||
+ (request.next_agent_todo != null && request.next_agent_todo !== "") ||
+ (request.next_continuation_policy != null &&
+ request.next_continuation_policy !== "") ||
+ (Array.isArray(request.linked_successors) &&
+ request.linked_successors.length > 0) ||
+ (request.evidence != null && request.evidence !== "")) {
+ throw new AuthorityStoreProtocolError(
+ "imported completion repair must not carry completion policy edits",
+ );
+ }
+}
+
+function planUntypedCompletionRepair(
+ todo: JsonObject,
+ input: ResolvedCoordinationTodoTerminalLifecycleInput,
+ authority: CoordinationTodoTerminalDecisionResult,
+): UntypedCompletionRepairPlan | null {
+ if (input.command !== "complete" || input.requested_no_followup || todo.status !== "done") return null;
+ const 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: false,
+ requested_completion_turn_key: input.requested_completion_turn_key,
+ requested_completion_identity_source: input.requested_completion_identity_source,
+ });
+ if (fence.outcome !== "continue" || fence.reason !== "untyped_completion_repair") return null;
+
+ const storedTurnKey = typeof todo.completion_turn_key === "string"
+ ? todo.completion_turn_key : null;
+ if (input.requested_completion_turn_key !== storedTurnKey) {
+ throw new AuthorityStoreProtocolError(
+ "imported completion repair must preserve the stored completion_turn_key",
+ );
+ }
+ if (authority.outcome !== "no_change") {
+ throw new AuthorityStoreProtocolError(
+ "imported completion repair cannot apply a fresh authority transition",
+ );
+ }
+ if (input.user_update !== undefined || input.note !== null || input.evidence !== null ||
+ input.reason !== null || input.decision_outcome !== null || input.clear_claim ||
+ input.linked_successor_todo_ids.length > 0 ||
+ input.successor_intents.length > 0 || input.completion_result != null ||
+ input.validation_declaration !== null ||
+ input.validation_receipt !== null || input.validation_source_provider_revision != null ||
+ input.goal_acceptance_source_binding != null ||
+ input.goal_acceptance_validation_receipts != null) {
+ throw new AuthorityStoreProtocolError(
+ "imported completion repair must not carry fresh completion content, policy, validation, or ownership edits",
+ );
+ }
+ if (input.completion_policy_request !== null) {
+ validateUntypedCompletionRepairPolicy(
+ input.completion_policy_request,
+ input,
+ todo,
+ );
+ }
+ if (input.validation_declaration_sha256 != null &&
+ input.validation_declaration_sha256 !== todo.completion_validation_sha256) {
+ throw new AuthorityStoreProtocolError(
+ "imported completion repair must preserve the stored validation commitment",
+ );
+ }
+ const successorTodoIds = Array.isArray(todo.successor_todo_ids)
+ ? todo.successor_todo_ids.map((value, index) =>
+ requireAuthorityStoreId(value, `todo.successor_todo_ids[${index}]`))
+ : [];
+ const completionState = selectTodoCompletionState({
+ schema_version: TODO_COMPLETION_STATE_REQUEST_SCHEMA,
+ todo,
+ requested_no_followup: false,
+ has_successor: successorTodoIds.length > 0,
+ completion_identity_source: input.requested_completion_identity_source,
+ });
+ const target: JsonObject = {
+ ...todo,
+ completion_continuation: completionState.continuation,
+ ...(completionState.recovery === null
+ ? {}
+ : {completion_recovery: completionState.recovery}),
+ };
+ canonicalTodoRecord(target, "imported completion repair");
+ return {todo: target, successorTodoIds};
+}
+
function semanticDuplicate(
- todos: ReadonlyMap,
+ todos: Iterable,
successor: JsonObject,
+ retiringTodoId: string | null = null,
): JsonObject | undefined {
- return [...todos.values()].find((todo) =>
- todo.role === successor.role && todo.archive_state === "active" &&
- todo.status !== "done" && todo.status !== "deferred" && todo.text === successor.text);
+ for (const todo of todos) {
+ if (todo.todo_id !== retiringTodoId && todo.role === successor.role &&
+ todo.archive_state === "active" && todo.status !== "done" &&
+ todo.status !== "deferred" && todo.text === successor.text) return todo;
+ }
+ return undefined;
}
/**
@@ -1094,7 +1238,7 @@ export async function executeCoordinationTodoTerminalLifecycle(
}
head = observation.authority;
}
- let update: CoordinationTodoUpdateInput | undefined;
+ let update: NormalizedCoordinationTodoUpdateInput | undefined;
if (input.user_update !== undefined) {
try {
const edit = canonicalAuthorityObject(input.user_update, "User completion edit");
@@ -1215,7 +1359,8 @@ export async function executeCoordinationTodoTerminalLifecycle(
if (authority.outcome === "rejected" || authority.outcome === "conflict") {
return terminalFailure(
authority.code,
- `canonical Todo terminal lifecycle rejected the request: ${authority.code}`,
+ authority.code === "handoff_mode_retired" ? HANDOFF_MODE_RETIRED_REASON
+ : `canonical Todo terminal lifecycle rejected the request: ${authority.code}`,
{terminal_decision: authority},
"decision_rejection",
);
@@ -1248,6 +1393,36 @@ export async function executeCoordinationTodoTerminalLifecycle(
completed_at: target.completed_at ?? null,
}, closed === null ? [] : [{kind: "todo_upsert", todo: closed}], authoritySourcesCurrent);
}
+ let untypedCompletionRepair: UntypedCompletionRepairPlan | null;
+ try {
+ untypedCompletionRepair = planUntypedCompletionRepair(todo, input, authority);
+ } catch (error) {
+ return terminalFailure(
+ "invalid_todo_completion_transaction",
+ error instanceof Error ? error.message : "Invalid imported completion repair",
+ {},
+ "decision_rejection",
+ );
+ }
+ if (untypedCompletionRepair !== null) {
+ const target = untypedCompletionRepair.todo;
+ return commitTerminalResult(store, input, requestSha, head, {
+ todo_id: input.todo_id,
+ command: input.command,
+ changed: true,
+ terminal_decision: authority,
+ successor_todo_ids: untypedCompletionRepair.successorTodoIds,
+ generated_successor_todo_ids: [],
+ validation_receipt: null,
+ completion_policy: null,
+ completion_identity_key: target.completion_turn_key ?? null,
+ completion_identity_source: input.requested_completion_identity_source,
+ completion_continuation: target.completion_continuation,
+ completion_recovery: target.completion_recovery ?? null,
+ completion_receipt_id: target.completion_receipt_id ?? null,
+ completed_at: target.completed_at ?? null,
+ }, [{kind: "todo_upsert", todo: target}], authoritySourcesCurrent);
+ }
// Supersede retires waiting work through its existing terminal owner; it is
// not execution of the deferred Todo or proof that its prerequisite ran.
if (input.command === "complete" && authority.outcome === "apply") {
@@ -1478,6 +1653,14 @@ export async function executeCoordinationTodoTerminalLifecycle(
cursor: head.cursor,
};
}
+ if (completion.decision === "policy_reject") {
+ return terminalFailure(
+ "completion_policy_rejected",
+ completion.completion_policy_failure.summary,
+ {completion_policy_failure: completion.completion_policy_failure},
+ "decision_rejection",
+ );
+ }
if (completion.decision === "reject") {
return terminalFailure(
completion.failure.kind,
@@ -1493,6 +1676,9 @@ export async function executeCoordinationTodoTerminalLifecycle(
);
}
+ const terminalDecisionReceipt = authority.authority_mode === "delegated_orchestration_override"
+ ? {...authority, authority_reason: input.authority_reason} : authority;
+
if (authority.outcome === "no_change" && (edit === null || !edit.changed) && terminalUpgradeReceipt === null) {
const existingSuccessorIds = Array.isArray(todo.successor_todo_ids)
? todo.successor_todo_ids.map((value, index) =>
@@ -1511,7 +1697,7 @@ export async function executeCoordinationTodoTerminalLifecycle(
todo_id: input.todo_id,
command: input.command,
changed: false,
- terminal_decision: authority,
+ terminal_decision: terminalDecisionReceipt,
successor_todo_ids: existingSuccessorIds,
generated_successor_todo_ids: [],
validation_receipt: null,
@@ -1617,7 +1803,10 @@ export async function executeCoordinationTodoTerminalLifecycle(
todo_id: successorId,
});
}
- const duplicate = semanticDuplicate(projection.todos, successor);
+ // This CAS retires the source and creates the successors together. Only
+ // that source stops competing for its title. Peers still participate in
+ // the unchanged rule; successor admission already permits one per role.
+ const duplicate = semanticDuplicate(projection.todos.values(), successor, input.todo_id);
if (duplicate !== undefined) {
return terminalFailure(
"todo_semantic_duplicate_conflict",
@@ -1649,7 +1838,8 @@ export async function executeCoordinationTodoTerminalLifecycle(
? planUserCompletion(todo, [...projection.todos.values()], input.decision_outcome) : null;
const dependent = followthrough && Object.keys(followthrough.updates).length
? projection.todos.get(String(todo.unblocks_todo_id)) : undefined;
- const changed = authority.outcome === "apply" || edit?.changed === true || terminalUpgradeReceipt !== null;
+ const changed = authority.outcome === "apply" || edit?.changed === true ||
+ terminalUpgradeReceipt !== null;
const result: JsonObject = {
todo_id: input.todo_id,
command: input.command,
@@ -1657,7 +1847,7 @@ export async function executeCoordinationTodoTerminalLifecycle(
...(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} : {}),
- terminal_decision: authority,
+ terminal_decision: terminalDecisionReceipt,
successor_todo_ids: successorIds,
generated_successor_todo_ids: generatedSuccessorIds,
generated_successors: successorCandidates,
diff --git a/loopx/control_plane/coordination/todo_update.ts b/loopx/control_plane/coordination/todo_update.ts
index 1ff28f40a7..e988d37b71 100644
--- a/loopx/control_plane/coordination/todo_update.ts
+++ b/loopx/control_plane/coordination/todo_update.ts
@@ -1,5 +1,6 @@
import {AUTHORITY_SOURCE_CHANGED, uncheckedAuthoritySource, type AuthoritySourceCheck} from "./authority_source.ts";
import type { JsonObject } from "../effect_program.ts";
+import {EffectRuntimeRequestError} from "../effect_runtime_errors.ts";
import {acceptanceWorkGuard} from "../goals/acceptance_contract.ts";
import type { AuthorityStore, AuthorityStoreCommit } from "./authority_store.ts";
import {
@@ -41,7 +42,13 @@ export const COORDINATION_TODO_OBSERVATION_UPDATE_REQUEST_SCHEMA =
export const COORDINATION_TODO_VALIDATION_REVISION_REQUEST_SCHEMA =
"loopx_local_coordination_todo_update_request_v5";
export type {CoordinationTodoUpdateInput} from "./todo_update_intent.ts";
-import {normalizeTodoUpdateInput, prepareUpdatedTodo, type CoordinationTodoUpdateInput} from "./todo_update_intent.ts";
+import {
+ normalizeTodoUpdateEnvelope,
+ normalizeTodoUpdateInput,
+ prepareUpdatedTodo,
+ type CoordinationTodoUpdateInput,
+ type NormalizedCoordinationTodoUpdateInput,
+} from "./todo_update_intent.ts";
import {executeCoordinationTodoTerminalLifecycle} from "./todo_terminal_lifecycle.ts";
import {planCompletionValidationRevision} from "../todos/completion_validation_revision.ts";
@@ -49,9 +56,19 @@ export type CoordinationTodoUpdateResult = JsonObject & {
readonly schema_version: typeof COORDINATION_TODO_UPDATE_RESULT_SCHEMA;
};
-function failure(code: string, reason: string): CoordinationTodoUpdateResult {
+type CoordinationTodoUpdateFailureKind = "decision_rejection" | "protocol_failure";
+
+function failure(
+ code: string,
+ reason: string,
+ kind: CoordinationTodoUpdateFailureKind = "protocol_failure",
+): CoordinationTodoUpdateResult {
return {schema_version: COORDINATION_TODO_UPDATE_RESULT_SCHEMA, status: "failed",
- changed: false, reason_code: code, reason};
+ changed: false, failure_kind: kind, reason_code: code, reason};
+}
+
+function rejection(code: string, reason: string): CoordinationTodoUpdateResult {
+ return failure(code, reason, "decision_rejection");
}
function isFailure(value: JsonObject): value is CoordinationTodoUpdateResult {
@@ -63,7 +80,11 @@ function updateReceipt(input: CoordinationTodoUpdateInput, requestSha: string) {
return new CoordinationCommandReceipt({result_schema: COORDINATION_TODO_UPDATE_RESULT_SCHEMA,
identity: {schema_version: COORDINATION_TODO_UPDATE_RECEIPT_SCHEMA,
operation_id: input.operation_id, goal_id: input.goal_id, todo_id: input.todo_id,
- request_sha256: requestSha}, failure,
+ request_sha256: requestSha},
+ failure: (code, reason) => failure(code, reason,
+ code === "coordination_operation_identity_mismatch"
+ ? "decision_rejection"
+ : "protocol_failure"),
decode(original) {
if (typeof original.changed !== "boolean") throw new AuthorityStoreProtocolError("update receipt changed must be boolean");
return {fields: {todo_id: input.todo_id, original_receipt: original,
@@ -110,7 +131,7 @@ function loadUpdateTarget(
const projection = indexCoordinationProjection(head, input.goal_id);
const found = projection.todos.get(input.todo_id);
return found === undefined
- ? failure("todo_not_found", "canonical Todo is missing")
+ ? rejection("todo_not_found", "canonical Todo is missing")
: {todo: found, leases: projection.leases};
} catch (error) {
return failure("invalid_coordination_projection",
@@ -124,8 +145,8 @@ export async function executeCoordinationTodoUpdate(
authoritySourcesCurrent: AuthoritySourceCheck = uncheckedAuthoritySource,
): Promise {
let input: CoordinationTodoUpdateInput;
- try { input = normalizeTodoUpdateInput(rawInput); } catch (error) {
- return failure("invalid_coordination_todo_update",
+ try { input = normalizeTodoUpdateEnvelope(rawInput); } catch (error) {
+ return rejection("invalid_coordination_todo_update",
error instanceof Error ? error.message : "invalid Todo update");
}
if (input.completion !== undefined) {
@@ -161,18 +182,21 @@ export async function executeCoordinationTodoUpdate(
const receipt = updateReceipt(input, requestSha);
const replay = await receipt.read(store);
if (replay !== null) return replay;
- const sourceChanged = () => failure(AUTHORITY_SOURCE_CHANGED.code, AUTHORITY_SOURCE_CHANGED.reason);
+ const sourceChanged = () => rejection(
+ AUTHORITY_SOURCE_CHANGED.code,
+ AUTHORITY_SOURCE_CHANGED.reason,
+ );
if (!await authoritySourcesCurrent()) return sourceChanged();
// Legacy single-agent callers historically omitted actor_agent_id for an
// unowned Todo. Keep that narrow compatibility path, while retaining the
// registered-actor requirement for multi-agent or explicitly-owned work.
if (input.actor_agent_id === null) {
if (input.registered_agents.length > 1) {
- return failure("actor_not_registered", "Todo update requires a registered actor");
+ return rejection("actor_not_registered", "Todo update requires a registered actor");
}
} else if (input.registered_agents.length === 0 ||
!input.registered_agents.includes(input.actor_agent_id)) {
- return failure("actor_not_registered", "Todo update requires a registered actor");
+ return rejection("actor_not_registered", "Todo update requires a registered actor");
}
const observation = await receipt.observe(store);
if (observation.kind === "receipt") return observation.result;
@@ -182,18 +206,25 @@ export async function executeCoordinationTodoUpdate(
}
if (input.expected_provider_revision !== undefined &&
input.expected_provider_revision !== head.provider_revision) {
- return failure("provider_revision_mismatch", "Current revision changed; inspect again before continuing");
+ return rejection("provider_revision_mismatch", "Current revision changed; inspect again before continuing");
}
const target = loadUpdateTarget(head.head, input);
if (isFailure(target)) return target;
const rejected = todoUpdateAdmissionRejection(head.head, target.todo, target.leases, input);
- if (rejected !== null) return {...failure(rejected.code, rejected.reason),
+ if (rejected !== null) return {...rejection(rejected.code, rejected.reason),
...(rejected.handoff_mode === undefined ? {} : {handoff_mode: rejected.handoff_mode}),
...(rejected.recovery === undefined ? {} : {recovery: rejected.recovery})};
+ let normalizedInput: NormalizedCoordinationTodoUpdateInput;
let prepared: ReturnType;
- try { prepared = prepareUpdatedTodo(target.todo, input, head.head); }
- catch (error) { return failure("invalid_coordination_todo_update",
+ try {
+ normalizedInput = normalizeTodoUpdateInput(input);
+ prepared = prepareUpdatedTodo(target.todo, normalizedInput, head.head);
+ }
+ catch (error) { return rejection(
+ error instanceof EffectRuntimeRequestError
+ ? error.code
+ : "invalid_coordination_todo_update",
error instanceof Error ? error.message : "invalid updated Todo"); }
let {next, changed, clearFields} = prepared;
let completionValidationRevisionReceipt: JsonObject | null = null;
@@ -219,7 +250,7 @@ export async function executeCoordinationTodoUpdate(
);
canonicalTodoRecord(next, "updated Todo");
} catch (error) {
- return failure(
+ return rejection(
"invalid_completion_validation_revision",
error instanceof Error
? error.message
@@ -234,19 +265,28 @@ export async function executeCoordinationTodoUpdate(
cycle = planMonitorCycleTransition({goal_id: input.goal_id, before: target.todo, after: next,
lease: target.leases.get(input.todo_id), handoff_mode: head.head.handoff_mode, now: input.now});
if ((head.head.handoff_mode === "hard_lease" || target.leases.has(input.todo_id)) &&
- (isDeferredReopen(input, target.todo) || isOwnerDeferral(input, target.todo))) {
+ (isDeferredReopen(normalizedInput, target.todo) || isOwnerDeferral(normalizedInput, target.todo))) {
deferredCycle = planDeferredLifecycle({goal_id: input.goal_id, before: target.todo, after: next,
lease: target.leases.get(input.todo_id), now: input.now});
}
- if (head.head.handoff_mode === "hard_lease" && isBlockedLifecycleTransition(input, target.todo)) {
+ if (head.head.handoff_mode === "hard_lease" &&
+ isBlockedLifecycleTransition(normalizedInput, target.todo)) {
blockedCycle = planBlockedLifecycleTransition({goal_id: input.goal_id, before: target.todo, after: next,
lease: target.leases.get(input.todo_id), now: input.now});
}
} catch (error) {
- return failure("invalid_coordination_projection", error instanceof Error ? error.message : "invalid retained lease");
+ return failure(
+ "invalid_coordination_projection",
+ error instanceof Error ? error.message : "invalid retained lease",
+ error instanceof EffectRuntimeRequestError
+ ? "decision_rejection"
+ : "protocol_failure",
+ );
}
const deferredTransition = deferredCycle?.transition == null ? {} : {
- [isOwnerDeferral(input, target.todo) ? "deferred_transition" : "deferred_resume_transition"]: deferredCycle.transition,
+ [isOwnerDeferral(normalizedInput, target.todo)
+ ? "deferred_transition"
+ : "deferred_resume_transition"]: deferredCycle.transition,
};
const commit: AuthorityStoreCommit = changed ? prepareCoordinationProjectionCommit({
goal_id: input.goal_id, operation_id: input.operation_id,
@@ -261,7 +301,7 @@ export async function executeCoordinationTodoUpdate(
if (input.planning_intent?.claimed_by != null) {
const acceptance = acceptanceWorkGuard(commit.next_projection, input.goal_id, input.todo_id);
if (acceptance !== null && !acceptance.allowed) {
- return {...failure(String(acceptance.reason_code), `${String(acceptance.reason)} Inspect Goal acceptance and ask the owner to configure or rebind this Todo.`),
+ return {...rejection(String(acceptance.reason_code), `${String(acceptance.reason)} Inspect Goal acceptance and ask the owner to configure or rebind this Todo.`),
goal_acceptance_guard: acceptance};
}
}
diff --git a/loopx/control_plane/coordination/todo_update_admission.ts b/loopx/control_plane/coordination/todo_update_admission.ts
index 4f2b41a056..a133a4881d 100644
--- a/loopx/control_plane/coordination/todo_update_admission.ts
+++ b/loopx/control_plane/coordination/todo_update_admission.ts
@@ -1,6 +1,7 @@
/** Admission for Todo edits; terminal completion retains its own lease proof.
* Grants may cross a claim owner;
* exclusions, bindings and execution lineage remain independent restrictions. */
+import {retiredHandoffModeRejection, HANDOFF_MODE_RETIRED_REASON} from "./handoff_mode_policy.ts";
import {leaseRepositoryRejection} from "../work_items/task_lease_repository.ts";
import {isExploreReferenceAppend} from "../todos/field_update.ts";
import {acceptanceRestoration} from "./todo_acceptance_restoration.ts";
@@ -15,6 +16,7 @@ import {decodeTaskLeaseProof, evaluateCanonicalTaskLeaseProof, todoUpdateLeaseRe
import {deferredReopenRejection, isDeferredReopen, isOwnerDeferral} from "./todo_deferred_lifecycle.ts";
import {blockedLifecycleRejection, isBlockedLifecycleTransition} from "./todo_blocked_lifecycle.ts";
import {indexCoordinationProjection} from "./coordination_projection.ts";
+import {canonicalTaskLease} from "./task_lease_state.ts";
interface TodoUpdateRejection {code: string; reason: string; handoff_mode?: string; recovery?: JsonObject}
const reject = (code: string, reason: string): TodoUpdateRejection => ({code, reason});
@@ -49,6 +51,8 @@ export function todoUpdateAdmissionRejection(
if (typeof mode !== "string" || !["legacy", "soft_claim", "hard_lease"].includes(mode)) {
return reject("invalid_handoff_mode", "canonical handoff mode is invalid");
}
+ const retired = retiredHandoffModeRejection(mode);
+ if (retired !== null) return {...reject(retired, HANDOFF_MODE_RETIRED_REASON), handoff_mode: mode};
const intent = input.planning_intent ?? {};
const ownershipMutation = ["claimed_by", "clear_claim", "excluded_agents", "bound_agent",
"goal_bound", "blocks_agent", "clear_blocks_agent", "global_gate", "clear_global_gate"]
@@ -155,12 +159,18 @@ export function todoUpdateAdmissionRejection(
const repositoryRejection = leaseRepositoryRejection(todo, lease);
return repositoryRejection === null ? null : reject(repositoryRejection, "Evidence association must retain the completed work repository");
}
- // User reminders cannot take agent claims. No-lineage copy edits retain the
- // existing provider-CAS path. Retained lineage or even a partial explicit
- // proof must pass the ordinary execution fence below before any copy edit.
+ // User reminders use an administrative provider-CAS edit. Retained released
+ // history is not an execution grant; active/expired history needs exact
+ // release first. Explicit or partial proof never grants this exception.
const boundUserActionMetadata = isBoundUserActionMetadataUpdate(todo, input);
- if (mode === "hard_lease" && boundUserActionMetadata && lease === undefined &&
+ if (boundUserActionMetadata && (lease === undefined || lease.status === "released") &&
input.lease_idempotency_key == null && input.lease_expected_version == null) {
+ try {
+ if (lease !== undefined) canonicalTaskLease(lease, input.goal_id, input.todo_id);
+ } catch (error) {
+ return reject("invalid_coordination_projection",
+ error instanceof Error ? error.message : "invalid retained lease facts");
+ }
return null;
}
// Policy migration retains released execution history. It must not force a
@@ -188,7 +198,9 @@ export function todoUpdateAdmissionRejection(
if (restoration?.kind === "unavailable") {
return reject("goal_acceptance_restoration_unavailable", restoration.reason);
}
- return {...reject(String(fence.code), "Todo update requires the current active lease execution proof"),
+ return {...reject(String(fence.code), fence.code === "todo_not_executable"
+ ? "User actions cannot consume execution proof; release retained execution before a bound administrative copy edit"
+ : "Todo update requires the current active lease execution proof"),
handoff_mode: mode, recovery: todoUpdateLeaseRecovery(head, input, mode)};
}
if (lease !== undefined && todo.claimed_by !== input.actor_agent_id && !boundUserActionMetadata) {
diff --git a/loopx/control_plane/coordination/todo_update_intent.ts b/loopx/control_plane/coordination/todo_update_intent.ts
index 8407462ba8..7774c75c44 100644
--- a/loopx/control_plane/coordination/todo_update_intent.ts
+++ b/loopx/control_plane/coordination/todo_update_intent.ts
@@ -11,6 +11,7 @@ import {planTodoAuthoringScope, TODO_AUTHORING_SCOPE_REQUEST_SCHEMA} from "../to
import {shouldRecordTodoMutationActor} from "../todos/public_update.ts";
import {normalizeNativePlanningIntent, planNativeTodoUpdate} from "../todos/native_update_plan.ts";
import {decodeMonitorPollObservation, type MonitorPollObservation} from "../todos/monitor_metadata.ts";
+import {validateLegacyContinuationPolicyRepair, removedTodoContinuationPolicy} from "../todos/legacy_continuation_policy_migration.ts";
import {
decodeCompletionValidationRevision,
type CompletionValidationRevision,
@@ -37,10 +38,15 @@ export interface CoordinationTodoUpdateInput {
readonly lease_expected_version?: number | null;
readonly planning_intent?: JsonObject;
readonly completion?: JsonObject;
- readonly monitor_observation?: MonitorPollObservation;
+ readonly monitor_observation?: JsonObject;
readonly completion_validation_revision?: CompletionValidationRevision;
}
+export type NormalizedCoordinationTodoUpdateInput =
+ Omit & {
+ readonly monitor_observation?: MonitorPollObservation;
+ };
+
/** Only edit intent crosses into the terminal owner; actor/lease authority is
* carried once, in the enclosing terminal request. */
export type TodoCompletionEdit = Pick
requireAuthorityStoreId(field, `clear_fields[${index}]`));
- const observation = raw.monitor_observation === undefined ? undefined : decodeMonitorPollObservation(raw.monitor_observation);
+ const observation = raw.monitor_observation === undefined
+ ? undefined
+ : canonicalAuthorityObject(raw.monitor_observation, "Monitor observation");
const validationRevision = raw.completion_validation_revision === undefined
? undefined
: decodeCompletionValidationRevision(raw.completion_validation_revision);
@@ -149,9 +159,33 @@ export function normalizeTodoUpdateInput(raw: CoordinationTodoUpdateInput): Coor
patch, clear_fields: clearFields};
}
+export function normalizeTodoUpdateInput(
+ raw: CoordinationTodoUpdateInput,
+): NormalizedCoordinationTodoUpdateInput {
+ const envelope = normalizeTodoUpdateEnvelope(raw);
+ const observation = envelope.monitor_observation === undefined
+ ? undefined
+ : decodeMonitorPollObservation(envelope.monitor_observation);
+ const {monitor_observation: _rawObservation, ...normalized} = envelope;
+ return {
+ ...normalized,
+ ...(observation === undefined ? {} : {monitor_observation: observation}),
+ };
+}
+
export function prepareUpdatedTodo(
- todo: JsonObject, input: CoordinationTodoUpdateInput, head: JsonObject, kind: "planning" | "user_completion" = "planning",
+ todo: JsonObject, input: NormalizedCoordinationTodoUpdateInput, head: JsonObject, kind: "planning" | "user_completion" = "planning",
): {next: JsonObject; changed: boolean; clearFields: string[]; monitorTransition?: JsonObject} {
+ // Copy-only edits must honor the same imported-policy repair as field plans.
+ // Receipt replay is handled before materialization by the transaction owner.
+ validateLegacyContinuationPolicyRepair(todo, input.planning_intent ?? {}, input.todo_id);
+ const removedContinuationPolicy = removedTodoContinuationPolicy(todo.removed_continuation_policy);
+ if (removedContinuationPolicy !== null &&
+ (Object.keys(input.patch).length > 0 || input.clear_fields.length > 0)) {
+ throw new AuthorityStoreProtocolError(
+ "removed continuation policy repair must be submitted without copy edits",
+ );
+ }
const next: JsonObject = {...todo, ...input.patch};
for (const field of input.clear_fields) delete next[field];
// Preserve the public planner's legacy metadata semantics. Raw copy edits
@@ -163,6 +197,10 @@ export function prepareUpdatedTodo(
}
next.updated_at = input.now.toISOString().replace(/\.\d{3}Z$/u, "Z");
const clearFields = new Set(input.clear_fields);
+ if (removedContinuationPolicy !== null) {
+ delete next.removed_continuation_policy;
+ clearFields.add("removed_continuation_policy");
+ }
let monitorTransition: JsonObject | undefined;
if (Object.keys(input.planning_intent ?? {}).length || input.monitor_observation !== undefined) {
const plan = planNativeTodoUpdate(todo, input.planning_intent ?? {}, head,
diff --git a/loopx/control_plane/effect_runtime_handlers.ts b/loopx/control_plane/effect_runtime_handlers.ts
index 7e0b25b5d9..b883eb7f65 100644
--- a/loopx/control_plane/effect_runtime_handlers.ts
+++ b/loopx/control_plane/effect_runtime_handlers.ts
@@ -431,7 +431,6 @@ export function createEffectRuntimeHandlers(
["work_items.team_plan.commit", lazyHandler(() => import("./work_items/team_plan_authority.ts"), ({commitLocalTeamPlan}) => commitLocalTeamPlan)],
["coordination.local_authority.todo_update", lazyHandler(() => import("./coordination/local_authority_runtime.ts"), ({updateLocalCoordinationTodo}) => updateLocalCoordinationTodo)],
["coordination.local_authority.monitor_poll", lazyHandler(() => import("./coordination/local_authority_runtime.ts"), ({pollLocalCoordinationMonitor}) => pollLocalCoordinationMonitor)],
- ["coordination.handoff_mode.legacy_plan", lazyHandler(() => import("./coordination/handoff_mode_legacy_plan.ts"), ({planLegacyHandoffMode}) => planLegacyHandoffMode)],
["coordination.local_authority.handoff_mode_set", lazyHandler(() => import("./coordination/handoff_mode_runtime.ts"), ({setLocalHandoffMode}) => setLocalHandoffMode)],
["coordination.local_authority.handoff_mode_migrate", lazyHandler(() => import("./coordination/handoff_mode_runtime.ts"), ({migrateLocalHandoffMode}) => migrateLocalHandoffMode)],
["coordination.local_authority.todo_terminal", lazyHandler(() => import("./coordination/local_authority_runtime.ts"), ({terminalLifecycleLocalCoordinationTodo}) => terminalLifecycleLocalCoordinationTodo)],
@@ -470,7 +469,7 @@ export function createEffectRuntimeHandlers(
["operation.agent_handoff.plan", lazyHandler(() => import("./work_items/operation_agent_handoff.ts"), ({planAgentOperationHandoff}) => planAgentOperationHandoff)],
["operation.agent_handoff.inbox", lazyHandler(() => import("./work_items/operation_agent_handoff.ts"), ({projectAgentOperationInbox}) => projectAgentOperationInbox)],
["scheduler.monitor_successor.plan", lazyHandler(() => import("./scheduler/monitor_successor.ts"), ({planMonitorSuccessor}) => planMonitorSuccessor)],
- ["scheduler.monitor_batch.plan", lazyHandler(() => import("./scheduler/monitor_batch.ts"), ({planLegacyMonitorBatch}) => planLegacyMonitorBatch)],
+ ["scheduler.monitor_batch.replay", lazyHandler(() => import("./scheduler/monitor_batch.ts"), ({replayLegacyMonitorBatch}) => replayLegacyMonitorBatch)],
["scheduler.monitor_target.select", lazyHandler(() => import("./scheduler/monitor_successor.ts"), ({selectMonitorTodoRequest}) => selectMonitorTodoRequest)],
["capabilities.issue_fix.monitor_reconciliation.plan", lazyHandler(() => import("./capabilities/issue_fix_monitor_reconciliation.ts"), ({planIssueFixMonitorReconciliation}) => planIssueFixMonitorReconciliation)],
["capabilities.change_quality.validation_gate", lazyHandler(() => import("./capabilities/change_quality_validation.ts"), ({qualifyChangeQualityValidation}) => qualifyChangeQualityValidation)],
diff --git a/loopx/control_plane/goals/goal_frontier/__init__.py b/loopx/control_plane/goals/goal_frontier/__init__.py
index ead42f1f24..ea8943eea8 100644
--- a/loopx/control_plane/goals/goal_frontier/__init__.py
+++ b/loopx/control_plane/goals/goal_frontier/__init__.py
@@ -47,9 +47,7 @@
completed_todo_replan_threshold,
goal_vision_repeats_advancement_until_closed,
)
-from ..goal_vision_state import (
- goal_vision_state_is_closed,
-)
+from ..goal_vision_state import goal_vision_state_is_closed
from ..goal_vision_read_model import (
VISION_ACCEPTANCE_GAP_TRIGGER as VISION_ACCEPTANCE_GAP_TRIGGER,
VISION_SUCCESSOR_GAP_TRIGGER as VISION_SUCCESSOR_GAP_TRIGGER,
@@ -77,20 +75,15 @@
declared_fallback_gap_from_agent_vision,
parse_fallback_declarations, # noqa: F401
)
-from .long_todo_chain import (
- LONG_TODO_CHAIN_TRIGGER,
- evaluate_long_todo_chain,
-)
+from .long_todo_chain import LONG_TODO_CHAIN_TRIGGER, evaluate_long_todo_chain
from .replan_rules import (
GoalFrontierReplanFacts,
GoalFrontierReplanRule,
select_goal_frontier_replan_rule,
)
-from .semantic_history import (
- latest_agent_vision_from_runs as latest_agent_vision_from_runs,
-)
from .semantic_history import (
_latest_runs_for_goal,
+ latest_agent_vision_from_runs as latest_agent_vision_from_runs,
latest_agent_vision_from_status_payload,
latest_autonomous_replan_ack_from_status_payload,
latest_missing_vision_checkpoint_from_status_payload,
diff --git a/loopx/control_plane/projects/registry.py b/loopx/control_plane/projects/registry.py
index 7dcf4eac04..642ed8a4cf 100644
--- a/loopx/control_plane/projects/registry.py
+++ b/loopx/control_plane/projects/registry.py
@@ -1,9 +1,12 @@
from __future__ import annotations
+import hashlib
import re
+from collections.abc import Callable
from pathlib import Path
from typing import Any
+from ...file_lock import exclusive_cross_runtime_file_lock
from ...control_plane.runtime.time import now_local_iso
from ...paths import (
registered_goal_state_file,
@@ -12,7 +15,11 @@
select_default_runtime_root,
)
from ..runtime.document_io import atomic_write_state_text as _atomic_write_text
-from ..coordination.legacy_writer_fence import legacy_todo_write_transaction, require_legacy_state_replacement_allowed
+from ..coordination.local_authority import (
+ read_canonical_todos_if_promoted,
+ require_canonical_authority,
+)
+from ..coordination.goal_storage_initialization import initialize_goal_storage_target
from ..goals.source_session_services import (
FreshSourceSessionRegistration,
RecreateGoalRequest,
@@ -134,7 +141,77 @@ def _resolve_exact_project_binding(
}
-def register_project_goal(
+def _bind_project_goal_storage(
+ *,
+ registry: dict[str, Any],
+ registry_path: Path,
+ runtime_root: Path | None,
+ project_id: str,
+ goal_id: str,
+ goal_record: dict[str, Any],
+ resolve_new_goal_storage_target: Callable[[Path], dict[str, Any]],
+) -> Path:
+ effective_root = resolve_runtime_root(
+ registry,
+ str(runtime_root) if runtime_root else None,
+ registry_path=registry_path,
+ )
+ existing_goal = next(
+ (
+ item
+ for item in registry.get("goals", [])
+ if isinstance(item, dict) and item.get("id") == goal_id
+ ),
+ None,
+ )
+ existing_target = (
+ (existing_goal.get("coordination") or {}).get("storage_target")
+ if existing_goal is not None
+ else None
+ )
+ resumable_creation = (
+ isinstance(existing_target, dict)
+ and existing_target.get("schema_version")
+ == "loopx_new_goal_storage_target_v1"
+ and isinstance(existing_goal.get("creation_operation_id"), str)
+ and existing_goal["creation_operation_id"] != ""
+ )
+ existing_authority = (
+ read_canonical_todos_if_promoted(
+ runtime_root=effective_root,
+ goal_id=goal_id,
+ )
+ if existing_goal is None or not resumable_creation
+ else None
+ )
+ if existing_goal is None and existing_authority is not None:
+ raise ValueError(
+ f"goal_id is already bound to canonical authority through another registry: {goal_id}"
+ )
+ if existing_goal is not None and existing_authority is None and not resumable_creation:
+ require_canonical_authority(goal_id=goal_id, operation="goal_create")
+ storage_target = (
+ existing_target
+ if existing_goal is not None
+ else resolve_new_goal_storage_target(effective_root)
+ )
+ if (
+ existing_goal is None
+ and (storage_target or {}).get("schema_version")
+ != "loopx_new_goal_storage_target_v1"
+ ):
+ require_canonical_authority(goal_id=goal_id, operation="goal_create")
+ if storage_target is not None:
+ goal_record.setdefault("coordination", {})["storage_target"] = storage_target
+ goal_record["creation_operation_id"] = (
+ (existing_goal or {}).get("creation_operation_id")
+ or "goal-create:project-register:"
+ + hashlib.sha256(f"{project_id}\0{goal_id}".encode()).hexdigest()[:24]
+ )
+ return effective_root
+
+
+def _register_project_goal_unlocked(
*,
registry_path: Path,
runtime_root: Path | None,
@@ -150,6 +227,7 @@ def register_project_goal(
stop_condition: str,
repository_bindings: list[str],
external_locator_bindings: list[str],
+ resolve_new_goal_storage_target: Callable[[Path], dict[str, Any]],
goal_instance_profile: str | None = None,
operation_id: str | None = None,
registered_agents: list[str] | None = None,
@@ -260,6 +338,22 @@ def register_project_goal(
if operation_id is not None:
raise ValueError("operation_id requires goal_instance_profile")
+ initial_registry = existing_registry or {
+ "schema_version": "0.1",
+ "registry_role": "project-local",
+ "common_runtime_root": str(runtime_root or select_default_runtime_root()),
+ }
+ effective_root = _bind_project_goal_storage(
+ registry=initial_registry,
+ registry_path=registry_path,
+ runtime_root=runtime_root,
+ project_id=project_id,
+ goal_id=goal_id,
+ goal_record=goal_record,
+ resolve_new_goal_storage_target=resolve_new_goal_storage_target,
+ )
+
+ changed = True
with project_registry_transaction(
registry_path,
operation="project_register",
@@ -298,55 +392,11 @@ def register_project_goal(
f"project_id already has its first registered goal: {project_id}"
)
effective_root = resolve_runtime_root(registry, str(runtime_root) if runtime_root else None, registry_path=registry_path)
- with legacy_todo_write_transaction(registry_path, goal_id, state_file, None, "project_register_state", False, runtime_root=effective_root):
- if not state_file.exists():
- require_legacy_state_replacement_allowed(runtime_root=effective_root, goal_id=goal_id, goal=existing_goal)
- if state_file.exists():
- existing_state = state_file.read_text(encoding="utf-8")
- existing_updated_at = registration_state_updated_at(existing_state)
- matching_state = (
- render_registration_state(
- project_id=project_id,
- goal_id=goal_id,
- objective=objective,
- non_goals=non_goals,
- acceptance=acceptance,
- unknowns=unknowns,
- next_effect=next_effect,
- stop_condition=stop_condition,
- updated_at=existing_updated_at,
- )
- if existing_updated_at is not None
- else None
- )
- if matching_state is None or not registration_state_matches(
- existing_state, matching_state, objective=objective,
- ):
- raise ValueError(
- f"goal state file conflicts with registration: {state_file}"
- )
- if existing_project is not None and existing_goal is not None:
- if not state_file.exists():
- _atomic_write_text(state_file, state_text)
- return {
- "ok": True,
- "schema_version": "loopx_project_registration_v0",
- "changed": True,
- "registry": str(registry_path),
- "project": existing_project,
- "goal": existing_goal,
- "state_file": str(state_file),
- }
- return {
- "ok": True,
- "schema_version": "loopx_project_registration_v0",
- "changed": False,
- "registry": str(registry_path),
- "project": existing_project,
- "goal": existing_goal,
- "state_file": str(state_file),
- }
-
+ if existing_project is not None and existing_goal is not None:
+ # A completed canonical operation can outlive its initial display.
+ # Defer source validation to the typed creation owner's request.
+ changed = False
+ else:
state_file.parent.mkdir(parents=True, exist_ok=True)
state_created = False
try:
@@ -361,18 +411,131 @@ def register_project_goal(
if state_created:
state_file.unlink(missing_ok=True)
raise
+ current_registry = load_project_registry(registry_path)
+ registered_goal = next(
+ item
+ for item in current_registry.get("goals", [])
+ if isinstance(item, dict) and item.get("id") == goal_id
+ )
+
+ def validate_registration_source(existing_state: str) -> None:
+ existing_updated_at = registration_state_updated_at(existing_state)
+ matching_state = (
+ render_registration_state(
+ project_id=project_id, goal_id=goal_id, objective=objective,
+ non_goals=non_goals, acceptance=acceptance, unknowns=unknowns,
+ next_effect=next_effect, stop_condition=stop_condition,
+ updated_at=existing_updated_at,
+ ) if existing_updated_at is not None else None
+ )
+ if matching_state is None or not registration_state_matches(
+ existing_state, matching_state, objective=objective,
+ ):
+ raise ValueError(f"goal state file conflicts with registration: {state_file}")
+
+ storage_selection = initialize_goal_storage_target(
+ effective_root,
+ registered_goal,
+ registry_path=registry_path,
+ validate_source=validate_registration_source,
+ )
+ if storage_selection is None or storage_selection.get("authority_initialized") is not True:
+ raise RuntimeError("canonical project Goal initialization did not complete")
+
+ state_projection = None
+ if not state_file.exists():
+ from ..todos.provider_projection import project_current_canonical_todos
+
+ state_projection = project_current_canonical_todos(
+ registry_path=registry_path, runtime_root=effective_root,
+ goal_id=goal_id, project=knowledge_root, state_file=state_file,
+ )
+ changed = changed or state_projection["changed"]
return {
"ok": True,
"schema_version": "loopx_project_registration_v0",
- "changed": True,
+ "changed": changed,
"registry": str(registry_path),
"project": project_record,
"goal": goal_record,
"state_file": str(state_file),
+ "storage_selection": storage_selection,
+ **({"state_projection": state_projection} if state_projection is not None else {}),
}
+def register_project_goal(
+ *,
+ registry_path: Path,
+ runtime_root: Path | None,
+ project_id: str,
+ project_kind: str,
+ knowledge_root: Path,
+ goal_id: str,
+ objective: str,
+ non_goals: list[str],
+ acceptance: list[str],
+ unknowns: list[str],
+ next_effect: str,
+ stop_condition: str,
+ repository_bindings: list[str],
+ external_locator_bindings: list[str],
+ resolve_new_goal_storage_target: Callable[[Path], dict[str, Any]],
+ goal_instance_profile: str | None = None,
+ operation_id: str | None = None,
+ registered_agents: list[str] | None = None,
+ canonical_storage: str | None = None,
+) -> dict[str, Any]:
+ normalized_goal_id = _identifier(goal_id, field="goal_id")
+ expanded_registry = registry_path.expanduser()
+ existing_registry = (
+ load_project_registry(expanded_registry) if expanded_registry.exists() else None
+ )
+ registry = existing_registry or {
+ "schema_version": "0.1",
+ "registry_role": "project-local",
+ "common_runtime_root": str(runtime_root or select_default_runtime_root()),
+ }
+ effective_root = resolve_runtime_root(
+ registry,
+ str(runtime_root) if runtime_root else None,
+ registry_path=expanded_registry,
+ )
+ digest = hashlib.sha256(normalized_goal_id.encode("utf-8")).hexdigest()[:16]
+ lock_target = (
+ effective_root
+ / "authority-transition"
+ / "project-registration"
+ / f"goal-{digest}"
+ )
+ with exclusive_cross_runtime_file_lock(
+ lock_target,
+ operation="project_register",
+ ):
+ return _register_project_goal_unlocked(
+ registry_path=expanded_registry,
+ runtime_root=runtime_root,
+ project_id=project_id,
+ project_kind=project_kind,
+ knowledge_root=knowledge_root,
+ goal_id=normalized_goal_id,
+ objective=objective,
+ non_goals=non_goals,
+ acceptance=acceptance,
+ unknowns=unknowns,
+ next_effect=next_effect,
+ stop_condition=stop_condition,
+ repository_bindings=repository_bindings,
+ external_locator_bindings=external_locator_bindings,
+ resolve_new_goal_storage_target=resolve_new_goal_storage_target,
+ goal_instance_profile=goal_instance_profile,
+ operation_id=operation_id,
+ registered_agents=registered_agents,
+ canonical_storage=canonical_storage,
+ )
+
+
def bind_session(
*,
registry_path: Path,
diff --git a/loopx/control_plane/quota/monitor_poll.py b/loopx/control_plane/quota/monitor_poll.py
index fa978cd9fc..7de8b03443 100644
--- a/loopx/control_plane/quota/monitor_poll.py
+++ b/loopx/control_plane/quota/monitor_poll.py
@@ -819,12 +819,16 @@ def record_quota_monitor_poll_for_decision(
agent_id=decision_agent_id,
effect_id=effect_id,
)
+ canonical_source = None
if execute and (safe_todo_id or safe_target_key) and not use_current_task_lease:
- from ..scheduler.provider_monitor_poll import (
- require_monitor_poll_source_available,
+ from ..coordination.local_authority import (
+ read_canonical_todos_if_promoted,
)
- require_monitor_poll_source_available(
+ # Provider loss is not permission to recover from a historical source.
+ # None alone means the source is unmigrated; the original quota owner
+ # may still validate and settle its existing transaction below.
+ canonical_source = read_canonical_todos_if_promoted(
runtime_root=runtime_root,
goal_id=goal_id,
)
@@ -934,6 +938,15 @@ def transact(
# Validate explicit claim routing before quota reserves a pending effect.
# Exact retries keep their frozen admission if registry membership changed.
receipt_path = _monitor_poll_receipt_path(runtime_root, goal_id, effect_id)
+ if (execute and not use_current_task_lease and canonical_source is None
+ and not receipt_path.exists()):
+ from ..coordination.local_authority import require_canonical_authority
+
+ # This existence check only routes the original operation while
+ # holding quota admission. TypeScript validates its contents,
+ # fingerprint and frozen decision before replay or provider dispatch.
+ # Without it, refuse before creating a new pending reservation.
+ require_canonical_authority(goal_id=goal_id, operation="monitor_poll")
if next_claimed_by and registry_path is not None and not receipt_path.exists():
plan_todo_authoring_scope(
command="create", role="agent", goal_id=goal_id,
@@ -999,6 +1012,15 @@ def transact(
) as source_admission:
native, after_status = transact(source_admission)
except ValueError as exc:
+ from ..coordination.local_authority import (
+ CanonicalAuthorityRequired,
+ LocalCoordinationAuthorityRejection,
+ )
+
+ if isinstance(exc, CanonicalAuthorityRequired):
+ # Moving fresh admission under the quota lock must preserve the
+ # public refusal type and its structured migration/no-write facts.
+ raise
payload = failure(
str(exc),
include_capability_retry=(
@@ -1008,6 +1030,9 @@ def transact(
)
if isinstance(exc, _NativeMonitorPollRejected):
payload["error_code"] = exc.diagnostic_code
+ else:
+ if isinstance(exc, LocalCoordinationAuthorityRejection):
+ payload["error_code"] = exc.code
return payload
if native.get("status") == "conflict":
diff --git a/loopx/control_plane/quota/monitor_poll_commit.ts b/loopx/control_plane/quota/monitor_poll_commit.ts
index 630991e2d6..8342f9c58d 100644
--- a/loopx/control_plane/quota/monitor_poll_commit.ts
+++ b/loopx/control_plane/quota/monitor_poll_commit.ts
@@ -942,7 +942,7 @@ function requestDigest(request: MonitorRequest): string {
function providerPlanFor(request: MonitorRequest, gateGuard = Boolean(
request.observation.settlement_todo_id && request.observation.settlement_todo_id !== request.observation.todo_id
-), legacyBatchVersion: 1 | null = 1): MonitorProviderPlan {
+), legacyBatchVersion: 1 | null = null): MonitorProviderPlan {
const resultHash = request.observation.result_hash;
if (!resultHash) {
throw new EffectRuntimeRequestError("monitor todo writeback requires --result-hash");
diff --git a/loopx/control_plane/scheduler/legacy_monitor_poll.py b/loopx/control_plane/scheduler/legacy_monitor_poll.py
deleted file mode 100644
index a1b2ab2af1..0000000000
--- a/loopx/control_plane/scheduler/legacy_monitor_poll.py
+++ /dev/null
@@ -1,230 +0,0 @@
-"""Atomic Markdown storage for the shared TypeScript Monitor batch.
-
-The operation receipt and every business mutation share one replacement.
-Parsing, locking and projection delivery stay here; retry and Todo rules stay
-in the typed planner. Retained receipts are outside the archivable Todo blocks.
-"""
-from __future__ import annotations
-
-import base64
-import json
-from pathlib import Path
-from typing import Any
-
-from ...agent_registry import load_goal_from_registry, registered_agent_ids_for_goal
-from ..coordination.legacy_writer_fence import legacy_todo_write_transaction
-from ..runtime.local_state_write_correctness import (
- build_local_state_write_correctness_dry_run_packet,
-)
-from ..runtime.document_io import verify_state_text_durable
-from ..todos.active_state_editing import (
- find_todo_block, insert_into_existing_section, insert_new_section,
- replace_updated_at, section_bounds,
-)
-from ..todos.contract import (
- TODO_METADATA_FIELDS, format_todo_metadata_line, metadata_line_for_todo_block,
- parse_todo_metadata_line,
- todo_marker_for_status,
-)
-from ..todos.goal_todo_projection import goal_todo_summaries
-from ..todos.handoff_mode import goal_handoff_mode
-from ..todos.mutation_response import serialize_added_todo_payload, serialize_todo_update_result
-from ..todos.mutation_authority import authorize_todo_lifecycle_mutation
-from ..todos.next_action_runtime import apply_added_todo_next_action
-from ..todos.path_resolution import resolve_todo_state_path
-from ..todos.todo_semantics import todo_priority_label
-from .monitor_batch_receipts import _plan, _read_receipt, _receipt_marker, _writeback
-
-
-def _metadata(record: dict[str, Any]) -> dict[str, Any]:
- """Use the public Markdown codec for the same wire types as Todo add/update."""
- line = format_todo_metadata_line(**{key: record[key] for key in TODO_METADATA_FIELDS
- if key != "role" and key in record and record[key] is not None})
- return parse_todo_metadata_line(line) or {}
-
-
-def _legacy_writeback(
- planned: dict[str, Any], *, request: dict[str, Any], original: str,
- todos: list[dict[str, Any]], state: Path, project: Path | None,
- mutation_authority: dict[str, Any],
-) -> dict[str, Any]:
- """Serialize typed batch facts through the existing public Todo result shapes."""
- result = _writeback(planned)
- monitor = next(todo for todo in todos if todo["todo_id"] == result["todo_id"])
- updated = planned["mutations"][0]["todo"]
- original_lines = original.splitlines()
- match = find_todo_block(original_lines, todo_id=result["todo_id"], role="agent")
- if match is None:
- raise ValueError("Monitor Todo disappeared during batch planning")
- section = match[1]
- status_changed = monitor.get("status") != updated.get("status")
- text_changed = monitor.get("text") != updated.get("text")
- metadata_updated = any(monitor.get(key) != updated.get(key)
- for key in TODO_METADATA_FIELDS if key != "role")
- changed = status_changed or text_changed or metadata_updated
- timestamp = request["observation"]["generated_at"]
- handoff = {"handoff_mode": goal_handoff_mode(original)}
- update = serialize_todo_update_result(
- role="agent", section=section, todo=monitor.get("text"),
- todo_id=result["todo_id"], status=str(updated["status"]),
- priority=todo_priority_label(monitor), status_changed=status_changed,
- text_changed=text_changed, metadata_updated=metadata_updated,
- metadata=_metadata(updated),
- monitor_poll_transition=result["todo_update"]["monitor_poll_transition"],
- )
- result["todo_update"] = {
- "ok": True, "dry_run": request["dry_run"], "changed": changed,
- "goal_id": request["goal_id"], "agent_id": request["actor_agent_id"],
- "mutation_authority": mutation_authority, **handoff, **update,
- "state_file": str(state), "project": str(project) if project else None,
- "updated_at": timestamp if changed else None,
- }
- created_ids = {mutation["todo"]["todo_id"] for mutation in planned["mutations"][1:]}
- successors = []
- for record in result["next_todos"]:
- role = record["role"]
- added = record["todo_id"] in created_ids
- metadata = _metadata(record)
- add_result = {**metadata, "todo_id": record["todo_id"],
- "section": next((match[2] for match in [section_bounds(original_lines, role)] if match),
- "User Todo / Owner Review Reading Queue" if role == "user" else "Agent Todo"),
- "status": record["status"], "task_class": record.get("task_class"),
- "priority": record.get("priority"),
- "action_kind": record.get("action_kind"),
- "required_write_scopes": record.get("required_write_scopes") or [],
- "required_capabilities": record.get("required_capabilities") or [],
- "target_capabilities": record.get("target_capabilities") or [],
- "explore_result_node_refs": record.get("explore_result_node_refs") or [],
- "required_decision_scopes": record.get("required_decision_scopes") or [],
- "excluded_agents": record.get("excluded_agents") or [],
- "already_exists": not added, "metadata_updated": False,
- "status_changed": False}
- successors.append(serialize_added_todo_payload(
- add_result=add_result, goal_id=request["goal_id"], role=role,
- todo_text=record["text"],
- agent_id=request["actor_agent_id"] if role == "user" else None,
- state_file=state, project=project, updated_at=timestamp,
- dry_run=request["dry_run"], added=added,
- metadata_updated=False, changed=added, handoff_gate=handoff,
- ))
- result["next_todos"] = successors
- result["successor_receipts"] = [
- {key: todo[key] for key in (
- "todo_id", "role", "priority", "task_class", "action_kind", "task_repository",
- "continuation_policy", "required_capabilities", "claimed_by",
- "unblocks_todo_id", "target_key") if todo.get(key) not in (None, "", [])}
- for todo in successors
- ]
- return result
-
-
-def _attach_batch_shadow(result: dict[str, Any]) -> dict[str, Any]:
- """Expose one real capture through the legacy nested result locations."""
- evidence = result.get("coordination_runtime_shadow")
- if evidence is not None:
- result["todo_update"]["coordination_runtime_shadow"] = evidence
- for todo in result["next_todos"]:
- todo["coordination_runtime_shadow"] = evidence
- return result
-
-
-def _attach_batch_dry_run(result: dict[str, Any], original: str) -> None:
- """One preview describes the single locked replacement and its Todos."""
- ids = [result["todo_id"], *(todo["todo_id"] for todo in result["next_todos"])]
- packet = build_local_state_write_correctness_dry_run_packet(
- goal_id=result["goal_id"], writer_id="loopx.todo", write_class="todo_update",
- state_text=original,
- target_refs={"state_file_ref": "registry.goal.state_file", "todo_ids": ids},
- patch_summary=f"preview one Monitor poll batch for {len(ids)} Todos: would change active state",
- expected_write_scopes=["active_state"],
- narrower_lock_allowed="not_for_multi_todo_batch",
- )
- result["todo_update"]["local_state_write_correctness"] = packet
- for todo in result["next_todos"]:
- todo["local_state_write_correctness"] = packet
-
-
-def _apply_mutations(lines: list[str], mutations: list[dict[str, Any]],
- todos: list[dict[str, Any]]) -> None:
- from ..todos.line_update import upsert_todo_metadata
-
- sources = {todo["todo_id"]: todo for todo in todos}
- for mutation in mutations:
- record = mutation["todo"]
- role, todo_id = record["role"], record["todo_id"]
- found = find_todo_block(lines, todo_id=todo_id, role=role)
- if found is not None:
- # Preserve narrative and private validation declarations in the
- # original block; only fields changed by the typed owner are edited.
- source = sources[todo_id]
- updates = {key: record.get(key) for key in set(source) | set(record)
- if key in TODO_METADATA_FIELDS and source.get(key) != record.get(key)}
- block = found[-1]
- upsert_todo_metadata(lines, block, metadata_line_for_todo_block(block, updates))
- else:
- metadata = format_todo_metadata_line(**{key: record[key] for key in TODO_METADATA_FIELDS
- if key != "role" and key in record and record[key] is not None})
- block = f"- [{todo_marker_for_status(record['status'])}] {record['text']}\n{metadata}"
- bounds = section_bounds(lines, role)
- if bounds:
- insert_into_existing_section(lines, bounds[0], bounds[1], block)
- else:
- insert_new_section(lines, role, block)
-
-
-def apply_legacy_monitor_poll(
- *, registry_path: Path, runtime_root: Path, request: dict[str, Any],
-) -> dict[str, Any]:
- goal_id, dry_run = request["goal_id"], request["dry_run"]
- project, state = resolve_todo_state_path(registry_path=registry_path, goal_id=goal_id)
- with legacy_todo_write_transaction(registry_path, goal_id, state,
- request["actor_agent_id"], "todo_update", dry_run, runtime_root=runtime_root):
- original = state.read_text(encoding="utf-8")
- previous = _read_receipt(original, request["operation_id"])
- goal = load_goal_from_registry(registry_path, goal_id)
- todos = goal_todo_summaries(goal, state_text=original, state_path=state,
- rollout_events=[], roles=["user", "agent"], status=None, todo_id=None,
- agent_id=None, limit=None, include_retained=True).todos
- planned = _plan(request, registered_agents=registered_agent_ids_for_goal(goal),
- todos=todos, previous_receipt=previous)
- if planned["replayed"]:
- verify_state_text_durable(state, original)
- return _writeback(planned)
- from ..coordination.runtime_shadow_writer_adapter import (
- begin_todo_runtime_shadow_capture, settle_todo_runtime_shadow_capture,
- write_captured_todo_state,
- )
-
- monitor = next(todo for todo in todos if todo["todo_id"] == planned["writeback"]["todo_id"])
- authority = authorize_todo_lifecycle_mutation(registry_path=registry_path, goal_id=goal_id,
- command="update", todo=monitor, actor_agent_id=request["actor_agent_id"])
- result = _legacy_writeback(planned, request=request, original=original,
- todos=todos, state=state, project=project, mutation_authority=authority)
- planned["receipt"]["writeback"] = result
- lines = original.splitlines()
- _apply_mutations(lines, planned["mutations"], todos)
- for todo in result["next_todos"]:
- apply_added_todo_next_action(lines, role=todo["role"],
- add_result={**todo, "changed": todo["added"]})
- if dry_run:
- _attach_batch_dry_run(result, original)
- capture = begin_todo_runtime_shadow_capture(registry_path=registry_path,
- runtime_root=runtime_root, goal_id=goal_id, state_path=state,
- write_class="todo_update", original_text=original)
- return _attach_batch_shadow(settle_todo_runtime_shadow_capture(result,
- registry_path=registry_path, runtime_root=runtime_root,
- goal_id=goal_id, capture=capture))
- capture = begin_todo_runtime_shadow_capture(registry_path=registry_path,
- runtime_root=runtime_root, goal_id=goal_id, state_path=state,
- write_class="todo_update", original_text=original)
- encoded = base64.b64encode(json.dumps(planned["receipt"], ensure_ascii=False,
- separators=(",", ":")).encode()).decode()
- first_heading = next((i for i, line in enumerate(lines) if line.startswith("#")), len(lines))
- lines[first_heading:first_heading] = [_receipt_marker(request["operation_id"]) + encoded + " -->", ""]
- text = replace_updated_at("\n".join(lines) + "\n", request["observation"]["generated_at"])
- write_captured_todo_state(capture, runtime_root=runtime_root, goal_id=goal_id,
- state_path=state, text=text)
- verify_state_text_durable(state, text)
- return _attach_batch_shadow(settle_todo_runtime_shadow_capture(result,
- registry_path=registry_path, runtime_root=runtime_root, goal_id=goal_id,
- capture=capture))
diff --git a/loopx/control_plane/scheduler/monitor_batch.ts b/loopx/control_plane/scheduler/monitor_batch.ts
index 38ddfa8382..af4434d4c8 100644
--- a/loopx/control_plane/scheduler/monitor_batch.ts
+++ b/loopx/control_plane/scheduler/monitor_batch.ts
@@ -53,8 +53,7 @@ export function normalizeMonitorPollFields(observation: unknown, intent: unknown
return fields;
}
-function planWriteback(input: MonitorBatchInput,
- claimPolicy: "actor_owned" | "registered_peer_handoff" = "actor_owned") {
+function planWriteback(input: MonitorBatchInput) {
const todos = input.todos;
const observation = input.observation;
const monitor = selectMonitorTodo(todos,
@@ -123,7 +122,7 @@ function planWriteback(input: MonitorBatchInput,
const created = planCoordinationTodoCreate({goal_id: input.goal_id, operation_id: input.operation_id,
actor_agent_id: actor, registered_agents: input.registered_agents, dry_run: input.dry_run,
now: new Date(String(observation.generated_at)), todo}, plannedTodos,
- input.read_model_schema ?? TODO_DOMAIN_READ_RECORD_SCHEMA, "role_text", claimPolicy);
+ input.read_model_schema ?? TODO_DOMAIN_READ_RECORD_SCHEMA, "role_text", "actor_owned");
if (created.status !== "planned" && created.status !== "no_change") throw new EffectRuntimeRequestError(String(created.reason));
const record = canonicalAuthorityObject(created.todo, "successor Todo");
nextTodos.push({...record, todo: record.text, ok: true, dry_run: input.dry_run});
@@ -159,18 +158,17 @@ function normalizeBatchInput(raw: MonitorBatchInput): MonitorBatchInput {
};
}
-/** Pure fresh batch planning. Canonical and legacy callers own their receipts. */
+/** Pure fresh batch planning for canonical authority transactions. */
export function planMonitorBatch(raw: MonitorBatchInput) {
const input = normalizeBatchInput(raw);
const todos = input.todos.map(todo => canonicalAuthorityObject(todo, "Monitor Todo snapshot item"));
return planWriteback({...input, todos});
}
-/** Public legacy adapter endpoint; the caller saves receipt and mutations in
- * the same atomic state-file replacement. */
-export function planLegacyMonitorBatch(value: unknown): JsonObject {
+/** Validate and replay one immutable pre-cutoff Markdown receipt. */
+export function replayLegacyMonitorBatch(value: unknown): JsonObject {
try {
- return planLegacyMonitorBatchRequest(value);
+ return replayLegacyMonitorBatchRequest(value);
} catch (error) {
if (error instanceof AuthorityStoreProtocolError) {
throw new EffectRuntimeRequestError(error.message);
@@ -179,62 +177,65 @@ export function planLegacyMonitorBatch(value: unknown): JsonObject {
}
}
-function planLegacyMonitorBatchRequest(value: unknown): JsonObject {
- const request = requireJsonObject(value, "legacy Monitor batch request");
- if (request.schema_version !== MONITOR_BATCH_PLAN_REQUEST_SCHEMA || !Array.isArray(request.todos)) {
- throw new EffectRuntimeRequestError("legacy Monitor batch request schema mismatch");
+function historicalReplayInput(request: JsonObject): MonitorBatchInput {
+ const actor = request.actor_agent_id;
+ if (actor !== null && typeof actor !== "string") {
+ throw new EffectRuntimeRequestError("actor_agent_id must be a string or null");
}
- const input = normalizeBatchInput(request as unknown as MonitorBatchInput);
+ return {
+ goal_id: requireAuthorityStoreId(request.goal_id, "goal id"),
+ operation_id: requireAuthorityStoreId(request.operation_id, "operation id"),
+ actor_agent_id: actor === null ? null : normalizeTodoAgent(actor, "actor_agent_id"),
+ registered_agents: [],
+ dry_run: requireBoolean(request.dry_run, "dry_run"),
+ ...normalizeMonitorPollFields(request.observation, request.intent),
+ todos: [],
+ };
+}
+
+function replayLegacyMonitorBatchRequest(value: unknown): JsonObject {
+ const request = requireJsonObject(value, "historical Monitor batch replay request");
+ if (request.schema_version !== MONITOR_BATCH_PLAN_REQUEST_SCHEMA ||
+ request.previous_receipt == null) {
+ throw new EffectRuntimeRequestError(
+ "historical Monitor batch replay requires an immutable previous receipt",
+ );
+ }
+ const input = historicalReplayInput(request);
const hash = monitorPollRequestHash(input);
- if (request.previous_receipt != null) {
- const previous = canonicalAuthorityObject(request.previous_receipt, "Monitor batch receipt");
- if (previous.schema_version !== MONITOR_BATCH_RECEIPT_SCHEMA || previous.goal_id !== input.goal_id ||
- previous.operation_id !== input.operation_id || previous.request_sha256 !== hash) {
- throw new EffectRuntimeRequestError("monitor effect identity is already bound: Monitor batch receipt identity or original request mismatch");
- }
- const writeback = canonicalAuthorityObject(previous.writeback, "Monitor batch receipt writeback");
- const expectedRoles = (["agent", "user"] as const).filter(role => {
- const successor = input.intent[role === "agent" ? "next_agent_todo" : "next_user_todo"];
- return typeof successor === "string" && stripPythonWhitespace(successor).length > 0;
- });
- if (writeback.schema_version !== "monitor_poll_todo_writeback_v0" ||
- writeback.goal_id !== input.goal_id || writeback.monitor_effect_id !== input.operation_id ||
- (input.observation.todo_id != null && writeback.todo_id !== input.observation.todo_id) ||
- writeback.result_hash !== input.observation.result_hash ||
- writeback.material_change !== input.observation.material_change ||
- writeback.last_checked_at !== input.observation.generated_at ||
- writeback.dry_run !== input.dry_run ||
- !Array.isArray(writeback.next_todos) || !Array.isArray(writeback.successor_receipts) ||
- writeback.next_todos.length !== writeback.successor_receipts.length ||
- writeback.next_todos.length !== expectedRoles.length) {
- throw new EffectRuntimeRequestError("Monitor batch receipt writeback is malformed");
- }
- const successorIds = new Set();
- for (const [index, todo] of writeback.next_todos.entries()) {
- const item = canonicalAuthorityObject(todo, "Monitor successor receipt");
- const summary = canonicalAuthorityObject(writeback.successor_receipts[index], "Monitor successor summary");
- if (typeof item.todo_id !== "string" || !item.todo_id.startsWith("todo_") ||
- successorIds.has(item.todo_id) || item.todo_id !== summary.todo_id ||
- item.role !== expectedRoles[index] || summary.role !== expectedRoles[index]) {
- throw new EffectRuntimeRequestError("Monitor batch receipt successor identity is malformed");
- }
- successorIds.add(item.todo_id);
- }
- return {schema_version: MONITOR_BATCH_PLAN_RESULT_SCHEMA, mutations: [],
- writeback: {...writeback, provider_replayed: true}, receipt: previous, replayed: true};
+ const previous = canonicalAuthorityObject(request.previous_receipt, "Monitor batch receipt");
+ if (previous.schema_version !== MONITOR_BATCH_RECEIPT_SCHEMA || previous.goal_id !== input.goal_id ||
+ previous.operation_id !== input.operation_id || previous.request_sha256 !== hash) {
+ throw new EffectRuntimeRequestError("monitor effect identity is already bound: Monitor batch receipt identity or original request mismatch");
}
- if (request.legacy_batch_version !== 1) {
- throw new EffectRuntimeRequestError("legacy Monitor recovery needs reconciliation: preserve the pending receipt and inspect the original Monitor and successor Todos before retrying; frozen plan predates atomic batch receipts", "legacy_monitor_recovery_required");
+ const writeback = canonicalAuthorityObject(previous.writeback, "Monitor batch receipt writeback");
+ const expectedRoles = (["agent", "user"] as const).filter(role => {
+ const successor = input.intent[role === "agent" ? "next_agent_todo" : "next_user_todo"];
+ return typeof successor === "string" && stripPythonWhitespace(successor).length > 0;
+ });
+ if (writeback.schema_version !== "monitor_poll_todo_writeback_v0" ||
+ writeback.goal_id !== input.goal_id || writeback.monitor_effect_id !== input.operation_id ||
+ (input.observation.todo_id != null && writeback.todo_id !== input.observation.todo_id) ||
+ writeback.result_hash !== input.observation.result_hash ||
+ writeback.material_change !== input.observation.material_change ||
+ writeback.last_checked_at !== input.observation.generated_at ||
+ writeback.dry_run !== input.dry_run ||
+ !Array.isArray(writeback.next_todos) || !Array.isArray(writeback.successor_receipts) ||
+ writeback.next_todos.length !== writeback.successor_receipts.length ||
+ writeback.next_todos.length !== expectedRoles.length) {
+ throw new EffectRuntimeRequestError("Monitor batch receipt writeback is malformed");
}
- const todos = input.todos.map(todo => canonicalAuthorityObject(todo, "Monitor Todo snapshot item"));
- if (todos.some(todo => todo.monitor_effect_id === input.operation_id)) {
- throw new EffectRuntimeRequestError("Monitor effect already applied without its batch receipt; preserve the pending receipt and reconcile the original successor Todos", "legacy_monitor_recovery_required");
+ const successorIds = new Set();
+ for (const [index, todo] of writeback.next_todos.entries()) {
+ const item = canonicalAuthorityObject(todo, "Monitor successor receipt");
+ const summary = canonicalAuthorityObject(writeback.successor_receipts[index], "Monitor successor summary");
+ if (typeof item.todo_id !== "string" || !item.todo_id.startsWith("todo_") ||
+ successorIds.has(item.todo_id) || item.todo_id !== summary.todo_id ||
+ item.role !== expectedRoles[index] || summary.role !== expectedRoles[index]) {
+ throw new EffectRuntimeRequestError("Monitor batch receipt successor identity is malformed");
+ }
+ successorIds.add(item.todo_id);
}
- // Legacy Monitor explicitly permits routing a successor to a registered peer.
- // Canonical Monitor and ordinary Todo create keep their actor-owned claim rule.
- const planned = planWriteback({...input, todos}, "registered_peer_handoff");
- const receipt = {schema_version: MONITOR_BATCH_RECEIPT_SCHEMA, goal_id: input.goal_id,
- operation_id: input.operation_id, request_sha256: hash, writeback: planned.writeback};
- return {schema_version: MONITOR_BATCH_PLAN_RESULT_SCHEMA, mutations: planned.mutations,
- writeback: planned.writeback, receipt, replayed: false};
+ return {schema_version: MONITOR_BATCH_PLAN_RESULT_SCHEMA, mutations: [],
+ writeback: {...writeback, provider_replayed: true}, receipt: previous, replayed: true};
}
diff --git a/loopx/control_plane/scheduler/monitor_batch_receipts.py b/loopx/control_plane/scheduler/monitor_batch_receipts.py
index dee4757b99..323a04b905 100644
--- a/loopx/control_plane/scheduler/monitor_batch_receipts.py
+++ b/loopx/control_plane/scheduler/monitor_batch_receipts.py
@@ -38,7 +38,10 @@ def _read_receipt(text: str, operation_id: str) -> dict[str, Any] | None:
def _plan(request: dict[str, Any], **facts: Any) -> dict[str, Any]:
try:
- result = effect_runtime_result("scheduler.monitor_batch.plan", {**request, **facts})
+ result = effect_runtime_result(
+ "scheduler.monitor_batch.replay",
+ {**request, **facts},
+ )
except EffectRuntimeRejected as error:
raise ValueError(str(error)) from error
if not isinstance(result, dict) or result.get("schema_version") != "loopx_monitor_batch_plan_result_v0":
diff --git a/loopx/control_plane/scheduler/monitor_poll_writeback.py b/loopx/control_plane/scheduler/monitor_poll_writeback.py
index 70432ca11b..2629e94b4f 100644
--- a/loopx/control_plane/scheduler/monitor_poll_writeback.py
+++ b/loopx/control_plane/scheduler/monitor_poll_writeback.py
@@ -8,6 +8,12 @@
normalize_todo_id,
)
from ..effect_runtime import EffectRuntimeRejected, effect_runtime_result
+from ..coordination.local_authority import (
+ LocalCoordinationAuthorityRejection,
+ read_canonical_todos_if_promoted,
+ require_canonical_authority,
+)
+from ...paths import effective_runtime_root
def resolve_monitor_todo_item(
@@ -18,19 +24,24 @@ def resolve_monitor_todo_item(
todo_id: str | None = None,
target_key: str | None = None,
) -> dict[str, Any]:
- from ...control_plane.todos.list_readback import list_goal_todos
-
normalized_todo_id = normalize_todo_id(todo_id) if todo_id else None
safe_target_key = str(target_key or "").strip()
if not normalized_todo_id and not safe_target_key:
raise ValueError("monitor todo writeback requires --todo-id or --target-key")
- payload = list_goal_todos(
- registry_path=registry_path,
+ authority = read_canonical_todos_if_promoted(
+ runtime_root=effective_runtime_root(
+ registry_path,
+ str(runtime_root) if runtime_root is not None else None,
+ ),
goal_id=goal_id,
- role="agent",
- runtime_root_arg=str(runtime_root) if runtime_root is not None else None,
)
- items = payload.get("todos") if isinstance(payload.get("todos"), list) else []
+ if authority is None:
+ require_canonical_authority(goal_id=goal_id, operation="monitor_poll")
+ items = [
+ item
+ for item in authority["todos"]
+ if item.get("role") == "agent" and item.get("archive_state") != "archive"
+ ]
try:
result = effect_runtime_result("scheduler.monitor_target.select", {
"schema_version": "loopx_monitor_target_request_v0", "items": items,
@@ -73,11 +84,10 @@ def write_monitor_poll_todo_state(
task_lease_idempotency_key: str | None = None,
task_lease_expected_version: int | None = None,
gate_scope_guard: bool = False,
- legacy_batch_version: int | None = 1,
+ legacy_batch_version: int | None = None,
) -> dict[str, Any] | None:
"""Route one typed Monitor transaction to its owning storage adapter."""
from .monitor_batch_receipts import replay_legacy_monitor_poll
- from .provider_monitor_poll import poll_canonical_monitor_if_promoted
lease_proof = ({"idempotency_key": task_lease_idempotency_key,
"expected_version": task_lease_expected_version}
@@ -106,35 +116,44 @@ def write_monitor_poll_todo_state(
"actor_agent_id": agent_id, "dry_run": not execute,
"observation": observation, "intent": intent,
"gate_scope_guard": gate_scope_guard, "legacy_batch_version": legacy_batch_version}
+ from .provider_monitor_poll import (
+ poll_canonical_monitor_if_promoted,
+ )
+
+ # A selected provider must be readable before any historical fallback.
+ # An unmigrated source may still replay its exact business receipt; that
+ # typed readback does not admit a fresh Monitor write or drain old captures.
+ authority = read_canonical_todos_if_promoted(
+ runtime_root=runtime_root,
+ goal_id=goal_id,
+ )
+ source_error: OSError | ValueError | None = None
if monitor_effect_id and lease_proof is None:
# Legacy receipts are historical facts even after authority promotion.
- previous = replay_legacy_monitor_poll(registry_path=registry_path, request=request)
+ try:
+ previous = replay_legacy_monitor_poll(registry_path=registry_path, request=request)
+ except (OSError, ValueError) as error:
+ if authority is None:
+ raise
+ # Keep the typed native receipt available when its display is
+ # damaged. v4 forbids planning or committing if that receipt is
+ # absent; a malformed historical source never permits fresh work.
+ previous = None
+ source_error = error
if previous is not None:
- from ..coordination.local_authority import local_authority_is_promoted
- # A crash after the one Markdown install can leave its prepared
- # shadow entry without a commit marker. The existing drain proves
- # that source replacement and recovers it from durable readback.
- # Historical receipts after promotion must not drain stale legacy
- # state into the canonical authority.
- if not local_authority_is_promoted(runtime_root=runtime_root, goal_id=goal_id):
- from ..coordination.local_authority_shadow_adapter import drain_local_authority_shadow_outbox
- from ..coordination.local_authority_shadow_outbox import outbox_root
-
- if outbox_root(runtime_root, goal_id).exists():
- drain_local_authority_shadow_outbox(
- registry_path=registry_path, runtime_root=runtime_root, goal_id=goal_id)
return previous
- canonical = poll_canonical_monitor_if_promoted(
- registry_path=registry_path, runtime_root=runtime_root, goal_id=goal_id,
- execute=execute, monitor_effect_id=monitor_effect_id, agent_id=agent_id,
- lease_proof=lease_proof, gate_scope_guard=gate_scope_guard,
- observation=observation, intent=intent,
- )
- if canonical is not None:
- return canonical
- if lease_proof is not None:
- raise ValueError("Monitor lease proof requires promoted canonical authority; no legacy write attempted")
- from .legacy_monitor_poll import apply_legacy_monitor_poll
-
- return apply_legacy_monitor_poll(
- registry_path=registry_path, runtime_root=runtime_root, request=request)
+ if authority is None:
+ require_canonical_authority(goal_id=goal_id, operation="monitor_poll")
+ try:
+ canonical = poll_canonical_monitor_if_promoted(
+ registry_path=registry_path, runtime_root=runtime_root, goal_id=goal_id,
+ execute=execute, monitor_effect_id=monitor_effect_id, agent_id=agent_id,
+ lease_proof=lease_proof, gate_scope_guard=gate_scope_guard,
+ receipt_only=source_error is not None,
+ observation=observation, intent=intent,
+ )
+ except LocalCoordinationAuthorityRejection as error:
+ if source_error is not None and error.code == "monitor_poll_original_receipt_required":
+ raise source_error from error
+ raise
+ return canonical
diff --git a/loopx/control_plane/scheduler/provider_monitor_poll.py b/loopx/control_plane/scheduler/provider_monitor_poll.py
index 2f9222f74a..17ceb3f860 100644
--- a/loopx/control_plane/scheduler/provider_monitor_poll.py
+++ b/loopx/control_plane/scheduler/provider_monitor_poll.py
@@ -9,36 +9,27 @@
from ..coordination.authority_source_capture import authority_registry_source
from ..coordination.local_authority import (
LOCAL_AUTHORITY_SOURCES,
+ LocalCoordinationAuthorityRejection,
LocalCoordinationAuthorityUnavailable,
- local_authority_is_promoted,
- read_canonical_todos_if_promoted,
+ raise_canonical_write_failure,
)
from ..effect_runtime import (
CANONICAL_AUTHORITY_WRITE_TIMEOUT_SECONDS,
effect_runtime_result,
)
from ..todos.provider_projection import settle_canonical_todo_projection
-
-
-
-
-def require_monitor_poll_source_available(*, runtime_root: Path, goal_id: str) -> None:
- """Fail closed on unavailable promoted authority before unrelated quota work."""
- read_canonical_todos_if_promoted(runtime_root=runtime_root, goal_id=goal_id)
-
-
def poll_canonical_monitor_if_promoted(
*, registry_path: Path, runtime_root: Path, goal_id: str, execute: bool,
monitor_effect_id: str | None, agent_id: str | None,
observation: dict[str, Any], intent: dict[str, Any],
lease_proof: dict[str, Any] | None = None, gate_scope_guard: bool = False,
-) -> dict[str, Any] | None:
- if not local_authority_is_promoted(runtime_root=runtime_root, goal_id=goal_id):
- return None
+ receipt_only: bool = False,
+) -> dict[str, Any]:
with authority_registry_source(registry_path) as registry_source:
registered = registered_agent_ids_from_registry(registry_path, goal_id)
result = effect_runtime_result("coordination.local_authority.monitor_poll", {
- "schema_version": ("loopx_coordination_monitor_poll_request_v3" if gate_scope_guard
+ "schema_version": ("loopx_coordination_monitor_poll_request_v4" if receipt_only
+ else "loopx_coordination_monitor_poll_request_v3" if gate_scope_guard
else "loopx_coordination_monitor_poll_request_v2"),
**({"lease_proof": lease_proof} if lease_proof is not None else {}),
**({"gate_scope_guard": True} if gate_scope_guard else {}),
@@ -56,6 +47,30 @@ def poll_canonical_monitor_if_promoted(
or result.get("legacy_fallback_used") is not False
or not isinstance(result.get("writeback"), dict)):
payload = result if isinstance(result, dict) else {}
+ raise_canonical_write_failure(
+ payload,
+ goal_id=goal_id,
+ operation="monitor_poll",
+ )
+ if (
+ payload.get("status") == "failed"
+ and payload.get("reason_code")
+ == "coordination_operation_identity_mismatch"
+ ):
+ raise LocalCoordinationAuthorityRejection(
+ "monitor effect identity is already bound to a different observation",
+ code="coordination_operation_identity_mismatch",
+ payload=payload,
+ )
+ if (
+ payload.get("status") == "failed"
+ and payload.get("failure_kind") == "decision_rejection"
+ ):
+ raise LocalCoordinationAuthorityRejection(
+ str(payload.get("reason") or "canonical Monitor poll was rejected"),
+ code=str(payload.get("reason_code") or "monitor_poll_rejected"),
+ payload=payload,
+ )
raise LocalCoordinationAuthorityUnavailable(
str(payload.get("reason") or "canonical Monitor transaction unavailable"),
code=str(payload.get("reason_code") or "monitor_poll_unavailable"), payload=payload,
diff --git a/loopx/control_plane/testing/actual_default_model_behavior_portfolio.py b/loopx/control_plane/testing/actual_default_model_behavior_portfolio.py
index 8ac6dae391..b792cb3591 100644
--- a/loopx/control_plane/testing/actual_default_model_behavior_portfolio.py
+++ b/loopx/control_plane/testing/actual_default_model_behavior_portfolio.py
@@ -54,6 +54,7 @@
SELECTED_TODO_TOOL_FIXTURE_ACTION_TEXT,
SELECTED_TODO_TOOL_FIXTURE_TODO_ID,
)
+from .terminal_settlement_tool_behavior import TERMINAL_SETTLEMENT_FIXTURE_TODO_ID
from .replan_vision_closeout_behavior import required_vision_scenario_contract
ACTUAL_DEFAULT_MODEL_BEHAVIOR_PORTFOLIO_SCHEMA_VERSION = (
@@ -529,7 +530,7 @@ def _terminal_settlement_scenario_source() -> dict[str, Any]:
payload = _turn_scenario_source(human_gate=False)
payload["selected_todo"] = {
**dict(payload["selected_todo"]),
- "todo_id": "todo_terminal001",
+ "todo_id": TERMINAL_SETTLEMENT_FIXTURE_TODO_ID,
"text": (
"Read `fixture/settlement-proof.json`, verify the bounded delivery, "
"then settle this final Todo from the quota-projected plan. "
@@ -910,7 +911,7 @@ def _validate_identity_scenario_contract(
if spec.scenario_id == "turn_terminal_settlement":
selected = dict(source_packet.get("selected_todo") or {})
if not (
- contract.get("selected_todo_id") == "todo_terminal001"
+ contract.get("selected_todo_id") == TERMINAL_SETTLEMENT_FIXTURE_TODO_ID
and "settlement-proof.json" in str(selected.get("text") or "")
):
raise ValueError(
diff --git a/loopx/control_plane/testing/authority_e2e_fixtures.py b/loopx/control_plane/testing/authority_e2e_fixtures.py
index ffe2283135..ca2aa94001 100644
--- a/loopx/control_plane/testing/authority_e2e_fixtures.py
+++ b/loopx/control_plane/testing/authority_e2e_fixtures.py
@@ -239,6 +239,108 @@ def build_goal_workspace(
)
+def build_canonical_file_workspace(root: Path, *, goal_id: str) -> GoalWorkspace:
+ """Create a new Goal through the public CLI with File as its authority."""
+
+ repo = root / goal_id
+ repo.mkdir()
+ runtime_root = root / f"{goal_id}-runtime"
+ home = root / f"{goal_id}-home"
+ home.mkdir()
+ registry_path = root / f"{goal_id}-registry.json"
+ provisional = GoalWorkspace(
+ goal_id=goal_id,
+ root=root,
+ repo=repo,
+ registry_path=registry_path,
+ state_path=repo / "ACTIVE_GOAL_STATE.md",
+ runtime_root=runtime_root,
+ home=home,
+ runtime_root_binding="cli_override",
+ registry_runtime_root=runtime_root,
+ )
+ defaults_path = root / f"{goal_id}-storage-defaults.json"
+ defaults_path.write_text(
+ json.dumps(
+ {
+ "schema_version": "loopx_goal_storage_defaults_v1",
+ "new_goal_provider": "file",
+ "canonical_creation": True,
+ "new_goal_handoff_mode": "hard_lease",
+ }
+ ),
+ encoding="utf-8",
+ )
+ preview = run_cli(
+ provisional,
+ "machine-config",
+ "preview",
+ "--namespace",
+ "goal_storage",
+ "--config-json",
+ str(defaults_path),
+ )
+ plan_revision = preview.get("plan_revision")
+ if not isinstance(plan_revision, str) or not plan_revision:
+ raise AssertionError("goal storage preview omitted its plan revision")
+ run_cli(
+ provisional,
+ "machine-config",
+ "apply",
+ "--namespace",
+ "goal_storage",
+ "--config-json",
+ str(defaults_path),
+ "--expected-plan-revision",
+ plan_revision,
+ "--execute",
+ )
+ created = run_cli(
+ provisional,
+ "bootstrap",
+ "--project",
+ str(repo),
+ "--goal-id",
+ goal_id,
+ "--objective",
+ "Qualify canonical File authority through the public CLI.",
+ "--no-global-sync",
+ )
+ selection = created.get("storage_selection")
+ if not isinstance(selection, dict) or (
+ selection.get("provider") != "file"
+ or selection.get("authority_initialized") is not True
+ or selection.get("legacy_fallback_used") is not False
+ ):
+ raise AssertionError("bootstrap did not initialize canonical File authority")
+ state_value = created.get("state_file")
+ if not isinstance(state_value, str) or not state_value:
+ raise AssertionError("bootstrap omitted the canonical projection path")
+ workspace = GoalWorkspace(
+ goal_id=goal_id,
+ root=root,
+ repo=repo,
+ registry_path=registry_path,
+ state_path=Path(state_value),
+ runtime_root=runtime_root,
+ home=home,
+ runtime_root_binding="cli_override",
+ registry_runtime_root=runtime_root,
+ )
+ run_cli(
+ workspace,
+ "configure-goal",
+ "--goal-id",
+ goal_id,
+ "--registered-agent",
+ "agent-a",
+ "--registered-agent",
+ "agent-b",
+ "--execute",
+ )
+ return workspace
+
+
def build_legacy_migration_source(
root: Path,
*,
@@ -534,6 +636,7 @@ def tap_summary(
"RUNTIME_ROOT_BINDINGS",
"TS_READBACK_PROBE",
"TapSummary",
+ "build_canonical_file_workspace",
"build_goal_workspace",
"build_legacy_migration_source",
"cli_command",
diff --git a/loopx/control_plane/testing/authority_e2e_ladder.py b/loopx/control_plane/testing/authority_e2e_ladder.py
index 826c1f04ee..68ac98d144 100644
--- a/loopx/control_plane/testing/authority_e2e_ladder.py
+++ b/loopx/control_plane/testing/authority_e2e_ladder.py
@@ -4,8 +4,8 @@
is green only when every selected row passed. A row that cannot run in this
environment (no PostgreSQL, no NoKV stack, no POSIX signals) reports
``unverified`` and, unless ``--allow-unverified`` is given, the ladder exits
-non-zero. Rows for stages that later PRs will land are declared as
-``pending`` so the report never silently claims them.
+non-zero. Future obligations may be declared as ``pending`` so the report never
+silently claims them.
Rows drive the product through ``python -m loopx.cli`` (``real_cli``) or run
the retained store-level probes (``store_direct``); this module adds no
@@ -39,27 +39,12 @@
sha256_hex,
unverified,
)
-from .authority_e2e_rows_stage2c import row_retired_observation_upgrade
-from .authority_e2e_rows_stage2c2 import (
- capture_workspace,
- delivered,
- row_archive_after_leased_completion_parity,
- row_drain_idempotent,
- row_event_only_todo_source_holds,
- row_growth_measurement_gate,
- row_migration_seeds_and_drains,
- row_outbox_prepared_then_committed_entries,
- row_parity_divergent_detects_foreign_edit,
- row_parity_equal,
- row_rollback_with_pending_entries,
- row_sigkill_between_primary_write_and_drain,
- row_sigkill_mid_drain,
-)
from .authority_e2e_fixtures import (
REPO_ROOT,
CliOutputError,
TS_READBACK_PROBE,
JsonObject,
+ build_canonical_file_workspace,
node_executable,
parse_json_object,
run_cli,
@@ -70,7 +55,7 @@
REPORT_SCHEMA = "loopx_shared_goal_authority_e2e_report_v0"
LIST_SCHEMA = "loopx_shared_goal_authority_e2e_rows_v0"
-STAGES: tuple[str, ...] = ("0", "1", "2a", "2b", "2c1", "2c2")
+STAGES: tuple[str, ...] = ("0", "1", "2a", "2b")
PRODUCT_PATHS: tuple[str, ...] = ("real_cli", "store_direct")
GATES: tuple[str, ...] = (
"deterministic",
@@ -137,8 +122,6 @@
Path("loopx") / "control_plane" / "testing" / "authority_e2e_ladder.py",
Path("loopx") / "control_plane" / "testing" / "authority_e2e_fixtures.py",
Path("loopx") / "control_plane" / "testing" / "authority_e2e_row_support.py",
- Path("loopx") / "control_plane" / "testing" / "authority_e2e_rows_stage2c.py",
- Path("loopx") / "control_plane" / "testing" / "authority_e2e_rows_stage2c2.py",
)
MINIMUM_POSTGRES_TAP_PASSES = 9
@dataclass(frozen=True)
@@ -249,8 +232,15 @@ def _row_local_conformance(context: RowContext, provider: str) -> RowOutcome:
def _row_cli_document_decodes_through_ts_store(context: RowContext) -> RowOutcome:
if node_executable() is None:
return unverified("node_missing")
- workspace = capture_workspace(context, "ladder-s1")
- added = add_todo(workspace, "Decode this source transaction through the TypeScript store.")
+ workspace = build_canonical_file_workspace(
+ context.root,
+ goal_id=unique_goal_id("ladder-s1"),
+ )
+ added = add_todo(
+ workspace,
+ "Decode this source transaction through the TypeScript store.",
+ claimed_by=AGENT_A,
+ )
todo_id = str(added["todo_id"])
acquired = acquire_lease(
workspace,
@@ -258,6 +248,9 @@ def _row_cli_document_decodes_through_ts_store(context: RowContext) -> RowOutcom
owner=AGENT_A,
idempotency_key="ladder-s1-lease",
)
+ lease = acquired.get("lease")
+ expect(isinstance(lease, dict), "task-lease acquire must return its canonical lease")
+ assert isinstance(lease, dict)
updated = run_cli(
workspace,
"todo",
@@ -270,10 +263,18 @@ def _row_cli_document_decodes_through_ts_store(context: RowContext) -> RowOutcom
"A public-safe note recorded after the lease.",
"--agent-id",
AGENT_A,
+ "--task-lease-idempotency-key",
+ str(lease["idempotency_key"]),
+ "--task-lease-expected-version",
+ str(lease["version"]),
)
for label, payload in (("todo add", added), ("task-lease acquire", acquired), ("todo update", updated)):
- delivered(payload, label=label)
- probe = ts_readback(workspace, directory=workspace.runtime_root / "authority-shadow" / "file-v0")
+ expect(payload.get("source_authority") == "file_v0", f"{label} must use canonical File authority")
+ expect(payload.get("decision_read_from_provider") is True, f"{label} must read provider authority")
+ expect(payload.get("legacy_fallback_used") is False, f"{label} must not use a legacy fallback")
+ expect("coordination_runtime_shadow" not in payload, f"{label} must not emit retired capture evidence")
+ directory = workspace.runtime_root / "authority" / "file-v0"
+ probe = ts_readback(workspace, directory=directory)
expect(probe is not None, "read-back probe requires node")
assert probe is not None
load, scan = probe.get("load"), probe.get("scan")
@@ -287,7 +288,7 @@ def _row_cli_document_decodes_through_ts_store(context: RowContext) -> RowOutcom
expect(scan.get("cursors") == ["1", "2", "3", "4"], "transactions must retain source order")
assert isinstance(operations, list)
receipt_probe = ts_readback(workspace, receipt=str(operations[1]),
- directory=workspace.runtime_root / "authority-shadow" / "file-v0")
+ directory=directory)
expect(isinstance(receipt_probe, dict), "receipt probe must load")
assert isinstance(receipt_probe, dict)
receipt = receipt_probe.get("receipt")
@@ -526,124 +527,9 @@ def _row_postgresql_conformance_live(context: RowContext) -> RowOutcome:
posix_only=False,
run=_row_postgresql_conformance_live,
),
- LadderRow(
- id="s2c1.retired_observation_upgrade",
- stage="2c1",
- title="Retired settings cannot write; explicit replacement bootstrap captures the next transaction",
- product_path="real_cli",
- gate="deterministic",
- posix_only=False,
- run=row_retired_observation_upgrade,
- ),
- LadderRow(
- id="s2c2.outbox_prepared_then_committed_entries",
- stage="2c2",
- title="Python and TypeScript writers leave prepared records with committed markers that one drain delivers once",
- product_path="real_cli",
- gate="deterministic",
- posix_only=False,
- run=row_outbox_prepared_then_committed_entries,
- ),
- LadderRow(
- id="s2c2.drain_idempotent",
- stage="2c2",
- title="Bounded drains are cumulative, an idle drain changes nothing, and a writer replay mints no entry",
- product_path="real_cli",
- gate="deterministic",
- posix_only=False,
- run=row_drain_idempotent,
- ),
- LadderRow(
- id="s2c2.sigkill_between_primary_write_and_drain",
- stage="2c2",
- title="A SIGKILL around the primary replace leaves a prepared-only entry that drain settles from the primary bytes",
- product_path="real_cli",
- gate="deterministic",
- posix_only=True,
- run=row_sigkill_between_primary_write_and_drain,
- ),
- LadderRow(
- id="s2c2.sigkill_mid_drain",
- stage="2c2",
- title="A SIGKILL inside the inline drain is recovered from exact receipts without a second delivery",
- product_path="real_cli",
- gate="deterministic",
- posix_only=True,
- run=row_sigkill_mid_drain,
- ),
- LadderRow(
- id="s2c2.rollback_with_pending_entries",
- stage="2c2",
- title="Rollback archives pending entries, holds capture until rebootstrap, and replays its historical result",
- product_path="real_cli",
- gate="deterministic",
- posix_only=True,
- run=row_rollback_with_pending_entries,
- ),
- LadderRow(
- id="s2c2.parity_equal",
- stage="2c2",
- title="Sustained interleaving of Python and TypeScript writers keeps every bounded qualification matched",
- product_path="real_cli",
- gate="deterministic",
- posix_only=False,
- run=row_parity_equal,
- ),
- LadderRow(
- id="s2c2.parity_divergent_detects_foreign_edit",
- stage="2c2",
- title="A direct primary edit is reported as drift, holds later captures, and recovers only by rollback and rebootstrap",
- product_path="real_cli",
- gate="deterministic",
- posix_only=False,
- run=row_parity_divergent_detects_foreign_edit,
- ),
- LadderRow(
- id="s2c2.event_only_todo_source_holds",
- stage="2c2",
- title="A retired Todo event source refuses reads and primary writes without changing either source",
- product_path="real_cli",
- gate="deterministic",
- posix_only=False,
- run=row_event_only_todo_source_holds,
- ),
- LadderRow(
- id="s2c2.migration_seeds_and_drains",
- stage="2c2",
- title="migrate-state refuses an active capture source; after rollback the migrated goal bootstraps a fresh lineage that drains",
- product_path="real_cli",
- gate="deterministic",
- posix_only=False,
- run=row_migration_seeds_and_drains,
- ),
- LadderRow(
- id="s2c2.growth_measurement_gate",
- stage="2c2",
- title="file-v0 history growth is measured per transaction and gated on retention integrity, claiming no capacity horizon",
- product_path="real_cli",
- gate="deterministic",
- posix_only=False,
- run=row_growth_measurement_gate,
- ),
- LadderRow(
- id="s2c2.archive_after_leased_completion_parity",
- stage="2c2",
- title="archiving a Todo whose released lease stays on disk keeps the candidate head matched and qualifiable",
- product_path="real_cli",
- gate="deterministic",
- posix_only=False,
- run=row_archive_after_leased_completion_parity,
- ),
)
-PENDING_ROWS: tuple[PendingRow, ...] = (
- PendingRow(
- "s2c2.sustained_parity_soak",
- "2c2",
- "a >=10-day synthetic-goal soak of the selected local profile (RFC section 7.2, lane L); "
- "bounded qualification reports sustained_parity_verdict=not_evaluated",
- ),
-)
+PENDING_ROWS: tuple[PendingRow, ...] = ()
def row_by_id(row_id: str) -> LadderRow:
diff --git a/loopx/control_plane/testing/authority_e2e_row_support.py b/loopx/control_plane/testing/authority_e2e_row_support.py
index 50fcfc7233..c2dbd8f801 100644
--- a/loopx/control_plane/testing/authority_e2e_row_support.py
+++ b/loopx/control_plane/testing/authority_e2e_row_support.py
@@ -59,7 +59,12 @@ def sha256_hex(value: str | bytes) -> str:
return hashlib.sha256(payload).hexdigest()
-def add_todo(workspace: GoalWorkspace, text: str) -> JsonObject:
+def add_todo(
+ workspace: GoalWorkspace,
+ text: str,
+ *,
+ claimed_by: str | None = None,
+) -> JsonObject:
return run_cli(
workspace,
"todo",
@@ -72,6 +77,7 @@ def add_todo(workspace: GoalWorkspace, text: str) -> JsonObject:
text,
"--task-class",
"advancement_task",
+ *(("--claimed-by", claimed_by) if claimed_by else ()),
)
diff --git a/loopx/control_plane/testing/authority_e2e_rows_stage2c.py b/loopx/control_plane/testing/authority_e2e_rows_stage2c.py
deleted file mode 100644
index 4090cc8b1d..0000000000
--- a/loopx/control_plane/testing/authority_e2e_rows_stage2c.py
+++ /dev/null
@@ -1,31 +0,0 @@
-"""Upgrade acceptance for the retired, post-commit observation lineage."""
-from __future__ import annotations
-
-from .authority_e2e_fixtures import build_goal_workspace, run_cli, unique_goal_id
-from .authority_e2e_row_support import RowContext, RowOutcome, add_todo, expect, passed
-from .authority_e2e_rows_stage2c2 import bootstrap_capture, delivered, goal_cli
-
-
-def row_retired_observation_upgrade(context: RowContext) -> RowOutcome:
- workspace = build_goal_workspace(context.root, goal_id=unique_goal_id("retired-observer"),
- handoff_mode="hard_lease", shadow_enabled=True,
- runtime_root_binding="cli_override")
- original = workspace.registry_path.read_bytes()
- rejected = goal_cli(workspace, "configure-goal", "--local-authority-shadow-file", "--execute", check=False)
- expect(rejected.get("ok") is False and "local_authority_shadow_retired" in str(rejected.get("error")),
- "retired enable must fail with actionable context")
- expect(workspace.registry_path.read_bytes() == original, "rejection must not change configuration")
- added = add_todo(workspace, "Continue work before explicit capture upgrade.")
- expect("authority_shadow" not in added, "a retained setting cannot restore observation writes")
- status = goal_cli(workspace, "authority-shadow", "status")
- expect(status["config"]["status"] == "retired" and status["config"]["enabled"] is False,
- "status must disclose the inactive historical configuration")
- expect(not (workspace.runtime_root / "authority-shadow").exists(), "primary work must create no shadow lineage")
- run_cli(workspace, "configure-goal", "--goal-id", workspace.goal_id,
- "--clear-local-authority-shadow", "--coordination-runtime-shadow-file", "--execute")
- expect(not (workspace.runtime_root / "authority-shadow" / "file-v0").exists(),
- "configuration must not implicitly bootstrap a source lineage")
- bootstrap_capture(workspace)
- delivered(add_todo(workspace, "Capture a transaction after explicit bootstrap."), label="upgraded write")
- expect(not workspace.shadow_directory.exists(), "replacement capture must not recreate historical observation storage")
- return passed(retired_enable_rejected=True, implicit_capture=False, explicit_bootstrap=True)
diff --git a/loopx/control_plane/testing/authority_e2e_rows_stage2c2.py b/loopx/control_plane/testing/authority_e2e_rows_stage2c2.py
deleted file mode 100644
index dfd6df21a4..0000000000
--- a/loopx/control_plane/testing/authority_e2e_rows_stage2c2.py
+++ /dev/null
@@ -1,1231 +0,0 @@
-"""Stage 2C parity-half rows (``s2c2.*``) of the shared-goal-authority ladder.
-
-Every row drives the product through the real ``python -m loopx.cli`` against
-one goal whose ``coordination.runtime_shadow`` capture is explicitly enabled
-and bootstrapped, and asserts only through shipped operator interfaces:
-``authority-shadow status|drain``, ``coordination-shadow bootstrap|inspect|
-qualify|read-candidate|rollback`` and ``migrate-state``. Candidate history is
-read back through the retained TypeScript store (the adapter's read RPC). The
-rows add no product path and never read the candidate for a decision.
-
-Two scheduling-only seams exist, both outside every product decision:
-
-* holding the stable maintenance lock defers a writer's post-commit drain, so
- its committed outbox entry stays pending (``drain_lock_busy``);
-* a POSIX crash worker pauses a real CLI process at one persistence window and
- the row SIGKILLs it there. It substitutes no result and edits no byte.
-"""
-
-from __future__ import annotations
-
-import json
-import select
-import subprocess
-import sys
-from collections.abc import Iterator, Mapping, Sequence
-from contextlib import contextmanager
-from dataclasses import dataclass, field
-from pathlib import Path
-
-from ...file_lock import exclusive_cross_runtime_file_lock
-from ..coordination import local_authority_shadow_outbox as shadow_outbox
-from .authority_e2e_fixtures import (
- REPO_ROOT,
- GoalWorkspace,
- JsonObject,
- build_goal_workspace,
- cli_env,
- node_executable,
- parse_json_object,
- run_cli,
- unique_goal_id,
-)
-from .authority_e2e_row_support import (
- AGENT_A,
- AGENT_B,
- RowContext,
- RowOutcome,
- acquire_lease,
- add_todo,
- expect,
- lease_version,
- passed,
-)
-
-RUNTIME_SHADOW_CONFIG_SCHEMA = "loopx_coordination_runtime_shadow_config_v0"
-RUNTIME_SHADOW_PROVIDER = "file_v0"
-CRASH_BARRIER_TIMEOUT_SECONDS = 30.0
-PRIMARY_CRASH_WINDOWS: tuple[tuple[str, bool], ...] = (
- ("before_replace", False),
- ("after_replace", True),
- ("before_marker", True),
-)
-DRAIN_CRASH_WINDOWS: tuple[str, ...] = ("before_commit", "after_commit", "after_cursor", "between_unlinks")
-PARITY_CYCLES = 3
-# ``todo archive-completed`` stays out of this shared cross-writer parity set
-# because it is a Python-only lifecycle writer with no TypeScript counterpart to
-# interleave; the archive-after-lease fold is pinned by its own deterministic row
-# ``s2c2.archive_after_leased_completion_parity`` instead.
-PARITY_REQUIRED_WRITE_CLASSES: tuple[str, ...] = (
- "todo_add",
- "todo_update",
- "todo_complete",
- "todo_supersede",
- "task_lease_acquire",
- "task_lease_renew",
- "task_lease_transfer",
- "task_lease_fence_close",
-)
-GROWTH_TRANSACTIONS = 10
-# The archive-after-lease row delivers six transactions: the add, the lease
-# acquire, the fenced complete (which also closes the lease fence), the anchor
-# add, and the archive that retires the completed Todo from the graph.
-ARCHIVE_PARITY_OPERATIONS = 6
-# The row's own coverage: the Todo add, the lease acquire, the fenced complete
-# with its lease fence close, and the archive-completed writer.
-ARCHIVE_PARITY_REQUIRED_WRITE_CLASSES: tuple[str, ...] = (
- "todo_add",
- "todo_complete",
- "todo_archive_completed",
- "task_lease_acquire",
- "task_lease_fence_close",
-)
-GROWTH_TEXT_TEMPLATE = "Growth workload todo %02d " + "x" * 160
-# Each file-v0 transaction retains the complete projection, so the per-transaction
-# byte delta may grow by about one Todo record per transaction. A larger jump
-# means something beyond the live projection is being re-published.
-GROWTH_DELTA_ACCELERATION_ENVELOPE_BYTES = 2048
-EVENT_ONLY_HOLD = "legacy_todo_event_source_retired"
-CONTINUITY_HOLD = "source_partition_continuity_unproved"
-SHADOW_READ_MODULE = Path("loopx") / "control_plane" / "coordination" / "local_authority_shadow.ts"
-SHADOW_READ_REQUEST_SCHEMA = "loopx_coordination_runtime_shadow_outbox_read_v0"
-SHADOW_READ_SCAN_LIMIT = 10_000
-
-# A real CLI process that pauses at exactly one persistence window. The parent
-# waits for the BARRIER line and SIGKILLs the child there. Every hook forwards
-# to the production function; nothing decides an outcome or substitutes bytes.
-CRASH_WORKER = r"""
-import json, pathlib, sys, time
-from loopx.cli import main
-from loopx.control_plane.coordination import local_authority_shadow_adapter as adapter
-from loopx.control_plane.coordination import local_authority_shadow_outbox as outbox
-from loopx.control_plane.runtime import document_io
-window, state = sys.argv[1], pathlib.Path(sys.argv[2])
-def pause(payload=None):
- print('BARRIER ' + json.dumps(payload or {}), flush=True)
- time.sleep(40)
- raise RuntimeError('parent failed to terminate at persistence barrier')
-actual_rpc = adapter.effect_runtime_result
-def rpc(method, request, **kwargs):
- if method == 'coordination.runtime_shadow.drain' and window in {'before_commit', 'after_commit', 'after_cursor', 'between_unlinks'}:
- import subprocess
- child = subprocess.Popen(['node', '--no-warnings', '--experimental-strip-types',
- 'loopx/control_plane/testing/shadow_drain_fault_process.ts', window],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
- child.stdin.write(json.dumps(request)); child.stdin.close()
- for line in child.stdout:
- if line.startswith('BARRIER '):
- print(line, end='', flush=True)
- sys.stdin.readline() # Parent requests death at the observed barrier.
- child.kill(); child.wait(timeout=10)
- print('REAPED', flush=True)
- time.sleep(40)
- raise RuntimeError('parent failed to terminate crash worker')
- raise RuntimeError('native barrier missing: ' + child.stderr.read())
- return actual_rpc(method, request, **kwargs)
-adapter.effect_runtime_result = rpc
-actual_json = outbox.durable_write_json
-def write_json(path, value):
- if window == 'before_marker' and path.name.endswith('.committed.json'): pause()
- return actual_json(path, value)
-outbox.durable_write_json = write_json
-actual_replace = document_io.os.replace
-def replace(source, target):
- is_primary = pathlib.Path(target) == state
- if is_primary and window == 'before_replace': pause()
- result = actual_replace(source, target)
- if is_primary and window == 'after_replace': pause()
- return result
-document_io.os.replace = replace
-actual_unlink = pathlib.Path.unlink
-def unlink(path, *args, **kwargs):
- result = actual_unlink(path, *args, **kwargs)
- if window == 'between_unlinks' and path.name.endswith('.prepared.json'): pause()
- return result
-pathlib.Path.unlink = unlink
-raise SystemExit(main(sys.argv[3:]))
-"""
-
-
-# ---------------------------------------------------------------------------
-# Workspace, CLI and read-back helpers
-# ---------------------------------------------------------------------------
-
-
-def _object(value: object, label: str) -> JsonObject:
- expect(isinstance(value, dict), f"{label} must be an object")
- assert isinstance(value, dict)
- return {str(key): item for key, item in value.items()}
-
-
-def _list(value: object, label: str) -> list[object]:
- expect(isinstance(value, list), f"{label} must be a list")
- assert isinstance(value, list)
- return list(value)
-
-
-def set_runtime_shadow(workspace: GoalWorkspace, *, enabled: bool) -> None:
- """The operator step: enable or disable transaction capture in the registry."""
-
- registry = parse_json_object(workspace.registry_path.read_text(encoding="utf-8"))
- goals = _list(registry.get("goals"), "registry goals")
- goal = _object(goals[0], "registry goal")
- coordination = _object(goal.get("coordination"), "goal coordination")
- coordination["runtime_shadow"] = {
- "schema_version": RUNTIME_SHADOW_CONFIG_SCHEMA,
- "enabled": enabled,
- "provider": RUNTIME_SHADOW_PROVIDER,
- }
- goal["coordination"] = coordination
- goals[0] = goal
- registry["goals"] = goals
- workspace.registry_path.write_text(json.dumps(registry), encoding="utf-8")
-
-
-def goal_cli(workspace: GoalWorkspace, *args: str, check: bool = True) -> JsonObject:
- return run_cli(workspace, *args, "--goal-id", workspace.goal_id, check=check)
-
-
-def bootstrap_capture(workspace: GoalWorkspace) -> JsonObject:
- """Bootstrap the capture lineage; returns the whole CLI payload."""
-
- payload = goal_cli(workspace, "coordination-shadow", "bootstrap", "--execute")
- bootstrap = _object(payload.get("bootstrap"), "bootstrap")
- expect(bootstrap.get("status") == "applied", "bootstrap must apply a fresh lineage")
- expect(bootstrap.get("cursor") == "1", "bootstrap must be the first transaction")
- expect(payload.get("decision_read_from_shadow") is False, "bootstrap must not read the candidate for a decision")
- return payload
-
-
-def capture_workspace(
- context: RowContext,
- prefix: str,
- *,
- handoff_mode: str = "hard_lease",
- bootstrap: bool = True,
-) -> GoalWorkspace:
- workspace = build_goal_workspace(
- context.root,
- goal_id=unique_goal_id(prefix),
- handoff_mode=handoff_mode,
- shadow_enabled=False,
- runtime_root_binding="cli_override",
- )
- set_runtime_shadow(workspace, enabled=True)
- if bootstrap:
- bootstrap_capture(workspace)
- return workspace
-
-
-def capture_evidence(payload: Mapping[str, object], *, label: str) -> JsonObject:
- evidence = _object(payload.get("coordination_runtime_shadow"), f"{label} capture evidence")
- expect(evidence.get("primary_writeback_preserved") is True, f"{label} must preserve the primary writeback")
- expect(evidence.get("provider_to_local_writes") is False, f"{label} must never write from provider to local state")
- expect(evidence.get("candidate_read_for_decision") is False, f"{label} must never read the candidate for a decision")
- expect(evidence.get("parity_verdict") == "not_evaluated", f"{label} must not claim parity on the write path")
- return evidence
-
-
-def delivered(payload: Mapping[str, object], *, label: str) -> JsonObject:
- """A primary write whose outbox entry was drained inline into the candidate."""
-
- evidence = capture_evidence(payload, label=label)
- expect(evidence.get("outcome") == "delivered", f"{label} must deliver its outbox entry")
- expect(
- evidence.get("source_transaction_correlated") is True and evidence.get("durable_source_outbox") is True,
- f"{label} must correlate a durable outbox entry",
- )
- drain = _object(evidence.get("drain"), f"{label} drain")
- expect(drain.get("outcome") == "drained" and drain.get("candidate_readback_verified") is True, f"{label} must verify its candidate read-back")
- return evidence
-
-
-def no_transaction(payload: Mapping[str, object], *, label: str) -> JsonObject:
- evidence = capture_evidence(payload, label=label)
- expect(evidence.get("outcome") == "no_transaction", f"{label} must record no transaction")
- expect(_object(evidence.get("entry"), f"{label} entry").get("entry_id") is None, f"{label} must mint no outbox entry")
- return evidence
-
-
-def deferred(payload: Mapping[str, object], *, label: str) -> JsonObject:
- evidence = capture_evidence(payload, label=label)
- expect(
- evidence.get("outcome") == "drain_deferred" and evidence.get("reason_code") == "drain_lock_busy",
- f"{label} must defer its drain behind the held maintenance lock",
- )
- expect(
- evidence.get("source_transaction_correlated") is True and evidence.get("durable_source_outbox") is True,
- f"{label} must still correlate a durable outbox entry",
- )
- return evidence
-
-
-def shadow_status(workspace: GoalWorkspace) -> JsonObject:
- return goal_cli(workspace, "authority-shadow", "status", check=False)
-
-
-def backlog(status: Mapping[str, object], partition: str) -> JsonObject:
- return _object(_object(status.get("outbox"), "status outbox").get(partition), f"{partition} backlog")
-
-
-def candidate(status: Mapping[str, object]) -> JsonObject:
- return _object(status.get("candidate"), "status candidate")
-
-
-def management_status(status: Mapping[str, object]) -> str:
- return str(_object(status.get("management"), "status management").get("status"))
-
-
-def drain(workspace: GoalWorkspace, *flags: str) -> JsonObject:
- return goal_cli(workspace, "authority-shadow", "drain", *flags, check=False)
-
-
-def inspect(workspace: GoalWorkspace) -> JsonObject:
- payload = goal_cli(workspace, "coordination-shadow", "inspect", check=False)
- expect(payload.get("decision_read_from_shadow") is False, "inspect must not read the candidate for a decision")
- return payload
-
-
-def qualify(workspace: GoalWorkspace, *flags: str) -> JsonObject:
- payload = goal_cli(workspace, "coordination-shadow", "qualify", *flags, check=False)
- expect(payload.get("decision_read_from_shadow") is False, "qualify must not read the candidate for a decision")
- return payload
-
-
-def read_candidate(workspace: GoalWorkspace, todo_id: str) -> JsonObject:
- payload = goal_cli(workspace, "coordination-shadow", "read-candidate", "--todo-id", todo_id, check=False)
- expect(payload.get("decision_read_from_shadow") is False, "read-candidate must not read the candidate for a decision")
- return payload
-
-
-def qualified(payload: Mapping[str, object], *, label: str) -> JsonObject:
- qualification = _object(payload.get("qualification"), f"{label} qualification")
- expect(payload.get("ok") is True and qualification.get("status") == "qualified", f"{label} must qualify")
- expect(qualification.get("parity_matches") is True and qualification.get("scope") == "bounded", f"{label} must be a bounded parity match")
- expect(
- qualification.get("sustained_parity_verified") is False and qualification.get("sustained_parity_verdict") == "not_evaluated",
- f"{label} must not claim sustained parity",
- )
- return qualification
-
-
-def rejected(payload: Mapping[str, object], key: str, *, label: str) -> JsonObject:
- result = _object(payload.get(key), f"{label} result")
- expect(payload.get("ok") is False and result.get("qualified") is False, f"{label} must be rejected")
- return result
-
-
-def history(workspace: GoalWorkspace) -> list[JsonObject]:
- """Complete candidate history through the TypeScript shadow read over the retained store.
-
- The read runs in an independent node process so the row never imports the
- Python adapter; it is the same ``readLocalAuthorityShadow`` the product
- uses for status and drain proof.
- """
-
- node = node_executable()
- expect(node is not None, "node is required to read the candidate history")
- assert node is not None
- request = {
- "schema_version": SHADOW_READ_REQUEST_SCHEMA,
- "runtime_root": str(workspace.runtime_root),
- "goal_id": workspace.goal_id,
- "scan_limit": SHADOW_READ_SCAN_LIMIT,
- }
- request_path = workspace.home / "shadow-read-request.json"
- request_path.write_text(json.dumps(request), encoding="utf-8")
- script = (
- f"import {{ readLocalAuthorityShadow }} from {json.dumps((REPO_ROOT / SHADOW_READ_MODULE).as_uri())};"
- "import { readFile } from 'node:fs/promises';"
- "process.stdout.write(JSON.stringify(await readLocalAuthorityShadow(JSON.parse(await readFile(process.argv[1], 'utf8')))));"
- )
- completed = subprocess.run(
- [node, "--no-warnings", "--experimental-strip-types", "--input-type=module", "-e", script, str(request_path)],
- cwd=REPO_ROOT,
- capture_output=True,
- text=True, encoding="utf-8", errors="replace",
- timeout=60,
- check=False,
- )
- expect(completed.returncode == 0, f"the TypeScript shadow read exited {completed.returncode}")
- view = parse_json_object(completed.stdout)
- expect(view.get("status") == "loaded", "candidate history must load through the TypeScript store")
- proof = _object(view.get("proof"), "history proof")
- return [_object(item, "transaction") for item in _list(proof.get("transactions"), "history transactions")]
-
-
-def single_receipt(transaction: Mapping[str, object], *, label: str) -> JsonObject:
- receipts = _list(transaction.get("receipts"), f"{label} receipts")
- expect(len(receipts) == 1, f"{label} must carry exactly one receipt")
- return _object(receipts[0], f"{label} receipt")
-
-
-def committed_receipt(transaction: Mapping[str, object], *, label: str) -> JsonObject:
- receipt = single_receipt(transaction, label=label)
- expect(receipt.get("resolution") == "committed" and receipt.get("no_op") is False, f"{label} must be a committed, effective receipt")
- return receipt
-
-
-def projected_todo_count(transaction: Mapping[str, object]) -> int:
- projection = _object(transaction.get("projection"), "transaction projection")
- todos = projection.get("todos")
- return len(todos) if isinstance(todos, list) else 0
-
-
-def partition_files(workspace: GoalWorkspace, partition: str) -> list[str]:
- directory = shadow_outbox.partition_directory(workspace.runtime_root, workspace.goal_id, partition)
- return sorted(path.name for path in directory.iterdir()) if directory.is_dir() else []
-
-
-def phase_count(names: Sequence[str], phase: str) -> int:
- return sum(1 for name in names if name.endswith(f".{phase}.json"))
-
-
-def outbox_present(workspace: GoalWorkspace) -> bool:
- return shadow_outbox.outbox_root(workspace.runtime_root, workspace.goal_id).exists()
-
-
-def store_documents(workspace: GoalWorkspace) -> list[Path]:
- return sorted((workspace.runtime_root / "authority-shadow" / "file-v0").glob("authority-store-*.json"))
-
-
-def todo_count(workspace: GoalWorkspace) -> int:
- listed = goal_cli(workspace, "todo", "list")
- return len(_list(listed.get("todos"), "todo list"))
-
-
-@contextmanager
-def hold_drain_lock(workspace: GoalWorkspace) -> Iterator[None]:
- """Hold the maintenance lock so writers defer their post-commit drain.
-
- The native batch takes the TypeScript mutation marker, so the window must
- hold the same cross-runtime lock the production readers take; a kernel-only
- flock would no longer exclude it.
- """
-
- with exclusive_cross_runtime_file_lock(
- shadow_outbox.drain_lock_target(workspace.runtime_root, workspace.goal_id),
- operation="e2e_window",
- ):
- yield
-
-
-def crash_cli(workspace: GoalWorkspace, window: str, *args: str) -> None:
- """Run one real CLI command and SIGKILL it at ``window``."""
-
- command = [sys.executable, "-c", CRASH_WORKER, window, str(workspace.state_path), *workspace.cli_prefix(), *args, "--goal-id", workspace.goal_id]
- process = subprocess.Popen(
- command,
- cwd=REPO_ROOT,
- env=cli_env(workspace),
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True, encoding="utf-8", errors="replace",
- )
- line = ""
- try:
- assert process.stdout is not None
- readable, _, _ = select.select([process.stdout], [], [], CRASH_BARRIER_TIMEOUT_SECONDS)
- if readable:
- line = process.stdout.readline()
- finally:
- if line.startswith("BARRIER "):
- barrier = json.loads(line.removeprefix("BARRIER "))
- if barrier.get("native_pid"):
- assert process.stdin is not None and process.stdout is not None
- process.stdin.write("terminate_native\n")
- process.stdin.flush()
- readable, _, _ = select.select([process.stdout], [], [], 10)
- expect(bool(readable) and process.stdout.readline().strip() == "REAPED", "native owner must be reaped")
- process.kill()
- _, stderr = process.communicate(timeout=10)
- expect(line.startswith("BARRIER "), f"{window}: the CLI did not reach its persistence window: {stderr[-200:]}")
- expect(process.returncode == -9, f"{window}: the crash worker must die by SIGKILL")
-
-
-# ---------------------------------------------------------------------------
-# Rows
-# ---------------------------------------------------------------------------
-
-
-def row_outbox_prepared_then_committed_entries(context: RowContext) -> RowOutcome:
- """A Python Todo writer and a TypeScript lease writer each leave one prepared record and committed marker; drain delivers both once."""
-
- workspace = capture_workspace(context, "ladder-outbox")
- with hold_drain_lock(workspace):
- added = add_todo(workspace, "Prepared and committed while the drain lock is held.")
- expect(added.get("added") is True, "the todo must commit while its drain is deferred")
- todo_id = str(added["todo_id"])
- acquired = acquire_lease(workspace, todo_id=todo_id, owner=AGENT_A, idempotency_key="ladder-outbox-lease")
- expect(acquired.get("acquired") is True, "the lease must commit while its drain is deferred")
- entries = [
- _object(deferred(added, label="todo add").get("entry"), "todo entry"),
- _object(deferred(acquired, label="task-lease acquire").get("entry"), "lease entry"),
- ]
- expect([entry.get("partition") for entry in entries] == ["todos", "leases"], "the writers must target their own partitions")
- expect(all(entry.get("seq") == 1 for entry in entries), "each partition must start at sequence 1")
- status = shadow_status(workspace)
- for partition in ("todos", "leases"):
- pending = backlog(status, partition)
- expect(
- pending.get("committed_pending") == 1 and pending.get("prepared_only") == 0 and pending.get("cursor_last_seq") is None,
- f"{partition} must hold exactly one committed, undrained entry",
- )
- names = partition_files(workspace, partition)
- expect(
- phase_count(names, "prepared") == 1 and phase_count(names, "committed") == 1,
- f"{partition} must retain its prepared record and committed marker",
- )
- expect(todo_count(workspace) == 1, "the primary must show the committed todo while its entry is pending")
- drained = drain(workspace)
- expect(
- drained.get("ok") is True and drained.get("outcome") == "drained" and drained.get("delivered") == 2 and drained.get("replayed") == 0,
- "one drain must deliver both pending entries exactly once",
- )
- settled = [_object(item, "drained entry") for item in _list(drained.get("entries"), "drain entries")]
- expect(
- all(item.get("outcome") == "delivered" and item.get("resolution") == "committed" for item in settled),
- "every drained entry must be a committed delivery",
- )
- status = shadow_status(workspace)
- for partition in ("todos", "leases"):
- pending = backlog(status, partition)
- expect(pending.get("committed_pending") == 0 and pending.get("prepared_only") == 0 and pending.get("cursor_last_seq") == 1, f"{partition} must be drained to sequence 1")
- expect(partition_files(workspace, partition) == ["drain-cursor.json"], f"{partition} must retain only its cursor after delivery")
- transactions = history(workspace)
- expect(len(transactions) == 3 and transactions[0].get("receipts") == [] and transactions[0].get("cursor") == "1", "history must be the bootstrap plus two deliveries")
- receipts = [committed_receipt(transaction, label=f"transaction {index}") for index, transaction in enumerate(transactions[1:], start=2)]
- runtimes = sorted({str(receipt.get("writer_runtime")) for receipt in receipts})
- classes = sorted({str(receipt.get("write_class")) for receipt in receipts})
- expect(runtimes == ["python", "typescript"], "the deliveries must come from both writer runtimes")
- expect(classes == ["task_lease_acquire", "todo_add"], "the deliveries must carry their write classes")
- inline = delivered(add_todo(workspace, "Delivered inline once the lock is free."), label="todo add (inline)")
- expect(_object(inline.get("drain"), "inline drain").get("last_cursor") == "4", "an inline delivery must continue the same lineage")
- return passed(
- deferred_entries=2,
- partitions=["leases", "todos"],
- drained=2,
- writer_runtimes=runtimes,
- write_classes=classes,
- inline_cursor="4",
- )
-
-
-def row_drain_idempotent(context: RowContext) -> RowOutcome:
- """Bounded drains are cumulative, an idle drain changes nothing, and a writer replay mints no entry."""
-
- workspace = capture_workspace(context, "ladder-drain")
- with hold_drain_lock(workspace):
- for index in range(3):
- deferred(add_todo(workspace, f"Deferred todo {index}."), label=f"todo add {index}")
- pending = backlog(shadow_status(workspace), "todos")
- expect(pending.get("committed_pending") == 3 and pending.get("next_seq") == 3, "three committed entries must be pending")
- first = drain(workspace, "--max-entries", "1")
- expect(
- first.get("ok") is True and first.get("delivered") == 1 and first.get("replayed") == 0 and first.get("pending_after") == 2 and first.get("budget_exhausted") is True,
- "a bounded drain must deliver one entry and preserve the rest",
- )
- expect(first.get("cursor_after") == "2", "the bounded drain must advance the candidate by one transaction")
- rest = drain(workspace)
- expect(rest.get("ok") is True and rest.get("delivered") == 2 and rest.get("replayed") == 0 and rest.get("pending_after") == 0, "the next drain must deliver the remaining entries")
- expect(rest.get("cursor_after") == "4", "the second drain must finish at cursor 4")
- idle = drain(workspace)
- expect(
- idle.get("ok") is True and idle.get("outcome") == "nothing_pending" and idle.get("delivered") == 0 and idle.get("replayed") == 0,
- "an idle drain must deliver and replay nothing",
- )
- expect(
- idle.get("cursor_before") == "4" and idle.get("cursor_after") == "4"
- and idle.get("head_digest") == rest.get("head_digest") and idle.get("provider_revision") == rest.get("provider_revision"),
- "an idle drain must leave the candidate head unchanged",
- )
- after = backlog(shadow_status(workspace), "todos")
- expect(after.get("committed_pending") == 0 and after.get("cursor_last_seq") == 3, "the backlog must be empty at sequence 3")
- expect(partition_files(workspace, "todos") == ["drain-cursor.json"], "only the cursor may remain")
- transactions = history(workspace)
- expect(len(transactions) == 4, "history must hold the bootstrap plus three deliveries")
- sequences = [committed_receipt(transaction, label=f"transaction {index}").get("seq") for index, transaction in enumerate(transactions[1:], start=2)]
- expect(sequences == [1, 2, 3], "receipts must settle the outbox sequences in order")
- listed = _list(goal_cli(workspace, "todo", "list").get("todos"), "todo list")
- todo_id = str(_object(listed[0], "listed todo")["todo_id"])
- acquired = delivered(
- acquire_lease(workspace, todo_id=todo_id, owner=AGENT_A, idempotency_key="ladder-drain-lease"),
- label="task-lease acquire",
- )
- replayed = acquire_lease(workspace, todo_id=todo_id, owner=AGENT_A, idempotency_key="ladder-drain-lease")
- expect(replayed.get("idempotent") is True, "a same-key re-acquire must be idempotent")
- expect("coordination_runtime_shadow" not in replayed, "an idempotent replay must not capture")
- expect(len(history(workspace)) == 5, "a writer replay must not add a candidate transaction")
- expect(backlog(shadow_status(workspace), "leases").get("committed_pending") == 0, "a writer replay must leave no pending entry")
- return passed(
- bounded_first_pass=1,
- second_pass=2,
- idle_pass_delivered=0,
- head_digest_stable=True,
- lease_partition_cursor=str(_object(acquired.get("drain"), "lease drain").get("last_cursor")),
- transactions=5,
- )
-
-
-def row_sigkill_between_primary_write_and_drain(context: RowContext) -> RowOutcome:
- """A writer killed around its primary replace leaves a prepared-only entry that drain settles from the primary bytes."""
-
- workspace = capture_workspace(context, "ladder-primary-crash")
- resolutions: dict[str, str] = {}
- visible = 0
- for index, (window, replaced) in enumerate(PRIMARY_CRASH_WINDOWS, start=1):
- crash_cli(workspace, window, "todo", "add", "--role", "agent", "--text", f"Primary write killed at {window}.", "--task-class", "advancement_task")
- visible += 1 if replaced else 0
- expect(todo_count(workspace) == visible, f"{window}: the primary must show exactly the replaced writes")
- pending = backlog(shadow_status(workspace), "todos")
- expect(pending.get("committed_pending") == 0 and pending.get("prepared_only") == 1, f"{window}: one prepared-only entry must remain")
- expect(phase_count(partition_files(workspace, "todos"), "committed") == 0, f"{window}: no committed marker may exist")
- drained = drain(workspace)
- expect(
- drained.get("ok") is True and drained.get("delivered") == 1 and drained.get("replayed") == 0 and drained.get("no_op") == (0 if replaced else 1),
- f"{window}: drain must settle the entry from the primary bytes",
- )
- transactions = history(workspace)
- expect(len(transactions) == index + 1, f"{window}: every settled entry must be one transaction")
- receipt = single_receipt(transactions[-1], label=window)
- resolution = str(receipt.get("resolution"))
- expect(
- resolution == ("committed_proven_by_readback" if replaced else "abandoned") and receipt.get("no_op") is (not replaced),
- f"{window}: the receipt must prove the primary outcome",
- )
- expect(projected_todo_count(transactions[-1]) == visible, f"{window}: the candidate must equal the primary after recovery")
- after = backlog(shadow_status(workspace), "todos")
- expect(after.get("prepared_only") == 0 and after.get("cursor_last_seq") == index, f"{window}: the cursor must settle sequence {index}")
- resolutions[window] = resolution
- inspection = _object(inspect(workspace).get("inspection"), "inspection")
- expect(inspection.get("status") == "matched" and inspection.get("parity_matches") is True, "recovered history must match the primary")
- return passed(windows=resolutions, primary_todos=visible, transactions=len(PRIMARY_CRASH_WINDOWS) + 1, parity="matched")
-
-
-def row_sigkill_mid_drain(context: RowContext) -> RowOutcome:
- """A writer killed inside its inline drain is recovered by the next drain from exact receipts, never delivered twice."""
-
- outcomes: dict[str, JsonObject] = {}
- for window in DRAIN_CRASH_WINDOWS:
- workspace = capture_workspace(context, "ladder-drain-crash")
- crash_cli(workspace, window, "todo", "add", "--role", "agent", "--text", f"Drain killed at {window}.", "--task-class", "advancement_task")
- expect(todo_count(workspace) == 1, f"{window}: the primary write must be committed")
- before = backlog(shadow_status(workspace), "todos")
- drained = drain(workspace)
- expected_delivered = 1 if window == "before_commit" else 0
- expect(
- drained.get("ok") is True and drained.get("outcome") == "drained"
- and drained.get("delivered") == expected_delivered and drained.get("replayed") == 1 - expected_delivered,
- f"{window}: recovery must deliver an uncommitted entry once and replay a committed one",
- )
- transactions = history(workspace)
- expect(len(transactions) == 2, f"{window}: the candidate must hold exactly one delivery")
- committed_receipt(transactions[1], label=window)
- expect(projected_todo_count(transactions[1]) == 1, f"{window}: the delivery must project the committed todo")
- after = backlog(shadow_status(workspace), "todos")
- expect(after.get("committed_pending") == 0 and after.get("prepared_only") == 0 and after.get("cursor_last_seq") == 1, f"{window}: the backlog must settle at sequence 1")
- expect(partition_files(workspace, "todos") == ["drain-cursor.json"], f"{window}: only the cursor may remain")
- idle = drain(workspace)
- expect(idle.get("outcome") == "nothing_pending" and idle.get("delivered") == 0 and idle.get("replayed") == 0, f"{window}: a further drain must be idle")
- outcomes[window] = {
- "delivered": drained.get("delivered"),
- "replayed": drained.get("replayed"),
- "committed_pending_before": before.get("committed_pending"),
- "backlog_invalid_before": before.get("invalid"),
- }
- return passed(windows=outcomes)
-
-
-def row_rollback_with_pending_entries(context: RowContext) -> RowOutcome:
- """Rollback archives the outbox with its pending entries, holds capture until an explicit rebootstrap, and replays."""
-
- workspace = capture_workspace(context, "ladder-rollback", bootstrap=False)
- first = _object(bootstrap_capture(workspace).get("bootstrap"), "first bootstrap")
- delivered(add_todo(workspace, "Delivered before rollback."), label="todo add")
- with hold_drain_lock(workspace):
- deferred(add_todo(workspace, "Committed but not yet drained."), label="todo add (deferred)")
- crash_cli(workspace, "before_replace", "todo", "add", "--role", "agent", "--text", "Prepared only.", "--task-class", "advancement_task")
- status = shadow_status(workspace)
- pending = backlog(status, "todos")
- expect(pending.get("committed_pending") == 1 and pending.get("prepared_only") == 1 and pending.get("cursor_last_seq") == 1, "one committed and one prepared-only entry must be pending")
- current = candidate(status)
- revision = str(current.get("provider_revision"))
- expect(current.get("status") == "loaded" and current.get("cursor") == "2", "the candidate must be at cursor 2")
- inspection = _object(inspect(workspace).get("inspection"), "inspection")
- expect(
- inspection.get("qualified") is False and inspection.get("reason_code") == "outbox_pending" and inspection.get("provider_revision") == revision,
- "pending entries must block qualification and name the exact revision",
- )
- preview = goal_cli(workspace, "coordination-shadow", "rollback", "--provider-revision", revision)
- expect(preview.get("ok") is True and preview.get("executed") is False and "rollback" not in preview, "a rollback preview must not execute")
- expect(outbox_present(workspace), "a preview must leave the outbox in place")
- executed = goal_cli(workspace, "coordination-shadow", "rollback", "--provider-revision", revision, "--execute")
- rollback = _object(executed.get("rollback"), "rollback")
- expect(
- rollback.get("status") == "applied" and rollback.get("active_shadow_removed") is True and rollback.get("archive_retained") is True,
- "rollback must retire the active candidate into a retained archive",
- )
- expect(rollback.get("archived_cursor") == "2" and rollback.get("archived_provider_revision") == revision, "the archive must bind the exact revision")
- archive = rollback.get("outbox_archive_path")
- expect(isinstance(archive, str) and Path(archive).is_dir(), "rollback must archive the goal outbox")
- assert isinstance(archive, str)
- archived = sorted(path.name for path in Path(archive).rglob("*") if path.is_file())
- expect(
- phase_count(archived, "prepared") == 2 and phase_count(archived, "committed") == 1
- and "drain-cursor.json" in archived and "manifest.json" in archived,
- "the archive must preserve pending entries, the marker, the cursor and the manifest",
- )
- expect(not outbox_present(workspace), "the active outbox must be moved, not copied")
- status = shadow_status(workspace)
- expect(management_status(status) == "inactive" and candidate(status).get("status") == "missing", "management must be inactive with no candidate")
- expect(backlog(status, "todos").get("committed_pending") == 0 and backlog(status, "todos").get("prepared_only") == 0, "no backlog may remain")
- after = add_todo(workspace, "Primary write after rollback.")
- expect(after.get("added") is True, "primary writes must resume after rollback")
- held = capture_evidence(after, label="todo add (after rollback)")
- expect(held.get("outcome") == "no_transaction" and held.get("reason_code") == "bootstrap_required", "capture must report bootstrap_required, not a transaction")
- rebootstrap = bootstrap_capture(workspace)
- second = _object(rebootstrap.get("bootstrap"), "second bootstrap")
- expect(second.get("capture_lineage_id") != first.get("capture_lineage_id"), "a rebootstrap must start a new lineage")
- summary = _object(rebootstrap.get("projection_summary"), "projection summary")
- expect(summary.get("todo_count") == 3 and summary.get("lease_count") == 0, "the new baseline must import the current primary")
- replay = _object(goal_cli(workspace, "coordination-shadow", "rollback", "--provider-revision", revision, "--execute").get("rollback"), "rollback replay")
- expect(
- replay.get("status") == "replayed" and replay.get("current_capture_lineage_id") == second.get("capture_lineage_id"),
- "a replayed rollback must return its historical result against the new lineage",
- )
- resumed = delivered(add_todo(workspace, "Captured in the new lineage."), label="todo add (new lineage)")
- expect(_object(resumed.get("drain"), "drain").get("last_cursor") == "2", "the new lineage must capture from cursor 2")
- transactions = history(workspace)
- expect(len(transactions) == 2 and transactions[0].get("receipts") == [] and projected_todo_count(transactions[0]) == 3, "the new history must start from the imported baseline")
- return passed(
- pending_before_rollback={"committed": 1, "prepared_only": 1},
- archived_files=len(archived),
- archived_prepared=2,
- archived_committed=1,
- capture_after_rollback="bootstrap_required",
- rebootstrap_baseline_todos=3,
- new_lineage=True,
- replayed_rollback=True,
- )
-
-
-@dataclass
-class _MixedWriterLedger:
- workspace: GoalWorkspace
- mutations: list[tuple[str, JsonObject]] = field(default_factory=list)
- deliveries: int = 0
- no_change_writes: int = 0
-
- def cli(self, *args: str) -> JsonObject:
- return goal_cli(self.workspace, *args)
-
- def mutate(self, label: str, payload: JsonObject, *, flag: str) -> JsonObject:
- expect(payload.get(flag) is True, f"{label} must report {flag}=true")
- evidence = delivered(payload, label=label)
- # A leased complete or supersede also closes its lease fence, so one
- # CLI call may deliver two transactions; count what the drain reports.
- count = _object(evidence.get("drain"), f"{label} drain").get("delivered")
- expect(isinstance(count, int) and count >= 1, f"{label} must deliver at least one transaction")
- assert isinstance(count, int)
- self.deliveries += count
- self.mutations.append((label, evidence))
- return payload
-
- def unchanged(self, label: str, payload: JsonObject) -> None:
- expect(payload.get("changed") is False, f"{label} must report changed=false")
- no_transaction(payload, label=label)
- self.no_change_writes += 1
-
-
-def _mixed_writer_cycle(ledger: _MixedWriterLedger, cycle: int) -> None:
- """One cycle of interleaved Python Markdown writers and TypeScript lease writers."""
-
- workspace = ledger.workspace
- key_a, key_b, key_c = (f"ladder-parity-{cycle}-{suffix}" for suffix in ("a", "b", "c"))
- first = ledger.mutate("todo add", add_todo(workspace, f"Cycle {cycle}: deliver one bounded change."), flag="added")
- todo_id = str(first["todo_id"])
- acquired = ledger.mutate("task-lease acquire", acquire_lease(workspace, todo_id=todo_id, owner=AGENT_A, idempotency_key=key_a), flag="acquired")
- note = ["todo", "update", "--todo-id", todo_id, "--note", f"Cycle {cycle} note.", "--agent-id", AGENT_A]
- ledger.mutate("todo update (note)", ledger.cli(*note), flag="changed")
- ledger.unchanged("todo update (same note)", ledger.cli(*note))
- renewed = ledger.mutate(
- "task-lease renew",
- ledger.cli(
- "task-lease", "renew", "--todo-id", todo_id, "--owner", AGENT_A, "--idempotency-key", key_a,
- "--expected-version", lease_version(acquired, label="acquire"), "--ttl-seconds", "120",
- ),
- flag="renewed",
- )
- transferred = ledger.mutate(
- "task-lease transfer",
- ledger.cli(
- "task-lease", "transfer", "--todo-id", todo_id, "--owner", AGENT_A, "--idempotency-key", key_a,
- "--new-owner", AGENT_B, "--new-idempotency-key", key_b,
- "--expected-version", lease_version(renewed, label="renew"), "--ttl-seconds", "120",
- ),
- flag="transferred",
- )
- ledger.mutate(
- "todo complete",
- ledger.cli(
- "todo", "complete", "--todo-id", todo_id, "--agent-id", AGENT_B,
- "--task-lease-idempotency-key", key_b, "--task-lease-expected-version", lease_version(transferred, label="transfer"),
- "--evidence", "validation://ladder-parity", "--no-follow-up",
- ),
- flag="completed",
- )
- second = ledger.mutate("todo add (second)", add_todo(workspace, f"Cycle {cycle}: replace this work with a successor."), flag="added")
- second_id = str(second["todo_id"])
- ledger.mutate(
- "todo update (explicit exclusion)",
- ledger.cli("todo", "update", "--todo-id", second_id, "--excluded-agent", AGENT_B, "--agent-id", AGENT_A),
- flag="changed",
- )
- clear = ["todo", "update", "--todo-id", second_id, "--clear-excluded-agents", "--agent-id", AGENT_A]
- ledger.mutate("todo update (explicit clear)", ledger.cli(*clear), flag="changed")
- ledger.unchanged("todo update (clear again)", ledger.cli(*clear))
- held = ledger.mutate("task-lease acquire (second)", acquire_lease(workspace, todo_id=second_id, owner=AGENT_A, idempotency_key=key_c), flag="acquired")
- ledger.mutate(
- "todo supersede",
- ledger.cli(
- "todo", "supersede", "--todo-id", second_id, "--agent-id", AGENT_A, "--reason", "Replace obsolete work.",
- "--next-agent-todo", f"Cycle {cycle}: carry the bounded work forward.",
- "--task-lease-idempotency-key", key_c, "--task-lease-expected-version", lease_version(held, label="acquire (second)"),
- ),
- flag="superseded",
- )
- ledger.mutate(
- "todo add (verification)",
- add_todo(workspace, f"Cycle {cycle}: verify the captured projection."),
- flag="added",
- )
-
-
-def _assert_bounded_parity(ledger: _MixedWriterLedger, cycle: int, anchor_todo_id: str) -> JsonObject:
- workspace = ledger.workspace
- inspection = _object(inspect(workspace).get("inspection"), f"cycle {cycle} inspection")
- expect(
- inspection.get("status") == "matched" and inspection.get("parity_matches") is True,
- f"cycle {cycle}: the candidate head must match the primary "
- f"(status={inspection.get('status')!r}, reason_code={inspection.get('reason_code')!r})",
- )
- flags = ["--minimum-operations", str(ledger.deliveries)]
- for write_class in PARITY_REQUIRED_WRITE_CLASSES:
- flags.extend(["--require-event-kind", write_class])
- qualification = qualified(qualify(workspace, *flags), label=f"cycle {cycle}")
- evidence = _object(qualification.get("evidence"), f"cycle {cycle} evidence")
- expect(evidence.get("operation_count") == ledger.deliveries, f"cycle {cycle}: every delivered transaction must count as one verified operation")
- expect(evidence.get("missing_required_event_kinds") == [] and evidence.get("pending_outbox") is False, f"cycle {cycle}: coverage must be complete with nothing pending")
- read = read_candidate(workspace, anchor_todo_id)
- result = _object(read.get("read_candidate"), f"cycle {cycle} read-candidate")
- expect(
- read.get("ok") is True and result.get("status") == "matched" and result.get("read_candidate_qualified") is True,
- f"cycle {cycle}: a qualified read must return the anchor todo from the verified head",
- )
- return qualification
-
-
-def row_parity_equal(context: RowContext) -> RowOutcome:
- """Sustained interleaving of Python and TypeScript writers keeps every bounded qualification matched."""
-
- workspace = capture_workspace(context, "ladder-parity")
- ledger = _MixedWriterLedger(workspace)
- anchor = ledger.mutate("todo add (anchor)", add_todo(workspace, "Anchor todo that stays open for qualified reads."), flag="added")
- anchor_todo_id = str(anchor["todo_id"])
- qualification: JsonObject = {}
- for cycle in range(1, PARITY_CYCLES + 1):
- _mixed_writer_cycle(ledger, cycle)
- qualification = _assert_bounded_parity(ledger, cycle, anchor_todo_id)
- transactions = history(workspace)
- expect(len(transactions) == ledger.deliveries + 1, "history must hold the bootstrap plus one transaction per delivery")
- receipts = [committed_receipt(transaction, label=f"transaction {index}") for index, transaction in enumerate(transactions[1:], start=2)]
- runtimes = sorted({str(receipt.get("writer_runtime")) for receipt in receipts})
- classes = sorted({str(receipt.get("write_class")) for receipt in receipts})
- expect(runtimes == ["python", "typescript"], "both writer runtimes must appear in one lineage")
- expect(set(PARITY_REQUIRED_WRITE_CLASSES) <= set(classes), "every required write class must be captured")
- expect(len({str(receipt.get("entry_id")) for receipt in receipts}) == len(receipts), "every receipt must carry a distinct entry id")
- return passed(
- cycles=PARITY_CYCLES,
- mutations=len(ledger.mutations),
- deliveries=ledger.deliveries,
- no_change_writes=ledger.no_change_writes,
- writer_runtimes=runtimes,
- write_classes=classes,
- final_cursor=str(qualification.get("cursor")),
- operation_count=_object(qualification.get("evidence"), "evidence").get("operation_count"),
- sustained_parity_verdict=str(qualification.get("sustained_parity_verdict")),
- )
-
-
-def _recover_by_rollback_and_rebootstrap(workspace: GoalWorkspace, *, label: str) -> JsonObject:
- """The documented recovery for a held lineage: exact-revision rollback, then a fresh bootstrap."""
-
- revision = str(candidate(shadow_status(workspace)).get("provider_revision"))
- rollback = _object(goal_cli(workspace, "coordination-shadow", "rollback", "--provider-revision", revision, "--execute").get("rollback"), f"{label} rollback")
- expect(rollback.get("status") == "applied" and rollback.get("archive_retained") is True, f"{label}: rollback must retire the held lineage into an archive")
- rebootstrap = bootstrap_capture(workspace)
- for index in range(3):
- delivered(add_todo(workspace, f"{label} recovery write {index}."), label=f"{label} recovery write {index}")
- qualified(qualify(workspace), label=f"{label} after recovery")
- return _object(rebootstrap.get("projection_summary"), f"{label} rebootstrap summary")
-
-
-def row_parity_divergent_detects_foreign_edit(context: RowContext) -> RowOutcome:
- """A direct edit of the primary is detected as drift, holds later captures, and is recovered only by rollback and rebootstrap."""
-
- workspace = capture_workspace(context, "ladder-drift")
- todo_ids: list[str] = []
- for index in range(3):
- added = add_todo(workspace, f"Drift baseline {index}.")
- delivered(added, label=f"todo add {index}")
- todo_ids.append(str(added["todo_id"]))
- qualified(qualify(workspace), label="baseline")
- original = workspace.state_path.read_text(encoding="utf-8")
- foreign = original.replace("handoff_mode: hard_lease", "handoff_mode: soft_claim")
- expect(foreign != original, "the foreign edit must change the primary")
- workspace.state_path.write_text(foreign, encoding="utf-8")
- inspection = _object(inspect(workspace).get("inspection"), "inspection")
- expect(
- inspection.get("status") == "drifted" and inspection.get("parity_matches") is False
- and inspection.get("reason_code") == "shadow_projection_drift" and inspection.get("qualified") is False,
- "inspect must report the drift",
- )
- rejected(qualify(workspace), "qualification", label="qualify after drift")
- read = read_candidate(workspace, todo_ids[0])
- expect(read.get("ok") is False and rejected(read, "read_candidate", label="read after drift").get("read_candidate_qualified") is False, "no candidate read may qualify after drift")
- after = add_todo(workspace, "Public write after the foreign edit.")
- expect(after.get("added") is True, "the primary write must still commit")
- held = capture_evidence(after, label="todo add (after drift)")
- expect(held.get("outcome") == "pending" and held.get("reason_code") == CONTINUITY_HOLD, "the capture must hold on unproven continuity")
- expect(_object(held.get("drain"), "held drain").get("outcome") == "stopped", "the inline drain must stop, not deliver")
- expect(backlog(shadow_status(workspace), "todos").get("committed_pending") == 1, "the held entry must stay pending")
- workspace.state_path.write_text(workspace.state_path.read_text(encoding="utf-8").replace("handoff_mode: soft_claim", "handoff_mode: hard_lease"), encoding="utf-8")
- restored = rejected(qualify(workspace), "qualification", label="qualify after restore")
- expect(restored.get("reason_code") == "outbox_pending", "restoring the bytes must not requalify a held lineage")
- stopped = drain(workspace)
- expect(
- stopped.get("ok") is False and stopped.get("outcome") == "stopped" and stopped.get("delivered") == 0 and stopped.get("reason_code") == CONTINUITY_HOLD,
- "drain must keep holding the entry rather than guess continuity",
- )
- summary = _recover_by_rollback_and_rebootstrap(workspace, label="drift")
- return passed(
- drift_status="drifted",
- drift_reason="shadow_projection_drift",
- held_write=CONTINUITY_HOLD,
- restore_requalifies=False,
- recovered_by="rollback_then_bootstrap",
- rebootstrap_baseline_todos=summary.get("todo_count"),
- recovered_qualification="qualified",
- )
-
-
-def row_event_only_todo_source_holds(context: RowContext) -> RowOutcome:
- """A retired source refuses candidate/primary operations; preserving both sources allows explicit cleanup."""
-
- workspace = capture_workspace(context, "ladder-event")
- todo_ids: list[str] = []
- for index in range(3):
- added = add_todo(workspace, f"Markdown baseline {index}.")
- delivered(added, label=f"todo add {index}")
- todo_ids.append(str(added["todo_id"]))
- qualified(qualify(workspace), label="baseline")
- log = workspace.state_path.with_name("events.jsonl")
- log.write_text(json.dumps({"schema_version": "loopx_state_event_v0",
- "event_type": "todo_added", "refs": {"todo_id": "todo_event_only"}}) + "\n", encoding="utf-8")
- log_bytes = log.read_bytes()
- surfaces = {"inspect": inspect(workspace), "qualify": qualify(workspace), "read-candidate": read_candidate(workspace, todo_ids[0])}
- for label, payload in surfaces.items():
- expect(payload.get("ok") is False and str(payload.get("error") or "").startswith(EVENT_ONLY_HOLD), f"{label} must refuse retired source: {payload.get('error')}")
- status = shadow_status(workspace)
- expect(status.get("ok") is True and management_status(status) == "active", "status must stay readable while the lineage is held")
- before = workspace.state_path.read_bytes()
- during = goal_cli(workspace, "todo", "add", "--role", "agent",
- "--text", "Markdown write during the retired event hold.", check=False)
- expect(during.get("ok") is False, "retired source must reject primary writes")
- expect(workspace.state_path.read_bytes() == before, "refusal must preserve Markdown")
- expect(log.read_bytes() == log_bytes, "refusal must preserve the event file")
- log.unlink() # The fixture owns this file; product never removes it.
- qualified(qualify(workspace), label="retired source removed without any write")
- return passed(hold=EVENT_ONLY_HOLD, held_surfaces=sorted(surfaces),
- primary_write_refused=True, event_log_untouched=True)
-
-
-@dataclass(frozen=True)
-class _MigrationTarget:
- """The target registry, repository and runtime root of one migration."""
-
- registry_path: Path
- repo: Path
- runtime_root: Path
- home: Path
- goal_id: str
-
- def cli_prefix(self) -> list[str]:
- return ["--registry", str(self.registry_path), "--runtime-root", str(self.runtime_root), "--format", "json"]
-
-
-MIGRATION_SENTINEL = b'{"schema_version":"existing","goals":[]}\n'
-
-
-def _migration_target(context: RowContext, legacy: GoalWorkspace) -> _MigrationTarget:
- root = context.root / "migration-target"
- repo = root / "repo"
- repo.mkdir(parents=True)
- registry_path = root / "registry.json"
- registry_path.write_bytes(MIGRATION_SENTINEL)
- return _MigrationTarget(registry_path=registry_path, repo=repo, runtime_root=root / "runtime", home=legacy.home, goal_id=unique_goal_id("migrated"))
-
-
-def _migration_arguments(legacy: GoalWorkspace, target: _MigrationTarget) -> list[str]:
- return [
- "migrate-state",
- "--legacy-registry", str(legacy.registry_path),
- "--legacy-runtime-root", str(legacy.runtime_root),
- "--target-runtime-root", str(target.runtime_root),
- "--goal-id", legacy.goal_id,
- "--goal-id-map", f"{legacy.goal_id}={target.goal_id}",
- "--path-map", f"{legacy.repo}={target.repo}",
- "--copy-active-state",
- "--copy-runtime",
- "--no-global-sync",
- ]
-
-
-def _set_target_runtime_shadow(target: _MigrationTarget, *, enabled: bool) -> JsonObject:
- registry = parse_json_object(target.registry_path.read_text(encoding="utf-8"))
- goals = [_object(goal, "target goal") for goal in _list(registry.get("goals"), "target goals")]
- expect(len(goals) == 1 and goals[0].get("id") == target.goal_id, "the target registry must carry exactly the migrated goal")
- coordination = _object(goals[0].get("coordination"), "target coordination")
- carried = _object(coordination.get("runtime_shadow"), "carried runtime_shadow")
- coordination["runtime_shadow"] = {**carried, "enabled": enabled}
- goals[0]["coordination"] = coordination
- registry["goals"] = goals
- target.registry_path.write_text(json.dumps(registry), encoding="utf-8")
- return carried
-
-
-def row_migration_seeds_and_drains(context: RowContext) -> RowOutcome:
- """migrate-state refuses an active capture source; after rollback the migrated goal bootstraps a fresh lineage that drains."""
-
- legacy = capture_workspace(context, "ladder-legacy", bootstrap=False)
- first = _object(bootstrap_capture(legacy).get("bootstrap"), "legacy bootstrap")
- delivered(add_todo(legacy, "Captured before migration."), label="legacy todo add")
- documents = store_documents(legacy)
- expect(len(documents) == 1, "the legacy runtime must hold one candidate document")
- legacy_bytes = documents[0].read_bytes()
- target = _migration_target(context, legacy)
- arguments = _migration_arguments(legacy, target)
- preview = run_cli(target, *arguments, check=False)
- expect(preview.get("ok") is True and preview.get("dry_run") is True and target.registry_path.read_bytes() == MIGRATION_SENTINEL, "a dry run must plan without writing")
- refused = run_cli(target, *arguments, "--execute", check=False)
- expect(
- refused.get("ok") is False and refused.get("error_code") == "shadow_source_replacement_requires_rebootstrap",
- "an active capture source must refuse a generic state rebuild",
- )
- expect(target.registry_path.read_bytes() == MIGRATION_SENTINEL and documents[0].read_bytes() == legacy_bytes, "a refused migration must write nothing")
- set_runtime_shadow(legacy, enabled=False)
- disabled_only = run_cli(target, *arguments, "--execute", check=False)
- expect(disabled_only.get("error_code") == "shadow_source_replacement_requires_rebootstrap", "disabling capture alone must not release an active lineage")
- set_runtime_shadow(legacy, enabled=True)
- revision = str(candidate(shadow_status(legacy)).get("provider_revision"))
- rollback = _object(goal_cli(legacy, "coordination-shadow", "rollback", "--provider-revision", revision, "--execute").get("rollback"), "legacy rollback")
- expect(rollback.get("status") == "applied" and rollback.get("archive_retained") is True, "the legacy lineage must be retired into an archive first")
- set_runtime_shadow(legacy, enabled=False)
- executed = run_cli(target, *arguments, "--execute")
- expect(executed.get("ok") is True and executed.get("wrote_project_registry") is True, "the migration must execute after rollback")
- runtime_goal = _object(_list(executed.get("runtime_goals"), "runtime goals")[0], "runtime goal")
- expect(runtime_goal.get("copied") is True, "the runtime goal directory must be copied")
- expect(executed.get("authority_shadow_seeds") == [], "a capture-only goal plans no observation seed")
- expect((target.repo / legacy.state_path.name).exists(), "the active state must be copied to the mapped repository")
- carried = _set_target_runtime_shadow(target, enabled=True)
- expect(carried.get("enabled") is False and carried.get("provider") == RUNTIME_SHADOW_PROVIDER, "the migrated goal must carry its disabled capture configuration")
- inspection = _object(run_cli(target, "coordination-shadow", "inspect", "--goal-id", target.goal_id, check=False).get("inspection"), "target inspection")
- expect(inspection.get("status") == "missing" and inspection.get("bootstrap_required") is True, "the migrated goal must require its own bootstrap")
- bootstrap_payload = run_cli(target, "coordination-shadow", "bootstrap", "--goal-id", target.goal_id, "--execute")
- bootstrap = _object(bootstrap_payload.get("bootstrap"), "target bootstrap")
- expect(bootstrap.get("status") == "applied" and bootstrap.get("capture_lineage_id") != first.get("capture_lineage_id"), "the target must seed a fresh lineage")
- summary = _object(bootstrap_payload.get("projection_summary"), "target projection summary")
- expect(summary.get("todo_count") == 1 and summary.get("lease_count") == 0, "the fresh lineage must import the migrated baseline")
- added = run_cli(target, "todo", "add", "--goal-id", target.goal_id, "--role", "agent", "--text", "Captured in the migrated runtime root.", "--task-class", "advancement_task")
- drained = _object(delivered(added, label="target todo add").get("drain"), "target drain")
- expect(drained.get("last_cursor") == "2", "the first migrated write must drain to cursor 2")
- qualification = _object(run_cli(target, "coordination-shadow", "qualify", "--goal-id", target.goal_id, "--minimum-operations", "1", check=False).get("qualification"), "target qualification")
- expect(qualification.get("status") == "qualified" and _object(qualification.get("evidence"), "evidence").get("operation_count") == 1, "the migrated lineage must qualify on its own write")
- archive = legacy.runtime_root / "authority-shadow" / "file-v0" / "rollback"
- expect(archive.is_dir() and any(archive.iterdir()), "the legacy archive must be retained")
- return passed(
- preview_dry_run=True,
- active_capture_refused="shadow_source_replacement_requires_rebootstrap",
- disabled_config_still_refused=True,
- executed_after_rollback=True,
- observation_seeds=0,
- migrated_baseline_todos=1,
- new_lineage=True,
- drained_cursor="2",
- legacy_archive_retained=True,
- )
-
-
-def row_growth_measurement_gate(context: RowContext) -> RowOutcome:
- """Measure file-v0 history growth per transaction and gate its integrity; no capacity horizon is claimed."""
-
- workspace = capture_workspace(context, "ladder-growth")
- status = shadow_status(workspace)
- sizes = [int(str(status.get("store_bytes")))]
- cursors = [str(candidate(status).get("cursor"))]
- for index in range(GROWTH_TRANSACTIONS):
- delivered(add_todo(workspace, GROWTH_TEXT_TEMPLATE % index), label=f"growth write {index}")
- status = shadow_status(workspace)
- expect(status.get("retention_pressure") is False, "the bounded workload must not trip retention pressure")
- sizes.append(int(str(status.get("store_bytes"))))
- cursors.append(str(candidate(status).get("cursor")))
- expect(cursors == [str(index) for index in range(1, GROWTH_TRANSACTIONS + 2)], "every write must advance the cursor by exactly one")
- deltas = [after - before for before, after in zip(sizes, sizes[1:])]
- expect(all(delta > 0 for delta in deltas), "every transaction must grow the retained history")
- accelerations = [later - earlier for earlier, later in zip(deltas, deltas[1:])]
- expect(
- max(abs(value) for value in accelerations) <= GROWTH_DELTA_ACCELERATION_ENVELOPE_BYTES,
- "per-transaction growth may increase by at most one live record per transaction",
- )
- transactions = history(workspace)
- expect(len(transactions) == GROWTH_TRANSACTIONS + 1, "history must retain every transaction")
- expect(
- [projected_todo_count(transaction) for transaction in transactions] == list(range(GROWTH_TRANSACTIONS + 1)),
- "every transaction must retain its complete projection",
- )
- final = sizes[-1]
- cumulative = sum(sizes)
- return passed(
- transactions=GROWTH_TRANSACTIONS + 1,
- final_history_bytes=final,
- cumulative_publication_bytes=cumulative,
- publication_rewrite_ratio=round(cumulative / final, 2),
- first_delta_bytes=deltas[0],
- last_delta_bytes=deltas[-1],
- delta_acceleration_max_bytes=max(abs(value) for value in accelerations),
- delta_acceleration_envelope_bytes=GROWTH_DELTA_ACCELERATION_ENVELOPE_BYTES,
- retention_pressure=False,
- capacity_verdict="not_evaluated",
- qualification_horizon="not_claimed",
- )
-
-
-def row_archive_after_leased_completion_parity(context: RowContext) -> RowOutcome:
- """Archiving a Todo whose released lease stays on disk keeps the candidate head matched and qualifiable."""
-
- workspace = capture_workspace(context, "ladder-archive-leased")
- leased = add_todo(workspace, "Leased todo archived after its completion is captured.")
- leased_todo_id = str(leased["todo_id"])
- delivered(leased, label="todo add (leased)")
- acquired = acquire_lease(workspace, todo_id=leased_todo_id, owner=AGENT_A, idempotency_key="ladder-archive-leased-a")
- delivered(acquired, label="task-lease acquire")
- completed = goal_cli(
- workspace, "todo", "complete", "--todo-id", leased_todo_id, "--agent-id", AGENT_A,
- "--task-lease-idempotency-key", "ladder-archive-leased-a",
- "--task-lease-expected-version", lease_version(acquired, label="acquire"),
- "--evidence", "validation://ladder-archive-leased", "--no-follow-up",
- )
- delivered(completed, label="todo complete")
- anchor = add_todo(workspace, "Anchor todo that stays open across the archive write.")
- anchor_todo_id = str(anchor["todo_id"])
- delivered(anchor, label="todo add (anchor)")
- qualified(qualify(workspace), label="baseline")
- archived = goal_cli(workspace, "todo", "archive-completed", "--role", "agent", "--max-active-done", "0", "--execute")
- expect(archived.get("changed") is True and archived.get("moved_count") == 1, "archive-completed must move exactly the completed todo")
- inspection = _object(inspect(workspace).get("inspection"), "post-archive inspection")
- expect(
- inspection.get("status") == "matched" and inspection.get("parity_matches") is True,
- "archiving a Todo whose released lease remains on disk must not orphan that lease in the candidate head",
- )
- expect(inspection.get("reason_code") is None, "a matched archive must report no drift reason")
- # Require exactly the event kinds this row delivers, including the archive
- # that retires the Todo from the graph while its released lease file stays
- # on disk as audit history. Reusing the mixed-writer parity set here would
- # demand writers this row never drives.
- flags = ["--minimum-operations", str(ARCHIVE_PARITY_OPERATIONS)]
- for write_class in ARCHIVE_PARITY_REQUIRED_WRITE_CLASSES:
- flags.extend(["--require-event-kind", write_class])
- qualification = qualified(qualify(workspace, *flags), label="post-archive")
- read = read_candidate(workspace, anchor_todo_id)
- expect(read.get("ok") is True, "a qualified read must remain available after the archive write")
- lease_dir = workspace.runtime_root / "goals" / workspace.goal_id / "task-leases"
- lease_names = sorted(path.name for path in lease_dir.glob("*.json"))
- expect(f"{leased_todo_id}.json" in lease_names, "the released lease file must stay on disk as audit history")
-
- # The second boundary the same rule covers: a later lease write must not
- # re-read the retained lease of the archived Todo into its own partition.
- successor = add_todo(workspace, "Successor todo that takes a fresh lease after the archive.")
- successor_todo_id = str(successor["todo_id"])
- delivered(successor, label="todo add (successor)")
- successor_lease = acquire_lease(
- workspace, todo_id=successor_todo_id, owner=AGENT_A, idempotency_key="ladder-archive-leased-b",
- )
- delivered(successor_lease, label="task-lease acquire (successor)")
- after_successor = _object(inspect(workspace).get("inspection"), "post-successor inspection")
- expect(
- after_successor.get("status") == "matched" and after_successor.get("parity_matches") is True,
- "a lease write after the archive must not inherit the retained lease of the archived Todo",
- )
- # Completion closes the lease fence in a separate native request. It must
- # retain the same current-graph rule as acquire, not reintroduce audit leases.
- successor_completed = goal_cli(
- workspace, "todo", "complete", "--todo-id", successor_todo_id, "--agent-id", AGENT_A,
- "--task-lease-idempotency-key", "ladder-archive-leased-b",
- "--task-lease-expected-version", lease_version(successor_lease, label="successor acquire"),
- "--evidence", "validation://ladder-successor-complete", "--no-follow-up",
- )
- delivered(successor_completed, label="todo complete (successor)")
- drained = drain(workspace)
- expect(drained.get("ok") is True and drained.get("pending_after") == 0,
- "successor fence-close must leave no unprovable lease partition")
- qualified(qualify(workspace), label="post-successor completion")
- expect(read_candidate(workspace, anchor_todo_id).get("ok") is True,
- "candidate reads must survive successor fence-close")
- final_lease_names = sorted(path.name for path in lease_dir.glob("*.json"))
- expect(
- {f"{leased_todo_id}.json", f"{successor_todo_id}.json"} <= set(final_lease_names),
- "both the archived Todo's audit lease and the successor's live lease must remain on disk",
- )
- return passed(
- archived_todo=leased_todo_id,
- retained_lease_files=len(lease_names),
- parity_status="matched",
- parity_reason=None,
- qualification_cursor=str(qualification.get("cursor")),
- anchor_read_qualified=True,
- successor_todo=successor_todo_id,
- post_successor_parity="matched",
- final_lease_files=len(final_lease_names),
- )
-
-__all__ = [
- "CRASH_WORKER",
- "DRAIN_CRASH_WINDOWS",
- "GROWTH_DELTA_ACCELERATION_ENVELOPE_BYTES",
- "GROWTH_TRANSACTIONS",
- "PARITY_CYCLES",
- "PARITY_REQUIRED_WRITE_CLASSES",
- "PRIMARY_CRASH_WINDOWS",
- "row_archive_after_leased_completion_parity",
- "row_drain_idempotent",
- "row_event_only_todo_source_holds",
- "row_growth_measurement_gate",
- "row_migration_seeds_and_drains",
- "row_outbox_prepared_then_committed_entries",
- "row_parity_divergent_detects_foreign_edit",
- "row_parity_equal",
- "row_rollback_with_pending_entries",
- "row_sigkill_between_primary_write_and_drain",
- "row_sigkill_mid_drain",
-]
diff --git a/loopx/control_plane/testing/canary_harness.py b/loopx/control_plane/testing/canary_harness.py
index c280b13f09..3fb37301d4 100644
--- a/loopx/control_plane/testing/canary_harness.py
+++ b/loopx/control_plane/testing/canary_harness.py
@@ -182,3 +182,59 @@ def read_run_index(runtime_root: Path, goal_id: str) -> list[dict[str, Any]]:
for line in index_path.read_text(encoding="utf-8").splitlines()
if line.strip()
]
+
+
+def import_fixture_authority(registry_path: Path, goal_id: str, *, provider: str = "file") -> None:
+ """Import a disposable smoke's complete source through the real saved plan.
+
+ Fixture writers have finished before this call. Historical metadata stays
+ intact; normal writes afterwards use the selected canonical/soft-claim owner.
+ Existing unpromoted/refusal fixtures opt in explicitly, never by default.
+ """
+ from ...state_backup import build_state_backup_plan, execute_state_backup_plan
+
+ registry_path = registry_path.resolve()
+ registry = json.loads(registry_path.read_text(encoding="utf-8"))
+ goal = next(item for item in registry["goals"] if item["id"] == goal_id)
+ project = Path(goal["repo"]).resolve()
+ runtime = Path(registry["common_runtime_root"]).resolve()
+ # Bind the physical source consistently on hosts whose temp path is a link.
+ goal["repo"] = str(project)
+ state_file = Path(goal["state_file"]).expanduser()
+ if not state_file.is_absolute():
+ state_file = project / state_file
+ goal["state_file"] = str(state_file.resolve())
+ registry_path.write_text(json.dumps(registry), encoding="utf-8")
+ runtime.mkdir(parents=True, exist_ok=True)
+ backup = execute_state_backup_plan(build_state_backup_plan(
+ project=project, runtime_root=runtime,
+ output_dir=runtime.parent / "fixture-backups", backup_id=goal_id,
+ include_automations=False, include_skills=False,
+ include_registry_projects=False, registry_path=registry_path,
+ ))
+ assert backup["ok"], backup
+ prefix = ("coordination-shadow",)
+ identity = ("--goal-id", goal_id, "--operation-id", f"fixture-import:{goal_id}")
+ prepared = run_json_cli(
+ *prefix, "prepare-import", *identity,
+ "--backup-manifest", backup["manifest_path"],
+ "--provider", provider, "--target-handoff-mode", "soft_claim",
+ registry_path=registry_path, runtime_root=runtime,
+ )["cold_import"]
+ assert prepared["status"] == "prepared", prepared
+ # Read the saved carrier rather than manufacturing a digest/target.
+ plan = json.loads(Path(prepared["plan_path"]).read_text(encoding="utf-8"))
+ assert plan["plan_sha256"] == prepared["plan_sha256"], plan
+ applied = run_json_cli(
+ *prefix, "apply-import", *identity,
+ "--plan-sha256", prepared["plan_sha256"], "--writers-stopped", "--execute",
+ registry_path=registry_path, runtime_root=runtime,
+ )["cold_import"]
+ assert applied["status"] in {"applied", "recovered"}, applied
+ readback = run_json_cli(
+ "todo", "list", "--goal-id", goal_id,
+ registry_path=registry_path, runtime_root=runtime,
+ )["authority_read"]
+ assert readback["source_authority"] == f"{provider}_v0", readback
+ assert readback["decision_read_from_provider"] is True, readback
+ assert readback["legacy_fallback_used"] is False, readback
diff --git a/loopx/control_plane/testing/replan_semantic_action_behavior.py b/loopx/control_plane/testing/replan_semantic_action_behavior.py
index 6189d43a44..52b0181e2c 100644
--- a/loopx/control_plane/testing/replan_semantic_action_behavior.py
+++ b/loopx/control_plane/testing/replan_semantic_action_behavior.py
@@ -57,7 +57,10 @@ def __init__(self, code: str, output: str, exit_code: int) -> None:
_FIXTURE_GOAL_ID = "replan-semantic-action-fixture"
_FIXTURE_AGENT_ID = "codex-replan-semantic-action"
-_FIXTURE_WORK_ITEM_ID = "todo-replan-semantic-action"
+_FIXTURE_WORK_ITEM_OPERATION_ID = "fixture-replan-semantic-monitor"
+_FIXTURE_WORK_ITEM_ID = (
+ "todo_" + sha256(_FIXTURE_WORK_ITEM_OPERATION_ID.encode()).hexdigest()[:24]
+)
_FIXTURE_FRONTIER_FILE = "replan-frontier.json"
_FIXTURE_SOURCE_FILE = "fixture/permission-config.json"
_FIXTURE_NEW_SURFACE_ID = "surface-permission-config"
@@ -204,26 +207,11 @@ def _build_fixture(
"refresh-state."
)
)
- fixture_agent_todo = (
- "- [ ] [P1-monitor] Observe the bounded fixture until its frontier "
- "changes.\n"
- " \n"
- )
- if required_vision:
- fixture_agent_todo += (
- "- [ ] [P0] Continue the peer-owned implementation.\n"
- " \n"
- )
state_path.write_text(
"---\n"
"status: active\n"
"owner_mode: goal\n"
+ "handoff_mode: soft_claim\n"
f'objective: "{fixture_objective}"\n'
"updated_at: 2026-08-13T00:00:00+08:00\n"
"---\n\n"
@@ -232,8 +220,7 @@ def _build_fixture(
f"{fixture_objective}\n\n"
"## Next Action\n\n"
f"- {fixture_next_action}\n\n"
- "## Agent Todo\n\n"
- f"{fixture_agent_todo}",
+ "## Agent Todo\n",
encoding="utf-8",
)
registry_goal: dict[str, Any] = {
@@ -249,6 +236,11 @@ def _build_fixture(
"coordination": {
"registered_agents": [_FIXTURE_AGENT_ID, "codex-fixture-peer"] if required_vision else [_FIXTURE_AGENT_ID],
"agent_model": "peer_v1",
+ "storage_target": {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": "file",
+ "handoff_mode": "soft_claim",
+ },
"agent_profiles": {
_FIXTURE_AGENT_ID: {
"schema_version": "agent_profile_v1",
@@ -269,6 +261,7 @@ def _build_fixture(
"window_hours": 24,
"allowed_slots": 5,
},
+ "creation_operation_id": "goal-create:replan-semantic-action-fixture",
}
if composition_frontier:
registry_goal["spawn_policy"] = {
@@ -285,6 +278,56 @@ def _build_fixture(
registry_path.parent.mkdir(parents=True, exist_ok=True)
registry_path.write_text(registry_text + "\n", encoding="utf-8")
+ from ..coordination.goal_storage_initialization import (
+ initialize_goal_storage_target,
+ )
+ from ..todos.mutation_api import add_goal_todo
+
+ initialized = initialize_goal_storage_target(
+ runtime_root,
+ registry_goal,
+ registry_path=global_registry_path,
+ )
+ if not isinstance(initialized, dict) or (
+ initialized.get("authority_initialized") is not True
+ or initialized.get("legacy_fallback_used") is not False
+ ):
+ raise AssertionError("fixture did not initialize canonical File authority")
+ monitor = add_goal_todo(
+ registry_path=global_registry_path,
+ runtime_root_arg=str(runtime_root),
+ goal_id=_FIXTURE_GOAL_ID,
+ role="agent",
+ text="Observe the bounded fixture until its frontier changes.",
+ priority="P1",
+ task_class="continuous_monitor",
+ action_kind="observe_fixture",
+ claimed_by=_FIXTURE_AGENT_ID,
+ agent_id=_FIXTURE_AGENT_ID,
+ monitor_metadata={
+ "target_key": "replan-semantic-fixture",
+ "cadence": "1d",
+ "next_due_at": "2999-01-01T00:00:00Z",
+ "watch_only": "true",
+ },
+ operation_id=_FIXTURE_WORK_ITEM_OPERATION_ID,
+ )
+ if monitor.get("todo_id") != _FIXTURE_WORK_ITEM_ID:
+ raise AssertionError("fixture monitor identity is not deterministic")
+ if required_vision:
+ add_goal_todo(
+ registry_path=global_registry_path,
+ runtime_root_arg=str(runtime_root),
+ goal_id=_FIXTURE_GOAL_ID,
+ role="agent",
+ text="Continue the peer-owned implementation.",
+ priority="P0",
+ task_class="advancement_task",
+ claimed_by="codex-fixture-peer",
+ agent_id="codex-fixture-peer",
+ operation_id="fixture-replan-peer-implementation",
+ )
+
runs_dir = runtime_root / "goals" / _FIXTURE_GOAL_ID / "runs"
runs_dir.mkdir(parents=True, exist_ok=True)
prior_runs: list[dict[str, Any]] = []
@@ -678,6 +721,7 @@ def _semantic_reentry_observation(
packet: Mapping[str, Any],
*,
obligation_id: str,
+ result_class: str,
) -> dict[str, Any]:
obligation = packet.get("autonomous_replan_obligation")
if (
@@ -710,12 +754,29 @@ def _semantic_reentry_observation(
and int(normalized_progress.get("agent_monitor_open_count") or 0) > 0
and int(normalized_progress.get("agent_monitor_due_count") or 0) == 0
)
- if not future_monitor_wait:
+ terminal_coverage_exit = bool(
+ result_class in {
+ ProgressResultClass.EXPLORATION_EXHAUSTED.value,
+ ProgressResultClass.NO_FOLLOWUP.value,
+ }
+ and packet.get("effective_action") == EffectiveAction.MONITOR_QUIET_SKIP.value
+ and isinstance(frontier, Mapping)
+ and frontier.get("replan_required") is False
+ and isinstance(monitor_lanes, Mapping)
+ and monitor_lanes.get("present") is False
+ and isinstance(normalized_progress, Mapping)
+ and int(normalized_progress.get("agent_open_count") or 0) == 0
+ )
+ if not future_monitor_wait and not terminal_coverage_exit:
raise ValueError("semantic_reentry_not_future_monitor_wait")
return {
"decision": packet.get("decision"),
"effective_action": packet.get("effective_action"),
- "replan_rule": "future_monitor_wait",
+ "replan_rule": (
+ "future_monitor_wait"
+ if future_monitor_wait
+ else "terminal_coverage_exhausted"
+ ),
"replan_closed": True,
"exited": True,
}
@@ -1150,6 +1211,7 @@ def _handle_semantic_writeback(
state.semantic_reentry_observation = _semantic_reentry_observation(
reentry_packet,
obligation_id=obligation_id,
+ result_class=result_class,
)
state.read_only_host_commands_executed = True
return output
diff --git a/loopx/control_plane/testing/terminal_settlement_tool_behavior.py b/loopx/control_plane/testing/terminal_settlement_tool_behavior.py
index 7bb0f570e5..0d588b9232 100644
--- a/loopx/control_plane/testing/terminal_settlement_tool_behavior.py
+++ b/loopx/control_plane/testing/terminal_settlement_tool_behavior.py
@@ -10,6 +10,7 @@
from pathlib import Path
from typing import Any, cast
+from ..coordination.goal_storage_initialization import initialize_goal_storage_target
from ...heartbeat_prompt import build_heartbeat_prompt
from ...control_plane.todos.mutation_api import add_goal_todo, complete_goal_todo
from ..quota.turn_envelope import quota_action_signature_document
@@ -41,7 +42,15 @@
TERMINAL_SETTLEMENT_FIXTURE_GOAL_ID = "terminal-settlement-fixture"
TERMINAL_SETTLEMENT_FIXTURE_AGENT_ID = "codex-terminal-settlement"
-TERMINAL_SETTLEMENT_FIXTURE_TODO_ID = "todo_terminal001"
+TERMINAL_SETTLEMENT_FIXTURE_TODO_OPERATION_ID = (
+ "terminal-settlement-fixture-todo"
+)
+TERMINAL_SETTLEMENT_FIXTURE_TODO_ID = (
+ "todo_"
+ + sha256(TERMINAL_SETTLEMENT_FIXTURE_TODO_OPERATION_ID.encode("utf-8")).hexdigest()[
+ :24
+ ]
+)
TERMINAL_SETTLEMENT_FIXTURE_PROOF = "fixture/settlement-proof.json"
TERMINAL_SETTLEMENT_FIXTURE_ACTION_TEXT = (
f"Read `{TERMINAL_SETTLEMENT_FIXTURE_PROOF}`, verify the bounded delivery, "
@@ -103,6 +112,21 @@ class _QualificationState:
seen_clock: bool = False
+def _initialize_fixture_goal_storage(
+ *,
+ runtime_root: Path,
+ goal: Mapping[str, Any],
+ registry_path: Path,
+) -> None:
+ result = initialize_goal_storage_target(
+ runtime_root,
+ goal,
+ registry_path=registry_path,
+ )
+ if not isinstance(result, dict) or result.get("authority_initialized") is not True:
+ raise ValueError("fixture canonical authority initialization did not complete")
+
+
def _build_fixture(root: Path) -> _TerminalSettlementFixture:
source_root = Path(__file__).resolve().parents[3]
project_root = root / "project"
@@ -173,48 +197,70 @@ def _build_fixture(root: Path) -> _TerminalSettlementFixture:
"Settle one final validated delivery without losing quota accounting.\n\n"
"## Next Action\n\n"
f"- {TERMINAL_SETTLEMENT_FIXTURE_ACTION_TEXT}\n\n"
+ "## User Todo / Owner Review Reading Queue\n\n"
"## Agent Todo\n\n"
- f"- [ ] [P0] {TERMINAL_SETTLEMENT_FIXTURE_ACTION_TEXT}\n"
- " \n",
+ "\n",
encoding="utf-8",
)
+ goal = {
+ "id": TERMINAL_SETTLEMENT_FIXTURE_GOAL_ID,
+ "creation_operation_id": "goal-create:terminal-settlement-fixture",
+ "domain": "terminal-settlement-live-fixture",
+ "status": "active",
+ "repo": str(project_root),
+ "state_file": str(state_relative),
+ "adapter": {
+ "kind": "fixture_connected_delivery_v0",
+ "status": "connected-delivery",
+ },
+ "coordination": {
+ "registered_agents": [TERMINAL_SETTLEMENT_FIXTURE_AGENT_ID],
+ "agent_model": "peer_v1",
+ "storage_target": {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": "file",
+ "handoff_mode": "soft_claim",
+ },
+ },
+ "authority_sources": [],
+ "quota": {
+ "compute": 1.0,
+ "window_hours": 24,
+ "allowed_slots": 5,
+ },
+ }
registry = {
"schema_version": "0.1",
"updated_at": "2026-08-16T00:00:00+08:00",
"common_runtime_root": str(runtime_root),
- "goals": [
- {
- "id": TERMINAL_SETTLEMENT_FIXTURE_GOAL_ID,
- "domain": "terminal-settlement-live-fixture",
- "status": "active",
- "repo": str(project_root),
- "state_file": str(state_relative),
- "adapter": {
- "kind": "fixture_connected_delivery_v0",
- "status": "connected-delivery",
- },
- "coordination": {
- "registered_agents": [TERMINAL_SETTLEMENT_FIXTURE_AGENT_ID],
- "agent_model": "peer_v1",
- },
- "authority_sources": [],
- "quota": {
- "compute": 1.0,
- "window_hours": 24,
- "allowed_slots": 5,
- },
- }
- ],
+ "goals": [goal],
}
registry_text = json.dumps(registry, ensure_ascii=False, indent=2, sort_keys=True)
for registry_path in (local_registry_path, global_registry_path):
registry_path.parent.mkdir(parents=True, exist_ok=True)
registry_path.write_text(registry_text + "\n", encoding="utf-8")
+ _initialize_fixture_goal_storage(
+ runtime_root=runtime_root,
+ goal=goal,
+ registry_path=global_registry_path,
+ )
+ created = add_goal_todo(
+ registry_path=global_registry_path,
+ goal_id=TERMINAL_SETTLEMENT_FIXTURE_GOAL_ID,
+ role="agent",
+ text=TERMINAL_SETTLEMENT_FIXTURE_ACTION_TEXT,
+ priority="P0",
+ task_class="advancement_task",
+ action_kind="terminal_settlement_qualification",
+ task_repository=(
+ "git:github.com/example/terminal-settlement-fixture"
+ ),
+ continuation_policy="independent_handoff",
+ claimed_by=TERMINAL_SETTLEMENT_FIXTURE_AGENT_ID,
+ operation_id=TERMINAL_SETTLEMENT_FIXTURE_TODO_OPERATION_ID,
+ )
+ if created.get("todo_id") != TERMINAL_SETTLEMENT_FIXTURE_TODO_ID:
+ raise ValueError("fixture canonical Todo identity drifted")
prompt = build_heartbeat_prompt(
goal_id=TERMINAL_SETTLEMENT_FIXTURE_GOAL_ID,
@@ -367,37 +413,40 @@ def _build_reentry_fixture(root: Path) -> _TerminalSettlementReentryFixture:
"## Agent Todo\n\n",
encoding="utf-8",
)
+ goal = {
+ "id": TERMINAL_SETTLEMENT_REENTRY_FIXTURE_GOAL_ID,
+ "creation_operation_id": "goal-create:terminal-reentry-fixture",
+ "domain": "terminal-reentry-live-fixture",
+ "status": "active",
+ "repo": str(project_root),
+ "state_file": str(state_relative),
+ "adapter": {
+ "kind": "fixture_connected_delivery_v0",
+ "status": "connected-delivery",
+ },
+ "coordination": {
+ "registered_agents": [TERMINAL_SETTLEMENT_REENTRY_FIXTURE_AGENT_ID],
+ "agent_model": "peer_v1",
+ "storage_target": {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": "file",
+ "handoff_mode": "soft_claim",
+ },
+ },
+ "authority_sources": [],
+ "quota": {
+ "compute": 1.0,
+ "window_hours": 24,
+ "allowed_slots": 5,
+ },
+ }
registry_path.write_text(
json.dumps(
{
"schema_version": "0.1",
"updated_at": "2026-08-23T00:00:00+08:00",
"common_runtime_root": str(runtime_root),
- "goals": [
- {
- "id": TERMINAL_SETTLEMENT_REENTRY_FIXTURE_GOAL_ID,
- "domain": "terminal-reentry-live-fixture",
- "status": "active",
- "repo": str(project_root),
- "state_file": str(state_relative),
- "adapter": {
- "kind": "fixture_connected_delivery_v0",
- "status": "connected-delivery",
- },
- "coordination": {
- "registered_agents": [
- TERMINAL_SETTLEMENT_REENTRY_FIXTURE_AGENT_ID
- ],
- "agent_model": "peer_v1",
- },
- "authority_sources": [],
- "quota": {
- "compute": 1.0,
- "window_hours": 24,
- "allowed_slots": 5,
- },
- }
- ],
+ "goals": [goal],
},
ensure_ascii=False,
indent=2,
@@ -406,6 +455,11 @@ def _build_reentry_fixture(root: Path) -> _TerminalSettlementReentryFixture:
+ "\n",
encoding="utf-8",
)
+ _initialize_fixture_goal_storage(
+ runtime_root=runtime_root,
+ goal=goal,
+ registry_path=registry_path,
+ )
todos = [
add_goal_todo(
registry_path=registry_path,
@@ -414,6 +468,7 @@ def _build_reentry_fixture(root: Path) -> _TerminalSettlementReentryFixture:
text=f"Validate terminal slice {index}.",
task_class="advancement_task",
claimed_by=TERMINAL_SETTLEMENT_REENTRY_FIXTURE_AGENT_ID,
+ operation_id=f"terminal-reentry-fixture-todo-{index}",
)
for index in (1, 2)
]
@@ -1090,7 +1145,6 @@ def receipt(*, passed: bool, failure_code: str | None) -> dict[str, Any]:
)
if todo_id in state.settled_todo_ids:
raise ValueError("repeated_lifecycle_settlement")
- note = str(argument_value(tokens, "--note") or "").strip()
if (
expected_tokens is None
or argument_value(tokens, "--goal-id")
@@ -1105,8 +1159,7 @@ def receipt(*, passed: bool, failure_code: str | None) -> dict[str, Any]:
or "--turn-instance-id" in tokens
or "--replan-obligation-id" in tokens
or "--no-follow-up" not in tokens
- or not note
- or note == ""
+ or "--note" in tokens
):
raise ValueError("lifecycle_settlement_identity_mismatch")
payload = _execute_reentry_cli(tool_call.command, state=state)
diff --git a/loopx/control_plane/todos/handoff_mode.py b/loopx/control_plane/todos/handoff_mode.py
index dd92a3558d..9732b2613b 100644
--- a/loopx/control_plane/todos/handoff_mode.py
+++ b/loopx/control_plane/todos/handoff_mode.py
@@ -1,46 +1,31 @@
"""Per-goal handoff mode: which ownership authority governs todo handoffs.
-Before promotion the active-state frontmatter declares ``handoff_mode``; afterward
-the selected canonical provider owns it. Public show/set route by that authority:
+The canonical provider owns the current policy; Markdown remains a historical
+source. Current execution uses only ``soft_claim`` or ``hard_lease``:
+
+* ``legacy`` (including an absent historical field) is decodable for import,
+ original receipts and exact lease cleanup. It cannot admit new execution or
+ turn a historical lease receipt into current proof. Existing Goals require
+ a reviewed, backed-up policy migration; their bytes are never silently changed.
+* ``soft_claim`` uses the Todo assignment. New lease acquire/renew/transfer are
+ rejected; retained release and inspection remain available.
+* ``hard_lease`` uses the current execution proof and typed ownership/terminal
+ rules. Claims, delegated actions and linked user decisions do not independently
+ grant a lease or bypass the transaction's fence.
+
+TypeScript owns policy admission, quiescence, migration, CAS and receipt recovery.
+Python retains source decoding, transport and public result adaptation. A missing
+or stale display never replaces canonical authority; unpromoted writes refuse.
-* absent / ``legacy`` (default): today's dual soft-claim + hard-lease
- behavior, byte-for-byte. The known soft-claim/hard-lease split brain stays
- open in this mode by design; it is surfaced additively, never silently
- repaired.
-* ``soft_claim``: the Todo claim is the only ownership record. Task-lease
- acquire/renew/transfer are typed-rejected; release and inspect stay allowed
- for cleanup and observability of legacy leftovers.
-* ``hard_lease``: ownership changes on an existing todo require the acting
- agent to hold that todo's time-active task lease, and the completion fence
- becomes mandatory for both user-role and agent-role todos. An exact linked
- user-gate decision-scope override authorizes only the exact linked decision
- transition; it does not bypass the completion fence. The delegated
- ``coordination.todo_lifecycle_authority`` override is the one audited door
- through the gate.
-
-Canonical mode, complete Todo/lease quiescence, CAS and replay share one TypeScript
-transaction. Stale or missing Markdown and local lease files are not fallback
-sources. The legacy mode below remains a frontmatter compatibility contract.
-
-Unpromoted transitions read complete Markdown Todos and local leases under
-their writer mutexes. The same typed quiescence rule governs both paths;
-nonempty retired Todo event sources are refused, never treated as empty.
"""
from __future__ import annotations
-from contextlib import ExitStack
from pathlib import Path
from typing import Any
from ...paths import effective_runtime_root
-from ..coordination.authority_core import (
- HandoffMode,
- OwnershipGate,
- ownership_gate_requirement,
-)
-from ..goals.active_state_metadata import parse_state_frontmatter, split_state_frontmatter
-from .contract import normalize_todo_claimed_by
+from ..goals.active_state_metadata import parse_state_frontmatter
HANDOFF_MODE_SCHEMA_VERSION = "goal_handoff_mode_v0"
HANDOFF_MODE_FRONTMATTER_KEY = "handoff_mode"
@@ -52,7 +37,6 @@
HANDOFF_MODE_SOFT_CLAIM,
HANDOFF_MODE_HARD_LEASE,
)
-DELEGATED_AUTHORITY_MODE = "delegated_orchestration_override"
class HandoffModeError(ValueError):
@@ -104,138 +88,6 @@ def _resolve_state(
)
-def goal_handoff_mode_for_goal(
- *,
- registry_path: Path,
- goal_id: str,
- project: Path | None = None,
- state_file: Path | None = None,
-) -> str:
- return str(show_goal_handoff_mode(registry_path=registry_path, goal_id=goal_id,
- project=project, state_file=state_file)["handoff_mode"])
-
-
-def enter_todo_ownership_handoff_gate(
- stack: ExitStack,
- *,
- state_text: str,
- registry_path: Path,
- goal_id: str,
- todo_id: str,
- mutation_authority: dict[str, Any],
- actor_agent_id: str | None,
- ownership_mutation: bool,
- runtime_root: Path | None = None,
-) -> dict[str, Any]:
- """Gate one claimed_by mutation on an existing todo behind the goal mode.
-
- Returns additive payload fields (always ``handoff_mode``; plus the door
- marker or the held holder-gate receipt in hard_lease mode). In hard_lease
- mode the per-goal lease lock is entered on ``stack`` so it stays held
- through the markdown commit, matching the completion fence's
- state-lock-then-lease-lock order.
- """
-
- mode = goal_handoff_mode(state_text)
- extras: dict[str, Any] = {"handoff_mode": mode}
- gate = ownership_gate_requirement(
- handoff_mode=HandoffMode(mode),
- ownership_mutation=ownership_mutation,
- authority_mode=str(mutation_authority.get("mode") or "") or None,
- )
- if gate is OwnershipGate.NOT_REQUIRED:
- return extras
- if gate is OwnershipGate.DELEGATED_OVERRIDE:
- extras["handoff_gate_overridden"] = True
- return extras
- from ..work_items.task_lease import hold_handoff_lease_holder_gate
-
- extras["task_lease_holder_gate"] = stack.enter_context(
- hold_handoff_lease_holder_gate(
- registry_path=registry_path,
- goal_id=goal_id,
- todo_id=todo_id,
- actor_agent_id=actor_agent_id,
- runtime_root=runtime_root,
- )
- )
- return extras
-
-
-def enter_added_todo_ownership_handoff_gate(
- stack: ExitStack,
- *,
- lines: list[str],
- state_text: str,
- registry_path: Path,
- goal_id: str,
- role: str,
- text: str,
- claimed_by: str | None,
- actor_agent_id: str | None,
- runtime_root: Path | None = None,
-) -> dict[str, Any]:
- """Gate the ``todo add`` path when it would reassign an existing todo.
-
- ``todo add`` matches an open todo by text and upserts its metadata, so a
- ``claimed_by`` argument on that branch changes the claim of a todo that
- may already hold a lease. Creating a todo stays ungated in every mode: a
- todo id that does not exist yet cannot hold one. Re-adding a todo with the
- claim it already carries is not an ownership change and is not gated.
-
- The delegated-authority door is not offered here. Reassignment under a
- ``coordination.todo_lifecycle_authority`` grant goes through
- ``todo update --claimed-by``, which is the verb that owns that action.
- """
-
- from .addition import matching_todo_block
- from .active_state_editing import section_bounds
-
- bounds = section_bounds(lines, role)
- existing = (
- matching_todo_block(
- lines, bounds[0], bounds[1], text, role=role, source_section=bounds[2]
- )
- if bounds
- else None
- )
- requested = normalize_todo_claimed_by(claimed_by) if claimed_by else None
- current = normalize_todo_claimed_by(existing.get("claimed_by")) if existing else None
- return enter_todo_ownership_handoff_gate(
- stack,
- state_text=state_text,
- registry_path=registry_path,
- goal_id=goal_id,
- todo_id=str(existing.get("todo_id") or "") if existing else "",
- mutation_authority={},
- actor_agent_id=actor_agent_id,
- ownership_mutation=(
- role == "agent"
- and existing is not None
- and requested is not None
- and requested != current
- ),
- runtime_root=runtime_root,
- )
-
-
-def resolve_todo_completion_handoff(
- *,
- state_text: str,
- mutation_authority: dict[str, Any],
-) -> dict[str, Any]:
- """Resolve mode plus the delegated-authority door for one todo completion."""
-
- mode = goal_handoff_mode(state_text)
- extras: dict[str, Any] = {"handoff_mode": mode}
- if (
- mode == HANDOFF_MODE_HARD_LEASE
- and mutation_authority.get("mode") == DELEGATED_AUTHORITY_MODE
- ):
- extras["handoff_gate_overridden"] = True
- return extras
-
-
def show_goal_handoff_mode(
*,
registry_path: Path,
@@ -273,21 +125,6 @@ def show_goal_handoff_mode(
}
-def _plan_legacy_mode(request: dict[str, Any]) -> dict[str, Any]:
- from ..effect_runtime import effect_runtime_result
-
- result = effect_runtime_result("coordination.handoff_mode.legacy_plan", request)
- if not isinstance(result, dict) or result.get("schema_version") != "loopx_legacy_handoff_mode_plan_result_v0":
- raise HandoffModeError("invalid typed handoff plan", code="handoff_mode_plan_unavailable")
- if result.get("outcome") == "rejected":
- raise HandoffModeError(
- "handoff mode change rejected; resolve the reported state or ownership blockers",
- code=result["code"], payload={key: value for key, value in result.items()
- if key not in {"schema_version", "outcome", "code", "changed"}},
- )
- return result
-
-
def set_goal_handoff_mode(
*,
registry_path: Path,
@@ -299,24 +136,7 @@ def set_goal_handoff_mode(
operation_id: str | None = None,
dry_run: bool = False,
) -> dict[str, Any]:
- """Set the authoritative goal mode; changed modes require quiescence.
-
- Promoted Goals use one provider transaction; the remaining text below
- describes the unpromoted compatibility writer.
-
- Changed modes require complete unclaimed Todo state and no time-active
- leases. Event source locks remain held through the frontmatter replacement.
- Hand-editing frontmatter bypasses this check and is outside the contract.
- """
-
- from ..coordination.legacy_writer_fence import (
- LegacyCoordinationWriterFenced,
- require_legacy_coordination_write_allowed,
- )
- from ..coordination.local_authority import (
- LocalCoordinationAuthorityRejection,
- LocalCoordinationAuthorityUnavailable,
- )
+ """Set the provider-owned mode; unpromoted Markdown is read-only in 2.0."""
requested = normalize_handoff_mode(mode)
if not str(mode or "").strip():
@@ -327,94 +147,10 @@ def set_goal_handoff_mode(
from .provider_handoff_mode import set_canonical_handoff_mode
runtime_root = effective_runtime_root(registry_path, runtime_root_arg)
- try:
- canonical = set_canonical_handoff_mode(
- runtime_root=runtime_root,
- goal_id=goal_id,
- mode=requested,
- operation_id=operation_id,
- dry_run=dry_run,
- )
- except LocalCoordinationAuthorityRejection:
- raise
- except LocalCoordinationAuthorityUnavailable:
- # A present legacy fence is the admission boundary for this caller.
- # Re-check it when canonical dispatch is unavailable so an outage cannot
- # turn a fenced legacy writer into an attempted Markdown mutation.
- try:
- require_legacy_coordination_write_allowed(
- runtime_root=runtime_root,
- goal_id=goal_id,
- )
- except LegacyCoordinationWriterFenced:
- raise
- raise
- if canonical is not None:
- return canonical
- if operation_id is not None:
- raise HandoffModeError("--operation-id requires canonical authority", code="handoff_mode_operation_id_unsupported")
- # Compatibility source IO is needed only after canonical routing declines.
- # Promoted commands and provider failures must survive producer retirement.
- from .handoff_mode_source import handoff_mode_source
- from ..coordination.legacy_writer_fence import legacy_todo_write_transaction
- from ..coordination.runtime_shadow_writer_adapter import (
- write_captured_todo_state,
- begin_todo_runtime_shadow_capture,
- settle_todo_runtime_shadow_capture,
- )
-
- _project, resolved_state_file = _resolve_state(
- registry_path=registry_path,
+ return set_canonical_handoff_mode(
+ runtime_root=runtime_root,
goal_id=goal_id,
- project=project,
- state_file=state_file,
- )
- # The already resolved root also governs the legacy lock, scan and capture.
- with legacy_todo_write_transaction(
- registry_path, goal_id, resolved_state_file, None, "handoff_mode_set",
- dry_run, runtime_root=runtime_root,
- ):
- with resolved_state_file.open(encoding="utf-8", newline="") as source:
- original = source.read()
- metadata, body = split_state_frontmatter(original)
- frontmatter = original[:len(original) - len(body)]
- request = {
- "schema_version": "loopx_legacy_handoff_mode_plan_request_v0",
- "previous_value": metadata.get(HANDOFF_MODE_FRONTMATTER_KEY),
- "requested_mode": requested, "frontmatter_text": frontmatter,
- "todos": None, "leases": None,
- }
- plan = _plan_legacy_mode(request)
- payload = {
- "ok": True, "schema_version": HANDOFF_MODE_SCHEMA_VERSION,
- "action": "set", "goal_id": goal_id, "state_file": str(resolved_state_file),
- **{key: value for key, value in plan.items() if key.startswith("previous_mode")},
- "handoff_mode": requested, "changed": False,
- **({"dry_run": True} if dry_run else {}),
- }
- if plan["outcome"] == "no_change":
- return payload
- if plan["outcome"] != "snapshot_required":
- raise HandoffModeError("unexpected handoff planning phase", code="handoff_mode_plan_unavailable")
- with handoff_mode_source(registry_path=registry_path, goal_id=goal_id,
- state_path=resolved_state_file, state_text=original, runtime_root=runtime_root) as facts:
- try:
- plan = _plan_legacy_mode({**request, **facts})
- except HandoffModeError as error:
- error.payload.update(goal_id=goal_id, requested_mode=requested)
- raise
- if plan.get("outcome") != "apply" or not isinstance(plan.get("next_frontmatter_text"), str):
- raise HandoffModeError("incomplete handoff mutation plan", code="handoff_mode_plan_unavailable")
- if dry_run:
- return {**payload, "changed": True}
- capture = begin_todo_runtime_shadow_capture(
- registry_path=registry_path, runtime_root=runtime_root, goal_id=goal_id,
- state_path=resolved_state_file, write_class="handoff_mode_set", original_text=original)
- write_captured_todo_state(capture, runtime_root=runtime_root, goal_id=goal_id,
- state_path=resolved_state_file, text=plan["next_frontmatter_text"] + body)
- payload["changed"] = True
- return settle_todo_runtime_shadow_capture(
- payload, registry_path=registry_path, runtime_root=runtime_root,
- goal_id=goal_id, capture=capture,
- emit_disabled=False,
+ mode=requested,
+ operation_id=operation_id,
+ dry_run=dry_run,
)
diff --git a/loopx/control_plane/todos/handoff_mode_source.py b/loopx/control_plane/todos/handoff_mode_source.py
deleted file mode 100644
index 3ab28a85b7..0000000000
--- a/loopx/control_plane/todos/handoff_mode_source.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""Locked compatibility inputs for the typed handoff transition planner.
-
-The caller holds the active-state mutex; local leases share their existing
-mutex. Retired Todo event sources are rejected before quiescence is projected.
-"""
-from __future__ import annotations
-
-from contextlib import ExitStack, contextmanager
-from collections.abc import Iterator
-from pathlib import Path
-from typing import Any
-
-
-@contextmanager
-def handoff_mode_source(
- *, registry_path: Path, goal_id: str, state_path: Path, state_text: str,
- runtime_root: Path,
-) -> Iterator[dict[str, Any]]:
- from ..goals.legacy_event_source import require_no_legacy_todo_events, RetiredTodoEventSourceError
- from ...file_lock import exclusive_cross_runtime_file_lock
- from ..runtime.time import now_local_iso
- from ..work_items.task_lease import read_lease, task_lease_dir, task_lease_lock_path
- from .goal_todo_projection import project_goal_todo_items
- from .handoff_mode import HandoffModeError
-
- from ...history import load_registry
- from ...registry import find_registry_goal
- goal = find_registry_goal(load_registry(registry_path), goal_id) or {"id": goal_id}
- with ExitStack() as stack:
- stack.enter_context(exclusive_cross_runtime_file_lock(
- task_lease_lock_path(runtime_root=runtime_root, goal_id=goal_id), operation="handoff_mode_set"))
- try:
- require_no_legacy_todo_events(goal, state_path=state_path)
- todos = project_goal_todo_items(goal, state_text=state_text,
- state_path=state_path, rollout_events=[])
- except (OSError, RetiredTodoEventSourceError) as error:
- raise HandoffModeError("cannot establish handoff quiescence from the legacy source",
- code="handoff_mode_source_unavailable") from error
- fields = ("todo_id", "done", "status", "claimed_by", "archive_state")
- leases = []
- for path in sorted(task_lease_dir(runtime_root=runtime_root, goal_id=goal_id).glob("todo_*.json")):
- lease = read_lease(path)
- if lease is not None:
- leases.append({**lease, "lease_path": str(path)})
- yield {"todos": [{key: item[key] for key in fields if key in item} for item in todos],
- "leases": leases, "observed_at": now_local_iso()}
diff --git a/loopx/control_plane/todos/legacy_continuation_policy_migration.ts b/loopx/control_plane/todos/legacy_continuation_policy_migration.ts
index 9b408dbf38..3a174372d3 100644
--- a/loopx/control_plane/todos/legacy_continuation_policy_migration.ts
+++ b/loopx/control_plane/todos/legacy_continuation_policy_migration.ts
@@ -13,6 +13,12 @@ function existingAgent(value: unknown): string | null {
}
}
+function isPublicPlannerRepairDefault(field: string, value: unknown): boolean {
+ if (field === "clear_user_binding") return value === false;
+ return ["bound_agent", "goal_bound", "resume_when", "resume_monitor_generation"].includes(field) &&
+ value === null;
+}
+
/** Recognize the retained legacy policy markers without authorizing a repair. */
export function removedTodoContinuationPolicy(value: unknown): "primary_review" | "review_handoff" | null {
const policy = stripPythonWhitespace(String(value ?? "")).toLowerCase();
@@ -44,4 +50,11 @@ export function validateLegacyContinuationPolicyRepair(
"excluded_agents=",
);
}
+ if (Object.entries(intent).some(([field, value]) =>
+ field !== "continuation_policy" && field !== "excluded_agents" &&
+ !isPublicPlannerRepairDefault(field, value))) {
+ throw new EffectRuntimeRequestError(
+ prefix + "repair must be submitted without other Todo edits",
+ );
+ }
}
diff --git a/loopx/control_plane/todos/legacy_mutation.py b/loopx/control_plane/todos/legacy_mutation.py
deleted file mode 100644
index 6b7d846eb1..0000000000
--- a/loopx/control_plane/todos/legacy_mutation.py
+++ /dev/null
@@ -1,1535 +0,0 @@
-"""Retained unpromoted Markdown writer; canonical callers never import this module."""
-
-from __future__ import annotations
-
-from collections.abc import Mapping
-from contextlib import ExitStack
-from json import dumps as json_dumps
-from pathlib import Path
-from typing import Any
-
-from ...agent_registry import (
- registered_agent_ids_from_registry,
- require_registered_agent_id,
-)
-from ...state_refresh import now_local
-from .contract import (
- TODO_STATUS_DONE,
- TODO_STATUS_OPEN,
- build_todo_id,
- format_todo_metadata_line,
- metadata_line_for_todo_block,
- normalize_required_capabilities,
- normalize_required_write_scopes,
- normalize_explore_result_node_refs,
- normalize_target_capabilities,
- normalize_todo_blocks_agent,
- normalize_todo_bound_agent,
- normalize_todo_capability_binding_ref,
- normalize_todo_claimed_by,
- normalize_todo_continuation_policy,
- normalize_todo_decision_scope,
- normalize_todo_excluded_agents,
- normalize_todo_global_gate,
- normalize_todo_goal_bound,
- normalize_todo_id,
- normalize_todo_id_list,
- normalize_todo_required_decision_scopes,
- normalize_todo_replan_obligation_id,
- normalize_todo_resume_when,
- normalize_todo_status,
- normalize_todo_task_domain,
- normalize_todo_task_repository,
- parse_todo_metadata_line,
- require_todo_excluded_agents,
- resolve_next_user_task_class,
- require_supported_todo_resume_when,
- todo_marker_for_status,
-)
-from .active_state_editing import (
- TODO_SECTION_HEADINGS,
- find_todo_block,
- insert_into_existing_section,
- insert_new_section,
- replace_updated_at,
- section_bounds,
- set_todo_marker,
- todo_blocks,
-)
-from .addition import matching_todo_block, require_replan_successor_rebinding
-from .completed_archive import (
- DEFAULT_COMPLETED_TODO_ARCHIVE_HEADROOM,
- DEFAULT_MAX_ACTIVE_DONE_TODOS_BEFORE_ARCHIVE,
- archive_completed_todo_lines,
-)
-from .completion_policy import (
- completion_policy_from_transaction,
-)
-from .completion_transaction import (
- locked_todo_completion_transaction,
- materialized_todo_completion_replay,
- require_completion_successor_todo_ids,
- user_todo_completion_metadata_updates,
-)
-from ..todos import completion_validation as completion_validation_module
-from .mutation_response import serialize_added_todo_payload
-from .next_action_runtime import (
- apply_added_todo_next_action,
- settle_completed_todo_next_action,
-)
-from ..todos import monitor_metadata as todo_monitor_metadata
-from .mutation_authority import (
- authorize_todo_lifecycle_mutation,
- todo_update_authority_action,
-)
-from .succession_warning import build_open_parent_successor_advisory
-from .successor_derivation import (
- build_successor_intents,
- derive_successor_proposals,
- successor_add_kwargs,
-)
-from .text import normalize_new_todo
-from .todo_semantics import todo_priority_label
-from .unblock_resume import (
- apply_completed_user_todo_lifecycle,
- completion_decision_target,
- require_completion_decision_outcome,
-)
-from .write_correctness import (
- attach_todo_write_correctness_dry_run_packet as _attach_todo_write_correctness_dry_run_packet,
-)
-from .authoring_scope import require_user_todo_task_class
-from ..coordination.legacy_writer_fence import legacy_todo_write_transaction
-from .path_resolution import resolve_todo_state_path
-from .handoff_mode import (
- goal_handoff_mode,
- enter_added_todo_ownership_handoff_gate,
- enter_todo_ownership_handoff_gate,
- resolve_todo_completion_handoff,
-)
-from ...paths import effective_runtime_root
-from ..work_items.task_lease import (
- enter_terminal_todo_lease_fence,
- hold_task_lease_mutation_fence,
- release_verified_task_lease_fence,
-)
-
-
-ARCHIVE_COMPLETED_DEFAULT_MAX_ACTIVE_DONE = max(
- 0, DEFAULT_MAX_ACTIVE_DONE_TODOS_BEFORE_ARCHIVE - DEFAULT_COMPLETED_TODO_ARCHIVE_HEADROOM
-)
-
-
-def _validate_legacy_add_declaration(
- *,
- role: str,
- task_class: str | None,
- blocks_agent: str | None,
- excluded_agents: list[str] | None,
- capability_binding_ref: str | None,
- global_gate: bool | None,
- validation_command: str | None,
- validation_command_json: str | None,
- validation_timeout_seconds: int | None,
-) -> list[str] | None:
- """Validate the retained writer's declaration before any line mutation."""
- if validation_command and validation_command_json:
- raise ValueError(
- "--validation-command and --validation-command-json are mutually "
- "exclusive; declare the validation command in exactly one form"
- )
- validation_argv = completion_validation_module.normalize_validation_command_json(
- validation_command_json
- )
- if validation_timeout_seconds is not None:
- if not validation_command and validation_argv is None:
- raise ValueError(
- "--validation-timeout-seconds requires --validation-command "
- "or --validation-command-json"
- )
- if not (
- 1
- <= validation_timeout_seconds
- <= completion_validation_module.COMPLETION_VALIDATION_TIMEOUT_MAX_SECONDS
- ):
- raise ValueError(
- "--validation-timeout-seconds must be between 1 and "
- f"{completion_validation_module.COMPLETION_VALIDATION_TIMEOUT_MAX_SECONDS} (the outer "
- "CLI/MCP subprocess budget is 30s, and a timed-out "
- "validation must still produce a typed receipt)"
- )
- if role == "agent" and blocks_agent:
- raise ValueError(
- "blocks_agent is only valid for user gates; use excluded_agents for "
- "agent executor constraints"
- )
- if role != "agent" and excluded_agents:
- raise ValueError("excluded_agents is only valid for agent todos")
- if role != "agent" and capability_binding_ref:
- raise ValueError("capability_binding_ref is only valid for agent todos")
- require_user_todo_task_class(
- role=role,
- task_class=task_class,
- blocks_agent=blocks_agent,
- global_gate=global_gate,
- )
- return validation_argv
-
-
-def add_todo_to_lines(
- lines: list[str],
- *,
- role: str,
- text: str,
- status: str | None = None,
- task_class: str | None = None,
- action_kind: str | None = None,
- task_domain: str | None = None,
- capability_binding_ref: str | None = None,
- task_repository: str | None = None,
- continuation_policy: str | None = None,
- required_write_scopes: list[str] | None = None,
- required_capabilities: list[str] | None = None,
- target_capabilities: list[str] | None = None,
- explore_result_node_refs: list[str] | None = None,
- decision_scope: Any = None,
- required_decision_scopes: Any = None,
- claimed_by: str | None = None,
- bound_agent: str | None = None,
- goal_bound: bool | None = None,
- blocks_agent: str | None = None,
- excluded_agents: list[str] | None = None,
- global_gate: bool | None = None,
- unblocks_todo_id: str | None = None,
- replan_obligation_id: str | None = None,
- resume_when: str | None = None,
- validation_command: str | None = None,
- validation_command_json: str | None = None,
- validation_label: str | None = None,
- validation_timeout_seconds: int | None = None,
- monitor_metadata: dict[str, Any] | None = None,
- note: str | None = None,
- evidence: str | None = None,
- updated_at: str | None = None,
- _prepared: dict[str, Any] | None = None,
-) -> dict[str, Any]:
- from .line_update import upsert_todo_metadata
-
- # Only public add supplies its same-call typed draft. Standalone line
- # codecs and successor creation still validate their own complete input.
- if _prepared is not None:
- validation_argv = _prepared["validation_argv"]
- todo_text = _prepared["todo_text"]
- normalized_status = _prepared["normalized_status"]
- normalized_resume_when = _prepared["normalized_resume_when"]
- normalized_monitor_metadata = _prepared["normalized_monitor_metadata"]
- else:
- validation_argv = _validate_legacy_add_declaration(
- role=role,
- task_class=task_class,
- blocks_agent=blocks_agent,
- excluded_agents=excluded_agents,
- capability_binding_ref=capability_binding_ref,
- global_gate=global_gate,
- validation_command=validation_command,
- validation_command_json=validation_command_json,
- validation_timeout_seconds=validation_timeout_seconds,
- )
- todo_text = normalize_new_todo(text)
- normalized_status = normalize_todo_status(status) if status else TODO_STATUS_OPEN
- if status and not normalized_status:
- raise ValueError("todo status must be one of: open, done, blocked, deferred")
- assert normalized_status is not None
- normalized_resume_when = require_supported_todo_resume_when(resume_when)
- normalized_monitor_metadata = todo_monitor_metadata.require_monitor_metadata_scope(
- monitor_metadata=monitor_metadata,
- role=role,
- task_class=task_class, generated_at=updated_at,
- )
- bounds = section_bounds(lines, role)
- section = bounds[2] if bounds else TODO_SECTION_HEADINGS[role]
- existing_blocks = (
- todo_blocks(lines, bounds[0], bounds[1], role=role, source_section=section)
- if bounds
- else []
- )
- block = matching_todo_block(
- lines,
- bounds[0],
- bounds[1],
- todo_text,
- role=role,
- source_section=section,
- ) if bounds else None
- added = block is None
- metadata_updated = False
- status_changed = False
-
- if block is None:
- todo_id = build_todo_id(
- role=role,
- source_section=section,
- index=len(existing_blocks) + 1,
- text=todo_text,
- )
- metadata_line = format_todo_metadata_line(
- todo_id=todo_id,
- status=normalized_status,
- task_class=task_class,
- action_kind=action_kind,
- task_domain=task_domain,
- capability_binding_ref=capability_binding_ref,
- task_repository=task_repository,
- continuation_policy=continuation_policy,
- required_write_scopes=required_write_scopes,
- required_capabilities=required_capabilities,
- target_capabilities=target_capabilities,
- explore_result_node_refs=explore_result_node_refs,
- decision_scope=decision_scope,
- required_decision_scopes=required_decision_scopes,
- claimed_by=claimed_by,
- bound_agent=bound_agent,
- goal_bound=goal_bound,
- blocks_agent=blocks_agent,
- excluded_agents=excluded_agents,
- global_gate=global_gate,
- unblocks_todo_id=unblocks_todo_id,
- replan_obligation_id=replan_obligation_id,
- resume_when=normalized_resume_when,
- validation_command=validation_command,
- validation_command_argv=(
- json_dumps(validation_argv)
- if validation_argv is not None
- else None
- ),
- validation_label=validation_label,
- validation_timeout_seconds=(
- str(validation_timeout_seconds)
- if validation_timeout_seconds is not None
- else None
- ),
- **normalized_monitor_metadata,
- note=note,
- evidence=evidence,
- updated_at=updated_at,
- )
- marker = todo_marker_for_status(normalized_status)
- todo_line = "\n".join([f"- [{marker}] {todo_text}", metadata_line] if metadata_line else [f"- [{marker}] {todo_text}"])
- if bounds:
- insert_into_existing_section(lines, bounds[0], bounds[1], todo_line)
- else:
- insert_new_section(lines, role, todo_line)
- effective_metadata = parse_todo_metadata_line(metadata_line or "") or {}
- else:
- updates: dict[str, Any] = {
- "todo_id": block.get("todo_id"),
- "status": normalized_status if status else block.get("status") or TODO_STATUS_OPEN,
- }
- if status:
- status_changed = set_todo_marker(lines, block, normalized_status)
- for metadata_field, metadata_value in (
- ("task_class", task_class),
- ("action_kind", action_kind),
- ("task_domain", task_domain),
- ("task_repository", task_repository),
- ("continuation_policy", continuation_policy),
- ("claimed_by", claimed_by),
- ("blocks_agent", blocks_agent),
- ("unblocks_todo_id", unblocks_todo_id),
- ("note", note),
- ("evidence", evidence),
- ):
- if metadata_value:
- updates[metadata_field] = metadata_value
- if capability_binding_ref:
- requested_binding_ref = normalize_todo_capability_binding_ref(
- capability_binding_ref
- )
- if not requested_binding_ref:
- raise ValueError(
- "capability_binding_ref must be a public-safe namespaced token"
- )
- existing_binding_ref = normalize_todo_capability_binding_ref(
- block.get("capability_binding_ref")
- )
- if (
- existing_binding_ref
- and existing_binding_ref != requested_binding_ref
- ):
- raise ValueError(
- "capability_binding_ref is immutable once set"
- )
- updates["capability_binding_ref"] = requested_binding_ref
- if required_write_scopes is not None:
- updates["required_write_scopes"] = required_write_scopes
- if required_capabilities is not None:
- updates["required_capabilities"] = required_capabilities
- if target_capabilities is not None:
- updates["target_capabilities"] = target_capabilities
- if explore_result_node_refs is not None:
- updates["explore_result_node_refs"] = explore_result_node_refs
- if decision_scope is not None:
- updates["decision_scope"] = decision_scope
- if required_decision_scopes is not None:
- updates["required_decision_scopes"] = required_decision_scopes
- if bound_agent:
- updates["bound_agent"] = bound_agent
- updates["goal_bound"] = None
- elif goal_bound is not None:
- updates["bound_agent"] = None
- updates["goal_bound"] = goal_bound
- if excluded_agents is not None:
- updates["excluded_agents"] = excluded_agents
- if global_gate is not None:
- updates["global_gate"] = global_gate
- if replan_obligation_id:
- updates["replan_obligation_id"] = require_replan_successor_rebinding(
- existing_obligation_id=block.get("replan_obligation_id"),
- requested_obligation_id=replan_obligation_id,
- )
- if normalized_resume_when:
- updates["resume_when"] = normalized_resume_when
- updates.update(normalized_monitor_metadata)
- if updated_at and not block.get("updated_at"):
- updates["updated_at"] = updated_at
- metadata_line = metadata_line_for_todo_block(block, updates)
- metadata_updated = upsert_todo_metadata(lines, block, metadata_line)
- todo_id = str(block.get("todo_id") or "")
- effective_metadata = parse_todo_metadata_line(metadata_line or "") or {}
-
- return {
- "added": added,
- "already_exists": not added,
- "metadata_updated": metadata_updated,
- "status_changed": status_changed,
- "changed": added or metadata_updated or status_changed,
- "role": role,
- "section": section,
- "todo": todo_text,
- "priority": todo_priority_label({"text": todo_text}),
- "todo_id": todo_id,
- "status": normalize_todo_status(effective_metadata.get("status")) or normalized_status,
- "task_class": effective_metadata.get("task_class") or task_class,
- "action_kind": effective_metadata.get("action_kind") or action_kind,
- "task_domain": normalize_todo_task_domain(
- effective_metadata.get("task_domain") or task_domain
- ),
- "capability_binding_ref": effective_metadata.get("capability_binding_ref")
- or capability_binding_ref,
- "task_repository": normalize_todo_task_repository(
- effective_metadata.get("task_repository") or task_repository
- ),
- "continuation_policy": normalize_todo_continuation_policy(
- effective_metadata.get("continuation_policy") or continuation_policy
- ),
- "required_write_scopes": normalize_required_write_scopes(
- effective_metadata.get("required_write_scopes") or required_write_scopes
- ),
- "required_capabilities": normalize_required_capabilities(
- effective_metadata.get("required_capabilities") or required_capabilities
- ),
- "target_capabilities": normalize_target_capabilities(
- effective_metadata.get("target_capabilities") or target_capabilities
- ),
- "explore_result_node_refs": normalize_explore_result_node_refs(
- effective_metadata.get("explore_result_node_refs") or explore_result_node_refs
- ),
- "decision_scope": normalize_todo_decision_scope(
- effective_metadata.get("decision_scope") or decision_scope
- ),
- "required_decision_scopes": normalize_todo_required_decision_scopes(
- effective_metadata.get("required_decision_scopes") or required_decision_scopes
- ),
- "claimed_by": normalize_todo_claimed_by(effective_metadata.get("claimed_by")),
- "bound_agent": normalize_todo_bound_agent(effective_metadata.get("bound_agent")),
- "goal_bound": normalize_todo_goal_bound(effective_metadata.get("goal_bound")),
- "blocks_agent": normalize_todo_blocks_agent(effective_metadata.get("blocks_agent")),
- "excluded_agents": normalize_todo_excluded_agents(
- effective_metadata.get("excluded_agents")
- ),
- "global_gate": normalize_todo_global_gate(effective_metadata.get("global_gate")),
- "unblocks_todo_id": normalize_todo_id(effective_metadata.get("unblocks_todo_id")),
- "replan_obligation_id": normalize_todo_replan_obligation_id(
- effective_metadata.get("replan_obligation_id")
- ),
- "resume_when": normalize_todo_resume_when(effective_metadata.get("resume_when")),
- "target_key": effective_metadata.get("target_key"),
- "cadence": effective_metadata.get("cadence"),
- "next_due_at": effective_metadata.get("next_due_at"),
- "expires_at": effective_metadata.get("expires_at"),
- "watch_only": effective_metadata.get("watch_only"),
- "note": effective_metadata.get("note") or note,
- "evidence": effective_metadata.get("evidence") or evidence,
- "updated_at": effective_metadata.get("updated_at") or updated_at,
- }
-
-
-def _add_goal_todo_legacy(
- *,
- registry_path: Path,
- goal_id: str,
- runtime_root_arg: str | None = None,
- role: str,
- text: str,
- priority: str | None = None,
- status: str | None = None,
- note: str | None = None,
- task_class: str | None = None,
- action_kind: str | None = None,
- task_domain: str | None = None,
- capability_binding_ref: str | None = None,
- task_repository: str | None = None,
- continuation_policy: str | None = None,
- required_write_scopes: list[str] | None = None,
- required_capabilities: list[str] | None = None,
- target_capabilities: list[str] | None = None,
- explore_result_node_refs: list[str] | None = None,
- decision_scope: Any = None,
- required_decision_scopes: Any = None,
- claimed_by: str | None = None,
- bound_agent: str | None = None,
- goal_bound: bool = False,
- blocks_agent: str | None = None,
- excluded_agents: list[str] | None = None,
- global_gate: bool = False,
- agent_id: str | None = None,
- unblocks_todo_id: str | None = None,
- replan_obligation_id: str | None = None,
- resume_when: str | None = None,
- validation_command: str | None = None,
- validation_command_json: str | None = None,
- validation_label: str | None = None,
- validation_timeout_seconds: int | None = None,
- monitor_metadata: dict[str, Any] | None = None,
- project: Path | None = None,
- state_file: Path | None = None,
- dry_run: bool = False,
- operation_id: str | None = None,
- _prepared: dict[str, Any],
-) -> dict[str, Any]:
- effective_agent_id = _prepared["effective_agent_id"]
- effective_blocks_agent = _prepared["effective_blocks_agent"]
- effective_bound_agent = _prepared["effective_bound_agent"]
- effective_claimed_by = _prepared["effective_claimed_by"]
- effective_excluded_agents = _prepared["effective_excluded_agents"]
- effective_goal_bound = _prepared["effective_goal_bound"]
- normalized_monitor_metadata = _prepared["normalized_monitor_metadata"]
- normalized_resume_when = _prepared["normalized_resume_when"]
- normalized_status = _prepared["normalized_status"]
- normalized_unblocks_todo_id = _prepared["normalized_unblocks_todo_id"]
- replan_obligation_id = _prepared["replan_obligation_id"]
- shadow_runtime_root = _prepared["shadow_runtime_root"]
- todo_text = _prepared["todo_text"]
- updated_at = _prepared["updated_at"]
- from ..coordination.runtime_shadow_writer_adapter import begin_todo_runtime_shadow_capture, write_captured_todo_state, settle_todo_runtime_shadow_capture
-
- resolved_project, resolved_state_file = resolve_todo_state_path(
- registry_path=registry_path,
- goal_id=goal_id,
- project=project,
- state_file=state_file,
- )
-
- with legacy_todo_write_transaction(
- registry_path, goal_id, resolved_state_file, agent_id or claimed_by, "todo_add", dry_run,
- runtime_root=shadow_runtime_root,
- ), ExitStack() as handoff_gate_stack:
- original = resolved_state_file.read_text(encoding="utf-8")
- shadow_capture = begin_todo_runtime_shadow_capture(
- registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, state_path=resolved_state_file,
- write_class="todo_add", original_text=original,
- )
- lines = original.splitlines()
- handoff_gate = enter_added_todo_ownership_handoff_gate(
- handoff_gate_stack,
- lines=lines,
- state_text=original,
- registry_path=registry_path,
- goal_id=goal_id,
- role=role,
- text=todo_text,
- claimed_by=effective_claimed_by,
- actor_agent_id=effective_agent_id or effective_claimed_by,
- runtime_root=shadow_runtime_root,
- )
- add_result = add_todo_to_lines(
- lines,
- role=role,
- text=todo_text,
- status=normalized_status,
- task_class=task_class,
- action_kind=action_kind,
- task_domain=task_domain,
- capability_binding_ref=capability_binding_ref,
- task_repository=task_repository,
- continuation_policy=continuation_policy,
- required_write_scopes=required_write_scopes,
- required_capabilities=required_capabilities,
- target_capabilities=target_capabilities,
- explore_result_node_refs=explore_result_node_refs,
- decision_scope=decision_scope,
- required_decision_scopes=required_decision_scopes,
- claimed_by=effective_claimed_by,
- bound_agent=effective_bound_agent,
- goal_bound=(
- True if role == "user" and effective_goal_bound else None
- ),
- blocks_agent=effective_blocks_agent,
- excluded_agents=effective_excluded_agents,
- global_gate=True if global_gate else None,
- unblocks_todo_id=normalized_unblocks_todo_id,
- replan_obligation_id=replan_obligation_id,
- resume_when=normalized_resume_when,
- validation_command=validation_command,
- validation_command_json=validation_command_json,
- validation_label=validation_label,
- validation_timeout_seconds=validation_timeout_seconds,
- monitor_metadata=normalized_monitor_metadata,
- note=note,
- updated_at=updated_at,
- _prepared=_prepared,
- )
- added = bool(add_result["added"])
- metadata_updated = bool(add_result["metadata_updated"])
- changed = apply_added_todo_next_action(lines, role=role, add_result=add_result)
-
- new_text = "\n".join(lines) + ("\n" if original.endswith("\n") else "")
- if changed:
- new_text = replace_updated_at(new_text, updated_at)
- if changed and not dry_run:
- write_captured_todo_state(shadow_capture, runtime_root=shadow_runtime_root, goal_id=goal_id,
- state_path=resolved_state_file, text=new_text)
-
- payload = serialize_added_todo_payload(
- add_result=add_result, goal_id=goal_id, role=role, todo_text=todo_text,
- agent_id=effective_agent_id, state_file=resolved_state_file,
- project=resolved_project, updated_at=updated_at, dry_run=dry_run,
- added=added, metadata_updated=metadata_updated, changed=changed,
- handoff_gate=handoff_gate,
- )
- payload = _attach_todo_write_correctness_dry_run_packet(
- payload,
- goal_id=goal_id,
- write_class="todo_add",
- state_text=original,
- )
- return settle_todo_runtime_shadow_capture(
- payload, registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, capture=shadow_capture,
- )
-
-
-def _update_goal_todo_legacy(
- *,
- registry_path: Path,
- goal_id: str,
- runtime_root_arg: str | None = None,
- todo_id: str,
- text: str | None = None,
- priority: str | None = None,
- clear_priority: bool = False,
- status: str | None = None,
- role: str | None = None,
- note: str | None = None,
- validation_command: str | None = None,
- validation_command_json: str | None = None,
- validation_label: str | None = None,
- validation_timeout_seconds: int | None = None,
- evidence: str | None = None,
- reason: str | None = None,
- task_class: str | None = None,
- action_kind: str | None = None,
- task_domain: str | None = None,
- task_repository: str | None = None,
- continuation_policy: str | None = None,
- required_write_scopes: list[str] | None = None,
- required_capabilities: list[str] | None = None,
- target_capabilities: list[str] | None = None,
- explore_result_node_refs: list[str] | None = None,
- append_explore_result_node_refs: list[str] | None = None,
- decision_scope: Any = None,
- required_decision_scopes: Any = None,
- claimed_by: str | None = None,
- bound_agent: str | None = None,
- goal_bound: bool = False,
- blocks_agent: str | None = None,
- clear_blocks_agent: bool = False,
- excluded_agents: list[str] | None = None,
- clear_excluded_agents: bool = False,
- global_gate: bool = False, clear_global_gate: bool = False,
- agent_id: str | None = None, authority_reason: str | None = None,
- unblocks_todo_id: str | None = None,
- successor_todo_ids: list[str] | None = None,
- resume_when: str | None = None,
- clear_resume_when: bool = False,
- no_followup: bool | None = None,
- monitor_metadata: todo_monitor_metadata.MonitorMetadataInput = None,
- enforce_monitor_boundedness: bool = True,
- monitor_gate_scope_guard: bool = False,
- clear_claim: bool = False,
- claim_only: bool = False,
- claim_operation_id: str | None = None,
- update_operation_id: str | None = None,
- update_expected_provider_revision: str | None = None,
- update_expected_registry_sha256: str | None = None,
- task_lease_idempotency_key: str | None = None,
- task_lease_expected_version: int | None = None,
- project: Path | None = None,
- state_file: Path | None = None,
- dry_run: bool = False,
- _prepared: dict[str, Any],
-) -> dict[str, Any]:
- monitor_intent = _prepared["monitor_intent"]
- shadow_runtime_root = _prepared["shadow_runtime_root"]
- from .line_update import apply_todo_update_to_lines
- from ..coordination.runtime_shadow_writer_adapter import begin_todo_runtime_shadow_capture, write_captured_todo_state, settle_todo_runtime_shadow_capture
-
- resolved_project, resolved_state_file = resolve_todo_state_path(
- registry_path=registry_path,
- goal_id=goal_id,
- project=project,
- state_file=state_file,
- )
- requested_successor_todo_ids = require_completion_successor_todo_ids(
- successor_todo_ids
- )
- completion_validation_gate = (
- completion_validation_module.prepare_user_todo_update_completion(
- status=status,
- state_file=resolved_state_file,
- todo_id=todo_id,
- role=role,
- registry_path=registry_path,
- goal_id=goal_id,
- dry_run=dry_run,
- no_followup=no_followup is True,
- requested_has_successor=bool(requested_successor_todo_ids),
- )
- )
- if completion_validation_gate is not None:
- validation_failure: dict[str, Any] | None = completion_validation_gate.get("failure")
- if validation_failure is not None:
- return validation_failure
- write_class = "todo_claim" if claim_only else "todo_update"
- with legacy_todo_write_transaction(
- registry_path, goal_id, resolved_state_file, agent_id or claimed_by, write_class, dry_run,
- runtime_root=shadow_runtime_root,
- ), ExitStack() as handoff_gate_stack:
- original = resolved_state_file.read_text(encoding="utf-8")
- shadow_capture = begin_todo_runtime_shadow_capture(
- registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, state_path=resolved_state_file,
- write_class=write_class, original_text=original,
- )
- lines = original.splitlines()
- updated_at = now_local()
- effective_claimed_by = (
- require_registered_agent_id(
- registry_path=registry_path,
- goal_id=goal_id,
- agent_id=claimed_by,
- )
- if claimed_by
- else None
- )
- effective_agent_id = (
- require_registered_agent_id(
- registry_path=registry_path,
- goal_id=goal_id,
- agent_id=agent_id,
- field="agent_id",
- )
- if agent_id
- else None
- )
- effective_blocks_agent = (
- require_registered_agent_id(
- registry_path=registry_path,
- goal_id=goal_id,
- agent_id=blocks_agent,
- field="blocks_agent",
- )
- if blocks_agent
- else None
- )
- effective_bound_agent = (
- require_registered_agent_id(
- registry_path=registry_path,
- goal_id=goal_id,
- agent_id=bound_agent,
- field="bound_agent",
- )
- if bound_agent
- else None
- )
- existing_block_match = find_todo_block(lines, todo_id=todo_id, role=role)
- if not existing_block_match:
- normalized_todo_id = normalize_todo_id(todo_id) or todo_id
- raise ValueError(f"todo_id {normalized_todo_id!r} was not found in active user or agent todos")
- existing_role, _section, _start, _end, existing_block = existing_block_match
- target_role = role or existing_role
- authority_todo = dict(existing_block)
- authority_todo["role"] = target_role
- authority_action = todo_update_authority_action(
- existing_role=existing_role,
- role=role,
- claimed_by=claimed_by,
- clear_claim=clear_claim,
- other_values=(
- text, status, note, evidence, reason, task_class, action_kind,
- task_domain,
- task_repository, continuation_policy, required_write_scopes,
- required_capabilities, target_capabilities,
- explore_result_node_refs, append_explore_result_node_refs, decision_scope,
- required_decision_scopes, blocks_agent, clear_blocks_agent,
- bound_agent, goal_bound,
- excluded_agents, clear_excluded_agents, global_gate,
- clear_global_gate, unblocks_todo_id, successor_todo_ids,
- resume_when, clear_resume_when, no_followup,
- ),
- monitor_metadata=monitor_intent["observation"] or monitor_intent["metadata"],
- )
- mutation_authority = authorize_todo_lifecycle_mutation(
- registry_path=registry_path,
- goal_id=goal_id,
- command="claim" if claim_only else "update",
- todo=authority_todo,
- actor_agent_id=effective_agent_id,
- authority_action=None if claim_only else authority_action,
- authority_reason=authority_reason,
- requested_claimed_by=effective_claimed_by,
- )
- if monitor_gate_scope_guard:
- todo_monitor_metadata.require_locked_monitor_gate_scope(state_text=original,
- todo=authority_todo, observation=monitor_intent["observation"], agent_id=effective_agent_id)
- if monitor_intent["observation"] is not None and task_lease_idempotency_key is not None:
- # Explicit observation proof uses the existing native held fence
- # under the Markdown writer lock. Closing this guard does not retire
- # execution; the acquiring caller owns release after observation.
- handoff_gate_stack.enter_context(hold_task_lease_mutation_fence(
- registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, todo_id=todo_id, todo=authority_todo, actor_agent_id=effective_agent_id,
- idempotency_key=task_lease_idempotency_key,
- expected_version=task_lease_expected_version,
- require_active_when_key_supplied=True,
- handoff={"handoff_mode": goal_handoff_mode(original)},
- ))
- handoff_gate = enter_todo_ownership_handoff_gate(
- handoff_gate_stack,
- state_text=original,
- registry_path=registry_path,
- goal_id=goal_id,
- todo_id=str(authority_todo.get("todo_id") or todo_id),
- mutation_authority=mutation_authority,
- actor_agent_id=effective_agent_id or effective_claimed_by,
- ownership_mutation=(claimed_by is not None or clear_claim) and target_role == "agent",
- runtime_root=shadow_runtime_root,
- )
- effective_excluded_agents = (
- [] if clear_excluded_agents else require_todo_excluded_agents(excluded_agents)
- if excluded_agents is not None else None
- )
- completion_metadata_updates_override = None
- if completion_validation_gate is not None:
- locked_completion = locked_todo_completion_transaction(
- validation_gate=completion_validation_gate,
- todo=existing_block,
- goal_id=goal_id,
- todo_id=todo_id,
- dry_run=dry_run,
- require_source_match=True,
- missing_is_drift=False,
- )
- locked_failure: dict[str, Any] | None = locked_completion["failure"]
- if locked_failure is not None:
- return locked_failure
- completion_metadata_updates_override = (
- user_todo_completion_metadata_updates(
- locked_completion["transaction"],
- todo_already_done=(
- str(existing_block.get("status") or "")
- == TODO_STATUS_DONE
- ),
- )
- )
- normalized_unblocks_todo_id = normalize_todo_id(unblocks_todo_id) if unblocks_todo_id else None
- if unblocks_todo_id and not normalized_unblocks_todo_id:
- raise ValueError("unblocks_todo_id must use the public token shape todo_")
- normalized_successor_todo_ids = requested_successor_todo_ids
- update_result = apply_todo_update_to_lines(
- lines,
- todo_id=todo_id,
- text=text,
- priority=priority,
- clear_priority=clear_priority,
- status=status,
- role=role,
- note=note,
- evidence=evidence,
- reason=reason,
- task_class=task_class,
- action_kind=action_kind,
- task_domain=task_domain,
- task_repository=task_repository,
- continuation_policy=continuation_policy,
- required_write_scopes=required_write_scopes,
- required_capabilities=required_capabilities,
- target_capabilities=target_capabilities,
- explore_result_node_refs=explore_result_node_refs,
- append_explore_result_node_refs=append_explore_result_node_refs,
- decision_scope=decision_scope,
- required_decision_scopes=required_decision_scopes,
- claimed_by=effective_claimed_by,
- bound_agent=effective_bound_agent,
- goal_bound=goal_bound,
- blocks_agent=effective_blocks_agent,
- clear_blocks_agent=clear_blocks_agent,
- excluded_agents=effective_excluded_agents,
- global_gate=True if global_gate else None,
- clear_global_gate=clear_global_gate,
- unblocks_todo_id=normalized_unblocks_todo_id,
- successor_todo_ids=normalized_successor_todo_ids if successor_todo_ids is not None else None,
- resume_when=resume_when,
- clear_resume_when=clear_resume_when,
- no_followup=no_followup,
- completion_metadata_updates_override=(
- completion_metadata_updates_override
- ),
- monitor_metadata=monitor_intent["metadata"],
- public_context={
- "goal_id": goal_id, "role": target_role,
- "actor_agent_id": mutation_authority.get("actor_agent_id"),
- "registered_agents": registered_agent_ids_from_registry(registry_path, goal_id),
- "monitor_observation": monitor_intent["observation"],
- "enforce_monitor_boundedness": enforce_monitor_boundedness,
- },
- clear_claim=clear_claim,
- claim_only=claim_only,
- updated_at=updated_at,
- )
- changed = bool(update_result["changed"])
- new_text = "\n".join(lines) + ("\n" if original.endswith("\n") else "")
- if changed:
- new_text = replace_updated_at(new_text, updated_at)
- if changed and not dry_run:
- write_captured_todo_state(shadow_capture, runtime_root=shadow_runtime_root, goal_id=goal_id,
- state_path=resolved_state_file, text=new_text)
- payload = {
- "ok": True,
- "dry_run": dry_run,
- "changed": changed,
- "goal_id": goal_id,
- "agent_id": effective_agent_id,
- "mutation_authority": mutation_authority,
- **handoff_gate,
- **update_result,
- "state_file": str(resolved_state_file),
- "project": str(resolved_project) if resolved_project else None,
- "updated_at": updated_at if changed else None,
- }
- if successor_todo_ids is not None:
- parent_successor_advisory = build_open_parent_successor_advisory(
- todo_id=update_result.get("todo_id"),
- status=update_result.get("status"),
- successor_todo_ids=update_result.get("successor_todo_ids"),
- )
- if parent_successor_advisory:
- payload["parent_successor_advisory"] = parent_successor_advisory
- payload = _attach_todo_write_correctness_dry_run_packet(
- payload,
- goal_id=goal_id,
- write_class=write_class,
- state_text=original,
- )
- return settle_todo_runtime_shadow_capture(
- payload, registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, capture=shadow_capture,
- )
-
-
-def _complete_goal_todo_legacy(
- *,
- registry_path: Path,
- goal_id: str,
- runtime_root_arg: str | None = None,
- todo_id: str,
- role: str | None = None,
- decision_outcome: str | None = None,
- evidence: str | None = None,
- completion_result_file: Path | None = None,
- completion_turn_key: str | None = None,
- completion_identity_source: str | None = None,
- terminal_review_basis: Mapping[str, Any] | None = None,
- completion_delivery_workspace: Mapping[str, Any] | None = None,
- completion_validation_workspace_path: Path | None = None,
- task_lease_idempotency_key: str | None = None,
- task_lease_expected_version: int | None = None,
- note: str | None = None,
- no_followup: bool = False,
- successor_todo_ids: list[str] | None = None,
- claimed_by: str | None = None,
- clear_claim: bool = False,
- next_agent_todo: str | None = None,
- next_user_todo: str | None = None,
- next_user_task_class: str | None = None,
- next_claimed_by: str | None = None,
- next_task_class: str | None = None,
- next_action_kind: str | None = None,
- next_task_repository: str | None = None,
- next_required_capabilities: list[str] | None = None,
- next_continuation_policy: str | None = None,
- next_excluded_agents: list[str] | None = None,
- self_merged: bool = False,
- agent_id: str | None = None, authority_reason: str | None = None,
- project: Path | None = None,
- state_file: Path | None = None,
- dry_run: bool = False,
-) -> dict[str, Any]:
- from .line_update import apply_todo_update_to_lines, link_generated_successor_todo_ids, record_todo_mutation_actor
- from ..coordination.runtime_shadow_writer_adapter import begin_todo_runtime_shadow_capture, write_captured_todo_state, settle_todo_runtime_shadow_capture
-
- shadow_runtime_root = effective_runtime_root(registry_path, runtime_root_arg)
- if next_task_repository and not next_agent_todo:
- raise ValueError("--next-task-repository requires --next-agent-todo")
- if next_required_capabilities and not next_agent_todo:
- raise ValueError("--next-required-capability requires --next-agent-todo")
- normalized_successor_todo_ids = require_completion_successor_todo_ids(
- successor_todo_ids
- )
- effective_next_user_task_class = resolve_next_user_task_class(
- next_user_todo,
- next_user_task_class,
- )
- completion_policy_facts = {
- "claimed_by": claimed_by, "next_claimed_by": next_claimed_by,
- "next_agent_todo": next_agent_todo, "next_action_kind": next_action_kind,
- "next_continuation_policy": next_continuation_policy,
- "next_excluded_agents": next_excluded_agents or [],
- "self_merged": self_merged, "evidence": evidence,
- }
- resolved_project, resolved_state_file = resolve_todo_state_path(
- registry_path=registry_path,
- goal_id=goal_id,
- project=project,
- state_file=state_file,
- )
- # Run caller-approved validation before locking so slow commands do not
- # hold the 5s mutation lock; return typed failure payloads unchanged.
- validation_gate = completion_validation_module.run_completion_validation_gate_with_source(
- state_file=resolved_state_file,
- todo_id=todo_id,
- role=role,
- registry_path=registry_path,
- goal_id=goal_id,
- dry_run=dry_run,
- no_followup=no_followup,
- completion_turn_key=completion_turn_key,
- completion_identity_source=completion_identity_source,
- requested_has_successor=bool(
- normalized_successor_todo_ids or next_agent_todo or next_user_todo
- ),
- completion_policy_facts=completion_policy_facts,
- requested_successor_todo_ids=normalized_successor_todo_ids,
- completion_delivery_workspace=completion_delivery_workspace,
- completion_validation_workspace_path=completion_validation_workspace_path,
- )
- validation_failure: dict[str, Any] | None = validation_gate.get("failure")
- if validation_failure is not None:
- return validation_failure
- with legacy_todo_write_transaction(
- registry_path, goal_id, resolved_state_file, agent_id or claimed_by, "todo_complete", dry_run,
- runtime_root=shadow_runtime_root,
- ), ExitStack() as lease_fence_stack:
- original = resolved_state_file.read_text(encoding="utf-8")
- shadow_capture = begin_todo_runtime_shadow_capture(
- registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, state_path=resolved_state_file,
- write_class="todo_complete", original_text=original,
- )
- lines = original.splitlines()
- updated_at = now_local()
- completion_match, completion_todo = (
- completion_validation_module.locked_todo_completion_source(
- lines=lines,
- state_file=resolved_state_file,
- project=resolved_project,
- registry_path=registry_path,
- goal_id=goal_id,
- todo_id=todo_id,
- role=role,
- )
- )
- effective_decision_outcome = require_completion_decision_outcome(
- completion_todo,
- decision_outcome,
- materialized=bool(completion_match),
- )
- if completion_todo is None:
- normalized_todo_id = normalize_todo_id(todo_id) or todo_id
- raise ValueError(
- f"todo_id {normalized_todo_id!r} was not found in active user or agent todos"
- )
- locked_completion_policy_source = (
- completion_validation_module.completion_policy_source_from_state(
- registry_path=registry_path,
- goal_id=goal_id,
- lines=lines,
- successor_todo_ids=normalized_successor_todo_ids,
- facts=completion_policy_facts,
- )
- )
- locked_completion = locked_todo_completion_transaction(
- validation_gate=validation_gate,
- todo=completion_todo,
- goal_id=goal_id,
- todo_id=todo_id,
- dry_run=dry_run,
- require_source_match=bool(completion_match),
- missing_is_drift=True,
- current_completion_policy_source=locked_completion_policy_source,
- )
- locked_failure: dict[str, Any] | None = locked_completion["failure"]
- if locked_failure is not None:
- return locked_failure
- completion_transaction = locked_completion["transaction"]
- completion_turn_key = completion_transaction.get(
- "completion_identity_key"
- )
- completion_identity_source = completion_transaction.get(
- "completion_identity_source"
- )
- decision_target = completion_decision_target(lines, completion_todo)
- mutation_authority = authorize_todo_lifecycle_mutation(
- registry_path=registry_path,
- goal_id=goal_id,
- command="complete",
- todo=completion_todo,
- actor_agent_id=agent_id, authority_reason=authority_reason,
- requested_claimed_by=claimed_by,
- decision_outcome=effective_decision_outcome,
- decision_target=decision_target,
- )
- completion_handoff = resolve_todo_completion_handoff(state_text=original, mutation_authority=mutation_authority)
- terminal_replay = materialized_todo_completion_replay(
- transaction=completion_transaction,
- todo=completion_todo,
- dry_run=dry_run,
- goal_id=goal_id,
- todo_id=todo_id,
- handoff=completion_handoff,
- mutation_authority=mutation_authority,
- state_file=str(resolved_state_file),
- project=str(resolved_project) if resolved_project else None,
- ) if completion_match else None
- if terminal_replay is not None:
- return terminal_replay
- task_lease_fence = lease_fence_stack.enter_context(
- hold_task_lease_mutation_fence(
- registry_path=registry_path,
- goal_id=goal_id,
- todo_id=todo_id,
- todo=completion_todo,
- actor_agent_id=agent_id or claimed_by,
- idempotency_key=(task_lease_idempotency_key or completion_turn_key)
- if completion_identity_source != "unscoped_completion"
- else task_lease_idempotency_key,
- expected_version=task_lease_expected_version,
- require_active_when_key_supplied=(
- task_lease_idempotency_key is not None
- or task_lease_expected_version is not None
- ),
- handoff=completion_handoff,
- runtime_root=shadow_runtime_root,
- )
- )
- completion_state = completion_transaction.get("completion_state")
- from ...capabilities.explore.research_frontier import hold_research_completion_evidence
- research_evidence = lease_fence_stack.enter_context(hold_research_completion_evidence(
- registry_path=registry_path, runtime_root=shadow_runtime_root, goal_id=goal_id,
- todo=completion_todo, state_text=original,
- actor_agent_id=mutation_authority.get("actor_agent_id"),
- ))
- completion_policy = completion_policy_from_transaction(completion_transaction)
- effective_claimed_by = completion_policy.effective_claimed_by
- registered_agents = completion_policy.registered_agents
- effective_next_claimed_by = completion_policy.effective_next_claimed_by
- effective_next_excluded_agents = (
- completion_policy.effective_next_excluded_agents
- )
- effective_self_merged = completion_policy.self_merged
- if not isinstance(completion_state, dict):
- raise RuntimeError(
- "TypeScript Todo completion transaction did not authorize a commit"
- )
- update_result = apply_todo_update_to_lines(
- lines,
- todo_id=todo_id,
- status=TODO_STATUS_DONE,
- role=role,
- decision_outcome=effective_decision_outcome,
- note=note,
- evidence=evidence,
- completion_turn_key=completion_turn_key,
- completion_continuation=str(completion_state["continuation"]),
- completion_recovery=(
- str(completion_state["recovery"])
- if completion_state.get("recovery") is not None
- else None
- ),
- completion_metadata_updates_override=dict(
- completion_transaction["metadata_updates"]
- ),
- claimed_by=effective_claimed_by,
- clear_claim=clear_claim,
- no_followup=True if no_followup else None,
- successor_todo_ids=normalized_successor_todo_ids if successor_todo_ids is not None else None,
- updated_at=updated_at,
- )
- record_todo_mutation_actor(lines, update_result, mutation_authority.get("actor_agent_id"))
- unblock_resume, decision_scope_resolution = (
- apply_completed_user_todo_lifecycle(
- lines,
- completion_todo=completion_todo,
- update_result=update_result,
- fallback_todo_id=todo_id,
- decision_outcome=effective_decision_outcome,
- updated_at=updated_at,
- apply_update=apply_todo_update_to_lines,
- )
- )
- successor_intents = build_successor_intents(
- next_agent_todo=next_agent_todo,
- next_user_todo=next_user_todo,
- next_user_task_class=effective_next_user_task_class,
- next_claimed_by=next_claimed_by,
- next_task_class=next_task_class,
- next_action_kind=next_action_kind,
- next_task_repository=next_task_repository,
- next_required_capabilities=next_required_capabilities,
- next_continuation_policy=next_continuation_policy,
- next_excluded_agents=next_excluded_agents,
- )
- successor_proposals = derive_successor_proposals(
- command="complete",
- predecessor=completion_todo,
- registered_agents=registered_agents,
- actor_agent_id=mutation_authority.get("actor_agent_id"),
- completion_policy={
- "effective_claimed_by": effective_claimed_by,
- "effective_next_claimed_by": effective_next_claimed_by,
- "effective_next_excluded_agents": effective_next_excluded_agents,
- },
- successor_intents=successor_intents,
- )
- next_results = [
- add_todo_to_lines(
- lines,
- **successor_add_kwargs(proposal),
- updated_at=updated_at,
- )
- for proposal in successor_proposals
- ]
- generated_successor_todo_ids = [
- todo_id
- for todo_id in normalize_todo_id_list([item.get("todo_id") for item in next_results])
- ]
- successor_metadata_updated = link_generated_successor_todo_ids(
- lines,
- update_result=update_result,
- role=role,
- successor_todo_ids=generated_successor_todo_ids,
- )
- next_action_changed = update_result.get("role") == "agent" and (
- settle_completed_todo_next_action(
- lines,
- completed_todo_id=str(update_result.get("todo_id") or todo_id),
- )
- )
- next_changed = any(item.get("added") or item.get("metadata_updated") for item in next_results)
- changed = bool(
- update_result["changed"]
- or next_changed
- or successor_metadata_updated
- or next_action_changed
- or (unblock_resume or {}).get("changed")
- or (decision_scope_resolution or {}).get("changed")
- )
- new_text = "\n".join(lines) + ("\n" if original.endswith("\n") else "")
- if changed:
- new_text = replace_updated_at(new_text, updated_at)
- if changed and not dry_run:
- write_captured_todo_state(shadow_capture, runtime_root=shadow_runtime_root, goal_id=goal_id,
- state_path=resolved_state_file, text=new_text)
- release_verified_task_lease_fence(
- task_lease_fence,
- committed=changed and not dry_run,
- )
- result = {
- "ok": True,
- **({"capability_completion_evidence": research_evidence} if research_evidence is not None else {}),
- "dry_run": dry_run,
- "completed": True,
- "goal_id": goal_id,
- **update_result,
- "changed": changed,
- "next_todos": next_results,
- "linked_successor_id": completion_policy.linked_successor_id,
- "mutation_authority": mutation_authority,
- "task_lease_fence": task_lease_fence,
- **completion_handoff,
- "state_file": str(resolved_state_file),
- "project": str(resolved_project) if resolved_project else None,
- "updated_at": updated_at if changed else None,
- }
- if unblock_resume:
- result["unblock_resume"] = unblock_resume
- if decision_scope_resolution:
- result["decision_scope_resolution"] = decision_scope_resolution
- if effective_decision_outcome:
- result["decision_outcome"] = effective_decision_outcome
- result["self_merged"] = effective_self_merged
- return settle_todo_runtime_shadow_capture(
- result, registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, capture=shadow_capture,
- )
-
-
-def _supersede_goal_todo_legacy(
- *,
- registry_path: Path,
- goal_id: str,
- runtime_root_arg: str | None = None,
- todo_id: str,
- role: str | None = None,
- reason: str | None = None,
- successor_todo_ids: list[str] | None = None,
- next_agent_todo: str | None = None,
- next_user_todo: str | None = None,
- next_user_task_class: str | None = None,
- next_claimed_by: str | None = None,
- next_task_class: str | None = None,
- next_action_kind: str | None = None,
- next_task_repository: str | None = None,
- next_required_capabilities: list[str] | None = None,
- next_continuation_policy: str | None = None,
- next_excluded_agents: list[str] | None = None,
- agent_id: str | None = None, authority_reason: str | None = None,
- task_lease_idempotency_key: str | None = None, task_lease_expected_version: int | None = None,
- project: Path | None = None,
- state_file: Path | None = None,
- dry_run: bool = False,
-) -> dict[str, Any]:
- from .line_update import apply_todo_update_to_lines, link_superseding_todo_id, record_todo_mutation_actor
- from ..coordination.runtime_shadow_writer_adapter import begin_todo_runtime_shadow_capture, write_captured_todo_state, settle_todo_runtime_shadow_capture
-
- if successor_todo_ids:
- raise ValueError("Existing-successor supersede requires promoted canonical Todo authority; migrate the Goal before retrying")
- shadow_runtime_root = effective_runtime_root(registry_path, runtime_root_arg)
- if next_task_repository and not next_agent_todo:
- raise ValueError("--next-task-repository requires --next-agent-todo")
- if next_required_capabilities and not next_agent_todo:
- raise ValueError("--next-required-capability requires --next-agent-todo")
- effective_next_user_task_class = resolve_next_user_task_class(
- next_user_todo,
- next_user_task_class,
- )
- resolved_project, resolved_state_file = resolve_todo_state_path(
- registry_path=registry_path,
- goal_id=goal_id,
- project=project,
- state_file=state_file,
- )
- with legacy_todo_write_transaction(
- registry_path, goal_id, resolved_state_file, agent_id, "todo_supersede", dry_run,
- runtime_root=shadow_runtime_root,
- ), ExitStack() as lease_fence_stack:
- original = resolved_state_file.read_text(encoding="utf-8")
- shadow_capture = begin_todo_runtime_shadow_capture(
- registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, state_path=resolved_state_file,
- write_class="todo_supersede", original_text=original,
- )
- lines = original.splitlines()
- updated_at = now_local()
- current_match = find_todo_block(lines, todo_id=todo_id, role=role)
- if not current_match:
- normalized_todo_id = normalize_todo_id(todo_id) or todo_id
- raise ValueError(
- f"todo_id {normalized_todo_id!r} was not found in active user or agent todos"
- )
- current_role, _section, _start, _end, current_block = current_match
- authority_todo = dict(current_block)
- authority_todo["role"] = current_role
- mutation_authority = authorize_todo_lifecycle_mutation(
- registry_path=registry_path,
- goal_id=goal_id,
- command="supersede",
- todo=authority_todo,
- actor_agent_id=agent_id, authority_reason=authority_reason,
- )
- completion_handoff, task_lease_fence = enter_terminal_todo_lease_fence(
- lease_fence_stack, registry_path=registry_path, goal_id=goal_id, todo_id=todo_id,
- todo=authority_todo, actor_agent_id=agent_id, state_text=original, mutation_authority=mutation_authority,
- idempotency_key=task_lease_idempotency_key, expected_version=task_lease_expected_version,
- runtime_root=shadow_runtime_root,
- )
- from ...capabilities.explore.research_frontier import hold_research_completion_evidence
- research_completion_evidence = lease_fence_stack.enter_context(hold_research_completion_evidence(
- registry_path=registry_path, runtime_root=shadow_runtime_root, goal_id=goal_id,
- todo=authority_todo, state_text=original, actor_agent_id=mutation_authority.get("actor_agent_id"),
- ))
- update_result = apply_todo_update_to_lines(
- lines,
- todo_id=todo_id,
- status=TODO_STATUS_DONE,
- role=role,
- reason=reason,
- note="superseded",
- updated_at=updated_at,
- )
- record_todo_mutation_actor(lines, update_result, mutation_authority.get("actor_agent_id"))
- registered_agents = registered_agent_ids_from_registry(registry_path, goal_id)
- successor_intents = build_successor_intents(
- next_agent_todo=next_agent_todo,
- next_user_todo=next_user_todo,
- next_user_task_class=effective_next_user_task_class,
- next_claimed_by=next_claimed_by,
- next_task_class=next_task_class,
- next_action_kind=next_action_kind,
- next_task_repository=next_task_repository,
- next_required_capabilities=next_required_capabilities,
- next_continuation_policy=next_continuation_policy,
- next_excluded_agents=next_excluded_agents,
- )
- successor_proposals = derive_successor_proposals(
- command="supersede",
- predecessor=authority_todo,
- registered_agents=registered_agents,
- actor_agent_id=mutation_authority.get("actor_agent_id"),
- completion_policy=None,
- successor_intents=successor_intents,
- )
- next_results = [
- add_todo_to_lines(
- lines,
- **successor_add_kwargs(proposal),
- updated_at=updated_at,
- )
- for proposal in successor_proposals
- ]
- generated_successor_todo_ids = [
- todo_id
- for todo_id in normalize_todo_id_list([item.get("todo_id") for item in next_results])
- ]
- link_superseding_todo_id(
- lines,
- update_result=update_result,
- role=role,
- successor_todo_ids=generated_successor_todo_ids,
- )
- next_action_changed = current_role == "agent" and (
- settle_completed_todo_next_action(
- lines,
- completed_todo_id=str(update_result.get("todo_id") or todo_id),
- )
- )
- next_changed = any(item.get("added") or item.get("metadata_updated") for item in next_results)
- changed = bool(
- update_result["changed"]
- or next_changed
- or next_action_changed
- )
- new_text = "\n".join(lines) + ("\n" if original.endswith("\n") else "")
- if changed:
- new_text = replace_updated_at(new_text, updated_at)
- if changed and not dry_run:
- write_captured_todo_state(shadow_capture, runtime_root=shadow_runtime_root, goal_id=goal_id,
- state_path=resolved_state_file, text=new_text)
- release_verified_task_lease_fence(task_lease_fence, committed=changed and not dry_run)
- result = {
- "ok": True,
- "dry_run": dry_run,
- "superseded": True,
- **({"capability_completion_evidence": research_completion_evidence} if research_completion_evidence else {}),
- "goal_id": goal_id,
- **update_result,
- "changed": changed,
- "mutation_authority": mutation_authority,
- "task_lease_fence": task_lease_fence, **completion_handoff,
- "next_todos": next_results,
- "state_file": str(resolved_state_file),
- "project": str(resolved_project) if resolved_project else None,
- "updated_at": updated_at if changed else None,
- }
- return settle_todo_runtime_shadow_capture(
- result, registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, capture=shadow_capture,
- )
-
-
-def _archive_completed_todos_legacy(
- *,
- registry_path: Path,
- goal_id: str,
- runtime_root_arg: str | None = None,
- role: str = "agent",
- max_active_done: int = ARCHIVE_COMPLETED_DEFAULT_MAX_ACTIVE_DONE,
- project: Path | None = None,
- state_file: Path | None = None,
- dry_run: bool = True,
-) -> dict[str, Any]:
- from ..coordination.runtime_shadow_writer_adapter import begin_todo_runtime_shadow_capture, write_captured_todo_state, settle_todo_runtime_shadow_capture
-
- shadow_runtime_root = effective_runtime_root(registry_path, runtime_root_arg)
- if role not in TODO_SECTION_HEADINGS:
- raise ValueError("todo role must be one of: user, agent")
- if max_active_done < 0:
- raise ValueError("max_active_done must be non-negative")
- resolved_project, resolved_state_file = resolve_todo_state_path(
- registry_path=registry_path,
- goal_id=goal_id,
- project=project,
- state_file=state_file,
- )
-
- with legacy_todo_write_transaction(
- registry_path, goal_id, resolved_state_file, None, "todo_archive_completed", dry_run,
- runtime_root=shadow_runtime_root,
- ):
- original = resolved_state_file.read_text(encoding="utf-8")
- shadow_capture = begin_todo_runtime_shadow_capture(
- registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, state_path=resolved_state_file,
- write_class="todo_archive_completed", original_text=original,
- )
- lines = original.splitlines()
- archive_result = archive_completed_todo_lines(
- lines,
- role=role,
- max_active_done=max_active_done,
- )
- lines = archive_result.pop("lines")
-
- updated_at = now_local()
- changed = bool(archive_result["changed"])
- new_text = "\n".join(lines) + ("\n" if original.endswith("\n") else "")
- if changed:
- new_text = replace_updated_at(new_text, updated_at)
- if changed and not dry_run:
- write_captured_todo_state(shadow_capture, runtime_root=shadow_runtime_root, goal_id=goal_id,
- state_path=resolved_state_file, text=new_text)
-
- result = {
- "ok": True,
- "dry_run": dry_run,
- "goal_id": goal_id,
- **archive_result,
- "state_file": str(resolved_state_file),
- "project": str(resolved_project) if resolved_project else None,
- "updated_at": updated_at if changed else None,
- }
- return settle_todo_runtime_shadow_capture(
- result, registry_path=registry_path, runtime_root=shadow_runtime_root,
- goal_id=goal_id, capture=shadow_capture,
- )
diff --git a/loopx/control_plane/todos/line_update.py b/loopx/control_plane/todos/line_update.py
deleted file mode 100644
index de64c86626..0000000000
--- a/loopx/control_plane/todos/line_update.py
+++ /dev/null
@@ -1,394 +0,0 @@
-from __future__ import annotations
-
-from collections.abc import Mapping
-from typing import Any
-
-from ..effect_runtime import EffectRuntimeRejected, effect_runtime_result
-from .mutation_response import serialize_todo_update_result
-from .authoring_scope import todo_authoring_facts
-from .external_wait_contract import TodoExternalWaitAuthoringError, build_monitor_advancement_authoring_contract
-from .update_source import todo_update_snapshot
-from .todo_semantics import todo_priority_label
-
-from .active_state_editing import (
- TODO_SECTION_HEADINGS,
- find_todo_block,
- set_todo_marker,
- set_todo_text,
- todo_metadata_would_change,
-)
-from .contract import (
- merge_todo_id_lists,
- metadata_line_for_todo_block,
- normalize_todo_id,
- normalize_todo_id_list,
- parse_todo_metadata_line,
-)
-from .completion_state import (
- TodoCompletionContinuation,
-)
-from .contract import TODO_MONITOR_METADATA_FIELDS
-
-
-def upsert_todo_metadata(
- lines: list[str], block: dict[str, Any], metadata_line: str | None
-) -> bool:
- if not metadata_line:
- return False
- start = int(block["start"])
- end = int(block["end"])
- for index in range(start + 1, end):
- if parse_todo_metadata_line(lines[index]) is not None:
- if lines[index] == metadata_line:
- return False
- lines[index] = metadata_line
- return True
- insert_at = end
- while insert_at > start + 1 and not lines[insert_at - 1].strip():
- insert_at -= 1
- lines.insert(insert_at, metadata_line)
- return True
-
-
-def record_todo_mutation_actor(
- lines: list[str],
- update_result: dict[str, Any],
- actor: str | None,
-) -> None:
- """Transport accepted mutation provenance inside the held writer transaction.
-
- Authority remains with the existing lifecycle decision. An unchanged replay
- must not mint a new actor or advance the source timestamp.
- """
- if not update_result.get("changed"):
- return
- if actor is None:
- return
- match = find_todo_block(
- lines, todo_id=update_result["todo_id"], role=update_result["role"],
- )
- if match is None:
- raise RuntimeError("Authorized Todo disappeared before provenance writeback")
- block = match[4]
- metadata_line = metadata_line_for_todo_block(block, {"last_actor_agent_id": actor})
- updated = upsert_todo_metadata(lines, block, metadata_line)
- update_result["metadata_updated"] = updated or update_result["metadata_updated"]
- update_result["last_actor_agent_id"] = actor
-
-
-def link_generated_successor_todo_ids(
- lines: list[str],
- *,
- update_result: dict[str, Any],
- role: str | None,
- successor_todo_ids: list[str],
-) -> bool:
- merged_successor_ids = merge_todo_id_lists(
- update_result.get("successor_todo_ids"),
- successor_todo_ids,
- )
- if merged_successor_ids == normalize_todo_id_list(
- update_result.get("successor_todo_ids")
- ):
- return False
- block_match = find_todo_block(
- lines,
- todo_id=str(update_result.get("todo_id") or ""),
- role=role,
- )
- if not block_match:
- return False
- _resolved_role, _section, _start, _end, block = block_match
- metadata_updated = upsert_todo_metadata(
- lines,
- block,
- metadata_line_for_todo_block(
- block,
- {
- "successor_todo_ids": merged_successor_ids,
- "completion_continuation": (TodoCompletionContinuation.SUCCESSOR.value),
- },
- ),
- )
- update_result["successor_todo_ids"] = merged_successor_ids
- update_result["metadata_updated"] = bool(
- update_result.get("metadata_updated") or metadata_updated
- )
- update_result["changed"] = bool(update_result.get("changed") or metadata_updated)
- return metadata_updated
-
-
-def link_superseding_todo_id(
- lines: list[str],
- *,
- update_result: dict[str, Any],
- role: str | None,
- successor_todo_ids: list[str],
-) -> bool:
- """Persist supersede lineage after generated successors are materialized."""
-
- if not successor_todo_ids:
- return False
- block_match = find_todo_block(
- lines,
- todo_id=str(update_result.get("todo_id") or ""),
- role=role,
- )
- if not block_match:
- return False
- _resolved_role, _section, _start, _end, block = block_match
- merged_successor_ids = merge_todo_id_lists(
- update_result.get("successor_todo_ids"),
- successor_todo_ids,
- )
- metadata_updated = upsert_todo_metadata(
- lines,
- block,
- metadata_line_for_todo_block(
- block,
- {
- "superseded_by": successor_todo_ids[0],
- "successor_todo_ids": merged_successor_ids,
- },
- ),
- )
- update_result["metadata_updated"] = bool(
- update_result.get("metadata_updated") or metadata_updated
- )
- update_result["superseded_by"] = successor_todo_ids[0]
- update_result["successor_todo_ids"] = merged_successor_ids
- update_result["changed"] = True
- return metadata_updated
-
-
-def _field_update_plan(
- block: Mapping[str, Any], intent: dict[str, Any], updated_at: str,
- monitor_context: dict[str, Any] | None = None,
- public_context: dict[str, Any] | None = None,
-) -> dict[str, Any]:
- """Adapt source facts only; the TS planner owns omission/clear/state rules."""
- try:
- result = effect_runtime_result(
- "todo.public_update.plan" if public_context is not None else "todo.field_update.plan",
- {
- "schema_version": "todo_public_update_request_v0" if public_context is not None else "loopx_todo_field_update_request_v0",
- "todo": {
- **todo_authoring_facts(dict(block)),
- "role": block.get("role"),
- **{key: block.get(key)
- for key in (
- "text",
- "note",
- "todo_id",
- "status",
- "claimed_by",
- "completed_at",
- "removed_continuation_policy",
- "no_followup",
- "completion_continuation",
- "successor_todo_ids",
- "explore_result_node_refs",
- "resume_monitor_generation",
- "task_class",
- *TODO_MONITOR_METADATA_FIELDS,
- )},
- },
- "intent": intent,
- "updated_at": updated_at,
- "monitor_context": monitor_context,
- "context": public_context,
- },
- )
- except EffectRuntimeRejected as exc:
- if public_context is not None and exc.diagnostic_code.startswith("external_wait_"):
- condition = str(intent.get("resume_when") or block.get("resume_when") or "").strip().lower()
- kind, _, target = condition.partition(":")
- raise TodoExternalWaitAuthoringError(str(exc), code=exc.diagnostic_code,
- monitor_todo_id=target if kind == "monitor_changed" else None,
- successor_todo_ids=intent.get("successor_todo_ids")
- if intent.get("successor_todo_ids") is not None else block.get("successor_todo_ids")) from None
- raise ValueError(str(exc)) from None
- if (
- not isinstance(result, dict)
- or result.get("schema_version") != "loopx_todo_field_update_result_v0"
- or result.get("target_status") not in {"open", "done", "blocked", "deferred"}
- or result.get("normalized_status")
- not in {None, "open", "done", "blocked", "deferred"}
- or not isinstance(result.get("metadata_updates"), dict)
- ):
- raise RuntimeError("TypeScript Todo field update result shape mismatch")
- transition = result.get("external_wait_transition")
- if isinstance(transition, dict) and transition.get("resume_kind") == "monitor_changed":
- transition["authoring_contract"] = build_monitor_advancement_authoring_contract(
- monitor_todo_id=transition["dependency_todo_id"],
- successor_todo_ids=transition["successor_todo_ids"])
- return result
-
-
-def apply_todo_update_to_lines(
- lines: list[str],
- *,
- todo_id: str,
- text: str | None = None,
- priority: str | None = None,
- clear_priority: bool = False,
- status: str | None = None,
- role: str | None = None,
- note: str | None = None,
- evidence: str | None = None,
- completion_turn_key: str | None = None,
- reason: str | None = None,
- task_class: str | None = None,
- action_kind: str | None = None,
- task_domain: str | None = None,
- task_repository: str | None = None,
- continuation_policy: str | None = None,
- required_write_scopes: list[str] | None = None,
- required_capabilities: list[str] | None = None,
- target_capabilities: list[str] | None = None,
- explore_result_node_refs: list[str] | None = None,
- append_explore_result_node_refs: list[str] | None = None,
- decision_scope: Any = None,
- required_decision_scopes: Any = None,
- decision_outcome: str | None = None,
- decision_scope_outcomes: Any = None,
- claimed_by: str | None = None,
- bound_agent: str | None = None,
- goal_bound: bool | None = None,
- clear_user_binding: bool = False,
- blocks_agent: str | None = None,
- clear_blocks_agent: bool = False,
- excluded_agents: list[str] | None = None,
- global_gate: bool | None = None,
- clear_global_gate: bool = False,
- unblocks_todo_id: str | None = None,
- successor_todo_ids: list[str] | None = None,
- completion_continuation: str | None = None,
- completion_recovery: str | None = None,
- completion_metadata_updates_override: Mapping[str, Any] | None = None,
- resume_when: str | None = None,
- resume_monitor_generation: int | None = None,
- clear_resume_when: bool = False,
- no_followup: bool | None = None,
- monitor_metadata: dict[str, Any] | None = None,
- monitor_context: dict[str, Any] | None = None,
- public_context: dict[str, Any] | None = None,
- clear_claim: bool = False,
- claim_only: bool = False,
- updated_at: str,
-) -> dict[str, Any]:
- # Blank Python compatibility values mean omission. The whole typed field
- # owner already normalizes and validates a supplied condition, including
- # contradictory clear intent; do not preflight it through a second RPC.
- if public_context is None and resume_when is not None and not str(resume_when).strip():
- resume_when = None
- normalized_todo_id = normalize_todo_id(todo_id)
- if not normalized_todo_id:
- raise ValueError(
- "todo_id must use the public token shape "
- "todo_"
- )
- if role is not None and role not in TODO_SECTION_HEADINGS:
- raise ValueError("todo role must be one of: user, agent")
- block_match = find_todo_block(lines, todo_id=normalized_todo_id, role=role)
- if not block_match:
- raise ValueError(
- f"todo_id {normalized_todo_id!r} was not found in active user or agent todos"
- )
- resolved_role, section, _start, _end, block = block_match
- if public_context is not None:
- public_context = {**public_context, "items": todo_update_snapshot(lines)
- if resume_when or block.get("resume_when") else []}
- raw_intent = {
- "text": text,
- "priority": priority,
- "clear_priority": clear_priority,
- "status": status,
- "note": note,
- "evidence": evidence,
- "completion_turn_key": completion_turn_key,
- "reason": reason,
- "task_class": task_class,
- "action_kind": action_kind,
- "task_domain": task_domain,
- "task_repository": task_repository,
- "continuation_policy": continuation_policy,
- "required_write_scopes": required_write_scopes,
- "required_capabilities": required_capabilities,
- "target_capabilities": target_capabilities,
- "explore_result_node_refs": explore_result_node_refs,
- "append_explore_result_node_refs": append_explore_result_node_refs,
- "decision_scope": decision_scope,
- "required_decision_scopes": required_decision_scopes,
- "decision_outcome": decision_outcome,
- "decision_scope_outcomes": decision_scope_outcomes,
- "claimed_by": claimed_by,
- "bound_agent": bound_agent,
- "goal_bound": goal_bound,
- "clear_user_binding": clear_user_binding,
- "blocks_agent": blocks_agent,
- "clear_blocks_agent": clear_blocks_agent,
- "excluded_agents": excluded_agents,
- "global_gate": global_gate,
- "clear_global_gate": clear_global_gate,
- "unblocks_todo_id": unblocks_todo_id,
- "successor_todo_ids": successor_todo_ids,
- "completion_continuation": completion_continuation,
- "completion_recovery": completion_recovery,
- "completion_metadata_updates_override": completion_metadata_updates_override,
- "resume_when": resume_when,
- "resume_monitor_generation": resume_monitor_generation,
- "clear_resume_when": clear_resume_when,
- "no_followup": no_followup,
- "monitor_metadata": monitor_metadata,
- "clear_claim": clear_claim,
- "claim_only": claim_only,
- }
- # Python's compatibility API uses None (and blank note text) for
- # omission. Strip those sentinels before crossing the typed planner; an
- # actual empty scalar such as reason="" remains an explicit clear.
- intent = {
- key: value for key, value in raw_intent.items()
- if value is not None and not (
- key == "note" and isinstance(value, str) and not value.strip()
- )
- }
- plan = _field_update_plan(
- {**block, "role": resolved_role},
- intent,
- updated_at,
- monitor_context,
- public_context,
- )
- normalized_status = plan["normalized_status"]
- target_status = plan["target_status"]
- updates = plan["metadata_updates"]
- accepted_actor = updates.pop("last_actor_agent_id", None)
- text = updates.pop("text", text)
- updated_priority = updates.pop("priority", todo_priority_label(block))
- updates.pop("title", None)
- status_changed = (
- set_todo_marker(lines, block, normalized_status) if normalized_status else False
- )
- text_changed = (
- set_todo_text(lines, block, text, status=target_status)
- if text is not None
- else False
- )
- metadata_line = metadata_line_for_todo_block(block, updates)
- semantic_metadata_changed = todo_metadata_would_change(lines, block, metadata_line)
- if status_changed or text_changed or semantic_metadata_changed:
- updates["updated_at"] = updated_at
- metadata_line = metadata_line_for_todo_block(block, updates)
- metadata_updated = upsert_todo_metadata(lines, block, metadata_line)
- effective_metadata = parse_todo_metadata_line(metadata_line or "") or {}
- result = serialize_todo_update_result(
- role=resolved_role, section=section, todo=block.get("text"),
- todo_id=normalized_todo_id, status=target_status, priority=updated_priority,
- status_changed=status_changed, text_changed=text_changed,
- metadata_updated=metadata_updated, metadata=effective_metadata,
- monitor_poll_transition=plan.get("monitor_poll_transition"),
- external_wait_transition=plan.get("external_wait_transition"),
- )
- record_todo_mutation_actor(lines, result, accepted_actor)
- return result
diff --git a/loopx/control_plane/todos/machine_region.py b/loopx/control_plane/todos/machine_region.py
index 381daf74b2..75b50c99dc 100644
--- a/loopx/control_plane/todos/machine_region.py
+++ b/loopx/control_plane/todos/machine_region.py
@@ -94,22 +94,35 @@ def find_todo_regions(lines: list[str], *, visible: frozenset[int] | None = None
marked = index < len(lines) and lines[index].rstrip("\r\n") == todo_region_marker(role, "begin")
if marked:
index += 1
+ legacy_task_open = False
while index < len(lines):
content = lines[index].rstrip("\r\n")
if content == todo_region_marker(role, "end") and marked:
break
if TODO_REGION_PREFIX in content:
raise ValueError("nested, mismatched, or malformed Todo region marker")
+ task = TODO_TASK_PATTERN.match(content) is not None
+ legacy_continuation = (
+ not marked and legacy_task_open and content.startswith((" ", "\t"))
+ )
generated = index in visible and (
not content.strip()
- or TODO_TASK_PATTERN.match(content) is not None
+ or task
or parse_todo_metadata_line(content) is not None
or content.startswith("", ""]
- text = replace_updated_at("\n".join(lines) + "\n", str(request["observed_at"]))
- write_captured_todo_state(capture, runtime_root=runtime, goal_id=goal_id,
- state_path=state, text=text)
- verify_state_text_durable(state, text)
- return settle_todo_runtime_shadow_capture(result, registry_path=registry_path,
- runtime_root=runtime, goal_id=goal_id, capture=capture)
+ source_bytes = state.read_bytes()
+ except FileNotFoundError:
+ source_bytes = None
+ try:
+ source_text = source_bytes.decode("utf-8") if source_bytes is not None else None
+ except UnicodeDecodeError:
+ if canonical is None:
+ raise
+ # Canonical history can still be read, but these bytes cannot
+ # establish fresh Goal intent or a historical Markdown receipt.
+ source_text = None
+ # An exact pre-cutoff receipt is read-only recovery, not fresh work.
+ previous = previous_receipt(source_text) if source_text is not None else None
+ if previous is not None:
+ return dict(effect_runtime_result("work_items.team_plan.plan", {
+ **request, "previous_receipt": previous})["result"])
+ if canonical is None:
+ require_canonical_authority(goal_id=goal_id, operation="team_plan")
+ response = effect_runtime_result("work_items.team_plan.commit", {
+ **request, "runtime_root": str(runtime.resolve()),
+ "expected_provider_revision": canonical["provider_revision"],
+ "current_state_fingerprint": read_fingerprint() if read_fingerprint else None,
+ "source": {
+ "registry": str(registry_path.resolve()), "state_file": str(state.resolve()),
+ "registry_sha256": hashlib.sha256(registry_path.read_bytes()).hexdigest(),
+ # Absence is an observation, never a fresh source witness.
+ # The native owner checks receipts before source admission.
+ "state_sha256": hashlib.sha256(source_bytes).hexdigest() if source_text is not None else None,
+ },
+ "locks": [cross_runtime_lock_witness(target) for target in targets],
+ })
+ raise_canonical_write_failure(response, goal_id=goal_id, operation="team_plan")
+ if response.get("status") not in {"applied", "recovered", "replayed", "no_change"}:
+ raise TeamPlanCommitError(str(response.get("reason_code") or "team_plan_commit_failed"),
+ str(response.get("reason") or "team plan commit could not be verified"),
+ read_fingerprint() if read_fingerprint else None)
+ projected = settle_canonical_todo_projection(dict(response), registry_path=registry_path,
+ runtime_root=runtime, goal_id=goal_id, project=project, state_file=state)
+ if projected.get("projection_delivery") == "pending":
+ raise TeamPlanCommitError("team_plan_projection_pending", "Tasks committed; display readback is pending")
+ result = dict(response["result"])
+ result["projection_delivery"] = projected.get("projection_delivery")
+ return result
diff --git a/loopx/control_plane/work_items/team_plan_authority.ts b/loopx/control_plane/work_items/team_plan_authority.ts
index 4957e4e071..cf260c3966 100644
--- a/loopx/control_plane/work_items/team_plan_authority.ts
+++ b/loopx/control_plane/work_items/team_plan_authority.ts
@@ -12,6 +12,10 @@ import {openLocalAuthorityStore, localAuthorityOpenFailure, requireLocalAuthorit
import {type AuthoritySourceCheck, uncheckedAuthoritySource} from "../coordination/authority_source.ts";
import {legacyCoordinationTodoLockPath} from "../coordination/legacy_writer_lock_paths.ts";
import {requireShadowPrimaryWriteAllowed, shadowMaintenanceLockPath} from "../coordination/shadow_management.ts";
+import {
+ canonicalWriteRouteFailure,
+ requireCanonicalBusinessWrite,
+} from "../coordination/local_authority_write.ts";
import {claimFileMutationLock, mutationLockOwner, releaseFileMutationLock,
releaseFileMutationLockClaim, type FileMutationLockClaim} from "../effect_runtime_io.ts";
import {planTeamTransaction, replayTeamTransaction, teamTransactionIdentity, TEAM_TRANSACTION_SCHEMA} from "./team_plan.ts";
@@ -86,12 +90,16 @@ export async function commitLocalTeamPlan(value: unknown): Promise {
if (owner?.token !== witness.token || owner.pid !== witness.pid) break;
if (i === witnesses.length - 1) adopted = true;
}
+ await requireShadowPrimaryWriteAllowed(root, goal);
+ await requireCanonicalBusinessWrite(root, goal, "team_plan");
const store = await openLocalAuthorityStore(root, goal);
const result = await commitTeamPlan(store, request, async () => {
// Exact committed receipts replay before these admission checks. An
// expired handoff may read history, but can never authorize a new write.
if (!adopted) throw new EffectRuntimeRequestError("team plan source lock handoff expired; retry the original confirmation");
- await requireShadowPrimaryWriteAllowed(root, goal);
+ // Unreadable display may recover an existing receipt, but cannot supply the
+ // Goal intent needed to admit a new plan, even if its absence was reviewed.
+ if (typeof source.state_sha256 !== "string") return false;
const digest = async (path: string) => createHash("sha256").update(await readFile(path)).digest("hex");
return await digest(registry) === source.registry_sha256 && await digest(state) === source.state_sha256;
});
@@ -99,7 +107,8 @@ export async function commitLocalTeamPlan(value: unknown): Promise {
decision_read_from_provider: true, legacy_fallback_used: false};
} catch (error) {
return {status: "failed", reason_code: error instanceof EffectRuntimeRequestError ? error.code : "team_plan_commit_failed",
- reason: error instanceof Error ? error.message : String(error), ...localAuthorityOpenFailure(error)};
+ reason: error instanceof Error ? error.message : String(error),
+ ...localAuthorityOpenFailure(error), ...canonicalWriteRouteFailure(error)};
} finally {
// Claims keep the source markers alive even after a requesting Python
// process exits. The native effect releases the handed-off critical section.
diff --git a/loopx/semantics/goal_instance_binding_inventory_v1.json b/loopx/semantics/goal_instance_binding_inventory_v1.json
index fc63ae2615..23720079be 100644
--- a/loopx/semantics/goal_instance_binding_inventory_v1.json
+++ b/loopx/semantics/goal_instance_binding_inventory_v1.json
@@ -121,7 +121,6 @@
"content_digest_signal": "outbox entry source digest",
"observed_reference": "goal_id + goal_instance_id",
"producer_sites": [
- "loopx/control_plane/coordination/local_authority_shadow_outbox.ts::beginLeaseOutboxEntry",
"loopx/control_plane/todos/handoff_note.py::attach_todo_handoff_note"
],
"consumer_sites": [
diff --git a/loopx/todos.py b/loopx/todos.py
index a9d37b9376..50f9335af3 100644
--- a/loopx/todos.py
+++ b/loopx/todos.py
@@ -23,7 +23,7 @@
resolve_todo_state_path as resolve_todo_state_path,
)
-# Preserve the existing import surface without loading unpromoted writer code.
+# Retain read and semantic compatibility imports without loading retired writers.
_EXPORTS = {
"add_goal_todo": "loopx.control_plane.todos.mutation_api",
"update_goal_todo": "loopx.control_plane.todos.mutation_api",
@@ -97,12 +97,9 @@
"todo_summaries_from_fields": "loopx.control_plane.todos.goal_todo_projection",
"parse_todo_source": "loopx.control_plane.todos.active_state_todo_parser",
"todo_monitor_metadata": "loopx.control_plane.todos",
- "authorize_todo_lifecycle_mutation": "loopx.control_plane.todos.mutation_authority",
- "todo_update_authority_action": "loopx.control_plane.todos.mutation_authority",
"build_open_parent_successor_advisory": "loopx.control_plane.todos.succession_warning",
"build_successor_intents": "loopx.control_plane.todos.successor_derivation",
"derive_successor_proposals": "loopx.control_plane.todos.successor_derivation",
- "successor_add_kwargs": "loopx.control_plane.todos.successor_derivation",
"MAX_TODO_INDEX_ROLLOUT_EVENTS_PER_GOAL": "loopx.control_plane.todos.todo_index",
"normalize_new_todo": "loopx.control_plane.todos.text",
"list_goal_todos": "loopx.control_plane.todos.list_readback",
@@ -112,32 +109,16 @@
"require_completion_decision_outcome": "loopx.control_plane.todos.unblock_resume",
"_attach_todo_write_correctness_dry_run_packet": "loopx.control_plane.todos.write_correctness",
"require_user_todo_task_class": "loopx.control_plane.todos.authoring_scope",
- "legacy_todo_write_transaction": "loopx.control_plane.coordination.legacy_writer_fence",
"canonical_todo_items": "loopx.control_plane.coordination.local_authority",
"canonical_todo_summary_fields": "loopx.control_plane.coordination.local_authority",
"read_canonical_todos_if_promoted": "loopx.control_plane.coordination.local_authority",
"resolve_todo_state_path": "loopx.control_plane.todos.path_resolution",
- "provider_first_terminal_lifecycle": "loopx.control_plane.todos.provider_terminal_lifecycle",
"goal_handoff_mode": "loopx.control_plane.todos.handoff_mode",
- "enter_added_todo_ownership_handoff_gate": "loopx.control_plane.todos.handoff_mode",
- "enter_todo_ownership_handoff_gate": "loopx.control_plane.todos.handoff_mode",
- "resolve_todo_completion_handoff": "loopx.control_plane.todos.handoff_mode",
"effective_runtime_root": "loopx.paths",
- "enter_terminal_todo_lease_fence": "loopx.control_plane.work_items.task_lease",
- "hold_task_lease_mutation_fence": "loopx.control_plane.work_items.task_lease",
- "release_verified_task_lease_fence": "loopx.control_plane.work_items.task_lease",
"supersede_goal_todo": "loopx.control_plane.todos.mutation_api",
"archive_completed_todos": "loopx.control_plane.todos.mutation_api",
"ARCHIVE_COMPLETED_DEFAULT_MAX_ACTIVE_DONE": "loopx.control_plane.todos.mutation_api",
"resolve_todo_state": "loopx.control_plane.todos.path_resolution",
- "add_todo_to_lines": "loopx.control_plane.todos.legacy_mutation",
- "_add_goal_todo_legacy": "loopx.control_plane.todos.legacy_mutation",
- "_update_goal_todo_legacy": "loopx.control_plane.todos.legacy_mutation",
- "_complete_goal_todo_legacy": "loopx.control_plane.todos.legacy_mutation",
- "apply_todo_update_to_lines": "loopx.control_plane.todos.line_update",
- "link_generated_successor_todo_ids": "loopx.control_plane.todos.line_update",
- "link_superseding_todo_id": "loopx.control_plane.todos.line_update",
- "upsert_todo_metadata": "loopx.control_plane.todos.line_update",
}
diff --git a/loopx/visible_governance.py b/loopx/visible_governance.py
deleted file mode 100644
index ca89e6c912..0000000000
--- a/loopx/visible_governance.py
+++ /dev/null
@@ -1,611 +0,0 @@
-"""Synthetic visible-governance slice for GH-C62.
-
-Projects per-goal/per-agent claims, optional task leases, quota, scheduler
-hints, decision scopes, and the shared-goal RFC's proposed authority/state-
-provider boundary into one read-only projection. Every section makes
-proposal-versus-shipped-truth explicit; no write API, no inference from
-prose, no presentation of provider observations or leases as authority.
-"""
-
-from __future__ import annotations
-
-from typing import Any
-
-from .control_plane.quota.decision_summary import compact_quota_decision
-from .control_plane.todos.decision_scope import (
- build_required_decision_scope_consistency,
-)
-from .control_plane.todos.contract import (
- normalize_todo_claimed_by,
- normalize_todo_id,
-)
-
-
-# ── schema constants ────────────────────────────────────────────────────────
-GOVERNANCE_SLICE_SCHEMA = "visible_governance_slice_v0"
-LEASE_NATURE = "optional runtime fence, not authority"
-
-
-# ── authority-boundary static table ─────────────────────────────────────────
-def _build_authority_boundary_table() -> list[dict[str, Any]]:
- """Implementation availability, not a claim that this Goal was promoted.
-
- Keep the original RFC section identifiers for report consumers. The original
- prototype commands/formats are distinct from their shipped native successors.
- Per-Goal authority must still be read from its actual binding and writer fence.
- """
- return [
- {
- "rfc_section": "3 -- State Classification",
- "proposal": "Shared canonical Todo lifecycle, claims, leases and operation receipts",
- "shipped": True,
- "shipped_equivalent": (
- "Promoted Goals use native TS commands and AuthorityStore transactions. "
- "Unpromoted Goals retain the Markdown/lease adapters."
- ),
- "gap": "Implementation availability does not select the default provider or migrate this Goal.",
- },
- {
- "rfc_section": "4 -- Coordination Ledger Shape",
- "proposal": "Original coordination head aggregate with an embedded receipt index",
- "shipped": False,
- "shipped_equivalent": (
- "The Python prototype is retired. Native File/SQLite journals retain "
- "head, committed history and receipts through one AuthorityStore contract. "
- "PostgreSQL and NoKV have separate candidate qualification."
- ),
- "gap": "The retired prototype head is not a supported Goal journal or migration source.",
- },
- {
- "rfc_section": "5.1 -- claim_work command",
- "proposal": "Atomic claim, lease and receipt publication",
- "shipped": False,
- "shipped_equivalent": (
- "The experimental claim_work executor is retired. Native todo_claim.ts "
- "owns atomic claim/lease acquisition on canonical providers."
- ),
- "gap": (
- "A historical receipt is not current execution authority. Lease/effect "
- "checks and the actual Goal binding remain required; claim_work is not a public command."
- ),
- },
- {
- "rfc_section": "7 -- Receipt Retention",
- "proposal": "Retain original receipts and fail closed on unproved outcomes",
- "shipped": True,
- "shipped_equivalent": "Native AuthorityStore readReceipt and committed history preserve replay evidence.",
- "gap": "Receipt correctness does not qualify unbounded capacity or the SQLite D2 profile.",
- },
- {
- "rfc_section": "8 -- Local vs Shared Mode",
- "proposal": "Explicit provider binding, writer fencing and migration parity",
- "shipped": True,
- "shipped_equivalent": "Reviewed per-Goal promotion and fenced rollback are implemented.",
- "gap": "Whole-Goal cohort qualification and default onboarding remain separate from per-Goal opt-in.",
- },
- {
- "rfc_section": "Appendix B -- handoff_mode",
- "proposal": "Explicit legacy, soft_claim or hard_lease coordination mode",
- "shipped": True,
- "shipped_equivalent": "The native handoff-mode command owns transition admission and quiescence checks.",
- "gap": "Provider selection does not silently replace the Goal's chosen handoff mode.",
- },
- {
- "rfc_section": "9 -- Offline/Local Boundaries",
- "proposal": "Unavailable shared authority must not silently fall back to local writers",
- "shipped": False,
- "shipped_equivalent": "Promoted local providers fail closed when canonical authority cannot be read.",
- "gap": "Deployed cross-host service availability and execution-interval fencing need separate qualification.",
- },
- ]
-
-
-# ── claims projection ───────────────────────────────────────────────────────
-def _build_claims_projection(
- status_payload: dict[str, Any],
- *,
- goal_id: str,
- agent_id: str | None,
-) -> dict[str, Any]:
- """Extract per-todo soft claims from the status attention queue.
-
- Looks inside ``agent_todos`` summary lists (``first_open_items``,
- ``first_executable_items``) within each goal-level attention queue
- item to find todos with a ``claimed_by`` field.
- """
- attention = (
- status_payload.get("attention_queue")
- if isinstance(status_payload.get("attention_queue"), dict)
- else {}
- )
- items = attention.get("items") if isinstance(attention.get("items"), list) else []
-
- claims: list[dict[str, Any]] = []
- by_agent: dict[str, list[dict[str, Any]]] = {}
- seen: set[str] = set()
-
- _SUMMARY_LIST_KEYS = (
- "first_open_items",
- "first_executable_items",
- "executable_backlog_items",
- "backlog_items",
- )
-
- for item in items:
- if not isinstance(item, dict):
- continue
- item_goal = str(item.get("goal_id") or "")
- if item_goal and item_goal != goal_id:
- continue
-
- agent_todos = (
- item.get("agent_todos")
- if isinstance(item.get("agent_todos"), dict)
- else {}
- )
- for list_key in _SUMMARY_LIST_KEYS:
- todo_list = agent_todos.get(list_key)
- if not isinstance(todo_list, list):
- continue
- for td in todo_list:
- if not isinstance(td, dict):
- continue
- todo_id = normalize_todo_id(td.get("todo_id"))
- claimed_by = normalize_todo_claimed_by(td.get("claimed_by"))
- if not claimed_by:
- continue
- if todo_id in seen:
- continue
- seen.add(todo_id)
- claim_entry: dict[str, Any] = {
- "todo_id": todo_id,
- "claimed_by": claimed_by,
- "task_class": td.get("task_class"),
- "status": td.get("status") or (
- "done" if td.get("done") else "open"
- ),
- }
- for key in ("text", "target_key", "action_kind"):
- value = td.get(key)
- if value:
- claim_entry[key] = str(value)[:200]
- claims.append(claim_entry)
- by_agent.setdefault(claimed_by, []).append(claim_entry)
-
- normalized_agent = normalize_todo_claimed_by(agent_id)
- return {
- "total_claims": len(claims),
- "claiming_agent_count": len(by_agent),
- "current_agent_claims": len(by_agent.get(normalized_agent, []))
- if normalized_agent
- else 0,
- "by_agent": {
- agent: len(agent_claims)
- for agent, agent_claims in sorted(by_agent.items())
- },
- "claims": claims,
- }
-
-
-# ── leases projection ───────────────────────────────────────────────────────
-def _build_leases_projection(
- status_payload: dict[str, Any],
- *,
- goal_id: str,
- agent_id: str | None,
- include_task_leases: bool,
-) -> dict[str, Any] | None:
- """Read active task leases for the goal (read-only, never writes)."""
- if not include_task_leases:
- return None
-
- from .control_plane.work_items.task_lease import (
- lease_epoch,
- lease_is_active,
- read_lease,
- task_lease_dir,
- )
-
- runtime_root_raw = status_payload.get("runtime_root")
- if not runtime_root_raw:
- return {"active_leases": 0, "leases": [], "nature": LEASE_NATURE}
-
- from pathlib import Path
-
- lease_dir = task_lease_dir(
- runtime_root=Path(str(runtime_root_raw)),
- goal_id=goal_id,
- )
- if not lease_dir.exists():
- return {"active_leases": 0, "leases": [], "nature": LEASE_NATURE}
-
- active_leases: list[dict[str, Any]] = []
- for path in sorted(lease_dir.glob("todo_*.json")):
- lease = read_lease(path)
- if not lease_is_active(lease):
- continue
- active_leases.append(
- {
- "todo_id": lease.get("todo_id"),
- "owner": lease.get("owner"),
- "expires_at": lease.get("expires_at"),
- "version": lease.get("version"),
- "lease_epoch": lease_epoch(lease),
- "write_scopes": lease.get("write_scopes") or [],
- "acquire_ttl_seconds": lease.get("acquire_ttl_seconds"),
- }
- )
-
- normalized_agent = normalize_todo_claimed_by(agent_id)
- return {
- "active_leases": len(active_leases),
- "current_agent_active_leases": sum(
- 1
- for lease in active_leases
- if lease.get("owner") == normalized_agent
- ),
- "leases": active_leases,
- "nature": LEASE_NATURE,
- }
-
-
-# ── quota projection ────────────────────────────────────────────────────────
-def _build_quota_projection(
- status_payload: dict[str, Any],
- *,
- goal_id: str,
- agent_id: str | None,
-) -> dict[str, Any] | None:
- """Compact quota decision via ``build_quota_should_run``."""
- from .quota import build_quota_should_run
-
- try:
- decision = build_quota_should_run(
- status_payload,
- goal_id=goal_id,
- agent_id=agent_id,
- )
- except (ValueError, KeyError, OSError):
- return None
- return compact_quota_decision(decision)
-
-
-# ── scheduler hints projection ──────────────────────────────────────────────
-def _build_scheduler_hints_projection(
- status_payload: dict[str, Any],
- *,
- goal_id: str,
- agent_id: str | None,
-) -> dict[str, Any] | None:
- """Extract scheduler hints from the quota should-run packet."""
- from .quota import build_quota_should_run
-
- try:
- decision = build_quota_should_run(
- status_payload,
- goal_id=goal_id,
- agent_id=agent_id,
- include_scheduler_detail=True,
- )
- except (ValueError, KeyError, OSError):
- return None
-
- scheduler = decision.get("scheduler_hint")
- if not isinstance(scheduler, dict):
- return None
-
- return {
- "action": scheduler.get("action"),
- "cadence_class": scheduler.get("cadence_class"),
- "recommended_interval_minutes": scheduler.get(
- "recommended_interval_minutes"
- ),
- "max_interval_minutes": scheduler.get("max_interval_minutes"),
- "reset_policy": scheduler.get("reset_policy"),
- "unchanged_poll": {
- k: scheduler.get("unchanged_poll", {}).get(k)
- for k in ("backoff_multiplier", "max_interval_minutes")
- if k in scheduler.get("unchanged_poll", {})
- }
- if isinstance(scheduler.get("unchanged_poll"), dict)
- else None,
- "arbitration": scheduler.get("arbitration_disposition"),
- }
-
-
-# ── decision scopes projection ──────────────────────────────────────────────
-def _build_decision_scopes_projection(
- status_payload: dict[str, Any],
- *,
- goal_id: str,
- agent_id: str | None,
-) -> dict[str, Any]:
- """Project decision scopes from quota should-run summaries.
-
- Uses ``build_quota_should_run`` to obtain agent/user todo summaries
- and standing decision authority, then runs the consistency check.
- """
- from .quota import build_quota_should_run
-
- try:
- decision = build_quota_should_run(
- status_payload,
- goal_id=goal_id,
- agent_id=agent_id,
- )
- except (ValueError, KeyError, OSError):
- return {
- "standing_decision_authority": None,
- "required_scope_consistency": {
- "ok": None,
- "status": "unavailable",
- },
- }
-
- # standing authority already computed by the quota guard
- standing = decision.get("standing_decision_authority")
-
- # Build consistency from the summaries in the should_run payload
- agent_summary = decision.get("agent_todo_summary")
- user_summary = decision.get("user_todo_summary")
- consistency = build_required_decision_scope_consistency(
- agent_todo_summary=agent_summary if isinstance(agent_summary, dict) else None,
- user_todo_summary=user_summary if isinstance(user_summary, dict) else None,
- agent_id=agent_id,
- registered_agent_ids=None,
- standing_decision_authority=standing,
- )
-
- return {
- "standing_decision_authority": standing,
- "required_scope_consistency": {
- "ok": consistency.get("ok"),
- "status": consistency.get("status"),
- "checked_agent_todo_count": consistency.get(
- "checked_agent_todo_count"
- ),
- "checked_required_scope_count": consistency.get(
- "checked_required_scope_count"
- ),
- "standing_authority_match_count": consistency.get(
- "standing_authority_match_count"
- ),
- "terminal_outcome_count": consistency.get("terminal_outcome_count"),
- },
- }
-
-
-# ── main builder ────────────────────────────────────────────────────────────
-def build_visible_governance_slice(
- status_payload: dict[str, Any],
- *,
- goal_id: str,
- agent_id: str | None = None,
- include_task_leases: bool = True,
-) -> dict[str, Any]:
- """Synthesize a read-only visible-governance slice for one goal.
-
- Args:
- status_payload: Full ``collect_status(...)`` result.
- goal_id: Goal to project governance for.
- agent_id: Optional agent lane to scope per-agent projections.
- include_task_leases: When True, read active lease files (read-only).
-
- Returns:
- ``{"schema_version": "visible_governance_slice_v0", "ok": True, ...}``
- with sections: claims, leases, quota, scheduler_hints,
- decision_scopes, authority_boundary.
- """
- safe_goal_id = str(goal_id or "").strip()
- if not safe_goal_id:
- return {
- "schema_version": GOVERNANCE_SLICE_SCHEMA,
- "ok": False,
- "error": "goal_id is required",
- }
-
- claims = _build_claims_projection(
- status_payload, goal_id=safe_goal_id, agent_id=agent_id
- )
- leases = _build_leases_projection(
- status_payload,
- goal_id=safe_goal_id,
- agent_id=agent_id,
- include_task_leases=include_task_leases,
- )
- quota = _build_quota_projection(
- status_payload, goal_id=safe_goal_id, agent_id=agent_id
- )
- scheduler_hints = _build_scheduler_hints_projection(
- status_payload, goal_id=safe_goal_id, agent_id=agent_id
- )
- decision_scopes = _build_decision_scopes_projection(
- status_payload, goal_id=safe_goal_id, agent_id=agent_id
- )
- authority_boundary = _build_authority_boundary_table()
-
- return {
- "schema_version": GOVERNANCE_SLICE_SCHEMA,
- "ok": True,
- "goal_id": safe_goal_id,
- "agent_id": normalize_todo_claimed_by(agent_id),
- "claims": claims,
- "leases": leases,
- "quota": quota,
- "scheduler_hints": scheduler_hints,
- "decision_scopes": decision_scopes,
- "authority_boundary": authority_boundary,
- }
-
-
-# ── markdown renderer ───────────────────────────────────────────────────────
-def render_visible_governance_markdown(slice_payload: dict[str, Any]) -> str:
- """Render a visible-governance slice as public-safe markdown."""
-
- goal_id = str(slice_payload.get("goal_id") or "unknown")
- agent_id = str(slice_payload.get("agent_id") or "")
-
- lines: list[str] = []
- lines.append("# Visible Governance Slice")
- lines.append("")
- lines.append(f"- **Goal**: `{goal_id}`")
- if agent_id:
- lines.append(f"- **Agent**: `{agent_id}`")
- lines.append(f"- **Schema**: `{slice_payload.get('schema_version')}`")
- lines.append("")
-
- # ── Claims ──
- claims = slice_payload.get("claims")
- if isinstance(claims, dict):
- lines.append("## Claims (Soft Ownership)")
- lines.append("")
- lines.append(
- f"- Total claims: **{claims.get('total_claims', 0)}** "
- f"across {claims.get('claiming_agent_count', 0)} agent(s)"
- )
- current = claims.get("current_agent_claims", 0)
- if current:
- lines.append(f"- Current agent claims: **{current}**")
- by_agent = claims.get("by_agent")
- if isinstance(by_agent, dict) and by_agent:
- lines.append("")
- lines.append("| Agent | Claim Count |")
- lines.append("|-------|-------------|")
- for agent, count in sorted(by_agent.items()):
- lines.append(f"| `{agent}` | {count} |")
- lines.append("")
-
- # ── Leases ──
- leases = slice_payload.get("leases")
- if isinstance(leases, dict):
- lines.append("## Task Leases (Optional Runtime Fence)")
- lines.append("")
- lines.append(f"- Nature: *{leases.get('nature', LEASE_NATURE)}*")
- lines.append(
- f"- Active leases: **{leases.get('active_leases', 0)}**"
- )
- lease_list = leases.get("leases")
- if isinstance(lease_list, list) and lease_list:
- lines.append("")
- lines.append(
- "| Todo ID | Owner | Expires | Version | TTL (s) |"
- )
- lines.append(
- "|---------|-------|---------|---------|---------|"
- )
- for lease in lease_list:
- lines.append(
- f"| `{lease.get('todo_id', '')}` "
- f"| `{lease.get('owner', '')}` "
- f"| {lease.get('expires_at', '')} "
- f"| {lease.get('version', '')} "
- f"| {lease.get('acquire_ttl_seconds', '')} |"
- )
- lines.append("")
-
- # ── Quota ──
- quota = slice_payload.get("quota")
- if isinstance(quota, dict):
- lines.append("## Quota Decision")
- lines.append("")
- lines.append(f"- Should run: `{quota.get('should_run')}`")
- lines.append(
- f"- Effective action: `{quota.get('effective_action', '')}`"
- )
- lines.append(f"- State: `{quota.get('state', '')}`")
- compute = quota.get("compute")
- if compute is not None:
- lines.append(f"- Compute: {compute}")
- slots_info = []
- for key in ("spent_slots", "allowed_slots", "slot_minutes"):
- value = quota.get(key)
- if value is not None:
- slots_info.append(f"{key}: {value}")
- if slots_info:
- lines.append(f"- Slots: {', '.join(slots_info)}")
- lines.append("")
-
- # ── Scheduler Hints ──
- sched = slice_payload.get("scheduler_hints")
- if isinstance(sched, dict):
- lines.append("## Scheduler Hints")
- lines.append("")
- lines.append(f"- Action: `{sched.get('action', '')}`")
- lines.append(
- f"- Cadence class: `{sched.get('cadence_class', '')}`"
- )
- interval = sched.get("recommended_interval_minutes")
- if interval is not None:
- lines.append(f"- Recommended interval: {interval} min")
- lines.append(
- f"- Arbitration: `{sched.get('arbitration', '')}`"
- )
- lines.append("")
-
- # ── Decision Scopes ──
- scopes = slice_payload.get("decision_scopes")
- if isinstance(scopes, dict):
- lines.append("## Decision Scopes")
- lines.append("")
- standing = scopes.get("standing_decision_authority")
- if isinstance(standing, dict):
- lines.append(
- f"- Standing authority receipts: "
- f"**{standing.get('active_count', 0)}** active, "
- f"{standing.get('inactive_count', 0)} inactive"
- )
- consistency = scopes.get("required_scope_consistency")
- if isinstance(consistency, dict):
- lines.append(
- f"- Scope consistency: "
- f"`{consistency.get('status', 'unknown')}` "
- f"(checked {consistency.get('checked_agent_todo_count', 0)} "
- f"todos, {consistency.get('checked_required_scope_count', 0)} "
- f"scopes)"
- )
- matches = consistency.get("standing_authority_match_count", 0)
- if matches:
- lines.append(
- f"- Standing authority matches: **{matches}**"
- )
- lines.append("")
-
- # ── Authority Boundary ──
- boundary = slice_payload.get("authority_boundary")
- if isinstance(boundary, list):
- lines.append("## Authority Boundary: Proposal vs Shipped")
- lines.append("")
- lines.append(
- "| RFC Section | Shipped | Gap |"
- )
- lines.append(
- "|-------------|---------|-----|"
- )
- for entry in boundary:
- shipped = "Yes" if entry.get("shipped") else "**No**"
- gap = str(entry.get("gap") or "")[:120]
- lines.append(
- f"| {entry.get('rfc_section', '')} "
- f"| {shipped} "
- f"| {gap} |"
- )
- lines.append("")
-
- # ── Summary ──
- lines.append("## Summary")
- lines.append("")
- lines.append(
- f"- **Proposals shipped**: "
- f"{sum(1 for e in (boundary or []) if e.get('shipped'))} / "
- f"{len(boundary) if boundary else 0}"
- )
- lines.append(
- "- **Key takeaway**: Stage-2 ships an additive coordination head, "
- "file provider, and claim_work executor plus handoff_mode; default "
- "runtime still uses Markdown soft claims, file-backed task leases "
- "(optional fence, not write authority), and turn-scoped settlement. "
- "Shared-mode promotion remains Stage 3."
- )
- lines.append("")
-
- return "\n".join(lines)
diff --git a/man/loopx.1 b/man/loopx.1
index 2cb9fce472..36ca34ba52 100644
--- a/man/loopx.1
+++ b/man/loopx.1
@@ -1,4 +1,4 @@
-.TH LOOPX 1 "" "LoopX 1.4.2" "User Commands"
+.TH LOOPX 1 "" "LoopX 2.0.0a1" "User Commands"
.SH NAME
loopx \- control-plane helper for long-running agent work
.SH SYNOPSIS
diff --git a/packages/dsh-loopx-plugin/tests/admission-closeout.integration.spec.ts b/packages/dsh-loopx-plugin/tests/admission-closeout.integration.spec.ts
index a16ad78ea5..3ba52f18fd 100644
--- a/packages/dsh-loopx-plugin/tests/admission-closeout.integration.spec.ts
+++ b/packages/dsh-loopx-plugin/tests/admission-closeout.integration.spec.ts
@@ -105,6 +105,7 @@ async function fixture(): Promise {
'---',
'status: active-read-only',
'owner_mode: goal',
+ 'handoff_mode: soft_claim',
'objective: "Validate one DSH continuation settlement."',
'updated_at: 2026-01-01T00:00:00+00:00',
'---',
@@ -152,6 +153,30 @@ async function fixture(): Promise {
quota: { compute: 1, window_hours: 24, allowed_slots: 2 },
}],
}, null, 2)}\n`, 'utf8')
+ // The closeout journey starts on supported authority. Markdown remains a
+ // projection; this test must not depend on the retired ordinary writer.
+ const canonical = await runFile(await integrationPython, ['-c', [
+ 'import sys',
+ 'from pathlib import Path',
+ 'sys.path.insert(0, str(Path(sys.argv[1]) / "tests/control_plane"))',
+ 'from canonical_authority_fixture import initialize_canonical_authority',
+ 'from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection',
+ 'projection = build_todo_runtime_shadow_projection(',
+ ' goal_id=sys.argv[3], handoff_mode="soft_claim", todos=[{',
+ ' "schema_version": "todo_item_v0", "todo_id": sys.argv[5],',
+ ' "role": "agent", "status": "open", "done": False,',
+ ' "text": "[P1] Validate and settle the selected delivery.",',
+ ' "title": "Validate and settle the selected delivery.", "priority": "P1",',
+ ' "archive_state": "active", "source_section": "Agent Todo", "index": 1,',
+ ' "task_class": "advancement_task", "action_kind": "validate",',
+ ' }])',
+ 'initialize_canonical_authority(Path(sys.argv[2]), sys.argv[3], projection,',
+ ' state_path=Path(sys.argv[4]), provider="file")',
+ ].join('\n'), repoRoot, runtime, goalId, state, todoId], {
+ cwd: repoRoot,
+ env: { ...process.env, PYTHONPATH: repoRoot },
+ })
+ expect(canonical.exitCode, canonical.stderr).toBe(0)
expect((await runFile('git', ['init', '--quiet'], { cwd: project })).exitCode).toBe(0)
expect((await runFile('git', [
'remote', 'add', 'origin',
diff --git a/pyproject.toml b/pyproject.toml
index 465a5980c3..953aaa3624 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "loopx"
-version = "1.4.2"
+version = "2.0.0a1"
description = "A Loop Engineering control plane for long-running agent goals (requires Node.js 22.22.3+)."
readme = "README.md"
requires-python = ">=3.11"
@@ -201,8 +201,6 @@ files = [
"loopx/control_plane/testing/authority_e2e_fixtures.py",
"loopx/control_plane/testing/authority_e2e_ladder.py",
"loopx/control_plane/testing/authority_e2e_row_support.py",
- "loopx/control_plane/testing/authority_e2e_rows_stage2c.py",
- "loopx/control_plane/testing/authority_e2e_rows_stage2c2.py",
"loopx/control_plane/work_items/delivery_batch_scale.py",
"loopx/control_plane/work_items/delivery_outcome.py",
"loopx/control_plane/work_items/lifecycle.py",
diff --git a/scripts/chat_bundle.py b/scripts/chat_bundle.py
index 079f5d0c14..bb8d881f5d 100644
--- a/scripts/chat_bundle.py
+++ b/scripts/chat_bundle.py
@@ -249,7 +249,21 @@ def release_build(tag: str, repository: str) -> None:
]
def number(value: str) -> tuple[int, ...]:
- return tuple(map(int, value.removeprefix("v").split(".")))
+ match = re.fullmatch(
+ r"v(\d+)\.(\d+)\.(\d+)(?:(a|b|rc)(\d+))?",
+ value,
+ )
+ if match is None:
+ raise ValueError(f"invalid LoopX release tag: {value}")
+ major, minor, patch, prerelease, prerelease_number = match.groups()
+ prerelease_rank = {None: 3, "a": 0, "b": 1, "rc": 2}[prerelease]
+ return (
+ int(major),
+ int(minor),
+ int(patch),
+ prerelease_rank,
+ int(prerelease_number or 0),
+ )
predecessors = [r for r in versions if number(r["tag_name"]) < number(tag)]
if not predecessors:
diff --git a/scripts/qualify-claude-goal-release.py b/scripts/qualify-claude-goal-release.py
index 0f0d475a7d..20a3bcdb4f 100644
--- a/scripts/qualify-claude-goal-release.py
+++ b/scripts/qualify-claude-goal-release.py
@@ -28,6 +28,17 @@
MANIFEST_ASSETS = {"assets/alpha.txt": b"alpha\n", "assets/nested/beta.bin": bytes(range(256))}
+def _replan_active_state() -> str:
+ return (
+ "---\nstatus: active\n---\n\n## Objective\n\nDeliver and validate TASK.md.\n\n"
+ "## Next Action\n\nVerify artifact integrity, beyond the completed filename inventory.\n\n"
+ "## User Todo\n\n## Agent Todo\n\n"
+ "- [ ] [P1] Inventory the regular asset filenames in manifest.json.\n"
+ " \n"
+ )
+
+
def run_host(command: list[str], *, cwd: Path, env: dict, timeout: float) -> str:
"""Bound output memory and clean this test's process group, including on timeout."""
with tempfile.TemporaryFile() as stdout, tempfile.TemporaryFile() as stderr:
@@ -157,7 +168,10 @@ def setup_replan(root: Path) -> tuple[Path, Path, Path]:
"""A legitimately finished inventory stage, not fabricated Goal acceptance."""
from loopx.goal_mode_mcp import GoalModeMCPConfig, GoalModeMCPControlPlane
- project, runtime, launcher = shared.setup(root)
+ project, runtime, launcher = shared.setup(
+ root,
+ active_state_text=_replan_active_state(),
+ )
assets = project / "assets"
for name, content in MANIFEST_ASSETS.items():
path = project / name
@@ -165,14 +179,6 @@ def setup_replan(root: Path) -> tuple[Path, Path, Path]:
path.write_bytes(content)
(project / "manifest.json").write_text(json.dumps(["assets/alpha.txt", "assets/nested/beta.bin"]))
shutil.copyfile(REPO / "tests/fixtures/host_vision_replan/TASK.md", project / "TASK.md")
- (project / "ACTIVE_GOAL_STATE.md").write_text(
- "---\nstatus: active\n---\n\n## Objective\n\nDeliver and validate TASK.md.\n\n"
- "## Next Action\n\nVerify artifact integrity, beyond the completed filename inventory.\n\n"
- "## User Todo\n\n## Agent Todo\n\n"
- "- [ ] [P1] Inventory the regular asset filenames in manifest.json.\n"
- " \n",
- )
assert json.loads((project / "manifest.json").read_text()) == sorted(
str(path.relative_to(project)) for path in assets.rglob("*") if path.is_file())
control = GoalModeMCPControlPlane(GoalModeMCPConfig(server_name="fixture",
diff --git a/scripts/qualify-native-goal-release.py b/scripts/qualify-native-goal-release.py
index 9af62a2ea4..c1b65664bd 100644
--- a/scripts/qualify-native-goal-release.py
+++ b/scripts/qualify-native-goal-release.py
@@ -22,6 +22,24 @@
TODOS = {"todo_reducer", "todo_cli"}
+def _default_active_state() -> str:
+ return (
+ '---\nstatus: active-read-only\nowner_mode: goal\n'
+ 'objective: "Deliver the finite local ledger specification."\n---\n\n'
+ '# Ledger\n\n## Objective\n\nDeliver and validate TASK.md.\n\n'
+ '## Next Action\n\nImplement the reducer, then the CLI.\n\n'
+ '## User Todo\n\n## Agent Todo\n\n'
+ '- [ ] [P1] Implement and validate the TASK.md reducer.\n'
+ ' \n'
+ '- [ ] [P1] Implement the CLI, verify full acceptance and close this finite Goal.\n'
+ ' \n'
+ )
+
+
def host_environment(root: Path, launcher: Path) -> dict[str, str]:
"""Explicit transport/tool environment; never clone the operator's environment."""
home, temporary = root / "home", root / "tmp"
@@ -83,7 +101,11 @@ def prerequisite_failure(codex: str) -> str | None:
return None
-def setup(root: Path) -> tuple[Path, Path, Path]:
+def setup(
+ root: Path,
+ *,
+ active_state_text: str | None = None,
+) -> tuple[Path, Path, Path]:
# Reuse the existing real CLI fixture, not an alternate registry contract.
spec = importlib.util.spec_from_file_location(
"heartbeat_fixture", REPO / "examples/control_plane/heartbeat-quota-flow-smoke.py",
@@ -99,19 +121,7 @@ def setup(root: Path) -> tuple[Path, Path, Path]:
runtime.mkdir(parents=True, exist_ok=True)
(runtime / "registry.global.json").write_text(json.dumps(config))
(project / "ACTIVE_GOAL_STATE.md").write_text(
- '---\nstatus: active-read-only\nowner_mode: goal\n'
- 'objective: "Deliver the finite local ledger specification."\n---\n\n'
- '# Ledger\n\n## Objective\n\nDeliver and validate TASK.md.\n\n'
- '## Next Action\n\nImplement the reducer, then the CLI.\n\n'
- '## User Todo\n\n## Agent Todo\n\n'
- '- [ ] [P1] Implement and validate the TASK.md reducer.\n'
- ' \n'
- '- [ ] [P1] Implement the CLI, verify full acceptance and close this finite Goal.\n'
- ' \n',
+ active_state_text if active_state_text is not None else _default_active_state(),
encoding="utf-8",
)
shutil.copyfile(FIXTURE / "TASK.md", project / "TASK.md")
@@ -128,6 +138,54 @@ def setup(root: Path) -> tuple[Path, Path, Path]:
for args in (("init", "-q"), ("remote", "add", "origin", "https://example.com/ledger.git"),
("add", "TASK.md"), ("commit", "-qm", "Initialize synthetic acceptance")):
subprocess.run([*git, *args], check=True, capture_output=True, timeout=30)
+ from loopx.state_backup import build_state_backup_plan, execute_state_backup_plan
+
+ backup = execute_state_backup_plan(
+ build_state_backup_plan(
+ project=project,
+ runtime_root=runtime,
+ output_dir=root / "backups",
+ backup_id="release-goal-source",
+ include_automations=False,
+ include_skills=False,
+ include_registry_projects=False,
+ registry_path=registry,
+ )
+ )
+ if backup.get("ok") is not True:
+ raise RuntimeError("release fixture source backup failed")
+ prepared = cli(
+ launcher,
+ "coordination-shadow",
+ "prepare-import",
+ "--goal-id",
+ GOAL,
+ "--operation-id",
+ "release-goal-source",
+ "--backup-manifest",
+ str(backup["manifest_path"]),
+ "--provider",
+ "file",
+ "--target-handoff-mode",
+ "soft_claim",
+ )["cold_import"]
+ if prepared.get("status") != "prepared":
+ raise RuntimeError("release fixture import plan failed")
+ applied = cli(
+ launcher,
+ "coordination-shadow",
+ "apply-import",
+ "--goal-id",
+ GOAL,
+ "--operation-id",
+ "release-goal-source",
+ "--plan-sha256",
+ str(prepared["plan_sha256"]),
+ "--writers-stopped",
+ "--execute",
+ )["cold_import"]
+ if applied.get("status") not in {"applied", "replayed"}:
+ raise RuntimeError("release fixture canonical import failed")
return project, runtime, launcher
diff --git a/tests/architecture/test_content_digest_single_owner.py b/tests/architecture/test_content_digest_single_owner.py
index a850876e36..b4739e4133 100644
--- a/tests/architecture/test_content_digest_single_owner.py
+++ b/tests/architecture/test_content_digest_single_owner.py
@@ -202,7 +202,6 @@
"loopx.control_plane.turn_driver.codex_sessions",
"loopx.control_plane.work_items.governed_transition_proposal",
"loopx.control_plane.work_items.progress_review_policy",
- "loopx.control_plane.work_items.task_lease",
"loopx.domain_packs.issue_fix",
"loopx.extensions.lark.document_comment_provider",
"loopx.extensions.openviking_semantic_preference.history_export",
diff --git a/tests/architecture/test_control_plane_import_boundaries.py b/tests/architecture/test_control_plane_import_boundaries.py
index 4b51d689a9..0c912989b1 100644
--- a/tests/architecture/test_control_plane_import_boundaries.py
+++ b/tests/architecture/test_control_plane_import_boundaries.py
@@ -208,8 +208,7 @@ def _public_contract_evidence() -> dict[tuple[str, str], set[str]]:
def _review_outward_dependencies(edges, *, reviewed=REVIEWED_MAINTAINABILITY_EXCEPTIONS):
- # Use the existing exact debt owner: the quarantined legacy writer retains
- # its Explore completion guard until retirement. This is not a prefix waiver.
+ # Review exact edges: one exception must never waive a module prefix.
findings = [
{"id": f"dependency_debt:{source}->{target}", "category": "dependency_debt"}
for source, target in sorted(edges)
@@ -237,35 +236,45 @@ def test_control_plane_has_no_unreviewed_outward_dependencies() -> None:
)
+@pytest.fixture
+def reviewed_outward_edge():
+ # Synthetic review evidence keeps the checker covered after real debt exits.
+ edge = ("loopx.control_plane.fixture_bridge", "loopx.capabilities.fixture_owner")
+ key = f"dependency_debt:{edge[0]}->{edge[1]}"
+ return edge, key, {
+ key: {
+ "reason": "The fixture bridge retains its existing owner during migration.",
+ "retirement_plan": "Remove the bridge after migrating its final caller.",
+ },
+ }
+
+
@pytest.mark.parametrize("edge", [
- ("loopx.control_plane.todos.provider_update", "loopx.capabilities.explore.research_frontier"),
- ("loopx.control_plane.todos.legacy_mutation_copy", "loopx.capabilities.explore.research_frontier"),
- ("loopx.control_plane.todos.legacy_mutation", "loopx.capabilities.explore.research_frontier_extra"),
- ("loopx.control_plane.todos.legacy_mutation", "loopx.capabilities.explore.research_frontier.child"),
- ("loopx.control_plane.todos.legacy_mutation", "loopx.capabilities.issue_fix"),
+ ("loopx.control_plane.other_bridge", "loopx.capabilities.fixture_owner"),
+ ("loopx.control_plane.fixture_bridge_copy", "loopx.capabilities.fixture_owner"),
+ ("loopx.control_plane.fixture_bridge", "loopx.capabilities.fixture_owner_extra"),
+ ("loopx.control_plane.fixture_bridge", "loopx.capabilities.fixture_owner.child"),
+ ("loopx.control_plane.fixture_bridge", "loopx.capabilities.other_owner"),
])
-def test_reviewed_legacy_bridge_never_allows_another_outward_edge(edge):
- legacy = ("loopx.control_plane.todos.legacy_mutation", "loopx.capabilities.explore.research_frontier")
- key = f"dependency_debt:{legacy[0]}->{legacy[1]}"
- reviewed = {key: REVIEWED_MAINTAINABILITY_EXCEPTIONS[key]}
- assert _review_outward_dependencies({legacy}, reviewed=reviewed)["ok"]
- report = _review_outward_dependencies({legacy, edge}, reviewed=reviewed)
+def test_reviewed_bridge_never_allows_another_outward_edge(edge, reviewed_outward_edge):
+ allowed, _, reviewed = reviewed_outward_edge
+ assert _review_outward_dependencies({allowed}, reviewed=reviewed)["ok"]
+ report = _review_outward_dependencies({allowed, edge}, reviewed=reviewed)
assert not report["ok"] and report["unreviewed_count"] == 1
assert report["unreviewed_findings"][0]["id"] == f"dependency_debt:{edge[0]}->{edge[1]}"
@pytest.mark.parametrize("field", ["reason", "retirement_plan"])
-def test_outward_dependency_exception_requires_its_existing_review_evidence(field):
- legacy = ("loopx.control_plane.todos.legacy_mutation", "loopx.capabilities.explore.research_frontier")
- key = f"dependency_debt:{legacy[0]}->{legacy[1]}"
- reviewed = {key: {**REVIEWED_MAINTAINABILITY_EXCEPTIONS[key], field: ""}}
- report = _review_outward_dependencies({legacy}, reviewed=reviewed)
+def test_outward_dependency_exception_requires_its_review_evidence(field, reviewed_outward_edge):
+ edge, key, reviewed = reviewed_outward_edge
+ reviewed[key][field] = ""
+ report = _review_outward_dependencies({edge}, reviewed=reviewed)
assert not report["ok"] and report["invalid_exception_count"] == 1
-def test_retired_outward_dependency_cannot_keep_a_stale_exception():
- key = "dependency_debt:loopx.control_plane.todos.legacy_mutation->loopx.capabilities.explore.research_frontier"
- report = _review_outward_dependencies(set(), reviewed={key: REVIEWED_MAINTAINABILITY_EXCEPTIONS[key]})
+def test_retired_outward_dependency_cannot_keep_a_stale_exception(reviewed_outward_edge):
+ _, _, reviewed = reviewed_outward_edge
+ report = _review_outward_dependencies(set(), reviewed=reviewed)
assert not report["ok"] and report["stale_exception_count"] == 1
diff --git a/tests/architecture/test_legacy_markdown_writer_retirement.py b/tests/architecture/test_legacy_markdown_writer_retirement.py
new file mode 100644
index 0000000000..fd39f1f4a8
--- /dev/null
+++ b/tests/architecture/test_legacy_markdown_writer_retirement.py
@@ -0,0 +1,208 @@
+"""Prevent the retired Markdown coordination writers from returning."""
+
+from __future__ import annotations
+
+import ast
+from importlib.util import resolve_name
+import json
+from pathlib import Path
+import subprocess
+
+
+REPOSITORY_ROOT = Path(__file__).resolve().parents[2]
+PACKAGE_ROOT = REPOSITORY_ROOT / "loopx"
+
+RETIRED_MODULES = {
+ "loopx.control_plane.coordination.runtime_shadow_writer_adapter",
+ "loopx.control_plane.scheduler.legacy_monitor_poll",
+ "loopx.control_plane.testing.authority_e2e_rows_stage2c",
+ "loopx.control_plane.testing.authority_e2e_rows_stage2c2",
+ "loopx.control_plane.todos.handoff_mode_source",
+ "loopx.control_plane.todos.legacy_mutation",
+ "loopx.control_plane.todos.line_update",
+}
+RETIRED_FILES = {
+ Path(*module.split(".")).with_suffix(".py")
+ for module in RETIRED_MODULES
+} | {
+ Path("loopx/control_plane/coordination/handoff_mode_legacy_plan.ts"),
+}
+RETIRED_PYTHON_SYMBOLS = {
+ "_VerifiedTaskLeaseFence",
+ "_add_goal_todo_legacy",
+ "_archive_completed_todos_legacy",
+ "_complete_goal_todo_legacy",
+ "_supersede_goal_todo_legacy",
+ "_update_goal_todo_legacy",
+ "apply_legacy_monitor_poll",
+ "apply_todo_update_to_lines",
+ "begin_todo_runtime_shadow_capture",
+ "enter_terminal_todo_lease_fence",
+ "goal_handoff_mode_for_lease",
+ "handoff_mode_source",
+ "hold_handoff_lease_holder_gate",
+ "hold_task_lease_mutation_fence",
+ "legacy_todo_write_transaction",
+ "link_generated_successor_todo_ids",
+ "link_superseding_todo_id",
+ "release_verified_task_lease_fence",
+ "require_runtime_shadow_capture_prepared",
+ "settle_lease_runtime_shadow_capture",
+ "settle_todo_runtime_shadow_capture",
+ "successor_add_kwargs",
+ "upsert_todo_metadata",
+ "write_captured_todo_state",
+}
+RETIRED_TYPESCRIPT_SYMBOLS = {
+ "LEGACY_HANDOFF_PLAN_SCHEMA",
+ "patchHandoffMode",
+ "planLegacyMonitorBatch",
+ "planLegacyHandoffMode",
+}
+
+TYPESCRIPT_RETIREMENT_SCAN = r"""
+import fs from "node:fs";
+import path from "node:path";
+import ts from "typescript";
+
+let input = "";
+for await (const chunk of process.stdin) input += chunk;
+const request = JSON.parse(input);
+const retiredModule = path.resolve(
+ request.root,
+ "loopx/control_plane/coordination/handoff_mode_legacy_plan",
+);
+const retiredSymbols = new Set(request.retired_symbols);
+const offenders = [];
+
+const normalizedModule = (sourcePath, specifier) => {
+ if (!specifier.startsWith(".")) return path.resolve(request.root, specifier);
+ return path.resolve(path.dirname(sourcePath), specifier);
+};
+const withoutScriptExtension = value =>
+ value.replace(/\.(?:[cm]?[jt]s)$/, "").replace(/[/\\]index$/, "");
+const noteModule = (sourcePath, node) => {
+ if (!node || !ts.isStringLiteralLike(node)) return;
+ const resolved = withoutScriptExtension(normalizedModule(sourcePath, node.text));
+ if (resolved === retiredModule) {
+ offenders.push({path: path.relative(request.root, sourcePath), kind: "import", name: node.text});
+ }
+};
+const noteName = (sourcePath, node) => {
+ if (node && ts.isIdentifier(node) && retiredSymbols.has(node.text)) {
+ offenders.push({path: path.relative(request.root, sourcePath), kind: "symbol", name: node.text});
+ }
+};
+
+for (const relative of request.sources) {
+ const sourcePath = path.resolve(request.root, relative);
+ const text = fs.readFileSync(sourcePath, "utf8");
+ const tree = ts.createSourceFile(
+ relative,
+ text,
+ ts.ScriptTarget.Latest,
+ true,
+ relative.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
+ );
+ if (tree.parseDiagnostics.length) {
+ const diagnostic = tree.parseDiagnostics[0];
+ const line = tree.getLineAndCharacterOfPosition(diagnostic.start ?? 0).line + 1;
+ throw new Error(`${relative}:${line}: invalid TypeScript source`);
+ }
+ const visit = node => {
+ if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
+ noteModule(sourcePath, node.moduleSpecifier);
+ } else if (ts.isImportEqualsDeclaration(node) &&
+ ts.isExternalModuleReference(node.moduleReference)) {
+ noteModule(sourcePath, node.moduleReference.expression);
+ } else if (ts.isCallExpression(node) &&
+ node.expression.kind === ts.SyntaxKind.ImportKeyword) {
+ noteModule(sourcePath, node.arguments[0]);
+ }
+ if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) ||
+ ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) ||
+ ts.isEnumDeclaration(node) || ts.isVariableDeclaration(node)) {
+ noteName(sourcePath, node.name);
+ }
+ ts.forEachChild(node, visit);
+ };
+ visit(tree);
+}
+process.stdout.write(JSON.stringify(offenders));
+"""
+
+
+def _module_name(path: Path) -> str:
+ relative = path.relative_to(PACKAGE_ROOT).with_suffix("")
+ parts = list(relative.parts)
+ if parts[-1] == "__init__":
+ parts.pop()
+ return ".".join(("loopx", *parts))
+
+
+def _python_offenders() -> list[tuple[str, str, str]]:
+ offenders: list[tuple[str, str, str]] = []
+ for path in sorted(PACKAGE_ROOT.rglob("*.py")):
+ relative = str(path.relative_to(REPOSITORY_ROOT))
+ module = _module_name(path)
+ package = module if path.name == "__init__.py" else module.rpartition(".")[0]
+ tree = ast.parse(path.read_text(encoding="utf-8"), filename=relative)
+ for node in ast.walk(tree):
+ if isinstance(node, ast.Import):
+ imported = {alias.name for alias in node.names}
+ elif isinstance(node, ast.ImportFrom):
+ imported_module = node.module or ""
+ if node.level:
+ imported_module = resolve_name(
+ "." * node.level + imported_module,
+ package,
+ )
+ imported = {imported_module}
+ imported.update(
+ f"{imported_module}.{alias.name}" for alias in node.names
+ )
+ else:
+ imported = set()
+ for imported_module in imported & RETIRED_MODULES:
+ offenders.append((relative, "import", imported_module))
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
+ if node.name in RETIRED_PYTHON_SYMBOLS:
+ offenders.append((relative, "symbol", node.name))
+ return offenders
+
+
+def _typescript_offenders() -> list[dict[str, str]]:
+ sources = sorted(
+ str(path.relative_to(REPOSITORY_ROOT))
+ for suffix in ("*.ts", "*.tsx", "*.mts")
+ for path in PACKAGE_ROOT.rglob(suffix)
+ )
+ completed = subprocess.run(
+ ["node", "--input-type=module", "-e", TYPESCRIPT_RETIREMENT_SCAN],
+ input=json.dumps(
+ {
+ "root": str(REPOSITORY_ROOT),
+ "sources": sources,
+ "retired_symbols": sorted(RETIRED_TYPESCRIPT_SYMBOLS),
+ }
+ ),
+ cwd=REPOSITORY_ROOT,
+ capture_output=True,
+ text=True,
+ timeout=60,
+ )
+ assert completed.returncode == 0, completed.stderr
+ result = json.loads(completed.stdout)
+ assert isinstance(result, list)
+ return result
+
+
+def test_legacy_markdown_writer_retirement_is_structural() -> None:
+ existing = sorted(
+ str(path)
+ for relative in RETIRED_FILES
+ if (path := REPOSITORY_ROOT / relative).exists()
+ )
+ assert existing == []
+ assert _python_offenders() == []
+ assert _typescript_offenders() == []
diff --git a/tests/architecture/test_settlement_receipt_source_boundaries.py b/tests/architecture/test_settlement_receipt_source_boundaries.py
index 9b2c647380..8ac56223dd 100644
--- a/tests/architecture/test_settlement_receipt_source_boundaries.py
+++ b/tests/architecture/test_settlement_receipt_source_boundaries.py
@@ -274,21 +274,39 @@ def test_external_failure_envelope_survives_ts_and_python_decoders(kind):
def test_native_lease_permission_denial_is_a_real_source_without_a_write(tmp_path):
+ from tests.control_plane.canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.coordination_state_contract_generated import (
+ TASK_LEASE_CANONICAL_ACQUIRE_REQUEST_SCHEMA,
+ )
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+ from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
+ from loopx.control_plane.todos.todo_summary import canonical_todo_read_record, structured_todo_item
+
source = tmp_path / "authority.json"
source.write_text("isolated-authority-v1")
runtime = tmp_path / "runtime"
+ initialize_canonical_authority(
+ runtime, "witness-goal",
+ build_todo_runtime_shadow_projection(
+ goal_id="witness-goal", handoff_mode="hard_lease",
+ todos=[canonical_todo_read_record(structured_todo_item({
+ "todo_id": "todo_witness", "role": "agent", "status": "open",
+ "text": "Validate the permission boundary.", "task_class": "advancement_task",
+ "claimed_by": None, "excluded_agents": ["witness-agent"]},
+ role="agent", source_section="Agent Todo"))],
+ ),
+ state_path=source,
+ )
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="witness-goal", include_leases=True)
+ assert before is not None and before["leases"] == []
request = {
- "schema_version": "loopx_task_lease_acquire_native_v0",
+ "schema_version": TASK_LEASE_CANONICAL_ACQUIRE_REQUEST_SCHEMA,
"runtime_root": str(runtime), "goal_id": "witness-goal",
"todo_id": "todo_witness", "owner": "witness-agent",
"idempotency_key": "witness-turn", "ttl_seconds": 120,
"write_scopes": [], "expected_version": None,
"authority": {
- "handoff_mode": "legacy",
"registered_agent_candidates": [["witness-agent"]],
- "todos": [{"todo_id": "todo_witness", "status": "open",
- "claimed_by": None, "excluded_agents": ["witness-agent"]}],
- "todo_projection_error": None,
"source_receipts": [{"source_id": "authority", "path": str(source),
"state": "file", "sha256": hashlib.sha256(source.read_bytes()).hexdigest()}],
},
@@ -297,11 +315,18 @@ def test_native_lease_permission_denial_is_a_real_source_without_a_write(tmp_pat
bridged = effect_runtime_result("task_lease.acquire.native", request)
assert bridged == direct
assert direct["ok"] is False
+ assert direct["source_authority"] == "file_v0"
+ assert direct["decision_read_from_provider"] is True
+ assert direct["legacy_fallback_used"] is False
# Lease settlement summaries have their own wire shape (step, not step_kind).
assert direct["settlement"]["receipts"] == []
assert direct["settlement"]["failure"] == {
"step": "validation", "kind": "permission_denied", "code": "owner_excluded_from_todo",
}
+ after = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="witness-goal", include_leases=True)
+ assert after is not None
+ for field in ("provider_revision", "todos", "leases"):
+ assert after[field] == before[field]
assert not list(runtime.rglob("task-leases/*.json"))
diff --git a/tests/canary/test_maintainability_ratchet.py b/tests/canary/test_maintainability_ratchet.py
index b7292e45f2..6e8f1a6a56 100644
--- a/tests/canary/test_maintainability_ratchet.py
+++ b/tests/canary/test_maintainability_ratchet.py
@@ -33,9 +33,7 @@ def test_current_repository_debt_is_reviewed_without_line_count_pins() -> None:
]
assert report["unreviewed_count"] == 0
assert report["stale_exception_count"] == 0
- # The legacy Todo writer's Explore guard moved with that writer. Its
- # exact dependency remains reviewed until the legacy path is retired.
- assert report["category_counts"] == {"compatibility_facade": 2, "dependency_debt": 1}
+ assert report["category_counts"] == {"compatibility_facade": 2}
assert report["category_counts"].get("oversized_decision_function", 0) == 0
assert report["reviewed_exception_count"] == report["finding_count"]
# One scan of the immutable checkout owns both debt and metric assertions.
diff --git a/tests/capabilities/test_explore_research_evidence.py b/tests/capabilities/test_explore_research_evidence.py
index 6941a6a78e..56ee6706c0 100644
--- a/tests/capabilities/test_explore_research_evidence.py
+++ b/tests/capabilities/test_explore_research_evidence.py
@@ -179,7 +179,18 @@ def command(*args: str) -> dict:
assert view["research_frontier"]["mode"] == "read_only_shadow"
-def execution_fixture(tmp_path: Path) -> tuple[Path, Path, Path, dict]:
+def execution_fixture(
+ tmp_path: Path,
+ *,
+ provider: str = "file",
+) -> tuple[Path, Path, Path, dict]:
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+ from tests.control_plane.canonical_authority_fixture import (
+ initialize_canonical_authority,
+ )
+
project = tmp_path / "project"
project.mkdir()
state = project / "ACTIVE_GOAL_STATE.md"
@@ -190,6 +201,15 @@ def execution_fixture(tmp_path: Path) -> tuple[Path, Path, Path, dict]:
"id": GOAL, "repo": str(project), "state_file": state.name, "status": "active",
"coordination": {"agent_model": "peer_v1", "registered_agents": ["fixture-agent"]},
}]}))
+ initialize_canonical_authority(
+ runtime,
+ GOAL,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL, todos=[], handoff_mode="soft_claim"
+ ),
+ state_path=state,
+ provider=provider,
+ )
path = explore_result_log_path(runtime, GOAL)
for name in ["a", "b"]:
node(path, name)
diff --git a/tests/capabilities/test_explore_turn_context.py b/tests/capabilities/test_explore_turn_context.py
index 6bb54d382a..dd77954efd 100644
--- a/tests/capabilities/test_explore_turn_context.py
+++ b/tests/capabilities/test_explore_turn_context.py
@@ -23,6 +23,9 @@
effect_runtime_result,
EffectRuntimeRejected,
)
+from loopx.control_plane.coordination.goal_storage_initialization import (
+ initialize_goal_storage_target,
+)
from loopx.explore_graph import compact_explore_graph_policy
from loopx.todos import add_goal_todo
@@ -38,10 +41,18 @@ def registry(tmp_path, *, graph=False, planning=False):
"goals": [
{
"id": "research",
+ "creation_operation_id": "goal-create:research",
"status": "active",
"repo": str(tmp_path),
"state_file": "goal.md",
- "coordination": {"registered_agents": ["worker"]},
+ "coordination": {
+ "registered_agents": ["worker"],
+ "storage_target": {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": "file",
+ "handoff_mode": "soft_claim",
+ },
+ },
"explore_graph": {"enabled": graph},
"spawn_policy": {
"allowed": False,
@@ -52,6 +63,14 @@ def registry(tmp_path, *, graph=False, planning=False):
}
)
)
+ goal = json.loads(path.read_text(encoding="utf-8"))["goals"][0]
+ initialized = initialize_goal_storage_target(
+ tmp_path / "runtime",
+ goal,
+ registry_path=path,
+ )
+ assert initialized["authority_initialized"] is True
+ assert initialized["legacy_fallback_used"] is False
return path
diff --git a/tests/capabilities/test_issue_fix_grouped_monitor_materialization.py b/tests/capabilities/test_issue_fix_grouped_monitor_materialization.py
index 229ed721af..7472e13de3 100644
--- a/tests/capabilities/test_issue_fix_grouped_monitor_materialization.py
+++ b/tests/capabilities/test_issue_fix_grouped_monitor_materialization.py
@@ -77,8 +77,6 @@ def _fixture(
def _use_provider(state, registry, provider, monkeypatch, *, handoff_mode="soft_claim"):
if handoff_mode == "hard_lease":
state.write_text("---\nhandoff_mode: hard_lease\n---\n" + state.read_text())
- if provider == "legacy":
- return
from tests.control_plane.canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
isolate_sqlite_runtime(registry.parent, monkeypatch)
@@ -127,7 +125,7 @@ def _packet(
)
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("handoff_mode", ["soft_claim", "hard_lease"])
def test_grouped_monitor_materialization_is_one_per_bucket_and_retires_empty_bucket(
tmp_path: Path, monkeypatch, provider, handoff_mode,
@@ -213,11 +211,10 @@ def test_grouped_monitor_materialization_is_one_per_bucket_and_retires_empty_buc
assert reopened_state.count("task_class=continuous_monitor") == 1
assert "status=open" in reopened_state
assert "no_followup=true" not in reopened_state
- if provider != "legacy":
- assert reopened["projection_delivery"] in {"delivered", "current"}
+ assert reopened["projection_delivery"] in {"delivered", "current"}
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_group_membership_change_advances_monitor_generation(tmp_path: Path, monkeypatch, provider) -> None:
project, state, registry = _fixture(tmp_path)
_use_provider(state, registry, provider, monkeypatch)
@@ -251,8 +248,9 @@ def test_group_membership_change_advances_monitor_generation(tmp_path: Path, mon
assert state.read_bytes() == unchanged
-def test_grouped_monitors_are_isolated_per_repository(tmp_path: Path) -> None:
+def test_grouped_monitors_are_isolated_per_repository(tmp_path: Path, monkeypatch) -> None:
project, state, registry = _fixture(tmp_path)
+ _use_provider(state, registry, "file", monkeypatch)
ledger = tmp_path / "pr-lifecycle.jsonl"
upsert_issue_fix_pr_lifecycle_ledger_jsonl(ledger, _packet(101))
upsert_issue_fix_pr_lifecycle_ledger_jsonl(
@@ -280,11 +278,13 @@ def test_grouped_monitors_are_isolated_per_repository(tmp_path: Path) -> None:
def test_grouped_monitor_keeps_creator_ownership_across_turns_and_due_poll(
tmp_path: Path,
+ monkeypatch,
) -> None:
project, state, registry = _fixture(
tmp_path,
registered_agents=[AGENT_ID, PEER_AGENT_ID],
)
+ _use_provider(state, registry, "file", monkeypatch)
ledger = tmp_path / "pr-lifecycle.jsonl"
upsert_issue_fix_pr_lifecycle_ledger_jsonl(ledger, _packet(101))
@@ -305,10 +305,13 @@ def test_grouped_monitor_keeps_creator_ownership_across_turns_and_due_poll(
todo_id = monitor["todo_id"]
state_before_peer_poll = state.read_text(encoding="utf-8")
- with pytest.raises(ValueError, match=f"claimed_by={AGENT_ID!r}"):
+ with pytest.raises(
+ ValueError,
+ match="outside the actor's registered owner/binding scope",
+ ):
write_monitor_poll_todo_state(
registry_path=registry,
- runtime_root=tmp_path / "runtime",
+ runtime_root=tmp_path,
goal_id=GOAL_ID,
todo_id=todo_id,
result_hash=monitor["result_hash"],
@@ -320,7 +323,7 @@ def test_grouped_monitor_keeps_creator_ownership_across_turns_and_due_poll(
polled = write_monitor_poll_todo_state(
registry_path=registry,
- runtime_root=tmp_path / "runtime",
+ runtime_root=tmp_path,
goal_id=GOAL_ID,
todo_id=todo_id,
result_hash=monitor["result_hash"],
@@ -329,16 +332,18 @@ def test_grouped_monitor_keeps_creator_ownership_across_turns_and_due_poll(
agent_id=AGENT_ID,
)
assert polled is not None
- assert polled["todo_update"]["mutation_authority"]["actor_agent_id"] == (
- AGENT_ID
- )
+ assert polled["source_authority"] in {"file_v0", "sqlite_v0"}
+ assert polled["todo_update"]["todo_id"] == todo_id
assert datetime.fromisoformat(polled["next_due_at"]).timestamp() == (
datetime.fromisoformat("2026-08-01T17:00:00+00:00").timestamp()
)
upsert_issue_fix_pr_lifecycle_ledger_jsonl(ledger, _packet(102))
state_before_peer_attempt = state.read_text(encoding="utf-8")
- with pytest.raises(ValueError, match=f"claimed_by={AGENT_ID!r}"):
+ with pytest.raises(
+ ValueError,
+ match="claimed_by does not grant this actor update authority",
+ ):
materialize_issue_fix_grouped_monitors(
registry_path=registry,
goal_id=GOAL_ID,
@@ -428,7 +433,7 @@ def test_pr_lifecycle_command_contract_is_shared_by_all_operator_surfaces() -> N
assert "--fetch-metadata" not in command
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_pr_lifecycle_execute_materializes_pending_monitor_and_keeps_goal_runnable(
tmp_path: Path, monkeypatch, provider,
) -> None:
@@ -533,6 +538,7 @@ def test_pr_lifecycle_execute_fetches_public_metadata_by_default(
monkeypatch: pytest.MonkeyPatch,
) -> None:
project, state, registry = _fixture(tmp_path)
+ _use_provider(state, registry, "file", monkeypatch)
def fake_fetch(reference: dict, *, timeout_seconds: int) -> dict:
assert reference["repo"] == "huangruiteng/loopx"
diff --git a/tests/capabilities/test_issue_fix_monitor_execution.py b/tests/capabilities/test_issue_fix_monitor_execution.py
index 8102f42c5e..5b21796421 100644
--- a/tests/capabilities/test_issue_fix_monitor_execution.py
+++ b/tests/capabilities/test_issue_fix_monitor_execution.py
@@ -24,7 +24,7 @@ def scenario(tmp_path, monkeypatch, provider):
return arguments, monitor, state
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_hard_lease_membership_observation_releases_only_its_execution(tmp_path, monkeypatch, provider):
args, before, state = scenario(tmp_path, monkeypatch, provider)
result = materialization.materialize_issue_fix_grouped_monitors(**args, generated_at="2030-01-01T01:00:00Z")
@@ -39,7 +39,7 @@ def test_hard_lease_membership_observation_releases_only_its_execution(tmp_path,
assert snapshot == state.read_bytes()
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_another_execution_even_for_same_actor_is_not_borrowed(tmp_path, monkeypatch, provider):
args, monitor, state = scenario(tmp_path, monkeypatch, provider)
acquired = acquire_task_lease(registry_path=args["registry_path"], runtime_root=tmp_path,
@@ -52,7 +52,7 @@ def test_another_execution_even_for_same_actor_is_not_borrowed(tmp_path, monkeyp
goal_id=GOAL_ID, todo_id=monitor["todo_id"])["lease"] == acquired["lease"]
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_retry_after_interrupted_acquisition_recovers_its_own_attempt(tmp_path, monkeypatch, provider):
args, monitor, _ = scenario(tmp_path, monkeypatch, provider)
original = materialization.acquire_task_lease
@@ -75,7 +75,7 @@ def interrupt(**kwargs):
assert lease_after["status"] == "released"
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_retry_recovers_its_own_attempt_despite_reordered_ledger(tmp_path, monkeypatch, provider):
"""The attempt identity is the reconciliation subject, not the row order."""
args, monitor, _ = scenario(tmp_path, monkeypatch, provider)
@@ -131,7 +131,7 @@ def test_attempt_decision_ignores_only_the_observation_clock():
assert materialization._decision_signature(changed) != materialization._decision_signature(step)
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_failed_write_cleans_attempt_and_identical_retry_can_acquire_again(tmp_path, monkeypatch, provider):
args, monitor, _ = scenario(tmp_path, monkeypatch, provider)
original = materialization.update_goal_todo
@@ -147,7 +147,7 @@ def unavailable(**kwargs):
assert materialization.materialize_issue_fix_grouped_monitors(**args, generated_at="2030-01-01T01:00:00Z")["write_performed"]
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_missing_or_stale_ledger_never_means_empty_current_work(tmp_path, monkeypatch, provider):
args, monitor, state = scenario(tmp_path, monkeypatch, provider)
before = state.read_bytes()
diff --git a/tests/capabilities/test_issue_fix_pr_gate_reconcile.py b/tests/capabilities/test_issue_fix_pr_gate_reconcile.py
index 62242d37b3..a9daec15a6 100644
--- a/tests/capabilities/test_issue_fix_pr_gate_reconcile.py
+++ b/tests/capabilities/test_issue_fix_pr_gate_reconcile.py
@@ -25,6 +25,14 @@
def _write_fixture(tmp_path: Path) -> tuple[Path, Path]:
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+ from loopx.todos import list_goal_todos
+ from tests.control_plane.canonical_authority_fixture import (
+ initialize_canonical_authority,
+ )
+
project = tmp_path / "repo"
state = project / "ACTIVE_GOAL_STATE.md"
state.parent.mkdir(parents=True)
@@ -37,10 +45,12 @@ def _write_fixture(tmp_path: Path) -> tuple[Path, Path]:
"## Agent Todo\n\n",
encoding="utf-8",
)
+ runtime = tmp_path / "runtime"
registry = tmp_path / "registry.global.json"
registry.write_text(
json.dumps(
{
+ "common_runtime_root": str(runtime),
"goals": [
{
"id": GOAL_ID,
@@ -58,6 +68,16 @@ def _write_fixture(tmp_path: Path) -> tuple[Path, Path]:
),
encoding="utf-8",
)
+ todos = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, todos=todos, handoff_mode="soft_claim"
+ ),
+ state_path=state,
+ provider="file",
+ )
return registry, state
@@ -79,7 +99,7 @@ def _reconcile(registry: Path, state: Path, *, agent_id: str | None) -> dict:
)
-def test_multi_agent_legacy_merge_gate_uses_attributed_actor(tmp_path: Path) -> None:
+def test_multi_agent_canonical_merge_gate_uses_attributed_actor(tmp_path: Path) -> None:
registry, state = _write_fixture(tmp_path)
result = _reconcile(registry, state, agent_id=ACTOR_AGENT)
@@ -87,22 +107,24 @@ def test_multi_agent_legacy_merge_gate_uses_attributed_actor(tmp_path: Path) ->
assert result["write_performed"] is True
assert result["todo_completion"]["status"] == "done"
assert result["todo_completion"]["mutation_authority"] == {
- "schema_version": "todo_mutation_authority_v0",
- "command": "complete",
- "mode": "registered_peer_actor",
- "actor_agent_id": ACTOR_AGENT,
- "todo_id": "todo_merge_gate_2298",
- "claim_owner": None,
- "registered_agent_count": 2,
+ "schema_version": "loopx_coordination_todo_terminal_decision_result_v0",
+ "outcome": "apply",
+ "code": "terminal_transition",
+ "next_todo_status": "done",
+ "next_lease": None,
+ "lease_fence": "not_required",
+ "ownership_gate": "not_required",
+ "authority_mode": "registered_peer_actor",
+ "idempotent": False,
}
assert "status=done" in state.read_text(encoding="utf-8")
-def test_multi_agent_legacy_merge_gate_without_actor_is_atomic(tmp_path: Path) -> None:
+def test_multi_agent_canonical_merge_gate_without_actor_is_atomic(tmp_path: Path) -> None:
registry, state = _write_fixture(tmp_path)
before = state.read_text(encoding="utf-8")
- with pytest.raises(ValueError, match="requires --agent-id"):
+ with pytest.raises(ValueError, match="actor_required"):
_reconcile(registry, state, agent_id=None)
assert state.read_text(encoding="utf-8") == before
diff --git a/tests/capabilities/test_periodic_report_pending_intent.py b/tests/capabilities/test_periodic_report_pending_intent.py
index 1f0e97f1e5..84ff02f63a 100644
--- a/tests/capabilities/test_periodic_report_pending_intent.py
+++ b/tests/capabilities/test_periodic_report_pending_intent.py
@@ -404,13 +404,26 @@ def _intent() -> dict[str, object]:
}
-def _fixture(tmp_path: Path) -> tuple[Path, Path]:
+def _fixture(
+ tmp_path: Path,
+ *,
+ finished_updated_at: str = "2026-08-30T09:00:00Z",
+ provider: str = "file",
+) -> tuple[Path, Path]:
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+ from loopx.todos import list_goal_todos
+ from tests.control_plane.canonical_authority_fixture import (
+ initialize_canonical_authority,
+ )
+
project = tmp_path / "project"
runtime = tmp_path / "runtime"
state = project / "ACTIVE_GOAL_STATE.md"
state.parent.mkdir(parents=True)
state.write_text(
- """---
+ f"""---
status: active
---
@@ -421,7 +434,7 @@ def _fixture(tmp_path: Path) -> tuple[Path, Path]:
## Agent Todo
- [x] Finish the bounded analysis.
-
+
""",
encoding="utf-8",
)
@@ -453,6 +466,16 @@ def _fixture(tmp_path: Path) -> tuple[Path, Path]:
),
encoding="utf-8",
)
+ todos = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, todos=todos, handoff_mode="soft_claim"
+ ),
+ state_path=state,
+ provider=provider,
+ )
sidecar_dir = runtime / "goals" / GOAL_ID / "post_writeback_hooks"
sidecar_dir.mkdir(parents=True)
sidecar = sidecar_dir / ("pwh_" + "a" * 64 + ".json")
@@ -1821,14 +1844,8 @@ def test_consumption_reuses_the_exact_frozen_fact_request(tmp_path: Path) -> Non
def test_consumption_derives_the_period_from_actual_report_facts(
tmp_path: Path,
) -> None:
- registry, runtime = _fixture(tmp_path)
- state_path = registry.parent / "ACTIVE_GOAL_STATE.md"
- state_path.write_text(
- state_path.read_text(encoding="utf-8").replace(
- "updated_at=2026-08-30T09:00:00Z",
- "updated_at=2026-08-29T22:42:26+08:00",
- ),
- encoding="utf-8",
+ registry, runtime = _fixture(
+ tmp_path, finished_updated_at="2026-08-29T22:42:26+08:00"
)
required = consume_pending_periodic_report_intent(
@@ -1921,26 +1938,13 @@ def test_real_cli_editorial_fallback_reads_canonical_work_without_display(
import subprocess
import sys
from tests.control_plane.canonical_authority_fixture import (
- initialize_canonical_authority,
isolate_sqlite_runtime,
)
- from loopx.control_plane.coordination.runtime_shadow import (
- build_todo_runtime_shadow_projection,
- )
if provider == "sqlite":
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime = _fixture(tmp_path)
+ registry, runtime = _fixture(tmp_path, provider=provider)
state = registry.parent / "ACTIVE_GOAL_STATE.md"
- records = parse_active_state_todos(state.read_text(), item_limit=None)[
- "agent_todos"
- ]["items"]
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, todos=records, handoff_mode="soft_claim"
- )
- initialize_canonical_authority(
- runtime, GOAL_ID, projection, state_path=state, provider=provider
- )
# A runtime override must route both intent IO and the Todo snapshot.
config = json.loads(registry.read_text())
config["common_runtime_root"] = str(tmp_path / "unused-runtime")
diff --git a/tests/capabilities/test_periodic_report_turn_writeback.py b/tests/capabilities/test_periodic_report_turn_writeback.py
index 138fa4d783..9ccd6c825f 100644
--- a/tests/capabilities/test_periodic_report_turn_writeback.py
+++ b/tests/capabilities/test_periodic_report_turn_writeback.py
@@ -18,12 +18,7 @@
)
from loopx.cli import main as cli_main
from loopx.cli_commands import project_lifecycle_refresh_state, turn_post_writeback
-from loopx.control_plane.coordination.runtime_shadow import (
- build_todo_runtime_shadow_projection,
-)
-from loopx.control_plane.todos.active_state_todo_parser import parse_active_state_todos
from tests.control_plane.canonical_authority_fixture import (
- initialize_canonical_authority,
isolate_sqlite_runtime,
)
from tests.test_loopx_turn_driver import (
@@ -52,17 +47,9 @@ def _promoted_fixture(
if provider == "sqlite":
isolate_sqlite_runtime(tmp_path, monkeypatch)
project, runtime, registry = _write_live_fixture(
- tmp_path, todo_metadata_extra="no_followup=true"
- )
- state = project / ".codex" / "goals" / GOAL_ID / "ACTIVE_GOAL_STATE.md"
- todos = parse_active_state_todos(state.read_text(encoding="utf-8"), item_limit=None)
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID,
- handoff_mode="soft_claim",
- todos=todos["agent_todos"]["items"],
- )
- initialize_canonical_authority(
- runtime, GOAL_ID, projection, state_path=state, provider=provider
+ tmp_path,
+ todo_metadata_extra="no_followup=true",
+ provider=provider,
)
return project, runtime, registry
diff --git a/tests/capabilities/test_research_composition_gate.py b/tests/capabilities/test_research_composition_gate.py
index a60c4865fa..ca4cdcfd3e 100644
--- a/tests/capabilities/test_research_composition_gate.py
+++ b/tests/capabilities/test_research_composition_gate.py
@@ -12,8 +12,11 @@
from loopx.capabilities.explore.result_log import (
append_explore_result_event, build_explore_edge_event, build_explore_node_event, explore_result_log_path,
)
+from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
+from loopx.control_plane.todos.list_readback import list_goal_todos
from loopx.control_plane.work_items.semantic_replan_writeback import qualify_replan_writeback
from loopx.capabilities.explore.research_frontier import prepare_research_replan_evidence
+from tests.control_plane.canonical_authority_fixture import initialize_canonical_authority
GOAL = "research-gate-fixture"
AGENT = "fixture-agent"
@@ -56,6 +59,17 @@ def fixture(tmp_path: Path):
append_research_observation(log, goal_id=GOAL, observation=observation("a", "b"))
for name in ["a", "b"]:
append_explore_result_event(log, build_explore_edge_event(goal_id=GOAL, from_node="joint", to_node=name, edge_type="depends_on"))
+ todos = list_goal_todos(registry_path=registry, goal_id=GOAL)["todos"]
+ initialize_canonical_authority(
+ runtime,
+ GOAL,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL,
+ todos=todos,
+ handoff_mode="soft_claim",
+ ),
+ state_path=state,
+ )
def cli(*args: str, success: bool = True) -> dict:
result = subprocess.run([sys.executable, "-m", "loopx.cli", "--format", "json", "--registry", str(registry),
diff --git a/tests/cli_commands/settlement_capability_dispatch_fixture.py b/tests/cli_commands/settlement_capability_dispatch_fixture.py
index 0f7afa4848..c9a54c3373 100644
--- a/tests/cli_commands/settlement_capability_dispatch_fixture.py
+++ b/tests/cli_commands/settlement_capability_dispatch_fixture.py
@@ -50,6 +50,10 @@ def write_periodic_report_registry(project: Path, runtime: Path) -> Path:
"id": GOAL_ID,
"repo": str(project),
"state_file": "goal.md",
+ "coordination": {
+ "agent_model": "peer_v1",
+ "registered_agents": [AGENT_ID],
+ },
"control_plane": {
"periodic_report": {
"enabled": True,
@@ -191,12 +195,30 @@ def complete_todo_via_cli(
journal_capabilities: list[str],
write_state: Callable[[Path], None],
) -> tuple[dict[str, object], Path, Path]:
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+ from loopx.todos import list_goal_todos
+ from tests.control_plane.canonical_authority_fixture import (
+ initialize_canonical_authority,
+ )
+
project = tmp_path / "repo"
project.mkdir()
runtime = tmp_path / "runtime"
runtime.mkdir()
write_state(project)
registry_path = write_periodic_report_registry(project, runtime)
+ todos = list_goal_todos(registry_path=registry_path, goal_id=GOAL_ID)["todos"]
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, todos=todos, handoff_mode="soft_claim"
+ ),
+ state_path=project / "goal.md",
+ provider="file",
+ )
write_successor_run_history(runtime)
write_turn_heartbeat_receipt(runtime)
write_turn_journal(runtime, observed=journal_capabilities)
@@ -243,6 +265,6 @@ def run_todo_completion_via_cli(
build_periodic_report_post_writeback_projection
),
)
- assert exit_code == 0
+ assert exit_code == 0, captured
assert captured.get("ok") is True, captured
return captured
diff --git a/tests/cli_commands/test_periodic_report_intent_capability_chain.py b/tests/cli_commands/test_periodic_report_intent_capability_chain.py
index 9ad4ec67da..6104b62643 100644
--- a/tests/cli_commands/test_periodic_report_intent_capability_chain.py
+++ b/tests/cli_commands/test_periodic_report_intent_capability_chain.py
@@ -6,12 +6,11 @@
from loopx.capabilities.periodic_report.pending_intent import (
pending_periodic_report_intents,
)
+from loopx.todos import add_goal_todo
from settlement_capability_dispatch_fixture import (
AGENT_ID,
COMPLETED_TODO,
- GATED_TODO,
GOAL_ID,
- PLAIN_TODO,
complete_todo_via_cli,
)
@@ -35,24 +34,26 @@ def _write_unclaimed_frontier_state(project: Path) -> None:
)
-def _claim_gated_successors(project: Path) -> None:
+def _claim_gated_successors(registry: Path) -> None:
"""Evolve durable state after the dispatch, as the next Turn would."""
- project.joinpath("goal.md").write_text(
- f"""# Goal
-
-## User Todo
-
-## Agent Todo
-
-- [x] Ship the localized weekly report slice.
-
-- [ ] Re-run the outbound channel sync after network capacity returns.
-
-- [ ] Outline the follow-up frontier analysis.
-
-""",
- encoding="utf-8",
+ add_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ role="agent",
+ text="Re-run the outbound channel sync after network capacity returns.",
+ task_class="advancement_task",
+ claimed_by=AGENT_ID,
+ action_kind="gated_work",
+ resume_when="capacity_available:network",
+ )
+ add_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ role="agent",
+ text="Outline the follow-up frontier analysis.",
+ task_class="advancement_task",
+ claimed_by=AGENT_ID,
)
@@ -105,15 +106,6 @@ def test_later_todo_completion_without_capabilities_does_not_replay_milestone(
def test_post_writeback_frontier_and_progress_share_one_canonical_snapshot(
tmp_path, monkeypatch
):
- from tests.control_plane.canonical_authority_fixture import (
- initialize_canonical_authority,
- )
- from loopx.control_plane.coordination.runtime_shadow import (
- build_todo_runtime_shadow_projection,
- )
- from loopx.control_plane.todos.active_state_todo_parser import (
- parse_active_state_todos,
- )
from loopx.capabilities.periodic_report.post_writeback_hook import (
build_periodic_report_post_writeback_projection,
)
@@ -124,15 +116,8 @@ def test_post_writeback_frontier_and_progress_share_one_canonical_snapshot(
journal_capabilities=["network"],
write_state=_write_unclaimed_frontier_state,
)
- _claim_gated_successors(registry.parent)
+ _claim_gated_successors(registry)
state = registry.parent / "goal.md"
- records = parse_active_state_todos(state.read_text(), item_limit=None)[
- "agent_todos"
- ]["items"]
- source = build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, todos=records, handoff_mode="soft_claim"
- )
- initialize_canonical_authority(runtime, GOAL_ID, source, state_path=state)
state.unlink()
reads = []
original = todo_source.read_canonical_todos_if_promoted
diff --git a/tests/cli_commands/test_project_registry.py b/tests/cli_commands/test_project_registry.py
index c1c501251e..ae9261211c 100644
--- a/tests/cli_commands/test_project_registry.py
+++ b/tests/cli_commands/test_project_registry.py
@@ -14,6 +14,110 @@
from loopx.control_plane.projects import registry_codec
+def canonical_storage_target(_runtime_root: Path) -> dict[str, object]:
+ return {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": "sqlite",
+ "handoff_mode": "hard_lease",
+ }
+
+
+@pytest.fixture(params=["file", "sqlite"])
+def recoverable_registration(tmp_path, monkeypatch, request):
+ from tests.control_plane.canonical_authority_fixture import isolate_sqlite_runtime
+
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ return dict(registry_path=tmp_path / "registry.json", runtime_root=tmp_path / "runtime",
+ project_id="atlas", project_kind="work", knowledge_root=tmp_path / "atlas",
+ goal_id="atlas-import", objective="Deliver the Atlas import pipeline.",
+ non_goals=[], acceptance=["A verified import report is produced."], unknowns=[],
+ next_effect="Inspect Atlas.", stop_condition="Stop while Atlas is unavailable.",
+ repository_bindings=[], external_locator_bindings=[],
+ resolve_new_goal_storage_target=lambda _root: {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": request.param, "handoff_mode": "hard_lease"})
+
+
+def test_completed_registration_replays_after_later_work_and_preserves_display(recoverable_registration):
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+ from loopx.todos import add_goal_todo
+
+ arguments = recoverable_registration
+ created = project_registry.register_project_goal(**arguments)
+ state = Path(created["state_file"])
+ add_goal_todo(registry_path=arguments["registry_path"], goal_id=arguments["goal_id"],
+ role="agent", text="Later canonical task", operation_id="registration-later-work")
+ authority = read_canonical_todos_if_promoted(
+ runtime_root=arguments["runtime_root"], goal_id=arguments["goal_id"])
+ registry_bytes = arguments["registry_path"].read_bytes()
+ for display in (state.read_bytes() + b"\nOwner recorded a later outcome.\n", b"\xffundecodable display"):
+ state.write_bytes(display)
+ replay = project_registry.register_project_goal(**arguments)
+ assert replay["changed"] is False
+ assert replay["storage_selection"]["status"] == "replayed"
+ assert replay["storage_selection"]["creation_operation_id"] == created["storage_selection"]["creation_operation_id"]
+ assert state.read_bytes() == display
+ assert arguments["registry_path"].read_bytes() == registry_bytes
+ assert read_canonical_todos_if_promoted(
+ runtime_root=arguments["runtime_root"], goal_id=arguments["goal_id"]) == authority
+
+
+def test_completed_registration_recovers_missing_display_from_current_authority(recoverable_registration):
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+ from loopx.control_plane.effect_runtime import EffectRuntimeConflict
+ from loopx.todos import add_goal_todo
+
+ arguments = recoverable_registration
+ created = project_registry.register_project_goal(**arguments)
+ state = Path(created["state_file"])
+ add_goal_todo(registry_path=arguments["registry_path"], goal_id=arguments["goal_id"],
+ role="agent", text="Later canonical task", operation_id="registration-later-work")
+ authority = read_canonical_todos_if_promoted(
+ runtime_root=arguments["runtime_root"], goal_id=arguments["goal_id"])
+ state.unlink()
+ backend = arguments["runtime_root"] / "authority" / authority["source_authority"].replace("_", "-")
+ offline = backend.with_name(backend.name + "-offline")
+ backend.rename(offline)
+ try:
+ with pytest.raises(EffectRuntimeConflict, match="restore.*backup"):
+ project_registry.register_project_goal(**arguments)
+ assert not state.exists() and not backend.exists()
+ finally:
+ offline.rename(backend)
+ for changed in ({"objective": "A different request"},
+ {"knowledge_root": arguments["knowledge_root"].with_name("other-root")}):
+ with pytest.raises(ValueError, match="conflicts with existing registration"):
+ project_registry.register_project_goal(**{**arguments, **changed})
+ assert not state.exists()
+ replay = project_registry.register_project_goal(**arguments)
+ assert replay["storage_selection"]["status"] == "replayed"
+ assert replay["state_projection"]["recovery_scope"] == "todo_sections_only"
+ assert replay["state_projection"]["narrative_preserved"] is False
+ assert state.read_text().count("Later canonical task") == 1
+ assert "Registered Project" not in state.read_text()
+ assert read_canonical_todos_if_promoted(
+ runtime_root=arguments["runtime_root"], goal_id=arguments["goal_id"]) == authority
+
+
+def test_pending_registration_rejects_source_changed_during_validation(recoverable_registration, monkeypatch):
+ from loopx.control_plane.coordination import goal_storage_initialization
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+
+ arguments = recoverable_registration
+ capture = goal_storage_initialization.build_runtime_shadow_source_snapshot
+
+ def changed_capture(**kwargs):
+ source = kwargs["state_path"]
+ source.write_text(source.read_text() + "\nUnreviewed source change.\n")
+ return capture(**kwargs)
+
+ monkeypatch.setattr(goal_storage_initialization, "build_runtime_shadow_source_snapshot", changed_capture)
+ with pytest.raises(ValueError, match="source changed during validation"):
+ project_registry.register_project_goal(**arguments)
+ assert read_canonical_todos_if_promoted(
+ runtime_root=arguments["runtime_root"], goal_id=arguments["goal_id"]) is None
+
+
@pytest.mark.parametrize("objective", [
"Deliver the Atlas import pipeline.",
"```text Deliver the Atlas import pipeline. ```",
@@ -107,6 +211,61 @@ def test_project_register_creates_project_goal_and_resumable_state(
assert active_state_section_text(state, "Objective") == " ".join(objective.split())
+def test_project_register_resumes_interrupted_canonical_initialization(
+ tmp_path: Path,
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ from loopx.control_plane.effect_runtime import restart_effect_runtime
+ from tests.control_plane.canonical_authority_fixture import isolate_sqlite_runtime
+
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ knowledge_root = tmp_path / "atlas"
+ registry_path = knowledge_root / ".loopx" / "registry.json"
+ runtime_root = tmp_path / "runtime"
+ arguments = {
+ "registry_path": registry_path,
+ "runtime_root": runtime_root,
+ "project_id": "atlas",
+ "project_kind": "work",
+ "knowledge_root": knowledge_root,
+ "goal_id": "atlas-import",
+ "objective": "Deliver the Atlas import pipeline.",
+ "non_goals": [],
+ "acceptance": ["A verified import report is produced."],
+ "unknowns": [],
+ "next_effect": "Inspect Atlas.",
+ "stop_condition": "Stop while Atlas is unavailable.",
+ "repository_bindings": [],
+ "external_locator_bindings": [],
+ "resolve_new_goal_storage_target": canonical_storage_target,
+ }
+ initialize = project_registry.initialize_goal_storage_target
+
+ def interrupt(*_args, **_kwargs):
+ raise TimeoutError("canonical initialization interrupted")
+
+ monkeypatch.setattr(project_registry, "initialize_goal_storage_target", interrupt)
+ with pytest.raises(TimeoutError, match="initialization interrupted"):
+ project_registry.register_project_goal(**arguments)
+
+ pending = json.loads(registry_path.read_text(encoding="utf-8"))["goals"][0]
+ assert pending["coordination"]["storage_target"]["schema_version"] == (
+ "loopx_new_goal_storage_target_v1"
+ )
+ assert pending["creation_operation_id"].startswith("goal-create:project-register:")
+
+ monkeypatch.setattr(project_registry, "initialize_goal_storage_target", initialize)
+ try:
+ recovered = project_registry.register_project_goal(**arguments)
+ assert recovered["changed"] is False
+ assert recovered["storage_selection"]["authority_initialized"] is True
+ assert recovered["storage_selection"]["creation_operation_id"] == (
+ pending["creation_operation_id"]
+ )
+ finally:
+ restart_effect_runtime()
+
+
def test_project_register_defaults_to_knowledge_root_when_global_registry_exists(
tmp_path: Path,
capsys,
@@ -221,23 +380,28 @@ def test_project_register_serializes_one_state_across_distinct_registries(
knowledge_root = tmp_path / "atlas"
registry_paths = [tmp_path / f"registry-{index}.json" for index in range(8)]
- def register(registry_path: Path) -> dict[str, object]:
- return project_registry.register_project_goal(
- registry_path=registry_path,
- runtime_root=tmp_path / "runtime",
- project_id="atlas",
- project_kind="work",
- knowledge_root=knowledge_root,
- goal_id="atlas-import",
- objective="Deliver the Atlas import pipeline.",
- non_goals=[],
- acceptance=["A verified import report is produced."],
- unknowns=[],
- next_effect="Inspect Atlas.",
- stop_condition="Stop while Atlas is unavailable.",
- repository_bindings=[],
- external_locator_bindings=[],
- )
+ def register(registry_path: Path) -> tuple[str, object]:
+ try:
+ result = project_registry.register_project_goal(
+ registry_path=registry_path,
+ runtime_root=tmp_path / "runtime",
+ project_id="atlas",
+ project_kind="work",
+ knowledge_root=knowledge_root,
+ goal_id="atlas-import",
+ objective="Deliver the Atlas import pipeline.",
+ non_goals=[],
+ acceptance=["A verified import report is produced."],
+ unknowns=[],
+ next_effect="Inspect Atlas.",
+ stop_condition="Stop while Atlas is unavailable.",
+ repository_bindings=[],
+ external_locator_bindings=[],
+ resolve_new_goal_storage_target=canonical_storage_target,
+ )
+ except ValueError as error:
+ return "rejected", str(error)
+ return "registered", result
with ThreadPoolExecutor(max_workers=len(registry_paths)) as executor:
results = list(executor.map(register, registry_paths))
@@ -245,8 +409,14 @@ def register(registry_path: Path) -> dict[str, object]:
state_file = (
knowledge_root / ".loopx" / "goals" / "atlas-import" / "ACTIVE_GOAL_STATE.md"
)
- assert all(result["ok"] is True for result in results)
- assert all(path.exists() for path in registry_paths)
+ registered = [result for status, result in results if status == "registered"]
+ rejected = [result for status, result in results if status == "rejected"]
+ assert len(registered) == 1
+ assert registered[0]["ok"] is True
+ assert len(rejected) == len(registry_paths) - 1
+ assert all("bound to canonical authority through another registry" in str(error)
+ for error in rejected)
+ assert sum(path.exists() for path in registry_paths) == 1
assert state_file.exists()
@@ -273,6 +443,7 @@ def register(knowledge_root: Path) -> tuple[str, Path]:
stop_condition="Stop while Atlas is unavailable.",
repository_bindings=[],
external_locator_bindings=[],
+ resolve_new_goal_storage_target=canonical_storage_target,
)
except ValueError:
return "conflict", knowledge_root
@@ -365,7 +536,8 @@ def test_project_register_repeated_identical_request_is_a_noop(
assert registry_path.read_bytes() == registry_before
assert state_file.read_bytes() == state_before
- # Equivalent JSON spelling is presentation, while changed content conflicts.
+ # A completed creation receipt does not roll later presentation back to the
+ # initial template, including updated metadata or owner-written narrative.
text = state_file.read_text()
header, body = text.split("\n---\n", 1)
header = "\n".join(
@@ -380,13 +552,15 @@ def test_project_register_repeated_identical_request_is_a_noop(
for drifted in (equivalent.replace("status: active", "status: paused", 1),
equivalent.replace("A verified Spec diff is produced.", "Different acceptance.", 1)):
state_file.write_text(drifted)
- assert main(arguments) == 1
- assert "conflicts with registration" in capsys.readouterr().out
+ assert main(arguments) == 0
+ replay = json.loads(capsys.readouterr().out)
+ assert replay["changed"] is False
+ assert replay["storage_selection"]["status"] == "replayed"
assert state_file.read_text() == drifted
assert registry_path.read_bytes() == registry_before
-def test_project_register_reuses_legacy_state_from_strict_registry(
+def test_project_register_rejects_rebinding_canonical_creation_source_from_strict_registry(
tmp_path: Path, capsys,
) -> None:
project = tmp_path / "atlas"
@@ -421,10 +595,9 @@ def test_project_register_reuses_legacy_state_from_strict_registry(
registry_path.write_text(json.dumps(envelope, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
before = registry_path.read_bytes()
- assert main(args) == 0
+ assert main(args) == 1
result = json.loads(capsys.readouterr().out)
- assert result["changed"] is False
- assert result["state_file"] == str(legacy_state)
+ assert "not bound to the registered original operation" in result["error"]
assert registry_path.read_bytes() == before
assert legacy_state.exists() and not new_state.exists()
@@ -842,19 +1015,33 @@ def test_project_register_repairs_missing_state_for_matching_records(
"Stop while Atlas is unavailable.",
]
assert main(arguments) == 0
- capsys.readouterr()
+ created = json.loads(capsys.readouterr().out)
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+ from loopx.paths import effective_runtime_root
+ from loopx.todos import add_goal_todo
+
+ runtime = effective_runtime_root(registry_path, None)
+ add_goal_todo(registry_path=registry_path, goal_id="atlas-import", role="agent",
+ text="A later CLI-visible task", operation_id="cli-registration-later-task")
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="atlas-import")
state_file.unlink()
assert main(arguments) == 0
payload = json.loads(capsys.readouterr().out)
assert payload["changed"] is True
- assert state_file.exists()
+ assert payload["storage_selection"]["creation_operation_id"] == created["storage_selection"]["creation_operation_id"]
+ assert payload["state_projection"]["recovery_scope"] == "todo_sections_only"
+ assert state_file.read_text().count("A later CLI-visible task") == 1
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="atlas-import") == before
-def test_project_register_rejects_tampered_matching_state(
+@pytest.mark.parametrize("added_todo", [False, True])
+def test_pending_project_registration_rejects_changed_source(
tmp_path: Path,
capsys,
+ monkeypatch: pytest.MonkeyPatch,
+ added_todo: bool,
) -> None:
knowledge_root = tmp_path / "atlas"
registry_path = knowledge_root / ".loopx" / "registry.json"
@@ -885,18 +1072,30 @@ def test_project_register_rejects_tampered_matching_state(
"--stop-condition",
"Stop while Atlas is unavailable.",
]
- assert main(arguments) == 0
+ initialize = project_registry.initialize_goal_storage_target
+
+ def interrupt(*_args, **_kwargs):
+ raise TimeoutError("initialization interrupted before authority creation")
+
+ monkeypatch.setattr(project_registry, "initialize_goal_storage_target", interrupt)
+ assert main(arguments) == 1
capsys.readouterr()
+ monkeypatch.setattr(project_registry, "initialize_goal_storage_target", initialize)
+ registry_before = registry_path.read_bytes()
original = state_file.read_text(encoding="utf-8")
state_file.write_text(
- original.replace("- Inspect Atlas.", "- Implement Atlas without inspection."),
+ original + "\n## Completed Work Archive\n\n- [x] Unreviewed preexisting work.\n"
+ if added_todo else original.replace("- Inspect Atlas.", "- Implement Atlas without inspection."),
encoding="utf-8",
)
+ changed_source = state_file.read_bytes()
assert main(arguments) == 1
payload = json.loads(capsys.readouterr().out)
assert "goal state file conflicts with registration" in payload["error"]
+ assert state_file.read_bytes() == changed_source
+ assert registry_path.read_bytes() == registry_before
def test_project_register_rejects_second_goal_for_existing_project(
diff --git a/tests/control_plane/canonical_authority_fixture.py b/tests/control_plane/canonical_authority_fixture.py
index b8d8fbd004..f2deeb13d8 100644
--- a/tests/control_plane/canonical_authority_fixture.py
+++ b/tests/control_plane/canonical_authority_fixture.py
@@ -73,7 +73,14 @@ def single_snapshot_page(result: dict, goal_id: str = "goal-a") -> dict:
}
-def promoted_create_fixture(tmp_path: Path, *, provider: str = "file") -> tuple[Path, Path, Path]:
+def promoted_create_fixture(
+ tmp_path: Path,
+ *,
+ provider: str = "file",
+ goal_id: str = "goal-a",
+ handoff_mode: str = "soft_claim",
+ registered_agents: list[str] | None = None,
+) -> tuple[Path, Path, Path]:
from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
from loopx.control_plane.coordination.coordination_state_contract import (
TODO_DOMAIN_READ_RECORD_SCHEMA_VERSION, TODO_DOMAIN_RECORD_FIELDS,
@@ -81,7 +88,7 @@ def promoted_create_fixture(tmp_path: Path, *, provider: str = "file") -> tuple[
runtime_root = tmp_path / "runtime"
project = tmp_path / "project"
- state_file = project / ".codex/goals/goal-a/ACTIVE_GOAL_STATE.md"
+ state_file = project / f".codex/goals/{goal_id}/ACTIVE_GOAL_STATE.md"
state_file.parent.mkdir(parents=True)
state_file.write_text(
"# Goal\n\n## User Todo / Owner Review Reading Queue\n\n"
@@ -96,10 +103,16 @@ def promoted_create_fixture(tmp_path: Path, *, provider: str = "file") -> tuple[
"common_runtime_root": str(runtime_root),
"goals": [
{
- "id": "goal-a",
+ "id": goal_id,
"repo": str(project),
- "state_file": ".codex/goals/goal-a/ACTIVE_GOAL_STATE.md",
- "coordination": {"registered_agents": ["agent-a"]},
+ "state_file": f".codex/goals/{goal_id}/ACTIVE_GOAL_STATE.md",
+ "coordination": {
+ "registered_agents": (
+ registered_agents
+ if registered_agents is not None
+ else ["agent-a"]
+ )
+ },
}
],
}
@@ -107,7 +120,7 @@ def promoted_create_fixture(tmp_path: Path, *, provider: str = "file") -> tuple[
encoding="utf-8",
)
projection = build_todo_runtime_shadow_projection(
- goal_id="goal-a", todos=[], handoff_mode="soft_claim"
+ goal_id=goal_id, todos=[], handoff_mode=handoff_mode
)
projection["todo_read_model"] = {
**projection["todo_read_model"],
@@ -115,6 +128,6 @@ def promoted_create_fixture(tmp_path: Path, *, provider: str = "file") -> tuple[
"contract_fields": list(TODO_DOMAIN_RECORD_FIELDS),
}
initialize_canonical_authority(
- runtime_root, "goal-a", projection, state_path=state_file, provider=provider
+ runtime_root, goal_id, projection, state_path=state_file, provider=provider
)
return registry_path, runtime_root, state_file
diff --git a/tests/control_plane/fixtures/retained_lease_receipts_1x.json b/tests/control_plane/fixtures/retained_lease_receipts_1x.json
new file mode 100644
index 0000000000..716d23d22c
--- /dev/null
+++ b/tests/control_plane/fixtures/retained_lease_receipts_1x.json
@@ -0,0 +1,431 @@
+{
+ "source_commit": "60d833b13c85135587b725013b247e827663ffb9",
+ "scenarios": {
+ "original": {
+ "request": {
+ "schema_version": "loopx_task_lease_acquire_native_v0",
+ "runtime_root": "$ROOT/runtime",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-a",
+ "idempotency_key": "lease-a",
+ "ttl_seconds": 600,
+ "write_scopes": [],
+ "expected_version": null,
+ "authority": {
+ "handoff_mode": "hard_lease",
+ "registered_agent_candidates": [
+ [
+ "agent-a",
+ "agent-b"
+ ]
+ ],
+ "todos": [
+ {
+ "todo_id": "todo_target",
+ "status": "open",
+ "claimed_by": null,
+ "excluded_agents": []
+ }
+ ],
+ "todo_projection_error": null,
+ "source_receipts": [
+ {
+ "source_id": "authority",
+ "path": "$ROOT/authority.json",
+ "state": "file",
+ "sha256": "408c3ee0aef794f423d49ba8141f3492961aa68b354d03c37c455d072d7e32c3"
+ }
+ ]
+ }
+ },
+ "files": {
+ "goals/goal-a/task-leases/todo_target.json": {
+ "schema_version": "task_lease_v0",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-a",
+ "idempotency_key": "lease-a",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 1,
+ "lease_epoch": 1,
+ "acquired_at": "2026-09-01T03:00:00Z",
+ "updated_at": "2026-09-01T03:00:00Z",
+ "expires_at": "2026-09-01T03:10:00Z",
+ "status": "active"
+ }
+ }
+ },
+ "renew": {
+ "request": {
+ "schema_version": "loopx_task_lease_lifecycle_native_v0",
+ "runtime_root": "$ROOT/runtime",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-a",
+ "idempotency_key": "lease-a",
+ "ttl_seconds": 600,
+ "write_scopes": [],
+ "expected_version": 1,
+ "authority": {
+ "handoff_mode": "hard_lease",
+ "registered_agent_candidates": [
+ [
+ "agent-a",
+ "agent-b"
+ ]
+ ],
+ "todos": [
+ {
+ "todo_id": "todo_target",
+ "status": "open",
+ "claimed_by": null,
+ "excluded_agents": []
+ }
+ ],
+ "todo_projection_error": null,
+ "source_receipts": [
+ {
+ "source_id": "authority",
+ "path": "$ROOT/authority.json",
+ "state": "file",
+ "sha256": "408c3ee0aef794f423d49ba8141f3492961aa68b354d03c37c455d072d7e32c3"
+ }
+ ]
+ },
+ "operation": "renew",
+ "new_owner": null,
+ "new_idempotency_key": null,
+ "lock_token": null,
+ "committed": false,
+ "release_lease": false,
+ "fence_owner": null,
+ "fence_idempotency_key": null,
+ "fence_expected_version": null,
+ "fence_expected_lease_epoch": 1
+ },
+ "files": {
+ "goals/goal-a/task-leases/.lifecycle-operations/83642ecca0b109ad9c67ef641c43a3cf84dfcb1a156bfc012e7b975475926323.json": {
+ "schema_version": "task_lease_lifecycle_receipt_v0",
+ "operation_id": "83642ecca0b109ad9c67ef641c43a3cf84dfcb1a156bfc012e7b975475926323",
+ "request_digest": "3c064882fc370f6dbc79e8526f25162e4c893830cc9be7ae3e455067f5d1a2dc",
+ "state": "committed",
+ "planned_lease": {
+ "schema_version": "task_lease_v0",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-a",
+ "idempotency_key": "lease-a",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 2,
+ "lease_epoch": 1,
+ "acquired_at": "2026-09-01T03:00:00Z",
+ "updated_at": "2026-09-01T03:01:00Z",
+ "expires_at": "2026-09-01T03:11:00Z",
+ "status": "active"
+ },
+ "response": {
+ "ok": true,
+ "schema_version": "task_lease_v0",
+ "action": "renew",
+ "renewed": true,
+ "lease": {
+ "schema_version": "task_lease_v0",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-a",
+ "idempotency_key": "lease-a",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 2,
+ "lease_epoch": 1,
+ "acquired_at": "2026-09-01T03:00:00Z",
+ "updated_at": "2026-09-01T03:01:00Z",
+ "expires_at": "2026-09-01T03:11:00Z",
+ "status": "active"
+ },
+ "lease_path": "$ROOT/runtime/goals/goal-a/task-leases/todo_target.json",
+ "handoff_mode": "hard_lease",
+ "idempotent": false,
+ "settlement": {
+ "effect_id": "goal-a:agent-a:todo_target:lease-a",
+ "receipts": [
+ {
+ "step": "validation",
+ "status": "committed",
+ "effect_id": "goal-a:agent-a:todo_target:lease-a"
+ },
+ {
+ "step": "durable_writeback",
+ "status": "committed",
+ "effect_id": "goal-a:agent-a:todo_target:lease-a"
+ }
+ ]
+ }
+ }
+ },
+ "goals/goal-a/task-leases/todo_target.json": {
+ "schema_version": "task_lease_v0",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-a",
+ "idempotency_key": "lease-a",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 2,
+ "lease_epoch": 1,
+ "acquired_at": "2026-09-01T03:00:00Z",
+ "updated_at": "2026-09-01T03:01:00Z",
+ "expires_at": "2026-09-01T03:11:00Z",
+ "status": "active"
+ }
+ }
+ },
+ "transfer": {
+ "request": {
+ "schema_version": "loopx_task_lease_lifecycle_native_v0",
+ "runtime_root": "$ROOT/runtime",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-a",
+ "idempotency_key": "lease-a",
+ "ttl_seconds": 600,
+ "write_scopes": [],
+ "expected_version": 1,
+ "authority": {
+ "handoff_mode": "hard_lease",
+ "registered_agent_candidates": [
+ [
+ "agent-a",
+ "agent-b"
+ ]
+ ],
+ "todos": [
+ {
+ "todo_id": "todo_target",
+ "status": "open",
+ "claimed_by": null,
+ "excluded_agents": []
+ }
+ ],
+ "todo_projection_error": null,
+ "source_receipts": [
+ {
+ "source_id": "authority",
+ "path": "$ROOT/authority.json",
+ "state": "file",
+ "sha256": "408c3ee0aef794f423d49ba8141f3492961aa68b354d03c37c455d072d7e32c3"
+ }
+ ]
+ },
+ "operation": "transfer",
+ "new_owner": "agent-b",
+ "new_idempotency_key": "lease-b",
+ "lock_token": null,
+ "committed": false,
+ "release_lease": false,
+ "fence_owner": null,
+ "fence_idempotency_key": null,
+ "fence_expected_version": null,
+ "fence_expected_lease_epoch": 1
+ },
+ "files": {
+ "goals/goal-a/task-leases/.lifecycle-operations/993e3b79909ee83f0dfcb96ee676b54b7435f12f5dfb14b4a0c7151216e3a5c5.json": {
+ "schema_version": "task_lease_lifecycle_receipt_v0",
+ "operation_id": "993e3b79909ee83f0dfcb96ee676b54b7435f12f5dfb14b4a0c7151216e3a5c5",
+ "request_digest": "3e7bf5cde7d9499d77ecb7b29d003f935ea2a868c99e5703ede7e54513864219",
+ "state": "committed",
+ "planned_lease": {
+ "schema_version": "task_lease_v0",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-b",
+ "idempotency_key": "lease-b",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 2,
+ "lease_epoch": 2,
+ "acquired_at": "2026-09-01T03:00:00Z",
+ "updated_at": "2026-09-01T03:01:00Z",
+ "expires_at": "2026-09-01T03:11:00Z",
+ "status": "active"
+ },
+ "response": {
+ "ok": true,
+ "schema_version": "task_lease_v0",
+ "action": "transfer",
+ "transferred": true,
+ "lease": {
+ "schema_version": "task_lease_v0",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-b",
+ "idempotency_key": "lease-b",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 2,
+ "lease_epoch": 2,
+ "acquired_at": "2026-09-01T03:00:00Z",
+ "updated_at": "2026-09-01T03:01:00Z",
+ "expires_at": "2026-09-01T03:11:00Z",
+ "status": "active"
+ },
+ "lease_path": "$ROOT/runtime/goals/goal-a/task-leases/todo_target.json",
+ "handoff_mode": "hard_lease",
+ "idempotent": false,
+ "settlement": {
+ "effect_id": "goal-a:agent-a:todo_target:lease-a",
+ "receipts": [
+ {
+ "step": "validation",
+ "status": "committed",
+ "effect_id": "goal-a:agent-a:todo_target:lease-a"
+ },
+ {
+ "step": "durable_writeback",
+ "status": "committed",
+ "effect_id": "goal-a:agent-a:todo_target:lease-a"
+ }
+ ]
+ }
+ }
+ },
+ "goals/goal-a/task-leases/todo_target.json": {
+ "schema_version": "task_lease_v0",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-b",
+ "idempotency_key": "lease-b",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 2,
+ "lease_epoch": 2,
+ "acquired_at": "2026-09-01T03:00:00Z",
+ "updated_at": "2026-09-01T03:01:00Z",
+ "expires_at": "2026-09-01T03:11:00Z",
+ "status": "active"
+ }
+ }
+ },
+ "terminal-held": {
+ "request": {
+ "schema_version": "loopx_task_lease_lifecycle_native_v0",
+ "runtime_root": "$ROOT/runtime",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-a",
+ "idempotency_key": "lease-a",
+ "ttl_seconds": 600,
+ "write_scopes": [],
+ "expected_version": 1,
+ "authority": {
+ "handoff_mode": "hard_lease",
+ "registered_agent_candidates": [
+ [
+ "agent-a",
+ "agent-b"
+ ]
+ ],
+ "todos": [
+ {
+ "todo_id": "todo_target",
+ "status": "open",
+ "claimed_by": null,
+ "excluded_agents": []
+ }
+ ],
+ "todo_projection_error": null,
+ "source_receipts": [
+ {
+ "source_id": "authority",
+ "path": "$ROOT/authority.json",
+ "state": "file",
+ "sha256": "408c3ee0aef794f423d49ba8141f3492961aa68b354d03c37c455d072d7e32c3"
+ }
+ ]
+ },
+ "operation": "terminal_verify",
+ "new_owner": null,
+ "new_idempotency_key": null,
+ "lock_token": null,
+ "committed": false,
+ "release_lease": false,
+ "fence_owner": null,
+ "fence_idempotency_key": null,
+ "fence_expected_version": null,
+ "fence_expected_lease_epoch": 1
+ },
+ "files": {
+ "goals/goal-a/task-leases/.lifecycle-fences/ee58a5ed5a4df4e8c8657e727f47f305250bacddc13bf50d27819573e9ed080a.json": {
+ "schema_version": "task_lease_fence_receipt_v0",
+ "operation_id": "ee58a5ed5a4df4e8c8657e727f47f305250bacddc13bf50d27819573e9ed080a",
+ "request_digest": "0e4b9170ebc4555e5c8aeb5d7195db3e5ad2c61750f1a29597c5ea61a2433f5d",
+ "close_request_digest": null,
+ "state": "held",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner_pid": 54181,
+ "lock_token": "8ff0637e-8406-4215-b76c-0041b84d235e",
+ "fence_owner": "agent-a",
+ "fence_idempotency_key": "lease-a",
+ "fence_expected_version": 1,
+ "fence_expected_lease_epoch": 1,
+ "lease": {
+ "schema_version": "task_lease_v0",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-a",
+ "idempotency_key": "lease-a",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 1,
+ "lease_epoch": 1,
+ "acquired_at": "2026-09-01T03:00:00Z",
+ "updated_at": "2026-09-01T03:00:00Z",
+ "expires_at": "2026-09-01T03:10:00Z",
+ "status": "active"
+ },
+ "response": {
+ "schema_version": "task_lease_v0",
+ "required": true,
+ "active": true,
+ "owner": "agent-a",
+ "version": 1,
+ "lease_epoch": 1,
+ "execution_instance_verified": true,
+ "lock_token": "8ff0637e-8406-4215-b76c-0041b84d235e",
+ "fence_operation_id": "ee58a5ed5a4df4e8c8657e727f47f305250bacddc13bf50d27819573e9ed080a"
+ },
+ "verify_response": {
+ "schema_version": "task_lease_v0",
+ "required": true,
+ "active": true,
+ "owner": "agent-a",
+ "version": 1,
+ "lease_epoch": 1,
+ "execution_instance_verified": true,
+ "lock_token": "8ff0637e-8406-4215-b76c-0041b84d235e",
+ "fence_operation_id": "ee58a5ed5a4df4e8c8657e727f47f305250bacddc13bf50d27819573e9ed080a"
+ }
+ },
+ "goals/goal-a/task-leases/todo_target.json": {
+ "schema_version": "task_lease_v0",
+ "goal_id": "goal-a",
+ "todo_id": "todo_target",
+ "owner": "agent-a",
+ "idempotency_key": "lease-a",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 1,
+ "lease_epoch": 1,
+ "acquired_at": "2026-09-01T03:00:00Z",
+ "updated_at": "2026-09-01T03:00:00Z",
+ "expires_at": "2026-09-01T03:10:00Z",
+ "status": "active"
+ }
+ }
+ }
+ }
+}
diff --git a/tests/control_plane/fixtures/retained_monitor_receipts_1x.json b/tests/control_plane/fixtures/retained_monitor_receipts_1x.json
new file mode 100644
index 0000000000..e53e830037
--- /dev/null
+++ b/tests/control_plane/fixtures/retained_monitor_receipts_1x.json
@@ -0,0 +1,356 @@
+{
+ "source_revision": "19c725e3fbf587b0d857863cd319e08ad0efe70c",
+ "source_state": "---\nstatus: active\nupdated_at: 2026-10-15T18:55:45+08:00\n---\n\n# Active Goal State\n\n## Agent Todo\n\n- [ ] Observe public synthetic target\n \n",
+ "pending": {
+ "schema_version": "quota_monitor_poll_pending_admission_v1",
+ "effect_id": "quota-monitor-poll:synthetic-monitor-recovery:codex-quality-qualification:synthetic-monitor-turn-1:todo:todo_7f667db6fd16",
+ "request_digest": "sha256:dbce33e18630d57c4bb67a6c21896cfbe453374d74a19739a3e67b579b365021",
+ "status": "provider_pending",
+ "index_path": "$FIXTURE_ROOT/runtime/goals/synthetic-monitor-recovery/runs/index.jsonl",
+ "expected_index_digest": null,
+ "expected_index_bytes": 0,
+ "provider_plan": {
+ "schema_version": "monitor_poll_todo_provider_plan_v0",
+ "legacy_batch_version": 1,
+ "monitor_effect_id": "quota-monitor-poll:synthetic-monitor-recovery:codex-quality-qualification:synthetic-monitor-turn-1:todo:todo_7f667db6fd16",
+ "goal_id": "synthetic-monitor-recovery",
+ "generated_at": "2026-10-15T18:55:45+08:00",
+ "execute": true,
+ "todo_id": "todo_7f667db6fd16",
+ "target_key": "synthetic-target",
+ "result_hash": "synthetic-head-1",
+ "material_change": false,
+ "cadence": null,
+ "next_due_at": null,
+ "reason_summary": null,
+ "next_agent_todo": null,
+ "next_action_kind": null,
+ "next_task_repository": null,
+ "next_required_capabilities": [],
+ "next_continuation_policy": null,
+ "next_target_key": null,
+ "next_user_todo": null,
+ "next_user_task_class": null,
+ "next_claimed_by": null,
+ "agent_id": "codex-quality-qualification"
+ },
+ "admitted_decision": {
+ "should_run": true,
+ "normal_delivery_allowed": true,
+ "recovery_delivery_allowed": false,
+ "effective_action": "normal_run",
+ "self_repair_allowed": false,
+ "capability_repair_allowed": false,
+ "workspace_repair_allowed": false,
+ "state": "eligible",
+ "safe_bypass_allowed": false,
+ "safe_bypass_kind": null,
+ "blocked_action_scope": null,
+ "compute": 1,
+ "window_hours": 24,
+ "slot_minutes": 1,
+ "spent_slots": 0,
+ "allowed_slots": 1440,
+ "goal_id": "synthetic-monitor-recovery",
+ "agent_id": "codex-quality-qualification",
+ "recommended_action": "Observe public synthetic target",
+ "reason": "1 compute quota; eligible for the next automatic agent turn",
+ "requires_user_action": false,
+ "heartbeat_recommendation": {
+ "source": "quota.should-run",
+ "recommended_mode": "follow_work_lane_contract",
+ "notify": "DONT_NOTIFY",
+ "spend_policy": "follow work_lane_contract.obligation; spend only after validated advancement, concrete blocker writeback, or a material monitor transition",
+ "reason": "work_lane_contract is the machine contract for monitor-vs-advancement routing",
+ "agent_must_attempt": true
+ },
+ "work_lane_contract": {
+ "schema_version": "work_lane_contract_v1",
+ "lane": "continuous_monitor",
+ "monitor_kind": "todo_monitor_due",
+ "next_lane": "continuous_monitor",
+ "obligation": "attempt_due_monitor",
+ "must_attempt_work": true,
+ "reason_codes": [
+ "due_monitor_context",
+ "heartbeat_receipt_bound_replay",
+ "same_turn_settlement_identity"
+ ],
+ "monitor_policy": "settle_receipt_bound_monitor_before_reselection",
+ "monitor_due_count": 1,
+ "monitor_due_items": [
+ {
+ "index": 1,
+ "text": "Observe public synthetic target",
+ "content_revision": "sha256:f2fde236687d8c6db2b5af05aea544a4e1d9f5dd46f4afd071abf42f3961095c",
+ "schema_version": "agent_lane_next_action_v0",
+ "todo_id": "todo_7f667db6fd16",
+ "role": "agent",
+ "status": "open",
+ "archive_state": "active",
+ "source_section": "Agent Todo",
+ "task_class": "continuous_monitor",
+ "claimed_by": "codex-quality-qualification",
+ "target_key": "synthetic-target",
+ "cadence": "1h",
+ "next_due_at": "2000-01-01T00:00:00+00:00",
+ "watch_only": "true",
+ "updated_at": "2026-10-15T18:55:42+08:00",
+ "agent_id": "codex-quality-qualification",
+ "source": "heartbeat_receipt.monitor_todo",
+ "selected_by": "current_agent_claimed_todo",
+ "confidence": "selected",
+ "preserves_goal_next_action": true,
+ "selection_binding": "heartbeat_receipt",
+ "receipt_bound_monitor_phase": "poll_due"
+ }
+ ],
+ "selected_todo_id": "todo_7f667db6fd16",
+ "selected_next_due_at": "2000-01-01T00:00:00+00:00",
+ "action": "settle the due monitor already bound to this heartbeat turn; reconsider newly runnable monitor priority on the next turn",
+ "selection_binding": "heartbeat_receipt",
+ "deferred_work_lane": {
+ "schema_version": "work_lane_contract_v1",
+ "lane": "continuous_monitor",
+ "monitor_kind": "todo_monitor_due",
+ "next_lane": "continuous_monitor",
+ "obligation": "attempt_due_monitor",
+ "must_attempt_work": true,
+ "reason_codes": [
+ "monitor_todo_only",
+ "monitor_due"
+ ],
+ "monitor_policy": "attempt_due_monitor_once_then_writeback_or_no_spend_if_unchanged",
+ "monitor_due_count": 1,
+ "monitor_due_items": [
+ {
+ "index": 1,
+ "text": "Observe public synthetic target",
+ "todo_id": "todo_7f667db6fd16",
+ "status": "open",
+ "task_class": "continuous_monitor",
+ "claimed_by": "codex-quality-qualification",
+ "target_key": "synthetic-target",
+ "next_due_at": "2000-01-01T00:00:00+00:00",
+ "watch_only": "true"
+ }
+ ],
+ "selected_todo_id": "todo_7f667db6fd16",
+ "selected_next_due_at": "2000-01-01T00:00:00+00:00",
+ "action": "attempt the selected due continuous_monitor todo; write back only a material transition, blocker, or compact reschedule/no-change note"
+ }
+ },
+ "external_evidence_observation": {},
+ "vision_wait_state": {},
+ "due_monitor_candidates": [
+ {
+ "todo_id": "todo_7f667db6fd16",
+ "target_key": "synthetic-target",
+ "task_class": "continuous_monitor"
+ }
+ ],
+ "registry_due_monitor": {
+ "due": true,
+ "todo_id": "todo_7f667db6fd16",
+ "target_key": "synthetic-target",
+ "task_class": "continuous_monitor",
+ "claimed_by": "codex-quality-qualification"
+ },
+ "auxiliary_gate_scope": null,
+ "auxiliary_settlement_todo": null
+ }
+ },
+ "request_fingerprint_input": {
+ "schema_version": "loopx_quota_monitor_poll_commit_request_v0",
+ "effect_id": "quota-monitor-poll:synthetic-monitor-recovery:codex-quality-qualification:synthetic-monitor-turn-1:todo:todo_7f667db6fd16",
+ "runtime_root": "$FIXTURE_ROOT/runtime",
+ "goal_id": "synthetic-monitor-recovery",
+ "source": "heartbeat",
+ "turn_instance_id": "synthetic-monitor-turn-1",
+ "observation": {
+ "actor_agent_id": "codex-quality-qualification",
+ "settlement_todo_id": "todo_7f667db6fd16",
+ "reason_summary": null,
+ "todo_id": "todo_7f667db6fd16",
+ "target_key": "synthetic-target",
+ "result_hash": "synthetic-head-1",
+ "material_change": false,
+ "cadence": null,
+ "next_due_at": null,
+ "next_agent_todo": null,
+ "next_action_kind": null,
+ "next_task_repository": null,
+ "next_required_capabilities": [],
+ "next_continuation_policy": null,
+ "next_target_key": null,
+ "next_user_todo": null,
+ "next_user_task_class": null,
+ "next_claimed_by": null
+ }
+ },
+ "business_receipt": {
+ "schema_version": "loopx_monitor_batch_receipt_v0",
+ "goal_id": "synthetic-monitor-recovery",
+ "operation_id": "quota-monitor-poll:synthetic-monitor-recovery:codex-quality-qualification:synthetic-monitor-turn-1:todo:todo_7f667db6fd16",
+ "request_sha256": "d582285b9e2e520aef1d83ee9f18dedb27215382504fba2890a421d4e572219c",
+ "writeback": {
+ "schema_version": "monitor_poll_todo_writeback_v0",
+ "dry_run": false,
+ "goal_id": "synthetic-monitor-recovery",
+ "todo_id": "todo_7f667db6fd16",
+ "monitor_effect_id": "quota-monitor-poll:synthetic-monitor-recovery:codex-quality-qualification:synthetic-monitor-turn-1:todo:todo_7f667db6fd16",
+ "target_key": "synthetic-target",
+ "result_hash": "synthetic-head-1",
+ "material_change": false,
+ "material_change_generation": 0,
+ "consecutive_no_change": 1,
+ "last_checked_at": "2026-10-15T18:55:45+08:00",
+ "next_due_at": "2026-10-15T11:55:45Z",
+ "cadence": "1h",
+ "todo_update": {
+ "ok": true,
+ "dry_run": false,
+ "changed": true,
+ "goal_id": "synthetic-monitor-recovery",
+ "agent_id": "codex-quality-qualification",
+ "mutation_authority": {
+ "schema_version": "todo_mutation_authority_v0",
+ "command": "update",
+ "mode": "single_agent_compatibility",
+ "actor_agent_id": "codex-quality-qualification",
+ "todo_id": "todo_7f667db6fd16",
+ "registered_agent_count": 1
+ },
+ "handoff_mode": "legacy",
+ "monitor_poll_transition": {
+ "cadence": "1h",
+ "consecutive_no_change": 1,
+ "last_checked_at": "2026-10-15T18:55:45+08:00",
+ "material_change": false,
+ "material_change_applied": false,
+ "material_change_generation": 0,
+ "monitor_effect_id": "quota-monitor-poll:synthetic-monitor-recovery:codex-quality-qualification:synthetic-monitor-turn-1:todo:todo_7f667db6fd16",
+ "next_due_at": "2026-10-15T11:55:45Z",
+ "provider_replayed": false,
+ "result_hash": "synthetic-head-1",
+ "target_key": "synthetic-target"
+ },
+ "role": "agent",
+ "section": "Agent Todo",
+ "todo": "Observe public synthetic target",
+ "todo_id": "todo_7f667db6fd16",
+ "status": "open",
+ "status_changed": false,
+ "text_changed": false,
+ "priority": null,
+ "metadata_updated": true,
+ "claimed_by": "codex-quality-qualification",
+ "bound_agent": null,
+ "goal_bound": null,
+ "task_class": "continuous_monitor",
+ "action_kind": null,
+ "task_domain": null,
+ "capability_binding_ref": null,
+ "task_repository": null,
+ "continuation_policy": null,
+ "required_capabilities": [],
+ "target_capabilities": [],
+ "explore_result_node_refs": [],
+ "decision_scope": null,
+ "required_decision_scopes": [],
+ "decision_outcome": null,
+ "decision_scope_outcomes": [],
+ "blocks_agent": null,
+ "excluded_agents": [],
+ "global_gate": null,
+ "unblocks_todo_id": null,
+ "successor_todo_ids": [],
+ "completion_continuation": null,
+ "completion_recovery": null,
+ "completion_receipt_id": null,
+ "resume_when": null,
+ "resume_monitor_generation": null,
+ "no_followup": null,
+ "target_key": "synthetic-target",
+ "cadence": "1h",
+ "next_due_at": "2026-10-15T11:55:45Z",
+ "expires_at": null,
+ "watch_only": "true",
+ "material_change_generation": "0",
+ "state_file": "$FIXTURE_ROOT/project/.codex/goals/synthetic-monitor-recovery/ACTIVE_GOAL_STATE.md",
+ "project": "$FIXTURE_ROOT/project",
+ "updated_at": "2026-10-15T18:55:45+08:00"
+ },
+ "next_todos": [],
+ "successor_receipts": [],
+ "provider_replayed": false
+ }
+ },
+ "poll": [
+ "quota",
+ "monitor-poll",
+ "--goal-id",
+ "synthetic-monitor-recovery",
+ "--agent-id",
+ "codex-quality-qualification",
+ "--runtime-profile",
+ "generic_cli",
+ "--turn-instance-id",
+ "synthetic-monitor-turn-1",
+ "--todo-id",
+ "todo_7f667db6fd16",
+ "--target-key",
+ "synthetic-target",
+ "--result-hash",
+ "synthetic-head-1",
+ "--execute"
+ ],
+ "heartbeat": {
+ "agent_id": "codex-quality-qualification",
+ "boundary": {
+ "absolute_paths_recorded": false,
+ "credential_values_recorded": false,
+ "raw_logs_recorded": false,
+ "raw_session_transcript_recorded": false,
+ "raw_task_text_recorded": false,
+ "raw_trajectory_recorded": false
+ },
+ "details": {
+ "appended": false,
+ "applied_rrule": null,
+ "closeout_required": true,
+ "command": "quota",
+ "delivery_allowed": true,
+ "delivery_workspace_causality_reason": "todo_write_contract_not_explicit",
+ "delivery_workspace_causality_schema_version": "delivery_workspace_causality_v0",
+ "delivery_workspace_causality_source": "selected_todo_contract",
+ "delivery_workspace_causality_todo_id": "todo_7f667db6fd16",
+ "delivery_workspace_requirement": "unknown",
+ "interaction_mode": "bounded_delivery",
+ "must_attempt_work": true,
+ "ok": true,
+ "quiet_noop_allowed": false,
+ "quota_command": "should-run",
+ "quota_spend_source": "heartbeat",
+ "replan_obligation_id": null,
+ "semantic_replan_obligation_id": null,
+ "settlement_effect_id": "synthetic-monitor-recovery:codex-quality-qualification:todo_7f667db6fd16:synthetic-monitor-turn-1",
+ "should_run": true,
+ "slots": null,
+ "source": "run_history",
+ "stall_observation": "not_applicable",
+ "successor_todo_ids": null,
+ "target_key": null,
+ "todo_id": "todo_7f667db6fd16",
+ "turn_instance_id": "synthetic-monitor-turn-1"
+ },
+ "event_id": "55deeb4d3e5b460c",
+ "event_kind": "quota_should_run",
+ "goal_id": "synthetic-monitor-recovery",
+ "recorded_at": "2026-10-15T10:55:44.415749Z",
+ "run_id": "synthetic-monitor-turn-1",
+ "schema_version": "loopx_rollout_event_v0",
+ "status": "normal_run",
+ "summary": "heartbeat quota receipt committed for turn=synthetic-monitor-turn-1 stall=not_applicable"
+ }
+}
diff --git a/tests/control_plane/fixtures/team_plan_1x.json b/tests/control_plane/fixtures/team_plan_1x.json
new file mode 100644
index 0000000000..2679da2cce
--- /dev/null
+++ b/tests/control_plane/fixtures/team_plan_1x.json
@@ -0,0 +1,80 @@
+{
+ "source_revision": "60d833b13c85135587b725013b247e827663ffb9",
+ "plan": {
+ "schema_version": "steward_team_plan_preview_v0",
+ "kind": "steward_team_plan_preview",
+ "goal_id": "goal-a",
+ "proposal_id": "historical-plan",
+ "objective": "Independent lane outcomes",
+ "quota_envelope": {
+ "slots": 2
+ },
+ "stop_condition": "Owner ends request",
+ "lanes": [
+ {
+ "lane_id": "lane-alpha",
+ "agent_id": "alpha",
+ "acceptance": "Return independent evidence",
+ "first_todo": {
+ "text": "Own work",
+ "priority": "P1",
+ "task_class": "advancement_task",
+ "action_kind": "implement"
+ }
+ }
+ ]
+ },
+ "basis": "e56dfe4695a07f36678032267de2d3aca064a7ce1cdf69db4cec2367a11da105",
+ "source": "\n\n# Goal\n\n## Agent Todo\n\n- [ ] [P1] Own work\n \n\n",
+ "result": {
+ "action": "created",
+ "todo_id": "todo_0257c02b0bf76ba6c7c3ed4e",
+ "target_key": null,
+ "created_todo_ids": [
+ "todo_0257c02b0bf76ba6c7c3ed4e"
+ ],
+ "lane_todo_ids": [
+ "todo_0257c02b0bf76ba6c7c3ed4e"
+ ],
+ "lane_settlements": [
+ {
+ "lane_id": "lane-alpha",
+ "agent_id": "alpha",
+ "priority": "P1",
+ "acceptance": "Return independent evidence",
+ "disposition": "created",
+ "todo_id": "todo_0257c02b0bf76ba6c7c3ed4e"
+ }
+ ],
+ "gap_count": 0,
+ "gap_lanes": [],
+ "reused_lane_count": 0,
+ "intent_basis": "sha256:0960f2bef565df76a1c073b25ca20c31db825c5b1b30ac66e03dc721b33b4c8e",
+ "lane_failure": null,
+ "coordination_runtime_shadow": {
+ "schema_version": "loopx_local_authority_shadow_evidence_v1",
+ "outcome": "no_transaction",
+ "reason_code": "shadow_disabled",
+ "goal_id": "goal-a",
+ "entry": {
+ "entry_id": null,
+ "partition": null,
+ "seq": null,
+ "partition_digest": null,
+ "source_bytes_digest": null
+ },
+ "drain": null,
+ "capture_kind": "source_transaction_outbox",
+ "source_transaction_correlated": false,
+ "durable_source_outbox": false,
+ "source_candidate_compared": false,
+ "parity_verdict": "not_evaluated",
+ "primary_authority": "legacy_local",
+ "candidate_provider": "file",
+ "candidate_read_for_decision": false,
+ "provider_to_local_writes": false,
+ "primary_writeback_preserved": true,
+ "store_identity": null
+ }
+ }
+}
diff --git a/tests/control_plane/monitor_poll_canonical_fixture.py b/tests/control_plane/monitor_poll_canonical_fixture.py
new file mode 100644
index 0000000000..20de2f7ed3
--- /dev/null
+++ b/tests/control_plane/monitor_poll_canonical_fixture.py
@@ -0,0 +1,200 @@
+"""Canonical Monitor poll fixtures shared by recovery tests."""
+
+from __future__ import annotations
+
+import json
+import os
+from pathlib import Path
+import subprocess
+import sys
+
+from canonical_authority_fixture import initialize_canonical_authority
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
+from loopx.control_plane.testing.canary_harness import (
+ read_run_index,
+ write_fixture_registry,
+)
+
+
+GOAL = "synthetic-monitor-recovery"
+AGENT = "codex-quality-qualification"
+MONITOR_TARGET = "synthetic-target"
+AGENT_TASK = "Validate observed synthetic head"
+USER_TASK = "Review observed synthetic head"
+REPO = Path(__file__).resolve().parents[2]
+
+_QUOTA_COMMIT_FAULT_WRAPPER = """
+import os, runpy, sys
+import loopx.control_plane.quota.monitor_poll as monitor
+original = monitor._native_result
+def interrupted_commit(request):
+ if request.get('phase') == 'commit' and request.get('provider_receipt'):
+ os._exit(73)
+ return original(request)
+monitor._native_result = interrupted_commit
+sys.argv = ['loopx.cli', *sys.argv[1:]]
+runpy.run_module('loopx.cli', run_name='__main__')
+"""
+
+
+def _cli(
+ registry: Path,
+ runtime: Path,
+ *args: str,
+ fault: str | None = None,
+) -> tuple[int, dict | None]:
+ launcher = (
+ [sys.executable, "-c", _QUOTA_COMMIT_FAULT_WRAPPER]
+ if fault == "quota_commit"
+ else [sys.executable, "-m", "loopx.cli"]
+ )
+ result = subprocess.run(
+ [
+ *launcher,
+ "--registry",
+ str(registry),
+ "--format",
+ "json",
+ "--runtime-root",
+ str(runtime),
+ *args,
+ ],
+ cwd=REPO,
+ env={**os.environ, "LOOPX_USAGE_PING": "0"},
+ capture_output=True,
+ text=True,
+ check=False,
+ timeout=60,
+ )
+ return (
+ result.returncode,
+ json.loads(result.stdout) if result.stdout.strip() else None,
+ )
+
+
+def _ok(registry: Path, runtime: Path, *args: str) -> dict:
+ code, result = _cli(registry, runtime, *args)
+ assert code == 0, result
+ assert isinstance(result, dict)
+ return result
+
+
+def _fixture(
+ tmp_path: Path,
+ *,
+ provider: str,
+) -> tuple[Path, Path, Path, str, tuple[str, ...]]:
+ project = tmp_path / "project"
+ runtime = tmp_path / "runtime"
+ state = project / ".codex" / "goals" / GOAL / "ACTIVE_GOAL_STATE.md"
+ registry = project / ".loopx" / "registry.json"
+ state.parent.mkdir(parents=True)
+ state.write_text(
+ "---\nstatus: active\n---\n\n# Active Goal State\n\n## Agent Todo\n",
+ encoding="utf-8",
+ )
+ write_fixture_registry(
+ project=project,
+ runtime_root=runtime,
+ registry_path=registry,
+ goal_id=GOAL,
+ domain="loopx-platform",
+ adapter_kind="harness_self_improvement",
+ registered_agents=[AGENT],
+ quota_allowed_slots=None,
+ )
+ monitor = {
+ "schema_version": "todo_item_v0",
+ "todo_id": "todo_synthetic_monitor",
+ "role": "agent",
+ "status": "open",
+ "done": False,
+ "text": "Observe public synthetic target",
+ "archive_state": "active",
+ "source_section": "Agent Todo",
+ "index": 1,
+ "task_class": "continuous_monitor",
+ "claimed_by": AGENT,
+ "target_key": MONITOR_TARGET,
+ "cadence": "1h",
+ "next_due_at": "2000-01-01T00:00:00+00:00",
+ "watch_only": "true",
+ }
+ initialize_canonical_authority(
+ runtime,
+ GOAL,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL,
+ todos=[monitor],
+ handoff_mode="soft_claim",
+ ),
+ state_path=state,
+ provider=provider,
+ )
+ turn = "synthetic-monitor-turn-1"
+ _ok(
+ registry,
+ runtime,
+ "quota",
+ "should-run",
+ "--goal-id",
+ GOAL,
+ "--agent-id",
+ AGENT,
+ "--runtime-profile",
+ "generic_cli",
+ "--turn-instance-id",
+ turn,
+ )
+ poll = (
+ "quota",
+ "monitor-poll",
+ "--goal-id",
+ GOAL,
+ "--agent-id",
+ AGENT,
+ "--runtime-profile",
+ "generic_cli",
+ "--turn-instance-id",
+ turn,
+ "--todo-id",
+ monitor["todo_id"],
+ "--target-key",
+ MONITOR_TARGET,
+ "--result-hash",
+ "synthetic-head-1",
+ "--material-change",
+ "--next-agent-todo",
+ AGENT_TASK,
+ "--next-action-kind",
+ "validate",
+ "--next-user-todo",
+ USER_TASK,
+ "--next-user-task-class",
+ "user_action",
+ "--execute",
+ )
+ return registry, runtime, state, monitor["todo_id"], poll
+
+
+def _poll_events(runtime: Path) -> list[dict]:
+ return [
+ row
+ for row in read_run_index(runtime, GOAL)
+ if row.get("classification") == "quota_monitor_poll"
+ ]
+
+
+def _quota_receipt(runtime: Path) -> Path:
+ directory = (
+ runtime
+ / "goals"
+ / GOAL
+ / "runs"
+ / ".transactions"
+ / "quota-monitor-poll"
+ )
+ (receipt,) = directory.glob("*.json")
+ return receipt
diff --git a/tests/control_plane/shadow_e2e_fixture.py b/tests/control_plane/shadow_e2e_fixture.py
index 0469688896..757ce17f6a 100644
--- a/tests/control_plane/shadow_e2e_fixture.py
+++ b/tests/control_plane/shadow_e2e_fixture.py
@@ -1,18 +1,12 @@
-"""Disposable public CLI fixtures and scheduling-only process crash seams."""
+"""Disposable public CLI fixtures for retained shadow management tests."""
from __future__ import annotations
import json
from pathlib import Path
-import select
import subprocess
import sys
from dataclasses import dataclass
-from tempfile import TemporaryDirectory
-
-import loopx
-
-from loopx.control_plane.testing.authority_e2e_rows_stage2c2 import CRASH_WORKER
REPO = Path(__file__).resolve().parents[2]
@@ -25,7 +19,6 @@ class ShadowWorkspace:
goal: str = "goal-e2e"
command: tuple[str, ...] = (sys.executable, "-m", "loopx.cli")
cwd: Path = REPO
- package: Path = Path(loopx.__file__).resolve().parent
def arguments(self, *args: str) -> list[str]:
return [
@@ -56,75 +49,6 @@ def cli(self, *args: str, success: bool = True) -> dict:
def add(self, text: str) -> dict:
return self.cli("todo", "add", "--role", "agent", "--text", text)
- def drain(self, **limits: str) -> dict:
- args = [
- item
- for key, value in limits.items()
- for item in ("--" + key.replace("_", "-"), value)
- ]
- return self.cli("authority-shadow", "drain", *args, success=False)
-
- def crash(self, window: str, *args: str) -> dict:
- # Test-only fault assets are not wheel data. Generate the scheduling
- # driver here and bind it to the actual package's production owner.
- with TemporaryDirectory(prefix="loopx-drain-fault-") as directory:
- driver = Path(directory) / "drain_fault.mts"
- module = self.package / "control_plane/coordination/shadow_drain.ts"
- driver.write_text(
- "import {drainShadowOutbox} from " + json.dumps(module.as_uri()) + ";"
- "let input=''; for await (const bytes of process.stdin) input+=bytes;"
- "const phase=process.argv[2]==='between_unlinks'?'after_unlink':process.argv[2];"
- "const request=JSON.parse(input);"
- "const result=await drainShadowOutbox(request,{afterEffect:async observed=>{"
- "if(observed===phase){"
- "process.stdout.write('BARRIER '+JSON.stringify({native_pid:process.pid,request})+'\\n');"
- "await new Promise(()=>{setInterval(()=>{},1000);});"
- "}}});process.stdout.write(JSON.stringify(result)+'\\n');",
- encoding="utf-8",
- )
- relative_driver = "'loopx/control_plane/testing/shadow_drain_fault_process.ts'"
- assert CRASH_WORKER.count(relative_driver) == 1
- worker = CRASH_WORKER.replace(relative_driver, repr(str(driver)))
- child = subprocess.Popen(
- [
- sys.executable,
- "-c",
- worker,
- window,
- str(self.state),
- *self.arguments(*args),
- ],
- cwd=self.cwd,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True,
- )
- assert child.stdout is not None
- try:
- readable, _, _ = select.select([child.stdout], [], [], 30)
- assert readable, "public CLI did not reach the requested persistence window"
- line = child.stdout.readline()
- if not line.startswith("BARRIER "):
- child.kill()
- stdout, stderr = child.communicate(timeout=10)
- raise AssertionError(f"No process barrier: {line}{stdout}\n{stderr}")
- payload = json.loads(line.removeprefix("BARRIER "))
- if payload.get("native_pid"):
- assert child.stdin is not None
- child.stdin.write("terminate_native\n")
- child.stdin.flush()
- readable, _, _ = select.select([child.stdout], [], [], 10)
- assert readable and child.stdout.readline().strip() == "REAPED", "native owner must be reaped"
- child.kill()
- child.communicate(timeout=10)
- assert child.returncode == -9
- return payload
- finally:
- if child.poll() is None:
- child.kill()
- child.communicate(timeout=10)
-
def workspace(path: Path, *, bootstrap: bool = True) -> ShadowWorkspace:
path.mkdir(parents=True, exist_ok=True)
diff --git a/tests/control_plane/test_actual_default_model_behavior_portfolio.py b/tests/control_plane/test_actual_default_model_behavior_portfolio.py
index 2d049bd12c..b3758de3dd 100644
--- a/tests/control_plane/test_actual_default_model_behavior_portfolio.py
+++ b/tests/control_plane/test_actual_default_model_behavior_portfolio.py
@@ -74,6 +74,7 @@
_build_fixture as _build_selected_fixture,
)
from loopx.control_plane.testing.terminal_settlement_tool_behavior import (
+ TERMINAL_SETTLEMENT_FIXTURE_TODO_ID,
DoubaoTerminalSettlementToolBehaviorActor,
)
from loopx.quota import build_quota_should_run
@@ -460,7 +461,7 @@ def _capability_monitor_repair_actor(_: str) -> dict[str, Any]:
def _terminal_settlement_actor(_: str) -> dict[str, Any]:
return _passing_tool_receipt(
"terminal_settlement_tool_behavior_receipt_v0",
- selected_todo_id="todo_terminal001",
+ selected_todo_id=TERMINAL_SETTLEMENT_FIXTURE_TODO_ID,
user_action_required=False,
delivery_allowed=True,
terminal_order_observed=True,
diff --git a/tests/control_plane/test_archive_dependency_capture.py b/tests/control_plane/test_archive_dependency_capture.py
index 002ef99f69..479435076b 100644
--- a/tests/control_plane/test_archive_dependency_capture.py
+++ b/tests/control_plane/test_archive_dependency_capture.py
@@ -7,7 +7,6 @@
import pytest
from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
-from test_runtime_shadow_bounded_e2e import workspace, enable, cli
from loopx.control_plane.coordination.authority_source_capture import (
build_runtime_shadow_source_snapshot, capture_todo_archive_dependencies,
)
@@ -18,6 +17,23 @@
from loopx.control_plane.testing.canary_harness import run_json_cli_result
+def workspace(tmp_path: Path) -> tuple[Path, Path, Path]:
+ runtime = tmp_path / "runtime"
+ state = tmp_path / "ACTIVE_GOAL_STATE.md"
+ state.write_text("---\ngoal_id: goal-a\nhandoff_mode: legacy\n---\n")
+ registry = tmp_path / "registry.json"
+ registry.write_text(json.dumps({
+ "common_runtime_root": str(runtime),
+ "goals": [{
+ "id": "goal-a",
+ "repo": str(tmp_path),
+ "state_file": state.name,
+ "coordination": {"registered_agents": ["agent-a"]},
+ }],
+ }))
+ return registry, runtime, state
+
+
def row(todo_id: str, *, text: str = "Delivered result", metadata: str = "", marker: str = "x") -> str:
return f"- [{marker}] {text}\n \n"
@@ -112,16 +128,3 @@ def test_complete_snapshot_and_real_provider_cli_keep_dependency_semantics(tmp_p
assert code == 0, listed
assert [r["todo_id"] for r in listed["todos"]] == ["todo_next"]
assert not state.exists()
-
-
-def test_public_bootstrap_and_writer_outbox_share_legacy_archive_admission(tmp_path: Path):
- registry, runtime, state = workspace(tmp_path)
- state.write_text("---\ngoal_id: goal-a\nhandoff_mode: legacy\n---\n" + source_graph())
- enable(registry)
- boot = cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- assert boot["ok"] is True
- write = cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent",
- "--text", "New captured work", "--task-class", "advancement_task")
- assert write["coordination_runtime_shadow"]["outcome"] == "delivered"
- inspected = cli(registry, runtime, "coordination-shadow", "inspect", "--goal-id", "goal-a")
- assert inspected["inspection"]["status"] == "matched", inspected
diff --git a/tests/control_plane/test_archive_retry_delivery.py b/tests/control_plane/test_archive_retry_delivery.py
index 40241aedc6..828d59d7f4 100644
--- a/tests/control_plane/test_archive_retry_delivery.py
+++ b/tests/control_plane/test_archive_retry_delivery.py
@@ -348,6 +348,15 @@ def test_unpromoted_archive_does_not_rebuild_missing_display(tmp_path: Path) ->
before = {path: path.read_bytes() for path in authority.rglob("*") if path.is_file()}
result = _archive_cli(registry)
assert result.returncode == 1
- assert "active state file does not exist" in json.loads(result.stdout)["error"]
+ failure = json.loads(result.stdout)
+ assert failure["error_code"] == "canonical_authority_required"
+ assert failure["operation"] == "todo_archive"
+ assert failure["write_admitted"] is False
+ assert failure["decision_read_from_provider"] is False
+ assert failure["legacy_fallback_used"] is False
+ assert failure["recovery"] == {
+ "action": "migrate_goal_authority",
+ "backup_required": True,
+ }
assert not state.exists()
assert {path: path.read_bytes() for path in authority.rglob("*") if path.is_file()} == before
diff --git a/tests/control_plane/test_auxiliary_scoped_gate.py b/tests/control_plane/test_auxiliary_scoped_gate.py
index e688414494..fa61a1e8fb 100644
--- a/tests/control_plane/test_auxiliary_scoped_gate.py
+++ b/tests/control_plane/test_auxiliary_scoped_gate.py
@@ -21,6 +21,20 @@
def _scoped_fixture(tmp_path, monkeypatch, provider, route="source", *, with_gate=True):
isolate_sqlite_runtime(tmp_path, monkeypatch)
project, runtime, source = _write_fixture(tmp_path)
+ _append_newly_due_monitor(project, priority="P1", watch_only=True)
+ todos = list_goal_todos(registry_path=source, goal_id=GOAL_ID)["todos"]
+ projection = build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=todos,
+ handoff_mode="soft_claim",
+ )
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ projection,
+ state_path=project / f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md",
+ provider=provider,
+ )
update_goal_todo(registry_path=source, goal_id=GOAL_ID, todo_id=TODO_ID,
claimed_by=AGENT_ID, agent_id=AGENT_ID, priority="P0")
gated = add_goal_todo(registry_path=source, goal_id=GOAL_ID, role="agent",
@@ -28,12 +42,12 @@ def _scoped_fixture(tmp_path, monkeypatch, provider, route="source", *, with_gat
gate = (add_goal_todo(registry_path=source, goal_id=GOAL_ID, role="user",
text="Decide the gated work", task_class="user_gate", bound_agent=AGENT_ID,
blocks_agent=AGENT_ID, unblocks_todo_id=gated["todo_id"], agent_id=AGENT_ID) if with_gate else None)
- _append_newly_due_monitor(project, priority="P1", watch_only=True)
- if provider != "legacy":
- todos = list_goal_todos(registry_path=source, goal_id=GOAL_ID)["todos"]
- projection = build_todo_runtime_shadow_projection(goal_id=GOAL_ID, todos=todos, handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, GOAL_ID, projection,
- state_path=project / f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md", provider=provider)
+ if gate is not None:
+ gate = list_goal_todos(
+ registry_path=source,
+ goal_id=GOAL_ID,
+ todo_id=gate["todo_id"],
+ )["todo"]
registry = source
if route == "global":
registry = runtime / "registry.global.json"
@@ -44,7 +58,7 @@ def _scoped_fixture(tmp_path, monkeypatch, provider, route="source", *, with_gat
return project, runtime, source, registry, gate
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("route", ["source", "global"])
@pytest.mark.parametrize("gate_change", ["none", "targets_monitor", "unknown_scope", "global_gate", "primary_blocked"])
def test_emitted_auxiliary_command_honors_an_unrelated_gate(tmp_path, monkeypatch, provider, route, gate_change):
@@ -111,7 +125,7 @@ def emitted():
assert rc == 0 and after["selected_todo"]["todo_id"] == TODO_ID, after
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_gate_changed_after_preflight_cannot_commit_observation(tmp_path, monkeypatch, provider):
from loopx.quota import record_quota_monitor_poll
from loopx.status import collect_status
@@ -137,14 +151,10 @@ def invoke():
target_key="due-monitor-fixture", result_hash="commit-observation", cadence="30m",
turn_instance_id=turn, receipt_bound_todo_id=TODO_ID,
available_capabilities=["network", "external_evidence_poll"])
- if provider == "legacy":
- result = invoke()
- assert result["ok"] is False and "current User gate dependencies" in result["reason"], result
- else:
- from loopx.control_plane.coordination.local_authority import LocalCoordinationAuthorityUnavailable
- with pytest.raises(LocalCoordinationAuthorityUnavailable, match="current User gate dependencies") as rejected:
- invoke()
- assert rejected.value.payload["no_effect"]["operation_id"]
+ rejected = invoke()
+ assert rejected["ok"] is False
+ assert rejected["error_code"] == "monitor_poll_rejected"
+ assert "current User gate dependencies" in rejected["reason"]
row = list_goal_todos(registry_path=source, goal_id=GOAL_ID, todo_id=DUE_MONITOR_TODO_ID)["todo"]
assert row.get("result_hash") is None
@@ -164,13 +174,12 @@ def invoke():
assert _spend_run_count(runtime) == 0
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("business_committed", [False, True])
def test_old_pending_plan_preserves_identity_but_not_new_write_authority(tmp_path, monkeypatch, provider, business_committed):
from loopx.quota import record_quota_monitor_poll
from loopx.status import collect_status
from loopx.control_plane.quota import monitor_poll
- from loopx.control_plane.coordination.local_authority import LocalCoordinationAuthorityUnavailable
project, runtime, source, registry, gate = _scoped_fixture(tmp_path, monkeypatch, provider)
turn = "historical-auxiliary-plan-turn"
rc, guard = _run_cli(registry, runtime, "quota", "should-run", "--codex-app", "--goal-id", GOAL_ID,
@@ -206,17 +215,16 @@ def invoke():
assert recovered["ok"] is True, recovered
assert invoke()["replayed"] is True
else:
- if provider == "legacy":
- assert invoke()["ok"] is False
- else:
- with pytest.raises(LocalCoordinationAuthorityUnavailable, match="current User gate dependencies"):
- invoke()
+ rejected = invoke()
+ assert rejected["ok"] is False
+ assert rejected["error_code"] == "monitor_poll_rejected"
+ assert "current User gate dependencies" in rejected["reason"]
row = list_goal_todos(registry_path=source, goal_id=GOAL_ID, todo_id=DUE_MONITOR_TODO_ID)["todo"]
assert row.get("result_hash") is None
assert _spend_run_count(runtime) == 0
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_no_gate_auxiliary_command_retains_commit_and_replay_parity(tmp_path, monkeypatch, provider):
project, runtime, _source, registry, _gate = _scoped_fixture(tmp_path, monkeypatch, provider, with_gate=False)
turn = "auxiliary-no-gate-parity"
diff --git a/tests/control_plane/test_backup_registered_source_retention.py b/tests/control_plane/test_backup_registered_source_retention.py
index 6f46b6614d..ab16e8f98a 100644
--- a/tests/control_plane/test_backup_registered_source_retention.py
+++ b/tests/control_plane/test_backup_registered_source_retention.py
@@ -1,33 +1,17 @@
"""Whole-source backup is distinct from canonical projection or reactivation."""
import json
-import hashlib
import os
import sqlite3
-import shutil
import subprocess
import sys
import tarfile
from pathlib import Path
import pytest
-import loopx
from loopx.control_plane.effect_runtime import restart_effect_runtime
-from loopx.control_plane.coordination.local_authority_shadow_adapter import read_local_authority_shadow
from loopx.state_backup import build_state_backup_plan, execute_state_backup_plan
from tests.control_plane.canonical_authority_fixture import isolate_sqlite_runtime
-from tests.control_plane.shadow_e2e_fixture import workspace
-
-
-def extract_verified_backup(archive_path, destination):
- """Check the saved member witness independently before using copied bytes."""
- with tarfile.open(archive_path) as archive:
- archive.extractall(destination, filter="data")
- manifest = json.loads((destination / "manifest.json").read_bytes())
- for member in manifest["execution"]["file_members"]:
- data = (destination / member["archive_path"]).read_bytes()
- assert len(data) == member["size_bytes"]
- assert hashlib.sha256(data).hexdigest() == member["sha256"]
@pytest.fixture(autouse=True)
@@ -154,447 +138,3 @@ def test_backup_retains_original_registry_outside_conventional_directories(tmp_p
source = next(item for item in manifest["included"] if item["source_path"] == str(registry))
with tarfile.open(result["archive_path"], "r:gz") as archive:
assert archive.extractfile(source["archive_path"]).read() == original
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_cold_import_then_new_writes_full_backup_and_independent_history_readback(tmp_path, provider):
- """Recover inert bytes and real provider history, without activating a copy.
-
- The old source includes actual capture/rollback and released lease receipts.
- A canonical archive alone cannot witness those original files. All commands
- run against disposable Goals, including when invoked by a wheel interpreter.
- """
- fixture = workspace(tmp_path / "source", bootstrap=False)
- project_registry = fixture.state.parent / ".loopx/registry.json"
- project_registry.parent.mkdir()
- fixture.registry.rename(project_registry)
- fixture.registry = project_registry
- fixture.cli("coordination-shadow", "bootstrap", "--execute")
- todo_id = fixture.add("Original requirement before cold import")["todo_id"]
- lease = fixture.cli("task-lease", "acquire", "--todo-id", todo_id, "--owner", "agent-a",
- "--idempotency-key", "old-execution")["lease"]
- released = fixture.cli("task-lease", "release", "--todo-id", todo_id, "--owner", "agent-a",
- "--idempotency-key", "old-execution", "--expected-version", str(lease["version"]))["lease"]
- revision = fixture.cli("coordination-shadow", "inspect")["inspection"]["provider_revision"]
- fixture.cli("coordination-shadow", "rollback", "--provider-revision", revision, "--execute")
- archived_body = "完整且未被引用的历史要求 " * 100
- with fixture.state.open("a", encoding="utf-8") as stream:
- stream.write("\n## Completed Work Archive\n\n- [x] " + archived_body + "\n"
- " \n")
- registry_document = json.loads(fixture.registry.read_text())
- registry_document["goals"][0]["extension"] = {"unknown": None, "disabled": False, "number": 1}
- fixture.registry.write_text(json.dumps(registry_document))
- original_state = fixture.state.read_bytes()
- historical = {str(path.relative_to(fixture.runtime)): path.read_bytes()
- for root in (fixture.runtime / "authority-shadow", fixture.runtime / "authority-transition",
- fixture.runtime / "goals/goal-e2e/task-leases")
- for path in root.rglob("*") if path.is_file() and not path.name.endswith(".lock")}
- assert historical and any("rollback" in path for path in historical)
-
- def command(*args, runtime=fixture.runtime, registry=fixture.registry, success=True):
- child = subprocess.run([sys.executable, "-c",
- "import loopx,runpy,sys; print(loopx.__file__,file=sys.stderr); runpy.run_module('loopx.cli',run_name='__main__')",
- "--registry", str(registry), "--runtime-root", str(runtime), "--format", "json", *map(str, args)],
- cwd=tmp_path, env={**os.environ, "LOOPX_USAGE_PING": "0"},
- capture_output=True, text=True, timeout=90)
- assert str(Path(loopx.__file__).resolve()) in child.stderr
- assert (child.returncode == 0) is success, child.stdout + child.stderr
- return json.loads(child.stdout)
-
- def backup(name):
- return command("backup-state", "--project", fixture.state.parent, "--output-dir", tmp_path / "backups",
- "--backup-id", name, "--current-project-only", "--no-skills", "--no-automations", "--execute")
-
- def authority(root):
- # Fresh selected provider, no registry adoption or Markdown projection.
- module = Path(loopx.__file__).parent / "control_plane/coordination/local_authority_provider.ts"
- script = (f"import {{openLocalAuthorityStoreHandle}} from {json.dumps(module.as_uri())};"
- f"const {{store}}=await openLocalAuthorityStoreHandle({json.dumps(str(root))},'goal-e2e');"
- "console.log(JSON.stringify({head:await store.loadAuthority(), history:await store.scanCommitted(null,1000)}));")
- result = subprocess.run(["node", "--no-warnings", "--experimental-strip-types", "--input-type=module", "-e", script],
- cwd=tmp_path, capture_output=True, text=True, check=True, timeout=45)
- return json.loads(result.stdout)
-
- saved = backup("before-import")
- prepared = fixture.cli("coordination-shadow", "prepare-import", "--operation-id", "cold-original",
- "--backup-manifest", saved["manifest_path"], "--provider", provider,
- "--target-handoff-mode", "hard_lease")["cold_import"]
- assert prepared["status"] == "prepared", prepared
- applied = fixture.cli("coordination-shadow", "apply-import", "--operation-id", "cold-original",
- "--plan-sha256", prepared["plan_sha256"], "--writers-stopped", "--execute")["cold_import"]
- assert applied["status"] == "applied" and not applied["execution_authority_granted"]
- later = fixture.cli("todo", "add", "--role", "agent", "--text", "New canonical data after import",
- "--operation-id", "after-import", "--claimed-by", "agent-a")
- assert later["ok"] is True
- observed = authority(fixture.runtime)
- expected = {row["todo_id"]: row for row in observed["head"]["head"]["todos"]}
- assert {todo_id, "todo_unreferenced", later["todo_id"]} <= expected.keys()
- assert expected["todo_unreferenced"]["text"] == archived_body.strip()
- assert expected["todo_unreferenced"]["evidence"] == "original"
- assert fixture.cli("task-lease", "inspect", "--todo-id", todo_id)["lease"] == released
-
- authority_archive = tmp_path / "canonical.ndjson"
- command("authority-archive", "export", "--goal-id", fixture.goal, "--archive", authority_archive)
- verified = command("authority-archive", "verify", "--archive", authority_archive)["archive"]
- assert int(verified["commits"]) >= 2
- post_import_state = fixture.state.read_bytes()
- saved = backup("after-import")
- assert saved["ok"] is True
- copy = tmp_path / "independent-empty-recovery"
- assert not copy.exists()
- extract_verified_backup(saved["archive_path"], copy)
- routes = {row["key"]: row["archive_path"] for row in saved["included"]}
- assert (copy / routes["registry_active_state:goal-e2e"]).read_bytes() == post_import_state
- assert (copy / routes["configuration_source_registry"]).read_bytes() == fixture.registry.read_bytes()
- configuration = json.loads((copy / "configuration-backup.json").read_bytes())
- assert configuration["data"]["goals"][0]["goal_configuration"]["extension"] == registry_document["goals"][0]["extension"]
- for relative, data in historical.items():
- assert (copy / "runtime-root" / relative).read_bytes() == data
- # The original source and its later projection have separate saved copies.
- with tarfile.open(Path(saved["archive_path"]).parent / "loopx-state-before-import.tar.gz") as archive:
- original_manifest = json.load(archive.extractfile("manifest.json"))
- original_route = next(row["archive_path"] for row in original_manifest["included"]
- if row["key"] == "registry_active_state:goal-e2e")
- assert archive.extractfile(original_route).read() == original_state
-
- # Remove access to every source route: the recovery cannot accidentally read
- # the live registry, original store or old absolute paths in historical data.
- fixture.state.parent.rename(tmp_path / "unavailable-original")
- restored_runtime = copy / "runtime-root"
- recovered_registry = copy / routes["configuration_source_registry"]
- audited = command("authority-archive", "audit", "--goal-id", fixture.goal, "--archive", authority_archive,
- "--archive-sha256", verified["archive_sha256"], runtime=restored_runtime, registry=recovered_registry)
- assert audited["audit"]["status"] == "matched" and audited["audit"]["scope"] == "exact"
- assert audited["audit"]["compared_commits"] == verified["commits"]
- recovered = authority(restored_runtime)
- assert recovered == observed
- assert len(recovered["history"]["transactions"]) == int(verified["commits"])
- assert next(row for row in recovered["head"]["head"]["leases"] if row["todo_id"] == todo_id) == released
- assert recovered["history"]["transactions"][0]["operation_id"] == applied["operation_id"]
- # This is isolated data recovery, not an alternate live writer or Host grant.
- assert not fixture.state.parent.exists()
- selected_directory = restored_runtime / "authority" / f"{provider}-v0"
- selected_directory.rename(tmp_path / "unavailable-selected-provider")
- rejected = command("authority-archive", "audit", "--goal-id", fixture.goal, "--archive", authority_archive,
- "--archive-sha256", verified["archive_sha256"], runtime=restored_runtime,
- registry=recovered_registry, success=False)
- assert rejected["status"] == "failed" and rejected["authority_changed"] is False
- assert not selected_directory.exists()
- if provider == "sqlite":
- assert not (restored_runtime / "authority/file-v0").exists()
-
-
-@pytest.mark.parametrize(
- "partition,policy,window",
- [
- ("todos", policy, window)
- for policy in ("soft_claim", "hard_lease")
- for window in (
- "before_replace",
- "before_marker",
- "before_commit",
- "after_commit",
- )
- ]
- + [
- ("leases", "hard_lease", window) for window in ("before_commit", "after_commit")
- ],
-)
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_restored_source_outbox_resumes_once_before_cold_import(
- tmp_path, partition, policy, window, provider
-):
- """Restore only the owned runtime at its original binding before import.
-
- Original source paths remain; this does not reactivate a whole Goal.
- """
- fixture = workspace(tmp_path / "project", bootstrap=False)
- registry = fixture.state.parent / ".loopx/registry.json"
- registry.parent.mkdir()
- fixture.registry.rename(registry)
- fixture.registry = registry
- fixture.state.write_text(
- fixture.state.read_text().replace(
- "handoff_mode: hard_lease", f"handoff_mode: {policy}"
- )
- )
- fixture.cli("coordination-shadow", "bootstrap", "--execute")
- first = fixture.add("Keep the original requirement")["todo_id"]
- source_before = fixture.state.read_bytes()
- lease_identity = (
- "--owner",
- "agent-a",
- "--idempotency-key",
- "original-source-lease",
- )
- interrupted = (
- (
- "task-lease",
- "acquire",
- "--todo-id",
- first,
- *lease_identity,
- "--ttl-seconds",
- "3600",
- )
- if partition == "leases"
- else (
- "todo",
- "add",
- "--role",
- "agent",
- "--text",
- "Retain the interrupted original operation",
- )
- )
- fixture.crash(window, *interrupted)
- source_after = fixture.state.read_bytes()
- source_unchanged = partition == "leases" or window == "before_replace"
- assert (source_after == source_before) == source_unchanged
- lease_path = (
- fixture.runtime / "goals" / fixture.goal / "task-leases" / (first + ".json")
- )
- lease_bytes = lease_path.read_bytes() if partition == "leases" else None
- original_lease = json.loads(lease_bytes) if lease_bytes is not None else None
- outbox = fixture.runtime / "authority-shadow/outbox" / fixture.goal / partition
- prepared = next(outbox.glob("*.prepared.json"))
- operation = json.loads(prepared.read_bytes())["entry_id"]
- original = {
- str(path.relative_to(fixture.runtime)): path.read_bytes()
- for path in fixture.runtime.rglob("*")
- if path.is_file() and not path.name.endswith(".lock")
- }
- registry_bytes = fixture.registry.read_bytes()
- inventory = fixture.cli("coordination-shadow", "inspect-source")["source_inventory"]
- assert (
- inventory["outbox_reconciliation_verified"] is False
- and inventory["writer_stop_verified"] is False
- )
- assert inventory["capture"]["management_state"]["status"] == "active"
- assert original == {
- str(path.relative_to(fixture.runtime)): path.read_bytes()
- for path in fixture.runtime.rglob("*")
- if path.is_file() and not path.name.endswith(".lock")
- }
-
- def backup(name):
- child = subprocess.run(
- [
- *fixture.command,
- "--registry",
- str(fixture.registry),
- "--runtime-root",
- str(fixture.runtime),
- "--format",
- "json",
- "backup-state",
- "--project",
- str(fixture.state.parent),
- "--output-dir",
- str(tmp_path / "saved"),
- "--backup-id",
- name,
- "--current-project-only",
- "--no-skills",
- "--no-automations",
- "--execute",
- ],
- cwd=fixture.cwd,
- capture_output=True,
- text=True,
- timeout=60,
- )
- assert child.returncode == 0, child.stdout + child.stderr
- return json.loads(child.stdout)
-
- saved = backup("interrupted-source")
- copy = tmp_path / "independent-copy"
- extract_verified_backup(saved["archive_path"], copy)
- for relative, data in original.items():
- assert (copy / "runtime-root" / relative).read_bytes() == data
- routes = {row["key"]: row["archive_path"] for row in saved["included"]}
- assert (
- copy / routes["registry_active_state:goal-e2e"]
- ).read_bytes() == source_after
- assert (
- copy / routes["configuration_source_registry"]
- ).read_bytes() == registry_bytes
-
- # The killed writer is owned by this fixture. Stop its remaining transport
- # before replacing only its disposable runtime at the original binding path.
- restart_effect_runtime()
- fixture.runtime.rename(tmp_path / "quarantined-original-runtime")
- shutil.copytree(copy / "runtime-root", fixture.runtime)
- assert (
- fixture.state.read_bytes() == source_after
- and fixture.registry.read_bytes() == registry_bytes
- )
- resumed = fixture.drain()
- assert resumed["ok"] is True and resumed["outcome"] == "drained", resumed
- assert resumed["pending_after"] == 0 and resumed["prepared_only_after"] == 0
- assert resumed["no_op"] == (1 if window == "before_replace" else 0)
- assert not list(outbox.glob("*.prepared.json")) and not list(
- outbox.glob("*.committed.json")
- )
- proof = read_local_authority_shadow(
- runtime_root=fixture.runtime, goal_id=fixture.goal, scan_limit=100
- )["proof"]
- transactions = proof["transactions"]
- # Bootstrap, original requirement and exactly one interrupted position.
- assert len(transactions) == 3, transactions
- assert transactions[-1]["receipts"][0]["entry_id"] == operation
- assert transactions[-1]["receipts"][0]["no_op"] == (window == "before_replace")
- head = transactions[-1]["projection"]
- todos = {row["todo_id"]: row for row in head["todos"]}
- assert first in todos and len(todos) == (1 if source_unchanged else 2)
- if partition == "leases":
- assert head["leases"] == [original_lease]
- assert lease_path.read_bytes() == lease_bytes
- recovered_lease = fixture.cli(
- "task-lease",
- "acquire",
- "--todo-id",
- first,
- *lease_identity,
- "--ttl-seconds",
- "3600",
- )
- assert (
- recovered_lease["idempotent"] and recovered_lease["lease"] == original_lease
- )
- assert lease_path.read_bytes() == lease_bytes
- assert fixture.state.read_bytes() == source_after
- revision = fixture.cli("coordination-shadow", "inspect")["inspection"][
- "provider_revision"
- ]
- again = fixture.drain()
- assert again["ok"] and again["outcome"] == "nothing_pending"
- assert (
- read_local_authority_shadow(
- runtime_root=fixture.runtime, goal_id=fixture.goal, scan_limit=100
- )["proof"]["transactions"]
- == transactions
- )
-
- rollback = fixture.cli(
- "coordination-shadow", "rollback", "--provider-revision", revision, "--execute"
- )["rollback"]
- assert rollback["status"] == "applied"
- if partition == "leases":
- # Recovering an outbox neither releases nor renews its execution grant.
- # A separate native owner settlement is required before cold import.
- unsettled = backup("unsettled-lease")
- refused = fixture.cli(
- "coordination-shadow",
- "prepare-import",
- "--operation-id",
- "unsettled-import",
- "--backup-manifest",
- unsettled["manifest_path"],
- "--provider",
- provider,
- "--target-handoff-mode",
- policy,
- success=False,
- )["cold_import"]
- assert refused["reason_code"] == "cold_import_lease_requires_settlement"
- assert not list(fixture.runtime.rglob("writer-fence.json"))
- assert lease_path.read_bytes() == lease_bytes
- released = fixture.cli(
- "task-lease",
- "release",
- "--todo-id",
- first,
- *lease_identity,
- "--expected-version",
- str(original_lease["version"]),
- )["lease"]
- assert released["status"] == "released"
- settled = backup("settled-source")
- plan = fixture.cli(
- "coordination-shadow",
- "prepare-import",
- "--operation-id",
- "original-reviewed-import",
- "--backup-manifest",
- settled["manifest_path"],
- "--provider",
- provider,
- "--target-handoff-mode",
- policy,
- )["cold_import"]
- assert (
- plan["status"] == "prepared" and plan["complete_goal_backup_verified"] is False
- )
- digest = plan["plan_sha256"]
- imported = fixture.cli(
- "coordination-shadow",
- "apply-import",
- "--operation-id",
- "original-reviewed-import",
- "--plan-sha256",
- digest,
- "--writers-stopped",
- "--execute",
- )["cold_import"]
- assert (
- imported["status"] == "applied"
- and imported["execution_authority_granted"] is False
- )
- if partition == "leases":
- assert (
- fixture.cli("task-lease", "inspect", "--todo-id", first)["lease"]
- == released
- )
- fresh_lease = fixture.cli(
- "task-lease",
- "acquire",
- "--todo-id",
- first,
- "--owner",
- "agent-a",
- "--idempotency-key",
- "fresh-canonical-lease",
- "--expected-version",
- str(released["version"]),
- "--ttl-seconds",
- "3600",
- )["lease"]
- assert fresh_lease["version"] == released["version"] + 1
- assert fresh_lease["lease_epoch"] == released["lease_epoch"] + 1
- created = fixture.cli(
- "todo",
- "add",
- "--role",
- "agent",
- "--text",
- "New canonical data after recovered source",
- "--operation-id",
- "later-new-data",
- "--claimed-by",
- "agent-a",
- )
- assert created["ok"]
- before = fixture.cli("todo", "list")["todos"]
- recovered = fixture.cli(
- "coordination-shadow",
- "recover-import",
- "--operation-id",
- "original-reviewed-import",
- "--plan-sha256",
- digest,
- "--execute",
- )["cold_import"]
- assert (
- recovered["status"] == "replayed"
- and recovered["execution_authority_granted"] is False
- )
- if partition == "leases":
- assert (
- fixture.cli("task-lease", "inspect", "--todo-id", first)["lease"]
- == fresh_lease
- )
- assert fixture.cli("todo", "list")["todos"] == before
- assert first in {row["todo_id"] for row in before} and created["todo_id"] in {
- row["todo_id"] for row in before
- }
diff --git a/tests/control_plane/test_canonical_authority_source.py b/tests/control_plane/test_canonical_authority_source.py
index 59c75d862f..a9b91dfd65 100644
--- a/tests/control_plane/test_canonical_authority_source.py
+++ b/tests/control_plane/test_canonical_authority_source.py
@@ -10,6 +10,7 @@
import pytest
+from canonical_authority_fixture import isolate_sqlite_runtime
from loopx.todos import add_goal_todo
from loopx.chat_action_store import ChatActionStore
from loopx.chat_actions import ChatActionService
@@ -19,8 +20,15 @@
@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("entrypoint", ["cli", "chat"])
-def test_completion_rejects_registration_revoked_by_real_validation(tmp_path: Path, provider: str, entrypoint: str):
- registry, _state = fixture(tmp_path, True, provider)
+def test_completion_rejects_registration_revoked_by_real_validation(
+ tmp_path: Path,
+ monkeypatch: pytest.MonkeyPatch,
+ provider: str,
+ entrypoint: str,
+) -> None:
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, _state = fixture(tmp_path, provider)
marker = tmp_path / "validation-ran"
# The validation is real and succeeds. It revokes this synthetic actor
# between admission and commit; no mock supplies the postcondition.
@@ -68,10 +76,11 @@ def test_completion_rejects_registration_revoked_by_real_validation(tmp_path: Pa
headers={"Content-Type": "application/json"})
response = connection.getresponse()
payload = json.loads(response.read())
- assert response.status == 400, payload
- assert "authority registration changed" in payload["error"]
+ assert response.status == 409, payload
+ assert payload["error_code"] == "action_stale"
+ assert payload["write_attempted"] is False
stored = store.load(proposal["proposal_id"])
- assert stored["status"] == "failed"
+ assert stored["status"] == "stale"
assert not stored.get("receipt"), "Chat must not report canonical success"
finally:
connection.close()
diff --git a/tests/control_plane/test_canonical_claim_transfer.py b/tests/control_plane/test_canonical_claim_transfer.py
index 0d830beb13..5a87bda83a 100644
--- a/tests/control_plane/test_canonical_claim_transfer.py
+++ b/tests/control_plane/test_canonical_claim_transfer.py
@@ -8,8 +8,9 @@
import pytest
from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
+from loopx.control_plane.coordination.local_authority import CanonicalAuthorityRequired
from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
-from loopx.control_plane.work_items.task_lease import TaskLeaseError, transfer_task_lease
+from loopx.control_plane.work_items.task_lease import transfer_task_lease
REPO = Path(__file__).resolve().parents[2]
@@ -98,10 +99,24 @@ def cli(command, *args, expected_exit=0):
def test_claim_transfer_never_emulates_a_legacy_two_write_handover(tmp_path):
registry = tmp_path / "registry.json"
- registry.write_text(json.dumps({"goals": []}))
- with pytest.raises(TaskLeaseError, match="requires canonical authority") as caught:
- transfer_task_lease(registry_path=registry, runtime_root=tmp_path / "runtime", goal_id="example",
+ runtime = tmp_path / "runtime"
+ registry.write_text(json.dumps({
+ "common_runtime_root": str(runtime),
+ "goals": [{
+ "id": "example",
+ "repo": str(tmp_path),
+ "state_file": "state.md",
+ "coordination": {
+ "registered_agents": ["agent-a", "agent-b"],
+ "agent_model": "peer_v1",
+ },
+ }],
+ }))
+ with pytest.raises(CanonicalAuthorityRequired, match="Canonical authority is required") as caught:
+ transfer_task_lease(registry_path=registry, runtime_root=runtime, goal_id="example",
todo_id="todo_example", owner="agent-a", idempotency_key="source-a", expected_version=1,
new_owner="agent-b", new_idempotency_key="receiver-b", transfer_claim=True)
- assert caught.value.code == "claim_transfer_requires_canonical_authority"
- assert not (tmp_path / "runtime").exists()
+ assert caught.value.code == "canonical_authority_required"
+ assert caught.value.payload["operation"] == "task_lease_transfer"
+ assert caught.value.payload["write_admitted"] is False
+ assert not (runtime / "goals/example/task-leases/todo_example.json").exists()
diff --git a/tests/control_plane/test_canonical_goal_channel_coordination.py b/tests/control_plane/test_canonical_goal_channel_coordination.py
index 6de461b1d7..7359432f40 100644
--- a/tests/control_plane/test_canonical_goal_channel_coordination.py
+++ b/tests/control_plane/test_canonical_goal_channel_coordination.py
@@ -100,3 +100,73 @@ def channels(value):
assert all(item['active_leases'] == [] for item in found)
assert all(item['coordination_observation']['source_authority'] == provider + '_v0' for item in found)
assert state.read_bytes() == before
+
+
+@pytest.mark.parametrize('provider', ['file', 'sqlite'])
+def test_real_cli_reports_native_lease_and_recovers_provider_without_legacy_fallback(tmp_path, provider):
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+ from loopx.control_plane.testing.canary_harness import import_fixture_authority, run_json_cli, run_json_cli_result
+ from test_goal_channel_hard_lease_visibility import _write_status_workspace
+
+ registry, repo, runtime = _write_status_workspace(tmp_path)
+ state = repo / 'ACTIVE_GOAL_STATE.md'
+ import_fixture_authority(registry, GOAL_ID, provider=provider)
+
+ def cli(*args):
+ return run_json_cli(*args, registry_path=registry, runtime_root=runtime)
+
+ cli('handoff-mode', 'set', '--goal-id', GOAL_ID, '--mode', 'hard_lease',
+ '--operation-id', 'channel-lease-fixture-policy')
+ task = cli('todo', 'add', '--goal-id', GOAL_ID, '--role', 'agent',
+ '--text', 'Observe the current canonical lease.', '--task-class', 'advancement_task',
+ '--claimed-by', 'agent-a')
+ lease = cli('task-lease', 'acquire', '--goal-id', GOAL_ID, '--todo-id', task['todo_id'],
+ '--owner', 'agent-a', '--idempotency-key', 'channel-lease-fixture', '--ttl-seconds', '600')
+ assert lease['acquired'] is True
+ original = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID, include_leases=True)
+ source, registry_bytes = state.read_bytes(), registry.read_bytes()
+ _write_active_lease(runtime, owner='stale-agent', todo_id=task['todo_id'])
+
+ def channel():
+ status = cli('status', '--goal-id', GOAL_ID)
+ items = status['attention_queue']['items']
+ item = next(row for row in items if row['goal_id'] == GOAL_ID)
+ return item['goal_channel_projection']
+
+ def assert_current(view):
+ assert view['coordination_observation']['source_authority'] == provider + '_v0'
+ assert view['coordination_authority'] == {'state': 'promoted'}
+ hard = [row for row in view['active_leases'] if row['status'] == 'hard_lease']
+ assert len(hard) == 1, view
+ assert hard[0]['todo_id'] == task['todo_id'] and hard[0]['owner_agent'] == 'agent-a', hard
+ assert hard[0]['lease_version'] == lease['lease']['version']
+ assert all(row.get('owner_agent') != 'stale-agent' for row in view['active_leases'])
+ assert view['truth_contract']['write_authority'] == 'none'
+
+ assert_current(channel())
+ state.unlink()
+ assert_current(channel())
+ assert not state.exists(), 'status must not reconstruct the display'
+ state.write_bytes(source)
+
+ authority, quarantine = runtime / 'authority', runtime / 'provider-quarantine'
+ authority.rename(quarantine)
+ try:
+ unavailable = build_goal_channel_projection(
+ goal_id=GOAL_ID, status_item=_status_item(claimed_by='stale-agent'), runtime_root=runtime,
+ )
+ assert unavailable['coordination_observation']['status'] == 'unavailable'
+ assert unavailable['coordination_authority'] == {'state': 'unavailable'}
+ assert unavailable['active_leases'] == []
+ assert unavailable['truth_contract']['write_authority'] == 'none'
+ code, rejected = run_json_cli_result('status', '--goal-id', GOAL_ID,
+ registry_path=registry, runtime_root=runtime)
+ assert code == 1 and rejected['ok'] is False, rejected
+ assert rejected['attention_queue']['items'][0]['status'] == 'status_collection_failed'
+ assert 'canonical Todo authority is unavailable' in rejected['error']
+ assert 'stale-agent' not in json.dumps(rejected)
+ finally:
+ quarantine.rename(authority)
+ assert_current(channel())
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID, include_leases=True) == original
+ assert state.read_bytes() == source and registry.read_bytes() == registry_bytes
diff --git a/tests/control_plane/test_canonical_handoff_invariants.py b/tests/control_plane/test_canonical_handoff_invariants.py
new file mode 100644
index 0000000000..0f1d9602f1
--- /dev/null
+++ b/tests/control_plane/test_canonical_handoff_invariants.py
@@ -0,0 +1,842 @@
+"""Canonical handoff invariants retained from the retired writer proposal.
+
+Both real local providers must enforce one claim/lease owner. Ownership transfer
+is atomic; metadata and duplicate create cannot perform the old two-write handoff.
+Current transactions use real File/SQLite; no retired producer is restored.
+"""
+
+from __future__ import annotations
+
+import json
+from datetime import timedelta
+from pathlib import Path
+from typing import Any
+
+import pytest
+from loopx.control_plane.coordination.local_authority import (
+ LocalCoordinationAuthorityRejection,
+ LocalCoordinationAuthorityUnavailable,
+ read_canonical_todos_if_promoted,
+)
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
+from loopx.control_plane.todos.handoff_mode import (
+ HandoffModeError,
+ set_goal_handoff_mode,
+)
+from loopx.control_plane.work_items import task_lease
+from loopx.control_plane.work_items.task_lease import (
+ TaskLeaseError,
+ acquire_task_lease,
+ inspect_task_lease,
+ release_task_lease,
+ renew_task_lease,
+ task_lease_path,
+ transfer_task_lease,
+)
+from loopx.todos import (
+ add_goal_todo,
+ list_goal_todos,
+ complete_goal_todo,
+ supersede_goal_todo,
+ update_goal_todo,
+)
+from tests.control_plane.canonical_authority_fixture import (
+ initialize_canonical_authority,
+ isolate_sqlite_runtime,
+)
+
+GOAL_ID = "handoff-mode-gate"
+AGENT_A, AGENT_B, ORCHESTRATOR = "agent-a", "agent-b", "agent-orchestrator"
+DECISION_SCOPE = "direction:action:approve_handoff"
+
+
+
+def _write_workspace(
+ tmp_path: Path,
+ *,
+ handoff_mode: str | None = None,
+ lifecycle_authority: list[dict[str, Any]] | None = None,
+) -> tuple[Path, Path]:
+ repo = tmp_path / "repo"
+ repo.mkdir(exist_ok=True)
+ state = repo / "ACTIVE_GOAL_STATE.md"
+ front_matter = [
+ "---",
+ f"goal_id: {GOAL_ID}",
+ "updated_at: 2026-08-01T00:00:00+00:00",
+ ]
+ if handoff_mode is not None:
+ front_matter.append(f"handoff_mode: {handoff_mode}")
+ front_matter.append("---")
+ state.write_text(
+ "\n".join([*front_matter, "", "## Agent Todo", ""]) + "\n",
+ encoding="utf-8",
+ )
+ coordination: dict[str, Any] = {
+ "agent_model": "peer_v1",
+ "registered_agents": [AGENT_A, AGENT_B, ORCHESTRATOR],
+ }
+ if lifecycle_authority is not None:
+ coordination["todo_lifecycle_authority"] = lifecycle_authority
+ registry = tmp_path / "registry.global.json"
+ registry.write_text(
+ json.dumps(
+ {
+ "common_runtime_root": str(tmp_path / "runtime"),
+ "goals": [
+ {
+ "id": GOAL_ID,
+ "domain": "harness_self_improvement",
+ "status": "active",
+ "repo": str(repo),
+ "state_file": state.name,
+ "adapter": {"kind": "harness_self_improvement"},
+ "coordination": coordination,
+ }
+ ],
+ }
+ ),
+ encoding="utf-8",
+ )
+ return registry, state
+
+
+def _set_frontmatter_mode(state: Path, mode: str) -> None:
+ """Hand-edit the front-matter mode (simulates the out-of-contract path)."""
+
+ lines = state.read_text(encoding="utf-8").splitlines()
+ close = next(i for i in range(1, len(lines)) if lines[i].strip() == "---")
+ for index in range(1, close):
+ if lines[index].split(":", 1)[0].strip() == "handoff_mode":
+ lines[index] = f"handoff_mode: {mode}"
+ break
+ else:
+ lines.insert(close, f"handoff_mode: {mode}")
+ state.write_text("\n".join(lines) + "\n", encoding="utf-8")
+
+
+def _add_todo(registry: Path, *, claimed_by: str | None = None) -> dict[str, Any]:
+ return add_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ role="agent",
+ text="Deliver one bounded control-plane change.",
+ task_class="advancement_task",
+ claimed_by=claimed_by,
+ )
+
+
+def _agent_todo(state: Path, todo_id: str) -> dict[str, Any]:
+ registry = state.parent.parent / "registry.global.json"
+ return list_goal_todos(registry_path=registry, goal_id=GOAL_ID, todo_id=todo_id)[
+ "todo"
+ ]
+
+
+def _add_exact_user_gate_pair(
+ registry: Path,
+) -> tuple[dict[str, Any], dict[str, Any]]:
+ target = add_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ role="agent",
+ text="Continue after the exact owner decision.",
+ task_class="advancement_task",
+ claimed_by=AGENT_A,
+ required_decision_scopes=[DECISION_SCOPE],
+ )
+ gate = add_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ role="user",
+ text="Approve the exact handoff decision.",
+ task_class="user_gate",
+ blocks_agent=AGENT_A,
+ decision_scope=DECISION_SCOPE,
+ unblocks_todo_id=target["todo_id"],
+ )
+ return target, gate
+
+
+def _acquire(
+ registry: Path,
+ tmp_path: Path,
+ todo_id: str,
+ *,
+ owner: str,
+ key: str = "turn-lease-1",
+ ttl_seconds: int = 600,
+) -> dict[str, Any]:
+ return acquire_task_lease(
+ registry_path=registry,
+ runtime_root=tmp_path / "runtime",
+ goal_id=GOAL_ID,
+ todo_id=todo_id,
+ owner=owner,
+ idempotency_key=key,
+ ttl_seconds=ttl_seconds,
+ )
+
+
+def _claim(registry: Path, todo_id: str, agent: str, **kwargs: Any) -> dict[str, Any]:
+ return update_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ todo_id=todo_id,
+ claimed_by=agent,
+ agent_id=agent,
+ claim_only=True,
+ **kwargs,
+ )
+
+
+def _force_bootstrap(
+ registry: Path, state: Path, tmp_path: Path, **overrides: Any
+) -> dict[str, Any]:
+ from loopx.bootstrap import bootstrap_project
+
+ options: dict[str, Any] = dict(
+ project=state.parent,
+ registry_path=registry,
+ runtime_root=tmp_path / "runtime",
+ goal_id=GOAL_ID,
+ objective="Rebuild the active state without changing the handoff contract.",
+ domain="harness_self_improvement",
+ role="owner",
+ parent_goal_id=None,
+ state_file=state,
+ goal_doc=None,
+ adapter_kind="harness_self_improvement",
+ adapter_status="active",
+ next_probe=None,
+ spawn_allowed=False,
+ max_children=0,
+ allowed_domains=None,
+ write_scope=None,
+ preserve_todos=False,
+ force=True,
+ dry_run=False,
+ sync_global=False,
+ )
+ options.update(overrides)
+ return bootstrap_project(**options)
+
+
+
+
+
+
+
+
+
+
+
+
+@pytest.fixture(params=["file", "sqlite"])
+def canonical_workspace(request, tmp_path, monkeypatch):
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+
+ def create(root, *, handoff_mode="soft_claim", retained=None, **kwargs):
+ registry, state = _write_workspace(root, handoff_mode=handoff_mode, **kwargs)
+ todos, leases = [], []
+ if retained:
+ # Synthetic imported facts, committed once before any current call.
+ # This fixture is consumer-policy evidence, not an import journey.
+ todos = [
+ {
+ "schema_version": "todo_item_v0",
+ "todo_id": "todo_retained",
+ "role": "agent",
+ "status": "open",
+ "done": False,
+ "text": "Retained work",
+ "archive_state": "active",
+ "source_section": "Agent Todo",
+ "index": 1,
+ "task_class": "advancement_task",
+ **retained.get("todo", {}),
+ }
+ ]
+ now = task_lease.now_utc()
+ start = now - timedelta(hours=2) if retained.get("expired") else now
+ leases = [
+ {
+ "schema_version": "task_lease_v0",
+ "status": "active",
+ "goal_id": GOAL_ID,
+ "todo_id": "todo_retained",
+ "owner": AGENT_A,
+ "idempotency_key": "turn-lease-1",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 1,
+ "lease_epoch": 1,
+ "acquired_at": task_lease.isoformat(start),
+ "updated_at": task_lease.isoformat(start),
+ "expires_at": task_lease.isoformat(start + timedelta(seconds=600)),
+ }
+ ]
+ projection = build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, todos=todos, leases=leases, handoff_mode=handoff_mode
+ )
+ initialize_canonical_authority(
+ root / "runtime",
+ GOAL_ID,
+ projection,
+ state_path=state,
+ provider=request.param,
+ )
+ assert _snapshot(registry)["source_authority"] == request.param + "_v0"
+ return registry, state
+
+ return create
+
+
+def _snapshot(registry):
+ result = read_canonical_todos_if_promoted(
+ runtime_root=registry.parent / "runtime", goal_id=GOAL_ID, include_leases=True
+ )
+ assert result["decision_read_from_provider"] is True
+ assert result["legacy_fallback_used"] is False
+ return result
+
+
+def _lease(registry, todo_id):
+ result = inspect_task_lease(
+ registry_path=registry,
+ runtime_root=registry.parent / "runtime",
+ goal_id=GOAL_ID,
+ todo_id=todo_id,
+ )
+ assert result["legacy_fallback_used"] is False
+ return result["lease"]
+
+
+def _terminal(registry, todo_id, operation="complete", **kwargs):
+ options = {
+ "registry_path": registry,
+ "goal_id": GOAL_ID,
+ "todo_id": todo_id,
+ "agent_id": AGENT_A,
+ **kwargs,
+ }
+ if operation == "complete":
+ return complete_goal_todo(**options, no_followup=True)
+ return supersede_goal_todo(**options, reason="Retire obsolete work")
+
+
+def _authority_unchanged(registry, before):
+ after = _snapshot(registry)
+ for field in ("provider_revision", "todos", "leases", "handoff_mode"):
+ assert after[field] == before[field], field
+
+
+def _unchanged(registry, state, before, display):
+ _authority_unchanged(registry, before)
+ assert state.read_bytes() == display
+
+
+@pytest.mark.parametrize("operation", ["claim", "replay"])
+def test_display_mode_cannot_override_canonical_policy(
+ tmp_path, canonical_workspace, operation
+):
+ registry, state = canonical_workspace(tmp_path)
+ todo = _add_todo(registry, claimed_by=AGENT_A)
+ if operation == "replay":
+ original = _terminal(
+ registry, todo["todo_id"], completion_turn_key="display-replay"
+ )
+ _set_frontmatter_mode(state, "banana")
+ if operation == "claim":
+ assert _claim(registry, todo["todo_id"], AGENT_A)["ok"]
+ else:
+ before, display = _snapshot(registry), state.read_bytes()
+ replay = _terminal(
+ registry, todo["todo_id"], completion_turn_key="display-replay"
+ )
+ assert replay["idempotent_replay"] and not replay["changed"]
+ assert replay["original_receipt"] == original["original_receipt"]
+ _unchanged(registry, state, before, display)
+ assert _snapshot(registry)["handoff_mode"] == "soft_claim"
+
+
+@pytest.mark.parametrize("verb", ["acquire", "renew", "transfer"])
+def test_soft_claim_forbids_new_lease_effects_even_with_retained_lineage(
+ tmp_path, canonical_workspace, verb
+):
+ registry, state = canonical_workspace(
+ tmp_path, retained={"todo": {"claimed_by": AGENT_A}}
+ )
+ before, display = _snapshot(registry), state.read_bytes()
+ with pytest.raises(TaskLeaseError) as error:
+ if verb == "acquire":
+ _acquire(registry, tmp_path, "todo_retained", owner=AGENT_A)
+ elif verb == "renew":
+ renew_task_lease(
+ registry_path=registry,
+ runtime_root=tmp_path / "runtime",
+ goal_id=GOAL_ID,
+ todo_id="todo_retained",
+ owner=AGENT_A,
+ idempotency_key="turn-lease-1",
+ expected_version=1,
+ )
+ else:
+ transfer_task_lease(
+ registry_path=registry,
+ runtime_root=tmp_path / "runtime",
+ goal_id=GOAL_ID,
+ todo_id="todo_retained",
+ owner=AGENT_A,
+ idempotency_key="turn-lease-1",
+ expected_version=1,
+ new_owner=AGENT_B,
+ new_idempotency_key="peer",
+ transfer_claim=True,
+ )
+ assert error.value.code == (
+ "claim_transfer_requires_hard_lease"
+ if verb == "transfer"
+ else "handoff_mode_forbids_lease"
+ )
+ _unchanged(registry, state, before, display)
+
+
+def test_soft_claim_retained_lease_can_be_inspected_and_released(
+ tmp_path, canonical_workspace
+):
+ registry, _ = canonical_workspace(
+ tmp_path, retained={"todo": {"claimed_by": AGENT_A}}
+ )
+ assert _lease(registry, "todo_retained")["status"] == "active"
+ result = release_task_lease(
+ registry_path=registry,
+ runtime_root=tmp_path / "runtime",
+ goal_id=GOAL_ID,
+ todo_id="todo_retained",
+ owner=AGENT_A,
+ idempotency_key="turn-lease-1",
+ expected_version=1,
+ )
+ assert result["released"]
+ assert _lease(registry, "todo_retained")["status"] == "released"
+
+
+@pytest.mark.parametrize("operation", ["complete", "supersede"])
+def test_soft_claim_claim_and_terminal_need_no_execution_lease(
+ tmp_path, canonical_workspace, operation
+):
+ registry, state = canonical_workspace(tmp_path)
+ todo = _add_todo(registry)
+ assert _claim(registry, todo["todo_id"], AGENT_A)["ok"]
+ result = _terminal(registry, todo["todo_id"], operation)
+ assert result["ok"] and result["task_lease_fence"]["lease_fence"] == "not_required"
+ assert _agent_todo(state, todo["todo_id"])["status"] == "done"
+ assert _snapshot(registry)["leases"] == []
+
+
+@pytest.mark.parametrize("mode", ["soft_claim", "hard_lease"])
+def test_terminal_replay_preserves_receipt_and_does_not_rewind_policy(
+ tmp_path, canonical_workspace, mode
+):
+ registry, state = canonical_workspace(tmp_path, handoff_mode=mode)
+ todo = _add_todo(registry, claimed_by=AGENT_A)
+ proof = {}
+ if mode == "hard_lease":
+ _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
+ proof = dict(
+ task_lease_idempotency_key="turn-lease-1", task_lease_expected_version=1
+ )
+ first = _terminal(
+ registry, todo["todo_id"], completion_turn_key="terminal-replay", **proof
+ )
+ target = "hard_lease" if mode == "soft_claim" else "soft_claim"
+ assert set_goal_handoff_mode(registry_path=registry, goal_id=GOAL_ID, mode=target)[
+ "changed"
+ ]
+ before = _snapshot(registry)
+ replay = _terminal(
+ registry, todo["todo_id"], completion_turn_key="terminal-replay", **proof
+ )
+ assert replay["idempotent_replay"] and not replay["changed"]
+ assert replay["original_receipt"] == first["original_receipt"]
+ assert _snapshot(registry)["handoff_mode"] == target
+ # Replaying may deliver a pending projection at the current revision;
+ # the business receipt and canonical state must remain unchanged.
+ _authority_unchanged(registry, before)
+ assert todo["todo_id"] in state.read_text()
+
+
+@pytest.mark.parametrize("lineage", ["missing", "foreign", "expired"])
+def test_hard_lease_claim_rejects_missing_foreign_or_expired_holder(
+ tmp_path, canonical_workspace, lineage
+):
+ registry, state = canonical_workspace(
+ tmp_path,
+ handoff_mode="hard_lease",
+ retained={"expired": True} if lineage == "expired" else None,
+ )
+ todo_id = (
+ "todo_retained" if lineage == "expired" else _add_todo(registry)["todo_id"]
+ )
+ if lineage == "foreign":
+ _acquire(registry, tmp_path, todo_id, owner=AGENT_B)
+ before, display = _snapshot(registry), state.read_bytes()
+ with pytest.raises(LocalCoordinationAuthorityRejection) as error:
+ _claim(registry, todo_id, AGENT_A)
+ assert error.value.code == "handoff_mode_requires_lease"
+ _unchanged(registry, state, before, display)
+
+
+@pytest.mark.parametrize(
+ "owner,claimant", [(AGENT_A, AGENT_A), (" Agent-A ", "AGENT-A")]
+)
+def test_hard_lease_holder_claim_normalizes_identities(
+ tmp_path, canonical_workspace, owner, claimant
+):
+ registry, state = canonical_workspace(tmp_path, handoff_mode="hard_lease")
+ todo = _add_todo(registry)
+ _acquire(registry, tmp_path, todo["todo_id"], owner=owner)
+ assert _claim(registry, todo["todo_id"], claimant)["ok"]
+ assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_A
+ assert _lease(registry, todo["todo_id"])["owner"] == AGENT_A
+
+
+@pytest.mark.parametrize(
+ "ownership",
+ [{"clear_claim": True}, {"claimed_by": AGENT_A}, {"claimed_by": AGENT_B}],
+)
+@pytest.mark.parametrize("leased", [False, True])
+def test_metadata_cannot_rewrite_hard_lease_execution_grants(
+ tmp_path, canonical_workspace, ownership, leased
+):
+ grant = [
+ {
+ "agent_id": ORCHESTRATOR,
+ "actions": ["reassign", "update"],
+ "requires_reason": True,
+ }
+ ]
+ registry, state = canonical_workspace(
+ tmp_path, handoff_mode="hard_lease", lifecycle_authority=grant
+ )
+ todo = _add_todo(registry, claimed_by=AGENT_A)
+ if leased:
+ _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
+ before, display = _snapshot(registry), state.read_bytes()
+ # Even explicit orchestration authority cannot split claim and lease owners.
+ with pytest.raises(LocalCoordinationAuthorityRejection) as error:
+ update_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ todo_id=todo["todo_id"],
+ agent_id=ORCHESTRATOR,
+ authority_reason="Hand over work",
+ **ownership,
+ )
+ assert error.value.code == "update_lease_ownership_transition_unsupported"
+ _unchanged(registry, state, before, display)
+
+
+def test_metadata_edit_requires_current_execution_proof(tmp_path, canonical_workspace):
+ registry, state = canonical_workspace(tmp_path, handoff_mode="hard_lease")
+ todo = _add_todo(registry, claimed_by=AGENT_A)
+ before, display = _snapshot(registry), state.read_bytes()
+ with pytest.raises(LocalCoordinationAuthorityRejection):
+ update_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ todo_id=todo["todo_id"],
+ agent_id=AGENT_A,
+ note="Validated progress",
+ )
+ _unchanged(registry, state, before, display)
+ _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
+ updated = update_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ todo_id=todo["todo_id"],
+ agent_id=AGENT_A,
+ note="Validated progress",
+ task_lease_idempotency_key="turn-lease-1",
+ task_lease_expected_version=1,
+ )
+ assert updated["ok"]
+ assert "Validated progress" in _agent_todo(state, todo["todo_id"])["note"]
+
+
+def test_create_assigns_new_work_but_duplicate_create_cannot_reassign(
+ tmp_path, canonical_workspace
+):
+ registry, state = canonical_workspace(tmp_path, handoff_mode="hard_lease")
+ created = _add_todo(registry, claimed_by=AGENT_A)
+ assert created["added"]
+ _acquire(registry, tmp_path, created["todo_id"], owner=AGENT_A)
+ before, display = _snapshot(registry), state.read_bytes()
+ assert _add_todo(registry, claimed_by=AGENT_A)["already_exists"]
+ _authority_unchanged(registry, before)
+ display = state.read_bytes()
+ with pytest.raises(
+ LocalCoordinationAuthorityUnavailable, match="different claimed_by"
+ ):
+ _add_todo(registry, claimed_by=AGENT_B)
+ _unchanged(registry, state, before, display)
+
+
+@pytest.mark.parametrize("operation", ["complete", "supersede"])
+@pytest.mark.parametrize(
+ "fault,code",
+ [
+ ("missing", "handoff_mode_requires_lease"),
+ ("keyless", "lease_fence_required"),
+ ("wrong_key", "lease_cas_mismatch"),
+ ("foreign", "lease_cas_mismatch"),
+ ("diverged", "handoff_mode_lease_claim_divergence"),
+ ],
+)
+def test_terminal_fence_rejects_invalid_execution_without_mutation(
+ tmp_path, canonical_workspace, operation, fault, code
+):
+ registry, state = canonical_workspace(
+ tmp_path,
+ handoff_mode="hard_lease",
+ retained={"todo": {"claimed_by": AGENT_B}} if fault == "diverged" else None,
+ )
+ todo_id = (
+ "todo_retained"
+ if fault == "diverged"
+ else _add_todo(registry, claimed_by=None if fault == "foreign" else AGENT_A)[
+ "todo_id"
+ ]
+ )
+ if fault not in ("missing", "diverged"):
+ _acquire(registry, tmp_path, todo_id, owner=AGENT_A)
+ proof = {}
+ if fault in ("wrong_key", "foreign"):
+ proof = dict(
+ task_lease_idempotency_key="wrong"
+ if fault == "wrong_key"
+ else "turn-lease-1",
+ task_lease_expected_version=1,
+ )
+ before, display = _snapshot(registry), state.read_bytes()
+ actor = AGENT_B if fault in ("diverged", "foreign") else AGENT_A
+ with pytest.raises(LocalCoordinationAuthorityRejection) as error:
+ _terminal(registry, todo_id, operation, agent_id=actor, **proof)
+ assert error.value.code == code
+ _unchanged(registry, state, before, display)
+
+
+@pytest.mark.parametrize("operation", ["complete", "supersede"])
+@pytest.mark.parametrize("dry_run", [False, True])
+def test_terminal_verified_execution_releases_only_on_commit(
+ tmp_path, canonical_workspace, operation, dry_run
+):
+ registry, state = canonical_workspace(tmp_path, handoff_mode="hard_lease")
+ todo = _add_todo(registry, claimed_by=AGENT_A)
+ _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
+ before, display = _snapshot(registry), state.read_bytes()
+ result = _terminal(
+ registry,
+ todo["todo_id"],
+ operation,
+ dry_run=dry_run,
+ task_lease_idempotency_key="turn-lease-1",
+ task_lease_expected_version=1,
+ )
+ assert result["ok"] and result["task_lease_fence"]["lease_fence"] == "required"
+ if dry_run:
+ _unchanged(registry, state, before, display)
+ else:
+ assert _agent_todo(state, todo["todo_id"])["status"] == "done"
+ assert _lease(registry, todo["todo_id"])["status"] == "released"
+ assert not task_lease_path(
+ runtime_root=tmp_path / "runtime", goal_id=GOAL_ID, todo_id=todo["todo_id"]
+ ).exists()
+
+
+def test_completion_can_assign_successor_without_inventing_its_lease(
+ tmp_path, canonical_workspace
+):
+ registry, state = canonical_workspace(tmp_path, handoff_mode="hard_lease")
+ todo = _add_todo(registry, claimed_by=AGENT_A)
+ _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
+ result = complete_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ todo_id=todo["todo_id"],
+ agent_id=AGENT_A,
+ task_lease_idempotency_key="turn-lease-1",
+ task_lease_expected_version=1,
+ next_agent_todo="Follow-up slice",
+ next_claimed_by=AGENT_B,
+ )
+ successor = result["next_todos"][0]["todo_id"]
+ assert _agent_todo(state, successor)["claimed_by"] == AGENT_B
+ assert all(lease["todo_id"] != successor for lease in _snapshot(registry)["leases"])
+
+
+@pytest.mark.parametrize("operation", ["complete", "supersede"])
+@pytest.mark.parametrize("authorized", [False, True])
+def test_delegated_terminal_authority_requires_real_grant(
+ tmp_path, canonical_workspace, operation, authorized
+):
+ grants = (
+ [{"agent_id": ORCHESTRATOR, "actions": [operation], "requires_reason": True}]
+ if authorized
+ else []
+ )
+ registry, state = canonical_workspace(
+ tmp_path, handoff_mode="hard_lease", lifecycle_authority=grants
+ )
+ todo = _add_todo(registry, claimed_by=AGENT_A)
+ before, display = _snapshot(registry), state.read_bytes()
+ if not authorized:
+ with pytest.raises(LocalCoordinationAuthorityRejection):
+ _terminal(
+ registry,
+ todo["todo_id"],
+ operation,
+ agent_id=ORCHESTRATOR,
+ authority_reason="Retire stalled item",
+ )
+ _unchanged(registry, state, before, display)
+ else:
+ result = _terminal(
+ registry,
+ todo["todo_id"],
+ operation,
+ agent_id=ORCHESTRATOR,
+ authority_reason="Retire stalled item",
+ )
+ assert (
+ result["mutation_authority"]["authority_mode"]
+ == "delegated_orchestration_override"
+ )
+ assert result["task_lease_fence"]["lease_fence"] == "delegated_override"
+ assert _agent_todo(state, todo["todo_id"])["status"] == "done"
+
+
+@pytest.mark.parametrize(
+ "proof", ["auto", "verified", "wrong_key", "foreign", "foreign_keyless"]
+)
+def test_exact_user_gate_completion_preserves_execution_fence(
+ tmp_path, canonical_workspace, proof
+):
+ registry, state = canonical_workspace(tmp_path, handoff_mode="hard_lease")
+ target, gate = _add_exact_user_gate_pair(registry)
+ options = dict(role="user", decision_outcome="approve")
+ if proof != "auto":
+ _acquire(
+ registry,
+ tmp_path,
+ gate["todo_id"],
+ owner=AGENT_B if proof.startswith("foreign") else AGENT_A,
+ )
+ if proof != "foreign_keyless":
+ options.update(
+ task_lease_idempotency_key="wrong"
+ if proof == "wrong_key"
+ else "turn-lease-1",
+ task_lease_expected_version=1,
+ )
+ before, display = _snapshot(registry), state.read_bytes()
+ if proof in ("wrong_key", "foreign", "foreign_keyless"):
+ with pytest.raises(LocalCoordinationAuthorityRejection) as error:
+ _terminal(registry, gate["todo_id"], **options)
+ assert error.value.code == (
+ "lease_fence_required"
+ if proof == "foreign_keyless"
+ else "lease_cas_mismatch"
+ )
+ _unchanged(registry, state, before, display)
+ else:
+ result = _terminal(registry, gate["todo_id"], **options)
+ assert (
+ result["mutation_authority"]["authority_mode"]
+ == "exact_user_gate_decision_scope_override"
+ )
+ assert result["task_lease_fence"]["lease_fence"] == (
+ "auto_acquire" if proof == "auto" else "required"
+ )
+ assert _lease(registry, gate["todo_id"])["status"] == "released"
+ assert _agent_todo(state, gate["todo_id"])["status"] == "done"
+ assert not _agent_todo(state, target["todo_id"]).get("required_decision_scopes")
+ assert result["decision_scope_resolution"]["state"] == "resolved"
+
+
+def test_bound_user_action_completes_without_gate_or_execution_claim(
+ tmp_path, canonical_workspace
+):
+ registry, state = canonical_workspace(tmp_path, handoff_mode="hard_lease")
+ todo = add_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ role="user",
+ text="Owner follow-up",
+ task_class="user_action",
+ bound_agent=AGENT_A,
+ )
+ result = _terminal(registry, todo["todo_id"], role="user")
+ assert result["task_lease_fence"]["lease_fence"] == "not_required"
+ assert _snapshot(registry)["leases"] == []
+ assert _agent_todo(state, todo["todo_id"])["status"] == "done"
+
+
+@pytest.mark.parametrize("mode", ["soft_claim", "hard_lease"])
+@pytest.mark.parametrize("dry_run", [False, True])
+def test_force_bootstrap_cannot_erase_canonical_policy_or_work(
+ tmp_path, canonical_workspace, mode, dry_run
+):
+ registry, state = canonical_workspace(tmp_path, handoff_mode=mode)
+ _add_todo(registry, claimed_by=AGENT_A)
+ before, display = _snapshot(registry), state.read_bytes()
+ with pytest.raises(
+ ValueError, match="Canonical authority cannot rebuild existing state"
+ ):
+ _force_bootstrap(registry, state, tmp_path, dry_run=dry_run)
+ _unchanged(registry, state, before, display)
+
+
+
+
+def test_delegated_terminal_replay_retains_original_authority_receipt(
+ tmp_path, canonical_workspace
+):
+ registry, state = canonical_workspace(
+ tmp_path,
+ handoff_mode="hard_lease",
+ lifecycle_authority=[
+ {"agent_id": ORCHESTRATOR, "actions": ["complete"], "requires_reason": True}
+ ],
+ )
+ todo = _add_todo(registry, claimed_by=AGENT_A)
+ intent = dict(
+ agent_id=ORCHESTRATOR,
+ authority_reason="Close stalled work",
+ completion_turn_key="delegated-close",
+ )
+ result = _terminal(registry, todo["todo_id"], **intent)
+ before, display = _snapshot(registry), state.read_bytes()
+ replay = _terminal(registry, todo["todo_id"], **intent)
+ assert replay["idempotent_replay"] and not replay["changed"]
+ assert replay["original_receipt"] == result["original_receipt"]
+ assert (
+ replay["mutation_authority"]["authority_mode"]
+ == "delegated_orchestration_override"
+ )
+ _unchanged(registry, state, before, display)
+
+
+def test_invalid_requested_policy_leaves_canonical_authority_unchanged(
+ tmp_path, canonical_workspace
+):
+ registry, state = canonical_workspace(tmp_path)
+ _add_todo(registry, claimed_by=AGENT_A)
+ before, display = _snapshot(registry), state.read_bytes()
+ with pytest.raises(HandoffModeError) as error:
+ set_goal_handoff_mode(registry_path=registry, goal_id=GOAL_ID, mode="banana")
+ assert error.value.code == "invalid_handoff_mode"
+ _unchanged(registry, state, before, display)
diff --git a/tests/control_plane/test_canonical_handoff_mode.py b/tests/control_plane/test_canonical_handoff_mode.py
index 4a40ab162b..1050a967d8 100644
--- a/tests/control_plane/test_canonical_handoff_mode.py
+++ b/tests/control_plane/test_canonical_handoff_mode.py
@@ -27,6 +27,11 @@ def cli(*args):
result = subprocess.run([sys.executable, "-m", "loopx.cli", "--registry", str(registry),
"--format", "json", "handoff-mode", *args, "--goal-id", "mode-goal"],
capture_output=True, text=True, timeout=90)
+ if result.returncode:
+ # Keep the complete native refusal in pytest's captured output;
+ # an assertion's abbreviated dictionary cannot diagnose a failure.
+ print(f"handoff-mode {args!r} exited {result.returncode}\n"
+ f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}")
assert result.stdout, result.stderr
return result.returncode, json.loads(result.stdout)
return state, runtime, cli
@@ -228,3 +233,94 @@ def test_provider_failure_is_not_a_legacy_fallback(canonical_mode, monkeypatch):
provider_handoff_mode.set_canonical_handoff_mode(runtime_root=runtime, goal_id="mode-goal",
mode="hard_lease", operation_id="unavailable", dry_run=False)
assert state.read_bytes() == before
+
+
+def test_retired_policy_requires_release_and_reviewed_migration_before_execution(canonical_mode):
+ state, runtime, cli = canonical_mode
+ from datetime import datetime, timedelta, timezone
+ from loopx.todos import add_goal_todo
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+
+ registry = runtime.parent / "registry.json"
+ todo = add_goal_todo(registry_path=registry, goal_id="mode-goal", role="agent",
+ text="Preserve imported ownership", claimed_by="agent-a",
+ task_class="advancement_task", note="Retain original metadata")
+ # Imported historical facts are installed directly in this isolated test
+ # provider. No current business writer or new legacy lease grant produces them.
+ at = datetime.now(timezone.utc).replace(microsecond=0)
+ lease = {"schema_version": "task_lease_v0", "goal_id": "mode-goal", "todo_id": todo["todo_id"],
+ "owner": "agent-a", "idempotency_key": "imported-execution", "version": 4, "lease_epoch": 2,
+ "status": "active", "write_scopes": [], "acquire_ttl_seconds": 600,
+ "acquired_at": (at - timedelta(seconds=30)).isoformat(), "updated_at": at.isoformat(),
+ "expires_at": (at + timedelta(seconds=600)).isoformat()}
+ coordination = Path(__file__).resolve().parents[2] / "loopx/control_plane/coordination"
+ script = f"""
+ import {{openLocalAuthorityStore}} from {json.dumps((coordination / 'local_authority_provider.ts').as_uri())};
+ const store = await openLocalAuthorityStore(process.argv[1], 'mode-goal');
+ const before = await store.loadAuthority();
+ if (before.status !== 'loaded') throw new Error(JSON.stringify(before));
+ const result = await store.commitAuthority({{operation_id: 'imported-policy-fixture',
+ expected_provider_revision: before.provider_revision, next_projection: {{...before.head,
+ handoff_mode: 'legacy', leases: [{json.dumps(lease)}]}}, events: [], receipts: []}});
+ if (result.status !== 'applied') throw new Error(JSON.stringify(result));
+ """
+ seeded = subprocess.run(["node", "--no-warnings", "--experimental-strip-types", "--input-type=module",
+ "-e", script, str(runtime)], capture_output=True, text=True, timeout=45)
+ assert seeded.returncode == 0, seeded.stderr
+
+ def read():
+ return read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="mode-goal", include_leases=True)
+
+ def work(*args, expected_exit=0):
+ child = subprocess.run([sys.executable, "-m", "loopx.cli", "--registry", str(registry), "--format", "json",
+ *args, "--goal-id", "mode-goal", "--todo-id", todo["todo_id"]], capture_output=True, text=True, timeout=90)
+ assert child.returncode == expected_exit, child.stdout + child.stderr
+ return json.loads(child.stdout)
+
+ before = read()
+ inspected = work("task-lease", "inspect")
+ assert inspected["active"] is False
+ assert inspected["executor_constraint"]["reason"] == "handoff_mode_retired"
+ assert inspected["lease"] == lease
+ for args in [
+ ("todo", "claim", "--agent-id", "agent-a", "--claimed-by", "agent-a"),
+ ("todo", "update", "--agent-id", "agent-a", "--note", "Unreviewed work"),
+ ("todo", "complete", "--agent-id", "agent-a", "--no-follow-up", "--note", "Bounded completion"),
+ ("todo", "update", "--agent-id", "agent-a", "--note", "Old proof cannot authorize",
+ "--task-lease-idempotency-key", "imported-execution", "--task-lease-expected-version", "4"),
+ ]:
+ refused = work(*args, expected_exit=1)
+ assert (refused.get("error_code") or refused.get("reason_code")) == "handoff_mode_retired", refused
+ assert read() == before
+ released = work("task-lease", "release", "--owner", "agent-a", "--idempotency-key", "imported-execution",
+ "--expected-version", "4")
+ assert released["released"] is True
+ retained = read()
+ assert retained["handoff_mode"] == "legacy" and retained["todos"] == before["todos"]
+ assert retained["leases"][0]["status"] == "released"
+ assert retained["leases"][0]["version"] == 4 and retained["leases"][0]["lease_epoch"] == 2
+ plan = runtime.parent / "retired-policy-upgrade.json"
+ code, planned = cli("plan-migration", "--mode", "hard_lease", "--plan", str(plan))
+ assert code == 0 and planned["status"] == "planned", planned
+ assert read() == retained
+ digest = planned["plan_sha256"]
+ code, applied = cli("migrate", "--plan", str(plan), "--plan-sha256", digest, "--execute")
+ assert code == 0 and applied["status"] == "applied", applied
+ assert applied["execution_authority_granted"] is False
+ assert Path(applied["backup_path"]).is_file()
+ migrated = read()
+ assert migrated["handoff_mode"] == "hard_lease"
+ assert migrated["todos"] == retained["todos"] and migrated["leases"] == retained["leases"]
+ acquired = work("task-lease", "acquire", "--owner", "agent-a", "--idempotency-key", "new-execution",
+ "--expected-version", "4", "--ttl-seconds", "600")
+ assert acquired["acquired"] is True and acquired["lease"]["version"] == 5
+ assert acquired["lease"]["lease_epoch"] == 3
+ assert work("todo", "update", "--agent-id", "agent-a", "--note", "Reviewed current work",
+ "--task-lease-idempotency-key", "new-execution", "--task-lease-expected-version", "5")["ok"] is True
+ later = read()
+ code, replay = cli("migrate", "--plan", str(plan), "--plan-sha256", digest, "--execute")
+ assert code == 0 and replay["status"] == "replayed", replay
+ assert read() == later, "historical migration cannot overwrite later execution"
+ assert work("task-lease", "release", "--owner", "agent-a", "--idempotency-key", "new-execution",
+ "--expected-version", "5")["released"] is True
+ assert state.exists()
diff --git a/tests/control_plane/test_canonical_lease_acquire.py b/tests/control_plane/test_canonical_lease_acquire.py
index feae5aebcc..7abd2f6ad3 100644
--- a/tests/control_plane/test_canonical_lease_acquire.py
+++ b/tests/control_plane/test_canonical_lease_acquire.py
@@ -3,16 +3,91 @@
import os
import subprocess
import sys
+from datetime import datetime, timedelta, timezone
from pathlib import Path
import pytest
from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
+from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
REPO = Path(__file__).resolve().parents[2]
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+@pytest.mark.parametrize("retained", [False, True])
+def test_user_action_cannot_grant_execution_and_retained_lease_can_be_released(
+ tmp_path, monkeypatch, provider, retained,
+):
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ runtime, state, registry = tmp_path / "runtime", tmp_path / "state.md", tmp_path / "registry.json"
+ goal, target = "user-action-lease", "todo_user_action"
+ state.write_text("# Synthetic bound User action\n")
+ registry.write_text(json.dumps({"common_runtime_root": str(runtime), "goals": [{
+ "id": goal, "repo": str(tmp_path), "state_file": state.name,
+ "coordination": {"registered_agents": ["agent-a", "agent-b"]},
+ }]}))
+ now = datetime.now(timezone.utc)
+ leases = [{"schema_version": "task_lease_v0", "goal_id": goal, "todo_id": target,
+ "owner": "agent-b", "idempotency_key": "retained-execution", "status": "active",
+ "version": 3, "lease_epoch": 2, "write_scopes": [], "acquire_ttl_seconds": 600,
+ "acquired_at": now.isoformat(), "updated_at": now.isoformat(),
+ "expires_at": (now + timedelta(minutes=10)).isoformat()}] if retained else []
+ projection = build_todo_runtime_shadow_projection(goal_id=goal, handoff_mode="hard_lease",
+ leases=leases, todos=[{"schema_version": "todo_item_v0", "todo_id": target,
+ "role": "user", "task_class": "user_action", "status": "open", "done": False,
+ "text": "Review the synthetic observation", "bound_agent": "agent-a",
+ "archive_state": "active", "source_section": "User Todo", "index": 1}])
+ initialize_canonical_authority(runtime, goal, projection, state_path=state, provider=provider)
+
+ def cli(*args, expected_exit=0):
+ child = subprocess.run([sys.executable, "-m", "loopx.entrypoint", "--registry", str(registry),
+ "--runtime-root", str(runtime), "--format", "json", *args,
+ "--goal-id", goal, "--todo-id", target], cwd=REPO,
+ capture_output=True, text=True, timeout=60, check=False)
+ assert child.returncode == expected_exit, child.stdout + child.stderr
+ return json.loads(child.stdout)
+
+ def current():
+ return read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=goal, include_leases=True)
+
+ before, display = current(), state.read_bytes()
+ rejected = cli("task-lease", "acquire", "--owner", "outsider", "--idempotency-key", "outsider", expected_exit=1)
+ assert rejected["error_code"] == "owner_not_registered"
+ for owner in ("agent-a", "agent-b"):
+ rejected = cli("task-lease", "acquire", "--owner", owner,
+ "--idempotency-key", "new-" + owner, "--expected-version", "3" if retained else "0", expected_exit=1)
+ assert rejected["error_code"] == "todo_not_executable"
+ assert current() == before and state.read_bytes() == display
+ inspected = cli("task-lease", "inspect")
+ assert inspected["active"] is False
+ if retained:
+ assert inspected["executor_constraint"] == {"effective": False, "reason": "todo_not_executable"}
+ proof = ("--owner", "agent-b", "--idempotency-key", "retained-execution", "--expected-version", "3")
+ for operation, extra in (("renew", ()), ("transfer", ("--new-owner", "agent-a", "--new-idempotency-key", "transfer-a"))):
+ rejected = cli("task-lease", operation, *proof, *extra, expected_exit=1)
+ assert rejected["error_code"] == "todo_not_executable"
+ assert current() == before and state.read_bytes() == display
+ released = cli("task-lease", "release", *proof)
+ assert released["lease"]["status"] == "released"
+ after_release = current()
+ replay = cli("task-lease", "release", *proof)
+ assert replay["idempotent"] is True and current() == after_release
+ assert replay["original_receipt"] == released["original_receipt"]
+ before_copy = current()
+ rejected = cli("todo", "update", "--agent-id", "agent-b", "--note", "Foreign copy", expected_exit=1)
+ assert rejected["reason_code"] == "bound_agent_mismatch" and current() == before_copy
+ assert cli("todo", "update", "--agent-id", "agent-a", "--note", "Bound administrative copy",
+ "--update-operation-id", "bound-copy", "--update-expected-provider-revision",
+ before_copy["provider_revision"])["ok"] is True
+ completion = ("todo", "complete", "--agent-id", "agent-a", "--decision-outcome", "cancel",
+ "--evidence", "The synthetic observation was withdrawn")
+ assert cli(*completion, "--dry-run")["ok"] is True
+ assert cli(*completion)["ok"] is True
+ assert cli("todo", "list")["todo"]["status"] == "done"
+
+
@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_public_acquire_renew_complete_and_retired_retry(tmp_path, monkeypatch, provider):
isolate_sqlite_runtime(tmp_path, monkeypatch)
diff --git a/tests/control_plane/test_canonical_lease_inspection.py b/tests/control_plane/test_canonical_lease_inspection.py
index 88ca782337..b00f5767bf 100644
--- a/tests/control_plane/test_canonical_lease_inspection.py
+++ b/tests/control_plane/test_canonical_lease_inspection.py
@@ -171,16 +171,6 @@ def change_source(method, payload, **kwargs):
assert len(calls) == 3
-def test_owner_constraint_requires_the_native_diagnostic_contract(monkeypatch):
- from loopx.control_plane import effect_runtime
- from loopx.control_plane.work_items.task_lease import task_lease_owner_constraint
- monkeypatch.setattr(effect_runtime, "effect_runtime_result", lambda *_: {
- "schema_version": "task_lease_owner_eligibility_v0", "outcome": "apply", "code": "lease_owner_allowed",
- })
- with pytest.raises(RuntimeError, match="constraint shape mismatch"):
- task_lease_owner_constraint({"status": "open"}, owner="agent-a")
-
-
def test_inactive_legacy_inspection_does_not_project_todos(tmp_path, monkeypatch):
registry = tmp_path / "registry.json"
state = tmp_path / "state.md"
@@ -200,10 +190,11 @@ def forbidden_projection(**_):
assert expired["active"] is False and expired["handoff_mode"] == "hard_lease"
-def test_legacy_inspection_reloads_lease_after_demanding_todo_facts(tmp_path, monkeypatch):
+@pytest.mark.parametrize("mode", ["legacy", "soft_claim", "hard_lease"])
+def test_retained_source_inspection_never_projects_current_execution(tmp_path, monkeypatch, mode):
registry = tmp_path / "registry.json"
state = tmp_path / "state.md"
- state.write_text("---\nhandoff_mode: hard_lease\n---\n# Synthetic state\n")
+ state.write_text(f"---\nhandoff_mode: {mode}\n---\n# Synthetic state\n")
registry.write_text(json.dumps({"goals": [{"id": GOAL, "repo": str(tmp_path), "state_file": str(state),
"coordination": {"registered_agents": ["agent-a"]}}]}))
runtime = tmp_path / "runtime"
@@ -212,16 +203,17 @@ def test_legacy_inspection_reloads_lease_after_demanding_todo_facts(tmp_path, mo
lease = {"schema_version": "task_lease_v0", "status": "active", "owner": "agent-a", "expires_at": "2099-01-01T00:00:00Z"}
lease_path.write_text(json.dumps(lease))
from loopx.control_plane.todos import list_readback
- calls = []
- def release_during_projection(**_):
- calls.append(True)
- lease_path.write_text(json.dumps({**lease, "status": "released"}))
- return {"todos": [{"todo_id": TODO, "status": "open", "claimed_by": None, "excluded_agents": []}]}
- monkeypatch.setattr(list_readback, "list_goal_todos", release_during_projection)
+ original = lease_path.read_bytes()
+ def forbidden_projection(**_):
+ raise AssertionError("retained source inspection must not revive Todo admission")
+ monkeypatch.setattr(list_readback, "list_goal_todos", forbidden_projection)
result = _inspect(registry, runtime)
- assert len(calls) == 1
assert result["active"] is False
- assert result["lease"]["status"] == "released"
+ assert result["lease"] == lease
+ assert lease_path.read_bytes() == original
+ assert result["executor_constraint"]["reason"] == "canonical_authority_required"
+ assert result["execution_policy"]["supported"] is False
+ assert result["execution_policy"]["execution_authority_granted"] is False
assert "todo_projection_required" not in result
diff --git a/tests/control_plane/test_canonical_todo_writer_isolation.py b/tests/control_plane/test_canonical_todo_writer_isolation.py
index 489877cfe4..ea12517f8a 100644
--- a/tests/control_plane/test_canonical_todo_writer_isolation.py
+++ b/tests/control_plane/test_canonical_todo_writer_isolation.py
@@ -1,7 +1,6 @@
"""Canonical callers must not load unpromoted Todo writers or capture producers."""
from __future__ import annotations
-import ast
import json
import os
from pathlib import Path
@@ -18,18 +17,16 @@
import test_quota_settlement_cli as settlement
-@pytest.mark.parametrize("module", ["mutation_api", "legacy_mutation"])
-def test_todo_mutation_import_does_not_load_status(module):
+def test_todo_mutation_import_does_not_load_status():
result = subprocess.run(
[sys.executable, "-c",
"import importlib,sys;sys.modules['loopx.status']=None;"
- f"owner=importlib.import_module('loopx.control_plane.todos.{module}');"
+ "owner=importlib.import_module('loopx.control_plane.todos.mutation_api');"
"assert owner.ARCHIVE_COMPLETED_DEFAULT_MAX_ACTIVE_DONE == 10"],
cwd=Path(__file__).resolve().parents[2], capture_output=True, text=True, timeout=45,
)
assert result.returncode == 0, result.stdout + result.stderr
-
@pytest.fixture(scope="session")
def retirement_wheel():
# Supply a normally built artifact: unit runs must not build the frontend
@@ -77,107 +74,27 @@ def isolated_todo_distribution(tmp_path, monkeypatch, request):
# The packaged typed owner must be shipped, not borrowed from checkout.
assert (package / "control_plane/todos/succession.ts").is_file()
assert (package / "control_plane/effect_runtime_server.ts").is_file()
+ _assert_source_todo_writers_absent(package)
return package, command
@pytest.fixture
def without_source_todo_writers(isolated_todo_distribution):
- package, command = isolated_todo_distribution
- _remove_source_todo_writers(package)
+ _package, command = isolated_todo_distribution
return command
-def _remove_source_todo_writers(package):
- (package / "control_plane/todos/line_update.py").unlink()
- (package / "control_plane/todos/legacy_mutation.py").unlink(missing_ok=True)
- capture_adapter = package / "control_plane/coordination/runtime_shadow_writer_adapter.py"
- source = capture_adapter.read_text()
- lines = source.splitlines(keepends=True)
- producers = {"begin_todo_runtime_shadow_capture", "require_runtime_shadow_capture_prepared",
- "write_captured_todo_state", "settle_todo_runtime_shadow_capture"}
- definitions = [node for node in ast.parse(source).body
- if isinstance(node, ast.FunctionDef) and node.name in producers]
- assert {node.name for node in definitions} == producers
- for node in reversed(definitions):
- del lines[node.lineno - 1:node.end_lineno]
- capture_adapter.write_text("".join(lines))
-
-
-def _remove_monitor_source_modules(package):
- (package / "control_plane/scheduler/legacy_monitor_poll.py").unlink()
- for name in ("runtime_shadow", "runtime_shadow_writer_adapter",
- "local_authority_shadow_adapter", "local_authority_shadow_outbox"):
- (package / f"control_plane/coordination/{name}.py").unlink()
-
-
-@pytest.mark.parametrize("window", ["before_commit", "after_commit"])
-@pytest.mark.parametrize("corrupt_cursor", [False, True], ids=["recover", "reject-unproved-cursor"])
-def test_pending_source_outbox_survives_producer_retirement(
- tmp_path, isolated_todo_distribution, window, corrupt_cursor,
-):
- from shadow_e2e_fixture import workspace
-
- package, command = isolated_todo_distribution
- w = workspace(tmp_path / "shadow", bootstrap=False)
- w.command, w.cwd, w.package = tuple(command), tmp_path, package
- assert w.cli("coordination-shadow", "bootstrap", "--execute")["bootstrap"]["status"] == "applied"
- original = w.add("Already delivered source work")["todo_id"]
- w.crash(window, "todo", "add", "--role", "agent", "--text", "Pending source work")
-
- def history():
- result = subprocess.run(
- [sys.executable, "-S", "-c",
- "import json,sys;from pathlib import Path;"
- "from loopx.control_plane.coordination.local_authority_shadow_adapter "
- "import read_local_authority_shadow;"
- "print(json.dumps(read_local_authority_shadow(runtime_root=Path(sys.argv[1]),"
- "goal_id=sys.argv[2],scan_limit=10000)))", str(w.runtime), w.goal],
- cwd=tmp_path, capture_output=True, text=True, timeout=45,
- )
- assert result.returncode == 0, result.stdout + result.stderr
- return json.loads(result.stdout)
+def _assert_source_todo_writers_absent(package):
+ retired = (
+ "control_plane/todos/legacy_mutation.py",
+ "control_plane/todos/line_update.py",
+ "control_plane/scheduler/legacy_monitor_poll.py",
+ "control_plane/todos/handoff_mode_source.py",
+ "control_plane/coordination/runtime_shadow_writer_adapter.py",
+ "control_plane/coordination/handoff_mode_legacy_plan.ts",
+ )
+ assert all(not (package / relative).exists() for relative in retired)
- before = history()
- assert len(before["proof"]["transactions"]) == (2 if window == "before_commit" else 3)
- assert original in {todo["todo_id"] for todo in before["head"]["todos"]}
- # Use the existing partition owner rather than assuming its on-disk layout.
- from loopx.control_plane.coordination.local_authority_shadow_outbox import partition_directory
- directory = partition_directory(w.runtime, w.goal, "todos")
- assert len(list(directory.glob("*.prepared.json"))) == 1
- assert len(list(directory.glob("*.committed.json"))) == 1
- source_bytes, registry_bytes = w.state.read_bytes(), w.registry.read_bytes()
- _remove_source_todo_writers(package)
-
- if corrupt_cursor:
- cursor_path = directory / "drain-cursor.json"
- cursor = json.loads(cursor_path.read_text())
- cursor["last_partition_digest"] = "sha256:" + "f" * 64
- cursor_path.write_text(json.dumps(cursor))
- pending_bytes = {f.name: f.read_bytes() for f in directory.iterdir() if f.is_file()}
- result = w.drain()
- assert result["ok"] is False, result
- assert result["reason_code"] == "outbox_cursor_unproved", result
- assert {f.name: f.read_bytes() for f in directory.iterdir() if f.is_file()} == pending_bytes
- assert history()["proof"]["transactions"] == before["proof"]["transactions"]
- else:
- result = w.drain()
- assert result["ok"] is True, result
- assert result["delivered"] == (1 if window == "before_commit" else 0)
- assert result["replayed"] == (0 if window == "before_commit" else 1)
- recovered = history()
- transactions = recovered["proof"]["transactions"]
- assert transactions[:len(before["proof"]["transactions"])] == before["proof"]["transactions"]
- assert len(transactions) == 3
- assert transactions[-1]["receipts"][0]["write_class"] == "todo_add"
- assert transactions[-1]["receipts"][0]["seq"] == 2
- assert {todo["text"] for todo in recovered["head"]["todos"]} == {
- "[P1] Already delivered source work", "[P1] Pending source work"}
- assert json.loads((directory / "drain-cursor.json").read_text())["last_seq"] == 2
- assert sorted(f.name for f in directory.iterdir()) == ["drain-cursor.json"]
- assert w.drain()["outcome"] == "nothing_pending"
- assert history()["proof"]["transactions"] == transactions
- assert w.state.read_bytes() == source_bytes
- assert w.registry.read_bytes() == registry_bytes
@pytest.mark.parametrize("provider", ["file", "sqlite"])
@@ -257,7 +174,7 @@ def test_handoff_mode_without_source_todo_writers(tmp_path, provider, without_so
def cli(*args, succeeds=True, override=False):
result = subprocess.run(
- [*without_source_todo_writers, "--format", "json", "--registry", str(registry),
+ [sys.executable, "-m", "loopx.cli", "--format", "json", "--registry", str(registry),
*(["--runtime-root", str(runtime)] if override else []), *args, "--goal-id", "goal-a"],
cwd=caller, capture_output=True, text=True, timeout=45,
)
@@ -365,10 +282,16 @@ def bootstrap():
def test_leased_delivery_settlement_and_recovery_without_source_todo_writers(tmp_path, provider, without_source_todo_writers):
project, runtime, registry, display, prefix = _source(
tmp_path, provider=provider, handoff_mode="hard_lease",
- extra=f"claimed_by={settlement.AGENT_ID}",
+ extra=(
+ f"claimed_by={settlement.AGENT_ID} "
+ "required_write_scopes=tests/**"
+ ),
)
- # The isolated package removes the writers; erase only the Todo display,
- # not the Goal objective and constraints needed for delivery admission.
+ config = json.loads(registry.read_text())
+ config["goals"][0]["coordination"]["write_scope"] = ["tests/**"]
+ registry.write_text(json.dumps(config))
+ # Goal intent remains readable while the Markdown Todo display contributes
+ # no candidate or lease facts after canonical promotion.
display.write_text(prefix)
def run(*args, succeeds=True):
@@ -386,10 +309,6 @@ def run(*args, succeeds=True):
def guard(turn):
return run("quota", "should-run", *identity, "--codex-app", "--turn-instance-id", turn)
- selected = guard(settlement.TURN_ID)
- assert selected["decision"] == "run"
- original = selected["heartbeat_receipt"]["settlement_identity"]
- assert original["todo_id"] == settlement.TODO_ID
update = ["todo", "update", *identity, "--note", "Canonical delivery retained"]
rejected = run(*update, succeeds=False)
assert rejected["error_code"] == "handoff_mode_requires_lease"
@@ -400,6 +319,10 @@ def guard(turn):
assert acquired["acquired"] is True
proof = ["--task-lease-idempotency-key", "delivery",
"--task-lease-expected-version", str(acquired["lease"]["version"])]
+ selected = guard(settlement.TURN_ID)
+ assert selected["decision"] == "run"
+ original = selected["heartbeat_receipt"]["settlement_identity"]
+ assert original["todo_id"] == settlement.TODO_ID
assert run(*update, *proof)["ok"] is True
before = run("todo", "list", *identity)["todo"]
assert before["note"] == "Canonical delivery retained"
@@ -454,104 +377,30 @@ def guard(turn):
assert settlement._spend_run_count(runtime) == 1
-def test_explicit_source_writer_import_preserves_the_existing_seam(tmp_path):
+def test_retired_source_writer_modules_and_exports_are_absent():
result = subprocess.run(
[sys.executable, "-c", """
+import importlib.util
import sys
-import sysconfig
import loopx.todos as todos
-assert 'loopx.control_plane.todos.line_update' not in sys.modules
-assert 'loopx.control_plane.todos.legacy_mutation' not in sys.modules
-from loopx.todos import apply_todo_update_to_lines
-from loopx.control_plane.todos import line_update
-assert apply_todo_update_to_lines is line_update.apply_todo_update_to_lines
-for name in ('link_generated_successor_todo_ids', 'link_superseding_todo_id', 'upsert_todo_metadata'):
- assert getattr(todos, name) is getattr(line_update, name)
-try:
- getattr(todos, 'unknown_writer_export')
-except AttributeError:
- pass
-else:
- raise AssertionError('unknown export must remain unavailable')
+for module in (
+ 'loopx.control_plane.todos.line_update',
+ 'loopx.control_plane.todos.legacy_mutation',
+ 'loopx.control_plane.scheduler.legacy_monitor_poll',
+ 'loopx.control_plane.todos.handoff_mode_source',
+ 'loopx.control_plane.coordination.runtime_shadow_writer_adapter',
+):
+ assert module not in sys.modules
+ assert importlib.util.find_spec(module) is None
+for name in (
+ 'apply_todo_update_to_lines',
+ 'link_generated_successor_todo_ids',
+ 'link_superseding_todo_id',
+ 'successor_add_kwargs',
+ 'upsert_todo_metadata',
+):
+ assert not hasattr(todos, name)
"""],
capture_output=True, text=True, timeout=45,
)
assert result.returncode == 0, result.stdout + result.stderr
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-@pytest.mark.parametrize("material_change", [False, True])
-@pytest.mark.parametrize("source_writers_present", [False, True], ids=["absent", "present"])
-def test_monitor_recovery_with_source_writer_isolation(
- tmp_path, request, provider, material_change, source_writers_present, isolated_todo_distribution,
-):
- from test_leased_monitor_poll import LEASE, PROOF
- from test_monitor_followthrough_contract import GOAL_ID, AGENT_ID
- from test_native_monitor_poll import _canonical
-
- _package, command = isolated_todo_distribution
- if not source_writers_present:
- command = request.getfixturevalue("without_source_todo_writers")
- _remove_monitor_source_modules(_package)
- registry, runtime, display, monitor = _canonical(
- tmp_path, native=True, provider=provider, lease=LEASE,
- )
- display.unlink()
-
- def cli(*args, succeeds=True):
- result = subprocess.run(
- [*command, "--format", "json",
- "--registry", str(registry), "--runtime-root", str(runtime), *args],
- cwd=tmp_path, capture_output=True, text=True, timeout=45,
- )
- assert (result.returncode == 0) is succeeds, result.stdout + result.stderr
- return json.loads(result.stdout)
-
- def current():
- return cli("todo", "list", "--goal-id", GOAL_ID)
-
- identity = ["--goal-id", GOAL_ID, "--agent-id", AGENT_ID,
- "--runtime-profile", "generic_cli", "--turn-instance-id", "monitor-retirement",
- "--available-capability", "network", "--available-capability", "external_evidence_poll"]
- guard = cli("quota", "should-run", *identity)
- assert guard["selected_todo"]["todo_id"] == monitor["todo_id"]
- before = current()
- poll = ["quota", "monitor-poll", *identity, "--todo-id", monitor["todo_id"],
- "--result-hash", "observed-revision", "--use-current-task-lease", "--execute"]
- if material_change:
- poll += ["--material-change", "--next-agent-todo", "Validate the observed revision",
- "--next-action-kind", "validate"]
-
- # A missing provider must not use the display or create another authority.
- backend = runtime / "authority" / f"{provider}-v0"
- offline = backend.with_name(backend.name + "-offline")
- backend.rename(offline)
- try:
- failed = cli(*poll, succeeds=False)
- assert failed["ok"] is False
- assert not backend.exists() and not display.exists()
- finally:
- offline.rename(backend)
- assert current()["todos"] == before["todos"]
-
- committed = cli(*poll)
- assert committed["todo_writeback"]["lease_proof"] == PROOF
- assert committed["todo_writeback"]["material_change_generation"] == int(material_change)
- after = current()
- assert len(after["todos"]) == len(before["todos"]) + int(material_change)
- observed = next(t for t in after["todos"] if t["todo_id"] == monitor["todo_id"])
- assert observed["result_hash"] == "observed-revision"
- assert int(observed["consecutive_no_change"]) == int(not material_change)
- assert display.exists()
-
- # Recover the same observation after the execution lease is released.
- assert cli("task-lease", "release", "--goal-id", GOAL_ID,
- "--todo-id", monitor["todo_id"], "--owner", AGENT_ID,
- "--idempotency-key", PROOF["idempotency_key"],
- "--expected-version", str(PROOF["expected_version"]))["released"]
- assert cli(*poll)["replayed"] is True
- assert current()["todos"] == after["todos"]
- rows = [json.loads(line) for line in
- (runtime / "goals" / GOAL_ID / "runs/index.jsonl").read_text().splitlines()]
- assert sum(row.get("classification") == "quota_monitor_poll" for row in rows) == 1
- assert all(row.get("classification") != "quota_slot_spend" for row in rows)
diff --git a/tests/control_plane/test_canonical_update_dispatch.py b/tests/control_plane/test_canonical_update_dispatch.py
index 2a2c778d95..0e9cf6838c 100644
--- a/tests/control_plane/test_canonical_update_dispatch.py
+++ b/tests/control_plane/test_canonical_update_dispatch.py
@@ -23,10 +23,63 @@
from loopx.control_plane.todos import provider_update
from loopx.entrypoint import main
from loopx.control_plane.coordination.local_authority import (
+ CanonicalAuthorityRequired,
LocalCoordinationAuthorityUnavailable,
read_canonical_todos_if_promoted,
)
-from loopx.todos import add_goal_todo, update_goal_todo
+from loopx.todos import add_goal_todo, complete_goal_todo, update_goal_todo
+
+
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+@pytest.mark.parametrize("policy", ["primary_review", "review_handoff"])
+def test_imported_policy_requires_repair_before_copy_edit(tmp_path, monkeypatch, provider, policy):
+ from loopx.control_plane.testing.canary_harness import run_json_cli_result
+
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ root = tmp_path.resolve()
+ runtime, state, registry = root / "runtime", root / "state.md", root / "registry.json"
+ goal, target = "policy-repair", "todo_imported"
+ state.write_text("# Goal\n\n## Agent Todo\n\n- [ ] Imported work.\n"
+ f" \n")
+ registry.write_text(json.dumps({"common_runtime_root": str(runtime), "goals": [{
+ "id": goal, "repo": str(root), "state_file": state.name,
+ "coordination": {"registered_agents": ["agent-a", "agent-b"]}}]}))
+ projection = build_todo_runtime_shadow_projection(goal_id=goal, handoff_mode="soft_claim", todos=[{
+ "schema_version": "todo_item_v0", "done": False,
+ "archive_state": "active", "source_section": "Agent Todo",
+ "todo_id": target, "text": "Imported work.", "role": "agent", "status": "open",
+ "task_class": "advancement_task", "removed_continuation_policy": policy,
+ }])
+ initialize_canonical_authority(runtime, goal, projection, state_path=state, provider=provider)
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=goal)
+ document = state.read_bytes()
+ prefix = ("todo", "update", "--goal-id", goal, "--todo-id", target, "--agent-id", "agent-a")
+ for edit in (("--text", "Unreviewed replacement."), ("--note", "Unreviewed annotation.")):
+ for preview in (("--dry-run",), ()):
+ code, rejected = run_json_cli_result(*prefix, *edit, *preview, registry_path=registry)
+ assert code == 1 and "repair it explicitly" in rejected["error"], rejected
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=goal) == before
+ assert state.read_bytes() == document
+ code, repaired = run_json_cli_result(
+ *prefix, "--continuation-policy", "independent_handoff", "--excluded-agent", "agent-b",
+ "--update-operation-id", "explicit-repair", registry_path=registry)
+ assert code == 0 and repaired["status"] == "applied", repaired
+ readback = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=goal)
+ assert readback["source_authority"] == f"{provider}_v0"
+ todo = readback["todos"][0]
+ assert todo["continuation_policy"] == "independent_handoff"
+ assert todo["excluded_agents"] == ["agent-b"]
+ assert not todo.get("removed_continuation_policy")
+ assert f"continuation_policy={policy}" not in state.read_text()
+ assert repaired["projection_delivery"] != "pending", repaired
+ code, edited = run_json_cli_result(*prefix, "--note", "After repair.", registry_path=registry)
+ assert code == 0 and edited["status"] == "applied", edited
+ later = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=goal)
+ code, replay = run_json_cli_result(
+ *prefix, "--continuation-policy", "independent_handoff", "--excluded-agent", "agent-b",
+ "--update-operation-id", "explicit-repair", registry_path=registry)
+ assert code == 0 and replay["status"] == "replayed", replay
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=goal)["todos"] == later["todos"]
@pytest.mark.parametrize("provider", ["file", "sqlite"])
@@ -140,14 +193,12 @@ def update_workspace(tmp_path, monkeypatch, request):
return registry, runtime, state, created["todo_id"]
-@pytest.fixture(params=[False, True], ids=["writer-present", "writer-absent"])
-def cli_update(tmp_path, monkeypatch, request, update_workspace):
+@pytest.fixture
+def cli_update(tmp_path, monkeypatch, update_workspace):
package_root = tmp_path / "package"
package = package_root / "loopx"
shutil.copytree(Path(__file__).resolve().parents[2] / "loopx", package,
ignore=shutil.ignore_patterns("__pycache__"))
- if request.param:
- (package / "control_plane/todos/line_update.py").unlink()
provenance = tmp_path / "cli-origin.txt"
(package_root / "sitecustomize.py").write_text(
"import importlib.util\nfrom pathlib import Path\n"
@@ -299,7 +350,10 @@ def dispatch(method, request, **kwargs):
assert caught.value.payload["recovery_command"][-1] == calls[0]["operation_id"]
-def test_unpromoted_facade_preserves_empty_edit_no_change(tmp_path):
+@pytest.mark.parametrize("dry_run", [False, True], ids=["execute", "dry-run"])
+def test_unpromoted_facade_rejects_create_and_update_without_writes(
+ tmp_path, dry_run,
+):
project = tmp_path / "project"
project.mkdir()
state = project / "state.md"
@@ -308,11 +362,48 @@ def test_unpromoted_facade_preserves_empty_edit_no_change(tmp_path):
registry.write_text(json.dumps({"schema_version": 1, "common_runtime_root": str(tmp_path / "runtime"),
"goals": [{"id": "goal-a", "repo": str(project), "state_file": "state.md",
"coordination": {"registered_agents": ["agent-a"]}}]}))
- created = add_goal_todo(registry_path=registry, goal_id="goal-a", role="agent",
- text="Keep legacy compatibility", agent_id="agent-a", note="Keep the note")
before = state.read_bytes()
- for intent in [{}, {"note": ""}, {"note": " \t\n"}]:
- result = update_goal_todo(registry_path=registry, goal_id="goal-a", todo_id=created["todo_id"],
- agent_id="agent-a", **intent)
- assert result["ok"] and result["changed"] is False
- assert state.read_bytes() == before
+ with pytest.raises(CanonicalAuthorityRequired) as create_error:
+ add_goal_todo(
+ registry_path=registry,
+ goal_id="goal-a",
+ role="agent",
+ text="Migrate before creating this Todo",
+ agent_id="agent-a",
+ dry_run=dry_run,
+ )
+ with pytest.raises(CanonicalAuthorityRequired) as update_error:
+ update_goal_todo(
+ registry_path=registry,
+ goal_id="goal-a",
+ todo_id="todo_existing",
+ note="Migrate before editing this Todo",
+ agent_id="agent-a",
+ dry_run=dry_run,
+ )
+ with pytest.raises(CanonicalAuthorityRequired) as terminal_error:
+ complete_goal_todo(
+ registry_path=registry,
+ goal_id="goal-a",
+ todo_id="todo_existing",
+ agent_id="agent-a",
+ evidence="Migrate before completing this Todo",
+ no_followup=True,
+ dry_run=dry_run,
+ )
+ for error, operation in (
+ (create_error.value, "todo_create"),
+ (update_error.value, "todo_update"),
+ (terminal_error.value, "todo_terminal"),
+ ):
+ assert error.code == "canonical_authority_required"
+ assert error.payload["operation"] == operation
+ assert error.payload["write_admitted"] is False
+ assert error.payload["decision_read_from_provider"] is False
+ assert error.payload["legacy_fallback_used"] is False
+ assert error.payload["recovery"] == {
+ "action": "migrate_goal_authority",
+ "backup_required": True,
+ }
+ assert state.read_bytes() == before
+ assert not (tmp_path / "runtime" / "authority").exists()
diff --git a/tests/control_plane/test_causal_blocked_closeout_cli.py b/tests/control_plane/test_causal_blocked_closeout_cli.py
index a91d232323..6582deaf5a 100644
--- a/tests/control_plane/test_causal_blocked_closeout_cli.py
+++ b/tests/control_plane/test_causal_blocked_closeout_cli.py
@@ -78,7 +78,7 @@ def cli(*args: str, cwd: Path = project) -> tuple[int, dict]:
if defer:
next(todo for todo in listed["todos"] if todo["todo_id"] == TODO_ID)["claimed_by"] = AGENT_ID
initialize_canonical_authority(runtime, GOAL_ID, build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, todos=listed["todos"], handoff_mode="legacy" if defer else "soft_claim", leases=[],
+ goal_id=GOAL_ID, todos=listed["todos"], handoff_mode="hard_lease" if defer else "soft_claim", leases=[],
), state_path=state, provider=provider)
binding = ("--agent-id", AGENT_ID, "--todo-id", TODO_ID,
"--turn-instance-id", f"causal-blocked-{kind}-{provider}")
diff --git a/tests/control_plane/test_chat_cold_source_import.py b/tests/control_plane/test_chat_cold_source_import.py
index b58658484a..e5bedaa830 100644
--- a/tests/control_plane/test_chat_cold_source_import.py
+++ b/tests/control_plane/test_chat_cold_source_import.py
@@ -14,17 +14,19 @@
from test_cold_source_import_cli import workspace
-@pytest.fixture(params=[True, False], ids=["producers-present", "four-producers-absent"])
-def capture_present(request):
+@pytest.fixture(params=[True, False], ids=["source-modules-present", "source-modules-absent"])
+def source_modules_present(request):
return request.param
@pytest.fixture(params=[False, True], ids=["physical-registry", "external-registry-parent-alias"])
-def cold_api(tmp_path, monkeypatch, request, capture_present):
+def cold_api(tmp_path, monkeypatch, request, source_modules_present):
_, state, _, body, runtime, receiver, env = workspace(tmp_path, monkeypatch)
- if not capture_present:
+ assert not (
+ receiver / "loopx/control_plane/coordination/runtime_shadow_writer_adapter.py"
+ ).exists()
+ if not source_modules_present:
for name in ("todos.py", "bootstrap.py",
- "control_plane/coordination/runtime_shadow_writer_adapter.py",
"control_plane/coordination/local_authority_shadow_outbox.py"):
# The consumer-retirement fixture can already have removed part
# of this same set before the cold receiver is copied.
diff --git a/tests/control_plane/test_chat_completion_execution_proof.py b/tests/control_plane/test_chat_completion_execution_proof.py
index 8c49fcbf71..7f0e93319d 100644
--- a/tests/control_plane/test_chat_completion_execution_proof.py
+++ b/tests/control_plane/test_chat_completion_execution_proof.py
@@ -8,10 +8,9 @@
import pytest
-from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
+from canonical_authority_fixture import isolate_sqlite_runtime, promoted_create_fixture
from loopx.chat_action_store import ChatActionStore
from loopx.chat_actions import ChatActionService
-from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
from loopx.control_plane.work_items.task_lease import acquire_task_lease, inspect_task_lease, release_task_lease
from loopx.todos import add_goal_todo, list_goal_todos
@@ -19,25 +18,22 @@
def fixture(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str, *,
ttl_seconds: int = 600, validation: bool = False):
isolate_sqlite_runtime(tmp_path, monkeypatch)
- state = tmp_path / "state.md"
- state.write_text("# Goal\n\n## Agent Todo\n\n## Completed Work Archive\n")
- runtime = tmp_path / "runtime"
- registry = tmp_path / "registry.json"
- registry.write_text(json.dumps({"common_runtime_root": str(runtime), "goals": [{
- "id": "goal-a", "repo": str(tmp_path), "state_file": state.name,
- "coordination": {"registered_agents": ["agent-a", "agent-b"], "handoff_mode": "hard_lease"},
- }]}))
+ # Create through the native owner after selecting an empty canonical store.
+ # The retired Markdown writer cannot bootstrap a completion-proof fixture.
+ registry, runtime, state = promoted_create_fixture(
+ tmp_path, provider=provider, handoff_mode="hard_lease",
+ registered_agents=["agent-a", "agent-b"],
+ )
marker = tmp_path / "validation-count"
command = shlex.join([sys.executable, "-c", "from pathlib import Path; "
f"p=Path({str(marker)!r}); p.write_text(p.read_text()+'x' if p.exists() else 'x')"])
- # These transport tests start with canonical authority; fixture creation
- # must not depend on an unpromoted Markdown writer before the first lease.
- initialize_canonical_authority(runtime, "goal-a",
- build_todo_runtime_shadow_projection(goal_id="goal-a", todos=[], handoff_mode="hard_lease"),
- state_path=state, provider=provider)
todo_id = add_goal_todo(registry_path=registry, goal_id="goal-a", role="agent",
text="Complete independently validated work", claimed_by="agent-a",
validation_command=command if validation else None)["todo_id"]
+ readback = list_goal_todos(registry_path=registry, goal_id="goal-a")
+ assert readback["authority_read"]["source_authority"] == f"{provider}_v0"
+ assert readback["authority_read"]["decision_read_from_provider"] is True
+ assert readback["authority_read"]["legacy_fallback_used"] is False
lease = acquire_task_lease(registry_path=registry, runtime_root=runtime, goal_id="goal-a",
todo_id=todo_id, owner="agent-a", idempotency_key="original-execution",
ttl_seconds=ttl_seconds, expected_version=0)["lease"]
diff --git a/tests/control_plane/test_chat_gate_decisions.py b/tests/control_plane/test_chat_gate_decisions.py
index 6f8e57dd11..8263532394 100644
--- a/tests/control_plane/test_chat_gate_decisions.py
+++ b/tests/control_plane/test_chat_gate_decisions.py
@@ -12,6 +12,10 @@
)
from loopx.chat_action_store import ChatActionStore
from loopx.chat_actions import ChatActionService, ProtectedActionGate
+from loopx.control_plane.coordination.coordination_state_contract import (
+ TODO_DOMAIN_READ_RECORD_SCHEMA_VERSION,
+ TODO_DOMAIN_RECORD_FIELDS,
+)
from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
from loopx.todos import add_goal_todo, list_goal_todos
@@ -23,16 +27,20 @@ def _goal(tmp_path: Path, monkeypatch, provider: str, handoff_mode: str = "soft_
_, state, registry = _write_fixture(tmp_path)
config = json.loads(registry.read_text())
config["common_runtime_root"] = str(tmp_path / "runtime")
+ config["goals"][0]["coordination"]["handoff_mode"] = handoff_mode
registry.write_text(json.dumps(config))
+ projection = build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, handoff_mode=handoff_mode, todos=[],
+ )
+ projection["todo_read_model"] = {
+ **projection["todo_read_model"],
+ "schema_version": TODO_DOMAIN_READ_RECORD_SCHEMA_VERSION,
+ "contract_fields": list(TODO_DOMAIN_RECORD_FIELDS),
+ }
+ initialize_canonical_authority(
+ tmp_path / "runtime", GOAL_ID, projection, state_path=state, provider=provider,
+ )
target, gate = _add_target_and_gate(registry, required_scopes=[PUBLISH_SCOPE], target_status="blocked")
- if provider != "legacy":
- config["goals"][0]["coordination"]["handoff_mode"] = handoff_mode
- registry.write_text(json.dumps(config))
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, handoff_mode=handoff_mode,
- todos=list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"])
- initialize_canonical_authority(tmp_path / "runtime", GOAL_ID, projection,
- state_path=state, provider=provider)
service = ChatActionService(store=ChatActionStore(tmp_path / "actions"), registry_path=registry)
return registry, service, target["todo_id"], gate["todo_id"]
@@ -51,15 +59,14 @@ def _rows(registry: Path) -> dict[str, dict]:
@pytest.mark.parametrize(("provider", "handoff_mode"),
- [("legacy", "soft_claim"), ("file", "hard_lease"), ("sqlite", "soft_claim")])
+ [("file", "hard_lease"), ("sqlite", "soft_claim")])
@pytest.mark.parametrize("decision", ["approve", "reject", "cancel"])
def test_app_decision_commits_through_user_completion(tmp_path, monkeypatch, provider, handoff_mode, decision):
registry, service, target_id, gate_id = _goal(tmp_path, monkeypatch, provider, handoff_mode)
proposal = _preview(service, gate_id, decision)
assert proposal["status"] == "preview_ready"
basis = proposal.get("canonical_update_basis")
- assert (basis or {}).get("schema_version") == (
- None if provider == "legacy" else "loopx_chat_canonical_terminal_basis_v0")
+ assert (basis or {}).get("schema_version") == "loopx_chat_canonical_terminal_basis_v0"
assert _rows(registry)[gate_id]["status"] != "done", "preview must not write"
applied = service.apply(proposal["proposal_id"])["proposal"]
@@ -75,7 +82,7 @@ def test_app_decision_commits_through_user_completion(tmp_path, monkeypatch, pro
assert _rows(registry) == rows
-@pytest.mark.parametrize("provider", ["legacy", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_changed_goal_turns_decision_stale_without_writing(tmp_path, monkeypatch, provider):
registry, service, target_id, gate_id = _goal(tmp_path, monkeypatch, provider)
proposal = _preview(service, gate_id, "approve")
diff --git a/tests/control_plane/test_chat_goal_ownership.py b/tests/control_plane/test_chat_goal_ownership.py
index cc7de9dece..e9029865bc 100644
--- a/tests/control_plane/test_chat_goal_ownership.py
+++ b/tests/control_plane/test_chat_goal_ownership.py
@@ -229,6 +229,10 @@ def test_pending_lease_downgrade_rejected_even_after_clock_expiry(api):
claimed_by="agent-a",
task_class="advancement_task",
)
+ # Migrate the historical policy through the real App owner before execution.
+ upgrade = preview(call, "hard_lease")
+ code, migrated = apply(call, upgrade)
+ assert code == 200 and migrated["ok"], migrated
# Use the real acquire owner; an expired lease is not proof the host stopped.
acquired = acquire_task_lease(
registry_path=registry,
diff --git a/tests/control_plane/test_chat_goal_storage.py b/tests/control_plane/test_chat_goal_storage.py
index 9da0e1d39b..6df840385c 100644
--- a/tests/control_plane/test_chat_goal_storage.py
+++ b/tests/control_plane/test_chat_goal_storage.py
@@ -24,12 +24,13 @@
@pytest.fixture(params=["file", "sqlite"])
def api(tmp_path, monkeypatch, request):
isolate_sqlite_runtime(tmp_path, monkeypatch)
+ provider, handoff_mode = request.param if isinstance(request.param, tuple) else (request.param, "hard_lease")
runtime = tmp_path / "runtime"
registry = tmp_path / "registry.json"
rows = []
for goal in ["example", "other"]:
state = tmp_path / f"{goal}.md"
- state.write_text("---\nhandoff_mode: legacy\n---\n\n## Agent Todo\n")
+ state.write_text(f"---\nhandoff_mode: {handoff_mode}\n---\n\n## Agent Todo\n")
rows.append(
{
"id": goal,
@@ -39,10 +40,10 @@ def api(tmp_path, monkeypatch, request):
}
)
projection = build_todo_runtime_shadow_projection(
- goal_id=goal, handoff_mode="legacy", todos=[]
+ goal_id=goal, handoff_mode=handoff_mode, todos=[]
)
initialize_canonical_authority(
- runtime, goal, projection, state_path=state, provider=request.param
+ runtime, goal, projection, state_path=state, provider=provider
)
registry.write_text(
json.dumps({"common_runtime_root": str(runtime), "goals": rows})
@@ -177,11 +178,36 @@ def test_no_automatic_migration_or_caller_paths_and_active_lease_rejected(api):
import time
time.sleep(1.1)
leased = read(runtime)
+ from datetime import datetime, timezone
+ assert datetime.fromisoformat(acquired["lease"]["expires_at"]) <= datetime.now(timezone.utc)
+ assert leased["leases"][0]["status"] == "active", "Expiry does not settle retained execution"
assert call()[1]["current"]["unsettled_lease_count"] == 1
assert call("preview", {"goal_id": "example", "provider": target})[0] == 409
assert read(runtime) == leased and call()[1]["current"]["provider"] == initial
+@pytest.mark.parametrize("api", [
+ pytest.param(("file", "legacy"), id="file-legacy"),
+ pytest.param(("sqlite", "legacy"), id="sqlite-legacy"),
+], indirect=True)
+def test_retired_policy_remains_inspectable_but_cannot_grant_host_execution(api):
+ from loopx.control_plane.work_items.task_lease import acquire_task_lease, TaskLeaseError
+
+ call, runtime, registry = api
+ initial = call()[1]["current"]["provider"]
+ task = add_goal_todo(registry_path=registry, goal_id="example", role="agent",
+ text="Retained legacy ownership", claimed_by="agent-a")
+ before = read(runtime)
+ display = (runtime.parent / "example.md").read_bytes()
+ with pytest.raises(TaskLeaseError) as rejected:
+ acquire_task_lease(registry_path=registry, runtime_root=runtime, goal_id="example",
+ todo_id=task["todo_id"], owner="agent-a", idempotency_key="retired-host", ttl_seconds=60)
+ assert rejected.value.code == "handoff_mode_retired"
+ assert read(runtime) == before and before["leases"] == []
+ assert (runtime.parent / "example.md").read_bytes() == display
+ assert call()[1]["current"]["provider"] == initial
+
+
def test_original_receipt_is_readable_when_live_provider_is_unavailable(api):
call, runtime, _ = api
initial = call()[1]["current"]["provider"]
@@ -245,17 +271,23 @@ def test_a_preview_cannot_bypass_a_later_host_lease_and_requires_a_fresh_plan(ap
assert read(runtime) == copied
-@pytest.fixture(params=[False, True], ids=["cold", "retained-capture"])
-def old_api(tmp_path, monkeypatch, request):
- from tests.control_plane.test_cold_source_inspection import cold_workspace
+@pytest.fixture
+def old_api(tmp_path, monkeypatch):
from tests.control_plane.shadow_e2e_fixture import workspace
isolate_sqlite_runtime(tmp_path, monkeypatch)
- ws = workspace(tmp_path) if request.param else cold_workspace(tmp_path)
- if request.param:
- ws.add("Private original history must stay on disk")
- outbox = ws.runtime / "authority-shadow" / "outbox" / ws.goal / "todos"
- (outbox / "original-unrecognized.json").write_bytes(b"{malformed original outbox")
+ ws = workspace(tmp_path, bootstrap=False)
+ registry = json.loads(ws.registry.read_text())
+ registry["goals"][0]["coordination"].pop("runtime_shadow")
+ ws.registry.write_text(json.dumps(registry))
+ ws.state.write_text(
+ "---\ngoal_id: goal-e2e\nhandoff_mode: soft_claim\n---\n"
+ "## Agent Todo\n- [ ] Keep active\n"
+ " \n"
+ "## Todo Archive\n- [x] Keep archived evidence\n"
+ " \n"
+ )
directory = ws.runtime / "goals" / ws.goal / "task-leases"
directory.mkdir(parents=True, exist_ok=True)
(directory / "removed.json").write_text(json.dumps({
@@ -280,7 +312,7 @@ def call():
assert "old-work" not in encoded and "malformed original outbox" not in encoded
return response.status, result
- yield call, ws, request.param
+ yield call, ws, False
server.shutdown()
thread.join(5)
server.server_close()
@@ -300,11 +332,12 @@ def test_registered_old_source_read_failure_is_unavailable_and_recovers(old_api)
def test_old_source_http_inventory_keeps_history_and_does_not_grant_import(old_api):
- from tests.control_plane.test_cold_source_inspection import capture_bytes
-
call, ws, captured = old_api
- before = capture_bytes(ws)
source = ws.state.read_bytes(), ws.registry.read_bytes()
+ leases = {
+ path: path.read_bytes()
+ for path in (ws.runtime / "goals" / ws.goal / "task-leases").glob("*.json")
+ }
for _ in range(2):
code, result = call()
assert code == 200 and result["ok"], result
@@ -319,8 +352,8 @@ def test_old_source_http_inventory_keeps_history_and_does_not_grant_import(old_a
assert old["writer_stop_verified"] is old["outbox_reconciliation_verified"] is old["import_ready"] is False
assert result["authority_changed"] is result["execution_authority_granted"] is False
assert "outbox_review" not in json.dumps(result)
- assert before == capture_bytes(ws)
assert source == (ws.state.read_bytes(), ws.registry.read_bytes())
+ assert leases == {path: path.read_bytes() for path in leases}
assert not (ws.runtime / "authority").exists()
diff --git a/tests/control_plane/test_chat_reviewed_create.py b/tests/control_plane/test_chat_reviewed_create.py
index bc09978a3c..c29a70d3fa 100644
--- a/tests/control_plane/test_chat_reviewed_create.py
+++ b/tests/control_plane/test_chat_reviewed_create.py
@@ -8,7 +8,7 @@
@pytest.mark.parametrize("provider", ["file", "sqlite"])
-@pytest.mark.parametrize("timing", ["before-confirmation", "after-final-preview"])
+@pytest.mark.parametrize("timing", ["before-confirmation", "between-preview-reads", "after-final-preview"])
def test_concurrent_create_refuses_reviewed_intent(tmp_path, monkeypatch, provider, timing):
isolate_sqlite_runtime(tmp_path, monkeypatch)
registry, runtime, _ = promoted_create_fixture(tmp_path, provider=provider)
@@ -25,7 +25,9 @@ def independent_write():
original = chat._add_review_todo
def interleaved(**kwargs):
- if not kwargs["dry_run"]:
+ if (timing == "after-final-preview" and not kwargs["dry_run"]) or (
+ timing == "between-preview-reads" and kwargs["dry_run"] and kwargs.get("operation_id")
+ ):
independent_write()
return original(**kwargs)
@@ -38,6 +40,15 @@ def interleaved(**kwargs):
assert ("current_preview_id" in conflict.value.receipt) is (timing == "before-confirmation")
snapshot = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
assert [todo["text"] for todo in snapshot["todos"]] == ["[P1] Independent accepted request"]
+ if timing != "before-confirmation":
+ monkeypatch.setattr(chat, "_add_review_todo", original)
+ refreshed = chat.build_todo_review_preview(
+ registry_path=registry, goal_id="goal-a", text="Reviewed request")
+ applied = chat.apply_todo_review_preview(registry_path=registry, goal_id="goal-a",
+ text="Reviewed request", preview_id=refreshed["preview_id"])
+ assert applied["todo"]["todo_id"] == refreshed["todo"]["todo_id"]
+ snapshot = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
+ assert {todo["text"] for todo in snapshot["todos"]} == {"[P1] Independent accepted request", "[P1] Reviewed request"}
@pytest.mark.parametrize("provider", ["file", "sqlite"])
@@ -45,10 +56,29 @@ def test_current_review_creates_once_and_duplicate_remains_noop(tmp_path, monkey
isolate_sqlite_runtime(tmp_path, monkeypatch)
registry, runtime, _ = promoted_create_fixture(tmp_path, provider=provider)
for _ in range(2):
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
preview = chat.build_todo_review_preview(
registry_path=registry, goal_id="goal-a", text="Reviewed request")
+ assert chat.build_todo_review_preview(
+ registry_path=registry, goal_id="goal-a", text="Reviewed request") == preview
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == before
result = chat.apply_todo_review_preview(registry_path=registry, goal_id="goal-a",
text="Reviewed request", preview_id=preview["preview_id"])
assert result["applied"] is True
+ assert result["todo"]["todo_id"] == preview["todo"]["todo_id"]
snapshot = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
assert [todo["text"] for todo in snapshot["todos"]] == ["[P1] Reviewed request"]
+
+
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_changed_intent_cannot_apply_another_preview(tmp_path, monkeypatch, provider):
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, runtime, _ = promoted_create_fixture(tmp_path, provider=provider)
+ preview = chat.build_todo_review_preview(
+ registry_path=registry, goal_id="goal-a", text="Reviewed request")
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
+ for changed in ({"text": "Different request"}, {"text": "Reviewed request", "priority": "P0"}):
+ with pytest.raises(chat.TodoReviewPreviewConflict):
+ chat.apply_todo_review_preview(registry_path=registry, goal_id="goal-a",
+ preview_id=preview["preview_id"], **changed)
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == before
diff --git a/tests/control_plane/test_chat_user_action_handling.py b/tests/control_plane/test_chat_user_action_handling.py
index f9685e7484..c05e9cf228 100644
--- a/tests/control_plane/test_chat_user_action_handling.py
+++ b/tests/control_plane/test_chat_user_action_handling.py
@@ -15,11 +15,15 @@
from test_todo_decision_scope_lifecycle import AGENT_ID, GOAL_ID, _write_fixture
from loopx.chat_action_store import ChatActionStore
from loopx.chat_actions import ChatActionService
+from loopx.control_plane.coordination.coordination_state_contract import (
+ TODO_DOMAIN_READ_RECORD_SCHEMA_VERSION,
+ TODO_DOMAIN_RECORD_FIELDS,
+)
from loopx.control_plane.coordination.local_authority import LocalCoordinationAuthorityUnavailable
from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
from loopx.todos import add_goal_todo, list_goal_todos
-PROVIDERS = [("legacy", "soft_claim"), ("file", "hard_lease"), ("sqlite", "soft_claim")]
+PROVIDERS = [("file", "hard_lease"), ("sqlite", "soft_claim")]
def _goal(tmp_path: Path, monkeypatch, provider: str, handoff_mode: str):
@@ -27,21 +31,25 @@ def _goal(tmp_path: Path, monkeypatch, provider: str, handoff_mode: str):
_, state, registry = _write_fixture(tmp_path)
config = json.loads(registry.read_text())
config["common_runtime_root"] = str(tmp_path / "runtime")
+ config["goals"][0]["coordination"]["handoff_mode"] = handoff_mode
registry.write_text(json.dumps(config))
+ projection = build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, handoff_mode=handoff_mode, todos=[],
+ )
+ projection["todo_read_model"] = {
+ **projection["todo_read_model"],
+ "schema_version": TODO_DOMAIN_READ_RECORD_SCHEMA_VERSION,
+ "contract_fields": list(TODO_DOMAIN_RECORD_FIELDS),
+ }
+ initialize_canonical_authority(
+ tmp_path / "runtime", GOAL_ID, projection, state_path=state, provider=provider,
+ )
target = add_goal_todo(registry_path=registry, goal_id=GOAL_ID, role="agent",
text="Continue once the owner has created the sessions.", status="blocked",
task_class="advancement_task", claimed_by=AGENT_ID)
action = add_goal_todo(registry_path=registry, goal_id=GOAL_ID, role="user",
text="Create the remaining role sessions in the desktop App.", task_class="user_action", bound_agent=AGENT_ID,
unblocks_todo_id=target["todo_id"])
- if provider != "legacy":
- config["goals"][0]["coordination"]["handoff_mode"] = handoff_mode
- registry.write_text(json.dumps(config))
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, handoff_mode=handoff_mode,
- todos=list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"])
- initialize_canonical_authority(tmp_path / "runtime", GOAL_ID, projection,
- state_path=state, provider=provider)
service = ChatActionService(store=ChatActionStore(tmp_path / "actions"), registry_path=registry)
return registry, service, target["todo_id"], action["todo_id"]
diff --git a/tests/control_plane/test_closed_monitor_replay.py b/tests/control_plane/test_closed_monitor_replay.py
index be38f7a6f2..85cd58c06b 100644
--- a/tests/control_plane/test_closed_monitor_replay.py
+++ b/tests/control_plane/test_closed_monitor_replay.py
@@ -5,31 +5,24 @@
import pytest
-from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
-from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
+from canonical_authority_fixture import isolate_sqlite_runtime
from loopx.control_plane.testing.canary_harness import run_json_cli, run_json_cli_result
-from loopx.todos import add_goal_todo, list_goal_todos
+from loopx.todos import add_goal_todo
from tests.control_plane.test_monitor_followthrough_contract import (
AGENT_ID, GOAL_ID, _add_monitor, _write_fixture,
)
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("material", [False, True])
@pytest.mark.parametrize("lifecycle", ["complete", "supersede"])
def test_closed_monitor_replays_original_no_spend_turn(
tmp_path, monkeypatch, provider, material, lifecycle,
):
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state = _write_fixture(tmp_path)
+ registry, runtime, _state = _write_fixture(tmp_path, provider=provider)
monitor = _add_monitor(registry, text="Observe a public target",
target_key="public-target", next_due_at="2000-01-01T00:00:00Z")
- if provider != "legacy":
- todos = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]
- projection = build_todo_runtime_shadow_projection(goal_id=GOAL_ID,
- todos=todos, handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, GOAL_ID, projection,
- state_path=state, provider=provider)
def call(*args):
return run_json_cli(*args, registry_path=registry, runtime_root=runtime)
diff --git a/tests/control_plane/test_cold_source_disposition_e2e.py b/tests/control_plane/test_cold_source_disposition_e2e.py
deleted file mode 100644
index 9097f6e8e1..0000000000
--- a/tests/control_plane/test_cold_source_disposition_e2e.py
+++ /dev/null
@@ -1,439 +0,0 @@
-"""Retained originals are handled without bringing back their Python producers.
-
-The original CLI creates real interrupted writes. A separate copied runtime
-then dispatches the shipped TS effects with those producer files absent. The
-OS-lock adapter remains: language retirement must preserve useful Host IO.
-"""
-from __future__ import annotations
-
-import json
-import os
-from pathlib import Path
-import shutil
-import subprocess
-import sys
-import tarfile
-
-import loopx
-import pytest
-
-from loopx.control_plane.coordination.coordination_state_contract_generated import (
- LOCAL_AUTHORITY_SHADOW_READ_REQUEST_SCHEMA,
-)
-from tests.control_plane.shadow_e2e_fixture import workspace
-from tests.control_plane.canonical_authority_fixture import isolate_sqlite_runtime
-
-
-pytestmark = pytest.mark.stage2c_e2e
-
-
-@pytest.fixture(scope="module")
-def receiver_package(tmp_path_factory):
- root = tmp_path_factory.mktemp("receiver")
- package = root / "loopx"
- shutil.copytree(Path(loopx.__file__).parent, package,
- ignore=shutil.ignore_patterns("__pycache__", "testing", "*.pyc"))
- removed = ["todos.py", "bootstrap.py",
- "control_plane/coordination/runtime_shadow_writer_adapter.py",
- "control_plane/coordination/local_authority_shadow_outbox.py"]
- for relative in removed:
- (package / relative).unlink()
- assert all(not (package / relative).exists() for relative in removed)
- assert (package / "control_plane/coordination/shadow_lock_host.py").is_file()
- return package
-
-
-@pytest.fixture(scope="module")
-def receiver(receiver_package):
- package = receiver_package
- root = package.parent
- module = package / "control_plane/effect_runtime_handlers.ts"
- script = (
- f"import {{createEffectRuntimeHandlers, dispatchEffectRuntimeMethod}} from {json.dumps(module.as_uri())};"
- "let raw=''; for await (const chunk of process.stdin) raw+=chunk;"
- "const {method,request}=JSON.parse(raw);"
- "const handlers=createEffectRuntimeHandlers({fingerprint:'disposable',requestShutdown:()=>{}});"
- "console.log(JSON.stringify(await dispatchEffectRuntimeMethod(handlers,method,request)));"
- )
-
- def dispatch(method, request):
- result = subprocess.run(
- ["node", "--no-warnings", "--experimental-strip-types", "--input-type=module", "-e", script],
- input=json.dumps({"method": method, "request": request}),
- cwd=root, capture_output=True, text=True, timeout=45,
- )
- assert result.returncode == 0, result.stdout + result.stderr
- return json.loads(result.stdout)
-
- return dispatch
-
-
-def drain(receiver, fixture):
- return receiver("coordination.runtime_shadow.drain", {
- "schema_version": "loopx_shadow_drain_v0", "runtime_root": str(fixture.runtime),
- "goal_id": fixture.goal, "python_executable": sys.executable, "config_enabled": True,
- "max_entries": 32, "budget_seconds": 10, "lock_timeout_seconds": 2,
- })
-
-
-def inspect(fixture):
- return fixture.cli("coordination-shadow", "inspect-source")["source_inventory"]
-
-
-def originals(path):
- return {str(p.relative_to(path)): p.read_bytes() for p in path.rglob("*")
- if p.is_file() and not p.name.endswith(".lock")}
-
-
-def cold_workspace(path):
- fixture = workspace(path, bootstrap=False)
- registry = fixture.state.parent / ".loopx/registry.json"
- registry.parent.mkdir()
- fixture.registry.rename(registry)
- fixture.registry = registry
- fixture.cli("coordination-shadow", "bootstrap", "--execute")
- return fixture
-
-
-def cold_cli(fixture, receiver_package, action, *args, success=True):
- child = subprocess.run([sys.executable, "-c",
- "import loopx,sys;print(loopx.__file__,file=sys.stderr);"
- "from loopx.entrypoint import main;raise SystemExit(main())",
- *fixture.arguments("coordination-shadow", action, *map(str, args))],
- cwd=fixture.state.parent.parent,
- env={**os.environ, "PYTHONPATH": str(receiver_package.parent)},
- capture_output=True, text=True, timeout=60)
- assert str(receiver_package / "__init__.py") in child.stderr
- assert (child.returncode == 0) is success, child.stdout + child.stderr
- assert "Traceback" not in child.stderr
- return json.loads(child.stdout)["cold_import"]
-
-
-def backup(fixture, name):
- child = subprocess.run([sys.executable, "-c",
- "import loopx,sys;print(loopx.__file__,file=sys.stderr);"
- "from loopx.entrypoint import main;raise SystemExit(main())",
- "--registry", str(fixture.registry), "--runtime-root", str(fixture.runtime),
- "--format", "json", "backup-state", "--project", str(fixture.state.parent),
- "--output-dir", str(fixture.state.parent.parent / "backups"), "--backup-id", name,
- "--current-project-only", "--no-skills", "--no-automations", "--execute"],
- cwd=fixture.state.parent.parent,
- env={**os.environ, "PYTHONPATH": str(Path(loopx.__file__).resolve().parent.parent)},
- capture_output=True, text=True, timeout=60)
- assert str(Path(loopx.__file__).resolve()) in child.stderr
- assert child.returncode == 0, child.stdout + child.stderr
- result = json.loads(child.stdout)
- assert result["ok"], result
- return result
-
-
-def provider_todos(receiver, fixture):
- return receiver("coordination.local_authority.todo_list", {
- "schema_version": "loopx_local_coordination_todo_list_request_v0",
- "runtime_root": str(fixture.runtime), "goal_id": fixture.goal,
- "role": None, "status": None, "todo_id": None, "agent_id": None, "limit": None})
-
-
-@pytest.mark.parametrize("window,resolution,no_op", [
- ("before_replace", "abandoned", True),
- ("before_marker", "committed_proven_by_readback", False),
- ("before_commit", "committed", False),
- ("after_commit", "committed", False),
-])
-def test_stopped_original_todo_recovers_once_without_python_producer(
- tmp_path, receiver, window, resolution, no_op,
-):
- fixture = workspace(tmp_path)
- fixture.crash(window, "todo", "add", "--role", "agent", "--text", "Interrupted original")
- source = fixture.state.read_bytes()
- before = inspect(fixture)
- partition = fixture.runtime / "authority-shadow/outbox" / fixture.goal / "todos"
- prepared = json.loads(next(partition.glob("*.prepared.json")).read_bytes())
- initial = before["capture"]["runtime_shadow_readback"]["proof"]["last_applied_sequences"]["todos"]
- result = drain(receiver, fixture)
- assert result["ok"] is True, result
- assert result["replayed"] == (1 if window == "after_commit" else 0)
- assert result["delivered"] + result["reconciled"] == (0 if window == "after_commit" else 1)
- assert fixture.state.read_bytes() == source
- after = inspect(fixture)
- view = after["capture"]["runtime_shadow_readback"]
- receipt_view = receiver("coordination.runtime_shadow.outbox_read", {
- "schema_version": LOCAL_AUTHORITY_SHADOW_READ_REQUEST_SCHEMA,
- "runtime_root": str(fixture.runtime), "goal_id": fixture.goal, "store_kind": "runtime_shadow",
- "scan_after_cursor": None, "scan_limit": 0, "read_model": "proof",
- "receipt_operation_id": prepared["entry_id"],
- })
- assert receipt_view["status"] == "loaded", receipt_view
- receipt = receipt_view["proof"]["receipt"]["receipts"][0]
- assert receipt["entry_id"] == prepared["entry_id"]
- assert receipt["resolution"] == resolution and receipt["no_op"] is no_op
- assert receipt["seq"] == 1 and view["proof"]["last_sequences"]["todos"] == 1
- assert view["proof"]["last_applied_sequences"]["todos"] == (0 if no_op else 1)
- assert initial == (1 if window == "after_commit" else 0)
- assert [p.name for p in partition.iterdir()] == ["drain-cursor.json"]
- retained = originals(fixture.runtime)
- assert drain(receiver, fixture)["outcome"] == "nothing_pending"
- assert originals(fixture.runtime) == retained
- # Successful original disposition is not stopped-Host/import qualification.
- assert after["import_ready"] is False and after["writer_stop_verified"] is False
- assert not (fixture.runtime / "authority").exists()
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-@pytest.mark.parametrize("window", ["before_commit", "after_commit"])
-def test_original_lease_disposition_requires_release_before_cold_import(
- tmp_path, monkeypatch, receiver, receiver_package, provider, window,
-):
- isolate_sqlite_runtime(tmp_path, monkeypatch)
- fixture = cold_workspace(tmp_path / "project")
- todo = fixture.add("Original leased task")["todo_id"]
- fixture.crash(window, "task-lease", "acquire", "--todo-id", todo, "--owner", "agent-a",
- "--idempotency-key", "original", "--ttl-seconds", "120")
- path = fixture.runtime / "goals" / fixture.goal / "task-leases" / (todo + ".json")
- lease = path.read_bytes()
- assert drain(receiver, fixture)["ok"] is True
- assert path.read_bytes() == lease
- after = inspect(fixture)
- assert after["leases_requiring_settlement"] == [todo]
- assert after["retained_leases"][0]["status"] == "active"
- assert after["import_ready"] is False
- retained = originals(fixture.runtime)
- assert drain(receiver, fixture)["outcome"] == "nothing_pending"
- assert originals(fixture.runtime) == retained and path.read_bytes() == lease
- inventory = inspect(fixture)
- rolled = receiver("coordination.runtime_shadow.rollback", {
- "schema_version": "loopx_coordination_runtime_shadow_rollback_v0",
- "runtime_root": str(fixture.runtime), "goal_id": fixture.goal,
- "operation_id": "retain-lease-original", "expected_bootstrap_operation_id": None,
- "expected_provider_revision": inventory["capture"]["runtime_shadow_readback"]["provider_revision"],
- "projection": inventory["projection"], "source_snapshot": inventory["source_snapshot"]})
- assert rolled["status"] == "applied", rolled
- archived = originals(fixture.runtime / "authority-shadow")
- active_backup = backup(fixture, "inactive-capture-active-lease")
- before = originals(fixture.runtime)
- rejected = cold_cli(fixture, receiver_package, "prepare-import", "--operation-id", "lease-import",
- "--backup-manifest", active_backup["manifest_path"], "--provider", provider,
- "--target-handoff-mode", "hard_lease", success=False)
- assert rejected["reason_code"] == "cold_import_lease_requires_settlement", rejected
- assert originals(fixture.runtime) == before and path.read_bytes() == lease
- assert not list(fixture.runtime.rglob("writer-fence.json"))
- assert not (fixture.runtime / "authority").exists()
- # Capture disposition does not settle the live source lease. Its original
- # owner must release it; neither its old receipt nor import grants work.
- released = fixture.cli("task-lease", "release", "--todo-id", todo, "--owner", "agent-a",
- "--idempotency-key", "original", "--expected-version", str(json.loads(lease)["version"]))
- assert released["released"] and released["lease"]["status"] == "released", released
- settled = path.read_bytes()
- fresh_backup = backup(fixture, "released-before-import")
- plan = cold_cli(fixture, receiver_package, "prepare-import", "--operation-id", "lease-import",
- "--backup-manifest", fresh_backup["manifest_path"], "--provider", provider,
- "--target-handoff-mode", "hard_lease")
- assert plan["source_inventory"]["lease_count"] == 1
- applied = cold_cli(fixture, receiver_package, "apply-import", "--operation-id", "lease-import",
- "--plan-sha256", plan["plan_sha256"], "--writers-stopped", "--execute")
- assert applied["status"] == "applied" and applied["execution_authority_granted"] is False
- current_lease = fixture.cli("task-lease", "inspect", "--todo-id", todo)
- assert current_lease["source_authority"] == f"{provider}_v0"
- assert current_lease["lease"] == released["lease"]
- fixture.cli("todo", "add", "--role", "agent", "--text", "Later lease-import write",
- "--operation-id", "later-lease-import", "--claimed-by", "agent-a")
- current = provider_todos(receiver, fixture)
- assert {row["text"] for row in current["todos"]} == {"[P1] Original leased task", "[P1] Later lease-import write"}
- assert {row["priority"] for row in current["todos"]} == {"P1"}
- fixture.state.unlink()
- replay = cold_cli(fixture, receiver_package, "recover-import", "--operation-id", "lease-import",
- "--plan-sha256", plan["plan_sha256"], "--execute")
- assert replay["status"] == "replayed" and replay["operation_id"] == applied["operation_id"]
- assert provider_todos(receiver, fixture) == current
- assert fixture.cli("task-lease", "inspect", "--todo-id", todo)["lease"] == released["lease"]
- assert path.read_bytes() == settled and originals(fixture.runtime / "authority-shadow") == archived
- with tarfile.open(fresh_backup["archive_path"]) as archive:
- assert archive.extractfile("runtime-root/" + str(path.relative_to(fixture.runtime))).read() == settled
- for relative, data in archived.items():
- assert archive.extractfile("runtime-root/authority-shadow/" + relative).read() == data
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_unproved_original_archival_then_cold_import_keeps_history_inert(
- tmp_path, monkeypatch, receiver, receiver_package, provider,
-):
- isolate_sqlite_runtime(tmp_path, monkeypatch)
- fixture = cold_workspace(tmp_path / "project")
- fixture.crash("before_marker", "handoff-mode", "set", "--mode", "soft_claim")
- fixture.cli("handoff-mode", "set", "--mode", "hard_lease")
- source_todo = fixture.add("Later source requirement")["todo_id"]
- pending_backup = backup(fixture, "ambiguous-original")
- before = originals(fixture.runtime)
- result = drain(receiver, fixture)
- assert result["ok"] is False and result["reason_code"] == "outbox_source_unproved"
- assert originals(fixture.runtime) == before
- inventory = inspect(fixture)
- source = fixture.state.read_bytes()
- artifacts = inventory["capture"]["artifacts"]
- candidate = Path(artifacts["runtime_store"]["path"]).read_bytes()
- pending = originals(Path(artifacts["outbox"]["path"]))
- rejected = cold_cli(fixture, receiver_package, "prepare-import", "--operation-id", "ambiguous-import",
- "--backup-manifest", pending_backup["manifest_path"], "--provider", provider,
- "--target-handoff-mode", "hard_lease", success=False)
- assert rejected["reason_code"] == "cold_import_capture_requires_disposition", rejected
- assert originals(fixture.runtime) == before and fixture.state.read_bytes() == source
- request = {"schema_version": "loopx_coordination_runtime_shadow_rollback_v0",
- "runtime_root": str(fixture.runtime), "goal_id": fixture.goal,
- "operation_id": "retain-original", "expected_bootstrap_operation_id": None,
- "expected_provider_revision": inventory["capture"]["runtime_shadow_readback"]["provider_revision"],
- "projection": inventory["projection"], "source_snapshot": inventory["source_snapshot"]}
- rolled = receiver("coordination.runtime_shadow.rollback", request)
- assert rolled["status"] == "applied", rolled
- assert Path(rolled["candidate_archive_path"]).read_bytes() == candidate
- assert originals(Path(rolled["outbox_archive_path"])) == pending
- retained = originals(fixture.runtime)
- replay = receiver("coordination.runtime_shadow.rollback", request)
- assert replay["status"] == "replayed" and replay["operation_id"] == rolled["operation_id"]
- assert originals(fixture.runtime) == retained and fixture.state.read_bytes() == source
- refused = drain(receiver, fixture)
- assert refused["ok"] is False and refused["outcome"] == "stopped"
- assert refused["delivered"] == 0 and refused["replayed"] == 0
- assert originals(fixture.runtime) == retained
- assert inspect(fixture)["capture"]["outbox_review"] is None
- assert not (fixture.runtime / "authority").exists()
- archived = originals(fixture.runtime / "authority-shadow")
- fresh_backup = backup(fixture, "archived-unproved-original")
- plan = cold_cli(fixture, receiver_package, "prepare-import", "--operation-id", "ambiguous-import",
- "--backup-manifest", fresh_backup["manifest_path"], "--provider", provider,
- "--target-handoff-mode", "hard_lease")
- applied = cold_cli(fixture, receiver_package, "apply-import", "--operation-id", "ambiguous-import",
- "--plan-sha256", plan["plan_sha256"], "--writers-stopped", "--execute")
- assert applied["status"] == "applied" and applied["execution_authority_granted"] is False
- imported = provider_todos(receiver, fixture)
- assert imported["source_authority"] == f"{provider}_v0"
- assert [(row["todo_id"], row["text"], row["priority"]) for row in imported["todos"]] == [
- (source_todo, "[P1] Later source requirement", "P1")]
- # The later current source, not the candidate or ambiguous queue, is the
- # import basis. Unproved entries remain archived and never become receipts.
- assert Path(rolled["candidate_archive_path"]).read_bytes() == candidate
- assert originals(Path(rolled["outbox_archive_path"])) == pending
- fixture.cli("todo", "add", "--role", "agent", "--text", "Later canonical write",
- "--operation-id", "after-ambiguous-import", "--claimed-by", "agent-a")
- current = provider_todos(receiver, fixture)
- assert {row["text"] for row in current["todos"]} == {"[P1] Later source requirement", "[P1] Later canonical write"}
- assert {row["priority"] for row in current["todos"]} == {"P1"}
- fixture.state.unlink()
- recovered = cold_cli(fixture, receiver_package, "recover-import", "--operation-id", "ambiguous-import",
- "--plan-sha256", plan["plan_sha256"], "--execute")
- assert recovered["status"] == "replayed" and recovered["operation_id"] == applied["operation_id"]
- assert provider_todos(receiver, fixture) == current
- assert originals(fixture.runtime / "authority-shadow") == archived
- assert originals(Path(rolled["outbox_archive_path"])) == pending
- for saved in (pending_backup, fresh_backup):
- with tarfile.open(saved["archive_path"]) as archive:
- for relative, data in pending.items():
- prefix = ("runtime-root/authority-shadow/outbox/" + fixture.goal if saved == pending_backup
- else "runtime-root/" + str(Path(rolled["outbox_archive_path"]).relative_to(fixture.runtime)))
- assert archive.extractfile(prefix + "/" + relative).read() == data
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-@pytest.mark.parametrize("window,committed", [
- ("before_replace", False), ("before_marker", True),
- ("before_commit", True), ("after_commit", True),
-])
-def test_original_outbox_disposition_then_cold_import_preserves_receipts_and_later_writes(
- tmp_path, monkeypatch, receiver, receiver_package, provider, window, committed,
-):
- """Join original effect recovery to cold import, without replaying history.
-
- Crash only disposable source writers. The receiver uses the shipped owners
- with the four normal Python producers absent; the real CLI imports the
- settled source, including when the originating interpreter is a wheel.
- """
- isolate_sqlite_runtime(tmp_path, monkeypatch)
- fixture = cold_workspace(tmp_path / "project")
- fixture.crash(window, "todo", "add", "--role", "agent", "--text", "Interrupted original")
- source = fixture.state.read_bytes()
- partition = fixture.runtime / "authority-shadow/outbox" / fixture.goal / "todos"
- prepared_path = next(partition.glob("*.prepared.json"))
- prepared_bytes = prepared_path.read_bytes()
- original_entry = json.loads(prepared_bytes)
-
- pending_backup = backup(fixture, "original-pending")
- before = originals(fixture.runtime)
- refused = cold_cli(fixture, receiver_package, "prepare-import", "--operation-id", "settled-import",
- "--backup-manifest", pending_backup["manifest_path"], "--provider", provider,
- "--target-handoff-mode", "hard_lease", success=False)
- assert refused["reason_code"] == "cold_import_capture_requires_disposition", refused
- assert originals(fixture.runtime) == before
- assert not list(fixture.runtime.rglob("writer-fence.json"))
- assert not (fixture.runtime / "authority").exists()
-
- drained = drain(receiver, fixture)
- assert drained["ok"], drained
- assert fixture.state.read_bytes() == source
- inventory = inspect(fixture)
- proof = receiver("coordination.runtime_shadow.outbox_read", {
- "schema_version": LOCAL_AUTHORITY_SHADOW_READ_REQUEST_SCHEMA,
- "runtime_root": str(fixture.runtime), "goal_id": fixture.goal, "store_kind": "runtime_shadow",
- "scan_after_cursor": None, "scan_limit": 0, "read_model": "proof",
- "receipt_operation_id": original_entry["entry_id"],
- })
- receipt = proof["proof"]["receipt"]["receipts"][0]
- assert receipt["entry_id"] == original_entry["entry_id"]
- assert receipt["no_op"] is (not committed)
- assert receipt["resolution"] == ({
- "before_replace": "abandoned", "before_marker": "committed_proven_by_readback",
- "before_commit": "committed", "after_commit": "committed",
- }[window])
- # Draining an empty queue is insufficient: capture must be stopped through
- # its own revision-bound lifecycle, retaining the original receipt bytes.
- settled_backup = backup(fixture, "drained-active")
- before = originals(fixture.runtime)
- refused = cold_cli(fixture, receiver_package, "prepare-import", "--operation-id", "settled-import",
- "--backup-manifest", settled_backup["manifest_path"], "--provider", provider,
- "--target-handoff-mode", "hard_lease", success=False)
- assert refused["reason_code"] == "cold_import_capture_requires_disposition", refused
- assert originals(fixture.runtime) == before
- request = {"schema_version": "loopx_coordination_runtime_shadow_rollback_v0",
- "runtime_root": str(fixture.runtime), "goal_id": fixture.goal,
- "operation_id": "retain-settled-original", "expected_bootstrap_operation_id": None,
- "expected_provider_revision": inventory["capture"]["runtime_shadow_readback"]["provider_revision"],
- "projection": inventory["projection"], "source_snapshot": inventory["source_snapshot"]}
- rolled = receiver("coordination.runtime_shadow.rollback", request)
- assert rolled["status"] == "applied", rolled
- candidate = Path(rolled["candidate_archive_path"]).read_bytes()
- retained = originals(fixture.runtime / "authority-shadow")
- assert receiver("coordination.runtime_shadow.rollback", request)["status"] == "replayed"
- assert originals(fixture.runtime / "authority-shadow") == retained
- final_backup = backup(fixture, "settled-before-import")
- plan = cold_cli(fixture, receiver_package, "prepare-import", "--operation-id", "settled-import",
- "--backup-manifest", final_backup["manifest_path"], "--provider", provider,
- "--target-handoff-mode", "hard_lease")
- assert plan["status"] == "prepared", plan
- applied = cold_cli(fixture, receiver_package, "apply-import", "--operation-id", "settled-import",
- "--plan-sha256", plan["plan_sha256"], "--writers-stopped", "--execute")
- assert applied["status"] == "applied" and applied["execution_authority_granted"] is False
- assert applied["operation_id"] != original_entry["entry_id"]
- assert fixture.state.read_bytes() == source
- later = fixture.cli("todo", "add", "--role", "agent", "--text", "Later canonical write",
- "--operation-id", "later-write", "--claimed-by", "agent-a")
- # Independent complete provider read, rather than the active-Todo CLI view.
- current = provider_todos(receiver, fixture)
- assert current["source_authority"] == f"{provider}_v0"
- assert {row["text"] for row in current["todos"]} == (
- {"[P1] Interrupted original", "[P1] Later canonical write"} if committed else {"[P1] Later canonical write"})
- assert {row["priority"] for row in current["todos"]} == {"P1"}
- assert len(current["todos"]) == (2 if committed else 1)
- assert later["todo_id"] in {row["todo_id"] for row in current["todos"]}
- fixture.state.unlink()
- replay = cold_cli(fixture, receiver_package, "recover-import", "--operation-id", "settled-import",
- "--plan-sha256", plan["plan_sha256"], "--execute")
- assert replay["status"] == "replayed" and replay["operation_id"] == applied["operation_id"]
- assert provider_todos(receiver, fixture) == current
- assert Path(rolled["candidate_archive_path"]).read_bytes() == candidate
- assert originals(fixture.runtime / "authority-shadow") == retained
- # The original queue and receipts have their own saved history; import does
- # not convert them into a new queue or silently discard the pending backup.
- with tarfile.open(pending_backup["archive_path"]) as archive:
- member = "runtime-root/" + str(prepared_path.relative_to(fixture.runtime))
- assert archive.extractfile(member).read() == prepared_bytes
- with tarfile.open(final_backup["archive_path"]) as archive:
- for relative, data in retained.items():
- assert archive.extractfile("runtime-root/authority-shadow/" + relative).read() == data
diff --git a/tests/control_plane/test_cold_source_import_cli.py b/tests/control_plane/test_cold_source_import_cli.py
index 690d244b9a..2ce7238edf 100644
--- a/tests/control_plane/test_cold_source_import_cli.py
+++ b/tests/control_plane/test_cold_source_import_cli.py
@@ -40,7 +40,7 @@ def canonical_todos(runtime, receiver, env, cwd):
return result
-def workspace(tmp_path, monkeypatch, *, storage_target=None):
+def workspace(tmp_path, monkeypatch, *, storage_target=None, retained_lease=None):
isolate_sqlite_runtime(tmp_path, monkeypatch)
project, runtime = tmp_path / "project", tmp_path / "runtime"
registry = project / ".loopx/registry.json"
@@ -59,6 +59,15 @@ def workspace(tmp_path, monkeypatch, *, storage_target=None):
goal["coordination"]["storage_target"] = storage_target
registry.write_text(json.dumps({"schema_version": 1, "common_runtime_root": str(runtime), "goals": [goal]}))
runtime.mkdir()
+ if retained_lease is not None:
+ # A completed Task may remain in the current graph. Archived Tasks are
+ # separate historical records and do not retain live lease edges.
+ state.write_text(state.read_text().replace("## Completed Work Archive",
+ f'- [x] Retained completed work\n \n\n## Completed Work Archive'))
+ lease_path = runtime / "goals/cold/task-leases" / f"{retained_lease['todo_id']}.json"
+ lease_path.parent.mkdir(parents=True)
+ lease_path.write_text(json.dumps(retained_lease, indent=2))
backup = execute_state_backup_plan(build_state_backup_plan(project=project, runtime_root=runtime,
output_dir=tmp_path / "backups", backup_id="cold-original", include_automations=False,
include_skills=False, include_registry_projects=False, registry_path=registry))
@@ -82,84 +91,6 @@ def cli(action, *args, success=True, output_format="json", runtime_override=None
return cli, state, backup, body, runtime, receiver, env
-@pytest.mark.parametrize("old_producers_present", [True, False])
-@pytest.mark.parametrize("configuration", ["absent", "disabled", "retired"])
-def test_unpromoted_default_off_lease_lifecycle_without_shadow_producers(
- tmp_path, monkeypatch, old_producers_present, configuration,
-):
- _, state, _, _, runtime, receiver, env = workspace(tmp_path, monkeypatch)
- registry = tmp_path / "project/.loopx/registry.json"
- data = json.loads(registry.read_text())
- coordination = data["goals"][0]["coordination"]
- coordination["registered_agents"] = ["agent-a", "agent-b"]
- if configuration == "disabled":
- coordination["runtime_shadow"] = {
- "schema_version": "loopx_coordination_runtime_shadow_config_v0",
- "enabled": False, "provider": "file_v0",
- }
- elif configuration == "retired":
- coordination["authority_shadow"] = {
- "schema_version": "loopx_local_authority_shadow_config_v0",
- "mode": "file_one_way",
- }
- registry.write_text(json.dumps(data))
- state.write_text(state.read_text().replace("handoff_mode: legacy", "handoff_mode: hard_lease")
- .replace(" claimed_by=agent-a", ""))
- original_state, original_registry = state.read_bytes(), registry.read_bytes()
- if not old_producers_present:
- for name in ("runtime_shadow.py", "runtime_shadow_writer_adapter.py",
- "local_authority_shadow_adapter.py", "local_authority_shadow_outbox.py"):
- (receiver / "loopx/control_plane/coordination" / name).unlink()
-
- def lease(action, *args, owner="agent-a", key="original", success=True):
- child = subprocess.run([sys.executable, "-c",
- "import loopx,runpy,sys; print(loopx.__file__,file=sys.stderr); runpy.run_module('loopx.cli',run_name='__main__')",
- "--registry", str(registry), "--runtime-root", str(runtime), "--format", "json",
- "task-lease", action, "--goal-id", "cold", "--todo-id", "todo_current",
- "--owner", owner, "--idempotency-key", key, *map(str, args)],
- cwd=tmp_path, env=env, capture_output=True, text=True, timeout=60)
- assert str(receiver / "loopx/__init__.py") in child.stderr
- assert "Traceback" not in child.stderr, child.stderr
- assert (child.returncode == 0) is success, child.stdout + child.stderr
- result = json.loads(child.stdout)
- assert "coordination_runtime_shadow_capture" not in result
- assert "coordination_runtime_shadow" not in result
- assert state.read_bytes() == original_state
- assert registry.read_bytes() == original_registry
- assert not (runtime / "authority-shadow").exists()
- assert not any(path.is_file() for path in (runtime / "authority-transition").rglob("*"))
- return result
-
- acquired = lease("acquire", "--ttl-seconds", 120, "--write-scope", "docs/**")
- assert acquired["acquired"] is True
- version = acquired["lease"]["version"]
- path = Path(acquired["lease_path"])
- original_lease = path.read_bytes()
- replay = lease("acquire", "--ttl-seconds", 120, "--write-scope", "docs/**")
- assert replay["idempotent"] is True and replay["lease"] == acquired["lease"]
- assert path.read_bytes() == original_lease
- rejected = lease("renew", "--expected-version", version + 1, "--ttl-seconds", 180, success=False)
- assert rejected["error_code"] == "version_mismatch"
- assert path.read_bytes() == original_lease
- renewed = lease("renew", "--expected-version", version, "--ttl-seconds", 180)
- assert renewed["lease"]["version"] == version + 1
- assert renewed["lease"]["lease_epoch"] == acquired["lease"]["lease_epoch"]
- transferred = lease("transfer", "--expected-version", version + 1,
- "--new-owner", "agent-b", "--new-idempotency-key", "receiver",
- "--ttl-seconds", 180)
- assert transferred["lease"]["owner"] == "agent-b"
- assert transferred["lease"]["version"] == version + 2
- assert transferred["lease"]["lease_epoch"] == acquired["lease"]["lease_epoch"] + 1
- released = lease("release", "--expected-version", version + 2, owner="agent-b", key="receiver")
- assert released["released"] is True and released["lease"]["status"] == "released"
- assert released["lease"]["write_scopes"] == ["docs/**"]
- assert released["lease"]["lease_epoch"] == transferred["lease"]["lease_epoch"]
- retained = path.read_bytes()
- assert lease("release", "--expected-version", version + 2,
- owner="agent-b", key="receiver")["idempotent"] is True
- assert path.read_bytes() == retained
-
-
def test_coordination_command_help_and_rejections_match_full_cli(tmp_path):
# Independently compare the existing compatibility parser/handler with the
# selected dispatcher, including diagnostics that deliberately fall back.
@@ -202,17 +133,12 @@ def test_coordination_command_help_and_rejections_match_full_cli(tmp_path):
assert all(row[0] == 2 for row in observations[0][5:])
-@pytest.mark.parametrize("old_producers_present", [True, False])
@pytest.mark.parametrize("readback_present", [True, False])
def test_unconfigured_shadow_inspection_matches_full_cli_without_import(
- tmp_path, monkeypatch, old_producers_present, readback_present, isolated_todo_distribution, # noqa: F811 -- shared fixture
+ tmp_path, monkeypatch, readback_present, isolated_todo_distribution, # noqa: F811 -- shared fixture
):
monkeypatch.setenv("LOOPX_COLD_IMPORT_PACKAGE", str(isolated_todo_distribution[0]))
_, state, _, _, runtime, receiver, env = workspace(tmp_path, monkeypatch)
- if not old_producers_present:
- for name in ("runtime_shadow.py", "runtime_shadow_writer_adapter.py",
- "local_authority_shadow_adapter.py", "local_authority_shadow_outbox.py"):
- (receiver / "loopx/control_plane/coordination" / name).unlink()
if not readback_present:
(receiver / "loopx/control_plane/coordination/local_authority_shadow_readback.py").unlink()
registry = tmp_path / "project/.loopx/registry.json"
@@ -283,17 +209,16 @@ def test_explicit_shadow_action_uses_only_its_required_modules_without_writing(
@pytest.mark.parametrize("provider", ["file", "sqlite"])
-@pytest.mark.parametrize("old_producers_present", [True, False])
+@pytest.mark.parametrize("source_modules_present", [True, False])
def test_cold_cli_import_and_source_free_original_recovery(
- tmp_path, monkeypatch, provider, old_producers_present,
+ tmp_path, monkeypatch, provider, source_modules_present,
):
cli, state, backup, body, runtime, receiver, env = workspace(tmp_path, monkeypatch)
- if not old_producers_present:
- for path in ("todos.py", "bootstrap.py", "control_plane/coordination/runtime_shadow_writer_adapter.py",
+ if not source_modules_present:
+ for path in ("todos.py", "bootstrap.py",
"control_plane/coordination/local_authority_shadow_outbox.py",
"control_plane/coordination/runtime_shadow.py",
- "control_plane/coordination/local_authority_shadow_adapter.py",
- "control_plane/todos/legacy_mutation.py", "control_plane/todos/line_update.py"):
+ "control_plane/coordination/local_authority_shadow_adapter.py"):
(receiver / "loopx" / path).unlink()
original = state.read_bytes()
prepared = cli("prepare-import", "--backup-manifest", backup["manifest_path"],
@@ -324,18 +249,16 @@ def test_cold_cli_import_and_source_free_original_recovery(
@pytest.mark.parametrize("provider", ["file", "sqlite"])
-@pytest.mark.parametrize("old_producers_present", [True, False])
+@pytest.mark.parametrize("bridge_modules_present", [True, False])
@pytest.mark.parametrize("old_target", [False, True], ids=["no-creation-target", "v0-opposite-target"])
def test_cold_import_recovery_preserves_later_canonical_writes(
- tmp_path, monkeypatch, provider, old_producers_present, old_target,
+ tmp_path, monkeypatch, provider, bridge_modules_present, old_target,
):
target = ({"schema_version": "loopx_new_goal_storage_target_v0",
"provider": "sqlite" if provider == "file" else "file"} if old_target else None)
cli, state, backup, body, runtime, receiver, env = workspace(tmp_path, monkeypatch, storage_target=target)
- if not old_producers_present:
- for path in ("control_plane/todos/legacy_mutation.py", "control_plane/todos/line_update.py",
- "control_plane/coordination/runtime_shadow.py",
- "control_plane/coordination/runtime_shadow_writer_adapter.py",
+ if not bridge_modules_present:
+ for path in ("control_plane/coordination/runtime_shadow.py",
"control_plane/coordination/local_authority_shadow_adapter.py",
"control_plane/coordination/local_authority_shadow_outbox.py"):
(receiver / "loopx" / path).unlink()
@@ -464,6 +387,49 @@ def listing():
assert current["authority_read"]["provider_revision"] == after["authority_read"]["provider_revision"]
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_cold_import_preserves_terminal_lease_and_later_work_on_original_recovery(
+ tmp_path, monkeypatch, provider, isolated_todo_distribution, # noqa: F811 -- shared fixture
+):
+ # Released history is inert input. Never revive an ordinary source writer
+ # to generate the migration fixture in the canonical-only distribution.
+ monkeypatch.setenv("LOOPX_COLD_IMPORT_PACKAGE", str(isolated_todo_distribution[0]))
+ expected = {
+ "schema_version": "task_lease_v0", "goal_id": "cold", "todo_id": "todo_terminal",
+ "owner": "agent-a", "version": 2, "lease_epoch": 1, "status": "released",
+ "released_at": "2026-09-01T00:00:00Z", "idempotency_key": "retained-identity",
+ "future_extension": {"missing_value": None, "flag": False},
+ }
+ cli, state, backup, _, runtime, receiver, env = workspace(
+ tmp_path, monkeypatch, retained_lease=expected)
+ lease_path = runtime / "goals/cold/task-leases/todo_terminal.json"
+ original = lease_path.read_bytes()
+ plan = cli("prepare-import", "--backup-manifest", backup["manifest_path"],
+ "--provider", provider, "--target-handoff-mode", "hard_lease")["cold_import"]
+ imported = cli("apply-import", "--plan-sha256", plan["plan_sha256"],
+ "--writers-stopped", "--execute")["cold_import"]
+ assert imported["status"] == "applied" and imported["execution_authority_granted"] is False
+ assert canonical_todos(runtime, receiver, env, tmp_path)["leases"] == [expected]
+ added = subprocess.run([sys.executable, "-S", "-m", "loopx.cli", "--format", "json",
+ "--registry", str(tmp_path / "project/.loopx/registry.json"), "--runtime-root", str(runtime),
+ "todo", "add", "--goal-id", "cold", "--role", "agent",
+ "--text", "Later canonical work must survive recovery", "--operation-id", "later-terminal-import"],
+ cwd=tmp_path, env={**env, "PYTHONPATH": os.pathsep.join([str(receiver), os.environ["PYTHONPATH"]])},
+ capture_output=True, text=True, timeout=60)
+ assert added.returncode == 0, added.stdout + added.stderr
+ later_id = json.loads(added.stdout)["todo_id"]
+ before = canonical_todos(runtime, receiver, env, tmp_path)
+ later = next(row for row in before["todos"] if row["todo_id"] == later_id)
+ assert (later["text"], later["priority"]) == ("[P1] Later canonical work must survive recovery", "P1")
+ state.unlink()
+ for _ in range(2):
+ recovered = cli("recover-import", "--plan-sha256", plan["plan_sha256"], "--execute")["cold_import"]
+ assert recovered["status"] == "replayed"
+ assert canonical_todos(runtime, receiver, env, tmp_path) == before
+ assert lease_path.read_bytes() == original
+ assert not state.exists()
+
+
@pytest.mark.parametrize("artifact", ["archive_path", "manifest_path"])
def test_changed_reviewed_backup_cannot_fence_or_import(tmp_path, monkeypatch, artifact):
cli, _, backup, _, runtime, _, _ = workspace(tmp_path, monkeypatch)
diff --git a/tests/control_plane/test_cold_source_import_host.py b/tests/control_plane/test_cold_source_import_host.py
index f7d4079001..9a0e261a51 100644
--- a/tests/control_plane/test_cold_source_import_host.py
+++ b/tests/control_plane/test_cold_source_import_host.py
@@ -6,7 +6,7 @@
"""
from __future__ import annotations
-from datetime import datetime
+from datetime import datetime, timedelta, timezone
import json
import os
import subprocess
@@ -58,9 +58,31 @@ def command(action, *args):
return json.loads(child.stdout)
identity = ["--owner", "agent-a", "--idempotency-key", "old-execution"]
- acquired = command("acquire", *identity, "--ttl-seconds", ttl)
- assert acquired["ok"] and acquired["lease"]["status"] == "active", acquired
- context = {"lease": acquired["lease"], "ttl_seconds": ttl,
+ now = datetime.now(timezone.utc)
+
+ def timestamp(value: datetime) -> str:
+ return value.isoformat().replace("+00:00", "Z")
+
+ lease = {
+ "schema_version": "task_lease_v0",
+ "goal_id": "cold",
+ "todo_id": "todo_current",
+ "owner": "agent-a",
+ "idempotency_key": "old-execution",
+ "write_scopes": [],
+ "acquire_ttl_seconds": ttl,
+ "version": 1,
+ "lease_epoch": 1,
+ "acquired_at": timestamp(now),
+ "updated_at": timestamp(now),
+ "expires_at": timestamp(now + timedelta(seconds=ttl)),
+ "status": "active",
+ }
+ lease_path = runtime / "goals/cold/task-leases/todo_current.json"
+ lease_path.parent.mkdir(parents=True)
+ lease_path.write_text(json.dumps(lease), encoding="utf-8")
+ assert command("inspect")["lease"] == lease
+ context = {"lease": lease, "ttl_seconds": ttl,
"read_argv": [*argv, "inspect", *selected],
"renew_argv": [*argv, "renew", *selected, *identity]}
diff --git a/tests/control_plane/test_cold_source_import_runtime.py b/tests/control_plane/test_cold_source_import_runtime.py
index 68e3f920e0..26d1ce136e 100644
--- a/tests/control_plane/test_cold_source_import_runtime.py
+++ b/tests/control_plane/test_cold_source_import_runtime.py
@@ -54,7 +54,10 @@ def test_cold_import_real_runtime_without_original_producers(tmp_path, monkeypat
receiver = tmp_path / "receiver"
package = Path(os.environ.get("LOOPX_COLD_IMPORT_PACKAGE", str(REPO / "loopx")))
shutil.copytree(package, receiver / "loopx", ignore=shutil.ignore_patterns("__pycache__", "*.pyc"))
- for path in ("todos.py", "bootstrap.py", "control_plane/coordination/runtime_shadow_writer_adapter.py",
+ assert not (
+ receiver / "loopx/control_plane/coordination/runtime_shadow_writer_adapter.py"
+ ).exists()
+ for path in ("todos.py", "bootstrap.py",
"control_plane/coordination/local_authority_shadow_outbox.py"):
(receiver / "loopx" / path).unlink()
env = {**os.environ, "PYTHONPATH": str(receiver)}
diff --git a/tests/control_plane/test_cold_source_inspection.py b/tests/control_plane/test_cold_source_inspection.py
index 2620fd7ea0..44c10456e6 100644
--- a/tests/control_plane/test_cold_source_inspection.py
+++ b/tests/control_plane/test_cold_source_inspection.py
@@ -1,7 +1,8 @@
-"""A cold source can be inventoried without manufacturing capture history."""
+"""Cold Markdown sources can be inventoried without creating capture history."""
+
+from __future__ import annotations
+
import json
-import hashlib
-from pathlib import Path
import subprocess
import sys
@@ -10,196 +11,6 @@
from tests.control_plane.shadow_e2e_fixture import workspace
-def capture_bytes(fixture):
- """Original history files; transient lock files are not receipt sources."""
- return {str(p.relative_to(fixture.runtime)): p.read_bytes()
- for root in (fixture.runtime / "authority-shadow", fixture.runtime / "authority-transition")
- for p in root.rglob("*") if p.is_file() and not p.name.endswith(".lock")}
-
-
-def test_capture_inventory_keeps_original_history_and_outbox_bytes(tmp_path):
- fixture = workspace(tmp_path)
- fixture.add("Original captured operation")
- outbox = fixture.runtime / "authority-shadow" / "outbox" / fixture.goal / "todos"
- outbox.mkdir(exist_ok=True)
- residue = outbox / "unrecognized-original-receipt.json"
- residue.write_bytes(b"{malformed original bytes")
- before = capture_bytes(fixture)
- result = fixture.cli("coordination-shadow", "inspect-source")["source_inventory"]
- capture = result["capture"]
- assert capture["management_state"]["status"] == "active"
- assert capture["runtime_shadow_readback"]["status"] == "loaded"
- assert capture["runtime_shadow_readback"]["proof"]["last_applied_sequences"]["todos"] >= 1
- witness = capture["artifacts"]["runtime_store"]
- original = next(data for path, data in before.items()
- if path == str(Path(witness["path"]).relative_to(fixture.runtime)))
- assert witness["sha256"] == "sha256:" + hashlib.sha256(original).hexdigest()
- entries = capture["artifacts"]["outbox"]["inventory"]["entries"]
- raw = next(e for e in entries if e["path"] == "todos/" + residue.name)
- assert raw["sha256"] == "sha256:" + hashlib.sha256(residue.read_bytes()).hexdigest()
- assert result["outbox_reconciliation_verified"] is False
- assert result["import_ready"] is False
- review = capture["outbox_review"]
- assert review["status"] == "failed"
- assert review["reason_code"] == "outbox_file_invalid"
- assert review["executed"] is False
- assert before == capture_bytes(fixture)
- assert not (fixture.runtime / "authority").exists()
-
-
-def test_capture_inventory_keeps_rolled_back_operation_archives(tmp_path):
- fixture = workspace(tmp_path)
- fixture.add("Original operation before rollback")
- revision = fixture.cli("coordination-shadow", "inspect")["inspection"]["provider_revision"]
- rollback = fixture.cli("coordination-shadow", "rollback", "--provider-revision", revision, "--execute")
- assert rollback["rollback"]["status"] == "applied"
- before = capture_bytes(fixture)
- capture = fixture.cli("coordination-shadow", "inspect-source")["source_inventory"]["capture"]
- assert capture["management_state"]["status"] == "inactive"
- assert capture["artifacts"]["runtime_store"] is None
- assert capture["runtime_shadow_readback"] is None
- assert capture["outbox_review"] is None
- archives = capture["artifacts"]["rollback_archives"]
- assert len(archives) == 1
- assert archives[0]["path"] == rollback["rollback"]["candidate_archive_path"]
- assert archives[0]["sha256"] == "sha256:" + hashlib.sha256(Path(archives[0]["path"]).read_bytes()).hexdigest()
- assert before == capture_bytes(fixture)
-
-
-@pytest.mark.parametrize("window", ["before_commit", "after_commit"])
-def test_original_lease_outbox_uses_its_own_partition_without_renewal_or_cleanup(tmp_path, window):
- fixture = workspace(tmp_path)
- todo_id = fixture.add("Original leased task")["todo_id"]
- fixture.crash(window, "task-lease", "acquire", "--todo-id", todo_id,
- "--owner", "agent-a", "--idempotency-key", "original-lease", "--ttl-seconds", "120")
- before = capture_bytes(fixture)
- lease_path = fixture.runtime / "goals" / fixture.goal / "task-leases" / (todo_id + ".json")
- lease_bytes = lease_path.read_bytes()
- result = fixture.cli("coordination-shadow", "inspect-source")["source_inventory"]
- review = result["capture"]["outbox_review"]
- assert review["status"] == "planned" and review["executed"] is False
- leases = next(p for p in review["partitions"] if p["partition"] == "leases")
- assert len(leases["pending_entry_ids"]) == (1 if window == "before_commit" else 0)
- assert len(leases["reclaim_entry_ids"]) == (0 if window == "before_commit" else 1)
- assert leases["next_seq"] == (1 if window == "before_commit" else 2)
- assert todo_id in result["leases_requiring_settlement"]
- assert lease_path.read_bytes() == lease_bytes and before == capture_bytes(fixture)
-
-
-@pytest.mark.parametrize("corrupt", [False, True])
-def test_capture_inventory_reads_original_observation_through_existing_store(tmp_path, corrupt):
- fixture = workspace(tmp_path)
- source = fixture.runtime / "authority-shadow" / "file-v0"
- retained = fixture.runtime / "authority-shadow" / "file" / fixture.goal
- retained.mkdir(parents=True)
- # A synthetic historical copy of a real provider document, not a mock or
- # an import receipt. The historical reader owns its schema validation.
- for path in source.iterdir():
- if path.is_file() and not path.name.endswith(".lock"):
- (retained / path.name).write_bytes(path.read_bytes())
- if corrupt:
- next(retained.glob("authority-store-*.json")).write_text('{"invalid":true}')
- before = capture_bytes(fixture)
- result = fixture.cli("coordination-shadow", "inspect-source", success=not corrupt)
- if corrupt:
- assert result["source_inventory"]["status"] == "failed"
- else:
- capture = result["source_inventory"]["capture"]
- assert capture["legacy_observation_readback"]["status"] == "loaded"
- assert capture["artifacts"]["legacy_store"]["path"].startswith(str(retained))
- assert result["source_inventory"]["import_ready"] is False
- assert before == capture_bytes(fixture)
-
-
-@pytest.mark.parametrize("unsafe", ["corrupt_history", "symlink_outbox", "symlink_shadow_root", "invalid_original_result"])
-def test_capture_inventory_refuses_unreadable_or_unsafe_original_history(tmp_path, unsafe):
- fixture = workspace(tmp_path)
- if unsafe == "corrupt_history":
- store = next((fixture.runtime / "authority-shadow" / "file-v0").glob("authority-store-*.json"))
- store.write_text('{"invalid":true}')
- elif unsafe == "symlink_outbox":
- outbox = fixture.runtime / "authority-shadow" / "outbox" / fixture.goal
- (outbox / "external").symlink_to(fixture.state)
- elif unsafe == "symlink_shadow_root":
- shadow = fixture.runtime / "authority-shadow"
- original = tmp_path / "external-shadow"
- shadow.rename(original)
- shadow.symlink_to(original, target_is_directory=True)
- else:
- state = next((fixture.runtime / "authority-transition").rglob("state.json"))
- original = json.loads(state.read_text())
- original["result"]["primary_writeback_preserved"] = False
- state.write_text(json.dumps(original))
- before = capture_bytes(fixture)
- result = fixture.cli("coordination-shadow", "inspect-source", success=False)
- assert result["ok"] is False
- assert result["source_inventory"]["status"] == "failed"
- assert result["source_inventory"]["import_ready"] is False
- assert before == capture_bytes(fixture)
-
-
-@pytest.mark.parametrize("window", ["before_commit", "after_commit"])
-def test_capture_inventory_does_not_drain_crashed_original_outbox(tmp_path, window):
- fixture = workspace(tmp_path)
- fixture.crash(window, "todo", "add", "--role", "agent", "--text", "Retained original operation")
- before = capture_bytes(fixture)
- result = fixture.cli("coordination-shadow", "inspect-source")["source_inventory"]
- entries = result["capture"]["artifacts"]["outbox"]["inventory"]["entries"]
- assert any(e["path"].endswith(".prepared.json") for e in entries)
- assert any(e["path"].endswith(".committed.json") for e in entries)
- assert result["import_ready"] is False and result["outbox_reconciliation_verified"] is False
- review = result["capture"]["outbox_review"]
- assert review["status"] == "planned" and review["executed"] is False
- plans = review["partitions"]
- todos = next(p for p in plans if p["partition"] == "todos")
- assert len(todos["pending_entry_ids"]) == (1 if window == "before_commit" else 0)
- assert len(todos["reclaim_entry_ids"]) == (0 if window == "before_commit" else 1)
- assert len(todos["replay_entries"]) == (0 if window == "before_commit" else 1)
- assert result["writer_stop_verified"] is False
- assert before == capture_bytes(fixture)
-
-
-@pytest.mark.parametrize("defect", ["receipt_bytes", "orphan_marker", "foreign_lineage"])
-def test_outbox_review_refuses_unproved_disposition_but_retains_original_bytes(tmp_path, defect):
- fixture = workspace(tmp_path)
- window = "after_commit" if defect == "receipt_bytes" else "before_commit"
- fixture.crash(window, "todo", "add", "--role", "agent", "--text", "Original operation")
- directory = fixture.runtime / "authority-shadow" / "outbox" / fixture.goal / "todos"
- prepared = next(directory.glob("*.prepared.json"))
- if defect == "orphan_marker":
- prepared.unlink()
- else:
- record = json.loads(prepared.read_text())
- if defect == "receipt_bytes":
- record["writer"]["operation_id"] = "different-original-operation"
- else:
- record["capture_lineage_id"] = "foreign-lineage"
- prepared.write_text(json.dumps(record))
- before = capture_bytes(fixture)
- result = fixture.cli("coordination-shadow", "inspect-source")["source_inventory"]
- review = result["capture"]["outbox_review"]
- assert review["status"] == "failed"
- assert review["reason_code"] == ("outbox_receipt_mismatch" if defect == "receipt_bytes" else "outbox_file_invalid")
- assert review["executed"] is False
- assert result["import_ready"] is False and result["outbox_reconciliation_verified"] is False
- assert before == capture_bytes(fixture)
-
-
-@pytest.mark.parametrize("archive", ["candidate", "outbox"])
-def test_capture_inventory_refuses_missing_terminal_rollback_archive(tmp_path, archive):
- fixture = workspace(tmp_path)
- revision = fixture.cli("coordination-shadow", "inspect")["inspection"]["provider_revision"]
- result = fixture.cli("coordination-shadow", "rollback", "--provider-revision", revision, "--execute")
- if archive == "candidate":
- Path(result["rollback"]["candidate_archive_path"]).unlink()
- else:
- Path(result["rollback"]["outbox_archive_path"], "manifest.json").unlink()
- before = capture_bytes(fixture)
- refused = fixture.cli("coordination-shadow", "inspect-source", success=False)
- assert refused["source_inventory"]["reason_code"] == "rollback_archive_readback_mismatch"
- assert before == capture_bytes(fixture)
-
-
def cold_workspace(tmp_path):
fixture = workspace(tmp_path, bootstrap=False)
registry = json.loads(fixture.registry.read_text())
@@ -208,24 +19,31 @@ def cold_workspace(tmp_path):
fixture.state.write_text(
"---\ngoal_id: goal-e2e\nhandoff_mode: soft_claim\n---\n"
"## Agent Todo\n- [ ] Keep active\n"
- " \n"
+ " \n"
"## Todo Archive\n- [x] Keep unrelated archived evidence\n"
- " \n"
+ " \n"
)
return fixture
def test_inventory_preserves_unreferenced_archive_without_shadow_or_effects(tmp_path):
fixture = cold_workspace(tmp_path)
- before = {p: p.read_bytes() for p in (fixture.registry, fixture.state)}
+ before = {path: path.read_bytes() for path in (fixture.registry, fixture.state)}
result = fixture.cli("coordination-shadow", "inspect-source")
inventory = result["source_inventory"]
assert inventory["status"] == "inspected"
assert inventory["active_todo_count"] == 1
assert inventory["archived_todo_count"] == 1
- archived = next(t for t in inventory["projection"]["todos"] if t["todo_id"] == "todo_archived")
+ archived = next(
+ todo
+ for todo in inventory["projection"]["todos"]
+ if todo["todo_id"] == "todo_archived"
+ )
assert archived["text"] == "Keep unrelated archived evidence"
- assert archived["note"] == "kept" and archived["watch_only"] == "false"
+ assert archived["note"] == "kept"
+ assert archived["watch_only"] == "false"
assert inventory["import_ready"] is False
assert inventory["writer_stop_verified"] is False
assert inventory["outbox_reconciliation_verified"] is False
@@ -233,36 +51,67 @@ def test_inventory_preserves_unreferenced_archive_without_shadow_or_effects(tmp_
assert result["executed"] is False
assert not (fixture.runtime / "authority-shadow").exists()
assert not (fixture.runtime / "authority").exists()
- assert before == {p: p.read_bytes() for p in before}
-
-
-@pytest.mark.parametrize("original, todo_id, heading", [
- ("Keep unrelated archived evidence", "todo_archived", "Todo Archive"),
- ("Keep active", "todo_active", "Agent Todo"),
-])
-def test_source_text_is_not_an_attention_summary(tmp_path, original, todo_id, heading):
+ assert before == {path: path.read_bytes() for path in before}
+
+
+@pytest.mark.parametrize(
+ ("original", "todo_id", "heading"),
+ [
+ ("Keep unrelated archived evidence", "todo_archived", "Todo Archive"),
+ ("Keep active", "todo_active", "Agent Todo"),
+ ],
+)
+def test_source_text_is_not_an_attention_summary(
+ tmp_path,
+ original,
+ todo_id,
+ heading,
+):
fixture = cold_workspace(tmp_path)
full_text = "Preserve " + "explicit recovery requirement " * 70
fixture.state.write_text(fixture.state.read_text().replace(original, full_text))
- inventory = fixture.cli("coordination-shadow", "inspect-source")["source_inventory"]
- archive = next(t for t in inventory["projection"]["todos"] if t["todo_id"] == todo_id)
- assert archive["text"] == full_text.strip()
- assert archive["source_section"] == heading
+ inventory = fixture.cli(
+ "coordination-shadow",
+ "inspect-source",
+ )["source_inventory"]
+ todo = next(
+ item for item in inventory["projection"]["todos"]
+ if item["todo_id"] == todo_id
+ )
+ assert todo["text"] == full_text.strip()
+ assert todo["source_section"] == heading
def test_stale_source_is_rejected_by_native_owner(tmp_path):
- from loopx.control_plane.coordination.runtime_shadow import build_runtime_shadow_source_snapshot
- from loopx.control_plane.coordination.local_authority_shadow_projection import source_effect_runtime_result
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_runtime_shadow_source_snapshot,
+ )
+ from loopx.control_plane.coordination.local_authority_shadow_projection import (
+ source_effect_runtime_result,
+ )
fixture = cold_workspace(tmp_path)
goal = json.loads(fixture.registry.read_text())["goals"][0]
- projection, snapshot = build_runtime_shadow_source_snapshot(goal=goal, runtime_root=fixture.runtime,
- state_path=fixture.state, registry_path=fixture.registry, include_all_archived_todos=True)
+ projection, snapshot = build_runtime_shadow_source_snapshot(
+ goal=goal,
+ runtime_root=fixture.runtime,
+ state_path=fixture.state,
+ registry_path=fixture.registry,
+ include_all_archived_todos=True,
+ )
fixture.state.write_text(fixture.state.read_text() + "\nNew source bytes\n")
- result = source_effect_runtime_result("coordination.source.inspect", {
- "schema_version": "loopx_cold_source_inspection_request_v0", "goal_id": fixture.goal,
- "runtime_root": str(fixture.runtime), "projection": projection, "source_snapshot": snapshot})
- assert result["status"] == "failed" and result["reason_code"] == "source_changed_retry"
+ result = source_effect_runtime_result(
+ "coordination.source.inspect",
+ {
+ "schema_version": "loopx_cold_source_inspection_request_v0",
+ "goal_id": fixture.goal,
+ "runtime_root": str(fixture.runtime),
+ "projection": projection,
+ "source_snapshot": snapshot,
+ },
+ )
+ assert result["status"] == "failed"
+ assert result["reason_code"] == "source_changed_retry"
assert result["import_ready"] is False
@@ -270,36 +119,57 @@ def test_selected_canonical_source_is_not_reinterpreted_as_markdown(tmp_path):
import hashlib
fixture = cold_workspace(tmp_path)
- marker = fixture.runtime / "authority" / ("provider-" + hashlib.sha256(fixture.goal.encode()).hexdigest() + ".json")
+ marker = fixture.runtime / "authority" / (
+ "provider-" + hashlib.sha256(fixture.goal.encode()).hexdigest() + ".json"
+ )
marker.parent.mkdir(parents=True)
marker.write_text('{"provider":"sqlite"}')
result = fixture.cli("coordination-shadow", "inspect-source", success=False)
- assert result["source_inventory"]["reason_code"] == "cold_source_canonical_authority_present"
+ assert result["source_inventory"]["reason_code"] == (
+ "cold_source_canonical_authority_present"
+ )
assert marker.read_text() == '{"provider":"sqlite"}'
-def test_existing_capture_still_excludes_unreferenced_archives(tmp_path):
- from loopx.control_plane.coordination.runtime_shadow import build_runtime_shadow_source_snapshot
+def test_capture_projection_excludes_unreferenced_archives(tmp_path):
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_runtime_shadow_source_snapshot,
+ )
fixture = cold_workspace(tmp_path)
goal = json.loads(fixture.registry.read_text())["goals"][0]
- projection, _ = build_runtime_shadow_source_snapshot(goal=goal, runtime_root=fixture.runtime,
- state_path=fixture.state, registry_path=fixture.registry)
- assert [t["todo_id"] for t in projection["todos"]] == ["todo_active"]
+ projection, _ = build_runtime_shadow_source_snapshot(
+ goal=goal,
+ runtime_root=fixture.runtime,
+ state_path=fixture.state,
+ registry_path=fixture.registry,
+ )
+ assert [todo["todo_id"] for todo in projection["todos"]] == ["todo_active"]
def test_expired_orphan_active_lease_still_requires_settlement(tmp_path):
fixture = cold_workspace(tmp_path)
directory = fixture.runtime / "goals" / fixture.goal / "task-leases"
directory.mkdir(parents=True)
- lease = {"schema_version": "task_lease_v0", "goal_id": fixture.goal,
- "todo_id": "removed", "owner": "agent-a", "status": "active",
- "idempotency_key": "old-work", "version": 4, "lease_epoch": 2,
- "expires_at": "2000-01-01T00:00:00Z", "write_scopes": []}
+ lease = {
+ "schema_version": "task_lease_v0",
+ "goal_id": fixture.goal,
+ "todo_id": "removed",
+ "owner": "agent-a",
+ "status": "active",
+ "idempotency_key": "old-work",
+ "version": 4,
+ "lease_epoch": 2,
+ "expires_at": "2000-01-01T00:00:00Z",
+ "write_scopes": [],
+ }
path = directory / "removed.json"
path.write_text(json.dumps(lease))
before = path.read_bytes()
- inventory = fixture.cli("coordination-shadow", "inspect-source")["source_inventory"]
+ inventory = fixture.cli(
+ "coordination-shadow",
+ "inspect-source",
+ )["source_inventory"]
assert inventory["lease_file_count"] == 1
assert inventory["leases_requiring_settlement"] == ["removed"]
assert inventory["retained_leases"] == [lease]
@@ -308,17 +178,36 @@ def test_expired_orphan_active_lease_still_requires_settlement(tmp_path):
assert path.read_bytes() == before
-@pytest.mark.parametrize("corruption", ["duplicate_archive", "unknown_archive_role", "invalid_orphan_lease"])
-def test_ambiguous_or_unsupported_history_is_not_an_empty_source(tmp_path, corruption):
+@pytest.mark.parametrize(
+ "corruption",
+ ["duplicate_archive", "unknown_archive_role", "invalid_orphan_lease"],
+)
+def test_ambiguous_or_unsupported_history_is_not_an_empty_source(
+ tmp_path,
+ corruption,
+):
fixture = cold_workspace(tmp_path)
if corruption == "duplicate_archive":
- fixture.state.write_text(fixture.state.read_text().replace("todo_id=todo_archived", "todo_id=todo_active"))
+ fixture.state.write_text(
+ fixture.state.read_text().replace(
+ "todo_id=todo_archived",
+ "todo_id=todo_active",
+ )
+ )
elif corruption == "unknown_archive_role":
- fixture.state.write_text(fixture.state.read_text().replace("todo_id=todo_archived role=agent", "todo_id=todo_archived"))
+ fixture.state.write_text(
+ fixture.state.read_text().replace(
+ "todo_id=todo_archived role=agent",
+ "todo_id=todo_archived",
+ )
+ )
else:
directory = fixture.runtime / "goals" / fixture.goal / "task-leases"
directory.mkdir(parents=True)
- (directory / "removed.json").write_text(json.dumps({"goal_id": fixture.goal, "todo_id": "removed"}))
+ (directory / "removed.json").write_text(json.dumps({
+ "goal_id": fixture.goal,
+ "todo_id": "removed",
+ }))
result = fixture.cli("coordination-shadow", "inspect-source", success=False)
assert result["ok"] is False
assert not (fixture.runtime / "authority").exists()
@@ -326,10 +215,22 @@ def test_ambiguous_or_unsupported_history_is_not_an_empty_source(tmp_path, corru
def test_source_inspection_has_no_execute_switch(tmp_path):
fixture = cold_workspace(tmp_path)
- result = subprocess.run([sys.executable, "-m", "loopx.cli",
- *fixture.arguments("coordination-shadow", "inspect-source", "--execute")],
- capture_output=True, text=True)
- assert result.returncode == 2 and "unrecognized arguments" in result.stderr
+ result = subprocess.run(
+ [
+ sys.executable,
+ "-m",
+ "loopx.cli",
+ *fixture.arguments(
+ "coordination-shadow",
+ "inspect-source",
+ "--execute",
+ ),
+ ],
+ capture_output=True,
+ text=True,
+ )
+ assert result.returncode == 2
+ assert "unrecognized arguments" in result.stderr
@pytest.mark.parametrize("kind", ["unsupported_name", "directory", "symlink"])
@@ -338,7 +239,7 @@ def test_unsafe_lease_inventory_is_not_silently_omitted(tmp_path, kind):
directory = fixture.runtime / "goals" / fixture.goal / "task-leases"
directory.mkdir(parents=True)
if kind == "unsupported_name":
- (directory / "历史.json").write_text("{}")
+ (directory / "unsafe lease.json").write_text("{}")
elif kind == "directory":
(directory / "todo_removed.json").mkdir()
else:
diff --git a/tests/control_plane/test_completion_policy.py b/tests/control_plane/test_completion_policy.py
index b6472ab877..37f525bf6f 100644
--- a/tests/control_plane/test_completion_policy.py
+++ b/tests/control_plane/test_completion_policy.py
@@ -4,6 +4,9 @@
from dataclasses import fields
from pathlib import Path
+from loopx.control_plane.coordination.goal_storage_initialization import (
+ initialize_goal_storage_target,
+)
from loopx.control_plane.todos.completion_policy import CompletionPolicy
from loopx.control_plane.testing.canary_harness import (
run_json_cli,
@@ -41,7 +44,27 @@ def test_completion_rejects_unknown_runtime_model_before_write(
adapter_kind="harness_self_improvement",
registered_agents=[AGENT_ID, "codex-main-control"],
quota_allowed_slots=None,
+ extra_goal_fields={
+ "creation_operation_id": f"goal-create:{GOAL_ID}",
+ "coordination": {
+ "registered_agents": [AGENT_ID, "codex-main-control"],
+ "agent_model": "peer_v1",
+ "storage_target": {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": "file",
+ "handoff_mode": "soft_claim",
+ },
+ },
+ },
)
+ registry = json.loads(registry_path.read_text(encoding="utf-8"))
+ initialized = initialize_goal_storage_target(
+ runtime,
+ registry["goals"][0],
+ registry_path=registry_path,
+ )
+ assert initialized is not None
+ assert initialized["authority_initialized"] is True
source = run_json_cli(
"todo",
"add",
@@ -55,7 +78,6 @@ def test_completion_rejects_unknown_runtime_model_before_write(
AGENT_ID,
registry_path=registry_path,
)
- registry = json.loads(registry_path.read_text(encoding="utf-8"))
registry["goals"][0]["coordination"]["agent_model"] = "hierarchy_v2"
registry_path.write_text(json.dumps(registry), encoding="utf-8")
before = state_file.read_text(encoding="utf-8")
diff --git a/tests/control_plane/test_dated_vision_wait_cli.py b/tests/control_plane/test_dated_vision_wait_cli.py
index 58992f95ab..2d54441e40 100644
--- a/tests/control_plane/test_dated_vision_wait_cli.py
+++ b/tests/control_plane/test_dated_vision_wait_cli.py
@@ -169,12 +169,22 @@ def test_independent_work_is_not_silenced_by_scheduled_wait(tmp_path, isolated_e
def test_bound_monitor_keeps_exact_turn_and_agent_fences(tmp_path, isolated_effect):
+ from canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
+
registry, runtime, state = fixture(tmp_path)
with state.open("a", encoding="utf-8") as output:
output.write("\n- [ ] [P1-monitor] Observe a public result.\n"
f" \n")
+ rc, listed = cli(registry, runtime, "todo", "list", "--goal-id", GOAL)
+ assert rc == 0, listed
+ initialize_canonical_authority(
+ runtime, GOAL,
+ build_todo_runtime_shadow_projection(goal_id=GOAL, todos=listed["todos"], handoff_mode="soft_claim"),
+ state_path=state,
+ )
rc, guard = cli(registry, runtime, "quota", "should-run", "--codex-app",
"--goal-id", GOAL, "--agent-id", AGENT, "--turn-instance-id", "bound-monitor")
assert rc == 0, guard
diff --git a/tests/control_plane/test_declared_terminal_settlement_cli.py b/tests/control_plane/test_declared_terminal_settlement_cli.py
index 00711d05df..2d7c3a97ac 100644
--- a/tests/control_plane/test_declared_terminal_settlement_cli.py
+++ b/tests/control_plane/test_declared_terminal_settlement_cli.py
@@ -159,7 +159,7 @@ def test_declared_leased_completion_writeback_spend_terminal_and_replay(
code, early_reentry = run("todo", "complete", "--goal-id", cli.GOAL_ID,
"--todo-id", cli.TODO_ID, "--agent-id", cli.AGENT_ID,
"--completion-identity-key", completed["completion_identity_key"],
- "--no-follow-up", "--evidence", complete_args[-1])
+ "--no-follow-up")
assert code == 1, early_reentry
code, continuing_guard = run(*guard_args)
assert code == 0, continuing_guard
@@ -194,7 +194,7 @@ def test_declared_leased_completion_writeback_spend_terminal_and_replay(
terminal_args = (*complete_args, "--no-follow-up") if closeout_source == "turn" else (
"todo", "complete", "--goal-id", cli.GOAL_ID, "--todo-id", cli.TODO_ID,
"--agent-id", cli.AGENT_ID, "--completion-identity-key", completed["completion_identity_key"],
- "--no-follow-up", "--evidence", complete_args[-1])
+ "--no-follow-up")
code, terminal = run(*terminal_args)
assert code == 0, terminal.get("error") or terminal.get("reason") or terminal
assert terminal["completion_continuation"] == "no_followup"
@@ -296,7 +296,7 @@ def call(*args: str, expected: int = 0):
assert len(admitted["autonomous_replan_obligation"]["triggers"]) == 1
call("todo", "complete", "--goal-id", cli.GOAL_ID, "--todo-id", cli.TODO_ID,
"--agent-id", cli.AGENT_ID, "--completion-identity-key", done["completion_identity_key"],
- "--no-follow-up", "--evidence", "Original declared source check passed.")
+ "--no-follow-up")
recovered = call(*guard)
assert recovered["heartbeat_receipt"]["settlement_identity"] == identity
assert recovered.get("selected_todo") is None
diff --git a/tests/control_plane/test_effective_turn_replan.py b/tests/control_plane/test_effective_turn_replan.py
index a804b54737..2bd174c0ae 100644
--- a/tests/control_plane/test_effective_turn_replan.py
+++ b/tests/control_plane/test_effective_turn_replan.py
@@ -17,6 +17,7 @@
GOAL_ID,
TODO_ID,
_configure_selectable_alternative,
+ _initialize_fixture_authority,
ALTERNATIVE_TODO_ID,
)
from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
@@ -515,6 +516,7 @@ def test_effective_cadence_rejects_invalid_units(invalid):
def test_verified_negative_work_counts_without_a_score_gain(tmp_path: Path) -> None:
project, runtime, registry = _write_fixture(tmp_path)
_configure_selectable_alternative(project)
+ _initialize_fixture_authority(project, runtime)
binding = (
"--goal-id",
GOAL_ID,
diff --git a/tests/control_plane/test_explore_capture_identity.py b/tests/control_plane/test_explore_capture_identity.py
index c49b63d7af..49287f7506 100644
--- a/tests/control_plane/test_explore_capture_identity.py
+++ b/tests/control_plane/test_explore_capture_identity.py
@@ -27,14 +27,14 @@ def test_first_capture_allocates_identity_and_recovers_without_rewriting_scope(
registry.write_text(json.dumps(config))
binding = ("--goal-id", GOAL_ID, "--agent-id", AGENT_ID, "--todo-id", TODO_ID,
"--turn-instance-id", TURN_ID)
- assert _run_cli(registry, runtime, "todo", "claim", "--claimed-by", AGENT_ID,
- *binding[:-2], cwd=project)[0] == 0
rows = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]
initialize_canonical_authority(runtime, GOAL_ID,
build_todo_runtime_shadow_projection(goal_id=GOAL_ID, todos=rows, handoff_mode="hard_lease"),
state_path=project / config["goals"][0]["state_file"], provider=provider)
acquire_task_lease(registry_path=registry, runtime_root=runtime, goal_id=GOAL_ID,
todo_id=TODO_ID, owner=AGENT_ID, idempotency_key="capture-identity")
+ assert _run_cli(registry, runtime, "todo", "claim", "--claimed-by", AGENT_ID,
+ *binding[:-2], cwd=project)[0] == 0
rc, guard = _run_cli(registry, runtime, "quota", "should-run", "--codex-app",
*binding, "--scan-path", str(project), cwd=project)
assert rc == 0, guard
diff --git a/tests/control_plane/test_explore_durable_links.py b/tests/control_plane/test_explore_durable_links.py
index 677aba5408..3931f1b133 100644
--- a/tests/control_plane/test_explore_durable_links.py
+++ b/tests/control_plane/test_explore_durable_links.py
@@ -25,9 +25,8 @@ def test_ninth_capture_keeps_history_and_survives_real_cli_replay(tmp_path, prov
registry.write_text(json.dumps(config))
binding = ("--goal-id", GOAL_ID, "--agent-id", AGENT_ID, "--todo-id", TODO_ID,
"--turn-instance-id", TURN_ID)
- assert _run_cli(registry, runtime, "todo", "claim", "--claimed-by", AGENT_ID,
- *binding[:-2], cwd=project)[0] == 0
rows = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]
+ next(row for row in rows if row["todo_id"] == TODO_ID)["claimed_by"] = AGENT_ID
initialize_canonical_authority(runtime, GOAL_ID,
build_todo_runtime_shadow_projection(goal_id=GOAL_ID, todos=rows, handoff_mode="hard_lease"),
state_path=project / config["goals"][0]["state_file"], provider=provider)
diff --git a/tests/control_plane/test_explore_linked_capture.py b/tests/control_plane/test_explore_linked_capture.py
index c6ab8c8368..5a46a0de12 100644
--- a/tests/control_plane/test_explore_linked_capture.py
+++ b/tests/control_plane/test_explore_linked_capture.py
@@ -47,7 +47,7 @@ def test_scope_uses_source_registry_and_canonical_todo_not_stale_shared_projecti
from tests.control_plane.test_native_todo_planning_update import fixture, records
from loopx.capabilities.explore.turn_context import explore_turn_context
- source, _state = fixture(tmp_path, True, provider)
+ source, _state = fixture(tmp_path, provider)
runtime = tmp_path / "runtime"
config = json.loads(source.read_text())
config["goals"][0]["explore_graph"] = {"enabled": True}
@@ -98,6 +98,11 @@ def test_scope_uses_source_registry_and_canonical_todo_not_stale_shared_projecti
@pytest.mark.parametrize("case", ["unknown", "unlinked", "partial"])
def test_real_cli_rejects_invalid_linked_capture_before_primary_commit_and_recovers(tmp_path, case):
+ from canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+ from loopx.todos import list_goal_todos
from tests.control_plane.test_quota_settlement_cli import (
_write_fixture, _run_cli, GOAL_ID, AGENT_ID, TODO_ID, TURN_ID,
)
@@ -107,6 +112,16 @@ def test_real_cli_rejects_invalid_linked_capture_before_primary_commit_and_recov
registry.write_text(json.dumps(config))
binding = ("--goal-id", GOAL_ID, "--agent-id", AGENT_ID, "--todo-id", TODO_ID,
"--turn-instance-id", TURN_ID)
+ todos = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, todos=todos, handoff_mode="soft_claim"
+ ),
+ state_path=project / config["goals"][0]["state_file"],
+ provider="file",
+ )
assert _run_cli(registry, runtime, "todo", "claim", "--claimed-by", AGENT_ID,
*binding[:-2], cwd=project)[0] == 0
assert _run_cli(registry, runtime, "quota", "should-run", "--codex-app",
diff --git a/tests/control_plane/test_explore_question_evolution.py b/tests/control_plane/test_explore_question_evolution.py
index 488b0fd8ba..ba01056a98 100644
--- a/tests/control_plane/test_explore_question_evolution.py
+++ b/tests/control_plane/test_explore_question_evolution.py
@@ -143,14 +143,28 @@ def race(*positional, **kwargs):
@pytest.mark.parametrize("case", ["valid", "foreign", "dangling", "cycle"])
-def test_real_cli_path_delta_result_evolves_question_and_reads_it_back(tmp_path, case):
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_real_cli_path_delta_result_evolves_question_and_reads_it_back(tmp_path, monkeypatch, case, provider):
+ from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
+ from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
+ from loopx.todos import list_goal_todos
from tests.control_plane.test_quota_settlement_cli import (
_write_fixture, _run_cli, AGENT_ID, GOAL_ID, TODO_ID, TURN_ID,
)
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
project, runtime, registry = _write_fixture(tmp_path)
config = json.loads(registry.read_text())
config["goals"][0]["explore_graph"] = {"enabled": True}
registry.write_text(json.dumps(config))
+ initialize_canonical_authority(
+ runtime, GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"],
+ handoff_mode="soft_claim",
+ ),
+ state_path=project / config["goals"][0]["state_file"], provider=provider,
+ )
def cli(*args):
rc, result = _run_cli(registry, runtime, *args, cwd=project)
diff --git a/tests/control_plane/test_explore_result_writeback.py b/tests/control_plane/test_explore_result_writeback.py
index 8358110791..7aa6ea04a7 100644
--- a/tests/control_plane/test_explore_result_writeback.py
+++ b/tests/control_plane/test_explore_result_writeback.py
@@ -187,6 +187,10 @@ def interrupt(**kwargs):
@pytest.mark.parametrize("case", ["disabled", "null", "invalid", "conflict", "off-without-result",
"path-disabled", "path-missing", "path-no-evidence", "path-overflow"])
def test_real_cli_inline_validation_precedes_primary_commit(tmp_path, case):
+ from canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
from tests.control_plane.test_quota_settlement_cli import (
_write_fixture, _run_cli, GOAL_ID, AGENT_ID, TODO_ID, TURN_ID,
)
@@ -195,6 +199,16 @@ def test_real_cli_inline_validation_precedes_primary_commit(tmp_path, case):
config = json.loads(path.read_text())
config["goals"][0]["explore_graph"] = {"enabled": case not in {"disabled", "off-without-result", "path-disabled"}}
path.write_text(json.dumps(config))
+ todos = list_goal_todos(registry_path=path, goal_id=GOAL_ID)["todos"]
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, todos=todos, handoff_mode="soft_claim"
+ ),
+ state_path=project / config["goals"][0]["state_file"],
+ provider="file",
+ )
rc, claimed = _run_cli(path, runtime, "todo", "claim", "--goal-id", GOAL_ID,
"--todo-id", TODO_ID, "--agent-id", AGENT_ID,
"--claimed-by", AGENT_ID, cwd=project)
@@ -262,6 +276,11 @@ def test_real_cli_inline_validation_precedes_primary_commit(tmp_path, case):
def test_real_cli_normal_writeback_persists_attachment_and_rejects_conflicting_retry(
tmp_path, terminal, interrupted, source_mode, monkeypatch, capsys,
):
+ from canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+ from loopx.control_plane.work_items.task_lease import acquire_task_lease
from tests.control_plane.test_quota_settlement_cli import (
_write_fixture,
_run_cli,
@@ -279,23 +298,10 @@ def test_real_cli_normal_writeback_persists_attachment_and_rejects_conflicting_r
"explore_harness": {"enabled": True},
}
path.write_text(json.dumps(config))
- rc, claimed = _run_cli(
- path,
- runtime,
- "todo",
- "claim",
- "--goal-id",
- GOAL_ID,
- "--todo-id",
- TODO_ID,
- "--agent-id",
- AGENT_ID,
- "--claimed-by",
- AGENT_ID,
- cwd=project,
- )
- assert rc == 0, claimed
+ todos = list_goal_todos(registry_path=path, goal_id=GOAL_ID)["todos"]
if source_mode == "linked_path_delta":
+ target = next(todo for todo in todos if todo["todo_id"] == TODO_ID)
+ target["explore_result_node_refs"] = [ATTACHMENT["node_id"]]
from loopx.capabilities.explore.result_log import (
append_explore_result_event, build_explore_node_event,
)
@@ -303,27 +309,19 @@ def test_real_cli_normal_writeback_persists_attachment_and_rejects_conflicting_r
build_explore_node_event(goal_id=GOAL_ID, node_id=ATTACHMENT["node_id"],
node_kind="question", title=ATTACHMENT["question"],
summary=ATTACHMENT["applicability"], status="open"))
- update_goal_todo(registry_path=path, goal_id=GOAL_ID, todo_id=TODO_ID,
- agent_id=AGENT_ID, explore_result_node_refs=[ATTACHMENT["node_id"]])
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=todos,
+ handoff_mode="hard_lease" if terminal else "soft_claim",
+ ),
+ state_path=project / config["goals"][0]["state_file"],
+ provider="file",
+ )
terminal_proof = {}
if terminal:
- from canonical_authority_fixture import initialize_canonical_authority
- from loopx.control_plane.coordination.runtime_shadow import (
- build_todo_runtime_shadow_projection,
- )
- from loopx.control_plane.work_items.task_lease import acquire_task_lease
- from loopx.todos import complete_goal_todo
-
- todos = list_goal_todos(registry_path=path, goal_id=GOAL_ID)["todos"]
- initialize_canonical_authority(
- runtime,
- GOAL_ID,
- build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, todos=todos, handoff_mode="hard_lease"
- ),
- state_path=project / config["goals"][0]["state_file"],
- provider="file",
- )
lease = acquire_task_lease(
registry_path=path,
runtime_root=runtime,
@@ -336,6 +334,25 @@ def test_real_cli_normal_writeback_persists_attachment_and_rejects_conflicting_r
task_lease_idempotency_key="cli-terminal-result",
task_lease_expected_version=lease["lease"]["version"],
)
+ rc, claimed = _run_cli(
+ path,
+ runtime,
+ "todo",
+ "claim",
+ "--goal-id",
+ GOAL_ID,
+ "--todo-id",
+ TODO_ID,
+ "--agent-id",
+ AGENT_ID,
+ "--claimed-by",
+ AGENT_ID,
+ cwd=project,
+ )
+ assert rc == 0, claimed
+ if terminal:
+ from loopx.todos import complete_goal_todo
+
binding = (
"--goal-id",
GOAL_ID,
@@ -510,7 +527,7 @@ def test_canonical_delivery_keeps_existing_refs_and_receiver_reads_scoped_result
records,
)
- path, _state = canonical_fixture(tmp_path, True, provider)
+ path, _state = canonical_fixture(tmp_path, provider)
config = json.loads(path.read_text())
config["goals"][0]["explore_graph"] = {"enabled": True}
config["goals"][0]["spawn_policy"] = {
@@ -600,7 +617,7 @@ def test_completed_work_can_attach_evidence_without_reopening_or_changing_comple
tmp_path, provider, mode
):
from tests.control_plane.test_native_todo_planning_update import (
- fixture as canonical_fixture,
+ source_fixture as canonical_source_fixture,
records,
)
from canonical_authority_fixture import initialize_canonical_authority
@@ -613,7 +630,7 @@ def test_completed_work_can_attach_evidence_without_reopening_or_changing_comple
from loopx.control_plane.work_items.task_lease import acquire_task_lease
from loopx.todos import complete_goal_todo
- path, state = canonical_fixture(tmp_path, False, provider)
+ path, state = canonical_source_fixture(tmp_path)
config = json.loads(path.read_text())
config["goals"][0]["explore_graph"] = {"enabled": True}
path.write_text(json.dumps(config))
@@ -719,7 +736,7 @@ def test_hard_lease_is_required_for_new_delivery_but_not_successful_readback(
tmp_path, provider
):
from tests.control_plane.test_native_todo_planning_update import (
- fixture as canonical_fixture,
+ source_fixture as canonical_source_fixture,
)
from canonical_authority_fixture import initialize_canonical_authority
from loopx.control_plane.coordination.runtime_shadow import (
@@ -730,7 +747,7 @@ def test_hard_lease_is_required_for_new_delivery_but_not_successful_readback(
release_task_lease,
)
- path, state = canonical_fixture(tmp_path, False, provider)
+ path, state = canonical_source_fixture(tmp_path)
config = json.loads(path.read_text())
config["goals"][0]["explore_graph"] = {"enabled": True}
path.write_text(json.dumps(config))
@@ -784,6 +801,7 @@ def test_real_cli_open_capture_release_order_and_readback_replay(tmp_path, provi
from canonical_authority_fixture import initialize_canonical_authority
from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+ from loopx.control_plane.work_items.task_lease import acquire_task_lease
from tests.control_plane.test_quota_settlement_cli import (
_write_fixture, _run_cli, GOAL_ID, AGENT_ID, TODO_ID, TURN_ID,
)
@@ -798,12 +816,17 @@ def run(*command):
assert rc == 0, packet
return packet
+ source_todos = list_goal_todos(registry_path=path, goal_id=GOAL_ID)["todos"]
+ initialize_canonical_authority(runtime, GOAL_ID,
+ build_todo_runtime_shadow_projection(goal_id=GOAL_ID, todos=source_todos, handoff_mode="hard_lease"),
+ state_path=project / config["goals"][0]["state_file"], provider=provider)
+ acquire_task_lease(
+ registry_path=path, runtime_root=runtime, goal_id=GOAL_ID,
+ todo_id=TODO_ID, owner=AGENT_ID, idempotency_key="early-release",
+ )
run("todo", "claim", "--goal-id", GOAL_ID, "--todo-id", TODO_ID,
"--agent-id", AGENT_ID, "--claimed-by", AGENT_ID)
todos = list_goal_todos(registry_path=path, goal_id=GOAL_ID)["todos"]
- initialize_canonical_authority(runtime, GOAL_ID,
- build_todo_runtime_shadow_projection(goal_id=GOAL_ID, todos=todos, handoff_mode="hard_lease"),
- state_path=project / config["goals"][0]["state_file"], provider=provider)
binding = ["--goal-id", GOAL_ID, "--agent-id", AGENT_ID,
"--todo-id", TODO_ID, "--turn-instance-id", TURN_ID]
guard_command = ["quota", "should-run", "--codex-app", *binding,
diff --git a/tests/control_plane/test_goal_attention_integration.py b/tests/control_plane/test_goal_attention_integration.py
index 729d923287..6f6e6420bd 100644
--- a/tests/control_plane/test_goal_attention_integration.py
+++ b/tests/control_plane/test_goal_attention_integration.py
@@ -66,18 +66,22 @@ def test_status_automatically_rebuilds_stale_run_routing(tmp_path, monkeypatch,
registry, runtime, state = promoted_create_fixture(tmp_path, provider=provider)
else:
registry, runtime, state = tmp_path / "registry.json", tmp_path / "runtime", tmp_path / "state.md"
- state.write_text("# Goal\n\n## Agent Todo\n", encoding="utf-8")
+ # Historical Markdown remains readable, but no longer accepts fresh writes.
+ state.write_text("# Goal\n\n## Agent Todo\n\n"
+ "- [ ] Verify the current deliverable\n"
+ " \n", encoding="utf-8")
registry.write_text(json.dumps({"schema_version": 1, "common_runtime_root": str(runtime),
"goals": [{"id": "goal-a", "repo": str(tmp_path), "state_file": str(state),
"coordination": {"registered_agents": ["agent-a"]}}]}), encoding="utf-8")
config = json.loads(registry.read_text(encoding="utf-8"))
config["goals"][0]["adapter"] = {"kind": "read_only_project_map_v0", "status": "connected-read-only"}
registry.write_text(json.dumps(config), encoding="utf-8")
- result = subprocess.run([sys.executable, "-m", "loopx.cli", "--format", "json",
- "--registry", str(registry), "todo", "add", "--goal-id", "goal-a", "--role", "agent",
- "--text", "Verify the current deliverable", *(["--operation-id", "current-work"] if provider else [])],
- capture_output=True, text=True, timeout=45)
- assert result.returncode == 0, result.stdout + result.stderr
+ if provider:
+ result = subprocess.run([sys.executable, "-m", "loopx.cli", "--format", "json",
+ "--registry", str(registry), "todo", "add", "--goal-id", "goal-a", "--role", "agent",
+ "--text", "Verify the current deliverable", "--operation-id", "current-work"],
+ capture_output=True, text=True, timeout=45)
+ assert result.returncode == 0, result.stdout + result.stderr
state.write_text("---\nstatus: active\nupdated_at: 2026-10-15T09:00:00Z\n---\n\n"
+ state.read_text(encoding="utf-8"), encoding="utf-8")
runs = runtime / "goals/goal-a/runs"
@@ -104,7 +108,10 @@ def test_status_automatically_rebuilds_stale_run_routing(tmp_path, monkeypatch,
else:
assert item["source"] == "active_state"
assert item["status"] == "active_state_agent_todo"
- assert item["recommended_action"] == "[P1] Verify the current deliverable"
+ # Only the provider arm creates fresh work. Reading the historical
+ # unranked source must not apply a fresh-creation default.
+ expected = "[P1] Verify the current deliverable" if provider else "Verify the current deliverable"
+ assert item["recommended_action"] == expected
assert item.get("stale_latest_run_warning") is None
historical = status["run_history"]["goals"][0]["latest_status_run"]
assert historical["generated_at"] == record["generated_at"]
diff --git a/tests/control_plane/test_goal_channel_hard_lease_visibility.py b/tests/control_plane/test_goal_channel_hard_lease_visibility.py
index 2f6189d3ff..344e35f46b 100644
--- a/tests/control_plane/test_goal_channel_hard_lease_visibility.py
+++ b/tests/control_plane/test_goal_channel_hard_lease_visibility.py
@@ -5,12 +5,15 @@
from pathlib import Path
from typing import Any
+import pytest
+
+from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
+
from loopx.control_plane.goals.goal_channel import attach_goal_channel_projection
from loopx.control_plane.goals.goal_channel_projection import (
build_goal_channel_projection,
)
from loopx.control_plane.work_items.task_lease import (
- build_lease,
isoformat,
now_utc,
task_lease_dir,
@@ -48,19 +51,21 @@ def _write_active_lease(
ttl_seconds: int = 600,
) -> dict[str, Any]:
at = now_utc()
- lease = build_lease(
- goal_id=GOAL_ID,
- todo_id=todo_id,
- owner=owner,
- idempotency_key=f"{owner}-instance-1",
- write_scopes=["src/"],
- acquire_ttl_seconds=ttl_seconds,
- version=version,
- lease_epoch=1,
- acquired_at=isoformat(at),
- updated_at=isoformat(at),
- expires_at=isoformat(at + timedelta(seconds=ttl_seconds)),
- )
+ lease = {
+ "schema_version": "task_lease_v0",
+ "status": "active",
+ "goal_id": GOAL_ID,
+ "todo_id": todo_id,
+ "owner": owner,
+ "idempotency_key": f"{owner}-instance-1",
+ "write_scopes": ["src/"],
+ "acquire_ttl_seconds": ttl_seconds,
+ "version": version,
+ "lease_epoch": 1,
+ "acquired_at": isoformat(at),
+ "updated_at": isoformat(at),
+ "expires_at": isoformat(at + timedelta(seconds=ttl_seconds)),
+ }
path = task_lease_path(runtime_root=runtime_root, goal_id=GOAL_ID, todo_id=todo_id)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(lease), encoding="utf-8")
@@ -152,19 +157,21 @@ def test_missing_runtime_root_preserves_previous_projection_shape() -> None:
def test_expired_lease_is_not_surfaced(tmp_path: Path) -> None:
at = now_utc()
- lease = build_lease(
- goal_id=GOAL_ID,
- todo_id=TODO_ID,
- owner="agent-a",
- idempotency_key="agent-a-instance-1",
- write_scopes=[],
- acquire_ttl_seconds=60,
- version=1,
- lease_epoch=1,
- acquired_at=isoformat(at - timedelta(seconds=120)),
- updated_at=isoformat(at - timedelta(seconds=120)),
- expires_at=isoformat(at - timedelta(seconds=60)),
- )
+ lease = {
+ "schema_version": "task_lease_v0",
+ "status": "active",
+ "goal_id": GOAL_ID,
+ "todo_id": TODO_ID,
+ "owner": "agent-a",
+ "idempotency_key": "agent-a-instance-1",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 60,
+ "version": 1,
+ "lease_epoch": 1,
+ "acquired_at": isoformat(at - timedelta(seconds=120)),
+ "updated_at": isoformat(at - timedelta(seconds=120)),
+ "expires_at": isoformat(at - timedelta(seconds=60)),
+ }
path = task_lease_path(runtime_root=tmp_path, goal_id=GOAL_ID, todo_id=TODO_ID)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(lease), encoding="utf-8")
@@ -309,8 +316,9 @@ def _write_status_workspace(tmp_path: Path) -> tuple[Path, Path, Path]:
return registry, repo, runtime_root
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_status_attention_queue_surfaces_hard_lease_end_to_end(
- tmp_path: Path,
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
) -> None:
"""Regression pin for the real delivery surface.
@@ -321,20 +329,28 @@ def test_status_attention_queue_surfaces_hard_lease_end_to_end(
from the attention item's goal_channel_projection and fails this test.
"""
+ from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
+ from loopx.control_plane.todos.todo_summary import canonical_todo_read_record, structured_todo_item
from loopx.status import collect_status
- from loopx.todos import add_goal_todo
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
registry, repo, runtime_root = _write_status_workspace(tmp_path)
- added = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Deliver one bounded control-plane change.",
- task_class="advancement_task",
- claimed_by="agent-b",
- )
- todo_id = added["todo_id"]
+ todo_id = TODO_ID
lease = _write_active_lease(runtime_root, owner="agent-a", todo_id=todo_id)
+ # Import the conflicting historical facts together. A fresh canonical
+ # acquire would correctly refuse this owner/claim mismatch.
+ initialize_canonical_authority(
+ runtime_root, GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, handoff_mode="hard_lease", leases=[lease],
+ todos=[canonical_todo_read_record(structured_todo_item({
+ "todo_id": todo_id, "role": "agent", "status": "open",
+ "text": "Deliver one bounded control-plane change.",
+ "task_class": "advancement_task", "claimed_by": "agent-b",
+ }, role="agent", source_section="Agent Todo"))],
+ ),
+ state_path=repo / "ACTIVE_GOAL_STATE.md", provider=provider,
+ )
payload = collect_status(
registry_path=registry,
@@ -351,6 +367,8 @@ def test_status_attention_queue_surfaces_hard_lease_end_to_end(
assert items, "expected an attention item for the fixture goal"
projection = items[0].get("goal_channel_projection")
assert isinstance(projection, dict)
+ assert projection["coordination_observation"]["source_authority"] == f"{provider}_v0"
+ assert projection["coordination_observation"]["legacy_fallback_used"] is False
assert _entries_with_status(projection, "soft_claim") == [
{"todo_id": todo_id, "owner_agent": "agent-b", "status": "soft_claim"}
]
diff --git a/tests/control_plane/test_goal_handoff_mode.py b/tests/control_plane/test_goal_handoff_mode.py
deleted file mode 100644
index fd2a148c53..0000000000
--- a/tests/control_plane/test_goal_handoff_mode.py
+++ /dev/null
@@ -1,1755 +0,0 @@
-"""Per-goal handoff-mode gate over the soft-claim / hard-lease split brain.
-
-Modes (goal front-matter ``handoff_mode``):
-
-* absent / ``legacy``: today's dual behavior, byte-for-byte. The split-brain
- hole stays open BY DESIGN; the legacy tests below are characterization pins
- that document it, not aspirations.
-* ``soft_claim``: markdown claim is the only ownership record; task-lease
- acquire/renew/transfer are typed-rejected, release/inspect stay allowed.
-* ``hard_lease``: ownership changes on an existing todo require the actor to
- hold that todo's time-active lease; completion fence is mandatory and the
- legacy self-disarm state becomes a loud typed error. The delegated
- ``todo_lifecycle_authority`` override is the one audited door through the
- gate.
-"""
-
-from __future__ import annotations
-
-import json
-from contextlib import ExitStack
-from datetime import timedelta
-from pathlib import Path
-from typing import Any
-
-import pytest
-
-from loopx.control_plane.todos.contract import (
- TODO_TASK_CLASS_USER_ACTION,
-)
-from loopx.control_plane.todos.handoff_mode import (
- HANDOFF_MODE_HARD_LEASE,
- HANDOFF_MODE_LEGACY,
- HANDOFF_MODE_SOFT_CLAIM,
- HandoffModeError,
- goal_handoff_mode,
- set_goal_handoff_mode,
-)
-from loopx.control_plane.work_items import task_lease
-from loopx.control_plane.work_items.task_lease import (
- TaskLeaseError,
- acquire_task_lease,
- build_lease,
- hold_handoff_lease_holder_gate,
- inspect_task_lease,
- release_task_lease,
- renew_task_lease,
- task_lease_path,
- transfer_task_lease,
- write_lease,
-)
-from loopx.status import parse_active_state_todos
-from loopx.todos import (
- add_goal_todo,
- complete_goal_todo,
- supersede_goal_todo,
- update_goal_todo,
-)
-
-GOAL_ID = "handoff-mode-gate"
-AGENT_A = "agent-a"
-AGENT_B = "agent-b"
-ORCHESTRATOR = "agent-orchestrator"
-DECISION_SCOPE = "direction:action:approve_handoff"
-
-
-def _write_workspace(
- tmp_path: Path,
- *,
- handoff_mode: str | None = None,
- lifecycle_authority: list[dict[str, Any]] | None = None,
-) -> tuple[Path, Path]:
- repo = tmp_path / "repo"
- repo.mkdir(exist_ok=True)
- state = repo / "ACTIVE_GOAL_STATE.md"
- front_matter = [
- "---",
- f"goal_id: {GOAL_ID}",
- "updated_at: 2026-08-01T00:00:00+00:00",
- ]
- if handoff_mode is not None:
- front_matter.append(f"handoff_mode: {handoff_mode}")
- front_matter.append("---")
- state.write_text(
- "\n".join([*front_matter, "", "## Agent Todo", ""]) + "\n",
- encoding="utf-8",
- )
- coordination: dict[str, Any] = {
- "agent_model": "peer_v1",
- "registered_agents": [AGENT_A, AGENT_B, ORCHESTRATOR],
- }
- if lifecycle_authority is not None:
- coordination["todo_lifecycle_authority"] = lifecycle_authority
- registry = tmp_path / "registry.global.json"
- registry.write_text(
- json.dumps(
- {
- "common_runtime_root": str(tmp_path / "runtime"),
- "goals": [
- {
- "id": GOAL_ID,
- "domain": "harness_self_improvement",
- "status": "active",
- "repo": str(repo),
- "state_file": state.name,
- "adapter": {"kind": "harness_self_improvement"},
- "coordination": coordination,
- }
- ],
- }
- ),
- encoding="utf-8",
- )
- return registry, state
-
-
-def _set_frontmatter_mode(state: Path, mode: str) -> None:
- """Hand-edit the front-matter mode (simulates the out-of-contract path)."""
-
- lines = state.read_text(encoding="utf-8").splitlines()
- close = next(i for i in range(1, len(lines)) if lines[i].strip() == "---")
- for index in range(1, close):
- if lines[index].split(":", 1)[0].strip() == "handoff_mode":
- lines[index] = f"handoff_mode: {mode}"
- break
- else:
- lines.insert(close, f"handoff_mode: {mode}")
- state.write_text("\n".join(lines) + "\n", encoding="utf-8")
-
-
-def _add_todo(registry: Path, *, claimed_by: str | None = None) -> dict[str, Any]:
- return add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Deliver one bounded control-plane change.",
- task_class="advancement_task",
- claimed_by=claimed_by,
- )
-
-
-def _agent_todo(state: Path, todo_id: str) -> dict[str, Any]:
- todos = parse_active_state_todos(state.read_text(encoding="utf-8"))
- return next(
- item for item in todos["agent_todos"]["items"] if item["todo_id"] == todo_id
- )
-
-
-def _user_todo(state: Path, todo_id: str) -> dict[str, Any]:
- todos = parse_active_state_todos(state.read_text(encoding="utf-8"))
- return next(
- item for item in todos["user_todos"]["items"] if item["todo_id"] == todo_id
- )
-
-
-def _add_exact_user_gate_pair(
- registry: Path,
-) -> tuple[dict[str, Any], dict[str, Any]]:
- target = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Continue after the exact owner decision.",
- task_class="advancement_task",
- claimed_by=AGENT_A,
- required_decision_scopes=[DECISION_SCOPE],
- )
- gate = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="user",
- text="Approve the exact handoff decision.",
- task_class="user_gate",
- blocks_agent=AGENT_A,
- decision_scope=DECISION_SCOPE,
- unblocks_todo_id=target["todo_id"],
- )
- return target, gate
-
-
-def _acquire(
- registry: Path,
- tmp_path: Path,
- todo_id: str,
- *,
- owner: str,
- key: str = "turn-lease-1",
- ttl_seconds: int = 600,
-) -> dict[str, Any]:
- return acquire_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo_id,
- owner=owner,
- idempotency_key=key,
- ttl_seconds=ttl_seconds,
- )
-
-
-def _claim(registry: Path, todo_id: str, agent: str, **kwargs: Any) -> dict[str, Any]:
- return update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo_id,
- claimed_by=agent,
- agent_id=agent,
- claim_only=True,
- **kwargs,
- )
-
-
-# ---------------------------------------------------------------------------
-# Mode reader
-# ---------------------------------------------------------------------------
-
-
-def test_goal_handoff_mode_defaults_to_legacy_when_absent() -> None:
- text = "---\ngoal_id: g\n---\n\n## Agent Todo\n"
- assert goal_handoff_mode(text) == HANDOFF_MODE_LEGACY
-
-
-def test_goal_handoff_mode_reads_explicit_values() -> None:
- for mode in (HANDOFF_MODE_LEGACY, HANDOFF_MODE_SOFT_CLAIM, HANDOFF_MODE_HARD_LEASE):
- text = f"---\ngoal_id: g\nhandoff_mode: {mode}\n---\n"
- assert goal_handoff_mode(text) == mode
-
-
-def test_goal_handoff_mode_rejects_unknown_value_loudly() -> None:
- with pytest.raises(HandoffModeError) as error:
- goal_handoff_mode("---\ngoal_id: g\nhandoff_mode: banana\n---\n")
-
- assert error.value.code == "invalid_handoff_mode"
-
-
-def test_invalid_goal_handoff_mode_runtime_read_fails_closed_without_mutation(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(tmp_path)
- todo = _add_todo(registry)
- _set_frontmatter_mode(state, "banana")
- before = state.read_bytes()
-
- with pytest.raises(HandoffModeError) as error:
- _claim(registry, todo["todo_id"], AGENT_A)
-
- assert error.value.code == "invalid_handoff_mode"
- assert error.value.payload["handoff_mode"] == "banana"
- assert state.read_bytes() == before
-
-
-def test_goal_handoff_mode_without_frontmatter_is_legacy() -> None:
- assert goal_handoff_mode("## Agent Todo\n") == HANDOFF_MODE_LEGACY
-
-
-
-
-# ---------------------------------------------------------------------------
-# (a) Legacy characterization pins: the split-brain hole stays open, loudly.
-# ---------------------------------------------------------------------------
-
-
-def test_legacy_pin_claim_over_foreign_active_lease_silently_succeeds(
- tmp_path: Path,
-) -> None:
- """S1 pin: soft claim never reads the lease store, so two owners coexist."""
-
- registry, state = _write_workspace(tmp_path)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
-
- result = _claim(registry, todo["todo_id"], AGENT_B)
-
- assert result["ok"] is True
- assert result["handoff_mode"] == HANDOFF_MODE_LEGACY
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_B
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- )
- lease = json.loads(lease_file.read_text(encoding="utf-8"))
- assert lease["owner"] == AGENT_A
- assert lease["status"] == "active"
-
-
-def test_legacy_pin_foreign_claim_makes_lease_renew_fail_typed(
- tmp_path: Path,
-) -> None:
- registry, _state = _write_workspace(tmp_path)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- _claim(registry, todo["todo_id"], AGENT_B)
-
- with pytest.raises(TaskLeaseError) as error:
- renew_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AGENT_A,
- idempotency_key="turn-lease-1",
- expected_version=1,
- )
-
- assert error.value.code == "owner_conflicts_with_claim"
-
-
-def test_legacy_pin_fence_self_disarms_and_claimant_completes_keyless(
- tmp_path: Path,
-) -> None:
- """The completion fence disarms itself in the split-brain state: the
- markdown claimant completes WITHOUT any lease credentials while the
- foreign lease sits on disk status=active."""
-
- registry, _state = _write_workspace(tmp_path)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- _claim(registry, todo["todo_id"], AGENT_B)
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_B,
- evidence="done without lease credentials",
- )
-
- assert result["ok"] is True
- assert result["task_lease_fence"] == {
- "schema_version": "task_lease_v0",
- "required": False,
- "active": False,
- }
- assert result["handoff_mode"] == HANDOFF_MODE_LEGACY
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- )
- assert json.loads(lease_file.read_text(encoding="utf-8"))["status"] == "active"
-
-
-def test_legacy_pin_lease_holder_cannot_complete_after_foreign_claim(
- tmp_path: Path,
-) -> None:
- registry, _state = _write_workspace(tmp_path)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- _claim(registry, todo["todo_id"], AGENT_B)
-
- with pytest.raises(ValueError, match="cannot complete"):
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- task_lease_idempotency_key="turn-lease-1",
- )
-
-
-def test_legacy_lease_verbs_report_handoff_mode(tmp_path: Path) -> None:
- registry, _state = _write_workspace(tmp_path)
- todo = _add_todo(registry)
-
- acquired = _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- assert acquired["handoff_mode"] == HANDOFF_MODE_LEGACY
- assert "settlement" not in acquired
-
- inspected = inspect_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- )
- assert inspected["handoff_mode"] == HANDOFF_MODE_LEGACY
-
-
-# ---------------------------------------------------------------------------
-# (b) soft_claim: markdown claim only; lease mutations typed-rejected.
-# ---------------------------------------------------------------------------
-
-
-def test_soft_claim_rejects_lease_acquire(tmp_path: Path) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_SOFT_CLAIM)
- todo = _add_todo(registry)
-
- with pytest.raises(TaskLeaseError) as error:
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
-
- assert error.value.code == "handoff_mode_forbids_lease"
- assert error.value.payload["handoff_mode"] == HANDOFF_MODE_SOFT_CLAIM
-
-
-def test_soft_claim_rejects_renew_and_transfer_of_legacy_leftover(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(tmp_path)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- _set_frontmatter_mode(state, HANDOFF_MODE_SOFT_CLAIM)
-
- with pytest.raises(TaskLeaseError) as renew_error:
- renew_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AGENT_A,
- idempotency_key="turn-lease-1",
- )
- assert renew_error.value.code == "handoff_mode_forbids_lease"
-
- with pytest.raises(TaskLeaseError) as transfer_error:
- transfer_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AGENT_A,
- idempotency_key="turn-lease-1",
- new_owner=AGENT_B,
- new_idempotency_key="turn-lease-2",
- )
- assert transfer_error.value.code == "handoff_mode_forbids_lease"
-
-
-def test_soft_claim_allows_release_and_inspect_of_legacy_leftover(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(tmp_path)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- _set_frontmatter_mode(state, HANDOFF_MODE_SOFT_CLAIM)
-
- inspected = inspect_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- )
- assert inspected["ok"] is True
- assert inspected["handoff_mode"] == HANDOFF_MODE_SOFT_CLAIM
-
- released = release_task_lease(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AGENT_A,
- idempotency_key="turn-lease-1",
- expected_version=1,
- registry_path=registry,
- )
- assert released["ok"] is True
- assert released["released"] is True
-
-
-def test_soft_claim_todo_verbs_behave_like_legacy(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_SOFT_CLAIM)
- todo = _add_todo(registry)
-
- claim = _claim(registry, todo["todo_id"], AGENT_B)
- assert claim["ok"] is True
- assert claim["handoff_mode"] == HANDOFF_MODE_SOFT_CLAIM
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_B
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_B,
- evidence="soft mode keyless completion",
- )
- assert result["ok"] is True
- assert result["handoff_mode"] == HANDOFF_MODE_SOFT_CLAIM
- assert result["task_lease_fence"]["required"] is False
-
-
-@pytest.mark.parametrize(
- ("configured_mode", "expected_mode"),
- [
- (None, HANDOFF_MODE_LEGACY),
- (HANDOFF_MODE_SOFT_CLAIM, HANDOFF_MODE_SOFT_CLAIM),
- ],
-)
-def test_local_terminal_replay_matches_completion_response_shape_without_lease(
- tmp_path: Path,
- configured_mode: str | None,
- expected_mode: str,
-) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=configured_mode)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- turn_key = "turn-terminal-response-parity"
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- evidence="complete the lease-free handoff-mode case",
- completion_turn_key=turn_key,
- no_followup=True,
- )
- completed_state = state.read_text(encoding="utf-8")
- replayed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- evidence="retry the same local completion turn",
- completion_turn_key=turn_key,
- no_followup=True,
- )
-
- assert completed["handoff_mode"] == expected_mode
- assert replayed["handoff_mode"] == expected_mode
- assert replayed["completed"] is True
- assert replayed["changed"] is False
- assert replayed["idempotent_replay"] is True
- assert state.read_text(encoding="utf-8") == completed_state
-
-
-def test_local_terminal_replay_reports_current_mode_not_original_result_proof(
- tmp_path: Path,
-) -> None:
- """Replay parity reads current goal mode; it is not an original receipt."""
-
- registry, state = _write_workspace(tmp_path)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- turn_key = "turn-current-mode-parity"
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- evidence="complete before a legal quiescent mode transition",
- completion_turn_key=turn_key,
- no_followup=True,
- )
- assert completed["handoff_mode"] == HANDOFF_MODE_LEGACY
- changed_mode = set_goal_handoff_mode(
- registry_path=registry,
- goal_id=GOAL_ID,
- mode=HANDOFF_MODE_SOFT_CLAIM,
- )
- assert changed_mode["changed"] is True
- mode_changed_state = state.read_text(encoding="utf-8")
-
- replayed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- evidence="retry after the goal mode changed",
- completion_turn_key=turn_key,
- no_followup=True,
- )
-
- assert replayed["handoff_mode"] == HANDOFF_MODE_SOFT_CLAIM
- assert replayed["changed"] is False
- assert replayed["idempotent_replay"] is True
- assert state.read_text(encoding="utf-8") == mode_changed_state
-
-
-def test_local_terminal_replay_fails_closed_on_current_invalid_mode(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(tmp_path)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- turn_key = "turn-invalid-mode-replay"
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- completion_turn_key=turn_key,
- no_followup=True,
- )
- _set_frontmatter_mode(state, "banana")
- invalid_state = state.read_text(encoding="utf-8")
-
- with pytest.raises(HandoffModeError) as error:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- completion_turn_key=turn_key,
- no_followup=True,
- )
-
- assert error.value.code == "invalid_handoff_mode"
- assert state.read_text(encoding="utf-8") == invalid_state
-
-
-# ---------------------------------------------------------------------------
-# (c) hard_lease: ownership mutations require the actor to hold the lease.
-# ---------------------------------------------------------------------------
-
-
-def test_hard_lease_claim_without_lease_is_typed_rejected(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry)
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(TaskLeaseError) as error:
- _claim(registry, todo["todo_id"], AGENT_B)
-
- assert error.value.code == "handoff_mode_requires_lease"
- assert error.value.payload["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert state.read_text(encoding="utf-8") == before
-
-
-def test_hard_lease_claim_by_non_owner_is_typed_rejected(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(TaskLeaseError) as error:
- _claim(registry, todo["todo_id"], AGENT_B)
-
- assert error.value.code == "handoff_mode_requires_lease"
- assert error.value.payload["lease_owner"] == AGENT_A
- assert state.read_text(encoding="utf-8") == before
-
-
-def test_hard_lease_holder_claim_succeeds(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
-
- result = _claim(registry, todo["todo_id"], AGENT_A)
-
- assert result["ok"] is True
- assert result["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_A
-
-
-def test_hard_lease_holder_can_handoff_claim_and_peer_can_finish(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- acquired_by_a = _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- )
-
- handed_off = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AGENT_B,
- agent_id=AGENT_A,
- )
-
- assert handed_off["ok"] is True
- assert handed_off["mutation_authority"]["actor_agent_id"] == AGENT_A
- assert handed_off["task_lease_holder_gate"]["owner"] == AGENT_A
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_B
- lease_held_by_a = json.loads(lease_file.read_text(encoding="utf-8"))
- assert lease_held_by_a["owner"] == AGENT_A
- assert lease_held_by_a["status"] == "active"
-
- state_during_handoff = state.read_text(encoding="utf-8")
- with pytest.raises(TaskLeaseError) as claim_error:
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_B,
- clear_claim=True,
- )
- assert claim_error.value.code == "handoff_mode_requires_lease"
- assert claim_error.value.payload["actor_agent_id"] == AGENT_B
- assert claim_error.value.payload["lease_owner"] == AGENT_A
- assert state.read_text(encoding="utf-8") == state_during_handoff
- assert json.loads(lease_file.read_text(encoding="utf-8")) == lease_held_by_a
-
- with pytest.raises(TaskLeaseError) as completion_error:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_B,
- evidence="peer cannot complete before acquiring the hard lease",
- )
- assert completion_error.value.code == "handoff_mode_lease_claim_divergence"
- assert completion_error.value.payload["lease_owner"] == AGENT_A
- assert state.read_text(encoding="utf-8") == state_during_handoff
- assert json.loads(lease_file.read_text(encoding="utf-8")) == lease_held_by_a
-
- released_by_a = release_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AGENT_A,
- idempotency_key="turn-lease-1",
- expected_version=acquired_by_a["lease"]["version"],
- )
- assert released_by_a["released"] is True
- assert json.loads(lease_file.read_text(encoding="utf-8"))["status"] == "released"
-
- acquired_by_b = _acquire(
- registry,
- tmp_path,
- todo["todo_id"],
- owner=AGENT_B,
- key="turn-lease-2",
- )
- completed_by_b = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_B,
- task_lease_idempotency_key="turn-lease-2",
- task_lease_expected_version=acquired_by_b["lease"]["version"],
- evidence="peer completed after acquiring the hard lease",
- )
-
- assert completed_by_b["ok"] is True
- assert completed_by_b["task_lease_fence"]["execution_instance_verified"] is True
- assert completed_by_b["task_lease_fence"]["released"] is True
- assert _agent_todo(state, todo["todo_id"])["status"] == "done"
- assert json.loads(lease_file.read_text(encoding="utf-8"))["status"] == "released"
-
-
-def test_hard_lease_blocks_todo_add_reassigning_a_leased_todo(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- )
- state_before = state.read_text(encoding="utf-8")
- lease_before = json.loads(lease_file.read_text(encoding="utf-8"))
-
- with pytest.raises(TaskLeaseError) as add_error:
- _add_todo(registry, claimed_by=AGENT_B)
-
- assert add_error.value.code == "handoff_mode_requires_lease"
- assert add_error.value.payload["actor_agent_id"] == AGENT_B
- assert add_error.value.payload["lease_owner"] == AGENT_A
- assert state.read_text(encoding="utf-8") == state_before
- assert json.loads(lease_file.read_text(encoding="utf-8")) == lease_before
-
-
-def test_hard_lease_allows_todo_add_creating_a_claimed_todo(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
-
- created = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Draft the follow-up migration note.",
- task_class="advancement_task",
- claimed_by=AGENT_A,
- )
-
- assert created["ok"] is True
- assert created["added"] is True
- assert created["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert "task_lease_holder_gate" not in created
- assert _agent_todo(state, created["todo_id"])["claimed_by"] == AGENT_A
-
-
-def test_hard_lease_allows_todo_add_repeating_the_existing_claim(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
-
- repeated = _add_todo(registry, claimed_by=AGENT_A)
-
- assert repeated["ok"] is True
- assert repeated["already_exists"] is True
- assert repeated["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert "task_lease_holder_gate" not in repeated
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_A
-
-
-def test_hard_lease_lease_holder_may_reassign_through_todo_add(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
-
- handed_off = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Deliver one bounded control-plane change.",
- task_class="advancement_task",
- claimed_by=AGENT_B,
- agent_id=AGENT_A,
- )
-
- assert handed_off["ok"] is True
- assert handed_off["already_exists"] is True
- assert handed_off["task_lease_holder_gate"]["owner"] == AGENT_A
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_B
-
-
-def test_legacy_pin_todo_add_reassigns_a_leased_todo(tmp_path: Path) -> None:
- """Characterizes today's behavior: the add path is ungated in legacy mode."""
-
- registry, state = _write_workspace(tmp_path)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- )
-
- reassigned = _add_todo(registry, claimed_by=AGENT_B)
-
- assert reassigned["ok"] is True
- assert reassigned["already_exists"] is True
- assert reassigned["handoff_mode"] == HANDOFF_MODE_LEGACY
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_B
- assert json.loads(lease_file.read_text(encoding="utf-8"))["owner"] == AGENT_A
-
-
-def test_hard_lease_expired_lease_does_not_authorize_claim(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
-) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A, ttl_seconds=60)
- real_now = task_lease.now_utc()
- monkeypatch.setattr(task_lease, "now_utc", lambda: real_now + timedelta(hours=2))
-
- with pytest.raises(TaskLeaseError) as error:
- _claim(registry, todo["todo_id"], AGENT_A)
-
- assert error.value.code == "handoff_mode_requires_lease"
-
-
-def test_hard_lease_clear_claim_requires_lease(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
-
- with pytest.raises(TaskLeaseError) as error:
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- clear_claim=True,
- )
- assert error.value.code == "handoff_mode_requires_lease"
-
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- result = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- clear_claim=True,
- )
- assert result["ok"] is True
- assert not _agent_todo(state, todo["todo_id"]).get("claimed_by")
-
-
-def test_hard_lease_claimed_by_update_requires_lease(tmp_path: Path) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
-
- with pytest.raises(TaskLeaseError) as error:
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AGENT_A,
- agent_id=AGENT_A,
- note="keep ownership, add note",
- )
-
- assert error.value.code == "handoff_mode_requires_lease"
-
-
-def test_hard_lease_non_ownership_update_needs_no_lease(tmp_path: Path) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry)
-
- result = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_B,
- note="metadata-only update",
- )
-
- assert result["ok"] is True
- assert result["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
-
-
-def test_hard_lease_creation_time_assignment_stays_allowed(tmp_path: Path) -> None:
- """A fresh todo id can never hold a lease; add --claimed-by is a
- deliberate boundary that stays open in hard_lease mode."""
-
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
-
- todo = _add_todo(registry, claimed_by=AGENT_A)
-
- assert todo["ok"] is True
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_A
-
-
-def test_hard_lease_completion_successor_assignment_stays_allowed(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- task_lease_idempotency_key="turn-lease-1",
- task_lease_expected_version=1,
- evidence="done with verified lease key",
- next_agent_todo="Follow-up slice for the peer lane.",
- next_claimed_by=AGENT_B,
- )
-
- assert result["ok"] is True
- successor_id = result["next_todos"][0]["todo_id"]
- assert _agent_todo(state, successor_id)["claimed_by"] == AGENT_B
-
-
-def test_hard_lease_completion_without_lease_is_typed_rejected(
- tmp_path: Path,
-) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
-
- with pytest.raises(TaskLeaseError) as error:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- evidence="no lease exists",
- )
-
- assert error.value.code == "handoff_mode_requires_lease"
- assert error.value.payload["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
-
-
-def test_hard_lease_completion_with_verified_key_succeeds(tmp_path: Path) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- task_lease_idempotency_key="turn-lease-1",
- task_lease_expected_version=1,
- evidence="done with verified lease key",
- )
-
- assert result["ok"] is True
- assert result["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert result["task_lease_fence"]["required"] is True
- assert result["task_lease_fence"]["execution_instance_verified"] is True
-
-
-def test_hard_lease_non_gate_user_todo_completion_without_lease_is_typed_rejected(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(
- tmp_path,
- handoff_mode=HANDOFF_MODE_HARD_LEASE,
- )
- action = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="user",
- text="Owner follow-up action.",
- task_class=TODO_TASK_CLASS_USER_ACTION,
- bound_agent=AGENT_A,
- )
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(TaskLeaseError) as error:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=action["todo_id"],
- role="user",
- agent_id=AGENT_A,
- evidence="non-gate user todos must still cross the lease fence",
- )
-
- assert error.value.code == "handoff_mode_requires_lease"
- assert state.read_text(encoding="utf-8") == before
-
-
-def test_hard_lease_exact_user_gate_completion_uses_fence_and_releases_lease(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(
- tmp_path,
- handoff_mode=HANDOFF_MODE_HARD_LEASE,
- )
- target, gate = _add_exact_user_gate_pair(registry)
- _acquire(registry, tmp_path, gate["todo_id"], owner=AGENT_A)
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=gate["todo_id"],
- )
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=gate["todo_id"],
- role="user",
- decision_outcome="approve",
- agent_id=AGENT_A,
- task_lease_idempotency_key="turn-lease-1",
- task_lease_expected_version=1,
- evidence="lease holder approved the exact decision scope",
- )
-
- assert result["ok"] is True
- assert result["mutation_authority"]["mode"] == (
- "exact_user_gate_decision_scope_override"
- )
- assert "handoff_gate_overridden" not in result
- assert result["task_lease_fence"] == {
- "schema_version": "task_lease_v0",
- "required": True,
- "active": True,
- "owner": AGENT_A,
- "version": 1,
- "lease_epoch": 1,
- "execution_instance_verified": True,
- "released": True,
- }
- assert json.loads(lease_file.read_text(encoding="utf-8"))["status"] == "released"
- assert _user_todo(state, gate["todo_id"])["status"] == "done"
- target_after = _agent_todo(state, target["todo_id"])
- assert target_after["status"] == "open"
- assert not target_after.get("required_decision_scopes")
- assert result["decision_scope_resolution"]["state"] == "resolved"
- assert (
- result["decision_scope_resolution"]["remaining_required_decision_scopes"] == []
- )
-
-
-@pytest.mark.parametrize(
- ("lease_owner", "actor_agent_id", "completion_key"),
- [
- (AGENT_A, AGENT_A, "wrong-turn-key"),
- (AGENT_B, AGENT_A, "turn-lease-1"),
- ],
- ids=("wrong-key", "foreign-holder"),
-)
-def test_hard_lease_exact_user_gate_rejects_invalid_lease_fence_without_state_change(
- tmp_path: Path,
- lease_owner: str,
- actor_agent_id: str,
- completion_key: str,
-) -> None:
- registry, state = _write_workspace(
- tmp_path,
- handoff_mode=HANDOFF_MODE_HARD_LEASE,
- )
- _target, gate = _add_exact_user_gate_pair(registry)
- _acquire(registry, tmp_path, gate["todo_id"], owner=lease_owner)
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=gate["todo_id"],
- )
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(TaskLeaseError) as error:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=gate["todo_id"],
- role="user",
- decision_outcome="approve",
- agent_id=actor_agent_id,
- task_lease_idempotency_key=completion_key,
- task_lease_expected_version=1,
- evidence="invalid execution instance must not consume the gate",
- )
-
- assert error.value.code == "lease_cas_mismatch"
- assert state.read_text(encoding="utf-8") == before
- persisted_lease = json.loads(lease_file.read_text(encoding="utf-8"))
- assert persisted_lease["status"] == "active"
- assert persisted_lease["owner"] == lease_owner
-
-
-def test_hard_lease_user_gate_completion_auto_acquires_key(tmp_path: Path) -> None:
- """A user-gate decision completes without caller-supplied lease credentials.
-
- The completion fence mints the key under the per-goal lease lock, verifies
- it, and releases it after the committed writeback - the human decision is
- the authority, so the presenter never needs to hold a key across the wait.
- """
-
- registry, state = _write_workspace(
- tmp_path,
- handoff_mode=HANDOFF_MODE_HARD_LEASE,
- )
- target, gate = _add_exact_user_gate_pair(registry)
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=gate["todo_id"],
- )
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=gate["todo_id"],
- role="user",
- decision_outcome="approve",
- agent_id=AGENT_A,
- evidence="owner decision recorded under an auto-acquired completion key",
- )
-
- assert result["ok"] is True
- assert result["task_lease_fence"]["auto_acquired"] is True
- assert result["task_lease_fence"]["required"] is True
- assert result["task_lease_fence"]["execution_instance_verified"] is True
- assert result["task_lease_fence"]["released"] is True
- assert json.loads(lease_file.read_text(encoding="utf-8"))["status"] == "released"
- assert _user_todo(state, gate["todo_id"])["status"] == "done"
- target_after = _agent_todo(state, target["todo_id"])
- assert target_after["status"] == "open"
- assert not target_after.get("required_decision_scopes")
-
-
-def test_hard_lease_user_gate_auto_acquire_does_not_displace_existing_lease(
- tmp_path: Path,
-) -> None:
- """An active lease held by another agent is never displaced automatically."""
-
- registry, state = _write_workspace(
- tmp_path,
- handoff_mode=HANDOFF_MODE_HARD_LEASE,
- )
- _target, gate = _add_exact_user_gate_pair(registry)
- _acquire(registry, tmp_path, gate["todo_id"], owner=AGENT_B, key="turn-lease-b")
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(TaskLeaseError) as error:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=gate["todo_id"],
- role="user",
- decision_outcome="approve",
- agent_id=AGENT_A,
- evidence="agent A must not displace B's completion key",
- )
-
- assert error.value.code == "lease_fence_required"
- assert state.read_text(encoding="utf-8") == before
-
-
-def test_hard_lease_local_terminal_replay_keeps_response_shape_after_release(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
-) -> None:
- """Same-turn local replay does not re-enter lease authority or mutate state."""
-
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- lease_key = "turn-hard-lease-instance"
- turn_key = "turn-hard-lease-completion"
- _acquire(
- registry,
- tmp_path,
- todo["todo_id"],
- owner=AGENT_A,
- key=lease_key,
- )
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- task_lease_idempotency_key=lease_key,
- task_lease_expected_version=1,
- completion_turn_key=turn_key,
- evidence="complete under the held hard lease",
- no_followup=True,
- )
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- )
- assert completed["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert completed["task_lease_fence"]["released"] is True
- terminal_lease = lease_file.read_text(encoding="utf-8")
- assert json.loads(terminal_lease)["status"] == "released"
- completed_state = state.read_text(encoding="utf-8")
-
- def unexpected_lease_effect(*_args: Any, **_kwargs: Any) -> None:
- pytest.fail("terminal replay must not re-enter the task-lease effect path")
-
- monkeypatch.setattr(
- "loopx.todos.hold_task_lease_mutation_fence",
- unexpected_lease_effect,
- )
- monkeypatch.setattr(
- "loopx.todos.release_verified_task_lease_fence",
- unexpected_lease_effect,
- )
- replayed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- task_lease_idempotency_key=lease_key,
- task_lease_expected_version=1,
- completion_turn_key=turn_key,
- evidence="retry the same completion after lease release",
- no_followup=True,
- )
-
- assert replayed["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert replayed["completed"] is True
- assert replayed["changed"] is False
- assert replayed["idempotent_replay"] is True
- assert state.read_text(encoding="utf-8") == completed_state
- assert lease_file.read_text(encoding="utf-8") == terminal_lease
-
-
-def test_hard_lease_self_disarm_state_becomes_loud_typed_error(
- tmp_path: Path,
-) -> None:
- """Legacy remnants or manual edits can leave claimed_by diverged from a
- time-active lease. In hard_lease mode that must fail loudly instead of
- silently completing keyless."""
-
- registry, state = _write_workspace(tmp_path)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- _claim(registry, todo["todo_id"], AGENT_B)
- _set_frontmatter_mode(state, HANDOFF_MODE_HARD_LEASE)
-
- with pytest.raises(TaskLeaseError) as error:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_B,
- evidence="split-brain keyless completion attempt",
- )
-
- assert error.value.code == "handoff_mode_lease_claim_divergence"
- assert error.value.payload["lease_owner"] == AGENT_A
-
-
-# ---------------------------------------------------------------------------
-# (d) the door: delegated todo_lifecycle_authority passes the hard gate.
-# ---------------------------------------------------------------------------
-
-
-def _orchestrator_grant() -> list[dict[str, Any]]:
- return [
- {
- "agent_id": ORCHESTRATOR,
- "actions": ["reassign", "update", "complete"],
- "requires_reason": True,
- }
- ]
-
-
-def test_hard_lease_delegated_reassign_passes_gate_with_marker(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(
- tmp_path,
- handoff_mode=HANDOFF_MODE_HARD_LEASE,
- lifecycle_authority=_orchestrator_grant(),
- )
- todo = _add_todo(registry, claimed_by=AGENT_A)
-
- result = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AGENT_B,
- agent_id=ORCHESTRATOR,
- authority_reason="peer lane is stalled; reassigning",
- )
-
- assert result["ok"] is True
- assert result["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert result["handoff_gate_overridden"] is True
- assert result["mutation_authority"]["mode"] == "delegated_orchestration_override"
- assert (
- result["mutation_authority"]["authority_reason"]
- == "peer lane is stalled; reassigning"
- )
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_B
-
-
-def test_hard_lease_delegated_complete_passes_gate_with_marker(
- tmp_path: Path,
-) -> None:
- registry, _state = _write_workspace(
- tmp_path,
- handoff_mode=HANDOFF_MODE_HARD_LEASE,
- lifecycle_authority=_orchestrator_grant(),
- )
- todo = _add_todo(registry, claimed_by=AGENT_A)
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=ORCHESTRATOR,
- authority_reason="closing stalled item for the goal owner",
- evidence="delegated completion",
- )
-
- assert result["ok"] is True
- assert result["handoff_gate_overridden"] is True
- assert result["task_lease_fence"]["required"] is False
-
-
-def test_delegated_terminal_replay_parity_excludes_original_gate_effect_marker(
- tmp_path: Path,
-) -> None:
- """Current mode is replayable; an original gate override effect is not."""
-
- registry, state = _write_workspace(
- tmp_path,
- handoff_mode=HANDOFF_MODE_HARD_LEASE,
- lifecycle_authority=_orchestrator_grant(),
- )
- todo = _add_todo(registry, claimed_by=AGENT_A)
- turn_key = "turn-delegated-terminal-replay"
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=ORCHESTRATOR,
- authority_reason="close the item through delegated authority",
- completion_turn_key=turn_key,
- no_followup=True,
- )
- completed_state = state.read_text(encoding="utf-8")
-
- replayed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=ORCHESTRATOR,
- authority_reason="retry the delegated completion turn",
- completion_turn_key=turn_key,
- no_followup=True,
- )
-
- assert completed["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert completed["handoff_gate_overridden"] is True
- assert replayed["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert replayed["mutation_authority"]["mode"] == (
- "delegated_orchestration_override"
- )
- assert "handoff_gate_overridden" not in replayed
- assert replayed["changed"] is False
- assert replayed["idempotent_replay"] is True
- assert state.read_text(encoding="utf-8") == completed_state
-
-
-def test_hard_lease_gate_has_no_untyped_bypass_without_grant(
- tmp_path: Path,
-) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
-
- with pytest.raises(ValueError):
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AGENT_B,
- agent_id=ORCHESTRATOR,
- authority_reason="no grant exists for this actor",
- )
-
-
-# ---------------------------------------------------------------------------
-# (f) identity normalization on both sides of the holder check.
-# ---------------------------------------------------------------------------
-
-
-def test_hard_lease_holder_check_normalizes_both_sides(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
-
- result = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=" Agent-A ",
- agent_id="AGENT-A",
- claim_only=True,
- )
-
- assert result["ok"] is True
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AGENT_A
-
-
-def test_holder_gate_normalizes_lease_owner_case_variants(tmp_path: Path) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry)
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- )
- now = task_lease.now_utc()
- write_lease(
- lease_file,
- build_lease(
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner="Agent-A",
- idempotency_key="turn-lease-1",
- write_scopes=[],
- acquire_ttl_seconds=600,
- version=1,
- lease_epoch=1,
- acquired_at=task_lease.isoformat(now),
- updated_at=task_lease.isoformat(now),
- expires_at=task_lease.isoformat(now + timedelta(seconds=600)),
- ),
- )
-
- with ExitStack() as stack:
- gate = stack.enter_context(
- hold_handoff_lease_holder_gate(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- actor_agent_id=" agent-a ",
- )
- )
- assert gate["active"] is True
- assert gate["owner"] == AGENT_A
-
-
-# ---------------------------------------------------------------------------
-# supersede is a completion-equivalent transition (status -> done) and must
-# cross the same lease fence as complete
-# ---------------------------------------------------------------------------
-
-
-def test_hard_lease_supersede_without_lease_is_typed_rejected(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- before = state.read_bytes()
-
- with pytest.raises(TaskLeaseError) as error:
- supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- reason="no lease exists",
- )
-
- assert error.value.code == "handoff_mode_requires_lease"
- assert error.value.payload["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert state.read_bytes() == before
-
-
-def test_hard_lease_supersede_by_non_holder_without_key_is_typed_rejected(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime", goal_id=GOAL_ID, todo_id=todo["todo_id"]
- )
- before = state.read_bytes()
-
- with pytest.raises(TaskLeaseError) as error:
- supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_B,
- reason="peer supersedes without any key",
- )
-
- assert error.value.code == "lease_fence_required"
- assert state.read_bytes() == before
- assert _agent_todo(state, todo["todo_id"])["done"] is False
- assert lease_file.exists()
-
-
-def test_hard_lease_supersede_by_holder_without_key_is_typed_rejected(
- tmp_path: Path,
-) -> None:
- """The fence is keyed to the execution instance, not to the agent identity."""
-
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
-
- with pytest.raises(TaskLeaseError) as error:
- supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- reason="holder forgot the key",
- )
-
- assert error.value.code == "lease_fence_required"
- assert _agent_todo(state, todo["todo_id"])["done"] is False
-
-
-def test_hard_lease_supersede_with_verified_key_succeeds_and_releases_lease(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime", goal_id=GOAL_ID, todo_id=todo["todo_id"]
- )
-
- result = supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- reason="superseded with the verified key",
- next_agent_todo="Continue the superseding work.",
- task_lease_idempotency_key="turn-lease-1",
- task_lease_expected_version=1,
- )
-
- assert result["ok"] is True
- assert result["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert result["task_lease_fence"]["required"] is True
- assert result["task_lease_fence"]["execution_instance_verified"] is True
- assert result["task_lease_fence"]["released"] is True
- assert _agent_todo(state, todo["todo_id"])["done"] is True
- assert json.loads(lease_file.read_text(encoding="utf-8"))["status"] == "released"
- assert result["next_todos"][0]["added"] is True
-
-
-def test_hard_lease_supersede_dry_run_keeps_lease_and_state(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
- lease_file = task_lease_path(
- runtime_root=tmp_path / "runtime", goal_id=GOAL_ID, todo_id=todo["todo_id"]
- )
- before = state.read_bytes()
-
- result = supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- reason="preview only",
- task_lease_idempotency_key="turn-lease-1",
- task_lease_expected_version=1,
- dry_run=True,
- )
-
- assert result["dry_run"] is True
- assert result["task_lease_fence"]["execution_instance_verified"] is True
- assert result["task_lease_fence"].get("released") is not True
- assert lease_file.exists()
- assert state.read_bytes() == before
-
-
-def test_hard_lease_delegated_supersede_passes_gate_with_marker(tmp_path: Path) -> None:
- """The delegated door mirrors complete: it opens the claim gate, never an
- effective lease. Without a lease the fence is optional and the marker rides."""
-
- registry, state = _write_workspace(
- tmp_path,
- handoff_mode=HANDOFF_MODE_HARD_LEASE,
- lifecycle_authority=[
- {
- "agent_id": ORCHESTRATOR,
- "actions": ["supersede"],
- "requires_reason": True,
- }
- ],
- )
- todo = _add_todo(registry, claimed_by=AGENT_A)
-
- result = supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=ORCHESTRATOR,
- authority_reason="retire stale work",
- reason="delegated supersede",
- )
-
- assert result["ok"] is True
- assert result["handoff_gate_overridden"] is True
- assert result["task_lease_fence"]["required"] is False
- assert _agent_todo(state, todo["todo_id"])["done"] is True
-
-
-def test_legacy_pin_supersede_ignores_missing_lease_but_fences_effective_lease(
- tmp_path: Path,
-) -> None:
- """Legacy parity with complete since the completion fence landed: no lease
- means no fence; an effective foreign lease requires the key."""
-
- registry, state = _write_workspace(tmp_path)
- free_todo = _add_todo(registry, claimed_by=AGENT_A)
- free = supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=free_todo["todo_id"],
- agent_id=AGENT_A,
- reason="legacy supersede without any lease",
- )
- assert free["ok"] is True
- assert free["handoff_mode"] == HANDOFF_MODE_LEGACY
- assert free["task_lease_fence"]["required"] is False
-
- leased_todo = _add_todo(registry)
- _acquire(registry, tmp_path, leased_todo["todo_id"], owner=AGENT_A)
- with pytest.raises(TaskLeaseError) as error:
- supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=leased_todo["todo_id"],
- agent_id=AGENT_B,
- reason="legacy peer supersedes a leased todo",
- )
- assert error.value.code == "lease_fence_required"
- assert _agent_todo(state, leased_todo["todo_id"])["done"] is False
-
-
-# ---------------------------------------------------------------------------
-# force bootstrap rewrites the state file: the declared mode must travel with it
-# ---------------------------------------------------------------------------
-
-
-def _force_bootstrap(
- registry: Path, state: Path, tmp_path: Path, **overrides: Any
-) -> dict[str, Any]:
- from loopx.bootstrap import bootstrap_project
-
- options: dict[str, Any] = dict(
- project=state.parent,
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- objective="Rebuild the active state without changing the handoff contract.",
- domain="harness_self_improvement",
- role="owner",
- parent_goal_id=None,
- state_file=state,
- goal_doc=None,
- adapter_kind="harness_self_improvement",
- adapter_status="active",
- next_probe=None,
- spawn_allowed=False,
- max_children=0,
- allowed_domains=None,
- write_scope=None,
- preserve_todos=False,
- force=True,
- dry_run=False,
- sync_global=False,
- )
- options.update(overrides)
- return bootstrap_project(**options)
-
-
-def test_force_bootstrap_replace_preserves_declared_handoff_mode(
- tmp_path: Path,
-) -> None:
- """Appendix B rule 5: a mode change needs quiescence; a forced state rebuild
- is not a mode change and must not silently fall back to legacy."""
-
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _acquire(registry, tmp_path, todo["todo_id"], owner=AGENT_A)
-
- payload = _force_bootstrap(registry, state, tmp_path)
-
- assert payload["state_action"] == "replaced"
- assert payload["force_bootstrap_warning"]["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- rebuilt = state.read_text(encoding="utf-8")
- assert goal_handoff_mode(rebuilt) == HANDOFF_MODE_HARD_LEASE
- assert todo["todo_id"] not in rebuilt
-
-
-def test_force_bootstrap_replace_keeps_legacy_default_absent(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path)
-
- payload = _force_bootstrap(registry, state, tmp_path)
-
- assert payload["state_action"] == "replaced"
- assert payload["force_bootstrap_warning"]["handoff_mode"] == HANDOFF_MODE_LEGACY
- assert "handoff_mode:" not in state.read_text(encoding="utf-8")
- assert goal_handoff_mode(state.read_text(encoding="utf-8")) == HANDOFF_MODE_LEGACY
-
-
-def test_force_bootstrap_dry_run_reports_preserved_mode_without_writing(
- tmp_path: Path,
-) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_SOFT_CLAIM)
- before = state.read_bytes()
-
- payload = _force_bootstrap(registry, state, tmp_path, dry_run=True)
-
- assert payload["force_bootstrap_warning"]["handoff_mode"] == HANDOFF_MODE_SOFT_CLAIM
- assert state.read_bytes() == before
-
-
-def test_force_bootstrap_replace_refuses_invalid_declared_mode(tmp_path: Path) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode="banana")
- before = state.read_bytes()
-
- with pytest.raises(HandoffModeError) as error:
- _force_bootstrap(registry, state, tmp_path)
-
- assert error.value.code == "invalid_handoff_mode"
- assert state.read_bytes() == before
diff --git a/tests/control_plane/test_goal_handoff_mode_cli.py b/tests/control_plane/test_goal_handoff_mode_cli.py
index 28cf4ec2c3..897d42f445 100644
--- a/tests/control_plane/test_goal_handoff_mode_cli.py
+++ b/tests/control_plane/test_goal_handoff_mode_cli.py
@@ -1,12 +1,4 @@
-"""CLI surface for the per-goal handoff mode: show/set plus error-code mapping.
-
-``loopx handoff-mode show|set`` reads and writes the ``handoff_mode`` key in
-the goal's ACTIVE_GOAL_STATE.md YAML front-matter. Setting requires quiescence
-(no open claimed todo, no time-active lease) and refuses with a typed offender
-list otherwise; there is no --force in v0. Gate rejections raised by todo and
-task-lease verbs must surface their typed error codes through the existing
-CLI error_code mapping.
-"""
+"""CLI coverage for canonical handoff mode and pre-cutoff read compatibility."""
from __future__ import annotations
@@ -23,7 +15,8 @@
HANDOFF_MODE_SOFT_CLAIM,
)
from loopx.control_plane.work_items.task_lease import acquire_task_lease
-from loopx.todos import add_goal_todo, update_goal_todo
+from loopx.todos import add_goal_todo
+from tests.control_plane.canonical_authority_fixture import promoted_create_fixture
GOAL_ID = "handoff-mode-cli"
AGENT_A = "agent-a"
@@ -76,6 +69,20 @@ def _write_workspace(
return registry, state
+def _write_canonical_workspace(
+ tmp_path: Path,
+ *,
+ handoff_mode: str,
+) -> tuple[Path, Path]:
+ registry, _runtime, state = promoted_create_fixture(
+ tmp_path,
+ goal_id=GOAL_ID,
+ handoff_mode=handoff_mode,
+ registered_agents=[AGENT_A, AGENT_B],
+ )
+ return registry, state
+
+
def _run_cli(
capsys: pytest.CaptureFixture[str],
registry: Path,
@@ -99,18 +106,6 @@ def _add_todo(registry: Path, *, claimed_by: str | None = None) -> dict[str, Any
)
-def _set_frontmatter_mode(state: Path, mode: str) -> None:
- lines = state.read_text(encoding="utf-8").splitlines()
- close = next(i for i in range(1, len(lines)) if lines[i].strip() == "---")
- for index in range(1, close):
- if lines[index].split(":", 1)[0].strip() == "handoff_mode":
- lines[index] = f"handoff_mode: {mode}"
- break
- else:
- lines.insert(close, f"handoff_mode: {mode}")
- state.write_text("\n".join(lines) + "\n", encoding="utf-8")
-
-
# ---------------------------------------------------------------------------
# (e) mode switch CLI: show / set with quiescence enforcement.
# ---------------------------------------------------------------------------
@@ -137,7 +132,15 @@ def test_unpromoted_legacy_set_is_retired_without_mutation(tmp_path, capsys):
before = state.read_bytes()
code, payload = _run_cli(capsys, registry, "handoff-mode", "set", "--goal-id", GOAL_ID,
"--mode", "legacy")
- assert code == 1 and payload["error_code"] == "handoff_mode_retired", payload
+ assert code == 1 and payload["error_code"] == "canonical_authority_required", payload
+ assert payload["operation"] == "handoff_mode_set"
+ assert payload["write_admitted"] is False
+ assert payload["decision_read_from_provider"] is False
+ assert payload["legacy_fallback_used"] is False
+ assert payload["recovery"] == {
+ "action": "migrate_goal_authority",
+ "backup_required": True,
+ }
assert state.read_bytes() == before
@@ -159,41 +162,15 @@ def test_handoff_mode_show_rejects_invalid_frontmatter_without_mutation(
assert state.read_bytes() == before
-def test_handoff_mode_set_repairs_invalid_frontmatter_on_quiescent_goal(
+def test_handoff_mode_set_on_quiescent_goal_updates_provider(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
- registry, state = _write_workspace(tmp_path, handoff_mode="banana")
- before = state.read_bytes()
-
- exit_code, payload = _run_cli(
- capsys,
- registry,
- "handoff-mode",
- "set",
- "--goal-id",
- GOAL_ID,
- "--mode",
- HANDOFF_MODE_HARD_LEASE,
+ registry, state = _write_canonical_workspace(
+ tmp_path,
+ handoff_mode=HANDOFF_MODE_SOFT_CLAIM,
)
-
- assert exit_code == 0
- assert payload["ok"] is True
- assert payload["changed"] is True
- assert payload["previous_mode"] == "banana"
- assert payload["previous_mode_valid"] is False
- assert payload["previous_mode_error_code"] == "invalid_handoff_mode"
- assert payload["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert state.read_bytes() == before.replace(
- b"handoff_mode: banana", b"handoff_mode: hard_lease"
- )
-
-
-def test_handoff_mode_set_on_quiescent_goal_updates_frontmatter(
- tmp_path: Path,
- capsys: pytest.CaptureFixture[str],
-) -> None:
- registry, state = _write_workspace(tmp_path)
+ before = state.read_bytes()
exit_code, payload = _run_cli(
capsys,
@@ -209,56 +186,28 @@ def test_handoff_mode_set_on_quiescent_goal_updates_frontmatter(
assert exit_code == 0
assert payload["ok"] is True
assert payload["changed"] is True
- assert payload["previous_mode"] == HANDOFF_MODE_LEGACY
+ assert payload["previous_mode"] == HANDOFF_MODE_SOFT_CLAIM
assert payload["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- text = state.read_text(encoding="utf-8")
- assert "handoff_mode: hard_lease" in text.split("---")[1]
- assert f"goal_id: {GOAL_ID}" in text
+ assert payload["decision_read_from_provider"] is True
+ assert payload["legacy_fallback_used"] is False
+ assert state.read_bytes() == before
show_code, show_payload = _run_cli(
capsys, registry, "handoff-mode", "show", "--goal-id", GOAL_ID
)
assert show_code == 0
assert show_payload["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert show_payload["source"] == "frontmatter"
-
-
-def test_handoff_mode_set_invalid_previous_mode_still_refuses_claimed_todo(
- tmp_path: Path,
- capsys: pytest.CaptureFixture[str],
-) -> None:
- registry, state = _write_workspace(tmp_path)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- _set_frontmatter_mode(state, "banana")
- before = state.read_bytes()
-
- exit_code, payload = _run_cli(
- capsys,
- registry,
- "handoff-mode",
- "set",
- "--goal-id",
- GOAL_ID,
- "--mode",
- HANDOFF_MODE_SOFT_CLAIM,
- )
-
- assert exit_code == 1
- assert payload["error_code"] == "handoff_mode_not_quiescent"
- assert payload["previous_mode"] == "banana"
- assert payload["previous_mode_valid"] is False
- assert payload["previous_mode_error_code"] == "invalid_handoff_mode"
- assert [item["todo_id"] for item in payload["claimed_todos"]] == [
- todo["todo_id"]
- ]
- assert state.read_bytes() == before
+ assert show_payload["source"] == "canonical_provider"
def test_handoff_mode_set_refused_when_open_claimed_todo_exists(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
- registry, state = _write_workspace(tmp_path)
+ registry, state = _write_canonical_workspace(
+ tmp_path,
+ handoff_mode=HANDOFF_MODE_HARD_LEASE,
+ )
todo = _add_todo(registry, claimed_by=AGENT_A)
before = state.read_text(encoding="utf-8")
@@ -282,51 +231,14 @@ def test_handoff_mode_set_refused_when_open_claimed_todo_exists(
assert state.read_text(encoding="utf-8") == before
-def test_handoff_mode_set_invalid_previous_mode_still_refuses_active_lease(
- tmp_path: Path,
- capsys: pytest.CaptureFixture[str],
-) -> None:
- registry, state = _write_workspace(tmp_path)
- todo = _add_todo(registry)
- acquire_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AGENT_A,
- idempotency_key="turn-invalid-mode-lease",
- ttl_seconds=600,
- )
- _set_frontmatter_mode(state, "banana")
- before = state.read_bytes()
-
- exit_code, payload = _run_cli(
- capsys,
- registry,
- "handoff-mode",
- "set",
- "--goal-id",
- GOAL_ID,
- "--mode",
- HANDOFF_MODE_HARD_LEASE,
- )
-
- assert exit_code == 1
- assert payload["error_code"] == "handoff_mode_not_quiescent"
- assert payload["previous_mode"] == "banana"
- assert payload["previous_mode_valid"] is False
- assert payload["previous_mode_error_code"] == "invalid_handoff_mode"
- assert [item["todo_id"] for item in payload["active_leases"]] == [
- todo["todo_id"]
- ]
- assert state.read_bytes() == before
-
-
def test_handoff_mode_set_refused_when_time_active_lease_exists(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
- registry, _state = _write_workspace(tmp_path)
+ registry, _state = _write_canonical_workspace(
+ tmp_path,
+ handoff_mode=HANDOFF_MODE_HARD_LEASE,
+ )
todo = _add_todo(registry)
acquire_task_lease(
registry_path=registry,
@@ -346,7 +258,7 @@ def test_handoff_mode_set_refused_when_time_active_lease_exists(
"--goal-id",
GOAL_ID,
"--mode",
- HANDOFF_MODE_HARD_LEASE,
+ HANDOFF_MODE_SOFT_CLAIM,
)
assert exit_code == 1
@@ -356,41 +268,14 @@ def test_handoff_mode_set_refused_when_time_active_lease_exists(
assert leases[0]["owner"] == AGENT_A
-def test_handoff_mode_set_allowed_after_goal_becomes_quiescent(
- tmp_path: Path,
- capsys: pytest.CaptureFixture[str],
-) -> None:
- registry, _state = _write_workspace(tmp_path)
- todo = _add_todo(registry, claimed_by=AGENT_A)
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AGENT_A,
- clear_claim=True,
- )
-
- exit_code, payload = _run_cli(
- capsys,
- registry,
- "handoff-mode",
- "set",
- "--goal-id",
- GOAL_ID,
- "--mode",
- HANDOFF_MODE_SOFT_CLAIM,
- )
-
- assert exit_code == 0
- assert payload["ok"] is True
- assert payload["handoff_mode"] == HANDOFF_MODE_SOFT_CLAIM
-
-
def test_handoff_mode_set_same_value_is_idempotent(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_SOFT_CLAIM)
+ registry, _state = _write_canonical_workspace(
+ tmp_path,
+ handoff_mode=HANDOFF_MODE_SOFT_CLAIM,
+ )
exit_code, payload = _run_cli(
capsys,
@@ -417,7 +302,10 @@ def test_cli_todo_claim_in_hard_mode_maps_typed_error_code(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
+ registry, _state = _write_canonical_workspace(
+ tmp_path,
+ handoff_mode=HANDOFF_MODE_HARD_LEASE,
+ )
todo = _add_todo(registry)
exit_code, payload = _run_cli(
@@ -445,7 +333,10 @@ def test_cli_task_lease_acquire_in_soft_mode_maps_typed_error_code(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_SOFT_CLAIM)
+ registry, _state = _write_canonical_workspace(
+ tmp_path,
+ handoff_mode=HANDOFF_MODE_SOFT_CLAIM,
+ )
todo = _add_todo(registry)
exit_code, payload = _run_cli(
@@ -478,7 +369,10 @@ def test_todo_supersede_cli_requires_key_in_hard_lease_and_accepts_it(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
- registry, _state = _write_workspace(tmp_path, handoff_mode=HANDOFF_MODE_HARD_LEASE)
+ registry, _state = _write_canonical_workspace(
+ tmp_path,
+ handoff_mode=HANDOFF_MODE_HARD_LEASE,
+ )
todo = _add_todo(registry, claimed_by=AGENT_A)
acquire_task_lease(
registry_path=registry,
@@ -528,6 +422,15 @@ def test_todo_supersede_cli_requires_key_in_hard_lease_and_accepts_it(
)
assert exit_code == 0
assert payload["ok"] is True
- assert payload["handoff_mode"] == HANDOFF_MODE_HARD_LEASE
- assert payload["task_lease_fence"]["execution_instance_verified"] is True
- assert payload["task_lease_fence"]["released"] is True
+ inspect_code, inspect_payload = _run_cli(
+ capsys,
+ registry,
+ "task-lease",
+ "inspect",
+ "--goal-id",
+ GOAL_ID,
+ "--todo-id",
+ todo["todo_id"],
+ )
+ assert inspect_code == 0
+ assert inspect_payload["lease"]["status"] == "released"
diff --git a/tests/control_plane/test_goal_terminal_no_followup.py b/tests/control_plane/test_goal_terminal_no_followup.py
index d2d77aff58..66b0988b18 100644
--- a/tests/control_plane/test_goal_terminal_no_followup.py
+++ b/tests/control_plane/test_goal_terminal_no_followup.py
@@ -1,10 +1,15 @@
from __future__ import annotations
+import pytest
+
from loopx.bootstrap import (
bootstrap_project,
render_state_markdown,
repair_missing_todo_source_sections,
)
+from loopx.control_plane.coordination.local_authority import (
+ CanonicalAuthorityRequired,
+)
from loopx.control_plane.goals.goal_frontier import (
build_goal_frontier_projection_from_summaries,
goal_frontier_is_terminal_no_followup,
@@ -97,11 +102,10 @@ def test_existing_state_migration_preserves_recognized_legacy_source_heading() -
assert "## Agent Todo" in migrated
-def test_bootstrap_repairs_existing_state_todo_sources_in_place(tmp_path) -> None:
+def test_bootstrap_rejects_existing_markdown_only_state_without_rewriting(tmp_path) -> None:
state_file = tmp_path / ".codex/goals/goal-terminal-test/ACTIVE_GOAL_STATE.md"
state_file.parent.mkdir(parents=True)
- state_file.write_text(
- """\
+ original = """\
# Active Goal State
## Objective
@@ -111,70 +115,41 @@ def test_bootstrap_repairs_existing_state_todo_sources_in_place(tmp_path) -> Non
## Next Action
- Continue.
-""",
- encoding="utf-8",
- )
-
- result = bootstrap_project(
- project=tmp_path,
- registry_path=tmp_path / ".loopx/registry.json",
- runtime_root=tmp_path / ".loopx-runtime",
- goal_id="goal-terminal-test",
- objective="Do not replace the existing objective.",
- domain="issue-fix",
- role="primary",
- parent_goal_id=None,
- state_file=state_file,
- goal_doc=None,
- adapter_kind="issue_fix_workflow_v0",
- adapter_status="connected",
- next_probe=None,
- spawn_allowed=False,
- max_children=0,
- allowed_domains=[],
- write_scope=[],
- force=False,
- dry_run=False,
- sync_global=False,
- )
-
- repaired = state_file.read_text(encoding="utf-8")
- assert result["state_action"] == "kept-existing"
- assert result["todo_source_migration"] == {
- "schema_version": "todo_source_section_migration_v0",
- "added_roles": ["user", "agent"],
- "applied": True,
- }
- assert "Preserve this existing state." in repaired
- assert "Do not replace the existing objective." not in repaired
- assert "## User Todo / Owner Review Reading Queue" in repaired
- assert "## Agent Todo" in repaired
-
- second_result = bootstrap_project(
- project=tmp_path,
- registry_path=tmp_path / ".loopx/registry.json",
- runtime_root=tmp_path / ".loopx-runtime",
- goal_id="goal-terminal-test",
- objective="Still do not replace the existing objective.",
- domain="issue-fix",
- role="primary",
- parent_goal_id=None,
- state_file=state_file,
- goal_doc=None,
- adapter_kind="issue_fix_workflow_v0",
- adapter_status="connected",
- next_probe=None,
- spawn_allowed=False,
- max_children=0,
- allowed_domains=[],
- write_scope=[],
- force=False,
- dry_run=False,
- sync_global=False,
- )
-
- assert second_result["todo_source_migration"] is None
- assert state_file.read_text(encoding="utf-8") == repaired
+"""
+ state_file.write_text(original, encoding="utf-8")
+ registry_path = tmp_path / ".loopx/registry.json"
+ runtime_root = tmp_path / ".loopx-runtime"
+
+ with pytest.raises(CanonicalAuthorityRequired) as caught:
+ bootstrap_project(
+ project=tmp_path,
+ registry_path=registry_path,
+ runtime_root=runtime_root,
+ goal_id="goal-terminal-test",
+ objective="Do not replace the existing objective.",
+ domain="issue-fix",
+ role="primary",
+ parent_goal_id=None,
+ state_file=state_file,
+ goal_doc=None,
+ adapter_kind="issue_fix_workflow_v0",
+ adapter_status="connected",
+ next_probe=None,
+ spawn_allowed=False,
+ max_children=0,
+ allowed_domains=[],
+ write_scope=[],
+ force=False,
+ dry_run=False,
+ sync_global=False,
+ )
+
+ assert caught.value.code == "canonical_authority_required"
+ assert caught.value.payload["operation"] == "goal_create"
+ assert caught.value.payload["legacy_fallback_used"] is False
+ assert state_file.read_text(encoding="utf-8") == original
+ assert not registry_path.exists()
+ assert not runtime_root.exists()
def test_agent_only_no_followup_closes_explicit_empty_user_source() -> None:
diff --git a/tests/control_plane/test_handoff_mode_source.py b/tests/control_plane/test_handoff_mode_source.py
deleted file mode 100644
index e925ca563c..0000000000
--- a/tests/control_plane/test_handoff_mode_source.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""Real compatibility sources: complete overlays and their mutation locks."""
-from __future__ import annotations
-
-import json
-from pathlib import Path
-
-import pytest
-
-from loopx.control_plane.todos.handoff_mode import HandoffModeError, set_goal_handoff_mode
-from loopx.control_plane.goals.legacy_event_source import RetiredTodoEventSourceError
-
-
-def workspace(root: Path) -> tuple[Path, Path, Path]:
- state = root / "ACTIVE_GOAL_STATE.md"
- state.write_text("---\ngoal_id: mode-source\nhandoff_mode: legacy\n---\n\n## Agent Todo\n", encoding="utf-8")
- registry = root / "registry.json"
- runtime = root / "runtime"
- registry.write_text(json.dumps({"common_runtime_root": str(runtime), "goals": [{"id": "mode-source",
- "repo": str(root), "state_file": state.name, "adapter": {"kind": "harness_self_improvement"}}]}))
- return registry, state, runtime
-
-
-
-
-def switch(registry: Path, **kwargs):
- return set_goal_handoff_mode(registry_path=registry, goal_id="mode-source", mode="hard_lease", **kwargs)
-
-
-
-
-
-
-@pytest.mark.parametrize("contents", ["not json\n", '{"schema_version":"unknown"}\n'])
-def test_unreadable_event_source_is_not_quiescence(tmp_path: Path, contents: str) -> None:
- registry, state, _ = workspace(tmp_path)
- path = state.with_name("events.jsonl")
- path.write_text(contents)
- before = state.read_bytes()
- with pytest.raises(RetiredTodoEventSourceError):
- switch(registry)
- assert state.read_bytes() == before
-
-
-
-
-def test_retired_source_refuses_even_a_mode_noop(tmp_path: Path) -> None:
- registry, state, _ = workspace(tmp_path)
- state.write_text(state.read_text().replace("legacy", "hard_lease"))
- state.with_name("events.jsonl").write_text("corrupt unrelated event\n")
- before = state.read_bytes()
- with pytest.raises(RetiredTodoEventSourceError):
- switch(registry)
- assert state.read_bytes() == before
-
-
-@pytest.mark.parametrize("ending", [b"", b"\r\n"])
-def test_public_mode_write_preserves_unrelated_bytes(tmp_path: Path, ending: bytes) -> None:
- registry, state, _ = workspace(tmp_path)
- source = '---\r\ngoal_id: mode-source\r\ntitle: "a\u2028b"\r\nhandoff_mode: legacy\r\n---\r\n\r\n## Agent Todo'.encode() + ending
- state.write_bytes(source)
- assert switch(registry)["changed"] is True
- assert state.read_bytes() == source.replace(b"handoff_mode: legacy", b"handoff_mode: hard_lease")
-
-
-def test_duplicate_mode_fields_reject_without_partial_repair(tmp_path: Path) -> None:
- registry, state, _ = workspace(tmp_path)
- state.write_text(state.read_text().replace("handoff_mode: legacy", "handoff_mode: legacy\nhandoff_mode: banana"))
- before = state.read_bytes()
- with pytest.raises(HandoffModeError) as caught:
- switch(registry)
- assert caught.value.code == "handoff_mode_duplicate_field"
- assert state.read_bytes() == before
-
-
-def test_large_prose_never_crosses_the_mode_plan_transport(tmp_path: Path) -> None:
- registry, state, _ = workspace(tmp_path)
- original = state.read_bytes() + b"\n## Evidence\n" + b"Unrelated durable prose.\n" * 150_000
- state.write_bytes(original)
- assert switch(registry)["changed"] is True
- assert state.read_bytes() == original.replace(b"handoff_mode: legacy", b"handoff_mode: hard_lease")
diff --git a/tests/control_plane/test_imported_completion_repair.py b/tests/control_plane/test_imported_completion_repair.py
new file mode 100644
index 0000000000..95abd9ab93
--- /dev/null
+++ b/tests/control_plane/test_imported_completion_repair.py
@@ -0,0 +1,171 @@
+from __future__ import annotations
+
+import json
+from pathlib import Path
+from typing import Any
+
+import pytest
+from canonical_authority_fixture import initialize_canonical_authority
+
+from loopx.control_plane.coordination.local_authority import (
+ read_canonical_todos_if_promoted,
+)
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
+from loopx.control_plane.todos import provider_terminal_lifecycle
+from loopx.todos import complete_goal_todo
+
+
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+@pytest.mark.parametrize(
+ "validation_digest",
+ [None, "a" * 64],
+ ids=["plain", "validated"],
+)
+def test_public_completion_repairs_imported_metadata_once(
+ tmp_path: Path,
+ monkeypatch: pytest.MonkeyPatch,
+ provider: str,
+ validation_digest: str | None,
+) -> None:
+ runtime_root = tmp_path / "runtime"
+ project = tmp_path / "project"
+ project.mkdir()
+ state_file = project / "ACTIVE_GOAL_STATE.md"
+ state_file.write_text(
+ "# Goal\n\n## User Todo\n\n## Agent Todo\n\n## Completed Work Archive\n",
+ encoding="utf-8",
+ )
+ registry = tmp_path / "registry.json"
+ registry.write_text(
+ json.dumps(
+ {
+ "schema_version": 1,
+ "common_runtime_root": str(runtime_root),
+ "goals": [
+ {
+ "id": "goal-a",
+ "status": "active",
+ "repo": str(project),
+ "state_file": state_file.name,
+ "coordination": {"registered_agents": ["agent-a"]},
+ }
+ ],
+ }
+ ),
+ encoding="utf-8",
+ )
+ todo = {
+ "schema_version": "todo_item_v0",
+ "todo_id": "todo-imported",
+ "role": "agent",
+ "source_section": "Agent Todo",
+ "status": "done",
+ "done": True,
+ "archive_state": "active",
+ "text": "Preserve the imported completion",
+ "task_class": "advancement_task",
+ "claimed_by": "agent-a",
+ "note": "Original note",
+ "evidence": "Original evidence",
+ "reason": "Original reason",
+ "completion_turn_key": "original-turn",
+ "completion_receipt_id": "tcw_original",
+ "completed_at": "2026-09-01T00:00:00Z",
+ "updated_at": "2026-09-01T01:00:00Z",
+ "last_actor_agent_id": "agent-before",
+ **(
+ {
+ "completion_validation_required": True,
+ "completion_validation_sha256": validation_digest,
+ }
+ if validation_digest is not None
+ else {}
+ ),
+ }
+ initialize_canonical_authority(
+ runtime_root,
+ "goal-a",
+ build_todo_runtime_shadow_projection(
+ goal_id="goal-a",
+ todos=[todo],
+ handoff_mode="soft_claim",
+ ),
+ state_path=state_file,
+ provider=provider,
+ )
+
+ terminal_requests: list[dict[str, Any]] = []
+ execute = provider_terminal_lifecycle.effect_runtime_result
+
+ def capture_request(
+ method: str,
+ params: dict[str, Any],
+ **kwargs: Any,
+ ) -> Any:
+ if method == "coordination.local_authority.todo_terminal":
+ terminal_requests.append(dict(params))
+ return execute(method, params, **kwargs)
+
+ monkeypatch.setattr(
+ provider_terminal_lifecycle,
+ "effect_runtime_result",
+ capture_request,
+ )
+ request = {
+ "registry_path": registry,
+ "goal_id": "goal-a",
+ "todo_id": "todo-imported",
+ "agent_id": "agent-a",
+ "completion_turn_key": "original-turn",
+ "completion_identity_source": "turn_settlement",
+ }
+ before = read_canonical_todos_if_promoted(
+ runtime_root=runtime_root,
+ goal_id="goal-a",
+ )
+ assert before is not None
+
+ preview = complete_goal_todo(**request, dry_run=True)
+ assert preview["provider_status"] == "planned"
+ assert preview["changed"] is True
+ assert read_canonical_todos_if_promoted(
+ runtime_root=runtime_root,
+ goal_id="goal-a",
+ ) == before
+
+ repaired = complete_goal_todo(**request)
+ assert repaired["provider_status"] == "applied"
+ assert repaired["completion_continuation"] == "active_goal"
+ after = read_canonical_todos_if_promoted(
+ runtime_root=runtime_root,
+ goal_id="goal-a",
+ )
+ assert after is not None
+ repaired_todo = after["todos"][0]
+ original_todo = before["todos"][0]
+ assert repaired_todo == {
+ **original_todo,
+ "completion_continuation": "active_goal",
+ }
+
+ producer_request = terminal_requests[0]
+ assert producer_request["allow_user_gate_auto_acquire"] is True
+ assert producer_request["completion_policy_request"] is not None
+ assert producer_request["validation_declaration_sha256"] == validation_digest
+
+ replay = complete_goal_todo(**request)
+ assert replay["provider_status"] == "replayed"
+ assert read_canonical_todos_if_promoted(
+ runtime_root=runtime_root,
+ goal_id="goal-a",
+ ) == after
+
+ if validation_digest is not None:
+ return
+
+ closed = complete_goal_todo(**request, no_followup=True)
+ assert closed["provider_status"] == "applied"
+ assert closed["completion_continuation"] == "no_followup"
+ assert closed["completion_recovery"] == "same_turn_terminal_closeout"
diff --git a/tests/control_plane/test_in_flight_turn_replay_cli.py b/tests/control_plane/test_in_flight_turn_replay_cli.py
index ae7699d5ff..eb5420c4ae 100644
--- a/tests/control_plane/test_in_flight_turn_replay_cli.py
+++ b/tests/control_plane/test_in_flight_turn_replay_cli.py
@@ -27,7 +27,7 @@
)
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("waiting", [False, True])
def test_in_flight_turn_replay_preserves_open_todo_and_fresh_turn_admission(
tmp_path: Path,
@@ -51,14 +51,13 @@ def todo() -> dict[str, Any]:
listed = cli("todo", "list", "--goal-id", GOAL_ID)
return next(item for item in listed["todos"] if item["todo_id"] == TODO_ID)
- if provider != "legacy":
- listed = cli("todo", "list", "--goal-id", GOAL_ID)
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, todos=listed["todos"], handoff_mode="soft_claim",
- )
- initialize_canonical_authority(
- runtime, GOAL_ID, projection, state_path=state, provider=provider,
- )
+ listed = cli("todo", "list", "--goal-id", GOAL_ID)
+ projection = build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID, todos=listed["todos"], handoff_mode="soft_claim",
+ )
+ initialize_canonical_authority(
+ runtime, GOAL_ID, projection, state_path=state, provider=provider,
+ )
before = todo()
turn_id = f"inflight-{provider}-{waiting}"
binding = ("--agent-id", AGENT_ID, "--todo-id", TODO_ID, "--turn-instance-id", turn_id)
diff --git a/tests/control_plane/test_issue_fix_private_evidence_settlement.py b/tests/control_plane/test_issue_fix_private_evidence_settlement.py
index 4907f8703f..7bfe85fa81 100644
--- a/tests/control_plane/test_issue_fix_private_evidence_settlement.py
+++ b/tests/control_plane/test_issue_fix_private_evidence_settlement.py
@@ -57,18 +57,6 @@ def run(*command: str) -> dict:
assert code == 0, json.dumps(result, indent=2)
return result
- run(
- "todo",
- "claim",
- "--goal-id",
- GOAL_ID,
- "--todo-id",
- TODO_ID,
- "--agent-id",
- AGENT_ID,
- "--claimed-by",
- AGENT_ID,
- )
todos = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]
initialize_canonical_authority(
runtime,
@@ -79,29 +67,45 @@ def run(*command: str) -> dict:
state_path=state,
provider="sqlite",
)
- binding = (
+ run(
+ "task-lease",
+ "acquire",
"--goal-id",
GOAL_ID,
"--todo-id",
TODO_ID,
- "--agent-id",
+ "--owner",
AGENT_ID,
- "--turn-instance-id",
- TURN_ID,
+ "--idempotency-key",
+ "private-evidence-fixture",
)
- run("quota", "should-run", "--codex-app", *binding, "--scan-path", str(project))
run(
- "task-lease",
- "acquire",
+ "todo",
+ "claim",
"--goal-id",
GOAL_ID,
"--todo-id",
TODO_ID,
- "--owner",
+ "--agent-id",
AGENT_ID,
- "--idempotency-key",
+ "--claimed-by",
+ AGENT_ID,
+ "--task-lease-idempotency-key",
"private-evidence-fixture",
+ "--task-lease-expected-version",
+ "1",
)
+ binding = (
+ "--goal-id",
+ GOAL_ID,
+ "--todo-id",
+ TODO_ID,
+ "--agent-id",
+ AGENT_ID,
+ "--turn-instance-id",
+ TURN_ID,
+ )
+ run("quota", "should-run", "--codex-app", *binding, "--scan-path", str(project))
refresh = (
"refresh-state",
*binding,
diff --git a/tests/control_plane/test_large_source_journey.py b/tests/control_plane/test_large_source_journey.py
deleted file mode 100644
index 3d9e839301..0000000000
--- a/tests/control_plane/test_large_source_journey.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""Full source transfer through public qualification and reviewed cutover.
-
-Large notes are semantic source fields, not discarded prose or padding outside
-Todos. Every CLI call runs in a fresh process against disposable real storage.
-"""
-import json
-
-import pytest
-
-from test_runtime_shadow_bounded_e2e import cli, enable, workspace
-from loopx.control_plane.coordination.runtime_shadow import build_runtime_shadow_source_snapshot
-from loopx.control_plane.effect_runtime import MAX_REQUEST_BYTES
-from loopx.control_plane.todos.contract import format_todo_metadata_line
-
-pytestmark = pytest.mark.stage2c_e2e
-
-
-def test_large_complete_source_can_qualify_promote_and_read_back(tmp_path):
- registry, runtime, state = workspace(tmp_path)
- note = "完整🙂" * 1600
- blocks = [f"- [ ] Retained work {index}\n " + format_todo_metadata_line(
- todo_id=f"todo_{index:03}", role="agent", status="open",
- task_class="advancement_task", note=note,
- ) for index in range(160)]
- state.write_text(state.read_text() + "\n".join(blocks) + "\n", encoding="utf-8")
- goal = enable(registry)
- projection, _ = build_runtime_shadow_source_snapshot(
- goal=goal, runtime_root=runtime, state_path=state, registry_path=registry)
- assert len(json.dumps(projection, ensure_ascii=False).encode()) > MAX_REQUEST_BYTES
- assert len(projection["todos"]) == 160
- assert all(todo["note"] == note for todo in projection["todos"])
-
- boot = cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- assert boot["bootstrap"]["status"] == "applied"
- added = cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent", "--text", "Captured after full bootstrap")
- assert added["coordination_runtime_shadow"]["source_transaction_correlated"] is True
- inspected = cli(registry, runtime, "coordination-shadow", "inspect", "--goal-id", "goal-a")
- assert inspected["inspection"]["status"] == "matched"
- qualified = cli(registry, runtime, "coordination-shadow", "qualify", "--goal-id", "goal-a",
- "--minimum-operations", "1", "--require-event-kind", "todo_add")
- assert qualified["qualification"]["qualified"] is True
- assert qualified["qualification"]["sustained_parity_verified"] is False
- common = ("coordination-shadow", "promote", "--goal-id", "goal-a",
- "--minimum-operations", "1", "--require-event-kind", "todo_add")
- preview = cli(registry, runtime, *common)
- assert preview["promotion"]["status"] == "preview_ready"
- assert not (runtime / "authority" / "file-v0").exists()
- applied = cli(registry, runtime, *common, "--execute")
- assert applied["promotion"]["status"] == "applied"
- assert applied["promotion"]["legacy_writer_fenced"] is True
- read = cli(registry, runtime, "todo", "list", "--goal-id", "goal-a", "--role", "agent")
- assert len(read["todos"]) == 161
- retained = [row for row in read["todos"] if row["todo_id"] != added["todo_id"]]
- assert {row["todo_id"] for row in retained} == {f"todo_{i:03}" for i in range(160)}
- assert all(row["note"] == note for row in retained)
diff --git a/tests/control_plane/test_leased_monitor_poll.py b/tests/control_plane/test_leased_monitor_poll.py
index ad9ce3bcf1..8214e2080d 100644
--- a/tests/control_plane/test_leased_monitor_poll.py
+++ b/tests/control_plane/test_leased_monitor_poll.py
@@ -9,8 +9,11 @@
from canonical_authority_fixture import isolate_sqlite_runtime
from test_native_monitor_poll import _canonical
-from test_monitor_followthrough_contract import GOAL_ID, AGENT_ID, _write_fixture, _add_monitor
-from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+from test_monitor_followthrough_contract import GOAL_ID, AGENT_ID, _write_fixture
+from loopx.control_plane.coordination.local_authority import (
+ CanonicalAuthorityRequired,
+ read_canonical_todos_if_promoted,
+)
from loopx.control_plane.quota.error_codes import QuotaCommandValidationError
from loopx.control_plane.quota.monitor_poll_lease_transport import current_monitor_lease_proof
from loopx.control_plane.scheduler.monitor_poll_writeback import write_monitor_poll_todo_state
@@ -216,13 +219,20 @@ def test_old_observation_time_cannot_revive_expired_proof(tmp_path, monkeypatch,
assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL_ID) == before
-def test_explicit_proof_never_falls_back_to_legacy_writer(tmp_path):
- registry, runtime, state = _write_fixture(tmp_path)
- monitor = _add_monitor(registry, text="Observe public progress", target_key="watch", next_due_at="2000-01-01T00:00:00Z")
+def test_explicit_proof_requires_canonical_authority(tmp_path):
+ registry, runtime, state = _write_fixture(tmp_path, canonical=False)
+ state.write_text(
+ state.read_text(encoding="utf-8")
+ + "\n- [ ] [P1] Observe public progress.\n"
+ + " \n",
+ encoding="utf-8",
+ )
before = state.read_bytes()
- with pytest.raises(ValueError, match="requires promoted canonical authority"):
+ with pytest.raises(CanonicalAuthorityRequired):
write_monitor_poll_todo_state(registry_path=registry, runtime_root=runtime, goal_id=GOAL_ID,
- execute=True, todo_id=monitor["todo_id"], agent_id=AGENT_ID,
+ execute=True, todo_id="todo_unpromoted_monitor", agent_id=AGENT_ID,
generated_at="2026-09-01T00:00:00Z", result_hash="a", material_change=False,
task_lease_idempotency_key=PROOF["idempotency_key"], task_lease_expected_version=3)
assert state.read_bytes() == before
diff --git a/tests/control_plane/test_legacy_completion_actor.py b/tests/control_plane/test_legacy_completion_actor.py
deleted file mode 100644
index 8ed2185254..0000000000
--- a/tests/control_plane/test_legacy_completion_actor.py
+++ /dev/null
@@ -1,254 +0,0 @@
-"""The ordinary legacy writer must qualify its own terminal reentry source."""
-from __future__ import annotations
-
-import contextlib
-import io
-import json
-import os
-import subprocess
-import sys
-import tempfile
-from datetime import datetime, timedelta, timezone
-
-import pytest
-
-from loopx.cli import main
-from loopx.control_plane.effect_runtime import EffectRuntimeRejected, effect_runtime_result
-from tests.control_plane.test_todo_mutation_authority import (
- AUTHOR_AGENT, GOAL_ID, REVIEW_AGENT, _write_fixture,
-)
-
-
-@pytest.mark.parametrize("edit,expected_actor", [
- (["--evidence", "evidence:planning-only"], AUTHOR_AGENT),
- (["--priority", "P2"], AUTHOR_AGENT),
- (["--clear-priority"], AUTHOR_AGENT),
- (["--note", "Peer context.", "--evidence", "evidence:mixed"], REVIEW_AGENT),
- (["--text", "Clarify source.", "--evidence", "evidence:mixed"], REVIEW_AGENT),
- (["--claimed-by", REVIEW_AGENT], REVIEW_AGENT),
-])
-def test_legacy_cli_update_preserves_planning_actor(tmp_path, edit, expected_actor):
- registry, state = _write_fixture(tmp_path, lifecycle_authority=[{
- "agent_id": REVIEW_AGENT, "actions": ["update", "reassign"], "requires_reason": True,
- }])
- prefix = ["--registry", str(registry), "--runtime-root", str(tmp_path / "runtime"),
- "--format", "json"]
-
- def call(*args, code=0):
- output = io.StringIO()
- with contextlib.redirect_stdout(output):
- actual = main([*prefix, *args])
- payload = json.loads(output.getvalue())
- assert actual == code, {key: payload.get(key) for key in ("error", "error_code")}
- return payload
-
- try:
- todo = call("todo", "add", "--goal-id", GOAL_ID, "--role", "agent",
- "--text", "Verify source.", "--priority", "P1",
- "--claimed-by", AUTHOR_AGENT, "--task-class", "advancement_task")["todo_id"]
- common = ["todo", "update", "--goal-id", GOAL_ID, "--todo-id", todo]
- call(*common, "--agent-id", AUTHOR_AGENT, "--note", "Owner context.",
- "--evidence", "evidence:original")
- detail = ["todo", "list", "--goal-id", GOAL_ID, "--todo-id", todo]
- assert call(*detail)["todo"]["last_actor_agent_id"] == AUTHOR_AGENT
- delegated = [*common, "--agent-id", REVIEW_AGENT, *edit]
- before = state.read_bytes()
- call(*delegated, code=1) # A grant still requires its actual reason.
- assert state.read_bytes() == before
- delegated += ["--authority-reason", "Maintain the source."]
- call(*delegated, "--dry-run")
- assert state.read_bytes() == before
- changed = call(*delegated)
- assert changed["changed"]
- row = call(*detail)["todo"]
- assert row["last_actor_agent_id"] == expected_actor
- if "--claimed-by" not in edit:
- assert row["claimed_by"] == AUTHOR_AGENT
- after = state.read_bytes()
- assert not call(*delegated)["changed"]
- assert state.read_bytes() == after
- finally:
- subprocess.run([sys.executable, "-c", "from loopx.control_plane.effect_runtime import restart_effect_runtime; restart_effect_runtime()"],
- env=dict(os.environ), check=True, capture_output=True, timeout=30)
-
-
-def test_legacy_cli_actor_survives_planning_clear_and_terminal_replay(tmp_path, monkeypatch):
- from tests.control_plane.test_todo_mutation_authority import _add_agent_todo
-
- for variable in ("TMPDIR", "TEMP", "TMP"):
- monkeypatch.setenv(variable, str(tmp_path))
- monkeypatch.setattr(tempfile, "tempdir", str(tmp_path))
- registry, state = _write_fixture(tmp_path, lifecycle_authority=[{
- "agent_id": REVIEW_AGENT, "actions": ["update"], "requires_reason": True,
- }])
- todo = _add_agent_todo(registry, claimed_by=None)["todo_id"]
- prefix = ["--registry", str(registry), "--runtime-root", str(tmp_path / "runtime"), "--format", "json"]
-
- def call(*args, code=0):
- output = io.StringIO()
- with contextlib.redirect_stdout(output):
- rc = main([*prefix, *args])
- payload = json.loads(output.getvalue())
- assert rc == code, {key: payload.get(key) for key in ("error", "error_code", "reason", "mutation_authority")}
- return payload
-
- def detail():
- return call("todo", "list", "--goal-id", GOAL_ID, "--todo-id", todo)["todo"]
-
- update = ["todo", "update", "--goal-id", GOAL_ID, "--todo-id", todo]
- call("todo", "claim", "--goal-id", GOAL_ID, "--todo-id", todo,
- "--agent-id", AUTHOR_AGENT, "--claimed-by", AUTHOR_AGENT)
- assert detail()["last_actor_agent_id"] == AUTHOR_AGENT
- call(*update, "--agent-id", AUTHOR_AGENT, "--note", "Owner context", "--evidence", "evidence:owner-validation", "--priority", "P2")
- assert detail()["last_actor_agent_id"] == AUTHOR_AGENT
- peer = [*update, "--agent-id", REVIEW_AGENT, "--authority-reason", "Maintain validated planning evidence"]
- planning_edit = ["--evidence", "evidence:peer-validation"]
- before = state.read_bytes()
- call(*update, "--agent-id", REVIEW_AGENT, "--evidence", "evidence:unadmitted", code=1)
- assert state.read_bytes() == before
- call(*peer, *planning_edit, "--dry-run")
- assert state.read_bytes() == before
- call(*peer, *planning_edit)
- assert detail()["last_actor_agent_id"] == AUTHOR_AGENT
- assert detail()["claimed_by"] == AUTHOR_AGENT
- before = state.read_bytes()
- call(*peer, *planning_edit)
- assert state.read_bytes() == before
-
- call(*peer, "--note", "Peer copy correction", "--evidence", "evidence:mixed-validation")
- assert detail()["last_actor_agent_id"] == REVIEW_AGENT
- assert detail()["claimed_by"] == AUTHOR_AGENT
- # A supplied but unchanged copy field must not turn planning into a new actor.
- call(*update, "--agent-id", AUTHOR_AGENT, "--note", "Peer copy correction", "--clear-priority")
- assert detail()["last_actor_agent_id"] == REVIEW_AGENT
- before = state.read_bytes()
- call(*peer, "--note", "Peer copy correction", "--clear-priority", "--dry-run")
- assert state.read_bytes() == before
-
- retire = ["todo", "supersede", "--goal-id", GOAL_ID, "--todo-id", todo,
- "--agent-id", AUTHOR_AGENT, "--reason", "Retire the validated approach."]
- call(*retire, "--dry-run")
- assert state.read_bytes() == before
- call(*retire)
- assert detail()["last_actor_agent_id"] == AUTHOR_AGENT
- before = state.read_bytes()
- call(*retire)
- assert state.read_bytes() == before
-
-
-@pytest.mark.parametrize("historical_source", [False, True])
-def test_legacy_completion_actor_recovers_and_settles_exact_turn(tmp_path, monkeypatch, historical_source):
- monkeypatch.setenv("TMPDIR", str(tmp_path))
- monkeypatch.setenv("TEMP", str(tmp_path))
- monkeypatch.setenv("TMP", str(tmp_path))
- monkeypatch.setattr(tempfile, "tempdir", str(tmp_path))
- project = tmp_path / "project"
- project.mkdir()
- runtime = tmp_path / "runtime"
- state = project / "ACTIVE_GOAL_STATE.md"
- state.write_text("---\nstatus: active\nobjective: Validate the source.\n---\n\n"
- "# Active Goal State\n\n## Objective\n\nValidate the source.\n\n"
- "## User Todo / Owner Review Reading Queue\n\n## Agent Todo\n\n")
- goal, agent = "legacy-source", "source-owner"
- registry = tmp_path / "registry.json"
- registry.write_text(json.dumps({"common_runtime_root": str(runtime), "goals": [{
- "id": goal, "domain": "synthetic-local-test", "status": "active", "repo": str(project),
- "state_file": state.name, "quota": {"compute": 1},
- "adapter": {"kind": "fixture_connected_delivery_v0", "status": "connected-delivery"},
- "coordination": {"agent_model": "peer_v1", "registered_agents": [agent, "other-owner"]},
- }]}))
- prefix = ["--registry", str(registry), "--runtime-root", str(runtime), "--format", "json"]
-
- def call(*args, code=0):
- output = io.StringIO()
- with contextlib.redirect_stdout(output):
- rc = main([*prefix, *args])
- payload = json.loads(output.getvalue())
- assert rc == code, {key: payload.get(key) for key in ("error", "error_code", "reason", "settlement_progress")}
- return payload
-
- try:
- assert call("check", "--scan-root", str(project))["ok"]
- added = call("todo", "add", "--goal-id", goal, "--role", "agent", "--claimed-by", agent,
- "--text", "Validate the source.", "--task-class", "advancement_task",
- "--action-kind", "validate", "--target-key", "source")
- todo = added["todo_id"]
- completion = ["todo", "complete", "--goal-id", goal, "--todo-id", todo, "--agent-id", agent,
- "--evidence", "evidence:source-validated"]
- call(*completion)
- detail = ["todo", "list", "--goal-id", goal, "--todo-id", todo]
- original = call(*detail)["todo"]
- assert original["last_actor_agent_id"] == agent
- original_bytes = state.read_bytes()
- call(*completion)
- assert state.read_bytes() == original_bytes # Exact replay does not stamp provenance.
- peer_completion = list(completion)
- peer_completion[peer_completion.index("--agent-id") + 1] = "other-owner"
- call(*peer_completion, code=1)
- assert state.read_bytes() == original_bytes
- if historical_source:
- # A persisted completion from before actor transport was supported.
- state.write_text(state.read_text().replace(f" last_actor_agent_id={agent}", ""))
- assert call(*detail)["todo"].get("last_actor_agent_id") is None
- original_bytes = state.read_bytes()
-
- turn = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
- quota = ["quota", "should-run", "--goal-id", goal, "--agent-id", agent,
- "--available-capability", "shell_command", "--codex-app", "--turn-instance-id", turn]
- guard = call(*quota)
- obligation = guard["autonomous_replan_obligation"]
- assert obligation["resolution_mode"] == "todo_lifecycle_settlement"
- receipt = guard["heartbeat_receipt"]
- closeout = ["todo", "complete", "--goal-id", goal, "--todo-id", todo, "--agent-id", agent,
- "--completion-identity-key", original["completion_turn_key"], "--no-follow-up",
- "--note", "The source is validated."]
- call(*closeout, "--dry-run")
- assert state.read_bytes() == original_bytes
- call(*closeout)
- row = call(*detail)["todo"]
- assert row["last_actor_agent_id"] == agent
- assert row["completion_turn_key"] == original["completion_turn_key"]
- assert row["completed_at"] == original["completed_at"]
- recovered = call(*quota)
- assert recovered["heartbeat_receipt"]["settlement_identity"] == receipt["settlement_identity"]
- assert recovered["autonomous_replan_obligation"]["resolution_mode"] == "receipt_bound_replan_settlement"
- assert recovered["autonomous_replan_obligation"]["obligation_id"] == obligation["obligation_id"]
-
- admitted = receipt["recorded_at"]
- old_updated = (datetime.fromisoformat(admitted.replace("Z", "+00:00")) - timedelta(seconds=5)).isoformat()
- for change in [{"last_actor_agent_id": None}, {"last_actor_agent_id": "other-owner"},
- {"completion_turn_key": "different-completion"}, {"updated_at": old_updated},
- {"no_followup": False}]:
- candidate = {"source_obligation": obligation, "obligation": obligation, "lifecycle_settled": True,
- "guard_recorded_at": admitted, "items": [{**row, **change}]}
- with pytest.raises(EffectRuntimeRejected) as rejected:
- effect_runtime_result("work_item.replan_semantics.project", {
- "operation": "receipt_bound_obligation", "guard_scoped": True, "replay_phase": "open",
- "selected_obligation_id": obligation["obligation_id"], "agent_id": agent,
- "current_obligation": None, "transition_candidates": [candidate]})
- assert rejected.value.diagnostic_code == "heartbeat_receipt_identity_conflict"
-
- vision = tmp_path / "vision.json"
- vision.write_text(json.dumps({"schema_version": "goal_vision_replan_contract_v0", "state": "vision_closed",
- "vision_patch": {"acceptance_summary": "The accepted source actor and identity fences are verified."},
- "path_delta": {"schema_version": "goal_path_delta_v0", "outcome": "replan",
- "prior_assumption": "The source was unverified.", "observed_reality": "The source and contrary cases passed.",
- "retained": ["Accepted actor provenance and exact completion identity"],
- "stopped": ["Repeat validation of the completed fixture"], "evidence_refs": ["evidence:source-validated"]}}))
- call("refresh-state", "--goal-id", goal, "--agent-id", agent,
- "--turn-instance-id", turn, "--replan-obligation-id", obligation["obligation_id"],
- "--autonomous-replan-recorded", "--repair-delta-kind", "goal_vision_patch",
- "--classification", "source_verified", "--delivery-batch-scale", "multi_surface",
- "--delivery-outcome", "outcome_progress", "--agent-vision-json", str(vision),
- "--progress-result-class", "advanced", "--progress-evidence-id", "evidence:source-validated",
- "--no-global-sync", "--suppress-external-sinks")
- spend = ["quota", "spend-slot", "--goal-id", goal, "--agent-id", agent, "--turn-instance-id", turn,
- "--replan-obligation-id", obligation["obligation_id"], "--slots", "1", "--source", "heartbeat", "--execute"]
- assert call(*spend)["ok"]
- assert call(*spend)["ok"]
- index = next(runtime.glob("goals/*/runs/index.jsonl"))
- runs = [json.loads(line) for line in index.read_text().splitlines() if line.strip()]
- assert sum(run.get("classification") == "quota_slot_spent" for run in runs) == 1
- finally:
- subprocess.run([sys.executable, "-c", "from loopx.control_plane.effect_runtime import restart_effect_runtime; restart_effect_runtime()"],
- env=dict(os.environ), check=True, capture_output=True, timeout=30)
diff --git a/tests/control_plane/test_legacy_coordination_writer_fence.py b/tests/control_plane/test_legacy_coordination_writer_fence.py
index 0e5b13014d..48e9d6e436 100644
--- a/tests/control_plane/test_legacy_coordination_writer_fence.py
+++ b/tests/control_plane/test_legacy_coordination_writer_fence.py
@@ -12,15 +12,12 @@
from loopx.control_plane.coordination.legacy_writer_fence import (
LegacyCoordinationWriterFenced,
legacy_coordination_write_remediation,
- legacy_coordination_todo_lock_path,
legacy_coordination_writer_fence_path,
- legacy_todo_write_transaction,
require_legacy_coordination_write_allowed,
)
from loopx.control_plane.coordination.local_authority import (
LocalCoordinationAuthorityUnavailable,
)
-from loopx.file_lock import lock_holder_path
from loopx.todos import add_goal_todo
SPLIT_ROOT_GOAL_ID = "legacy-fence-split-root"
@@ -236,86 +233,6 @@ def test_remediation_renders_identically_in_python_and_typescript(
assert rendered.stdout == expected, name
-def test_todo_write_transaction_fences_under_the_effective_runtime_root(
- monkeypatch: pytest.MonkeyPatch,
- tmp_path: Path,
-) -> None:
- """A fence engaged under --runtime-root must fence the legacy writer."""
-
- registry, state, _runtime_registry, runtime_override = _write_split_root_fixture(
- tmp_path
- )
- _engage_fence(runtime_override)
- _blocked_effect_runtime(monkeypatch)
-
- with pytest.raises(LegacyCoordinationWriterFenced):
- with legacy_todo_write_transaction(
- registry,
- SPLIT_ROOT_GOAL_ID,
- state,
- SPLIT_ROOT_AGENT,
- "todo_add",
- False,
- runtime_root=runtime_override,
- ):
- pytest.fail("transaction body must not run while fenced")
-
-
-def test_todo_write_transaction_keeps_registry_root_without_runtime_root(
- monkeypatch: pytest.MonkeyPatch,
- tmp_path: Path,
-) -> None:
- """Omitting runtime_root keeps the registry-derived root (backward compat)."""
-
- registry, state, runtime_registry, _runtime_override = _write_split_root_fixture(
- tmp_path
- )
- _engage_fence(runtime_registry)
- _blocked_effect_runtime(monkeypatch)
-
- with pytest.raises(LegacyCoordinationWriterFenced):
- with legacy_todo_write_transaction(
- registry,
- SPLIT_ROOT_GOAL_ID,
- state,
- SPLIT_ROOT_AGENT,
- "todo_add",
- False,
- ):
- pytest.fail("transaction body must not run while fenced")
-
-
-def test_todo_write_lock_lands_under_the_effective_runtime_root(
- tmp_path: Path,
-) -> None:
- """The todo mutex must serialize with promotion under the override root."""
-
- registry, state, runtime_registry, runtime_override = _write_split_root_fixture(
- tmp_path
- )
- override_lock = legacy_coordination_todo_lock_path(
- runtime_root=runtime_override,
- goal_id=SPLIT_ROOT_GOAL_ID,
- )
-
- registry_lock = legacy_coordination_todo_lock_path(
- runtime_root=runtime_registry,
- goal_id=SPLIT_ROOT_GOAL_ID,
- )
-
- with legacy_todo_write_transaction(
- registry,
- SPLIT_ROOT_GOAL_ID,
- state,
- SPLIT_ROOT_AGENT,
- "todo_add",
- False,
- runtime_root=runtime_override,
- ):
- assert lock_holder_path(override_lock).exists()
- assert not lock_holder_path(registry_lock).exists()
-
-
def test_goal_todo_add_uses_provider_at_override_root_when_promoted(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
diff --git a/tests/control_plane/test_local_authority_shadow_cli_e2e.py b/tests/control_plane/test_local_authority_shadow_cli_e2e.py
deleted file mode 100644
index 4e05b66688..0000000000
--- a/tests/control_plane/test_local_authority_shadow_cli_e2e.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""Real CLI upgrade from a retired setting to an explicit capture lineage."""
-from __future__ import annotations
-
-import json
-from pathlib import Path
-
-from tests.control_plane.shadow_e2e_fixture import workspace
-
-
-def test_retired_setting_cannot_enable_capture_or_satisfy_bootstrap(tmp_path: Path) -> None:
- ws = workspace(tmp_path, bootstrap=False)
- data = json.loads(ws.registry.read_text())
- config = data["goals"][0]["coordination"]
- config.pop("runtime_shadow")
- config["authority_shadow"] = {"schema_version": "loopx_local_authority_shadow_config_v0", "mode": "file_one_way"}
- ws.registry.write_text(json.dumps(data))
- original = ws.registry.read_bytes()
- rejected = ws.cli("configure-goal", "--local-authority-shadow-file", "--execute", success=False)
- assert rejected["ok"] is False
- assert "local_authority_shadow_retired" in rejected["error"]
- assert ws.registry.read_bytes() == original
- initial = ws.add("Continue work with a retained retired configuration.")
- assert "authority_shadow" not in initial
- status = ws.cli("authority-shadow", "status")
- assert status["config"]["status"] == "retired"
- assert status["config"]["enabled"] is False
- assert status["candidate"]["status"] == "missing"
- assert status["management"]["status"] == "missing"
- drain = ws.drain()
- assert drain["config_enabled"] is False
- assert not (ws.runtime / "authority-shadow").exists()
-
- # Configuration is not a source transaction or an automatic bootstrap.
- ws.cli("configure-goal", "--clear-local-authority-shadow", "--coordination-runtime-shadow-file", "--execute")
- assert not (ws.runtime / "authority-shadow" / "file-v0").exists()
- boot = ws.cli("coordination-shadow", "bootstrap", "--execute")
- assert boot["bootstrap"]["status"] == "applied"
- added = ws.add("Capture the next transaction using the existing outbox.")
- assert added["coordination_runtime_shadow"]["outcome"] in {"delivered", "replayed"}
- assert "authority_shadow" not in added
- status = ws.cli("authority-shadow", "status")
- assert status["config"]["status"] == "disabled"
- assert status["management"]["status"] == "active"
- assert status["candidate"]["codec_agreement"] is True
- assert not (ws.runtime / "authority-shadow" / "file" / ws.goal).exists()
-
-
-def test_clearing_old_setting_does_not_retire_an_active_capture(tmp_path: Path) -> None:
- ws = workspace(tmp_path)
- data = json.loads(ws.registry.read_text())
- data["goals"][0]["coordination"]["authority_shadow"] = {
- "schema_version": "loopx_local_authority_shadow_config_v0", "mode": "file_one_way"}
- ws.registry.write_text(json.dumps(data))
- before = ws.cli("authority-shadow", "status")["management"]
- ws.cli("configure-goal", "--clear-local-authority-shadow", "--execute")
- after = ws.cli("authority-shadow", "status")["management"]
- assert after == before
- # Existing process-loss recovery still owns the source transaction.
- crashed = ws.crash("before_marker", "todo", "add", "--role", "agent", "--text", "Recover captured source.")
- assert crashed is not None
- result = ws.drain()
- assert result["ok"] is True
- assert result["drained_count"] == 1
- assert ws.drain()["drained_count"] == 0
diff --git a/tests/control_plane/test_local_authority_shadow_drain.py b/tests/control_plane/test_local_authority_shadow_drain.py
deleted file mode 100644
index 7874ad2229..0000000000
--- a/tests/control_plane/test_local_authority_shadow_drain.py
+++ /dev/null
@@ -1,537 +0,0 @@
-# Native persistence-window regressions live in shadow_drain.test.ts and the real-process CLI suite.
-from __future__ import annotations
-
-import json
-import uuid
-from pathlib import Path
-
-import pytest
-
-from loopx.control_plane.coordination import local_authority_shadow_adapter as adapter
-from loopx.control_plane.coordination import local_authority_shadow_outbox as outbox
-from loopx.control_plane.coordination.local_authority_shadow_projection import (
- head_digest,
- partition_digest,
- text_digest,
-)
-from loopx.file_lock import exclusive_file_lock, exclusive_cross_runtime_file_lock
-from loopx.control_plane.coordination.shadow_management import require_shadow_primary_write_allowed
-from shadow_e2e_fixture import workspace
-from loopx.history import load_registry
-from loopx.registry import find_registry_goal
-from loopx.todos import list_goal_todos
-
-
-GOAL_ID = "goal-e2e"
-
-
-def test_shadow_parity_ignores_only_resume_evaluation_observation_clock() -> None:
- head = {
- "handoff_mode": "hard_lease",
- "todos": [{
- "todo_id": "todo-a",
- "resume_ready": False,
- "resume_condition": {
- "evaluated_at": "2026-09-20T00:00:00Z",
- "satisfied": False,
- "availability_reason": "resume_condition_pending",
- },
- }],
- "leases": [],
- }
- later_observation = json.loads(json.dumps(head))
- later_observation["todos"][0]["resume_condition"]["evaluated_at"] = (
- "2026-09-21T00:00:00Z"
- )
-
- assert head_digest(head) == head_digest(later_observation)
- assert partition_digest({
- "handoff_mode": head["handoff_mode"],
- "todos": head["todos"],
- }) == partition_digest({
- "handoff_mode": later_observation["handoff_mode"],
- "todos": later_observation["todos"],
- })
-
- changed_decision = json.loads(json.dumps(later_observation))
- changed_decision["todos"][0]["resume_condition"]["satisfied"] = True
- assert head_digest(head) != head_digest(changed_decision)
- assert partition_digest({
- "handoff_mode": head["handoff_mode"],
- "todos": head["todos"],
- }) != partition_digest({
- "handoff_mode": changed_decision["handoff_mode"],
- "todos": changed_decision["todos"],
- })
-
-
-def _fixture(tmp_path: Path) -> tuple[Path, Path, Path]:
- real = workspace(tmp_path / "repo")
- return real.registry, real.state, real.runtime
-
-
-def _record_todo_write(
- registry: Path,
- state: Path,
- runtime_root: Path,
- text: str,
- *,
- mark_committed: bool = True,
- write_file: bool = True,
-) -> outbox.TodoPartitionCapture:
- """Prepare a source transaction without invoking a second capture-enabled writer."""
-
- original = state.read_text(encoding="utf-8")
- goal = find_registry_goal(load_registry(registry), GOAL_ID)
- capture = outbox.TodoPartitionCapture.begin(
- enabled=True,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- state_path=state,
- write_class="todo_add",
- original_text=original,
- projector=adapter.todo_partition_projector(goal, state_path=state),
- )
- todo_id = f"todo_{uuid.uuid4().hex[:12]}"
- new_text = original + (
- f"- [ ] {text}\n"
- f" \n"
- )
- capture.prepare(new_text)
- assert capture.outcome.failure is None
- assert capture.outcome.entry_id is not None
- if write_file:
- state.write_text(new_text, encoding="utf-8")
- if mark_committed:
- capture.committed()
- return capture
-
-
-def _drain(registry: Path, runtime_root: Path, **overrides: object) -> adapter.DrainResult:
- limits = {"max_entries": 20, "budget_seconds": 10, **overrides}
- return adapter.drain_local_authority_shadow_outbox(
- registry_path=registry, runtime_root=runtime_root, goal_id=GOAL_ID,
- **limits, # type: ignore[arg-type]
- )
-
-
-def _todo_dir(runtime_root: Path) -> Path:
- return outbox.partition_directory(runtime_root, GOAL_ID, "todos")
-
-
-def test_drain_delivers_each_committed_entry_once_in_order_and_verifies_readback(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- captures = [
- _record_todo_write(registry, state, runtime_root, f"Fact {index}") for index in range(3)
- ]
- assert [capture.outcome.seq for capture in captures] == [1, 2, 3]
-
- result = _drain(registry, runtime_root)
-
- assert result.ok is True
- assert result.outcome == "drained"
- assert result.config_enabled is True
- assert (result.delivered, result.replayed, result.no_op) == (3, 0, 0)
- assert result.pending_after == 0
- assert result.prepared_only_after == 0
- assert result.budget_exhausted is False
- assert result.candidate_readback_verified is True
- assert result.last_cursor == "4"
- assert (result.cursor_before, result.cursor_after, result.drained_count) == ("1", "4", 3)
- payload = result.to_payload()
- assert payload["ok"] is True
- assert payload["drained_count"] == 3
- assert payload["cursor_after"] == "4"
- assert [item["outcome"] for item in result.entries] == ["delivered"] * 3
- assert [item["cursor"] for item in result.entries] == ["2", "3", "4"]
- assert [item["entry_id"] for item in result.entries] == [
- capture.outcome.entry_id for capture in captures
- ]
- assert result.store_identity is not None
- assert result.store_identity.startswith("file:")
- assert list(_todo_dir(runtime_root).iterdir()) == [_todo_dir(runtime_root) / "drain-cursor.json"]
- cursor = outbox.read_cursor(_todo_dir(runtime_root))
- assert cursor is not None
- assert cursor["last_seq"] == 3
- assert cursor["last_entry_id"] == captures[-1].outcome.entry_id
- assert cursor["last_partition_digest"] == captures[-1].outcome.partition_digest
-
- view = adapter.read_local_authority_shadow(runtime_root=runtime_root, goal_id=GOAL_ID, scan_limit=10)
- assert view["status"] == "loaded"
- head = view["head"]
- assert head["schema_version"] == "loopx_coordination_runtime_shadow_projection_v0"
- assert head["partitions"]["todos"] == {
- "seq": 3,
- "partition_digest": captures[-1].outcome.partition_digest,
- }
- assert head["partitions"]["leases"] is None
- listed = list_goal_todos(registry_path=registry, goal_id=GOAL_ID, runtime_root_arg=str(runtime_root))
- assert [todo["todo_id"] for todo in head["todos"]] == sorted(
- todo["todo_id"] for todo in listed["todos"]
- )
- assert view["head_digest"] == head_digest(head) == result.head_digest
- assert [tx["operation_id"] for tx in view["scan"]["transactions"][1:]] == [
- capture.outcome.entry_id for capture in captures
- ]
- receipts = [tx["receipts"][0] for tx in view["scan"]["transactions"][1:]]
- assert all(receipt["source_transaction_correlated"] is True for receipt in receipts)
- assert all(receipt["durable_source_outbox"] is True for receipt in receipts)
- assert all(receipt["parity_verdict"] == "not_evaluated" for receipt in receipts)
- assert [receipt["source_bytes_digest"] for receipt in receipts] == [
- capture.outcome.source_bytes_digest for capture in captures
- ]
- assert str(runtime_root) not in json.dumps(view)
-
- again = _drain(registry, runtime_root)
- assert again.outcome == "nothing_pending"
- assert again.ok is True
-
-
-def test_drain_defers_when_another_drainer_holds_the_lock(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- _record_todo_write(registry, state, runtime_root, "Pending behind a drainer")
- with exclusive_cross_runtime_file_lock(
- outbox.drain_lock_target(runtime_root, GOAL_ID), timeout_seconds=1.0, operation="test_hold"
- ):
- result = _drain(registry, runtime_root, lock_timeout_seconds=0.05)
- assert result.outcome == "drain_deferred"
- assert result.reason_code == "drain_lock_busy"
- assert result.ok is False
- assert result.pending_after == 1
- assert not (runtime_root / "authority-shadow" / "file").exists()
-
-
-def test_lost_drain_response_never_replays_the_effect(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
-) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- _record_todo_write(registry, state, runtime_root, "Commit whose response is lost")
- source = state.read_bytes()
- original = adapter.effect_runtime_result
- attempts: list[dict] = []
-
- def lose_response(method: str, params: dict, **kwargs: object) -> object:
- if method != "coordination.runtime_shadow.drain":
- return original(method, params, **kwargs)
- attempts.append(kwargs)
- committed = original(method, params, **kwargs)
- assert committed["delivered"] == 1
- raise TimeoutError("injected lost response after the real drain committed")
-
- limits = {"max_entries": 16, "budget_seconds": 2, "lock_timeout_seconds": .25}
- with monkeypatch.context() as patch:
- patch.setattr(adapter, "effect_runtime_result", lose_response)
- unknown = _drain(registry, runtime_root, **limits)
- assert unknown.outcome == "stopped"
- assert unknown.reason_code == "shadow_drain_outcome_unknown"
- assert len(attempts) == 1
- assert attempts[0]["retry_safe"] is False
- assert attempts[0]["timeout"] == 17
- committed = adapter.read_local_authority_shadow(
- runtime_root=runtime_root, goal_id=GOAL_ID, scan_limit=20)
- assert len(committed["proof"]["transactions"]) == 2
- assert state.read_bytes() == source
- assert _drain(registry, runtime_root, **limits).outcome == "nothing_pending"
- assert adapter.read_local_authority_shadow(
- runtime_root=runtime_root, goal_id=GOAL_ID, scan_limit=20,
- )["proof"]["transactions"] == committed["proof"]["transactions"]
- assert state.read_bytes() == source
-
-
-def test_drain_batch_is_bounded_and_reports_what_it_left(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- for index in range(3):
- _record_todo_write(registry, state, runtime_root, f"Bounded {index}")
-
- first = _drain(registry, runtime_root, max_entries=2)
- assert first.outcome == "drained"
- assert first.ok is True
- assert first.delivered == 2
- assert first.budget_exhausted is True
- assert first.pending_after == 1
- assert first.candidate_readback_verified is True
-
- second = _drain(registry, runtime_root)
- assert second.delivered == 1
- assert second.pending_after == 0
- assert (second.cursor_before, second.cursor_after) == ("3", "4")
- view = adapter.read_local_authority_shadow(runtime_root=runtime_root, goal_id=GOAL_ID)
- assert view["cursor"] == "4"
- assert view["head"]["partitions"]["todos"]["seq"] == 3
-
-
-def test_prepared_only_entries_resolve_only_under_a_free_primary_lock(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- proven = _record_todo_write(registry, state, runtime_root, "Marker lost after write", mark_committed=False)
- with exclusive_file_lock(state, timeout_seconds=1.0, operation="writer_in_flight"):
- busy = _drain(registry, runtime_root)
- assert busy.outcome == "stopped"
- assert busy.reason_code == "primary_writer_busy"
- assert busy.prepared_only_after == 1
- assert busy.delivered == 0
-
- result = _drain(registry, runtime_root)
- assert result.ok is True
- assert result.delivered == 1
- assert result.no_op == 0
- assert result.entries[0]["resolution"] == "committed_proven_by_readback"
- assert result.entries[0]["entry_id"] == proven.outcome.entry_id
- view = adapter.read_local_authority_shadow(runtime_root=runtime_root, goal_id=GOAL_ID, scan_limit=5)
- assert view["head"]["partitions"]["todos"]["partition_digest"] == proven.outcome.partition_digest
- receipt = view["scan"]["transactions"][1]["receipts"][0]
- assert receipt["resolution"] == "committed_proven_by_readback"
-
- abandoned = _record_todo_write(
- registry, state, runtime_root, "Never landed", mark_committed=False, write_file=False
- )
- result = _drain(registry, runtime_root)
- assert result.delivered == 1
- assert result.no_op == 1
- assert result.entries[0]["resolution"] == "abandoned"
- assert result.entries[0]["entry_id"] == abandoned.outcome.entry_id
- view = adapter.read_local_authority_shadow(runtime_root=runtime_root, goal_id=GOAL_ID, scan_limit=5)
- assert view["cursor"] == "3"
- assert view["head"]["partitions"]["todos"]["seq"] == 1
- assert view["scan"]["transactions"][2]["events"][0]["kind"] == "source_transaction_abandoned"
-
-
-def test_unexplained_prepared_entry_holds_without_reseed_or_deletion(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- _record_todo_write(registry, state, runtime_root, "Baseline")
- assert _drain(registry, runtime_root).delivered == 1
- stale = _record_todo_write(registry, state, runtime_root, "Half-recorded", mark_committed=False)
- state.write_text(state.read_text() + "\nExternal source change after the writer stopped.\n")
- before = {str(path.relative_to(runtime_root)): path.read_bytes()
- for path in (runtime_root / "authority-shadow").rglob("*") if path.is_file()}
- result = _drain(registry, runtime_root)
- assert result.outcome == "stopped"
- assert result.reason_code == "outbox_source_unproved"
- assert result.delivered == result.no_op == 0
- assert result.entries == []
- assert [entry.entry_id for entry in outbox.list_entries(_todo_dir(runtime_root))] == [stale.outcome.entry_id]
- assert {str(path.relative_to(runtime_root)): path.read_bytes()
- for path in (runtime_root / "authority-shadow").rglob("*") if path.is_file()} == before
-
-
-def test_lease_partition_entries_retain_complete_records_at_drain(tmp_path: Path) -> None:
- registry, _state, runtime_root = _fixture(tmp_path)
- directory = outbox.partition_directory(runtime_root, GOAL_ID, "leases")
- record = {
- "goal_id": GOAL_ID,
- "todo_id": "todo_00000000000a",
- "owner": "agent-a",
- "idempotency_key": "k1",
- "write_scopes": ["loopx/**"],
- "version": 2,
- "lease_epoch": 1,
- "acquired_at": "2026-09-03T00:00:00+00:00",
- "updated_at": "2026-09-03T00:01:00+00:00",
- "expires_at": "2026-09-03T00:31:00+00:00",
- "released_at": None,
- "status": "active",
- "lease_path": "/should/never/be/compared",
- "acquire_ttl_seconds": 1800,
- }
- bytes_digest = text_digest(json.dumps(record, indent=2) + "\n")
- binding = require_shadow_primary_write_allowed(runtime_root, GOAL_ID)
- assert binding is not None
- entry_id = outbox.entry_identity(goal_id=GOAL_ID, partition="leases", seq=1, source_ref=bytes_digest,
- capture_lineage_id=binding["capture_lineage_id"], source_root_digest=binding["source_root_digest"])
- outbox.durable_write_json(
- directory / outbox.entry_file_name(1, entry_id, "prepared"),
- {
- "schema_version": outbox.OUTBOX_ENTRY_SCHEMA,
- "goal_id": GOAL_ID,
- "capture_lineage_id": binding["capture_lineage_id"],
- "partition": "leases",
- "seq": 1,
- "entry_id": entry_id,
- "writer": {"runtime": "typescript", "write_class": "task_lease_acquire", "operation_id": "op-1"},
- "source": {
- "kind": "task_lease_record",
- "previous_bytes_digest": None,
- "previous_partition_digest": partition_digest({"leases": []}),
- "bytes_digest": bytes_digest,
- "lease": {"todo_id": record["todo_id"], "version": 2, "lease_epoch": 1, "status": "active", "updated_at": record["updated_at"]},
- "previous_lease": None,
- "event_id": None,
- },
- "source_root_digest": outbox.runtime_root_digest(runtime_root),
- "projection": {"leases": [{"file_stem": record["todo_id"], "record": record}]},
- "partition_digest": None,
- "prepared_at": "2026-09-03T00:01:00.000Z",
- },
- )
- outbox.durable_write_json(
- directory / outbox.entry_file_name(1, entry_id, "committed"),
- {"schema_version": outbox.OUTBOX_COMMIT_SCHEMA, "entry_id": entry_id, "capture_lineage_id": binding["capture_lineage_id"], "committed_at": "2026-09-03T00:01:00.100Z"},
- )
-
- primary_lease = outbox.lease_directory(runtime_root, GOAL_ID) / (record["todo_id"] + ".json")
- primary_lease.parent.mkdir(parents=True, exist_ok=True)
- primary_lease.write_text(json.dumps(record, indent=2) + "\n")
- result = _drain(registry, runtime_root)
-
- assert result.ok is True
- assert result.delivered == 1
- view = adapter.read_local_authority_shadow(runtime_root=runtime_root, goal_id=GOAL_ID, scan_limit=5)
- head = view["head"]
- assert head["todos"] == []
- assert head["handoff_mode"] == "hard_lease"
- assert head["leases"] == [record]
- expected_digest = partition_digest({"leases": head["leases"]})
- assert head["partitions"]["leases"] == {"seq": 1, "partition_digest": expected_digest}
- assert result.entries[0]["partition_digest"] == expected_digest
- assert "/should/never/be/compared" in json.dumps(view)
-
-
-def test_status_reports_backlog_candidate_and_growth_facts(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- empty = adapter.local_authority_shadow_status(registry_path=registry, runtime_root=runtime_root, goal_id=GOAL_ID)
- assert empty["ok"] is True
- assert empty["config"]["status"] == "disabled"
- assert empty["candidate"]["status"] == "loaded"
- assert empty["candidate"]["cursor"] == "1"
- assert empty["store_bytes"] > 0
- assert empty["retention_pressure"] is False
- assert str(runtime_root) not in json.dumps(empty)
-
- _record_todo_write(registry, state, runtime_root, "Status fact")
- pending = adapter.local_authority_shadow_status(registry_path=registry, runtime_root=runtime_root, goal_id=GOAL_ID)
- assert pending["outbox"]["todos"]["committed_pending"] == 1
- assert _drain(registry, runtime_root).delivered == 1
-
- drained = adapter.local_authority_shadow_status(registry_path=registry, runtime_root=runtime_root, goal_id=GOAL_ID)
- assert drained["outbox"]["todos"] == {
- "committed_pending": 0,
- "prepared_only": 0,
- "retired_residue": 0,
- "next_seq": 0,
- "cursor_last_seq": 1,
- "cursor_last_entry_id": outbox.read_cursor(_todo_dir(runtime_root))["last_entry_id"],
- "invalid": None,
- }
- assert drained["candidate"]["status"] == "loaded"
- assert drained["candidate"]["cursor"] == "2"
- assert drained["candidate"]["codec_agreement"] is True
- assert drained["candidate"]["head_schema_version"] == "loopx_coordination_runtime_shadow_projection_v0"
- assert drained["store_bytes"] > 0
-
-
-def test_capture_evidence_v1_reports_measured_facts_only(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- capture = _record_todo_write(registry, state, runtime_root, "Evidence fact")
- # A prepared entry and its commit marker are the two files this pass retires.
- assert len(list(_todo_dir(runtime_root).iterdir())) == 2
- drain = _drain(registry, runtime_root)
- evidence = adapter.capture_evidence(goal_id=GOAL_ID, capture=capture.outcome, drain=drain)
- assert evidence["outcome"] == "delivered"
- assert evidence["reason_code"] is None
- assert evidence["entry"] == {
- "entry_id": capture.outcome.entry_id,
- "partition": "todos",
- "seq": 1,
- "partition_digest": capture.outcome.partition_digest,
- "source_bytes_digest": text_digest(state.read_text(encoding="utf-8")),
- }
- assert evidence["source_transaction_correlated"] is True
- assert evidence["durable_source_outbox"] is True
- assert evidence["source_candidate_compared"] is False
- assert evidence["parity_verdict"] == "not_evaluated"
- assert evidence["drain"] == {
- "outcome": "drained",
- "delivered": 1,
- "replayed": 0,
- "reclaimed_residue": 2,
- "pending_after": 0,
- "prepared_only_after": 0,
- "stopped_at": None,
- "last_cursor": "2",
- "provider_revision": drain.provider_revision,
- "candidate_readback_verified": True,
- }
- assert evidence["store_identity"] == drain.store_identity
-
- deferred = adapter.DrainResult(goal_id=GOAL_ID, outcome="drain_deferred", reason_code="drain_lock_busy")
- deferred_evidence = adapter.capture_evidence(goal_id=GOAL_ID, capture=capture.outcome, drain=deferred)
- assert deferred_evidence["outcome"] == "drain_deferred"
- assert deferred_evidence["reason_code"] == "drain_lock_busy"
- assert deferred_evidence["drain"]["candidate_readback_verified"] is None
- pending = adapter.capture_evidence(goal_id=GOAL_ID, capture=capture.outcome, drain=None)
- assert pending["outcome"] == "pending"
- assert pending["drain"] is None
- observed = [evidence, deferred_evidence, pending]
-
- for skipped_reason in ("partition_unchanged", "shadow_disabled"):
- skipped = outbox.CaptureOutcome(partition="todos", skipped_reason=skipped_reason)
- no_transaction = adapter.capture_evidence(goal_id=GOAL_ID, capture=skipped, drain=None)
- assert no_transaction["outcome"] == "no_transaction"
- assert no_transaction["reason_code"] == skipped_reason
- # Nothing was recorded, so nothing durable or correlated may be claimed.
- assert no_transaction["durable_source_outbox"] is False
- assert no_transaction["source_transaction_correlated"] is False
- assert no_transaction["entry"] == {
- "entry_id": None,
- "partition": "todos",
- "seq": None,
- "partition_digest": None,
- "source_bytes_digest": None,
- }
- observed.append(no_transaction)
- failed = outbox.CaptureOutcome(partition="todos", failure={"reason_code": "outbox_prepare_failed", "error_class": "OSError"})
- failed_evidence = adapter.capture_evidence(goal_id=GOAL_ID, capture=failed, drain=None)
- assert failed_evidence["outcome"] == "capture_failed"
- assert failed_evidence["durable_source_outbox"] is False
- assert failed_evidence["source_transaction_correlated"] is False
- assert failed_evidence["reason_code"] == "outbox_prepare_failed"
- assert failed_evidence["entry"]["entry_id"] is None
- observed.append(failed_evidence)
-
- # Independent wire expectations: observation never grants candidate authority.
- common_facts = {
- "schema_version": "loopx_local_authority_shadow_evidence_v1",
- "goal_id": GOAL_ID,
- "capture_kind": "source_transaction_outbox",
- "source_candidate_compared": False,
- "parity_verdict": "not_evaluated",
- "primary_authority": "legacy_local",
- "candidate_provider": "file",
- "candidate_read_for_decision": False,
- "provider_to_local_writes": False,
- "primary_writeback_preserved": True,
- }
- for payload in observed:
- assert {key: payload[key] for key in common_facts} == common_facts
-
-
-def test_an_orphan_marker_above_the_cursor_is_still_corruption(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- capture = _record_todo_write(registry, state, runtime_root, "Marker without its prepared file")
- (_todo_dir(runtime_root) / outbox.entry_file_name(1, str(capture.outcome.entry_id), "prepared")).unlink()
- with pytest.raises(outbox.OutboxError) as raised:
- outbox.list_entries(_todo_dir(runtime_root))
- assert raised.value.reason_code == "outbox_file_invalid"
- result = _drain(registry, runtime_root)
- assert result.outcome == "stopped"
- assert result.reason_code == "outbox_file_invalid"
- status = adapter.local_authority_shadow_status(registry_path=registry, runtime_root=runtime_root, goal_id=GOAL_ID)
- assert status["ok"] is False
- assert status["outbox"]["todos"]["invalid"] == "outbox_file_invalid"
-
-
-def test_drain_fails_closed_on_an_entry_recorded_for_another_runtime_root(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- capture = _record_todo_write(registry, state, runtime_root, "Written under a foreign root")
- entry_id = str(capture.outcome.entry_id)
- prepared_path = _todo_dir(runtime_root) / outbox.entry_file_name(1, entry_id, "prepared")
- record = json.loads(prepared_path.read_text())
- record["source_root_digest"] = outbox.runtime_root_digest(tmp_path / "elsewhere")
- outbox.durable_write_json(prepared_path, record)
- before = prepared_path.read_bytes()
- result = _drain(registry, runtime_root)
- assert result.outcome == "stopped"
- assert result.reason_code == "outbox_file_invalid"
- assert result.delivered == 0
- assert prepared_path.read_bytes() == before
- with pytest.raises(outbox.OutboxError):
- outbox.list_entries(_todo_dir(runtime_root))
diff --git a/tests/control_plane/test_local_authority_shadow_outbox.py b/tests/control_plane/test_local_authority_shadow_outbox.py
deleted file mode 100644
index f00bb2e994..0000000000
--- a/tests/control_plane/test_local_authority_shadow_outbox.py
+++ /dev/null
@@ -1,546 +0,0 @@
-from __future__ import annotations
-
-import json
-import uuid
-from pathlib import Path
-
-import pytest
-
-from loopx.control_plane.coordination import local_authority_shadow_adapter as adapter
-from loopx.control_plane.coordination import local_authority_shadow_outbox as outbox
-from loopx.control_plane.coordination.coordination_state_contract_generated import (
- COORDINATION_RUNTIME_SHADOW_BOOTSTRAP_RESULT_SCHEMA,
-)
-from loopx.control_plane.coordination.local_authority_shadow_projection import (
- ProjectionValueError,
- canonical_bytes,
- compact_lease,
- partition_digest,
- sha256_digest,
- source_effect_runtime_result,
- text_digest,
- todo_partition_projection,
-)
-from loopx.control_plane.coordination.runtime_shadow import (
- bootstrap_coordination_runtime_shadow,
- build_runtime_shadow_source_snapshot,
- RuntimeInvoker,
-)
-from loopx.control_plane.coordination.shadow_goal_scope import shadow_goal_scope
-from loopx.control_plane.coordination.shadow_management import (
- read_shadow_bootstrap_source_path,
- read_shadow_management_state,
- require_shadow_primary_write_allowed,
- shadow_maintenance_lock_target,
- shadow_management_directory,
- shadow_management_state_path,
-)
-from loopx.control_plane.effect_runtime import EffectRuntimeResponseAmbiguous, EffectRuntimeRejected
-from loopx.file_lock import exclusive_mutation_file_lock
-from loopx.history import load_registry
-from loopx.registry import find_registry_goal
-
-
-GOAL_ID = "goal-outbox"
-
-
-def _fixture(
- tmp_path: Path,
- *,
- bootstrap: bool = True,
- incidental_goal_instance_id: bool = False,
- runtime_invoker: RuntimeInvoker = source_effect_runtime_result,
-) -> tuple[Path, Path, Path]:
- repo = tmp_path / "repo"
- repo.mkdir()
- state = repo / "ACTIVE_GOAL_STATE.md"
- state.write_text(
- "---\n"
- f"goal_id: {GOAL_ID}\n"
- "handoff_mode: hard_lease\n"
- "updated_at: 2026-09-03T00:00:00+00:00\n"
- "---\n\n"
- "## Agent Todo\n\n",
- encoding="utf-8",
- )
- runtime_root = tmp_path / "runtime"
- registry = tmp_path / "registry.json"
- goal = {
- "id": GOAL_ID,
- "domain": "harness_self_improvement",
- "status": "active",
- "repo": str(repo),
- "state_file": state.name,
- "adapter": {"kind": "harness_self_improvement"},
- "coordination": {
- "agent_model": "peer_v1",
- "registered_agents": ["agent-a"],
- },
- }
- if incidental_goal_instance_id:
- goal["goal_instance_id"] = "ginst_0123456789abcdef0123456789abcdef"
- registry.write_text(
- json.dumps(
- {
- "common_runtime_root": str(runtime_root),
- "goals": [goal],
- }
- ),
- encoding="utf-8",
- )
- if bootstrap:
- goal = find_registry_goal(load_registry(registry), GOAL_ID)
- projection, snapshot = build_runtime_shadow_source_snapshot(
- goal=goal, runtime_root=runtime_root, state_path=state, registry_path=registry,
- )
- enabled_goal = {**goal, "coordination": {**goal["coordination"], "runtime_shadow": {
- "schema_version": "loopx_coordination_runtime_shadow_config_v0",
- "enabled": True, "provider": "file_v0",
- }}}
- def bootstrap_or_receipt(method: str, request: dict) -> dict:
- try:
- return runtime_invoker(method, request)
- except EffectRuntimeResponseAmbiguous:
- # The RPC deadline still applies. Do not resend an uncertain
- # write; read its exact completed receipt after the existing
- # maintenance lock releases, using the normal lock budget.
- with exclusive_mutation_file_lock(shadow_maintenance_lock_target(runtime_root, GOAL_ID)):
- journal = read_shadow_management_state(runtime_root, GOAL_ID)
- if journal is None:
- raise
- assert journal["status"] == "active", journal
- assert journal["operation"]["operation_id"] == request["operation_id"], journal
- assert journal["operation"]["request_digest"] == sha256_digest(request), journal
- binding = require_shadow_primary_write_allowed(runtime_root, GOAL_ID)
- assert binding is not None, journal
- assert read_shadow_bootstrap_source_path(runtime_root, GOAL_ID, binding) == state.resolve()
- receipt = journal["result"]
- assert receipt["operation_id"] == request["operation_id"], receipt
- assert {key: receipt[key] for key in binding} == binding, receipt
- return {"schema_version": COORDINATION_RUNTIME_SHADOW_BOOTSTRAP_RESULT_SCHEMA, **receipt}
-
- with shadow_goal_scope(registry, goal_id=GOAL_ID) as scope:
- result = bootstrap_coordination_runtime_shadow(
- goal=enabled_goal, runtime_root=runtime_root, goal_id=GOAL_ID,
- operation_id="bootstrap:outbox-test", source_version="source:initial",
- projection=projection, source_snapshot=snapshot,
- runtime_invoker=bootstrap_or_receipt,
- goal_ref=scope.goal_ref,
- )
- # Outbox assertions require a verified bootstrap, including an exact
- # durable receipt when the managed RPC response is ambiguous.
- assert result["status"] in {"applied", "recovered", "replayed"}, result
- assert result["operation_id"] == "bootstrap:outbox-test", result
- assert result["cursor"] == "1", result
- assert result["provider_revision"] == result["bootstrap_provider_revision"], result
- binding = require_shadow_primary_write_allowed(runtime_root, GOAL_ID)
- assert binding is not None, result
- assert binding["bootstrap_operation_id"] == result["operation_id"], result
- assert binding["bootstrap_provider_revision"] == result["provider_revision"], result
- return registry, state, runtime_root
-
-
-def _capture(
- registry: Path,
- state: Path,
- runtime_root: Path,
- *,
- original_text: str,
- enabled: bool = True,
- write_class: str = "todo_add",
-) -> outbox.TodoPartitionCapture:
- goal = find_registry_goal(load_registry(registry), GOAL_ID)
- return outbox.TodoPartitionCapture.begin(
- enabled=enabled,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- state_path=state,
- write_class=write_class,
- original_text=original_text,
- projector=adapter.todo_partition_projector(goal, state_path=state),
- )
-
-
-def _planned_todo(original: str, text: str) -> tuple[str, str]:
- """Build source bytes for a low-level capture test; do not run another writer."""
- todo_id = f"todo_{uuid.uuid4().hex[:12]}"
- return todo_id, original + (
- f"- [ ] {text}\n"
- f" \n"
- )
-
-
-def _todo_dir(runtime_root: Path) -> Path:
- return outbox.partition_directory(runtime_root, GOAL_ID, "todos")
-
-
-def _drain(registry: Path, runtime_root: Path) -> adapter.DrainResult:
- original = registry.read_bytes()
- data = json.loads(original)
- data["goals"][0]["coordination"]["runtime_shadow"] = {
- "schema_version": "loopx_coordination_runtime_shadow_config_v0",
- "enabled": True, "provider": "file_v0",
- }
- registry.write_text(json.dumps(data))
- try:
- return adapter.drain_local_authority_shadow_outbox(
- registry_path=registry, runtime_root=runtime_root, goal_id=GOAL_ID,
- max_entries=10, budget_seconds=10, lock_timeout_seconds=2,
- )
- finally:
- registry.write_bytes(original)
-
-
-def _record_change(registry: Path, state: Path, runtime_root: Path, text: str) -> outbox.TodoPartitionCapture:
- original = state.read_text()
- _, proposed = _planned_todo(original, text)
- capture = _capture(registry, state, runtime_root, original_text=original)
- capture.prepare(proposed)
- state.write_text(proposed)
- capture.committed()
- assert capture.outcome.failure is None, capture.outcome.failure
- return capture
-
-
-def test_legacy_profile_ignores_an_incidental_goal_instance_id(
- tmp_path: Path,
-) -> None:
- registry, state, runtime_root = _fixture(
- tmp_path,
- incidental_goal_instance_id=True,
- )
-
- capture = _record_change(
- registry,
- state,
- runtime_root,
- "Capture work through the legacy profile.",
- )
-
- assert capture.outcome.failure is None
- binding = require_shadow_primary_write_allowed(runtime_root, GOAL_ID)
- assert binding is not None
- assert "goal_ref" not in binding
-
-
-def _files(directory: Path) -> dict[str, bytes]:
- return {str(path.relative_to(directory)): path.read_bytes() for path in directory.rglob("*") if path.is_file()}
-
-
-def test_outbox_fixture_reads_exact_bootstrap_receipt_after_lost_response(tmp_path: Path) -> None:
- calls = []
- committed = {}
-
- def lose_response(method: str, request: dict) -> dict:
- result = source_effect_runtime_result(method, request)
- assert result["status"] in {"applied", "recovered", "replayed"}, result
- calls.append(request)
- path = shadow_management_state_path(Path(request["runtime_root"]), GOAL_ID)
- committed["journal_bytes"] = path.read_bytes()
- raise EffectRuntimeResponseAmbiguous(method, timeout=10)
-
- registry, state, runtime_root = _fixture(tmp_path, runtime_invoker=lose_response)
- assert len(calls) == 1 # A receipt readback must never dispatch another write.
- assert shadow_management_state_path(runtime_root, GOAL_ID).read_bytes() == committed["journal_bytes"]
- _record_change(registry, state, runtime_root, "Continue after verified bootstrap readback.")
- assert _drain(registry, runtime_root).reason_code is None
-
-
-@pytest.mark.parametrize("damage", ["missing", "pending", "other_operation", "changed_request", "changed_manifest"])
-def test_outbox_fixture_rejects_unverified_bootstrap_after_lost_response(tmp_path: Path, damage: str) -> None:
- calls = []
-
- def lose_response(method: str, request: dict) -> dict:
- calls.append(request)
- if damage != "missing":
- source_effect_runtime_result(method, request)
- root = Path(request["runtime_root"])
- path = shadow_management_state_path(root, GOAL_ID)
- journal = json.loads(path.read_text(encoding="utf-8"))
- if damage == "pending":
- journal.update(status="bootstrapping", binding=None, result=None)
- journal["operation"]["phase"] = "prepared"
- elif damage == "other_operation":
- journal["operation"]["operation_id"] = "bootstrap:another-operation"
- elif damage == "changed_request":
- journal["operation"]["request_digest"] = "sha256:" + "0" * 64
- else:
- manifest = next((shadow_management_directory(root, GOAL_ID) / "operations").glob("*/manifest.json"))
- manifest.write_text("{}", encoding="utf-8")
- path.write_text(json.dumps(journal), encoding="utf-8")
- raise EffectRuntimeResponseAmbiguous(method, timeout=10)
-
- with pytest.raises(AssertionError):
- _fixture(tmp_path, runtime_invoker=lose_response)
- assert len(calls) == 1
- assert not _todo_dir(tmp_path / "runtime").exists()
-
-
-def test_outbox_fixture_preserves_semantic_rejection_even_with_a_bootstrap_receipt(tmp_path: Path) -> None:
- calls = []
-
- def reject_response(method: str, request: dict) -> dict:
- source_effect_runtime_result(method, request)
- calls.append(request)
- raise EffectRuntimeRejected("deliberate semantic rejection", diagnostic_code="invalid_request")
-
- with pytest.raises(AssertionError, match="deliberate semantic rejection"):
- _fixture(tmp_path, runtime_invoker=reject_response)
- assert len(calls) == 1
-
-
-def test_capture_records_prepared_then_committed_and_skips_prose_only_writes(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- original = state.read_text(encoding="utf-8")
- todo_id, new_text = _planned_todo(original, "Bind the shadow to the primary transaction.")
-
- capture = _capture(registry, state, runtime_root, original_text=original)
- capture.prepare(new_text)
- names = sorted(path.name for path in _todo_dir(runtime_root).iterdir())
- assert len(names) == 1
- assert names[0].endswith(".prepared.json")
- assert capture.outcome.entry_id is not None
- assert capture.outcome.seq == 1
- assert capture.outcome.source_bytes_digest == text_digest(new_text)
- assert capture.outcome.entry_id == outbox.entry_identity(
- goal_id=GOAL_ID, partition="todos", seq=1, source_ref=text_digest(new_text),
- capture_lineage_id=require_shadow_primary_write_allowed(runtime_root, GOAL_ID)["capture_lineage_id"],
- source_root_digest=outbox.runtime_root_digest(runtime_root),
- )
-
- state.write_text(new_text)
- capture.committed()
- entries = outbox.list_entries(_todo_dir(runtime_root))
- assert [entry.is_committed for entry in entries] == [True]
- entry = entries[0]
- assert entry.prepared["schema_version"] == outbox.OUTBOX_ENTRY_SCHEMA
- assert entry.committed is not None
- assert entry.committed["schema_version"] == outbox.OUTBOX_COMMIT_SCHEMA
- assert entry.prepared["source"] == {
- "kind": "markdown_active_state",
- "previous_bytes_digest": text_digest(original),
- "previous_partition_digest": partition_digest({"handoff_mode": "hard_lease", "todos": []}),
- "bytes_digest": text_digest(new_text),
- "lease": None,
- "event_id": None,
- }
- assert entry.prepared["writer"] == {
- "runtime": "python",
- "write_class": "todo_add",
- "operation_id": None,
- }
- projection = entry.projection()
- assert projection is not None
- assert projection["handoff_mode"] == "hard_lease"
- assert [item["todo_id"] for item in projection["todos"]] == [todo_id]
- assert entry.recorded_partition_digest() == partition_digest(projection)
- assert str(runtime_root) not in json.dumps(entry.prepared)
-
- prose_only = new_text + "\nOperator note that changes no coordination fact.\n"
- prose = _capture(registry, state, runtime_root, original_text=new_text, write_class="todo_update")
- prose.prepare(prose_only)
- prose.committed()
- assert prose.outcome.entry_id is None
- assert prose.outcome.skipped_reason == "partition_unchanged"
- assert len(outbox.list_entries(_todo_dir(runtime_root))) == 1
-
- _, third_text = _planned_todo(new_text, "Second coordination fact.")
- third = _capture(registry, state, runtime_root, original_text=new_text)
- third.prepare(third_text)
- state.write_text(third_text)
- third.committed()
- assert third.outcome.seq == 2
- assert [entry.seq for entry in outbox.list_entries(_todo_dir(runtime_root))] == [1, 2]
-
-
-def test_capture_and_drain_use_the_active_binding_digest_through_a_root_alias(
- tmp_path: Path,
-) -> None:
- target = tmp_path / "target"
- target.mkdir()
- alias = tmp_path / "alias"
- try:
- alias.symlink_to(target, target_is_directory=True)
- except OSError as exc:
- pytest.skip(f"directory symlinks unavailable: {exc}")
- registry, state, runtime_root = _fixture(alias)
-
- capture = _record_change(
- registry, state, runtime_root, "Capture through the runtime alias."
- )
- binding = require_shadow_primary_write_allowed(runtime_root, GOAL_ID)
- assert binding is not None
- [entry] = outbox.list_entries(_todo_dir(runtime_root))
- assert entry.prepared["source_root_digest"] == binding["source_root_digest"]
-
- drained = _drain(registry, runtime_root)
- assert drained.outcome == "drained"
- assert drained.delivered == 1
- assert capture.outcome.entry_id == drained.entries[0]["entry_id"]
-
-
-def test_disabled_capture_creates_nothing(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path, bootstrap=False)
- capture = _capture(registry, state, runtime_root, original_text="", enabled=False)
- capture.prepare("# anything")
- capture.committed()
- assert capture.outcome.skipped_reason == "shadow_disabled"
- assert capture.outcome.failure is None
- assert not (runtime_root / "authority-shadow").exists()
-
-
-
-
-def test_cursor_allocation_hint_does_not_authorize_a_gap_or_candidate_write(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- directory = _todo_dir(runtime_root)
- directory.mkdir(parents=True)
- # Synthetic wire input: only the native drain may publish real cursors.
- outbox.durable_write_json(outbox.cursor_path(directory), {
- "schema_version": outbox.DRAIN_CURSOR_SCHEMA, "partition": "todos", "last_seq": 5,
- "last_entry_id": "local-shadow-tx-" + "a" * 64, "last_partition_digest": None,
- "last_cursor": "5", "last_provider_revision": "rev-5", "updated_at": "2026-09-05T00:00:00Z",
- })
- assert outbox.next_seq(directory) == 6
- first = _record_change(registry, state, runtime_root, "Recorded after the cursor hint.")
- second = _record_change(registry, state, runtime_root, "Another source transaction.")
- assert (first.outcome.seq, second.outcome.seq) == (6, 7)
- assert [entry.seq for entry in outbox.list_entries(directory)] == [6, 7]
- summary = outbox.outbox_summary(runtime_root, GOAL_ID)
- assert summary["todos"]["committed_pending"] == 2
- assert summary["todos"]["cursor_last_seq"] == 5
- assert summary["leases"] == {
- "committed_pending": 0, "prepared_only": 0, "retired_residue": 0, "next_seq": 0,
- "cursor_last_seq": None, "cursor_last_entry_id": None, "invalid": None,
- }
- before = _files(runtime_root / "authority-shadow")
- result = _drain(registry, runtime_root)
- assert result.reason_code == "outbox_cursor_unproved"
- assert result.delivered == 0
- assert _files(runtime_root / "authority-shadow") == before
-
-
-def test_canonical_projection_rejects_floats_and_bad_lease_identity() -> None:
- with pytest.raises(ProjectionValueError):
- canonical_bytes({"version": 1.0})
- with pytest.raises(ProjectionValueError):
- todo_partition_projection(handoff_mode="hard_lease", todos=[{"todo_id": "a", "status": 2.5}])
- assert canonical_bytes({"b": 1, "a": [True, None, "\u00e9"]}) == '{"a":[true,null,"\u00e9"],"b":1}'.encode("utf-8")
- assert sha256_digest({"b": [1, None], "a": "x"}) == sha256_digest({"a": "x", "b": [1, None]})
- with pytest.raises(ProjectionValueError):
- compact_lease({"goal_id": "other", "todo_id": "todo-a"}, goal_id=GOAL_ID, file_stem="todo-a")
- lease = compact_lease(
- {"goal_id": GOAL_ID, "todo_id": "todo-b", "version": 1, "extra": "retained"},
- goal_id=GOAL_ID, file_stem="todo-b",
- )
- assert lease["extra"] == "retained"
-
-
-def test_capture_failure_is_typed_and_preserves_the_primary_result(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- original = state.read_text()
- _, new_text = _planned_todo(original, "Primary survives an unavailable outbox directory.")
- blocker = _todo_dir(runtime_root)
- blocker.write_text("not a directory")
- capture = _capture(registry, state, runtime_root, original_text=original)
- capture.prepare(new_text)
- state.write_text(new_text)
- capture.committed()
- assert capture.outcome.entry_id is None
- assert capture.outcome.failure is not None
- assert capture.outcome.failure["reason_code"] == "outbox_prepare_failed"
- assert state.read_text() == new_text
-
-
-def test_prepared_records_must_bind_their_directory_identity_and_source(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- original = state.read_text(encoding="utf-8")
- _, new_text = _planned_todo(original, "Bound to this goal and partition.")
- capture = _capture(registry, state, runtime_root, original_text=original)
- capture.prepare(new_text)
- state.write_text(new_text)
- capture.committed()
- directory = _todo_dir(runtime_root)
- [entry] = outbox.list_entries(directory)
- assert entry.prepared["goal_id"] == GOAL_ID
- assert entry.prepared["partition"] == "todos"
- assert entry.prepared["source_root_digest"] == outbox.runtime_root_digest(runtime_root)
- assert outbox.record_source_ref(entry.prepared) == text_digest(state.read_text(encoding="utf-8"))
-
- def tampered(**changes: object) -> None:
- record = dict(entry.prepared)
- for key, value in changes.items():
- if isinstance(value, dict) and isinstance(record.get(key), dict):
- record[key] = {**record[key], **value}
- else:
- record[key] = value
- outbox.durable_write_json(entry.prepared_path, record)
- with pytest.raises(outbox.OutboxError) as raised:
- outbox.list_entries(directory)
- assert raised.value.reason_code == "outbox_file_invalid"
-
- tampered(goal_id="goal-other")
- tampered(partition="leases")
- tampered(source={"bytes_digest": text_digest("some other bytes")})
- tampered(source_root_digest="not-a-digest")
- tampered(writer={"runtime": "ruby"})
- tampered(source={"kind": "unknown_source"})
- outbox.durable_write_json(entry.prepared_path, entry.prepared)
- assert len(outbox.list_entries(directory)) == 1
-
- # A valid-looking root/lineage from another generation cannot reuse this ID.
- tampered(capture_lineage_id="another-lineage")
- tampered(source_root_digest="sha256:" + "1" * 64)
- tampered(schema_version="loopx_local_authority_shadow_outbox_entry_v0")
- outbox.durable_write_json(entry.prepared_path, entry.prepared)
- assert entry.committed_path is not None and entry.committed is not None
- outbox.durable_write_json(entry.committed_path, {**entry.committed, "capture_lineage_id": "another-lineage"})
- with pytest.raises(outbox.OutboxError) as invalid_marker:
- outbox.list_entries(directory)
- assert invalid_marker.value.reason_code == "outbox_file_invalid"
- outbox.durable_write_json(entry.committed_path, entry.committed)
- assert len(outbox.list_entries(directory)) == 1
-
-
-def test_cursor_residue_is_diagnostic_and_requires_an_exact_receipt(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- _record_change(registry, state, runtime_root, "First source transaction.")
- _record_change(registry, state, runtime_root, "Second source transaction.")
- directory = _todo_dir(runtime_root)
- first, second = outbox.list_entries(directory)
- assert outbox.retired_residue(directory) == []
- outbox.durable_write_json(outbox.cursor_path(directory), {
- "schema_version": outbox.DRAIN_CURSOR_SCHEMA, "partition": "todos", "last_seq": first.seq,
- "last_entry_id": first.entry_id, "last_partition_digest": first.recorded_partition_digest(),
- "last_cursor": "2", "last_provider_revision": "file:2:" + "1" * 24,
- "updated_at": "2026-09-05T00:00:00Z",
- })
- assert [path.name for path in outbox.retired_residue(directory)] == sorted(
- [first.committed_path.name, first.prepared_path.name]
- )
- # Hiding entries below an unproved watermark would lose the only delivery evidence.
- assert [entry.seq for entry in outbox.list_entries(directory)] == [first.seq, second.seq]
- assert outbox.next_seq(directory) == 3
- before = _files(runtime_root / "authority-shadow")
- result = _drain(registry, runtime_root)
- assert result.reason_code == "outbox_cursor_unproved"
- assert _files(runtime_root / "authority-shadow") == before
-
-
-def test_exact_receipts_allow_cursor_then_residue_cleanup(tmp_path: Path) -> None:
- registry, state, runtime_root = _fixture(tmp_path)
- _record_change(registry, state, runtime_root, "An exact transaction to deliver.")
- directory = _todo_dir(runtime_root)
- original = _files(directory)
- delivered = _drain(registry, runtime_root)
- assert delivered.delivered == 1, delivered.reason_code
- cursor = outbox.read_cursor(directory)
- assert cursor is not None and cursor["last_seq"] == 1
- assert outbox.list_entries(directory) == []
- for name, raw in original.items():
- (directory / name).write_bytes(raw)
- assert len(outbox.retired_residue(directory)) == 2
- recovered = _drain(registry, runtime_root)
- assert recovered.reason_code is None, recovered.reason_code
- assert outbox.list_entries(directory) == []
- assert outbox.read_cursor(directory) == cursor
diff --git a/tests/control_plane/test_local_authority_shadow_runtime.py b/tests/control_plane/test_local_authority_shadow_runtime.py
deleted file mode 100644
index 9a5629e2af..0000000000
--- a/tests/control_plane/test_local_authority_shadow_runtime.py
+++ /dev/null
@@ -1,107 +0,0 @@
-from __future__ import annotations
-
-import json
-from pathlib import Path
-
-import pytest
-
-from loopx.control_plane.work_items.task_lease import (
- acquire_task_lease,
- release_task_lease,
- renew_task_lease,
-)
-from loopx.todos import (
- add_goal_todo,
-)
-
-
-GOAL_ID = "goal-shadow"
-AGENT_A = "agent-a"
-AGENT_B = "agent-b"
-
-
-def _fixture(tmp_path: Path, *, enabled: bool) -> tuple[Path, Path, Path]:
- repo = tmp_path / "repo"
- repo.mkdir()
- state = repo / "ACTIVE_GOAL_STATE.md"
- state.write_text(
- "---\n"
- f"goal_id: {GOAL_ID}\n"
- "handoff_mode: hard_lease\n"
- "updated_at: 2026-09-02T00:00:00+00:00\n"
- "---\n\n"
- "## Agent Todo\n\n",
- encoding="utf-8",
- )
- runtime_root = tmp_path / "runtime"
- coordination: dict[str, object] = {
- "agent_model": "peer_v1",
- "registered_agents": [AGENT_A, AGENT_B],
- }
- if enabled:
- coordination["authority_shadow"] = {
- "schema_version": "loopx_local_authority_shadow_config_v0",
- "mode": "file_one_way",
- }
- registry = tmp_path / "registry.json"
- registry.write_text(
- json.dumps(
- {
- "common_runtime_root": str(runtime_root),
- "goals": [
- {
- "id": GOAL_ID,
- "domain": "harness_self_improvement",
- "status": "active",
- "repo": str(repo),
- "state_file": state.name,
- "adapter": {"kind": "harness_self_improvement"},
- "coordination": coordination,
- }
- ],
- }
- ),
- encoding="utf-8",
- )
- return registry, state, runtime_root
-
-
-def _add(registry: Path) -> dict:
- return add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Continue primary work with a retained observation setting.",
- task_class="advancement_task",
- )
-
-
-@pytest.mark.parametrize("enabled", [False, True])
-def test_retired_and_absent_settings_leave_public_writers_on_primary_only(tmp_path: Path, enabled: bool) -> None:
- registry, state, runtime = _fixture(tmp_path, enabled=enabled)
- retained = runtime / "authority-shadow" / "file" / GOAL_ID / "historical.json"
- if enabled:
- retained.parent.mkdir(parents=True)
- retained.write_bytes(b'{"historical":true}\n')
- before = retained.read_bytes() if enabled else None
- added = _add(registry)
- todo_id = added["todo_id"]
- acquired = acquire_task_lease(registry_path=registry, runtime_root=runtime, goal_id=GOAL_ID,
- todo_id=todo_id, owner=AGENT_A, idempotency_key="retired-writer", ttl_seconds=120)
- renewed = renew_task_lease(registry_path=registry, runtime_root=runtime, goal_id=GOAL_ID,
- todo_id=todo_id, owner=AGENT_A, idempotency_key="retired-writer",
- expected_version=acquired["lease"]["version"], ttl_seconds=180)
- released = release_task_lease(registry_path=registry, runtime_root=runtime, goal_id=GOAL_ID,
- todo_id=todo_id, owner=AGENT_A, idempotency_key="retired-writer",
- expected_version=renewed["lease"]["version"])
- for result in (added, acquired, renewed, released):
- assert result["ok"] is True
- assert "authority_shadow" not in result
- assert todo_id in state.read_text()
- assert not (runtime / "authority-shadow" / "outbox").exists()
- assert not (runtime / "authority-shadow" / "file-v0").exists()
- if enabled:
- assert retained.read_bytes() == before
- assert list(retained.parent.iterdir()) == [retained]
- else:
- assert not (runtime / "authority-shadow").exists()
diff --git a/tests/control_plane/test_local_coordination_authority.py b/tests/control_plane/test_local_coordination_authority.py
index b47debecfd..960b7dd1de 100644
--- a/tests/control_plane/test_local_coordination_authority.py
+++ b/tests/control_plane/test_local_coordination_authority.py
@@ -9,7 +9,7 @@
import pytest
from canonical_authority_fixture import (
- initialize_canonical_authority, single_snapshot_page,
+ initialize_canonical_authority, isolate_sqlite_runtime, single_snapshot_page,
promoted_create_fixture as _promoted_create_fixture,
)
@@ -37,8 +37,6 @@
)
from loopx.control_plane.todos.active_state_editing import (
TODO_SECTION_HEADINGS,
- section_bounds,
- todo_blocks,
)
from loopx.control_plane.todos.completion_validation_projection import (
completion_validation_declaration_sha256,
@@ -943,10 +941,9 @@ def _seed_promoted_store(
}
],
)
- # main's builder carries no handoff mode and the claim owner reads a missing
- # mode as legacy; this branch's builder defaults to hard_lease, so seed the
- # legacy mode explicitly unless a test asks for another one.
- projection["handoff_mode"] = str(handoff_mode) if handoff_mode is not None else "legacy"
+ # Normal claim tests need a current policy; historical wire shape does not
+ # imply the retired ownership strategy. Explicit modes stay caller-owned.
+ projection["handoff_mode"] = str(handoff_mode) if handoff_mode is not None else "soft_claim"
state_path = runtime_root / "ACTIVE_GOAL_STATE.md"
if not state_path.exists():
state_path.write_text(
@@ -1030,9 +1027,11 @@ def test_promoted_claim_protocol_failure_stays_infrastructure_outage(
assert not isinstance(exc_info.value, LocalCoordinationAuthorityRejection)
-def test_promoted_claim_folds_agent_id_whitespace_like_legacy(tmp_path: Path) -> None:
+def test_canonical_claim_uses_public_agent_id_whitespace_normalization(
+ tmp_path: Path,
+) -> None:
"""Every Python whitespace character (including U+0085 NEL, U+001C..U+001F,
- tabs, NBSP) in claimed_by must fold to '-' identically before and after promotion.
+ tabs, NBSP) in claimed_by must fold to '-' through the public normalizer.
"""
from loopx.control_plane.todos.contract import normalize_todo_claimed_by
@@ -1594,9 +1593,14 @@ def count_authority_runtime_call(
assert "Continue provider-first delivery." in rendered
-def test_public_terminal_optional_prose_matches_before_and_after_promotion(
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_public_terminal_optional_prose_matches_canonical_providers(
tmp_path: Path,
+ monkeypatch: pytest.MonkeyPatch,
+ provider: str,
) -> None:
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
cases = [
("note", None, None),
("note", "", None),
@@ -1615,7 +1619,7 @@ def test_public_terminal_optional_prose_matches_before_and_after_promotion(
("reason", " first\u0085 second ", "first second"),
]
- def exercise(root: Path, *, promoted: bool) -> dict[str, object]:
+ def exercise(root: Path) -> dict[str, object]:
runtime_root = root / "runtime"
project = root / "project"
state_file = project / "ACTIVE_GOAL_STATE.md"
@@ -1675,17 +1679,17 @@ def exercise(root: Path, *, promoted: bool) -> dict[str, object]:
),
encoding="utf-8",
)
- if promoted:
- initialize_canonical_authority(
- runtime_root,
- "goal-a",
- build_todo_runtime_shadow_projection(
- goal_id="goal-a",
- todos=records,
- handoff_mode="soft_claim",
- ),
- state_path=state_file,
- )
+ initialize_canonical_authority(
+ runtime_root,
+ "goal-a",
+ build_todo_runtime_shadow_projection(
+ goal_id="goal-a",
+ todos=records,
+ handoff_mode="soft_claim",
+ ),
+ state_path=state_file,
+ provider=provider,
+ )
for index, (field, value, _expected) in enumerate(cases, start=1):
common = {
@@ -1708,44 +1712,36 @@ def exercise(root: Path, *, promoted: bool) -> dict[str, object]:
)
assert result["completed"] is True
- if promoted:
- projection = read_canonical_todos_if_promoted(
- runtime_root=runtime_root,
- goal_id="goal-a",
- )
- assert projection is not None
- todos = projection["todos"]
- else:
- lines = state_file.read_text(encoding="utf-8").splitlines()
- bounds = section_bounds(lines, "agent")
- assert bounds is not None
- todos = todo_blocks(
- lines,
- bounds[0],
- bounds[1],
- role="agent",
- source_section=bounds[2],
- )
+ projection = read_canonical_todos_if_promoted(
+ runtime_root=runtime_root,
+ goal_id="goal-a",
+ )
+ assert projection is not None
+ todos = projection["todos"]
by_id = {todo["todo_id"]: todo for todo in todos}
return {
f"{field}:{index}": by_id[f"todo_prose_{index:02d}"].get(field)
for index, (field, _value, _expected) in enumerate(cases, start=1)
}
- legacy = exercise(tmp_path / "legacy", promoted=False)
- canonical = exercise(tmp_path / "canonical", promoted=True)
+ canonical = exercise(tmp_path / provider)
expected = {
f"{field}:{index}": value
for index, (field, _input, value) in enumerate(cases, start=1)
}
- assert legacy == expected
assert canonical == expected
-def test_illegal_terminal_actor_is_a_domain_valueerror_before_and_after_promotion(
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_illegal_terminal_actor_is_rejected_by_canonical_provider(
tmp_path: Path,
+ monkeypatch: pytest.MonkeyPatch,
+ provider: str,
) -> None:
- def rejected(root: Path, *, promoted: bool) -> ValueError:
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+
+ def rejected(root: Path) -> ValueError:
runtime_root = root / "runtime"
project = root / "project"
state_file = project / "ACTIVE_GOAL_STATE.md"
@@ -1783,31 +1779,31 @@ def rejected(root: Path, *, promoted: bool) -> ValueError:
),
encoding="utf-8",
)
- if promoted:
- initialize_canonical_authority(
- runtime_root,
- "goal-a",
- build_todo_runtime_shadow_projection(
- goal_id="goal-a",
- handoff_mode="soft_claim",
- todos=[
- {
- "schema_version": "todo_item_v0",
- "index": 1,
- "done": False,
- "text": "Reject an illegal terminal actor",
- "todo_id": "todo_terminal_actor",
- "role": "agent",
- "status": "open",
- "archive_state": "active",
- "source_section": TODO_SECTION_HEADINGS["agent"],
- "task_class": "advancement_task",
- "claimed_by": "agent-a",
- }
- ],
- ),
- state_path=state_file,
- )
+ initialize_canonical_authority(
+ runtime_root,
+ "goal-a",
+ build_todo_runtime_shadow_projection(
+ goal_id="goal-a",
+ handoff_mode="soft_claim",
+ todos=[
+ {
+ "schema_version": "todo_item_v0",
+ "index": 1,
+ "done": False,
+ "text": "Reject an illegal terminal actor",
+ "todo_id": "todo_terminal_actor",
+ "role": "agent",
+ "status": "open",
+ "archive_state": "active",
+ "source_section": TODO_SECTION_HEADINGS["agent"],
+ "task_class": "advancement_task",
+ "claimed_by": "agent-a",
+ }
+ ],
+ ),
+ state_path=state_file,
+ provider=provider,
+ )
with pytest.raises(ValueError) as exc_info:
complete_goal_todo(
registry_path=registry_path,
@@ -1821,9 +1817,7 @@ def rejected(root: Path, *, promoted: bool) -> ValueError:
)
return exc_info.value
- legacy = rejected(tmp_path / "legacy", promoted=False)
- canonical = rejected(tmp_path / "canonical", promoted=True)
- assert not isinstance(legacy, LocalCoordinationAuthorityUnavailable)
+ canonical = rejected(tmp_path / provider)
assert isinstance(canonical, LocalCoordinationAuthorityRejection)
assert canonical.code == "actor_not_registered"
diff --git a/tests/control_plane/test_long_chain_projected_closeout.py b/tests/control_plane/test_long_chain_projected_closeout.py
index 9c5e002baf..9982a4c673 100644
--- a/tests/control_plane/test_long_chain_projected_closeout.py
+++ b/tests/control_plane/test_long_chain_projected_closeout.py
@@ -12,7 +12,7 @@
from tests.control_plane.test_quota_settlement_cli import (
AGENT_ID, GOAL_ID, SELECTED_REPLAN_TODO_ID, TURN_ID,
_bind_selected_replan_guard, _configure_selected_todo_replan_fixture,
- _projected_cli_args,
+ _initialize_fixture_authority, _projected_cli_args,
_run_cli, _spend_run_count, _write_fixture,
)
@@ -118,6 +118,7 @@ def test_projected_vision_replan_settles_without_a_meta_successor(
f"todo_id={last_id} status=open task_class=advancement_task action_kind=validate",
)
state.write_text(source)
+ _initialize_fixture_authority(project, runtime)
def call(*args):
rc, result = _run_cli(registry, runtime, *args, cwd=project)
diff --git a/tests/control_plane/test_long_history_todo_reads.py b/tests/control_plane/test_long_history_todo_reads.py
index 5ac1103796..3eb17f44d1 100644
--- a/tests/control_plane/test_long_history_todo_reads.py
+++ b/tests/control_plane/test_long_history_todo_reads.py
@@ -3,10 +3,36 @@
import pytest
+from loopx.control_plane.coordination.authority_source_capture import (
+ build_runtime_shadow_source_snapshot,
+ todo_partition_projector,
+)
from loopx.control_plane.testing.canary_harness import write_fixture_registry, run_json_cli_result
from loopx.control_plane.todos.active_state_todo_parser import parse_active_state_todos, parse_todo_source
from loopx.control_plane.todos.goal_todo_projection import todo_summaries_from_fields
from loopx.control_plane.todos.todo_summary import compact_evaluated_todo_group
+from loopx.history import load_registry
+from tests.control_plane.canonical_authority_fixture import initialize_canonical_authority
+
+
+def _initialize_source_authority(
+ *,
+ state: Path,
+ runtime: Path,
+ registry: Path,
+) -> dict:
+ goal = next(goal for goal in load_registry(registry)["goals"] if goal["id"] == "goal-a")
+ projection, _ = build_runtime_shadow_source_snapshot(
+ goal=goal,
+ runtime_root=runtime,
+ state_path=state,
+ registry_path=registry,
+ )
+ assert todo_partition_projector(goal, state_path=state)(
+ state.read_text(encoding="utf-8")
+ )["todos"] == projection["todos"]
+ initialize_canonical_authority(runtime, "goal-a", projection, state_path=state)
+ return projection
@pytest.mark.parametrize("history_count", [0, 4096])
@@ -23,6 +49,7 @@ def test_public_list_preserves_completed_dependency_after_archive(tmp_path: Path
write_fixture_registry(project=tmp_path, runtime_root=runtime, registry_path=registry, goal_id="goal-a",
domain="long-history", adapter_kind="generic_project_goal_v0", state_file=str(state),
registered_agents=["agent-a"], quota_allowed_slots=None)
+ _initialize_source_authority(state=state, runtime=runtime, registry=registry)
code, archived = run_json_cli_result("todo", "archive-completed", "--goal-id", "goal-a",
"--role", "agent", "--max-active-done", "0", "--execute", registry_path=registry, runtime_root=runtime)
assert code == 0, archived
@@ -57,10 +84,6 @@ def test_display_cannot_bypass_full_source_resume_evaluation(condition: dict | N
def test_capture_preserves_archived_dependency_for_real_canonical_cli_read(tmp_path: Path) -> None:
- from loopx.history import load_registry
- from loopx.control_plane.coordination.runtime_shadow import build_runtime_shadow_source_snapshot
- from loopx.control_plane.coordination.local_authority_shadow_adapter import todo_partition_projector
- from tests.control_plane.canonical_authority_fixture import initialize_canonical_authority
state, runtime, registry = tmp_path / "STATE.md", tmp_path / "runtime", tmp_path / "registry.json"
state.write_text("# Goal\n\n## Agent Todo\n\n- [ ] Waiting work\n"
" \n\n"
@@ -70,17 +93,16 @@ def test_capture_preserves_archived_dependency_for_real_canonical_cli_read(tmp_p
domain="long-history", adapter_kind="generic_project_goal_v0", state_file=str(state),
registered_agents=["agent-a"], quota_allowed_slots=None)
original = state.read_bytes()
- goal = next(goal for goal in load_registry(registry)["goals"] if goal["id"] == "goal-a")
- projection, _ = build_runtime_shadow_source_snapshot(goal=goal, runtime_root=runtime,
- state_path=state, registry_path=registry)
- writer_projection = todo_partition_projector(goal, state_path=state)(state.read_text())
- assert writer_projection["todos"] == projection["todos"]
+ projection = _initialize_source_authority(
+ state=state,
+ runtime=runtime,
+ registry=registry,
+ )
assert state.read_bytes() == original
records = {item["todo_id"]: item for item in projection["todos"]}
assert records["todo_history"]["archive_state"] == "archive"
assert records["todo_history"]["status"] == "done"
assert records["todo_history"]["role"] == "agent"
- initialize_canonical_authority(runtime, "goal-a", projection, state_path=state)
state.unlink()
code, result = run_json_cli_result("todo", "list", "--goal-id", "goal-a", "--role", "agent",
"--todo-id", "todo_waiting", registry_path=registry, runtime_root=runtime)
diff --git a/tests/control_plane/test_migrated_terminal_closeout_cli.py b/tests/control_plane/test_migrated_terminal_closeout_cli.py
deleted file mode 100644
index 30701ae252..0000000000
--- a/tests/control_plane/test_migrated_terminal_closeout_cli.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""A legacy completion stays settled across real reviewed provider promotion."""
-
-import json
-import sys
-
-import pytest
-
-from tests.control_plane.test_reviewed_promotion_cli import prepare
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_completed_legacy_todo_closes_after_promotion_without_revalidation(tmp_path, provider):
- ws, _, _ = prepare(tmp_path, provider, source_mode="legacy")
- marker = tmp_path / "validation-count"
- validator = tmp_path / "validate.py"
- validator.write_text(f"from pathlib import Path\np=Path({str(marker)!r})\np.write_text(p.read_text()+'run\\n' if p.exists() else 'run\\n')\n")
- todo_id = ws.cli("todo", "add", "--role", "agent", "--text", "Preserve the completed migration work",
- "--validation-command-json", json.dumps([sys.executable, str(validator)]),
- "--validation-timeout-seconds", "5")["todo_id"]
- completed = ws.cli("todo", "complete", "--todo-id", todo_id, "--agent-id", "agent-a",
- "--evidence", "Original check passed")
- assert completed["ok"], completed
- assert marker.read_text() == "run\n"
- before = ws.cli("todo", "list", "--todo-id", todo_id)["todo"]
- assert before["completion_continuation"] == "active_goal"
- key = before["completion_turn_key"]
- assert ws.drain(budget_seconds="60")["ok"]
- preview = ws.cli("coordination-shadow", "promote")
- saved = tmp_path / "completed-review.json"
- saved.write_text(json.dumps(preview), encoding="utf-8")
- promoted = ws.cli("coordination-shadow", "promote", "--reviewed-plan", str(saved), "--execute")
- assert promoted["ok"], promoted
- # This intentionally minimal migration fixture is not a healthy runnable
- # Goal. Its guard still exposes the canonical lifecycle diagnostic.
- guard_args = ("quota", "should-run", "--codex-app", "--agent-id", "agent-a",
- "--turn-instance-id", "migration-readback")
- guard_before = ws.cli(*guard_args, success=False)
- assert guard_before["agent_todo_summary"]["todo_succession_warning"]["count"] == 1
- validator.unlink() # A metadata closeout must not need the old validator.
- closeout_args = ("todo", "complete", "--todo-id", todo_id, "--agent-id", "agent-a",
- "--completion-identity-key", key, "--evidence", "Original check passed", "--no-follow-up")
- closed = ws.cli(*closeout_args)
- assert closed["ok"] and closed["changed"], closed
- after = ws.cli("todo", "list", "--todo-id", todo_id)["todo"]
- assert after["no_followup"] is True
- assert after["completion_continuation"] == "no_followup"
- assert after["completion_recovery"] == "lifecycle_reentry_terminal_closeout"
- for field in ("completed_at", "completion_turn_key", "completion_receipt_id", "evidence"):
- assert after[field] == before[field]
- replay = ws.cli(*closeout_args)
- assert replay["ok"] and replay["idempotent_replay"], replay
- assert ws.cli("todo", "list", "--todo-id", todo_id)["todo"] == after
- assert marker.read_text() == "run\n"
- guard_after = ws.cli(*guard_args, success=False)
- assert "todo_succession_warning" not in guard_after["agent_todo_summary"]
diff --git a/tests/control_plane/test_monitor_followthrough_contract.py b/tests/control_plane/test_monitor_followthrough_contract.py
index 7a3ee6e811..80ea23edd7 100644
--- a/tests/control_plane/test_monitor_followthrough_contract.py
+++ b/tests/control_plane/test_monitor_followthrough_contract.py
@@ -5,6 +5,13 @@
import pytest
+from canonical_authority_fixture import initialize_canonical_authority
+from loopx.control_plane.coordination.local_authority import (
+ CanonicalAuthorityRequired,
+)
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
from loopx.control_plane.scheduler.monitor_poll_writeback import (
resolve_monitor_todo_item,
write_monitor_poll_todo_state,
@@ -35,7 +42,13 @@
AGENT_ID = "codex-quality-qualification"
-def _write_fixture(tmp_path: Path) -> tuple[Path, Path, Path]:
+def _write_fixture(
+ tmp_path: Path,
+ *,
+ provider: str = "file",
+ canonical: bool = True,
+ handoff_mode: str = "soft_claim",
+) -> tuple[Path, Path, Path]:
project = tmp_path / "project"
runtime = tmp_path / "runtime"
state = project / ".codex" / "goals" / GOAL_ID / "ACTIVE_GOAL_STATE.md"
@@ -55,6 +68,18 @@ def _write_fixture(tmp_path: Path) -> tuple[Path, Path, Path]:
registered_agents=[AGENT_ID, "codex-main-control"],
quota_allowed_slots=None,
)
+ if canonical:
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=[],
+ handoff_mode=handoff_mode,
+ ),
+ state_path=state,
+ provider=provider,
+ )
return registry, runtime, state
@@ -129,10 +154,15 @@ def test_todo_update_preserves_omitted_monitor_schedule_fields(tmp_path: Path) -
runtime_root=runtime,
)
- assert updated["target_key"] == "public-release:42"
- assert updated["cadence"] == "1h"
- assert updated["next_due_at"] == "2099-01-01T00:00:00+00:00"
- assert updated["watch_only"] == "true"
+ persisted = resolve_monitor_todo_item(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ todo_id=updated["todo_id"],
+ )
+ assert persisted["target_key"] == "public-release:42"
+ assert persisted["cadence"] == "1h"
+ assert persisted["next_due_at"] == "2099-01-01T00:00:00+00:00"
+ assert persisted["watch_only"] == "true"
def test_monitor_resume_when_is_a_supported_bounded_policy(
@@ -150,14 +180,13 @@ def test_monitor_resume_when_is_a_supported_bounded_policy(
resume_when="pr_merged:owner/repo#42",
monitor_metadata={"target_key": "public-pr:42", "cadence": "1h"},
)
- assert bounded["resume_when"] == "pr_merged:owner/repo#42"
- assert bounded["next_due_at"]
persisted = resolve_monitor_todo_item(
registry_path=registry,
goal_id=GOAL_ID,
todo_id=bounded["todo_id"],
)
- assert persisted["next_due_at"] == bounded["next_due_at"]
+ assert persisted["resume_when"] == "pr_merged:owner/repo#42"
+ assert persisted["next_due_at"]
monitor = _add_monitor(
registry,
@@ -171,7 +200,12 @@ def test_monitor_resume_when_is_a_supported_bounded_policy(
agent_id=AGENT_ID,
resume_when="pr_merged:owner/repo#42",
)
- assert updated["resume_when"] == "pr_merged:owner/repo#42"
+ persisted = resolve_monitor_todo_item(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ todo_id=updated["todo_id"],
+ )
+ assert persisted["resume_when"] == "pr_merged:owner/repo#42"
assert monitor_todo_is_actionable_open(
{
@@ -212,19 +246,20 @@ def test_todo_add_cli_materializes_cadence_only_monitor_due_time(
runtime_root=runtime,
)
- assert added["cadence"] == "30m"
- assert added["next_due_at"]
persisted = resolve_monitor_todo_item(
registry_path=registry,
goal_id=GOAL_ID,
todo_id=added["todo_id"],
)
- assert persisted["next_due_at"] == added["next_due_at"]
+ assert persisted["cadence"] == "30m"
+ assert persisted["next_due_at"]
assert persisted["target_key"] == "public-dependency:cadence-only"
-def test_runtime_writeback_can_read_legacy_unbounded_monitor(tmp_path: Path) -> None:
- registry, _runtime, state = _write_fixture(tmp_path)
+def test_runtime_writeback_rejects_unpromoted_legacy_monitor(
+ tmp_path: Path,
+) -> None:
+ registry, runtime, state = _write_fixture(tmp_path, canonical=False)
state.write_text(
state.read_text(encoding="utf-8")
+ "\n- [ ] [P2] Legacy public monitor.\n"
@@ -233,20 +268,21 @@ def test_runtime_writeback_can_read_legacy_unbounded_monitor(tmp_path: Path) ->
+ "target_key=legacy-public cadence=1h -->\n",
encoding="utf-8",
)
+ before = state.read_bytes()
- result = write_monitor_poll_todo_state(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- generated_at="2026-08-11T00:00:00Z",
- execute=False,
- todo_id="todo_legacy_monitor",
- result_hash="unchanged",
- agent_id=AGENT_ID,
- )
+ with pytest.raises(CanonicalAuthorityRequired):
+ write_monitor_poll_todo_state(
+ registry_path=registry,
+ runtime_root=runtime,
+ goal_id=GOAL_ID,
+ generated_at="2026-08-11T00:00:00Z",
+ execute=False,
+ todo_id="todo_legacy_monitor",
+ result_hash="unchanged",
+ agent_id=AGENT_ID,
+ )
- assert result is not None
- assert result["todo_update"]["changed"] is True
+ assert state.read_bytes() == before
def test_material_poll_reloads_status_and_projects_declared_successor(
@@ -1017,8 +1053,8 @@ def test_material_poll_user_action_does_not_block_existing_advancement(
reminder = result["todo_writeback"]["next_todos"][0]
assert reminder["task_class"] == "user_action"
assert reminder["bound_agent"] == AGENT_ID
- assert reminder["blocks_agent"] is None
- assert reminder["unblocks_todo_id"] is None
+ assert reminder.get("blocks_agent") is None
+ assert reminder.get("unblocks_todo_id") is None
assert result["after"]["selected_todo"]["todo_id"] == advancement["todo_id"]
diff --git a/tests/control_plane/test_monitor_observation_admission.py b/tests/control_plane/test_monitor_observation_admission.py
index 79648c207d..9654170bd7 100644
--- a/tests/control_plane/test_monitor_observation_admission.py
+++ b/tests/control_plane/test_monitor_observation_admission.py
@@ -13,6 +13,7 @@
TODO_ID,
_append_newly_due_monitor,
_classification_count,
+ _initialize_fixture_authority,
_projected_cli_args,
_run_cli,
_spend_run_count,
@@ -44,6 +45,7 @@ def test_settled_advancement_hold_preserves_independent_monitor(
rc, first = _run_cli(registry, runtime, *guard)
assert rc == 0, first
_append_newly_due_monitor(project, watch_only=True)
+ _initialize_fixture_authority(project, runtime)
rc, admitted = _run_cli(registry, runtime, *guard)
assert rc == 0, admitted
poll_command = admitted["interaction_contract"]["cli_channel"][
@@ -114,6 +116,7 @@ def test_receipt_bound_advancement_allows_one_auxiliary_due_monitor_receipt(
assert first["heartbeat_receipt"]["settlement_identity"]["todo_id"] == TODO_ID
_append_newly_due_monitor(project, watch_only=True)
+ _initialize_fixture_authority(project, runtime)
capability_args = (
"--available-capability",
"network",
@@ -203,11 +206,9 @@ def test_receipt_bound_advancement_allows_one_auxiliary_due_monitor_receipt(
@pytest.mark.parametrize(
("provider", "writeback", "spend_first"),
[
- ("legacy", False, False),
- ("legacy", True, False),
+ ("file", False, False),
("file", True, False),
("sqlite", True, False),
- ("legacy", True, True),
("file", True, True),
("sqlite", True, True),
],
@@ -220,13 +221,7 @@ def test_completed_advancement_retains_auxiliary_monitor_admission(
spend_first: bool,
) -> None:
tmp_path = settlement_budget_root
- from canonical_authority_fixture import (
- initialize_canonical_authority,
- isolate_sqlite_runtime,
- )
- from loopx.control_plane.coordination.runtime_shadow import (
- build_todo_runtime_shadow_projection,
- )
+ from canonical_authority_fixture import isolate_sqlite_runtime
from loopx.control_plane.coordination.local_authority import (
read_canonical_todos_if_promoted,
)
@@ -261,6 +256,32 @@ def test_completed_advancement_retains_auxiliary_monitor_admission(
rc, first = _run_cli(registry_path, runtime, *guard)
assert rc == 0, first
_append_newly_due_monitor(project, watch_only=True)
+ leases = [
+ {
+ "schema_version": "task_lease_v0",
+ "goal_id": GOAL_ID,
+ "todo_id": todo_id,
+ "owner": AGENT_ID,
+ "write_scopes": [],
+ "status": "active",
+ "idempotency_key": idempotency_key,
+ "version": 3,
+ "lease_epoch": 2,
+ "acquired_at": "2026-01-01T00:00:00Z",
+ "expires_at": "2099-01-01T00:00:00Z",
+ }
+ for todo_id, idempotency_key in (
+ (TODO_ID, "primary-execution"),
+ (DUE_MONITOR_TODO_ID, "own-monitor-execution"),
+ )
+ ]
+ _initialize_fixture_authority(
+ project,
+ runtime,
+ provider=provider,
+ handoff_mode="hard_lease",
+ leases=leases,
+ )
rc, open_guard = _run_cli(registry_path, runtime, *guard)
assert rc == 0, open_guard
auxiliary = open_guard["interaction_contract"]["cli_channel"][
@@ -288,41 +309,18 @@ def test_completed_advancement_retains_auxiliary_monitor_admission(
"--evidence",
"read-only fixture delivery validated",
"--no-follow-up",
+ "--task-lease-idempotency-key",
+ "primary-execution",
+ "--task-lease-expected-version",
+ "3",
)
assert rc == 0, complete
- if provider != "legacy":
- rc, listed = _run_cli(
- registry_path, runtime, "todo", "list", "--goal-id", GOAL_ID
- )
- assert rc == 0, listed
- primary = next(todo for todo in listed["todos"] if todo["todo_id"] == TODO_ID)
- assert primary["status"] == "done"
- lease = {
- "schema_version": "task_lease_v0",
- "goal_id": GOAL_ID,
- "todo_id": DUE_MONITOR_TODO_ID,
- "owner": AGENT_ID,
- "write_scopes": [],
- "status": "active",
- "idempotency_key": "own-monitor-execution",
- "version": 3,
- "lease_epoch": 2,
- "acquired_at": "2026-01-01T00:00:00Z",
- "expires_at": "2099-01-01T00:00:00Z",
- }
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID,
- todos=listed["todos"],
- handoff_mode="hard_lease",
- leases=[lease],
- )
- initialize_canonical_authority(
- runtime,
- GOAL_ID,
- projection,
- state_path=project / f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md",
- provider=provider,
- )
+ rc, listed = _run_cli(
+ registry_path, runtime, "todo", "list", "--goal-id", GOAL_ID
+ )
+ assert rc == 0, listed
+ primary = next(todo for todo in listed["todos"] if todo["todo_id"] == TODO_ID)
+ assert primary["status"] == "done"
if writeback:
rc, refresh = _run_cli(
registry_path,
@@ -358,21 +356,20 @@ def test_completed_advancement_retains_auxiliary_monitor_admission(
assert poll["settlement_todo_id"] == TODO_ID
assert poll["turn_continuation"]["current_turn_settled"] is spend_first
assert poll["turn_continuation"]["next_turn_required"] is spend_first
- if provider != "legacy":
- canonical = read_canonical_todos_if_promoted(
- runtime_root=runtime, goal_id=GOAL_ID, include_leases=True
- )
- assert canonical["source_authority"] == f"{provider}_v0"
- observed = next(
- todo
- for todo in canonical["todos"]
- if todo["todo_id"] == DUE_MONITOR_TODO_ID
- )
- assert observed["result_hash"] == "completed-primary-observation"
- assert poll["todo_writeback"]["lease_proof"] == {
- "idempotency_key": "own-monitor-execution",
- "expected_version": 3,
- }
+ canonical = read_canonical_todos_if_promoted(
+ runtime_root=runtime, goal_id=GOAL_ID, include_leases=True
+ )
+ assert canonical["source_authority"] == f"{provider}_v0"
+ observed = next(
+ todo
+ for todo in canonical["todos"]
+ if todo["todo_id"] == DUE_MONITOR_TODO_ID
+ )
+ assert observed["result_hash"] == "completed-primary-observation"
+ assert poll["todo_writeback"]["lease_proof"] == {
+ "idempotency_key": "own-monitor-execution",
+ "expected_version": 3,
+ }
resume = poll["settlement_resume"]
assert resume["identity"]["todo_id"] == TODO_ID
assert resume["identity"]["turn_instance_id"] == turn
@@ -402,24 +399,23 @@ def test_completed_advancement_retains_auxiliary_monitor_admission(
assert replay["replayed"] is True
assert _classification_count(runtime, "quota_monitor_poll") == 1
assert _spend_run_count(runtime) == int(spend_first)
- if provider != "legacy":
- rc, released = _run_cli(
- registry_path,
- runtime,
- "task-lease",
- "release",
- "--goal-id",
- GOAL_ID,
- "--todo-id",
- DUE_MONITOR_TODO_ID,
- "--owner",
- AGENT_ID,
- "--idempotency-key",
- "own-monitor-execution",
- "--expected-version",
- "3",
- )
- assert rc == 0, released
+ rc, released = _run_cli(
+ registry_path,
+ runtime,
+ "task-lease",
+ "release",
+ "--goal-id",
+ GOAL_ID,
+ "--todo-id",
+ DUE_MONITOR_TODO_ID,
+ "--owner",
+ AGENT_ID,
+ "--idempotency-key",
+ "own-monitor-execution",
+ "--expected-version",
+ "3",
+ )
+ assert rc == 0, released
if spend_first:
assert resume["next_step"] is None
assert "settlement_owed" not in poll
diff --git a/tests/control_plane/test_monitor_poll_transaction_recovery.py b/tests/control_plane/test_monitor_poll_transaction_recovery.py
index 2e0e7b2542..8931387ac0 100644
--- a/tests/control_plane/test_monitor_poll_transaction_recovery.py
+++ b/tests/control_plane/test_monitor_poll_transaction_recovery.py
@@ -1,671 +1,471 @@
-"""Legacy Monitor poll recovery at the public CLI boundary."""
+"""Original Monitor receipts recover without admitting fresh source writes."""
from __future__ import annotations
-import json
import base64
-import os
+import hashlib
+import json
import re
-import subprocess
-import sys
-from contextlib import nullcontext
from pathlib import Path
import pytest
-from loopx.control_plane.testing.canary_harness import (
- read_run_index,
- write_fixture_registry,
-)
-from loopx.control_plane.coordination.legacy_writer_fence import (
- legacy_coordination_writer_fence_path,
-)
+from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
from loopx.control_plane.coordination.local_authority import (
+ CanonicalAuthorityRequired,
+ LocalCoordinationAuthorityUnavailable,
read_canonical_todos_if_promoted,
)
+from loopx.control_plane.coordination.local_authority_shadow_projection import (
+ canonical_bytes,
+)
from loopx.control_plane.coordination.runtime_shadow import (
build_todo_runtime_shadow_projection,
)
-from loopx.control_plane.coordination.local_authority_shadow_adapter import (
- read_local_authority_shadow,
-)
-from loopx.control_plane.effect_runtime import EffectRuntimeRejected, effect_runtime_result
-from loopx.file_lock import exclusive_cross_runtime_file_lock
-from loopx.control_plane.coordination.local_authority_shadow_outbox import drain_lock_target
-from loopx.todos import add_goal_todo, list_goal_todos
-
-from canonical_authority_fixture import (
- initialize_canonical_authority,
- isolate_sqlite_runtime,
+from loopx.control_plane.coordination.local_authority_shadow_history import outbox_root
+from loopx.control_plane.scheduler.monitor_poll_writeback import (
+ write_monitor_poll_todo_state,
)
+from loopx.control_plane.testing.canary_harness import read_run_index, write_fixture_registry
+from monitor_poll_canonical_fixture import _cli, _poll_events
GOAL = "synthetic-monitor-recovery"
AGENT = "codex-quality-qualification"
-MONITOR_TARGET = "synthetic-target"
+OPERATION = "historical-monitor-effect"
+MONITOR_ID = "todo_historical_monitor"
+AGENT_SUCCESSOR = "todo_historical_agent"
+USER_SUCCESSOR = "todo_historical_user"
AGENT_TASK = "Validate observed synthetic head"
USER_TASK = "Review observed synthetic head"
-REPO = Path(__file__).resolve().parents[2]
-
-# A fresh CLI process dies at the one state-file install boundary. The process
-# exits without Python cleanup, as a real interrupted writer would.
-_FAULT_WRAPPER = """
-import os, runpy, sys
-original_replace = os.replace
-def interrupted_replace(source, target, *args, **kwargs):
- if str(target).endswith('/ACTIVE_GOAL_STATE.md'):
- if os.environ['MONITOR_FAULT_PHASE'] == 'before':
- os._exit(83)
- original_replace(source, target, *args, **kwargs)
- os._exit(83)
- return original_replace(source, target, *args, **kwargs)
-os.replace = interrupted_replace
-sys.argv = ['loopx.cli', *sys.argv[1:]]
-runpy.run_module('loopx.cli', run_name='__main__')
-"""
-_QUOTA_COMMIT_FAULT_WRAPPER = """
-import os, runpy, sys
-import loopx.control_plane.quota.monitor_poll as monitor
-original = monitor._native_result
-def interrupted_commit(request):
- if request.get('phase') == 'commit' and request.get('provider_receipt'):
- os._exit(73)
- return original(request)
-monitor._native_result = interrupted_commit
-sys.argv = ['loopx.cli', *sys.argv[1:]]
-runpy.run_module('loopx.cli', run_name='__main__')
-"""
-
-
-def _cli(
- registry: Path, runtime: Path, *args: str, fault: str | None = None,
-) -> tuple[int, dict | None]:
- if fault == "quota_commit":
- launcher = [sys.executable, "-c", _QUOTA_COMMIT_FAULT_WRAPPER]
- elif fault:
- launcher = [sys.executable, "-c", _FAULT_WRAPPER]
- else:
- launcher = [sys.executable, "-m", "loopx.cli"]
- result = subprocess.run(
- [*launcher, "--registry", str(registry), "--format", "json",
- "--runtime-root", str(runtime), *args],
- cwd=REPO,
- env={**os.environ, "LOOPX_USAGE_PING": "0",
- "MONITOR_FAULT_PHASE": fault or ""},
- capture_output=True, text=True, check=False, timeout=60,
- )
- return result.returncode, json.loads(result.stdout) if result.stdout.strip() else None
-def _ok(registry: Path, runtime: Path, *args: str) -> dict:
- code, result = _cli(registry, runtime, *args)
- assert code == 0, result
- assert isinstance(result, dict)
- return result
-
-
-def _fixture(tmp_path: Path) -> tuple[Path, Path, Path, str, tuple[str, ...]]:
- project, runtime = tmp_path / "project", tmp_path / "runtime"
+def _fixture(tmp_path: Path) -> tuple[Path, Path, Path]:
+ project = tmp_path / "project"
+ runtime = tmp_path / "runtime"
state = project / ".codex" / "goals" / GOAL / "ACTIVE_GOAL_STATE.md"
registry = project / ".loopx" / "registry.json"
state.parent.mkdir(parents=True)
- state.write_text("---\nstatus: active\n---\n\n# Active Goal State\n\n## Agent Todo\n",
- encoding="utf-8")
+ state.write_text(
+ "---\nstatus: active\n---\n\n# Active Goal State\n\n## Agent Todo\n",
+ encoding="utf-8",
+ )
write_fixture_registry(
- project=project, runtime_root=runtime, registry_path=registry,
- goal_id=GOAL, domain="loopx-platform",
- adapter_kind="harness_self_improvement", registered_agents=[AGENT],
+ project=project,
+ runtime_root=runtime,
+ registry_path=registry,
+ goal_id=GOAL,
+ domain="loopx-platform",
+ adapter_kind="harness_self_improvement",
+ registered_agents=[AGENT],
quota_allowed_slots=None,
)
- monitor = add_goal_todo(
- registry_path=registry, goal_id=GOAL, role="agent",
- text="Observe public synthetic target", task_class="continuous_monitor",
- claimed_by=AGENT,
- monitor_metadata={"target_key": MONITOR_TARGET, "cadence": "1h",
- "next_due_at": "2000-01-01T00:00:00+00:00",
- "watch_only": "true"},
+ return registry, runtime, state
+
+
+def _request() -> tuple[dict, dict]:
+ observation = {
+ "todo_id": MONITOR_ID,
+ "target_key": "synthetic-target",
+ "result_hash": "synthetic-head-1",
+ "material_change": True,
+ "generated_at": "2026-09-01T00:00:00Z",
+ "cadence": None,
+ "next_due_at": None,
+ "reason_summary": None,
+ }
+ intent = {
+ "next_agent_todo": AGENT_TASK,
+ "next_action_kind": "validate",
+ "next_task_repository": None,
+ "next_required_capabilities": [],
+ "next_continuation_policy": None,
+ "next_target_key": None,
+ "next_claimed_by": None,
+ "next_user_todo": USER_TASK,
+ "next_user_task_class": "user_action",
+ }
+ return observation, intent
+
+
+def _historical_receipt() -> dict:
+ observation, intent = _request()
+ request_sha256 = hashlib.sha256(
+ canonical_bytes(
+ {
+ "goal_id": GOAL,
+ "observation": observation,
+ "intent": intent,
+ "actor_agent_id": AGENT,
+ "dry_run": False,
+ }
+ )
+ ).hexdigest()
+ next_todos = [
+ {"todo_id": AGENT_SUCCESSOR, "role": "agent", "todo": AGENT_TASK},
+ {"todo_id": USER_SUCCESSOR, "role": "user", "todo": USER_TASK},
+ ]
+ successor_receipts = [
+ {"todo_id": AGENT_SUCCESSOR, "role": "agent"},
+ {"todo_id": USER_SUCCESSOR, "role": "user"},
+ ]
+ return {
+ "schema_version": "loopx_monitor_batch_receipt_v0",
+ "goal_id": GOAL,
+ "operation_id": OPERATION,
+ "request_sha256": request_sha256,
+ "writeback": {
+ "schema_version": "monitor_poll_todo_writeback_v0",
+ "dry_run": False,
+ "goal_id": GOAL,
+ "todo_id": MONITOR_ID,
+ "monitor_effect_id": OPERATION,
+ "result_hash": observation["result_hash"],
+ "material_change": True,
+ "last_checked_at": observation["generated_at"],
+ "todo_update": {"ok": True},
+ "next_todos": next_todos,
+ "successor_receipts": successor_receipts,
+ "provider_replayed": False,
+ },
+ }
+
+
+def _seed_receipt(state: Path, receipt: object) -> None:
+ encoded = base64.b64encode(
+ json.dumps(receipt, separators=(",", ":")).encode("utf-8")
+ ).decode("ascii")
+ digest = hashlib.sha256(OPERATION.encode("utf-8")).hexdigest()
+ with state.open("a", encoding="utf-8") as handle:
+ handle.write(f"\n\n")
+
+
+def _promote(runtime: Path, state: Path, provider: str = "file") -> None:
+ projection = build_todo_runtime_shadow_projection(
+ goal_id=GOAL,
+ todos=[],
+ handoff_mode="soft_claim",
)
- turn = "synthetic-monitor-turn-1"
- _ok(registry, runtime, "quota", "should-run", "--goal-id", GOAL,
- "--agent-id", AGENT, "--runtime-profile", "generic_cli",
- "--turn-instance-id", turn)
- poll = (
- "quota", "monitor-poll", "--goal-id", GOAL, "--agent-id", AGENT,
- "--runtime-profile", "generic_cli", "--turn-instance-id", turn,
- "--todo-id", monitor["todo_id"], "--target-key", MONITOR_TARGET,
- "--result-hash", "synthetic-head-1", "--material-change",
- "--next-agent-todo", AGENT_TASK, "--next-action-kind", "validate",
- "--next-user-todo", USER_TASK, "--next-user-task-class", "user_action",
- "--execute",
+ initialize_canonical_authority(
+ runtime,
+ GOAL,
+ projection,
+ state_path=state,
+ provider=provider,
)
- return registry, runtime, state, monitor["todo_id"], poll
-def _successors(registry: Path, runtime: Path) -> dict[str, dict]:
- todos = _ok(registry, runtime, "todo", "list", "--goal-id", GOAL)["todos"]
- return {todo["title"]: todo for todo in todos
- if todo["title"] in (AGENT_TASK, USER_TASK)}
+def _replay(registry: Path, runtime: Path, **overrides: object) -> dict:
+ observation, intent = _request()
+ arguments = {
+ "registry_path": registry,
+ "runtime_root": runtime,
+ "goal_id": GOAL,
+ "generated_at": observation["generated_at"],
+ "execute": True,
+ "monitor_effect_id": OPERATION,
+ "todo_id": observation["todo_id"],
+ "target_key": observation["target_key"],
+ "result_hash": observation["result_hash"],
+ "material_change": True,
+ "cadence": None,
+ "next_due_at": None,
+ "reason_summary": None,
+ "next_agent_todo": intent["next_agent_todo"],
+ "next_action_kind": intent["next_action_kind"],
+ "next_task_repository": None,
+ "next_required_capabilities": [],
+ "next_continuation_policy": None,
+ "next_target_key": None,
+ "next_claimed_by": None,
+ "next_user_todo": intent["next_user_todo"],
+ "next_user_task_class": intent["next_user_task_class"],
+ "agent_id": AGENT,
+ }
+ arguments.update(overrides)
+ result = write_monitor_poll_todo_state(**arguments)
+ assert isinstance(result, dict)
+ return result
-def _poll_events(runtime: Path) -> list[dict]:
- return [row for row in read_run_index(runtime, GOAL)
- if row.get("classification") == "quota_monitor_poll"]
+@pytest.mark.parametrize("provider", ["unpromoted", "file", "sqlite"])
+def test_original_receipt_replays_before_fresh_admission_without_effects(
+ tmp_path: Path, monkeypatch, provider: str,
+) -> None:
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, runtime, state = _fixture(tmp_path)
+ _seed_receipt(state, _historical_receipt())
+ if provider != "unpromoted":
+ from loopx.todos import add_goal_todo
+
+ _promote(runtime, state, provider)
+ add_goal_todo(registry_path=registry, goal_id=GOAL, role="agent",
+ text="Preserve work accepted after import", operation_id="later-canonical-work")
+ outbox = outbox_root(runtime, GOAL) / "unreconciled-original.json"
+ outbox.parent.mkdir(parents=True, exist_ok=True)
+ outbox.write_bytes(b'{"historical":"requires explicit reconciliation"}\n')
+ outbox_before = outbox.read_bytes()
+ state_before = state.read_bytes()
+ provider_before = read_canonical_todos_if_promoted(
+ runtime_root=runtime,
+ goal_id=GOAL,
+ )
+ replayed = _replay(registry, runtime)
+
+ assert replayed["provider_replayed"] is True
+ assert [item["todo_id"] for item in replayed["next_todos"]] == [
+ AGENT_SUCCESSOR,
+ USER_SUCCESSOR,
+ ]
+ assert state.read_bytes() == state_before
+ assert outbox.read_bytes() == outbox_before
+ assert read_canonical_todos_if_promoted(
+ runtime_root=runtime,
+ goal_id=GOAL,
+ ) == provider_before
+ if provider == "unpromoted":
+ assert not (runtime / "authority").exists()
+
+
+@pytest.mark.parametrize("promoted", [False, True])
+@pytest.mark.parametrize("change", [
+ {"next_user_todo": "Request a different review"},
+ {"agent_id": "another-agent"},
+ {"result_hash": "a-different-observation"},
+ {"next_agent_priority": "P0"},
+ {"next_user_priority": "P3"},
+])
+def test_historical_receipt_rejects_changed_identity(tmp_path: Path, promoted, change) -> None:
+ registry, runtime, state = _fixture(tmp_path)
+ _seed_receipt(state, _historical_receipt())
+ if promoted:
+ _promote(runtime, state)
+ state_before = state.read_bytes()
+ provider_before = read_canonical_todos_if_promoted(
+ runtime_root=runtime,
+ goal_id=GOAL,
+ )
-def _quota_receipt(runtime: Path) -> Path:
- directory = (runtime / "goals" / GOAL / "runs" / ".transactions" /
- "quota-monitor-poll")
- (receipt,) = directory.glob("*.json")
- return receipt
+ with pytest.raises(ValueError, match="request mismatch"):
+ _replay(registry, runtime, **change)
+ assert state.read_bytes() == state_before
+ assert read_canonical_todos_if_promoted(
+ runtime_root=runtime,
+ goal_id=GOAL,
+ ) == provider_before
-def _business_receipt(state: Path) -> dict:
- match = re.search(r"",
- state.read_text(encoding="utf-8"))
- assert match is not None
- return json.loads(base64.b64decode(match[1]))
+@pytest.mark.parametrize("promoted", [False, True])
+@pytest.mark.parametrize("damage", ["not_object", "duplicate", "truncated"])
+def test_malformed_historical_receipt_is_not_treated_as_absent(
+ tmp_path: Path, promoted, damage,
+) -> None:
+ registry, runtime, state = _fixture(tmp_path)
+ _seed_receipt(state, None if damage == "not_object" else _historical_receipt())
+ if damage == "duplicate":
+ _seed_receipt(state, _historical_receipt())
+ elif damage == "truncated":
+ state.write_text(state.read_text().replace(" -->", " --"))
+ if promoted:
+ _promote(runtime, state)
+ state_before = state.read_bytes()
-def _engage_fence(runtime: Path) -> None:
- fence = legacy_coordination_writer_fence_path(runtime_root=runtime, goal_id=GOAL)
- fence.parent.mkdir(parents=True, exist_ok=True)
- fence.write_text(json.dumps({
- "schema_version": "loopx_legacy_coordination_writer_fence_v0",
- "state": "engaged", "goal_id": GOAL, "fence_id": "synthetic-fence",
- "source_version": "synthetic-source",
- "source_projection_sha256": "a" * 64,
- "expected_shadow_provider_revision": "file:1:aaaaaaaaaaaaaaaaaaaaaaaa",
- }), encoding="utf-8")
+ with pytest.raises(ValueError, match="receipt must be an object|duplicate|malformed"):
+ _replay(registry, runtime)
+ assert state.read_bytes() == state_before
-@pytest.mark.parametrize("owner", [AGENT, "registered-builder", "unknown-builder"])
-def test_public_monitor_successor_claim_checks_registered_peer_before_writing(
- tmp_path: Path, owner: str,
+
+def test_unpromoted_goal_without_original_receipt_cannot_start_monitor_work(
+ tmp_path: Path,
) -> None:
- registry, runtime, state, _, poll = _fixture(tmp_path)
- data = json.loads(registry.read_text(encoding="utf-8"))
- data["goals"][0]["coordination"]["registered_agents"].append("registered-builder")
- registry.write_text(json.dumps(data), encoding="utf-8")
- before_state = state.read_bytes()
- before_index = read_run_index(runtime, GOAL)
- transaction_dir = runtime / "goals" / GOAL / "runs" / ".transactions" / "quota-monitor-poll"
- before_receipts = {path.name: path.read_bytes() for path in transaction_dir.glob("*.json")}
- code, response = _cli(registry, runtime, *poll[:-1], "--next-claimed-by", owner, "--execute")
- assert response is not None
- if owner == "unknown-builder":
- assert code != 0
- assert "is not registered" in response["reason"]
- assert "quota_unexpected_collection_error" not in json.dumps(response)
- assert state.read_bytes() == before_state
- assert read_run_index(runtime, GOAL) == before_index
- assert {path.name: path.read_bytes() for path in transaction_dir.glob("*.json")} == before_receipts
- assert _poll_events(runtime) == []
- else:
- assert code == 0, json.dumps(response, indent=2)
- successor = response["todo_writeback"]["next_todos"][0]
- assert successor["claimed_by"] == owner
- assert _successors(registry, runtime)[AGENT_TASK]["claimed_by"] == owner
- assert len(_poll_events(runtime)) == 1
- if owner == "registered-builder":
- saved_state = state.read_bytes()
- saved_events = read_run_index(runtime, GOAL)
- data["goals"][0]["coordination"]["registered_agents"].remove(owner)
- registry.write_text(json.dumps(data), encoding="utf-8")
- replay_code, replay = _cli(registry, runtime, *poll[:-1], "--next-claimed-by", owner, "--execute")
- assert replay_code == 0, replay
- assert state.read_bytes() == saved_state
- assert read_run_index(runtime, GOAL) == saved_events
-
-
-def test_monitor_batch_handler_classifies_expected_input_errors() -> None:
- request = {
- "schema_version": "loopx_monitor_batch_plan_request_v0", "legacy_batch_version": 1,
- "goal_id": "synthetic-monitor", "operation_id": "synthetic-effect",
- "actor_agent_id": "observer", "registered_agents": ["observer"],
- "dry_run": False, "observation": {
- "todo_id": "todo_watch", "target_key": "watch", "generated_at": "2026-09-01T00:00:00Z",
- "result_hash": "head", "material_change": True,
- },
- "intent": {"next_agent_todo": "Validate head", "next_action_kind": "validate",
- "next_claimed_by": "unknown-builder"},
- "todos": [{"todo_id": "todo_watch", "role": "agent", "status": "open",
- "done": False, "archive_state": "active", "text": "Observe head",
- "task_class": "continuous_monitor", "target_key": "watch", "cadence": "1h"}],
- }
- with pytest.raises(EffectRuntimeRejected, match="claim owner is not registered") as rejected:
- effect_runtime_result("scheduler.monitor_batch.plan", request)
- assert rejected.value.error_kind == "request_rejected"
- assert rejected.value.diagnostic_code == "invalid_request"
+ registry, runtime, state = _fixture(tmp_path)
+ state_before = state.read_bytes()
+ with pytest.raises(CanonicalAuthorityRequired):
+ _replay(registry, runtime)
-@pytest.mark.parametrize("fault", ["before", "after"])
-def test_interrupted_legacy_poll_retries_one_complete_batch(
- tmp_path: Path, fault: str,
-) -> None:
- registry, runtime, state, monitor_id, poll = _fixture(tmp_path)
- before = state.read_bytes()
-
- code, result = _cli(registry, runtime, *poll, fault=fault)
- assert (code, result) == (83, None)
- if fault == "before":
- assert state.read_bytes() == before
- assert _successors(registry, runtime) == {}
- else:
- assert set(_successors(registry, runtime)) == {AGENT_TASK, USER_TASK}
- assert _poll_events(runtime) == []
+ assert state.read_bytes() == state_before
+ assert not (runtime / "authority").exists()
- recovered = _ok(registry, runtime, *poll)
- successors = _successors(registry, runtime)
- assert set(successors) == {AGENT_TASK, USER_TASK}
- assert all(item["unblocks_todo_id"] == monitor_id for item in successors.values()
- if item["role"] == "agent")
- ids = {item["todo_id"] for item in successors.values()}
- assert set(recovered["successor_todo_ids"]) == ids
- assert len(_poll_events(runtime)) == 1
-
- state_after, index_after = state.read_bytes(), read_run_index(runtime, GOAL)
- replay = _ok(registry, runtime, *poll)
- assert set(replay["successor_todo_ids"]) == ids
- assert state.read_bytes() == state_after
- assert read_run_index(runtime, GOAL) == index_after
-
-
-def test_identity_less_legacy_pending_cannot_guess_successors(tmp_path: Path) -> None:
- registry, runtime, state, _monitor_id, poll = _fixture(tmp_path)
- before = state.read_bytes()
- code, result = _cli(registry, runtime, *poll, fault="before")
- assert (code, result) == (83, None)
- pending_path = _quota_receipt(runtime)
- pending = json.loads(pending_path.read_text(encoding="utf-8"))
- assert pending["status"] == "provider_pending"
- # This represents an already persisted older pending admission. Its plan
- # did not freeze the complete legacy batch identity before any business
- # write, so recovery must preserve it for reconciliation.
- pending["provider_plan"].pop("legacy_batch_version", None)
- pending_path.write_text(json.dumps(pending), encoding="utf-8")
-
- code, rejected = _cli(registry, runtime, *poll)
- assert code != 0, rejected
- assert "reconciliation" in str(rejected.get("reason")), rejected
- assert state.read_bytes() == before
- assert _successors(registry, runtime) == {}
- assert _poll_events(runtime) == []
- assert _quota_receipt(runtime).exists()
-
-
-def test_malformed_business_receipt_is_not_treated_as_absent(tmp_path: Path) -> None:
- registry, runtime, state, _monitor_id, poll = _fixture(tmp_path)
- assert _cli(registry, runtime, *poll, fault="after") == (83, None)
- # A present JSON null receipt is corrupt history, not an unstarted effect.
- text = re.sub(r"(",
- r"\g<1>bnVsbA== -->", state.read_text(encoding="utf-8"))
- state.write_text(text, encoding="utf-8")
- code, rejected = _cli(registry, runtime, *poll)
- assert code != 0 and "receipt must be an object" in rejected["reason"]
- assert state.read_text(encoding="utf-8") == text
- assert _poll_events(runtime) == []
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_historical_receipt_cannot_mask_selected_provider_loss(tmp_path, monkeypatch, provider):
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, runtime, state = _fixture(tmp_path)
+ _seed_receipt(state, _historical_receipt())
+ _promote(runtime, state, provider)
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL)
+ backend = runtime / "authority" / before["source_authority"].replace("_", "-")
+ offline = backend.with_name(backend.name + "-offline")
+ display = state.read_bytes()
+ backend.rename(offline)
+ try:
+ with pytest.raises(LocalCoordinationAuthorityUnavailable):
+ _replay(registry, runtime)
+ assert not backend.exists() and state.read_bytes() == display
+ finally:
+ offline.rename(backend)
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL) == before
+
+
+def _restore_1x_poll(tmp_path: Path):
+ # Fixed output of the ordinary CLI at 19c725e3f, interrupted after its
+ # Markdown business write and before quota settlement. Only filesystem
+ # roots are rebound. The quota fingerprint includes that physical root,
+ # so re-encode its frozen input with the original Python JSON contract;
+ # the current transaction implementation is not the expected-value oracle.
+ frozen = Path(__file__).parent / "fixtures" / "retained_monitor_receipts_1x.json"
+ history = json.loads(frozen.read_text().replace("$FIXTURE_ROOT", str(tmp_path)))
+ registry, runtime, state = _fixture(tmp_path)
+ receipt = history["business_receipt"]
+ digest = hashlib.sha256(receipt["operation_id"].encode()).hexdigest()
+ encoded = base64.b64encode(json.dumps(receipt).encode()).decode()
+ marker = f"\n"
+ state.write_text(history["source_state"] + "\n" + marker)
+ runs = runtime / "goals" / GOAL / "runs"
+ pending = runs / ".transactions" / "quota-monitor-poll" / f"{digest[:24]}.json"
+ pending.parent.mkdir(parents=True)
+ history["pending"]["request_digest"] = "sha256:" + hashlib.sha256(
+ json.dumps(history["request_fingerprint_input"], sort_keys=True, ensure_ascii=False).encode()
+ ).hexdigest()
+ pending.write_text(json.dumps(history["pending"]))
+ (runs.parent / "rollout-event-log.jsonl").write_text(json.dumps(history["heartbeat"]) + "\n")
+ return registry, runtime, state, pending, history["poll"], marker
+
+
+def test_original_1x_cli_poll_settles_once_then_replays_without_new_business_work(tmp_path):
+ registry, runtime, state, pending, poll, _marker = _restore_1x_poll(tmp_path)
+ display, reservation = state.read_bytes(), pending.read_bytes()
+ changed = list(poll)
+ changed[changed.index("--result-hash") + 1] = "changed-original-observation"
+ rc, rejected = _cli(registry, runtime, *changed)
+ assert rc != 0 and rejected["error_code"] == "heartbeat_receipt_identity_conflict", rejected
+ assert state.read_bytes() == display and pending.read_bytes() == reservation
+ assert _poll_events(runtime) == []
-@pytest.mark.parametrize("held_drain_lock", [False, True], ids=["uncontended", "deferred"])
-def test_enabled_shadow_business_replay_then_bounded_drain_recovers_batch(
- tmp_path: Path, held_drain_lock: bool,
-) -> None:
- registry, runtime, state, _monitor_id, poll = _fixture(tmp_path)
- config = json.loads(registry.read_text(encoding="utf-8"))
- config["goals"][0]["coordination"]["runtime_shadow"] = {
- "enabled": True,
- "schema_version": "loopx_coordination_runtime_shadow_config_v0",
- "provider": "file_v0",
- }
- registry.write_text(json.dumps(config), encoding="utf-8")
- _ok(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", GOAL,
- "--execute")
-
- assert _cli(registry, runtime, *poll, fault="after") == (83, None)
- outbox = runtime / "authority-shadow" / "outbox" / GOAL / "todos"
- (prepared,) = outbox.glob("*.prepared.json")
- prepared_bytes = prepared.read_bytes()
- assert list(outbox.glob("*.committed.json")) == []
- assert "coordination_runtime_shadow" not in _business_receipt(state)["writeback"]
-
- before = read_local_authority_shadow(runtime_root=runtime, goal_id=GOAL,
- scan_limit=20)
- assert {todo["text"] for todo in before["head"]["todos"]} == {
- "[P1] Observe public synthetic target"}
- state_after = state.read_bytes()
- original_ids = {
- todo["todo_id"] for todo in _business_receipt(state)["writeback"]["next_todos"]}
- drain = ("authority-shadow", "drain", "--goal-id", GOAL,
- "--max-entries", "16", "--budget-seconds", "2",
- "--lock-timeout-seconds", "0.25")
- lock = (exclusive_cross_runtime_file_lock(
- drain_lock_target(runtime, GOAL), timeout_seconds=1, operation="test_drain_deferral")
- if held_drain_lock else nullcontext())
- with lock:
- retry = _ok(registry, runtime, *poll)
- assert state.read_bytes() == state_after
- if held_drain_lock:
- # Business replay succeeds even when independent catch-up must defer.
- code, deferred = _cli(registry, runtime, *drain)
- assert code == 1 and isinstance(deferred, dict), deferred
- assert deferred["outcome"] == "drain_deferred"
- assert deferred["reason_code"] == "drain_lock_busy"
- assert deferred["prepared_only_after"] == 1
- assert prepared.read_bytes() == prepared_bytes
- assert read_local_authority_shadow(
- runtime_root=runtime, goal_id=GOAL, scan_limit=20,
- )["proof"]["transactions"] == before["proof"]["transactions"]
- assert {todo["todo"] for todo in retry["todo_writeback"]["next_todos"]} == {
- f"[P1] {AGENT_TASK}", f"[P1] {USER_TASK}"}
- assert set(retry["successor_todo_ids"]) == original_ids
- assert state.read_bytes() == state_after
- receipt_after = _quota_receipt(runtime).read_bytes()
- events_after = _poll_events(runtime)
- assert len(events_after) == 1
- # A bounded inline attempt is not a catch-up guarantee. Use the existing
- # independent drain with its original limits, without replaying the writer.
- recovered = _ok(registry, runtime, *drain)
- assert recovered["pending_after"] == recovered["prepared_only_after"] == 0
- assert state.read_bytes() == state_after
- view = read_local_authority_shadow(runtime_root=runtime, goal_id=GOAL,
- scan_limit=20)
- assert {todo["text"] for todo in view["head"]["todos"]} == {
- "[P1] Observe public synthetic target", f"[P1] {AGENT_TASK}", f"[P1] {USER_TASK}"}
- assert len(view["proof"]["transactions"]) == 2 # Baseline and original batch.
- assert list(outbox.glob("*.prepared.json")) == []
- late_retry = _ok(registry, runtime, *poll)
- assert late_retry["replayed"] is True
- assert set(late_retry["successor_todo_ids"]) == original_ids
- assert state.read_bytes() == state_after
- assert _quota_receipt(runtime).read_bytes() == receipt_after
- assert _poll_events(runtime) == events_after
- assert read_local_authority_shadow(
- runtime_root=runtime, goal_id=GOAL, scan_limit=20,
- )["proof"]["transactions"] == view["proof"]["transactions"]
-
-
-def test_committed_receipt_without_batch_version_still_replays(tmp_path: Path) -> None:
- registry, runtime, state, _monitor_id, poll = _fixture(tmp_path)
- committed = _ok(registry, runtime, *poll)
- receipt = json.loads(_quota_receipt(runtime).read_text(encoding="utf-8"))
- assert receipt["status"] != "provider_pending"
- assert "legacy_batch_version" not in receipt
- assert "provider_plan" not in receipt
- state_after = state.read_bytes()
- index_after = read_run_index(runtime, GOAL)
-
- replay = _ok(registry, runtime, *poll)
- assert replay["replayed"] is True
- assert replay["successor_todo_ids"] == committed["successor_todo_ids"]
- original_writeback = dict(committed["todo_writeback"])
- repeated_writeback = dict(_business_receipt(state)["writeback"])
- for payload in (original_writeback, repeated_writeback):
- payload.pop("provider_replayed", None)
- payload.pop("monitor_effect_id", None)
- # One batch capture is reported at the original call. Its post-commit
- # drain receipt cannot be in the atomically installed business receipt.
- original_writeback.pop("coordination_runtime_shadow", None)
- for result in [original_writeback["todo_update"], *original_writeback["next_todos"]]:
- result.pop("coordination_runtime_shadow", None)
- assert repeated_writeback == original_writeback
- assert state.read_bytes() == state_after
- assert read_run_index(runtime, GOAL) == index_after
-
-
-@pytest.mark.parametrize("dry_run", [True, False])
-@pytest.mark.parametrize("user_class", ["user_action", "user_gate"])
-def test_public_monitor_writeback_retains_todo_result_contract(
- tmp_path: Path, dry_run: bool, user_class: str,
-) -> None:
- registry, runtime, state, monitor_id, poll = _fixture(tmp_path)
- before = state.read_bytes()
- args = list(poll)
- args[args.index("user_action")] = user_class
- if dry_run:
- args.remove("--execute")
- writeback = _ok(registry, runtime, *args)["todo_writeback"]
- update = writeback["todo_update"]
- assert update["dry_run"] is dry_run
- assert update["changed"] is True
- assert update["mutation_authority"]["actor_agent_id"] == AGENT
- assert update["agent_id"] == AGENT
- assert update["handoff_mode"] == "legacy"
- assert update["role"] == "agent" and update["section"] == "Agent Todo"
- assert update["status_changed"] is False
- assert update["text_changed"] is False
- assert update["metadata_updated"] is True
- assert update["required_capabilities"] == []
- assert update["state_file"] == str(state)
- assert update["project"] == str(state.parents[3])
- assert update["updated_at"] == writeback["last_checked_at"]
- assert len(writeback["next_todos"]) == 2
- agent, user = writeback["next_todos"]
- for item in (agent, user):
- assert item["ok"] is True and item["dry_run"] is dry_run
- assert item["added"] is True and item["already_exists"] is False
- assert item["metadata_updated"] is False
- assert item["status_changed"] is False
- assert item["goal_id"] == GOAL
- assert item["required_write_scopes"] == []
- assert item["required_capabilities"] == []
- assert item["excluded_agents"] == []
- assert item["state_file"] == str(state)
- assert item["project"] == str(state.parents[3])
- assert item["handoff_mode"] == "legacy"
- assert item["coordination_runtime_shadow"] == update["coordination_runtime_shadow"]
- assert agent["unblocks_todo_id"] == monitor_id
- assert agent["agent_id"] is None
- assert user["agent_id"] == AGENT
- assert user["task_class"] == user_class
- assert user["action_kind"] == ("gate" if user_class == "user_gate" else None)
- assert user["global_gate"] is None and user["goal_bound"] is None
- if user_class == "user_gate":
- assert user["blocks_agent"] == AGENT
- assert user["unblocks_todo_id"] == monitor_id
+ rc, recovered = _cli(registry, runtime, *poll)
+ assert rc == 0 and recovered["appended"] is True, recovered
+ # The index is the commit proof; current quota may retain a prepared WAL.
+ assert state.read_bytes() == display
+ assert not (runtime / "authority").exists()
+ events = _poll_events(runtime)
+ assert len(events) == 1
+ assert events[0]["todo_id"] == "todo_7f667db6fd16"
+ assert events[0]["agent_id"] == AGENT
+ committed = pending.read_bytes()
+ rc, replay = _cli(registry, runtime, *poll)
+ assert rc == 0 and replay["replayed"] is True, replay
+ assert state.read_bytes() == display and pending.read_bytes() == committed
+ assert _poll_events(runtime) == events
+ assert not any(row.get("classification") == "quota_slot_spent" for row in read_run_index(runtime, GOAL))
+
+
+@pytest.mark.parametrize("execute", [False, True])
+def test_fresh_unpromoted_monitor_preview_and_execute_refuse_without_effects(tmp_path, execute):
+ registry, runtime, state, pending, poll, marker = _restore_1x_poll(tmp_path)
+ pending.unlink()
+ state.write_text(re.sub(r"next_due_at=\S+", "next_due_at=2000-01-01T00%3A00%3A00Z",
+ state.read_text().replace(marker, "")))
+ display = state.read_bytes()
+ args = poll if execute else [arg for arg in poll if arg != "--execute"]
+ rc, refused = _cli(registry, runtime, *args)
+ assert rc != 0 and refused["error_code"] == "canonical_authority_required", refused
+ assert state.read_bytes() == display and not pending.exists()
+ assert read_run_index(runtime, GOAL) == []
+ assert not (runtime / "authority").exists()
+
+
+@pytest.mark.parametrize("damage", ["missing_quota", "missing_business", "duplicate_business", "corrupt_generation", "corrupt_quota"])
+def test_original_1x_cli_refuses_incomplete_or_corrupt_history_without_fresh_work(tmp_path, damage):
+ registry, runtime, state, pending, poll, marker = _restore_1x_poll(tmp_path)
+ if damage == "missing_quota":
+ pending.unlink()
+ elif damage == "missing_business":
+ state.write_text(state.read_text().replace(marker, ""))
+ elif damage == "duplicate_business":
+ state.write_text(state.read_text() + marker)
+ elif damage == "corrupt_generation":
+ encoded = marker.split(" ")[2]
+ receipt = json.loads(base64.b64decode(encoded))
+ receipt["writeback"]["material_change_generation"] = True
+ state.write_text(state.read_text().replace(encoded, base64.b64encode(json.dumps(receipt).encode()).decode()))
else:
- assert user["blocks_agent"] is None
- assert user["unblocks_todo_id"] is None
- if dry_run:
- packet = update["local_state_write_correctness"]
- assert packet["write_intent"]["target_refs"]["todo_ids"] == [
- monitor_id, agent["todo_id"], user["todo_id"]]
- assert packet["write_intent"]["expected_revision"]["kind"] == "active_state_revision"
- assert packet["apply_result"]["status"] == "preview_only"
- assert agent["local_state_write_correctness"] == packet
- assert user["local_state_write_correctness"] == packet
- assert state.read_bytes() == before
-
-
-@pytest.mark.parametrize("conflict", [False, True])
-def test_existing_successor_semantics_are_checked_before_batch_write(
- tmp_path: Path, conflict: bool,
-) -> None:
- registry, runtime, state, monitor_id, poll = _fixture(tmp_path)
- preview = _ok(registry, runtime, *poll[:-1])["todo_writeback"]
- route = preview["next_todos"][0]
- existing = add_goal_todo(
- registry_path=registry, goal_id=GOAL, role="agent", text=AGENT_TASK,
- task_class="advancement_task",
- action_kind="implement" if conflict else "validate",
- continuation_policy=route["continuation_policy"],
- unblocks_todo_id=monitor_id,
- monitor_metadata={"target_key": route["target_key"]},
- )
- before = state.read_bytes()
- if conflict:
- code, rejected = _cli(registry, runtime, *poll)
- assert code != 0 and "different action_kind" in rejected["reason"]
- assert state.read_bytes() == before
- assert _poll_events(runtime) == []
- else:
- writeback = _ok(registry, runtime, *poll)["todo_writeback"]
- agent = writeback["next_todos"][0]
- assert agent["todo_id"] == existing["todo_id"]
- assert agent["added"] is False and agent["already_exists"] is True
- assert agent["metadata_updated"] is False
- assert len(_poll_events(runtime)) == 1
-
-
-def test_pending_legacy_receipt_replays_after_canonical_cutover(tmp_path: Path) -> None:
- registry, runtime, state, _monitor_id, poll = _fixture(tmp_path)
- code, result = _cli(registry, runtime, *poll, fault="after")
- assert (code, result) == (83, None)
- original_ids = {todo["todo_id"] for todo in _successors(registry, runtime).values()}
- state_after = state.read_bytes()
- assert _poll_events(runtime) == []
- pending_path = _quota_receipt(runtime)
- pending = json.loads(pending_path.read_text(encoding="utf-8"))
- pending["provider_plan"].pop("legacy_batch_version", None)
- pending_path.write_text(json.dumps(pending), encoding="utf-8")
- items = list_goal_todos(registry_path=registry, goal_id=GOAL)["todos"]
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL, todos=items, handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, GOAL, projection, state_path=state)
- before_provider = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL)
-
- replay = _ok(registry, runtime, *poll)
- assert set(replay["successor_todo_ids"]) == original_ids
- assert state.read_bytes() == state_after
- assert len(_poll_events(runtime)) == 1
- assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL) == before_provider
- index_after = read_run_index(runtime, GOAL)
- repeated = _ok(registry, runtime, *poll)
- assert repeated["replayed"] is True
- assert set(repeated["successor_todo_ids"]) == original_ids
- assert state.read_bytes() == state_after
- assert read_run_index(runtime, GOAL) == index_after
- assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL) == before_provider
+ pending.write_text("{invalid receipt")
+ display = state.read_bytes()
+ reservations = {p.name: p.read_bytes() for p in pending.parent.glob("*.json")}
+ rc, refused = _cli(registry, runtime, *poll)
+ assert rc != 0, refused
+ if damage in {"missing_quota", "missing_business"}:
+ assert refused["error_code"] == "canonical_authority_required", refused
+ assert state.read_bytes() == display
+ assert {p.name: p.read_bytes() for p in pending.parent.glob("*.json")} == reservations
+ assert read_run_index(runtime, GOAL) == []
+ assert not (runtime / "authority").exists()
@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_historical_canonical_pending_plan_settles_existing_business_receipt(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
-) -> None:
+def test_original_1x_cli_refuses_unavailable_selected_provider(tmp_path, monkeypatch, provider):
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state, _monitor_id, poll = _fixture(tmp_path)
- items = list_goal_todos(registry_path=registry, goal_id=GOAL)["todos"]
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL, todos=items, handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, GOAL, projection,
- state_path=state, provider=provider)
- _ok(registry, runtime, "quota", "should-run", "--goal-id", GOAL,
- "--agent-id", AGENT, "--runtime-profile", "generic_cli",
- "--turn-instance-id", "synthetic-monitor-turn-1")
-
- code, result = _cli(registry, runtime, *poll, fault="quota_commit")
- assert (code, result) == (73, None)
- committed = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL)
- successor_ids = {todo["todo_id"] for todo in committed["todos"]
- if todo["title"] in (AGENT_TASK, USER_TASK)}
- assert len(successor_ids) == 2
- assert _poll_events(runtime) == []
- pending_path = _quota_receipt(runtime)
- pending = json.loads(pending_path.read_text(encoding="utf-8"))
- assert pending["status"] == "provider_pending"
- # Historical canonical plans predate the legacy batch version. The
- # already committed native provider receipt remains the recovery source.
- pending["provider_plan"].pop("legacy_batch_version", None)
- pending_path.write_text(json.dumps(pending), encoding="utf-8")
-
- settled = _ok(registry, runtime, *poll)
- assert set(settled["successor_todo_ids"]) == successor_ids
- assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL) == committed
- assert len(_poll_events(runtime)) == 1
- index_after = read_run_index(runtime, GOAL)
- repeated = _ok(registry, runtime, *poll)
- assert repeated["replayed"] is True
- assert set(repeated["successor_todo_ids"]) == successor_ids
- assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL) == committed
- assert read_run_index(runtime, GOAL) == index_after
-
-
-def test_legacy_writer_fence_rejects_full_monitor_batch(tmp_path: Path) -> None:
- registry, runtime, state, _monitor_id, poll = _fixture(tmp_path)
- before = state.read_bytes()
- _engage_fence(runtime)
-
- code, rejected = _cli(registry, runtime, *poll)
- assert code != 0, rejected
- assert state.read_bytes() == before
- assert _poll_events(runtime) == []
+ registry, runtime, state, pending, poll, _marker = _restore_1x_poll(tmp_path)
+ _promote(runtime, state, provider)
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL)
+ backend = runtime / "authority" / before["source_authority"].replace("_", "-")
+ offline = backend.with_name(backend.name + "-offline")
+ display, reservation = state.read_bytes(), pending.read_bytes()
+ backend.rename(offline)
+ try:
+ rc, refused = _cli(registry, runtime, *poll)
+ assert rc != 0 and refused["legacy_fallback_used"] is False, refused
+ assert not backend.exists() and state.read_bytes() == display
+ assert pending.read_bytes() == reservation and _poll_events(runtime) == []
+ finally:
+ offline.rename(backend)
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL) == before
-def test_atomic_successor_reconciles_existing_typed_next_action(tmp_path: Path) -> None:
- registry, runtime, state, _monitor_id, poll = _fixture(tmp_path)
- lower = _ok(registry, runtime, "todo", "add", "--goal-id", GOAL,
- "--role", "agent", "--text", "[P1] Existing lower priority task",
- "--task-class", "advancement_task", "--action-kind", "validate")
- with state.open("a", encoding="utf-8") as handle:
- handle.write("\n## Next Action\n\n- [P1] Existing lower priority task\n"
- "\n")
- command = list(poll)
- command[command.index(AGENT_TASK)] = "[P0] Validate urgent observed result"
- code, result = _cli(registry, runtime, *command, fault="after")
- assert (code, result) == (83, None)
- successor = next(todo for todo in _ok(registry, runtime, "todo", "list",
- "--goal-id", GOAL)["todos"] if todo.get("unblocks_todo_id") == _monitor_id)
- expected = ("")
- assert expected in state.read_text(encoding="utf-8")
- committed = state.read_bytes()
- retry = _ok(registry, runtime, *command)
- assert successor["todo_id"] in retry["successor_todo_ids"]
- assert state.read_bytes() == committed
-
-
-@pytest.mark.parametrize("change", ["rename", "complete", "archive"])
-def test_pending_retry_keeps_original_successor_identity_after_public_edit(
- tmp_path: Path, change: str,
-) -> None:
- registry, runtime, state, _monitor_id, poll = _fixture(tmp_path)
- code, result = _cli(registry, runtime, *poll, fault="after")
- assert (code, result) == (83, None)
- first = _successors(registry, runtime)
- assert set(first) == {AGENT_TASK, USER_TASK}
- original_ids = {item["todo_id"] for item in first.values()}
- agent_id = first[AGENT_TASK]["todo_id"]
-
- if change == "rename":
- _ok(registry, runtime, "todo", "update", "--goal-id", GOAL,
- "--todo-id", agent_id, "--role", "agent", "--agent-id", AGENT,
- "--text", "Validate observed synthetic head with revised scope")
- else:
- _ok(registry, runtime, "todo", "complete", "--goal-id", GOAL,
- "--todo-id", agent_id, "--role", "agent", "--agent-id", AGENT,
- "--no-follow-up", "--evidence", "Synthetic validation complete")
- if change == "archive":
- _ok(registry, runtime, "todo", "archive-completed", "--goal-id", GOAL,
- "--role", "agent", "--max-active-done", "0", "--execute")
- archived = _ok(registry, runtime, "todo", "list", "--goal-id", GOAL,
- "--role", "agent", "--todo-id", agent_id)
- assert archived["todo"]["archive_state"] == "archive"
- edited_state = state.read_bytes()
- assert len(_poll_events(runtime)) == 0
-
- retry = _ok(registry, runtime, *poll)
- writeback = retry["todo_writeback"]
- assert writeback["todo_update"]["todo"] == "[P1] Observe public synthetic target"
- assert [todo["todo"] for todo in writeback["next_todos"]] == [f"[P1] {AGENT_TASK}", f"[P1] {USER_TASK}"]
- assert all(todo["added"] is True for todo in writeback["next_todos"])
- assert set(retry["successor_todo_ids"]) == original_ids
- assert state.read_bytes() == edited_state
- assert len(_poll_events(runtime)) == 1
- index_after = read_run_index(runtime, GOAL)
-
- replay = _ok(registry, runtime, *poll)
- receipt = _business_receipt(state)
- assert receipt["writeback"]["next_todos"] == [
- {key: value for key, value in todo.items()
- if key != "coordination_runtime_shadow"}
- for todo in writeback["next_todos"]]
- assert set(replay["successor_todo_ids"]) == original_ids
- assert state.read_bytes() == edited_state
- assert read_run_index(runtime, GOAL) == index_after
-
- changed_intent = list(poll)
- changed_intent[changed_intent.index(USER_TASK)] = "Request a different review"
- code, conflict = _cli(registry, runtime, *changed_intent)
- assert code != 0, conflict
- assert state.read_bytes() == edited_state
- assert read_run_index(runtime, GOAL) == index_after
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+@pytest.mark.parametrize("gate_scope_guard", [False, True], ids=["v2", "v3"])
+def test_canonical_original_monitor_replays_with_unreadable_display_but_never_creates_work(
+ tmp_path, monkeypatch, provider, gate_scope_guard,
+):
+ from loopx.control_plane.scheduler import provider_monitor_poll
+
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, runtime, state = _fixture(tmp_path)
+ initialize_canonical_authority(runtime, GOAL, build_todo_runtime_shadow_projection(
+ goal_id=GOAL, handoff_mode="soft_claim", todos=[{
+ "schema_version": "todo_item_v0", "todo_id": MONITOR_ID, "role": "agent",
+ "text": "Observe the public source", "status": "open", "done": False,
+ "archive_state": "active", "source_section": "Agent Todo",
+ "task_class": "continuous_monitor", "claimed_by": AGENT,
+ "target_key": "synthetic-target", "cadence": "1h",
+ }]), state_path=state, provider=provider)
+ calls = []
+ native = provider_monitor_poll.effect_runtime_result
+
+ def record(method, params, **kwargs):
+ calls.append(params["schema_version"])
+ return native(method, params, **kwargs)
+
+ monkeypatch.setattr(provider_monitor_poll, "effect_runtime_result", record)
+ first = _replay(registry, runtime, gate_scope_guard=gate_scope_guard)
+ assert first["provider_replayed"] is False
+ assert calls == ["loopx_coordination_monitor_poll_request_v3" if gate_scope_guard
+ else "loopx_coordination_monitor_poll_request_v2"]
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL)
+ state.write_bytes(b"\xffunreadable display")
+ display = state.read_bytes()
+ recovered = _replay(registry, runtime, gate_scope_guard=gate_scope_guard)
+ assert recovered["provider_replayed"] is True
+ assert calls[-1] == "loopx_coordination_monitor_poll_request_v4"
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL) == before
+ assert state.read_bytes() == display
+ with pytest.raises(UnicodeError):
+ _replay(registry, runtime, monitor_effect_id="new-work-is-not-recovery", gate_scope_guard=gate_scope_guard)
+ with pytest.raises(ValueError):
+ _replay(registry, runtime, next_user_todo="Changed original intent", gate_scope_guard=gate_scope_guard)
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id=GOAL) == before
+ assert state.read_bytes() == display
diff --git a/tests/control_plane/test_monitor_poll_unavailable_recovery.py b/tests/control_plane/test_monitor_poll_unavailable_recovery.py
index 64aab772fe..6c60e2c1ec 100644
--- a/tests/control_plane/test_monitor_poll_unavailable_recovery.py
+++ b/tests/control_plane/test_monitor_poll_unavailable_recovery.py
@@ -7,12 +7,19 @@
from pathlib import Path
import pytest
-from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
+from canonical_authority_fixture import isolate_sqlite_runtime
from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
-from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
from loopx.control_plane.testing.canary_harness import read_run_index
-from loopx.todos import list_goal_todos
-from test_monitor_poll_transaction_recovery import _fixture, _cli, _poll_events, _quota_receipt, GOAL, AGENT, REPO
+from loopx.todos import update_goal_todo
+from monitor_poll_canonical_fixture import (
+ AGENT,
+ GOAL,
+ REPO,
+ _cli,
+ _fixture,
+ _poll_events,
+ _quota_receipt,
+)
_ACTOR_POLL_WRAPPER = """
import os, runpy, sys
@@ -48,17 +55,22 @@ def test_pending_poll_cannot_be_closed_as_unavailable(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str, material: bool, actor: str | None,
) -> None:
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state, monitor, poll = _fixture(tmp_path)
- todos = list_goal_todos(registry_path=registry, goal_id=GOAL)['todos']
+ registry, runtime, _state, monitor, poll = _fixture(
+ tmp_path,
+ provider=provider,
+ )
if actor is not None:
data = json.loads(registry.read_text())
data['goals'][0]['coordination']['registered_agents'].append(actor)
registry.write_text(json.dumps(data))
- for todo in todos:
- if todo['todo_id'] == monitor:
- todo['claimed_by'] = None
- initialize_canonical_authority(runtime, GOAL, build_todo_runtime_shadow_projection(
- goal_id=GOAL, todos=todos, handoff_mode='soft_claim'), state_path=state, provider=provider)
+ update_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL,
+ todo_id=monitor,
+ clear_claim=True,
+ agent_id=AGENT,
+ update_operation_id="synthetic-monitor-clear-claim",
+ )
if not material:
args = list(poll)
args.remove('--material-change')
diff --git a/tests/control_plane/test_monitor_quiet_due_recovery.py b/tests/control_plane/test_monitor_quiet_due_recovery.py
index 47bfbcc192..54b0419410 100644
--- a/tests/control_plane/test_monitor_quiet_due_recovery.py
+++ b/tests/control_plane/test_monitor_quiet_due_recovery.py
@@ -12,24 +12,19 @@
import pytest
-from canonical_authority_fixture import (
- initialize_canonical_authority,
- isolate_sqlite_runtime,
-)
+from canonical_authority_fixture import isolate_sqlite_runtime
from tests.control_plane.test_monitor_followthrough_contract import (
AGENT_ID,
GOAL_ID,
_add_monitor,
_write_fixture,
)
-from loopx.control_plane.coordination.runtime_shadow import (
- build_todo_runtime_shadow_projection,
-)
+from tests.control_plane.test_native_monitor_poll import _canonical
from loopx.control_plane.testing.canary_harness import run_json_cli, run_json_cli_result
-from loopx.todos import add_goal_todo, list_goal_todos
+from loopx.todos import add_goal_todo
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("material", [False, True], ids=["unchanged", "material"])
@pytest.mark.parametrize("peer_gate", [False, True], ids=["no-gate", "peer-gate"])
def test_quiet_turn_binds_due_monitor_and_recovers_lost_response(
@@ -40,7 +35,7 @@ def test_quiet_turn_binds_due_monitor_and_recovers_lost_response(
peer_gate,
):
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state = _write_fixture(tmp_path)
+ registry, runtime, state = _write_fixture(tmp_path, provider=provider)
add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -54,15 +49,6 @@ def test_quiet_turn_binds_due_monitor_and_recovers_lost_response(
monitor = _add_monitor(
registry, text="Observe a public target", target_key="public-target"
)
- if provider != "legacy":
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID,
- todos=list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"],
- handoff_mode="soft_claim",
- )
- initialize_canonical_authority(
- runtime, GOAL_ID, projection, state_path=state, provider=provider
- )
def call(*args):
return run_json_cli(*args, registry_path=registry, runtime_root=runtime)
@@ -234,7 +220,7 @@ def todos():
@pytest.mark.parametrize(("provider", "handoff_mode"), [
- ("legacy", "soft_claim"), ("file", "soft_claim"), ("sqlite", "soft_claim"),
+ ("file", "soft_claim"), ("sqlite", "soft_claim"),
("file", "hard_lease"), ("sqlite", "hard_lease"),
])
def test_blocked_bound_monitor_requires_verified_lifecycle_repair(
@@ -245,25 +231,17 @@ def test_blocked_bound_monitor_requires_verified_lifecycle_repair(
):
"""A blocker changes execution eligibility, never the committed binding."""
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state = _write_fixture(tmp_path)
+ registry, runtime, state = _write_fixture(
+ tmp_path,
+ provider=provider,
+ handoff_mode=handoff_mode,
+ )
monitor = _add_monitor(
registry,
text="Observe a public target",
target_key="public-target",
next_due_at="2000-01-01T00:00:00Z",
)
- if provider != "legacy":
- initialize_canonical_authority(
- runtime,
- GOAL_ID,
- build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID,
- todos=list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"],
- handoff_mode=handoff_mode,
- ),
- state_path=state,
- provider=provider,
- )
def call(*args):
return run_json_cli(*args, registry_path=registry, runtime_root=runtime)
@@ -372,20 +350,18 @@ def test_projected_restore_rejects_an_active_execution_lease(tmp_path, monkeypat
from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state = _write_fixture(tmp_path)
- monitor = _add_monitor(registry, text="Observe a public target", target_key="public-target")
- run_json_cli("todo", "update", "--goal-id", GOAL_ID, "--agent-id", AGENT_ID,
- "--todo-id", monitor["todo_id"], "--status", "blocked", registry_path=registry,
- runtime_root=runtime)
- lease = {"schema_version": "task_lease_v0", "goal_id": GOAL_ID,
- "todo_id": monitor["todo_id"], "owner": AGENT_ID, "idempotency_key": "active-monitor",
- "status": "active", "expires_at": "2099-01-01T00:00:00Z", "version": 1,
- "lease_epoch": 1, "write_scopes": [], "acquire_ttl_seconds": 900}
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, todos=list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"],
- handoff_mode="hard_lease", leases=[lease],
+ registry, runtime, state, monitor = _canonical(
+ tmp_path,
+ provider=provider,
+ monitor_status="blocked",
+ lease={
+ "status": "active",
+ "idempotency_key": "active-monitor",
+ "version": 1,
+ "lease_epoch": 1,
+ "expires_at": "2099-01-01T00:00:00Z",
+ },
)
- initialize_canonical_authority(runtime, GOAL_ID, projection, state_path=state, provider=provider)
actions = recovery_cli_actions(
{"unsettled_host_turn_recovery": {"repair": "lifecycle", "scope": "current_turn",
"binding_id": monitor["todo_id"], "turn_instance_id": "held-monitor-turn"}},
diff --git a/tests/control_plane/test_monitor_receipt_writer_isolation.py b/tests/control_plane/test_monitor_receipt_writer_isolation.py
deleted file mode 100644
index 425a06c8d1..0000000000
--- a/tests/control_plane/test_monitor_receipt_writer_isolation.py
+++ /dev/null
@@ -1,117 +0,0 @@
-"""Retained Monitor receipts survive retirement of their original writer."""
-from __future__ import annotations
-
-import json
-import subprocess
-import sys
-
-import pytest
-
-from canonical_authority_fixture import initialize_canonical_authority
-from test_canonical_todo_writer_isolation import (
- isolated_todo_distribution, retirement_wheel, # noqa: F401 -- shared distribution fixtures
- _remove_monitor_source_modules,
-)
-from test_monitor_poll_transaction_recovery import (
- AGENT, GOAL, _business_receipt, _cli, _fixture, _poll_events, _quota_receipt,
-)
-
-from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
-from loopx.control_plane.coordination.local_authority_shadow_outbox import outbox_root
-from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
-from loopx.todos import list_goal_todos
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-@pytest.mark.parametrize("outbox_present", [False, True])
-@pytest.mark.parametrize("writer_present", [False, True], ids=["absent", "present"])
-def test_imported_monitor_receipt_settles_without_legacy_writer(
- tmp_path, provider, outbox_present, writer_present, isolated_todo_distribution, # noqa: F811
-):
- package, command = isolated_todo_distribution
- registry, runtime, state, _monitor, poll = _fixture(tmp_path)
- code, response = _cli(registry, runtime, *poll, fault="after")
- assert (code, response) == (83, None)
- original = _business_receipt(state)["writeback"]
- successor_ids = {item["todo_id"] for item in original["next_todos"]}
- pending_path = _quota_receipt(runtime)
- pending = json.loads(pending_path.read_text())
- pending["provider_plan"].pop("legacy_batch_version", None)
- pending_path.write_text(json.dumps(pending))
- items = list_goal_todos(registry_path=registry, goal_id=GOAL)["todos"]
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL, todos=items, handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, GOAL, projection,
- state_path=state, provider=provider)
-
- def cli(*args):
- result = subprocess.run(
- [*command, "--registry", str(registry), "--runtime-root", str(runtime),
- "--format", "json", *args], cwd=tmp_path,
- capture_output=True, text=True, timeout=60,
- )
- assert result.returncode == 0, result.stdout + result.stderr
- payload = json.loads(result.stdout)
- assert payload["ok"], payload
- return payload
-
- if not writer_present:
- _remove_monitor_source_modules(package)
- # New work after cutover must survive recovery of the old operation.
- later = cli("todo", "add", "--goal-id", GOAL, "--role", "agent",
- "--text", "Later canonical work", "--claimed-by", AGENT,
- "--operation-id", "later-canonical-work")
- root = outbox_root(runtime, GOAL)
- if outbox_present:
- root.mkdir(parents=True, exist_ok=True)
- # Deliberately ambiguous old evidence must remain inert after cutover.
- (root / "retained.json").write_text('{"ambiguous_original":true}')
-
- def facts():
- return (state.read_bytes(), read_canonical_todos_if_promoted(
- runtime_root=runtime, goal_id=GOAL),
- {str(p.relative_to(root)): p.read_bytes() for p in root.rglob("*") if p.is_file()})
-
- before = facts()
- assert later["todo_id"] in {t["todo_id"] for t in before[1]["todos"]}
- settled = cli(*poll)
- assert set(settled["successor_todo_ids"]) == successor_ids
- assert facts() == before
- assert len(_poll_events(runtime)) == 1
- events = _poll_events(runtime)
- assert cli(*poll)["replayed"] is True
- assert _poll_events(runtime) == events and facts() == before
-
- # Cold provider replay must remain independent of the same source modules.
- request = tmp_path / "request.json"
- request.write_text(json.dumps({"plan": pending["provider_plan"],
- "registry": str(registry), "runtime": str(runtime)}))
- child = """
-import json, sys
-from pathlib import Path
-from loopx.control_plane.quota.monitor_poll import _provider_writeback
-request = json.loads(Path(sys.argv[1]).read_text())
-result = _provider_writeback(request['plan'], registry_path=Path(request['registry']),
- runtime_root=Path(request['runtime']))
-for changed in ({'next_agent_todo': 'Changed historical intent'},
- {'agent_id': 'different-observer'}, {'result_hash': 'different-evidence'}):
- try:
- _provider_writeback({**request['plan'], **changed},
- registry_path=Path(request['registry']),
- runtime_root=Path(request['runtime']))
- except ValueError as error:
- assert 'request mismatch' in str(error), str(error)
- else:
- raise AssertionError('different original-operation intent must be rejected')
-assert 'loopx.control_plane.scheduler.legacy_monitor_poll' not in sys.modules
-assert 'loopx.control_plane.coordination.local_authority_shadow_adapter' not in sys.modules
-print(json.dumps(result))
-"""
- result = subprocess.run([sys.executable, "-c", child, str(request)], cwd=tmp_path,
- capture_output=True, text=True, timeout=60)
- assert result.returncode == 0, result.stdout + result.stderr
- replay = json.loads(result.stdout)
- assert replay["provider_replayed"] is True
- assert {k: v for k, v in replay.items() if k != "provider_replayed"} == {
- k: v for k, v in original.items() if k != "provider_replayed"}
- assert facts() == before
diff --git a/tests/control_plane/test_monitor_state_owner.py b/tests/control_plane/test_monitor_state_owner.py
index 386a32c2c4..cd06e0f6c6 100644
--- a/tests/control_plane/test_monitor_state_owner.py
+++ b/tests/control_plane/test_monitor_state_owner.py
@@ -65,7 +65,7 @@ def test_monitor_effect_replay_and_generation_are_locked_public_writer_semantics
before = state.read_bytes()
replay = _poll(registry, todo["todo_id"], "2030-01-01T02:00:00Z", "new",
monitor_effect_id="effect-one")
- assert replay["monitor_poll_transition"]["provider_replayed"] is True
+ assert replay["status"] == "replayed"
assert state.read_bytes() == before
with pytest.raises(ValueError, match="different observation"):
_poll(registry, todo["todo_id"], "2030-01-01T02:00:00Z", "other",
diff --git a/tests/control_plane/test_monitor_successor_validation.py b/tests/control_plane/test_monitor_successor_validation.py
index ee70a9329d..beeead5da4 100644
--- a/tests/control_plane/test_monitor_successor_validation.py
+++ b/tests/control_plane/test_monitor_successor_validation.py
@@ -46,7 +46,7 @@ def test_public_monitor_poll_accepts_repository_and_action_aliases(tmp_path):
assert successor["unblocks_todo_id"] == monitor["todo_id"]
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("explicit", [False, True])
def test_monitor_priority_cli_and_readback(tmp_path, monkeypatch, provider, explicit):
from canonical_authority_fixture import isolate_sqlite_runtime
@@ -54,12 +54,7 @@ def test_monitor_priority_cli_and_readback(tmp_path, monkeypatch, provider, expl
from loopx.todos import update_goal_todo, list_goal_todos
isolate_sqlite_runtime(tmp_path, monkeypatch)
- if provider == "legacy":
- registry, runtime, _ = _write_fixture(tmp_path)
- monitor = _add_monitor(registry, text="Watch ordinary changes", target_key="public-target",
- next_due_at="2000-01-01T00:00:00Z")
- else:
- registry, runtime, _, monitor = _canonical(tmp_path, provider=provider)
+ registry, runtime, _, monitor = _canonical(tmp_path, provider=provider)
update_goal_todo(registry_path=registry, runtime_root_arg=str(runtime), goal_id=GOAL_ID,
todo_id=monitor["todo_id"], agent_id=AGENT_ID, priority="P0")
flags = ["--next-agent-priority", "P2", "--next-user-priority", "P3"] if explicit else []
@@ -79,17 +74,13 @@ def test_monitor_priority_cli_and_readback(tmp_path, monkeypatch, provider, expl
assert next(row for row in current if row["todo_id"] == created["todo_id"])["priority"] == priority
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_monitor_priority_retry_keeps_original_intent(tmp_path, monkeypatch, provider):
from canonical_authority_fixture import isolate_sqlite_runtime
from test_native_monitor_poll import _canonical
isolate_sqlite_runtime(tmp_path, monkeypatch)
- if provider == "legacy":
- registry, runtime, _ = _write_fixture(tmp_path)
- monitor = _add_monitor(registry, text="Watch changes", target_key="public-target")
- else:
- registry, runtime, _, monitor = _canonical(tmp_path, provider=provider)
+ registry, runtime, _, monitor = _canonical(tmp_path, provider=provider)
args = dict(registry_path=registry, runtime_root=runtime, goal_id=GOAL_ID,
todo_id=monitor["todo_id"], agent_id=AGENT_ID, execute=True,
generated_at="2026-09-09T12:00:00Z", result_hash="priority-a", material_change=True,
diff --git a/tests/control_plane/test_monitor_target_key_replay.py b/tests/control_plane/test_monitor_target_key_replay.py
index 2335d07c14..29fe588fcc 100644
--- a/tests/control_plane/test_monitor_target_key_replay.py
+++ b/tests/control_plane/test_monitor_target_key_replay.py
@@ -9,19 +9,26 @@
import pytest
-from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
+from canonical_authority_fixture import isolate_sqlite_runtime
from loopx.cli import main as cli_main
from loopx.control_plane.coordination.local_authority import LocalCoordinationAuthorityUnavailable
from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
-from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
from loopx.control_plane.scheduler.monitor_todo import monitor_todo_is_due
from loopx.control_plane.scheduler.monitor_poll_writeback import write_monitor_poll_todo_state
from loopx.control_plane.scheduler.provider_monitor_poll import poll_canonical_monitor_if_promoted
from loopx.control_plane.quota import monitor_poll as monitor_runtime
from loopx.control_plane.quota.accounting_admission import quota_accounting_admission
from loopx.control_plane.testing.canary_harness import read_run_index
-from loopx.todos import add_goal_todo, list_goal_todos, update_goal_todo
-from test_monitor_poll_transaction_recovery import AGENT, GOAL, MONITOR_TARGET, REPO, _cli, _fixture, _poll_events
+from loopx.todos import add_goal_todo, update_goal_todo
+from monitor_poll_canonical_fixture import (
+ AGENT,
+ GOAL,
+ MONITOR_TARGET,
+ REPO,
+ _cli,
+ _fixture,
+ _poll_events,
+)
_NULL_TODO_WRAPPER = """
import hashlib, os, runpy, sys
@@ -88,14 +95,15 @@ def test_target_only_monitor_poll_replays_accepted_identity(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str, completion: str, material: bool,
) -> None:
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state, monitor, poll = _fixture(tmp_path)
+ registry, runtime, _state, monitor, poll = _fixture(
+ tmp_path,
+ provider=provider,
+ )
other = add_goal_todo(registry_path=registry, goal_id=GOAL, role='agent',
text='Observe a different public synthetic target', task_class='continuous_monitor', claimed_by=AGENT,
monitor_metadata={'target_key': 'synthetic-other-target', 'cadence': '1h',
- 'next_due_at': '2000-01-01T00:00:00+00:00', 'watch_only': 'true'})
- todos = list_goal_todos(registry_path=registry, goal_id=GOAL)['todos']
- initialize_canonical_authority(runtime, GOAL, build_todo_runtime_shadow_projection(
- goal_id=GOAL, todos=todos, handoff_mode='soft_claim'), state_path=state, provider=provider)
+ 'next_due_at': '2000-01-01T00:00:00+00:00', 'watch_only': 'true'},
+ operation_id='synthetic-other-monitor')
target_poll = _target_poll(poll, material=material)
rc, first = _cli(registry, runtime, *target_poll,
fault='quota_commit' if completion == 'interrupted' else None)
@@ -149,14 +157,14 @@ def test_target_only_retry_rejects_changed_provider_actor(
provider: str, completion: str,
) -> None:
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state, monitor, poll = _fixture(tmp_path)
+ registry, runtime, _state, monitor, poll = _fixture(
+ tmp_path,
+ provider=provider,
+ )
peer = 'synthetic-registered-observer'
data = json.loads(registry.read_text())
data['goals'][0]['coordination']['registered_agents'].append(peer)
registry.write_text(json.dumps(data))
- todos = list_goal_todos(registry_path=registry, goal_id=GOAL)['todos']
- initialize_canonical_authority(runtime, GOAL, build_todo_runtime_shadow_projection(
- goal_id=GOAL, todos=todos, handoff_mode='soft_claim'), state_path=state, provider=provider)
target_poll = _target_poll(poll, material=False)
rc, original = _cli(registry, runtime, *target_poll,
fault='quota_commit' if completion == 'interrupted' else None)
@@ -214,10 +222,10 @@ def test_target_only_concurrent_pending_retries_keep_one_observation(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str, competitor: str,
) -> None:
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state, monitor, poll = _fixture(tmp_path)
- todos = list_goal_todos(registry_path=registry, goal_id=GOAL)['todos']
- initialize_canonical_authority(runtime, GOAL, build_todo_runtime_shadow_projection(
- goal_id=GOAL, todos=todos, handoff_mode='soft_claim'), state_path=state, provider=provider)
+ registry, runtime, _state, monitor, poll = _fixture(
+ tmp_path,
+ provider=provider,
+ )
target_poll = _target_poll(poll, material=False)
rc, interrupted = _cli(registry, runtime, *target_poll, fault='quota_commit')
assert (rc, interrupted) == (73, None)
@@ -279,10 +287,10 @@ def test_target_only_new_observation_remains_rejected_when_not_due(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
) -> None:
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state, monitor, poll = _fixture(tmp_path)
- todos = list_goal_todos(registry_path=registry, goal_id=GOAL)['todos']
- initialize_canonical_authority(runtime, GOAL, build_todo_runtime_shadow_projection(
- goal_id=GOAL, todos=todos, handoff_mode='soft_claim'), state_path=state, provider=provider)
+ registry, runtime, _state, monitor, poll = _fixture(
+ tmp_path,
+ provider=provider,
+ )
# Another legitimate business observation advances cadence without this
# Turn accepting a quota poll. No historical quota receipt may be inferred.
write_monitor_poll_todo_state(registry_path=registry, runtime_root=runtime, goal_id=GOAL,
@@ -307,10 +315,10 @@ def test_target_only_auxiliary_recovery_preserves_advancement_binding(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str, historical_null_todo: bool,
) -> None:
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state, monitor, poll = _fixture(tmp_path)
- todos = list_goal_todos(registry_path=registry, goal_id=GOAL)['todos']
- initialize_canonical_authority(runtime, GOAL, build_todo_runtime_shadow_projection(
- goal_id=GOAL, todos=todos, handoff_mode='soft_claim'), state_path=state, provider=provider)
+ registry, runtime, _state, monitor, poll = _fixture(
+ tmp_path,
+ provider=provider,
+ )
rc, original = _cli(registry, runtime, *_target_poll(poll, material=False))
assert rc == 0, original
rc, advancement = _cli(registry, runtime, 'todo', 'add', '--goal-id', GOAL, '--role', 'agent',
@@ -357,14 +365,14 @@ def test_target_recovery_hint_cannot_create_poll_after_no_effect_abort(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], provider: str,
) -> None:
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state, monitor, poll = _fixture(tmp_path)
+ registry, runtime, _state, monitor, poll = _fixture(
+ tmp_path,
+ provider=provider,
+ )
peer = 'synthetic-registered-peer'
data = json.loads(registry.read_text())
data['goals'][0]['coordination']['registered_agents'].append(peer)
registry.write_text(json.dumps(data))
- todos = list_goal_todos(registry_path=registry, goal_id=GOAL)['todos']
- initialize_canonical_authority(runtime, GOAL, build_todo_runtime_shadow_projection(
- goal_id=GOAL, todos=todos, handoff_mode='soft_claim'), state_path=state, provider=provider)
target_poll = _target_poll(poll, material=False)
captured = {}
native = monitor_runtime._native_result
diff --git a/tests/control_plane/test_monitor_unavailable_closeout_cli.py b/tests/control_plane/test_monitor_unavailable_closeout_cli.py
index 06ada5970f..e738ec3c5e 100644
--- a/tests/control_plane/test_monitor_unavailable_closeout_cli.py
+++ b/tests/control_plane/test_monitor_unavailable_closeout_cli.py
@@ -117,6 +117,13 @@ def cli(*args: str) -> tuple[int, dict]:
"--target-key", "synthetic-source", "--result-hash", "synthetic-observed",
*lease_args,
"--execute")
+ if provider == "markdown":
+ assert rc == 1 and poll["error_code"] == "canonical_authority_required", poll
+ assert poll["write_admitted"] is False
+ assert poll["legacy_fallback_used"] is False
+ assert _classification_count(runtime, "quota_monitor_poll") == 0
+ assert _spend_run_count(runtime) == 0
+ return
assert rc == 0 and poll["appended"] is True, poll
rc, denied = cli(*refresh)
assert rc != 0 and denied["appended"] is False, denied
@@ -171,16 +178,25 @@ def cli(*args: str) -> tuple[int, dict]:
poll_args += ("--material-change", "--next-agent-todo", "Inspect the source transition.",
"--next-action-kind", "validate")
for execute in (True, False):
+ # Execution stops at the retired-writer boundary. A preview still
+ # evaluates the original settled-Turn admission without writing.
+ rejection_code = (
+ "canonical_authority_required" if provider == "markdown" and execute
+ else "monitor_poll_admission_rejected"
+ )
rc, late = cli(*poll_args, *(("--execute",) if execute else ()))
read_rc, current = cli("todo", "list", "--goal-id", GOAL_ID, "--todo-id", MONITOR_ID)
replay_rc, frozen = cli(*refresh)
- assert rc != 0 and late["error_code"] == "monitor_poll_admission_rejected", json.dumps({
+ assert rc != 0 and late["error_code"] == rejection_code, json.dumps({
"poll_rc": rc, "appended": late.get("appended"), "error_code": late.get("error_code"),
"observation_changed": read_rc == 0 and
{k: current["todo"].get(k) for k in OBSERVATION_FIELDS} != fields,
"refresh_rc": replay_rc, "refresh_error_code": frozen.get("error_code"),
})
assert read_rc == 0 and {k: current["todo"].get(k) for k in OBSERVATION_FIELDS} == fields
+ if provider == "markdown" and execute:
+ assert late["write_admitted"] is False
+ assert late["legacy_fallback_used"] is False
assert replay_rc == 0 and frozen["idempotent_replay"] is True, frozen
assert frozen["blocked_retry"] == wait
if scenario == "late_nonmaterial":
@@ -197,7 +213,11 @@ def actor_metadata_request(request):
with monkeypatch.context() as context:
context.setattr(monitor_runtime, "_native_result", actor_metadata_request)
rc, actor_reply = cli(*poll_args, "--execute")
- assert rc != 0 and actor_reply["error_code"] == "monitor_poll_admission_rejected", actor_reply
+ expected_error = (
+ "canonical_authority_required" if provider == "markdown"
+ else "monitor_poll_admission_rejected"
+ )
+ assert rc != 0 and actor_reply["error_code"] == expected_error, actor_reply
rc, after_late = cli("todo", "list", "--goal-id", GOAL_ID)
assert rc == 0, after_late
monitor = next(todo for todo in after_late["todos"] if todo["todo_id"] == MONITOR_ID)
@@ -212,11 +232,20 @@ def actor_metadata_request(request):
# a third identity would leave the already-admitted recovery unresolved.
# Its advancement lane observes this due Monitor through the existing
# auxiliary route, rather than selecting a Monitor as advancement work.
+ state_before_add = state.read_bytes()
rc, independent = cli("todo", "add", "--goal-id", GOAL_ID, "--role", "agent",
"--text", "Inspect an independent artifact.",
"--task-class", "advancement_task", "--claimed-by", AGENT_ID,
*(("--operation-id", f"fresh-independent-{provider}")
if provider != "markdown" else ()))
+ if provider == "markdown":
+ assert rc == 1 and independent["error_code"] == "canonical_authority_required", independent
+ assert independent["write_admitted"] is False
+ assert independent["legacy_fallback_used"] is False
+ assert state.read_bytes() == state_before_add
+ assert _classification_count(runtime, "quota_monitor_poll") == 0
+ assert _spend_run_count(runtime) == 0
+ return
assert rc == 0, independent
rc, fresh_guard = cli("quota", "should-run", "--goal-id", GOAL_ID, "--codex-app",
"--agent-id", AGENT_ID, "--turn-instance-id", recovery_turn)
@@ -247,15 +276,25 @@ def actor_metadata_request(request):
# A newly executable independent advancement belongs to the same recovery
# host Turn after the original effect is closed, without a third identity.
- if provider != "markdown":
- rc, restored = cli("task-lease", "release", "--goal-id", GOAL_ID, "--todo-id", MONITOR_ID,
- "--owner", AGENT_ID, "--idempotency-key", f"unavailable-{provider}",
- "--expected-version", str(lease["lease"]["version"]))
- assert rc == 0, restored
- rc, successor = cli("todo", "add", "--goal-id", GOAL_ID, "--role", "agent",
- "--text", "Inspect an independent public source.", "--task-class", "advancement_task",
- "--claimed-by", AGENT_ID,
- *(("--operation-id", f"independent-{provider}") if provider != "markdown" else ()))
+ successor_args = (
+ "todo", "add", "--goal-id", GOAL_ID, "--role", "agent",
+ "--text", "Inspect an independent public source.",
+ "--task-class", "advancement_task", "--claimed-by", AGENT_ID,
+ )
+ if provider == "markdown":
+ state_before_add = state.read_bytes()
+ rc, successor = cli(*successor_args)
+ assert rc == 1 and successor["error_code"] == "canonical_authority_required", successor
+ assert successor["write_admitted"] is False
+ assert successor["legacy_fallback_used"] is False
+ assert state.read_bytes() == state_before_add
+ return
+
+ rc, restored = cli("task-lease", "release", "--goal-id", GOAL_ID, "--todo-id", MONITOR_ID,
+ "--owner", AGENT_ID, "--idempotency-key", f"unavailable-{provider}",
+ "--expected-version", str(lease["lease"]["version"]))
+ assert rc == 0, restored
+ rc, successor = cli(*successor_args, "--operation-id", f"independent-{provider}")
assert rc == 0, successor
rc, next_turn = cli("quota", "should-run", "--goal-id", GOAL_ID, "--codex-app",
"--agent-id", AGENT_ID, "--turn-instance-id", recovery_turn, *capabilities)
diff --git a/tests/control_plane/test_native_monitor_configuration.py b/tests/control_plane/test_native_monitor_configuration.py
index efa94e53f6..21df19460f 100644
--- a/tests/control_plane/test_native_monitor_configuration.py
+++ b/tests/control_plane/test_native_monitor_configuration.py
@@ -6,7 +6,7 @@
import pytest
from canonical_authority_fixture import isolate_sqlite_runtime
from test_native_monitor_poll import _canonical
-from test_monitor_followthrough_contract import _write_fixture, _add_monitor, GOAL_ID, AGENT_ID
+from test_monitor_followthrough_contract import GOAL_ID, AGENT_ID
from loopx.control_plane.scheduler.monitor_poll_writeback import write_monitor_poll_todo_state
from loopx.control_plane.testing.canary_harness import run_json_cli
from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
@@ -19,10 +19,6 @@
def setup(tmp_path, provider):
- if provider == "legacy":
- registry, runtime, state = _write_fixture(tmp_path)
- monitor = _add_monitor(registry, text="Observe public changes", target_key="public-watch", next_due_at="2000-01-01T00:00:00Z")
- return registry, runtime, state, monitor
return _canonical(tmp_path, provider=provider)
@@ -41,7 +37,7 @@ def _observe(registry, runtime, monitor, result_hash="observed-before-configurat
return receipt
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_configuration_cli_and_clear_preserve_observation(tmp_path, monkeypatch, provider):
isolate_sqlite_runtime(tmp_path, monkeypatch)
registry, runtime, state, monitor = setup(tmp_path, provider)
@@ -53,24 +49,21 @@ def test_configuration_cli_and_clear_preserve_observation(tmp_path, monkeypatch,
assert before["monitor_effect_id"] == "configuration-observation"
assert before["last_checked_at"]
assert int(before["material_change_generation"]) >= 1
- if provider != "legacy":
- state.unlink()
+ state.unlink()
args = ("todo", "update", "--goal-id", GOAL_ID, "--todo-id", monitor["todo_id"],
"--agent-id", AGENT_ID, "--cadence", "2h", "--next-due-at", "2099-01-01T02:00:00Z")
- identity = () if provider == "legacy" else ("--update-operation-id", "configuration-a")
+ identity = ("--update-operation-id", "configuration-a")
run_json_cli(*args, *identity, "--dry-run", registry_path=registry, runtime_root=runtime)
- if provider != "legacy":
- assert not state.exists()
+ assert not state.exists()
result = run_json_cli(*args, *identity, registry_path=registry, runtime_root=runtime)
current = _row(registry, monitor["todo_id"])
assert current["cadence"] == "2h"
assert current["next_due_at"] == "2099-01-01T02:00:00Z"
for field in OBSERVATION_FIELDS:
assert current.get(field) == before.get(field), field
- if provider != "legacy":
- assert result["source_authority"] == ("file_v0" if provider == "file" else "sqlite_v0")
- replay = run_json_cli(*args, *identity, registry_path=registry, runtime_root=runtime)
- assert replay["status"] == "replayed"
+ assert result["source_authority"] == ("file_v0" if provider == "file" else "sqlite_v0")
+ replay = run_json_cli(*args, *identity, registry_path=registry, runtime_root=runtime)
+ assert replay["status"] == "replayed"
# Clearing watch_only needs another retained bound, and an expiry-only edit
# must keep the due time the explicit cadence edit pinned.
update_goal_todo(registry_path=registry, runtime_root_arg=str(runtime), goal_id=GOAL_ID,
@@ -94,7 +87,7 @@ def test_configuration_cli_and_clear_preserve_observation(tmp_path, monkeypatch,
assert recadenced.get(field) == before.get(field), field
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_route_identity_target_key_is_not_schedule_configuration(tmp_path, monkeypatch, provider):
"""A Monitor successor keeps its routing target without becoming a Monitor."""
isolate_sqlite_runtime(tmp_path, monkeypatch)
@@ -137,7 +130,7 @@ def unavailable(**kwargs):
assert replay["projection_delivery"] == "delivered"
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_observed_target_cannot_be_repurposed_by_configuration(tmp_path, monkeypatch, provider):
isolate_sqlite_runtime(tmp_path, monkeypatch)
registry, runtime, _state, monitor = setup(tmp_path, provider)
diff --git a/tests/control_plane/test_native_monitor_poll.py b/tests/control_plane/test_native_monitor_poll.py
index 8f8fd64e2d..d1f30f791c 100644
--- a/tests/control_plane/test_native_monitor_poll.py
+++ b/tests/control_plane/test_native_monitor_poll.py
@@ -6,24 +6,41 @@
import json
from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
-from test_monitor_followthrough_contract import _write_fixture, _add_monitor, GOAL_ID, AGENT_ID
+from test_monitor_followthrough_contract import _write_fixture, GOAL_ID, AGENT_ID
from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
from loopx.control_plane.scheduler.monitor_poll_writeback import write_monitor_poll_todo_state
from loopx.control_plane.testing.canary_harness import run_json_cli
-from loopx.todos import list_goal_todos
-def _canonical(tmp_path, native=False, provider="file", lease=None):
- registry, runtime, state = _write_fixture(tmp_path)
- monitor = _add_monitor(registry, text="Observe a public target", target_key="public-watch",
- next_due_at="2000-01-01T00:00:00Z")
- items = list_goal_todos(registry_path=registry, goal_id=GOAL_ID, role="agent")["todos"]
+def _canonical(
+ tmp_path,
+ native=False,
+ provider="file",
+ lease=None,
+ monitor_status="open",
+):
+ registry, runtime, state = _write_fixture(tmp_path, canonical=False)
+ monitor = {
+ "schema_version": "todo_item_v0",
+ "todo_id": "todo_public_watch",
+ "role": "agent",
+ "status": monitor_status,
+ "done": monitor_status == "done",
+ "text": "Observe a public target",
+ "task_class": "continuous_monitor",
+ "archive_state": "active",
+ "source_section": "Agent Todo",
+ "index": 1,
+ "claimed_by": AGENT_ID,
+ "target_key": "public-watch",
+ "cadence": "1h",
+ "next_due_at": "2000-01-01T00:00:00Z",
+ "watch_only": "true",
+ }
+ items = [monitor]
leases = []
if lease is not None:
- for item in items:
- if item["todo_id"] == monitor["todo_id"]:
- item["claimed_by"] = AGENT_ID
leases = [{"schema_version": "task_lease_v0", "goal_id": GOAL_ID,
"todo_id": monitor["todo_id"], "owner": AGENT_ID, "write_scopes": [], **lease}]
projection = build_todo_runtime_shadow_projection(goal_id=GOAL_ID, todos=items, leases=leases,
diff --git a/tests/control_plane/test_native_todo_ownership_update.py b/tests/control_plane/test_native_todo_ownership_update.py
index ccf5c984cb..4625822166 100644
--- a/tests/control_plane/test_native_todo_ownership_update.py
+++ b/tests/control_plane/test_native_todo_ownership_update.py
@@ -6,15 +6,12 @@
from loopx.todos import update_goal_todo
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_public_transfer_clear_exclusions_and_replay(tmp_path, monkeypatch, provider):
isolate_sqlite_runtime(tmp_path, monkeypatch)
- promoted = provider != "legacy"
- registry, state = fixture(tmp_path, promoted, provider)
+ registry, state = fixture(tmp_path, provider)
before = records(registry)
- transfer = ["--claimed-by", "Agent B"]
- if promoted:
- transfer += ["--update-operation-id", "transfer-first"]
+ transfer = ["--claimed-by", "Agent B", "--update-operation-id", "transfer-first"]
update(registry, *transfer, "--dry-run")
assert records(registry) == before
update(registry, *transfer)
@@ -23,9 +20,8 @@ def test_public_transfer_clear_exclusions_and_replay(tmp_path, monkeypatch, prov
update(registry, "--agent-id", "agent-b", "--clear-claim")
cleared = records(registry)
assert not cleared["todo_target"].get("claimed_by")
- if promoted:
- assert update(registry, *transfer)["status"] == "replayed"
- assert records(registry) == cleared
+ assert update(registry, *transfer)["status"] == "replayed"
+ assert records(registry) == cleared
update(registry, "--excluded-agent", "agent-b")
excluded = records(registry)
assert excluded["todo_target"]["excluded_agents"] == ["agent-b"]
@@ -37,27 +33,24 @@ def test_public_transfer_clear_exclusions_and_replay(tmp_path, monkeypatch, prov
assert state.exists(), "canonical commit still delivers its permanent Markdown projection"
-@pytest.mark.parametrize("promoted", [False, True])
@pytest.mark.parametrize("intent", [
{"claimed_by": "unregistered"}, {"excluded_agents": ["agent-b", "unregistered"]},
{"claimed_by": "agent-b", "clear_claim": True},
{"claimed_by": "agent-b", "excluded_agents": ["agent-b"]},
{"excluded_agents": ["agent-a"]},
])
-def test_invalid_ownership_intent_never_partially_writes_copy(tmp_path, promoted, intent):
- registry, state = fixture(tmp_path, promoted)
+def test_invalid_ownership_intent_never_partially_writes_copy(tmp_path, intent):
+ registry, state = fixture(tmp_path)
before = records(registry)
with pytest.raises((ValueError, RuntimeError)):
update_goal_todo(registry_path=registry, goal_id="goal-a", todo_id="todo_target",
agent_id="agent-a", text="Must not partially commit", **intent)
assert records(registry) == before
- if promoted:
- assert not state.exists()
+ assert not state.exists()
-@pytest.mark.parametrize("promoted", [False, True])
-def test_clear_claim_and_exclude_former_holder_in_one_edit(tmp_path, promoted):
- registry, _ = fixture(tmp_path, promoted)
+def test_clear_claim_and_exclude_former_holder_in_one_edit(tmp_path):
+ registry, _ = fixture(tmp_path)
result = update_goal_todo(registry_path=registry, goal_id="goal-a", todo_id="todo_target",
agent_id="agent-a", clear_claim=True, excluded_agents=["agent-a"])
assert result["ok"]
diff --git a/tests/control_plane/test_native_todo_planning_update.py b/tests/control_plane/test_native_todo_planning_update.py
index d69fd0822d..c9ece39345 100644
--- a/tests/control_plane/test_native_todo_planning_update.py
+++ b/tests/control_plane/test_native_todo_planning_update.py
@@ -15,7 +15,7 @@
from loopx.todos import list_goal_todos, update_goal_todo
-def fixture(tmp_path: Path, promoted: bool, provider: str = "file") -> tuple[Path, Path]:
+def source_fixture(tmp_path: Path) -> tuple[Path, Path]:
project = tmp_path / "project"
state = project / ".codex/goals/goal-a/ACTIVE_GOAL_STATE.md"
state.parent.mkdir(parents=True)
@@ -37,18 +37,26 @@ def fixture(tmp_path: Path, promoted: bool, provider: str = "file") -> tuple[Pat
"state_file": ".codex/goals/goal-a/ACTIVE_GOAL_STATE.md",
"coordination": {"registered_agents": ["agent-a", "agent-b"]},
}]}), encoding="utf-8")
- if promoted:
- todos = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
- projection = build_todo_runtime_shadow_projection(goal_id="goal-a", todos=todos, handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, "goal-a", projection, state_path=state, provider=provider)
- state.unlink() # The update must neither require nor import a Markdown authority source.
return registry, state
-def update(registry: Path, *args: str, ok: bool = True) -> dict:
+def fixture(tmp_path: Path, provider: str = "file") -> tuple[Path, Path]:
+ registry, state = source_fixture(tmp_path)
+ todos = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
+ projection = build_todo_runtime_shadow_projection(
+ goal_id="goal-a", todos=todos, handoff_mode="soft_claim",
+ )
+ initialize_canonical_authority(
+ tmp_path / "runtime", "goal-a", projection, state_path=state, provider=provider,
+ )
+ state.unlink() # The update must neither require nor import a Markdown authority source.
+ return registry, state
+
+
+def update(registry: Path, *args: str, ok: bool = True, actor: str = "agent-a") -> dict:
process = subprocess.run([
sys.executable, "-m", "loopx.cli", "--format", "json", "--registry", str(registry),
- "todo", "update", "--goal-id", "goal-a", "--todo-id", "todo_target", "--agent-id", "agent-a", *args,
+ "todo", "update", "--goal-id", "goal-a", "--todo-id", "todo_target", "--agent-id", actor, *args,
], capture_output=True, text=True, timeout=45)
result = json.loads(process.stdout)
assert (process.returncode == 0) is ok, (result, process.stderr)
@@ -59,9 +67,160 @@ def records(registry: Path) -> dict[str, dict]:
return {item["todo_id"]: item for item in list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]}
-@pytest.mark.parametrize("promoted", [False, True])
-def test_work_requirements_update_preserves_authority_and_supports_explicit_empty(tmp_path: Path, promoted: bool) -> None:
- registry, _state = fixture(tmp_path, promoted)
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+@pytest.mark.parametrize("claim_flag", [False, True])
+def test_cli_imported_completion_repairs_only_metadata(tmp_path, monkeypatch, provider, claim_flag):
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+ from loopx.control_plane.testing.canary_harness import run_json_cli, run_json_cli_result
+
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, state = source_fixture(tmp_path)
+ rows = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
+ imported = next(row for row in rows if row["todo_id"] == "todo_target")
+ imported.update(status="done", done=True, evidence="Original evidence", reason="Original reason",
+ completed_at="2026-01-02T00:00:00Z", updated_at="2026-01-02T01:00:00Z",
+ last_actor_agent_id="agent-b", completion_receipt_id="tcw_original")
+ projection = build_todo_runtime_shadow_projection(goal_id="goal-a", todos=rows, handoff_mode="soft_claim")
+ runtime = tmp_path / "runtime"
+ initialize_canonical_authority(runtime, "goal-a", projection, state_path=state, provider=provider)
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
+ source_before = state.read_bytes()
+ prefix = ("todo", "complete", "--goal-id", "goal-a", "--todo-id", "todo_target")
+ for actor, extra, code in [
+ ("agent-b", (), "claim_owner_mismatch"),
+ ("agent-a", ("--note", "A fresh annotation"), "invalid_todo_completion_transaction"),
+ ("agent-a", ("--evidence", "A fresh completion result"), "invalid_todo_completion_transaction"),
+ ("agent-a", ("--next-agent-todo", "Fresh successor work"), "invalid_todo_completion_transaction"),
+ ]:
+ returncode, rejected = run_json_cli_result(*prefix, "--agent-id", actor, *extra, registry_path=registry)
+ assert returncode == 1 and rejected["error_code"] == code, rejected
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == before
+ assert state.read_bytes() == source_before
+ repair = (*prefix, "--agent-id", "agent-a", *(("--claimed-by", "agent-a") if claim_flag else ()))
+ preview = run_json_cli(*repair, "--dry-run", registry_path=registry)
+ assert preview["dry_run"] is True, preview
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == before
+ assert state.read_bytes() == source_before
+ applied = run_json_cli(*repair, registry_path=registry)
+ assert applied["changed"] is True and applied["completion_policy"] is None, applied
+ original = next(row for row in before["todos"] if row["todo_id"] == "todo_target")
+ current = records(registry)["todo_target"]
+ assert {k: v for k, v in current.items() if k != "content_revision"} == {
+ **{k: v for k, v in original.items() if k != "content_revision"}, "completion_continuation": "active_goal",
+ }
+ run_json_cli("todo", "add", "--goal-id", "goal-a", "--role", "agent",
+ "--text", "A later independent operation", registry_path=registry)
+ later = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
+ replay = run_json_cli(*repair, registry_path=registry)
+ assert replay["changed"] is False, replay
+ after = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
+ assert after["todos"] == later["todos"] and after["provider_revision"] == later["provider_revision"]
+
+
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+@pytest.mark.parametrize("command", ["complete", "supersede"])
+def test_cli_same_title_terminal_successor_preserves_peers_and_replay(tmp_path, monkeypatch, provider, command):
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+ from loopx.control_plane.testing.canary_harness import run_json_cli, run_json_cli_result
+
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, state = fixture(tmp_path, provider)
+ runtime = tmp_path / "runtime"
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
+ prefix = ("todo", command, "--goal-id", "goal-a", "--todo-id", "todo_target")
+ flags = ("--evidence", "Synthetic accepted result", "--self-merged") if command == "complete" else (
+ "--reason", "Synthetic replacement of the accepted work")
+ for actor, title, code in (("agent-b", "Synthetic task", "claim_owner_mismatch"),
+ ("agent-a", "Independent task", "todo_semantic_duplicate_conflict")):
+ result_code, rejected = run_json_cli_result(
+ *prefix, "--agent-id", actor, *flags, "--next-agent-todo", title,
+ "--next-claimed-by", "agent-a", registry_path=registry)
+ assert result_code == 1 and rejected["error_code"] == code, rejected
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == before
+ assert not state.exists()
+ terminal = (*prefix, "--agent-id", "agent-a", *flags,
+ "--next-agent-todo", "Synthetic task", "--next-claimed-by", "agent-a")
+ preview = run_json_cli(*terminal, "--dry-run", registry_path=registry)
+ assert preview["dry_run"] is True, preview
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == before
+ assert not state.exists()
+ applied = run_json_cli(*terminal, registry_path=registry)
+ assert applied["changed"] is True and applied["projection_delivery"] != "pending", applied
+ current = records(registry)
+ origin = current["todo_target"]
+ successor_id = origin["successor_todo_ids"][0]
+ assert successor_id not in {"todo_target", "todo_other"}
+ assert origin["status"] == "done"
+ assert current[successor_id]["text"] == "Synthetic task"
+ assert current[successor_id]["status"] == "open"
+ assert current["todo_other"]["status"] == "open"
+ if command == "complete":
+ assert current[successor_id]["unblocks_todo_id"] == "todo_target"
+ else:
+ assert origin["superseded_by"] == successor_id
+ run_json_cli("todo", "add", "--goal-id", "goal-a", "--role", "agent",
+ "--text", "A later independently admitted Todo", registry_path=registry)
+ later = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
+ replay = run_json_cli(*terminal, registry_path=registry)
+ assert replay["changed"] is False, replay
+ after = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
+ assert after["todos"] == later["todos"] and after["provider_revision"] == later["provider_revision"]
+
+
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+@pytest.mark.parametrize("edit,expected_actor", [
+ (["--evidence", "Synthetic planning validation"], "agent-a"),
+ (["--priority", "P2"], "agent-a"),
+ (["--clear-priority"], "agent-a"),
+ (["--note", "Owner context", "--clear-priority"], "agent-a"),
+ (["--note", "Peer correction", "--evidence", "Synthetic mixed validation"], "agent-b"),
+ (["--text", "Corrected task", "--evidence", "Synthetic mixed validation"], "agent-b"),
+])
+def test_delegated_cli_planning_preserves_actor_until_copy_changes(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
+ edit: list[str], expected_actor: str,
+) -> None:
+ # Preserve the supported audit contract on the canonical path; the retired
+ # Markdown writer and its mutation-authority fixture are not dependencies.
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, state = fixture(tmp_path, provider)
+ registration = json.loads(registry.read_text())
+ registration["goals"][0]["coordination"]["todo_lifecycle_authority"] = [{
+ "agent_id": "agent-b", "actions": ["update"], "requires_reason": True,
+ }]
+ registry.write_text(json.dumps(registration))
+ update(registry, "--note", "Owner context", "--priority", "P1",
+ "--update-operation-id", "owner-audit")
+ before = records(registry)
+ assert before["todo_target"]["last_actor_agent_id"] == "agent-a"
+ display = state.read_bytes()
+ attempt = [*edit, "--update-operation-id", "delegated-audit"]
+ rejected = update(registry, *attempt, actor="agent-b", ok=False)
+ assert rejected["error_code"] == "delegation_reason_required"
+ assert records(registry) == before
+ assert state.read_bytes() == display
+ attempt += ["--authority-reason", "Review the existing task"]
+ assert update(registry, *attempt, "--dry-run", actor="agent-b")["status"] == "planned"
+ assert records(registry) == before
+ assert state.read_bytes() == display
+ assert update(registry, *attempt, actor="agent-b")["status"] == "applied"
+ after = records(registry)
+ assert after["todo_target"]["last_actor_agent_id"] == expected_actor
+ assert after["todo_target"]["claimed_by"] == "agent-a"
+ assert after["todo_other"] == before["todo_other"]
+ display = state.read_bytes()
+ assert update(registry, *attempt, actor="agent-b")["status"] == "replayed"
+ assert records(registry) == after
+ assert state.read_bytes() == display
+
+
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_work_requirements_update_preserves_authority_and_supports_explicit_empty(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
+) -> None:
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, _state = fixture(tmp_path, provider)
before = records(registry)
result = update_goal_todo(
registry_path=registry, goal_id="goal-a", todo_id="todo_target", agent_id="agent-a",
@@ -91,7 +250,7 @@ def test_work_requirements_update_preserves_authority_and_supports_explicit_empt
assert cleared["task_repository"] == todo["task_repository"]
-@pytest.mark.parametrize("promoted", [False, True])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("intent", [
{"required_capabilities": ["code_review", "bad/token"]},
{"required_write_scopes": ["src/**", "../escape"]},
@@ -99,33 +258,37 @@ def test_work_requirements_update_preserves_authority_and_supports_explicit_empt
{"task_repository": "user:password@example.com:project"},
{"explore_result_node_refs": ["Node:alpha", "bad/ref"]},
])
-def test_invalid_work_requirement_is_not_silently_dropped(tmp_path: Path, promoted: bool, intent: dict) -> None:
- registry, state = fixture(tmp_path, promoted)
+def test_invalid_work_requirement_is_not_silently_dropped(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str, intent: dict,
+) -> None:
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, state = fixture(tmp_path, provider)
before = records(registry)
with pytest.raises((ValueError, RuntimeError)):
update_goal_todo(registry_path=registry, goal_id="goal-a", todo_id="todo_target",
agent_id="agent-a", text="Must not partially commit", **intent)
assert records(registry) == before
- if promoted:
- assert not state.exists()
+ assert not state.exists()
-@pytest.mark.parametrize("promoted", [False, True])
-def test_cli_routes_work_requirements_without_display_dependency(tmp_path: Path, promoted: bool) -> None:
- registry, state = fixture(tmp_path, promoted)
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_cli_routes_work_requirements_without_display_dependency(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
+) -> None:
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, state = fixture(tmp_path, provider)
args = ["--action-kind", "IMPLEMENT", "--task-domain", "code",
"--task-repository", "https://github.com/example/project",
"--required-capability", "Code-Review", "--target-capability", "Delivery",
- "--required-write-scope", "src/**", "--explore-result-node-ref", "Node:alpha"]
- if promoted:
- args += ["--update-operation-id", "requirements-cli"]
+ "--required-write-scope", "src/**", "--explore-result-node-ref", "Node:alpha",
+ "--update-operation-id", "requirements-cli"]
update(registry, *args, "--dry-run")
- if promoted:
- assert not state.exists()
+ assert not state.exists()
update(registry, *args)
assert records(registry)["todo_target"]["required_capabilities"] == ["code_review"]
- if promoted:
- assert update(registry, *args)["status"] == "replayed"
+ assert update(registry, *args)["status"] == "replayed"
update(registry, "--clear-explore-result-node-refs")
assert not records(registry)["todo_target"].get("explore_result_node_refs")
before = records(registry)
@@ -133,7 +296,7 @@ def test_cli_routes_work_requirements_without_display_dependency(tmp_path: Path,
assert records(registry) == before
-@pytest.mark.parametrize("promoted", [False, True])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize(("remote", "expected"), [
("https://github.com:443/example/project", "git:github.com/example/project"),
("ssh://git@github.com:22/example/project", "git:github.com/example/project"),
@@ -143,24 +306,27 @@ def test_cli_routes_work_requirements_without_display_dependency(tmp_path: Path,
("git://github.com:80/example/project", "git:github.com:80/example/project"),
("git://github.com:0/example/project", "git:github.com:0/example/project"),
])
-def test_cli_repository_ports_match_before_and_after_promotion(
- tmp_path: Path, promoted: bool, remote: str, expected: str,
+def test_cli_repository_ports_match_across_canonical_providers(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
+ remote: str, expected: str,
) -> None:
- registry, _state = fixture(tmp_path, promoted)
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, _state = fixture(tmp_path, provider)
before = records(registry)
- args = ["--task-repository", remote]
- if promoted:
- args += ["--update-operation-id", "repository-port-cli"]
+ args = [
+ "--task-repository", remote,
+ "--update-operation-id", "repository-port-cli",
+ ]
update(registry, *args)
after = records(registry)
assert after["todo_target"]["task_repository"] == expected
assert after["todo_other"] == before["todo_other"]
- if promoted:
- assert update(registry, *args)["status"] == "replayed"
- assert records(registry) == after
+ assert update(registry, *args)["status"] == "replayed"
+ assert records(registry) == after
-@pytest.mark.parametrize("promoted", [False, True])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("surface", ["cli", "python_api"])
@pytest.mark.parametrize(("label", "note", "expected_note"), [
("omitted", None, "Preserved note"),
@@ -168,18 +334,20 @@ def test_cli_repository_ports_match_before_and_after_promotion(
("unicode_whitespace", " \t\u2003\n", "Preserved note"),
("nonempty", " Updated\u2003note ", "Updated note"),
])
-def test_note_input_semantics_match_before_and_after_promotion(
- tmp_path: Path, promoted: bool, surface: str, label: str,
+def test_note_input_semantics_match_across_canonical_providers(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
+ surface: str, label: str,
note: str | None, expected_note: str,
) -> None:
- registry, _state = fixture(tmp_path, promoted)
- operation_id = f"note-{surface}-{label}" if promoted else None
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, _state = fixture(tmp_path, provider)
+ operation_id = f"note-{surface}-{label}"
if surface == "cli":
args = ["--text", "Corrected task"]
if note is not None:
args += ["--note", note]
- if operation_id is not None:
- args += ["--update-operation-id", operation_id]
+ args += ["--update-operation-id", operation_id]
update(registry, *args)
else:
result = update_goal_todo(
@@ -194,7 +362,7 @@ def test_note_input_semantics_match_before_and_after_promotion(
def test_promoted_v0_replay_uses_normalized_empty_note_identity(tmp_path: Path) -> None:
- registry, _state = fixture(tmp_path, True)
+ registry, _state = fixture(tmp_path)
base = ["--text", "Corrected task", "--update-operation-id", "note-v0-replay"]
assert update(registry, *base)["status"] == "applied"
assert update(registry, *base, "--note", "")["status"] == "replayed"
@@ -203,18 +371,20 @@ def test_promoted_v0_replay_uses_normalized_empty_note_identity(tmp_path: Path)
assert records(registry)["todo_target"]["note"] == "Preserved note"
-@pytest.mark.parametrize("promoted", [False, True])
-def test_public_cli_nonterminal_wait_update_and_clear(tmp_path: Path, promoted: bool) -> None:
- registry, state = fixture(tmp_path, promoted)
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_public_cli_nonterminal_wait_update_and_clear(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
+) -> None:
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, state = fixture(tmp_path, provider)
before = records(registry)
args = ["--status", "deferred", "--resume-when", "pr_merged:#123", "--reason", "Await upstream",
- "--evidence", "Synthetic evidence", "--text", " Corrected\u2003task "]
- if promoted:
- args += ["--update-operation-id", "planning-attempt"]
+ "--evidence", "Synthetic evidence", "--text", " Corrected\u2003task ",
+ "--update-operation-id", "planning-attempt"]
update(registry, *args, "--dry-run")
assert records(registry) == before
- if promoted:
- assert not state.exists()
+ assert not state.exists()
update(registry, *args)
waiting = records(registry)
todo = waiting["todo_target"]
@@ -225,11 +395,10 @@ def test_public_cli_nonterminal_wait_update_and_clear(tmp_path: Path, promoted:
assert todo["note"] == "Preserved note"
assert todo["claimed_by"] == "agent-a"
assert waiting["todo_other"] == before["todo_other"]
- if promoted:
- assert update(registry, *args)["status"] == "replayed"
- update(registry, *args, "--note", "Changed retry", ok=False)
- assert records(registry) == waiting
- assert state.exists(), "accepted commit should drain its independent display projection"
+ assert update(registry, *args)["status"] == "replayed"
+ update(registry, *args, "--note", "Changed retry", ok=False)
+ assert records(registry) == waiting
+ assert state.exists(), "accepted commit should drain its independent display projection"
update(registry, "--status", "open", "--clear-resume-when")
resumed = records(registry)["todo_target"]
assert resumed["status"] == "open" and resumed["done"] is False
@@ -242,7 +411,7 @@ def deferred_hard_lease_fixture(
) -> tuple[Path, Path]:
if provider == "sqlite":
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, state = fixture(tmp_path, False)
+ registry, state = source_fixture(tmp_path)
todos = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
target = next(todo for todo in todos if todo["todo_id"] == "todo_target")
target.update(status="deferred", done=True, resume_when="resume_at:2020-01-01T00:00:00Z")
@@ -261,7 +430,7 @@ def test_dependency_replan_uses_existing_inactive_lifecycle_then_fresh_lease(
) -> None:
if provider == "sqlite":
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, state = fixture(tmp_path, False)
+ registry, state = source_fixture(tmp_path)
todos = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
target = next(todo for todo in todos if todo["todo_id"] == "todo_target")
target.update(resume_when="todo_done:todo_other", successor_todo_ids=["todo_other"])
@@ -343,8 +512,9 @@ def lease(action: str, *args: str) -> tuple[int, dict]:
def test_bound_user_action_metadata_through_real_cli(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
) -> None:
- isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, state = fixture(tmp_path, False)
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, state = source_fixture(tmp_path)
todos = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
target = next(todo for todo in todos if todo["todo_id"] == "todo_target")
target.pop("claimed_by")
@@ -408,18 +578,25 @@ def test_bound_user_action_metadata_through_real_cli(
@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_bound_user_action_leased_metadata_through_real_cli(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
+@pytest.mark.parametrize("expired", [False, True])
+def test_bound_user_action_releases_retained_execution_before_administrative_copy(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str, expired: bool,
) -> None:
- isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, state = fixture(tmp_path, False)
+ if provider == "sqlite":
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ registry, state = source_fixture(tmp_path)
todos = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
target = next(todo for todo in todos if todo["todo_id"] == "todo_target")
target.pop("claimed_by")
target.update(role="user", task_class="user_action", source_section="User Todo",
created_by="agent-a", bound_agent="agent-a")
+ retained = {"schema_version": "task_lease_v0", "goal_id": "goal-a", "todo_id": "todo_target",
+ "owner": "agent-a", "idempotency_key": "retained-copy", "status": "active", "version": 2,
+ "lease_epoch": 1, "write_scopes": [], "acquired_at": "2020-01-01T00:00:00Z",
+ "updated_at": "2020-01-01T00:00:00Z", "acquire_ttl_seconds": 600,
+ "expires_at": "2020-01-01T00:10:00Z" if expired else "2099-01-01T00:00:00Z"}
projection = build_todo_runtime_shadow_projection(
- goal_id="goal-a", todos=todos, handoff_mode="hard_lease",
+ goal_id="goal-a", todos=todos, handoff_mode="hard_lease", leases=[retained],
)
initialize_canonical_authority(tmp_path / "runtime", "goal-a", projection,
state_path=state, provider=provider)
@@ -433,15 +610,26 @@ def lease(action: str, *args: str) -> dict:
assert process.returncode == 0, process.stderr
return json.loads(process.stdout)
- acquired = lease("acquire", "--owner", "agent-a", "--idempotency-key", "copy-execution")
- retained = acquired["lease"]
- proof = ["--task-lease-idempotency-key", "copy-execution",
+ proof = ["--task-lease-idempotency-key", "retained-copy",
"--task-lease-expected-version", str(retained["version"])]
before = records(registry)
+ for supplied in ([], proof):
+ rejected = update(registry, "--role", "user", "--note", "Blocked administrative copy", *supplied, ok=False)
+ assert rejected["reason_code"] == "todo_not_executable"
+ assert rejected["recovery"]["action"] == "reconcile_lease_owner"
+ assert "acquire" not in rejected["recovery"]
+ assert records(registry) == before and not state.exists()
+ inspected = lease("inspect")
+ assert inspected["active"] is False
+ if not expired:
+ assert inspected["executor_constraint"]["reason"] == "todo_not_executable"
+ released = lease("release", "--owner", "agent-a", "--idempotency-key", "retained-copy",
+ "--expected-version", str(retained["version"]))
+ revision = lease("inspect")["provider_revision"]
args = ["--role", "user", "--text", "Reminder: disposition awaits user execution",
"--note", "Delivery receipt only", "--evidence", "Synthetic delivery receipt",
- "--update-operation-id", "leased-action-copy",
- "--update-expected-provider-revision", acquired["provider_revision"], *proof]
+ "--update-operation-id", "administrative-action-copy",
+ "--update-expected-provider-revision", revision]
assert update(registry, *args, "--dry-run")["status"] == "planned"
assert records(registry) == before
applied = update(registry, *args)
@@ -452,37 +640,33 @@ def lease(action: str, *args: str) -> dict:
assert after["todo_target"]["evidence"] == "Synthetic delivery receipt"
for field in ("role", "task_class", "status", "done", "created_by", "bound_agent", "claimed_by"):
assert after["todo_target"].get(field) == before["todo_target"].get(field)
- assert lease("inspect")["lease"] == retained
+ assert lease("inspect")["lease"] == released["lease"]
assert update(registry, *args)["status"] == "replayed"
assert update(registry, *args, "--note", "Conflicting retry", ok=False)["reason_code"] == "coordination_operation_identity_mismatch"
- assert update(registry, "--role", "user", "--note", "Stale writer", *proof,
+ assert update(registry, "--role", "user", "--note", "Stale writer",
"--update-operation-id", "stale-copy",
- "--update-expected-provider-revision", acquired["provider_revision"], ok=False)["reason_code"] == "provider_revision_mismatch"
+ "--update-expected-provider-revision", revision, ok=False)["reason_code"] == "provider_revision_mismatch"
for attempt in (["--agent-id", "agent-b"], ["--status", "blocked"],
["--required-capability", "shell"], ["--bound-agent", "agent-b"],
["--task-lease-idempotency-key", "wrong-key"],
["--task-lease-expected-version", "0"]):
- update(registry, "--role", "user", "--note", "Refused edit", *proof, *attempt, ok=False)
+ rejected = update(registry, "--role", "user", "--note", "Refused edit", *attempt, ok=False)
+ if attempt == ["--agent-id", "agent-b"]:
+ assert rejected["reason_code"] == "bound_agent_mismatch"
assert records(registry) == after
- assert lease("inspect")["lease"] == retained
- released = lease("release", "--owner", "agent-a", "--idempotency-key", "copy-execution",
- "--expected-version", str(retained["version"]))
+ assert lease("inspect")["lease"] == released["lease"]
rejected = update(registry, "--role", "user", "--note", "Old execution", *proof, ok=False)
- assert rejected["recovery"]["action"] == "acquire_fresh_lease"
+ assert rejected["recovery"]["action"] == "resolve_lifecycle_edit"
+ assert "acquire" not in rejected["recovery"]
assert rejected["recovery"]["execution_authority_granted"] is False
assert records(registry) == after
# Historical success replays but grants no authority for a new operation.
assert update(registry, *args)["status"] == "replayed"
- fresh = lease("acquire", "--owner", "agent-a", "--idempotency-key", "next-copy-execution",
- "--expected-version", str(released["lease"]["version"]))
- assert fresh["lease"]["lease_epoch"] == retained["lease_epoch"] + 1
recovered = update(registry, "--role", "user", "--note", "Recovered metadata",
"--update-operation-id", "fresh-action-copy",
- "--update-expected-provider-revision", fresh["provider_revision"],
- "--task-lease-idempotency-key", "next-copy-execution",
- "--task-lease-expected-version", str(fresh["lease"]["version"]))
+ "--update-expected-provider-revision", applied["provider_revision"])
assert recovered["status"] == "applied"
- assert lease("inspect")["lease"] == fresh["lease"]
+ assert lease("inspect")["lease"] == released["lease"]
assert not records(registry)["todo_target"].get("claimed_by")
@@ -548,7 +732,7 @@ def supersede(*args: str) -> tuple[int, dict]:
["--task-class", "continuous_monitor"], ["--status", "blocked", "--agent-id", "agent-b"],
])
def test_promoted_unsupported_or_unauthorized_update_never_falls_back(tmp_path: Path, args: list[str]) -> None:
- registry, state = fixture(tmp_path, True)
+ registry, state = fixture(tmp_path)
before = records(registry)
update(registry, *args, ok=False)
assert records(registry) == before
@@ -560,7 +744,7 @@ def test_promoted_unsupported_or_unauthorized_update_never_falls_back(tmp_path:
("todo_target", "user", "Todo does not have the requested role"),
])
def test_native_target_lookup_keeps_public_value_error(tmp_path: Path, todo_id: str, role: str, reason: str) -> None:
- registry, state = fixture(tmp_path, True)
+ registry, state = fixture(tmp_path)
before = records(registry)
with pytest.raises(ValueError, match=reason):
update_goal_todo(registry_path=registry, goal_id="goal-a", todo_id=todo_id,
@@ -569,7 +753,7 @@ def test_native_target_lookup_keeps_public_value_error(tmp_path: Path, todo_id:
assert not state.exists()
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["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(
@@ -577,7 +761,7 @@ def test_public_cli_external_wait_uses_current_successor_source(
) -> None:
if provider == "sqlite":
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, state = fixture(tmp_path, False)
+ registry, state = source_fixture(tmp_path)
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",
@@ -594,24 +778,20 @@ def test_public_cli_external_wait_uses_current_successor_source(
"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()
+ 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"]
+ args = [
+ "--resume-when", "monitor_changed:todo_monitor",
+ "--successor-todo-id", "todo_other",
+ "--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
@@ -630,6 +810,5 @@ def test_public_cli_external_wait_uses_current_successor_source(
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
+ assert update(registry, *args)["status"] == "replayed"
+ assert records(registry) == after
diff --git a/tests/control_plane/test_native_user_completion_update.py b/tests/control_plane/test_native_user_completion_update.py
index f39710ed55..0f050091d0 100644
--- a/tests/control_plane/test_native_user_completion_update.py
+++ b/tests/control_plane/test_native_user_completion_update.py
@@ -22,15 +22,22 @@ def fixture(tmp_path: Path, provider: str, *, validation: str | None = None) ->
registry.write_text(json.dumps({"schema_version": 1, "common_runtime_root": str(tmp_path / "runtime"),
"goals": [{"id": "goal-a", "status": "active", "repo": str(project), "state_file": state.name,
"coordination": {"registered_agents": ["agent-a"]}}]}), encoding="utf-8")
+ projection = build_todo_runtime_shadow_projection(
+ goal_id="goal-a", todos=[], handoff_mode="soft_claim"
+ )
+ initialize_canonical_authority(
+ tmp_path / "runtime",
+ "goal-a",
+ projection,
+ state_path=state,
+ provider=provider,
+ )
dependent = add_goal_todo(registry_path=registry, goal_id="goal-a", role="agent",
text="Continue after the observed outcome", task_class="advancement_task",
status="blocked", claimed_by="agent-a")
added = add_goal_todo(registry_path=registry, goal_id="goal-a", role="user",
text="Record the observed outcome", task_class="user_action", validation_command=validation,
unblocks_todo_id=dependent["todo_id"])
- todos = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
- projection = build_todo_runtime_shadow_projection(goal_id="goal-a", todos=todos, handoff_mode="soft_claim")
- initialize_canonical_authority(tmp_path / "runtime", "goal-a", projection, state_path=state, provider=provider)
return registry, state, str(added["todo_id"])
diff --git a/tests/control_plane/test_new_goal_storage_defaults.py b/tests/control_plane/test_new_goal_storage_defaults.py
index 71b2e22fc6..a7e916fa4e 100644
--- a/tests/control_plane/test_new_goal_storage_defaults.py
+++ b/tests/control_plane/test_new_goal_storage_defaults.py
@@ -25,10 +25,13 @@ def environment(tmp_path, monkeypatch):
project.mkdir()
config = runtime / "machine/configuration.json"
config.parent.mkdir(parents=True)
- def configure(provider, *, mode=None):
- defaults = {"schema_version": "loopx_goal_storage_defaults_v0", "new_goal_provider": provider}
- if mode is not None:
- defaults.update(schema_version="loopx_goal_storage_defaults_v1", canonical_creation=True, new_goal_handoff_mode=mode)
+ def configure(provider, *, mode="hard_lease"):
+ defaults = {
+ "schema_version": "loopx_goal_storage_defaults_v1",
+ "new_goal_provider": provider,
+ "canonical_creation": True,
+ "new_goal_handoff_mode": mode,
+ }
config.write_text(json.dumps({"schema_version": "loopx_machine_configuration_v0", "namespaces": {
"goal_storage": defaults}}))
def bootstrap(goal="first", *extra, expected_code=0):
@@ -107,16 +110,23 @@ def test_unconfigured_new_goal_defaults_to_canonical_sqlite_and_retains_later_wr
@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_explicit_canonical_creation_disabled_remains_target_only(environment, provider):
- _, bootstrap, _, _, runtime = environment
+def test_explicit_canonical_creation_disabled_is_rejected(environment, provider):
+ _, bootstrap, _, project, runtime = environment
configuration = runtime / "machine/configuration.json"
configuration.write_text(json.dumps({"schema_version": "loopx_machine_configuration_v0", "namespaces": {
"goal_storage": {"schema_version": "loopx_goal_storage_defaults_v1", "new_goal_provider": provider,
"canonical_creation": False, "new_goal_handoff_mode": "soft_claim"}}}))
- created = bootstrap()
- assert created["storage_target"] == {"schema_version": "loopx_new_goal_storage_target_v0", "provider": provider}
- assert created["storage_selection"]["promotion_performed"] is False
- assert "authority_initialized" not in created["storage_selection"]
+ rejected = bootstrap(expected_code=1)
+ assert rejected["error_code"] == "canonical_authority_required"
+ assert rejected["operation"] == "goal_create"
+ assert rejected["write_admitted"] is False
+ assert rejected["decision_read_from_provider"] is False
+ assert rejected["legacy_fallback_used"] is False
+ assert rejected["recovery"] == {
+ "action": "migrate_goal_authority",
+ "backup_required": True,
+ }
+ assert not (project / ".loopx/registry.json").exists()
@pytest.mark.parametrize("invalid_configuration", ["invalid_json", "invalid_namespace", "directory", "broken_symlink"])
@@ -233,19 +243,28 @@ def test_cli_creation_recovery_cannot_recreate_or_adopt_authority(environment, p
assert not wrong_source.exists()
-def test_pending_creation_uses_frozen_intent_after_machine_default_changes(environment):
- configure, bootstrap, marker, project, _ = environment
- # Independently model the durable boundary: registry/state published, selector absent.
+def test_completed_canonical_creation_ignores_stale_v0_target(environment):
+ from loopx.control_plane.coordination.local_authority import (
+ read_canonical_todos_if_promoted,
+ )
+
+ configure, bootstrap, marker, project, runtime = environment
configure("file")
bootstrap()
+ before = read_canonical_todos_if_promoted(
+ runtime_root=runtime, goal_id="first", include_leases=True,
+ )
registry = project / ".loopx/registry.json"
data = json.loads(registry.read_text())
data["goals"][0].setdefault("coordination", {})["storage_target"] = {
"schema_version": "loopx_new_goal_storage_target_v0", "provider": "sqlite"}
registry.write_text(json.dumps(data))
configure("file")
- assert bootstrap()["storage_selection"]["provider"] == "sqlite"
- assert json.loads(marker().read_text())["provider"] == "sqlite"
+ assert bootstrap()["storage_selection"] is None
+ assert not marker().exists()
+ assert read_canonical_todos_if_promoted(
+ runtime_root=runtime, goal_id="first", include_leases=True,
+ ) == before
@pytest.fixture
@@ -629,7 +648,7 @@ def test_app_creation_selects_server_configured_non_git_workspace(environment, a
@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("relative_runtime", [False, True])
-@pytest.mark.parametrize("mode", [None, "soft_claim", "hard_lease"])
+@pytest.mark.parametrize("mode", ["soft_claim", "hard_lease"])
def test_app_creation_retries_storage_before_reporting_success(environment, app, monkeypatch, provider, relative_runtime, mode):
from loopx.control_plane.coordination import new_goal_storage_transport as goal_storage
from loopx.todos import add_goal_todo
@@ -659,7 +678,11 @@ def storage_effect(method, payload):
return result
return effect(method, payload)
- monkeypatch.setattr(goal_storage, "effect_runtime_result", storage_effect)
+ monkeypatch.setattr(
+ goal_storage,
+ "effect_runtime_result",
+ storage_effect,
+ )
code, preview = request("/api/actions/preview", {
"action_kind": "goal.create", "summary": "Create a Goal",
"normalized_parameters": {
@@ -698,14 +721,13 @@ def storage_effect(method, payload):
assert recovered["proposal"]["receipt"]["outcome"] == "goal_created"
assert selections[-1]["provider"] == provider
assert selections[-1]["promotion_performed"] is False
- if mode is not None:
- from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
- source = read_canonical_todos_if_promoted(runtime_root=project.parent / "runtime", goal_id="recovery", include_leases=True)
- assert source["handoff_mode"] == mode
- assert len(source["todos"]) == 1
- assert (source["todos"][0]["text"], source["todos"][0]["priority"]) == ("[P1] Verify recovery", "P1")
- assert source["source_authority"] == f"{provider}_v0"
- assert selections[-1]["authority_initialized"] is True
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+ source = read_canonical_todos_if_promoted(runtime_root=project.parent / "runtime", goal_id="recovery", include_leases=True)
+ assert source["handoff_mode"] == mode
+ assert len(source["todos"]) == 1
+ assert (source["todos"][0]["text"], source["todos"][0]["priority"]) == ("[P1] Verify recovery", "P1")
+ assert source["source_authority"] == f"{provider}_v0"
+ assert selections[-1]["authority_initialized"] is True
if provider == "sqlite":
assert json.loads(marker("recovery").read_text())["provider"] == provider
else:
@@ -713,7 +735,7 @@ def storage_effect(method, payload):
assert add.call_count == 1
assert request(apply_path, {})[1]["proposal"]["receipt"] == recovered["proposal"]["receipt"]
assert add.call_count == 1
- assert len(selections) == (1 if mode is None else 2)
+ assert len(selections) == 2
@pytest.mark.parametrize("provider", ["file", "sqlite"])
@@ -803,7 +825,8 @@ def test_machine_editor_is_machine_only_and_configuration_rejects_activation():
@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_created_target_is_used_by_reviewed_promotion(environment, provider):
+def test_created_target_is_canonical_and_rejects_legacy_bootstrap(environment, provider):
+ from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
from tests.control_plane.shadow_e2e_fixture import ShadowWorkspace
configure, bootstrap, _, project, runtime = environment
configure(provider)
@@ -813,21 +836,28 @@ def test_created_target_is_used_by_reviewed_promotion(environment, provider):
ws = ShadowWorkspace(registry, runtime, state, "first")
configured = ws.cli("configure-goal", "--coordination-runtime-shadow-file", "--execute")
assert configured["ok"] is True
- assert ws.cli("coordination-shadow", "bootstrap", "--execute")["bootstrap"]["status"] == "applied"
- for n in range(3):
- assert ws.add(f"Qualify creation target {n}")["ok"] is True
- assert ws.drain(budget_seconds="60")["ok"] is True
- assert bootstrap()["storage_selection"]["changed"] is False
- preview = ws.cli("coordination-shadow", "promote", "--handoff-mode-migration", "preserve")
- assert preview["promotion"]["status"] == "preview_ready", preview
- plan = project / "reviewed.json"
- plan.write_text(json.dumps(preview))
- promoted = ws.cli("coordination-shadow", "promote", "--reviewed-plan", str(plan), "--execute")
- assert promoted["promotion"]["canonical_authority"] == f"{provider}_v0", promoted
+ rejected = ws.cli("coordination-shadow", "bootstrap", "--execute", success=False)
+ assert rejected["bootstrap"]["status"] == "failed"
+ assert rejected["bootstrap"]["reason_code"] == "legacy_authority_already_promoted"
+ source = read_canonical_todos_if_promoted(
+ runtime_root=runtime,
+ goal_id="first",
+ include_leases=True,
+ )
+ assert source["source_authority"] == f"{provider}_v0"
+ reconnected = bootstrap()["storage_selection"]
+ assert reconnected["provider"] == provider
+ assert reconnected["authority_initialized"] is True
created = ws.add("Continue on selected provider")
assert ws.cli("todo", "list", "--todo-id", created["todo_id"])["authority_read"]["source_authority"] == f"{provider}_v0"
# The initializer must not override a promoted selection. The separate legacy
# bootstrap command still rejects a fenced Goal before reaching this helper.
from loopx.control_plane.coordination.new_goal_storage_transport import initialize_goal_storage_target
goal = json.loads(registry.read_text())["goals"][0]
- assert initialize_goal_storage_target(runtime, goal)["status"] == "existing_authority_preserved"
+ replayed = initialize_goal_storage_target(
+ runtime,
+ goal,
+ registry_path=registry,
+ )
+ assert replayed["status"] == "replayed"
+ assert replayed["provider"] == provider
diff --git a/tests/control_plane/test_post_writeback_capability_hooks.py b/tests/control_plane/test_post_writeback_capability_hooks.py
index 07839b015b..72e6803857 100644
--- a/tests/control_plane/test_post_writeback_capability_hooks.py
+++ b/tests/control_plane/test_post_writeback_capability_hooks.py
@@ -1378,7 +1378,10 @@ def test_todo_complete_survives_unreadable_periodic_report_machine_store(
"id": "goal-1",
"repo": str(tmp_path),
"state_file": "STATE.md",
- "agents": [{"id": "agent-a"}],
+ "coordination": {
+ "registered_agents": ["agent-a"],
+ "agent_model": "peer_v1",
+ },
}
],
"common_runtime_root": str(runtime_root),
@@ -1386,6 +1389,23 @@ def test_todo_complete_survives_unreadable_periodic_report_machine_store(
),
encoding="utf-8",
)
+ from canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+ from loopx.todos import list_goal_todos
+
+ todos = list_goal_todos(registry_path=registry_path, goal_id="goal-1")["todos"]
+ initialize_canonical_authority(
+ runtime_root,
+ "goal-1",
+ build_todo_runtime_shadow_projection(
+ goal_id="goal-1",
+ todos=todos,
+ handoff_mode="soft_claim",
+ ),
+ state_path=state,
+ )
_write_unreadable_periodic_report_machine_store(runtime_root)
with pytest.warns(UserWarning, match="periodic_report.enabled must be a boolean"):
@@ -1410,7 +1430,7 @@ def test_todo_complete_survives_unreadable_periodic_report_machine_store(
captured = capsys.readouterr()
payload = json.loads(captured.out)
- assert exit_code == 0
+ assert exit_code == 0, json.dumps(payload, indent=2)
assert payload["ok"] is True
assert payload["completed"] is True
diff --git a/tests/control_plane/test_promoted_archive_retention.py b/tests/control_plane/test_promoted_archive_retention.py
deleted file mode 100644
index 95e04a7612..0000000000
--- a/tests/control_plane/test_promoted_archive_retention.py
+++ /dev/null
@@ -1,129 +0,0 @@
-"""Canonical archives retain promoted history, not the complete old source.
-
-This characterizes the existing qualified shadow route. It cannot qualify a
-cold Markdown import or authorize removal of historical source readers.
-"""
-from __future__ import annotations
-
-import json
-import subprocess
-import pytest
-
-from canonical_authority_fixture import isolate_sqlite_runtime
-from shadow_e2e_fixture import REPO, workspace
-from loopx.control_plane.effect_runtime import restart_effect_runtime
-from loopx.rollout_event_log import ROLLOUT_EVENT_SCHEMA_VERSION
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_promoted_archive_scope_and_later_write_recovery(tmp_path, monkeypatch, provider):
- isolate_sqlite_runtime(tmp_path, monkeypatch)
- ws = workspace(tmp_path / "source", bootstrap=False)
- ws.state.write_text(
- "---\ngoal_id: goal-e2e\nhandoff_mode: hard_lease\n---\n\n## Agent Todo\n"
- "- [ ] Continue work\n"
- " \n"
- "- [x] Retained work\n"
- " \n"
- "\n## Completed Work Archive\n"
- "- [x] Referenced work\n"
- " \n"
- "- [x] Unreferenced historical result\n"
- " \n"
- )
- terminal = {
- "schema_version": "task_lease_v0", "goal_id": ws.goal,
- "todo_id": "todo_retained", "owner": "agent-a", "version": 2,
- "lease_epoch": 1, "status": "released",
- "released_at": "2026-09-01T00:00:00Z", "idempotency_key": "old-retained",
- "future_extension": {"missing_value": None, "flag": False},
- }
- orphan = {**terminal, "todo_id": "todo_unrelated", "idempotency_key": "old-orphan"}
- directory = ws.runtime / "goals" / ws.goal / "task-leases"
- directory.mkdir(parents=True)
- for record in (terminal, orphan):
- (directory / (record["todo_id"] + ".json")).write_text(json.dumps(record))
- lease_bytes = {path: path.read_bytes() for path in directory.glob("*.json")}
- rollout = directory.parent / "rollout-event-log.jsonl"
- original_receipt = json.dumps({
- "schema_version": ROLLOUT_EVENT_SCHEMA_VERSION, "kind": "todo_complete",
- "goal_id": ws.goal, "todo_id": "todo_unrelated",
- "timestamp": "2026-09-01T00:00:00Z", "receipt_id": "original-unrelated-receipt",
- }) + "\n"
- rollout.write_text(original_receipt)
- if provider == "sqlite":
- selected = subprocess.run([
- "node", "--no-warnings", "--experimental-strip-types", "--experimental-sqlite",
- str(REPO / "loopx/control_plane/coordination/local_authority_provider.ts"),
- "--runtime-root", str(ws.runtime), "--goal-id", ws.goal, "--execute",
- ], cwd=REPO, capture_output=True, text=True, timeout=45, check=True)
- assert json.loads(selected.stdout)["provider"] == "sqlite"
-
- assert ws.cli("coordination-shadow", "bootstrap", "--execute")["bootstrap"]["status"] == "applied"
- cold_bytes = ws.state.read_bytes()
- refused = ws.cli("coordination-shadow", "promote", success=False)
- assert refused["ok"] is False
- assert refused["promotion"]["reason_code"] == "local_authority_shadow_not_qualified"
- assert ws.state.read_bytes() == cold_bytes
-
- # Real warm-profile operations are not evidence of a cold import.
- captured = {ws.add(f"Independent qualification work {index}")["todo_id"] for index in range(3)}
- assert ws.drain(budget_seconds="60")["ok"] is True
- preview = ws.cli("coordination-shadow", "promote")
- assert preview["promotion"]["status"] == "preview_ready"
- reviewed = tmp_path / "reviewed.json"
- reviewed.write_text(json.dumps(preview))
- source_bytes = ws.state.read_bytes()
- promoted = ws.cli("coordination-shadow", "promote", "--reviewed-plan", str(reviewed), "--execute")
- assert promoted["promotion"]["canonical_authority"] == f"{provider}_v0"
- assert ws.state.read_bytes() == source_bytes
- assert {path: path.read_bytes() for path in lease_bytes} == lease_bytes
- assert rollout.read_text().startswith(original_receipt)
- assert ws.cli("task-lease", "inspect", "--todo-id", "todo_retained")["lease"] == terminal
-
- later = ws.add("Later canonical work")
- ws.state.unlink() # Only the disposable source: canonical recovery must not need it.
- assert restart_effect_runtime()["status"] in {"stopped", "not_running"}
- recovered = ws.cli("coordination-shadow", "recover-promotion", "--reviewed-plan", str(reviewed), "--execute")
- assert recovered["promotion"]["status"] == "replayed"
- current = ws.cli("todo", "list", "--todo-id", later["todo_id"])
- assert current["todo"]["text"] == "[P1] Later canonical work"
- assert current["authority_read"]["legacy_fallback_used"] is False
- assert not ws.state.exists()
-
- archive = tmp_path / "authority.ndjson"
- exported = ws.cli("authority-archive", "export", "--archive", str(archive))
- assert exported["status"] == "exported"
- assert "original-unrelated-receipt" not in archive.read_text()
- digest = exported["archive"]["archive_sha256"]
- for target in ("file", "sqlite"):
- destination = tmp_path / f"restore-{target}"
- restored = ws.cli("authority-archive", "restore", "--archive", str(archive),
- "--archive-sha256", digest, "--destination", str(destination), "--provider", target, "--execute")
- assert restored["status"] == "restored"
- assert restored["execution_authority_granted"] is False
- assert restored["requires_separate_authority_cutover"] is True
- audit = ws.cli("authority-archive", "audit", "--archive", str(archive),
- "--archive-sha256", digest, "--destination", str(destination))
- assert audit["audit"]["status"] == "matched"
-
- # Independently reopen the real restored store, including the other provider.
- module = REPO / f"loopx/control_plane/coordination/{target}_authority_store.ts"
- owner = "FileAuthorityStore" if target == "file" else "SqliteAuthorityStore"
- script = (
- f"import {{{owner}}} from {json.dumps(module.as_uri())};"
- f"const store=new {owner}({json.dumps(str(destination / 'store'))},{json.dumps(ws.goal)});"
- "process.stdout.write(JSON.stringify(await store.loadAuthority()));"
- )
- process = subprocess.run([
- "node", "--no-warnings", "--experimental-strip-types", "--experimental-sqlite",
- "--input-type=module", "-e", script,
- ], capture_output=True, text=True, timeout=45, check=True)
- head = json.loads(process.stdout)["head"]
- assert {row["todo_id"] for row in head["todos"]} == {
- "todo_active", "todo_retained", "todo_prior", later["todo_id"],
- } | captured
- prior = next(row for row in head["todos"] if row["todo_id"] == "todo_prior")
- assert prior["archive_state"] == "archive"
- assert prior["completion_receipt_id"] == "receipt-prior"
- assert head["leases"] == [terminal]
diff --git a/tests/control_plane/test_public_todo_update_plan.py b/tests/control_plane/test_public_todo_update_plan.py
index 789be33c56..b14f465111 100644
--- a/tests/control_plane/test_public_todo_update_plan.py
+++ b/tests/control_plane/test_public_todo_update_plan.py
@@ -1,15 +1,25 @@
"""Public update invariants: a partial edit must not invalidate an armed wait."""
import pytest
+from loopx.control_plane.coordination.local_authority import (
+ LocalCoordinationAuthorityUnavailable,
+ read_canonical_todos_if_promoted,
+)
from loopx.todos import add_goal_todo, update_goal_todo
from loopx.control_plane.testing.canary_harness import run_json_cli_result
from tests.control_plane.test_monitor_followthrough_contract import (
- AGENT_ID, GOAL_ID, _write_fixture,
+ AGENT_ID, GOAL_ID,
)
+from tests.control_plane.canonical_authority_fixture import promoted_create_fixture
def waiting_goal(tmp_path):
- registry, runtime, state = _write_fixture(tmp_path)
+ registry, runtime, state = promoted_create_fixture(
+ tmp_path,
+ goal_id=GOAL_ID,
+ handoff_mode="soft_claim",
+ registered_agents=[AGENT_ID],
+ )
# Initialize historical evidence through the create/import boundary;
# ordinary configuration cannot manufacture an observation generation.
monitor = add_goal_todo(registry_path=registry, goal_id=GOAL_ID, role="agent",
@@ -27,31 +37,44 @@ def add(text):
return registry, runtime, state, waiting, successor
+def current_todo(runtime, todo_id):
+ source = read_canonical_todos_if_promoted(
+ runtime_root=runtime,
+ goal_id=GOAL_ID,
+ )
+ return next(todo for todo in source["todos"] if todo["todo_id"] == todo_id)
+
+
@pytest.mark.parametrize("edit", [{"successor_todo_ids": []}, {"status": "blocked"},
{"task_class": "continuous_monitor"}])
def test_partial_edit_cannot_invalidate_retained_monitor_wait(tmp_path, edit):
registry, _, state, waiting, _ = waiting_goal(tmp_path)
before = state.read_bytes()
- with pytest.raises(ValueError, match="external-wait"):
+ with pytest.raises(
+ (ValueError, LocalCoordinationAuthorityUnavailable),
+ match="external-wait",
+ ):
update_goal_todo(registry_path=registry, goal_id=GOAL_ID,
todo_id=waiting["todo_id"], agent_id=AGENT_ID, **edit)
assert state.read_bytes() == before
def test_clear_condition_and_successors_together_is_valid(tmp_path):
- registry, _, state, waiting, _ = waiting_goal(tmp_path)
- result = update_goal_todo(registry_path=registry, goal_id=GOAL_ID,
+ registry, runtime, state, waiting, _ = waiting_goal(tmp_path)
+ update_goal_todo(registry_path=registry, goal_id=GOAL_ID,
todo_id=waiting["todo_id"], agent_id=AGENT_ID,
clear_resume_when=True, successor_todo_ids=[])
- assert result["resume_when"] is None
- assert result["resume_monitor_generation"] is None
- assert result["successor_todo_ids"] == []
+ result = current_todo(runtime, waiting["todo_id"])
+ assert result.get("resume_when") is None
+ assert result.get("resume_monitor_generation") is None
+ assert result.get("successor_todo_ids", []) == []
def test_copy_edit_preserves_existing_wait_fence(tmp_path):
- registry, _, _, waiting, _ = waiting_goal(tmp_path)
- result = update_goal_todo(registry_path=registry, goal_id=GOAL_ID,
+ registry, runtime, _, waiting, _ = waiting_goal(tmp_path)
+ update_goal_todo(registry_path=registry, goal_id=GOAL_ID,
todo_id=waiting["todo_id"], agent_id=AGENT_ID, note="Clarify context")
+ result = current_todo(runtime, waiting["todo_id"])
assert result["resume_when"].startswith("monitor_changed:")
assert result["resume_monitor_generation"] == 3
assert "external_wait_transition" not in result
@@ -71,18 +94,23 @@ def test_real_cli_rejects_partial_wait_edit_and_supports_explicit_repair(tmp_pat
assert state.read_bytes() == before
-def test_public_update_uses_one_scope_wait_field_crossing(tmp_path, monkeypatch):
- from loopx.control_plane.todos import line_update, authoring_scope, resume_condition
+def test_public_update_commits_through_canonical_transaction(tmp_path, monkeypatch):
+ from loopx.control_plane.todos import authoring_scope, provider_update, resume_condition
+
registry, _, _, waiting, successor = waiting_goal(tmp_path)
- actual = line_update.effect_runtime_result
+ actual = provider_update.effect_runtime_result
calls = []
- def traced(method, params):
+
+ def traced(method, params, **kwargs):
calls.append(method)
- return actual(method, params)
- for module in (line_update, authoring_scope, resume_condition):
+ return actual(method, params, **kwargs)
+
+ for module in (provider_update, authoring_scope, resume_condition):
monkeypatch.setattr(module, "effect_runtime_result", traced)
- result = update_goal_todo(registry_path=registry, goal_id=GOAL_ID,
+ update_goal_todo(registry_path=registry, goal_id=GOAL_ID,
todo_id=waiting["todo_id"], agent_id=AGENT_ID,
successor_todo_ids=[successor["todo_id"]])
+ result = current_todo(tmp_path / "runtime", waiting["todo_id"])
assert result["resume_monitor_generation"] == 3
- assert calls == ["todo.public_update.plan"]
+ assert calls.count("coordination.local_authority.todo_update") == 1
+ assert "todo.public_update.plan" not in calls
diff --git a/tests/control_plane/test_quota_authority_settlement_journey.py b/tests/control_plane/test_quota_authority_settlement_journey.py
index 8810789fb9..10aec6500e 100644
--- a/tests/control_plane/test_quota_authority_settlement_journey.py
+++ b/tests/control_plane/test_quota_authority_settlement_journey.py
@@ -80,8 +80,8 @@ def test_stale_markdown_cannot_admit_terminal_or_blocked_work(tmp_path, provider
def test_exact_selection_reaches_work_beyond_display_limits(tmp_path, provider):
project, runtime, registry, path, prefix = _source(tmp_path, provider=provider)
if provider != "legacy":
- # Canonical Todo admission does not need displayed rows. Goal intent
- # and constraints remain required independently of the Todo provider.
+ # Goal intent remains readable, while its bounded Todo display cannot
+ # supply the exact candidate from the canonical provider.
path.write_text(prefix)
code, guard = _guard(project, runtime, registry)
assert code == 0, guard
@@ -286,8 +286,8 @@ def run(*args):
assert code == 0 and migrated["status"] == "applied", migrated
assert Path(migrated["backup_path"]).is_file()
assert migrated["execution_authority_granted"] is False
- # Remove Todo display while retaining the independent Goal frame.
- path.write_text(prefix)
+ goal_context = path.read_text().split("## Agent Todo")[0]
+ path.write_text(goal_context + "## Agent Todo\n\n")
code, guard = _guard(project, runtime, registry)
assert code == 0 and guard["decision"] == "run", guard
diff --git a/tests/control_plane/test_quota_monitor_clock.py b/tests/control_plane/test_quota_monitor_clock.py
index a6f4b40b5f..fff0672c23 100644
--- a/tests/control_plane/test_quota_monitor_clock.py
+++ b/tests/control_plane/test_quota_monitor_clock.py
@@ -13,7 +13,6 @@
from loopx.control_plane.todos.active_state_todo_parser import parse_active_state_todos
from loopx.control_plane.todos import quota_selection
from loopx.control_plane.todos.quota_summary import summarize_user_todos_for_quota
-from loopx.todos import list_goal_todos
from tests.control_plane.test_monitor_followthrough_contract import (
AGENT_ID, GOAL_ID, _add_monitor, _write_fixture,
)
@@ -94,7 +93,7 @@ def test_gap_presentation_order_does_not_inherit_claim_or_profile_order() -> Non
assert result["monitor_schedule_gap_items"][0]["todo_id"] == "todo_unclaimed"
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_public_quota_gap_count_uses_real_full_source_without_writing_it(tmp_path, monkeypatch, provider) -> None:
isolate_sqlite_runtime(tmp_path, monkeypatch)
runtime, registry, state = tmp_path / "runtime", tmp_path / "registry.json", tmp_path / "state.md"
@@ -110,13 +109,12 @@ def test_public_quota_gap_count_uses_real_full_source_without_writing_it(tmp_pat
write_fixture_registry(project=tmp_path, runtime_root=runtime, registry_path=registry,
goal_id="goal-a", domain="monitor-clock", adapter_kind="generic_project_goal_v0",
state_file=str(state), registered_agents=["agent-a", "agent-b"], quota_allowed_slots=None)
- if provider != "legacy":
- goal = json.loads(registry.read_text())["goals"][0]
- fields = parse_active_state_todos(state.read_text(), goal=goal, item_limit=None)
- projection = build_todo_runtime_shadow_projection(goal_id="goal-a", todos=fields["agent_todos"]["items"],
- handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, "goal-a", projection, state_path=state, provider=provider)
- state.unlink()
+ goal = json.loads(registry.read_text())["goals"][0]
+ fields = parse_active_state_todos(state.read_text(), goal=goal, item_limit=None)
+ projection = build_todo_runtime_shadow_projection(goal_id="goal-a", todos=fields["agent_todos"]["items"],
+ handoff_mode="soft_claim")
+ initialize_canonical_authority(runtime, "goal-a", projection, state_path=state, provider=provider)
+ state.unlink()
before = state.read_bytes() if state.exists() else None
code, packet = run_json_cli_result("quota", "should-run", "--goal-id", "goal-a", "--agent-id", "agent-a",
"--include-detail", "agent-todos", "--scan-path", str(tmp_path), registry_path=registry, runtime_root=runtime)
@@ -144,12 +142,12 @@ def unavailable(*_args, **_kwargs):
assert source == before
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_cli_due_order_and_successive_turns_preserve_exact_poll_settlement(
tmp_path, monkeypatch, provider,
) -> None:
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state = _write_fixture(tmp_path)
+ registry, runtime, state = _write_fixture(tmp_path, provider=provider)
# Deliberately create out of deadline order, so display order cannot
# accidentally satisfy the independently specified scheduling oracle.
monitors = [
@@ -160,13 +158,7 @@ def test_cli_due_order_and_successive_turns_preserve_exact_poll_settlement(
_add_monitor(registry, text="[P1] Observe middle target", target_key="middle",
next_due_at="2001-01-01T00:00:00Z"),
]
- if provider != "legacy":
- rows = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]
- projection = build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, todos=rows, handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, GOAL_ID, projection,
- state_path=state, provider=provider)
- state.unlink() # Canonical authority, not a stale Markdown fallback.
+ state.unlink() # Canonical authority, not a stale Markdown fallback.
def call(*args):
code, result = run_json_cli_result(
@@ -177,6 +169,12 @@ def call(*args):
scope = ["--goal-id", GOAL_ID, "--agent-id", AGENT_ID,
"--runtime-profile", "codex_app_ssh_goal"]
expected = [monitors[1], monitors[2], monitors[0]]
+ expected_targets = ["oldest", "middle", "recent"]
+ expected_due_at = {
+ monitors[0]["todo_id"]: "2002-01-01T00:00:00Z",
+ monitors[1]["todo_id"]: "2000-01-01T00:00:00Z",
+ monitors[2]["todo_id"]: "2001-01-01T00:00:00Z",
+ }
previous_guard = None
for ordinal, item in enumerate(expected):
turn = f"monitor-order-{ordinal}"
@@ -191,7 +189,7 @@ def call(*args):
if previous_guard:
assert call(*previous_guard)["should_run"] is False
poll = ["quota", "monitor-poll", *scope, "--turn-instance-id", turn,
- "--todo-id", item["todo_id"], "--target-key", item["target_key"],
+ "--todo-id", item["todo_id"], "--target-key", expected_targets[ordinal],
"--result-hash", f"checked-{ordinal}",
"--next-due-at", "2099-01-01T00:00:00Z", "--execute"]
settled = call(*poll)
@@ -205,7 +203,7 @@ def call(*args):
records = call("todo", "list", "--goal-id", GOAL_ID, "--role", "agent")["todos"]
by_id = {row["todo_id"]: row for row in records}
for unpolled in expected[ordinal + 1:]:
- assert by_id[unpolled["todo_id"]]["next_due_at"] == unpolled["next_due_at"]
+ assert by_id[unpolled["todo_id"]]["next_due_at"] == expected_due_at[unpolled["todo_id"]]
assert not by_id[unpolled["todo_id"]].get("last_checked_at")
previous_guard = guard
events = [json.loads(line) for line in
diff --git a/tests/control_plane/test_quota_settlement_cli.py b/tests/control_plane/test_quota_settlement_cli.py
index e31849371c..99171ee8b2 100644
--- a/tests/control_plane/test_quota_settlement_cli.py
+++ b/tests/control_plane/test_quota_settlement_cli.py
@@ -234,6 +234,43 @@ def _run_cli(
return result.returncode, json.loads(result.stdout)
+def _initialize_fixture_authority(
+ project: Path,
+ runtime: Path,
+ *,
+ provider: str = "file",
+ handoff_mode: str = "soft_claim",
+ leases: list[dict[str, Any]] | None = None,
+) -> None:
+ from canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+
+ state_path = project / f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md"
+ summary = parse_active_state_todos(
+ state_path.read_text(encoding="utf-8"),
+ state_path=state_path,
+ item_limit=None,
+ )
+ todos = [
+ *(summary.get("user_todos", {}).get("items") or []),
+ *(summary.get("agent_todos", {}).get("items") or []),
+ ]
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=todos,
+ handoff_mode=handoff_mode,
+ leases=leases or [],
+ ),
+ state_path=state_path,
+ provider=provider,
+ )
+
+
def _run_generated_cli(
command: str,
*,
@@ -895,6 +932,7 @@ def test_codex_app_refresh_stages_validated_memory_and_spend_finalizes_hook(
),
encoding="utf-8",
)
+ _initialize_fixture_authority(project, runtime)
turn_id = "turn-reward-memory-app"
binding = (
"--agent-id",
@@ -1037,6 +1075,7 @@ def test_typed_outcome_gap_settles_exact_turn_without_becoming_progress(
)
_configure_completion_validation_todo(project)
_configure_selectable_alternative(project)
+ _initialize_fixture_authority(project, runtime)
turn_id = "turn-typed-blocker-settlement"
binding = (
"--agent-id",
@@ -1123,22 +1162,6 @@ def test_typed_outcome_gap_settles_exact_turn_without_becoming_progress(
assert mismatch_rc == 1, mismatch
assert "settlement binding does not match" in mismatch["error"]
- unscheduled_rc, unscheduled = _run_cli(
- registry_path,
- runtime,
- *common_refresh_args,
- "--progress-result-class",
- "blocked",
- "--progress-blocker-id",
- "blocker:runtime-boundary",
- "--progress-evidence-id",
- "evidence:runtime-boundary",
- cwd=project,
- )
- assert unscheduled_rc == 1, unscheduled
- assert "pending resume_when=resume_at" in unscheduled["error"]
- assert _classification_count(runtime, "typed_blocker_writeback") == 0
-
due_at = (datetime.now(timezone.utc) + timedelta(minutes=5)).replace(
microsecond=0
).isoformat().replace("+00:00", "Z")
@@ -1249,6 +1272,7 @@ def test_typed_blocked_retry_without_successor_defers_the_only_todo(
) -> None:
project, runtime, registry_path = _write_fixture(tmp_path)
_configure_completion_validation_todo(project)
+ _initialize_fixture_authority(project, runtime)
turn_id = "turn-typed-blocker-only-todo"
binding = (
"--agent-id", AGENT_ID,
@@ -1307,6 +1331,7 @@ def test_scheduler_cache_misses_after_todo_state_changes_without_run(
tmp_path: Path,
) -> None:
project, runtime, registry_path = _write_fixture(tmp_path)
+ _initialize_fixture_authority(project, runtime)
guard = (
"quota",
"should-run",
@@ -1347,7 +1372,16 @@ def test_scheduler_cache_misses_after_todo_state_changes_without_run(
"resume_at:2099-01-01T00:00:00Z",
)
assert rc == 0, updated
- assert updated["status"] == "deferred"
+ assert updated["status"] == "applied"
+ listed_rc, listed = _run_cli(
+ registry_path, runtime, "todo", "list", "--goal-id", GOAL_ID
+ )
+ assert listed_rc == 0, listed
+ deferred = next(
+ item for item in listed["todos"] if item["todo_id"] == TODO_ID
+ )
+ assert deferred["status"] == "deferred"
+ assert deferred["resume_when"] == "resume_at:2099-01-01T00:00:00Z"
assert (index_path.read_bytes() if index_path.exists() else None) == index_before
rc, refreshed = _run_cli(
@@ -1383,6 +1417,7 @@ def test_scheduler_cache_tracks_todo_changes_beyond_display_limit(
state_path.read_text(encoding="utf-8").rstrip() + "\n" + tail_rows,
encoding="utf-8",
)
+ _initialize_fixture_authority(project, runtime)
guard = (
"quota",
"should-run",
@@ -2316,6 +2351,7 @@ def test_recovery_does_not_bind_current_replan_and_reenters_same_turn(
# append-only heartbeat receipt. The same recovery Turn must then be able
# to bind an independent successor instead of preserving an obsolete replan.
state_path.write_text(state_before_replan, encoding="utf-8")
+ _initialize_fixture_authority(project, runtime)
wait_rc, wait = _run_cli(
registry_path,
@@ -2336,9 +2372,13 @@ def test_recovery_does_not_bind_current_replan_and_reenters_same_turn(
ALTERNATIVE_TODO_ID,
)
assert wait_rc == 0, wait
- assert wait["external_wait_transition"]["successor_todo_ids"] == [
- ALTERNATIVE_TODO_ID
- ]
+ listed_rc, listed = _run_cli(
+ registry_path, runtime, "todo", "list", "--goal-id", GOAL_ID
+ )
+ assert listed_rc == 0, listed
+ waiting = next(item for item in listed["todos"] if item["todo_id"] == TODO_ID)
+ assert waiting["resume_when"] == f"monitor_changed:{DUE_MONITOR_TODO_ID}"
+ assert waiting["successor_todo_ids"] == [ALTERNATIVE_TODO_ID]
current_turn_id = recovery["heartbeat_receipt"]["turn_instance_id"]
resumed_rc, resumed = _run_cli(
@@ -2606,7 +2646,7 @@ def test_reopened_prior_turn_recovers_before_its_bound_successor(
assert _spend_run_count(runtime) == 1
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("hidden_count", [0, 6])
def test_prior_host_closeout_survives_hidden_todo_lifecycle(
tmp_path: Path,
@@ -2714,7 +2754,7 @@ def test_prior_host_closeout_survives_hidden_todo_lifecycle(
assert resumed["quota"]["spent_slots"] == prior["quota"]["spent_slots"]
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_prior_host_closeout_reads_archived_exact_todo(
tmp_path: Path,
provider: str,
@@ -2845,6 +2885,7 @@ def test_standard_codex_app_settlement_is_receipted_and_idempotent(
) -> None:
project, runtime, registry_path = _write_fixture(tmp_path)
_configure_read_only_todo(project)
+ _initialize_fixture_authority(project, runtime)
binding = (
"--agent-id",
AGENT_ID,
@@ -3262,6 +3303,7 @@ def test_same_turn_identityless_guard_upgrades_and_settles_full_chain(
),
encoding="utf-8",
)
+ _initialize_fixture_authority(project, runtime)
binding = (
"--agent-id",
AGENT_ID,
@@ -5186,6 +5228,7 @@ def test_pending_action_selection_does_not_preempt_newly_due_monitor(
assert "settlement_identity" not in first["heartbeat_receipt"]
_append_newly_due_monitor(project)
+ _initialize_fixture_authority(project, runtime)
selected_rc, selected = _run_cli(
registry_path,
runtime,
@@ -5363,6 +5406,7 @@ def test_pending_action_selection_can_bind_exact_newly_due_monitor(
assert "settlement_identity" not in first["heartbeat_receipt"]
_append_newly_due_monitor(project)
+ _initialize_fixture_authority(project, runtime)
selected_rc, selected = _run_cli(
registry_path,
runtime,
@@ -5445,6 +5489,7 @@ def test_prior_monitor_turn_accepts_exact_committed_receipt(
) -> None:
project, runtime, registry_path = _write_fixture(tmp_path)
_append_newly_due_monitor(project)
+ _initialize_fixture_authority(project, runtime)
prior_turn_id = "turn-monitor-exact-closeout"
guard_args = (
"quota",
@@ -5520,6 +5565,7 @@ def test_prior_monitor_turn_recovery_rejects_wrong_identity_receipt(
) -> None:
project, runtime, registry_path = _write_fixture(tmp_path)
_append_newly_due_monitor(project)
+ _initialize_fixture_authority(project, runtime)
prior_turn_id = "turn-monitor-wrong-identity"
guard_args = (
"quota",
@@ -5626,6 +5672,7 @@ def test_receipt_bound_advancement_turn_records_multiple_due_monitors(
("todo_fixture_monitor_gamma", "monitor-gamma"),
]
_append_due_monitors(project, monitors)
+ _initialize_fixture_authority(project, runtime)
turn_instance_id = "turn-multiple-auxiliary-monitors"
guard_args = (
"quota",
@@ -5811,6 +5858,7 @@ def test_todoless_autonomous_replan_settles_quota_refresh_spend_chain(
json.dumps(registry, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
+ _initialize_fixture_authority(project, runtime)
turn_instance_id = "turn-autonomous-replan-settlement-1"
guard_rc, guard = _run_cli(
@@ -6735,6 +6783,7 @@ def test_same_turn_receipt_replay_defers_newly_due_higher_priority_monitor(
assert first["heartbeat_receipt"]["settlement_identity"]["todo_id"] == TODO_ID
_append_newly_due_monitor(project)
+ _initialize_fixture_authority(project, runtime)
capability_args = (
"--available-capability",
"network",
@@ -6890,12 +6939,28 @@ def test_same_turn_receipt_replay_defers_newly_due_higher_priority_monitor(
assert resumed_turn["selected_todo"]["todo_id"] == DUE_MONITOR_TODO_ID
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_read_only_settlement_omits_non_causal_delivery_workspace(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
provider: str,
) -> None:
+ # The two legal completion phases may commit within one clock tick. Keep
+ # the real CLI, authority and hook journal; freeze only their commit clock.
+ run = subprocess.run
+
+ def frozen_completion_clock(argv, *args, **kwargs):
+ if isinstance(argv, list) and argv[1:3] == ["-m", "loopx.cli"]:
+ program = (
+ "import loopx.control_plane.todos.provider_terminal_lifecycle as native; "
+ "clock=lambda:'2026-09-02T12:00:00+00:00'; "
+ "native.now_local=clock; "
+ "from loopx.cli import main; raise SystemExit(main())"
+ )
+ argv = [argv[0], "-c", program, *argv[3:]]
+ return run(argv, *args, **kwargs)
+
+ monkeypatch.setattr(subprocess, "run", frozen_completion_clock)
project, runtime, registry_path = _write_fixture(tmp_path)
registry = json.loads(registry_path.read_text(encoding="utf-8"))
registry["goals"][0]["control_plane"] = {
@@ -7217,6 +7282,7 @@ def test_same_turn_terminal_receipt_replay_preempts_autonomous_replan(
) -> None:
project, runtime, registry_path = _write_fixture(tmp_path)
_configure_read_only_todo(project)
+ _initialize_fixture_authority(project, runtime)
guard_args = (
"quota",
"should-run",
@@ -7326,6 +7392,7 @@ def test_settled_turn_defers_prior_unsettled_history_to_fresh_turn(
) -> None:
project, runtime, registry_path = _write_fixture(tmp_path)
_configure_selectable_alternative(project)
+ _initialize_fixture_authority(project, runtime)
guard_args = (
"quota",
"should-run",
@@ -7462,122 +7529,3 @@ def test_settled_turn_defers_prior_unsettled_history_to_fresh_turn(
assert fresh["unsettled_host_turn_recovery"]["binding_id"] == (
ALTERNATIVE_TODO_ID
)
-
-
-def test_legacy_read_only_workspace_mismatch_fails_then_corrects_from_todo_contract(
- tmp_path: Path,
-) -> None:
- project, runtime, registry_path = _write_fixture(tmp_path)
- state_path = _configure_read_only_todo(project)
- _initialize_git_checkout(project)
- binding = (
- "--agent-id",
- AGENT_ID,
- "--todo-id",
- TODO_ID,
- "--turn-instance-id",
- TURN_ID,
- )
-
- guard_rc, guard = _run_cli(
- registry_path,
- runtime,
- "quota",
- "should-run",
- "--codex-app",
- "--goal-id",
- GOAL_ID,
- "--agent-id",
- AGENT_ID,
- "--turn-instance-id",
- TURN_ID,
- "--scan-path",
- str(project),
- )
- assert guard_rc == 0, guard
- _strip_heartbeat_workspace_causality(runtime)
-
- complete_rc, complete = _run_cli(
- registry_path,
- runtime,
- "todo",
- "complete",
- "--goal-id",
- GOAL_ID,
- "--todo-id",
- TODO_ID,
- "--agent-id",
- AGENT_ID,
- "--claimed-by",
- AGENT_ID,
- "--evidence",
- "legacy read-only characterization validated",
- "--no-follow-up",
- )
- assert complete_rc == 0, complete
-
- refresh_rc, refresh = _run_cli(
- registry_path,
- runtime,
- "refresh-state",
- "--goal-id",
- GOAL_ID,
- "--classification",
- "legacy_read_only_characterization_complete",
- "--delivery-batch-scale",
- "single_surface",
- "--delivery-outcome",
- "outcome_progress",
- "--delivery-workspace-path",
- str(project),
- *binding,
- "--no-global-sync",
- "--suppress-external-sinks",
- )
- assert refresh_rc == 0, refresh
- assert refresh["delivery_workspace"]["workspace_kind"] == "canonical_checkout"
-
- causal_state = state_path.read_text(encoding="utf-8")
- state_path.write_text(
- causal_state.replace(
- "continuation_policy=same_agent_non_delivery "
- "required_capabilities=shell%2Cfilesystem_read ",
- "",
- ),
- encoding="utf-8",
- )
- spend_args = (
- "quota",
- "spend-slot",
- "--goal-id",
- GOAL_ID,
- "--slots",
- "1",
- "--source",
- "heartbeat",
- "--execute",
- *binding,
- "--scan-path",
- str(project),
- )
- failed_rc, failed = _run_cli(registry_path, runtime, *spend_args)
- assert failed_rc == 1, failed
- assert failed["workspace_guard"]["current_workspace"] == "foreign_git_worktree"
- assert _spend_run_count(runtime) == 0
-
- state_path.write_text(causal_state, encoding="utf-8")
- corrected_rc, corrected = _run_cli(registry_path, runtime, *spend_args)
- replay_rc, replay = _run_cli(registry_path, runtime, *spend_args)
-
- assert corrected_rc == 0, corrected
- assert corrected["delivery_workspace_causality"]["source"] == (
- "completed_todo_contract_fallback"
- )
- assert corrected["delivery_workspace_causality"]["requirement"] == ("not_required")
- assert corrected["delivery_workspace_validated"] is False
- assert replay_rc == 0, replay
- assert replay["idempotent_replay"] is True
- assert [
- receipt["step_kind"] for receipt in replay["settlement_result"]["receipts"]
- ] == ["validation", "durable_writeback", "quota_spend"]
- assert _spend_run_count(runtime) == 1
diff --git a/tests/control_plane/test_recovery_due_successor_identity.py b/tests/control_plane/test_recovery_due_successor_identity.py
index 804754a2ae..9ab8e6a342 100644
--- a/tests/control_plane/test_recovery_due_successor_identity.py
+++ b/tests/control_plane/test_recovery_due_successor_identity.py
@@ -29,7 +29,7 @@
)
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("crowded", [False, True])
def test_interrupted_binding_survives_new_due_successor(
tmp_path: Path,
@@ -101,30 +101,29 @@ def cli(*args: str) -> tuple[int, dict]:
)
)
- if provider != "legacy":
- rc, listed = cli("todo", "list", "--goal-id", GOAL_ID, "--limit", "200")
- assert rc == 0, listed
- initialize_canonical_authority(
- runtime,
- GOAL_ID,
- build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID,
- todos=[
- canonical_todo_read_record(
- structured_todo_item(
- row,
- role="agent",
- source_section="Agent Todo",
- )
+ rc, listed = cli("todo", "list", "--goal-id", GOAL_ID, "--limit", "200")
+ assert rc == 0, listed
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=[
+ canonical_todo_read_record(
+ structured_todo_item(
+ row,
+ role="agent",
+ source_section="Agent Todo",
)
- for row in listed["todos"]
- ],
- handoff_mode="hard_lease",
- leases=[],
- ),
- state_path=state,
- provider=provider,
- )
+ )
+ for row in listed["todos"]
+ ],
+ handoff_mode="hard_lease",
+ leases=[],
+ ),
+ state_path=state,
+ provider=provider,
+ )
old_turn, new_turn = "interrupted-original", "current-after-interruption"
old_binding = (
@@ -158,7 +157,8 @@ def cli(*args: str) -> tuple[int, dict]:
GOAL_ID,
"--role",
"agent",
- *(("--operation-id", "add-new-due-successor") if provider != "legacy" else ()),
+ "--operation-id",
+ "add-new-due-successor",
"--text",
"[P0] Evaluate the fixed deadline once runnable.",
"--priority",
@@ -233,9 +233,6 @@ def cli(*args: str) -> tuple[int, dict]:
assert peer["heartbeat_receipt"]["status"] == "not_committed"
assert _spend_run_count(runtime) == 0
- if provider == "legacy":
- # A legacy writer has no promoted Turn-owned retry closeout.
- return
before = state.read_bytes()
refresh_args = (
"refresh-state",
diff --git a/tests/control_plane/test_refresh_state_replan_gate.py b/tests/control_plane/test_refresh_state_replan_gate.py
index 6de1158ace..66890149d6 100644
--- a/tests/control_plane/test_refresh_state_replan_gate.py
+++ b/tests/control_plane/test_refresh_state_replan_gate.py
@@ -1043,6 +1043,21 @@ def test_todo_complete_then_refresh_cli_projects_direct_settlement(
),
encoding="utf-8",
)
+ from canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+
+ initialize_canonical_authority(
+ runtime_root,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=[],
+ handoff_mode="soft_claim",
+ ),
+ state_path=state,
+ )
assert cli_main(
[
"--registry",
@@ -1190,7 +1205,7 @@ def test_todo_complete_then_refresh_cli_projects_direct_settlement(
),
],
)
-def test_completed_todo_projects_executable_identity_repair(
+def test_completed_todo_identity_repair_preserves_settlement_fence(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
stored_completion_key: str | None,
@@ -1201,6 +1216,7 @@ def test_completed_todo_projects_executable_identity_repair(
state.write_text(
"---\n"
f"goal_id: {GOAL_ID}\n"
+ "handoff_mode: soft_claim\n"
"updated_at: 2026-08-22T00:00:00+00:00\n"
"---\n\n"
"## User Todo / Owner Review Reading Queue\n\n"
@@ -1243,6 +1259,30 @@ def test_completed_todo_projects_executable_identity_repair(
),
encoding="utf-8",
)
+ from canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.authority_source_capture import (
+ build_runtime_shadow_source_snapshot,
+ )
+ from loopx.history import load_registry
+
+ runtime_root = tmp_path / "runtime"
+ goal = next(
+ goal
+ for goal in load_registry(registry_path)["goals"]
+ if goal["id"] == GOAL_ID
+ )
+ projection, _ = build_runtime_shadow_source_snapshot(
+ goal=goal,
+ runtime_root=runtime_root,
+ state_path=state,
+ registry_path=registry_path,
+ )
+ initialize_canonical_authority(
+ runtime_root,
+ GOAL_ID,
+ projection,
+ state_path=state,
+ )
assert cli_main(
[
@@ -1294,7 +1334,15 @@ def test_completed_todo_projects_executable_identity_repair(
assert "--turn-instance-id" not in action
action_tokens = shlex.split(action)
loopx_index = action_tokens.index("loopx")
- assert cli_main(
+ from loopx.control_plane.coordination.local_authority import (
+ read_canonical_todos_if_promoted,
+ )
+
+ before_state = state.read_bytes()
+ before_authority = read_canonical_todos_if_promoted(
+ runtime_root=runtime_root, goal_id=GOAL_ID,
+ )
+ exit_code = cli_main(
[
"--registry",
str(registry_path),
@@ -1302,9 +1350,23 @@ def test_completed_todo_projects_executable_identity_repair(
"json",
*action_tokens[loopx_index + 1 :],
]
- ) == 0
+ )
repaired = json.loads(capsys.readouterr().out)
-
+ if stored_completion_key is not None:
+ # An imported scoped identity is not proof of its original settlement.
+ # Real settled-turn success/replay is covered by the declared terminal
+ # settlement CLI journey; this fixture deliberately has no receipts.
+ assert exit_code == 1, repaired
+ assert repaired["error_code"] == "invalid_todo_completion_transaction"
+ assert "original quota identity and settled receipts" in repaired["error"]
+ assert state.read_bytes() == before_state
+ assert read_canonical_todos_if_promoted(
+ runtime_root=runtime_root, goal_id=GOAL_ID,
+ ) == before_authority
+ assert not (runtime_root / "goals" / GOAL_ID / "runs" / "index.jsonl").exists()
+ return
+
+ assert exit_code == 0, repaired
assert repaired["completion_continuation"] == "no_followup"
assert repaired["completion_recovery"] == (
"lifecycle_reentry_terminal_closeout"
diff --git a/tests/control_plane/test_replan_completion_validation.py b/tests/control_plane/test_replan_completion_validation.py
index 9bf87993b2..5622ff6d34 100644
--- a/tests/control_plane/test_replan_completion_validation.py
+++ b/tests/control_plane/test_replan_completion_validation.py
@@ -29,6 +29,7 @@
TODO_ID,
_configure_completion_validation_todo,
_configure_selectable_alternative,
+ _initialize_fixture_authority,
_run_cli,
_spend_run_count,
_write_fixture,
@@ -78,6 +79,16 @@ def test_qualified_path_replan_settles_without_completing_validation_todo(
project, runtime, registry = _write_fixture(tmp_path)
state_path = _configure_completion_validation_todo(project)
_configure_selectable_alternative(project)
+ _initialize_fixture_authority(project, runtime)
+ from loopx.control_plane.todos.provider_projection import (
+ project_current_canonical_todos,
+ )
+
+ project_current_canonical_todos(
+ registry_path=registry,
+ runtime_root=runtime,
+ goal_id=GOAL_ID,
+ )
initial_state = state_path.read_text(encoding="utf-8")
turn_id = "turn-validation-bearing-replan"
diff --git a/tests/control_plane/test_replan_semantic_action_behavior.py b/tests/control_plane/test_replan_semantic_action_behavior.py
index 0312184c70..7597ffa0c0 100644
--- a/tests/control_plane/test_replan_semantic_action_behavior.py
+++ b/tests/control_plane/test_replan_semantic_action_behavior.py
@@ -409,7 +409,7 @@ def test_model_exits_after_replan_confirms_goal_coverage_is_exhausted(
assert receipt["semantic_reentry"] == {
"decision": "skip",
"effective_action": "monitor_quiet_skip",
- "replan_rule": "future_monitor_wait",
+ "replan_rule": "terminal_coverage_exhausted",
"replan_closed": True,
"exited": True,
}
@@ -767,6 +767,20 @@ def test_successor_dry_run_shares_quota_agent_scope_for_user_gates(
fixture.project_root / ".loopx" / "registry.json",
):
registry_path.write_text(registry_text, encoding="utf-8")
+ from loopx.todos import add_goal_todo
+
+ added = add_goal_todo(
+ registry_path=fixture.global_registry_path,
+ runtime_root_arg=str(fixture.runtime_root),
+ goal_id="replan-semantic-action-fixture",
+ role="user",
+ text="Review the other agent's independent delivery.",
+ priority="P0",
+ task_class="user_action",
+ bound_agent=other_agent_id,
+ operation_id="fixture-other-agent-review",
+ )
+ assert added["added"] is True
state_path = (
fixture.project_root
/ ".codex"
@@ -774,18 +788,6 @@ def test_successor_dry_run_shares_quota_agent_scope_for_user_gates(
/ "replan-semantic-action-fixture"
/ "ACTIVE_GOAL_STATE.md"
)
- state_text = state_path.read_text(encoding="utf-8").replace(
- "## Agent Todo\n",
- (
- "## User Todo / Owner Review Reading Queue\n\n"
- "- [ ] [P0] Review the other agent's independent delivery.\n"
- " \n\n"
- "## Agent Todo\n"
- ),
- )
- state_path.write_text(state_text, encoding="utf-8")
quota = json.loads(
_execute_loopx(
diff --git a/tests/control_plane/test_replan_successor_durable_ack.py b/tests/control_plane/test_replan_successor_durable_ack.py
index 7ae86771bb..50cb72a47e 100644
--- a/tests/control_plane/test_replan_successor_durable_ack.py
+++ b/tests/control_plane/test_replan_successor_durable_ack.py
@@ -40,6 +40,11 @@ def successor_state(obligation_id: str, *, owner: str = AGENT) -> str:
def test_cli_completed_todo_successor_refresh_resets_periodic_window(tmp_path: Path, capsys) -> None:
+ from canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+
project = tmp_path / "project"
project.mkdir()
state = project / "ACTIVE_GOAL_STATE.md"
@@ -56,6 +61,16 @@ def test_cli_completed_todo_successor_refresh_resets_periodic_window(tmp_path: P
"coordination": {"agent_model": "peer_v1", "registered_agents": [AGENT]},
"execution_profile": {"replan_after_completed_todos": 5},
}]}))
+ initialize_canonical_authority(
+ runtime,
+ GOAL,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL,
+ todos=[],
+ handoff_mode="soft_claim",
+ ),
+ state_path=state,
+ )
obligation = autonomous_replan_obligation_from_runs(runs, agent_todos={}, agent_id=AGENT)
assert obligation is not None
common = ["--registry", str(registry), "--runtime-root", str(runtime), "--format", "json"]
@@ -261,11 +276,19 @@ def call(*args: str, expected_error: str | None = None) -> dict:
expected_error="controller-declared completion validation")
-@pytest.mark.parametrize("route", ["writeback", "successor", "canonical-successor"])
+@pytest.mark.parametrize("route", ["writeback", "successor"])
def test_long_chain_ack_survives_real_cli_history_and_peer_claim(tmp_path: Path, route: str) -> None:
import subprocess
import sys
+ from canonical_authority_fixture import initialize_canonical_authority
+ from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+ )
+ from loopx.control_plane.todos.active_state_todo_parser import (
+ parse_active_state_todos,
+ )
+
project = tmp_path / "project"
project.mkdir()
runtime = tmp_path / "runtime"
@@ -290,17 +313,20 @@ def test_long_chain_ack_survives_real_cli_history_and_peer_claim(tmp_path: Path,
"coordination": {"agent_model": "peer_v1", "registered_agents": [AGENT, "peer-agent"]},
}]}))
- if route == "canonical-successor":
- from canonical_authority_fixture import initialize_canonical_authority
- from loopx.control_plane.todos.active_state_todo_parser import parse_active_state_todos
- todos = parse_active_state_todos(state.read_text(), item_limit=None)["agent_todos"]["items"]
- module = (Path(__file__).resolve().parents[2] / "tests/control_plane_ts/authority_projection_fixture.ts").as_uri()
- built = subprocess.run(["node", "--no-warnings", "--experimental-strip-types", "--input-type=module", "-e",
- f"import {{authorityProjectionFixture}} from {json.dumps(module)};"
- "let s='';for await(const c of process.stdin)s+=c;"
- "process.stdout.write(JSON.stringify(authorityProjectionFixture(process.argv[1],JSON.parse(s),[], 'legacy')));", GOAL],
- input=json.dumps(todos), capture_output=True, text=True, check=True, timeout=30)
- initialize_canonical_authority(runtime, GOAL, json.loads(built.stdout), state_path=state)
+ todos = parse_active_state_todos(
+ state.read_text(),
+ item_limit=None,
+ )["agent_todos"]["items"]
+ initialize_canonical_authority(
+ runtime,
+ GOAL,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL,
+ todos=todos,
+ handoff_mode="soft_claim",
+ ),
+ state_path=state,
+ )
def call(*args):
completed = subprocess.run([sys.executable, "-m", "loopx.cli", "--registry", str(registry),
diff --git a/tests/control_plane/test_retained_team_plan_receipt.py b/tests/control_plane/test_retained_team_plan_receipt.py
new file mode 100644
index 0000000000..fa41ed520b
--- /dev/null
+++ b/tests/control_plane/test_retained_team_plan_receipt.py
@@ -0,0 +1,45 @@
+"""Recover a fixed 1.x receipt through the current public adapter."""
+import json
+from pathlib import Path
+
+import pytest
+
+from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
+from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
+from loopx.control_plane.work_items.team_plan_adapter import apply_team_plan
+from loopx.todos import add_goal_todo, list_goal_todos
+
+
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_original_1x_team_receipt_survives_canonical_later_writes(tmp_path, monkeypatch, provider):
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ history = json.loads((Path(__file__).parent / "fixtures/team_plan_1x.json").read_text())
+ assert history["source_revision"] == "60d833b13c85135587b725013b247e827663ffb9"
+ runtime, state, registry = tmp_path / "runtime", tmp_path / "state.md", tmp_path / "registry.json"
+ state.write_text(history["source"])
+ registry.write_text(json.dumps({"common_runtime_root": str(runtime), "goals": [{
+ "id": "goal-a", "repo": str(tmp_path), "state_file": "state.md",
+ "coordination": {"registered_agents": ["alpha"], "agent_model": "peer_v1"}}]}))
+
+ def replay(**overrides):
+ return apply_team_plan(registry_path=registry, goal_id="goal-a",
+ agent_id=None, proposal=history["plan"], expected_state_fingerprint=history["basis"], **overrides)
+
+ original = state.read_bytes()
+ assert replay()["action"] == "reused"
+ assert state.read_bytes() == original
+ rows = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
+ initialize_canonical_authority(runtime, "goal-a",
+ build_todo_runtime_shadow_projection(goal_id="goal-a", todos=rows, handoff_mode="soft_claim"),
+ state_path=state, provider=provider)
+ add_goal_todo(registry_path=registry, goal_id="goal-a", role="agent", text="Later accepted work", agent_id="alpha")
+ before, display = list_goal_todos(registry_path=registry, goal_id="goal-a"), state.read_bytes()
+ recovered = replay()
+ assert recovered["action"] == "reused"
+ assert recovered["lane_todo_ids"] == history["result"]["lane_todo_ids"]
+ with pytest.raises(Exception, match="identity mismatch"):
+ apply_team_plan(registry_path=registry, goal_id="goal-a", agent_id=None,
+ proposal={**history["plan"], "objective": "Changed original intent"},
+ expected_state_fingerprint=history["basis"])
+ assert list_goal_todos(registry_path=registry, goal_id="goal-a") == before
+ assert state.read_bytes() == display
diff --git a/tests/control_plane/test_retired_todo_event_source.py b/tests/control_plane/test_retired_todo_event_source.py
index d1d74043f8..c044aa731d 100644
--- a/tests/control_plane/test_retired_todo_event_source.py
+++ b/tests/control_plane/test_retired_todo_event_source.py
@@ -11,7 +11,10 @@
import pytest
from loopx.control_plane.goals.legacy_event_source import (
- RetiredTodoEventSourceError, require_no_legacy_todo_events,
+ require_no_legacy_todo_events,
+)
+from loopx.control_plane.coordination.local_authority import (
+ CanonicalAuthorityRequired,
)
from loopx.control_plane.testing.canary_harness import run_json_cli_result
from loopx.control_plane.todos import completion_validation
@@ -59,28 +62,44 @@ def test_all_old_source_selectors_refuse_reads_and_writes_without_data_loss(tmp_
registry = tmp_path / "registry.json"
registry.write_text(json.dumps({"common_runtime_root": str(tmp_path / "runtime"), "goals": [goal]}))
before = state.read_bytes(), old.read_bytes()
- for args in [("todo", "list"),
- ("shared-goal-alignment", "--agent-id", "agent-a", "--project", str(tmp_path)), ("todo", "add", "--text", "New work", "--role", "agent"),
- ("todo", "complete", "--todo-id", "todo_existing", "--evidence", "validated")]:
+ for args in [
+ ("todo", "list"),
+ ("shared-goal-alignment", "--agent-id", "agent-a", "--project", str(tmp_path)),
+ ]:
code, result = run_json_cli_result(*args, "--goal-id", goal["id"], registry_path=registry)
assert code != 0 or result.get("ok") is False, result
assert "legacy_todo_event_source_retired" in json.dumps(result), result
assert (state.read_bytes(), old.read_bytes()) == before
+ for args in [
+ ("todo", "add", "--text", "New work", "--role", "agent"),
+ ("todo", "complete", "--todo-id", "todo_existing", "--evidence", "validated"),
+ ]:
+ code, result = run_json_cli_result(*args, "--goal-id", goal["id"], registry_path=registry)
+ assert code != 0 or result.get("ok") is False, result
+ assert result["error_code"] == "canonical_authority_required"
+ assert result["write_admitted"] is False
+ assert result["legacy_fallback_used"] is False
+ assert (state.read_bytes(), old.read_bytes()) == before
-def test_completion_rejects_before_any_validation_effect(tmp_path, monkeypatch):
+def test_completion_requires_canonical_authority_before_any_validation_effect(tmp_path, monkeypatch):
state = tmp_path / "ACTIVE_GOAL_STATE.md"
state.write_text("## Agent Todo\n- [ ] [P1] Work\n"
" \n")
- state.with_name("events.jsonl").write_text("retired\n")
+ old = state.with_name("events.jsonl")
+ old.write_text("retired\n")
registry = tmp_path / "registry.json"
registry.write_text(json.dumps({"goals": [{"id": "retired-fixture", "repo": str(tmp_path), "state_file": state.name}]}))
def forbidden(**kwargs):
raise AssertionError("a retired source executed a completion effect")
monkeypatch.setattr(completion_validation, "run_declared_completion_validation_effect", forbidden)
- with pytest.raises(RetiredTodoEventSourceError):
+ before = state.read_bytes(), old.read_bytes()
+ with pytest.raises(CanonicalAuthorityRequired) as error:
complete_goal_todo(registry_path=registry, goal_id="retired-fixture", todo_id="todo_existing",
evidence="validated", no_followup=True)
+ assert error.value.code == "canonical_authority_required"
+ assert error.value.payload["operation"] == "todo_terminal"
+ assert (state.read_bytes(), old.read_bytes()) == before
def test_empty_file_is_not_an_event_authority_and_is_preserved(tmp_path):
@@ -158,7 +177,9 @@ def test_retired_source_is_scoped_and_later_healthy_goal_remains_visible(tmp_pat
code, mutation = run_json_cli_result("todo", "add", "--goal-id", "retired",
"--role", "agent", "--text", "Must refuse", registry_path=registry)
assert code != 0 or mutation.get("ok") is False
- assert "legacy_todo_event_source_retired" in json.dumps(mutation)
+ assert mutation["error_code"] == "canonical_authority_required"
+ assert mutation["write_admitted"] is False
+ assert mutation["legacy_fallback_used"] is False
assert old.read_bytes() == before
old.unlink() # Explicit owner recovery in a disposable synthetic fixture.
code, recovered = run_json_cli_result("status", registry_path=registry)
diff --git a/tests/control_plane/test_reviewed_canonical_edits.py b/tests/control_plane/test_reviewed_canonical_edits.py
index 3759a23a1a..53fed908ee 100644
--- a/tests/control_plane/test_reviewed_canonical_edits.py
+++ b/tests/control_plane/test_reviewed_canonical_edits.py
@@ -15,7 +15,7 @@
def service_fixture(tmp_path: Path, provider: str = "file"):
- registry, state = fixture(tmp_path, True, provider)
+ registry, state = fixture(tmp_path, provider)
service = ChatActionService(
store=ChatActionStore(tmp_path / "actions"), registry_path=registry,
)
@@ -185,20 +185,8 @@ def test_monitor_preview_and_apply_share_the_real_update(tmp_path, operation, pr
"normalized_parameters": {**parameters, "agent_id": "agent-b"}})
-def test_unpromoted_review_keeps_legacy_shape_and_freshness(tmp_path):
- registry, state = fixture(tmp_path, False)
- service = ChatActionService(store=ChatActionStore(tmp_path / "actions"), registry_path=registry)
- proposal = preview(service)
- assert "canonical_update_basis" not in proposal
- state.write_text(state.read_text() + "\nOwner changed the source.\n")
- before = state.read_bytes()
- assert service.apply(proposal["proposal_id"])["proposal"]["status"] == "stale"
- assert state.read_bytes() == before
-
-
-@pytest.mark.parametrize("promoted", [False, True])
-def test_update_basis_cannot_be_silently_discarded_by_claim(tmp_path, promoted):
- registry, _state = fixture(tmp_path, promoted)
+def test_update_basis_cannot_be_silently_discarded_by_claim(tmp_path):
+ registry, _state = fixture(tmp_path)
before = records(registry)
with pytest.raises(ValueError, match="cannot be used for todo claim"):
update_goal_todo(registry_path=registry, goal_id="goal-a", todo_id="todo_target",
diff --git a/tests/control_plane/test_reviewed_promotion_cli.py b/tests/control_plane/test_reviewed_promotion_cli.py
deleted file mode 100644
index 63820a886f..0000000000
--- a/tests/control_plane/test_reviewed_promotion_cli.py
+++ /dev/null
@@ -1,172 +0,0 @@
-"""Real public CLI cutover, registration drift and receipt recovery on local stores."""
-
-from __future__ import annotations
-
-import json
-import os
-from pathlib import Path
-import subprocess
-
-import pytest
-
-from tests.control_plane.shadow_e2e_fixture import REPO, workspace
-
-
-def prepare(tmp_path: Path, provider: str, strategy: str | None = None, source_mode: str | None = None):
- ws = workspace(tmp_path, bootstrap=False)
- if strategy is not None or source_mode is not None:
- ws.state.write_text(
- ws.state.read_text().replace(
- "handoff_mode: hard_lease", f"handoff_mode: {source_mode or 'legacy'}"
- )
- )
- if provider == "sqlite":
- subprocess.run(
- [
- os.environ.get("LOOPX_CONTROL_PLANE_NODE", "node"),
- "--no-warnings",
- "--experimental-strip-types",
- "--experimental-sqlite",
- "loopx/control_plane/coordination/local_authority_provider.ts",
- "--runtime-root",
- str(ws.runtime),
- "--goal-id",
- ws.goal,
- "--execute",
- ],
- cwd=REPO,
- check=True,
- capture_output=True,
- text=True,
- )
- assert (
- ws.cli("coordination-shadow", "bootstrap", "--execute")["bootstrap"]["status"]
- == "applied"
- )
- for index in range(3):
- ws.add(f"Preserve migration record {index}")
- drained = ws.drain(budget_seconds="60")
- assert drained["ok"] is True, drained
- arguments = () if strategy is None else ("--handoff-mode-migration", strategy)
- preview = ws.cli("coordination-shadow", "promote", *arguments)
- assert preview["promotion"]["status"] == "preview_ready", preview
- saved = tmp_path / "reviewed.json"
- saved.write_text(json.dumps(preview), encoding="utf-8")
- return ws, saved, preview
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-@pytest.mark.parametrize("strategy", [None, "preserve", "hard_lease"])
-def test_saved_plan_cutover_and_recovery_after_canonical_write_and_missing_legacy(
- tmp_path, provider, strategy
-):
- ws, saved, preview = prepare(tmp_path, provider, strategy)
- arguments = ("coordination-shadow", "promote", "--reviewed-plan", str(saved))
- dry_run = ws.cli(*arguments)
- assert dry_run["promotion"]["status"] == "preview_ready"
- assert dry_run["executed"] is False
- applied = ws.cli(*arguments, "--execute")
- assert applied["promotion"]["status"] == "applied", applied
- assert applied["promotion"]["canonical_authority"] == f"{provider}_v0"
- assert (
- applied["promotion"]["promotion_plan_sha256"]
- == preview["promotion"]["plan"]["promotion_plan_sha256"]
- )
- created = ws.add("Continue after provider cutover")
- readback = ws.cli("todo", "list", "--todo-id", created["todo_id"])
- assert readback["authority_read"]["source_authority"] == f"{provider}_v0"
- ws.state.unlink()
- # Recovery follows durable proof, not a fresh source or shadow opt-in.
- config = json.loads(ws.registry.read_text())
- config["goals"][0]["coordination"].pop("runtime_shadow", None)
- ws.registry.write_text(json.dumps(config))
- for execution in [(), ("--execute",)]:
- replay = ws.cli(
- "coordination-shadow",
- "recover-promotion",
- "--reviewed-plan",
- str(saved),
- *execution,
- )
- assert replay["promotion"]["status"] == "replayed", replay
- assert replay["promotion"]["cursor"] == "1"
- assert (
- replay["promotion"]["provider_revision"]
- == applied["promotion"]["provider_revision"]
- )
- assert replay["executed"] is False
- assert not ws.state.exists()
-
-
-def test_saved_plan_source_drift_does_not_freeze_legacy_writes(tmp_path):
- ws, saved, _ = prepare(tmp_path, "file")
- ws.add("New work before cutover")
- result = ws.cli(
- "coordination-shadow",
- "promote",
- "--reviewed-plan",
- str(saved),
- "--execute",
- success=False,
- )
- assert result["ok"] is False
- assert result["promotion"]["reason_code"] == "local_authority_reviewed_plan_changed"
- assert result["promotion"]["legacy_writer_fenced"] is False
- assert ws.add("Legacy writer remains usable")["ok"] is True
-
-
-def test_saved_plan_rejects_policy_override_and_recovery_without_fence(tmp_path):
- ws, saved, _ = prepare(tmp_path, "file")
- result = ws.cli(
- "coordination-shadow",
- "promote",
- "--reviewed-plan",
- str(saved),
- "--minimum-operations",
- "1",
- "--execute",
- success=False,
- )
- assert result["ok"] is False and "owns qualification policy" in result["error"]
- result = ws.cli(
- "coordination-shadow",
- "recover-promotion",
- "--reviewed-plan",
- str(saved),
- "--execute",
- success=False,
- )
- assert result["ok"] is False
- assert (
- result["promotion"]["reason_code"]
- == "local_authority_writer_fence_not_verified"
- )
-
-
-def test_saved_plan_rechecks_current_registered_agents(tmp_path):
- ws, saved, _ = prepare(tmp_path, "file", "preserve")
- registry = json.loads(ws.registry.read_text())
- registry["goals"][0]["coordination"]["registered_agents"].remove("agent-b")
- ws.registry.write_text(json.dumps(registry))
- result = ws.cli(
- "coordination-shadow",
- "promote",
- "--reviewed-plan",
- str(saved),
- "--execute",
- success=False,
- )
- assert result["ok"] is False
- assert result["promotion"]["reason_code"] == "promotion_registration_changed_retry"
- assert result["promotion"]["legacy_writer_fenced"] is False
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_default_promotion_preserves_soft_policy(tmp_path, provider):
- ws, saved, preview = prepare(tmp_path, provider, source_mode="soft_claim")
- policy = preview["promotion"]["plan"]["handoff_mode_migration"]
- assert policy["strategy"] == "preserve" and policy["target_mode"] == "soft_claim"
- applied = ws.cli("coordination-shadow", "promote", "--reviewed-plan", str(saved), "--execute")
- assert applied["ok"], applied
- shown = ws.cli("handoff-mode", "show")
- assert shown["handoff_mode"] == "soft_claim"
diff --git a/tests/control_plane/test_reviewed_terminal_actions.py b/tests/control_plane/test_reviewed_terminal_actions.py
index 0116b2b097..3b3348b159 100644
--- a/tests/control_plane/test_reviewed_terminal_actions.py
+++ b/tests/control_plane/test_reviewed_terminal_actions.py
@@ -24,15 +24,24 @@ def fixture(tmp_path: Path, provider: str, operation: str, *, validation: str |
registry.write_text(json.dumps({"schema_version": 1, "common_runtime_root": str(tmp_path / "runtime"),
"goals": [{"id": "goal-a", "status": "active", "repo": str(project), "state_file": state.name,
"coordination": {"registered_agents": ["agent-a"]}}]}))
+ projection = build_todo_runtime_shadow_projection(
+ goal_id="goal-a",
+ todos=[],
+ handoff_mode="soft_claim",
+ )
+ initialize_canonical_authority(
+ tmp_path / "runtime",
+ "goal-a",
+ projection,
+ state_path=state,
+ provider=provider,
+ )
monitor = operation == "stop"
added = add_goal_todo(registry_path=registry, goal_id="goal-a", role="agent",
text="Observe the supported release" if monitor else "Deliver the bounded result",
task_class="continuous_monitor" if monitor else "advancement_task", claimed_by="agent-a",
validation_command=validation,
**({"monitor_metadata": {"target_key": "release-status", "cadence": "1d", "watch_only": "true"}} if monitor else {}))
- todos = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
- projection = build_todo_runtime_shadow_projection(goal_id="goal-a", todos=todos, handoff_mode="soft_claim")
- initialize_canonical_authority(tmp_path / "runtime", "goal-a", projection, state_path=state, provider=provider)
service = ChatActionService(store=ChatActionStore(tmp_path / "actions"), registry_path=registry)
proposal = service.preview({"action_kind": "monitor.update" if monitor else "todo.update",
"summary": "Close the reviewed work", "normalized_parameters": {"goal_id": "goal-a",
diff --git a/tests/control_plane/test_reward_memory_host_isolation.py b/tests/control_plane/test_reward_memory_host_isolation.py
index 58d19b1431..a93bf7e544 100644
--- a/tests/control_plane/test_reward_memory_host_isolation.py
+++ b/tests/control_plane/test_reward_memory_host_isolation.py
@@ -124,21 +124,12 @@ def test_old_host_reflection_is_ignored_when_ingest_is_off(ingest: bool) -> None
@pytest.mark.parametrize("mode", ["unconfigured", "disabled", "stale", "both_off", "recall_only", "ingest_only", "both_on"])
def test_real_turn_cli_memory_projection_and_settlement(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str, mode: str) -> None:
from loopx.cli import main
- from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
- from tests.control_plane.canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
+ from tests.control_plane.canonical_authority_fixture import isolate_sqlite_runtime
from tests.control_plane.reward_memory_host_fixture import enable_live_memory
from test_loopx_turn_driver import _write_live_fixture
isolate_sqlite_runtime(tmp_path, monkeypatch)
- project, runtime, registry = _write_live_fixture(tmp_path)
- state = project / ".codex/goals/loopx-turn-fixture/ACTIVE_GOAL_STATE.md"
- projection = build_todo_runtime_shadow_projection(goal_id="loopx-turn-fixture", handoff_mode="soft_claim", todos=[{
- "schema_version": "todo_item_v0", "todo_id": "todo_fixture0001", "role": "agent",
- "status": "open", "archive_state": "active", "source_section": "Agent Todo",
- "task_class": "advancement_task", "action_kind": "fixture", "claimed_by": "codex-fixture",
- "priority": "P0", "done": False, "text": "Advance one public fixture.",
- }])
- initialize_canonical_authority(runtime, "loopx-turn-fixture", projection, state_path=state, provider=provider)
+ project, runtime, registry = _write_live_fixture(tmp_path, provider=provider)
ingest = mode in {"ingest_only", "both_on"}
if mode != "unconfigured":
config = enable_live_memory(registry, recall=mode in {"recall_only", "both_on"}, ingest=ingest)
diff --git a/tests/control_plane/test_runtime_shadow_bounded_e2e.py b/tests/control_plane/test_runtime_shadow_bounded_e2e.py
deleted file mode 100644
index 623cdc6550..0000000000
--- a/tests/control_plane/test_runtime_shadow_bounded_e2e.py
+++ /dev/null
@@ -1,599 +0,0 @@
-"""Real public CLI and independent native TypeScript file-profile qualification.
-
-The production writers, file provider and parsers run unchanged. Tests control
-only process scheduling and deliberately edited source bytes.
-"""
-from __future__ import annotations
-
-import hashlib
-import json
-import os
-import subprocess
-import sys
-from pathlib import Path
-
-import pytest
-
-from loopx.control_plane.effect_runtime import EffectRuntimeRejected
-
-from loopx.control_plane.coordination.coordination_state_contract_generated import (
- LOCAL_AUTHORITY_SHADOW_COMMIT_ENTRY_REQUEST_SCHEMA,
- TASK_LEASE_ACQUIRE_REQUEST_SCHEMA,
-)
-from loopx.control_plane.coordination.runtime_shadow import (
- build_runtime_shadow_source_snapshot,
-)
-from loopx.control_plane.work_items.task_lease_acquire_adapter import (
- task_lease_acquire_authority_facts,
-)
-
-REPO = Path(__file__).resolve().parents[2]
-
-
-pytestmark = pytest.mark.stage2c_e2e
-
-
-def workspace(tmp_path: Path) -> tuple[Path, Path, Path]:
- state = tmp_path / "ACTIVE_GOAL_STATE.md"
- state.write_text("---\ngoal_id: goal-a\nhandoff_mode: hard_lease\n---\n\n## Agent Todo\n\n", encoding="utf-8")
- runtime = tmp_path / "runtime"
- registry = tmp_path / "registry.json"
- registry.write_text(json.dumps({"common_runtime_root": str(runtime), "goals": [{
- "id": "goal-a", "status": "active", "repo": str(tmp_path), "state_file": state.name,
- "coordination": {"agent_model": "peer_v1", "registered_agents": ["agent-a", "agent-b"],
- "runtime_shadow": {"schema_version": "loopx_coordination_runtime_shadow_config_v0", "enabled": False, "provider": "file_v0"}},
- }]}), encoding="utf-8")
- return registry, runtime, state
-
-
-def cli(registry: Path, runtime: Path, *arguments: str, success: bool = True) -> dict:
- completed = subprocess.run([sys.executable, "-m", "loopx.cli", "--registry", str(registry),
- "--runtime-root", str(runtime), "--format", "json", *arguments], cwd=REPO,
- env={**os.environ, "PYTHONPATH": str(REPO)}, capture_output=True, text=True,
- timeout=45, check=False)
- assert completed.stdout.strip(), completed.stderr
- payload = json.loads(completed.stdout)
- if success:
- assert completed.returncode == 0, completed.stderr + "\n" + json.dumps(payload, indent=2)
- assert payload.get("ok") is True, payload
- return payload
-
-
-def enable(registry: Path) -> dict:
- value = json.loads(registry.read_text())
- value["goals"][0]["coordination"]["runtime_shadow"]["enabled"] = True
- registry.write_text(json.dumps(value), encoding="utf-8")
- return value["goals"][0]
-
-
-def native(tmp_path: Path, module: str, function: str, request: dict) -> dict:
- path = tmp_path / "native-request.json"
- path.write_text(json.dumps(request), encoding="utf-8")
- script = (f"import {{ {function} }} from {json.dumps((REPO / module).as_uri())};"
- "import {readFile} from 'node:fs/promises';"
- f"process.stdout.write(JSON.stringify(await {function}(JSON.parse(await readFile(process.argv[1],'utf8')))));" )
- process = subprocess.run(["node", "--no-warnings", "--experimental-strip-types", "--input-type=module", "-e", script, str(path)],
- cwd=tmp_path, capture_output=True, text=True, timeout=45, check=False)
- assert process.returncode == 0, process.stderr
- return json.loads(process.stdout)
-
-
-def history(tmp_path: Path, runtime: Path) -> list[dict]:
- result = native(tmp_path, "loopx/control_plane/coordination/local_authority_shadow.ts", "readLocalAuthorityShadow", {
- "schema_version": "loopx_coordination_runtime_shadow_outbox_read_v0", "runtime_root": str(runtime),
- "goal_id": "goal-a", "scan_limit": 10000,
- })
- assert result["status"] == "loaded", result
- return result["proof"]["transactions"]
-
-
-def acquire_native(tmp_path: Path, registry: Path, runtime: Path, todo_id: str) -> dict:
- return native(tmp_path, "loopx/control_plane/work_items/task_lease_acquire.ts", "executeTaskLeaseAcquire", {
- "schema_version": TASK_LEASE_ACQUIRE_REQUEST_SCHEMA, "runtime_root": str(runtime), "goal_id": "goal-a",
- "todo_id": todo_id, "owner": "agent-b", "idempotency_key": "native-" + todo_id, "ttl_seconds": 120,
- "write_scopes": [], "expected_version": None,
- "authority": task_lease_acquire_authority_facts(registry_path=registry, goal_id="goal-a", todo_id=todo_id),
- "runtime_shadow": {"schema_version": "loopx_coordination_runtime_shadow_binding_v0", "provider": "file_v0"},
- })
-
-
-def test_source_snapshot_preserves_inventory_without_projecting_orphan_leases(
- tmp_path: Path,
-) -> None:
- registry, runtime, state = workspace(tmp_path)
- directory = runtime / "goals" / "goal-a" / "task-leases"
- directory.mkdir(parents=True)
- # Imported baseline files use the snapshot contract's ASCII filename range.
- # Their order is independent of locale and filesystem enumeration order.
- for todo_id in ("todo_alpha", "todo_Zulu", "todo_Bravo"):
- (directory / f"{todo_id}.json").write_text(json.dumps({
- "schema_version": "task_lease_v0", "goal_id": "goal-a", "todo_id": todo_id,
- "owner": "agent-a", "status": "released", "version": 1,
- "write_scopes": ["src/"], "idempotency_key": f"baseline-{todo_id}",
- }), encoding="utf-8")
- goal = enable(registry)
- projection, snapshot = build_runtime_shadow_source_snapshot(
- goal=goal, runtime_root=runtime, state_path=state, registry_path=registry)
- expected = ["todo_Bravo", "todo_Zulu", "todo_alpha"]
- assert [entry["name"] for entry in snapshot["lease_inventory"]] == [f"{name}.json" for name in expected]
- assert projection["leases"] == []
- boot = cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- assert boot["bootstrap"]["status"] == "applied", boot
- inspected = cli(registry, runtime, "coordination-shadow", "inspect", "--goal-id", "goal-a")
- assert inspected["inspection"]["status"] == "matched", inspected
- assert history(tmp_path, runtime)[0]["projection"]["leases"] == []
-
-
-def test_public_cli_and_independent_native_writer_qualify_one_complete_lineage(tmp_path: Path) -> None:
- registry, runtime, state = workspace(tmp_path)
- cli(registry, runtime, "handoff-mode", "set", "--goal-id", "goal-a", "--mode", "soft_claim")
- archived = cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent", "--text", "Previously completed task", "--claimed-by", "agent-a")
- cli(registry, runtime, "todo", "complete", "--goal-id", "goal-a", "--todo-id", archived["todo_id"],
- "--agent-id", "agent-a", "--evidence", "validation://completed", "--no-follow-up")
- cli(registry, runtime, "todo", "archive-completed", "--goal-id", "goal-a", "--max-active-done", "0", "--execute")
- cli(registry, runtime, "handoff-mode", "set", "--goal-id", "goal-a", "--mode", "hard_lease")
- first = cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent", "--text", "Existing first task")
- second = cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent", "--text", "Existing second task")
- baseline_lease = cli(registry, runtime, "task-lease", "acquire", "--goal-id", "goal-a", "--todo-id", first["todo_id"],
- "--owner", "agent-a", "--idempotency-key", "baseline-lease", "--ttl-seconds", "120")
- assert not (runtime / "authority-shadow" / "file-v0").exists()
- enable(registry)
- boot = cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- assert boot["projection_summary"] == {"todo_count": 2, "lease_count": 1}
- assert boot["bootstrap"]["cursor"] == "1"
- native_request = {
- "schema_version": TASK_LEASE_ACQUIRE_REQUEST_SCHEMA, "runtime_root": str(runtime), "goal_id": "goal-a",
- "todo_id": second["todo_id"], "owner": "agent-b", "idempotency_key": "native-second", "ttl_seconds": 120,
- "write_scopes": [], "expected_version": None,
- "authority": task_lease_acquire_authority_facts(registry_path=registry, goal_id="goal-a", todo_id=second["todo_id"]),
- "runtime_shadow": {"schema_version": "loopx_coordination_runtime_shadow_binding_v0", "provider": "file_v0"},
- }
- acquired = native(tmp_path, "loopx/control_plane/work_items/task_lease_acquire.ts", "executeTaskLeaseAcquire", native_request)
- assert acquired["acquired"] is True, acquired
- additions = [cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent", "--text", f"New captured task {index}") for index in range(2)]
- for addition in additions:
- assert addition["coordination_runtime_shadow"]["source_transaction_correlated"] is True
- inspect = cli(registry, runtime, "coordination-shadow", "inspect", "--goal-id", "goal-a")
- assert inspect["inspection"]["status"] == "matched", inspect
- qualified = cli(registry, runtime, "coordination-shadow", "qualify", "--goal-id", "goal-a", "--minimum-operations", "3",
- "--require-event-kind", "todo_add", "--require-event-kind", "task_lease_acquire")
- assert qualified["qualification"]["scope"] == "bounded"
- assert qualified["qualification"]["sustained_parity_verified"] is False
- assert qualified["qualification"]["sustained_parity_verdict"] == "not_evaluated"
- assert qualified["qualification"]["evidence"]["operation_count"] == 3
- assert qualified["qualification"]["cursor"] == "4"
- read = cli(registry, runtime, "coordination-shadow", "read-candidate", "--goal-id", "goal-a", "--todo-id", first["todo_id"])
- assert read["read_candidate"]["read_candidate_qualified"] is True
- assert read["read_candidate"]["decision_read_from_shadow"] is False
- proof = native(tmp_path, "loopx/control_plane/coordination/local_authority_shadow.ts", "readLocalAuthorityShadow", {
- "schema_version": "loopx_coordination_runtime_shadow_outbox_read_v0", "runtime_root": str(runtime),
- "goal_id": "goal-a", "scan_limit": 10000,
- })
- transactions = proof["proof"]["transactions"]
- assert len(transactions) == 4
- assert transactions[0]["receipts"] == []
- assert transactions[0]["projection"]["leases"] == [baseline_lease["lease"]]
- baseline_ids = [todo["todo_id"] for todo in transactions[0]["projection"]["todos"]]
- assert baseline_ids == sorted([first["todo_id"], second["todo_id"]])
- assert archived["todo_id"] not in baseline_ids
- assert "Previously completed task" in state.read_text()
- receipts = [transaction["receipts"][0] for transaction in transactions[1:]]
- assert {receipt["writer_runtime"] for receipt in receipts} == {"python", "typescript"}
- assert len({receipt["entry_id"] for receipt in receipts}) == 3
- assert all(receipt["capture_lineage_id"] == boot["bootstrap"]["capture_lineage_id"] for receipt in receipts)
-
-
-def test_reviewed_promotion_survives_restart_and_enables_managed_codex_preflight(
- tmp_path: Path,
-) -> None:
- registry, runtime, _state = workspace(tmp_path)
- registry_payload = json.loads(registry.read_text(encoding="utf-8"))
- registry_payload["goals"][0]["adapter"] = {
- "kind": "generic_project_goal_v0",
- "status": "connected",
- }
- registry_payload["goals"][0]["domain"] = "coordination-promotion"
- registry_payload["goals"][0]["quota"] = {
- "compute": 1.0,
- "window_hours": 24,
- "slot_minutes": 1,
- "allowed_slots": 10,
- }
- registry.write_text(
- json.dumps(registry_payload, ensure_ascii=False, indent=2) + "\n",
- encoding="utf-8",
- )
- delegated = cli(
- registry,
- runtime,
- "todo",
- "add",
- "--goal-id",
- "goal-a",
- "--role",
- "agent",
- "--text",
- "Validate a managed worker only after reviewed promotion",
- "--task-class",
- "advancement_task",
- "--action-kind",
- "validate",
- "--claimed-by",
- "agent-b",
- )
- cli(
- registry,
- runtime,
- "task-lease",
- "acquire",
- "--goal-id",
- "goal-a",
- "--todo-id",
- delegated["todo_id"],
- "--owner",
- "agent-b",
- "--idempotency-key",
- "managed-worker-before-promotion",
- "--ttl-seconds",
- "600",
- )
- enable(registry)
- cli(
- registry,
- runtime,
- "coordination-shadow",
- "bootstrap",
- "--goal-id",
- "goal-a",
- "--execute",
- )
- cli(
- registry,
- runtime,
- "todo",
- "add",
- "--goal-id",
- "goal-a",
- "--role",
- "agent",
- "--text",
- "Capture one promotion qualification mutation",
- "--task-class",
- "advancement_task",
- "--action-kind",
- "capture",
- "--claimed-by",
- "agent-a",
- )
- common = (
- "coordination-shadow",
- "promote",
- "--goal-id",
- "goal-a",
- "--minimum-operations",
- "1",
- "--require-event-kind",
- "todo_add",
- )
- preview = cli(registry, runtime, *common)
- assert preview["promotion"]["status"] == "preview_ready"
- assert preview["promotion"]["promotion_ready"] is True
- assert not (runtime / "authority" / "file-v0").exists()
- assert not (runtime / ".local" / "manager-context" / "executions").exists()
-
- applied = cli(registry, runtime, *common, "--execute")
- assert applied["promotion"]["status"] == "applied"
- assert applied["promotion"]["legacy_writer_fenced"] is True
-
- # A fresh CLI process owns each call below. The first post-promotion
- # canonical mutation therefore proves that runtime-root identity and the
- # promoted authority survive process restart before any worker can launch.
- inspected = cli(registry, runtime, "goal-acceptance", "inspect", "--goal-id", "goal-a")
- document = tmp_path / "managed-acceptance.json"
- document.write_text(
- json.dumps(
- {
- "objective": "Validate managed worker acceptance after restart",
- "scope": {"kind": "selected_work", "todo_ids": [delegated["todo_id"]]},
- "non_goals": ["Start the managed worker during inspection"],
- "criteria": [
- {
- "id": "ready",
- "description": "The fixture validator succeeds",
- "validation_argv": [sys.executable, "-c", "raise SystemExit(0)"],
- }
- ],
- "bindings": [
- {"todo_id": delegated["todo_id"], "criterion_ids": ["ready"]}
- ],
- }
- ),
- encoding="utf-8",
- )
- configured = cli(
- registry,
- runtime,
- "goal-acceptance",
- "configure",
- "--goal-id",
- "goal-a",
- "--document",
- str(document),
- "--expected-provider-revision",
- inspected["provider_revision"],
- "--operation-id",
- "post-promotion-managed-acceptance",
- "--execute",
- )
- bound = next(
- item
- for item in configured["goal_acceptance_contract"]["tasks"]
- if item["todo_id"] == delegated["todo_id"]
- )
- assert bound == {
- "todo_id": delegated["todo_id"],
- "state": "ready",
- "criterion_ids": ["ready"],
- "reason": "The owner confirmed this work's current acceptance association.",
- "reason_code": "goal_acceptance_ready",
- "applicable": True,
- }
-
- worker = tmp_path / "managed-worker"
- worker.mkdir()
- config = tmp_path / "delegations.json"
- config.write_text(
- json.dumps(
- {
- "schema_version": "loopx_local_delegation_v0",
- "bindings": [
- {
- "id": "managed-sol",
- "agent_id": "agent-b",
- "todo_id": delegated["todo_id"],
- "requesters": ["agent-a"],
- "workspace": str(worker),
- "timeout_seconds": 60,
- "output_refs": ["output.json"],
- "host_args": [
- "--host",
- "codex-cli",
- "--codex-model",
- "gpt-5.6-sol",
- "--codex-reasoning-effort",
- "xhigh",
- ],
- }
- ],
- }
- ),
- encoding="utf-8",
- )
- preflight = cli(
- registry,
- runtime,
- "delegation",
- "inspect",
- "--goal-id",
- "goal-a",
- "--agent-id",
- "agent-a",
- "--execution-config",
- str(config),
- "--binding-id",
- "managed-sol",
- )
- assert preflight["authority_state"] == "promoted"
- assert preflight["authority_ready"] is True
- assert preflight["acceptance_ready"] is True
- assert preflight["executor"]["profile"] == "gpt-5.6-sol@xhigh"
- assert preflight["state"] in {"launchable", "runtime_unverified"}
- assert not any(preflight["effects"].values())
- assert not (runtime / ".local" / "manager-context" / "executions").exists()
- assert not (runtime / "goals" / "goal-a" / "turns").exists()
-
-
-def test_unrecorded_canonical_change_cannot_become_qualified_after_a_later_public_write(tmp_path: Path) -> None:
- registry, runtime, state = workspace(tmp_path)
- enable(registry)
- cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- original = state.read_text()
- state.write_text(original.replace("handoff_mode: hard_lease", "handoff_mode: soft_claim"))
- added = cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent", "--text", "After unrecorded mutation")
- assert added["added"] is True
- qualified = cli(registry, runtime, "coordination-shadow", "qualify", "--goal-id", "goal-a", success=False)
- assert qualified["qualification"]["qualified"] is False
- assert list((runtime / "authority-shadow" / "outbox" / "goal-a" / "todos").glob("*.prepared.json"))
- state.write_text(original)
- again = cli(registry, runtime, "coordination-shadow", "qualify", "--goal-id", "goal-a", success=False)
- assert again["qualification"]["qualified"] is False
-
-
-def test_snapshot_changed_between_python_builder_and_native_inspection_is_rejected(tmp_path: Path) -> None:
- registry, runtime, state = workspace(tmp_path)
- goal = enable(registry)
- cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- projection, snapshot = build_runtime_shadow_source_snapshot(goal=goal, runtime_root=runtime, state_path=state, registry_path=registry)
- state.write_text(state.read_text() + "\n## Notes\nProse changed after snapshot.\n")
- result = native(tmp_path, "loopx/control_plane/coordination/runtime_shadow.ts", "inspectCoordinationRuntimeShadow", {
- "schema_version": "loopx_coordination_runtime_shadow_inspect_v0", "runtime_root": str(runtime), "goal_id": "goal-a",
- "projection": projection, "source_snapshot": snapshot,
- })
- assert result["status"] == "failed"
- assert result["reason_code"] == "source_changed_retry"
-
-
-def test_public_handoff_todo_and_atomic_monitor_batch_capture_each_primary_mutation(tmp_path: Path) -> None:
- registry, runtime, _state = workspace(tmp_path)
- enable(registry)
- cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- cli(registry, runtime, "handoff-mode", "set", "--goal-id", "goal-a", "--mode", "soft_claim")
- assert len(history(tmp_path, runtime)) == 2
- cli(
- registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent",
- "--text", "Validate the retained projection", "--evidence", "validation://todo-add",
- )
- assert len(history(tmp_path, runtime)) == 3
- monitor = cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent",
- "--text", "Observe the public release", "--task-class", "continuous_monitor", "--action-kind", "monitor",
- "--claimed-by", "agent-a", "--target-key", "release:bounded", "--cadence", "30m",
- "--next-due-at", "2000-01-01T00:00:00+00:00", "--watch-only")
- assert len(history(tmp_path, runtime)) == 4
- result = cli(registry, runtime, "quota", "monitor-poll", "--goal-id", "goal-a", "--agent-id", "agent-a",
- "--runtime-profile", "generic_cli", "--available-capability", "network", "--todo-id", monitor["todo_id"],
- "--target-key", "release:bounded", "--result-hash", "release-v1", "--material-change",
- "--next-agent-todo", "Validate the released head", "--next-action-kind", "validate_release_head",
- "--next-task-repository", "git:github.com/huangruiteng/loopx", "--next-required-capability", "network",
- "--next-continuation-policy", "same_agent_non_delivery", "--next-claimed-by", "agent-a", "--execute")
- assert len(result["successor_todo_ids"]) == 1
- transactions = history(tmp_path, runtime)
- assert len(transactions) == 5 # Baseline, handoff, todo add, monitor add, atomic Monitor batch.
- batch_todos = {todo["todo_id"]: todo for todo in transactions[-1]["projection"]["todos"]}
- assert batch_todos[monitor["todo_id"]]["result_hash"] == "release-v1"
- successor = batch_todos[result["successor_todo_ids"][0]]
- assert (successor["text"], successor["priority"]) == ("[P1] Validate the released head", "P1")
- receipts = [transaction["receipts"][0] for transaction in transactions[1:]]
- assert len({receipt["entry_id"] for receipt in receipts}) == 4
- assert [receipt["seq"] for receipt in receipts] == [1, 2, 3, 4]
- assert {receipt["write_class"] for receipt in receipts} >= {"handoff_mode_set", "todo_add", "todo_update"}
- qualified = cli(registry, runtime, "coordination-shadow", "qualify", "--goal-id", "goal-a", "--minimum-operations", "4")
- assert qualified["qualification"]["qualified"] is True
- assert qualified["qualification"]["evidence"]["operation_count"] == 4
-
-
-def test_disabling_configuration_cannot_cancel_an_active_capture_obligation(tmp_path: Path) -> None:
- registry, runtime, state = workspace(tmp_path)
- enable(registry)
- cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- initial = state.read_bytes()
- config = json.loads(registry.read_text())
- config["goals"][0]["coordination"]["runtime_shadow"]["enabled"] = False
- registry.write_text(json.dumps(config))
- cli(registry, runtime, "handoff-mode", "set", "--goal-id", "goal-a", "--mode", "soft_claim")
- cli(registry, runtime, "handoff-mode", "set", "--goal-id", "goal-a", "--mode", "hard_lease")
- assert state.read_bytes() == initial
- enable(registry)
- # The identical final source must not erase the two intervening mutations.
- transactions = history(tmp_path, runtime)
- assert len(transactions) == 3
- assert [row["receipts"][0]["seq"] for row in transactions[1:]] == [1, 2]
- qualified = cli(registry, runtime, "coordination-shadow", "qualify", "--goal-id", "goal-a", "--minimum-operations", "2")
- assert qualified["qualification"]["qualified"] is True
- assert qualified["qualification"]["evidence"]["operation_count"] == 2
-
-
-def test_native_lease_writer_cannot_reuse_a_sequence_when_its_cursor_is_missing(tmp_path: Path) -> None:
- registry, runtime, _state = workspace(tmp_path)
- first = cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent", "--text", "First lease task")
- second = cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent", "--text", "Second lease task")
- enable(registry)
- cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- cli(registry, runtime, "task-lease", "acquire", "--goal-id", "goal-a", "--todo-id", first["todo_id"],
- "--owner", "agent-a", "--idempotency-key", "first-lease", "--ttl-seconds", "120")
- assert len(history(tmp_path, runtime)) == 2
- cursor = runtime / "authority-shadow/outbox/goal-a/leases/drain-cursor.json"
- cursor.unlink()
- acquired = acquire_native(tmp_path, registry, runtime, second["todo_id"])
- assert acquired["acquired"] is True, acquired
- pending = list(cursor.parent.glob("*.prepared.json"))
- assert len(pending) == 1
- assert json.loads(pending[0].read_text())["seq"] == 2
- assert not cursor.exists() # The writer recovers a sequence, never a cursor.
- cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent", "--text", "Trigger bounded drain")
- # Inline capture may leave receipt-proven cleanup for explicit recovery.
- drained = cli(registry, runtime, "authority-shadow", "drain", "--goal-id", "goal-a")
- assert drained["pending_after"] == drained["prepared_only_after"] == 0, drained
- transactions = history(tmp_path, runtime)
- lease_receipts = [row["receipts"][0] for row in transactions[1:] if row["receipts"][0]["partition"] == "leases"]
- assert [receipt["seq"] for receipt in lease_receipts] == [1, 2]
- assert len({receipt["entry_id"] for receipt in lease_receipts}) == 2
- assert cli(registry, runtime, "coordination-shadow", "qualify", "--goal-id", "goal-a")["qualification"]["qualified"] is True
-
-
-def test_public_committed_primary_cannot_be_relabelled_abandoned_by_native_request(tmp_path: Path) -> None:
- from shadow_e2e_fixture import workspace as crash_workspace
-
- from loopx.control_plane.coordination import (
- local_authority_shadow_adapter as adapter,
- )
- from loopx.control_plane.coordination import local_authority_shadow_outbox as outbox
-
- w = crash_workspace(tmp_path)
- w.crash("before_commit", "todo", "add", "--role", "agent", "--text", "A committed primary is never abandoned")
- directory = outbox.partition_directory(w.runtime, w.goal, "todos")
- [entry] = outbox.list_entries(directory)
- # The batch drain owns public commits now, so the witnessed selection is
- # built here from durable entry bytes instead of a retired private helper.
- request = {
- "schema_version": LOCAL_AUTHORITY_SHADOW_COMMIT_ENTRY_REQUEST_SCHEMA,
- "runtime_root": str(w.runtime),
- "goal_id": w.goal,
- "entry_id": entry.entry_id,
- "partition": entry.partition,
- "seq": entry.seq,
- "capture_lineage_id": entry.prepared.get("capture_lineage_id"),
- "prepared_sha256": "sha256:" + hashlib.sha256(entry.prepared_path.read_bytes()).hexdigest(),
- "committed_sha256": (
- "sha256:" + hashlib.sha256(entry.committed_path.read_bytes()).hexdigest()
- if entry.committed_path
- else None
- ),
- }
- assert request["committed_sha256"] is not None
- request["resolution"] = "abandoned"
- before = {path.name: path.read_bytes() for path in directory.iterdir()}
- primary = w.state.read_bytes()
- with pytest.raises(EffectRuntimeRejected, match="shadow_entry_selection_invalid"):
- adapter.effect_runtime_result("coordination.runtime_shadow.commit_entry", request, timeout=15)
- assert {path.name: path.read_bytes() for path in directory.iterdir()} == before
- assert w.state.read_bytes() == primary
- # The byte witness itself is legal; only caller-supplied resolution rejects.
- request.pop("resolution")
- delivered = adapter.effect_runtime_result("coordination.runtime_shadow.commit_entry", request, timeout=15)
- assert delivered["outcome"] == "delivered" and delivered["resolution"] == "committed"
- assert w.drain()["ok"] is True
- view = adapter.read_local_authority_shadow(runtime_root=w.runtime, goal_id=w.goal, scan_limit=20)
- [transaction] = view["proof"]["transactions"][1:]
- assert transaction["receipts"][0]["resolution"] == "committed"
- assert transaction["receipts"][0]["no_op"] is False
-
-
-def test_controller_cannot_bind_the_registered_source_to_an_override_runtime_root(tmp_path: Path) -> None:
- registry, runtime, state = workspace(tmp_path)
- enable(registry)
- original = state.read_bytes()
- override = tmp_path / "override-runtime"
- for registered_is_active in (False, True):
- if registered_is_active:
- cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- rejected = cli(registry, override, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute", success=False)
- assert rejected["bootstrap"]["status"] == "failed"
- assert rejected["bootstrap"]["reason_code"] == "shadow_source_runtime_root_mismatch", rejected
- assert not (override / "authority-shadow/file-v0").exists()
- assert state.read_bytes() == original
- qualified = cli(registry, override, "coordination-shadow", "qualify", "--goal-id", "goal-a", success=False)
- assert qualified["qualification"]["qualified"] is False
- assert qualified["qualification"]["reason_code"] == "shadow_source_runtime_root_mismatch"
-
-
-def test_controller_cannot_bind_an_alternate_state_file_before_or_after_bootstrap(tmp_path: Path) -> None:
- registry, runtime, state = workspace(tmp_path)
- enable(registry)
- alternate = tmp_path / "alternate-state.md"
- alternate.write_bytes(state.read_bytes())
- original = state.read_bytes()
- for active in (False, True):
- if active:
- cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a", "--execute")
- rejected = cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", "goal-a",
- "--state-file", str(alternate), "--execute", success=False)
- assert rejected["bootstrap"]["status"] == "failed"
- expected = "management_operation_identity_mismatch" if active else "shadow_source_state_path_mismatch"
- assert rejected["bootstrap"]["reason_code"] == expected, rejected
- assert state.read_bytes() == alternate.read_bytes() == original
- qualified = cli(registry, runtime, "coordination-shadow", "qualify", "--goal-id", "goal-a",
- "--state-file", str(alternate), success=False)
- assert qualified["qualification"]["qualified"] is False
- assert qualified["qualification"]["reason_code"] == "shadow_source_state_path_mismatch"
- if not active:
- assert not (runtime / "authority-shadow/file-v0").exists()
diff --git a/tests/control_plane/test_runtime_shadow_writer_capture.py b/tests/control_plane/test_runtime_shadow_writer_capture.py
deleted file mode 100644
index 3413b9a65d..0000000000
--- a/tests/control_plane/test_runtime_shadow_writer_capture.py
+++ /dev/null
@@ -1,263 +0,0 @@
-from __future__ import annotations
-
-import json
-import subprocess
-import sys
-from pathlib import Path
-
-import pytest
-
-from loopx.control_plane.coordination import local_authority_shadow_adapter as adapter
-from loopx.control_plane.work_items.task_lease import (
- acquire_task_lease,
- release_task_lease,
- renew_task_lease,
- transfer_task_lease,
-)
-from loopx.todos import add_goal_todo, update_goal_todo
-
-
-GOAL_ID = "runtime-shadow-writer"
-
-
-def _fixture(
- tmp_path: Path,
- *,
- enabled: bool,
- handoff_mode: str = "hard_lease",
-) -> tuple[Path, Path, Path]:
- repo = tmp_path / "repo"
- repo.mkdir()
- state = repo / "ACTIVE_GOAL_STATE.md"
- state.write_text(
- "---\n"
- f"goal_id: {GOAL_ID}\n"
- f"handoff_mode: {handoff_mode}\n"
- "updated_at: 2026-09-04T00:00:00+00:00\n"
- "---\n\n## Agent Todo\n\n",
- encoding="utf-8",
- )
- runtime_root = tmp_path / "runtime"
- coordination: dict[str, object] = {
- "agent_model": "peer_v1",
- "registered_agents": ["agent-a", "agent-b"],
- }
- if enabled:
- coordination["runtime_shadow"] = {
- "enabled": True,
- "schema_version": "loopx_coordination_runtime_shadow_config_v0",
- "provider": "file_v0",
- }
- registry = tmp_path / "registry.json"
- registry.write_text(
- json.dumps(
- {
- "common_runtime_root": str(runtime_root),
- "goals": [
- {
- "id": GOAL_ID,
- "domain": "harness_self_improvement",
- "status": "active",
- "repo": str(repo),
- "state_file": state.name,
- "adapter": {"kind": "harness_self_improvement"},
- "coordination": coordination,
- }
- ],
- }
- ),
- encoding="utf-8",
- )
- if enabled:
- boot = subprocess.run([sys.executable, "-m", "loopx.entrypoint", "--registry", str(registry),
- "--format", "json", "coordination-shadow", "bootstrap", "--goal-id", GOAL_ID, "--execute"],
- cwd=Path(__file__).resolve().parents[2], text=True, capture_output=True, timeout=30)
- assert boot.returncode == 0, boot.stdout + boot.stderr
- return registry, state, runtime_root
-
-
-def test_runtime_shadow_todo_writer_captures_full_records_and_reuses_one_store(
- tmp_path: Path,
-) -> None:
- registry, _state, runtime_root = _fixture(tmp_path, enabled=True)
-
- added = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Retain complete canonical Todo fields.",
- task_class="advancement_task",
- action_kind="implement",
- claimed_by="agent-a",
- )
- updated = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=str(added["todo_id"]),
- note="transaction-bound",
- agent_id="agent-a",
- )
-
- assert added["coordination_runtime_shadow"]["outcome"] == "delivered"
- assert updated["coordination_runtime_shadow"]["outcome"] == "delivered"
- view = adapter.read_local_authority_shadow(
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- scan_limit=10,
- )
- head = view["head"]
- assert head["schema_version"] == "loopx_coordination_runtime_shadow_projection_v0"
- assert head["todos"][0]["text"] == "[P1] Retain complete canonical Todo fields."
- assert head["todos"][0]["note"] == "transaction-bound"
- assert head["todo_read_model"]["todo_count"] == 1
- assert view["cursor"] == "3"
- assert not (runtime_root / "authority-shadow" / "file" / GOAL_ID).exists()
-
-
-def test_runtime_shadow_todo_claim_preserves_claim_write_class(tmp_path: Path) -> None:
- registry, _state, runtime_root = _fixture(
- tmp_path,
- enabled=True,
- handoff_mode="legacy",
- )
- added = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Qualify the legacy claim path.",
- task_class="advancement_task",
- )
-
- claimed = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=str(added["todo_id"]),
- claimed_by="agent-a",
- agent_id="agent-a",
- claim_only=True,
- )
-
- assert claimed["coordination_runtime_shadow"]["outcome"] == "delivered"
- view = adapter.read_local_authority_shadow(
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- scan_limit=10,
- )
- receipt = view["proof"]["transactions"][-1]["receipts"][0]
- assert receipt["write_class"] == "todo_claim"
-
-
-def test_runtime_shadow_todo_writer_is_zero_effect_by_default(tmp_path: Path) -> None:
- registry, _state, runtime_root = _fixture(tmp_path, enabled=False)
-
- result = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Default-off capture.",
- task_class="advancement_task",
- )
-
- assert result["coordination_runtime_shadow"]["outcome"] == "no_transaction"
- assert result["coordination_runtime_shadow"]["reason_code"] == "shadow_disabled"
- assert not (runtime_root / "authority-shadow").exists()
-
-
-@pytest.mark.parametrize("configuration_cleared", [False, True])
-def test_runtime_shadow_native_lease_writers_capture_complete_records(
- tmp_path: Path, configuration_cleared: bool,
-) -> None:
- registry, _state, runtime_root = _fixture(tmp_path, enabled=True)
- added = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Capture the native lease transaction.",
- task_class="advancement_task",
- )
- todo_id = str(added["todo_id"])
- if configuration_cleared:
- config = json.loads(registry.read_text(encoding="utf-8"))
- config["goals"][0]["coordination"].pop("runtime_shadow")
- registry.write_text(json.dumps(config), encoding="utf-8")
-
- acquired = acquire_task_lease(
- registry_path=registry,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- todo_id=todo_id,
- owner="agent-a",
- idempotency_key="runtime-shadow-lease",
- write_scopes=["loopx/**"],
- ttl_seconds=120,
- )
- renewed = renew_task_lease(
- registry_path=registry,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- todo_id=todo_id,
- owner="agent-a",
- idempotency_key="runtime-shadow-lease",
- ttl_seconds=180,
- expected_version=int(acquired["lease"]["version"]),
- )
- transferred = transfer_task_lease(
- registry_path=registry,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- todo_id=todo_id,
- owner="agent-a",
- idempotency_key="runtime-shadow-lease",
- new_owner="agent-b",
- new_idempotency_key="runtime-shadow-lease-b",
- ttl_seconds=180,
- expected_version=int(renewed["lease"]["version"]),
- )
- released = release_task_lease(
- registry_path=registry,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- todo_id=todo_id,
- owner="agent-b",
- idempotency_key="runtime-shadow-lease-b",
- expected_version=int(transferred["lease"]["version"]),
- )
-
- assert acquired["coordination_runtime_shadow"]["outcome"] == "delivered"
- assert renewed["coordination_runtime_shadow"]["outcome"] == "delivered"
- assert transferred["coordination_runtime_shadow"]["outcome"] == "delivered"
- assert released["coordination_runtime_shadow"]["outcome"] == "delivered"
- view = adapter.read_local_authority_shadow(runtime_root=runtime_root, goal_id=GOAL_ID)
- lease = view["head"]["leases"][0]
- assert lease["goal_id"] == GOAL_ID
- assert lease["owner"] == "agent-b"
- assert lease["idempotency_key"] == "runtime-shadow-lease-b"
- assert lease["write_scopes"] == ["loopx/**"]
- assert lease["status"] == "released"
-
-
-def test_runtime_shadow_native_lease_writer_is_zero_effect_by_default(tmp_path: Path) -> None:
- registry, _state, runtime_root = _fixture(tmp_path, enabled=False)
- added = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Do not capture an unconfigured lease.",
- task_class="advancement_task",
- claimed_by="agent-a",
- )
-
- acquired = acquire_task_lease(
- registry_path=registry,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- todo_id=str(added["todo_id"]),
- owner="agent-a",
- idempotency_key="default-off-lease",
- write_scopes=["loopx/**"],
- ttl_seconds=120,
- )
-
- assert "coordination_runtime_shadow_capture" not in acquired
- assert "coordination_runtime_shadow" not in acquired
- assert not (runtime_root / "authority-shadow").exists()
diff --git a/tests/control_plane/test_selected_work_requirements.py b/tests/control_plane/test_selected_work_requirements.py
index 8ff455f560..b0a7b40259 100644
--- a/tests/control_plane/test_selected_work_requirements.py
+++ b/tests/control_plane/test_selected_work_requirements.py
@@ -22,6 +22,8 @@
from loopx.control_plane.turn_driver.executor import build_loopx_turn_host_request
from loopx.control_plane.turn_driver.host_candidate import extract_turn_authority, render_prompt
+REPO = Path(__file__).resolve().parents[2]
+
def test_legacy_selected_todo_requires_same_goal_state_revision(tmp_path: Path) -> None:
state = tmp_path / "state.md"
@@ -114,7 +116,7 @@ def guard(*extra):
assert guard()["interaction_contract"]["agent_channel"]["work_context"]["complete"] is True
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_current_work_requirements_reach_real_guard_and_host_without_display_loss(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, provider: str,
) -> None:
@@ -135,15 +137,22 @@ def test_current_work_requirements_reach_real_guard_and_host_without_display_los
goal_id="requirements-goal", domain="requirements", adapter_kind="generic_project_goal_v0",
state_file=str(state), registered_agents=["agent-a", "agent-b"], quota_allowed_slots=None)
expected = "[P1] " + work
- if provider != "legacy":
- goal = json.loads(registry.read_text())["goals"][0]
- fields, _archived, _sections = parse_todo_source(state.read_text(), goal=goal, state_path=state)
- full_items = retained_todo_summary_fields(fields["agent"], rollout_events=[])["agent_todos"]["items"]
- snapshot = build_todo_runtime_shadow_projection(
- goal_id="requirements-goal", todos=full_items, handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, "requirements-goal", snapshot,
- state_path=state, provider=provider)
- state.write_text(goal_requirements + "## Agent Todo\n- [ ] Stale display: do only an easier check.\n")
+ goal = json.loads(registry.read_text())["goals"][0]
+ fields, _archived, _sections = parse_todo_source(
+ state.read_text(), goal=goal, state_path=state,
+ )
+ full_items = retained_todo_summary_fields(
+ fields["agent"], rollout_events=[],
+ )["agent_todos"]["items"]
+ snapshot = build_todo_runtime_shadow_projection(
+ goal_id="requirements-goal", todos=full_items, handoff_mode="soft_claim",
+ )
+ initialize_canonical_authority(
+ runtime, "requirements-goal", snapshot, state_path=state, provider=provider,
+ )
+ state.write_text(
+ goal_requirements + "## Agent Todo\n- [ ] Stale display: do only an easier check.\n",
+ )
def guard(*extra: str) -> dict:
code, packet = run_json_cli_result("quota", "should-run", "--goal-id", "requirements-goal",
@@ -164,9 +173,13 @@ def guard(*extra: str) -> dict:
assert "work_context" in body
script = re.search(r"```sh\n(.*?)\n```", body, re.S).group(1)
assert "--turn-envelope" not in script and "--decision-output-root" not in script
- env = {**os.environ, "LOOPX_REGISTRY": str(registry)}
+ env = {
+ **os.environ,
+ "LOOPX_REGISTRY": str(registry),
+ "PYTHONPATH": str(REPO),
+ }
result = subprocess.run(["sh", "-c", script.replace("", "requirements-default-wake")],
- cwd=tmp_path, env=env, capture_output=True, text=True, check=True)
+ cwd=REPO, env=env, capture_output=True, text=True, check=True)
full = json.loads(result.stdout)
assert full["should_run"] is True, full.get("reason")
channel = full["interaction_contract"]["agent_channel"]
@@ -385,9 +398,13 @@ def test_short_selected_todo_deduplicates_body_but_keeps_canonical_note(
registry_path=registry, runtime_root=runtime)
assert code == 0 and generated["ok"], generated
script = re.search(r"```sh\n(.*?)\n```", generated["task_body"], re.S).group(1)
- env = {**os.environ, "LOOPX_REGISTRY": str(registry)}
+ env = {
+ **os.environ,
+ "LOOPX_REGISTRY": str(registry),
+ "PYTHONPATH": str(REPO),
+ }
result = subprocess.run(["sh", "-c", script.replace("", "short-note-wake")],
- cwd=tmp_path, env=env, capture_output=True, text=True, check=True)
+ cwd=REPO, env=env, capture_output=True, text=True, check=True)
full = json.loads(result.stdout)
channel = full["interaction_contract"]["agent_channel"]
context = channel["work_context"]
diff --git a/tests/control_plane/test_settled_monitor_user_gate.py b/tests/control_plane/test_settled_monitor_user_gate.py
index 1db31a85ca..a84e6cc050 100644
--- a/tests/control_plane/test_settled_monitor_user_gate.py
+++ b/tests/control_plane/test_settled_monitor_user_gate.py
@@ -5,13 +5,12 @@
import pytest
-from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
+from canonical_authority_fixture import isolate_sqlite_runtime
from tests.control_plane.test_monitor_followthrough_contract import (
AGENT_ID, GOAL_ID, _add_monitor, _write_fixture,
)
-from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
from loopx.control_plane.testing.canary_harness import run_json_cli
-from loopx.todos import add_goal_todo, list_goal_todos
+from loopx.todos import add_goal_todo
from loopx.control_plane.effect_program import ReceiptBoundMonitorPhase
from loopx.control_plane.quota.should_run import build_quota_should_run
from loopx.control_plane.testing.quota_fixtures import (
@@ -44,11 +43,11 @@ def test_incomplete_frontier_cannot_erase_settled_monitor_receipt():
assert replay["interaction_contract"]["agent_channel"]["delivery_allowed"] is False
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("material", [False, True])
def test_scoped_gate_cannot_reopen_an_exact_settled_monitor(tmp_path, monkeypatch, provider, material):
isolate_sqlite_runtime(tmp_path, monkeypatch)
- registry, runtime, state = _write_fixture(tmp_path)
+ registry, runtime, _state = _write_fixture(tmp_path, provider=provider)
gated = add_goal_todo(registry_path=registry, goal_id=GOAL_ID, role="agent",
text="Work awaiting an owner decision", task_class="advancement_task",
status="blocked", claimed_by=AGENT_ID, agent_id=AGENT_ID)
@@ -57,11 +56,6 @@ def test_scoped_gate_cannot_reopen_an_exact_settled_monitor(tmp_path, monkeypatc
blocks_agent=AGENT_ID, unblocks_todo_id=gated["todo_id"], agent_id=AGENT_ID)
monitor = _add_monitor(registry, text="Observe an independent public target",
target_key="public-target", next_due_at="2000-01-01T00:00:00Z")
- if provider != "legacy":
- todos = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]
- projection = build_todo_runtime_shadow_projection(goal_id=GOAL_ID,
- todos=todos, handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, GOAL_ID, projection, state_path=state, provider=provider)
turn = "monitor-scoped-gate-turn"
guard_args = ["quota", "should-run", "--goal-id", GOAL_ID, "--agent-id", AGENT_ID,
"--runtime-profile", "generic_cli", "--turn-instance-id", turn,
diff --git a/tests/control_plane/test_settlement_input_discovery_cli.py b/tests/control_plane/test_settlement_input_discovery_cli.py
index 4132559a5f..f1f4f68286 100644
--- a/tests/control_plane/test_settlement_input_discovery_cli.py
+++ b/tests/control_plane/test_settlement_input_discovery_cli.py
@@ -4,6 +4,7 @@
import json
import shlex
from copy import deepcopy
+from pathlib import Path
import pytest
from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime
@@ -15,11 +16,42 @@
AGENT_ID, GOAL_ID, TODO_ID, TURN_ID, _run_cli, _run_generated_cli,
_spend_run_count, _write_fixture,
)
+from loopx.todos import complete_goal_todo
BODY = "[P1] " + "Retain the original requirements and verify the result. " * 24 + "TAIL: preserve cancellation."
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+def _initialize_authority(
+ runtime: Path,
+ registry: Path,
+ state: Path,
+ provider: str,
+) -> None:
+ goal = json.loads(registry.read_text())["goals"][0]
+ active, archived, _ = parse_todo_source(
+ state.read_text(),
+ goal=goal,
+ state_path=state,
+ )
+ rows = [
+ {"schema_version": "todo_item_v0", **row}
+ for row in [*active["agent"], *active["user"], *archived]
+ ]
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=rows,
+ handoff_mode="soft_claim",
+ leases=[],
+ ),
+ state_path=state,
+ provider=provider,
+ )
+
+
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
@pytest.mark.parametrize("in_flight", [False, True])
def test_projected_inputs_support_real_readback_writeback_and_one_debit(tmp_path, monkeypatch, provider, in_flight):
isolate_sqlite_runtime(tmp_path, monkeypatch)
@@ -27,14 +59,7 @@ def test_projected_inputs_support_real_readback_writeback_and_one_debit(tmp_path
state = project / f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md"
state.write_text(state.read_text().replace("[P1] Validate and settle the selected delivery.", BODY).replace(
"action_kind=validate -->", f"action_kind=validate claimed_by={AGENT_ID} -->"))
- if provider != "legacy":
- goal = json.loads(registry.read_text())["goals"][0]
- active, archived, _ = parse_todo_source(state.read_text(), goal=goal, state_path=state)
- rows = [{"schema_version": "todo_item_v0", **row}
- for row in [*active["agent"], *active["user"], *archived]]
- initialize_canonical_authority(runtime, GOAL_ID, build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, todos=rows, handoff_mode="legacy", leases=[]),
- state_path=state, provider=provider)
+ _initialize_authority(runtime, registry, state, provider)
def run(*args):
return _run_cli(registry, runtime, *args, cwd=project)
@@ -104,10 +129,12 @@ def run(*args):
assert rc == 0 and final_todo["todo"]["status"] == ("open" if in_flight else "done")
-@pytest.mark.parametrize("provider", ["legacy", "file", "sqlite"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_todoless_periodic_review_uses_default_authoring_and_settles_once(tmp_path, monkeypatch, provider):
isolate_sqlite_runtime(tmp_path, monkeypatch)
project, runtime, registry = _write_fixture(tmp_path)
+ state = project / f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md"
+ _initialize_authority(runtime, registry, state, provider)
def run(*args):
return _run_cli(registry, runtime, *args, cwd=project)
@@ -127,21 +154,16 @@ def run(*args):
rc, spent = run("quota", "spend-slot", *binding, "--slots", "1", "--source", "heartbeat", "--execute")
assert rc == 0 and _spend_run_count(runtime) == 1, spent
- # Synthetic absence of runnable work; retain the real committed receipt.
- # Initialize each authority from this fixture, never rewrite a live store.
- state = project / f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md"
- state.write_text(state.read_text().replace(
- "- [ ] [P1] Validate and settle the selected delivery.",
- "- [x] [P1] Validate and settle the selected delivery.").replace(
- f"todo_id={TODO_ID} status=open", f"todo_id={TODO_ID} status=done"))
- if provider != "legacy":
- goal = json.loads(registry.read_text())["goals"][0]
- active, archived, _ = parse_todo_source(state.read_text(), goal=goal, state_path=state)
- rows = [{"schema_version": "todo_item_v0", **row}
- for row in [*active["agent"], *active["user"], *archived]]
- initialize_canonical_authority(runtime, GOAL_ID, build_todo_runtime_shadow_projection(
- goal_id=GOAL_ID, todos=rows, handoff_mode="legacy", leases=[]),
- state_path=state, provider=provider)
+ # Synthetic absence of runnable work; retain the real committed receipt and
+ # close the selected Todo through the canonical public writer.
+ complete_goal_todo(
+ registry_path=registry,
+ goal_id=GOAL_ID,
+ todo_id=TODO_ID,
+ role="agent",
+ agent_id=AGENT_ID,
+ evidence="Synthetic fixture completion after receipt commit.",
+ )
current = ("--goal-id", GOAL_ID, "--agent-id", AGENT_ID,
"--turn-instance-id", "periodic-without-todo", "--scan-path", str(project))
diff --git a/tests/control_plane/test_shadow_cursor_recovery_e2e.py b/tests/control_plane/test_shadow_cursor_recovery_e2e.py
deleted file mode 100644
index 105f632c6a..0000000000
--- a/tests/control_plane/test_shadow_cursor_recovery_e2e.py
+++ /dev/null
@@ -1,181 +0,0 @@
-"""A settled position and its last applied digest are separate cursor facts."""
-
-from __future__ import annotations
-
-import json
-from pathlib import Path
-import select
-import subprocess
-
-import pytest
-
-from shadow_e2e_fixture import REPO, ShadowWorkspace, workspace
-from loopx.control_plane.coordination import local_authority_shadow_adapter as adapter
-from loopx.control_plane.coordination import local_authority_shadow_outbox as outbox
-from loopx.control_plane.coordination.coordination_state_contract_generated import TASK_LEASE_ACQUIRE_REQUEST_SCHEMA
-from loopx.control_plane.work_items.task_lease_acquire_adapter import task_lease_acquire_authority_facts
-
-pytestmark = pytest.mark.stage2c_e2e
-
-# Pause only the actual primary rename, after durable prepare. No result is replaced.
-LEASE_WORKER = r"""
-import fs from 'node:fs';
-import {syncBuiltinESMExports} from 'node:module';
-const input = JSON.parse(process.argv[1]);
-const rename = fs.promises.rename;
-fs.promises.rename = async (source, target) => {
- if (String(target) === input.stop_before) {
- process.stdout.write('BARRIER primary-rename\n');
- await new Promise(resolve => setTimeout(resolve, 40000));
- throw new Error('parent did not terminate the paused writer');
- }
- return await rename(source, target);
-};
-syncBuiltinESMExports();
-const {executeTaskLeaseAcquire} = await import(input.module);
-process.stdout.write(JSON.stringify(await executeTaskLeaseAcquire(input.request)) + '\n');
-"""
-
-
-def acquire(w: ShadowWorkspace, todo: str, *, crash: bool = False) -> None:
- lease = w.runtime / 'goals' / w.goal / 'task-leases' / f'{todo}.json'
- request = {
- 'schema_version': TASK_LEASE_ACQUIRE_REQUEST_SCHEMA,
- 'runtime_root': str(w.runtime), 'goal_id': w.goal, 'todo_id': todo,
- 'owner': 'agent-a', 'idempotency_key': f'acquire-{todo}',
- 'ttl_seconds': 3600, 'write_scopes': [], 'expected_version': None,
- 'authority': task_lease_acquire_authority_facts(
- registry_path=w.registry, goal_id=w.goal, todo_id=todo),
- }
- args = ['node', '--no-warnings', '--experimental-strip-types', '--input-type=module', '-e',
- LEASE_WORKER, json.dumps({'request': request, 'stop_before': str(lease) if crash else None,
- 'module': (REPO / 'loopx/control_plane/work_items/task_lease_acquire.ts').as_uri()})]
- if not crash:
- result = subprocess.run(args, cwd=REPO, capture_output=True, text=True, timeout=30, check=True)
- assert json.loads(result.stdout)['acquired'] is True, result.stdout
- assert json.loads(lease.read_bytes())['owner'] == 'agent-a'
- return
- child = subprocess.Popen(args, cwd=REPO, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
- try:
- assert child.stdout is not None
- assert select.select([child.stdout], [], [], 30)[0], 'native writer missed primary rename'
- assert child.stdout.readline().strip() == 'BARRIER primary-rename'
- child.kill()
- child.communicate(timeout=10)
- assert child.returncode == -9
- assert not lease.exists()
- finally:
- if child.poll() is None:
- child.kill()
- child.communicate(timeout=10)
-
-
-@pytest.mark.parametrize('partition', ['todos', 'leases'])
-@pytest.mark.parametrize('prior_mutations', [0, 1])
-@pytest.mark.parametrize('abandoned', [1, 2])
-def test_abandoned_cursor_survives_all_consumers(
- tmp_path: Path, partition: str, prior_mutations: int, abandoned: int,
-) -> None:
- w = workspace(tmp_path, bootstrap=False)
- # A nonempty baseline must still have no applied-mutation digest.
- ids = [w.add(f'Baseline task {index}')['todo_id'] for index in range(3)]
- assert w.cli('coordination-shadow', 'bootstrap', '--execute')['bootstrap']['status'] == 'applied'
-
- def mutate(index: int) -> None:
- if partition == 'todos':
- ids.append(w.add(f'Applied task {index}')['todo_id'])
- else:
- acquire(w, ids[index])
- assert w.drain()['ok'] is True
-
- for index in range(prior_mutations):
- mutate(index)
- before = w.state.read_bytes()
- for index in range(abandoned):
- if partition == 'todos':
- w.crash('before_replace', 'todo', 'add', '--role', 'agent', '--text', f'Never applied {index}')
- else:
- acquire(w, ids[prior_mutations], crash=True)
- assert w.state.read_bytes() == before
- recovered = w.drain()
- assert recovered['ok'] is True and recovered['no_op'] == 1, recovered
- inspected = w.cli('coordination-shadow', 'inspect', success=False)['inspection']
- assert inspected['status'] == 'matched', inspected
- assert inspected['evidence']['operation_count'] == prior_mutations
-
- directory = outbox.partition_directory(w.runtime, w.goal, partition)
- path = directory / 'drain-cursor.json'
- cursor = json.loads(path.read_bytes())
- assert cursor['last_seq'] == prior_mutations + abandoned
- assert (cursor['last_partition_digest'] is None) == (prior_mutations == 0)
- assert w.drain()['outcome'] == 'nothing_pending'
- # Legitimate cursor loss reconstructs the same facts from real history.
- path.unlink()
- assert w.drain()['ok'] is True
- restored = json.loads(path.read_bytes())
- assert {k: v for k, v in restored.items() if k != 'updated_at'} == {
- k: v for k, v in cursor.items() if k != 'updated_at'}
- assert w.cli('coordination-shadow', 'inspect')['inspection']['status'] == 'matched'
- early = w.cli('coordination-shadow', 'qualify', success=False)['qualification']
- assert early['qualified'] is False and early['evidence']['operation_count'] == prior_mutations
- early_read = w.cli('coordination-shadow', 'read-candidate', '--todo-id', ids[-1], success=False)
- assert early_read['read_candidate']['read_candidate_qualified'] is False
- for index in range(prior_mutations, 3):
- mutate(index)
- qualified = w.cli('coordination-shadow', 'qualify')['qualification']
- assert qualified['qualified'] is True and qualified['scope'] == 'bounded', qualified
- assert qualified['evidence']['operation_count'] == 3
- assert qualified['sustained_parity_verdict'] == 'not_evaluated'
- transactions = adapter.read_local_authority_shadow(
- runtime_root=w.runtime, goal_id=w.goal, scan_limit=100,
- )['proof']['transactions']
- assert len(transactions) == 4 + abandoned
- assert [tx['receipts'][0]['seq'] for tx in transactions[1:]] == list(range(1, 4 + abandoned))
- assert sum(tx['receipts'][0]['no_op'] is True for tx in transactions[1:]) == abandoned
- candidate = w.cli('coordination-shadow', 'read-candidate', '--todo-id', ids[-1])
- assert candidate['read_candidate']['read_candidate_qualified'] is True, candidate
-
-
-@pytest.mark.parametrize('partition', ['todos', 'leases'])
-@pytest.mark.parametrize('applied', [False, True])
-def test_forged_applied_digest_holds_every_consumer_without_rewriting_bytes(
- tmp_path: Path, partition: str, applied: bool,
-) -> None:
- import hashlib
-
- w = workspace(tmp_path, bootstrap=False)
- todo = w.add('Baseline is not mutation coverage')['todo_id']
- w.cli('coordination-shadow', 'bootstrap', '--execute')
- if applied:
- if partition == 'todos':
- w.add('Real mutation with an applied digest')
- else:
- acquire(w, todo)
- elif partition == 'todos':
- w.crash('before_replace', 'todo', 'add', '--role', 'agent', '--text', 'Abandoned')
- else:
- acquire(w, todo, crash=True)
- assert w.drain()['ok'] is True
- view = adapter.read_local_authority_shadow(runtime_root=w.runtime, goal_id=w.goal, scan_limit=20)
- projection = view['proof']['transactions'][-1]['projection']
- fields = ('handoff_mode', 'todos') if partition == 'todos' else ('leases',)
- snapshot = {key: projection[key] for key in fields}
- snapshot_digest = 'sha256:' + hashlib.sha256(json.dumps(
- snapshot, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode()).hexdigest()
- path = outbox.partition_directory(w.runtime, w.goal, partition) / 'drain-cursor.json'
- cursor = json.loads(path.read_bytes())
- cursor['last_partition_digest'] = None if applied else snapshot_digest
- path.write_text(json.dumps(cursor, indent=3) + '\n')
- # Exact raw bytes matter even when all the cursor's identity fields are valid.
- # Lock diagnostics change and dead writer locks are reclaimed; authority evidence does not.
- before = {p.relative_to(w.runtime): p.read_bytes() for p in w.runtime.rglob('*')
- if p.is_file() and not p.name.endswith('.lock')}
- for command, key in [('inspect', 'inspection'), ('qualify', 'qualification'), ('read-candidate', 'read_candidate')]:
- args = ('--todo-id', todo) if command == 'read-candidate' else ()
- result = w.cli('coordination-shadow', command, *args, success=False)[key]
- assert result.get('reason_code') == 'outbox_cursor_unproved', result
- result = w.drain()
- assert result['ok'] is False and result['reason_code'] == 'outbox_cursor_unproved', result
- after = {p.relative_to(w.runtime): p.read_bytes() for p in w.runtime.rglob('*')
- if p.is_file() and not p.name.endswith('.lock')}
- assert after == before
diff --git a/tests/control_plane/test_shadow_drain_adversarial.py b/tests/control_plane/test_shadow_drain_adversarial.py
deleted file mode 100644
index 26aeb6b39a..0000000000
--- a/tests/control_plane/test_shadow_drain_adversarial.py
+++ /dev/null
@@ -1,224 +0,0 @@
-"""Adversarial recovery ordering through the public CLI and real file provider."""
-from __future__ import annotations
-
-import hashlib
-from pathlib import Path
-
-import pytest
-
-from loopx.control_plane.effect_runtime import EffectRuntimeRejected
-
-
-from shadow_e2e_fixture import workspace
-from loopx.control_plane.coordination import local_authority_shadow_adapter as adapter
-from loopx.control_plane.coordination import local_authority_shadow_outbox as outbox
-
-
-pytestmark = pytest.mark.stage2c_e2e
-
-
-@pytest.mark.parametrize("window", ["before_commit", "after_commit", "after_cursor"])
-def test_cleanup_permission_failure_reports_verified_commit_and_recovers(
- tmp_path: Path, window: str,
-) -> None:
- """A failed local checkpoint cannot hide a proven candidate transaction."""
- w = workspace(tmp_path)
- w.crash(window, "todo", "add", "--role", "agent", "--text", "Durable despite cleanup failure")
- directory = outbox.partition_directory(w.runtime, w.goal, "todos")
- before = {path.name: path.read_bytes() for path in directory.iterdir()}
- mode = directory.stat().st_mode & 0o777
- directory.chmod(0o500)
- try:
- # Exercise a real filesystem denial, with the provider still writable.
- with pytest.raises(PermissionError):
- (directory / "permission-control").write_bytes(b"must not be writable")
- stopped = w.drain()
- assert stopped["ok"] is False and stopped["reason_code"], stopped
- assert {path.name: path.read_bytes() for path in directory.iterdir()} == before
- view = adapter.read_local_authority_shadow(runtime_root=w.runtime, goal_id=w.goal, scan_limit=20)
- assert len(view["proof"]["transactions"]) == 2 # Baseline and the actual mutation.
- assert stopped["candidate_readback_verified"] is True, stopped
- assert stopped["provider_revision"] == view["provider_revision"], stopped
- finally:
- directory.chmod(mode)
- recovered = w.drain()
- assert recovered["ok"] is True and recovered["replayed"] == 1, recovered
- assert recovered["delivered"] == 0, recovered
- assert outbox.read_cursor(directory)["last_seq"] == 1
- assert {path.name for path in directory.iterdir()} == {"drain-cursor.json"}
- assert adapter.read_local_authority_shadow(
- runtime_root=w.runtime, goal_id=w.goal, scan_limit=20,
- )["proof"]["transactions"] == view["proof"]["transactions"]
-
-
-def test_missing_cursor_cannot_reuse_a_sequence_when_the_next_writer_arrives_first(tmp_path: Path) -> None:
- w = workspace(tmp_path)
- w.add("First complete transaction")
- w.add("Second complete transaction")
- directory = outbox.partition_directory(w.runtime, w.goal, "todos")
- (directory / "drain-cursor.json").unlink()
- # No operator drain runs between evidence loss and the next public writer.
- result = w.add("Writer arrives before cursor recovery")
- assert result["ok"] is True
- recovered = w.drain()
- assert recovered["ok"] is True, recovered
- view = adapter.read_local_authority_shadow(runtime_root=w.runtime, goal_id=w.goal, scan_limit=20)
- receipts = [tx["receipts"][0] for tx in view["proof"]["transactions"][1:]]
- assert [receipt["seq"] for receipt in receipts] == [1, 2, 3]
- assert outbox.read_cursor(directory)["last_seq"] == 3
-
-
-def test_small_recovery_budget_makes_progress_through_verified_residue(tmp_path: Path) -> None:
- w = workspace(tmp_path)
- directory = outbox.partition_directory(w.runtime, w.goal, "todos")
- residue: dict[str, bytes] = {}
- for index in range(3):
- w.crash("after_commit", "todo", "add", "--role", "agent", "--text", f"Acknowledged transaction {index}")
- residue.update({path.name: path.read_bytes() for path in directory.glob("*.json") if path.name != "drain-cursor.json"})
- assert w.drain()["ok"] is True
- for name, data in residue.items():
- (directory / name).write_bytes(data)
- before = adapter.read_local_authority_shadow(runtime_root=w.runtime, goal_id=w.goal, scan_limit=20)
- for left in (2, 1, 0):
- result = w.drain(max_entries="1")
- assert result["ok"] is True, result
- assert result["replayed"] == 1, result
- assert len(outbox.list_entries(directory)) == left
- after = adapter.read_local_authority_shadow(runtime_root=w.runtime, goal_id=w.goal, scan_limit=20)
- assert after["proof"]["transactions"] == before["proof"]["transactions"]
-
-
-def test_raw_residue_mismatch_preserves_every_file_before_any_cleanup(tmp_path: Path) -> None:
- w = workspace(tmp_path)
- directory = outbox.partition_directory(w.runtime, w.goal, "todos")
- residue: dict[str, bytes] = {}
- for index in range(2):
- w.crash("after_commit", "todo", "add", "--role", "agent", "--text", f"Exact receipt {index}")
- residue.update({path.name: path.read_bytes() for path in directory.glob("*.json") if path.name != "drain-cursor.json"})
- assert w.drain()["ok"] is True
- for name, data in residue.items():
- (directory / name).write_bytes(data)
- last = sorted(directory.glob("*.prepared.json"))[-1]
- # Valid identical JSON semantics still do not match the receipt's exact bytes.
- last.write_bytes(last.read_bytes() + b"\n")
- before = {path.name: path.read_bytes() for path in directory.iterdir()}
- result = w.drain()
- assert result["ok"] is False and result["reason_code"] == "outbox_receipt_mismatch", result
- assert result["reclaimed_residue"] == 0
- assert {path.name: path.read_bytes() for path in directory.iterdir()} == before
-
-
-def test_real_prepare_io_failure_holds_public_primary_before_replace(tmp_path: Path) -> None:
- w = workspace(tmp_path)
- directory = outbox.partition_directory(w.runtime, w.goal, "todos")
- directory.write_text("A real filesystem obstruction, not a directory")
- before = w.state.read_bytes()
- rejected = w.cli("handoff-mode", "set", "--mode", "soft_claim", success=False)
- assert rejected["ok"] is False, rejected
- assert w.state.read_bytes() == before
- assert directory.read_text() == "A real filesystem obstruction, not a directory"
- directory.unlink()
- w.cli("handoff-mode", "set", "--mode", "soft_claim")
- assert w.drain()["ok"] is True
- view = adapter.read_local_authority_shadow(runtime_root=w.runtime, goal_id=w.goal, scan_limit=20)
- assert view["head"]["handoff_mode"] == "soft_claim"
- assert len(view["proof"]["transactions"]) == 2
- assert view["proof"]["transactions"][1]["receipts"][0]["seq"] == 1
-
-
-@pytest.mark.parametrize(
- ("window", "claimed_resolution"),
- [("before_replace", "committed_proven_by_readback"), ("before_marker", "abandoned")],
-)
-def test_native_markerless_resolution_requires_source_evidence(
- tmp_path: Path, window: str, claimed_resolution: str,
-) -> None:
- w = workspace(tmp_path)
- w.crash(window, "todo", "add", "--role", "agent", "--text", "Source proof is not a caller flag")
- directory = outbox.partition_directory(w.runtime, w.goal, "todos")
- [entry] = outbox.list_entries(directory)
- request = {"schema_version": "loopx_shadow_entry_delivery_request_v0", "runtime_root": str(w.runtime),
- "goal_id": w.goal, "partition": entry.partition, "seq": entry.seq, "entry_id": entry.entry_id,
- "capture_lineage_id": entry.prepared["capture_lineage_id"],
- "prepared_sha256": "sha256:" + hashlib.sha256(entry.prepared_path.read_bytes()).hexdigest(),
- "committed_sha256": None}
- request["resolution"] = claimed_resolution
- before = {path.name: path.read_bytes() for path in directory.iterdir()}
- with pytest.raises(EffectRuntimeRejected, match="shadow_entry_selection_invalid"):
- adapter.effect_runtime_result("coordination.runtime_shadow.commit_entry", request, timeout=15)
- assert {path.name: path.read_bytes() for path in directory.iterdir()} == before
- view = adapter.read_local_authority_shadow(runtime_root=w.runtime, goal_id=w.goal, scan_limit=20)
- assert len(view["proof"]["transactions"]) == 1
-
- # Without the caller flag, the same legal witness derives from source bytes.
- request.pop("resolution")
- delivered = adapter.effect_runtime_result("coordination.runtime_shadow.commit_entry", request, timeout=15)
- assert delivered["outcome"] == "delivered"
- assert delivered["resolution"] == (
- "abandoned" if window == "before_replace" else "committed_proven_by_readback"
- )
- assert delivered["no_op"] is (window == "before_replace")
-
-
-def test_registry_runtime_override_cannot_bypass_an_active_source_binding(tmp_path: Path) -> None:
- import json
- import subprocess
- import sys
- from shadow_e2e_fixture import REPO
-
- w = workspace(tmp_path)
- for index in range(3):
- w.add(f"Qualification evidence {index}")
- before = w.state.read_bytes()
- other_root = tmp_path / "other-runtime"
- results = []
- for mode in ("soft_claim", "hard_lease"):
- argv = w.arguments("handoff-mode", "set", "--mode", mode)
- argv[argv.index("--runtime-root") + 1] = str(other_root)
- command = subprocess.run([sys.executable, "-m", "loopx.cli", *argv], cwd=REPO,
- capture_output=True, text=True, timeout=45)
- assert "Traceback" not in command.stderr, command.stderr
- results.append(json.loads(command.stdout))
- after_qualification = w.cli("coordination-shadow", "qualify", success=False)
- assert not any(result.get("ok") and result.get("changed") for result in results), {
- "override_results": results, "qualification_at_original_root": after_qualification,
- }
- assert w.state.read_bytes() == before
-
-
-
-
-@pytest.mark.parametrize("missing", ["identity", "candidate"])
-def test_missing_cursor_source_allocation_never_recreates_missing_store_files(
- tmp_path: Path, missing: str,
-) -> None:
- w = workspace(tmp_path)
- w.add("Existing generation")
- directory = outbox.partition_directory(w.runtime, w.goal, "todos")
- (directory / "drain-cursor.json").unlink()
- store = w.runtime / "authority-shadow" / "file-v0"
- path = store / "store-identity" if missing == "identity" else next(store.glob("authority-store-*.json"))
- path.unlink()
- shadow = w.runtime / "authority-shadow"
- before = {str(item.relative_to(shadow)): item.read_bytes() for item in shadow.rglob("*") if item.is_file()}
- primary = w.state.read_bytes()
- result = w.cli("todo", "add", "--role", "agent", "--text", "Read-only recovery cannot bootstrap", success=False)
- assert result["ok"] is False, result
- assert w.state.read_bytes() == primary
- assert not path.exists()
- assert {str(item.relative_to(shadow)): item.read_bytes() for item in shadow.rglob("*") if item.is_file()} == before
-
-
-def test_state_file_override_cannot_attribute_an_unbound_source_to_active_lineage(tmp_path: Path) -> None:
- w = workspace(tmp_path)
- other = tmp_path / "OTHER_ACTIVE_STATE.md"
- other.write_bytes(w.state.read_bytes())
- original = w.state.read_bytes()
- outcomes = []
- for mode in ("soft_claim", "hard_lease", "soft_claim", "hard_lease"):
- outcomes.append(w.cli("handoff-mode", "set", "--mode", mode, "--state-file", str(other), success=False))
- qualification = w.cli("coordination-shadow", "qualify", success=False)
- assert not any(result.get("ok") and result.get("changed") for result in outcomes), {
- "override_results": outcomes, "qualification_for_bound_source": qualification,
- }
- assert w.state.read_bytes() == original
diff --git a/tests/control_plane/test_shadow_drain_e2e.py b/tests/control_plane/test_shadow_drain_e2e.py
deleted file mode 100644
index 760069d95e..0000000000
--- a/tests/control_plane/test_shadow_drain_e2e.py
+++ /dev/null
@@ -1,268 +0,0 @@
-"""Receipt, cursor and crash invariants through real CLI and independent TS processes."""
-
-from __future__ import annotations
-
-import json
-from pathlib import Path
-import subprocess
-import sys
-
-import pytest
-
-from shadow_e2e_fixture import REPO, ShadowWorkspace, workspace
-from loopx.control_plane.coordination import local_authority_shadow_adapter as adapter
-from loopx.control_plane.coordination import local_authority_shadow_outbox as outbox
-
-
-pytestmark = pytest.mark.stage2c_e2e
-
-
-def directory(w: ShadowWorkspace) -> Path:
- return outbox.partition_directory(w.runtime, w.goal, "todos")
-
-
-def history(w: ShadowWorkspace) -> dict:
- return adapter.read_local_authority_shadow(
- runtime_root=w.runtime, goal_id=w.goal, scan_limit=10_000
- )
-
-
-def pending(w: ShadowWorkspace, text: str = "One primary mutation") -> dict:
- return w.crash("before_commit", "todo", "add", "--role", "agent", "--text", text)
-
-
-def snapshot(path: Path) -> dict[str, bytes]:
- return {
- str(item.relative_to(path)): item.read_bytes()
- for item in path.rglob("*")
- if item.is_file()
- }
-
-
-def test_public_primary_maps_one_to_one_to_receipts_and_replays_idempotently(
- tmp_path: Path,
-) -> None:
- w = workspace(tmp_path)
- ids = [w.add(f"Public fact {index}")["todo_id"] for index in range(3)]
- w.drain()
- view = history(w)
- transactions = view["proof"]["transactions"]
- assert len(transactions) == 4 # One complete baseline, three actual mutations.
- assert [item["todo_id"] for item in view["head"]["todos"]] == sorted(ids)
- assert view["head"]["handoff_mode"] == "hard_lease"
- assert view["head"]["leases"] == []
- assert [tx["receipts"][0]["seq"] for tx in transactions[1:]] == [1, 2, 3]
- assert all(tx["receipts"][0]["no_op"] is False for tx in transactions[1:])
- assert w.drain()["outcome"] == "nothing_pending"
- assert history(w)["proof"]["transactions"] == transactions
- assert [p.name for p in directory(w).iterdir()] == ["drain-cursor.json"]
- for args in (("qualify",), ("read-candidate", "--todo-id", ids[0])):
- arguments = w.arguments("coordination-shadow", *args)
- arguments[arguments.index("--format") + 1] = "markdown"
- result = subprocess.run([sys.executable, "-m", "loopx.cli", *arguments],
- cwd=REPO, capture_output=True, text=True, timeout=30)
- assert result.returncode == 0, result.stdout + result.stderr
- assert "qualification_scope: `bounded`" in result.stdout
- assert "sustained_parity_verdict: `not_evaluated`" in result.stdout
- assert "minimum_primary_mutations: `3`" in result.stdout
-
-
-def test_public_mutation_has_no_second_snapshot_mirror(tmp_path: Path) -> None:
- w = workspace(tmp_path)
- assert w.add("Exactly one durable primary mutation")["added"] is True
- module = REPO / "loopx/control_plane/coordination/file_authority_store.ts"
- script = (
- f"import {{FileAuthorityStore}} from {json.dumps(module.as_uri())};"
- "const store=new FileAuthorityStore(process.argv[1],process.argv[2],{existingOnly:true});"
- "process.stdout.write(JSON.stringify(await store.scanCommitted(null,10000)));"
- )
- readback = subprocess.run(
- ["node", "--no-warnings", "--experimental-strip-types", "--input-type=module", "-e", script,
- str(w.runtime / "authority-shadow/file-v0"), w.goal],
- capture_output=True, text=True, check=True, timeout=30,
- )
- page = json.loads(readback.stdout)
- assert page["status"] == "page", page
- assert len(page["transactions"]) == 2, page # Full baseline plus one mutation.
- assert page["transactions"][0]["receipts"] == []
- assert len(page["transactions"][1]["receipts"]) == 1
- assert page["transactions"][1]["receipts"][0]["write_class"] == "todo_add"
-
-
-@pytest.mark.parametrize(
- "attack",
- [
- "high_water",
- "fake_digest",
- "string_seq",
- "boolean_seq",
- "missing_anchor",
- "wrong_root",
- "wrong_lineage",
- ],
-)
-def test_cursor_cannot_authorize_deletion_or_hide_mutations(
- tmp_path: Path, attack: str
-) -> None:
- w = workspace(tmp_path)
- w.add("First captured mutation")
- pending(w, "Second real mutation")
- d = directory(w)
- cursor_path = d / "drain-cursor.json"
- value = json.loads(cursor_path.read_bytes())
- if attack == "high_water":
- value["last_seq"] = 9_999_999_999
- elif attack == "fake_digest":
- value["last_partition_digest"] = "sha256:" + "f" * 64
- elif attack == "string_seq":
- value["last_seq"] = "1"
- elif attack == "boolean_seq":
- value["last_seq"] = True
- elif attack == "missing_anchor":
- value["last_cursor"] = "uncommitted-cursor"
- else:
- entry = next(d.glob("*.prepared.json"))
- record = json.loads(entry.read_bytes())
- record[
- "source_root_digest" if attack == "wrong_root" else "capture_lineage_id"
- ] = "sha256:" + "a" * 64 if attack == "wrong_root" else "other-lineage"
- entry.write_text(json.dumps(record))
- cursor_path.write_text(json.dumps(value))
- before = snapshot(d)
- result = w.drain()
- assert result["ok"] is False, result
- assert result["reason_code"] in {
- "outbox_cursor_invalid",
- "outbox_cursor_unproved",
- "outbox_file_invalid",
- "stale_generation",
- }
- assert snapshot(d) == before
- assert len(history(w)["proof"]["transactions"]) == 2
- status = w.cli("authority-shadow", "status", success=False)
- assert "outbox" in status
-
-
-@pytest.mark.parametrize("window", ["after_commit", "after_cursor", "between_unlinks"])
-def test_real_sigkill_drain_windows_recover_from_exact_receipts(
- tmp_path: Path, window: str
-) -> None:
- w = workspace(tmp_path)
- w.crash(
- window,
- "todo",
- "add",
- "--role",
- "agent",
- "--text",
- "Committed before process death",
- )
- before = history(w)["proof"]["transactions"]
- assert len(before) == 2
- result = w.drain()
- assert result["ok"] is True, result
- assert result["replayed"] == 1
- assert result["delivered"] == 0
- assert history(w)["proof"]["transactions"] == before
- assert outbox.read_cursor(directory(w))["last_seq"] == 1
- assert [p.name for p in directory(w).iterdir()] == ["drain-cursor.json"]
-
-
-def test_missing_cursor_recovers_only_from_complete_verified_history(
- tmp_path: Path,
-) -> None:
- w = workspace(tmp_path)
- w.add("First")
- w.add("Second")
- before = history(w)["proof"]["transactions"]
- (directory(w) / "drain-cursor.json").unlink()
- assert w.drain()["ok"] is True
- assert outbox.read_cursor(directory(w))["last_seq"] == 2
- w.add("Third")
- assert history(w)["proof"]["transactions"][:3] == before
- assert outbox.read_cursor(directory(w))["last_seq"] == 3
-
-
-@pytest.mark.parametrize("window", ["before_replace", "after_replace", "before_marker"])
-def test_primary_sigkill_preserves_complete_bytes_and_proves_before_marker(
- tmp_path: Path, window: str
-) -> None:
- w = workspace(tmp_path)
- old = w.state.read_bytes()
- w.crash(
- window, "todo", "add", "--role", "agent", "--text", "Atomic primary sentence"
- )
- current = w.state.read_bytes()
- listed = w.cli("todo", "list")["todos"]
- assert (current == old) is (window == "before_replace")
- assert len(listed) == (0 if window == "before_replace" else 1)
- assert not list(directory(w).glob("*.committed.json"))
- result = w.drain()
- assert result["ok"] is True, result
- receipt = history(w)["proof"]["transactions"][-1]["receipts"][0]
- assert receipt["resolution"] == (
- "abandoned" if window == "before_replace" else "committed_proven_by_readback"
- )
- assert receipt["no_op"] is (window == "before_replace")
-
-
-def test_prepared_a_b_a_never_infers_first_write_was_abandoned(tmp_path: Path) -> None:
- w = workspace(tmp_path)
- w.crash("before_marker", "handoff-mode", "set", "--mode", "soft_claim")
- # A later real writer returns the canonical primary to its initial A.
- w.cli("handoff-mode", "set", "--mode", "hard_lease")
- assert w.cli("handoff-mode", "show")["handoff_mode"] == "hard_lease"
- before = snapshot(directory(w))
- result = w.drain()
- assert (
- result["ok"] is False and result["reason_code"] == "outbox_source_unproved"
- ), result
- assert snapshot(directory(w)) == before
- assert len(history(w)["proof"]["transactions"]) == 1
- qualify = w.cli("coordination-shadow", "qualify", success=False)
- assert qualify["ok"] is False
-
-
-def test_concurrent_real_drainers_commit_once_without_cursor_regression(
- tmp_path: Path,
-) -> None:
- w = workspace(tmp_path)
- for index in range(3):
- pending(w, f"Concurrent {index}")
- argv = [
- sys.executable,
- "-m",
- "loopx.cli",
- *w.arguments("authority-shadow", "drain", "--lock-timeout-seconds", "2"),
- ]
- children = [
- subprocess.Popen(
- argv, cwd=REPO, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
- )
- for _ in range(2)
- ]
- for child in children:
- stdout, stderr = child.communicate(timeout=45)
- assert "Traceback" not in stderr
- result = json.loads(stdout)
- assert result["outcome"] in {"drained", "nothing_pending", "drain_deferred"}, (
- result
- )
- assert w.drain()["ok"] is True
- txs = history(w)["proof"]["transactions"]
- assert len(txs) == 4
- assert len({tx["operation_id"] for tx in txs}) == 4
- assert outbox.read_cursor(directory(w))["last_seq"] == 3
-
-
-def test_bounded_drain_preserves_unprocessed_entries(tmp_path: Path) -> None:
- w = workspace(tmp_path)
- for index in range(3):
- pending(w, f"Bounded {index}")
- result = w.drain(max_entries="1")
- assert result["ok"] and result["delivered"] == 1 and result["budget_exhausted"], (
- result
- )
- assert [entry.seq for entry in outbox.list_entries(directory(w))] == [2, 3]
- assert w.drain()["ok"]
- assert len(history(w)["proof"]["transactions"]) == 4
diff --git a/tests/control_plane/test_shadow_drain_native_plan.py b/tests/control_plane/test_shadow_drain_native_plan.py
index c2b5801f31..973619bda4 100644
--- a/tests/control_plane/test_shadow_drain_native_plan.py
+++ b/tests/control_plane/test_shadow_drain_native_plan.py
@@ -1,21 +1,15 @@
-"""The Python entry transports one batch, with no source/history decision owner.
-
-Filesystem mutation and failure-window cases moved to shadow_drain.test.ts and
-real TS-process SIGKILL tests; patching retired Python internals proves nothing.
-"""
+"""The retained Python drain adapter stays inert when no history exists."""
from __future__ import annotations
-from pathlib import Path
import json
-from typing import Any
-
-import pytest
from loopx.control_plane.coordination import local_authority_shadow_adapter as adapter
-from test_local_authority_shadow_drain import _drain, _fixture, _record_todo_write
-def test_disabled_without_capture_state_does_not_start_native_runtime(tmp_path, monkeypatch):
+def test_disabled_without_history_does_not_start_native_runtime(
+ tmp_path,
+ monkeypatch,
+):
registry = tmp_path / "registry.json"
registry.write_text(json.dumps({"goals": [{"id": "goal-e2e"}]}))
@@ -23,52 +17,13 @@ def unexpected(*args, **kwargs):
raise AssertionError("feature-off drain must not invoke the runtime")
monkeypatch.setattr(adapter, "effect_runtime_result", unexpected)
- result = _drain(registry, tmp_path / "runtime")
- assert result.ok and result.outcome == "nothing_pending"
+ result = adapter.drain_local_authority_shadow_outbox(
+ registry_path=registry,
+ runtime_root=tmp_path / "runtime",
+ goal_id="goal-e2e",
+ max_entries=20,
+ budget_seconds=10,
+ )
+ assert result.ok
+ assert result.outcome == "nothing_pending"
assert not (tmp_path / "runtime").exists()
-
-
-def test_adapter_transports_one_batch_without_per_entry_rpc(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
-) -> None:
- registry, state, runtime = _fixture(tmp_path)
- for i in range(3):
- _record_todo_write(registry, state, runtime, f"Private source text {i}")
- actual = adapter.effect_runtime_result
- seen = []
-
- def rpc(method: str, request: dict, **kwargs: Any) -> dict:
- result = actual(method, request, **kwargs)
- seen.append((method, request, result, kwargs))
- return result
-
- monkeypatch.setattr(adapter, "effect_runtime_result", rpc)
- result = _drain(registry, runtime)
- assert result.ok and result.delivered == 3
- assert len(seen) == 1
- method, request, response, kwargs = seen[0]
- assert method == "coordination.runtime_shadow.drain"
- assert kwargs["retry_safe"] is False
- assert "projection" not in request and "entries" not in request
- assert "proof" not in response and "transactions" not in response and "head" not in response
-
-
-def test_lost_batch_response_reports_unknown_and_explicit_retry_reads_durable_facts(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
-) -> None:
- registry, state, runtime = _fixture(tmp_path)
- _record_todo_write(registry, state, runtime, "Retain this committed write")
- actual = adapter.effect_runtime_result
-
- def lose_response(method: str, request: dict, **kwargs: Any) -> dict:
- actual(method, request, **kwargs)
- raise TimeoutError("lost response after successful native drain")
-
- monkeypatch.setattr(adapter, "effect_runtime_result", lose_response)
- failed = _drain(registry, runtime)
- assert not failed.ok and failed.reason_code == "shadow_drain_outcome_unknown"
- monkeypatch.setattr(adapter, "effect_runtime_result", actual)
- recovered = _drain(registry, runtime)
- assert recovered.ok and recovered.outcome == "nothing_pending"
- view = adapter.read_local_authority_shadow(runtime_root=runtime, goal_id="goal-e2e", scan_limit=10)
- assert len(view["proof"]["transactions"]) == 2
diff --git a/tests/control_plane/test_shadow_fence_caller_parity_e2e.py b/tests/control_plane/test_shadow_fence_caller_parity_e2e.py
deleted file mode 100644
index e660be3c87..0000000000
--- a/tests/control_plane/test_shadow_fence_caller_parity_e2e.py
+++ /dev/null
@@ -1,342 +0,0 @@
-"""Fenced sibling-caller parity through the real CLI (Stage 2C E2E).
-
-Every row runs one public command against a workspace whose durable legacy
-writer fence is in one state, and compares the complete printed envelope, the
-exit status, and the primary-record effect with the literal expectation in
-tests/fixtures/control_plane/legacy_writer_fence_caller_parity_v0.json.
-Nothing is mocked; the fence is engaged by the real TypeScript owner.
-"""
-from __future__ import annotations
-
-import json
-import shutil
-import sys
-from collections.abc import Callable, Iterator
-from pathlib import Path
-
-import pytest
-from test_shadow_observable_e2e import Caller
-from test_shadow_observable_native_e2e import native
-
-pytestmark = pytest.mark.stage2c_e2e
-
-FIXTURE = Path(__file__).resolve().parents[1] / "fixtures" / "control_plane" / "legacy_writer_fence_caller_parity_v0.json"
-BUILDER = (
- "from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection as build; "
- "import json,sys; value=build(goal_id='observable', todos=json.loads(sys.argv[1]), "
- "leases=json.loads(sys.argv[2])); "
- "value['handoff_mode']='hard_lease'; print(json.dumps(value))"
-)
-PLACEHOLDERS = ("runtime_root", "todo_a", "todo_b", "todo_c", "todo_gate")
-
-
-def _posix_runtime_paths(value: object) -> object:
- """Spell paths under the runtime root with the fixture's POSIX separators."""
- if isinstance(value, str) and value.startswith("{runtime_root}"):
- return value.replace("\\", "/")
- if isinstance(value, list):
- return [_posix_runtime_paths(item) for item in value]
- if isinstance(value, dict):
- return {key: _posix_runtime_paths(item) for key, item in value.items()}
- return value
-
-
-class Workspace:
- """One seeded goal; rows are executed against it in fixture order."""
-
- def __init__(self, path: Path, mode: str, name: str) -> None:
- self.w = Caller(path, mode, name)
- self.ids: dict[str, str] = {}
- self.lease_version = 0
- self.lease_owner = "agent-a"
- self.lease_key = "parity-lease"
- self.control: dict[str, dict] | None = None
-
- def call(self, *args: str) -> dict:
- return self.w.call(*args)
-
- def exit(self) -> int:
- return int(self.w.rows[-1]["exit"])
-
- def outbox(self) -> set[str]:
- return {k for k in self.w.files() if k.startswith("runtime/authority-shadow/outbox/")}
-
- def fence_path(self) -> Path:
- from loopx.control_plane.coordination.legacy_writer_fence import (
- legacy_coordination_writer_fence_path,
- )
-
- return legacy_coordination_writer_fence_path(runtime_root=self.w.root, goal_id="observable")
-
- def normalize(self, value: object) -> object:
- text = json.dumps(value)
- text = text.replace(json.dumps(str(self.w.root))[1:-1], "{runtime_root}")
- for key in ("todo_gate", "todo_c", "todo_b", "todo_a"):
- if key in self.ids:
- text = text.replace(self.ids[key], "{" + key + "}")
- return _posix_runtime_paths(json.loads(text))
-
- def observe(self, args: tuple[str, ...]) -> dict:
- before, outbox_before = self.w.primary(), self.outbox()
- envelope = self.call(*args)
- after, outbox_after = self.w.primary(), self.outbox()
- return {
- "envelope": self.normalize(envelope),
- "exit": self.exit(),
- # Keep the exact effect shape and target; Todo IDs are opaque.
- "effect": self.normalize({
- "added": sorted(k for k in after if k not in before),
- "removed": sorted(k for k in before if k not in after),
- "changed": sorted(k for k in after if k in before and after[k] != before[k]),
- }),
- "outbox_added": sorted(outbox_after - outbox_before),
- }
-
-
-def seed_and_fence(ws: Workspace, todo_keys: tuple[str, ...]) -> None:
- records = [ws.w.read(ws.ids[key]) for key in todo_keys]
- lease_path = (
- ws.w.root / "goals" / "observable" / "task-leases" /
- f"{ws.ids['todo_b']}.json"
- )
- leases = [json.loads(lease_path.read_text(encoding="utf-8"))]
- projection = ws.w.invoke(
- [sys.executable, "-c", BUILDER, json.dumps(records), json.dumps(leases)],
- ["fixture-projection", json.dumps(records), ""],
- )
- assert native(ws.w, "seed", projection)["status"] == "applied"
-
-
-def build_seeded(path: Path, mode: str, name: str, *, gate: bool) -> Workspace:
- ws = Workspace(path, mode, name)
- assert ws.call("handoff-mode", "set", "--mode", "hard_lease")["ok"] is True
- ws.ids["todo_a"] = ws.w.add("Parity target A")
- ws.ids["todo_b"] = ws.w.add("Parity leased B")
- ws.ids["todo_c"] = ws.w.add("Parity fresh acquisition C")
- if gate:
- gate_add = ws.call("todo", "add", "--role", "user", "--task-class", "user_gate", "--global-gate",
- "--text", "Approve the parity plan")
- assert gate_add["ok"] is True, gate_add
- ws.ids["todo_gate"] = gate_add["todo_id"]
- ws.control = {"cli-task_lease_acquire-absent": ws.observe((
- "task-lease", "acquire", "--todo-id", ws.ids["todo_b"], "--owner", "agent-a",
- "--idempotency-key", "parity-lease", "--ttl-seconds", "3600"))}
- lease = ws.control["cli-task_lease_acquire-absent"]["envelope"]
- assert lease.get("acquired") is True, lease
- ws.lease_version = int(lease["lease"]["version"])
- seed_and_fence(ws, ("todo_a", "todo_b", "todo_c", "todo_gate") if gate else ("todo_a", "todo_b", "todo_c"))
- return ws
-
-
-def build_w3(path: Path) -> Workspace:
- ws = build_seeded(path, "absent", "fence-parity-invalid", gate=False)
- marker = json.loads(ws.fence_path().read_text(encoding="utf-8"))
- marker["state"] = "disengaged"
- ws.fence_path().write_text(json.dumps(marker), encoding="utf-8")
- return ws
-
-
-def build_w4(path: Path) -> Workspace:
- ws = build_seeded(path, "absent", "fence-parity-unreadable", gate=False)
- ws.fence_path().unlink()
- ws.fence_path().mkdir()
- return ws
-
-
-def build_w5(path: Path) -> Workspace:
- ws = Workspace(path, "absent", "fence-parity-no-store")
- assert ws.call("handoff-mode", "set", "--mode", "hard_lease")["ok"] is True
- ws.ids["todo_a"] = ws.w.add("Parity target A")
- ws.fence_path().parent.mkdir(parents=True, exist_ok=True)
- ws.fence_path().write_text(json.dumps({
- "schema_version": "loopx_legacy_coordination_writer_fence_v0", "state": "engaged", "goal_id": "observable",
- "fence_id": "caller-fixture", "source_version": "caller-fixture",
- "source_projection_sha256": "a" * 64, "expected_shadow_provider_revision": "file:1:aaaaaaaaaaaaaaaaaaaaaaaa",
- }), encoding="utf-8")
- return ws
-
-
-def lease_args(ws: Workspace, verb: str) -> tuple[str, ...]:
- todo_b, version = ws.ids.get("todo_b", ""), str(ws.lease_version)
- if verb == "acquire":
- return ("task-lease", "acquire", "--todo-id", ws.ids.get("todo_c", ws.ids.get("todo_a", "")), "--owner", "agent-a",
- "--idempotency-key", "parity-acquire", "--ttl-seconds", "3600")
- common = ("task-lease", verb, "--todo-id", todo_b, "--owner", ws.lease_owner,
- "--idempotency-key", ws.lease_key, "--expected-version", version)
- if verb == "renew":
- return (*common, "--ttl-seconds", "7200")
- if verb == "transfer":
- return (*common, "--new-owner", "agent-b", "--new-idempotency-key", "parity-transfer")
- return common
-
-
-def row_args(ws: Workspace, caller: str) -> tuple[str, ...]:
- a = ws.ids.get("todo_a", "")
- b = ws.ids.get("todo_b", "")
- gate = ws.ids.get("todo_gate", "")
- version = str(ws.lease_version)
- table: dict[str, tuple[str, ...]] = {
- "task_lease_acquire": lease_args(ws, "acquire"),
- "task_lease_renew": lease_args(ws, "renew"),
- "task_lease_transfer": lease_args(ws, "transfer"),
- "task_lease_release": lease_args(ws, "release"),
- "todo_complete": ("todo", "complete", "--todo-id", a, "--agent-id", "agent-a",
- "--evidence", "validation://parity", "--no-follow-up"),
- "todo_update_status": ("todo", "update", "--todo-id", a, "--agent-id", "agent-a", "--status", "deferred"),
- "todo_supersede": ("todo", "supersede", "--todo-id", a, "--agent-id", "agent-a",
- "--text", "Parity successor", "--evidence", "validation://parity"),
- "todo_archive_completed_execute": ("todo", "archive-completed", "--execute"),
- "todo_archive_completed_preview": ("todo", "archive-completed"),
- "handoff_mode_set": ("handoff-mode", "set", "--mode", "soft_claim"),
- "todo_complete_dry_run_gate": ("todo", "complete", "--todo-id", gate, "--agent-id", "agent-a",
- "--decision-outcome", "approve", "--evidence", "validation://parity",
- "--no-follow-up", "--dry-run"),
- "todo_supersede_dry_run": ("todo", "supersede", "--todo-id", a, "--agent-id", "agent-a",
- "--text", "Parity successor", "--evidence", "validation://parity", "--dry-run"),
- "todo_complete_dry_run_leased": ("todo", "complete", "--todo-id", b, "--agent-id", ws.lease_owner,
- "--task-lease-idempotency-key", ws.lease_key,
- "--task-lease-expected-version", version,
- "--evidence", "validation://parity", "--no-follow-up", "--dry-run"),
- }
- return table[caller]
-
-
-WORKSPACES = {
- "w1": lambda path: build_seeded(path, "absent", "fence-parity-engaged", gate=True),
- "w2": lambda path: build_seeded(path, "enabled", "fence-parity-engaged-capture", gate=False),
- "w3": build_w3,
- "w4": build_w4,
- "w5": build_w5,
-}
-
-
-def load_rows() -> list[dict]:
- if not FIXTURE.exists():
- # Keep the module importable for the fixture recorder; the test itself fails loudly.
- return [{"id": "fixture-missing", "surface": "cli", "workspace": None}]
- return [row for row in json.loads(FIXTURE.read_text(encoding="utf-8"))["rows"] if row["surface"] == "cli"]
-
-
-@pytest.fixture(scope="module")
-def workspaces(tmp_path_factory: pytest.TempPathFactory) -> Iterator[Callable[[str], Workspace]]:
- built: dict[str, Workspace] = {}
-
- def get(key: str) -> Workspace:
- # Single-row mutation probes need only this row's workspace. Keep reuse
- # and ordered mutations within a module, never across worker processes.
- if key not in built:
- built[key] = WORKSPACES[key](tmp_path_factory.mktemp(key))
- return built[key]
-
- yield get
- for ws in built.values():
- shutil.rmtree(ws.w.path, ignore_errors=True)
-
-
-def observe_row(ws: Workspace, row: dict) -> dict:
- if ws.control and row["id"] in ws.control:
- return ws.control[row["id"]]
- return ws.observe(row_args(ws, row["caller"]))
-
-
-def assert_canonical_lease_transition(ws: Workspace, caller: str, before: dict, observed: dict) -> None:
- """Prove the caller's transition and carry only independently checked proof."""
- assert "lease_path" not in observed["envelope"], observed
- canonical_after = native(ws.w, "read", {})
- old_head, new_head = before["head"], canonical_after["head"]
- assert int(new_head["cursor"]) == int(old_head["cursor"]) + 1
- assert new_head["head"]["todos"] == old_head["head"]["todos"]
- target = ws.ids["todo_b"]
- old_leases, new_leases = old_head["head"]["leases"], new_head["head"]["leases"]
- assert [lease for lease in new_leases if lease["todo_id"] != target] == [
- lease for lease in old_leases if lease["todo_id"] != target
- ]
- old = next(lease for lease in old_leases if lease["todo_id"] == target)
- current = next(lease for lease in new_leases if lease["todo_id"] == target)
- assert observed["envelope"]["lease"] == ws.normalize(current)
- released, transferred = caller == "task_lease_release", caller == "task_lease_transfer"
- expected = {
- "version": old["version"] + (0 if released else 1),
- "lease_epoch": old["lease_epoch"] + (1 if transferred else 0),
- "owner": "agent-b" if transferred else old["owner"],
- "idempotency_key": "parity-transfer" if transferred else old["idempotency_key"],
- "write_scopes": old["write_scopes"],
- "status": "released" if released else "active",
- }
- assert {key: current[key] for key in expected} == expected
- if released:
- assert current["expires_at"] == old["expires_at"]
- assert current["released_at"] == current["updated_at"]
- else:
- # Renew invalidates the old version; transfer also retires the sender's
- # owner/key. Preserve those distinct completion-proof rejections.
- stale = ws.call(*row_args(ws, "todo_complete_dry_run_leased"))
- code = "lease_cas_mismatch" if transferred else "version_mismatch"
- assert stale["ok"] is False and stale["error_code"] == code, stale
- assert native(ws.w, "read", {}) == canonical_after
- ws.lease_version = expected["version"]
- ws.lease_owner, ws.lease_key = expected["owner"], expected["idempotency_key"]
- # The receiver's current proof previews successfully, while a released
- # proof cannot authorize completion. Neither preview may mutate authority.
- preview = ws.call(*row_args(ws, "todo_complete_dry_run_leased"))
- if released:
- assert preview["ok"] is False and preview["error_code"] == "handoff_mode_requires_lease", preview
- else:
- assert preview["ok"] is True and preview["completed"] is True, preview
- assert native(ws.w, "read", {}) == canonical_after
-
-
-def assert_canonical_acquisition(ws: Workspace, before: dict, observed: dict) -> None:
- """New execution C must not change the existing B proof or keyless A tests."""
- after = native(ws.w, "read", {})
- old, current = before["head"], after["head"]
- assert int(current["cursor"]) == int(old["cursor"]) + 1
- assert current["head"]["todos"] == old["head"]["todos"]
- target = ws.ids["todo_c"]
- assert [r for r in current["head"]["leases"] if r["todo_id"] != target] == old["head"]["leases"]
- lease = next(r for r in current["head"]["leases"] if r["todo_id"] == target)
- assert lease["owner"] == "agent-a" and lease["idempotency_key"] == "parity-acquire"
- assert lease["version"] == lease["lease_epoch"] == 1 and lease["status"] == "active"
- assert observed["envelope"]["lease"] == ws.normalize(lease)
- assert "lease_path" not in observed["envelope"]
- replay = ws.call(*lease_args(ws, "acquire"))
- assert replay["ok"] and replay["idempotent"] and not replay["acquired"]
- assert replay["lease"] == lease
- assert native(ws.w, "read", {}) == after
-
-
-@pytest.mark.parametrize("row", load_rows(), ids=[row["id"] for row in load_rows()])
-def test_fence_caller_parity(workspaces: Callable[[str], Workspace], row: dict) -> None:
- if row["id"] == "fixture-missing":
- pytest.fail(f"parity fixture is missing: {FIXTURE}")
- ws = workspaces(row["workspace"])
- canonical_before = native(ws.w, "read", {}) if row["caller"] in {
- "task_lease_renew", "task_lease_transfer", "task_lease_release",
- } or (row["caller"] == "task_lease_acquire" and row["fence_state"] == "engaged") else None
- observed = observe_row(ws, row)
- assert observed["exit"] == row["exit"], observed
- if row.get("match") == "subset":
- assert {key: observed["envelope"].get(key) for key in row["expect"]} == row["expect"], observed
- else:
- assert observed["envelope"] == row["expect"], observed
- if row["caller"] == "handoff_mode_set":
- # This command now crosses the canonical boundary. Its dynamic revision,
- # operation ID and lease expiry are not a literal legacy-writer envelope.
- assert observed["envelope"].get("claimed_todos") or observed["envelope"].get("active_leases"), observed
- assert observed["envelope"].get("provider_revision"), observed
- if canonical_before is not None:
- # Public canonical callers change the provider; legacy wire rows stay fenced.
- if row["caller"] == "task_lease_acquire":
- assert_canonical_acquisition(ws, canonical_before, observed)
- else:
- assert_canonical_lease_transition(ws, row["caller"], canonical_before, observed)
- assert observed["effect"] == row["effect"], observed
- assert observed["outbox_added"] == row.get("outbox_added", []), observed
-
-
-def test_baseline_annotations_are_not_stale() -> None:
- for row in load_rows():
- for revision, delta in (row.get("baseline") or {}).items():
- if revision == "note":
- continue
- assert delta != row["expect"], row["id"]
diff --git a/tests/control_plane/test_shadow_goal_instance.py b/tests/control_plane/test_shadow_goal_instance.py
deleted file mode 100644
index ff979fbd17..0000000000
--- a/tests/control_plane/test_shadow_goal_instance.py
+++ /dev/null
@@ -1,379 +0,0 @@
-from __future__ import annotations
-
-import json
-import os
-import subprocess
-import sys
-from pathlib import Path
-
-import pytest
-
-from loopx.control_plane.coordination import local_authority_shadow_adapter as adapter
-from loopx.control_plane.coordination.runtime_shadow import (
- bootstrap_coordination_runtime_shadow,
- build_runtime_shadow_source_snapshot,
- rollback_coordination_runtime_shadow,
-)
-from loopx.control_plane.coordination.runtime_shadow_writer_adapter import (
- begin_todo_runtime_shadow_capture,
- write_captured_todo_state,
-)
-from loopx.control_plane.coordination.shadow_management import (
- ShadowManagementError,
- read_shadow_management_state,
-)
-from loopx.control_plane.coordination.shadow_goal_scope import shadow_goal_scope
-from loopx.control_plane.goals.source_session_recreation import (
- RecreateGoalRequest,
- recreate_goal_instance,
-)
-from loopx.control_plane.goals.source_session_registration import (
- FreshSourceSessionRegistration,
- register_fresh_source_session_project,
-)
-from loopx.control_plane.projects.registry_codec import load_project_registry
-from loopx.registry import find_registry_goal
-
-
-GOAL_ID = "shadow-lifetime"
-AGENT_ID = "shadow-worker"
-REPO_ROOT = Path(__file__).resolve().parents[2]
-
-
-def _register(tmp_path: Path) -> tuple[Path, Path, Path, dict[str, str]]:
- project_root = tmp_path / "project"
- project_root.mkdir()
- runtime_root = tmp_path / "runtime"
- registry_path = project_root / ".loopx" / "registry.json"
- result = register_fresh_source_session_project(
- FreshSourceSessionRegistration(
- registry_path=registry_path,
- runtime_root=runtime_root,
- operation_id="register-shadow-lifetime",
- project_id="project",
- goal_id=GOAL_ID,
- objective="Qualify exact shadow delivery.",
- non_goals=[],
- acceptance=["A replacement cannot drain the retired Goal outbox."],
- unknowns=[],
- next_effect="Bootstrap the exact shadow.",
- stop_condition="Stop when the exact drain is fenced.",
- project_record={
- "id": "project",
- "kind": "work",
- "path": str(project_root),
- },
- goal_record={
- "id": GOAL_ID,
- "project_id": "project",
- "title": "Shadow lifetime",
- "status": "active",
- "repo": str(project_root),
- "state_file": "GOAL.md",
- "coordination": {
- "registered_agents": [AGENT_ID],
- "runtime_shadow": {
- "schema_version": (
- "loopx_coordination_runtime_shadow_config_v0"
- ),
- "enabled": True,
- "provider": "file_v0",
- },
- },
- },
- state_file=project_root / "GOAL.md",
- )
- )
- goal_ref = dict(result["goal_ref"])
- return registry_path, runtime_root, project_root / "GOAL.md", goal_ref
-
-
-def _bootstrap(
- registry_path: Path,
- runtime_root: Path,
- state_path: Path,
- goal_ref: dict[str, str],
- operation_id: str,
-) -> dict[str, object]:
- registry = load_project_registry(registry_path)
- goal = find_registry_goal(registry, GOAL_ID)
- assert goal is not None
- projection, snapshot = build_runtime_shadow_source_snapshot(
- goal=goal,
- runtime_root=runtime_root,
- state_path=state_path,
- registry_path=registry_path,
- )
- result = bootstrap_coordination_runtime_shadow(
- goal=goal,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- operation_id=operation_id,
- source_version=operation_id,
- projection=projection,
- source_snapshot=snapshot,
- goal_ref=goal_ref,
- )
- assert result["status"] in {"applied", "recovered", "replayed"}, result
- return result
-
-
-def _add_todo(
- registry_path: Path,
- runtime_root: Path,
- state_path: Path,
- text: str,
- todo_id: str,
-) -> str:
- original = state_path.read_text(encoding="utf-8")
- heading = "## Agent Todo\n"
- planned = original.replace(
- heading,
- heading
- + f"\n- [ ] {text}\n"
- + f" \n",
- 1,
- )
- capture = begin_todo_runtime_shadow_capture(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- state_path=state_path,
- write_class="todo_add",
- original_text=original,
- )
- write_captured_todo_state(
- capture,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- state_path=state_path,
- text=planned,
- )
- assert capture.outcome.entry_id is not None
- return capture.outcome.entry_id
-
-
-def _outbox_bytes(runtime_root: Path) -> dict[str, bytes]:
- root = runtime_root / "authority-shadow" / "outbox" / GOAL_ID
- return {
- str(path.relative_to(root)): path.read_bytes()
- for path in root.rglob("*")
- if path.is_file()
- }
-
-
-def test_source_session_bootstrap_is_reachable_from_shipped_cli(
- tmp_path: Path,
-) -> None:
- registry_path, runtime_root, _state_path, goal_ref = _register(tmp_path)
-
- command = subprocess.run(
- [
- sys.executable,
- "-m",
- "loopx.entrypoint",
- "--registry",
- str(registry_path),
- "--format",
- "json",
- "coordination-shadow",
- "bootstrap",
- "--goal-id",
- GOAL_ID,
- "--execute",
- ],
- cwd=REPO_ROOT,
- env={**os.environ, "PYTHONPATH": str(REPO_ROOT), "LOOPX_USAGE_PING": "0"},
- text=True,
- capture_output=True,
- check=False,
- timeout=45,
- )
-
- assert command.returncode == 0, command.stdout + command.stderr
- assert json.loads(command.stdout)["bootstrap"]["status"] in {
- "applied",
- "recovered",
- "replayed",
- }
- state = read_shadow_management_state(runtime_root, GOAL_ID)
- assert state is not None
- assert state["schema_version"] == "loopx_shadow_management_state_v2"
- assert state["binding"]["goal_ref"] == goal_ref
-
-
-def test_source_session_bootstrap_uses_the_current_typed_goal_scope(
- tmp_path: Path,
-) -> None:
- registry_path, runtime_root, state_path, goal_ref = _register(tmp_path)
- goal = find_registry_goal(load_project_registry(registry_path), GOAL_ID)
- assert goal is not None
- projection, snapshot = build_runtime_shadow_source_snapshot(
- goal=goal,
- runtime_root=runtime_root,
- state_path=state_path,
- registry_path=registry_path,
- )
- with shadow_goal_scope(registry_path, goal_id=GOAL_ID) as scope:
- result = bootstrap_coordination_runtime_shadow(
- goal=scope.goal,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- operation_id="bootstrap:implicit-goal-ref",
- source_version="bootstrap:implicit-goal-ref",
- projection=projection,
- source_snapshot=snapshot,
- goal_ref=scope.goal_ref,
- )
-
- assert result["status"] in {"applied", "recovered", "replayed"}, result
- _add_todo(
- registry_path,
- runtime_root,
- state_path,
- "Capture work after an implicit exact bootstrap.",
- "todo_shadow_implicit",
- )
- state = read_shadow_management_state(runtime_root, GOAL_ID)
- assert state is not None
- assert state["binding"]["goal_ref"] == goal_ref
-
-
-def test_recreated_goal_cannot_capture_or_drain_the_retired_shadow(
- tmp_path: Path,
-) -> None:
- registry_path, runtime_root, state_path, goal_a = _register(tmp_path)
- first = _bootstrap(
- registry_path,
- runtime_root,
- state_path,
- goal_a,
- "bootstrap:goal-a",
- )
- _add_todo(
- registry_path,
- runtime_root,
- state_path,
- "Persist Goal A work before replacement.",
- "todo_shadow_a",
- )
- pending_a = _outbox_bytes(runtime_root)
- in_flight_original = state_path.read_text(encoding="utf-8")
- in_flight_a = begin_todo_runtime_shadow_capture(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- state_path=state_path,
- write_class="todo_update",
- original_text=in_flight_original,
- )
-
- recreated = recreate_goal_instance(
- RecreateGoalRequest(
- registry_path=registry_path,
- goal_id=GOAL_ID,
- goal_instance_id=goal_a["goal_instance_id"],
- operation_id="recreate-shadow-lifetime",
- )
- )
- goal_b = dict(recreated["goal_ref"])
- assert goal_b != goal_a
-
- with pytest.raises(ShadowManagementError, match="retired Goal instance"):
- write_captured_todo_state(
- in_flight_a,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- state_path=state_path,
- text=in_flight_original.replace(
- "Persist Goal A",
- "Continue retired Goal A",
- ),
- )
- assert state_path.read_text(encoding="utf-8") == in_flight_original
-
- stale = adapter.drain_local_authority_shadow_outbox(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- max_entries=10,
- budget_seconds=10,
- lock_timeout_seconds=2,
- )
- assert stale.outcome == "stopped"
- assert stale.reason_code == "stale_goal_instance"
- assert stale.delivered == 0
- assert _outbox_bytes(runtime_root) == pending_a
-
- original = state_path.read_text(encoding="utf-8")
- capture_b = begin_todo_runtime_shadow_capture(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- state_path=state_path,
- write_class="todo_add",
- original_text=original,
- )
- with pytest.raises(ShadowManagementError, match="durable shadow preparation"):
- write_captured_todo_state(
- capture_b,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- state_path=state_path,
- text=original.replace("Persist Goal A", "Mutate Goal B"),
- )
- assert state_path.read_text(encoding="utf-8") == original
-
- goal = find_registry_goal(load_project_registry(registry_path), GOAL_ID)
- assert goal is not None
- retired = rollback_coordination_runtime_shadow(
- goal=goal,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- operation_id="rollback:goal-a",
- expected_provider_revision=str(first["provider_revision"]),
- projection={},
- source_snapshot={"state_path": str(state_path)},
- )
- assert retired["status"] == "applied", retired
- archived = Path(str(retired["outbox_archive_path"]))
- assert {
- str(path.relative_to(archived)): path.read_bytes()
- for path in archived.rglob("*")
- if path.is_file()
- } == pending_a
-
- _bootstrap(
- registry_path,
- runtime_root,
- state_path,
- goal_b,
- "bootstrap:goal-b",
- )
- _add_todo(
- registry_path,
- runtime_root,
- state_path,
- "Persist Goal B work after replacement.",
- "todo_shadow_b",
- )
- current = adapter.drain_local_authority_shadow_outbox(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- max_entries=10,
- budget_seconds=10,
- lock_timeout_seconds=2,
- )
- assert current.outcome == "drained"
- assert current.delivered == 1
- replay = adapter.drain_local_authority_shadow_outbox(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- max_entries=10,
- budget_seconds=10,
- lock_timeout_seconds=2,
- )
- assert replay.outcome == "nothing_pending"
diff --git a/tests/control_plane/test_shadow_management_e2e.py b/tests/control_plane/test_shadow_management_e2e.py
deleted file mode 100644
index 7491afec69..0000000000
--- a/tests/control_plane/test_shadow_management_e2e.py
+++ /dev/null
@@ -1,312 +0,0 @@
-"""Public CLI and real Python -> TypeScript management interleavings.
-
-All goals and providers are disposable. The scheduling seam delays a real RPC;
-it never supplies a substitute provider result or edits an active user goal.
-"""
-
-from __future__ import annotations
-
-import hashlib
-import json
-from pathlib import Path
-import select
-import subprocess
-import sys
-import time
-
-import pytest
-
-from loopx.control_plane.coordination.shadow_management import read_shadow_management_state
-from loopx.control_plane.coordination.runtime_shadow import build_runtime_shadow_source_snapshot
-from loopx.control_plane.coordination.coordination_state_contract_generated import (
- COORDINATION_RUNTIME_SHADOW_BOOTSTRAP_REQUEST_SCHEMA,
-)
-from loopx.control_plane.effect_runtime import effect_runtime_result
-
-REPO_ROOT = Path(__file__).resolve().parents[2]
-
-
-pytestmark = pytest.mark.stage2c_e2e
-
-
-def _workspace(tmp_path: Path) -> tuple[Path, Path]:
- runtime = tmp_path / "runtime"
- goals = []
- for goal_id in ("goal-a", "goal-b"):
- repo = tmp_path / goal_id
- repo.mkdir()
- (repo / "ACTIVE_GOAL_STATE.md").write_text(
- f"---\ngoal_id: {goal_id}\nhandoff_mode: hard_lease\n"
- "updated_at: 2026-01-01T00:00:00+00:00\n---\n\n## Agent Todo\n\n"
- )
- goals.append({
- "id": goal_id, "status": "active", "repo": str(repo),
- "state_file": "ACTIVE_GOAL_STATE.md",
- "coordination": {
- "agent_model": "peer_v1", "registered_agents": ["agent-a", "agent-b"],
- "runtime_shadow": {
- "schema_version": "loopx_coordination_runtime_shadow_config_v0",
- "enabled": True, "provider": "file_v0",
- },
- },
- })
- registry = tmp_path / "registry.json"
- registry.write_text(json.dumps({"common_runtime_root": str(runtime), "goals": goals}))
- return registry, runtime
-
-
-def _arguments(registry: Path, runtime: Path, *args: str) -> list[str]:
- return ["--registry", str(registry), "--runtime-root", str(runtime), "--format", "json", *args]
-
-
-def _cli(registry: Path, runtime: Path, *args: str, success: bool = True) -> dict:
- result = subprocess.run(
- [sys.executable, "-m", "loopx.cli", *_arguments(registry, runtime, *args)],
- cwd=REPO_ROOT, capture_output=True, text=True, timeout=30,
- )
- if success:
- assert result.returncode == 0, f"{result.stdout}\n{result.stderr}"
- return json.loads(result.stdout)
-
-
-def _candidate(runtime: Path, goal: str) -> Path:
- digest = hashlib.sha256(goal.encode()).hexdigest()[:16]
- return runtime / "authority-shadow" / "file-v0" / f"authority-store-{digest}.json"
-
-
-def _bootstrap(registry: Path, runtime: Path, goal: str = "goal-a") -> dict:
- return _cli(registry, runtime, "coordination-shadow", "bootstrap", "--goal-id", goal, "--execute")["bootstrap"]
-
-
-def test_public_rollback_preserves_other_goal_and_replays_after_primary_changes(tmp_path: Path) -> None:
- registry, runtime = _workspace(tmp_path)
- first = _bootstrap(registry, runtime)
- _bootstrap(registry, runtime, "goal-b")
- other = _candidate(runtime, "goal-b").read_bytes()
- identity = (runtime / "authority-shadow" / "file-v0" / "store-identity").read_bytes()
- result = _cli(registry, runtime, "coordination-shadow", "rollback", "--goal-id", "goal-a",
- "--provider-revision", first["provider_revision"], "--execute")["rollback"]
- assert result["status"] == "applied"
- _cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent",
- "--text", "Write primary while awaiting bootstrap", "--claimed-by", "agent-a")
- assert not _candidate(runtime, "goal-a").exists()
- second = _bootstrap(registry, runtime)
- assert first["capture_lineage_id"] != second["capture_lineage_id"]
- current = _candidate(runtime, "goal-a").read_bytes()
- historical = _cli(registry, runtime, "coordination-shadow", "rollback", "--goal-id", "goal-a",
- "--provider-revision", first["provider_revision"], "--execute")["rollback"]
- assert historical["status"] == "replayed"
- assert historical["current_capture_lineage_id"] == second["capture_lineage_id"]
- assert _candidate(runtime, "goal-a").read_bytes() == current
- assert _candidate(runtime, "goal-b").read_bytes() == other
- assert (runtime / "authority-shadow" / "file-v0" / "store-identity").read_bytes() == identity
-
-
-_DRAIN_PHASE = {"before": "before_commit", "after": "after_commit"}
-
-# The public write keeps its own inline drain, but the pause now happens inside
-# the real native batch: the private fault driver runs the same
-# ``drainShadowOutbox`` the production RPC handler runs and stops at the actual
-# commit boundary. Releasing it lets that same batch continue, so a commit or a
-# cursor update attempted after the generation changed is still real.
-_DELAYED_WRITER = """
-import json, pathlib, subprocess, sys, time
-from loopx.control_plane.coordination import local_authority_shadow_adapter as adapter
-from loopx.cli import main
-barrier, release, phase = pathlib.Path(sys.argv[1]), pathlib.Path(sys.argv[2]), sys.argv[3]
-actual = adapter.effect_runtime_result
-def delayed(method, request, **kwargs):
- if method != 'coordination.runtime_shadow.drain':
- return actual(method, request, **kwargs)
- child = subprocess.Popen(
- ['node', '--no-warnings', '--experimental-strip-types',
- 'loopx/control_plane/testing/shadow_drain_fault_process.ts', phase, str(release)],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
- )
- child.stdin.write(json.dumps(request))
- child.stdin.close()
- for line in child.stdout:
- if line.startswith('BARRIER '):
- barrier.write_text(line.removeprefix('BARRIER '))
- break
- else:
- raise RuntimeError('native barrier missing: ' + child.stderr.read())
- deadline = time.monotonic() + 60
- while not release.exists():
- if time.monotonic() > deadline:
- child.kill(); child.wait()
- raise RuntimeError('test scheduling barrier timed out')
- time.sleep(.01)
- remaining, errors = child.stdout.read(), child.stderr.read()
- if child.wait() != 0:
- raise RuntimeError(errors or remaining)
- return json.loads(remaining.strip().splitlines()[-1])
-adapter.effect_runtime_result = delayed
-raise SystemExit(main(sys.argv[4:]))
-"""
-
-
-def _paused_writer(tmp_path: Path, registry: Path, runtime: Path, timing: str) -> tuple[subprocess.Popen, Path, dict]:
- barrier = tmp_path / "commit-barrier.json"
- release = tmp_path / "commit-release"
- args = _arguments(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent",
- "--text", "Transaction across a management boundary", "--claimed-by", "agent-a")
- child = subprocess.Popen(
- [sys.executable, "-c", _DELAYED_WRITER, str(barrier), str(release), _DRAIN_PHASE[timing], *args],
- cwd=REPO_ROOT, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
- )
- deadline = time.monotonic() + 20
- while not barrier.exists():
- if child.poll() is not None:
- stdout, stderr = child.communicate()
- pytest.fail(f"public writer exited before real commit RPC: {stdout}\n{stderr}")
- if time.monotonic() > deadline:
- child.kill()
- stdout, stderr = child.communicate()
- pytest.fail(f"public writer did not reach real commit RPC: {stdout}\n{stderr}")
- time.sleep(.01)
- # Atomic JSON is unnecessary for authority here; retry the test notification only.
- for _ in range(100):
- try:
- return child, release, json.loads(barrier.read_text())
- except json.JSONDecodeError:
- time.sleep(.01)
- child.kill()
- raise AssertionError("incomplete test barrier")
-
-
-def _release(child: subprocess.Popen, path: Path) -> dict:
- path.write_text("continue")
- try:
- stdout, stderr = child.communicate(timeout=30)
- except subprocess.TimeoutExpired:
- child.kill()
- child.communicate()
- raise
- assert child.returncode == 0, f"{stdout}\n{stderr}"
- return json.loads(stdout)
-
-
-@pytest.mark.parametrize("timing", ["before", "after"])
-def test_late_real_commit_cannot_cross_rollback_and_rebootstrap(tmp_path: Path, timing: str) -> None:
- registry, runtime = _workspace(tmp_path)
- first = _bootstrap(registry, runtime)
- _bootstrap(registry, runtime, "goal-b")
- other = _candidate(runtime, "goal-b").read_bytes()
- child, release, barrier = _paused_writer(tmp_path, registry, runtime, timing)
- try:
- request = barrier["request"]
- assert request["schema_version"] == "loopx_shadow_drain_v0"
- assert request["goal_id"] == "goal-a"
- assert request["runtime_root"] == str(runtime.resolve())
- # The paused batch still runs under the generation that is about to be
- # superseded, and the revision it targets is durable readback.
- management = read_shadow_management_state(runtime, "goal-a")
- assert management is not None and management["status"] == "active"
- assert management["binding"]["capture_lineage_id"] == first["capture_lineage_id"]
- revision = json.loads(_candidate(runtime, "goal-a").read_text())["provider_revision"]
- if timing == "before":
- assert revision == first["provider_revision"]
- else:
- assert revision != first["provider_revision"]
- rollback = _cli(registry, runtime, "coordination-shadow", "rollback", "--goal-id", "goal-a",
- "--provider-revision", revision, "--execute")["rollback"]
- archived = Path(rollback["outbox_archive_path"])
- retained = {str(path.relative_to(archived)): path.read_bytes() for path in archived.rglob("*") if path.is_file()}
- assert any(name.endswith(".prepared.json") for name in retained)
- second = _bootstrap(registry, runtime)
- assert second["capture_lineage_id"] != first["capture_lineage_id"]
- candidate = _candidate(runtime, "goal-a").read_bytes()
- payload = _release(child, release)
- # The resumed batch reports this write from its own drain evidence; it
- # must never claim a delivery into the generation that replaced it.
- assert payload["coordination_runtime_shadow"]["outcome"] != "delivered"
- assert _candidate(runtime, "goal-a").read_bytes() == candidate
- assert _candidate(runtime, "goal-b").read_bytes() == other
- assert {str(path.relative_to(archived)): path.read_bytes() for path in archived.rglob("*") if path.is_file()} == retained
- active_outbox = runtime / "authority-shadow" / "outbox" / "goal-a"
- assert not list(active_outbox.rglob("drain-cursor.json"))
- assert not list(active_outbox.rglob("*.prepared.json"))
- # An explicit retry of the superseded batch reads durable receipts and
- # still cannot deliver into the generation that replaced it.
- late = effect_runtime_result("coordination.runtime_shadow.drain", request)
- assert late["outcome"] not in {"delivered", "replayed", "reconciled"}
- assert late["delivered"] == 0 and late["replayed"] == 0
- assert _candidate(runtime, "goal-a").read_bytes() == candidate
- finally:
- if child.poll() is None:
- child.kill()
- child.communicate()
-
-
-def test_corrupt_history_after_real_commit_cannot_authorize_cursor_cleanup(tmp_path: Path) -> None:
- registry, runtime = _workspace(tmp_path)
- first = _bootstrap(registry, runtime)
- child, release, barrier = _paused_writer(tmp_path, registry, runtime, "after")
- try:
- # The commit already happened: the durable candidate moved past bootstrap.
- assert barrier["request"]["goal_id"] == "goal-a"
- candidate = _candidate(runtime, "goal-a")
- assert json.loads(candidate.read_text())["provider_revision"] != first["provider_revision"]
- directory = runtime / "authority-shadow" / "outbox" / "goal-a" / "todos"
- entries = {path.name: path.read_bytes() for path in directory.glob("*.json")}
- assert any(name.endswith(".prepared.json") for name in entries)
- record = json.loads(candidate.read_text())
- record["committed"][0]["provider_revision"] = "file:1:" + "0" * 24
- candidate.write_text(json.dumps(record))
- corrupt = candidate.read_bytes()
- _release(child, release)
- assert {path.name: path.read_bytes() for path in directory.glob("*.json")} == entries
- assert candidate.read_bytes() == corrupt
- assert not (directory / "drain-cursor.json").exists()
- finally:
- if child.poll() is None:
- child.kill()
- child.communicate()
-
-
-@pytest.mark.parametrize("phase", ["bootstrap_prepared", "bootstrap_candidate_committed", "bootstrap_outbox_ready"])
-def test_public_bootstrap_operation_selector_aborts_real_crash(tmp_path: Path, phase: str) -> None:
- registry, runtime = _workspace(tmp_path)
- _bootstrap(registry, runtime, "goal-b")
- other = _candidate(runtime, "goal-b").read_bytes()
- goal = json.loads(registry.read_text())["goals"][0]
- state_path = Path(goal["repo"]) / goal["state_file"]
- original = state_path.read_bytes()
- projection, snapshot = build_runtime_shadow_source_snapshot(
- goal=goal, runtime_root=runtime, state_path=state_path, registry_path=registry,
- )
- operation = "bootstrap:public-crash"
- request = {"schema_version": COORDINATION_RUNTIME_SHADOW_BOOTSTRAP_REQUEST_SCHEMA,
- "runtime_root": str(runtime), "goal_id": "goal-a", "operation_id": operation,
- "source_version": "source:before-crash", "projection": projection, "source_snapshot": snapshot}
- worker = REPO_ROOT / "tests" / "control_plane_ts" / "fixtures" / "shadow_management_crash_worker.ts"
- child = subprocess.Popen(
- ["node", "--no-warnings", "--experimental-strip-types", str(worker), "bootstrap-public", json.dumps(request), phase],
- cwd=REPO_ROOT, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
- )
- try:
- ready, _, _ = select.select([child.stdout], [], [], 10)
- assert ready, "bootstrap did not reach the real filesystem barrier"
- line = child.stdout.readline()
- assert line.strip() == f"ready:{phase}", line
- child.kill()
- child.communicate(timeout=10)
- pending = read_shadow_management_state(runtime, "goal-a")
- assert pending is not None and pending["status"] == "bootstrapping"
- blocked = _cli(registry, runtime, "todo", "add", "--goal-id", "goal-a", "--role", "agent",
- "--text", "Must not write while bootstrap is pending", "--claimed-by", "agent-a", success=False)
- assert blocked["ok"] is False
- assert state_path.read_bytes() == original
- stopped = _cli(registry, runtime, "coordination-shadow", "rollback", "--goal-id", "goal-a",
- "--bootstrap-operation-id", operation, "--execute")["rollback"]
- assert stopped["status"] == "applied"
- assert read_shadow_management_state(runtime, "goal-a")["status"] == "inactive"
- assert _candidate(runtime, "goal-b").read_bytes() == other
- current = _bootstrap(registry, runtime)
- assert current["status"] == "applied"
- assert current["capture_lineage_id"] != stopped["capture_lineage_id"]
- finally:
- if child.poll() is None:
- child.kill()
- child.communicate()
diff --git a/tests/control_plane/test_shadow_management_variant_e2e.py b/tests/control_plane/test_shadow_management_variant_e2e.py
index ab045bfd02..601afaa992 100644
--- a/tests/control_plane/test_shadow_management_variant_e2e.py
+++ b/tests/control_plane/test_shadow_management_variant_e2e.py
@@ -11,6 +11,7 @@
from pathlib import Path
import select
import subprocess
+import sys
import pytest
@@ -22,12 +23,92 @@
from loopx.control_plane.coordination.runtime_shadow import build_runtime_shadow_source_snapshot
from loopx.control_plane.coordination.shadow_management import read_shadow_management_state
from loopx.control_plane.effect_runtime import effect_runtime_result
-from tests.control_plane.shadow_e2e_fixture import ShadowWorkspace
-from tests.control_plane.test_shadow_management_e2e import (
- REPO_ROOT, _bootstrap, _candidate, _cli, _workspace,
-)
pytestmark = pytest.mark.stage2c_e2e
+REPO_ROOT = Path(__file__).resolve().parents[2]
+
+
+def _workspace(tmp_path: Path) -> tuple[Path, Path]:
+ runtime = tmp_path / "runtime"
+ goals = []
+ for goal_id in ("goal-a", "goal-b"):
+ repo = tmp_path / goal_id
+ repo.mkdir()
+ (repo / "ACTIVE_GOAL_STATE.md").write_text(
+ f"---\ngoal_id: {goal_id}\nhandoff_mode: hard_lease\n"
+ "updated_at: 2026-01-01T00:00:00+00:00\n---\n\n## Agent Todo\n\n"
+ )
+ goals.append({
+ "id": goal_id,
+ "status": "active",
+ "repo": str(repo),
+ "state_file": "ACTIVE_GOAL_STATE.md",
+ "coordination": {
+ "agent_model": "peer_v1",
+ "registered_agents": ["agent-a", "agent-b"],
+ "runtime_shadow": {
+ "schema_version": "loopx_coordination_runtime_shadow_config_v0",
+ "enabled": True,
+ "provider": "file_v0",
+ },
+ },
+ })
+ registry = tmp_path / "registry.json"
+ registry.write_text(json.dumps({
+ "common_runtime_root": str(runtime),
+ "goals": goals,
+ }))
+ return registry, runtime
+
+
+def _cli(
+ registry: Path,
+ runtime: Path,
+ *args: str,
+ success: bool = True,
+) -> dict:
+ result = subprocess.run(
+ [
+ sys.executable,
+ "-m",
+ "loopx.cli",
+ "--registry",
+ str(registry),
+ "--runtime-root",
+ str(runtime),
+ "--format",
+ "json",
+ *args,
+ ],
+ cwd=REPO_ROOT,
+ capture_output=True,
+ text=True,
+ timeout=30,
+ )
+ if success:
+ assert result.returncode == 0, f"{result.stdout}\n{result.stderr}"
+ return json.loads(result.stdout)
+
+
+def _candidate(runtime: Path, goal: str) -> Path:
+ digest = hashlib.sha256(goal.encode()).hexdigest()[:16]
+ return runtime / "authority-shadow" / "file-v0" / f"authority-store-{digest}.json"
+
+
+def _bootstrap(
+ registry: Path,
+ runtime: Path,
+ goal: str = "goal-a",
+) -> dict:
+ return _cli(
+ registry,
+ runtime,
+ "coordination-shadow",
+ "bootstrap",
+ "--goal-id",
+ goal,
+ "--execute",
+ )["bootstrap"]
_CRASH_RPC = r"""
@@ -120,49 +201,6 @@ def _durable_files(root: Path) -> dict[str, bytes]:
}
-@pytest.mark.parametrize("archive", ["candidate", "outbox"])
-def test_pending_rollback_checks_corrupted_archive_before_finishing_and_can_retry_exact_repair(
- tmp_path: Path, archive: str,
-) -> None:
- registry, runtime = _workspace(tmp_path)
- first = _bootstrap(registry, runtime)
- _bootstrap(registry, runtime, "goal-b")
- _, state = _source(registry, runtime)
- w = ShadowWorkspace(registry, runtime, state, "goal-a")
- w.crash("before_commit", "todo", "add", "--role", "agent", "--text", "Pending retained mutation")
- candidate, outbox = _candidate(runtime, "goal-a"), runtime / "authority-shadow/outbox/goal-a"
- pending_bytes = _durable_files(outbox)
- assert any(name.endswith(".prepared.json") for name in pending_bytes)
- candidate_bytes, primary_bytes = candidate.read_bytes(), state.read_bytes()
- request = _rollback_request(runtime, state, first["provider_revision"])
- moved = _kill_rpc("rollback", request, candidate if archive == "candidate" else outbox)
- archived = Path(moved["destination"])
- damaged = archived if archive == "candidate" else next(archived.rglob("*.prepared.json"))
- original = damaged.read_bytes()
- damaged.write_bytes(original + b"\ncorrupted fixture archive")
- before = _durable_files(runtime)
- result = _cli(registry, runtime, "coordination-shadow", "rollback", "--goal-id", "goal-a",
- "--provider-revision", first["provider_revision"], "--execute", success=False)
- assert result["ok"] is False, result
- assert result["rollback"]["reason_code"] == f"rollback_{archive}_identity_mismatch", result
- after = _durable_files(runtime)
- journal = str((_management(runtime) / "state.json").relative_to(runtime))
- # Recovery may persist the same verified phase in canonical JSON order;
- # only representation may differ, never transition state or evidence bytes.
- assert json.loads(after.pop(journal)) == json.loads(before.pop(journal))
- assert after == before
- assert state.read_bytes() == primary_bytes
- assert read_shadow_management_state(runtime, "goal-a")["status"] == "rolling_back"
- damaged.write_bytes(original)
- recovered = _cli(registry, runtime, "coordination-shadow", "rollback", "--goal-id", "goal-a",
- "--provider-revision", first["provider_revision"], "--execute")["rollback"]
- assert recovered["status"] == "recovered", recovered
- assert Path(recovered["candidate_archive_path"]).read_bytes() == candidate_bytes
- assert _durable_files(Path(recovered["outbox_archive_path"])) == pending_bytes
- assert read_shadow_management_state(runtime, "goal-a")["status"] == "inactive"
- assert state.read_bytes() == primary_bytes
-
-
def test_changed_source_after_bootstrap_commit_requires_abort_before_new_baseline(tmp_path: Path) -> None:
registry, runtime = _workspace(tmp_path)
request, state = _source(registry, runtime)
diff --git a/tests/control_plane/test_shadow_mutant_locators.py b/tests/control_plane/test_shadow_mutant_locators.py
index 6378b62639..0914f62c9b 100644
--- a/tests/control_plane/test_shadow_mutant_locators.py
+++ b/tests/control_plane/test_shadow_mutant_locators.py
@@ -1,7 +1,6 @@
"""Every Stage 2C mutation must still target the current production source."""
from __future__ import annotations
-import ast
import importlib.util
import sys
from pathlib import Path
@@ -27,19 +26,3 @@ def test_mutation_targets_current_production_source(case) -> None:
original = (ROOT / relative_path).read_text(encoding="utf-8")
mutated = edit(original)
assert mutated != original, f"{case.name}: mutation did not change {relative_path}"
-
-
-@pytest.mark.parametrize("name", ["fence_outside_lock", "source_binding_outside_lock"])
-def test_lock_mutations_preserve_executable_dry_run_and_locked_body(name: str) -> None:
- case = next(case for case in HARNESS.CASES if case.name == name)
- relative_path, edit = case.edits[0]
- mutated = edit((ROOT / relative_path).read_text(encoding="utf-8"))
- compile(mutated, relative_path, "exec")
- function = next(node for node in ast.parse(mutated).body
- if isinstance(node, ast.FunctionDef)
- and node.name == "legacy_todo_write_transaction")
- guard, locks = function.body[-2:]
- assert isinstance(guard, ast.If) and ast.unparse(guard.test) == "not dry_run"
- assert isinstance(locks, ast.With) and len(locks.items) == 2
- assert len(locks.body) == 1 and isinstance(locks.body[0], ast.Expr)
- assert isinstance(locks.body[0].value, ast.Yield)
diff --git a/tests/control_plane/test_shadow_observable_e2e.py b/tests/control_plane/test_shadow_observable_e2e.py
deleted file mode 100644
index cf5a88691b..0000000000
--- a/tests/control_plane/test_shadow_observable_e2e.py
+++ /dev/null
@@ -1,396 +0,0 @@
-"""Caller semantics across capture activation, with optional base/head evidence.
-
-LOOPX_SHADOW_COMPARISON_SOURCE selects an immutable checkout for every child;
-LOOPX_SHADOW_COMPARISON_OUTPUT retains complete responses and persisted bytes.
-Neither changes the oracle. No product result or persistence operation is mocked.
-"""
-from __future__ import annotations
-
-import json
-import os
-from pathlib import Path
-import subprocess
-import sys
-
-import pytest
-
-pytestmark = pytest.mark.stage2c_e2e
-SOURCE = Path(os.environ.get('LOOPX_SHADOW_COMPARISON_SOURCE', Path(__file__).resolve().parents[2])).resolve()
-
-
-class Caller:
- def __init__(self, path: Path, mode: str, name: str):
- self.path, self.mode, self.name = path, mode, name
- self.state, self.registry, self.root = path / 'STATE.md', path / 'registry.json', path / 'runtime'
- self.env = dict(os.environ, PYTHONPATH=str(SOURCE))
- self.rows: list[dict] = []
- self.state.write_text('---\ngoal_id: observable\nhandoff_mode: soft_claim\n'
- 'updated_at: 2026-09-01T00:00:00+00:00\n---\n\n## Agent Todo\n\n'
- '## Progress Ledger\n\n## Next Action\n\n- Inspect.\n')
- coordination = {'agent_model': 'peer_v1', 'registered_agents': ['agent-a', 'agent-b']}
- if mode != 'absent':
- coordination['runtime_shadow'] = {'schema_version': 'loopx_coordination_runtime_shadow_config_v0',
- 'enabled': mode == 'enabled', 'provider': 'file_v0'}
- self.registry.write_text(json.dumps({'common_runtime_root': str(self.root), 'goals': [{
- 'id': 'observable', 'status': 'active', 'repo': str(path), 'state_file': self.state.name,
- 'coordination': coordination}]}))
- actual = subprocess.check_output([sys.executable, '-c', 'import loopx; print(loopx.__file__)'],
- env=self.env, cwd=path, text=True).strip()
- assert Path(actual).resolve().is_relative_to(SOURCE)
- if mode == 'enabled':
- assert self.call('coordination-shadow', 'bootstrap', '--execute')['bootstrap']['status'] == 'applied'
-
- def files(self) -> dict[str, str]:
- # All fixture files, including receipts. Locks are retained in raw evidence too.
- # Keys use POSIX separators so prefix filters work on every host.
- return {p.relative_to(self.path).as_posix(): p.read_bytes().hex()
- for p in sorted(self.path.rglob('*')) if p.is_file()}
-
- def primary(self) -> dict[str, str]:
- return {k: v for k, v in self.files().items()
- if k == 'STATE.md' or k.startswith('runtime/goals/')
- and not k.endswith(('.lock', '.lock.holder'))} # Windows keeps holder records apart.
-
- def call(self, *args: str, goal: str = 'observable') -> dict:
- command = [sys.executable, '-m', 'loopx.cli', '--registry', str(self.registry),
- '--runtime-root', str(self.root), '--format', 'json', *args, '--goal-id', goal]
- return self.invoke(command, command[3:])
-
- def invoke(self, command: list[str], arguments: list[str], *, cwd: Path | None = None) -> dict:
- before = self.files()
- result = subprocess.run(command, env=self.env, cwd=cwd or self.path, capture_output=True, text=True, timeout=45)
- assert 'Traceback' not in result.stderr, result.stderr
- row = {'arguments': arguments, 'exit': result.returncode, 'stdout': result.stdout,
- 'stderr': result.stderr, 'files_before': before, 'files_after': self.files()}
- self.rows.append(row)
- destination = os.environ.get('LOOPX_SHADOW_COMPARISON_OUTPUT')
- if destination:
- output = Path(destination)
- output.mkdir(parents=True, exist_ok=True)
- (output / (self.name + '.json')).write_text(json.dumps({
- 'source': str(SOURCE), 'workspace': str(self.path), 'mode': self.mode, 'rows': self.rows}, indent=2))
- return json.loads(result.stdout) if result.stdout else {'stderr': result.stderr, 'exit': result.returncode}
-
- def add(self, text: str, *extra: str) -> str:
- result = self.call('todo', 'add', '--role', 'agent', '--text', text, *extra)
- assert result['ok'] is True, result
- return result['todo_id']
-
- def read(self, todo: str) -> dict:
- result = self.call('todo', 'list')
- assert result['ok'] is True, result
- return next(item for item in result['todos'] if item['todo_id'] == todo)
-
-
-@pytest.fixture(params=['absent', 'disabled', 'enabled'])
-def caller(tmp_path: Path, request: pytest.FixtureRequest) -> Caller:
- return Caller(tmp_path, request.param, request.node.name)
-
-
-def test_todo_argument_intent_and_rejections(caller: Caller) -> None:
- w = caller
- before = w.primary()
- preview = w.call('todo', 'add', '--role', 'agent', '--text', 'Retain operator intent', '--dry-run')
- assert preview['ok'] is True and w.primary() == before
- todo = w.add('Retain operator intent', '--claimed-by', 'agent-a', '--note', 'Initial context')
- created = w.read(todo)
- assert created['claimed_by'] == 'agent-a'
- assert (created['text'], created['priority']) == ('[P1] Retain operator intent', 'P1')
- for args, expected_note in [(('--note', 'Updated context'), 'Updated context'),
- (('--text', 'Corrected operator intent'), 'Updated context')]:
- result = w.call('todo', 'update', '--todo-id', todo, '--agent-id', 'agent-a', *args)
- assert result['ok'] is True, result
- current = w.read(todo)
- assert current['note'] == expected_note and current['claimed_by'] == 'agent-a'
- assert (current['text'], current['priority']) == ('[P1] Corrected operator intent', 'P1')
- # CLI mutable-field validation treats an empty note as absent, not a clear.
- before = w.primary()
- empty = w.call('todo', 'update', '--todo-id', todo, '--agent-id', 'agent-a', '--note', '')
- assert empty['error'] == 'todo update requires at least one mutable todo field'
- assert w.primary() == before
- assert w.call('todo', 'update', '--todo-id', todo, '--agent-id', 'agent-a', '--clear-claim')['ok'] is True
- assert not w.read(todo).get('claimed_by')
- before = w.primary()
- # Overlapping invalid target and actor: preserve the complete diagnostic/priority.
- rejected = w.call('todo', 'update', '--todo-id', 'todo_missing', '--agent-id', 'unknown', '--note', 'Must not write')
- assert rejected['ok'] is False and w.primary() == before, rejected
- assert w.call('todo', 'claim', '--todo-id', todo, '--claimed-by', 'agent-a', '--agent-id', 'agent-a')['ok'] is True
- complete = w.call('todo', 'complete', '--todo-id', todo, '--agent-id', 'agent-a',
- '--evidence', 'validation://caller', '--no-follow-up')
- assert complete['ok'] is True, complete
- assert w.read(todo)['done'] is True
- archived = w.call('todo', 'archive-completed', '--max-active-done', '0', '--execute')
- assert archived['ok'] is True, archived
- assert 'Corrected operator intent' in w.state.read_text()
-
-
-def test_handoff_mode_quiescence(caller: Caller) -> None:
- w = caller
- assert w.call('handoff-mode', 'set', '--mode', 'hard_lease')['changed'] is True
- before = w.primary()
- assert w.call('handoff-mode', 'set', '--mode', 'hard_lease')['changed'] is False
- assert w.primary() == before
- todo = w.add('Quiescence ownership')
- acquired = w.call('task-lease', 'acquire', '--todo-id', todo, '--owner', 'agent-a', '--idempotency-key', 'quiescence')
- assert acquired['acquired'] is True, acquired
- before = w.primary()
- blocked = w.call('handoff-mode', 'set', '--mode', 'soft_claim')
- assert blocked['error_code'] == 'handoff_mode_not_quiescent' and w.primary() == before
- malformed = w.call('handoff-mode', 'set', '--mode', 'invalid')
- assert malformed['exit'] == 2 and w.primary() == before
-
-
-def test_lease_arguments_cas_transfer_and_replay(caller: Caller) -> None:
- w = caller
- assert w.call('handoff-mode', 'set', '--mode', 'hard_lease')['ok'] is True
- todo = w.add('Lease argument round trip')
- args = ('task-lease', 'acquire', '--todo-id', todo, '--owner', 'agent-a',
- '--idempotency-key', 'caller-acquire', '--ttl-seconds', '3600', '--write-scope', 'src/**')
- result = w.call(*args)
- assert result['acquired'] is True, result
- lease_path = w.root / 'goals' / 'observable' / 'task-leases' / f'{todo}.json'
- persisted = json.loads(lease_path.read_bytes())
- assert persisted['owner'] == 'agent-a' and persisted['write_scopes'] == ['src/**']
- assert persisted['acquire_ttl_seconds'] == 3600
- before = lease_path.read_bytes()
- assert w.call(*args)['ok'] is True
- assert lease_path.read_bytes() == before
- before = w.primary()
- rejected = w.call('task-lease', 'renew', '--todo-id', todo, '--owner', 'agent-b',
- '--idempotency-key', 'wrong-key', '--expected-version', '999')
- assert rejected['ok'] is False and w.primary() == before, rejected
- renewed = w.call('task-lease', 'renew', '--todo-id', todo, '--owner', 'agent-a',
- '--idempotency-key', 'caller-acquire', '--expected-version', str(persisted['version']), '--ttl-seconds', '7200')
- assert renewed['ok'] is True, renewed
- transferred = w.call('task-lease', 'transfer', '--todo-id', todo, '--owner', 'agent-a',
- '--idempotency-key', 'caller-acquire', '--expected-version', str(renewed['lease']['version']),
- '--new-owner', 'agent-b', '--new-idempotency-key', 'caller-transfer')
- assert transferred['ok'] is True, transferred
- assert json.loads(lease_path.read_bytes())['owner'] == 'agent-b'
- released = w.call('task-lease', 'release', '--todo-id', todo, '--owner', 'agent-b',
- '--idempotency-key', 'caller-transfer', '--expected-version', str(transferred['lease']['version']))
- assert released['ok'] is True, released
- assert json.loads(lease_path.read_bytes())['status'] == 'released'
- assert w.call('task-lease', 'inspect', '--todo-id', todo)['ok'] is True
-
-
-def test_refresh_bound_step_and_reward_owned_prose(caller: Caller) -> None:
- w = caller
- todo = w.add('Canonical record must survive prose')
- record = w.read(todo)
- args = ('refresh-state', '--agent-id', 'agent-a', '--progress-scope', 'goal', '--classification', 'continue',
- '--recommended-action', 'Read the independent lease snapshot.', '--vision-unchanged-reason', 'Same bounded validation.',
- '--next-action', 'Read the independent lease snapshot.', '--no-global-sync')
- before = w.primary()
- assert w.call(*args, '--dry-run')['ok'] is True
- assert w.primary() == before
- refreshed = w.call(*args)
- assert refreshed['ok'] is True, refreshed
- assert 'Read the independent lease snapshot.' not in w.state.read_text()
- assert refreshed['recommended_action_resolution']['recommended_action_source'] == 'agent_lane_step'
- assert w.read(todo) == record
- args = ('reward', '--actor-kind', 'owner', '--recorded-at', '2026-09-01T12:00:00+00:00', '--decision', 'continue',
- '--reward', 'positive', '--reason-summary', 'Retained argument evidence.', '--write-active-state-summary')
- before = w.primary()
- assert w.call(*args, '--dry-run')['ok'] is True
- assert w.primary() == before
- reward = w.call(*args)
- assert reward['ok'] is True, reward
- assert 'Retained argument evidence.' in w.state.read_text()
- index = w.root / 'goals' / 'observable' / 'runs' / 'index.jsonl'
- assert 'Retained argument evidence.' in index.read_text()
- assert w.read(todo) == record
- before = w.primary()
- rejected = w.call('reward', '--run-generated-at', 'missing', '--decision', '', '--reward', 'positive', '--reason-summary', '')
- assert rejected['ok'] is False and w.primary() == before, rejected
-
-
-@pytest.mark.parametrize('replacement', ['force', 'missing'])
-def test_bootstrap_replacement_preserves_existing_authority(caller: Caller, replacement: str) -> None:
- w = caller
- w.add('Existing canonical state')
- args = ('bootstrap', '--project', str(w.path), '--state-file', 'STATE.md',
- '--objective', 'Replacement objective', '--no-global-sync')
- assert w.call(*args, '--dry-run')['ok'] is True
- if replacement == 'missing':
- w.state.unlink()
- before = w.registry.read_bytes(), w.state.read_bytes() if w.state.exists() else None
- result = w.call(*args, *(['--force'] if replacement == 'force' else []))
- if w.mode == 'enabled':
- assert result['ok'] is False, result
- assert (w.registry.read_bytes(), w.state.read_bytes() if w.state.exists() else None) == before
- else:
- assert result['ok'] is True, result
- assert 'Replacement objective' in w.state.read_text()
- assert json.loads(w.registry.read_bytes())['common_runtime_root'] == str(w.root)
-
-
-def test_project_registration_and_missing_state_reconstruction(caller: Caller) -> None:
- w = caller
- args = ('project', 'register', '--project-id', 'registered-project', '--project-kind', 'work',
- '--knowledge-root', str(w.path / 'knowledge'), '--objective', 'Preserve project intent',
- '--acceptance', 'Independent state readback', '--non-goal', 'Remote promotion',
- '--next-effect', 'Inspect the source', '--stop-condition', 'Readback matches')
- created = w.call(*args, goal='registered')
- assert created['ok'] is True, created
- state = Path(created['state_file'])
- assert 'Preserve project intent' in state.read_text()
- assert 'Independent state readback' in state.read_text()
- assert w.call(*args, goal='registered')['ok'] is True
- if w.mode == 'enabled':
- registry = json.loads(w.registry.read_bytes())
- goal = next(g for g in registry['goals'] if g['id'] == 'registered')
- goal.setdefault('coordination', {})['runtime_shadow'] = {
- 'schema_version': 'loopx_coordination_runtime_shadow_config_v0', 'enabled': True, 'provider': 'file_v0'}
- w.registry.write_text(json.dumps(registry))
- assert w.call('coordination-shadow', 'bootstrap', '--execute', goal='registered')['bootstrap']['status'] == 'applied'
- state.unlink()
- before = w.registry.read_bytes()
- rebuilt = w.call(*args, goal='registered')
- if w.mode == 'enabled':
- assert rebuilt['ok'] is False, rebuilt
- assert not state.exists() and w.registry.read_bytes() == before
- else:
- assert rebuilt['ok'] is True, rebuilt
- assert 'Preserve project intent' in state.read_text()
-
-
-def test_migration_target_and_preview_ownership(caller: Caller) -> None:
- w = caller
- legacy = w.path / 'legacy'
- legacy.mkdir()
- source = legacy / 'STATE.md'
- source.write_text('---\ngoal_id: legacy\nhandoff_mode: soft_claim\n---\n\n## Agent Todo\n\n- [ ] Migrated source.\n')
- source_registry = legacy / 'registry.json'
- source_registry.write_text(json.dumps({'goals': [{'id': 'legacy', 'repo': str(legacy), 'state_file': 'STATE.md'}]}))
- args = ('migrate-state', '--legacy-registry', str(source_registry),
- '--legacy-runtime-root', str(legacy / 'runtime'), '--target-runtime-root', str(w.root),
- '--goal-id-map', 'legacy=observable', '--path-map', f'{legacy}={w.path}',
- '--copy-active-state', '--no-global-sync')
- before = w.registry.read_bytes(), w.state.read_bytes()
- preview = w.call(*args, goal='legacy')
- assert preview['ok'] is True, preview
- assert (w.registry.read_bytes(), w.state.read_bytes()) == before
- result = w.call(*args, '--execute', goal='legacy')
- if w.mode == 'enabled':
- assert result['ok'] is False, result
- assert (w.registry.read_bytes(), w.state.read_bytes()) == before
- else:
- assert result['ok'] is True, result
- assert 'goal_id: observable' in w.state.read_text() and 'Migrated source.' in w.state.read_text()
- assert json.loads(w.registry.read_bytes())['goals'][0]['id'] == 'observable'
-
-
-def test_operator_reads_and_invalid_selector_have_no_primary_effect(caller: Caller) -> None:
- w = caller
- todo = w.add('Read policy fixture')
- before = w.primary()
- for command in ['inspect', 'qualify', 'read-candidate']:
- args = ('--todo-id', todo) if command == 'read-candidate' else ()
- result = w.call('coordination-shadow', command, *args)
- assert result['ok'] is (w.mode == 'enabled' and command == 'inspect'), result
- assert w.primary() == before
- invalid = w.call('coordination-shadow', 'rollback', '--provider-revision', '', '--execute')
- assert invalid['ok'] is False and w.primary() == before, invalid
- # Availability alone never constructs a candidate/outbox in the disabled goal.
- if w.mode != 'enabled':
- assert not (w.root / 'authority-shadow').exists()
-
-
-def test_monitor_successor_retains_caller_routing(caller: Caller) -> None:
- w = caller
- todo = w.add('Observe the public release', '--task-class', 'continuous_monitor', '--action-kind', 'monitor',
- '--claimed-by', 'agent-a', '--target-key', 'release:bounded', '--cadence', '30m',
- '--next-due-at', '2000-01-01T00:00:00+00:00', '--watch-only')
- result = w.call('quota', 'monitor-poll', '--agent-id', 'agent-a', '--runtime-profile', 'generic_cli',
- '--available-capability', 'network', '--todo-id', todo, '--target-key', 'release:bounded',
- '--result-hash', 'release-v1', '--material-change', '--next-agent-todo', 'Validate released head',
- '--next-action-kind', 'validate_release_head', '--next-task-repository', 'git:github.com/huangruiteng/loopx',
- '--next-required-capability', 'network', '--next-continuation-policy', 'same_agent_non_delivery',
- '--next-claimed-by', 'agent-a', '--execute')
- assert result['ok'] is True and len(result['successor_todo_ids']) == 1, result
- successor = w.read(result['successor_todo_ids'][0])
- assert successor['text'] == '[P1] Validate released head' and successor['claimed_by'] == 'agent-a'
- assert successor['priority'] == 'P1'
- assert successor['action_kind'] == 'validate_release_head' and successor['required_capabilities'] == ['network']
-
-
-def test_retired_observation_request_preserves_runtime_capture(caller: Caller) -> None:
- w = caller
- before = w.primary()
- for extra in ((), ('--execute',)):
- rejected = w.call('configure-goal', '--local-authority-shadow-file', *extra)
- assert rejected['ok'] is False and 'local_authority_shadow_retired' in rejected['error'], rejected
- assert w.primary() == before
- assert not (w.root / 'authority-shadow' / 'file' / 'observable').exists()
- todo = w.add('Independent observation contract')
- created = w.read(todo)
- assert (created['text'], created['priority']) == ('[P1] Independent observation contract', 'P1')
-
-
-def test_turn_input_rejection_has_no_host_or_primary_effect(caller: Caller) -> None:
- w = caller
- before = w.primary()
- result = w.call('turn', 'run-once', '--project', str(w.path), '--agent-id', 'unknown', '--no-global-sync')
- assert result['ok'] is False, result
- assert result['effects']['host_invoked'] is False and result['effects']['state_written'] is False
- assert w.primary() == before
-
-
-def test_registry_relative_root_does_not_depend_on_callers_cwd(caller: Caller) -> None:
- w = caller
- registry = json.loads(w.registry.read_bytes())
- registry['common_runtime_root'] = 'runtime'
- w.registry.write_text(json.dumps(registry))
- cwd = w.path / 'unrelated-cwd'
- cwd.mkdir()
- command = [sys.executable, '-m', 'loopx.cli', '--registry', str(w.registry), '--format', 'json',
- 'todo', 'add', '--goal-id', 'observable', '--role', 'agent', '--text', 'Registry relative root']
- added = w.invoke(command, command[3:], cwd=cwd)
- assert added['ok'] is True, added
- created = w.read(added['todo_id'])
- assert (created['text'], created['priority']) == ('[P1] Registry relative root', 'P1')
- assert (w.root / 'goals' / 'observable' / 'rollout-event-log.jsonl').exists()
- assert not (cwd / 'runtime').exists()
- assert w.call('handoff-mode', 'set', '--mode', 'hard_lease')['ok'] is True
- command = [*command[:7], 'task-lease', 'acquire', '--goal-id', 'observable',
- '--todo-id', added['todo_id'], '--owner', 'agent-a', '--idempotency-key', 'relative-lease']
- lease = w.invoke(command, command[3:], cwd=cwd)
- assert lease['ok'] is True, lease
- assert (w.root / 'goals' / 'observable' / 'task-leases' / f"{added['todo_id']}.json").exists()
- assert not (cwd / 'runtime').exists()
-
-
-def test_legacy_holder_verify_and_terminal_release(caller: Caller) -> None:
- w = caller
- w.call('handoff-mode', 'set', '--mode', 'hard_lease')
- todo = w.add('Holder and terminal write boundaries')
- before = w.primary()
- rejected_claim = w.call('todo', 'claim', '--todo-id', todo, '--claimed-by', 'agent-a', '--agent-id', 'agent-a',
- '--task-lease-idempotency-key', 'caller-holder', '--task-lease-expected-version', '0')
- assert rejected_claim['error'] == '--task-lease-idempotency-key on todo claim requires promoted canonical authority; no legacy write attempted'
- assert w.primary() == before
- assert w.call('task-lease', 'acquire', '--todo-id', todo, '--owner', 'agent-a', '--idempotency-key', 'caller-holder')['ok'] is True
- claim = w.call('todo', 'claim', '--todo-id', todo, '--claimed-by', 'agent-a', '--agent-id', 'agent-a')
- assert claim['ok'] is True, claim
- path = w.root / 'goals' / 'observable' / 'task-leases' / f'{todo}.json'
- lease = json.loads(path.read_bytes())
- assert lease['owner'] == 'agent-a' and lease['status'] == 'active'
- before = w.primary()
- rejected = w.call('todo', 'complete', '--todo-id', todo, '--agent-id', 'agent-a',
- '--evidence', 'validation://terminal', '--no-follow-up')
- assert rejected['error_code'] == 'lease_fence_required', rejected
- after = w.primary()
- assert {key: after[key] for key in before} == before
- # The existing verify protocol retains an acquired intent, never a lease or success receipt.
- added = set(after) - set(before)
- assert len(added) == 1
- intent = json.loads(bytes.fromhex(after[added.pop()]))
- assert intent['schema_version'] == 'task_lease_fence_receipt_v0' and intent['state'] == 'acquired'
- assert intent['lease'] is None and intent['response'] is None and intent['verify_response'] is None
- result = w.call('todo', 'complete', '--todo-id', todo, '--agent-id', 'agent-a',
- '--task-lease-idempotency-key', 'caller-holder', '--task-lease-expected-version', str(lease['version']),
- '--evidence', 'validation://terminal', '--no-follow-up')
- assert result['ok'] is True and result['task_lease_fence']['released'] is True, result
- assert json.loads(path.read_bytes())['status'] == 'released'
- assert w.read(todo)['done'] is True
diff --git a/tests/control_plane/test_shadow_observable_native_e2e.py b/tests/control_plane/test_shadow_observable_native_e2e.py
deleted file mode 100644
index fc927e75a9..0000000000
--- a/tests/control_plane/test_shadow_observable_native_e2e.py
+++ /dev/null
@@ -1,169 +0,0 @@
-"""Independent native and HTTP consumers of the shared changed writer boundary."""
-from __future__ import annotations
-
-import json
-import sys
-
-import pytest
-
-from test_shadow_observable_e2e import Caller, SOURCE, caller as caller
-
-pytestmark = pytest.mark.stage2c_e2e
-
-NATIVE = r"""
-import {join} from 'node:path';
-const input = JSON.parse(process.argv[1]);
-const {FileAuthorityStore} = await import(input.base + '/file_authority_store.ts');
-const store = new FileAuthorityStore(join(input.root, 'authority', 'file-v0'), 'observable');
-let result;
-if (input.action === 'seed') {
- result = await store.commitAuthority({expected_provider_revision:null, operation_id:'caller-fixture',
- events:[], next_projection:input.request, receipts:[]});
- const {engageLegacyCoordinationWriterFence} = await import(input.base + '/legacy_writer_fence.ts');
- const {canonicalAuthoritySha256} = await import(input.base + '/authority_store_codec.ts');
- const fence = await engageLegacyCoordinationWriterFence({schema_version:'loopx_legacy_coordination_writer_fence_engage_request_v0',
- runtime_root:input.root,goal_id:'observable',state_path:input.state,
- fence:{schema_version:'loopx_legacy_coordination_writer_fence_v0',state:'engaged',goal_id:'observable',
- fence_id:'caller-fixture',source_version:'caller-fixture',source_projection_sha256:canonicalAuthoritySha256(input.request),
- expected_shadow_provider_revision:result.provider_revision}});
- if(fence.status !== 'applied') throw new Error(JSON.stringify(fence));
-} else if (input.action === 'read') {
- result = {head:await store.loadAuthority(), history:await store.scanCommitted(null, 100)};
-} else {
- const owner = await import(input.base + '/local_authority_runtime.ts');
- result = await owner[input.action](input.request);
-}
-process.stdout.write(JSON.stringify(result) + '\n');
-"""
-
-
-def native(w: Caller, action: str, request: dict) -> dict:
- value = {'base': (SOURCE / 'loopx/control_plane/coordination').as_uri(),
- 'root': str(w.root), 'state': str(w.state), 'action': action, 'request': request}
- return w.invoke(['node', '--no-warnings', '--experimental-strip-types', '--input-type=module',
- '-e', NATIVE, json.dumps(value)], ['native', action, json.dumps(request)])
-
-
-def test_canonical_argument_intent_and_atomic_claim(caller: Caller) -> None:
- w = caller
- todo = w.add('Native canonical input', '--note', 'Preserve operator note', '--required-write-scope', 'src/**')
- record = w.read(todo)
- builder = "from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection as build; import json,sys; value=build(goal_id='observable', todos=[json.loads(sys.argv[1])]); value['handoff_mode']='hard_lease'; print(json.dumps(value))"
- projection = w.invoke([sys.executable, '-c', builder, json.dumps(record)], ['fixture-projection', json.dumps(record)])
- assert native(w, 'seed', projection)['status'] == 'applied'
- w.state.unlink()
- before = native(w, 'read', {})
- claim = ('todo', 'claim', '--todo-id', todo, '--claimed-by', 'agent-a', '--agent-id', 'agent-a',
- '--claim-operation-id', 'caller-atomic-claim', '--task-lease-idempotency-key', 'caller-ownership',
- '--task-lease-expected-version', '0')
- assert w.call(*claim, '--dry-run')['status'] == 'planned'
- assert native(w, 'read', {}) == before
- applied = w.call(*claim)
- assert applied['status'] == 'applied', applied
- assert applied['projection_delivery'] == 'delivered', applied
- recovered = w.state.read_bytes()
- assert b'claimed_by=agent-a' in recovered
- replay = w.call(*claim)
- assert replay['status'] == 'replayed' and replay['original_receipt'] == applied['original_receipt']
- stored = native(w, 'read', {})['head']['head']
- assert stored['todos'][0]['claimed_by'] == 'agent-a'
- assert stored['leases'][0]['owner'] == 'agent-a' and stored['leases'][0]['write_scopes'] == ['src/**']
- before = native(w, 'read', {})
- rejected = w.call('todo', 'update', '--todo-id', todo, '--agent-id', 'agent-b', '--note', 'Foreign owner edit')
- assert rejected.get('error_code') == 'update_owner_mismatch', rejected
- assert rejected['error'] == rejected['reason'] == "Todo update cannot edit another claim owner's work"
- assert native(w, 'read', {}) == before
- assert w.state.read_bytes() == recovered
-
-
-def test_native_unclaimed_edit_and_explicit_note_clear(caller: Caller) -> None:
- w = caller
- todo = w.add('Unclaimed correction', '--note', 'Keep until explicitly cleared')
- record = w.read(todo)
- builder = "from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection as build; import json,sys; value=build(goal_id='observable', todos=[json.loads(sys.argv[1])]); value['handoff_mode']='soft_claim'; print(json.dumps(value))"
- projection = w.invoke([sys.executable, '-c', builder, json.dumps(record)], ['fixture-projection', json.dumps(record)])
- assert native(w, 'seed', projection)['status'] == 'applied'
- w.state.unlink()
- updated = w.call('todo', 'update', '--todo-id', todo, '--agent-id', 'agent-b', '--note', 'Claim-neutral context')
- assert updated['ok'] is True and updated['projection_delivery'] == 'delivered', updated
- recovered = w.state.read_bytes()
- assert todo.encode() in recovered
- stored = native(w, 'read', {})['head']['head']
- assert stored['todos'][0]['note'] == 'Claim-neutral context'
- assert not stored['todos'][0].get('claimed_by') and stored['leases'] == []
- request = {'schema_version': 'loopx_local_coordination_todo_update_request_v0',
- 'runtime_root': str(w.root), 'goal_id': 'observable', 'todo_id': todo, 'role': 'agent',
- 'actor_agent_id': 'agent-b', 'registered_agents': ['agent-a', 'agent-b'],
- 'operation_id': 'caller-native-clear', 'patch': {}, 'clear_fields': ['note'],
- 'dry_run': False, 'observed_at': '2026-09-07T00:00:00Z'}
- cleared = native(w, 'updateLocalCoordinationTodo', request)
- assert cleared['status'] == 'applied', cleared
- after = native(w, 'read', {})
- assert 'note' not in after['head']['head']['todos'][0]
- assert after['head']['head']['leases'] == stored['leases']
- rejected = native(w, 'updateLocalCoordinationTodo', {**request, 'operation_id': 'rejected',
- 'todo_id': 'todo_missing', 'actor_agent_id': 'unknown', 'patch': {'text': 'Must not persist'}, 'clear_fields': []})
- assert rejected['status'] in {'failed', 'rejected'}, rejected
- assert native(w, 'read', {}) == after
- # Native transactions do not own display delivery; only the CLI facade
- # regenerated it. Neither direct-native update nor rejection rewrites it.
- assert w.state.read_bytes() == recovered
-
-
-HTTP = r"""
-import http.client, json, pathlib, sys, threading
-from loopx.status_server import StatusHTTPServer, StatusRequestHandler
-args=json.loads(sys.argv[1]); server=StatusHTTPServer(('127.0.0.1',0),StatusRequestHandler)
-server.verbose=False; server.registry_path=pathlib.Path(args['registry']); server.runtime_root_override=args['root']
-server.reward_write_enabled=True; server.reward_dry_run_path='/reward/dry-run'; server.reward_append_path='/reward/append'
-thread=threading.Thread(target=server.serve_forever,daemon=True);thread.start()
-rows=[]
-def post(path,body):
- conn=http.client.HTTPConnection('127.0.0.1',server.server_address[1],timeout=10)
- conn.request('POST',path,json.dumps(body),{'Content-Type':'application/json','Origin':'http://localhost'})
- response=conn.getresponse(); payload=json.loads(response.read()); conn.close()
- rows.append({'request':body,'status':response.status,'body':payload}); return payload
-try:
- body={'goal_id':'observable','run_generated_at':'2026-09-01T00:00:00Z',
- 'recorded_at':'2026-09-01T01:00:00Z','decision':'continue','reward':'positive',
- 'reason_summary':'HTTP argument readback','write_active_state_summary':True}
- post('/reward/append',body)
- preview=post('/reward/dry-run',{k:v for k,v in body.items() if k!='write_active_state_summary'})
- if 'preview_id' in preview:
- post('/reward/append',{**body,'preview_id':preview['preview_id']})
-finally:
- server.shutdown(); server.server_close(); thread.join(timeout=5)
-print(json.dumps({'rows':rows}))
-"""
-
-
-def test_http_reward_preserves_preview_gate_and_persisted_arguments(caller: Caller) -> None:
- w = caller
- todo = w.add('HTTP must preserve Todo ownership', '--claimed-by', 'agent-a')
- record = w.read(todo)
- index = w.root / 'goals' / 'observable' / 'runs' / 'index.jsonl'
- index.parent.mkdir(parents=True, exist_ok=True)
- index.write_text(json.dumps({'generated_at': '2026-09-01T00:00:00Z', 'json_path': 'run.json',
- 'markdown_path': 'run.md', 'classification': 'continue'}) + '\n')
- result = w.invoke([sys.executable, '-c', HTTP, json.dumps({'registry': str(w.registry), 'root': str(w.root)})],
- ['HTTP', 'reward preview then append'])
- first, preview, appended = result['rows']
- assert first['status'] == 400 and first['body']['error'] == 'preview_id is required'
- assert preview['status'] == 200 and preview['body']['dry_run'] is True
- assert appended['status'] == 200 and appended['body']['appended'] is True
- assert len(index.read_text().splitlines()) == 2
- assert 'HTTP argument readback' in index.read_text() and 'HTTP argument readback' in w.state.read_text()
- assert w.read(todo) == record
-
-
-
-
-def test_retired_event_source_refuses_without_primary_write(caller: Caller) -> None:
- w = caller
- log = w.path / "events.jsonl"
- log.write_text('{"event_type":"todo_added","refs":{"todo_id":"todo_event_fixture"}}\n')
- before = w.state.read_bytes(), log.read_bytes()
- result = w.call('todo', 'complete', '--todo-id', 'todo_event_fixture', '--agent-id', 'agent-a',
- '--evidence', 'validation://retired-source', '--no-follow-up')
- assert result['ok'] is False, result
- assert (w.state.read_bytes(), log.read_bytes()) == before
diff --git a/tests/control_plane/test_shadow_observation_retirement.py b/tests/control_plane/test_shadow_observation_retirement.py
index 1b748b0e39..fa1835a7ca 100644
--- a/tests/control_plane/test_shadow_observation_retirement.py
+++ b/tests/control_plane/test_shadow_observation_retirement.py
@@ -3,35 +3,129 @@
import json
from pathlib import Path
+import shutil
import subprocess
import pytest
+from loopx.control_plane.coordination.authority_source_capture import (
+ build_runtime_shadow_source_snapshot,
+)
from shadow_e2e_fixture import workspace
from test_canonical_todo_writer_isolation import (
isolated_todo_distribution, retirement_wheel, # noqa: F401,F811
)
+REPO = Path(__file__).resolve().parents[2]
+
def _bytes(root: Path):
return {str(path.relative_to(root)): path.read_bytes()
for path in root.rglob("*") if path.is_file() and path.suffix != ".lock"}
-@pytest.mark.parametrize("producers_present", [True, False], ids=["present", "absent"])
+def _seed_historical_shadow_transactions(
+ runtime: Path,
+ state: Path,
+ partitions: list[dict[str, object]],
+) -> None:
+ node = shutil.which("node")
+ assert node is not None
+ script = """
+import {pathToFileURL} from "node:url";
+import {join} from "node:path";
+import {readFileSync} from "node:fs";
+const [storePath, shadowPath, fixturePath, root, statePath] = process.argv.slice(1);
+const {FileAuthorityStore} = await import(pathToFileURL(storePath).href);
+const {commitLocalAuthorityShadowEntry} = await import(pathToFileURL(shadowPath).href);
+const {pendingEntry, settleFiles} = await import(pathToFileURL(fixturePath).href);
+const store = new FileAuthorityStore(join(root, "authority-shadow", "file-v0"), "goal-a",
+ {existingOnly: true});
+const loaded = await store.loadAuthority();
+if (loaded.status !== "loaded") throw new Error(`shadow bootstrap missing: ${JSON.stringify(loaded)}`);
+const fixture = {root, statePath, store, baseline: loaded.head};
+const partitions = JSON.parse(readFileSync(0, "utf8"));
+for (const [index, partition] of partitions.entries()) {
+ const seq = index + 1;
+ const request = await pendingEntry(fixture, seq, partition);
+ const result = await commitLocalAuthorityShadowEntry(request);
+ if (result.outcome !== "delivered") {
+ throw new Error(`historical shadow seed failed: ${JSON.stringify(result)}`);
+ }
+ await settleFiles(fixture, request, result);
+}
+"""
+ result = subprocess.run(
+ [
+ node,
+ "--no-warnings",
+ "--experimental-strip-types",
+ "--input-type=module",
+ "-e",
+ script,
+ str(REPO / "loopx/control_plane/coordination/file_authority_store.ts"),
+ str(REPO / "loopx/control_plane/coordination/local_authority_shadow.ts"),
+ str(REPO / "tests/control_plane_ts/shadow_file_fixture.ts"),
+ str(runtime),
+ str(state),
+ ],
+ cwd=REPO,
+ input=json.dumps(partitions),
+ capture_output=True,
+ text=True,
+ timeout=45,
+ )
+ assert result.returncode == 0, result.stdout + result.stderr
+
+
+@pytest.mark.parametrize("bridges_present", [True, False], ids=["present", "absent"])
def test_complete_shadow_observation_without_producers(
- tmp_path, isolated_todo_distribution, producers_present, # noqa: F811 -- imported pytest fixture
+ tmp_path, isolated_todo_distribution, bridges_present, # noqa: F811 -- imported pytest fixture
):
package, command = isolated_todo_distribution
w = workspace(tmp_path / "shadow", bootstrap=False)
+ registry = json.loads(w.registry.read_text())
+ registry["goals"][0]["id"] = "goal-a"
+ w.registry.write_text(json.dumps(registry))
+ w.goal = "goal-a"
+ todo_id = "todo_observed"
+ header = (
+ "---\ngoal_id: goal-a\nhandoff_mode: hard_lease\n"
+ "updated_at: 2026-09-01T00:00:00+00:00\n---\n\n## Agent Todo\n\n"
+ )
+ records = [
+ ("Preserve the original source work", todo_id),
+ ("Second source mutation", "todo_second"),
+ ("Third source mutation", "todo_third"),
+ ]
+ w.state.write_text(header, encoding="utf-8")
w.command, w.cwd, w.package = tuple(command), tmp_path, package
assert w.cli("coordination-shadow", "bootstrap", "--execute")["bootstrap"]["status"] == "applied"
- todo_id = w.add("Preserve the original source work")["todo_id"]
- w.add("Second source mutation")
- w.add("Third source mutation")
- if not producers_present:
- for name in ("runtime_shadow", "runtime_shadow_writer_adapter",
- "local_authority_shadow_adapter", "local_authority_shadow_outbox"):
+ partitions = []
+ body = header
+ for text, record_id in records:
+ body += (
+ f"- [ ] {text}\n"
+ f" \n"
+ )
+ w.state.write_text(body, encoding="utf-8")
+ projection, _snapshot = build_runtime_shadow_source_snapshot(
+ goal=registry["goals"][0],
+ runtime_root=w.runtime,
+ state_path=w.state,
+ registry_path=w.registry,
+ )
+ partitions.append({
+ "handoff_mode": projection["handoff_mode"],
+ "todos": projection["todos"],
+ })
+ source = w.state.read_bytes()
+ _seed_historical_shadow_transactions(w.runtime, w.state, partitions)
+ w.state.write_bytes(source)
+ if not bridges_present:
+ for name in ("runtime_shadow", "local_authority_shadow_adapter",
+ "local_authority_shadow_outbox"):
(package / f"control_plane/coordination/{name}.py").unlink()
check = subprocess.run([command[0], "-S", "-c",
"import importlib.util; assert importlib.util.find_spec("
diff --git a/tests/control_plane/test_shadow_readback_writer_isolation.py b/tests/control_plane/test_shadow_readback_writer_isolation.py
deleted file mode 100644
index d1b452b819..0000000000
--- a/tests/control_plane/test_shadow_readback_writer_isolation.py
+++ /dev/null
@@ -1,153 +0,0 @@
-"""Retained shadow diagnostics must survive source-producer retirement."""
-from __future__ import annotations
-
-import json
-import subprocess
-
-import pytest
-
-from canonical_authority_fixture import initialize_canonical_authority
-from test_canonical_todo_writer_isolation import (
- isolated_todo_distribution, retirement_wheel, # noqa: F401 -- distribution fixtures
-)
-from test_local_authority_shadow_outbox import (
- GOAL_ID, _capture, _fixture, _planned_todo, _record_change,
-)
-from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
-from loopx.todos import list_goal_todos
-
-
-def _remove_capture_producers(package):
- for name in ("runtime_shadow", "runtime_shadow_writer_adapter",
- "local_authority_shadow_adapter", "local_authority_shadow_outbox"):
- (package / "control_plane/coordination" / f"{name}.py").unlink()
- (package / "control_plane/todos/legacy_mutation.py").unlink()
-
-
-@pytest.mark.parametrize("configuration", [None, {"enabled": False}, {"enabled": True}])
-def test_empty_status_without_capture_producers_preserves_default_off(
- tmp_path, configuration, isolated_todo_distribution, # noqa: F811
-):
- package, command = isolated_todo_distribution
- registry, state, runtime = _fixture(tmp_path, bootstrap=False)
- if configuration is not None:
- config = json.loads(registry.read_text())
- # Retired legacy observation configuration has no runtime schema.
- config["goals"][0]["coordination"]["runtime_shadow"] = configuration
- registry.write_text(json.dumps(config))
- original = registry.read_bytes(), state.read_bytes()
-
- def status():
- process = subprocess.run(
- [*command, "--registry", str(registry), "--runtime-root", str(runtime),
- "--format", "json", "authority-shadow", "status", "--goal-id", GOAL_ID],
- cwd=tmp_path, capture_output=True, text=True, timeout=60,
- )
- assert process.returncode == 0, process.stdout + process.stderr
- result = json.loads(process.stdout)
- assert result["runtime_config"]["enabled"] is False
- assert result["candidate"]["status"] == "missing"
- assert result["management"]["status"] == "missing"
- assert not runtime.exists()
- assert (registry.read_bytes(), state.read_bytes()) == original
- return result
-
- expected = status()
- _remove_capture_producers(package)
- assert status() == expected
-
-
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_retained_status_is_read_only_with_capture_producers_absent(
- tmp_path, provider, isolated_todo_distribution, # noqa: F811
-):
- package, command = isolated_todo_distribution
- registry, state, runtime = _fixture(tmp_path)
- committed = _record_change(registry, state, runtime, "Retained committed work")
- original = state.read_text()
- _, planned = _planned_todo(original, "Prepared work never written")
- prepared = _capture(registry, state, runtime, original_text=original)
- prepared.prepare(planned)
- assert prepared.outcome.failure is None
- todos = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)["todos"]
- initialize_canonical_authority(
- runtime, GOAL_ID,
- build_todo_runtime_shadow_projection(goal_id=GOAL_ID, todos=todos,
- handoff_mode="hard_lease"),
- state_path=state, provider=provider,
- )
- directory = runtime / "authority-shadow/outbox" / GOAL_ID / "todos"
- candidate = next((runtime / "authority-shadow/file-v0").glob("authority-store-*.json"))
- original_candidate = candidate.read_bytes()
-
- def files():
- return {str(p.relative_to(tmp_path)): p.read_bytes()
- for root in (runtime, state.parent) for p in root.rglob("*") if p.is_file()}
-
- def status():
- before = files()
- process = subprocess.run(
- [*command, "--registry", str(registry), "--runtime-root", str(runtime),
- "--format", "json", "authority-shadow", "status", "--goal-id", GOAL_ID],
- cwd=tmp_path, capture_output=True, text=True, timeout=60,
- )
- assert "Traceback" not in process.stderr, process.stderr
- payload = json.loads(process.stdout)
- assert files() == before, "a status read must not drain, repair or initialize anything"
- assert process.returncode == (0 if payload["ok"] else 1), payload
- return payload
-
- # Each oracle has independent invariant checks as well as complete
- # base/head parity; original bytes and identities are never rewritten.
- def scenarios():
- answers = {}
- answers["pending"] = status()
- assert answers["pending"]["ok"]
- assert answers["pending"]["outbox"]["todos"]["committed_pending"] == 1
- assert answers["pending"]["outbox"]["todos"]["prepared_only"] == 1
- assert answers["pending"]["candidate"]["codec_agreement"] is True
- assert answers["pending"]["management"]["status"] == "active"
-
- cursor = directory / "drain-cursor.json"
- cursor.write_text(json.dumps({
- "schema_version": "loopx_local_authority_shadow_drain_cursor_v0",
- "partition": "todos", "last_seq": committed.outcome.seq,
- "last_entry_id": committed.outcome.entry_id,
- "last_partition_digest": committed.outcome.partition_digest,
- "last_cursor": "retained:1", "last_provider_revision": "retained:1",
- "updated_at": "2026-09-03T00:00:00Z",
- }))
- answers["residue"] = status()
- assert answers["residue"]["ok"]
- assert answers["residue"]["outbox"]["todos"]["retired_residue"] == 2
- assert answers["residue"]["outbox"]["todos"]["committed_pending"] == 1
- cursor.write_text('{"invalid_original":true}')
- answers["bad_cursor"] = status()
- assert not answers["bad_cursor"]["ok"]
- assert answers["bad_cursor"]["outbox"]["todos"]["invalid"] == "outbox_file_invalid"
- cursor.unlink()
-
- extra = directory / "unclassified.json"
- extra.write_text('{"retain_original":true}')
- answers["bad_entry"] = status()
- assert not answers["bad_entry"]["ok"]
- assert answers["bad_entry"]["outbox"]["todos"]["invalid"] == "outbox_file_invalid"
- extra.unlink()
-
- candidate.write_text('{"damaged_candidate":true}')
- answers["bad_candidate"] = status()
- # The typed store reader returns a failed result for invalid bytes;
- # only transport exceptions become the adapter's unavailable status.
- assert answers["bad_candidate"]["candidate"]["status"] == "failed"
- assert answers["bad_candidate"]["candidate"]["reason_code"] is not None
- candidate.unlink()
- answers["missing_candidate"] = status()
- assert answers["missing_candidate"]["candidate"]["status"] == "missing"
- assert not candidate.exists(), "status must not mint a replacement store identity"
- candidate.write_bytes(original_candidate)
- return answers
-
- expected = scenarios()
- _remove_capture_producers(package)
- assert scenarios() == expected
- assert scenarios() == expected, "cold repeated CLI reads preserve the original history"
diff --git a/tests/control_plane/test_shadow_registry_source.py b/tests/control_plane/test_shadow_registry_source.py
index 9c4767aed5..d5c8a5a0ea 100644
--- a/tests/control_plane/test_shadow_registry_source.py
+++ b/tests/control_plane/test_shadow_registry_source.py
@@ -4,8 +4,6 @@
import hashlib
import json
-import os
-import subprocess
from pathlib import Path
import pytest
@@ -25,7 +23,10 @@
from loopx.control_plane.coordination.local_authority_shadow_projection import (
ProjectionValueError,
)
-from tests.control_plane.shadow_e2e_fixture import REPO, workspace as cli_workspace
+from tests.control_plane.shadow_e2e_fixture import (
+ ShadowWorkspace,
+ workspace as cli_workspace,
+)
def workspace(root: Path):
@@ -103,6 +104,45 @@ def test_real_cli_rejects_unsafe_lease_source_before_bootstrap(tmp_path: Path, k
assert list(outside.iterdir()) == [retained]
+def test_source_snapshot_preserves_inventory_without_projecting_orphan_leases(
+ tmp_path: Path,
+):
+ state, runtime, registry, goal, _ = workspace(tmp_path)
+ directory = runtime / "goals" / goal["id"] / "task-leases"
+ directory.mkdir(parents=True)
+ todo_ids = ("todo_alpha", "todo_Zulu", "todo_Bravo")
+ for todo_id in todo_ids:
+ (directory / f"{todo_id}.json").write_text(
+ json.dumps(
+ {
+ "schema_version": "task_lease_v0",
+ "goal_id": goal["id"],
+ "todo_id": todo_id,
+ "owner": "agent-a",
+ "status": "released",
+ "version": 1,
+ "write_scopes": ["src/"],
+ "idempotency_key": f"baseline-{todo_id}",
+ }
+ ),
+ encoding="utf-8",
+ )
+
+ projection, snapshot = capture(state, runtime, registry, goal)
+
+ assert [entry["name"] for entry in snapshot["lease_inventory"]] == [
+ "todo_Bravo.json",
+ "todo_Zulu.json",
+ "todo_alpha.json",
+ ]
+ assert projection["leases"] == []
+ assert bootstrap(runtime, goal, projection, snapshot)["status"] == "applied"
+ view = read_local_authority_shadow(
+ runtime_root=runtime, goal_id=goal["id"], scan_limit=1
+ )
+ assert view["proof"]["transactions"][0]["projection"]["leases"] == []
+
+
@pytest.mark.parametrize("extension", [
{}, {"future_extension": None}, {"future_extension": False},
{"future_extension": {"missing_value": None, "flag": False}},
@@ -164,53 +204,6 @@ def test_terminal_lease_source_rejects_drift_before_publication(tmp_path: Path,
assert lease.read_bytes() == changed_bytes
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_terminal_lease_survives_public_cutover_and_original_recovery(tmp_path: Path, provider):
- ws = cli_workspace(tmp_path, bootstrap=False)
- todo_id = "todo_0123456789ab"
- ws.state.write_text(ws.state.read_text() +
- f'- [x] Retained work\n \n')
- expected = {
- "schema_version": "task_lease_v0", "goal_id": ws.goal, "todo_id": todo_id,
- "owner": "agent-a", "version": 2, "lease_epoch": 1, "status": "released",
- "released_at": "2026-09-01T00:00:00Z", "idempotency_key": "retained-identity",
- "future_extension": {"missing_value": None, "flag": False},
- }
- lease = ws.runtime / "goals" / ws.goal / "task-leases" / f"{todo_id}.json"
- lease.parent.mkdir(parents=True)
- original = json.dumps(expected, indent=2).encode()
- lease.write_bytes(original)
- if provider == "sqlite":
- selection = subprocess.run([os.environ.get("LOOPX_CONTROL_PLANE_NODE", "node"),
- "--no-warnings", "--experimental-strip-types", "--experimental-sqlite",
- str((REPO / "loopx/control_plane/coordination/local_authority_provider.ts").resolve()), "--runtime-root",
- str(ws.runtime), "--goal-id", ws.goal, "--execute"], cwd=REPO,
- capture_output=True, text=True, timeout=45, check=True)
- assert json.loads(selection.stdout) == {
- "ok": True, "provider": "sqlite", "changed": True, "executed": True,
- }
- assert ws.cli("coordination-shadow", "bootstrap", "--execute")["bootstrap"]["status"] == "applied"
- for index in range(3):
- ws.add(f"Independent migration work {index}")
- assert ws.drain(budget_seconds="60")["ok"] is True
- preview = ws.cli("coordination-shadow", "promote")
- assert preview["promotion"]["status"] == "preview_ready", preview
- saved = tmp_path / "reviewed.json"
- saved.write_text(json.dumps(preview))
- applied = ws.cli("coordination-shadow", "promote", "--reviewed-plan", str(saved), "--execute")
- assert applied["promotion"]["canonical_authority"] == f"{provider}_v0"
- assert ws.cli("task-lease", "inspect", "--todo-id", todo_id)["lease"] == expected
- later = ws.add("Later canonical work must survive recovery")
- ws.state.unlink()
- recovered = ws.cli("coordination-shadow", "recover-promotion", "--reviewed-plan", str(saved), "--execute")
- assert recovered["promotion"]["status"] == "replayed", recovered
- assert ws.cli("task-lease", "inspect", "--todo-id", todo_id)["lease"] == expected
- retained = ws.cli("todo", "list", "--todo-id", later["todo_id"])
- assert (retained["todo"]["text"], retained["todo"]["priority"]) == ("[P1] Later canonical work must survive recovery", "P1")
- assert lease.read_bytes() == original
- assert not ws.state.exists()
-
-
@pytest.mark.parametrize(
"change",
["delete_goal", "move_state", "move_runtime", "remove_agent", "disable_capture"],
@@ -300,6 +293,60 @@ def test_strict_registry_envelope_keeps_existing_codec(tmp_path: Path):
assert bootstrap(runtime, goal, projection, snapshot)["status"] == "applied"
+def test_controller_rejects_an_override_runtime_root(tmp_path: Path):
+ ws = cli_workspace(tmp_path / "workspace", bootstrap=False)
+ original = ws.state.read_bytes()
+ override = ShadowWorkspace(ws.registry, tmp_path / "override-runtime", ws.state)
+
+ for registered_is_active in (False, True):
+ if registered_is_active:
+ result = ws.cli("coordination-shadow", "bootstrap", "--execute")
+ assert result["bootstrap"]["status"] == "applied", result
+
+ rejected = override.cli(
+ "coordination-shadow", "bootstrap", "--execute", success=False
+ )
+ assert rejected["bootstrap"]["status"] == "failed"
+ assert (
+ rejected["bootstrap"]["reason_code"]
+ == "shadow_source_runtime_root_mismatch"
+ )
+ assert not (override.runtime / "authority-shadow/file-v0").exists()
+ assert ws.state.read_bytes() == original
+
+
+def test_controller_rejects_an_alternate_state_file(tmp_path: Path):
+ ws = cli_workspace(tmp_path / "workspace", bootstrap=False)
+ alternate = ws.state.parent / "alternate-state.md"
+ alternate.write_bytes(ws.state.read_bytes())
+ original = ws.state.read_bytes()
+
+ for active in (False, True):
+ if active:
+ result = ws.cli("coordination-shadow", "bootstrap", "--execute")
+ assert result["bootstrap"]["status"] == "applied", result
+
+ rejected = ws.cli(
+ "coordination-shadow",
+ "bootstrap",
+ "--project",
+ str(ws.state.parent),
+ "--state-file",
+ str(alternate),
+ "--execute",
+ success=False,
+ )
+ assert rejected["bootstrap"]["status"] == "failed"
+ assert rejected["bootstrap"]["reason_code"] == (
+ "management_operation_identity_mismatch"
+ if active
+ else "shadow_source_state_path_mismatch"
+ )
+ assert ws.state.read_bytes() == alternate.read_bytes() == original
+ if not active:
+ assert not (ws.runtime / "authority-shadow/file-v0").exists()
+
+
@pytest.mark.parametrize("writer", ["sync", "activation", "deletion"])
def test_global_registry_writers_respect_native_promotion_marker(tmp_path, writer):
from loopx.file_lock import exclusive_mutation_file_lock, LockAcquireTimeoutError
diff --git a/tests/control_plane/test_shadow_writer_boundaries.py b/tests/control_plane/test_shadow_writer_boundaries.py
index 71be3c41b1..34503c6e8a 100644
--- a/tests/control_plane/test_shadow_writer_boundaries.py
+++ b/tests/control_plane/test_shadow_writer_boundaries.py
@@ -1,21 +1,17 @@
from __future__ import annotations
-import hashlib
import json
import subprocess
import sys
-import time
from pathlib import Path
import pytest
from loopx.control_plane.coordination.legacy_writer_fence import (
- LegacyCoordinationWriterFenced,
+ ActiveStateAuthorityMutationError,
legacy_coordination_writer_fence_path,
- legacy_todo_write_transaction,
+ require_prose_state_write_allowed,
)
-from loopx.control_plane.todos.handoff_mode import set_goal_handoff_mode
-from loopx.todos import add_goal_todo
GOAL = "writer-boundary"
@@ -42,6 +38,28 @@ def fixture(tmp_path: Path) -> tuple[Path, Path, Path]:
return registry, state, root
+def seed_todo(
+ state: Path,
+ text: str = "Retain canonical facts.",
+ *,
+ status: str = "open",
+ resume_when: str | None = None,
+) -> None:
+ source = state.read_text(encoding="utf-8")
+ resume = f" resume_when={resume_when}" if resume_when else ""
+ state.write_text(
+ source.replace(
+ "## Agent Todo\n",
+ "## Agent Todo\n\n"
+ f"- [ ] {text}\n"
+ f" \n",
+ 1,
+ ),
+ encoding="utf-8",
+ )
+
+
@pytest.mark.parametrize("registry_directory", [".loopx", "standalone"])
@pytest.mark.parametrize("override", [None, "", "override", Path("path-override")])
def test_runtime_route_keeps_project_relative_precedence_without_writes(
@@ -64,74 +82,6 @@ def test_runtime_route_keeps_project_relative_precedence_without_writes(
assert not (owner / "declared").exists()
-def test_handoff_writer_refuses_a_fence_before_primary(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
-) -> None:
- registry, state, root = fixture(tmp_path)
- fence = legacy_coordination_writer_fence_path(runtime_root=root, goal_id=GOAL)
- fence.parent.mkdir(parents=True)
- fence.write_text("{}", encoding="utf-8")
- monkeypatch.setattr(
- "loopx.control_plane.coordination.legacy_writer_fence.effect_runtime_result",
- lambda *_args, **_kwargs: {
- "status": "blocked", "reason_code": "legacy_coordination_writer_fenced",
- },
- )
- before = state.read_bytes()
- with pytest.raises(LegacyCoordinationWriterFenced):
- set_goal_handoff_mode(registry_path=registry, goal_id=GOAL, mode="soft_claim")
- assert state.read_bytes() == before
- assert not (root / "authority-shadow").exists()
-
-
-def test_handoff_mode_rechecks_legacy_fence_when_canonical_unavailable(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
-) -> None:
- """A provider outage must not bypass an already-present legacy fence."""
-
- registry, state, root = fixture(tmp_path)
- fence = legacy_coordination_writer_fence_path(runtime_root=root, goal_id=GOAL)
- fence.parent.mkdir(parents=True)
- fence.write_text("{}", encoding="utf-8")
- monkeypatch.setattr(
- "loopx.control_plane.todos.provider_handoff_mode.effect_runtime_result",
- lambda *_args, **_kwargs: {
- "status": "unavailable",
- "reason_code": "canonical_provider_unavailable",
- "reason": "canonical provider is unavailable",
- },
- )
- monkeypatch.setattr(
- "loopx.control_plane.coordination.legacy_writer_fence.effect_runtime_result",
- lambda *_args, **_kwargs: {
- "status": "blocked",
- "reason_code": "legacy_coordination_writer_fenced",
- },
- )
- before = state.read_bytes()
- with pytest.raises(LegacyCoordinationWriterFenced) as error:
- set_goal_handoff_mode(registry_path=registry, goal_id=GOAL, mode="soft_claim")
- assert error.value.code == "legacy_coordination_writer_fenced"
- assert error.value.payload["write_check"]["reason_code"] == "legacy_coordination_writer_fenced"
- assert state.read_bytes() == before
- assert not (root / "authority-shadow").exists()
-
-
-def test_corrupt_management_state_blocks_before_transaction_body(tmp_path: Path) -> None:
- registry, state, root = fixture(tmp_path)
- digest = hashlib.sha256(GOAL.encode()).hexdigest()[:16]
- management = root / "authority-transition" / "file-v0" / f"shadow-management-{digest}" / "state.json"
- management.parent.mkdir(parents=True)
- management.write_text("{corrupt", encoding="utf-8")
- reached = False
- try:
- with legacy_todo_write_transaction(registry, GOAL, state, None, "test", False):
- reached = True
- except RuntimeError as error:
- assert getattr(error, "reason_code", "")
- assert not reached, "maintenance errors must not fall through to a primary write"
-
-
def test_atomic_state_writer_keeps_original_on_failed_replace(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
) -> None:
@@ -167,7 +117,6 @@ def reward_fixture(tmp_path: Path) -> tuple[Path, Path, Path, dict[str, str]]:
def test_reward_summary_cannot_inject_a_canonical_todo(tmp_path: Path) -> None:
from loopx.feedback import append_human_reward
- from loopx.control_plane.coordination.runtime_shadow_writer_adapter import ActiveStateAuthorityMutationError
registry, state, index, reward = reward_fixture(tmp_path)
# Insert before the first recognized Todo source, rather than after it.
@@ -202,27 +151,6 @@ def plan_then_edit(**kwargs: object):
assert "Review accepted." in state.read_text()
-@pytest.mark.parametrize("preserve", [False, True])
-def test_force_bootstrap_cannot_erase_an_active_shadow_binding(tmp_path: Path, preserve: bool) -> None:
- from loopx.bootstrap import bootstrap_project
-
- registry, state, root = fixture(tmp_path)
- value = json.loads(registry.read_text())
- value["goals"][0]["coordination"]["runtime_shadow"] = {
- "enabled": True, "schema_version": "loopx_coordination_runtime_shadow_config_v0", "provider": "file_v0",
- }
- registry.write_text(json.dumps(value))
- before = registry.read_bytes(), state.read_bytes()
- with pytest.raises(RuntimeError, match="shadow"):
- bootstrap_project(project=tmp_path, registry_path=registry, runtime_root=root,
- goal_id=GOAL, objective="Rebuild safely.", domain="test", role="primary",
- parent_goal_id=None, state_file=state, goal_doc=None, adapter_kind="generic_project_goal_v0",
- adapter_status="connected", next_probe=None, spawn_allowed=False, max_children=0,
- allowed_domains=[], write_scope=[],
- force=True, preserve_todos=preserve, dry_run=False, sync_global=False)
- assert (registry.read_bytes(), state.read_bytes()) == before
-
-
def cli(registry: Path, *args: str) -> dict:
result = subprocess.run([sys.executable, "-m", "loopx.entrypoint", "--registry", str(registry), "--format", "json", *args],
cwd=REPO, text=True, capture_output=True, timeout=30)
@@ -230,307 +158,6 @@ def cli(registry: Path, *args: str) -> dict:
return json.loads(result.stdout)
-@pytest.mark.parametrize("configured", [None, False])
-@pytest.mark.parametrize("operation", ["add", "bootstrap"])
-def test_disabled_capture_skips_shadow_inputs_on_real_writers(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch, configured: bool | None, operation: str,
-) -> None:
- from loopx import bootstrap, rollout_event_log
- from loopx.control_plane.coordination import local_authority_shadow_adapter as projection
- from loopx.control_plane.coordination import runtime_shadow_writer_adapter as writer
-
- registry, state, root = fixture(tmp_path)
- if configured is not None:
- value = json.loads(registry.read_text())
- value["goals"][0]["coordination"]["runtime_shadow"] = {
- "enabled": configured,
- "schema_version": "loopx_coordination_runtime_shadow_config_v0",
- "provider": "file_v0",
- }
- registry.write_text(json.dumps(value))
- begin = writer.begin_todo_runtime_shadow_capture
- load = rollout_event_log.load_rollout_events
- project = projection.todo_partition_projector
- observed: list[str] = []
-
- def read_events(*args, **kwargs):
- observed.append("events")
- return load(*args, **kwargs)
-
- def build_projector(*args, **kwargs):
- observed.append("projector")
- return project(*args, **kwargs)
-
- def observe_capture(**kwargs):
- observed.append("begin")
- # Observe only capture IO; other writer readers retain their real inputs.
- with monkeypatch.context() as capture_observer:
- capture_observer.setattr(rollout_event_log, "load_rollout_events", read_events)
- capture_observer.setattr(projection, "todo_partition_projector", build_projector)
- return begin(**kwargs)
-
- monkeypatch.setattr(writer, "begin_todo_runtime_shadow_capture", observe_capture)
- before = state.read_bytes()
- if operation == "add":
- result = add_goal_todo(registry_path=registry, goal_id=GOAL, role="agent",
- text="Keep disabled capture inert.", task_class="advancement_task", agent_id="agent-a")
- assert result["added"] is True
- assert "Keep disabled capture inert." in state.read_text()
- assert result["coordination_runtime_shadow"]["outcome"] == "no_transaction"
- assert result["coordination_runtime_shadow"]["reason_code"] == "shadow_disabled"
- else:
- result = bootstrap.bootstrap_project(project=tmp_path, registry_path=registry,
- runtime_root=root, goal_id=GOAL, objective="Keep the legacy source complete.",
- domain="test", role="primary", parent_goal_id=None, state_file=state,
- goal_doc=None, adapter_kind="generic_project_goal_v0", adapter_status="connected",
- next_probe=None, spawn_allowed=False, max_children=0, allowed_domains=[],
- write_scope=[], force=False, preserve_todos=False, dry_run=False, sync_global=False)
- assert result["ok"] is True
- assert "## User Todo" in state.read_text()
- assert state.read_bytes() != before
- assert observed == ["begin"]
- assert not (root / "authority-shadow").exists()
-
-
-@pytest.mark.parametrize("projector_fails", [False, True])
-def test_active_binding_keeps_capture_after_configuration_is_disabled(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch, projector_fails: bool,
-) -> None:
- from loopx.control_plane.coordination import local_authority_shadow_adapter as projection
- from loopx.control_plane.coordination.shadow_management import ShadowManagementError
-
- registry, state, root = fixture(tmp_path)
- value = json.loads(registry.read_text())
- value["goals"][0]["coordination"]["runtime_shadow"] = {
- "enabled": True, "schema_version": "loopx_coordination_runtime_shadow_config_v0", "provider": "file_v0",
- }
- registry.write_text(json.dumps(value))
- cli(registry, "coordination-shadow", "bootstrap", "--goal-id", GOAL, "--execute")
- value["goals"][0]["coordination"]["runtime_shadow"]["enabled"] = False
- registry.write_text(json.dumps(value))
- before = state.read_bytes()
- if projector_fails:
- def unavailable(*args, **kwargs):
- raise OSError("projection input unavailable")
- monkeypatch.setattr(projection, "todo_partition_projector", unavailable)
- with pytest.raises(ShadowManagementError) as error:
- add_goal_todo(registry_path=registry, goal_id=GOAL, role="agent",
- text="Preserve the active lineage.", task_class="advancement_task", agent_id="agent-a")
- assert error.value.reason_code == "shadow_capture_prepare_failed"
- assert state.read_bytes() == before
- assert not list((root / "authority-shadow" / "outbox" / GOAL / "todos").glob("*.committed.json"))
- else:
- result = add_goal_todo(registry_path=registry, goal_id=GOAL, role="agent",
- text="Preserve the active lineage.", task_class="advancement_task", agent_id="agent-a")
- assert result["added"] is True
- assert result["coordination_runtime_shadow"]["outcome"] == "delivered"
- assert "Preserve the active lineage." in state.read_text()
- digest = hashlib.sha256(GOAL.encode()).hexdigest()[:16]
- candidate = json.loads((root / "authority-shadow" / "file-v0" / f"authority-store-{digest}.json").read_text())
- assert candidate["cursor"] == "2"
-
-
-def test_real_cli_handoff_and_todo_add_have_one_receipt_each(tmp_path: Path) -> None:
- registry, state, root = fixture(tmp_path)
- value = json.loads(registry.read_text())
- value["goals"][0]["coordination"]["runtime_shadow"] = {
- "enabled": True, "schema_version": "loopx_coordination_runtime_shadow_config_v0", "provider": "file_v0",
- }
- registry.write_text(json.dumps(value))
- cli(registry, "coordination-shadow", "bootstrap", "--goal-id", GOAL, "--execute")
- handoff = cli(registry, "handoff-mode", "set", "--goal-id", GOAL, "--mode", "soft_claim")
- added = cli(registry, "todo", "add", "--goal-id", GOAL, "--role", "agent",
- "--text", "Inspect the read path.", "--task-class", "advancement_task",
- "--evidence", "review fixture")
- assert handoff["coordination_runtime_shadow"]["outcome"] == "delivered", handoff
- assert added["coordination_runtime_shadow"]["outcome"] == "delivered", added
- assert added["added"] is True
- digest = hashlib.sha256(GOAL.encode()).hexdigest()[:16]
- candidate = json.loads((root / "authority-shadow" / "file-v0" / f"authority-store-{digest}.json").read_text())
- assert candidate["cursor"] == "3", "bootstrap plus two primary writes must not get CLI mirror receipts"
- assert len(candidate["committed"]) == 3
- assert "Inspect the read path." in state.read_text()
-
-
-@pytest.mark.parametrize("phase", ["before", "after"])
-def test_real_process_kill_around_state_replace_preserves_complete_bytes(tmp_path: Path, phase: str) -> None:
- state = tmp_path / "state.md"
- state.write_bytes(b"original\r\n")
- code = """
-import sys
-from pathlib import Path
-from loopx.control_plane.runtime import document_io as editing
-original = editing.os.replace
-def replace(source, target):
- if sys.argv[2] == 'after': original(source, target)
- print('replace-cut', flush=True)
- sys.stdin.readline()
- if sys.argv[2] == 'before': original(source, target)
-editing.os.replace = replace
-editing.atomic_write_state_text(Path(sys.argv[1]), 'replacement\\r\\n')
-"""
- child = subprocess.Popen([sys.executable, "-c", code, str(state), phase], cwd=REPO,
- text=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- try:
- assert child.stdout is not None
- assert child.stdout.readline().strip() == "replace-cut"
- child.kill()
- child.communicate(timeout=10)
- finally:
- if child.poll() is None:
- child.kill()
- child.communicate(timeout=10)
- assert state.read_bytes() == (b"original\r\n" if phase == "before" else b"replacement\r\n")
-
-
-def test_cli_waiting_for_todo_mutex_rechecks_fence_after_engagement(tmp_path: Path) -> None:
- from loopx.file_lock import exclusive_cross_runtime_file_lock
- from loopx.control_plane.coordination.legacy_writer_fence import legacy_coordination_todo_lock_path
-
- registry, state, root = fixture(tmp_path)
- before = state.read_bytes()
- code = """
-import sys
-from contextlib import contextmanager
-from loopx.control_plane.coordination import legacy_writer_fence as guard
-original = guard.exclusive_cross_runtime_file_lock
-@contextmanager
-def traced(path, **kwargs):
- if path.name.startswith('legacy-todo-writer-'): print('waiting-for-todo-lock', flush=True)
- with original(path, **kwargs) as held: yield held
-guard.exclusive_cross_runtime_file_lock = traced
-from loopx.entrypoint import main
-sys.argv = ['loopx', *sys.argv[1:]]
-raise SystemExit(main())
-"""
- target = legacy_coordination_todo_lock_path(runtime_root=root, goal_id=GOAL)
- lease_lock = root / "goals" / GOAL / "task-leases" / ".task-leases"
- request = {"schema_version": "loopx_legacy_coordination_writer_fence_engage_request_v0",
- "runtime_root": str(root), "goal_id": GOAL, "state_path": str(state),
- "fence": {"schema_version": "loopx_legacy_coordination_writer_fence_v0",
- "state": "engaged", "goal_id": GOAL, "fence_id": "race-fence",
- "source_version": "race-source", "source_projection_sha256": "a" * 64,
- "expected_shadow_provider_revision": "file:1:aaaaaaaaaaaaaaaaaaaaaaaa"}}
- engage_code = "from loopx.control_plane.effect_runtime import effect_runtime_result; import sys,json; print(json.dumps(effect_runtime_result('coordination.local_authority.legacy_writer_fence.engage', json.loads(sys.argv[1]))))"
- children = []
- try:
- # Real engagement owns T and waits behind K; the public writer then waits
- # behind engagement's T. No fixture writes a fence marker on its behalf.
- with exclusive_cross_runtime_file_lock(lease_lock):
- engage = subprocess.Popen([sys.executable, "-c", engage_code, json.dumps(request)],
- cwd=REPO, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- children.append(engage)
- deadline = time.monotonic() + 10
- while not Path(str(target) + ".ts-effect.lock").exists():
- assert engage.poll() is None, engage.communicate(timeout=1)
- assert time.monotonic() < deadline, "engagement did not acquire the Todo lock"
- time.sleep(0.01)
- child = subprocess.Popen([sys.executable, "-c", code, "--registry", str(registry), "--format", "json",
- "todo", "add", "--goal-id", GOAL, "--role", "agent", "--text", "Must be fenced.",
- "--evidence", "race fixture"], cwd=REPO,
- text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- children.append(child)
- assert child.stdout is not None
- assert child.stdout.readline().strip() == "waiting-for-todo-lock"
- assert child.poll() is None
- engage_output, engage_error = engage.communicate(timeout=30)
- assert json.loads(engage_output)["status"] == "applied", engage_output + engage_error
- output, error = child.communicate(timeout=30)
- payload = json.loads(output)
- assert child.returncode == 1, output + error
- assert payload["error_code"] == "legacy_coordination_writer_fenced"
- assert state.read_bytes() == before
- finally:
- for child in children:
- if child.poll() is None:
- child.kill()
- child.communicate(timeout=10)
-
-
-@pytest.mark.parametrize("override_root", [False, True])
-def test_real_writer_commits_before_a_later_fence_is_published(tmp_path: Path, override_root: bool) -> None:
- registry, state, root = fixture(tmp_path)
- writer_code = """
-import sys
-from loopx.control_plane.runtime import document_io
-original = document_io.atomic_write_state_text
-def paused(*args):
- print('primary-write-cut', flush=True)
- sys.stdin.readline()
- return original(*args)
-document_io.atomic_write_state_text = paused
-from loopx.entrypoint import main
-raise SystemExit(main(sys.argv[1:]))
-"""
- request = {"schema_version": "loopx_legacy_coordination_writer_fence_engage_request_v0",
- "runtime_root": str(root), "goal_id": GOAL, "state_path": str(state),
- "fence": {"schema_version": "loopx_legacy_coordination_writer_fence_v0",
- "state": "engaged", "goal_id": GOAL, "fence_id": "later-fence",
- "source_version": "source-after-write", "source_projection_sha256": "a" * 64,
- "expected_shadow_provider_revision": "file:1:aaaaaaaaaaaaaaaaaaaaaaaa"}}
- children = []
- try:
- writer = subprocess.Popen([sys.executable, "-c", writer_code, "--registry", str(registry),
- "--runtime-root", str(tmp_path / "override" if override_root else root),
- "--format", "json", "todo", "add", "--goal-id", GOAL, "--role", "agent",
- "--text", "Primary won the lock.", "--evidence", "ordering fixture"],
- cwd=REPO, text=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- children.append(writer)
- assert writer.stdout is not None
- assert writer.stdout.readline().strip() == "primary-write-cut"
- engage_code = "from loopx.control_plane.effect_runtime import effect_runtime_result; import sys,json; print(json.dumps(effect_runtime_result('coordination.local_authority.legacy_writer_fence.engage', json.loads(sys.argv[1]))))"
- engager = subprocess.Popen([sys.executable, "-c", engage_code, json.dumps(request)],
- cwd=REPO, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- children.append(engager)
- from loopx.control_plane.coordination.shadow_management import shadow_maintenance_lock_target
- mutex = Path(str(shadow_maintenance_lock_target(root, GOAL)) + ".ts-effect.lock")
- deadline = time.monotonic() + 10
- while not mutex.exists():
- assert engager.poll() is None, engager.communicate(timeout=1)
- assert time.monotonic() < deadline
- time.sleep(0.01)
- time.sleep(.2)
- assert not legacy_coordination_writer_fence_path(runtime_root=root, goal_id=GOAL).exists()
- assert engager.poll() is None
- output, error = writer.communicate("continue\n", timeout=30)
- assert writer.returncode == 0, output + error
- assert json.loads(output)["added"] is True
- output, error = engager.communicate(timeout=30)
- assert json.loads(output)["status"] == "applied", output + error
- assert "Primary won the lock." in state.read_text()
- finally:
- for child in children:
- if child.poll() is None:
- child.kill()
- child.communicate(timeout=10)
-
-
-def test_failed_primary_replace_never_marks_shadow_committed(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
- from loopx.control_plane.runtime import document_io
- registry, state, root = fixture(tmp_path)
- value = json.loads(registry.read_text())
- value["goals"][0]["coordination"]["runtime_shadow"] = {
- "enabled": True, "schema_version": "loopx_coordination_runtime_shadow_config_v0", "provider": "file_v0"}
- registry.write_text(json.dumps(value))
- cli(registry, "coordination-shadow", "bootstrap", "--goal-id", GOAL, "--execute")
- before = state.read_bytes()
- original = document_io.os.replace
- def fail_primary(source: object, target: object) -> None:
- if Path(str(target)) == state:
- raise OSError("primary replace refused")
- original(source, target)
- monkeypatch.setattr(document_io.os, "replace", fail_primary)
- with pytest.raises(OSError, match="primary replace refused"):
- add_goal_todo(
- registry_path=registry, goal_id=GOAL, role="agent",
- text="Must stay prepared.",
- )
- assert state.read_bytes() == before
- directory = root / "authority-shadow" / "outbox" / GOAL / "todos"
- assert len(list(directory.glob("*.prepared.json"))) == 1
- assert list(directory.glob("*.committed.json")) == []
-
-
def test_prose_only_reward_remains_allowed_under_a_legacy_fence(tmp_path: Path) -> None:
from loopx.feedback import append_human_reward
registry, state, _index, reward = reward_fixture(tmp_path)
@@ -551,23 +178,17 @@ def test_prose_only_reward_remains_allowed_under_a_legacy_fence(tmp_path: Path)
def test_prose_guard_ignores_resume_evaluation_clock(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
) -> None:
- from loopx.control_plane.coordination.runtime_shadow_writer_adapter import (
- require_prose_state_write_allowed,
- )
-
registry, state, root = fixture(tmp_path)
- add_goal_todo(
- registry_path=registry,
- goal_id=GOAL,
- role="agent",
- text="Resume the bounded inspection.",
+ seed_todo(
+ state,
+ "Resume the bounded inspection.",
status="deferred",
resume_when="resume_at:2099-01-01T00:00:00Z",
)
original = state.read_text(encoding="utf-8")
planned = original + "\nA bounded narrative observation.\n"
- from loopx.control_plane.coordination.local_authority_shadow_adapter import (
+ from loopx.control_plane.coordination.authority_source_capture import (
todo_partition_projector,
)
from loopx.control_plane.coordination.local_authority_shadow_projection import (
@@ -625,12 +246,9 @@ def test_prose_guard_preserves_json_scalar_identity(
from loopx.control_plane.coordination.local_authority_shadow_projection import (
todo_partition_projection,
)
- from loopx.control_plane.coordination.runtime_shadow_writer_adapter import (
- ActiveStateAuthorityMutationError, require_prose_state_write_allowed,
- )
registry, state, root = fixture(tmp_path)
- add_goal_todo(registry_path=registry, goal_id=GOAL, role="agent", text="Retain result facts.")
+ seed_todo(state, "Retain result facts.")
original = state.read_text(encoding="utf-8")
planned = original + "\nA narrative observation.\n"
real_projector = adapter.todo_partition_projector
@@ -675,9 +293,6 @@ def test_prose_only_reward_holds_before_index_append_during_maintenance(tmp_path
def test_prose_guard_rejects_a_handoff_change(tmp_path: Path) -> None:
- from loopx.control_plane.coordination.runtime_shadow_writer_adapter import (
- ActiveStateAuthorityMutationError, require_prose_state_write_allowed,
- )
registry, state, root = fixture(tmp_path)
original = state.read_text()
with pytest.raises(ActiveStateAuthorityMutationError, match="would change canonical"):
@@ -694,7 +309,6 @@ def test_prose_projection_failure_preserves_source_and_reward_index(
) -> None:
from loopx.feedback import append_human_reward
from loopx.control_plane.coordination import authority_source_capture as source
- from loopx.control_plane.coordination.runtime_shadow_writer_adapter import ActiveStateAuthorityMutationError
from loopx.control_plane.coordination.shadow_management import ShadowManagementError, shadow_management_state_path
registry, state, index, reward = reward_fixture(tmp_path)
@@ -723,8 +337,8 @@ def test_configuration_and_prose_guard_load_without_capture_modules(tmp_path, mo
from test_cold_source_import_cli import workspace
_, state, _, _, runtime, receiver, env = workspace(tmp_path, monkeypatch)
- for name in ("runtime_shadow.py", "runtime_shadow_writer_adapter.py",
- "local_authority_shadow_adapter.py", "local_authority_shadow_outbox.py"):
+ for name in ("runtime_shadow.py", "local_authority_shadow_adapter.py",
+ "local_authority_shadow_outbox.py"):
(receiver / "loopx/control_plane/coordination" / name).unlink()
registry = tmp_path / "project/.loopx/registry.json"
child = subprocess.run([sys.executable, "-c", """
@@ -750,127 +364,6 @@ def test_configuration_and_prose_guard_load_without_capture_modules(tmp_path, mo
assert child.returncode == 0, child.stdout + child.stderr
-def test_override_root_is_the_only_maintenance_authority(tmp_path: Path) -> None:
- from loopx.control_plane.coordination.shadow_management import ShadowManagementError, shadow_management_state_path
- registry, state, root = fixture(tmp_path)
- override = tmp_path / "override"
- management = shadow_management_state_path(override, GOAL)
- management.parent.mkdir(parents=True)
- management.write_text("{}")
- with pytest.raises(ShadowManagementError):
- add_goal_todo(
- registry_path=registry, goal_id=GOAL, runtime_root_arg=str(override),
- role="agent", text="Hold override.",
- )
- assert "Hold override." not in state.read_text()
- assert not (root / "authority-transition").exists()
- result = add_goal_todo(
- registry_path=registry, goal_id=GOAL, role="agent",
- text="Default root remains writable.",
- )
- assert result["added"] is True
-
-
-@pytest.mark.parametrize("writer", ["todo", "prose"])
-def test_override_root_cannot_bypass_registry_source_maintenance(tmp_path: Path, writer: str) -> None:
- from loopx.control_plane.coordination.shadow_management import ShadowManagementError, shadow_management_state_path
- from loopx.feedback import append_human_reward
- registry, state, index, reward = reward_fixture(tmp_path)
- root = tmp_path / "runtime"
- override = tmp_path / "override"
- override_index = override / "goals" / GOAL / "runs" / "index.jsonl"
- override_index.parent.mkdir(parents=True)
- override_index.write_bytes(index.read_bytes())
- management = shadow_management_state_path(root, GOAL)
- management.parent.mkdir(parents=True)
- management.write_text("{}")
- before = state.read_bytes()
- with pytest.raises(ShadowManagementError):
- if writer == "todo":
- add_goal_todo(
- registry_path=registry, goal_id=GOAL, runtime_root_arg=str(override),
- role="agent", text="Cannot bypass source maintenance.",
- )
- else:
- append_human_reward(registry_path=registry, runtime_root_override=str(override),
- goal_id=GOAL, run_generated_at=None, reward=reward, actor_kind="owner",
- write_active_state_summary=True)
- assert state.read_bytes() == before
-
-
-def test_override_root_keeps_prose_writable_with_an_active_source_binding(tmp_path: Path) -> None:
- from loopx.feedback import append_human_reward
- registry, state, index, reward = reward_fixture(tmp_path)
- value = json.loads(registry.read_text())
- value["goals"][0]["coordination"]["runtime_shadow"] = {
- "enabled": True, "schema_version": "loopx_coordination_runtime_shadow_config_v0", "provider": "file_v0"}
- registry.write_text(json.dumps(value))
- cli(registry, "coordination-shadow", "bootstrap", "--goal-id", GOAL, "--execute")
- override = tmp_path / "override"
- override_index = override / "goals" / GOAL / "runs" / "index.jsonl"
- override_index.parent.mkdir(parents=True)
- override_index.write_bytes(index.read_bytes())
- refreshed = append_human_reward(registry_path=registry, runtime_root_override=str(override),
- goal_id=GOAL, run_generated_at=None, reward=reward, actor_kind="owner",
- write_active_state_summary=True)
- assert refreshed["appended"] is True
- assert "Review accepted." in state.read_text()
- assert not (override / "authority-shadow" / "outbox" / GOAL).exists()
-
-
-def test_waiting_override_writer_rechecks_registry_binding_inside_shared_state_lock(tmp_path: Path) -> None:
- registry, state, _root = fixture(tmp_path)
- value = json.loads(registry.read_text())
- value["goals"][0]["coordination"]["runtime_shadow"] = {
- "enabled": True, "schema_version": "loopx_coordination_runtime_shadow_config_v0", "provider": "file_v0"}
- registry.write_text(json.dumps(value))
- before = state.read_bytes()
- waiting, proceed = tmp_path / "waiting", tmp_path / "proceed"
- code = """
-import sys,time
-from contextlib import contextmanager
-from pathlib import Path
-from loopx.control_plane.coordination import legacy_writer_fence as guard
-state,waiting,proceed = map(Path, sys.argv[1:4])
-original = guard.exclusive_cross_runtime_file_lock
-@contextmanager
-def traced(path, **kwargs):
- if path == state:
- waiting.write_text('waiting for actual shared state lock')
- deadline = time.monotonic()+20
- while not proceed.exists():
- if time.monotonic()>deadline: raise TimeoutError('parent did not release writer')
- time.sleep(.01)
- with original(path, **kwargs) as held: yield held
-guard.exclusive_cross_runtime_file_lock = traced
-from loopx.entrypoint import main
-sys.argv = ['loopx', *sys.argv[4:]]
-raise SystemExit(main())
-"""
- child = subprocess.Popen([sys.executable, "-c", code, str(state), str(waiting), str(proceed),
- "--registry", str(registry), "--runtime-root", str(tmp_path / "override"), "--format", "json",
- "todo", "add", "--goal-id", GOAL, "--role", "agent", "--text", "Must observe the new source binding.",
- "--evidence", "cross-root race"], cwd=REPO, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- try:
- deadline = time.monotonic() + 10
- while not waiting.exists():
- assert child.poll() is None, child.communicate(timeout=1)
- assert time.monotonic() < deadline, "writer did not reach shared state lock"
- time.sleep(.01)
- bootstrap = cli(registry, "coordination-shadow", "bootstrap", "--goal-id", GOAL, "--execute")
- assert bootstrap["bootstrap"]["status"] == "applied"
- proceed.write_text("source root is now active")
- output, error = child.communicate(timeout=30)
- payload = json.loads(output)
- assert child.returncode == 1, output + error
- assert payload["error_code"] == "shadow_source_runtime_root_mismatch"
- assert state.read_bytes() == before
- finally:
- if child.poll() is None:
- child.kill()
- child.communicate(timeout=10)
-
-
def test_migration_refuses_overwriting_a_managed_target_before_copy(tmp_path: Path) -> None:
from loopx.state_migration import migrate_legacy_state
source = tmp_path / "source"
@@ -892,104 +385,9 @@ def test_migration_refuses_overwriting_a_managed_target_before_copy(tmp_path: Pa
assert (target_state.read_bytes(), target_registry.read_bytes(), source_state.read_bytes()) == before
-def test_registry_missing_state_reconstruction_respects_maintenance(tmp_path: Path) -> None:
- from loopx.control_plane.projects.registry import register_project_goal
- from loopx.control_plane.coordination.shadow_management import ShadowManagementError, shadow_management_state_path
- registry = tmp_path / "registry.json"
- root = tmp_path / "runtime"
- args = dict(registry_path=registry, runtime_root=root, project_id="project-a", project_kind="work",
- knowledge_root=tmp_path, goal_id=GOAL, objective="Keep the source intact.", non_goals=[],
- acceptance=["State exists."], unknowns=[], next_effect="Inspect state.", stop_condition="Done.",
- repository_bindings=[], external_locator_bindings=[])
- created = register_project_goal(**args)
- state = Path(created["state_file"])
- state.unlink()
- before = registry.read_bytes()
- management = shadow_management_state_path(root, GOAL)
- management.parent.mkdir(parents=True, exist_ok=True)
- management.write_text("{}")
- with pytest.raises(ShadowManagementError):
- register_project_goal(**args)
- assert not state.exists()
- assert registry.read_bytes() == before
-
-
-
-def test_active_capture_prepare_failure_holds_primary_before_any_transition(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
- from loopx.control_plane.coordination import local_authority_shadow_outbox as outbox
- from loopx.control_plane.coordination.shadow_management import ShadowManagementError
- registry, state, root = fixture(tmp_path)
- value = json.loads(registry.read_text())
- value["goals"][0]["coordination"]["runtime_shadow"] = {
- "enabled": True, "schema_version": "loopx_coordination_runtime_shadow_config_v0", "provider": "file_v0"}
- registry.write_text(json.dumps(value))
- cli(registry, "coordination-shadow", "bootstrap", "--goal-id", GOAL, "--execute")
- before = state.read_bytes()
- original = outbox.durable_write_json
- def refuse_prepare(path: Path, record: object) -> None:
- if path.name.endswith(".prepared.json"):
- raise OSError("prepared durability unavailable")
- original(path, record)
- monkeypatch.setattr(outbox, "durable_write_json", refuse_prepare)
- for mode in ["soft_claim", "hard_lease"]:
- with pytest.raises(ShadowManagementError) as error:
- set_goal_handoff_mode(registry_path=registry, goal_id=GOAL, mode=mode)
- assert error.value.reason_code == "shadow_capture_prepare_failed"
- assert state.read_bytes() == before
- directory = root / "authority-shadow" / "outbox" / GOAL / "todos"
- assert list(directory.glob("*.committed.json")) == []
-
-
-@pytest.mark.parametrize("operation", ["add", "update", "complete", "supersede", "archive"])
-def test_all_todo_transaction_owners_enforce_active_preparation(
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch, operation: str,
-) -> None:
- from loopx import todos
- from loopx.control_plane.coordination import local_authority_shadow_outbox as outbox
- from loopx.control_plane.coordination.shadow_management import ShadowManagementError
- registry, state, root = fixture(tmp_path)
- seed = todos.add_goal_todo(registry_path=registry, goal_id=GOAL, role="agent",
- text="Characterize this transaction owner.", status="open",
- task_class="advancement_task", action_kind="analyze", agent_id="agent-a")
- if operation == "archive":
- completed = todos.complete_goal_todo(registry_path=registry, goal_id=GOAL, todo_id=seed["todo_id"],
- evidence="Seed completion.", next_agent_todo="Next bounded seed.",
- next_task_class="advancement_task", agent_id="agent-a")
- assert completed["ok"] is True
- value = json.loads(registry.read_text())
- value["goals"][0]["coordination"]["runtime_shadow"] = {
- "enabled": True, "schema_version": "loopx_coordination_runtime_shadow_config_v0", "provider": "file_v0"}
- registry.write_text(json.dumps(value))
- cli(registry, "coordination-shadow", "bootstrap", "--goal-id", GOAL, "--execute")
- original = outbox.durable_write_json
- def fail_prepare(path: Path, record: object) -> None:
- if path.name.endswith(".prepared.json"):
- raise OSError("prepare is unavailable")
- original(path, record)
- monkeypatch.setattr(outbox, "durable_write_json", fail_prepare)
- before = state.read_bytes()
- identity = dict(registry_path=registry, goal_id=GOAL)
- with pytest.raises(ShadowManagementError) as held:
- if operation == "add":
- todos.add_goal_todo(**identity, role="agent", text="A new obligation.", task_class="advancement_task")
- elif operation == "update":
- todos.update_goal_todo(**identity, todo_id=seed["todo_id"], note="Changed note.", agent_id="agent-a")
- elif operation == "complete":
- todos.complete_goal_todo(**identity, todo_id=seed["todo_id"], evidence="Check completed.",
- next_agent_todo="Next bounded check.", next_task_class="advancement_task", agent_id="agent-a")
- elif operation == "supersede":
- todos.supersede_goal_todo(**identity, todo_id=seed["todo_id"], reason="Replace the approach.",
- next_agent_todo="Use a better check.", next_task_class="advancement_task", agent_id="agent-a")
- else:
- todos.archive_completed_todos(**identity, max_active_done=0, dry_run=False)
- assert held.value.reason_code == "shadow_capture_prepare_failed"
- assert state.read_bytes() == before
-
-
def test_public_refresh_step_does_not_write_owned_paragraph(tmp_path: Path) -> None:
registry, state, root = fixture(tmp_path)
- add_goal_todo(registry_path=registry, goal_id=GOAL, role="agent",
- text="Inspect the bounded result.", task_class="advancement_task", agent_id="agent-a")
+ seed_todo(state, "Inspect the bounded result.")
before = state.read_bytes()
result = cli(registry, "refresh-state", "--goal-id", GOAL, "--agent-id", "agent-a",
"--next-action", "Compare the current evidence.", "--no-global-sync")
diff --git a/tests/control_plane/test_shadow_writer_variant_e2e.py b/tests/control_plane/test_shadow_writer_variant_e2e.py
deleted file mode 100644
index 569ac4da71..0000000000
--- a/tests/control_plane/test_shadow_writer_variant_e2e.py
+++ /dev/null
@@ -1,321 +0,0 @@
-"""Public writer combinations with real persistence and scheduling-only seams."""
-from __future__ import annotations
-
-import json
-from pathlib import Path
-import select
-import subprocess
-import sys
-
-import pytest
-
-from shadow_e2e_fixture import ShadowWorkspace, workspace as make_workspace
-from loopx.control_plane.coordination.coordination_state_contract_generated import TASK_LEASE_ACQUIRE_REQUEST_SCHEMA
-from loopx.control_plane.coordination.legacy_writer_fence import legacy_coordination_writer_fence_path
-from loopx.control_plane.work_items.task_lease_acquire_adapter import task_lease_acquire_authority_facts
-
-REPO = Path(__file__).resolve().parents[2]
-pytestmark = pytest.mark.stage2c_e2e
-
-
-def workspace(path: Path) -> ShadowWorkspace:
- result = make_workspace(path, bootstrap=False)
- result.state.write_text(result.state.read_text() + "\n## Progress Ledger\n\n## Next Action\n\n- Inspect.\n")
- return result
-
-
-def command(ws: ShadowWorkspace, *args: str, goal: str | None = None) -> list[str]:
- return [sys.executable, "-m", "loopx.cli", "--registry", str(ws.registry),
- "--runtime-root", str(ws.runtime), "--format", "json", *args, "--goal-id", goal or ws.goal]
-
-
-def public(ws: ShadowWorkspace, *args: str, goal: str | None = None) -> dict:
- result = subprocess.run(command(ws, *args, goal=goal), cwd=REPO, capture_output=True, text=True, timeout=30)
- assert "Traceback" not in result.stderr, result.stderr
- return json.loads(result.stdout)
-
-
-def start(args: list[str]) -> subprocess.Popen[str]:
- return subprocess.Popen(args, cwd=REPO, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
-
-
-def barrier(child: subprocess.Popen[str], phase: str) -> None:
- assert child.stdout is not None
- ready, _, _ = select.select([child.stdout], [], [], 10)
- assert ready, "public writer did not reach its real scheduling boundary"
- line = child.stdout.readline().strip()
- assert line == "BARRIER " + phase, line
-
-
-def finish(child: subprocess.Popen[str], *, resume: bool = False) -> dict:
- output, error = child.communicate("continue\n" if resume else None, timeout=30)
- assert "Traceback" not in error, output + error
- return json.loads(output)
-
-
-def stop(child: subprocess.Popen[str] | None) -> None:
- if child is not None:
- if child.poll() is None:
- child.kill()
- child.communicate(timeout=10)
-
-
-NATIVE = r"""
-import fs from 'node:fs';
-import {syncBuiltinESMExports} from 'node:module';
-import {once} from 'node:events';
-const input = JSON.parse(process.argv[1]);
-if (input.wait_lock) {
- const actual = fs.promises.open;
- let notified = false;
- fs.promises.open = async (path, flags, ...args) => {
- if (String(path) === input.wait_lock + '.ts-effect.lock' && flags === 'wx' && !notified) {
- notified = true; process.stdout.write('BARRIER native-lock\n');
- }
- return await actual(path, flags, ...args);
- };
- syncBuiltinESMExports();
-}
-const owner = await import(input.module);
-const dependencies = input.pause_write ? {beforeWrite:async () => {
- process.stdout.write('BARRIER native-write\n'); await once(process.stdin, 'data');
-}} : {};
-process.stdout.write(JSON.stringify(await owner[input.function](input.request, dependencies)) + '\n');
-"""
-
-
-def native(module: str, function: str, request: dict, **options: object) -> list[str]:
- return ["node", "--no-warnings", "--experimental-strip-types", "--input-type=module", "-e", NATIVE,
- json.dumps({"module": (REPO / module).as_uri(), "function": function, "request": request, **options})]
-
-
-def fence_request(ws: ShadowWorkspace) -> dict:
- return {"schema_version": "loopx_legacy_coordination_writer_fence_engage_request_v0",
- "runtime_root": str(ws.runtime), "goal_id": ws.goal, "state_path": str(ws.state),
- "fence": {"schema_version": "loopx_legacy_coordination_writer_fence_v0", "state": "engaged",
- "goal_id": ws.goal, "fence_id": "variant-fence", "source_version": "variant-source",
- "source_projection_sha256": "a" * 64,
- "expected_shadow_provider_revision": "file:1:aaaaaaaaaaaaaaaaaaaaaaaa"}}
-
-
-def fence_command(ws: ShadowWorkspace, **options: object) -> list[str]:
- return native("loopx/control_plane/coordination/legacy_writer_fence.ts",
- "engageLegacyCoordinationWriterFence", fence_request(ws), **options)
-
-
-@pytest.mark.parametrize("authority", ["fence", "active_capture"])
-def test_other_goal_cannot_write_a_protected_goal_source_via_state_override(tmp_path: Path, authority: str) -> None:
- ws = workspace(tmp_path)
- other_state = tmp_path / "OTHER_GOAL_STATE.md"
- other_state.write_text("---\ngoal_id: goal-other\n---\n\n## Agent Todo\n")
- registry = json.loads(ws.registry.read_text())
- registry["goals"].append({"id": "goal-other", "repo": str(tmp_path), "state_file": other_state.name,
- "coordination": {"registered_agents": ["agent-a"]}})
- ws.registry.write_text(json.dumps(registry))
- # Preserve the existing opt-out contract before any source authority exists.
- legacy = public(ws, "todo", "add", "--state-file", str(ws.state), "--role", "agent",
- "--text", "An unbound shared-state write remains supported.",
- "--evidence", "Legacy compatibility control.", goal="goal-other")
- assert legacy["ok"] is True and legacy["added"] is True, legacy
- if authority == "active_capture":
- # A registered source stays protected even without a frontmatter owner.
- ws.state.write_text(ws.state.read_text().replace(f"goal_id: {ws.goal}\n", ""))
- assert public(ws, "coordination-shadow", "bootstrap", "--execute")["bootstrap"]["status"] == "applied"
- else:
- result = subprocess.run(fence_command(ws), cwd=REPO, capture_output=True, text=True, check=True, timeout=20)
- assert json.loads(result.stdout)["status"] == "applied"
- before = ws.state.read_bytes(), other_state.read_bytes()
- result = public(ws, "todo", "add", "--state-file", str(ws.state), "--role", "agent",
- "--text", "Must not bypass another goal's source authority.",
- "--evidence", "Cross-goal source boundary.", goal="goal-other")
- assert not result.get("ok"), json.dumps(result, indent=2)
- expected = "shadow_source_goal_mismatch" if authority == "active_capture" else "legacy_coordination_writer_fenced"
- assert result["error_code"] == expected, result
- assert (ws.state.read_bytes(), other_state.read_bytes()) == before
- assert not (ws.runtime / "authority-shadow" / "outbox" / "goal-other").exists()
- if authority == "active_capture":
- unknown = public(ws, "todo", "add", "--state-file", str(ws.state), "--role", "agent",
- "--text", "An unregistered goal cannot bypass source ownership.",
- "--evidence", "Unregistered goal control.", goal="unregistered-goal")
- assert not unknown.get("ok"), unknown
- assert (ws.state.read_bytes(), other_state.read_bytes()) == before
-
-
-PUBLIC_WORKER = r"""
-import sys
-from contextlib import contextmanager
-from pathlib import Path
-stage = sys.argv[1]
-def pause(label):
- print('BARRIER ' + label, flush=True)
- sys.stdin.readline()
-if stage == 'reward_plan':
- from loopx import feedback
- original = feedback.plan_active_state_update
- def planned(*args, **kwargs):
- result = original(*args, **kwargs)
- pause('reward-plan')
- return result
- feedback.plan_active_state_update = planned
-elif stage == 'reward_write':
- from loopx import feedback
- original = feedback.atomic_write_state_text
- def write(*args, **kwargs):
- pause('reward-write')
- return original(*args, **kwargs)
- feedback.atomic_write_state_text = write
-elif stage == 'handoff_write':
- from loopx.control_plane.runtime import document_io
- original = document_io.atomic_write_state_text
- def write(*args, **kwargs):
- pause('handoff-write')
- return original(*args, **kwargs)
- document_io.atomic_write_state_text = write
-elif stage == 'handoff_k':
- from loopx import file_lock
- original = file_lock.exclusive_cross_runtime_file_lock
- @contextmanager
- def lock(path, *args, **kwargs):
- if Path(path).name == '.task-leases':
- print('BARRIER handoff-lock', flush=True)
- with original(path, *args, **kwargs) as held:
- yield held
- file_lock.exclusive_cross_runtime_file_lock = lock
-from loopx.entrypoint import main
-raise SystemExit(main(sys.argv[2:]))
-"""
-
-
-def paused_public(ws: ShadowWorkspace, stage: str, *args: str) -> subprocess.Popen[str]:
- return start([sys.executable, "-c", PUBLIC_WORKER, stage, *command(ws, *args)[3:]])
-
-
-@pytest.mark.parametrize("first", ["lease", "handoff"])
-def test_handoff_and_native_acquire_serialize_quiescence_and_source_evidence(tmp_path: Path, first: str) -> None:
- ws = workspace(tmp_path)
- added = ws.add("Unclaimed work eligible for a lease.")
- todo_id = added["todo_id"]
- assert public(ws, "coordination-shadow", "bootstrap", "--execute")["bootstrap"]["status"] == "applied"
- request = {"schema_version": TASK_LEASE_ACQUIRE_REQUEST_SCHEMA, "runtime_root": str(ws.runtime), "goal_id": ws.goal,
- "todo_id": todo_id, "owner": "agent-a", "idempotency_key": "native-variant-acquire", "ttl_seconds": 120,
- "write_scopes": [], "expected_version": None,
- "authority": task_lease_acquire_authority_facts(registry_path=ws.registry, goal_id=ws.goal, todo_id=todo_id)}
- lease_dir = ws.runtime / "goals" / ws.goal / "task-leases"
- def lease_command(**options: object) -> list[str]:
- return native("loopx/control_plane/work_items/task_lease_acquire.ts",
- "executeTaskLeaseAcquire", request, **options)
- handoff_args = ("handoff-mode", "set", "--mode", "soft_claim")
- lease = handoff = None
- original = ws.state.read_bytes()
- try:
- if first == "lease":
- lease = start(lease_command(pause_write=True))
- barrier(lease, "native-write")
- handoff = paused_public(ws, "handoff_k", *handoff_args)
- barrier(handoff, "handoff-lock")
- assert handoff.poll() is None
- acquired = finish(lease, resume=True)
- refused = finish(handoff)
- assert acquired["acquired"] is True, acquired
- assert refused["error_code"] == "handoff_mode_not_quiescent", refused
- assert ws.state.read_bytes() == original
- assert json.loads((lease_dir / f"{todo_id}.json").read_text())["owner"] == "agent-a"
- else:
- handoff = paused_public(ws, "handoff_write", *handoff_args)
- barrier(handoff, "handoff-write")
- lease = start(lease_command(wait_lock=str(lease_dir / ".task-leases")))
- barrier(lease, "native-lock")
- assert lease.poll() is None
- changed = finish(handoff, resume=True)
- refused = finish(lease)
- assert changed["changed"] is True, changed
- assert refused["error_code"] == "authority_source_changed", refused
- assert "handoff_mode: soft_claim" in ws.state.read_text()
- assert not (lease_dir / f"{todo_id}.json").exists()
- public(ws, "authority-shadow", "drain")
- inspected = public(ws, "coordination-shadow", "inspect")
- assert inspected["inspection"]["status"] == "matched", inspected
- assert inspected["inspection"]["cursor"] == "2", inspected
- finally:
- stop(lease)
- stop(handoff)
-
-
-def prepare_rewards(ws: ShadowWorkspace) -> Path:
- index = ws.runtime / "goals" / ws.goal / "runs" / "index.jsonl"
- index.parent.mkdir(parents=True, exist_ok=True)
- index.write_text(json.dumps({"generated_at": "2026-09-05T00:00:00Z", "json_path": "run.json",
- "markdown_path": "run.md", "classification": "continue"}) + "\n")
- return index
-
-
-def reward_args(reason: str, timestamp: str) -> tuple[str, ...]:
- return ("reward", "--actor-kind", "owner", "--decision", "continue", "--reward", "positive",
- "--reason-summary", reason, "--recorded-at", timestamp, "--write-active-state-summary")
-
-
-def test_concurrent_public_rewards_preserve_both_summaries_and_run_overlays(tmp_path: Path) -> None:
- ws = workspace(tmp_path)
- index = prepare_rewards(ws)
- assert public(ws, "coordination-shadow", "bootstrap", "--execute")["bootstrap"]["status"] == "applied"
- first = paused_public(ws, "reward_plan", *reward_args("First independent review.", "2026-09-05T00:01:00Z"))
- try:
- barrier(first, "reward-plan")
- second = public(ws, *reward_args("Second independent review.", "2026-09-05T00:02:00Z"))
- assert second["ok"] is True, second
- result = finish(first, resume=True)
- assert result["ok"] is True, result
- text = ws.state.read_text()
- assert text.count("First independent review.") == 1
- assert text.count("Second independent review.") == 1
- overlays = [json.loads(line)["human_reward"]["reason_summary"] for line in index.read_text().splitlines()[1:]]
- assert sorted(overlays) == ["First independent review.", "Second independent review."]
- inspection = public(ws, "coordination-shadow", "inspect")["inspection"]
- assert inspection["status"] == "matched", inspection
- assert inspection["cursor"] == "1", inspection
- finally:
- stop(first)
-
-
-def test_native_fence_waits_for_public_prose_and_then_blocks_todo_writes(tmp_path: Path) -> None:
- ws = workspace(tmp_path)
- index = prepare_rewards(ws)
- prose = paused_public(ws, "reward_write", *reward_args("Prose committed before the fence.", "2026-09-05T00:01:00Z"))
- fence = None
- try:
- barrier(prose, "reward-write")
- fence = start(fence_command(ws, wait_lock=str(ws.state)))
- barrier(fence, "native-lock")
- fence_path = legacy_coordination_writer_fence_path(runtime_root=ws.runtime, goal_id=ws.goal)
- assert not fence_path.exists()
- assert fence.poll() is None
- assert finish(prose, resume=True)["ok"] is True
- assert finish(fence)["status"] == "applied"
- before = ws.state.read_bytes()
- result = public(
- ws, "todo", "add", "--role", "agent", "--text", "Must now be fenced.",
- "--evidence", "Boundary check.",
- )
- # The fence selects native create; its owner reads the complete head,
- # rather than the retired Python list preflight. Missing authority must
- # reject without falling through to this still-writable source document.
- assert result["ok"] is False and result["added"] is False, result
- assert result["error_code"] == "local_authority_todo_list_unavailable", result
- assert result["error"] == "canonical Todo authority is unavailable", result
- assert result["status"] == "missing", result
- assert result["source_authority"] == "file_v0", result
- assert result["decision_read_from_provider"] is True, result
- assert result["legacy_fallback_used"] is False, result
- assert result["recovery"] == {
- "action": "restore_canonical_authority",
- "runtime_root": str(ws.runtime.resolve()),
- "goal_id": ws.goal,
- "legacy_markdown_fallback_allowed": False,
- "retry_after": "canonical_provider_readback_loaded",
- }, result
- assert ws.state.read_bytes() == before
- assert "Prose committed before the fence." in ws.state.read_text()
- assert len(index.read_text().splitlines()) == 2
- finally:
- stop(prose)
- stop(fence)
diff --git a/tests/control_plane/test_shared_goal_authority_e2e.py b/tests/control_plane/test_shared_goal_authority_e2e.py
index 9be2ebbe50..702d308611 100644
--- a/tests/control_plane/test_shared_goal_authority_e2e.py
+++ b/tests/control_plane/test_shared_goal_authority_e2e.py
@@ -8,11 +8,9 @@
from __future__ import annotations
-import ast
import json
import os
from collections.abc import Iterator
-from dataclasses import replace
from pathlib import Path
import pytest
@@ -29,48 +27,8 @@
"s2a.nokv_live_qualification",
"s2b.postgresql_conformance_live",
)
-PENDING_ONLY_ROW_ID = "s2c2.sustained_parity_soak"
-PENDING_ROW_IDS = (PENDING_ONLY_ROW_ID,)
LOCAL_FILE_ROW_ID = "s0.native_file_conformance"
FULL_LADDER_VARIABLE = "LOOPX_LADDER_FULL"
-# Rows whose assertions the in-repo CLI E2E suite already pins through the same
-# product path. The pytest job runs close to its time budget, so the default CI
-# projection keeps the rows that only the ladder exercises and defers these to
-# the example runner (or LOOPX_LADDER_FULL=1).
-# Rows the default CI projection skips, each named with the product-CLI E2E
-# test that pins the same assertions; a guard below fails when a twin is
-# renamed or deleted so the skip cannot outlive its coverage.
-CLI_E2E_TWIN_FILE = Path(__file__).with_name("test_local_authority_shadow_cli_e2e.py")
-CLI_E2E_COVERAGE = {
- "s2c1.retired_observation_upgrade": "test_retired_setting_cannot_enable_capture_or_satisfy_bootstrap",
-}
-CLI_E2E_COVERED_ROW_IDS = tuple(CLI_E2E_COVERAGE)
-CLI_E2E_COVERAGE_REASON = (
- "pinned by tests/control_plane/test_local_authority_shadow_cli_e2e.py; "
- "run examples/shared-goal-authority-e2e/ladder.py or set LOOPX_LADDER_FULL=1"
-)
-# The Stage 2C parity half: every row below is executable through the public
-# CLI and the shadow management interfaces. They carry the ``stage2c_e2e``
-# marker so CI runs them in the stage2c correctness job with the other real
-# CLI, process-death and recovery suites instead of the pytest shards.
-STAGE_2C2_ROW_IDS = (
- "s2c2.outbox_prepared_then_committed_entries",
- "s2c2.drain_idempotent",
- "s2c2.sigkill_between_primary_write_and_drain",
- "s2c2.sigkill_mid_drain",
- "s2c2.rollback_with_pending_entries",
- "s2c2.parity_equal",
- "s2c2.parity_divergent_detects_foreign_edit",
- "s2c2.event_only_todo_source_holds",
- "s2c2.migration_seeds_and_drains",
- "s2c2.growth_measurement_gate",
- "s2c2.archive_after_leased_completion_parity",
-)
-STAGE_2C2_POSIX_ONLY_ROW_IDS = (
- "s2c2.sigkill_between_primary_write_and_drain",
- "s2c2.sigkill_mid_drain",
- "s2c2.rollback_with_pending_entries",
-)
def _row_parameters() -> Iterator[object]:
@@ -81,15 +39,11 @@ def _row_parameters() -> Iterator[object]:
marks.append(
pytest.mark.skipif(os.name == "nt", reason="requires POSIX cross-process flock and SIGKILL")
)
- if row.id in CLI_E2E_COVERED_ROW_IDS and not full_ladder:
- marks.append(pytest.mark.skip(reason=CLI_E2E_COVERAGE_REASON))
if row.stage == "0" and not full_ladder:
marks.append(pytest.mark.skip(reason=(
"native provider suites run in npm run test:control-plane; "
"run the standalone ladder or set LOOPX_LADDER_FULL=1 for integrated evidence"
)))
- if row.stage == "2c2":
- marks.append(pytest.mark.stage2c_e2e)
yield pytest.param(row, id=row.id, marks=marks)
@@ -116,19 +70,11 @@ def test_ladder_row_passes_or_is_declared_unverified(
def test_registry_vocabulary_and_pending_rows_are_declared_not_claimed() -> None:
row_ids = [row.id for row in ladder.LADDER_ROWS]
- assert set(CLI_E2E_COVERED_ROW_IDS) < set(row_ids)
pending_ids = [row.id for row in ladder.PENDING_ROWS]
assert len(set(row_ids)) == len(row_ids)
assert set(row_ids).isdisjoint(pending_ids)
- assert [row.id for row in ladder.LADDER_ROWS if row.stage == "2c2"] == list(STAGE_2C2_ROW_IDS)
- assert pending_ids == list(PENDING_ROW_IDS)
- assert {row.stage for row in ladder.LADDER_ROWS} == {"0", "1", "2a", "2b", "2c1", "2c2"}
- assert {row.stage for row in ladder.PENDING_ROWS} == {"2c2"}
- assert all("#3819" not in row.pending_until for row in ladder.PENDING_ROWS)
- for row_id in STAGE_2C2_ROW_IDS:
- row = ladder.row_by_id(row_id)
- assert row.product_path == "real_cli" and row.gate == "deterministic"
- assert row.posix_only is (row_id in STAGE_2C2_POSIX_ONLY_ROW_IDS)
+ assert pending_ids == []
+ assert {row.stage for row in ladder.LADDER_ROWS} == {"0", "1", "2a", "2b"}
assert all(row.gate in ladder.GATES for row in ladder.LADDER_ROWS)
assert all(row.product_path in ladder.PRODUCT_PATHS for row in ladder.LADDER_ROWS)
with pytest.raises(ValueError):
@@ -238,9 +184,10 @@ def test_stage_2a_row_reports_specific_unverified_reasons_for_each_missing_input
assert ladder.collect_bindings(inputs)["nokv_client_config_sha256"] is not None
-@pytest.mark.stage2c_e2e
-def test_stage_2c2_row_passes_when_the_ladder_root_is_reached_through_a_symlink(tmp_path: Path) -> None:
- """macOS's default temp directory is a symlink; the shadow rows must not care (#4892)."""
+def test_stage_1_row_passes_when_the_ladder_root_is_reached_through_a_symlink(
+ tmp_path: Path,
+) -> None:
+ """macOS's default temp directory is a symlink; the CLI row must not care (#4892)."""
real = tmp_path / "real-root"
real.mkdir()
@@ -249,7 +196,7 @@ def test_stage_2c2_row_passes_when_the_ladder_root_is_reached_through_a_symlink(
link.symlink_to(real, target_is_directory=True)
except OSError as exc:
pytest.skip(f"directory symlinks unavailable: {exc}")
- row = ladder.row_by_id("s2c2.outbox_prepared_then_committed_entries")
+ row = ladder.row_by_id("s1.cli_document_decodes_through_ts_store")
result = ladder.run_row(row, root=link, environ=os.environ)
if result.status == "unverified":
pytest.skip(f"unverified: {result.reason_code}")
@@ -273,21 +220,27 @@ def test_pending_rows_never_exit_green_without_allow_pending(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
+ pending_row_id = "future.canonical_soak"
+ monkeypatch.setattr(
+ ladder,
+ "PENDING_ROWS",
+ (ladder.PendingRow(pending_row_id, "1", "a separately owned canonical soak"),),
+ )
for name in LIVE_ENVIRONMENT_VARIABLES:
monkeypatch.delenv(name, raising=False)
report_path = tmp_path / "report.json"
# Pending-only selection: zero executions must not read as success.
- assert ladder.main(["--row", PENDING_ONLY_ROW_ID, "--report-json", str(report_path)]) == 1
+ assert ladder.main(["--row", pending_row_id, "--report-json", str(report_path)]) == 1
report = json.loads(report_path.read_text(encoding="utf-8"))
assert report["summary"] == {"pass": 0, "fail": 0, "unverified": 0, "pending": 1, "executed": 0, "privacy_violations": 0}
assert report["rows"] == []
- assert report["pending"][0]["id"] == PENDING_ONLY_ROW_ID
+ assert report["pending"][0]["id"] == pending_row_id
assert report["pending"][0]["status"] == "pending"
assert report["exit_policy"]["exit_code"] == 1
assert "pending rows (not verified):" in capsys.readouterr().err
- assert ladder.main(["--row", PENDING_ONLY_ROW_ID, "--allow-pending", "--report-json", str(report_path)]) == 0
+ assert ladder.main(["--row", pending_row_id, "--allow-pending", "--report-json", str(report_path)]) == 0
allowed = json.loads(report_path.read_text(encoding="utf-8"))
assert allowed["exit_policy"] == {
"allow_unverified": False,
@@ -298,19 +251,33 @@ def test_pending_rows_never_exit_green_without_allow_pending(
assert allowed["summary"]["pending"] == 1
capsys.readouterr()
- # A stage whose only remaining declaration is pending behaves the same way;
- # the executable 2c2 rows are removed from the registry for this check only.
+ # A stage whose only selected declaration is pending behaves the same way.
with pytest.MonkeyPatch.context() as registry:
- registry.setattr(ladder, "LADDER_ROWS", tuple(row for row in ladder.LADDER_ROWS if row.stage != "2c2"))
- assert ladder.main(["--stage", "2c2", "--report-json", str(report_path)]) == 1
+ registry.setattr(ladder, "LADDER_ROWS", tuple(row for row in ladder.LADDER_ROWS if row.stage != "1"))
+ assert ladder.main(["--stage", "1", "--report-json", str(report_path)]) == 1
capsys.readouterr()
# Mixed selection: one executable pass does not excuse a pending obligation.
- monkeypatch.setattr(ladder, "LADDER_ROWS", tuple(
- replace(row, run=lambda _context: ladder.passed()) if row.id == LOCAL_FILE_ROW_ID else row
- for row in ladder.LADDER_ROWS
- ))
- mixed = ["--row", LOCAL_FILE_ROW_ID, "--row", PENDING_ONLY_ROW_ID, "--report-json", str(report_path)]
+ local = ladder.row_by_id(LOCAL_FILE_ROW_ID)
+ monkeypatch.setattr(
+ ladder,
+ "LADDER_ROWS",
+ tuple(
+ ladder.LadderRow(
+ id=row.id,
+ stage=row.stage,
+ title=row.title,
+ product_path=row.product_path,
+ gate=row.gate,
+ posix_only=row.posix_only,
+ run=lambda _context: ladder.passed(),
+ )
+ if row is local
+ else row
+ for row in ladder.LADDER_ROWS
+ ),
+ )
+ mixed = ["--row", LOCAL_FILE_ROW_ID, "--row", pending_row_id, "--report-json", str(report_path)]
assert ladder.main(mixed) == 1
report = json.loads(report_path.read_text(encoding="utf-8"))
assert report["summary"] == {"pass": 1, "fail": 0, "unverified": 0, "pending": 1, "executed": 1, "privacy_violations": 0}
@@ -319,11 +286,11 @@ def test_pending_rows_never_exit_green_without_allow_pending(
capsys.readouterr()
# List-only never claims verification: it prints the registry and exits 0.
- assert ladder.main(["--list", "--row", PENDING_ONLY_ROW_ID]) == 0
+ assert ladder.main(["--list", "--row", pending_row_id]) == 0
listing = json.loads(capsys.readouterr().out)
assert listing["schema_version"] == ladder.LIST_SCHEMA
assert listing["rows"] == []
- assert [row["id"] for row in listing["pending"]] == [PENDING_ONLY_ROW_ID]
+ assert [row["id"] for row in listing["pending"]] == [pending_row_id]
assert "exit_policy" not in listing
@@ -366,7 +333,7 @@ def test_privacy_scan_turns_leaks_into_failures(tmp_path: Path) -> None:
"privacy_violations": 1,
}
assert report["exit_policy"]["exit_code"] == 1
- assert {row["status"] for row in report["pending"]} == {"pending"}
+ assert report["pending"] == []
def test_privacy_leak_confined_to_bindings_still_fails_the_run() -> None:
@@ -403,14 +370,6 @@ def test_privacy_leak_confined_to_bindings_still_fails_the_run() -> None:
assert ladder.exit_code_for(report["summary"], allow_unverified=True, allow_pending=True) == 1
-def test_ci_projection_skips_only_rows_whose_cli_e2e_twin_still_exists() -> None:
- tree = ast.parse(CLI_E2E_TWIN_FILE.read_text(encoding="utf-8"))
- defined = {node.name for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)}
- for row_id, twin in CLI_E2E_COVERAGE.items():
- ladder.row_by_id(row_id)
- assert twin in defined, f"{row_id} is skipped by default but its twin {twin} no longer exists"
-
-
def test_list_prints_rows_and_pending_declarations(
capsys: pytest.CaptureFixture[str],
) -> None:
@@ -420,11 +379,12 @@ def test_list_prints_rows_and_pending_declarations(
assert [row["id"] for row in listing["pending"]] == [row.id for row in ladder.PENDING_ROWS]
assert all(row["status"] == "pending" for row in listing["pending"])
- assert ladder.main(["--list", "--stage", "2c2"]) == 0
+ assert ladder.main(["--list", "--stage", "1"]) == 0
stage_listing = json.loads(capsys.readouterr().out)
- assert [row["id"] for row in stage_listing["rows"]] == list(STAGE_2C2_ROW_IDS)
- assert [row["id"] for row in stage_listing["pending"]] == list(PENDING_ROW_IDS)
- assert {row["stage"] for row in stage_listing["pending"]} == {"2c2"}
+ assert [row["id"] for row in stage_listing["rows"]] == [
+ "s1.cli_document_decodes_through_ts_store"
+ ]
+ assert stage_listing["pending"] == []
@pytest.mark.parametrize("provider", ["file", "sqlite"])
diff --git a/tests/control_plane/test_source_lease_reader_isolation.py b/tests/control_plane/test_source_lease_reader_isolation.py
index 95d7e4704f..32514ab51b 100644
--- a/tests/control_plane/test_source_lease_reader_isolation.py
+++ b/tests/control_plane/test_source_lease_reader_isolation.py
@@ -1,4 +1,4 @@
-"""Supported source leases retain readback without the historic mutation facade."""
+"""Historical source leases retain readback and exact cleanup without the facade."""
from __future__ import annotations
@@ -65,7 +65,7 @@ def lease(action, *args, success=True):
"task-lease", action, "--todo-id", "todo_source", *args, success=success
)
- first = lease(
+ rejected = lease(
"acquire",
"--owner",
"agent-a",
@@ -73,43 +73,34 @@ def lease(action, *args, success=True):
"source-a",
"--ttl-seconds",
"600",
- success=mode != "soft_claim",
+ success=False,
)
- if mode == "soft_claim":
- assert not first["ok"] and first["error_code"] == "handoff_mode_forbids_lease"
- assert lease("inspect")["lease"] is None
- assert (w.state.read_bytes(), w.registry.read_bytes()) == original
- return
+ assert not rejected["ok"]
+ assert rejected["error_code"] == "canonical_authority_required"
- assert first["ok"] and first["acquired"] and first["lease"]["owner"] == "agent-a"
- assert first["lease"]["version"] == first["lease"]["lease_epoch"] == 1
- assert lease("inspect")["active"] is True
- retry = lease(
- "acquire",
- "--owner",
- "agent-a",
- "--idempotency-key",
- "source-a",
- "--ttl-seconds",
- "600",
- )
- assert retry["idempotent"] and retry["lease"] == first["lease"]
- denied = lease(
- "acquire", "--owner", "agent-b", "--idempotency-key", "other", success=False
- )
- assert not denied["ok"] and denied["error_code"] == "todo_lease_conflict"
- renewed = lease(
- "renew",
- "--owner",
- "agent-a",
- "--idempotency-key",
- "source-a",
- "--expected-version",
- "1",
+ lease_path = (
+ w.runtime / "goals" / w.goal / "task-leases" / "todo_source.json"
)
- assert renewed["lease"]["version"] == 2 and renewed["lease"]["lease_epoch"] == 1
- # The exact original renewal is receipt recovery, not a stale new write.
- replay = lease(
+ lease_path.parent.mkdir(parents=True, exist_ok=True)
+ historical = {
+ "schema_version": "task_lease_v0",
+ "goal_id": w.goal,
+ "todo_id": "todo_source",
+ "status": "active",
+ "owner": "agent-a",
+ "idempotency_key": "source-a",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 600,
+ "version": 1,
+ "lease_epoch": 1,
+ "acquired_at": "2026-09-01T00:00:00Z",
+ "updated_at": "2026-09-01T00:00:00Z",
+ "expires_at": "2099-01-01T00:00:00Z",
+ }
+ lease_path.write_text(json.dumps(historical))
+ assert lease("inspect")["lease"] == historical
+
+ renew = lease(
"renew",
"--owner",
"agent-a",
@@ -117,47 +108,33 @@ def lease(action, *args, success=True):
"source-a",
"--expected-version",
"1",
+ success=False,
)
- assert replay["idempotent"] and replay["lease"] == renewed["lease"]
- assert lease("inspect")["lease"] == renewed["lease"]
- transferred = lease(
+ assert renew["error_code"] == "canonical_authority_required"
+ transfer = lease(
"transfer",
"--owner",
"agent-a",
"--idempotency-key",
"source-a",
"--expected-version",
- "2",
+ "1",
"--new-owner",
"agent-b",
"--new-idempotency-key",
"source-b",
- )
- assert (
- transferred["lease"]["version"] == 3
- and transferred["lease"]["lease_epoch"] == 2
- )
- assert transferred["lease"]["owner"] == "agent-b"
- stale = lease(
- "renew",
- "--owner",
- "agent-b",
- "--idempotency-key",
- "source-b",
- "--expected-version",
- "2",
success=False,
)
- assert not stale["ok"] and stale["error_code"] == "version_mismatch"
- assert lease("inspect")["lease"] == transferred["lease"]
+ assert transfer["error_code"] == "canonical_authority_required"
+ assert lease("inspect")["lease"] == historical
+
released = lease(
"release",
- "--owner",
- "agent-b",
+ "--owner", "agent-a",
"--idempotency-key",
- "source-b",
+ "source-a",
"--expected-version",
- "3",
+ "1",
)
assert released["lease"]["status"] == "released"
retained = lease("inspect")
diff --git a/tests/control_plane/test_split_root_todo_writeback_fence.py b/tests/control_plane/test_split_root_todo_writeback_fence.py
index dd53e4546a..62b8ec55c0 100644
--- a/tests/control_plane/test_split_root_todo_writeback_fence.py
+++ b/tests/control_plane/test_split_root_todo_writeback_fence.py
@@ -1,7 +1,7 @@
-"""Split-root fence regressions for the monitor-poll and Turn writebacks.
+"""Split-root authority regressions for the monitor-poll and Turn writebacks.
-Each writeback uses its effective runtime root and also respects the authority
-of the registered source file. An override cannot bypass either source fence.
+Each writeback uses its effective runtime root. A write override must resolve
+to canonical authority instead of mutating the registered Markdown projection.
"""
from __future__ import annotations
@@ -14,17 +14,15 @@
import pytest
+from canonical_authority_fixture import initialize_canonical_authority
from loopx.cli import main
from loopx.cli_commands.turn_todo_writeback import (
write_turn_repair_update,
write_turn_validated_completion,
)
-from loopx.control_plane.coordination.legacy_writer_fence import (
- LegacyCoordinationWriterFenced,
- legacy_coordination_writer_fence_path,
-)
-from loopx.control_plane.coordination.local_authority import (
- LocalCoordinationAuthorityUnavailable,
+from loopx.control_plane.coordination.local_authority import CanonicalAuthorityRequired
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
)
from loopx.control_plane.quota import monitor_poll
from loopx.control_plane.scheduler.monitor_poll_writeback import (
@@ -36,7 +34,7 @@
ADVANCE_ID = "todo_splitroot_advance"
MONITOR_ID = "todo_splitroot_monitor"
OVERRIDE_POLL_HASH = "split-v2"
-LEGACY_POLL_HASH = "split-v1"
+INITIAL_POLL_HASH = "split-v1"
REPOSITORY = Path(__file__).resolve().parents[2]
@@ -58,7 +56,7 @@ def _write_split_root_goal(tmp_path: Path) -> tuple[Path, Path, Path, Path]:
" \n",
+ f"result_hash={INITIAL_POLL_HASH} material_change=false -->\n",
encoding="utf-8",
)
runtime_registry = tmp_path / "registry-runtime"
@@ -91,27 +89,47 @@ def _write_split_root_goal(tmp_path: Path) -> tuple[Path, Path, Path, Path]:
return registry, state, runtime_registry, runtime_override
-def _engage_fence_at(runtime_root: Path) -> None:
- fence = legacy_coordination_writer_fence_path(
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- )
- fence.parent.mkdir(parents=True, exist_ok=True)
- fence.write_text(
- json.dumps({"state": "present", "engaged_by": "promotion"}),
- encoding="utf-8",
- )
-
-
-def _fence_check_blocks(monkeypatch: pytest.MonkeyPatch) -> None:
- monkeypatch.setattr(
- "loopx.control_plane.coordination.legacy_writer_fence."
- "effect_runtime_result",
- lambda *_args, **_kwargs: {
- "status": "blocked",
- "reason_code": "legacy_coordination_writer_fenced",
- "authority_mode": "file_v0",
- },
+def _initialize_canonical_override(runtime_root: Path, state: Path) -> None:
+ initialize_canonical_authority(
+ runtime_root,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ handoff_mode="soft_claim",
+ todos=[
+ {
+ "schema_version": "todo_item_v0",
+ "todo_id": ADVANCE_ID,
+ "role": "agent",
+ "status": "open",
+ "done": False,
+ "text": "Advance the fenced writeback slice.",
+ "archive_state": "active",
+ "source_section": "Agent Todo",
+ "index": 1,
+ "task_class": "advancement_task",
+ "claimed_by": AGENT_ID,
+ },
+ {
+ "schema_version": "todo_item_v0",
+ "todo_id": MONITOR_ID,
+ "role": "agent",
+ "status": "open",
+ "done": False,
+ "text": "Watch the fenced writeback channel.",
+ "archive_state": "active",
+ "source_section": "Agent Todo",
+ "index": 2,
+ "task_class": "continuous_monitor",
+ "claimed_by": AGENT_ID,
+ "target_key": "splitroot-review",
+ "cadence": "30m",
+ "result_hash": INITIAL_POLL_HASH,
+ "material_change": False,
+ },
+ ],
+ ),
+ state_path=state,
)
@@ -136,51 +154,26 @@ def _poll_kwargs(
def test_monitor_poll_writeback_rejects_unavailable_canonical_override(
- monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
registry, state, _runtime_registry, runtime_override = (
_write_split_root_goal(tmp_path)
)
- _engage_fence_at(runtime_override)
- _fence_check_blocks(monkeypatch)
state_before = state.read_text(encoding="utf-8")
- with pytest.raises(LocalCoordinationAuthorityUnavailable):
+ with pytest.raises(CanonicalAuthorityRequired) as error:
write_monitor_poll_todo_state(**_poll_kwargs(registry, runtime_override))
- assert LEGACY_POLL_HASH in state.read_text(encoding="utf-8")
+ assert error.value.code == "canonical_authority_required"
+ assert error.value.payload["operation"] == "monitor_poll"
+ assert INITIAL_POLL_HASH in state.read_text(encoding="utf-8")
assert OVERRIDE_POLL_HASH not in state.read_text(encoding="utf-8")
assert state.read_text(encoding="utf-8") == state_before
-def test_monitor_poll_writeback_blocks_when_the_registry_source_is_fenced(
- tmp_path: Path,
-) -> None:
- """An override shares the source state, so its original fence still applies."""
-
- from loopx.control_plane.effect_runtime import effect_runtime_result
-
- registry, state, runtime_registry, runtime_override = (
- _write_split_root_goal(tmp_path)
- )
- before = state.read_bytes()
- engaged = effect_runtime_result("coordination.local_authority.legacy_writer_fence.engage", {
- "schema_version": "loopx_legacy_coordination_writer_fence_engage_request_v0",
- "runtime_root": str(runtime_registry), "goal_id": GOAL_ID, "state_path": str(state),
- "fence": {"schema_version": "loopx_legacy_coordination_writer_fence_v0", "state": "engaged",
- "goal_id": GOAL_ID, "fence_id": "original-source", "source_version": "source:1",
- "source_projection_sha256": "a" * 64, "expected_shadow_provider_revision": "file:1:aaaaaaaaaaaaaaaaaaaaaaaa"},
- })
- assert engaged["status"] == "applied", engaged
- with pytest.raises(LegacyCoordinationWriterFenced) as error:
- write_monitor_poll_todo_state(**_poll_kwargs(registry, runtime_override))
- assert error.value.code == "legacy_coordination_writer_fenced"
- assert state.read_bytes() == before
-
-
-def test_monitor_poll_writeback_allows_an_unfenced_runtime_override(tmp_path: Path) -> None:
+def test_monitor_poll_writeback_uses_canonical_runtime_override(tmp_path: Path) -> None:
registry, state, _runtime_registry, runtime_override = _write_split_root_goal(tmp_path)
+ _initialize_canonical_override(runtime_override, state)
receipt = write_monitor_poll_todo_state(
**_poll_kwargs(registry, runtime_override)
@@ -193,7 +186,7 @@ def test_monitor_poll_writeback_allows_an_unfenced_runtime_override(tmp_path: Pa
assert f"result_hash={OVERRIDE_POLL_HASH}" in active
-def test_quota_monitor_poll_provider_writeback_blocked_under_override_fence(
+def test_quota_monitor_poll_provider_writeback_requires_canonical_override(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
@@ -202,8 +195,6 @@ def test_quota_monitor_poll_provider_writeback_blocked_under_override_fence(
registry, state, _runtime_registry, runtime_override = (
_write_split_root_goal(tmp_path)
)
- _engage_fence_at(runtime_override)
- _fence_check_blocks(monkeypatch)
def native(_method: str, request: dict[str, Any]) -> dict[str, Any]:
assert request["phase"] == "preflight"
@@ -229,7 +220,7 @@ def native(_method: str, request: dict[str, Any]) -> dict[str, Any]:
"agent_identity": {"agent_id": AGENT_ID},
}
- with pytest.raises(LocalCoordinationAuthorityUnavailable):
+ with pytest.raises(CanonicalAuthorityRequired) as error:
monitor_poll.record_quota_monitor_poll_for_decision(
before,
{"runtime_root": str(runtime_override)},
@@ -243,6 +234,7 @@ def native(_method: str, request: dict[str, Any]) -> dict[str, Any]:
agent_id=AGENT_ID,
)
+ assert error.value.code == "canonical_authority_required"
assert OVERRIDE_POLL_HASH not in state.read_text(encoding="utf-8")
@@ -254,12 +246,14 @@ def test_quota_monitor_poll_cli_rejects_unavailable_canonical_before_collection(
registry, state, _runtime_registry, runtime_override = (
_write_split_root_goal(tmp_path)
)
- _engage_fence_at(runtime_override)
- _fence_check_blocks(monkeypatch)
+ _initialize_canonical_override(runtime_override, state)
+ backend = runtime_override / "authority" / "file-v0"
+ offline = backend.with_name("file-v0-offline")
+ backend.rename(offline)
before = state.read_bytes()
def unexpected_collection(**_kwargs: Any) -> dict[str, Any]:
- pytest.fail("fenced monitor write must be rejected before status collection")
+ pytest.fail("selected provider loss must be rejected before status collection")
monkeypatch.setattr("loopx.cli_commands.quota.collect_status", unexpected_collection)
@@ -291,22 +285,21 @@ def unexpected_collection(**_kwargs: Any) -> dict[str, Any]:
assert exit_code == 1
payload = json.loads(capsys.readouterr().out)
- assert payload["error_code"] == "local_authority_todo_list_unavailable"
- assert payload["source_authority"] == "file_v0"
+ assert payload["error_code"] != "canonical_authority_required"
assert payload["legacy_fallback_used"] is False
assert state.read_bytes() == before
+ assert not backend.exists()
+ offline.rename(backend)
@pytest.mark.parametrize("command", ["should-run", "monitor-poll"])
-def test_read_only_quota_still_collects_promoted_state(
- command: str, monkeypatch: pytest.MonkeyPatch, tmp_path: Path,
+def test_read_only_quota_still_collects_unmigrated_state(
+ command: str, tmp_path: Path,
) -> None:
from loopx.cli import build_parser
from loopx.cli_commands.quota_context import prepare_quota_command_context
registry, _state, _registered_root, runtime_override = _write_split_root_goal(tmp_path)
- _engage_fence_at(runtime_override)
- _fence_check_blocks(monkeypatch)
args = build_parser().parse_args([
"quota", command, "--goal-id", GOAL_ID, "--agent-id", AGENT_ID,
*(["--todo-id", MONITOR_ID] if command == "monitor-poll" else []),
@@ -326,18 +319,15 @@ def collector(**kwargs: Any) -> dict[str, object]:
assert context.status_payload["runtime_root"] == str(runtime_override)
-def test_turn_repair_update_blocked_when_override_root_is_fenced(
- monkeypatch: pytest.MonkeyPatch,
+def test_turn_repair_update_blocked_without_canonical_override(
tmp_path: Path,
) -> None:
registry, state, runtime_registry, runtime_override = (
_write_split_root_goal(tmp_path)
)
- _engage_fence_at(runtime_override)
- _fence_check_blocks(monkeypatch)
before = state.read_bytes()
- with pytest.raises(LocalCoordinationAuthorityUnavailable) as error:
+ with pytest.raises(CanonicalAuthorityRequired) as error:
write_turn_repair_update(
registry_path=registry,
runtime_root_arg=str(runtime_override),
@@ -348,40 +338,29 @@ def test_turn_repair_update_blocked_when_override_root_is_fenced(
agent_id=AGENT_ID,
)
- assert error.value.code == "local_authority_todo_list_unavailable"
- assert str(error.value) == "canonical Todo authority is unavailable"
- assert error.value.payload == {
- "schema_version": "loopx_coordination_todo_update_result_v0",
- "status": "missing",
- "changed": False,
- "source_authority": "file_v0",
- "decision_read_from_provider": True,
- "legacy_fallback_used": False,
- "recovery": {
- "action": "restore_canonical_authority",
- "runtime_root": str(runtime_override.resolve()),
- "goal_id": GOAL_ID,
- "legacy_markdown_fallback_allowed": False,
- "retry_after": "canonical_provider_readback_loaded",
- },
+ assert error.value.code == "canonical_authority_required"
+ assert error.value.payload["operation"] == "todo_update"
+ assert error.value.payload["write_admitted"] is False
+ assert error.value.payload["decision_read_from_provider"] is False
+ assert error.value.payload["legacy_fallback_used"] is False
+ assert error.value.payload["recovery"] == {
+ "action": "migrate_goal_authority",
+ "backup_required": True,
}
assert state.read_bytes() == before
assert not (runtime_override / "authority").exists()
assert not (runtime_registry / "authority").exists()
-def test_turn_validated_completion_blocked_when_override_root_is_fenced(
- monkeypatch: pytest.MonkeyPatch,
+def test_turn_validated_completion_blocked_without_canonical_override(
tmp_path: Path,
) -> None:
registry, state, runtime_registry, runtime_override = (
_write_split_root_goal(tmp_path)
)
- _engage_fence_at(runtime_override)
- _fence_check_blocks(monkeypatch)
before = state.read_bytes()
- with pytest.raises(LocalCoordinationAuthorityUnavailable) as error:
+ with pytest.raises(CanonicalAuthorityRequired) as error:
write_turn_validated_completion(
registry_path=registry,
runtime_root_arg=str(runtime_override),
@@ -393,20 +372,14 @@ def test_turn_validated_completion_blocked_when_override_root_is_fenced(
agent_id=AGENT_ID,
)
- assert error.value.code == "local_authority_todo_list_unavailable"
- assert error.value.payload == {
- "schema_version": "loopx_local_coordination_todo_list_result_v0",
- "status": "missing",
- "source_authority": "file_v0",
- "decision_read_from_provider": True,
- "legacy_fallback_used": False,
- "recovery": {
- "action": "restore_canonical_authority",
- "runtime_root": str(runtime_override.resolve()),
- "goal_id": GOAL_ID,
- "legacy_markdown_fallback_allowed": False,
- "retry_after": "canonical_provider_readback_loaded",
- },
+ assert error.value.code == "canonical_authority_required"
+ assert error.value.payload["operation"] == "todo_terminal"
+ assert error.value.payload["write_admitted"] is False
+ assert error.value.payload["decision_read_from_provider"] is False
+ assert error.value.payload["legacy_fallback_used"] is False
+ assert error.value.payload["recovery"] == {
+ "action": "migrate_goal_authority",
+ "backup_required": True,
}
assert state.read_bytes() == before
assert not (runtime_override / "authority").exists()
@@ -448,10 +421,9 @@ def test_turn_validated_completion_blocked_when_override_root_is_fenced(
)
assert process.returncode == 1, process.stdout + process.stderr
payload = json.loads(process.stdout)
- assert payload["error_code"] == "local_authority_todo_list_unavailable"
- assert payload["status"] == "missing"
- assert payload["source_authority"] == "file_v0"
- assert payload["decision_read_from_provider"] is True
+ assert payload["error_code"] == "canonical_authority_required"
+ assert payload["write_admitted"] is False
+ assert payload["decision_read_from_provider"] is False
assert payload["legacy_fallback_used"] is False
assert payload["recovery"] == error.value.payload["recovery"]
assert state.read_bytes() == before
diff --git a/tests/control_plane/test_sqlite_authority_cli.py b/tests/control_plane/test_sqlite_authority_cli.py
index 6da7f9eaa3..dd76d7551b 100644
--- a/tests/control_plane/test_sqlite_authority_cli.py
+++ b/tests/control_plane/test_sqlite_authority_cli.py
@@ -143,8 +143,10 @@ def durable_bytes():
assert not list((runtime / "authority-transition").rglob("legacy-writer-fence-*.json"))
-@pytest.mark.parametrize("old_producers_present", [True, False])
-def test_new_default_goal_settles_once_and_returns_current_writes_to_file(tmp_path, monkeypatch, old_producers_present):
+@pytest.mark.parametrize("bridge_modules_present", [True, False])
+def test_new_default_goal_settles_once_and_returns_current_writes_to_file(
+ tmp_path, monkeypatch, bridge_modules_present,
+):
"""Join the shipped creation, ordinary work and reversible-exit owners."""
from loopx.control_plane.effect_runtime import restart_effect_runtime
@@ -156,9 +158,12 @@ def test_new_default_goal_settles_once_and_returns_current_writes_to_file(tmp_pa
package = Path(os.environ.get("LOOPX_COLD_IMPORT_PACKAGE", str(Path(__file__).resolve().parents[2] / "loopx")))
receiver = tmp_path / "receiver"
shutil.copytree(package, receiver / "loopx", ignore=shutil.ignore_patterns("__pycache__", "*.pyc"))
- if not old_producers_present:
- for name in ("runtime_shadow.py", "runtime_shadow_writer_adapter.py",
- "local_authority_shadow_adapter.py", "local_authority_shadow_outbox.py"):
+ assert not (
+ receiver / "loopx/control_plane/coordination/runtime_shadow_writer_adapter.py"
+ ).exists()
+ if not bridge_modules_present:
+ for name in ("runtime_shadow.py", "local_authority_shadow_adapter.py",
+ "local_authority_shadow_outbox.py"):
(receiver / "loopx/control_plane/coordination" / name).unlink()
# Isolated interpreter, real full entrypoint, and verified copied-package
# provenance on every command; no fallback to the test runner's checkout.
diff --git a/tests/control_plane/test_superseded_turn_settlement_cli.py b/tests/control_plane/test_superseded_turn_settlement_cli.py
index a86a66b682..1629d28929 100644
--- a/tests/control_plane/test_superseded_turn_settlement_cli.py
+++ b/tests/control_plane/test_superseded_turn_settlement_cli.py
@@ -5,30 +5,9 @@
import pytest
import test_declared_terminal_settlement_cli as declared
-import test_goal_handoff_mode as legacy
import test_quota_settlement_cli as cli
from loopx.control_plane.quota.settlement import read_heartbeat_settlement
-from loopx.todos import supersede_goal_todo
-
-
-@pytest.mark.parametrize("successors", [None, [], ["todo_existing_successor"]])
-def test_unpromoted_supersede_preserves_omitted_links_and_rejects_existing_links(
- tmp_path: Path, successors: list[str] | None,
-) -> None:
- registry, state = legacy._write_workspace(tmp_path)
- todo = legacy._add_todo(registry, claimed_by=legacy.AGENT_A)
- before = state.read_bytes()
- arguments = dict(registry_path=registry, goal_id=legacy.GOAL_ID,
- todo_id=todo["todo_id"], agent_id=legacy.AGENT_A,
- reason="Retire the original approach.", successor_todo_ids=successors)
- if successors:
- with pytest.raises(ValueError, match="requires promoted canonical Todo authority"):
- supersede_goal_todo(**arguments)
- assert state.read_bytes() == before
- else:
- result = supersede_goal_todo(**arguments)
- assert result["ok"] is True and result["superseded"] is True
def _future_monitor(run, lease, *, link=True):
diff --git a/tests/control_plane/test_task_lease.py b/tests/control_plane/test_task_lease.py
index cef9f73541..06fbb99ff1 100644
--- a/tests/control_plane/test_task_lease.py
+++ b/tests/control_plane/test_task_lease.py
@@ -1,7 +1,6 @@
from __future__ import annotations
import json
-import os
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any
@@ -13,16 +12,12 @@
from loopx.control_plane.work_items.task_lease import (
MAX_TASK_LEASE_TTL_SECONDS,
TaskLeaseError,
- assert_expected_version,
normalize_idempotency_key,
normalize_ttl_seconds,
release_task_lease,
renew_task_lease,
- task_lease_owner_constraint,
- transfer_task_lease,
write_scopes_overlap,
)
-from loopx.todos import add_goal_todo
@pytest.mark.parametrize("raw", [None, b'{"status":"active"}', b"{broken", b"\xff"],
@@ -67,57 +62,6 @@ def test_write_scopes_overlap(
assert write_scopes_overlap(left, right) is expected
-@pytest.mark.parametrize(
- ("todo", "owner", "registered_agents", "reason"),
- [
- (None, "agent-a", ["agent-a"], "todo_not_found"),
- ({"status": "done"}, "agent-a", ["agent-a"], "todo_not_open"),
- ({"status": "open"}, "", ["agent-a"], "invalid_owner"),
- ({"status": "open"}, "agent-b", ["agent-a"], "owner_not_registered"),
- (
- {"status": "open", "excluded_agents": ["agent-a"]},
- "agent-a",
- ["agent-a"],
- "owner_excluded_from_todo",
- ),
- (
- {"status": "open", "claimed_by": "agent-b"},
- "agent-a",
- ["agent-a", "agent-b"],
- "owner_conflicts_with_claim",
- ),
- ],
-)
-def test_task_lease_owner_constraint_rejects_ineligible_owner(
- todo: dict[str, Any] | None,
- owner: str,
- registered_agents: list[str],
- reason: str,
-) -> None:
- constraint = task_lease_owner_constraint(
- todo,
- owner=owner,
- registered_agents=registered_agents,
- )
-
- assert constraint["effective"] is False
- assert constraint["reason"] == reason
-
-
-def test_task_lease_owner_constraint_accepts_matching_claim() -> None:
- constraint = task_lease_owner_constraint(
- {
- "status": "open",
- "claimed_by": "agent-a",
- "excluded_agents": ["agent-b"],
- },
- owner="agent-a",
- registered_agents=["agent-a", "agent-b"],
- )
-
- assert constraint == {"effective": True}
-
-
@pytest.mark.parametrize("ttl", [0, -1, MAX_TASK_LEASE_TTL_SECONDS + 1])
def test_normalize_ttl_seconds_rejects_out_of_range(ttl: int) -> None:
with pytest.raises(TaskLeaseError, match="ttl seconds") as error:
@@ -134,207 +78,40 @@ def test_normalize_idempotency_key_rejects_non_token(key: str) -> None:
assert error.value.code == "invalid_idempotency_key"
-def test_expected_version_is_compare_and_swap_guard() -> None:
- with pytest.raises(TaskLeaseError) as error:
- assert_expected_version({"version": 3}, 2)
-
- assert error.value.code == "version_mismatch"
- assert error.value.payload == {"expected_version": 2, "actual_version": 3}
-
-
-def test_lease_epoch_migrates_legacy_records_and_rejects_corruption() -> None:
- assert task_lease.lease_epoch(None) == 0
- assert (
- task_lease.lease_epoch({"schema_version": "task_lease_v0", "version": 7}) == 1
- )
-
- for raw_epoch in (0, -1, "not-an-epoch"):
- with pytest.raises(TaskLeaseError) as error:
- task_lease.lease_epoch({"lease_epoch": raw_epoch})
- assert error.value.code == "corrupt_lease"
-
-
-def test_bool_disguised_lease_integers_fail_closed() -> None:
- """bool is an int subclass: a JSON ``true`` must not become epoch/version/
- TTL ``1`` silently. The shared Stage 2 head codec already rejects bool
- (head._is_count); the local record codec is the migration source for
- ``last_lease_epoch`` seeding, so the same typed corrupt_lease applies
- before any local-to-shared migration."""
-
- for raw in (True, False):
- with pytest.raises(TaskLeaseError) as error:
- task_lease.lease_epoch({"lease_epoch": raw})
- assert error.value.code == "corrupt_lease"
- with pytest.raises(TaskLeaseError) as error:
- task_lease.lease_version({"version": raw})
- assert error.value.code == "corrupt_lease"
- with pytest.raises(TaskLeaseError) as error:
- task_lease.lease_acquire_ttl_seconds({"acquire_ttl_seconds": raw})
- assert error.value.code == "corrupt_lease"
-
-
-def test_retained_task_lease_lifecycle_preserves_versions(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
-) -> None:
- now = datetime(2026, 7, 13, tzinfo=timezone.utc)
- monkeypatch.setattr(task_lease, "now_utc", lambda: now)
- repo = tmp_path / "repo"
- repo.mkdir()
- state = repo / "ACTIVE_GOAL_STATE.md"
- state.write_text(
- "---\n"
- "goal_id: goal-a\n"
- "updated_at: 2026-07-13T00:00:00+00:00\n"
- "---\n\n"
- "## Agent Todo\n\n",
- encoding="utf-8",
- )
- registry_path = tmp_path / "registry.json"
- runtime_root = tmp_path / "runtime"
- registry_path.write_text(
- json.dumps(
- {
- "common_runtime_root": str(runtime_root),
- "goals": [
- {
- "id": "goal-a",
- "domain": "harness_self_improvement",
- "status": "active",
- "repo": str(repo),
- "state_file": state.name,
- "adapter": {"kind": "harness_self_improvement"},
- "coordination": {
- "agent_model": "peer_v1",
- "registered_agents": ["agent-a", "agent-b"],
- },
- }
- ],
- }
- ),
- encoding="utf-8",
- )
- todo = add_goal_todo(
- registry_path=registry_path,
- goal_id="goal-a",
- role="agent",
- text="Exercise the retained task-lease lifecycle.",
- task_class="advancement_task",
- )
- todo_id = str(todo["todo_id"])
- lease_path = task_lease.task_lease_path(
- runtime_root=runtime_root,
- goal_id="goal-a",
- todo_id=todo_id,
- )
- task_lease.write_lease(
- lease_path,
- task_lease.build_lease(
- goal_id="goal-a",
- todo_id=todo_id,
- owner="agent-a",
- idempotency_key="turn-1",
- write_scopes=["loopx/**"],
- acquire_ttl_seconds=120,
- version=1,
- lease_epoch=1,
- acquired_at=now.isoformat().replace("+00:00", "Z"),
- updated_at=now.isoformat().replace("+00:00", "Z"),
- expires_at=(now + timedelta(seconds=120))
- .isoformat()
- .replace("+00:00", "Z"),
- ),
- )
-
- with pytest.raises(TaskLeaseError) as renew_without_version:
- renew_task_lease(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id="goal-a",
- todo_id=todo_id,
- owner="agent-a",
- idempotency_key="turn-1",
- )
- assert renew_without_version.value.code == "version_required"
-
- with pytest.raises(TaskLeaseError) as transfer_without_version:
- transfer_task_lease(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id="goal-a",
- todo_id=todo_id,
- owner="agent-a",
- idempotency_key="turn-1",
- new_owner="agent-b",
- new_idempotency_key="turn-2",
- )
- assert transfer_without_version.value.code == "version_required"
+@pytest.fixture
+def retained_lease(tmp_path: Path):
+ # Fixed original 1.x bytes; the current runtime must read/release these
+ # without relying on a surviving Python lease producer.
+ original = json.loads((Path(__file__).parent / "fixtures" / "retained_lease_receipts_1x.json").read_text())["scenarios"]["original"]
+ relative, lease = next(iter(original["files"].items()))
+ runtime = tmp_path / "runtime"
+ path = runtime / relative
+ path.parent.mkdir(parents=True)
+ path.write_text(json.dumps(lease))
+ request = {key: original["request"][key] for key in ("goal_id", "todo_id", "owner", "idempotency_key")}
+ return path, lease, {**request, "runtime_root": runtime, "expected_version": lease["version"]}
- renewed = renew_task_lease(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id="goal-a",
- todo_id=todo_id,
- owner="agent-a",
- idempotency_key="turn-1",
- expected_version=1,
- )
- assert renewed["lease"]["version"] == 2
- assert renewed["lease"]["lease_epoch"] == 1
- transferred = transfer_task_lease(
- registry_path=registry_path,
- runtime_root=runtime_root,
- goal_id="goal-a",
- todo_id=todo_id,
- owner="agent-a",
- idempotency_key="turn-1",
- new_owner="agent-b",
- new_idempotency_key="turn-2",
- expected_version=2,
- )
- assert transferred["lease"]["owner"] == "agent-b"
- assert transferred["lease"]["version"] == 3
- assert transferred["lease"]["lease_epoch"] == 2
+def test_retained_release_enforces_cas_without_rewriting_history(retained_lease) -> None:
+ path, lease, request = retained_lease
+ before = path.read_bytes()
+ with pytest.raises(TaskLeaseError) as error:
+ release_task_lease(**{**request, "expected_version": lease["version"] + 1})
+ assert error.value.code == "version_mismatch"
+ assert path.read_bytes() == before
- released = release_task_lease(
- runtime_root=runtime_root,
- goal_id="goal-a",
- todo_id=todo_id,
- owner="agent-b",
- idempotency_key="turn-2",
- expected_version=3,
- )
- assert released["released"] is True
- assert released["lease"]["status"] == "released"
- assert released["lease"]["released_at"] == now.isoformat().replace("+00:00", "Z")
- assert released["lease"]["updated_at"] == released["lease"]["released_at"]
- assert task_lease.lease_is_active(released["lease"], at=now) is False
- persisted_path = Path(str(released["lease_path"]))
- assert persisted_path.exists()
- assert task_lease.read_lease(persisted_path) == released["lease"]
-
- replayed_release = release_task_lease(
- runtime_root=runtime_root,
- goal_id="goal-a",
- todo_id=todo_id,
- owner="agent-b",
- idempotency_key="turn-2",
- expected_version=3,
- )
- assert replayed_release["released"] is True
- assert replayed_release["idempotent"] is True
- assert replayed_release["lease"] == released["lease"]
- with pytest.raises(TaskLeaseError) as missing_version:
- release_task_lease(
- runtime_root=runtime_root,
- goal_id="goal-a",
- todo_id=todo_id,
- owner="agent-b",
- idempotency_key="turn-2",
- )
- assert missing_version.value.code == "version_required"
+@pytest.mark.parametrize(("field", "raw"), [
+ ("lease_epoch", value) for value in [0, -1, "not-an-epoch", True, False]
+] + [("version", value) for value in [-1, "not-a-version", True, False]])
+def test_retained_release_rejects_corrupt_generation_without_writes(retained_lease, field, raw) -> None:
+ path, lease, request = retained_lease
+ path.write_text(json.dumps({**lease, field: raw}))
+ before = path.read_bytes()
+ with pytest.raises(TaskLeaseError) as error:
+ release_task_lease(**request)
+ assert error.value.code == "corrupt_lease"
+ assert path.read_bytes() == before
def test_lifecycle_mutation_fails_closed_without_registry(
@@ -349,24 +126,24 @@ def test_lifecycle_mutation_fails_closed_without_registry(
goal_id="goal-a",
todo_id="todo_missing_registry",
)
- task_lease.write_lease(
- lease_path,
- task_lease.build_lease(
- goal_id="goal-a",
- todo_id="todo_missing_registry",
- owner="agent-a",
- idempotency_key="turn-1",
- write_scopes=[],
- acquire_ttl_seconds=120,
- version=1,
- lease_epoch=1,
- acquired_at=now.isoformat().replace("+00:00", "Z"),
- updated_at=now.isoformat().replace("+00:00", "Z"),
- expires_at=(now + timedelta(seconds=120))
+ lease_path.parent.mkdir(parents=True)
+ lease_path.write_text(json.dumps({
+ "schema_version": "task_lease_v0",
+ "status": "active",
+ "goal_id": "goal-a",
+ "todo_id": "todo_missing_registry",
+ "owner": "agent-a",
+ "idempotency_key": "turn-1",
+ "write_scopes": [],
+ "acquire_ttl_seconds": 120,
+ "version": 1,
+ "lease_epoch": 1,
+ "acquired_at": now.isoformat().replace("+00:00", "Z"),
+ "updated_at": now.isoformat().replace("+00:00", "Z"),
+ "expires_at": (now + timedelta(seconds=120))
.isoformat()
.replace("+00:00", "Z"),
- ),
- )
+ }))
before = task_lease.read_lease(lease_path)
with pytest.raises(TaskLeaseError) as error:
@@ -380,7 +157,7 @@ def test_lifecycle_mutation_fails_closed_without_registry(
expected_version=1,
)
- assert error.value.code == "todo_not_found"
+ assert error.value.code == "goal_not_found"
assert task_lease.read_lease(lease_path) == before
@@ -396,9 +173,8 @@ def test_legacy_generation_and_unrelated_ttl_do_not_block_release(
goal_id="goal-a",
todo_id="todo_legacy",
)
- task_lease.write_lease(
- lease_path,
- {
+ lease_path.parent.mkdir(parents=True)
+ lease_path.write_text(json.dumps({
"schema_version": "task_lease_v0",
"goal_id": "goal-a",
"todo_id": "todo_legacy",
@@ -413,8 +189,7 @@ def test_legacy_generation_and_unrelated_ttl_do_not_block_release(
"expires_at": (now + timedelta(seconds=120))
.isoformat()
.replace("+00:00", "Z"),
- },
- )
+ }))
released = release_task_lease(
runtime_root=runtime_root,
@@ -427,167 +202,6 @@ def test_legacy_generation_and_unrelated_ttl_do_not_block_release(
assert released["lease"]["lease_epoch"] == 1
-def test_holder_verify_transport_retry_reuses_one_fence_operation_id(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
-) -> None:
- registry_path = tmp_path / "registry.json"
- registry_path.write_text("{}", encoding="utf-8")
- authority = {
- "handoff_mode": "hard_lease",
- "registered_agent_candidates": [["agent-a"]],
- "todos": [
- {
- "todo_id": "todo_retry",
- "status": "open",
- "claimed_by": "agent-a",
- "excluded_agents": [],
- }
- ],
- "todo_projection_error": None,
- "source_receipts": [],
- }
- monkeypatch.setattr(
- task_lease_acquire_adapter,
- "task_lease_acquire_authority_facts",
- lambda **_kwargs: authority,
- )
- seen_operation_ids: list[str] = []
-
- def retrying_effect_runtime_result(
- _method: str,
- params: dict[str, Any],
- *,
- timeout: float,
- ) -> dict[str, Any]:
- assert timeout == 15.0
- operation_id = params.get("fence_operation_id")
- assert isinstance(operation_id, str)
- seen_operation_ids.append(operation_id)
- if len(seen_operation_ids) == 1:
- return {
- "ok": False,
- "schema_version": "task_lease_v0",
- "action": "holder_verify",
- "error": "authority changed while the response was in flight",
- "error_code": "authority_source_changed",
- }
- return {
- "ok": True,
- "schema_version": "task_lease_v0",
- "action": "holder_verify",
- "fence": {
- "schema_version": "task_lease_v0",
- "checked": True,
- "active": True,
- "owner": "agent-a",
- "version": 1,
- "lease_epoch": 1,
- "lock_token": "held-token",
- "fence_operation_id": operation_id,
- },
- }
-
- import loopx.control_plane.effect_runtime as effect_runtime
-
- monkeypatch.setattr(
- effect_runtime,
- "effect_runtime_result",
- retrying_effect_runtime_result,
- )
-
- first = task_lease_acquire_adapter.execute_native_task_lease_lifecycle(
- runtime_root=tmp_path / "runtime",
- registry_path=registry_path,
- goal_id="goal-a",
- todo_id="todo_retry",
- operation="holder_verify",
- owner="agent-a",
- )
- second = task_lease_acquire_adapter.execute_native_task_lease_lifecycle(
- runtime_root=tmp_path / "runtime",
- registry_path=registry_path,
- goal_id="goal-a",
- todo_id="todo_retry",
- operation="holder_verify",
- owner="agent-a",
- )
-
- assert len(seen_operation_ids) == 3
- assert seen_operation_ids[0] == seen_operation_ids[1]
- assert seen_operation_ids[0] != seen_operation_ids[2]
- assert first["fence"]["fence_operation_id"] == seen_operation_ids[0]
- assert second["fence"]["fence_operation_id"] == seen_operation_ids[2]
-
-
-def test_failed_fence_close_abandons_its_owned_cross_runtime_lock(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
-) -> None:
- runtime_root = tmp_path / "runtime"
- token = "held-fence-token"
- lock_target = task_lease.task_lease_lock_path(
- runtime_root=runtime_root,
- goal_id="goal-a",
- )
- effect_lock = Path(f"{lock_target}.ts-effect.lock")
- close_calls = 0
-
- monkeypatch.setattr(
- task_lease,
- "runtime_root_from_registry",
- lambda _registry_path, _override: runtime_root,
- )
-
- def failing_lifecycle(**kwargs: Any) -> dict[str, Any]:
- nonlocal close_calls
- if kwargs["operation"] == "terminal_verify":
- effect_lock.parent.mkdir(parents=True, exist_ok=True)
- effect_lock.write_text(
- json.dumps({"pid": os.getpid(), "token": token}),
- encoding="utf-8",
- )
- return {
- "ok": True,
- "schema_version": "task_lease_v0",
- "action": "terminal_verify",
- "fence": {
- "schema_version": "task_lease_v0",
- "required": True,
- "active": True,
- "owner": "agent-a",
- "version": 1,
- "lease_epoch": 1,
- "execution_instance_verified": True,
- "lock_token": token,
- "fence_operation_id": "a" * 64,
- },
- }
- close_calls += 1
- raise RuntimeError("simulated managed-runtime transport loss")
-
- monkeypatch.setattr(
- task_lease,
- "_execute_native_task_lease_lifecycle",
- failing_lifecycle,
- )
-
- with task_lease.hold_task_lease_mutation_fence(
- registry_path=tmp_path / "registry.json",
- goal_id="goal-a",
- todo_id="todo_target",
- todo={"todo_id": "todo_target", "status": "open"},
- actor_agent_id="agent-a",
- idempotency_key="lease-a",
- expected_version=1,
- ) as fence:
- task_lease.release_verified_task_lease_fence(fence, committed=True)
- assert fence["released"] is False
-
- assert close_calls == 1
- assert not effect_lock.exists()
-
-
@pytest.mark.parametrize(
("expected_version", "expected_forwarded"),
[("2", 2), (2.5, 2)],
diff --git a/tests/control_plane/test_task_lease_argument_recovery.py b/tests/control_plane/test_task_lease_argument_recovery.py
index b1912a13f3..ebc103118a 100644
--- a/tests/control_plane/test_task_lease_argument_recovery.py
+++ b/tests/control_plane/test_task_lease_argument_recovery.py
@@ -15,7 +15,7 @@
GOAL, TODO = "lease-recovery", "todo_lease_recovery"
-@pytest.fixture(params=["legacy", "file", "sqlite"])
+@pytest.fixture(params=["file", "sqlite"])
def lease_cli(tmp_path, monkeypatch, request):
isolate_sqlite_runtime(tmp_path, monkeypatch)
project, runtime = tmp_path / "project with spaces", tmp_path / "runtime"
@@ -27,15 +27,14 @@ def lease_cli(tmp_path, monkeypatch, request):
"id": GOAL, "repo": str(project), "state_file": state.name,
"coordination": {"registered_agents": ["peer-a", "peer-b"]},
}]}))
- if request.param != "legacy":
- initialize_canonical_authority(runtime, GOAL, build_todo_runtime_shadow_projection(
- goal_id=GOAL, handoff_mode="hard_lease", leases=[], todos=[{
- "schema_version": "todo_item_v0", "todo_id": TODO, "role": "agent",
- "status": "open", "done": False, "text": "Verify a source artifact.",
- "archive_state": "active", "source_section": "Agent Todo", "index": 1,
- "task_class": "advancement_task", "claimed_by": "peer-a", "required_write_scopes": ["src/**"],
- }]), state_path=state, provider=request.param)
- state.unlink()
+ initialize_canonical_authority(runtime, GOAL, build_todo_runtime_shadow_projection(
+ goal_id=GOAL, handoff_mode="hard_lease", leases=[], todos=[{
+ "schema_version": "todo_item_v0", "todo_id": TODO, "role": "agent",
+ "status": "open", "done": False, "text": "Verify a source artifact.",
+ "archive_state": "active", "source_section": "Agent Todo", "index": 1,
+ "task_class": "advancement_task", "claimed_by": "peer-a", "required_write_scopes": ["src/**"],
+ }]), state_path=state, provider=request.param)
+ state.unlink()
prefix = ["--registry", str(registry), "--runtime-root", str(runtime), "--format", "json"]
def run(args, code=0):
@@ -149,7 +148,7 @@ def test_recovery_preserves_routing_and_repeatable_string_values():
assert recovered.new_idempotency_key is None
-def test_transfer_repair_retains_explicit_claim_handover(lease_cli, request):
+def test_transfer_repair_retains_explicit_claim_handover(lease_cli):
run, action, _, _ = lease_cli
before = run(action("inspect"))["lease"]
rejected = run(action("transfer", "--owner", "peer-a", "--idempotency-key", "original",
@@ -161,19 +160,13 @@ def test_transfer_repair_retains_explicit_claim_handover(lease_cli, request):
assert "--transfer-claim" in repaired
assert repaired[repaired.index("--expected-version") + 1] == "1"
assert run(action("inspect"))["lease"] == before
- if request.node.callspec.params["lease_cli"] == "legacy":
- # Syntax repair cannot make atomic claim transfer available on legacy files.
- denied = run(repaired, code=1)
- assert denied["ok"] is False and "recovery" not in denied
- assert run(action("inspect"))["lease"] == before
- else:
- transferred = run(repaired)
- assert transferred["lease"]["owner"] == transferred["claimed_by"] == "peer-b"
- assert transferred["lease"]["version"] == 2
- assert transferred["lease"]["write_scopes"] == ["src/**"]
- released = run(action("release", "--owner", "peer-b", "--idempotency-key", "receiver",
- "--expected-version", "2"))
- assert released["lease"]["status"] == "released"
+ transferred = run(repaired)
+ assert transferred["lease"]["owner"] == transferred["claimed_by"] == "peer-b"
+ assert transferred["lease"]["version"] == 2
+ assert transferred["lease"]["write_scopes"] == ["src/**"]
+ released = run(action("release", "--owner", "peer-b", "--idempotency-key", "receiver",
+ "--expected-version", "2"))
+ assert released["lease"]["status"] == "released"
def test_grammar_rejection_precedes_runtime_resolution(monkeypatch):
@@ -189,3 +182,147 @@ def test_grammar_rejection_precedes_runtime_resolution(monkeypatch):
print_payload=lambda payload, *_: captured.append(payload)) == 1
assert captured[0]["recovery"]["remove_flags"] == ["--write-worktree"]
assert "canonical lease checks" in render_task_lease_markdown(captured[0])
+
+
+def _retained_lifecycle_cli(tmp_path: Path, operation: str):
+ """Restore fixed 1.x records; never invoke a retired writer to seed them."""
+ fixture = Path(__file__).parent / "fixtures" / "retained_lease_receipts_1x.json"
+ history = json.loads(fixture.read_text().replace("$ROOT", str(tmp_path)))["scenarios"]
+ scenario = history["original" if operation == "release" else operation]
+ runtime, state, registry = tmp_path / "runtime", tmp_path / "state.md", tmp_path / "registry.json"
+ state.write_text("# Historical Goal\n\n## Agent Todo\n\n")
+ registry.write_text(json.dumps({"common_runtime_root": str(runtime), "goals": [{
+ "id": "goal-a", "repo": str(tmp_path), "state_file": state.name,
+ "coordination": {"registered_agents": ["agent-a", "agent-b"]},
+ }]}))
+ lease = runtime / "goals/goal-a/task-leases/todo_target.json"
+ receipt = None
+ for relative, value in scenario["files"].items():
+ path = runtime / relative
+ path.parent.mkdir(parents=True, exist_ok=True)
+ path.write_text(json.dumps(value))
+ if ".lifecycle-operations" in path.parts:
+ receipt = path
+
+ def run(**overrides):
+ request = {**scenario["request"], **overrides}
+ args = ["--registry", str(registry), "--runtime-root", str(runtime), "--format", "json",
+ "task-lease", operation, "--goal-id", "goal-a", "--todo-id", "todo_target",
+ "--owner", request["owner"], "--idempotency-key", request["idempotency_key"],
+ "--expected-version", str(request["expected_version"] if operation != "release" else 1)]
+ if operation != "release":
+ args += ["--ttl-seconds", str(request["ttl_seconds"])]
+ if operation == "transfer":
+ args += ["--new-owner", request["new_owner"], "--new-idempotency-key", request["new_idempotency_key"]]
+ if request.get("transfer_claim") is True:
+ args += ["--transfer-claim"]
+ completed = subprocess.run([sys.executable, "-m", "loopx.cli", *args], cwd=ROOT,
+ capture_output=True, text=True, timeout=90)
+ return completed.returncode, json.loads(completed.stdout)
+
+ return run, runtime, state, registry, lease, receipt, history
+
+
+@pytest.mark.parametrize("operation", ["renew", "transfer"])
+@pytest.mark.parametrize("receipt_state", ["prepared", "committed"])
+def test_retained_lifecycle_public_cli_recovers_only_the_original_effect(tmp_path, operation, receipt_state):
+ run, runtime, state, registry, lease, receipt, _history = _retained_lifecycle_cli(tmp_path, operation)
+ assert receipt is not None
+ original = json.loads(receipt.read_text())
+ receipt.write_text(json.dumps({**original, "state": receipt_state}))
+ source_before = {path: path.read_bytes() for path in (state, registry, lease)}
+ receipt_before = receipt.read_bytes()
+ rc, replay = run()
+ assert rc == 0 and replay["idempotent"] is True, replay
+ assert replay["lease"] == json.loads(source_before[lease])
+ assert replay["settlement"]["receipts"][-1]["status"] == "replayed"
+ assert {path: path.read_bytes() for path in source_before} == source_before
+ if receipt_state == "committed":
+ assert receipt.read_bytes() == receipt_before
+ else:
+ assert json.loads(receipt.read_text())["state"] == "committed"
+ repaired_receipt = receipt.read_bytes()
+ rc, again = run()
+ assert rc == 0 and again["lease"] == replay["lease"] and again["idempotent"] is True, again
+ assert receipt.read_bytes() == repaired_receipt
+ assert {path: path.read_bytes() for path in source_before} == source_before
+ assert not (runtime / "authority").exists()
+
+
+@pytest.mark.parametrize("operation", ["renew", "transfer"])
+@pytest.mark.parametrize("change", ["ttl", "owner", "key", "version", "later_generation", "corrupt_receipt", "prepared_without_effect", "missing_receipt"])
+def test_retained_lifecycle_public_cli_refuses_changes_and_fresh_source_work(tmp_path, operation, change):
+ run, runtime, state, registry, lease, receipt, history = _retained_lifecycle_cli(tmp_path, operation)
+ assert receipt is not None
+ overrides = {"ttl": {"ttl_seconds": 1200}, "owner": {"owner": "agent-b"},
+ "key": {"idempotency_key": "another-execution"}, "version": {"expected_version": 2}}.get(change, {})
+ if change == "later_generation":
+ current = json.loads(lease.read_text())
+ lease.write_text(json.dumps({**current, "version": current["version"] + 1, "lease_epoch": current["lease_epoch"] + 1}))
+ elif change == "corrupt_receipt":
+ receipt.write_text("{invalid original receipt")
+ elif change == "prepared_without_effect":
+ original = json.loads(receipt.read_text())
+ receipt.write_text(json.dumps({**original, "state": "prepared"}))
+ lease.write_text(json.dumps(history["original"]["files"]["goals/goal-a/task-leases/todo_target.json"]))
+ elif change == "missing_receipt":
+ receipt.unlink()
+ before = {path: path.read_bytes() if path.exists() else None for path in (state, registry, lease, receipt)}
+ rc, rejected = run(**overrides)
+ assert rc != 0 and rejected["ok"] is False, rejected
+ if change in {"prepared_without_effect", "missing_receipt"}:
+ assert rejected["error_code"] == "canonical_authority_required", rejected
+ assert rejected["write_admitted"] is False and rejected["legacy_fallback_used"] is False
+ elif change == "ttl":
+ assert rejected["error_code"] == "idempotency_key_reuse", rejected
+ elif change == "later_generation":
+ assert rejected["error_code"] == "lifecycle_receipt_state_mismatch", rejected
+ assert {path: path.read_bytes() if path.exists() else None for path in before} == before
+ assert not (runtime / "authority").exists()
+
+
+@pytest.mark.parametrize("change", [{"new_owner": "agent-a"}, {"new_idempotency_key": "different-receiver"}, {"transfer_claim": True}])
+def test_retained_transfer_cli_cannot_change_the_receiver_or_add_claim_authority(tmp_path, change):
+ run, runtime, state, registry, lease, receipt, _history = _retained_lifecycle_cli(tmp_path, "transfer")
+ before = {path: path.read_bytes() for path in (state, registry, lease, receipt)}
+ rc, rejected = run(**change)
+ assert rc != 0 and rejected["ok"] is False, rejected
+ assert {path: path.read_bytes() for path in before} == before
+ assert not (runtime / "authority").exists()
+
+
+@pytest.mark.parametrize("operation", ["renew", "transfer"])
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_retained_lifecycle_cli_never_falls_back_after_selected_provider_loss(tmp_path, monkeypatch, operation, provider):
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+ run, runtime, state, registry, lease, receipt, _history = _retained_lifecycle_cli(tmp_path, operation)
+ initialize_canonical_authority(runtime, "goal-a", build_todo_runtime_shadow_projection(
+ goal_id="goal-a", todos=[], leases=[], handoff_mode="hard_lease"), state_path=state, provider=provider)
+ backend = runtime / "authority" / f"{provider}-v0"
+ offline = backend.with_name(backend.name + "-offline")
+ backend.rename(offline)
+ before = {path: path.read_bytes() for path in (state, registry, lease, receipt)}
+ try:
+ rc, rejected = run()
+ assert rc != 0 and rejected["ok"] is False, rejected
+ assert rejected["legacy_fallback_used"] is False
+ assert {path: path.read_bytes() for path in before} == before
+ assert not backend.exists()
+ finally:
+ offline.rename(backend)
+
+
+def test_retained_expired_lease_public_release_still_settles_once(tmp_path):
+ run, runtime, state, registry, lease, _receipt, _history = _retained_lifecycle_cli(tmp_path, "release")
+ original = json.loads(lease.read_text())
+ before = {path: path.read_bytes() for path in (state, registry)}
+ rc, released = run()
+ assert rc == 0 and released["lease"]["status"] == "released", released
+ assert released["lease"]["owner"] == original["owner"]
+ assert released["lease"]["idempotency_key"] == original["idempotency_key"]
+ after = lease.read_bytes()
+ rc, replay = run()
+ assert rc == 0 and replay["idempotent"] is True and replay["lease"] == released["lease"], replay
+ assert lease.read_bytes() == after
+ assert {path: path.read_bytes() for path in before} == before
+ assert not (runtime / "authority").exists()
diff --git a/tests/control_plane/test_team_plan_authority.py b/tests/control_plane/test_team_plan_authority.py
index 1d9b369166..af9638520e 100644
--- a/tests/control_plane/test_team_plan_authority.py
+++ b/tests/control_plane/test_team_plan_authority.py
@@ -1,4 +1,5 @@
"""Actual Chat entrypoint against a fenced FileAuthorityStore and projection IO."""
+import base64
import hashlib
import json
import os
@@ -20,7 +21,10 @@
from loopx.control_plane.coordination.coordination_state_contract import (
TODO_DOMAIN_READ_RECORD_SCHEMA_VERSION, TODO_DOMAIN_RECORD_FIELDS,
)
-from loopx.control_plane.coordination.local_authority import read_canonical_todos_if_promoted
+from loopx.control_plane.coordination.local_authority import (
+ CanonicalAuthorityRequired,
+ read_canonical_todos_if_promoted,
+)
from loopx.control_plane.coordination.local_authority_shadow_projection import canonical_bytes
from loopx.control_plane.todos import provider_projection
@@ -51,6 +55,63 @@ def canonical_team(tmp_path, request, monkeypatch):
return runtime, state, service, preview
+def test_unpromoted_team_plan_preview_requires_canonical_authority(tmp_path):
+ runtime = tmp_path / "runtime"
+ state = tmp_path / "state.md"
+ state.write_text("# Goal\n\n## Agent Todo\n\n")
+ registry = tmp_path / "registry.json"
+ registry.write_text(json.dumps({
+ "common_runtime_root": str(runtime),
+ "goals": [{
+ "id": "goal-a",
+ "repo": str(tmp_path),
+ "state_file": state.name,
+ "coordination": {
+ "registered_agents": ["alpha"],
+ "agent_model": "peer_v1",
+ },
+ }],
+ }))
+ service = ChatActionService(
+ store=ChatActionStore(runtime / "chat/actions"),
+ registry_path=registry,
+ )
+ before = {
+ path: path.read_bytes()
+ for root in (tmp_path, runtime)
+ for path in root.rglob("*")
+ if path.is_file()
+ }
+
+ with pytest.raises(CanonicalAuthorityRequired) as error:
+ service.preview({
+ "action_kind": "team.plan",
+ "summary": "Assign work",
+ "context": {},
+ "normalized_parameters": {
+ "goal_id": "goal-a",
+ "plan": _agent_plan(),
+ },
+ "idempotency_key": "unpromoted-team-plan",
+ })
+
+ assert error.value.code == "canonical_authority_required"
+ assert error.value.payload["operation"] == "team_plan"
+ assert error.value.payload["write_admitted"] is False
+ assert error.value.payload["decision_read_from_provider"] is False
+ assert error.value.payload["legacy_fallback_used"] is False
+ assert error.value.payload["recovery"] == {
+ "action": "migrate_goal_authority",
+ "backup_required": True,
+ }
+ assert {
+ path: path.read_bytes()
+ for root in (tmp_path, runtime)
+ for path in root.rglob("*")
+ if path.is_file()
+ } == before
+
+
@pytest.mark.parametrize("source", ["state", "registry"])
def test_reviewed_sources_remain_locked_through_native_commit(canonical_team, monkeypatch, source):
"""A compliant writer cannot replace reviewed facts after fingerprinting."""
@@ -254,6 +315,83 @@ def read_after_write(**kwargs):
assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")["provider_revision"] == read["provider_revision"]
+@pytest.mark.parametrize("corrupt_display", [False, True])
+def test_canonical_team_replay_recovers_unavailable_display_without_recreating_work(canonical_team, corrupt_display):
+ from loopx.control_plane.coordination.local_authority import LocalCoordinationAuthorityUnavailable
+ from loopx.control_plane.work_items.team_plan_adapter import TeamPlanCommitError, settle_team_plan
+ from loopx.todos import add_goal_todo
+
+ runtime, state, service, preview = canonical_team
+ arguments = dict(registry_path=service.registry_path, goal_id="goal-a", agent_id=None,
+ proposal={**preview["normalized_parameters"]["plan"], "proposal_id": preview["proposal_id"]},
+ expected_state_fingerprint=preview["expected_state_fingerprint"], basis_agent_id="alpha")
+ committed = settle_team_plan(**arguments)
+ add_goal_todo(registry_path=service.registry_path, goal_id="goal-a", role="agent",
+ text="Later canonical work", operation_id="later-team-work")
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
+ state.unlink()
+ if corrupt_display:
+ state.write_bytes(b"\xffunreadable display")
+
+ def assert_display_unchanged():
+ assert state.read_bytes() == b"\xffunreadable display" if corrupt_display else not state.exists()
+
+ for changed in ({"agent_id": "alpha"},
+ {"proposal": {**arguments["proposal"], "objective": "Changed reviewed intent"}},
+ {"expected_state_fingerprint": "different-reviewed-basis"}):
+ with pytest.raises(TeamPlanCommitError):
+ settle_team_plan(**{**arguments, **changed})
+ assert_display_unchanged()
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == before
+
+ backend = runtime / "authority" / before["source_authority"].replace("_", "-")
+ offline = backend.with_name(backend.name + "-offline")
+ backend.rename(offline)
+ try:
+ with pytest.raises(LocalCoordinationAuthorityUnavailable):
+ settle_team_plan(**arguments)
+ assert_display_unchanged()
+ assert not backend.exists()
+ finally:
+ offline.rename(backend)
+
+ if corrupt_display:
+ with pytest.raises(TeamPlanCommitError) as pending:
+ settle_team_plan(**arguments)
+ assert pending.value.code == "team_plan_projection_pending"
+ assert_display_unchanged()
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == before
+ state.unlink()
+ replay = settle_team_plan(**arguments)
+ assert replay["action"] == "reused"
+ assert replay["lane_todo_ids"] == committed["lane_todo_ids"]
+ assert replay["created_todo_ids"] == []
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == before
+ assert state.read_text().count("Same work") == 2
+ assert state.read_text().count("Later canonical work") == 1
+
+
+@pytest.mark.parametrize("corrupt_display", [False, True])
+def test_fresh_team_plan_cannot_use_unavailable_display_as_reviewed_source(canonical_team, corrupt_display):
+ from loopx.control_plane.work_items.team_plan_adapter import TeamPlanCommitError, settle_team_plan
+
+ runtime, state, service, preview = canonical_team
+ state.unlink()
+ if corrupt_display:
+ state.write_bytes(b"\xffunreadable display")
+ # Even a new fingerprint observing absence cannot replace the missing Goal intent.
+ missing_basis = team_plan_state_fingerprint(
+ registry_path=service.registry_path, goal_id="goal-a", basis_agent_id="alpha")
+ before = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
+ with pytest.raises(TeamPlanCommitError) as error:
+ settle_team_plan(registry_path=service.registry_path, goal_id="goal-a", agent_id=None,
+ proposal={**preview["normalized_parameters"]["plan"], "proposal_id": preview["proposal_id"]},
+ expected_state_fingerprint=missing_basis, basis_agent_id="alpha")
+ assert error.value.code == "team_plan_preview_stale"
+ assert state.read_bytes() == b"\xffunreadable display" if corrupt_display else not state.exists()
+ assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == before
+
+
def test_canonical_change_without_markdown_refresh_invalidates_preview(canonical_team):
runtime, state, service, preview = canonical_team
from loopx.todos import add_goal_todo
@@ -334,7 +472,9 @@ def _capture_free_chat(team, tmp_path):
package = package_root / "loopx"
shutil.copytree(Path(loopx.__file__).parent, package,
ignore=shutil.ignore_patterns("__pycache__"))
- (package / "control_plane/coordination/runtime_shadow_writer_adapter.py").unlink()
+ assert not (
+ package / "control_plane/coordination/runtime_shadow_writer_adapter.py"
+ ).exists()
script = r'''
import json, sys
from pathlib import Path
@@ -444,11 +584,10 @@ def test_capture_free_chat_recovers_committed_projection(canonical_team, capture
assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")["provider_revision"] == committed["provider_revision"]
-@pytest.mark.parametrize("provider", ["file", "sqlite"])
-def test_capture_free_chat_preserves_pre_promotion_receipt(tmp_path, monkeypatch, provider):
- """Imported history remains readable without recreating or overwriting work."""
- from loopx.todos import list_goal_todos
- from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
+@pytest.fixture
+def historical_team(tmp_path, monkeypatch):
+ """Persist the original typed receipt without calling a retired writer."""
+ from loopx.control_plane.effect_runtime import effect_runtime_result
isolate_sqlite_runtime(tmp_path, monkeypatch)
runtime = tmp_path / "runtime"
@@ -460,138 +599,124 @@ def test_capture_free_chat_preserves_pre_promotion_receipt(tmp_path, monkeypatch
"coordination": {"registered_agents": ["alpha"], "agent_model": "peer_v1"}}]}))
service = ChatActionService(store=ChatActionStore(runtime / "chat/actions"), registry_path=registry)
plan = _agent_plan()
- preview = service.preview({"action_kind": "team.plan", "summary": "Assign work", "context": {},
- "normalized_parameters": {"goal_id": "goal-a", "plan": plan}, "idempotency_key": "legacy-plan"})
- applied = service.apply(preview["proposal_id"])["proposal"]
- assert applied["status"] == "applied", applied
- assert "loopx:team-plan:" in state.read_text()
- rows = list_goal_todos(registry_path=registry, goal_id="goal-a")["todos"]
- projection = build_todo_runtime_shadow_projection(goal_id="goal-a", todos=rows, handoff_mode="soft_claim")
- initialize_canonical_authority(runtime, "goal-a", projection, state_path=state, provider=provider)
+ historical_basis = "historical-team-plan-basis"
+ preview = service.store.create_preview(
+ action_kind="team.plan", summary="Assign work",
+ normalized_parameters={"goal_id": "goal-a", "plan": plan}, context={},
+ expected_state_fingerprint=historical_basis,
+ permission_classification="owner_confirmation_required",
+ validation_evidence=[], available_transitions=["apply"],
+ idempotency_key="legacy-plan",
+ )
+ planned = effect_runtime_result("work_items.team_plan.plan", {
+ "goal_id": "goal-a",
+ "plan": {**plan, "proposal_id": preview["proposal_id"]},
+ "actor_agent_id": None,
+ "registered_agents": ["alpha"],
+ "supported_action_kinds": ["implement"],
+ "observed_at": "2026-09-01T00:00:00Z",
+ "expected_state_fingerprint": historical_basis,
+ "current_state_fingerprint": historical_basis,
+ "todos": [],
+ })
+ receipt = planned["receipt"]
+ marker_id = receipt["operation_id"].split(":", 1)[1]
+ encoded_receipt = base64.b64encode(
+ json.dumps(receipt, separators=(",", ":")).encode()
+ ).decode()
+ state.write_text(
+ f"# Goal\n\n\n\n"
+ "## Agent Todo\n\n"
+ )
+ return runtime, state, registry, service, preview, planned
+
+
+@pytest.mark.parametrize("provider", ["file", "sqlite"])
+def test_capture_free_chat_preserves_pre_promotion_receipt(
+ historical_team, tmp_path, provider,
+):
+ """Original history is read-only before import and after newer writes."""
+ from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
+
+ runtime, state, registry, service, preview, planned = historical_team
invoke = _capture_free_chat((runtime, state, service, preview), tmp_path)
+ before_import = state.read_bytes()
+ recovered = invoke("historical")
+ assert recovered["action"] == "reused"
+ assert recovered["lane_todo_ids"] == planned["result"]["lane_todo_ids"]
+ assert state.read_bytes() == before_import
+ assert not (runtime / "authority").exists()
+
+ projection = build_todo_runtime_shadow_projection(
+ goal_id="goal-a", todos=planned["todos"], handoff_mode="soft_claim",
+ )
+ initialize_canonical_authority(runtime, "goal-a", projection, state_path=state, provider=provider)
later = invoke("preview", text="New work after import")
- assert invoke("apply", proposal=later["proposal_id"])["proposal"]["status"] == "applied"
+ later_apply = invoke("apply", proposal=later["proposal_id"])
+ assert later_apply["proposal"]["status"] == "applied", later_apply["proposal"].get("failure")
current = read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a")
display = state.read_bytes()
replay = invoke("historical")
assert replay["action"] == "reused"
- assert replay["lane_todo_ids"] == applied["receipt"]["resource_ids"]["lane_todo_ids"]
+ assert replay["lane_todo_ids"] == planned["result"]["lane_todo_ids"]
assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == current
assert state.read_bytes() == display
- # Simulate promotion finishing after the first mode observation but before
- # acquiring source locks; its selected backend subsequently goes offline.
+ # A selected but missing provider still refuses historical readback.
+ # Use the actual failed store, not a mocked unpromoted observation.
from loopx.control_plane.coordination.local_authority import LocalCoordinationAuthorityUnavailable
from loopx.control_plane.work_items import team_plan_adapter
- read_authority = team_plan_adapter.read_canonical_todos_if_promoted
- observed = []
-
- def promotion_race(**kwargs):
- observed.append(True)
- return None if len(observed) == 1 else read_authority(**kwargs)
backend = runtime / "authority" / current["source_authority"].replace("_", "-")
offline = backend.with_name(backend.name + "-offline")
backend.rename(offline)
try:
- with monkeypatch.context() as race:
- race.setattr(team_plan_adapter, "read_canonical_todos_if_promoted", promotion_race)
- saved = service.store.load(preview["proposal_id"])
- with pytest.raises(LocalCoordinationAuthorityUnavailable):
- team_plan_adapter.settle_team_plan(
- registry_path=registry, goal_id="goal-a", agent_id=None,
- proposal={**saved["normalized_parameters"]["plan"], "proposal_id": preview["proposal_id"]},
- expected_state_fingerprint=saved["expected_state_fingerprint"], basis_agent_id="alpha")
- assert len(observed) == 2
+ saved = service.store.load(preview["proposal_id"])
+ with pytest.raises(LocalCoordinationAuthorityUnavailable):
+ team_plan_adapter.settle_team_plan(
+ registry_path=registry, goal_id="goal-a", agent_id=None,
+ proposal={**saved["normalized_parameters"]["plan"], "proposal_id": preview["proposal_id"]},
+ expected_state_fingerprint=saved["expected_state_fingerprint"], basis_agent_id="alpha")
assert state.read_bytes() == display and not backend.exists()
finally:
offline.rename(backend)
assert read_canonical_todos_if_promoted(runtime_root=runtime, goal_id="goal-a") == current
-@pytest.mark.skipif(os.name != "posix", reason="real bootstrap crash barrier uses select on a pipe")
-def test_legacy_chat_replay_survives_interrupted_shadow_bootstrap(tmp_path, monkeypatch):
- """A stopped writer must not strand an already-published plan receipt."""
- import select
- from loopx.control_plane.work_items import team_plan_adapter
- from loopx.control_plane.coordination.runtime_shadow import build_runtime_shadow_source_snapshot
- from loopx.control_plane.coordination.shadow_management import read_shadow_management_state
- from loopx.control_plane.coordination.coordination_state_contract_generated import (
- COORDINATION_RUNTIME_SHADOW_BOOTSTRAP_REQUEST_SCHEMA,
- )
+@pytest.mark.parametrize("conflict", ["actor", "intent", "basis"])
+def test_pre_cutoff_team_receipt_rejects_changed_identity(historical_team, conflict):
+ from loopx.control_plane.effect_runtime import EffectRuntimeRejected
+ from loopx.control_plane.work_items.team_plan_adapter import apply_team_plan
- runtime = tmp_path / "runtime"
- state = tmp_path / "state.md"
- state.write_text("# Goal\n\n## Agent Todo\n\n")
- goal = {"id": "goal-a", "repo": str(tmp_path), "state_file": "state.md",
- "coordination": {"registered_agents": ["alpha"], "agent_model": "peer_v1"}}
- registry = tmp_path / "registry.json"
- registry.write_text(json.dumps({"common_runtime_root": str(runtime), "goals": [goal]}))
- service = ChatActionService(store=ChatActionStore(runtime / "chat/actions"), registry_path=registry)
- preview = service.preview({"action_kind": "team.plan", "summary": "Assign work", "context": {},
- "normalized_parameters": {"goal_id": "goal-a", "plan": _agent_plan()},
- "idempotency_key": "lost-original-response"})
- write = team_plan_adapter.write_captured_todo_state
-
- def lose_response(*args, **kwargs):
- write(*args, **kwargs)
- raise OSError("response lost after publication")
-
- with monkeypatch.context() as failed_response:
- failed_response.setattr(team_plan_adapter, "write_captured_todo_state", lose_response)
- assert service.apply(preview["proposal_id"])["proposal"]["status"] == "failed"
- assert state.read_text().count("loopx:team-plan:") == 1
- state.write_text(state.read_text() + "\nLater owner data must survive.\n")
- original = state.read_bytes()
- projection, snapshot = build_runtime_shadow_source_snapshot(
- goal=goal, runtime_root=runtime, state_path=state, registry_path=registry)
- request = {"schema_version": COORDINATION_RUNTIME_SHADOW_BOOTSTRAP_REQUEST_SCHEMA,
- "runtime_root": str(runtime), "goal_id": "goal-a", "operation_id": "bootstrap:plan-recovery",
- "source_version": "source:before-crash", "projection": projection, "source_snapshot": snapshot}
- repo = Path(__file__).resolve().parents[2]
- child = subprocess.Popen(["node", "--no-warnings", "--experimental-strip-types",
- str(repo / "tests/control_plane_ts/fixtures/shadow_management_crash_worker.ts"),
- "bootstrap-public", json.dumps(request), "bootstrap_prepared"],
- cwd=repo, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
- try:
- ready, _, _ = select.select([child.stdout], [], [], 15)
- assert ready, "bootstrap did not reach its real filesystem barrier"
- assert child.stdout.readline().strip() == "ready:bootstrap_prepared"
- finally:
- child.kill()
- child.communicate(timeout=10)
- pending = read_shadow_management_state(runtime, "goal-a")
- assert pending["status"] == "bootstrapping"
- # Restart from an isolated package with the old capture producer absent.
- invoke = _capture_free_chat((runtime, state, service, preview), tmp_path)
- replay = invoke("historical")
- assert replay["action"] == "reused" and replay["created_todo_ids"] == []
- recovered = service.apply(preview["proposal_id"])["proposal"]
- assert recovered["status"] == "applied", recovered
- assert replay["lane_todo_ids"] == recovered["receipt"]["resource_ids"]["lane_todo_ids"]
- assert state.read_bytes() == original
- assert read_shadow_management_state(runtime, "goal-a") == pending
-
- # Recovery is tied to the exact intent, actor and original reviewed basis.
- saved = service.store.load(preview["proposal_id"])
- original_plan = {**saved["normalized_parameters"]["plan"], "proposal_id": preview["proposal_id"]}
- for changed in (
- {"proposal": {**original_plan, "objective": "Changed intent"}},
- {"agent_id": "alpha"},
- {"expected_state_fingerprint": "different-basis"},
- ):
- with pytest.raises(team_plan_adapter.EffectRuntimeRejected, match="identity mismatch"):
- team_plan_adapter.settle_team_plan(**{
- "registry_path": registry, "goal_id": "goal-a", "agent_id": None,
- "proposal": original_plan, "expected_state_fingerprint": saved["expected_state_fingerprint"],
- "basis_agent_id": "alpha", **changed,
- })
- assert state.read_bytes() == original
- # A different plan still needs normal admission; recovery cannot restart work.
- fresh = service.preview({"action_kind": "team.plan", "summary": "New work", "context": {},
- "normalized_parameters": {"goal_id": "goal-a", "plan": _agent_plan()},
- "idempotency_key": "new-request-during-hold"})
- refused = service.apply(fresh["proposal_id"])["proposal"]
- assert refused["status"] == "failed", refused
- assert state.read_bytes() == original
- assert read_shadow_management_state(runtime, "goal-a") == pending
+ runtime, state, registry, _service, preview, _planned = historical_team
+ plan = {**preview["normalized_parameters"]["plan"], "proposal_id": preview["proposal_id"]}
+ actor = "alpha" if conflict == "actor" else None
+ if conflict == "intent":
+ plan = {**plan, "objective": "Changed original intent"}
+ basis = "changed-basis" if conflict == "basis" else preview["expected_state_fingerprint"]
+ before = state.read_bytes()
+ with pytest.raises(EffectRuntimeRejected, match="identity mismatch"):
+ apply_team_plan(registry_path=registry, goal_id="goal-a", agent_id=actor,
+ proposal=plan, expected_state_fingerprint=basis)
+ assert state.read_bytes() == before
+ assert not (runtime / "authority").exists()
+
+
+@pytest.mark.parametrize("history", ["missing", "malformed", "duplicate"])
+def test_pre_cutoff_team_receipt_never_recreates_work(historical_team, history):
+ from loopx.control_plane.work_items.team_plan_adapter import apply_team_plan
+
+ runtime, state, registry, _service, preview, _planned = historical_team
+ original = state.read_text()
+ marker = next(line for line in original.splitlines() if line.startswith("",
- "",
- ]
-
-
-@pytest.mark.parametrize(
- "intent, initial, expected",
- [
- ({"clear_claim": True}, "claimed_by=agent-a", {"claimed_by": None}),
- ({"claimed_by": "agent-b"}, "claimed_by=agent-a", {"claimed_by": "agent-b"}),
- (
- {"clear_user_binding": True},
- "bound_agent=agent-a",
- {"bound_agent": None, "goal_bound": None},
- ),
- (
- {"goal_bound": True},
- "bound_agent=agent-a",
- {"bound_agent": None, "goal_bound": True},
- ),
- ({"excluded_agents": []}, "excluded_agents=agent-a", {"excluded_agents": []}),
- ({"clear_blocks_agent": True}, "blocks_agent=agent-a", {"blocks_agent": None}),
- ({"clear_global_gate": True}, "global_gate=true", {"global_gate": None}),
- (
- {"clear_resume_when": True},
- "resume_when=monitor_changed:todo_monitor resume_monitor_generation=4",
- {"resume_when": None, "resume_monitor_generation": None},
- ),
- (
- {"resume_when": "todo_done:todo_dependency"},
- "resume_monitor_generation=4",
- {
- "resume_when": "todo_done:todo_dependency",
- "resume_monitor_generation": None,
- },
- ),
- (
- {
- "resume_when": "monitor_changed:todo_monitor",
- "resume_monitor_generation": 0,
- },
- "",
- {
- "resume_when": "monitor_changed:todo_monitor",
- "resume_monitor_generation": 0,
- },
- ),
- (
- {"status": "done", "no_followup": True},
- "",
- {
- "status": "done",
- "no_followup": True,
- "completion_continuation": "no_followup",
- },
- ),
- (
- {"status": "done", "successor_todo_ids": ["todo_successor"]},
- "",
- {"completion_continuation": "successor"},
- ),
- (
- {"status": "open"},
- "completed_at=2026-08-01T00%3A00%3A00Z",
- {"status": "open"},
- ),
- (
- {"note": "", "required_capabilities": []},
- "note=keep required_capabilities=network",
- {"required_capabilities": []},
- ),
- (
- {"monitor_metadata": {"consecutive_no_change": 0, "watch_only": False}},
- "watch_only=true",
- {"watch_only": False},
- ),
- ],
-)
-def test_field_omission_clear_and_completion_semantics(intent, initial, expected):
- lines = _lines(initial)
- result = apply_todo_update_to_lines(
- lines, todo_id="todo_field_test", updated_at=AT, **intent
- )
- for key, value in expected.items():
- # Legacy result fields retain the Markdown codec's scalar representation.
- actual = (
- normalize_todo_watch_only(result[key])
- if key == "watch_only"
- else result[key]
- )
- assert actual == value
- block = find_todo_block(lines, todo_id="todo_field_test")[4]
- assert block["todo_id"] == "todo_field_test"
- if intent.get("status") == "done":
- assert block["completed_at"] == AT
- if intent.get("status") == "open":
- assert not block.get("completed_at")
- if intent.get("note") == "":
- assert block["note"] == "keep" # Empty note historically means omit here.
-
-
-def test_noop_does_not_advance_updated_at_or_touch_unrelated_lines():
- lines = _lines("updated_at=2026-08-01T00%3A00%3A00Z")
- # Stabilize representation once; the next call must be an actual no-op.
- apply_todo_update_to_lines(lines, todo_id="todo_field_test", updated_at=AT)
- before = deepcopy(lines)
- result = apply_todo_update_to_lines(
- lines, todo_id="todo_field_test", updated_at="2026-09-02T00:00:00Z"
- )
- assert not result["changed"]
- assert lines == before
-
-
-@pytest.mark.parametrize(
- "intent, initial, error",
- [
- ({"status": "invalid"}, "", "todo status"),
- ({"claim_only": True}, "claimed_by=agent-a", None),
- (
- {"claim_only": True, "claimed_by": "agent-b"},
- "claimed_by=agent-a",
- "already claimed_by",
- ),
- (
- {"status": "deferred", "clear_resume_when": True},
- "",
- "cannot clear resume_when",
- ),
- (
- {
- "status": "done",
- "no_followup": True,
- "successor_todo_ids": ["todo_successor"],
- },
- "",
- "both no_followup",
- ),
- ],
-)
-def test_invalid_lifecycle_field_combinations_are_rejected(intent, initial, error):
- lines = _lines(initial)
- if error is None:
- apply_todo_update_to_lines(
- lines, todo_id="todo_field_test", updated_at=AT, **intent
- )
- else:
- before = deepcopy(lines)
- with pytest.raises(ValueError, match=error):
- apply_todo_update_to_lines(
- lines, todo_id="todo_field_test", updated_at=AT, **intent
- )
- assert lines == before
-
-
-def test_completion_field_plan_has_one_crossing_and_retires_metadata_rpc(monkeypatch):
- from loopx.control_plane import effect_runtime
- from loopx.control_plane.todos import line_update
-
- actual = effect_runtime.effect_runtime_result
- calls = []
-
- def traced(method, params):
- calls.append(method)
- return actual(method, params)
-
- monkeypatch.setattr(line_update, "effect_runtime_result", traced)
- result = apply_todo_update_to_lines(
- _lines(),
- todo_id="todo_field_test",
- updated_at=AT,
- status="done",
- no_followup=True,
- )
- assert result["completion_continuation"] == "no_followup"
- assert calls == ["todo.field_update.plan"]
- with pytest.raises(
- effect_runtime.EffectRuntimeRejected, match="[Uu]nknown|[Uu]nsupported"
- ):
- actual("todo.completion_state.metadata_updates", {})
-
-
-def test_rejected_plan_does_not_partially_change_text_or_checkbox():
- lines = _lines()
- before = deepcopy(lines)
- with pytest.raises(ValueError, match="both no_followup"):
- apply_todo_update_to_lines(
- lines,
- todo_id="todo_field_test",
- updated_at=AT,
- text="Do not commit",
- status="done",
- no_followup=True,
- successor_todo_ids=["todo_next"],
- )
- assert lines == before
-
-
-@pytest.mark.parametrize(
- "resume, clear, expected, error",
- [
- (None, False, "todo_done:todo_old", None),
- ("", False, "todo_done:todo_old", None),
- ("\u001c\t \u2003", False, "todo_done:todo_old", None),
- ("\u2003TODO_DONE:todo_dependency\u2003", False, "todo_done:todo_dependency", None),
- ("resume_at:2026-09-01T08:00:00+08:00", False, "resume_at:2026-09-01T00:00:00Z", None),
- ("", True, None, None),
- ("todo_done:todo_dependency", True, None, "either resume_when"),
- ("unknown:todo_dependency", False, None, "supported"),
- ("resume_at:2026-09-01T08:00:00", False, None, "supported"),
- ],
-)
-def test_compatibility_field_update_preserves_resume_authoring(resume, clear, expected, error):
- lines = _lines("resume_when=todo_done:todo_old")
- before = deepcopy(lines)
- kwargs = dict(todo_id="todo_field_test", updated_at=AT,
- resume_when=resume, clear_resume_when=clear)
- if error:
- with pytest.raises(ValueError, match=error):
- apply_todo_update_to_lines(lines, text="Do not commit", **kwargs)
- assert lines == before
- else:
- result = apply_todo_update_to_lines(lines, **kwargs)
- assert result["resume_when"] == expected
-
-
-def test_compatibility_resume_update_uses_the_whole_field_owner(monkeypatch):
- from loopx.control_plane.todos import line_update, resume_condition
-
- actual = line_update.effect_runtime_result
- calls = []
-
- def traced(method, params):
- calls.append(method)
- return actual(method, params)
-
- for module in (line_update, resume_condition):
- monkeypatch.setattr(module, "effect_runtime_result", traced)
- result = apply_todo_update_to_lines(
- _lines(), todo_id="todo_field_test", updated_at=AT,
- resume_when="TODO_DONE:todo_dependency",
- )
- assert result["resume_when"] == "todo_done:todo_dependency"
- assert calls == ["todo.field_update.plan"]
-
-
-@pytest.mark.parametrize("exclusions", [[], ["!!!"]])
-def test_removed_policy_repair_requires_a_valid_exclusion(exclusions):
- lines = _lines("continuation_policy=primary_review")
- before = deepcopy(lines)
- with pytest.raises(ValueError, match="repair it explicitly"):
- apply_todo_update_to_lines(
- lines,
- todo_id="todo_field_test",
- updated_at=AT,
- continuation_policy="independent_handoff",
- excluded_agents=exclusions,
- )
- assert lines == before
-
-
-def test_field_planner_covers_the_existing_monitor_codec_fields():
- from loopx.control_plane.effect_runtime import effect_runtime_result
- from loopx.control_plane.todos.contract import TODO_MONITOR_METADATA_FIELDS
-
- clears = dict.fromkeys(TODO_MONITOR_METADATA_FIELDS)
- plan = effect_runtime_result(
- "todo.field_update.plan",
- {
- "schema_version": "loopx_todo_field_update_request_v0",
- "todo": {"todo_id": "todo_field_test", "status": "open"},
- "intent": {"monitor_metadata": clears},
- "updated_at": AT,
- },
- )
- assert plan["metadata_updates"] == {
- "todo_id": "todo_field_test",
- "status": "open",
- **clears,
- }
diff --git a/tests/control_plane/test_todo_lifecycle_authority_config.py b/tests/control_plane/test_todo_lifecycle_authority_config.py
index 8e55b1197c..72c7ed9852 100644
--- a/tests/control_plane/test_todo_lifecycle_authority_config.py
+++ b/tests/control_plane/test_todo_lifecycle_authority_config.py
@@ -5,8 +5,12 @@
import pytest
+from canonical_authority_fixture import initialize_canonical_authority
from loopx.cli import main
from loopx.configure_goal import configure_goal
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
from loopx.todos import add_goal_todo
@@ -27,6 +31,7 @@ def _registry(tmp_path: Path) -> Path:
registry.write_text(
json.dumps(
{
+ "common_runtime_root": str(tmp_path / "runtime"),
"goals": [
{
"id": GOAL_ID,
@@ -45,6 +50,16 @@ def _registry(tmp_path: Path) -> Path:
),
encoding="utf-8",
)
+ initialize_canonical_authority(
+ tmp_path / "runtime",
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=[],
+ handoff_mode="soft_claim",
+ ),
+ state_path=tmp_path / "ACTIVE_GOAL_STATE.md",
+ )
return registry
@@ -225,9 +240,8 @@ def test_todo_cli_emits_delegated_override_receipt(
assert exit_code == 0
payload = json.loads(capsys.readouterr().out)
- assert payload["mutation_authority"]["mode"] == (
+ assert payload["mutation_authority"]["authority_mode"] == (
"delegated_orchestration_override"
)
- assert payload["mutation_authority"]["authority_reason"] == (
- "Verified the result before orchestration closeout."
- )
+ assert payload["mutation_authority"]["code"] == "terminal_transition"
+ assert payload["source_authority"] == "file_v0"
diff --git a/tests/control_plane/test_todo_machine_region.py b/tests/control_plane/test_todo_machine_region.py
index a2ff949f65..0e9bb4b926 100644
--- a/tests/control_plane/test_todo_machine_region.py
+++ b/tests/control_plane/test_todo_machine_region.py
@@ -36,6 +36,31 @@ def test_legacy_adoption_preserves_non_generated_suffix(tail: str, newline: str)
).markdown == projected.markdown
+@pytest.mark.parametrize("newline", ["\n", "\r\n"])
+@pytest.mark.parametrize("indent", [" ", "\t"])
+def test_legacy_folded_task_adoption_replaces_its_complete_block(newline, indent):
+ from loopx.control_plane.todos.active_state_todo_parser import parse_todo_source
+
+ task = ("- [ ] A folded task with\n" + indent + "a retained identity.\n" +
+ " \n")
+ tail = " Indented narrative after the task block.\n\n```markdown\n## Agent Todo\n- [ ] Example only.\n```\n"
+ source = (_source() + task + "\n" + tail).replace("\n", newline)
+ record = {"schema_version": "todo_item_v0", "role": "agent", "index": 1,
+ "source_section": "Agent Todo", "archive_state": "active",
+ "status": "open", "done": False, "task_class": "advancement_task",
+ "todo_id": "todo_folded", "text": "A folded task with a retained identity."}
+ projected = render_canonical_todo_sections(source, [record], provider_revision="rev-1")
+ assert projected.markdown.endswith(tail.replace("\n", newline))
+ assert projected.markdown.count("todo_id=todo_folded") == 1
+ assert projected.markdown.count("A folded task with a retained identity.") == 1
+ readback, _, _ = parse_todo_source(projected.markdown)
+ assert readback["agent"][0]["text"] == "A folded task with a retained identity."
+ assert readback["agent"][0]["todo_id"] == "todo_folded"
+ assert render_canonical_todo_sections(
+ projected.markdown, [record], provider_revision="rev-1"
+ ).markdown == projected.markdown
+
+
@pytest.mark.parametrize("example", [
"```markdown\n## Agent Todo\n- [ ] Example only.\n```\n",
"~~~~markdown\n## Agent Todo\n- [ ] Example only.\n~~~~\n",
diff --git a/tests/control_plane/test_todo_mutation_authority.py b/tests/control_plane/test_todo_mutation_authority.py
index b8f4100b41..3c1a169667 100644
--- a/tests/control_plane/test_todo_mutation_authority.py
+++ b/tests/control_plane/test_todo_mutation_authority.py
@@ -5,22 +5,26 @@
import pytest
-from loopx.cli import main as cli_main
+from canonical_authority_fixture import initialize_canonical_authority
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
+from loopx.control_plane.coordination.local_authority import (
+ LocalCoordinationAuthorityRejection, LocalCoordinationAuthorityUnavailable,
+)
from loopx.control_plane.scheduler.monitor_poll_writeback import (
write_monitor_poll_todo_state,
)
-import loopx.control_plane.todos.mutation_authority as mutation_authority_module
-import loopx.control_plane.work_items.task_lease as task_lease_module
+from loopx.status import parse_active_state_todos
+import loopx.control_plane.todos.provider_terminal_lifecycle as terminal_provider
from loopx.control_plane.work_items.task_lease import (
- TaskLeaseError,
- acquire_task_lease,
- release_task_lease,
+ acquire_task_lease, inspect_task_lease,
)
-from loopx.status import parse_active_state_todos
+from loopx.todos import list_goal_todos, archive_completed_todos
+from canonical_authority_fixture import isolate_sqlite_runtime
from loopx.todos import (
add_goal_todo,
complete_goal_todo,
- supersede_goal_todo,
update_goal_todo,
)
@@ -36,6 +40,8 @@ def _write_fixture(
*,
multi_agent: bool = True,
lifecycle_authority: list[dict] | None = None,
+ handoff_mode: str = "soft_claim",
+ provider: str = "file",
) -> tuple[Path, Path]:
repo = tmp_path / "repo"
repo.mkdir()
@@ -86,13 +92,20 @@ def _write_fixture(
),
encoding="utf-8",
)
+ initialize_canonical_authority(
+ tmp_path / "runtime",
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=[],
+ handoff_mode=handoff_mode,
+ ),
+ state_path=state,
+ provider=provider,
+ )
return registry, state
-def _lease_path(tmp_path: Path, todo_id: str) -> Path:
- return tmp_path / "runtime" / "goals" / GOAL_ID / "task-leases" / f"{todo_id}.json"
-
-
def _agent_todo(state: Path, todo_id: str) -> dict:
todos = parse_active_state_todos(state.read_text(encoding="utf-8"))
return next(
@@ -196,83 +209,6 @@ def test_continuous_monitor_accepts_resume_condition_bound(tmp_path: Path) -> No
assert item["resume_when"] == "pr_merged:#3083"
-def test_multi_agent_update_requires_actor_and_is_atomic(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = _add_agent_todo(registry)
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="requires --agent-id"):
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- note="This must not be written without an attributed actor.",
- )
-
- assert state.read_text(encoding="utf-8") == before
-
-
-def test_registered_actor_can_correct_unclaimed_todo_without_claim(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = _add_agent_todo(registry, claimed_by=None)
- result = update_goal_todo(
- registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT, text="Correct unclaimed copy", note="Correct note",
- )
- assert result["mutation_authority"]["mode"] == "registered_peer_actor"
- corrected = _agent_todo(state, todo["todo_id"])
- assert corrected["text"] == "[P1] Correct unclaimed copy"
- assert corrected["note"] == "Correct note"
- assert not corrected.get("claimed_by")
-
-
-def test_excluded_actor_cannot_mutate_unclaimed_todo(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = _add_agent_todo(
- registry,
- claimed_by=None,
- excluded_agents=[AUTHOR_AGENT],
- )
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="is excluded from mutating"):
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- note="An excluded author cannot rewrite review work.",
- )
-
- assert state.read_text(encoding="utf-8") == before
-
-
-def test_idempotent_add_preserves_existing_executor_exclusions(
- tmp_path: Path,
-) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = _add_agent_todo(
- registry,
- text="Independently review one exact PR head.",
- claimed_by=None,
- excluded_agents=[AUTHOR_AGENT],
- )
-
- replay = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Independently review one exact PR head.",
- task_class="advancement_task",
- )
-
- assert replay["todo_id"] == todo["todo_id"]
- assert replay["already_exists"] is True
- assert replay["metadata_updated"] is False
- assert replay["excluded_agents"] == [AUTHOR_AGENT]
- assert _agent_todo(state, todo["todo_id"])["excluded_agents"] == [AUTHOR_AGENT]
-
-
def test_unresolved_decision_scope_is_not_a_local_claim_gate(tmp_path: Path) -> None:
"""Characterize the local writer before shared revision publishers exist.
@@ -314,44 +250,6 @@ def test_unresolved_decision_scope_is_not_a_local_claim_gate(tmp_path: Path) ->
assert item["claimed_by"] == AUTHOR_AGENT
-@pytest.mark.parametrize("command", ["update", "complete", "supersede"])
-def test_non_owner_cannot_mutate_claimed_todo(
- tmp_path: Path,
- command: str,
-) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = _add_agent_todo(registry, claimed_by=REVIEW_AGENT)
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="claimed_by='codex-review'"):
- if command == "update":
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- note="unauthorized",
- )
- elif command == "complete":
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- evidence="unauthorized",
- )
- else:
- supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- reason="unauthorized",
- )
-
- assert state.read_text(encoding="utf-8") == before
-
-
def test_completion_policy_unicode_parity_through_public_facade(
tmp_path: Path,
) -> None:
@@ -402,13 +300,13 @@ def test_completion_policy_unicode_parity_through_public_facade(
evidence="\ufeff",
no_followup=True,
)
- assert accepted["self_merged"] is True
+ assert accepted["completion_policy"]["self_merged"] is True
def test_completion_policy_errors_do_not_preempt_actor_or_lease_fences(
tmp_path: Path,
) -> None:
- registry, state = _write_fixture(tmp_path)
+ registry, state = _write_fixture(tmp_path, handoff_mode="hard_lease")
claimed_by_other = _add_agent_todo(
registry,
text="Keep actor authority ahead of policy diagnostics.",
@@ -416,7 +314,7 @@ def test_completion_policy_errors_do_not_preempt_actor_or_lease_fences(
)
before = state.read_text(encoding="utf-8")
- with pytest.raises(ValueError, match="claimed_by='codex-review'"):
+ with pytest.raises(LocalCoordinationAuthorityRejection) as denied:
complete_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -426,251 +324,57 @@ def test_completion_policy_errors_do_not_preempt_actor_or_lease_fences(
evidence="\u0085",
no_followup=True,
)
+ assert denied.value.code == "claim_owner_mismatch"
assert state.read_text(encoding="utf-8") == before
leased = _add_agent_todo(
registry,
text="Keep the task lease ahead of policy diagnostics.",
- )
- lease_key = "policy-priority-instance"
- acquire_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=leased["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key=lease_key,
- ttl_seconds=600,
+ claimed_by=None,
)
before = state.read_text(encoding="utf-8")
- with pytest.raises(TaskLeaseError) as stale_lease:
+ with pytest.raises(LocalCoordinationAuthorityRejection) as missing_lease:
complete_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
todo_id=leased["todo_id"],
agent_id=AUTHOR_AGENT,
- task_lease_idempotency_key=lease_key,
- task_lease_expected_version=0,
self_merged=True,
evidence="\u0085",
no_followup=True,
)
- assert stale_lease.value.code == "version_mismatch"
- assert state.read_text(encoding="utf-8") == before
-
-
-def test_owner_actor_update_returns_typed_receipt(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = _add_agent_todo(registry)
-
- result = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- note="owner-attributed update",
- )
-
- assert result["mutation_authority"] == {
- "schema_version": "todo_mutation_authority_v0",
- "command": "update",
- "mode": "registered_peer_actor",
- "actor_agent_id": AUTHOR_AGENT,
- "todo_id": todo["todo_id"],
- "claim_owner": AUTHOR_AGENT,
- "registered_agent_count": 3,
- }
- assert _agent_todo(state, todo["todo_id"])["note"] == "owner-attributed update"
-
-
-@pytest.mark.parametrize("status", ["deferred", "blocked", "done"])
-def test_claim_rejection_preserves_status_suffix(
- tmp_path: Path,
- status: str,
-) -> None:
- """Claim rejections keep the legacy todo_id/status diagnostic suffix."""
-
- registry, state = _write_fixture(tmp_path)
- if status == "deferred":
- todo = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Wait for external capacity before claim.",
- status="deferred",
- resume_when="capacity_available:network",
- )
- else:
- todo = _add_agent_todo(registry)
- if status == "done":
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- evidence="finished before the contested claim",
- )
- else:
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- status=status,
- )
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError) as excinfo:
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claim_only=True,
- claimed_by=AUTHOR_AGENT,
- agent_id=AUTHOR_AGENT,
- )
-
- assert str(excinfo.value) == (
- f"todo claim requires status=open; todo_id '{todo['todo_id']}' "
- f"is status='{status}'"
- )
+ assert missing_lease.value.code == "handoff_mode_requires_lease"
assert state.read_text(encoding="utf-8") == before
-def test_claim_user_todo_rejection_preserves_binding_hint(
- tmp_path: Path,
-) -> None:
- """Claiming a user Todo keeps the legacy --bound-agent remediation hint."""
-
+def test_capability_binding_cannot_be_rebound_by_duplicate_add(tmp_path: Path) -> None:
registry, state = _write_fixture(tmp_path)
- todo = add_goal_todo(
+ original = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
- role="user",
- text="Owner reviews the delivered change.",
- task_class="user_action",
- bound_agent=AUTHOR_AGENT,
- )
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError) as excinfo:
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claim_only=True,
- claimed_by=AUTHOR_AGENT,
- agent_id=AUTHOR_AGENT,
- )
-
- assert str(excinfo.value) == (
- "claimed_by is execution ownership for agent todos, not a user-todo "
- "binding; use --bound-agent or --goal-bound"
- )
- assert state.read_text(encoding="utf-8") == before
-
-
-def test_claim_rejection_preserves_removed_policy_repair_hint(
- tmp_path: Path,
-) -> None:
- """Claiming a removed-policy Todo keeps the legacy repair instructions."""
-
- registry, state = _write_fixture(tmp_path)
- todo = _add_agent_todo(registry, claimed_by=None)
- text = state.read_text(encoding="utf-8")
- marker = f"todo_id={todo['todo_id']} "
- assert marker in text
- state.write_text(
- text.replace(
- marker,
- f"{marker}removed_continuation_policy=primary_review ",
- 1,
- ),
- encoding="utf-8",
+ role="agent",
+ text="Advance the admitted issue-fix route.",
+ task_class="advancement_task",
+ capability_binding_ref="issue-fix:feasibility-a1b2c3d4",
)
before = state.read_text(encoding="utf-8")
- with pytest.raises(ValueError) as excinfo:
- update_goal_todo(
+ with pytest.raises(
+ Exception,
+ match="different capability_binding_ref",
+ ):
+ add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claim_only=True,
- claimed_by=AUTHOR_AGENT,
- agent_id=AUTHOR_AGENT,
+ role="agent",
+ text="Advance the admitted issue-fix route.",
+ task_class="advancement_task",
+ capability_binding_ref="issue-fix:feasibility-e5f6a7b8",
)
- assert str(excinfo.value) == (
- f"todo_id '{todo['todo_id']}' uses removed continuation_policy="
- "primary_review; repair it before claiming"
- )
assert state.read_text(encoding="utf-8") == before
-
-
-def test_claim_archived_todo_rejection_preserves_not_found_message(
- tmp_path: Path,
-) -> None:
- """Archived-Todo claim rejections keep the legacy not-found message."""
-
- registry, _state = _write_fixture(tmp_path)
-
- with pytest.raises(ValueError) as excinfo:
- mutation_authority_module.authorize_todo_lifecycle_mutation(
- registry_path=registry,
- goal_id=GOAL_ID,
- command="claim",
- todo={
- "todo_id": "todo_archivedprobe",
- "role": "agent",
- "status": "open",
- "archive_state": "archive",
- },
- actor_agent_id=AUTHOR_AGENT,
- requested_claimed_by=AUTHOR_AGENT,
- )
-
- assert str(excinfo.value) == (
- "todo_id 'todo_archivedprobe' was not found in active user or agent todos"
- )
-
-
-def test_claim_role_mismatch_rejection_preserves_not_found_message(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
-) -> None:
- """Role-mismatch claim rejections keep the legacy not-found message."""
-
- registry, _state = _write_fixture(tmp_path)
-
- def fake_typescript_decision(_command: str, _payload: dict) -> dict:
- return {
- "status": "rejected",
- "reason_code": "todo_role_mismatch",
- "reason": "Todo does not have the requested role",
- }
-
- monkeypatch.setattr(
- mutation_authority_module,
- "effect_runtime_result",
- fake_typescript_decision,
- )
-
- with pytest.raises(ValueError) as excinfo:
- mutation_authority_module.authorize_todo_lifecycle_mutation(
- registry_path=registry,
- goal_id=GOAL_ID,
- command="claim",
- todo={
- "todo_id": "todo_rolemismatchprobe",
- "role": "agent",
- "status": "open",
- },
- actor_agent_id=AUTHOR_AGENT,
- requested_claimed_by=AUTHOR_AGENT,
- )
-
- assert str(excinfo.value) == (
- "todo_id 'todo_rolemismatchprobe' was not found in active user or agent todos"
+ assert _agent_todo(state, original["todo_id"])["capability_binding_ref"] == (
+ "issue-fix:feasibility-a1b2c3d4"
)
@@ -722,780 +426,46 @@ def test_capability_binding_follows_generated_agent_successor(tmp_path: Path) ->
assert successor["capability_binding_ref"] == ("issue-fix:feasibility-a1b2c3d4")
-def test_active_task_lease_fences_same_agent_completion_instance(
+def test_duplicate_todo_add_rejects_invalid_task_domain_atomically(
tmp_path: Path,
) -> None:
- registry, state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
- lease_key = "turn-owner-instance"
- acquire_task_lease(
+ registry, state = _write_fixture(tmp_path)
+ todo = add_goal_todo(
registry_path=registry,
- runtime_root=tmp_path / "runtime",
goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key=lease_key,
- ttl_seconds=600,
+ role="agent",
+ text="Validate one adaptive lane.",
+ task_class="advancement_task",
+ task_domain="validation",
)
before = state.read_text(encoding="utf-8")
- with pytest.raises(TaskLeaseError) as missing_fence:
- complete_goal_todo(
+ with pytest.raises(ValueError, match="task_domain"):
+ add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- evidence="stale execution instance",
- next_agent_todo="Create a stale successor.",
+ role="agent",
+ text="Validate one adaptive lane.",
+ task_class="advancement_task",
+ task_domain="../private",
)
- assert missing_fence.value.code == "lease_fence_required"
- assert state.read_text(encoding="utf-8") == before
- with pytest.raises(TaskLeaseError) as mismatched_fence:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key="other-instance",
- evidence="wrong execution instance",
- )
- assert mismatched_fence.value.code == "lease_cas_mismatch"
assert state.read_text(encoding="utf-8") == before
+ assert _agent_todo(state, todo["todo_id"])["task_domain"] == "validation"
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key=lease_key,
- task_lease_expected_version=1,
- evidence="lease owner validated the result",
- next_agent_todo="Create the canonical successor.",
- )
- assert completed["task_lease_fence"] == {
- "schema_version": "task_lease_v0",
- "required": True,
- "active": True,
- "owner": AUTHOR_AGENT,
- "version": 1,
- "lease_epoch": 1,
- "execution_instance_verified": True,
- "released": True,
- }
- assert (
- json.loads(_lease_path(tmp_path, todo["todo_id"]).read_text(encoding="utf-8"))[
- "status"
- ]
- == "released"
- )
- replayed = complete_goal_todo(
+def test_todo_update_rejects_invalid_task_domain_atomically(tmp_path: Path) -> None:
+ registry, state = _write_fixture(tmp_path)
+ todo = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
+ role="agent",
+ text="Validate one adaptive lane.",
+ task_class="advancement_task",
+ task_domain="validation",
claimed_by=AUTHOR_AGENT,
- evidence="late result from another execution instance",
- next_agent_todo="Create a duplicate stale successor.",
)
- assert replayed["idempotent_replay"] is True
- assert replayed["changed"] is False
- agent_todos = parse_active_state_todos(state.read_text(encoding="utf-8"))[
- "agent_todos"
- ]["items"]
- todo_titles = [
- str(item.get("title") or item.get("text") or "") for item in agent_todos
- ]
- assert todo_titles.count("Create the canonical successor.") == 1
- assert "Create a duplicate stale successor." not in todo_titles
-
-
-def test_released_lease_generation_rejects_stale_completion_fences(
- tmp_path: Path,
-) -> None:
- registry, state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
- runtime_root = tmp_path / "runtime"
- first = acquire_task_lease(
- registry_path=registry,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key="execution-one",
- ttl_seconds=600,
- )
- assert first["lease"]["version"] == 1
- assert first["lease"]["lease_epoch"] == 1
-
- released = release_task_lease(
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key="execution-one",
- expected_version=1,
- registry_path=registry,
- )
- assert released["lease"]["status"] == "released"
- assert _lease_path(tmp_path, todo["todo_id"]).exists()
-
- with pytest.raises(TaskLeaseError) as reused_execution:
- acquire_task_lease(
- registry_path=registry,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key="execution-one",
- ttl_seconds=600,
- )
- assert reused_execution.value.code == "idempotency_key_reuse"
-
- second = acquire_task_lease(
- registry_path=registry,
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key="execution-two",
- ttl_seconds=600,
- )
- assert second["lease"]["version"] == 2
- assert second["lease"]["lease_epoch"] == 2
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(TaskLeaseError) as old_key:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key="execution-one",
- task_lease_expected_version=1,
- evidence="stale first execution",
- no_followup=True,
- )
- assert old_key.value.code == "lease_cas_mismatch"
- assert state.read_text(encoding="utf-8") == before
-
- with pytest.raises(TaskLeaseError) as old_version:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key="execution-two",
- task_lease_expected_version=1,
- evidence="stale version from the first generation",
- no_followup=True,
- )
- assert old_version.value.code == "version_mismatch"
- assert state.read_text(encoding="utf-8") == before
-
- with pytest.raises(TaskLeaseError) as missing_version:
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key="execution-two",
- evidence="incomplete fence",
- no_followup=True,
- )
- assert missing_version.value.code == "version_required"
- assert state.read_text(encoding="utf-8") == before
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key="execution-two",
- task_lease_expected_version=2,
- evidence="current generation completed",
- no_followup=True,
- )
- assert completed["task_lease_fence"]["lease_epoch"] == 2
- tombstone = json.loads(
- _lease_path(tmp_path, todo["todo_id"]).read_text(encoding="utf-8")
- )
- assert tombstone["status"] == "released"
- assert tombstone["version"] == 2
- assert tombstone["lease_epoch"] == 2
-
-
-def test_completion_turn_key_rejects_cross_turn_replay(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = _add_agent_todo(registry)
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- evidence="turn A validated the selected Todo",
- completion_turn_key="turn-a",
- no_followup=True,
- )
- assert completed["completed"] is True
- completed_todo = _agent_todo(state, todo["todo_id"])
- assert completed_todo["completion_turn_key"] == "turn-a"
- assert completed_todo["completion_continuation"] == "no_followup"
- completed_state = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="different completion_turn_key"):
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- evidence="turn B must not claim turn A's completion",
- completion_turn_key="turn-b",
- no_followup=True,
- )
- assert state.read_text(encoding="utf-8") == completed_state
-
- replayed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- evidence="turn A retried after its durable write",
- completion_turn_key="turn-a",
- no_followup=True,
- )
- assert replayed["idempotent_replay"] is True
- assert replayed["changed"] is False
-
-
-def test_terminal_upgrade_cannot_replace_existing_successor(tmp_path: Path) -> None:
- registry, _state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- completion_turn_key="turn-a",
- evidence="ordinary completion created the successor",
- next_agent_todo="Continue the durable successor.",
- )
- assert completed["changed"] is True
- assert completed["completion_continuation"] == "successor"
-
- with pytest.raises(ValueError, match="cannot replace an existing successor"):
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- completion_turn_key="turn-a",
- evidence="terminal closeout must not erase the successor",
- no_followup=True,
- )
-
-
-def test_terminal_upgrade_requires_original_completion_turn_key(tmp_path: Path) -> None:
- registry, _state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- completion_turn_key="turn-a",
- evidence="ordinary completion from the original turn",
- )
- assert completed["changed"] is True
- assert completed["completion_continuation"] == "active_goal"
-
- with pytest.raises(ValueError, match="requires the original completion_turn_key"):
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- evidence="an unscoped caller cannot claim terminal closeout",
- no_followup=True,
- )
-
-
-def test_terminal_upgrade_rejects_untyped_legacy_completion_until_repaired(
- tmp_path: Path,
-) -> None:
- registry, state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
- todo_id = todo["todo_id"]
-
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo_id,
- claimed_by=AUTHOR_AGENT,
- completion_turn_key="turn-a",
- evidence="ordinary completion from the original turn",
- )
- state.write_text(
- state.read_text(encoding="utf-8").replace(
- " completion_continuation=active_goal",
- "",
- ),
- encoding="utf-8",
- )
-
- with pytest.raises(ValueError, match="missing completion_continuation"):
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo_id,
- claimed_by=AUTHOR_AGENT,
- completion_turn_key="turn-a",
- evidence="legacy ambiguity must not be inferred",
- no_followup=True,
- )
-
- repaired = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo_id,
- claimed_by=AUTHOR_AGENT,
- completion_turn_key="turn-a",
- evidence="repair explicit completion continuation",
- note="repair explicit completion continuation",
- )
- assert repaired["completion_continuation"] == "active_goal"
-
- upgraded = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo_id,
- claimed_by=AUTHOR_AGENT,
- completion_turn_key="turn-a",
- evidence="same-turn terminal closeout after explicit repair",
- no_followup=True,
- )
- assert upgraded["completion_continuation"] == "no_followup"
- assert upgraded["completion_recovery"] == "same_turn_terminal_closeout"
-
-
-
-
-def test_unfenced_completion_leaves_no_lease_artifacts(tmp_path: Path) -> None:
- registry, _state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- evidence="no lease was ever taken",
- no_followup=True,
- )
-
- assert completed["ok"] is True
- assert completed["task_lease_fence"] == {
- "schema_version": "task_lease_v0",
- "required": False,
- "active": False,
- }
- lease_dir = _lease_path(tmp_path, todo["todo_id"]).parent
- assert list(lease_dir.glob("todo_*.json")) == []
-
-
-def test_divergent_claim_completion_leaves_foreign_lease_untouched(
- tmp_path: Path,
-) -> None:
- registry, _state = _write_fixture(tmp_path)
- todo = _add_agent_todo(registry, claimed_by=None)
- acquire_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key="author-instance",
- ttl_seconds=600,
- )
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=REVIEW_AGENT,
- agent_id=REVIEW_AGENT,
- claim_only=True,
- )
- lease_path = _lease_path(tmp_path, todo["todo_id"])
- lease_before = lease_path.read_text(encoding="utf-8")
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=REVIEW_AGENT,
- agent_id=REVIEW_AGENT,
- evidence="split-brain claimer completed without the lease key",
- no_followup=True,
- )
-
- assert completed["ok"] is True
- assert completed["task_lease_fence"] == {
- "schema_version": "task_lease_v0",
- "required": False,
- "active": False,
- }
- assert lease_path.read_text(encoding="utf-8") == lease_before
- assert json.loads(lease_before)["owner"] == AUTHOR_AGENT
-
-
-def test_dry_run_completion_does_not_release_lease(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
- lease_key = "dry-run-instance"
- acquire_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key=lease_key,
- ttl_seconds=600,
- )
- before = state.read_text(encoding="utf-8")
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key=lease_key,
- task_lease_expected_version=1,
- evidence="dry run must not touch the lease",
- no_followup=True,
- dry_run=True,
- )
-
- assert completed["dry_run"] is True
- assert completed["task_lease_fence"]["execution_instance_verified"] is True
- assert "released" not in completed["task_lease_fence"]
- lease_path = _lease_path(tmp_path, todo["todo_id"])
- assert json.loads(lease_path.read_text(encoding="utf-8"))["status"] == "active"
- assert state.read_text(encoding="utf-8") == before
-
-
-def test_release_failure_after_commit_keeps_completion_ok(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
-) -> None:
- registry, _state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
- lease_key = "release-failure-instance"
- acquire_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key=lease_key,
- ttl_seconds=600,
- )
-
- native_lifecycle = task_lease_module._execute_native_task_lease_lifecycle
-
- def _fail_release(**kwargs: object) -> dict:
- if (
- kwargs.get("operation") == "fence_close"
- and kwargs.get("committed") is True
- and kwargs.get("release_lease") is True
- ):
- native_lifecycle(
- **{
- **kwargs,
- "committed": False,
- "release_lease": False,
- }
- )
- raise OSError("simulated terminal write failure")
- return native_lifecycle(**kwargs)
-
- monkeypatch.setattr(
- task_lease_module,
- "_execute_native_task_lease_lifecycle",
- _fail_release,
- )
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key=lease_key,
- task_lease_expected_version=1,
- evidence="completion must survive a failed lease release",
- no_followup=True,
- )
-
- assert completed["ok"] is True
- assert completed["completed"] is True
- assert completed["task_lease_fence"]["execution_instance_verified"] is True
- assert completed["task_lease_fence"]["released"] is False
- lease_path = _lease_path(tmp_path, todo["todo_id"])
- assert json.loads(lease_path.read_text(encoding="utf-8"))["status"] == "active"
-
-
-def test_reopened_todo_completes_unfenced_after_lease_release(
- tmp_path: Path,
-) -> None:
- """Pin the accepted reopen-window divergence of release-on-completion.
-
- The terminal tombstone retains the generation but is not time-active, so a
- manually reopened todo completes unfenced. The tombstone exists only to
- prevent a later acquire from reusing the old execution generation.
- """
-
- registry, _state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
- lease_key = "reopen-window-instance"
- acquire_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key=lease_key,
- ttl_seconds=600,
- )
-
- completed = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key=lease_key,
- task_lease_expected_version=1,
- evidence="first completion releases the verified lease",
- no_followup=True,
- )
- assert completed["task_lease_fence"]["released"] is True
- assert (
- json.loads(_lease_path(tmp_path, todo["todo_id"]).read_text(encoding="utf-8"))[
- "status"
- ]
- == "released"
- )
-
- reopened = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- status="open",
- note="manual reopen inside the old lease TTL",
- )
- assert reopened["ok"] is True
-
- recompleted = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- evidence="reopened todo completes without a lease fence",
- no_followup=True,
- )
- assert recompleted["ok"] is True
- assert recompleted["task_lease_fence"] == {
- "schema_version": "task_lease_v0",
- "required": False,
- "active": False,
- }
-
-
-def test_cli_release_after_auto_release_replays_terminal_tombstone(
- tmp_path: Path,
- capsys: pytest.CaptureFixture[str],
-) -> None:
- """The public CLI replays the retained terminal result, not `missing`."""
-
- registry, _state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
- lease_key = "double-release-instance"
- acquire_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key=lease_key,
- ttl_seconds=600,
- )
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key=lease_key,
- task_lease_expected_version=1,
- evidence="completion already released the lease",
- no_followup=True,
- )
- lease_path = _lease_path(tmp_path, todo["todo_id"])
- tombstone = json.loads(lease_path.read_text(encoding="utf-8"))
-
- exit_code = cli_main(
- [
- "--registry",
- str(registry),
- "--format",
- "json",
- "task-lease",
- "release",
- "--goal-id",
- GOAL_ID,
- "--todo-id",
- todo["todo_id"],
- "--owner",
- AUTHOR_AGENT,
- "--idempotency-key",
- lease_key,
- "--expected-version",
- "1",
- ]
- )
- released = json.loads(capsys.readouterr().out)
-
- assert exit_code == 0
- assert released["ok"] is True
- assert released["action"] == "release"
- assert released["released"] is True
- assert released["idempotent"] is True
- assert released["lease"] == tombstone
- assert released["lease"]["status"] == "released"
- assert released["lease"]["version"] == 1
- assert released["lease"]["lease_epoch"] == 1
- assert "missing" not in released
- assert json.loads(lease_path.read_text(encoding="utf-8")) == tombstone
-
-
-def test_exception_after_verified_fence_leaves_lease_intact(
- tmp_path: Path,
-) -> None:
- registry, state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
- lease_key = "post-fence-failure-instance"
- acquire_task_lease(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- owner=AUTHOR_AGENT,
- idempotency_key=lease_key,
- ttl_seconds=600,
- )
- lease_path = _lease_path(tmp_path, todo["todo_id"])
- lease_before = lease_path.read_text(encoding="utf-8")
- state_before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="successor_todo_ids"):
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- task_lease_idempotency_key=lease_key,
- task_lease_expected_version=1,
- successor_todo_ids=["!!not-a-todo-id!!"],
- evidence="fails after the fence verified",
- no_followup=True,
- )
-
- assert lease_path.read_text(encoding="utf-8") == lease_before
- assert json.loads(lease_before)["status"] == "active"
- assert state.read_text(encoding="utf-8") == state_before
-
-
-
-
-
-
-
-
-def test_capability_binding_cannot_be_rebound_by_duplicate_add(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path)
- add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Advance the admitted issue-fix route.",
- task_class="advancement_task",
- capability_binding_ref="issue-fix:feasibility-a1b2c3d4",
- )
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="immutable once set"):
- add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Advance the admitted issue-fix route.",
- task_class="advancement_task",
- capability_binding_ref="issue-fix:feasibility-e5f6a7b8",
- )
-
- assert state.read_text(encoding="utf-8") == before
-
-
-
-
-
-
-
-
-
-
-
-
-def test_duplicate_todo_add_rejects_invalid_task_domain_atomically(
- tmp_path: Path,
-) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Validate one adaptive lane.",
- task_class="advancement_task",
- task_domain="validation",
- )
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="task_domain"):
- add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Validate one adaptive lane.",
- task_class="advancement_task",
- task_domain="../private",
- )
-
- assert state.read_text(encoding="utf-8") == before
- assert _agent_todo(state, todo["todo_id"])["task_domain"] == "validation"
-
-
-def test_todo_update_rejects_invalid_task_domain_atomically(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Validate one adaptive lane.",
- task_class="advancement_task",
- task_domain="validation",
- claimed_by=AUTHOR_AGENT,
- )
- before = state.read_text(encoding="utf-8")
+ before = state.read_text(encoding="utf-8")
with pytest.raises(ValueError, match="task_domain"):
update_goal_todo(
@@ -1527,67 +497,6 @@ def test_monitor_schedule_fields_remain_monitor_only(tmp_path: Path) -> None:
)
-def test_claim_actor_must_match_requested_owner(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = _add_agent_todo(registry, claimed_by=None)
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="requires --claimed-by to match"):
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- agent_id=REVIEW_AGENT,
- claim_only=True,
- )
- assert state.read_text(encoding="utf-8") == before
-
- result = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- claimed_by=AUTHOR_AGENT,
- agent_id=AUTHOR_AGENT,
- claim_only=True,
- )
- assert result["mutation_authority"]["command"] == "claim"
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AUTHOR_AGENT
-
-
-def test_monitor_writeback_propagates_multi_agent_actor(tmp_path: Path) -> None:
- registry, _state = _write_fixture(tmp_path)
- todo = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Poll one public monitor target.",
- task_class="continuous_monitor",
- claimed_by=AUTHOR_AGENT,
- monitor_metadata={
- "target_key": "public-pr:42",
- "cadence": "15m",
- "watch_only": "true",
- },
- )
-
- result = write_monitor_poll_todo_state(
- registry_path=registry,
- runtime_root=tmp_path / "runtime",
- goal_id=GOAL_ID,
- generated_at="2026-07-18T00:15:00+00:00",
- execute=False,
- todo_id=todo["todo_id"],
- result_hash="unchanged",
- agent_id=AUTHOR_AGENT,
- )
-
- assert result is not None
- authority = result["todo_update"]["mutation_authority"]
- assert authority["mode"] == "registered_peer_actor"
- assert authority["actor_agent_id"] == AUTHOR_AGENT
-
-
def test_monitor_successor_replay_preserves_independent_reviewer_exclusion(
tmp_path: Path,
) -> None:
@@ -1627,79 +536,29 @@ def test_monitor_successor_replay_preserves_independent_reviewer_exclusion(
registry_path=registry,
runtime_root=tmp_path / "runtime",
goal_id=GOAL_ID,
- generated_at="2026-07-18T00:15:00+00:00",
- execute=True,
- todo_id=monitor["todo_id"],
- result_hash="all-checks-green",
- material_change=True,
- next_agent_todo=review_text,
- next_action_kind="review_and_merge",
- next_task_repository="git:github.com/example/project",
- next_required_capabilities=["network"],
- next_continuation_policy="independent_handoff",
- next_target_key="public-pr:42:review",
- agent_id=AUTHOR_AGENT,
- )
-
- assert result is not None
- assert result["successor_receipts"][0]["todo_id"] == review["todo_id"]
- assert _agent_todo(state, review["todo_id"])["excluded_agents"] == [
- AUTHOR_AGENT
- ]
-
-
-def test_exact_user_gate_decision_scope_uses_controller_override(
- tmp_path: Path,
-) -> None:
- registry, _state = _write_fixture(tmp_path)
- target = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Publish the approved release.",
- task_class="advancement_task",
- claimed_by=AUTHOR_AGENT,
- required_decision_scopes=[DECISION_SCOPE],
- )
- gate = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="user",
- text="Approve the exact release publication.",
- task_class="user_gate",
- blocks_agent=AUTHOR_AGENT,
- decision_scope=DECISION_SCOPE,
- unblocks_todo_id=target["todo_id"],
- )
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=gate["todo_id"],
- role="user",
- decision_outcome="approve",
- evidence="owner approved the exact decision scope",
+ generated_at="2026-07-18T00:15:00+00:00",
+ execute=True,
+ todo_id=monitor["todo_id"],
+ result_hash="all-checks-green",
+ material_change=True,
+ next_agent_todo=review_text,
+ next_action_kind="review_and_merge",
+ next_task_repository="git:github.com/example/project",
+ next_required_capabilities=["network"],
+ next_continuation_policy="independent_handoff",
+ next_target_key="public-pr:42:review",
+ agent_id=AUTHOR_AGENT,
)
- authority = result["mutation_authority"]
- assert authority["mode"] == "exact_user_gate_decision_scope_override"
- assert authority["actor_agent_id"] is None
- assert authority["target_todo_id"] == target["todo_id"]
- assert authority["decision_scope"]["scope_key"] == "publish_release"
+ assert result is not None
+ assert result["successor_receipts"][0]["todo_id"] == review["todo_id"]
+ assert _agent_todo(state, review["todo_id"])["excluded_agents"] == [AUTHOR_AGENT]
-def test_exact_user_gate_ignores_unrelated_malformed_lifecycle_grant(
+def test_exact_user_gate_decision_scope_uses_controller_override(
tmp_path: Path,
) -> None:
- registry, _state = _write_fixture(
- tmp_path,
- lifecycle_authority=[
- {
- "agent_id": ORCHESTRATION_AGENT,
- "actions": [],
- }
- ],
- )
+ registry, state = _write_fixture(tmp_path)
target = add_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
@@ -1729,296 +588,317 @@ def test_exact_user_gate_ignores_unrelated_malformed_lifecycle_grant(
evidence="owner approved the exact decision scope",
)
- assert result["mutation_authority"]["mode"] == (
- "exact_user_gate_decision_scope_override"
- )
-
-
-def test_non_exact_user_gate_cannot_bypass_actor_attribution(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path)
- target = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="Announce the approved release.",
- task_class="advancement_task",
- claimed_by=AUTHOR_AGENT,
- required_decision_scopes=["direction:action:announce_release"],
- )
- gate = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="user",
- text="Approve a different action.",
- task_class="user_gate",
- blocks_agent=AUTHOR_AGENT,
- decision_scope=DECISION_SCOPE,
- unblocks_todo_id=target["todo_id"],
- )
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="requires --agent-id"):
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=gate["todo_id"],
- role="user",
- decision_outcome="approve",
- evidence="scope mismatch",
- )
-
- assert state.read_text(encoding="utf-8") == before
-
-
-def test_single_agent_goal_keeps_lifecycle_compatibility(tmp_path: Path) -> None:
- registry, state = _write_fixture(tmp_path, multi_agent=False)
- todo = _add_agent_todo(registry)
-
- result = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- note="legacy single-agent update",
- )
-
- assert result["mutation_authority"]["mode"] == "single_agent_compatibility"
- assert _agent_todo(state, todo["todo_id"])["note"] == "legacy single-agent update"
+ authority = result["mutation_authority"]
+ assert authority["authority_mode"] == "exact_user_gate_decision_scope_override"
+ assert authority["code"] == "terminal_transition"
+ assert _agent_todo(state, target["todo_id"]).get(
+ "required_decision_scopes", []
+ ) == []
-def test_single_agent_ignores_unrelated_malformed_lifecycle_grant(
+def test_delegated_orchestrator_can_complete_claimed_todo_with_reason(
tmp_path: Path,
) -> None:
registry, state = _write_fixture(
tmp_path,
- multi_agent=False,
lifecycle_authority=[
{
- "agent_id": REVIEW_AGENT,
- "actions": ["complete"],
+ "agent_id": ORCHESTRATION_AGENT,
+ "actions": ["complete", "reassign", "supersede"],
+ "requires_reason": True,
}
],
)
- todo = _add_agent_todo(registry)
+ todo = _add_agent_todo(registry, claimed_by=REVIEW_AGENT)
- result = update_goal_todo(
+ result = complete_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
todo_id=todo["todo_id"],
- note="legacy single-agent update",
+ agent_id=ORCHESTRATION_AGENT,
+ authority_reason="Verified the worker result and closed the stalled lane.",
+ evidence="validation://orchestrator-closeout",
)
- assert result["mutation_authority"]["mode"] == "single_agent_compatibility"
- assert _agent_todo(state, todo["todo_id"])["note"] == "legacy single-agent update"
+ authority = result["mutation_authority"]
+ assert authority["schema_version"] == (
+ "loopx_coordination_todo_terminal_decision_result_v0"
+ )
+ assert authority["authority_mode"] == "delegated_orchestration_override"
+ assert authority["code"] == "terminal_transition"
+ assert _agent_todo(state, todo["todo_id"])["status"] == "done"
-def test_current_owner_ignores_unrelated_malformed_lifecycle_grant(
- tmp_path: Path,
-) -> None:
- registry, state = _write_fixture(
- tmp_path,
- lifecycle_authority=[
- {
- "agent_id": ORCHESTRATION_AGENT,
- "actions": ["unsupported-action"],
- }
- ],
- )
- todo = _add_agent_todo(registry)
+def _canonical_todo(registry: Path, todo_id: str) -> dict:
+ return list_goal_todos(registry_path=registry, goal_id=GOAL_ID, todo_id=todo_id)["todo"]
- result = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- note="owner-attributed update",
- )
- assert result["mutation_authority"]["mode"] == "registered_peer_actor"
- assert _agent_todo(state, todo["todo_id"])["note"] == "owner-attributed update"
+def _canonical_snapshot(registry: Path, state: Path) -> tuple:
+ result = list_goal_todos(registry_path=registry, goal_id=GOAL_ID)
+ return result["authority_read"]["provider_revision"], result["todos"], state.read_bytes()
-@pytest.mark.parametrize("command", ["complete", "supersede"])
-def test_author_cannot_replace_explicit_independent_review(
- tmp_path: Path,
- command: str,
-) -> None:
- registry, state = _write_fixture(tmp_path)
- todo = _add_agent_todo(
- registry,
- claimed_by=REVIEW_AGENT,
- excluded_agents=[AUTHOR_AGENT],
+def _canonical_lease(registry: Path, tmp_path: Path, todo_id: str) -> dict | None:
+ result = inspect_task_lease(
+ registry_path=registry, runtime_root=tmp_path / "runtime",
+ goal_id=GOAL_ID, todo_id=todo_id,
)
- before = state.read_text(encoding="utf-8")
+ assert result["source_authority"] in {"file_v0", "sqlite_v0"}
+ assert result["legacy_fallback_used"] is False
+ return result.get("lease")
+
+
+def _lease_path(tmp_path: Path, todo_id: str) -> Path:
+ return tmp_path / "runtime" / "goals" / GOAL_ID / "task-leases" / f"{todo_id}.json"
- with pytest.raises(ValueError, match="is excluded from mutating"):
- if command == "complete":
+
+@pytest.mark.parametrize("status", ["deferred", "blocked", "done"])
+def test_canonical_claim_rejects_nonopen_status_atomically(
+ tmp_path: Path, canonical_workspace, status: str,
+) -> None:
+ registry, state = canonical_workspace(tmp_path)
+ if status == "deferred":
+ todo = add_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, role="agent",
+ text="Wait for external capacity before claim.", status="deferred",
+ resume_when="capacity_available:network",
+ )
+ else:
+ todo = _add_agent_todo(registry)
+ if status == "done":
complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- evidence="author cannot self-review",
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"],
+ agent_id=AUTHOR_AGENT, evidence="finished before the contested claim",
)
else:
- supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=AUTHOR_AGENT,
- reason="author cannot replace review",
+ update_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"],
+ agent_id=AUTHOR_AGENT, status=status,
)
-
- assert state.read_text(encoding="utf-8") == before
+ before = _canonical_snapshot(registry, state)
+ with pytest.raises(LocalCoordinationAuthorityRejection) as error:
+ update_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"],
+ claim_only=True, claimed_by=AUTHOR_AGENT, agent_id=AUTHOR_AGENT,
+ )
+ assert error.value.code == "todo_not_open"
+ assert error.value.payload["todo_status"] == status
+ assert _canonical_snapshot(registry, state) == before
+ assert _canonical_lease(registry, tmp_path, todo["todo_id"]) is None
-def test_delegated_orchestrator_can_complete_claimed_todo_with_reason(
- tmp_path: Path,
-) -> None:
- registry, state = _write_fixture(
- tmp_path,
- lifecycle_authority=[
- {
- "agent_id": ORCHESTRATION_AGENT,
- "actions": ["complete", "reassign", "supersede"],
- "requires_reason": True,
- }
- ],
+def test_canonical_claim_rejects_user_todo_atomically(tmp_path: Path, canonical_workspace) -> None:
+ registry, state = canonical_workspace(tmp_path)
+ todo = add_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, role="user",
+ text="Owner reviews the delivered change.", task_class="user_action",
+ bound_agent=AUTHOR_AGENT,
)
- todo = _add_agent_todo(registry, claimed_by=REVIEW_AGENT)
+ before = _canonical_snapshot(registry, state)
+ with pytest.raises(LocalCoordinationAuthorityRejection) as error:
+ update_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"],
+ claim_only=True, claimed_by=AUTHOR_AGENT, agent_id=AUTHOR_AGENT,
+ )
+ assert error.value.code == "todo_not_agent"
+ assert _canonical_snapshot(registry, state) == before
+ assert _canonical_todo(registry, todo["todo_id"])["bound_agent"] == AUTHOR_AGENT
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=ORCHESTRATION_AGENT,
- authority_reason="Verified the worker result and closed the stalled lane.",
- evidence="validation://orchestrator-closeout",
+
+def test_canonical_claim_cannot_reactivate_archived_work(tmp_path: Path, canonical_workspace) -> None:
+ registry, state = canonical_workspace(tmp_path)
+ todo = _add_agent_todo(registry)
+ complete_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"],
+ agent_id=AUTHOR_AGENT, evidence="finished before archival",
+ )
+ archive_completed_todos(
+ registry_path=registry, goal_id=GOAL_ID, max_active_done=0, dry_run=False,
)
+ assert _canonical_todo(registry, todo["todo_id"])["archive_state"] == "archive"
+ before = _canonical_snapshot(registry, state)
+ with pytest.raises(LocalCoordinationAuthorityRejection) as error:
+ update_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"],
+ claim_only=True, claimed_by=AUTHOR_AGENT, agent_id=AUTHOR_AGENT,
+ )
+ # Status eligibility precedes archive eligibility in the shared claim owner.
+ assert error.value.code == "todo_not_open"
+ assert _canonical_snapshot(registry, state) == before
- assert result["mutation_authority"] == {
- "schema_version": "todo_mutation_authority_v0",
- "command": "complete",
- "mode": "delegated_orchestration_override",
- "actor_agent_id": ORCHESTRATION_AGENT,
- "todo_id": todo["todo_id"],
- "claim_owner": REVIEW_AGENT,
- "authority_action": "complete",
- "authority_source": "coordination.todo_lifecycle_authority",
- "authority_reason": "Verified the worker result and closed the stalled lane.",
- "requires_reason": True,
- "registered_agent_count": 3,
- }
- assert _agent_todo(state, todo["todo_id"])["status"] == "done"
+
+def test_canonical_claim_rejects_role_mismatch_atomically(tmp_path: Path, canonical_workspace) -> None:
+ registry, state = canonical_workspace(tmp_path)
+ todo = _add_agent_todo(registry, claimed_by=None)
+ before = _canonical_snapshot(registry, state)
+ with pytest.raises(LocalCoordinationAuthorityRejection) as error:
+ update_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"], role="user",
+ claim_only=True, claimed_by=AUTHOR_AGENT, agent_id=AUTHOR_AGENT,
+ )
+ assert error.value.code == "todo_role_mismatch"
+ assert error.value.payload["requested_role"] == "user"
+ assert error.value.payload["todo_role"] == "agent"
+ assert _canonical_snapshot(registry, state) == before
-def test_delegated_orchestrator_requires_reason_before_state_write(
- tmp_path: Path,
+def test_completion_and_lease_release_recover_after_response_loss(
+ tmp_path: Path, canonical_workspace, monkeypatch: pytest.MonkeyPatch,
) -> None:
- registry, state = _write_fixture(
- tmp_path,
- lifecycle_authority=[
- {
- "agent_id": ORCHESTRATION_AGENT,
- "actions": ["complete"],
- "requires_reason": True,
- }
- ],
+ # Canonical completion releases its lease in the same commit; there is no
+ # independent post-commit release call to fail. Lose the real response instead.
+ registry, state = canonical_workspace(tmp_path, multi_agent=False, handoff_mode="hard_lease")
+ todo = _add_agent_todo(registry)
+ lease_key = "response-loss-instance"
+ acquire_task_lease(
+ registry_path=registry, runtime_root=tmp_path / "runtime", goal_id=GOAL_ID,
+ todo_id=todo["todo_id"], owner=AUTHOR_AGENT, idempotency_key=lease_key, ttl_seconds=600,
)
- todo = _add_agent_todo(registry, claimed_by=REVIEW_AGENT)
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="requires --authority-reason"):
+ request = dict(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"], agent_id=AUTHOR_AGENT,
+ task_lease_idempotency_key=lease_key, task_lease_expected_version=1,
+ completion_turn_key="response-loss-turn", evidence="Original completion committed once.",
+ no_followup=True,
+ )
+ native_effect = terminal_provider.effect_runtime_result
+ committed = []
+ def lose_response(method, payload, **kwargs):
+ result = native_effect(method, payload, **kwargs)
+ if method == "coordination.local_authority.todo_terminal" and not committed:
+ assert result["status"] == "applied"
+ committed.append(result)
+ raise OSError("response lost after real canonical commit")
+ return result
+ with monkeypatch.context() as patch:
+ patch.setattr(terminal_provider, "effect_runtime_result", lose_response)
+ with pytest.raises(OSError, match="response lost"):
+ complete_goal_todo(**request)
+ assert len(committed) == 1
+ assert _canonical_todo(registry, todo["todo_id"])["status"] == "done"
+ lease_after_commit = _canonical_lease(registry, tmp_path, todo["todo_id"])
+ assert lease_after_commit["status"] == "released"
+ replay = complete_goal_todo(**request)
+ assert replay["ok"] is True and replay["idempotent_replay"] is True
+ assert _canonical_lease(registry, tmp_path, todo["todo_id"]) == lease_after_commit
+ after_recovery = _canonical_snapshot(registry, state)
+ assert complete_goal_todo(**request)["idempotent_replay"] is True
+ assert _canonical_snapshot(registry, state) == after_recovery
+
+
+def test_completed_todo_rejects_metadata_reopen_and_new_execution(
+ tmp_path: Path, canonical_workspace,
+) -> None:
+ registry, state = canonical_workspace(tmp_path, multi_agent=False, handoff_mode="hard_lease")
+ todo = _add_agent_todo(registry)
+ acquire_task_lease(
+ registry_path=registry, runtime_root=tmp_path / "runtime", goal_id=GOAL_ID,
+ todo_id=todo["todo_id"], owner=AUTHOR_AGENT, idempotency_key="first-instance", ttl_seconds=600,
+ )
+ complete_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"], agent_id=AUTHOR_AGENT,
+ task_lease_idempotency_key="first-instance", task_lease_expected_version=1,
+ completion_turn_key="first-turn", evidence="First completion releases its lease.",
+ )
+ released = _canonical_lease(registry, tmp_path, todo["todo_id"])
+ assert released["status"] == "released"
+ before = _canonical_snapshot(registry, state)
+ # A completed ordinary Todo has no metadata-reopen contract. Keep terminal
+ # history intact rather than silently resurrecting a retired execution.
+ with pytest.raises(LocalCoordinationAuthorityRejection) as rejected:
+ update_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"], agent_id=AUTHOR_AGENT,
+ status="open", note="Metadata cannot reopen a completed execution.",
+ )
+ assert rejected.value.code == "unsupported_todo_update_target"
+ assert _canonical_snapshot(registry, state) == before
+ assert _canonical_lease(registry, tmp_path, todo["todo_id"]) == released
+ with pytest.raises((ValueError, LocalCoordinationAuthorityRejection)):
complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=ORCHESTRATION_AGENT,
- evidence="must remain atomic",
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"], agent_id=AUTHOR_AGENT,
+ task_lease_idempotency_key="first-instance", task_lease_expected_version=1,
+ completion_turn_key="second-turn", evidence="Released proof cannot authorize new execution.",
)
-
- assert state.read_text(encoding="utf-8") == before
+ assert _canonical_snapshot(registry, state) == before
+ assert _canonical_lease(registry, tmp_path, todo["todo_id"]) == released
-def test_delegated_orchestration_authority_is_action_scoped(
- tmp_path: Path,
+def test_invalid_completion_keeps_existing_lease_and_state(
+ tmp_path: Path, canonical_workspace,
) -> None:
- registry, state = _write_fixture(
- tmp_path,
- lifecycle_authority=[
- {
- "agent_id": ORCHESTRATION_AGENT,
- "actions": ["reassign"],
- "requires_reason": True,
- }
- ],
+ registry, state = canonical_workspace(tmp_path, multi_agent=False, handoff_mode="hard_lease")
+ todo = _add_agent_todo(registry)
+ lease_key = "post-fence-failure-instance"
+ acquire_task_lease(
+ registry_path=registry,
+ runtime_root=tmp_path / "runtime",
+ goal_id=GOAL_ID,
+ todo_id=todo["todo_id"],
+ owner=AUTHOR_AGENT,
+ idempotency_key=lease_key,
+ ttl_seconds=600,
)
- todo = _add_agent_todo(registry, claimed_by=REVIEW_AGENT)
- before = state.read_text(encoding="utf-8")
+ lease_before = _canonical_lease(registry, tmp_path, todo["todo_id"])
+ state_before = _canonical_snapshot(registry, state)
- with pytest.raises(ValueError, match="does not grant action='complete'"):
+ with pytest.raises(ValueError, match="successor_todo_ids"):
complete_goal_todo(
registry_path=registry,
goal_id=GOAL_ID,
todo_id=todo["todo_id"],
- agent_id=ORCHESTRATION_AGENT,
- authority_reason="This grant only permits reassignment.",
- evidence="unauthorized-action",
+ agent_id=AUTHOR_AGENT,
+ task_lease_idempotency_key=lease_key,
+ task_lease_expected_version=1,
+ successor_todo_ids=["!!not-a-todo-id!!"],
+ evidence="invalid successor must leave state and lease unchanged",
+ no_followup=True,
)
- assert state.read_text(encoding="utf-8") == before
- with pytest.raises(ValueError, match="does not grant action='update'"):
- update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=ORCHESTRATION_AGENT,
- claimed_by=AUTHOR_AGENT,
- status="done",
- authority_reason="A reassign grant cannot carry another mutation.",
- )
- assert state.read_text(encoding="utf-8") == before
+ assert _canonical_lease(registry, tmp_path, todo["todo_id"]) == lease_before
+ assert lease_before["status"] == "active"
+ assert _canonical_snapshot(registry, state) == state_before
- result = update_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=ORCHESTRATION_AGENT,
- claimed_by=AUTHOR_AGENT,
- authority_reason="Move the stalled work to an available peer.",
- )
- assert result["mutation_authority"]["authority_action"] == "reassign"
- assert _agent_todo(state, todo["todo_id"])["claimed_by"] == AUTHOR_AGENT
+@pytest.mark.parametrize("command", ["update", "complete"])
+def test_canonical_single_agent_owned_todo_requires_explicit_actor(
+ tmp_path: Path, canonical_workspace, command: str,
+) -> None:
+ registry, state = canonical_workspace(tmp_path, multi_agent=False)
+ todo = _add_agent_todo(registry)
+ before = _canonical_snapshot(registry, state)
+ with pytest.raises(LocalCoordinationAuthorityRejection) as rejected:
+ if command == "update":
+ update_goal_todo(registry_path=registry, goal_id=GOAL_ID,
+ todo_id=todo["todo_id"], note="Actorless owned mutation is forbidden")
+ else:
+ complete_goal_todo(registry_path=registry, goal_id=GOAL_ID,
+ todo_id=todo["todo_id"], evidence="Actorless owned mutation is forbidden")
+ assert rejected.value.code == "actor_required"
+ assert _canonical_snapshot(registry, state) == before
-def test_delegated_override_never_bypasses_explicit_exclusion(
- tmp_path: Path,
+def test_canonical_update_revision_conflict_is_typed_without_actor_mutation(
+ tmp_path: Path, canonical_workspace,
) -> None:
- registry, state = _write_fixture(
- tmp_path,
- lifecycle_authority=[
- {
- "agent_id": ORCHESTRATION_AGENT,
- "actions": ["complete", "supersede"],
- "requires_reason": True,
- }
- ],
- )
- todo = _add_agent_todo(
- registry,
- claimed_by=REVIEW_AGENT,
- excluded_agents=[ORCHESTRATION_AGENT],
- )
- before = state.read_text(encoding="utf-8")
-
- with pytest.raises(ValueError, match="is excluded from mutating"):
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=todo["todo_id"],
- agent_id=ORCHESTRATION_AGENT,
- authority_reason="An explicit exclusion remains authoritative.",
- evidence="must-not-write",
+ registry, state = canonical_workspace(tmp_path)
+ todo = _add_agent_todo(registry)
+ before = _canonical_snapshot(registry, state)
+ with pytest.raises(LocalCoordinationAuthorityUnavailable) as rejected:
+ update_goal_todo(
+ registry_path=registry, goal_id=GOAL_ID, todo_id=todo["todo_id"],
+ agent_id=AUTHOR_AGENT, note="A stale writer cannot commit",
+ update_expected_provider_revision="stale-revision",
)
+ assert rejected.value.code == "provider_revision_mismatch"
+ assert isinstance(rejected.value, LocalCoordinationAuthorityRejection)
+ assert rejected.value.payload["failure_kind"] == "decision_rejection"
+ assert _canonical_snapshot(registry, state) == before
- assert state.read_text(encoding="utf-8") == before
+
+@pytest.fixture(params=["file", "sqlite"])
+def canonical_workspace(request, tmp_path, monkeypatch):
+ isolate_sqlite_runtime(tmp_path, monkeypatch)
+
+ def create(root, **kwargs):
+ return _write_fixture(root, provider=request.param, **kwargs)
+
+ return create
diff --git a/tests/control_plane/test_todo_next_action_settlement.py b/tests/control_plane/test_todo_next_action_settlement.py
index 9a97b4bfa7..f5f27a3cad 100644
--- a/tests/control_plane/test_todo_next_action_settlement.py
+++ b/tests/control_plane/test_todo_next_action_settlement.py
@@ -3,22 +3,41 @@
import json
from pathlib import Path
+from canonical_authority_fixture import initialize_canonical_authority
from loopx.bootstrap import render_state_markdown
-from loopx.control_plane.effect_runtime import MAX_REQUEST_BYTES
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
from loopx.control_plane.todos.next_action_runtime import (
_agent_todo_snapshots,
apply_added_todo_next_action,
reconcile_added_todo_next_action,
)
-from loopx.control_plane.todos.projection import TODO_MISSING_PRIORITY_RANK, todo_priority_rank
+from loopx.control_plane.todos.projection import (
+ TODO_MISSING_PRIORITY_RANK,
+ todo_priority_rank,
+)
from loopx.state_projection import active_state_next_action_entries
from loopx.state_refresh import build_state_refresh_record
-from loopx.todos import add_goal_todo, complete_goal_todo, supersede_goal_todo
+from loopx.todos import add_goal_todo, complete_goal_todo
GOAL_ID = "todo-next-action-settlement"
AGENT_ID = "codex-next-action"
+def _initialize_canonical(tmp_path: Path, state: Path) -> None:
+ initialize_canonical_authority(
+ tmp_path / "runtime",
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=[],
+ handoff_mode="soft_claim",
+ ),
+ state_path=state,
+ )
+
+
def test_agent_todo_snapshot_uses_structured_prefix_priority() -> None:
snapshots = _agent_todo_snapshots(
[
@@ -86,6 +105,7 @@ def _write_fixture(tmp_path: Path, *, next_action: str) -> tuple[Path, Path]:
),
encoding="utf-8",
)
+ _initialize_canonical(tmp_path, state)
return registry, state
@@ -164,6 +184,7 @@ def test_agent_todo_add_after_bootstrap_keeps_default_next_action(
),
encoding="utf-8",
)
+ _initialize_canonical(tmp_path, state)
added = add_goal_todo(
registry_path=registry,
@@ -310,24 +331,30 @@ def reject_unexpected_transition(*_args, **_kwargs):
)
lines = ["## Next Action", "", "- Preserve the existing route.", ""]
- assert apply_added_todo_next_action(
- lines,
- role="agent",
- add_result={
- "changed": False,
- "todo_id": "todo_existing",
- "task_class": "advancement_task",
- },
- ) is False
- assert apply_added_todo_next_action(
- lines,
- role="agent",
- add_result={
- "changed": True,
- "todo_id": "todo_monitor",
- "task_class": "continuous_monitor",
- },
- ) is True
+ assert (
+ apply_added_todo_next_action(
+ lines,
+ role="agent",
+ add_result={
+ "changed": False,
+ "todo_id": "todo_existing",
+ "task_class": "advancement_task",
+ },
+ )
+ is False
+ )
+ assert (
+ apply_added_todo_next_action(
+ lines,
+ role="agent",
+ add_result={
+ "changed": True,
+ "todo_id": "todo_monitor",
+ "task_class": "continuous_monitor",
+ },
+ )
+ is True
+ )
def test_refresh_record_preserves_a_long_wrapped_next_action_losslessly(
@@ -365,152 +392,6 @@ def test_refresh_record_preserves_a_long_wrapped_next_action_losslessly(
assert "…" not in record["state"]["next_action"][0]
-def test_complete_reprojects_typed_next_action_to_open_successor(
- tmp_path: Path,
-) -> None:
- completed_text = "[P1] Validate the project connection."
- registry, state = _write_fixture(tmp_path, next_action=completed_text)
- completed = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text=completed_text,
- task_class="advancement_task",
- action_kind="project_connection_review",
- claimed_by=AGENT_ID,
- )
- successor = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="[P0] Implement and validate the requested behavior.",
- task_class="advancement_task",
- action_kind="implementation",
- claimed_by=AGENT_ID,
- )
- state.write_text(
- state.read_text(encoding="utf-8").replace(
- f"- {completed_text}\n",
- f"- {completed_text}\n"
- "\n",
- 1,
- ),
- encoding="utf-8",
- )
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=str(completed["todo_id"]),
- successor_todo_ids=[str(successor["todo_id"])],
- agent_id=AGENT_ID,
- evidence="connection preflight passed",
- )
-
- assert result["changed"] is True
- assert active_state_next_action_entries(state.read_text(encoding="utf-8")) == [
- "[P0] Implement and validate the requested behavior."
- ]
-
-
-def test_complete_projects_next_action_when_archived_state_exceeds_runtime_limit(
- tmp_path: Path,
-) -> None:
- completed_text = "[P1] Finish the bounded implementation."
- registry, state = _write_fixture(tmp_path, next_action=completed_text)
- completed = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text=completed_text,
- task_class="advancement_task",
- action_kind="implementation",
- claimed_by=AGENT_ID,
- )
- successor = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text="[P1] Validate the bounded implementation.",
- task_class="advancement_task",
- action_kind="validation_review",
- claimed_by=AGENT_ID,
- )
- archived_evidence = "x" * (MAX_REQUEST_BYTES + 1024)
- state_text = state.read_text(encoding="utf-8").replace(
- f"- {completed_text}\n",
- f"- {completed_text}\n"
- "\n",
- 1,
- )
- state.write_text(
- f"{state_text}\n## Completed Work Archive\n\n{archived_evidence}\n",
- encoding="utf-8",
- )
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=str(completed["todo_id"]),
- successor_todo_ids=[str(successor["todo_id"])],
- agent_id=AGENT_ID,
- evidence="implementation completed",
- )
-
- final_text = state.read_text(encoding="utf-8")
- assert result["changed"] is True
- assert active_state_next_action_entries(final_text) == [
- "[P1] Validate the bounded implementation."
- ]
- assert archived_evidence in final_text
-
-
-def test_supersede_reprojects_after_generated_successor_is_materialized(
- tmp_path: Path,
-) -> None:
- completed_text = "[P1] Replace the obsolete implementation."
- registry, state = _write_fixture(tmp_path, next_action=completed_text)
- completed = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text=completed_text,
- task_class="advancement_task",
- action_kind="implementation",
- claimed_by=AGENT_ID,
- )
- state.write_text(
- state.read_text(encoding="utf-8").replace(
- f"- {completed_text}\n",
- f"- {completed_text}\n"
- "\n",
- 1,
- ),
- encoding="utf-8",
- )
-
- result = supersede_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=str(completed["todo_id"]),
- reason="replace the obsolete route",
- next_agent_todo="[P1] Implement the corrected route.",
- next_action_kind="implementation",
- next_claimed_by=AGENT_ID,
- agent_id=AGENT_ID,
- )
-
- successor_id = result["next_todos"][0]["todo_id"]
- state_text = state.read_text(encoding="utf-8")
- assert active_state_next_action_entries(state_text) == [
- "[P1] Implement the corrected route."
- ]
- assert f"todo_id={successor_id} -->" in state_text
-
-
def test_complete_preserves_unrelated_owner_next_action(tmp_path: Path) -> None:
registry, state = _write_fixture(
tmp_path,
@@ -539,121 +420,3 @@ def test_complete_preserves_unrelated_owner_next_action(tmp_path: Path) -> None:
assert active_state_next_action_entries(state.read_text(encoding="utf-8")) == [
"Keep the owner-approved release route unchanged."
]
-
-
-def test_complete_waits_for_generated_successor_before_reprojecting(
- tmp_path: Path,
-) -> None:
- completed_text = "[P0] Finish the bounded implementation."
- registry, state = _write_fixture(tmp_path, next_action=completed_text)
- completed = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text=completed_text,
- task_class="advancement_task",
- action_kind="implementation",
- claimed_by=AGENT_ID,
- )
- state.write_text(
- state.read_text(encoding="utf-8").replace(
- f"- {completed_text}\n",
- f"- {completed_text}\n"
- "\n",
- 1,
- ),
- encoding="utf-8",
- )
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=str(completed["todo_id"]),
- next_agent_todo="[P1] Review and validate the implementation.",
- next_action_kind="validation_review",
- next_claimed_by=AGENT_ID,
- agent_id=AGENT_ID,
- evidence="implementation completed",
- )
-
- successor_id = result["next_todos"][0]["todo_id"]
- state_text = state.read_text(encoding="utf-8")
- assert active_state_next_action_entries(state_text) == [
- "[P1] Review and validate the implementation."
- ]
- assert f"todo_id={successor_id} -->" in state_text
-
-
-def test_complete_migrates_legacy_exact_text_next_action(tmp_path: Path) -> None:
- completed_text = "[P1] Validate the legacy project connection."
- registry, state = _write_fixture(tmp_path, next_action=completed_text)
- completed = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text=completed_text,
- task_class="advancement_task",
- action_kind="project_connection_review",
- claimed_by=AGENT_ID,
- )
-
- result = complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=str(completed["todo_id"]),
- no_followup=True,
- agent_id=AGENT_ID,
- evidence="legacy connection preflight passed",
- )
-
- assert result["changed"] is True
- assert active_state_next_action_entries(state.read_text(encoding="utf-8")) == []
-
-
-def test_complete_upgrades_legacy_open_todo_to_typed_binding(
- tmp_path: Path,
-) -> None:
- completed_text = "[P0] Finish the bound implementation."
- registry, state = _write_fixture(tmp_path, next_action=completed_text)
- completed = add_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- role="agent",
- text=completed_text,
- task_class="advancement_task",
- action_kind="implementation",
- claimed_by=AGENT_ID,
- )
- state_text = state.read_text(encoding="utf-8")
- state_text = state_text.replace(
- "## Next Action",
- "- [ ] [P1] Legacy open work without metadata.\n\n## Next Action",
- 1,
- ).replace(
- f"- {completed_text}\n",
- f"- {completed_text}\n"
- "\n",
- 1,
- )
- state.write_text(state_text, encoding="utf-8")
-
- complete_goal_todo(
- registry_path=registry,
- goal_id=GOAL_ID,
- todo_id=str(completed["todo_id"]),
- no_followup=True,
- agent_id=AGENT_ID,
- evidence="implementation completed",
- )
-
- final_text = state.read_text(encoding="utf-8")
- assert active_state_next_action_entries(final_text) == [
- "[P1] Legacy open work without metadata."
- ]
- assert (
- "\n"
- ))
+ "claimed_by=worker explore_result_node_refs="
+ + encode_metadata_value("question")
+ + " -->\n",
+ encoding="utf-8",
+ )
if canonical:
from pathlib import Path
monkeypatch.syspath_prepend(str(Path(__file__).parent / "control_plane"))
diff --git a/tests/test_chat_team_plan_action.py b/tests/test_chat_team_plan_action.py
index ad47d09216..900192f335 100644
--- a/tests/test_chat_team_plan_action.py
+++ b/tests/test_chat_team_plan_action.py
@@ -9,8 +9,12 @@
import pytest
+from control_plane.canonical_authority_fixture import initialize_canonical_authority
from loopx.chat_action_store import ActionConflictError, ChatActionStore
from loopx.chat_actions import ChatActionService
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
GOAL_ID = "team-plan-action-fixture"
AGENT_ID = "agent-alpha"
@@ -60,6 +64,16 @@ def _fixture(tmp_path: Path, *, agents: tuple[str, ...] = (AGENT_ID,)):
),
encoding="utf-8",
)
+ initialize_canonical_authority(
+ tmp_path / "runtime",
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=[],
+ handoff_mode="soft_claim",
+ ),
+ state_path=state_path,
+ )
service = ChatActionService(
store=ChatActionStore(tmp_path / "runtime" / "chat" / "actions"),
registry_path=registry_path,
@@ -411,16 +425,29 @@ def test_an_unchanged_objective_still_confirms_and_records_its_basis(
def test_failed_batch_retries_same_card_without_partial_work(tmp_path: Path, monkeypatch) -> None:
from loopx.control_plane.work_items import team_plan_adapter
+ from loopx.control_plane.coordination.local_authority import (
+ read_canonical_todos_if_promoted,
+ )
+
project, _, service = _fixture(tmp_path)
before = _todos(project)
preview = _preview(service, _two_lane_plan())
- def fail(*args, **kwargs):
- raise OSError("batch unavailable")
- monkeypatch.setattr(team_plan_adapter, "write_captured_todo_state", fail)
+ original_effect = team_plan_adapter.effect_runtime_result
+
+ def fail(method, request):
+ if method == "work_items.team_plan.commit":
+ raise OSError("batch unavailable")
+ return original_effect(method, request)
+
+ monkeypatch.setattr(team_plan_adapter, "effect_runtime_result", fail)
failed = service.apply(preview["proposal_id"])["proposal"]
assert failed["status"] == "failed"
assert failed["failure"]["error_code"] == "team_plan_commit_failed"
assert _todos(project) == before
+ assert read_canonical_todos_if_promoted(
+ runtime_root=project.parent / "runtime",
+ goal_id=GOAL_ID,
+ )["todos"] == []
monkeypatch.undo()
applied = service.apply(preview["proposal_id"])["proposal"]
assert applied["status"] == "applied"
@@ -430,24 +457,36 @@ def fail(*args, **kwargs):
@pytest.mark.parametrize("has_gap", [False, True])
def test_lost_response_recovers_original_commit_after_work_changes(tmp_path: Path, monkeypatch, has_gap: bool) -> None:
+ from loopx.control_plane.coordination.local_authority import (
+ read_canonical_todos_if_promoted,
+ )
from loopx.control_plane.work_items import team_plan_adapter
+
project, _, service = _fixture(tmp_path)
plan = _two_lane_plan()
if has_gap:
plan["lanes"][1]["agent_id"] = "unregistered-agent"
preview = _preview(service, plan)
- real = team_plan_adapter.write_captured_todo_state
- def lose_response(*args, **kwargs):
- real(*args, **kwargs)
- raise OSError("response lost after commit")
- monkeypatch.setattr(team_plan_adapter, "write_captured_todo_state", lose_response)
+ original_effect = team_plan_adapter.effect_runtime_result
+
+ def lose_response(method, request):
+ result = original_effect(method, request)
+ if method == "work_items.team_plan.commit":
+ raise OSError("response lost after commit")
+ return result
+
+ monkeypatch.setattr(team_plan_adapter, "effect_runtime_result", lose_response)
assert service.apply(preview["proposal_id"])["proposal"]["status"] == "failed"
- assert _todos(project).count("loopx:todo ") == (1 if has_gap else 2)
+ committed = read_canonical_todos_if_promoted(
+ runtime_root=project.parent / "runtime",
+ goal_id=GOAL_ID,
+ )
+ assert len(committed["todos"]) == (1 if has_gap else 2)
+ assert _todos(project).count("loopx:todo ") == 0
monkeypatch.undo()
state = project / f".codex/goals/{GOAL_ID}/ACTIVE_GOAL_STATE.md"
# A receiver's later edit must survive historical commit recovery.
- state.write_text(state.read_text().replace("Advance the intake contract", "Receiver revised the work"))
- before = state.read_bytes()
+ state.write_text(state.read_text() + "\nReceiver revised the work.\n")
recovered = service.apply(preview["proposal_id"])["proposal"]
assert recovered["status"] == "applied"
assert recovered["receipt"]["outcome"] == "team_plan_commit_recovered"
@@ -458,7 +497,12 @@ def lose_response(*args, **kwargs):
"agent_id": "unregistered-agent",
"reason_code": "agent_not_registered",
}]
- assert state.read_bytes() == before
+ assert "Receiver revised the work." in state.read_text()
+ assert _todos(project).count("loopx:todo ") == (1 if has_gap else 2)
+ assert read_canonical_todos_if_promoted(
+ runtime_root=project.parent / "runtime",
+ goal_id=GOAL_ID,
+ )["provider_revision"] == committed["provider_revision"]
def test_equal_text_lanes_keep_distinct_identity_and_owner(tmp_path: Path) -> None:
diff --git a/tests/test_cli_argument_diagnostics.py b/tests/test_cli_argument_diagnostics.py
index 428479ce72..3c7e312ad8 100644
--- a/tests/test_cli_argument_diagnostics.py
+++ b/tests/test_cli_argument_diagnostics.py
@@ -777,6 +777,27 @@ def test_todo_update_validation_accepts_a_validator_revision() -> None:
["--todo-id", "todo_example", "--no-follow-up"],
"--no-follow-up requires --note or --evidence",
),
+ (
+ [
+ "--todo-id",
+ "todo_example",
+ "--completion-identity-key",
+ "local_completion_example",
+ ],
+ "--completion-identity-key is only valid with --no-follow-up",
+ ),
+ (
+ [
+ "--todo-id",
+ "todo_example",
+ "--completion-identity-key",
+ "local_completion_example",
+ "--no-follow-up",
+ "--evidence",
+ "replacement evidence",
+ ],
+ "--completion-identity-key reentry preserves the original note and evidence",
+ ),
(
["--todo-id", "todo_example", "--continuation-policy", "same_agent_non_delivery"],
"todo complete does not update --continuation-policy; use todo update first",
diff --git a/tests/test_delegation_lease_lifetime.py b/tests/test_delegation_lease_lifetime.py
index 850fc39950..32c58634a5 100644
--- a/tests/test_delegation_lease_lifetime.py
+++ b/tests/test_delegation_lease_lifetime.py
@@ -435,3 +435,39 @@ def test_real_stop_leaves_a_newer_generation_alone(service, monkeypatch):
assert receipt["stop"]["settled"]["lease"] == "not_owed"
held = inspect(runner)["lease"]
assert held["status"] == "active" and held["idempotency_key"] == "new-execution"
+
+
+@pytest.mark.parametrize("status", ["prepared", "running"])
+def test_retired_policy_blocks_original_delegation_before_host_launch(service, monkeypatch, status):
+ root, runner = service
+ monkeypatch.setattr(runner, "_spawn", lambda _: None)
+ operation_id = "retired-policy-" + status
+ runner.start("analysis", operation_id, brief())
+ path = runner.path(operation_id)
+ row = _read(path)
+ if status == "running":
+ row["status"] = status
+ row["task_lease"] = {"required": False, "handoff_mode": "legacy"}
+ runner._fenced_write(path, row)
+ # Original operation records may outlive a pre-cutoff ownership snapshot.
+ # Install those historical facts only in this disposable test provider.
+ program = """
+import {openLocalAuthorityStore} from "./loopx/control_plane/coordination/local_authority_provider.ts";
+const [root, goal] = process.argv.slice(1);
+const store = await openLocalAuthorityStore(root, goal);
+const state = await store.loadAuthority();
+const result = await store.commitAuthority({expected_provider_revision: state.provider_revision,
+ operation_id: "retained-policy-fixture", events: [], receipts: [],
+ next_projection: {...state.head, handoff_mode: "legacy"}});
+if (result.status !== "applied") throw new Error(JSON.stringify(result));
+"""
+ installed = subprocess.run(["node", "--no-warnings", "--experimental-sqlite", "--experimental-strip-types",
+ "--input-type=module", "-e", program, str(runner.root), runner.goal_id],
+ capture_output=True, text=True, timeout=30)
+ assert installed.returncode == 0, installed.stderr
+ before = read_canonical_todos_if_promoted(runtime_root=runner.root, goal_id=runner.goal_id, include_leases=True)
+ with pytest.raises(ValueError, match="historical only"):
+ runner._execute(path, row, runner.binding("analysis"))
+ assert not list(root.rglob("host-invocations")), "an old no-lease record must not start its model Host"
+ assert read_canonical_todos_if_promoted(runtime_root=runner.root, goal_id=runner.goal_id, include_leases=True) == before
+ assert _read(path)["status"] == status
diff --git a/tests/test_delegation_preflight.py b/tests/test_delegation_preflight.py
index 16812e7e9a..abf76b6120 100644
--- a/tests/test_delegation_preflight.py
+++ b/tests/test_delegation_preflight.py
@@ -737,15 +737,26 @@ def rejected_turn(_binding, *args, **_kwargs):
def test_hard_lease_delegation_claims_before_host_launch(service, monkeypatch):
- from loopx import collaboration_mcp as delegation
-
- _, runner = service
+ root, runner = service
+ # Set up this new disposable canonical fixture through its actual store.
+ # Public policy migration has separate quiescence/backup requirements;
+ # patching the retired display reader would bypass typed lease inspection.
+ program = '''
+import {openLocalAuthorityStore} from "./loopx/control_plane/coordination/local_authority_provider.ts";
+const [root,goal] = process.argv.slice(1);
+const store = await openLocalAuthorityStore(root,goal);
+const state = await store.loadAuthority();
+const committed = await store.commitAuthority({expected_provider_revision:state.provider_revision,
+ operation_id:"fixture-hard-lease-dispatch",events:[],receipts:[],
+ next_projection:{...state.head,handoff_mode:"hard_lease"}});
+if(committed.status!=="applied") throw new Error(JSON.stringify(committed));
+'''
+ prepared = subprocess.run([
+ "node", "--no-warnings", "--experimental-sqlite", "--experimental-strip-types",
+ "--input-type=module", "-e", program, str(runner.root), runner.goal_id,
+ ], capture_output=True, text=True, timeout=30)
+ assert prepared.returncode == 0, prepared.stderr
monkeypatch.setattr(runner, "_spawn", lambda _: None)
- monkeypatch.setattr(
- delegation,
- "show_goal_handoff_mode",
- lambda **_kwargs: {"handoff_mode": "hard_lease"},
- )
runner.start("analysis", "leased-dispatch", brief={
"schema_version": "collaboration_brief_v0",
"purpose": "Exercise an atomic hard-lease dispatch",
@@ -798,6 +809,7 @@ def canonical(_binding, *args, **_kwargs):
assert runner._delegated_lease_context(row, runner.binding("analysis"))["ttl_seconds"] is None
assert result["status"] == "rejected"
assert result["error"] == "stop after lease evidence"
+ assert not (root / "analyst" / "initial" / "host-invocations").exists()
def test_exact_validated_turn_can_reopen_a_false_terminal_observation(
diff --git a/tests/test_delegation_result_use.py b/tests/test_delegation_result_use.py
index 2995ff785e..6935c896aa 100644
--- a/tests/test_delegation_result_use.py
+++ b/tests/test_delegation_result_use.py
@@ -234,10 +234,20 @@ def test_deadline_after_link_qualification_blocks_admission_and_adoption(tmp_pat
qualify = module._ResultUseRead.qualify
def expire_after_link(self, roots):
result = qualify(self, roots)
- elapsed[0] = 16
+ elapsed[0] = 14
return result
monkeypatch.setattr(module._ResultUseRead, "qualify", expire_after_link)
links, aggregate = module._dependency_observation(runner, {}, request)
+ assert aggregate["state"] == "current"
+ assert links[0]["state"] == "current"
+
+ def exceed_budget_after_link(self, roots):
+ result = qualify(self, roots)
+ elapsed[0] = 16
+ return result
+ monkeypatch.setattr(module._ResultUseRead, "qualify", exceed_budget_after_link)
+ elapsed[0] = 0
+ links, aggregate = module._dependency_observation(runner, {}, request)
assert aggregate["reason"] == "verification_budget_exhausted"
assert links[0]["state"] == "unavailable"
elapsed[0] = 0
diff --git a/tests/test_goal_mode_mcp_completion_validation.py b/tests/test_goal_mode_mcp_completion_validation.py
index 2da1e2e47c..9ab079898b 100644
--- a/tests/test_goal_mode_mcp_completion_validation.py
+++ b/tests/test_goal_mode_mcp_completion_validation.py
@@ -7,9 +7,15 @@
import pytest
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
from loopx.goal_mode_mcp import GoalModeMCPConfig, GoalModeMCPControlPlane
from loopx.status import parse_active_state_todos
from loopx.todos import add_goal_todo
+from tests.control_plane.canonical_authority_fixture import (
+ initialize_canonical_authority,
+)
GOAL_ID = "mcp-completion-validation"
AGENT = "claude"
@@ -20,6 +26,7 @@
def _write_fixture(tmp_path: Path) -> tuple[Path, Path]:
repo = tmp_path / "repo"
repo.mkdir()
+ runtime = tmp_path / "runtime"
state = repo / "ACTIVE_GOAL_STATE.md"
state.write_text(
"\n".join(
@@ -40,7 +47,7 @@ def _write_fixture(tmp_path: Path) -> tuple[Path, Path]:
registry.write_text(
json.dumps(
{
- "common_runtime_root": str(tmp_path / "runtime"),
+ "common_runtime_root": str(runtime),
"goals": [
{
"id": GOAL_ID,
@@ -62,6 +69,16 @@ def _write_fixture(tmp_path: Path) -> tuple[Path, Path]:
),
encoding="utf-8",
)
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=[],
+ handoff_mode="soft_claim",
+ ),
+ state_path=state,
+ )
return registry, state
@@ -123,7 +140,10 @@ def test_mcp_complete_task_fails_closed_on_failing_declared_validation(
assert payload["ok"] is False
assert payload["completed"] is False
assert payload["validation_blocked_completion"] is True
- assert payload["validation"]["passed"] is False
+ assert (
+ payload["validation_failure"]["validation_receipt"]["passed"]
+ is False
+ )
assert "spend-slot" not in output
assert "refresh-state" not in output
assert _agent_todo_status(state, todo_id) != "done"
diff --git a/tests/test_goal_mode_mcp_settlement.py b/tests/test_goal_mode_mcp_settlement.py
index 5a76e5e292..3a8d84284d 100644
--- a/tests/test_goal_mode_mcp_settlement.py
+++ b/tests/test_goal_mode_mcp_settlement.py
@@ -13,6 +13,9 @@
host_adapter_turn_instance_id,
settle_host_todo_completion,
)
+from loopx.control_plane.coordination.goal_storage_initialization import (
+ initialize_goal_storage_target,
+)
from loopx.goal_mode_mcp import GoalModeMCPConfig, GoalModeMCPControlPlane
from loopx.status import parse_active_state_todos
from loopx.todos import add_goal_todo
@@ -196,6 +199,7 @@ def _write_fixture(tmp_path: Path) -> tuple[Path, Path]:
"goals": [
{
"id": GOAL_ID,
+ "creation_operation_id": f"goal-create:{GOAL_ID}",
"domain": "harness_self_improvement",
"status": "active",
"repo": str(project),
@@ -212,6 +216,11 @@ def _write_fixture(tmp_path: Path) -> tuple[Path, Path]:
"coordination": {
"agent_model": "peer_v1",
"registered_agents": [AGENT_ID],
+ "storage_target": {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": "file",
+ "handoff_mode": "soft_claim",
+ },
},
}
],
@@ -219,6 +228,14 @@ def _write_fixture(tmp_path: Path) -> tuple[Path, Path]:
),
encoding="utf-8",
)
+ goal = json.loads(registry.read_text(encoding="utf-8"))["goals"][0]
+ initialized = initialize_goal_storage_target(
+ tmp_path / "runtime",
+ goal,
+ registry_path=registry,
+ )
+ assert initialized["authority_initialized"] is True
+ assert initialized["legacy_fallback_used"] is False
return registry, state_file
diff --git a/tests/test_kiro_cli_mcp.py b/tests/test_kiro_cli_mcp.py
index 93b297b6fe..af00cddd6f 100644
--- a/tests/test_kiro_cli_mcp.py
+++ b/tests/test_kiro_cli_mcp.py
@@ -21,6 +21,9 @@
import pytest
from loopx import slash_command_install
+from loopx.control_plane.coordination.goal_storage_initialization import (
+ initialize_goal_storage_target,
+)
from loopx.goal_mode_mcp import GoalModeMCPControlPlane
from loopx.kiro_cli_goal_mode import (
KIRO_CLI_MCP_CONFIG_SUBPATH,
@@ -62,6 +65,7 @@ def _write_project(
"goals": [
{
"id": GOAL_ID,
+ "creation_operation_id": f"goal-create:{GOAL_ID}",
"domain": "harness_self_improvement",
"status": "active",
"repo": str(project),
@@ -80,6 +84,11 @@ def _write_project(
# A different agent first: resolution must never
# fall back to registry order.
"registered_agents": [OTHER_AGENT_ID, AGENT_ID],
+ "storage_target": {
+ "schema_version": "loopx_new_goal_storage_target_v1",
+ "provider": "file",
+ "handoff_mode": "soft_claim",
+ },
"thread_agent_bindings": (
bindings
if bindings is not None
@@ -98,6 +107,14 @@ def _write_project(
),
encoding="utf-8",
)
+ goal = json.loads(registry.read_text(encoding="utf-8"))["goals"][0]
+ initialized = initialize_goal_storage_target(
+ tmp_path / "runtime",
+ goal,
+ registry_path=registry,
+ )
+ assert initialized["authority_initialized"] is True
+ assert initialized["legacy_fallback_used"] is False
return project, registry, state_file
diff --git a/tests/test_lark_private_progress.py b/tests/test_lark_private_progress.py
index 68c55b07c7..c7c9c4a618 100644
--- a/tests/test_lark_private_progress.py
+++ b/tests/test_lark_private_progress.py
@@ -339,7 +339,7 @@ def test_lost_edit_ack_recovered_by_readback_then_exact_stop_closes_draft(ordina
def test_literal_mentions_use_exact_wire_proof_through_update_replay_and_final(ordinary): # noqa: F811
fake = ordinary[-2]
final = "完整结果:@Fixture 与 @Later 都是普通文字。"
- fake.write_text(fake.read_text().replace('"message": "Steered response.",', f'"message": {final!r},'))
+ fake.write_text(fake.read_text().replace('"message": "Steered response.",', f'"message": {ascii(final)},'))
store, runtime, provider, transport, row = start(ordinary, first_text="阅读 @Fixture。\n")
try:
sid, tid = row["session_id"], row["turn_id"]
@@ -373,7 +373,7 @@ def test_literal_mentions_use_exact_wire_proof_through_update_replay_and_final(o
def test_full_canonical_final_replaces_bounded_draft_without_truncation(ordinary): # noqa: F811
final = "**完整结果**\n\n" + "保留每一段证据。\n" * 850
fake = ordinary[-2]
- fake.write_text(fake.read_text().replace('"message": "Steered response.",', f'"message": {final!r},'))
+ fake.write_text(fake.read_text().replace('"message": "Steered response.",', f'"message": {ascii(final)},'))
store, runtime, provider, transport, row = start(ordinary)
try:
sid, tid = row["session_id"], row["turn_id"]
diff --git a/tests/test_loopx_turn_driver.py b/tests/test_loopx_turn_driver.py
index 3c9dd51de5..6231a45be5 100644
--- a/tests/test_loopx_turn_driver.py
+++ b/tests/test_loopx_turn_driver.py
@@ -43,7 +43,6 @@
)
from loopx.control_plane.turn_driver.executor import BuiltInHostError
from loopx.control_plane.quota.live_decision import bind_scheduler_followup_cli_routes
-from loopx.todos import complete_goal_todo
def _envelope(
@@ -1388,6 +1387,7 @@ def _write_live_fixture(
*,
todo_metadata_extra: str = "",
extra_agent_todo_lines: tuple[str, ...] = (),
+ provider: str = "file",
) -> tuple[Path, Path, Path]:
project = root / "project"
runtime = root / "runtime"
@@ -1462,44 +1462,23 @@ def _write_live_fixture(
+ "\n",
encoding="utf-8",
)
- return project, runtime, registry
-
-
-def _promote_turn_fixture(project: Path, runtime: Path) -> None:
- state = (
- project
- / ".codex"
- / "goals"
- / "loopx-turn-fixture"
- / "ACTIVE_GOAL_STATE.md"
+ parsed = parse_active_state_todos(
+ state.read_text(encoding="utf-8"),
+ item_limit=None,
)
projection = build_todo_runtime_shadow_projection(
goal_id="loopx-turn-fixture",
handoff_mode="soft_claim",
- todos=[
- {
- "schema_version": "todo_item_v0",
- "index": 1,
- "done": False,
- "text": "[P0] Advance one public fixture.",
- "todo_id": "todo_fixture0001",
- "role": "agent",
- "status": "open",
- "archive_state": "active",
- "source_section": "Agent Todo",
- "task_class": "advancement_task",
- "action_kind": "fixture",
- "claimed_by": "codex-fixture",
- "priority": "P0",
- }
- ],
+ todos=parsed["agent_todos"]["items"],
)
initialize_canonical_authority(
runtime,
"loopx-turn-fixture",
projection,
state_path=state,
+ provider=provider,
)
+ return project, runtime, registry
def test_quota_cli_projects_outer_controller_without_codex_app_action(
@@ -2351,17 +2330,10 @@ def test_turn_run_once_cli_dsh_preserves_material_path_delta(
tmp_path: Path, malformed_packet: bool,
) -> None:
project, runtime, registry = _write_live_fixture(
- tmp_path, todo_metadata_extra="no_followup=true"
+ tmp_path,
+ todo_metadata_extra="no_followup=true",
)
state_path = project / ".codex/goals/loopx-turn-fixture/ACTIVE_GOAL_STATE.md"
- parsed = parse_active_state_todos(state_path.read_text(encoding="utf-8"), item_limit=None)
- projection = build_todo_runtime_shadow_projection(
- goal_id="loopx-turn-fixture", handoff_mode="soft_claim",
- todos=parsed["agent_todos"]["items"],
- )
- initialize_canonical_authority(
- runtime, "loopx-turn-fixture", projection, state_path=state_path
- )
host_project = project / "isolated-dsh-workspace"
host_project.mkdir()
vision = {
@@ -2595,17 +2567,6 @@ def test_turn_run_once_cli_completes_selected_todo_after_validation(
next_obligation = next_plan["turn_envelope"]["replan_action_packet"]
assert next_obligation["decision"] == "replan_required"
- recovered_completion = complete_goal_todo(
- registry_path=registry,
- goal_id="loopx-turn-fixture",
- todo_id="todo_fixture0001",
- role="agent",
- agent_id="codex-fixture",
- completion_turn_key=payload["resume_turn_key"],
- )
- assert recovered_completion["idempotent_replay"] is True
- assert recovered_completion["changed"] is False
-
replayed_output = io.StringIO()
with contextlib.redirect_stdout(replayed_output):
replayed_exit_code = cli_main(
@@ -2627,7 +2588,6 @@ def test_promoted_turn_completion_replays_after_commit_before_journal_crash(
monkeypatch: pytest.MonkeyPatch,
) -> None:
project, runtime, registry = _write_live_fixture(tmp_path)
- _promote_turn_fixture(project, runtime)
host_project = project / "isolated-host-workspace"
host_project.mkdir()
host_script, validation_script = _completion_host_and_validation_scripts()
@@ -2956,13 +2916,13 @@ def test_turn_run_once_cli_projects_durable_no_followup_continuation(
@pytest.mark.parametrize(
- "durable_completion_turn_key",
+ "display_completion_turn_key",
[None, "sha256:different-turn"],
ids=["missing-turn-key", "different-turn-key"],
)
-def test_turn_run_once_cli_terminal_recovery_rejects_unowned_completion(
+def test_turn_run_once_cli_terminal_recovery_ignores_stale_display_identity(
tmp_path: Path,
- durable_completion_turn_key: str | None,
+ display_completion_turn_key: str | None,
) -> None:
project, runtime, registry = _write_live_fixture(
tmp_path,
@@ -2997,8 +2957,8 @@ def test_turn_run_once_cli_terminal_recovery_rejects_unowned_completion(
assert current_turn_field in state
replacement = (
""
- if durable_completion_turn_key is None
- else f" completion_turn_key={durable_completion_turn_key}"
+ if display_completion_turn_key is None
+ else f" completion_turn_key={display_completion_turn_key}"
)
state_path.write_text(
state.replace(current_turn_field, replacement),
@@ -3067,19 +3027,32 @@ def test_turn_run_once_cli_terminal_recovery_rejects_unowned_completion(
)
resumed = json.loads(resumed_output.getvalue())
- assert resumed_exit_code == 1, resumed
- assert resumed["settlement_result"]["failure"] == {
- "kind": "effect_outcome_unknown",
- "step_kind": "terminal_closeout",
- "reason": "closeout readback failed",
+ assert resumed_exit_code == 0, resumed
+ assert resumed["status"] == "committed"
+ assert resumed["settlement_result"]["failure"] is None
+ assert not any(resumed["effects"].values())
+ assert resumed["todo_completion"] == {
+ "todo_id": "todo_fixture0001",
+ "continuation": "no_followup",
}
recovered_events = [
json.loads(line)
for line in event_path.read_text(encoding="utf-8").splitlines()
]
+ terminal_events = [
+ event
+ for event in recovered_events
+ if (
+ event.get("event_kind") == "todo_complete"
+ and event.get("run_id") == turn_key
+ )
+ ]
+ assert len(terminal_events) == 1
+ assert terminal_events[0]["status"] == "terminal_no_followup"
+ assert terminal_events[0]["details"]["command"] == "turn recovery probe"
assert not any(
event.get("event_kind") == "todo_complete"
- and event.get("run_id") == turn_key
+ and event.get("run_id") != turn_key
for event in recovered_events
)
diff --git a/tests/test_loopx_turn_executor.py b/tests/test_loopx_turn_executor.py
index 915ea98d54..0eed761ed1 100644
--- a/tests/test_loopx_turn_executor.py
+++ b/tests/test_loopx_turn_executor.py
@@ -712,10 +712,11 @@ def write_source_journal() -> None:
assert calls == {"writeback": 0, "spend": 0, "scheduler": 0}
-def test_host_result_requires_bounded_public_material_fields() -> None:
+@pytest.mark.parametrize("field", ["raw_trajectory", "agent_vision"])
+def test_host_result_requires_bounded_public_material_fields(field: str) -> None:
plan = _plan()
result = _host_result(plan)
- result["raw_trajectory"] = "not allowed"
+ result[field] = "not allowed"
validation = validate_loopx_turn_host_result(plan, result)
@@ -723,6 +724,40 @@ def test_host_result_requires_bounded_public_material_fields() -> None:
assert "unsupported host result fields" in " ".join(validation["errors"])
+@pytest.mark.parametrize("packet, expected_error", [
+ (None, "cached agent_vision must be a JSON object"),
+ ([], "cached agent_vision must be a JSON object"),
+ ("not an object", "cached agent_vision must be a JSON object"),
+ ({"vision_patch": {"acceptance_summary": "x" * 421}}, "acceptance_summary"),
+])
+def test_cached_host_result_revalidates_decoded_vision(
+ packet: object, expected_error: str,
+) -> None:
+ plan = _plan()
+ result = _host_result(plan)
+ result.update(result_kind="replan_required", path_delta_mode="material_replan",
+ agent_vision=packet)
+ result.pop("vision_unchanged_reason", None)
+
+ validation = validate_loopx_turn_host_result(plan, result, from_journal=True)
+
+ assert validation["ok"] is False
+ assert expected_error in " ".join(validation["errors"])
+
+
+def test_cached_host_result_rejects_conflicting_vision_forms() -> None:
+ plan = _plan()
+ result = _host_result(plan)
+ result.update(result_kind="replan_required", path_delta_mode="material_replan",
+ agent_vision={}, agent_vision_json="{}")
+ result.pop("vision_unchanged_reason", None)
+
+ validation = validate_loopx_turn_host_result(plan, result, from_journal=True)
+
+ assert validation["ok"] is False
+ assert "cached vision cannot carry both packet forms" in " ".join(validation["errors"])
+
+
def test_child_receipt_schema_excludes_registered_peer_authority() -> None:
schema = child_execution_receipts_json_schema()
item_schema = schema["items"]
diff --git a/tests/test_native_goal_release_qualification.py b/tests/test_native_goal_release_qualification.py
index 6d476d3494..df2345f5c0 100644
--- a/tests/test_native_goal_release_qualification.py
+++ b/tests/test_native_goal_release_qualification.py
@@ -127,20 +127,23 @@ def test_synthetic_fixture_real_cli_projects_identity_reentry(tmp_path):
assert len(actions) == 1 and "--turn-instance-id" in actions[0]
assert "spend-slot" not in actions[0]
- # Qualify the fixture's terminal shape without paying for a model run. A
- # missing user section is not proof of zero user obligations.
+ # The Markdown file is only the display projection after migration. A
+ # direct edit cannot fabricate canonical completion or Goal terminality.
state = project / "ACTIVE_GOAL_STATE.md"
state.write_text(state.read_text().replace("- [ ]", "- [x]").replace(
"status=open", "status=done no_followup=true",
))
- terminal = runner.cli(launcher, "quota", "should-run", "--runtime-profile", "codex_cli",
- "--goal-id", runner.GOAL, "--agent-id", runner.AGENT)
- assert terminal["should_run"] is False
- assert terminal["interaction_contract"]["mode"] == "terminal_no_followup"
+ unchanged = runner.cli(launcher, "quota", "should-run", "--runtime-profile", "codex_cli",
+ "--goal-id", runner.GOAL, "--agent-id", runner.AGENT)
+ assert unchanged["should_run"] is True
+ assert unchanged["selected_todo"]["todo_id"] == "todo_reducer"
+ assert unchanged["interaction_contract"]["mode"] != "terminal_no_followup"
state.write_text(state.read_text().replace("## User Todo\n\n", ""))
incomplete = runner.cli(launcher, "quota", "should-run", "--runtime-profile", "codex_cli",
"--goal-id", runner.GOAL, "--agent-id", runner.AGENT)
+ assert incomplete["should_run"] is True
+ assert incomplete["selected_todo"]["todo_id"] == "todo_reducer"
assert incomplete["interaction_contract"]["mode"] != "terminal_no_followup"
diff --git a/tests/test_steward_team_plan_apply.py b/tests/test_steward_team_plan_apply.py
index 910e95817b..01a3663186 100644
--- a/tests/test_steward_team_plan_apply.py
+++ b/tests/test_steward_team_plan_apply.py
@@ -8,6 +8,10 @@
import pytest
+from control_plane.canonical_authority_fixture import initialize_canonical_authority
+from loopx.control_plane.coordination.runtime_shadow import (
+ build_todo_runtime_shadow_projection,
+)
from loopx.control_plane.work_items.governed_transition_proposal import (
GovernedTransitionSettlementPhase,
settle_governed_transition_proposals,
@@ -71,6 +75,16 @@ def _fixture(
),
encoding="utf-8",
)
+ initialize_canonical_authority(
+ runtime,
+ GOAL_ID,
+ build_todo_runtime_shadow_projection(
+ goal_id=GOAL_ID,
+ todos=[],
+ handoff_mode="soft_claim",
+ ),
+ state_path=state_path,
+ )
return project, registry_path
@@ -110,30 +124,6 @@ def _basis(registry_path: Path) -> str:
)
-def test_legacy_registry_stays_locked_through_plan_and_write(tmp_path, monkeypatch):
- from loopx.file_lock import exclusive_cross_runtime_file_lock, LockAcquireTimeoutError
- from loopx.control_plane.work_items import team_plan_adapter
-
- _project, registry = _fixture(tmp_path)
- original_effect = team_plan_adapter.effect_runtime_result
- checked = []
-
- def inspect(method, request):
- if method == "work_items.team_plan.plan":
- with pytest.raises(LockAcquireTimeoutError):
- with exclusive_cross_runtime_file_lock(registry, timeout_seconds=0):
- pass
- checked.append(True)
- return original_effect(method, request)
-
- monkeypatch.setattr(team_plan_adapter, "effect_runtime_result", inspect)
- result = _settle(registry, _proposal())
- assert result[0]["status"] == "committed", result
- assert checked == [True]
- with exclusive_cross_runtime_file_lock(registry, timeout_seconds=0):
- pass
-
-
def _settle(
registry_path: Path,
proposal: dict,
@@ -651,11 +641,17 @@ def test_a_failed_receipt_carries_a_typed_verdict_and_no_lane_identity() -> None
def test_batch_write_failure_leaves_every_lane_unwritten(tmp_path: Path, monkeypatch) -> None:
from loopx.control_plane.work_items import team_plan_adapter
+
project, registry_path = _fixture(tmp_path, agents=(AGENT_ID, "agent-beta"))
before = _todos(project)
- def fail(*args, **kwargs):
- raise OSError("batch write unavailable")
- monkeypatch.setattr(team_plan_adapter, "write_captured_todo_state", fail)
+ original_effect = team_plan_adapter.effect_runtime_result
+
+ def fail(method, request):
+ if method == "work_items.team_plan.commit":
+ raise OSError("batch write unavailable")
+ return original_effect(method, request)
+
+ monkeypatch.setattr(team_plan_adapter, "effect_runtime_result", fail)
with pytest.raises(OSError, match="batch write unavailable"):
_settle(registry_path, _proposal(extra_lane=_second_lane()))
assert _todos(project) == before
diff --git a/tests/test_todo_write_serialization.py b/tests/test_todo_write_serialization.py
deleted file mode 100644
index cb51c74d35..0000000000
--- a/tests/test_todo_write_serialization.py
+++ /dev/null
@@ -1,152 +0,0 @@
-from __future__ import annotations
-
-from concurrent.futures import ThreadPoolExecutor
-from contextlib import contextmanager
-import json
-from pathlib import Path
-from threading import Barrier, Lock
-from typing import Any, Iterator
-
-import pytest
-
-from loopx.control_plane.coordination import legacy_writer_fence
-from loopx.control_plane.coordination.legacy_writer_fence import (
- legacy_coordination_todo_lock_path,
-)
-from loopx.status import parse_active_state_todos
-from loopx.todos import add_goal_todo, update_goal_todo
-
-
-GOAL_ID = "todo-write-serialization"
-AGENT_ID = "fixture-agent"
-INITIAL_TODO = "Update this Todo while another Todo is added."
-CONCURRENT_TODO = "Preserve this concurrently added Todo."
-UPDATE_REASON = "waiting-for-concurrent-dependency"
-
-
-def _write_fixture(tmp_path: Path) -> tuple[Path, Path, Path]:
- project = tmp_path / "project"
- project.mkdir()
- state_file = project / "ACTIVE_GOAL_STATE.md"
- state_file.write_text(
- "---\n"
- f"goal_id: {GOAL_ID}\n"
- "updated_at: 2026-09-07T00:00:00+00:00\n"
- "---\n\n"
- "## Agent Todo\n\n",
- encoding="utf-8",
- )
- runtime_root = tmp_path / "runtime"
- registry_path = tmp_path / "registry.global.json"
- registry_path.write_text(
- json.dumps(
- {
- "common_runtime_root": str(runtime_root),
- "goals": [
- {
- "id": GOAL_ID,
- "domain": "todo-write-serialization-fixture",
- "status": "active",
- "repo": str(project),
- "state_file": state_file.name,
- "adapter": {"kind": "fixture"},
- "coordination": {
- "agent_model": "peer_v1",
- "registered_agents": [AGENT_ID],
- },
- }
- ],
- }
- ),
- encoding="utf-8",
- )
- return registry_path, state_file, runtime_root
-
-
-def test_concurrent_add_and_update_share_the_goal_todo_write_lock(
- tmp_path: Path,
- monkeypatch: pytest.MonkeyPatch,
-) -> None:
- registry_path, state_file, runtime_root = _write_fixture(tmp_path)
- target = add_goal_todo(
- registry_path=registry_path,
- goal_id=GOAL_ID,
- role="agent",
- text=INITIAL_TODO,
- task_class="advancement_task",
- claimed_by=AGENT_ID,
- )
- target_id = str(target["todo_id"])
- goal_lock_path = legacy_coordination_todo_lock_path(
- runtime_root=runtime_root,
- goal_id=GOAL_ID,
- )
-
- arrivals = Barrier(2)
- records_guard = Lock()
- locks_by_path: dict[Path, Lock] = {}
- lock_attempts: list[tuple[Path, str | None]] = []
-
- @contextmanager
- def deterministic_lock(
- path: Path,
- **kwargs: Any,
- ) -> Iterator[Path]:
- with records_guard:
- path_lock = locks_by_path.setdefault(path, Lock())
- lock_attempts.append((path, kwargs.get("operation")))
- if path == goal_lock_path:
- arrivals.wait(timeout=5)
- with path_lock:
- yield path
-
- monkeypatch.setattr(
- legacy_writer_fence,
- "exclusive_cross_runtime_file_lock",
- deterministic_lock,
- )
-
- with ThreadPoolExecutor(max_workers=2) as executor:
- add_future = executor.submit(
- add_goal_todo,
- registry_path=registry_path,
- goal_id=GOAL_ID,
- role="agent",
- text=CONCURRENT_TODO,
- task_class="advancement_task",
- claimed_by=AGENT_ID,
- )
- update_future = executor.submit(
- update_goal_todo,
- registry_path=registry_path,
- goal_id=GOAL_ID,
- todo_id=target_id,
- status="blocked",
- reason=UPDATE_REASON,
- agent_id=AGENT_ID,
- )
- added = add_future.result(timeout=10)
- updated = update_future.result(timeout=10)
-
- assert added["added"] is True, added
- assert updated["changed"] is True, updated
- assert [attempt for attempt in lock_attempts if attempt[0] == goal_lock_path] == [
- (goal_lock_path, "legacy_coordination_todo_write"),
- (goal_lock_path, "legacy_coordination_todo_write"),
- ]
- assert sorted(
- operation
- for path, operation in lock_attempts
- if path == state_file
- ) == ["todo_add", "todo_update"]
-
- parsed = parse_active_state_todos(state_file.read_text(encoding="utf-8"))
- items = parsed["agent_todos"]["items"]
- todo_ids = [str(item["todo_id"]) for item in items]
- assert len(todo_ids) == len(set(todo_ids)), items
-
- by_id = {str(item["todo_id"]): item for item in items}
- assert str(added["todo_id"]) in by_id, items
- assert by_id[str(added["todo_id"])]["text"] == f"[P1] {CONCURRENT_TODO}"
- assert by_id[target_id]["status"] == "blocked"
- assert by_id[target_id]["reason"] == UPDATE_REASON
diff --git a/tsconfig.control-plane.json b/tsconfig.control-plane.json
index a009df79e7..4d7905a462 100644
--- a/tsconfig.control-plane.json
+++ b/tsconfig.control-plane.json
@@ -93,8 +93,6 @@
"tests/control_plane_ts/task_lease_acquire.test.ts",
"tests/control_plane_ts/canonical_task_lease_renew.test.ts",
"tests/control_plane_ts/task_lease_acquire_cli.test.ts",
- "tests/control_plane_ts/legacy_writer_fence_caller_parity_support.ts",
- "tests/control_plane_ts/legacy_writer_fence_caller_parity.test.ts",
"tests/control_plane_ts/legacy_writer_fence_read_error.test.ts",
"tests/control_plane_ts/turn_journal.test.ts",
"tests/control_plane_ts/turn_journal_query.test.ts",