Procházet zdrojové kódy

qa: integration_test re-baselined onto the post-R3 world — the harness left behind by the R stages (75 passes + stale) is green again end-to-end against both real binaries, twice on fresh environments: 114/0 (was 100 checks pre-R3).

The legacy-wire section rides the push era: 401s pin the id-less POST /api/saddle/results bearer (no/invalid token), the deleted /update + /poll pair 404s for any caller (the subtask/plan refusal arms of plan 12 die with the channel, matching the unit pins), typed updates become typed entries on results pushes (comment authors its agent row output-first, query persists with its piped input, progress lands newest-first, review flips review_requested), the block raise is a blocked push carrying the payload, exactly-once moves to the daemon-global entry ids (a redelivered id is consumed silently, one projection), and a park-unwinding working push provably never resurrects the parked round. Verdicts ride the block answer actions as before; the harness now posts S2 shapes (verdict=accept markers, feedback text) instead of shipping the stable constants.

S1 canonical-on-accept: the pre-accept report reads the completion_pending_block body with goals.completion_report pinned EMPTY; after the accept, the winning block's body is pinned equal to the canonised completion_report. U2: the mechanical bookkeeping (restores, park notices) is pinned to the RAW terminal (typed system session_commands) and out of every user-facing thread and the narrative kinds. D4: every run/worker session closes exactly once — parks stamp outcome 'parked', none dangle. Item 23: a mid-run pin derives busy from the worker's open session (the mock now lingers 0.5s per reply — a local instant mock could open-and-close a worker inside one sampling interval), the quiesce pin stays the derived idle, and the fresh-DB schema diff the joint QA asked for asserts the gone surfaces stay gone (saddles.current_task_id/allocated_at, tasks.working_branch, api_tokens).

The mock's plan revision rides --on-plan-feedback (the feedback verdict now routes there since the server wraps it in the stable prefix) with the Plan v2 body moved out of the fallback script — the worker's degenerate first round stays its FIRST round. No product change: the S1/U2/D4/derivation behaviours were verified correct against both binaries (a planted open session provably reads busy/current-task through the derived view); every failure in the 75-pass baseline was a stale assertion.
clanker před 1 měsícem
rodič
revize
903361929c
2 změnil soubory, kde provedl 190 přidání a 84 odebrání
  1. 4 2
      README.md
  2. 186 82
      test/integration_test.py

+ 4 - 2
README.md

@@ -279,8 +279,10 @@ callbacks → the worker's ephemeral worktree (auto-pruned, branch kept)
 push-before-review → completion review accept (gated on the live
 push-before-review → completion review accept (gated on the live
 per-repo push flags) → goal complete, plus the `/status`
 per-repo push flags) → goal complete, plus the `/status`
 `goal_branches` live probe flags, SSE over the agent's raw terminal,
 `goal_branches` live probe flags, SSE over the agent's raw terminal,
-the DERIVED goal narrative, the legacy ingest API surface and the
-stm-markdown renderer. Host run (the harness prepares a `demo` repo
+the DERIVED goal narrative, the push wire surface (typed entries and
+block raises riding POST /api/saddle/results, the deleted poll-era
+endpoints 404ing) and the stm-markdown renderer. Host run (the harness
+prepares a `demo` repo
 with a pushable bare origin when the workspace is empty; the database
 with a pushable bare origin when the workspace is empty; the database
 must be FRESH — the harness refuses a used one):
 must be FRESH — the harness refuses a used one):
 
 

+ 186 - 82
test/integration_test.py

@@ -36,10 +36,12 @@ The consolidated goal-era arc (plan 15, re-baselined for C+D):
    complete, run task done
    complete, run task done
 7. the goal agent's terminal sessions (SSE replay), the audit trail
 7. the goal agent's terminal sessions (SSE replay), the audit trail
    (the DERIVED goal narrative, typed session_commands, the
    (the DERIVED goal narrative, typed session_commands, the
-   harness-authored system rows), the /status goal_branches cache
-   sampled by the manual saddle refresh
-8. the legacy ingest API surface (401s, typed updates, exactly-once
-   poll handover) and the stm-markdown renderer
+   harness-authored bookkeeping riding the RAW terminal only), the
+   /status goal_branches cache sampled by the manual saddle refresh
+8. the push wire surface (401s on the id-less routes, typed entries
+   and block raises riding POST /api/saddle/results with per-entry
+   exactly-once dedup, the deleted poll-era endpoints 404ing) and the
+   stm-markdown renderer
 
 
 Prerequisites (see README.md "Integration testing"): the web app
 Prerequisites (see README.md "Integration testing"): the web app
 running with SPRY_SEED_ADMIN_USERNAME=admin,
 running with SPRY_SEED_ADMIN_USERNAME=admin,
