Multi-agent process harness — extracted from henrik-me/guesswhatisnext for reuse across projects.
Status: v0.17.0 shipped (2026-07-05) — minor release bundling the work merged since v0.16.0: CS86 (the canonical
harness dispatchsub-agent briefing preamble + its language-profile fences are relocated out of the always-loadedOPERATIONS.mdinto a new harness-owned managedDISPATCH-PREAMBLE.md— the single sourceharness dispatchnow machine-extracts, byte-identically, with a fail-closed fallback to the legacy inlineOPERATIONS.mdfor un-synced consumers; the new managed file is a Minor trigger), CS71 (the managedreview-gates.yml/pr-evidence-lint.ymlderive theworkboard-onlyevidence-gate skip from the PR's allowlist-confined diff rather than the racy label, so a correctly-shaped workboard PR is green on its first CI event, plus a newcheck-workboard-allowlist-consistencylinter), CS91 (fixes #394 —workboard-auto-approve.ymlhardening — and #395 Rec A/C: the merge-posture reframe + a boundedworkboard/maint-*auto-merge pattern; Rec B stays open in CS106), CS26 (a newcheck-config-placeholderslinter + seeded.gitattributes+ a realversionpin on freshharness init), CS68 (harness review --implementer-modelsfor non-CS dependency/maintenance branches), CS24 (check-clickstopnow mechanically requires a CHANGELOG-touch task row on distributed-surface CSs; LRN-101), CS75 (check-clickstopdirectory-form recursion + fence-aware plan-vs-impl gate), CS76 (fixes #229 — consumer-shipped composed process-doc cross-references resolve), and CS87 (copilot-engage --helpaccuracy). The new managed file + new linters + new--implementer-modelsflag are the Minor triggers. SeeCHANGELOG.mdfor the full delta. Prior: v0.16.0 shipped (2026-07-04) — minor release shipping CS102 (fixes #423): the canonicalharness dispatchsub-agent briefing preamble is split into a language-agnostic managed core (preflight, file ownership, required reading, fail-closed, report shape) plus selectable language profiles (node|dotnet). A new optionaldispatch.language_profilekey inharness.config.json+ a--language-profile <name>CLI override splice the ecosystem-specific conventions + self-checks into the core, so a .NET consumer no longer has to negate Node/ESM/npm conventions in every dispatch (config parsed fail-closed; unknown profile exits 2; defaultnode, backward-compatible). The new config key + flag are the Minor trigger. SeeCHANGELOG.mdfor the full delta. Prior: v0.15.0 shipped (2026-07-03) — minor release bundling the merged #420–#424 arc: CS100 (the new opt-inharness install-hooksverb — a gitprepare-commit-msghook that auto-adds theCo-authored-by: Copilottrailer including on merge commits, so the commit-trailers (B1) gate passes by construction and the recurring manualgit commit --amendon merges goes away; fixes #421), CS103 (the managedpr-evidence-lint.ymlworkflow now re-runsread-only-gatesonpull_request_reviewsubmissions so the async Copilot review auto-passes A5+A16 without a manualgh run rerun, plus a newREVIEWS.md§2.4.3 "leaner review sequencing" doctrine; fixes #424), CS97 (check-commit-trailersno longer false-fails on.git/COMMIT_EDITMSG#-comment/>8-scissors lines left by a rebase/merge; fixes #420), and CS101 (harness review's Copilot leg delegates to the hardenedcopilot-engageREST--add-reviewerpath; fixes #422). The newinstall-hooksverb + the consumer-visible managed-workflow trigger are the Minor triggers. SeeCHANGELOG.mdfor the full delta. Prior: v0.14.0 shipped (2026-07-03) — minor release shipping CS95 (fixes #417):harness statusandharness claimnow compare a clickstop's Owner to the current full agent-id, so an orchestrator in a concurrent same-machine clone (<id>-c<N>) can't mistake another orchestrator's active CS for its own —statusannotates each Active-Work / on-disk row(you)/(not you: <id>), andclaim's already-active no-op refuses (exit 1, naming the owner) on an owner mismatch unless the new--takeoverflag reassigns ownership (the minor-bump trigger). All comparisons are exact full-id equality — never a prefix match (yoga-ae≠yoga-ae-c3). SeeCHANGELOG.mdfor the full delta. Prior: v0.13.0 shipped (2026-07-03) — minor release bundling the work accumulated since v0.12.0: CS65 (LEARNINGS archive tier — agedapplied/obsoleteentries move to a siblingLEARNINGS-archive.mdbehind anchor-stable### LRN-NNNstub redirects), CS85 (thecheck-clickstop-link-durabilityguard, closing the #371 bootstrap-durability defect class in both self-host and consumer mode), CS92 (harness copilot-engagereliability hardening — transient-ghretry, reviewer-add verification, and an honestverifiedsignal), and CS93 (fixes #407 —harness review <pr>'s non-dry-run clickstop lookup now resolves zero-padded and directory-form clickstops via a padding-insensitive, fail-closed resolver). SeeCHANGELOG.mdfor the full delta. Prior: v0.12.0 shipped (2026-07-02) — minor release shipping CS83 (consumer-doc invocation-form genericity, #370): consumer-shipped onboarding docs (INSTRUCTIONS.md,.github/copilot-instructions.md,RETROSPECTIVES.md,READMEGUIDE.md) and theOPERATIONS.mdprocess base now render consumer-valid CLI invocations via a render-context-gated{{harness_invoke}}templating placeholder — the self-host rendersnode bin/harness.mjswhile consumers rendernpx -y github:henrik-me/agent-harness#<ref>(injected bylib/sync.mjsas a default underconfig.templating, so existing consumers get the corrected form on the nextharness syncwith no re-init) — replacing the harness-repo-localnode bin/harness.mjs/node scripts/*.mjscommands that don't exist in a consumer;scripts/check-consumer-template-genericity.mjsgains an orthogonal invocation scan so the regression cannot recur (the new optionaltemplating.harness_invokekey is the minor-bump trigger). Re-files the command-example half of #356. SeeCHANGELOG.mdfor the full delta. Prior: v0.11.0 shipped (2026-07-02) — minor release closing the consumer-feedback loop on issues #352/#356: CS80 made theharness releaseverb the single creator of the GitHub Release (deleted the redundant pre-verbrelease.yml); CS81 fixed three dangling cross-references shipped in the v0.10.0 consumer templates (placeholderLRN-A/LRN-B→LRN-164/LRN-165, a staleINSTRUCTIONS.mdanchor, andREADMEGUIDE.mddocs/adrlinks) and added thecheck-doc-xref-resolvabilitylinter (the minor-bump trigger); CS82 madeharness sync --mode=applyderive correct lock provenance undernpx/npminstalls (or fail closed) instead of silently writing placeholders. Prior: v0.10.0 shipped (2026-07-01) — minor release shipping theharness releaseverb (CS67): a previewable, two-phase, dry-run-first CLI that mechanizes the release cut that OPERATIONS.md § Release process documents by hand — Phase A previews/applies the version bump + CHANGELOG[Unreleased]→[x.y.z]promotion + README pin sweep (refusing a SemVer-inconsistent bump); Phase B verifies the squash SHA, cuts the annotated tag (git tag -a+ push) + GitHub Release (gh release create --verify-tag), and files issue-only consumer notifications — with CS78 aligning Phase B to the manual annotated-tag process. SeeCHANGELOG.mdfor the full delta. Earlier: v0.9.0 shipped (2026-06-30) — minor release bundling the lifecycle-verb arc: new CLI subcommandsharness startup/status/claim/close-out/dispatch(CS64),harness doctor(CS64b), and the review-family verbsharness review-doc/review-cs/perf-review/security-review(CS66); new linters (CS69### LRN-NNNH3-header enforcement, CS70 directory-form close-out orphan guard, CS72 consumer-template genericity); and the consumer-onboarding docs (INSTRUCTIONS.md,.github/copilot-instructions.md) genericized + reclassified to the composed file class (CS72). SeeCHANGELOG.mdfor the full delta. v0.8.0 shipped (2026-06-09) — minor release bundling the CS63 harness-hardening arc plus a multi-CS open-learnings cleanup. New CLI subcommands (the minor-bump triggers per OPERATIONS.md § SemVer policy):harness upgrade <ref>(CS63c, #270) — a read-only dry-run preview of bumping the pinned harness to<ref>; andharness harvest(CS63b, #267) — the de-stubbed advisory scan of staleopenlearnings. New default-on consumer merge gate:template/managed/.github/workflows/harness-pr-check.yml(CS63a, #264) runsharness lint+ a managed/composed file-class drift classifier on every consumer PR; freshharness initopts in by default (setpr_check.enabled: falseto opt out). Existing consumers adopt manually (copy the workflow + add it tomanaged.files). Other shipped work: CS54b (#258) deletes the orphaned pre-strict PR template; CS61 (#250) ships the shared dep-freelib/reviews-policy.mjsconfig reader (LRN-145, applies/closes residual LRN-142); CS62 (#251) makesharness whoamitests hermetic against the checkout folder name (LRN-146); CS60 (#244) lands a 7-LRN cleanup bundle (LRN-132/133/140/141/142/143/144); CS57 (#232) hardens the post-CS48 model-independence linter; CS47 (#236) closes out the LRN-124 detached-HEAD investigation with a permanent registry-driven regression guard; CS27 (#239) tightens thelib/sync.mjsWORKBOARD active-row detector + addsharness lintadoption hints; CS63b/C63-5 addsscripts/check-closeout-freshness.mjs(close-out PRs touchingactive_csNN_* → done_csNN_*must touchCONTEXT.md); and CS68 documents the clickstop-filing procedure end-to-end. v0.7.0 shipped (2026-06-03) — minor release bundling three clickstops: CS54 (v0.x doc cleanups: cross-repo pin-bump PR-body checklist [LRN-134], narrow re-attest pattern [LRN-135], Review logmodel-column bare-id rule +check-review-log-evidence.mjsgate hardening [LRN-136], and areviews.*vsreview_gates.*config-block disambiguation section; LRN-139 filed for the plan-side fact-claim verification gap), CS55/#213 (cross-repo handoff doctrine — Hard Rule § 6 "file issues, never commit" in non-harness repos; LRN-137), and CS56/#216 (newharness cross-repo open-issueCLI subcommand with realpath-based--body-filecwd-containment; LRN-138). The new CLI subcommand (CS56) is the minor-bump trigger per OPERATIONS.md § SemVer policy ("New CLI subcommand added → Minor"); CS54/CS55 alone would have been a patch. v0.6.0 shipped (2026-05-27) — minor release packaging the v0.5.2-to-v0.6.0 review-doctrine arc: CS48/#142 (ban implementer self-review as review evidence; LRN-127 + Sub Invaders PR #28 regression), CS49/#139 (codify orchestrator availability + 15-minute progress/stall reporting + Workboard-first status for out-of-CS work; LRN-126), CS50/#138 (optionalWORKBOARD_MERGE_TOKENPAT admin-bypass fallback for validated workboard-only PRs without the G3 App), CS51/#140 (PR-side enforcement gates:review-log-evidence,copilot-review-attached,independence-invariant,review-threads-resolved), CS52/#141 (harness review <pr>CLI as the canonical content-PR review orchestrator), the CS47 plan-filing doc (planned_cs47_detached-head-investigation.mdfor the v0.5.1 detached-HEAD trap; CS47 fix itself ships post-v0.6.0), and one consumer-visible default flip:scripts/check-review-evidence.mjs--strict-agent-columnsis now the default behavior (CS53 C53-5; fulfills the v0.5.0-era CS42 C42-6 promise) — missingImplementer agent/Reviewer agentrows in## Model auditbecome errors rather than warnings. Pass the new--no-strict-agent-columnsflag to opt out for transitional consumers. v0.5.2 (2026-05-14) shipped the post-v0.5.1 accumulated work: CS46/#146 discoverability (canonical workboard empty-state + verbatim Plan-vs-impl review labels + self-documenting linter hints), CS45 typed-error fs envelope aroundlib/copilot-engage.mjscache-write seam (new exit code5+--cache-direscape hatch), CS44 Copilot Bot doc-impl alignment, CS43 clickstop-implementer-not-reviewer linter recursion + date-gated grandfathering, CS23pull_request: types: [edited]sogh pr edit --bodyre-firespr-body. v0.5.0 (CS42, 2026-05-14) shipped the v0.5.0 arc (CS40/CS41/CS42):harness copilot-engage <pr-number>CLI, theclickstop-implementer-not-reviewerlinter, first-classImplementer agent+Reviewer agentcolumns in## Model audit,harness review-outputreviewer-output validator, and two default flips (review_gates.enabled: trueon fresh init;--strictdefault forcheck-clickstop-plan-review.mjs). v0.4.0 (CS39, 2026-05-13) shipped the #145 enforcement-doctrine arc (CS35–CS38b:harness pr-evidencePR-time gates B1+A3+A4+A5+A6+A16, canonical PR template skeleton + sync migration,pr-evidence-lint.ymlpre-merge enforcement). SeeCHANGELOG.mdfor the full delta andproject/clickstops/done/done_cs01_bootstrap-repo/harness-cs-plan.mdfor the roadmap.
A shippable kit for running coordinated, multi-agent work on a software project:
- Process docs — INSTRUCTIONS, CONVENTIONS, OPERATIONS, REVIEWS, TRACKING, RETROSPECTIVES — that define how orchestrators and sub-agents claim, dispatch, review, and close clickstops (CSs).
- Structured-doc linters — one per doc — that turn the process into mechanical enforcement.
- Scaffolds — opt-in starting points for smoke tests, migrations, container validation, health checks, seeders, deploy verification, feature flags, and one-shot CS probes.
- Reusable GitHub workflow so a consumer wires up the whole thing in ~10 lines.
Three file classes:
- managed — overwritten on every
harness sync; the source of truth. - composed — managed core + marker-preserved local blocks for project-specific extensions.
- seeded — created if missing, never overwritten.
Three install models are supported:
Option A — clone and run directly (recommended for CI and for previewing upgrades): clone the harness and invoke its CLI with Node —
git clone https://github.com/henrik-me/agent-harness.git
node agent-harness/bin/harness.mjs <command>This avoids the npm GitFetcher regression noted below and is the pattern the harness's own reusable workflow uses (clone-then-node bin/harness.mjs). Pin the harness version in harness.config.json version for reproducibility, and use harness upgrade <ref> (see § Upgrading) to preview a bump before applying it.
Option B — install from GitHub by ref (today, default npx path): npx -y github:henrik-me/agent-harness#<ref> works anonymously now that the repo is public — no token required. <ref> is a semver tag (e.g. v0.17.0), branch name, or 40-character commit SHA. Recommend pinning to a semver tag in harness.config.json version for reproducibility. (For private forks of this harness, see docs/private-consumption.md for the GITHUB_TOKEN setup.)
Note: as of v0.2.0 the bare
npx -y "github:owner/repo#<sha>"install path hits an npm 10.8.x/10.9.xGitFetcher requires an Arborist constructorregression on GitHub Actions runners. The harness's own reusable workflow (harness-checks.yml) bypasses this by cloning + invokingnode bin/harness.mjsdirectly. External consumers running their own CI may want to do the same. Tracked as a known issue. (Still applies under v0.17.0 — same npm CLI versions on the runners.)
Option C — install from npm by version (planned for CS15+ post-public-flip; not active today): npx -y @henrik-me/agent-harness@<version> will work once the package is published. The name field in package.json already reserves the npm scope; the package is currently private: true. Same pinning advice via harness.config.json version.
# In a consumer repo:
npx -y github:henrik-me/agent-harness#v0.17.0 init
# review the generated harness.config.json, then:
npx -y github:henrik-me/agent-harness#v0.17.0 syncharness upgrade <ref> previews upgrading the pinned harness to <ref> (a
semver tag, branch, or 40-char SHA): it fetches that ref's templates and runs a
dry-run sync against your repo, printing the list of files that would change
(per-file action + class) plus a change-count summary. Nothing is applied — it
is a safe, read-only preview (additive over sync; no apply-path rewrite). To
apply after reviewing, set harness.config.json version to <ref> and run
harness sync --mode=apply (add --accept-major for a major bump). See
OPERATIONS.md § Sync for the full preview-then-apply flow.
node agent-harness/bin/harness.mjs upgrade v0.17.0 # preview only
# review the change list, then bump harness.config.json "version" to v0.17.0 and:
node agent-harness/bin/harness.mjs sync --mode=apply.
├── bin/ # harness CLI dispatcher (CS04)
├── lib/ # sync engine, composed parser, templating, lock-file (CS03–CS05)
├── template/
│ ├── managed/ # process truth — synced into consumers, overwrite-on-sync
│ ├── composed/ # managed core + local-block extensions
│ └── seeded/ # create-if-missing skeletons
├── scripts/ # structured-doc linters + policy checks (CS05–CS07)
├── scaffolds/ # opt-in copy-and-customize patterns (CS10)
├── schemas/ # JSON Schema for config, lock file, per-doc shapes
├── .github/workflows/ # CI + reusable workflow for consumers (CS12)
└── project/clickstops/ # the harness's own CS lifecycle (planned / active / done)
The repo IS the persistent memory between sessions. There is no other state. Per-path purpose:
| Path | Purpose |
|---|---|
INSTRUCTIONS.md |
Orchestrator workflow — bootstrap reading order, Session Start checklist (incl. sanity-check commands), Per-CS Loop, "When to Add X" recipes |
OPERATIONS.md |
Lifecycle procedures — Claim / Dispatch / Sync / Harvest / SemVer / Conventions; canonical sub-agent briefing preamble |
CONTEXT.md |
Current state, recently completed CSs with commit refs, active CS pointer, blockers, parallelism posture |
WORKBOARD.md |
Live coordination only — Orchestrators table + Active Work table. Nothing else. The queue lives in project/clickstops/planned/ and history in project/clickstops/done/; WORKBOARD never duplicates either. |
LEARNINGS.md |
Process learnings (LRN-001..N), schema-validated, sectioned by status |
ARCHITECTURE.md |
Architecture overview — Components, Data model, Decision log |
REVIEWS.md |
Independent-reviewer model, taxonomy, HIGH-RISK CS list, GPT-5.6 Sol fallback rules |
template/managed/ |
Process truth — files synced verbatim into consumer repos; overwrite-on-sync |
template/composed/ |
Managed-core docs that consumers can extend via local blocks; recomposed on sync |
template/seeded/ |
Create-if-missing skeletons; seeded once into consumers and never overwritten |
project/clickstops/active/ |
Currently in-flight CS spec (one file when active, empty when stable) |
project/clickstops/planned/ |
Queued CSs in priority order (planned_cs<NN>_<short-name>.md) |
project/clickstops/done/ |
Completed CS files with full actuals (sub-agent ledger, review log, learnings filed, follow-up planned CSs) |
One-time setup (per fresh clone): install dev dependencies before your first session — Node ≥ 20, then
npm ciat the repo root (node_modulesis gitignored and per-checkout). See CONTRIBUTING.md for detail. Skipping this makes the Session Start bootstrap check fail withERR_MODULE_NOT_FOUND—mainis not broken.
Open a fresh Copilot CLI (or equivalent) at the repo root and use a starter prompt like:
cd <repo>, then read INSTRUCTIONS.md carefully and follow the Quick Reference
Checklist (especially the Session Start bootstrap sanity check). After that,
continue from where the prior session left off (check CONTEXT.md and
WORKBOARD.md for the current state). Operate autonomously from the current
repo state. Check in only for substantive design decisions not derivable from
the cs-plan + LRNs, or for changes that would materially alter the public
repo/security posture.
That's all you need to type. INSTRUCTIONS.md pulls in everything else in
the right order via its Pointers section.
This repo follows the harness from CS01. Bootstrap proto docs (INSTRUCTIONS, CONVENTIONS, OPERATIONS, REVIEWS, TRACKING, RETROSPECTIVES) are hand-authored; from CS11 onward they are produced by harness sync against the canonical template/managed/ + template/composed/ (with local blocks preserved) and a CI gate prevents drift. Seeded project-state docs (CONTEXT, ARCHITECTURE, LEARNINGS, WORKBOARD) are preserved as-is. Project-owned files (this README, LICENSE, package.json, .gitignore, .editorconfig) are excluded from sync entirely. See INSTRUCTIONS.md and project/clickstops/done/done_cs01_bootstrap-repo/harness-cs-plan.md.
See ARCHITECTURE.md for the full design: file-class model (managed / composed / seeded), sync engine internals, lock-file format, and the linter pipeline.
See CONTEXT.md for current project state, the active clickstop, and known blockers.
When harness orchestration needs work in a non-harness repository (e.g. henrik-me/sub-invaders), Hard Rule § 6 (see template/managed/.github/copilot-instructions.md) requires filing a GitHub issue rather than opening a PR. The harness cross-repo open-issue command is the supported way to do this.
node bin/harness.mjs cross-repo open-issue \
--repo henrik-me/sub-invaders \
--title "[harness:cs55] Adopt v0.6.x cross-repo handoff doctrine" \
--body-file issue-body.md \
--label harness-syncNotes:
- The
harness-orchestratorlabel is always added automatically; additional--labelflags append. - Titles MUST be prefixed with
[harness:cs<NN>]so two different clickstops cannot collide on the same handoff issue (idempotency safety per D56-4). - There is intentionally NO
harness cross-repo open-prcommand — the harness orchestrator never opens PRs in non-harness repos. - The command refuses
--repo henrik-me/agent-harness(use plainghfor harness-internal issues).
MIT — see LICENSE.