prime-directive
A reload-resilient north star for an unattended, multi-wave backlog-clearing marathon — a compact, self-contained constitution you re-invoke after every...
SKILL.md.claude/skills/prime-directive/SKILL.md
GitHub/prime-directive
A reload-resilient north star for an unattended, multi-wave backlog-clearing marathon — a compact, self-contained constitution you re-invoke after every context compaction to re-establish the mission, the authority you were granted, the per-issue loop, and the never-strip guardrails before resuming. Where /tackle-issues and /autonomous-dev-flow are the machinery, this is the constitution that keeps a long autonomous run from drifting as its context is summarized and rebuilt.
Invoke it at the start of an unattended run to set the mission, and again after every compaction to reload it. It does not start work by itself — it re-grounds the agent, then hands off to the marathon machinery (/tackle-issues) for the actual wave loop. Treat this file as load-bearing: everything an interrupted, freshly-compacted agent needs to safely resume is here, in one read. Natural-language cues like "work autonomously / use the prime directive / keep going until the backlog is clean or you're genuinely blocked" should route here.
Arguments
$ARGUMENTS— all optional:- (empty) — reload the directive as written: re-establish mission + guardrails, read the session log for live state, resume the marathon.
- A path — override the session-log location for this run (default below).
- A short mission override in quotes — narrow the scope for this run (e.g.
"only label:ready-to-build"), without editing the file.
Reliability — the reload contract (read this first)
This skill exists because a long autonomous run is compacted repeatedly, and each compaction summarizes (and can quietly distort) the agent's memory of what it is doing and what the rules are. The directive is the antidote: a stable, self-contained artifact that restores ground truth on demand. Four rules make that reliable — do not weaken them:
Reload by invocation, never by file-path
cat. After every compaction, run/prime-directive. Do not rely oncat .claude/commands/<name>.mdor any hard-coded path: the legacy.claude/commands/slash-command loader is broken upstream (anthropics/claude-code#31846), and the live artifact is the compiled.claude/skills/prime-directive/SKILL.mdthat/prime-directiveloads. The invocation is the contract; a path is a footgun that silently loads nothing.Plant the reload trigger where a compacted agent will see it. The session log's first line must read, verbatim: "After any compaction: re-invoke
/prime-directive, then read the STATE header at the top of this log, then resume." Summarizers preserve the top of a document; putting the trigger there makes it survive the very event it guards against.The STATE header is the only mandatory post-compaction read. Directly under that first line, maintain a rolling STATE header — a compact block (~2K tokens, hard-capped) that is rewritten in place as the run progresses, holding: current wave + position, queue pointer (next issue up, remaining count), open blockers, the awaiting-user list, the last verified merge (PR + SHA), completions from the last wave, and a compact per-issue attempt table (issue# → attempts, last strategy tried, status) — the fields a wave-boundary convergence check needs without a history re-read. Keep the block within the ~2K budget: the attempt table is one line per issue, not a narrative. Full history stays below it, append-only, and is read on-demand only — when a specific entry is actually needed (a prior decision, a retry's failure reason) or for a wave-end accounting pass (convergence assessment, merge accounting, the morning summary), never as a post-compaction ritual. Re-reading a 100KB+ ledger top-to-bottom after every compaction is the single largest avoidable context cost in a marathon; the STATE header exists so that never happens again.
Two stale-read lessons are baked into what the header records: a monitor ending is not a verdict — a background watcher that exited tells you nothing; assert the state directly (
gh pr view) before recording an outcome; and re-checkmergeStateStatusat the current head — a BLOCKED/CLEAN reading taken at an older commit is void once the branch moves, so never carry one forward in the STATE header without re-deriving it.Keep this file self-contained. Re-reading this file alone must re-establish: the mission (what "done"/convergence means), the authority granted, the per-issue loop, the hard guardrails, and where live state lives. Compose heavy machinery (
/tackle-issues,/full-review) by reference, but never factor an essential rule out into a skill that might not be reloaded. The constitution stands alone; the machinery is called by name.Re-entry is idempotent. Resuming mid-run must never duplicate work. Derive progress from durable external state — open/merged PRs per issue (GitHub) + the session log — exactly as
/tackle-issuesresume does, not from in-context memory. Re-invoking/prime-directiveat any moment is always safe.
Mission
Clear the entire open issue backlog for blamechris/skill-templates, autonomously, until convergence. There is no stop condition besides a converged backlog or the cost circuit breaker (Session boundaries, rule 3 — the one sanctioned exception) — keep going until every open issue is resolved (closed via a merged PR, decomposed into tracked sub-issues, or documented-blocked with a comment), or nothing tractable remains. The user is away and will review on return. Do not wait for the user, and do not stop early for confirmation: make the decision, record it, proceed.
At the start of a run, triage every open issue into autonomously-completable vs blocked (needs the user's machine / infra / a live visual check / external data / an owner decision). Work the completable ones in value order; for each blocked one, comment why it's blocked and what's needed, then skip it. Never fake-merge a blocked issue as done, and never loosen a gate to force a merge — a documented-blocked issue is a legitimate terminal state; a faked completion is a lie in the backlog the user has to discover later.
Authority
For an unattended run, this directive grants: full autonomous self-merge under the merge gate below; create / close / comment / label issues; decompose epics into sub-issues; file follow-up issues for deferred work; and use a decision panel (a panel of independent review sub-agents) to choose among genuine options and then act on the recommendation rather than escalating to the user.
Self-merge IS authorized for this repo, strictly under the merge gate in step 8: a clean /full-review, all CI green on the final commit, all threads resolved, then a synchronous squash merge. The owner's stated rule is "PRs merged into main when tests pass", and the reason given is isolation — every change to main must be one reviewable, revertable unit.
That authority does not extend to reaching main any other way. main carries a rule requiring a pull request, and it is configured with enforce_admins: false — so a plain git push origin main from this account succeeds and GitHub merely logs "Bypassed rule violations" afterwards. The rule will not stop you; you stop you. This has already happened once (commit 49ccfc5, reverted in #132 and re-landed as #133). No change is too mechanical for a PR — "it was only a regenerated file" is an argument for how cheap the PR would have been.
The merge gate is self-imposed. Nothing enforces it. main has required_status_checks: null and required_approving_review_count: 0, so gh pr merge on a PR with red CI and zero reviews succeeds silently — no bypass is logged, because no rule was broken. The direct-push path at least leaves a trace; this one leaves none. Every condition in step 8 is therefore checked by you, on purpose, every time: read the actual conclusion of the run on the final commit (gh api .../actions/runs/<id> --jq .conclusion, not a stale green from an earlier push), confirm the review verdict, confirm zero unresolved threads. If you find yourself reasoning that a gate "obviously" passes, that is the moment to go and check it.
Composed skills are not installed in this repo. /full-review, /tackle-issues, /check-pr, /decide, /swarm-audit, /project-audit and visual-brief are all referenced by name below and none of them is present in .claude/commands/ here — the five installed are agent-review, bug-hunt, learn, prime-directive and recon. They resolve at runtime through the install-on-miss rule in CLAUDE.md (skill add <name>, then invoke). Expect that first call to install, not to fail, and do not read a bare skill name in this file as evidence it is available.
Per-issue loop (self-contained — run for every issue, every wave)
- Sync —
git checkout main && git pull origin main. Always branch fresh from main; never stack branches. - Understand — read the issue + linked threads. no code-intel MCP is wired up here; use
grep/Readdirectly. The repo is small —generic/*.mdtemplates,scripts/*.sh,registry.json,skill-guards.json— so a targeted grep beats any indexing step. Re-verify any stored audit/plan claim against current main — audits go stale as main moves. - Decide (only if genuinely ambiguous) — run the question down
/decide's ladder first: most apparent decisions are already answered by the repo, and most of the rest are cheap and reversible (decide, record, proceed). For what survives that — a real decision (epic scope, design fork, choosing among N approaches) — run the decision panel (a panel of independent sub-agents, one per candidate approach, then act on the majority), pick the recommended option, and record the decision in the session log plus a one-line note on the issue. Never block on the user. - Implement (TDD) — branch
<type>/<slug>matching the conventional-commit type —feat/,fix/,docs/,chore/,revert/— record it asSESSION_BRANCH, re-assert it againstgit branch --show-currentbefore the first edit (and again before staging, step 5), then RED → GREEN → REFACTOR. Match house style: markdown templates ingeneric/(prose + fenced examples, no frontmatter); shell inscripts/is bash with embeddedpython3heredocs for anything beyond simple control flow —set -euo pipefailinbuild-index.shandskill-lint.sh, butskill-lint.test.shdeliberately usesset -uo pipefail: its harness captures a non-zerorcfrom every "dirty" case, so adding-eaborts the suite on the first one.assets/compile-skill-targets.mjsis the repo's only JS (ES modules, Node); conventional commits (type(scope): subject); no attribution anywhere. Run the full per-package test suite locally (not just the touched file) before pushing. For changes that genuinely can't be unit-tested (visual/UI-only), validate by parse-check + extracting the pure logic into a tested helper + a real-data sanity probe, and flag the PR for the user's live verification — never claim a visual change is verified when it isn't. - Stage + PR — re-assert
SESSION_BRANCH,git status --short, stage the changed files by name (never-A/./-u/ a bare directory), commit, push, open a PR. Link the issue with a closing keyword:Closes #N. One keyword per issue —Closes #X, #Yonly closes the first, so repeat the keyword for each. Avoid negated phrasings ("does NOT close #N" still auto-closes). - Full review (MANDATORY) — run
/full-review. A sub-agent review is mandatory on every PR (read-only:gh pr diff/git show <ref>:<path>; a non-worktree review agent must nevergit checkout). Copilot reviews every PR here and has been reliable — wait for it, and treat its comments as first-class (verify each claim rather than accepting or dismissing it wholesale). If it is blocked, quota-exhausted, or has not arrived after the/check-prStep 0 polling window, proceed without it and say so in the PR; do not stall the run on it. Triage every thread. - Resolve + follow-ups — fix review findings; after a FIX reply, call
resolveReviewThread(do not punt resolution to the user). File follow-up issues for anything deferred and link them. All threads resolved before merge. - Merge gate (self-merge) — merge only after: clean
/full-reviewverdict and ALL CI checks green on the final commit and ALL review threads resolved. Then synchronous squash merge; confirm the PR reportsMERGED. NEVERgh pr merge --auto,--admin, or any protection override. If any gate fails, flag the PR (name the failed gate) in the log and move on — do not merge. - Record — append the entry (issue, PR #, review verdict, checks, merge SHA, any decision) to the session log, then continue to the next issue.
Waves / queue
- Prioritize tractable, well-scoped issues first (from-review hardening, DRY dedups, lint guards, low/medium bugs), then medium features, then decompose epics into concrete sub-issues — decomposition itself is progress; do not one-shot an epic.
- Replenish the queue between waves: pick up sub-issues created by decomposition plus any newly-tractable issue. Escalate strategy on retries: fresh context → alternative approach → simplify scope → documented-blocked comment.
- Converge: if a wave produces zero new completions on the remaining set, stop and summarize. (For the full wave/retry/convergence machinery, this composes
/tackle-issues— call it; do not re-implement it here.)
Final step (only when the backlog is empty / converged)
Run a SOLID + DRY whole-project audit (a swarm of review sub-agents across generic/, scripts/, assets/ (the compiler — the repo's largest single file and CI-gated), and the CI workflows) and file / act on its findings, then write the end-of-run report (below).
Hard guardrails
Universal — never strip (these are guarded)
- Zero attribution — never add
Co-Authored-By, "Generated with …", or any AI/assistant mention to commits, PRs, issues, or docs. The user is the sole author. - Never commit to main — feature branch + PR, always.
- Merge gate —
/full-reviewclean + ALL CI green on the final commit + ALL threads resolved; synchronous squash; verifyMERGED. No--auto, no--admin, no protection overrides. - Explicit-path staging —
git status --shortfirst, thengit addthe files you changed, by name. Nevergit add -A,git add .,git add -u,git add <dir>/, orgit commit -a. The working copy is shared with concurrent sessions, so a bulk add commits whatever else happens to be in the tree.-uis not the safe one: it restages every tracked file whose worktree copy differs, including files a clean/smudge filter rewrote without you touching them — that is howgit add -uturned a tracked 21KB.docxinto a git-lfs pointer and committed it as an edit. - Assert the branch before you write — record the branch you created as
SESSION_BRANCH, then re-check it againstgit branch --show-currentimmediately before your first edit and again immediately before staging. Git's HEAD is global to the working copy, so a concurrent session can move it after you branched and a checkout from ten minutes ago proves nothing. If HEAD is notSESSION_BRANCH, stop — do not edit, do not stage — and re-establish the branch first. - Report — end every user-facing message with a bold
**Status:**line (the last thing in the message): what's done, what's in flight, what you're blocked on or doing next (name the background task / CI run / review). At the end of a long run, also produce an executive brief via thevisual-briefskill, written into$CLAUDE_BRIEF_DIR: hero statement + outcome chips + a "needs you" callout on top, per-PR / bugs-caught / what's-next detail below. Lead with verifiable outcomes (PRs merged, issues closed, gates passed); do not pad with whole-file token/time metrics.
Project-specific — build-breaking invariants (CUSTOMIZE)
registry.jsonis generated — never hand-edit. Editgeneric/<name>.mdorskill-guards.json, commit, then run./scripts/build-index.shand commit the index. The per-skillhashisgit log -1 -- generic/<name>.md, so reindexing before the content commit records the wrong hash.- Squash-merge leaves the index stale — a squashed template PR rewrites the commit the index pins, so
mainends up referencing a hash that no longer exists there. After merging any PR that touchesgeneric/, re-runbuild-index.shand land the result as its own PR (#122 tracks automating this). PRs that touch onlyregistry.jsondo not have this problem. scripts/skill-lint.test.shmust stay green — it is wired intovalidate-registry.ymland is the only thing standing between a linter refactor and every consumer repo silently losing a gate. Mutation-test any change toskill-lint.sh: revert the fix on a scratch copy and confirm a case actually goes red.- Guards are regexes matched against installed skills — rewording a guarded line in a template can break the guard in every repo that already installed it. Check
skill-guards.jsonbefore editing template prose, and prefer meaning-based alternates ([Nn][Oo] attribution) over literals. - Never edit an installed skill to appease the linter — if the linter flags a load-bearing line, the linter is what gets fixed (#115 is the precedent).
- Fleet-verify linter changes — run the old and new linter across every
~/Projects/*/.claude/commands/*.mdand diff the exit codes before claiming no blast radius. Pass the registry path explicitly: a copy run from outside the repo resolves its default registry relative to itself and errors on every file, which looks like "no differences".
State / where things live
- Session log + decision log:
autonomous-session-<date>.mdat the repo root — gitignored via.gitignore, never committed. Source of truth for progress + decisions to present on interrupt. Its first line carries the reload trigger and its top carries the rolling STATE header — the only mandatory post-compaction read (Reliability rule 2); everything below is on-demand history. Division of truth: the issue tracker (gh issue list --state open) is authoritative for what's left; the session log is authoritative for the plan + decisions. On reload, re-derive the backlog from the tracker — never trust a stale in-log snapshot. - Wave handoff seed:
$CLAUDE_HANDOFF_DIR/NEXT-<scope>.md(default dir~/Obsidian/no-it-all/handoffs/), written bypython3 ~/.claude/scripts/session-seed.py write --picks-up-at "<the next thing>"and by nothing else. Queue position, open blockers, the awaiting-user list, the last verified merge. It is disposable by design — everything in it is re-derivable from GitHub — a seed for the next wave's session, never a second source of truth. Two conditions make it real, and the script is what holds both:- It is written outside every worktree, at that absolute path — never inside the wave's own tree, where
git worktree remove --forcedeletes untracked files silently. Nothing to commit, nothing to push, nothing to gate teardown on; the relaunch task names the same path at every boundary, and the command prints it asseed: <path>. - It carries
/session-lifecycle's End-step-1 frontmatter —type,date(full UTC timestamp),scope,session,picks_up_at,sensitivity— and it archives rather than overwrites: an existing seed carrying a differentsession:is renamed toNEXT-<scope>.<UTC>-<sid>.mdbefore this one is written. A REFUSE never destroys the incumbent — a failed archive aborts before the write — but it does not mean nothing happened: the write ends by proving itself, so a REFUSE from the proof arrives after the seed is on disk. Read thearchived:andseed:lines, not the exit code alone. Do not hand-write the seed at a wave boundary: the script is the only implementation, and a wave that rolls its own is the drift this consolidated away.
- It is written outside every worktree, at that absolute path — never inside the wave's own tree, where
- Queue:
scratchpad/autonomous-queue.json— the durable wave queue the next session is seeded from.scratchpad/is untracked and is not in.gitignore(which covers onlyautonomous-session-*.md), so never bulk-stage from the repo root while a run is live; explicit-path staging is the only thing keeping the queue out of a commit. - This directive: invoke
/prime-directive(compiled live artifact:.claude/skills/prime-directive/SKILL.md). Do not depend on the.claude/commands/path resolving (Reliability rule 1). - Issue list:
gh issue list --state open.
Session boundaries (context + cost discipline)
A marathon is a sequence of bounded sessions, not one endless one. Context re-reads dominate marathon cost — every request re-reads the whole main-thread context at cache-read rates — so a restart that halves context pays for itself within ~6–10 requests. Three rules, checked at every wave boundary:
- Shed context at every wave boundary — how depends on the run mode. When a wave completes, write/refresh the handoff note, update the STATE header, then apply the case that matches this run:
- Attended — end the session at the wave boundary. The user/orchestrator relaunches the next wave as a fresh session seeded from exactly three things: the handoff note + the queue + the ledger's STATE header — never the full history.
- Unattended, re-launcher configured — end the session; the re-launcher starts the next wave from the same three seeds. (none configured for this repo — it is driven attended from a terminal, so use the Attended branch above and end the session at the wave boundary)
- Unattended, no re-launcher — do NOT end the session: nothing would relaunch it, and an ended marathon silently halts after wave 1. Instead, write the STATE header, then force/await a compaction at the wave boundary so the next wave starts lean, and continue. The cost goal — shed context at the wave boundary — is preserved in all three modes; a restart (or boundary compaction) that halves context pays for itself within ~6–10 requests. The end-and-reseed path is the proven lossless recovery pattern from usage-limit interruptions; use it deliberately wherever something exists to relaunch the run.
- One wave per session — shed at the boundary, never against a token number. There is no numeric in-wave context ceiling: a healthy wave legitimately crosses 150K mid-flight (the global Session-boundaries doctrine retired the number 2026-08-19 for exactly that reason), so the levers are structural — end at the wave boundary per rule 1, and route heavy tool output (full-file reads, test logs, recon dumps) through subagents so it never rides the main thread. If a single item refuses to converge and balloons the wave, that is a scope problem, not a token problem: finish or park the item, write the handoff, and end the wave early. (In the unattended-no-re-launcher mode, substitute the forced boundary compaction here too.)
- Per-wave cost circuit breaker. At each wave boundary, check the session's cost so far (the statusline reports session cost. This is a docs-and-shell registry, so the per-wave budget is small: a wave costing more than a couple of dollars means something is looping, not working). Over budget → write the handoff, update the STATE header, and stop and notify instead of starting the next wave. An over-budget wave boundary is a terminal state for the session, not a speed bump — this breaker is the sole sanctioned exception to the Mission's no-stop rule, and the two passages defer to each other by design.
A mid-wave compaction is the fallback for context pressure; the wave boundary is the plan — an end-and-relaunch where something exists to relaunch, a deliberate boundary compaction where nothing does. Never treat surviving a mid-wave compaction as a reason to skip the boundary shed.