@@ -95,8 +97,8 @@ CAPTURE_PATH = os.environ.get("CAPTURE_PATH",
                               os.path.join(WORKSPACE, "..", "mock-capture.jsonl"))
                               os.path.join(WORKSPACE, "..", "mock-capture.jsonl"))
 DEMO = os.path.join(WORKSPACE, "demo")
 DEMO = os.path.join(WORKSPACE, "demo")
 HOSTNAME = socket.gethostname().split(".")[0]
 HOSTNAME = socket.gethostname().split(".")[0]
-PLAN_APPROVED = "Plan approved. Proceed with implementation."
-REVIEW_APPROVED = "Review approved. Continue with the task."
+# The verdict wording is SERVER-OWNED (plan 16 S2/R4): the harness
+# posts `verdict` markers / `feedback` text and never the constants.
 
 
 passes = 0
 passes = 0
 failures = 0
 failures = 0
@@ -305,9 +307,6 @@ def mock_argv():
         "# two commits and one file — the blast radius is greeting.txt alone\n"
         "# two commits and one file — the blast radius is greeting.txt alone\n"
         + (heredoc % 'saddle block question "Demo only" "Whole workspace"') % (
         + (heredoc % 'saddle block question "Demo only" "Whole workspace"') % (
             "Which scope should the greeting cover? The demo repo is small; confirm the blast radius."),
             "Which scope should the greeting cover? The demo repo is small; confirm the blast radius."),
-        # R3 (plan revision after feedback): the rejected plan, re-submitted
-        (heredoc % "saddle plan") % (
-            "# Plan v2\n\n1. Add greeting.txt to the demo repo.\n2. Export it for reuse."),
         # W1 (the worker's first round): a DEGENERATE repetition (plan
         # W1 (the worker's first round): a DEGENERATE repetition (plan
         # 15 D2) — the validator catches it before feeding; nothing
         # 15 D2) — the validator catches it before feeding; nothing
         # executes, one rejection receipt, the round is consumed
         # executes, one rejection receipt, the round is consumed
@@ -327,6 +326,10 @@ def mock_argv():
         # the question's verdict ("Human selected: Demo only") -> plan v1
         # the question's verdict ("Human selected: Demo only") -> plan v1
         "--on-answer", (heredoc % "saddle plan") % (
         "--on-answer", (heredoc % "saddle plan") % (
             "# Plan v1\n\n1. Add greeting.txt to the demo repo."),
             "# Plan v1\n\n1. Add greeting.txt to the demo repo."),
+        # the plan-feedback verdict -> the revision (the export step
+        # incorporates the harness's feedback)
+        "--on-plan-feedback", (heredoc % "saddle plan") % (
+            "# Plan v2\n\n1. Add greeting.txt to the demo repo.\n2. Export it for reuse."),
         # the approval verdict -> task fan-out + suspend
         # the approval verdict -> task fan-out + suspend
         "--on-plan-approved", fanout,
         "--on-plan-approved", fanout,
         # the batched "Goal update" resume -> merge, prune, push, complete
         # the batched "Goal update" resume -> merge, prune, push, complete
@@ -342,6 +345,10 @@ def mock_argv():
             "# Goal completion report\n\n- achieved: greeting.txt added via one worker task\n"
             "# Goal completion report\n\n- achieved: greeting.txt added via one worker task\n"
             "- verified: the goal branch is pushed to origin"),
             "- verified: the goal branch is pushed to origin"),
         "--on-query", "The hostname is %s." % HOSTNAME,
         "--on-query", "The hostname is %s." % HOSTNAME,
