{"type":"meta","schema":"session-state/v1","slug":"harness-gated","topic":"Harness sub-project deliverables requiring Plan A landed (control-api + adapter catalog/validator + Web UI)","created":"2026-06-30","exec_mode":"ship","scheduler":"dag-parallel","land_mode":"merge-to-main","base_branch":"main","gate0_cmd":"bash run-tests.sh","status_enum":["BLOCKED","PENDING","WORKING","WRITTEN","REVIEWED","COMMITTED"],"truth_rule":"this file is ground truth for INTENT (goals/directions/task list); git is ground truth for DONE-NESS — reconcile statuses against git, never trust a COMMITTED line with no commit","refresh_how":"native Read of THIS file — NEVER ctx_execute/ctx_search","refresh_what":"meta + every goal/methodology/int/direction record + all gated records + all WORKING and BLOCKED tasks (skip COMMITTED lines)","refresh_when":["wave/phase boundary","any task status change","after a compaction system-reminder","before calling advisor"],"update_rule":"on status change, rewrite that task's single line in place; append new tasks/session_memory as new lines","report_rule":"at session end emit brief: done / blocked / deferred / canceled — NO next action"}
{"type":"goal","text":"Build the three Plan-A-dependent harness tracks: control-api (per-wrapper session continuity + --health/--list-models, events normalizer, liveness sidecar, run-registry pointer, --supervised control plane, Node-stdlib HTTP+SSE server), adapter catalog+validator cross-check (presets/adapters.json + spec/adapters.schema.json + presets/_validate.mjs), and the full Web UI (Astro+React scaffold, gateway endpoints, run-store, SSE hook, DAG island, Overview/Job/Agent/Adapter/OKF pages)."}
{"type":"methodology","text":"Contract-level TDD per task: failing tests from the pinned seam, minimal body, one acceptance check, commit. Control-api = Node stdlib zero-dep; wrappers = POSIX shell; validator = ajv-free JSON-Schema 2020-12; web = Astro/@astrojs/node standalone + React + Tailwind + xyflow + zustand. Fail-closed everywhere. Wrapper edits target repo-vendored canonical wrappers/*.sh (NOT global ~/.claude/skills). One editor per wrapper file (session-continuity + adapter modes merged into ca-T1/T2/T3 to avoid same-wave file overlap)."}
{"type":"intent","text":"Everything here builds on Plan A's landed artifacts (wrappers/*.sh, src/runner.js, src/dynwf-driver.js, presets/, presets/_validate.mjs). The Web UI additionally consumes bin/okf + bin/harness-adapter from the harness-nongated plan."}
{"type":"direction","verbatim":"all the gated specs and tasks for after plan a lands"}
{"type":"direction","verbatim":"No stubs/placeholders/workarounds in DELIVERABLE code — full functionality. A contract/spec/plan that pins the seam + leaves the body to the implementer is the CORRECT shape, NOT a stub."}
{"type":"direction","verbatim":"NODE_AUTH_TOKEN for GitHub Packages auth is NEVER committed; .npmrc carries no literal token, sourced from developer env at install; .gitignore excludes any token file."}
{"type":"direction","verbatim":"control-api binds 127.0.0.1 only, no auth, single-user; ?token= shared-secret hook STUBBED for eventual non-local case."}
{"type":"deferred","text":"Attachment-send in the Agent steer composer","reason":"Non-goal v1 per web-ui spec — text-only steer for now."}
{"type":"gated","id":"g1","category":"policy","needs":"Plan A (mega-plan-harness) merged to main","why":"Every task here edits or builds on Plan A's landed artifacts (wrappers/*.sh, src/runner.js, src/dynwf-driver.js, presets/, presets/_validate.mjs). Running before Plan A lands = editing files that do not yet exist / colliding with Plan A's live integration branch and worktrees.","options":["proceed","abort"],"default":null,"status":"RESOLVED","answer":"proceed","resolved_by":"agent-verified","source":"author","binds_meta":null}
{"type":"gated","id":"g2","category":"policy","needs":"Plan harness-nongated landed (bin/okf + bin/harness-adapter on main)","why":"The Web UI gateway (ui-T13) shells bin/okf and bin/harness-adapter; the OKF page (ui-T21) and Adapter page (ui-T20) depend on them transitively. Those binaries are deliverables of the harness-nongated plan, not this one — cross-plan dependency expressed as a gate, not a task->task deps edge.","options":["proceed","abort"],"default":null,"status":"RESOLVED","answer":"proceed","resolved_by":"agent-verified","source":"author","binds_meta":null}
{"type":"anchor","path":"docs/specs/2026-06-30-harness-control-api-design.md","what":"spec (control-api)"}
{"type":"anchor","path":"docs/specs/2026-06-30-harness-adapter-config-design.md","what":"spec (adapter-config; component 4 = --health/--list-models)"}
{"type":"anchor","path":"docs/specs/2026-06-30-harness-web-ui-design.md","what":"spec (web ui)"}
{"type":"anchor","path":"docs/plans/2026-06-30-harness-gated.md","what":"plan"}
{"type":"task","id":"ca-T1","wave":1,"phase":1,"desc":"na.sh — session continuity (--session-id, stream-json flip, session_id status line) + --interactive stub + --health/--list-models modes","status":"COMMITTED","deps":[],"requires_decision":"g1","files":["wrappers/na.sh","wrappers/test/na-session.sh"],"blocker":"g1: Plan A merged to main"}
{"type":"task","id":"ca-T2","wave":1,"phase":1,"desc":"ca.sh — create-chat session continuity (chatId pre-step, --resume bind, session_id status line) + --health/--list-models modes","status":"COMMITTED","deps":[],"requires_decision":"g1","files":["wrappers/ca.sh","wrappers/test/ca-session.sh"],"blocker":"g1: Plan A merged to main"}
{"type":"task","id":"ca-T3","wave":1,"phase":1,"desc":"codex.sh — exec->--json, thread_id capture from first thread.started event, verdict reparse from item.completed, steer-forbidden/kill-allowed + --health/--list-models modes","status":"COMMITTED","deps":[],"requires_decision":"g1","files":["wrappers/codex.sh","wrappers/test/codex-session.sh"],"blocker":"g1: Plan A merged to main"}
{"type":"task","id":"ca-T4","wave":1,"phase":1,"desc":"lib/normalize-events + spec/events.schema.json — 3 dialects (claude/cursor/codex) -> events/v1, position-derived deterministic seq, garbled line -> one error event, stateless per line","status":"COMMITTED","deps":[],"requires_decision":"g1","files":["lib/normalize-events","spec/events.schema.json","lib/test/normalize-events.test.*","lib/test/fixtures/"],"blocker":"g1: Plan A merged to main"}
{"type":"task","id":"ca-T5","wave":1,"phase":1,"desc":"lib/liveness — liveness/v1 ephemeral sidecar runstate/<slug>.live.json, set/get/reap, kill -0 reap, journal-wins on conflict (never source of truth)","status":"COMMITTED","deps":[],"requires_decision":"g1","files":["lib/liveness","lib/test/liveness.test.*"],"blocker":"g1: Plan A merged to main"}
{"type":"task","id":"ca-T9","wave":1,"phase":1,"desc":"runner.js + dynwf-driver.js — additive global run-registry pointer write at run start (runId=sha256(repoRoot\\0worktree)[:12]--slug -> ~/.harness/runs/<runId>.json), both planes, reap-on-read. Moved to wave 1 (file-disjoint from all other wave-1 tasks).","status":"COMMITTED","deps":[],"requires_decision":"g1","files":["src/runner.js","src/dynwf-driver.js","src/test/registry-pointer.test.*"],"blocker":"g1: Plan A merged to main"}
{"type":"task","id":"adpg-T6","wave":1,"phase":1,"desc":"adapter catalog (presets/adapters.json, adapters/v1) + spec/adapters.schema.json — additionalProperties:false, id+wrapper required, wrapper-path-UNIQUE invariant, no arbitrary-exec field (listModels bool gates --list-models)","status":"COMMITTED","deps":[],"requires_decision":"g1","files":["presets/adapters.json","spec/adapters.schema.json","test/adapters-schema.sh"],"blocker":"g1: Plan A merged to main"}
{"type":"task","id":"ca-T7","wave":2,"phase":1,"desc":"WRAPPER-CONTRACT.md + _contract-probe.sh — add session-continuity section + --health/--list-models section (additive; exit-code/flag contract untouched); probe asserts session_id on completion + both modes","status":"COMMITTED","deps":["ca-T1","ca-T2","ca-T3"],"requires_decision":null,"files":["spec/WRAPPER-CONTRACT.md","wrappers/_contract-probe.sh"],"blocker":null}
{"type":"task","id":"adpg-T8","wave":2,"phase":1,"desc":"presets/_validate.mjs binding<->catalog cross-check — binding.wrapper resolves to a catalog adapter (unique path), binding.model in adapter.models; author/CI-time error, runtime dispatch unchanged","status":"COMMITTED","deps":["adpg-T6"],"requires_decision":null,"files":["presets/_validate.mjs","test/validate-crosscheck.sh"],"blocker":null,"note":"transitively gated on g1 via deps->adpg-T6 (wave-1 frontier carries requires_decision g1); engine won't reach wave 2 until wave 1 clears the gate"}
{"type":"task","id":"ca-T10","wave":3,"phase":1,"desc":"src/supervisor.js — --supervised control plane (default OFF): liveness registration, unix socket $HARNESS_HOME/sock/<runId>.sock, kill/steer/pause/resume verbs each journaled, state-machine leased->implemented->gated->[reviewed]->committed, codex steer-forbidden, DynWF inbox plane","status":"COMMITTED","deps":["ca-T1","ca-T2","ca-T3","ca-T4","ca-T5","ca-T9"],"requires_decision":null,"files":["src/supervisor.js","src/test/supervisor.test.*"],"blocker":null}
{"type":"task","id":"ca-T11","wave":4,"phase":1,"desc":"src/control-api.js — Node stdlib http bind 127.0.0.1, runId-keyed route table (GET /runs, /runs/:id, /tasks/:tid, /stream SSE; POST steer/kill/pause/resume), Last-Event-ID replay at seq, fail-closed 409 on unsupervised run","status":"COMMITTED","deps":["ca-T4","ca-T5","ca-T9","ca-T10"],"requires_decision":null,"files":["src/control-api.js","src/test/control-api.test.*"],"blocker":null}
{"type":"task","id":"ui-T12","wave":5,"phase":1,"desc":"Scaffold web/ app — package.json + astro.config (@astrojs/node standalone + @astrojs/react) + .npmrc (GitHub Packages, no committed token) + tailwind.config; deps pinned (ui-primitives/tokens/editor, xyflow, elk/dagre, react-markdown+remark-gfm, zustand)","status":"COMMITTED","deps":[],"requires_decision":null,"files":["mega-plan-harness/web/package.json","mega-plan-harness/web/astro.config.*","mega-plan-harness/web/.npmrc","mega-plan-harness/web/tailwind.config.*","mega-plan-harness/web/test/scaffold.test.ts"],"blocker":null}
{"type":"task","id":"ui-T13","wave":6,"phase":1,"desc":"Gateway endpoints web/src/pages/api/** — runId-keyed relay fronting run sockets + okf + harness-adapter; two SSE tiers (multiplexed summary + per-run), forwards Last-Event-ID upstream, fail-closed 502/404/500. Cross-plan: shells bin/okf + bin/harness-adapter (gate g2).","status":"COMMITTED","deps":["ui-T12","ca-T11"],"requires_decision":"g2","files":["web/src/pages/api/**","web/test/gateway.test.ts"],"blocker":"g2: plan harness-nongated landed (bin/okf + bin/harness-adapter)"}
{"type":"task","id":"ui-T14","wave":7,"phase":1,"desc":"Client run-store web/src/lib/run-store.ts (zustand) — single client-truth, apply-by-seq idempotent dedupe (ignore seq <= last applied), only place SSE frames apply","status":"COMMITTED","deps":["ui-T13"],"requires_decision":null,"files":["web/src/lib/run-store.ts","web/test/run-store.test.ts"],"blocker":null}
{"type":"task","id":"ui-T15","wave":7,"phase":1,"desc":"SSE client hook web/src/lib/use-run-stream.ts — native EventSource('/api/runs/:runId/events'), parse frame -> dispatch into run-store, relies on browser Last-Event-ID reconnect (no custom backoff)","status":"COMMITTED","deps":["ui-T13"],"requires_decision":null,"files":["web/src/lib/use-run-stream.ts","web/test/use-run-stream.test.ts"],"blocker":null}
{"type":"task","id":"ui-T16","wave":7,"phase":1,"desc":"DAG island web/src/islands/Dag.tsx — @xyflow/react + elk/dagre, WaveNode/TaskNode/edge shapes, status->color, reused by Job (interactive) + Overview (fitView non-interactive mini-preview)","status":"COMMITTED","deps":["ui-T13"],"requires_decision":null,"files":["web/src/islands/Dag.tsx","web/test/dag.test.ts"],"blocker":null}
{"type":"task","id":"ui-T17","wave":8,"phase":1,"desc":"Overview page index.astro + Overview.tsx — tile grid over not-done runs, ALL badges from ONE /api/runs/events summary stream, status->CTA map verbatim, DAG mini-preview per tile","status":"COMMITTED","deps":["ui-T14","ui-T16"],"requires_decision":null,"files":["web/src/pages/index.astro","web/src/islands/Overview.tsx","web/test/overview.test.ts"],"blocker":null}
{"type":"task","id":"ui-T18","wave":8,"phase":1,"desc":"Job page runs/[runId].astro + Job.tsx — metadata panel (7 registry fields + land_mode, NO type field) + full interactive DAG + per-wave badges; opens per-run detail stream on mount","status":"COMMITTED","deps":["ui-T14","ui-T15","ui-T16"],"requires_decision":null,"files":["web/src/pages/runs/[runId].astro","web/src/islands/Job.tsx","web/test/job.test.ts"],"blocker":null}
{"type":"task","id":"ui-T19","wave":8,"phase":1,"desc":"Agent view runs/[runId]/agents/[agentId].astro + AgentChat.tsx — react-markdown timeline filtered to agentId, detail rail, steer composer disabled when steer-forbidden(codex)/committed with 409 reason inline, killswitch all seats","status":"COMMITTED","deps":["ui-T14","ui-T15"],"requires_decision":null,"files":["web/src/pages/runs/[runId]/agents/[agentId].astro","web/src/islands/AgentChat.tsx","web/test/agent-chat.test.ts"],"blocker":null}
{"type":"task","id":"ui-T20","wave":8,"phase":1,"desc":"Adapter page adapters.astro + Adapters.tsx — enable/disable + per-adapter config + granular model select + health via /api/adapters; descriptor {id,kind,enabled,models,availableModels?,config,health}. Cross-plan: adp-T9 adapter CLI (gate g2) + adpg-T6 catalog (this plan).","status":"COMMITTED","deps":["ui-T13","adpg-T6"],"requires_decision":null,"files":["web/src/pages/adapters.astro","web/src/islands/Adapters.tsx","web/test/adapters.test.ts"],"blocker":null}
{"type":"task","id":"ui-T21","wave":8,"phase":1,"desc":"OKF KB page okf.astro + OkfKb.tsx — progressive index tree, concept read/edit, sync; write failing okf doctor rejected with output shown (atomic). Cross-plan: bin/okf from harness-nongated (gate g2).","status":"COMMITTED","deps":["ui-T13"],"requires_decision":null,"files":["web/src/pages/okf.astro","web/src/islands/OkfKb.tsx","web/test/okf.test.ts"],"blocker":null}
{"type":"session_memory","note":"ca-T9 was authored in scratch at wave 2 but moved to wave 1: its files (src/runner.js, src/dynwf-driver.js) are disjoint from every other wave-1 task, so serializing it wasted parallelism. It blocks ca-T10/ca-T11 (later waves) — no wave-1 conflict."}
{"type":"session_memory","note":"Hazard solved at authoring: control-api session-continuity edits AND adapter --health/--list-models modes both touch wrappers na.sh/ca.sh/codex.sh. Merged into ONE task per wrapper (ca-T1/T2/T3 — one editor per file) to avoid same-wave file overlap."}
{"type":"session_memory","note":"Cross-plan deps (okf-T6, adp-T9, from harness-nongated) feed ui-T13/ui-T20/ui-T21 but are NOT task->task deps edges here — those ids live in the other plan's graph. Expressed as gate g2 + task-desc notes. requires_decision holds ONE gated id: ui-T13->g2; g1 reaches downstream control-api/adapter tasks transitively via deps from the g1-gated wave-1 frontier."}
