Delta-review touch-ups: receipt sentence made exact, a proof made red on the old tree, a comment made honest

The §6 sentence now says which owner message's receipt is left standing (a steer that
belongs to no owner message) and that a steer relaying a drained owner message keys its
receipt on that message. The drained-entry-without-client-id test starts at generation 0
so it fails on the write-counter tree instead of passing by accident. The implicit-id
comment names what the shared sanitizer normalises and what it does not.
This commit is contained in:
Ouroboros 2026-09-14 21:26:51 +03:00
parent dbaf872d6f
commit 645091c444
3 changed files with 5 additions and 4 deletions

View file

@ -1788,7 +1788,7 @@ The projects registry owns immutable project identity, canonical chat id, option
Project `journal.jsonl` records curated milestones and `workpad.md` retains active working context; focused context includes the workpad in full and recent journal rows with a visible pointer to older entries. On root completion, only high-signal blockers, questions, and interface contracts are mirrored once from the ephemeral task-tree ledger into the durable journal, and a finished root whose effective working tree is not the registered `working_dir` writes one typed "work lives at <path> @ <sha>" journal row from facts the task record already holds. A project digest gives consciousness a concise completion signal without pretending to be the raw project memory.
`promote_chat_to_task`, `route_to_project`, and `steer_task` become successful only after their token-matched supervisor facts are durable in the existing task result, queue snapshot, annotation, or mailbox authority; with several possible tasks the LLM chooses, code auto-delivers only the unambiguous one-target case, and an unconfirmed or stale receipt fails visibly rather than launching a second root. A routing/promote decision turn receives host-built ground truth (each project's registry `working_dir` plus bounded typed projections of recent task results — never raw result text), read through the registry row's durable `last_task_result_id` pointer with a bounded scan fallback; only the absent-pointer case writes the pointer back, because a non-empty pointer that failed to resolve is typically a split-drive result whose canonical copy-back has not landed. Those recent results are the owner's ROOT results only: a swarm child is not an addressable predecessor (it is reachable through its root) and is counted as its own omission rather than evicting a root from the window, while a project room's direct-chat root stamps the same pointer, so the room's own work is offerable without acquiring letters home. The turn is also shown any routing receipt that already exists for the SAME owner message, a fact on the contract it already receives and never a ban: whether one message becomes a second root stays the model's judgment. `steer_task` transports the owner's exact ingress bytes on the turn's FIRST routing act, while it is still acting on the message that started it. Two typed facts end that window: the latest owner message the turn actually DRAINED from its mailbox (`ToolContext.last_owner_delivery`, stamped at the loop's mailbox drain — a message merely written to the mailbox has not reached the turn and ends nothing), or a landed `promote_chat_to_task`/`route_to_project`/`steer_task` receipt already on the origin message (a refused or unconfirmed act carried nothing, so the message stays unrouted and the next act still relays it). Past either one the turn is RELAYING its own words, so it delivers the message the model was given, and its receipt is written against the owner message actually relayed — the drained delivery's own client id when it carries one, else the synthetic `agent-steer:<routing token>` id — never again on an origin message this turn has already routed. An agent-authored steer that belongs to no owner message earns its receipt under a synthetic `agent-steer:<routing token>` id: delivery and typed refusal stay confirmable through the same `routing_wait` poll, while no chat row carries that id, so no owner message is labelled by the agent's act and the owner message's own routing receipt (what a later decision turn reads) is left standing. Each steer's mailbox entry is keyed by its own routing token beside the owner message id and target, so several instructions relayed under one origin are several deliveries while a retried emit of the same steer stays one. Canonical owner routing and project UI are projections over those same authorities: a routing receipt proves admission, not completion; an unread indicator proves a visible revision, not memory isolation.
`promote_chat_to_task`, `route_to_project`, and `steer_task` become successful only after their token-matched supervisor facts are durable in the existing task result, queue snapshot, annotation, or mailbox authority; with several possible tasks the LLM chooses, code auto-delivers only the unambiguous one-target case, and an unconfirmed or stale receipt fails visibly rather than launching a second root. A routing/promote decision turn receives host-built ground truth (each project's registry `working_dir` plus bounded typed projections of recent task results — never raw result text), read through the registry row's durable `last_task_result_id` pointer with a bounded scan fallback; only the absent-pointer case writes the pointer back, because a non-empty pointer that failed to resolve is typically a split-drive result whose canonical copy-back has not landed. Those recent results are the owner's ROOT results only: a swarm child is not an addressable predecessor (it is reachable through its root) and is counted as its own omission rather than evicting a root from the window, while a project room's direct-chat root stamps the same pointer, so the room's own work is offerable without acquiring letters home. The turn is also shown any routing receipt that already exists for the SAME owner message, a fact on the contract it already receives and never a ban: whether one message becomes a second root stays the model's judgment. `steer_task` transports the owner's exact ingress bytes on the turn's FIRST routing act, while it is still acting on the message that started it. Two typed facts end that window: the latest owner message the turn actually DRAINED from its mailbox (`ToolContext.last_owner_delivery`, stamped at the loop's mailbox drain — a message merely written to the mailbox has not reached the turn and ends nothing), or a landed `promote_chat_to_task`/`route_to_project`/`steer_task` receipt already on the origin message (a refused or unconfirmed act carried nothing, so the message stays unrouted and the next act still relays it). Past either one the turn is RELAYING its own words, so it delivers the message the model was given, and its receipt is written against the owner message actually relayed — the drained delivery's own client id when it carries one, else the synthetic `agent-steer:<routing token>` id — never again on an origin message this turn has already routed. An agent-authored steer that belongs to no owner message earns its receipt under a synthetic `agent-steer:<routing token>` id: delivery and typed refusal stay confirmable through the same `routing_wait` poll, while no chat row carries that id, so no owner message is labelled by the agent's act and, for a steer that belongs to no owner message, the owner message's own routing receipt (what a later decision turn reads) is left standing; a steer that relays a drained owner message keys its receipt on that message, replacing whatever receipt it carried. Each steer's mailbox entry is keyed by its own routing token beside the owner message id and target, so several instructions relayed under one origin are several deliveries while a retried emit of the same steer stays one. Canonical owner routing and project UI are projections over those same authorities: a routing receipt proves admission, not completion; an unread indicator proves a visible revision, not memory isolation.
### Skills and extensions

View file

@ -768,8 +768,9 @@ async def api_project_from_task(request: Request) -> JSONResponse:
# default id for this task (chat_activity.js::projectIdFromTask) or none at all
# — and ``raw_id`` already defaults to that same ``task-<task_id>`` — so the
# request names no particular room and the work's own project answers it. Both
# halves go through the SERVER's own sanitizer, so the client's slug rules and
# this check cannot drift. An EXPLICIT different id is a caller naming a room.
# halves go through the SERVER's own sanitizer (case and single stray characters;
# not the client's run-collapsing or dash trimming, which today's hex/sha task ids
# never trigger). An EXPLICIT different id is a caller naming a room.
implicit = sanitize_project_id(raw_id) == sanitize_project_id(f"task-{task_id}")
disclosed: list = []

View file

@ -240,7 +240,7 @@ def test_a_drained_entry_without_a_client_id_uses_the_steers_own_receipt_id(tmp_
monkeypatch.setattr(queue, "DRIVE_ROOT", str(tmp_path))
notices, emitted = [], []
ctx = _live_tool_ctx(
tmp_path, _supervisor_ctx(tmp_path, notices), emitted, generation=2, delivery={
tmp_path, _supervisor_ctx(tmp_path, notices), emitted, generation=0, delivery={
"msg_id": "legacy-1", "client_message_id": "", "text": "a later owner message",
"ts": "2026-09-14T12:41:23+00:00",
}, metadata={