+        # Every reply lingers half a second: the local mock answers so
+        # instantly that a worker's whole run (its DERIVED-busy open
+        # window) can close inside one sampling interval otherwise.
+        "--delay", "0.5",
         # receipt forensics: every prompt the mock received, so the
         # receipt forensics: every prompt the mock received, so the
         # harness can assert the D-side receipts reached the model
         # harness can assert the D-side receipts reached the model
         "--capture", CAPTURE_PATH,
         "--capture", CAPTURE_PATH,
@@ -518,7 +525,10 @@ def main():
               "the pending plan block renders on the goal page")
               "the pending plan block renders on the goal page")
         pending = goal_state(auth, goal1)["plan_pending_block"]
         pending = goal_state(auth, goal1)["plan_pending_block"]
         check("Plan v1" in pending["body"], "the plan block carries the submitted markdown")
         check("Plan v1" in pending["body"], "the plan block carries the submitted markdown")
-        act(auth, pending["answer_action"], {"answer": "Feedback: also export the greeting"})
+        # Plan 16 S2/R4: the goal page posts a `feedback` reply / a
+        # `verdict` marker — the server owns the stable wording the
+        # daemon's resume matching reads.
+        act(auth, pending["answer_action"], {"feedback": "also export the greeting"})
         check(wait_goal_status(auth, goal1, "planning", timeout=30) is not None,
         check(wait_goal_status(auth, goal1, "planning", timeout=30) is not None,
               "rejecting with feedback returns the goal to planning")
               "rejecting with feedback returns the goal to planning")
         check(wait_for(lambda: goal_state(auth, goal1).get("plan_pending_block") not in
         check(wait_for(lambda: goal_state(auth, goal1).get("plan_pending_block") not in
@@ -529,7 +539,7 @@ def main():
         revised = goal_state(auth, goal1)
         revised = goal_state(auth, goal1)
         check(len([r for r in revised["plan_revisions"] if r.get("answered")]) >= 1,
         check(len([r for r in revised["plan_revisions"] if r.get("answered")]) >= 1,
               "the rejected revision stays discoverable behind the expander")
               "the rejected revision stays discoverable behind the expander")
-        act(auth, revised["plan_pending_block"]["answer_action"], {"answer": PLAN_APPROVED})
+        act(auth, revised["plan_pending_block"]["answer_action"], {"verdict": "accept"})
         check(wait_goal_status(auth, goal1, "active", timeout=30) is not None,
         check(wait_goal_status(auth, goal1, "active", timeout=30) is not None,
               "approving with the agreed wording activates the goal")
               "approving with the agreed wording activates the goal")
 
 
@@ -554,6 +564,21 @@ def main():
             "select suspended_at is not null from tasks where id = ?", (run_task,))[0][0] or False,
             "select suspended_at is not null from tasks where id = ?", (run_task,))[0][0] or False,
             timeout=30, what="the goal agent parks") is not None,
             timeout=30, what="the goal agent parks") is not None,
             "the goal agent parked suspended after fanning out (suspended_at stamped)")
             "the goal agent parked suspended after fanning out (suspended_at stamped)")
