Imported from MadhavMareddy/AgentMarvel (
harness/framework/skills/run-harness-task/SKILL.md). Install upstream withnpx skills add MadhavMareddy/AgentMarvel --skill run-harness-task. Copyright stays with the author.
Skill: run-harness-task
Purpose
Execute a complete harness task through the full agent pipeline with verifier-author + verifier-judge separation, filesystem-locked tests, and a bounded retry loop.
When to use
- When a task spec is filled and ready for execution.
- When resuming a partially completed task from progress state.
Required inputs
- Filled task spec (from any workflow template, conforming to
the
task-specschema). - Pipeline mode:
interactive,autonomous, orreview.
Prerequisites and run start (prepare-run)
Run start is one deterministic call — the preconditions, profile resolution,
the rigor guard, the backbone assertion, and the .harness/run.json write are
all enforced by prepare-run, not hand-sequenced here:
node harness/framework/tools/run/prepare-run.js <task-spec-path> [--profile <name>]
<task-spec-path> accepts BOTH spec shapes: a JSON file conforming to the
task-spec schema, or the markdown active-spec file spec-from-requirement
writes (parsed deterministically from the template format). Pass the
active-spec path directly — never hand-convert it to JSON.
It reads the task spec + your-project/knowledge/project.json +
your-project/check-registry.json, resolves the profile via
resolve-workflow-bindings.js, applies the rigor guard and the backbone
assertion, writes .harness/run.json, and prints
{block, run_json_path, workflow}.
Pass --profile <name> when the task spec names a workflow: or a picker
started the run with a profile; omit it for the default sequence. Capture
workflow (the FROZEN workflow block that governs the run — see Workflow-aware
pipeline below; null = no workflow, full default sequence) and
run_json_path. block is only ever an abort object (null on success) —
never confuse it with the workflow block.
A non-zero exit is a fail-loud stop, not a warning. What each failure means and what you (the orchestrator) do about it:
- Bootstrap incomplete —
project.jsonhas no non-emptyname. Stop and tell the developer to run bootstrap-harness first. - Check registry unpopulated —
check-registry.jsonmissing, or lacking at least one tier 1 and one tier 2 check. Stop and tell the developer to run populate-verification first. - Task spec incomplete — missing/empty
id,title,type,scope,acceptance, orverify. Stop and tell the developer to fill a workflow template (framework/workflows/templates/) first. - No acceptance criteria —
len(task_spec.acceptance) == 0. This surfaces asabort_type: invalid_spec; the developer re-fills the spec. - Rigor / backbone violations surface as
abort_type: wrong_pathorabort_type: backbone_missing— see the Rigor guard and Backbone assertion under Workflow-aware pipeline for the exact developer message each carries.
prepare-run is the ONLY writer of .harness/run.json at start. Do not
hand-assemble the control file — the field semantics it writes (and the
lifecycle it participates in) are documented next.
Active-run control file (.harness/run.json)
The scope-guard PreToolUse hook (.claude/hooks/harness--scope-guard.js) is the
only HARD write-guard in the harness. It governs Write/Edit/MultiEdit by reading
.harness/run.json. run-harness-task owns this file's lifecycle:
-
CREATE at run start —
prepare-run(see Prerequisites above) writes.harness/run.jsonbefore Step 1 with all required keys:{ "task_id": "<task-id>", "started_at": "<ISO8601 UTC>", "pid": <process.pid of the orchestrator>, "scope": ["<glob from task_spec.scope>", "..."], "locked": [], "run_type": "<feature | bugfix | refactor | migration | harness-dev>", "phase": "active" }Do not write these keys by hand —
prepare-runis the single start-time writer. The field meanings below are the contract the rest of the run relies on:scopeis the set of globs fromtask_spec.scope— these are the only paths the run is permitted to write. A write outside every scope glob is blocked (rule a).lockedstarts empty and is populated by Step 5 (the lock-tie): after the lock record is written, itspathsare copied intorun.json::lockedso the scope-guard hook blocks tool-writes to the same paths the OS lock protects. Locked test paths can never be written, even underrun_type: "harness-dev"(rule b never lifts). It is cleared back to[]when tests unlock on PASS.run_type: "harness-dev"lifts ONLY rule (c) — writes to verification/gate/ test machinery (harness/your-project/check-registry.json, theharness/framework/workflows/verify-policy contracts, the lock/unlock scripts, the verifier agent files,.harness/locks/**). Use it only for harness self- development tasks. It does NOT lift rule (a) or rule (b).workflow(OPTIONAL) — frozen task-type workflow block, present when the run was started viastart-task(or when the task spec names aworkflow:— see Workflow-aware pipeline below):"workflow": { "name": "full-feature", "mode": "interactive", "agents": ["researcher", "planner", "architect", "verifier-author", "executor", "verifier-judge", "observer"], "bindings": { "executor": ["harness--react-component-patterns"] }, "bind_families": { "executor": ["backend", "ui", "db"] }, "todo": [ { "agent": "executor", "family": "db", "status": "todo" } ] }bind_familiesrecords the family names as chosen (the resolver output alone loses family names for resolved entries) — required by the exit ramp (save offer). Custom compositions record"name": "custom"with the full block — the run is reproducible from this block while the run is active, and durably from the observer's run report (Step 10 copies it). Absence of the key = no workflow governs; the full default sequence below applies. The scope-guard hook ignores this key (extra keys are tolerated by design). Under the parallel-only overlay,workflow.invocations(integer, starts at 0) is the global invocation ceiling counter — incremented before everyWorkflow(...)call at either fan-out call site (authors/execute — the only two call sites); if incrementing it would exceed 6, ABORT to human review regardless of other counters (seereference/invoking-workflow.md§"Self-contained fan-outs and the invocation ceiling").
-
State precedence is defined once in the shared resolver (
.claude/hooks/harness--run-state.js), imported by every hook that reads run.json. Absent → INERT (no run governs); malformed (bad JSON / missing key / scope not array) → DENY (fail closed); stale (past 6h TTL OR dead pid) → INERT + WARN (a crashed run must never lock the dev out); valid → ENFORCE scope. -
set
phase: "complete"after the observer step (Step 10) finishes. (scope-guard enforces scope identically for active and complete; the evidence- card hook will key onphaseseparately later.) On a PASS run this phase flip is performed byfinalize-run(Step 8/8b); on non-PASS exits set it as part of the abort handling. -
REMOVE
.harness/run.jsonon ALL exit paths — PASS, FAIL, and every abort type — via a finally/trap so a crashed run leaves at most a stale file (which the resolver treats as INERT), never a lingering active guard. On PASS the post-observer teardown (run.json removal, accepted promotes/saves) runs afterfinalize-runhas released the lock-tie — see Step 10 inreference/prose-backbone.md.
Full field semantics (locked lock-tie, run_type rule matrix, precedence
states): see reference/locking-and-retry.md.
Workflow-aware pipeline (task-type workflows)
Before Step 1, determine the agent sequence. prepare-run (Prerequisites) does
the resolution — the workflow it prints IS the governing sequence. The cases:
.harness/run.jsonalready carries aworkflowblock (picker-started) → the pipeline sequence is that block'sagentslist, in order. (A picker-started run has already written the block;prepare-runpreserves it.)- No block, but the task spec has
workflow: <name>→ pass--profile <name>toprepare-run. It looks for<name>.mdframework-preset-first (harness/framework/workflows/profiles/<name>.mdshadowsyour-project/workflows/profiles/<name>.md), mirroring the COLLISION RULEprofile-discovery.jsenforces — a framework profile name always wins over a same-named user profile. It callsresolve-workflow-bindings.js, whosefreezeWorkflowBlockis the single typed freeze point for the workflow block{ name, mode, agents, rigor, bindings, todo, run_as_workflow }— carryrun_as_workflowfrom the profile frontmatter verbatim — written verbatim into run.json. That freeze guarantees the two silent-downgrade fixes —rigordefaults tofull(a dropped rigor would silently downgrade full→quick, skipping locked verification) andrun_as_workflowis coerced from the frontmatter STRING to a real boolean (a"true"string is otherwise never=== true, forcing silent all-prose;coerceBoolcloses that). A droppedrun_as_workflowis the silent-prose bug: Step 0 gates onblock.run_as_workflow, so the flag MUST ride through verbatim — absent/false in the frontmatter means the block'srun_as_workflowis false (all-prose), same as the default no-flag path. Surface eachtodoentry (same wording as start-task §3), then proceed as (1). Unknown profile name →prepare-runstops and reports (never substitutes a default). - Neither →
prepare-runfalls back to the shippedfullprofile (the locked-pipeline default; itsrun_as_workflow: trueroutes the run onto the parallel-only overlay — the prose backbone with Workflow fan-outs). Only when that profile file is absent (bare install) does the run proceed blockless on the full default sequence below, unchanged.
Rigor guard (fail loud). prepare-run reads the resolved profile's rigor
field (default full). If rigor is quick or mid, it stops with
abort_type: wrong_path — no .harness/run.json is written and no pipeline step
runs. Relay to the developer: "<name> is a rigor: <rigor> workflow — it runs
on the light path. Start it via start-task (which routes Quick/Mid to
run-light-task), not by invoking run-harness-task directly." Only rigor: full
(or unset) profiles proceed past this guard.
Backbone assertion (fail loud). A rigor: full profile claims the locked-test
guarantee, so it MUST contain the hard backbone: planner, verifier-author,
executor, verifier-judge. prepare-run checks the resolved agents list; if
any hard-backbone agent is missing it stops with abort_type: backbone_missing
and no pipeline step runs. Relay: "<name> is rigor: full but omits backbone
agent(s) <missing> — full rigor cannot skip the locked-test regime. Add the
missing agent(s), or set rigor: quick|mid to run the honest lighter path." This
closes the silent path where a hand-authored profile keeps rigor: full after
trimming the agent list — which would otherwise skip verification while still
claiming proof-of-behavior. The compose path (start-task §2) already pre-selects
a non-removable backbone; this asserts the same contract for profiles authored as
files.
observer is required-when-present rather than hard-backbone: it is one of the
optional agents (researcher, researcher-coordinator, architect,
security-auditor, observer) a profile may remove via skip: [observer]
(the preset-inheritance rules for extends/skip). When observer IS listed
in the resolved agents, it must still run last — the canonical-order guard
applies to it whenever it is present, but its absence alone does not trip
backbone_missing.
Pipeline order
- researcher or researcher-coordinator (skip if context_pack exists OR task.scope.files <= 2)
- planner (always)
- architect (skip if scope.files <= 3 AND no consumer_boundaries) planner <-> architect: max 2 iterations
- verifier-author (always when ACs exist; abort if no ACs)
- [hash + chmod lock] (deterministic, no model)
- executor (attempt N, where N in {1, 2, 3})
- verifier-judge (attempt N, Haiku subagent via Task tool)
- [retry decision] (PASS -> step 9; PARTIAL routed by classification; FAIL -> step 6 with N+1; N=3 fail -> abort path)
- security-auditor (skip if planner.security_surface == false)
- observer (last, when present, including abort cases, with abort_flag — a
profile may remove it via
skip: [observer]; see backbone assertion above)
Status discipline (both paths)
One fixed-format row per phase, nothing between phases:
[<n>/<total>] <agent> <✓|✗|⏳> <one clause of substance> (<duration>)
Examples: [2/6] planner ✓ 3 slices, scopes disjoint (41s) ·
[4/6] executor ⏳ attempt 2/3. <total> = the resolved block's agent count.
No LLM narration between phases — findings belong in the phase's artifact and
the final gate.
Path split: on the all-prose path the coordinator invokes every agent, so
it emits every phase row. On the workflow-fan-out path the coordinator
still invokes every sequential agent as prose — researcher, planner,
architect, verifier-judge, security-auditor, observer — and emits their rows
exactly as on the all-prose path; only the two fan-out points (a multi-author
authors call, a multi-executor execute call) run inside a Workflow, where
per-agent visibility comes from the Workflow live progress tree (the
coordinator does not sit between those agents and must not synthesize rows
for them). For each fan-out the coordinator emits ONE row after the call
returns, covering the agents it ran, e.g.
[4/7] authors ✓ 3 verifier-authors, modules disjoint (1m40s) ·
[5/7] execute ✓ 2 executors, batch 1/2 (3m05s). When no workflow block
governs, <total> is the default-sequence agent count.
The run ends with the fixed result gate (see Step 10's output gate / run-light-task Step 4): diff | tests | verdict | cost — same four labels on every path, every run.
Procedure
Step 0: Route — parallel-only overlay or all-prose? (FIRST, before anything)
This is the FIRST executable action of the procedure — do it before Step 1. It is
the entry gate the "Parallel-only overlay" (reference/invoking-workflow.md)
describes; that reference is HOW, this Step is WHEN. Do not read past it into Step 1
without deciding here.
- If the frozen block carries
run_as_workflow: trueAND the target is Claude Code AND a Workflow tool is available to you: → Take the prose backbone with Workflow fan-outs. The model in one line: workflow = fan-out, prose = everything else. Every sequential agent — researcher, planner, architect (Steps 1-4), verifier-judge (Step 7, fresh Task subagent with the hash gate), security-auditor (Step 9), observer (Step 10) — is a normal PROSE agent dispatch, exactly as the Steps below write it, and the coordinator persists their artifacts exactly as the prose path always did. TheWorkflowtool enters at exactly TWO call sites, both fan-outs:- Authors fan-out (Step 5 entry). When the approved plan yields 2+
verifier-authors (one per disjoint module), call
Workflow({ name: "harness-pipeline", args })withargs.segment = "authors"— args viabuildAuthorsSegment+assertAuthorFanout+assertArgsTaskId(reference/invoking-workflow.md). The SAME call serves the initial author dispatch AND the architect-REVISE-converged re-dispatch — one call site, not two mechanics. - Execute batches (Step 6). When an executor batch holds 2+ executors, call
the Workflow with
args.segment = "execute"— batches viabuildBatches→buildExecutorBatches. A judge-FAIL retry re-invokes this same call site with the incrementedattempt_number(seereference/locking-and-retry.mdStep 6). - Single-agent rule (explicit). Invoke the Workflow ONLY when a batch has 2+ agents. A single-author task or a single-executor task runs those agents as plain prose dispatches — a 1-item fan-out earns nothing. The lock, the fingerprint bracket, and every other trust step stay coordinator code, unchanged (Steps 5, 7).
- THE ONE RULE (enforced by a PreToolUse hook,
segment-guard): theWorkflowtool is MANDATORY for every MULTI-AGENT batch of verifier-author or executor — never spawn a parallel batch member via the Agent/Task tool, no matter how many prose turns (REVISE loops, escalations, retries) came in between. Single sequential agents — researcher, planner, architect, verifier-judge, security-auditor, observer, a single author, a single executor — are prose dispatches, always. A blocked Task spawn means: rebuild args and re-enter the fan-out — do not retry the spawn. - If a fan-out invoke ERRORS, is REFUSED, or the tool turns out unavailable →
fall through to prose dispatch of that batch's agents (sequentially, per the
Steps below). Do NOT improvise a lighter run. BEFORE dispatching, record the
fall-through in
.harness/run.json: setworkflow.fallback = "all-prose"plus a one-lineworkflow.fallback_reason. This is what tellssegment-guardthe prose dispatch is legal; it also makes fallbacks observable in the run record.
- Authors fan-out (Step 5 entry). When the approved plan yields 2+
verifier-authors (one per disjoint module), call
- Else (
run_as_workflowabsent/false, non-Claude-Code target, or no Workflow tool) → proceed to Step 1 and run the all-prose Steps 1-10 as written.
Dispatch identity is sacred (enforced by segment-guard). Every prose dispatch
sets subagent_type to the agent's EXACT registered id — planner, architect,
verifier-judge, … — never decorated with the task name, ticket id, or a nickname
("planner-enh", "architect_users-page" are NOT agents: the platform silently
substitutes a generic agent with no definition, no capsule discipline, no
guardrails — observed live). Run context goes in the PROMPT, never the identity.
A blocked decorated spawn means: re-dispatch immediately with the exact id and the
same prompt, then continue the pipeline.
Sequencing is sacred too (enforced by segment-guard). verifier-author and
executor are NEVER in flight at the same time: the order is author → lock →
executor, and an executor dispatch is legal only AFTER run.json.locked is
filled by the lock step. Running them concurrently is not an "acceptable
deviation" — an unlocked test set means rule (b) protects nothing and the
author's blind oracle becomes timing luck. A blocked premature executor spawn
means: finish the author, run the lock step, then re-dispatch the executor
with the same prompt.
Stalled-agent protocol. An agent that produces no substantive output across repeated waits: kill it, re-dispatch the SAME agent fresh ONCE (same prompt). If the fresh dispatch stalls too, STOP and escalate to the dev. The coordinator NEVER absorbs a stalled agent's role — a coordinator self-review satisfies no gate (the reviewer's value IS its independence), and a self-implemented change satisfies no executor step. Two stalls = human decision, not improvisation.
Two legal paths, nothing else. A rigor: full run executes by EXACTLY ONE of two
paths: the prose backbone with Workflow fan-outs (when the flag and the tool line
up) or the all-prose backbone (Steps 1-10 with every dispatch prose). The paths
share Steps 1-10 — they differ ONLY in how multi-agent author/executor batches enter.
Executor-only is never a legal outcome — the backbone assertion above guarantees
block.agents contains the hard backbone: planner, verifier-author, executor,
verifier-judge (plus observer when the profile lists it — it runs last on either
path); the EXECUTION must honor that on whichever path runs. Both paths run the hard
backbone; executor-only is never a legal outcome of either.
Record which path was taken for the observer self-report
("prose-with-workflow-fanouts" or "all-prose-backbone").
Step 1: the prose backbone begins here
Steps 1-10 are the prose backbone — BOTH paths run them; the overlay only swaps
multi-agent author/executor batches into Workflow fan-out calls. Their full
prose lives in the reference files; this skeleton row and the ## Pipeline order
table above are the spine. Load the reference for the branch you are in:
- Steps 1-4, 9, 10 (research, plan, architect, security, observer) and the
light-path boundary →
reference/prose-backbone.md. - Steps 5, 6, 7, 8 (lock, executor, tamper bracket, retry, aborts) →
reference/locking-and-retry.md. - Fan-out invocation (Step 0 took the workflow-fan-out path) →
reference/invoking-workflow.md.
Reference
Load the file for the branch you are actually in — do not preload all four:
- Invoking the Workflow fan-outs (Step 0 took the workflow-fan-out path): the args mechanic, the two call sites (authors/execute), artifact persistence —
reference/invoking-workflow.md. - Running the all-prose backbone (Step 0 fell through, or on either path — the backbone is shared): Steps 1-4, 9, 10 in full, and the light-path boundary —
reference/prose-backbone.md. - Lock, tamper bracket, retry, aborts (you reached Step 5): the filesystem lock, the Step 7 tamper-detection bracket, the Step 8 retry table, and every abort type —
reference/locking-and-retry.md. - Output level + handoff-by-reference: how to set each agent's
output_leveland pass artifacts by path —reference/output-policy.md.