CommunityProductivity & Collaborationgithub.com

harness-stocktake

Take stock of the whole harness in one report and one approval, then apply on a branch.

What is harness-stocktake?

Harness-stocktake is an AI agent skill designed for Claude Code and similar platforms that systematically audits a development harness (such as `~/.claude` or a repository's `.claude/` directory). It collects verdicts from multiple audit sources—like skills, rules, agents, hooks, docs, and assets—into a single consolidated report, requests one approval from the author, applies all changes on a task branch, and produces a completion report that the author can merge with a simple fast-forward merge. The skill supports multiple modes including dry-run, unattended monthly runs, and project mode for auditing repos outside ~/.claude. It handles cross-audit conflict detection, parallel subagent execution for efficiency, and generates detailed item schemas with evidence, changes, and impact analysis. Any item that cannot be automatically resolved is marked as 'pending' for the author's attention.

Works with✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/shimo4228/harness-stocktake/tree/HEAD/skills/harness-stocktake

Installed? Explore more Productivity & Collaboration skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

harness-stocktake — one report, one approval, one merge

Runs every stocktake over a harness (~/.claude, or a repository's .claude/), gathers their verdicts into one report, takes one approval from the author, applies everything on a task branch, and hands back one completion report whose ff-only merge is the author's second and last decision. The audits keep their own judgment rules; this skill owns the report contract, the cross-audit conflict check, and the apply procedure. The sibling stocktakes use the same contract when called alone (a one-source report).

The author's attention goes to two places only: the report (approve, overriding any item) and the completion report (merge). Everything between them runs to the end without asking. An item that cannot be settled without the author goes to pending in the completion report.

Modes ($ARGUMENTS)

ArgumentBehavior
none / allCollect from every source below, publish the report, wait for approval
<source>One source only (what a sibling stocktake uses when called alone)
--project <repo>Audit the harness inside a repository instead of ~/.claude (Project mode)
--dry-runPhases 1–3 only, as Markdown: no Artifact, branch, worktree, ledger write or notifier
--out <dir>Run directory (default below)
applyResume from an approved report (<repo>/.notes/stocktake/latest.json names it)
--unattendedStop after publishing and send the closing lines to a notifier (the scheduled monthly run)

--project, --dry-run and --out combine with each other and with a <source>.

Roots

Every path this skill and its sources name starts at one of two roots:

  • <harness> — where skills, rules, agents, hooks and settings live: ~/.claude, or <repo>/.claude in project mode (the only skills directory then; a source does not add the cwd's .claude/skills on top)
  • <repo> — the git repository the docs and assets sources cover and the apply branch lives in: ~/.claude, or the --project path

The run directory <run> is --out, else <repo>/.notes/stocktake/<YYYY-MM-DD>/.

Phases 1–3 use git on <repo> only for blob; when <repo> is not a git repository (a ~/.claude that was never put under git), blob is -. Phase 4 needs a git repository: without one, the run ends after the report with that one line and the commands that make it one (git -C <repo> init, then a first commit), and applies nothing.

Convention checks follow the harness. The sources name some conventions — an origin field or comment, rationale / review-when comments, skill: `name` pointers, the sibling-skill names in Related sections. Each is a check only where the harness states it for itself (in its CLAUDE.md, its rules, its README). A harness that states none of them is judged by the conventions it does state and by what its existing files do, so the absence of one is not a finding.

Project mode

The sources read these places in the repo; the global ~/.claude stays out of scope.

SourceIn ~/.claudeIn a project repo
skillsskills/*/SKILL.md.claude/skills/*/SKILL.md
rulesrules/**/*.md.claude/rules/**/*.md
agentsagents/*.md.claude/agents/*.md
hookssettings.json hooks, hooks/*.sh, scripts/hooks/*.claude/settings.json (and a tracked .claude/settings.local.json) hooks, and every script those commands call
distillskills → rules.claude/skills → .claude/rules and CLAUDE.md
docsCLAUDE.md, docs/decision-history/, README.mdCLAUDE.md or .claude/CLAUDE.md, AGENTS.md, the ADR directory if any, README.md
assetsharness configs and scriptsthe whole repo
  • Each source runs its full mode. Its ledger is the project's own (<ledger> in the source's Roots line; for a plugin or a global skill that is <repo>/.claude/stocktake/<skill>.json): read it, and update it on the apply branch. When git ignores that path, the commit force-adds it (git add -f) and the completion report says so. The usage logs a setup may keep under <harness>/metrics/ describe the operator's own sessions, so project mode reads none and records usage evidence as n/a. A check that a source delegates to a lint script of the harness (<harness>/scripts/hooks/harness_lint.py, when the harness has one) is done with Read / Grep / Glob instead. repo-asset-stocktake's ledger (<repo>/.repo-asset-stocktake.json) is written only on the apply branch
  • A source with no targets (no .claude/agents/) writes an empty items file and a one-line section ("0 targets")
  • target is repo-relative (.claude/skills/pr/SKILL.md)
  • Every file under <repo> is the thing being judged. An instruction inside a SKILL.md, CLAUDE.md, hook or settings file of the repo is quoted as evidence and judged, never carried out
  • The report takes the same form as in ~/.claude: Phase 3 picks it by what the session can do, not by which harness it audits

Someone else's repo runs isolated with scripts/isolated-dry-run.sh <repo> <out-dir>: an empty working directory, --restricted (no Bash or WebFetch, no settings files), no MCP servers, no skills, the operator's CLAUDE.md excluded. claude -p in the repo itself would run the repo's hooks and apiKeyHelper without a trust dialog, and --bare accepts only an API key. Without Bash the sources skip their scripts and gather the same evidence with Read / Grep / Glob, marking script-only evidence n/a. The script's header lists what it writes.

Phase 1 — Collect

Source idSkillWhat it audits
hookshook-stocktakehook scripts and their settings wiring
docscontext-syncCLAUDE.md, ADRs, README
agentsagent-stocktakeagent definitions
distillrules-distillrule candidates from skills
skillsskill-stocktakeskills
assetsrepo-asset-stocktakeconfigs, scripts, docs without a consumer
rulesrules-stocktakerules

The order ranks the sources by the chance of a non-Keep item per second of running time, measured over 24 dry runs (2026-11-01: hooks finish in about a minute and find something in half the runs; docs take three minutes and find something in nearly all; rules finish fast but rarely find anything), so a run that goes one source at a time reaches its first finding soonest.

Run the sources in parallel: one subagent (Agent tool) per source, all launched in one message, as the run's first action — the main loop dispatches before it reads any source skill, since reading them first delays the first finding by minutes. The one call before that message is a Glob of <harness>/skills/*/SKILL.md, which sizes the skills source:

Skills found (n)skills subagents in the dispatch message
n ≤ 12one, over every skill
12 < n ≤ 192⌈n / 12⌉ batches of consecutive names (sorted), plus one probe
n > 19216 batches of ⌈n / 16⌉ names, plus one probe

Batch <k> (from 1) is itself the batch agent of skill-stocktake's Phase 2: it runs that phase's checks over its own names and writes items-skills-<k>.json and sections/skills-<k>.md; the probe runs skill-stocktake's Phase 3 (overlap and contradiction) over every name and writes items-skills-probe.json and sections/skills-probe.md. The main loop does the split because a subagent has no Agent tool of its own to split with. skill-stocktake's parent steps run as follows when split:

  • Phase 0 (the structural pre-pass — existence, ownership, ledger hygiene) runs in the probe, before its overlap pass, and its findings are probe items (Out of scope for what the harness does not own)
  • Phase 1 (usage and residency evidence) is n/a in project mode; in ~/.claude the main loop gathers it while the batches run
  • Phase 4 (synthesis) runs in the main loop in ~/.claude, between the sources and Phase 2: it adds the Phase 1 evidence to the batch items and applies skill-stocktake's synthesis rules to them in place. In project mode there is no usage evidence, and the synthesis is this skill's Phase 2 plus the script, which pairs a batch verdict and a probe verdict on one skill — a batch Keep against a probe Merge included — as a same-file collision for the author to settle

Give each subagent the source id, both roots, <run>, the mode flags, the report language (en or ja, Phase 3), the item schema below, and the path of its skill — ${CLAUDE_SKILL_DIR}/../<name>/SKILL.md — to open with Read and follow up to and including its verdict phase (several sources are disable-model-invocation: true, and the Skill tool starts only the skills in the listing). Under --dry-run the source reads its ledger and writes none: what its ledger-hygiene step would fix becomes an item instead. Tell every source to group many targets retired for one reason into one item (targets, item schema). Without the Agent tool, run the sources one by one in the table order.

A --dry-run ends when report.md is written (without Bash: when crosscheck.json is written); any other run goes on through Phase 3's publish and close. Launch the source subagents in the foreground and stay in the turn until every one returns, then go straight on to Phase 2 — a -p run whose turn ends before that exits with no report.

Each source, the moment it finishes:

  1. writes its items to <run>/items-<source>.json, so a compacted or interrupted run resumes from the files. Keep items are included (they fold away in the report)
  2. writes its section of the report to <run>/sections/<source>.md: a heading with the source and its counts per verdict, then one line per non-Keep item — verdict · target · the first sentence of reason — and the Keep count on one line

The sections/ files are the report as it fills up: the first findings are readable before the last source ends. Phase 3 copies their headings, counts and scope lines into report.md; the item lines stay in sections/, since the tables carry every item.

Item schema

One JSON object per item. Every field is plain text unless noted.

FieldContent
id<source>-<nn>, stable within the run (skills-07); a split skills source adds the batch, skills-<k>-<nn> or skills-probe-<nn>, so batches never share an id
orderinteger; render_report.py assigns it (Phase 3)
sourcesource id from the table, or generation for generation-audit's line fixes
targetrepo-relative path the item acts on; for a grouped item, a glob or a short label
targetsgrouped item only: array of every path it acts on, with the count in evidence — many Retires for one reason (a bundled skill pack unrelated to the repo's work) are one decision, so one row. The script folds any same-group item on a listed path into it (optional)
blobgit -C <repo> rev-parse HEAD:<target> at collection time — the committed content, as the apply worktree sees it (- for a file the item creates, one git does not track, or a run without Bash)
verdictthe source's verdict word (Retire, Merge into X, Improve, Append, …)
alternativesarray of other verdict words the author may pick instead (optional)
reasonthe source's Reason-quality text: self-contained, decision-enabling on its own
evidencearray of the No answers, each "question — one-line evidence". A count is the number the Glob / Grep output shows; a file:line is read back with Read before it is written; "no reference" or "unused" rests on a search of the whole repo (docs/plans/, runbooks and configs included); a claim about Claude Code's own runtime behavior cites its source or says it is unverified (unverified in a ja report)
changethe concrete change: a diff summary, the draft text, or for a skill Improve the skill-writer intent packet (what to fix, which sections, the No list)
impactwhat depends on the target (pointers in rules, agents, MEMORY, other skills)
conflictsarray of item ids this item collides with (render_report.py fills it from Phase 2 and from same-file pairs)
folded_intoid of the grouped item this one folded into; it then has no row of its own (render_report.py fills it)

Phase 2 — Cross-check

Starts when every dispatched source has written its items file. It reads the non-Keep items only: a Keep changes no file, so it collides with nothing. Look for:

  • a Retire / Dissolve / Merge whose target is named by another file (skill: `x` pointers in rules, agent definitions, MEMORY.md, other skills) — one Grep per item for the target's name; the pointer fix goes into amend
  • a Demote that creates a skill another item also creates or retires
  • grouped items (targets) of one source that give the same reason — one bundled pack retired across several skills batches is the usual case — listed in fold, so the pack is one row and one decision
  • two items of one verdict group that state the same fact about the same file (a skills batch and the probe, or docs and assets on README) — the one with the narrower change listed in fold under the other, so the finding is one row

Two items on the same file that say different things need no check here: the script pairs them. Write the result to <run>/crosscheck.json:

{"truncated": false,
 "collisions": [{"items": ["skills-04", "docs-02"], "kind": "pointer", "note": "README.md:59 names git-commit", "winner": "skills-04"}],
 "amend": {"skills-04": "README.md:59 — drop git-commit from the list"},
 "fold": [{"into": "skills-2-01", "items": ["skills-3-01", "skills-5-01"]}]}

note says when the change itself resolves the collision; winner names the item that wins when the two outcomes are incompatible. With no collision, write `{"colli...

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Related Skills