+        # Plan 16 item 23: busy DERIVES from the open session (the
+        # schema carries no work cache) — while the worker runs, the
+        # card reads busy with the worker as its current task. The
+        # open window can be sub-second (the local mock answers
+        # instantly), so the card read shares the predicate iteration
+        # with the open-session probe.
+        check(wait_for(lambda: bool(db_query(
+            "select 1 from sessions where task_id = ? and ended_at is null",
+            (worker_task,))[0:1]) and (lambda card: card is not None
+                                       and card["status"] == "busy"
+                                       and int(card["current_task_id"] or 0) == worker_task)(
+            next((s for s in slot_public(auth, "/saddles", "saddles")["saddles"]
+                  if s["name"] == SADDLE_NAME), None)) or False,
+            timeout=90, interval=0.2, what="derived busy while the worker runs") is not None,
+              "the saddle card derives busy from the worker's open session (no cached row)")
 
 
         print("== the worker's ephemeral worktree ==")
         print("== the worker's ephemeral worktree ==")
         branch_rows = wait_for(lambda: db_query(
         branch_rows = wait_for(lambda: db_query(
@@ -598,8 +623,14 @@ def main():
         check(wait_goal_status(auth, goal1, "completion_review", timeout=120) is not None,
         check(wait_goal_status(auth, goal1, "completion_review", timeout=120) is not None,
               "the resumed agent merges, pushes and enters completion review")
               "the resumed agent merges, pushes and enters completion review")
         state = goal_state(auth, goal1)
         state = goal_state(auth, goal1)
-        check("greeting.txt added via one worker task" in state.get("completion_report", ""),
-              "the completion report renders on the goal page")
+        completion = state["completion_pending_block"]
+        # Plan 16 S1 (canonical-on-accept): pending, the report lives
+        # ONLY in the block — goals.completion_report stays empty
+        # until the accept verdict writes the winning body.
+        check("greeting.txt added via one worker task" in (completion or {}).get("body", ""),
+              "the completion report renders on the goal page (the pending block's body)")
+        check(not (state.get("completion_report") or ""),
+              "goals.completion_report stays clean before the verdict (canonical-on-accept)")
         merged = subprocess.run(
         merged = subprocess.run(
             ["git", "-C", goal_clone, "show", "goal/demo-run:greeting.txt"],
             ["git", "-C", goal_clone, "show", "goal/demo-run:greeting.txt"],
             capture_output=True, text=True).stdout
             capture_output=True, text=True).stdout
@@ -628,7 +659,7 @@ def main():
             for b in goal_state(auth, goal1).get("push_branches", [])) or False,
             for b in goal_state(auth, goal1).get("push_branches", [])) or False,
             timeout=60, what="unpushed chip") is not None,
             timeout=60, what="unpushed chip") is not None,
               "the rewound origin flips the live push chip to unpushed")
               "the rewound origin flips the live push chip to unpushed")
