Harness authoring
Skill GhostlyGawd/recursive-harness/skills/harness-authoring
Portable, evidence-driven agent development harness for Codex, Claude Code, and generic Agent Skills. Active beta v0.1.2.
npx -y skills add GhostlyGawd/recursive-harness --skill harness-authoringAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Standards for writing or modifying the harness's own artifacts — skills, commands, agents, hooks, user-model entries. Use whenever /retro routes a learning into a new or edited artifact, when promoting a project-level learning upstream, or when refactoring existing skills. Encodes budgets, provenance, falsifiability, and the duplication check. Writing an artifact without this skill produces lint failures and junk-drawer drift.
SKILL.md
15.3 KB, as published. Nobody here has run it
Harness Authoring
You are writing instructions for a future you with no memory of today. Optimize for that reader: terse, falsifiable, triggered at the right moment.
Before writing anything
- Duplication check:
grep -ri <topic> skills/ commands/ agents/ CLAUDE.md. If a near-match exists, STRENGTHEN it (and bump its provenance) instead of adding a sibling. Two overlapping skills split the trigger and both rot. - Adopt-vs-rebuild gate (the dup check at FLEET scale): before building or
porting a capability a SIBLING/PARENT harness already implements, STOP — grep
the sibling harnesses too, not just this repo. Run the comparison, then put an
explicit choice to the USER — adopt/vendor the sibling's artifact (skill
vendoring-skills), let it flow via the master-harness consolidation, or rebuild here — and get a BUILD decision before writing code. A predict-first that only scores the port's mechanics (additive? lint-clean?) is NOT a build decision; predict the strategic question ("is a competing copy worth maintaining in this trunk?"). Kernel directive 6: never fork the brain. Resolve lineage DIRECTION first: a consolidation/aggregate harness is usually DOWNSTREAM (it syncs FROM this repo), so building a capability INTO it forks the brain, while building it in the upstream parent is legitimate and flows down. (session 7d2da048, 2026-06-21: master-harness looked adoptable until its LINEAGE.md showed it consolidates recursive+fable — the adopt call flipped to rebuild-native+graft.) (session a0a4278d, 2026-06-14: ported fable's competitor-scan into a new recursive competitive-research skill, commit b948a8a; the user then cancelled the whole effort — fable's was better "in every way" — so the port was sunk work the gate would have prevented.) - Right artifact check: re-run the routing-learnings tree. The most common
authoring error is writing a skill for what should be a hook. Before proposing an
enforcement GATE, grep ADRs + test docstrings +
memory/decisions/for whether it SHOULD gate — reversing a recorded "advisory, not a blocker" choice is reward-hack-adjacent, and an auditor catches it (edd67875: a CI drift-guard contradicted test_atlas.py's ritual line). - Source-of-truth gate (artifacts asserting external behavior): if the
artifact states facts about an external process, tool, or environment — CLI
behavior, where files live, how cleanup works, what a hook blocks — verify
each load-bearing claim against the authoritative source (live docs via
WebFetch, and/or an empirical test) BEFORE shipping, not after. Porting a
sibling-repo skill does NOT inherit verification — its facts were true in the
OTHER repo. (session 9147f304, 2026-06-14: ported the worktree skill and
opened PR #14 before reading the live Claude Code docs the skill's own §5
mandated; shipped a false guard-hook claim a user-requested second pass caught.)
A digest from a fan-out / background research workflow (Discover→Read→Synthesize)
is NOT grounding: an empty fetch phase still returns a fluent, confident summary
written from training memory, with no signal of failure. Before trusting or
relaying one, read its provenance counters (urlsRead / sources / pages-read); if
zero sources were read, DISCARD the synthesis and fetch directly. (session
5af1bbc4, 2026-06-18: a read-skills-docs workflow returned a full docs digest with
urlsRead:[] / "Discovered 0 canonical doc URLs"; only that field exposed the fabrication.)
The same gate covers claims about the HARNESS'S OWN code, not just external tools: any
"X is shared by / already wired to / consumed by Y" sentence in a plan, proposal, or PR
body is a FACTUAL claim a fresh-context auditor WILL check against the call sites. Grep
the call sites first and write the present-tense truth ("
fleetis the FIRST consumer; the ledger migration is separate work"), never the intended end-state as if it already shipped. When an audit returns "revise" on an over-claim, the fix must RE-SWEEP the whole artifact for the same claim-class before re-submitting — one over-claim found means look for its siblings. (session 453daf00, 2026-06-22: a plan asserted a new state-resolver was "shared by the existing ledgers" when only one caller used it; the agent fixed one sentence after audit-1 but the v2 plan still carried the claim, caught only by audit-2 — prediction 87b42efa MISS.)
Budgets (lint-enforced — they are quality pressure, not bureaucracy)
- Skill description <= 600 chars. It's always-loaded; every char taxes every session forever. Make it pushy about WHEN to trigger (skills under-trigger by default), concrete about contexts, and include the cost of not using it.
- Skill body <= 200 lines; overflow goes to references/ with explicit "read references/X.md when Y" pointers. CLAUDE.md <= 60 lines. Commands/agents <= 80.
Required in every artifact
provenance:line — date, session id(s), triggering event. Rules without receipts get deleted by the next /meta-retro, as they should.- Falsifiable content. "Be careful with the database" is decoration; "run migrations in a transaction; the 2026-05 incident was a half-applied migration" is a learning.
- One name per concept. Name each concept ONCE and reuse that exact token throughout the artifact (and across siblings that share it) — never alternate synonyms ("Stop gate"/"retro gate", "blackboard"/"ledger", RESUME.md vs HANDOFF.md). A reader with no memory of today cannot tell whether two names mean two things; terminology drift was the single recurring defect across the 2026-06-17 skill audit. (followup a4f372.)
Enforcement exemptions (lint / hook rules)
An exemption to a lint or hook rule MUST be a human-gated allowlist edited only
via /harness-pr (e.g. SEED_ARTIFACTS, VENDORED_SKILLS) — NEVER a field the
checked artifact can set on itself (frontmatter flag, marker line, env var). A
self-assertable exemption silently turns the rule into opt-out for the whole tree.
If an artifact must carry a human-readable marker, keep it inert and make the
linter ignore it; always surface the waiver in lint output, never skip silently.
(session 61f58113, 2026-06-13: a self-asserted vendored: true B3 waiver was
caught by the harness-auditor as a backdoor, then replaced with a path allowlist.
session c32fdd41, 2026-06-17: a self-assertable HARNESS_SESSION_TTL_SECONDS env
override in Guard B let any second session set the staleness TTL to ~0 to evict the
live owner — same class, caught again by the harness-auditor, removed for a fixed
compile-time TTL.) A tunable that moves a guard's eviction/staleness threshold is
itself enforcement-relevant — make it a constant, not env-readable.
Mentioning the enforcement marker in a commit/PR body
When a commit message or PR body must MENTION the enforcement-unlock marker token
(quoting directive 5, an ADR, or a guard's own message), never put that literal
token in the Bash command line — the enforcement guard's prose-scan reads it as a
self-grant and BLOCKS the command. Write the body to a temp file and pass it by
reference: git commit -F FILE / gh pr create --body-file FILE, which keeps the
token out of the command text. (session 6390db39, 2026-06-19: an inline heredoc
that quoted the marker was blocked; the guard's own block message gave the fix.)
Per-type notes
- Agents: define what context they get — and what they must NOT get. Any agent that evaluates your work must receive only the original request + rubric + artifact paths. Sharing your reasoning contaminates the verdict.
- Hooks: stdin JSON in, exit 0/2 out, fail OPEN on malformed input (a
broken hook must never brick the session), narrowest possible matcher, and
always propose via PR — the guard will (correctly) block direct edits.
When a hook keys on a path that ENCODES identity (e.g.
.claude/worktrees/<name>), canonicalize LEXICALLY (abspath + normcase + textual\\?\strip), NEVERos.path.realpath— resolving symlinks collapses a relocated/symlinked worktree to its target and destroys the identity the guard keys on; sweep any path-normalization change with a regression test before landing. (session c32fdd41, 2026-06-17: realpath adopted to close\\?\/8.3 aliases, reverted — it broke symlinked-worktree identity and didn't even strip\\?\; lexical was right.) Any RE-EDIT of an enforcement guard made to address an auditor BLOCK must itself be re-audited before push — the corrected-direction fix routinely opens a DIFFERENT hole. (session 86f913c0, 2026-06-17: a fd-dup relaxation>{1,2}(?!&)over-excluded>&FILE, leaving a real write into protected bin/; the round-2 auditor caught it empirically and>{1,2}(?!&[0-9-])was the verified fix.) When a NEW guard's detection (path-in-repo scoping, the mutating-verb set, the deny-decision shape) OVERLAPS an existing hardened guard, REUSE or mirror that guard's logic — never re-derive it. Re-derivation silently reproduces bugs the hardened guard already fixed AND documents: a whitespace token-split of a Bash command fails OPEN on a repo path containing a space ("GitHub Projects"), becauseroot in tokennever matches the split fragment — scan the WHOLE command for the basename/prefix instead (guard_enforcement_layer.py). If a shared util is not yet extracted, make the divergence deliberate + documented and file the refactor. (session 21078e9b, 2026-06-23: a staged anti-scratchpad guard re-rolled a token-split and reproduced the spaced-path fail-open on this very repo, and missed writers the hardened set already covers — sed -i / truncate / ln / pythonopen(...,'w'); only the harness-auditor caught it.) - User-model entries: claims about THIS user's behavior only, with evidence counts. Decay rules live in commands/gc.md.
- Multi-stage skill SUITES (a gated pipeline of skills/phases — e.g. product strategy → product README): give the suite ONE canonical funnel view — the ordered stages plus the gate between each — not only a router that says "pick the smallest entry point." A pure-router front door trains reactive, one-stage-at-a-time use; the funnel shows the whole path so a session entering mid-pipeline still sees what precedes and follows. (followup 337ac0; retro-backlog 2026-06-19, session d7de6b55.)
Editing tracked files on this Windows checkout (core.autocrlf=true)
Working tree is CRLF, committed blobs are LF. The Edit tool matches an LF
old_string against a CRLF working-tree file fine — do NOT pre-emptively
normalize CRLF→LF, since a whole-file EOL flip pollutes the diff (the opposite
of the goal). Only if an exact-match Edit UNEXPECTEDLY fails to match, fall back
to reading the committed view (git show HEAD:<path>) or normalizing. A file
showing "modified" may be a pure EOL artifact: confirm real change with
git diff --ignore-cr-at-eol before calling the tree dirty (git restore is
then content-neutral). Stage only intended files (git add -- <paths>, never
-A) so a gitignored marker/state file can't ride into an enforcement-layer commit.
(session cca5ccb9, 2026-06-14: a subagent normalized two guard files CRLF→LF
before editing; a "modified" .claude/settings.json was a pure EOL artifact.
session f36989d6, 2026-06-21: 8 exact-match Edits matched LF old_strings against
CRLF cartograph/extract.py with no normalization — pre-emptive-normalize advice
was overcautious and contradicted the no-EOL-flip rule in the same paragraph.)
The same autocrlf gotcha bites TESTS, not just edits: any test or --check that
compares a COMMITTED text file against freshly GENERATED content passes on the
authoring machine yet FALSE-FAILS on a clean checkout / CI — the committed-LF file is
checked out CRLF while a generator emits LF. Normalize EOL on BOTH sides before
comparing (s.replace(/\r\n/g,"\n")); a drift gate must catch CONTENT drift, not
line-ending churn. Prove it by feeding the comparator a CRLF copy. (session 1a5cff26,
2026-06-22: a generated-docs drift test — committed .md == generator output — would
have false-failed on every fresh clone until the comparison was EOL-normalized.)
Running scripts on this Windows checkout (cp1252 default + multiple drives)
Python here defaults to cp1252, not UTF-8. Any script that reads/writes files
containing characters outside cp1252 (arrows like →, box-drawing, many smart-quote
forms — common in harness output) MUST force UTF-8: open(p, encoding="utf-8"),
and PYTHONIOENCODING=utf-8 for stdout. A
cp1252 crash mid-write can leave the file TRUNCATED TO EMPTY (it wrote None) — so
after any encoding crash, re-verify the file's contents before trusting it; never
assume a partial write left it intact. Separately, os.path.relpath(path, start)
raises ValueError when the two are on DIFFERENT drive letters (temp on C: while the
repo is on D:) — guard it with try/except falling back to the absolute path, or
normalize to the repo drive first.
(session dc1c3470, 2026-06-19: cp1252 repeatedly crashed the cartograph extractor on
non-cp1252 glyphs like arrows and once truncated cartograph/extract.py to empty; its
new eval also caught a cross-drive relpath ValueError when --json wrote to C: from the D: repo.)
A CLI that ECHOES user-supplied data to stdout (a summary, tag, or note the user
typed) cannot guarantee ASCII output even when its OWN framing strings are pure
ASCII -- so set BOTH stdout and stderr to UTF-8 with errors=replace at the top of
main() (for s in (sys.stdout, sys.stderr): s.reconfigure(encoding="utf-8", errors="replace")),
or a stored non-cp1252 char (CJK, emoji) raises UnicodeEncodeError mid-print on a
strict cp1252 console. ASCII framing of your own text is NOT enough once you echo
arbitrary input.
(session 04fb5c5c, 2026-06-21: auto-healer's heal.py had pure-ASCII framing yet
crashed review on a CJK summary under PYTHONIOENCODING=cp1252; the harness-auditor
reproduced it, and stdout/stderr errors=replace fixed it.)
Creating or moving hooks on this Windows checkout (git drops the +x bit)
core.fileMode is effectively off here, so git does not track the executable bit: a hook
authored via Write or moved with git mv lands at mode 100644, and lint's H1 SKIPS the exec
check on Windows — local lint passes clean while Linux CI's H1 FAILS ("not executable"). After
creating/moving any hook (or an evals/corpus check.py), set it before committing:
git update-index --chmod=+x hooks/<file> (verify git ls-files -s → 100755). Same trap on a
test RELOCATION: a test_*.py moved from proposals/ (CI-excluded) into tests/ must be wired into
.github/workflows/ci.yml or test_ci_coverage.py fails — run the FULL suite locally before pushing.
(session 59d5001b, 2026-06-24: the Mission Control bundle moved 2 hooks + 3 tests; both the dropped
+x and the unwired tests passed Windows lint but cost two Linux-CI round-trips before green.)