From 86936e4763371c27f7b88388b8c0207768b08217 Mon Sep 17 00:00:00 2001 From: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> Date: Tue, 3 Nov 2026 01:28:48 +0800 Subject: [PATCH] fix(todos): report the canonical source in readback Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com> --- loopx/control_plane/todos/read_transaction.ts | 4 ++-- .../references/repair-patterns.md | 1 + .../test_todo_list_read_transport.py | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/loopx/control_plane/todos/read_transaction.ts b/loopx/control_plane/todos/read_transaction.ts index 1c636a9280..64af374076 100644 --- a/loopx/control_plane/todos/read_transaction.ts +++ b/loopx/control_plane/todos/read_transaction.ts @@ -1,6 +1,6 @@ /** Canonical provider IO and full-source summaries share one read transaction. */ import type {JsonObject} from "../effect_program.ts"; -import {requireBoolean, requireJsonObject} from "../runtime_decode.ts"; +import {requireBoolean, requireJsonObject, requireNonEmptyString} from "../runtime_decode.ts"; import {EffectRuntimeRequestError} from "../effect_runtime_errors.ts"; import {readCanonicalSnapshot} from "../coordination/canonical_snapshot_page.ts"; import {readLocalCoordinationTodoSource} from "../coordination/local_authority_read.ts"; @@ -63,7 +63,7 @@ async function todoListPayload(input: JsonObject, original: JsonObject[], fields if (fullText && (query.thin === true || query.json_references === true || summary)) throw new EffectRuntimeRequestError("Full Todo body reads cannot use compact presentation"); if (summary && (query.thin || query.todo_id)) throw new EffectRuntimeRequestError("Todo summary cannot be combined with thin or exact reads"); - const sourceName = "file_authority"; + const sourceName = requireNonEmptyString(canonical.source_authority, "canonical Todo source authority"); const roles: unknown[] = query.role == null ? ["user", "agent"] : [query.role]; const select = async (source: JsonObject) => selectTodoReadFields({...input, fields: source.fields, field_evaluations: source.field_evaluations, roles, status: query.status ?? null, diff --git a/skills/loopx-self-repair/references/repair-patterns.md b/skills/loopx-self-repair/references/repair-patterns.md index 7d8de58837..763ebda6a7 100644 --- a/skills/loopx-self-repair/references/repair-patterns.md +++ b/skills/loopx-self-repair/references/repair-patterns.md @@ -5,6 +5,7 @@ teaches a reusable control-plane lesson. | Pattern | Symptoms | Evidence To Read | Likely Root | Durable Repair | | --- | --- | --- | --- | --- | +| `todo_read_source_authority_label` | A canonical Todo read reports one provider in `authority_read` but labels the public result or projection source as File. | The same read's canonical provider revision, public source, envelope sources and Markdown across full, filtered, exact and compact views. | The shared formatter retained a provider-specific constant after provider routing moved to the canonical reader. | Reuse the existing canonical source authority for presentation and provenance. Exercise real CLI File/SQLite reads, missing results and source failure/recovery; preserve filtering, read-only behavior and fail-closed routing. A display correction does not select, migrate or qualify a provider. | | `async_command_completion_metadata_lost` | A code cell appears complete with empty stdout, then the agent searches saved files or reissues a stateful command without checking whether its child is still running. | The original tool result's running handle, terminal exit code and stderr; caller projection code; the same command's completion; original operation/capture identity. Keep empty success, empty failure and a pending command distinct. | The caller forwarded only stdout and dropped the command tool's completion metadata; a completed wrapper was mistaken for a completed child. Empty output alone cannot establish this cause in a historical trace whose inner result is unavailable. | Preserve the tool result envelope, retain a running handle and collect the same execution's terminal result. If the handle was lost, follow original-receipt or digest-bound capture recovery before any retry. Keep admission/lease/settlement fences and original identities; qualify with pending, silent-success and silent-failure controls. See targeted diagnostics; no timeout, quota, packet or authority change is implied. | | `same_packet_json_type_alias` | Quota sharing replaces a distinct Vision or authoring observation with a reference because Boolean/number or signed-zero values compare equal in the host language. | Original captured values, projected body/ref, exact scalar types and zero signs, equal-content/key-order controls, actual CLI recovery and unchanged receipt/spend counts. | Language-level value equality was used as lossless JSON identity, while another sharing path already retained original serialized types. | Reuse one local serialization identity at the existing compatibility owner; share only complete equal JSON bodies and retain unique values/instructions. Preserve exact references, source input and existing reader/settlement ownership. Cover ordinary/native File/SQLite reads and digest refusal, and count transport rather than adding a late full-payload RPC. Keep installed/model adoption and whole-C1 benefit separate. | | `original_conversation_cli_goal_instance_drift` | `manager-inbox original-conversation` accepts an explicit `--goal-instance-id` but returns success for a stale instance, or follows the currently registered generation instead. | The physical CLI argv, selected registry profile, explicit versus omitted instance, current Goal reference, and real HTTP/source/queue/return readback in disposable File/SQLite state. | The CLI branch dropped the selector before the existing collaboration owner; current-instance inference concealed the missing caller reference. An unsupported source profile is a separate boundary, not proof that forwarding alone enables it. | Validate and forward the explicit Goal reference through the existing owner. Preserve omitted-selector compatibility and keep stale-instance, source/grant and audience checks authoritative. Cover real CLI discovery, request and control, unchanged inferred discovery, stale-instance rejection without queued work, and original recipient/result return; do not promote profiles or infer grants to make a live request pass. | diff --git a/tests/control_plane/test_todo_list_read_transport.py b/tests/control_plane/test_todo_list_read_transport.py index 1415ea0f01..207cee171b 100644 --- a/tests/control_plane/test_todo_list_read_transport.py +++ b/tests/control_plane/test_todo_list_read_transport.py @@ -149,6 +149,21 @@ def test_display_limits_report_complete_sources_and_actual_shown_items(read_cli) assert coverage["truncated"] == (len(payload["todos"]) < available) +def test_read_lenses_report_the_actual_canonical_source(read_cli): + provider, run, _, state = read_cli + before = state.read_bytes() + expected = f"{provider}_v0" + for options in [(), ("--agent-id", "agent-a"), ("--limit", "1"), + ("--thin",), ("--summary",), ("--todo-id", OWN), + ("--todo-id", "todo_missing"), ("--status", "done")]: + payload = run(*options) + assert payload["source"] == expected + assert payload["authority_read"]["source_authority"] == expected + assert [source["source_id"] for source in payload["projection_envelope"]["sources"]] == [expected] + assert f"- source: `{expected}`" in run(format_name="markdown") + assert state.read_bytes() == before + + def test_read_entrypoint_does_not_load_mutation_owners(read_cli): _, run, _, _ = read_cli assert run(isolated=True)["todo_count"] == 3