-        refusal = act(auth, completion["answer_action"], {"answer": REVIEW_APPROVED})
+        refusal = act(auth, completion["answer_action"], {"verdict": "accept"})
         check(any(d.get("type") == "notify" and d.get("kind") == "error"
         check(any(d.get("type") == "notify" and d.get("kind") == "error"
                   and "Origin is behind" in (d.get("message") or "")
                   and "Origin is behind" in (d.get("message") or "")
                   for d in refusal),
                   for d in refusal),
@@ -649,23 +680,29 @@ def main():
         completion = goal_state(auth, goal1)["completion_pending_block"]
         completion = goal_state(auth, goal1)["completion_pending_block"]
 
 
         print("== completion accept ==")
         print("== completion accept ==")
-        check(completion and completion.get("body", "") == state.get("completion_report", ""),
+        check("greeting.txt added via one worker task" in (completion or {}).get("body", ""),
               "the run task's completion block carries the report")
               "the run task's completion block carries the report")
-        accept = act(auth, completion["answer_action"], {"answer": REVIEW_APPROVED})
+        winning_body = (completion or {}).get("body", "")
+        accept = act(auth, completion["answer_action"], {"verdict": "accept"})
         check(not any(d.get("type") == "notify" and d.get("kind") == "error" for d in accept),
         check(not any(d.get("type") == "notify" and d.get("kind") == "error" for d in accept),
               "the accept passes once the push flags read pushed")
               "the accept passes once the push flags read pushed")
         check(wait_goal_status(auth, goal1, "complete", timeout=30) is not None,
         check(wait_goal_status(auth, goal1, "complete", timeout=30) is not None,
-              "accepting with the agreed wording completes the goal")
+              "accepting the verdict marker completes the goal (server-owned wording)")
         check(wait_task_status(auth, run_task, "done", timeout=30) is not None,
         check(wait_task_status(auth, run_task, "done", timeout=30) is not None,
               "the goal-run task closes done with the goal")
               "the goal-run task closes done with the goal")
+        final = goal_state(auth, goal1)
+        check(final.get("completion_report") == winning_body,
+              "the accept canonises the winning block's body as goals.completion_report (S1)")
 
 
         print("== audit trail (the derived narrative) ==")
         print("== audit trail (the derived narrative) ==")
         narrative = goal_state(auth, goal1)["events"]
         narrative = goal_state(auth, goal1)["events"]
         kinds = [e["kind"] for e in narrative]
         kinds = [e["kind"] for e in narrative]
         for expected in ("started", "block-raised", "plan-submitted", "plan-rejected",
         for expected in ("started", "block-raised", "plan-submitted", "plan-rejected",
                          "plan-approved", "task-created", "task-done",
                          "plan-approved", "task-created", "task-done",
-                         "completion-submitted", "goal-approved", "system"):
+                         "completion-submitted", "goal-approved"):
             check(expected in kinds, "the derived narrative records %s" % expected)
             check(expected in kinds, "the derived narrative records %s" % expected)
+        check("system" not in kinds,
+              "mechanical system rows never reach the user-facing narrative (U2: raw log only)")
         check(all(k != "task-enqueued" for k in kinds),
         check(all(k != "task-enqueued" for k in kinds),
               "timestamp-less bookkeeping never returns (task-enqueued is gone)")
               "timestamp-less bookkeeping never returns (task-enqueued is gone)")
         check(db_query(
         check(db_query(
@@ -684,6 +721,20 @@ def main():
               "the worker's terminal carries typed entries (%s)" % sorted(worker_types))
               "the worker's terminal carries typed entries (%s)" % sorted(worker_types))
 
 
         print("== the terminal watermark (C3) drives the resume ==")
         print("== the terminal watermark (C3) drives the resume ==")
+        # The fresh-DB schema diff (the joint-QA gate): the removed
+        # surfaces stay removed — the saddle work cache (item 23), the
+        # /update-era columns (arch 19) and the legacy token table
+        # (arch 24).
+        saddle_cols = {row[1] for row in db_query("pragma table_info('saddles')")}
+        check(not ({"current_task_id", "allocated_at"} & saddle_cols),
+              "the saddles work-cache columns are gone (sessions are truth)")
+        task_cols = {row[1] for row in db_query("pragma table_info('tasks')")}
+        check("working_branch" not in task_cols,
+              "tasks.working_branch is gone (only the deleted /update done wrote it)")
+        check(db_query(
+            "select count(*) from sqlite_master where type = 'table'"
+            " and name = 'api_tokens'")[0][0] == 0,
+            "the api_tokens table is gone (saddles.token is the single credential store)")
         watermark_row = db_query(
         watermark_row = db_query(
             "select last_terminal_watermark from goals where id = ?", (goal1,))
             "select last_terminal_watermark from goals where id = ?", (goal1,))
         worker_done_at = db_query(
         worker_done_at = db_query(
@@ -693,23 +744,39 @@ def main():
               "the goals row's terminal watermark advanced past the worker's transition"
               "the goals row's terminal watermark advanced past the worker's transition"
               " (the resume consumed it)")
               " (the resume consumed it)")
 
 
-        print("== system rows on the ONE thread (C2) ==")
+        print("== the harness bookkeeping rides the RAW terminal (C2 + U2) ==")
+        # Plan 16 S5 (U2): the mechanical rows (restores, park
+        # notices) author NO comment rows — they persist as typed
+        # 'system' session_commands (the raw terminal log) and never
+        # render on a user-facing timeline.
+        raw_system = db_query(
+            "select sc.command_text, sc.output_text from session_commands sc"
+            " join sessions s on s.id = sc.session_id where s.task_id = ?"
+            " and sc.type = 'system'", (run_task,))
+        check(any("session context restored" in (row[1] or "") for row in raw_system)
+              and any("parked until an owned task" in (row[1] or "") for row in raw_system),
+              "the harness bookkeeping (restore, park) persists in the raw terminal log")
         thread = goal_state(auth, goal1)["comments"]
         thread = goal_state(auth, goal1)["comments"]
-        system_rows = [c for c in thread if c.get("is_system")]
-        check(system_rows and all(c["author"] == "system" for c in system_rows)
-              and not any(c.get("from_agent") for c in system_rows)
-              and any("session context restored" in c["body"] for c in system_rows)
-              and any("parked until an owned task" in c["body"] for c in system_rows),
-              "the harness bookkeeping (restore, park) rides the thread as system rows,"
-              " never agent rows")
+        check(not any("session context restored" in c["body"] or "parked until" in c["body"]
+                      for c in thread),
+              "mechanical rows never render on the goal thread (U2)")
         run_thread = task_state(auth, run_task)["comments"]
         run_thread = task_state(auth, run_task)["comments"]
-        check(any(c.get("is_system") and "session context restored" in c["body"]
-                  for c in run_thread),
-              "the task page reads the same thread (the system rows included)")
+        check(not any("session context restored" in c["body"] or "parked until" in c["body"]
+                      for c in run_thread),
+              "the task page reads the same filtered thread (no mechanical rows)")
         check(not any(c.get("from_agent") and "[harness]" in c["body"]
         check(not any(c.get("from_agent") and "[harness]" in c["body"]
                       for c in thread),
                       for c in thread),
               "no harness-authored row renders as agent content")
               "no harness-authored row renders as agent content")
 
 
+        print("== parks close their sessions (D4) ==")
+        outcomes = db_query(
+            "select outcome, ended_at is not null from sessions where task_id in (?, ?)",
+            (run_task, worker_task))
+        check(bool(outcomes)
+              and all(row[0] in ("parked", "done") and row[1] for row in outcomes),
+              "every run/worker session closed exactly once — parks stamp outcome 'parked',"
+              " none dangle open (%s)" % sorted({row[0] for row in outcomes}))
+
         print("== D-side through both binaries: commentary + degenerate ==")
         print("== D-side through both binaries: commentary + degenerate ==")
         run_bash = db_query(
         run_bash = db_query(
             "select sc.command_text from session_commands sc join sessions s"
             "select sc.command_text from session_commands sc join sessions s"
@@ -780,7 +847,7 @@ def main():
         check(any(d.get("type") == "subscribe" for d in pulse),
         check(any(d.get("type") == "subscribe" for d in pulse),
               "goal page subscribes to the pulse channel for live refresh")
               "goal page subscribes to the pulse channel for live refresh")
 
 
-        print("== legacy ingest API surface ==")
+        print("== push wire surface (the poll-era endpoints deleted) ==")
         saddles_state = slot_public(auth, "/saddles", "saddles")
         saddles_state = slot_public(auth, "/saddles", "saddles")
         act(auth, saddles_state["register"], {"name": "saddle-e2e-api", "host": "localhost:7199"})
         act(auth, saddles_state["register"], {"name": "saddle-e2e-api", "host": "localhost:7199"})
         api_rows = [s for s in slot_public(auth, "/saddles", "saddles")["saddles"]
         api_rows = [s for s in slot_public(auth, "/saddles", "saddles")["saddles"]
@@ -794,7 +861,7 @@ def main():
         act(auth, tasks_state["create"], {
         act(auth, tasks_state["create"], {
             "project_id": "1",
             "project_id": "1",
             "title": "API surface probe",
             "title": "API surface probe",
-            "description": "Typed updates, exactly-once poll",
+            "description": "Typed entries, exactly-once pushes",
             "priority": "6",
             "priority": "6",
             "task_branch": "task/api-surface",
             "task_branch": "task/api-surface",
         })
         })
@@ -808,73 +875,110 @@ def main():
         api_session = db.execute("select max(id) from sessions").fetchone()[0]
         api_session = db.execute("select max(id) from sessions").fetchone()[0]
         db.close()
         db.close()
 
 
-        status, _ = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, None,
-                               {"type": "comment", "session_id": api_session, "data": {"body": "x"}})
-        check(status == 401, "update without a token is rejected")
-        status, _ = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, "saddle-not-a-token",
-                               {"type": "comment", "session_id": api_session, "data": {"body": "x"}})
-        check(status == 401, "update with an invalid token is rejected")
-
-        status, body = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, api_token,
-                                  {"type": "comment", "session_id": api_session,
-                                   "data": {"body": "Surveyed the module"}})
-        check(status == 200 and body.get("ok"), "comment update accepted with token")
-        status, body = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, api_token,
-                                  {"type": "block", "session_id": api_session, "data": {
-                                      "question": "Which database?",
-                                      "type": "question",
-                                      "options": ["SQLite", "Postgres"],
-                                      "body": "# Pick one"}})
-        check(status == 200 and body.get("block_id"), "typed block returns block_id")
+        def push(entry_id, entry_type, state="working", command="", output="",
+                 block=None, **extra):
+            """One round-results push in the daemon's shape (the
+            push-era wire the real daemon speaks — the poll-era
+            /update channel is gone, 404-pinned below)."""
+            payload = {"session_id": api_session, "state": state}
+            if entry_type is not None:
+                entry = {"id": entry_id, "command": command, "output": output,
+                         "exit_code": 0, "type": entry_type,
+                         "timestamp": "2026-09-13T00:00:00Z"}
+                entry.update(extra)
+                payload["entries"] = [entry]
+            if block is not None:
+                payload["block"] = block
+            return saddle_api("POST", "/api/saddle/results", api_token, payload)
+
+        status, _ = saddle_api("POST", "/api/saddle/results", None,
+                               {"session_id": api_session, "state": "working"})
+        check(status == 401, "results push without a token is rejected")
+        status, _ = saddle_api("POST", "/api/saddle/results", "saddle-not-a-token",
+                               {"session_id": api_session, "state": "working"})
+        check(status == 401, "results push with an invalid token is rejected")
+
+        # The poll-era ingest pair is GONE (arch 19): both routes 404
+        # for any caller, authenticated or not — the subtask/plan
+        # refusal arms (plan 12 removal) died with the channel.
         status, _ = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, api_token,
         status, _ = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, api_token,
-                               {"type": "output", "session_id": api_session, "data": {
-                                   "command": "cat notes.md | saddle query 'meaning?'",
-                                   "output": "42",
-                                   "type": "query",
-                                   "input": "# notes\nmeaning of life"}})
-        check(status == 200, "query output update accepted")
+                               {"type": "status", "session_id": api_session,
+                                "data": {"state": "working"}})
+        check(status == 404, "POST /api/saddle/{id}/update is gone (404)")
+        status, _ = saddle_api("GET", "/api/saddle/%d/poll" % api_saddle, api_token)
+        check(status == 404, "GET /api/saddle/{id}/poll is gone (404)")
         status, _ = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, api_token,
         status, _ = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, api_token,
-                               {"type": "progress", "session_id": api_session,
-                                "data": {"summary": "Migrated the schemas"}})
-        check(status == 200, "progress update accepted")
+                               {"type": "subtask", "session_id": api_session,
+                                "data": {"title": "Ghost child"}})
+        check(status == 404,
+              "the /update subtask arm is gone with the channel (404, plan 12 removal)")
         status, _ = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, api_token,
         status, _ = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, api_token,
