Engineering archive
These notes describe work at the date shown. See the usage reference for current behavior.
FLOW: review loop, result card, dashboard replies (stages 6.1–6.3)
Date: 2026-09-10. Base: faf6eff437390dc1f996b498f9fb8aae48c6c83d. Revised after root-contract-review-1.md and protocol-decision.md (close fence + PROTOCOL_VERSION 7).
Goal
Make the operator loop explicit: ready for review → follow-up → accepted, without changing default CLI close-on-done compatibility, without auto-merge or laptop shell execution, and without calling agent-reported checks “verified by mac-worker”. Dashboard replies and acceptance go through the same TaskClient::say / TaskClient::close paths the CLI uses, fenced by durable CAS so two clients cannot overwrite each other’s turn.
Architecture
Reuse, do not replace:
- Task states
queued|active|open|closed|abandoned|lostandClosePolicy::{Done,Never}. TaskOutcomeas the agent result (doneis not human acceptance).ClientStateStore::update_task_if_current(full-record CAS already used by cancel).- Saved task project context (
task_project_path) — never request filesystem paths. - Existing fetch/import ref
refs/remotes/mac-worker/<worker>/task/<id>andfetched_head. - Settings POST protections: loopback Host, matching Origin, JSON content-type, bounded body, custom header.
TaskClient::say/close(CLI signatures unchanged). Dashboard is another client of those methods.- PROTOCOL_VERSION 7 (FLOW version commit). Typed
reported_checkson TaskStatus. v6 mismatches at preflight. Spec:docs/superpowers/specs/2026-09-10-protocol-v7.md. Persisted JobMeta/fingerprint identity is not the wire constant; storage split is in/private/tmp/mac-worker-roadmap-jn00iibh/protocol-storage-contract.md(root review before that sweep).
Review projection (derived, not a new durable state):
| Durable facts | UI review_state |
|---|---|
open + close_intent | close_pending |
open + needs_input | waiting_on_you |
open + done | ready_for_review |
open + other terminal outcome | ready_for_follow_up |
closed + close_policy=never + done | accepted (human close) |
closed + close_policy=done + done | closed_after_done (legacy auto-close) |
| other closed/abandoned/lost | closed |
| queued/active | not_reviewable |
Default --close-on done stays. Iterative review: worker task submit --close-on never then worker task say / worker task close. No new worker task accept CLI verb.
Agent result checks and host TaskStatus.reported_checks are the same typed records (source: agent_reported). Parser defaults when omitted; status omits the field when empty so old on-disk records round-trip. Absent → UI not reported. Do not scrape logs for checks.
Constraints
- No auto-merge, no dashboard command endpoint, no executing review git/shell from the server.
- UI mutex / stale precheck is not the fence. CAS is
update_task_if_currentagainst the caller’s expected snapshot (say_from_expected/close_from_expected) with expected state, last turn id, turn count, head, updated_at. Dashboard must not discardload_matchingand let TaskClient reload a fresh CAS base. - Production dashboard reply uses
DetachedRunnerExecutor. Snapshot and detail skip remote refresh whileclose_intentis set;review_statestaysclose_pendinguntil local Closed + intent cleared. rollback_followupmay restore only the record this client just CAS-committed (expected = that Active snapshot). A loser must not restore a stale Open record over a winner, and must only delete its own prompt/queue row.- Lost HTTP response: retry with the same expected Open revision must not append another turn (CAS miss / 409).
- Close fence is durable
close_intenton still-Open. Remotetask_closeruns after the fence. Local Closed/accepted only after remote ack (or idempotent Closed retry). Transport failure leaves intent, not Closed. - Do not complete close inside
reconcile_runners. Skip status refresh while intent is set. - serde(default) is one-way (new reader of old disk). New work mismatches at probe preflight (
PROTOCOL_MISMATCH) before jobs. Persisted tasks may refuse at the next host request (INCOMPATIBLE_PROTOCOL/ DTOvalidate) before task or lease writes; that path does not need a second probe. Client and helper upgrade together (worker setup). - PERF owns
DashboardService/ snapshot GET lifecycle. FLOW UI must rendercollection.freshness=staleeven on HTTP 200 using originalgenerated_at_millis. 503DASHBOARD_SNAPSHOT_PENDINGis loading. - ENV owns batch/project CLI. FLOW exposes
declared_acceptance_instructionsfor ENV to prepend; resultchecksstay agent-reported. - SCHED owns queue dispatch. Do not edit reconcile-repair or auth-incident worktrees.
- Rebuild
src/dashboard/static/apponce after UI source is done. - Focused tests only; Cargo through the host slot wrapper. No full all-targets.
Interfaces
Dashboard mutations (FLOW):
POST /api/v1/tasks/{task_id}/reply
POST /api/v1/tasks/{task_id}/acceptHeaders: Host = listener, Origin = http://{host}, Content-Type: application/json, X-Mac-Worker-Task: 1. Body ≤ 8192 bytes.
Request (no paths):
{
"message": "optional for reply",
"expected_task_id": "<canonical>",
"expected_turn_id": "<last turn>",
"expected_turn_count": 1,
"expected_head_oid": "<40 hex or null>",
"expected_updated_at_millis": 123,
"expected_state": "open"
}409 TASK_REVISION_CONFLICT on mismatch. say/close public codes otherwise (TASK_BUSY, TASK_CLOSED, FOLLOWUP_LIMIT).
DashboardHttpState.mutation_source is optional (tests stay read-only unless they install a fake). Production command.rs wires MacWorkerTaskMutationSource using existing Config + ClientStateStore + PathLayout and the production DetachedRunnerExecutor. Tests inject a recording executor and an after_expected_check barrier.
Deep links: #/tasks/<id> selects on load and on hashchange (back/forward). No new server routes.
Close tests (two TaskClients): transport failure after fence; remote success + lost response; crash/retry of durable intent; concurrent say during close; stale rollback must not overwrite a winner. Dashboard mutation tests: barrier after load_matching, competing turn completion, 409 with zero enqueue/close/start; reply starts exactly one follow-up; close_intent snapshot+detail skip remote refresh.