-                               {"type": "review", "session_id": api_session,
-                                "data": {"summary": "Migration **ready** for review"}})
-        check(status == 200, "review update accepted")
-        status, body = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, api_token,
-                                  {"type": "plan", "session_id": api_session,
-                                   "data": {"plan": "# Plan\n- migrate schemas"}})
-        check(status == 400 and "gone" in (body.get("error") or ""),
-              "the task-plan update type fails loudly (plan 12 removal)")
-        status, body = saddle_api("POST", "/api/saddle/%d/update" % api_saddle, api_token,
-                                  {"type": "subtask", "session_id": api_session, "data": {}})
-        check(status == 400 and "gone" in (body.get("error") or ""),
-              "the subtask update type fails loudly (plan 12 removal)")
+                               {"type": "plan", "session_id": api_session,
+                                "data": {"plan": "# nope"}})
+        check(status == 404,
+              "the /update plan arm is gone with the channel (404, plans are goal-only)")
+
+        status, body = push(9001, "comment", command="saddle comment",
+                            output="Surveyed the module")
+        check(status == 200 and body.get("ok"), "comment entry accepted on the results push")
+        check(any(c.get("from_agent") and c["body"] == "Surveyed the module"
+                  for c in task_state(auth, api_task)["comments"]),
+              "the comment entry authors an agent row (output-first)")
+        status, body = push(None, None, state="blocked", block={
+            "question": "Which database?", "type": "question",
+            "options": ["SQLite", "Postgres"], "body": "# Pick one"})
+        check(status == 200 and body.get("ok"),
+              "the blocked push raises the question through the results channel")
+        parked = db_query("select outcome from sessions where id = ?", (api_session,))[0][0]
+        check(parked == "parked",
+              "the raise parked the round's session (outcome 'parked', D4)")
+        status, body = push(9002, "query", command="cat notes.md | saddle query 'meaning?'",
+                            output="42", input="# notes\nmeaning of life")
+        check(status == 200, "query entry accepted on the results push")
+        query_rows = db_query(
+            "select sc.type, sc.piped_input from session_commands sc"
+            " where sc.session_id = ? and sc.command_text like '%saddle query%'",
+            (api_session,))
+        check(query_rows and query_rows[0][0] == "query"
+              and query_rows[0][1] == "# notes\nmeaning of life",
+              "the query entry persists typed with its piped input")
+        status, body = push(9003, "progress", command="saddle progress-update",
+                            output="Migrated the schemas")
+        check(status == 200, "progress entry accepted on the results push")
+        status, body = push(9003, "progress", command="saddle progress-update",
+                            output="Migrated the schemas AGAIN")
+        check(status == 200, "the redelivered entry id is consumed silently (200, no projection)")
+        check(len([p for p in task_state(auth, api_task)["progress"]
+                   if p["summary"] in ("Migrated the schemas", "Migrated the schemas AGAIN")])
+              == 1 and task_state(auth, api_task)["progress"][0]["summary"] == "Migrated the schemas",
+              "entries project exactly once per daemon-global id (push-era exactly-once)")
+        status, body = push(9004, "review", command="saddle review",
+                            output="Migration **ready** for review")
+        check(status == 200, "review entry accepted on the results push")
 
 
         detail = task_state(auth, api_task)
         detail = task_state(auth, api_task)
-        check(detail["status"] == "review_requested", "review flips the task status")
+        check(detail["status"] == "review_requested", "the review entry flips the task status")
         check([b["options"] for b in detail["blocks"]
         check([b["options"] for b in detail["blocks"]
                if b["block_type"] == "question"] == [["SQLite", "Postgres"]],
                if b["block_type"] == "question"] == [["SQLite", "Postgres"]],
-              "question block carries its options")
+              "the pushed block carries its options")
         check(detail["progress"][0]["summary"] == "Migrated the schemas",
         check(detail["progress"][0]["summary"] == "Migrated the schemas",
               "progress history newest-first")
               "progress history newest-first")
+        status, _ = push(None, None, state="working")
+        check(status == 200 and task_state(auth, api_task)["status"] == "review_requested",
+              "a park-unwinding working push never resurrects the parked round")
 
 
         question = [b for b in detail["blocks"] if b["block_type"] == "question"][0]
         question = [b for b in detail["blocks"] if b["block_type"] == "question"][0]
         act(auth, question["answer_action"], {"option": "SQLite"})
         act(auth, question["answer_action"], {"option": "SQLite"})
         check([b["answer"] for b in task_state(auth, api_task)["blocks"]
         check([b["answer"] for b in task_state(auth, api_task)["blocks"]
                if b["block_type"] == "question"] == ["SQLite"],
                if b["block_type"] == "question"] == ["SQLite"],
-              "option answer stored verbatim (the resume notice formats it)")
+              "option answer stored verbatim (the resuming assignment formats it)")
         act(auth, detail["comment"],
         act(auth, detail["comment"],
             {"body": "Check the migration output", "quote": "ERROR: table demos exists"})
             {"body": "Check the migration output", "quote": "ERROR: table demos exists"})
-        status, body = saddle_api("GET", "/api/saddle/%d/poll" % api_saddle, api_token)
-        check(any(a["answer"] == "SQLite" for a in body.get("answers", [])),
-              "poll hands over the block answer")
-        check(any(c["body"] == "Check the migration output" and "ERROR" in c["quote"]
-                  for c in body.get("comments", [])),
-              "poll hands over the quoted user comment")
-        status, body = saddle_api("GET", "/api/saddle/%d/poll" % api_saddle, api_token)
-        check(body.get("answers") == [] and body.get("comments") == [],
-              "poll delivers answers and comments exactly once")
+        check(any(c["body"] == "Check the migration output" and "ERROR" in (c.get("quote") or "")
+                  and not c.get("from_agent")
+                  for c in task_state(auth, api_task)["comments"]),
+              "the quoted user comment lands on the task thread (delivery rides the"
+              " assignment carrier / dispatcher pass — unit-pinned)")
         act(auth, detail["cancel"], {"task_id": str(api_task)})
         act(auth, detail["cancel"], {"task_id": str(api_task)})
 
 
         print("== stm-markdown ==")
         print("== stm-markdown ==")