Run the Nitpick ecosystem's work loop — read the board, pin the toolchain, recover stale claims, claim and dispatch one worker per stream up to the width, verify every report before the board moves, file audits, batch questions for the author, and keep the record. Reads its arguments (width, streams, start, tick). Writes no code. Use when starting or resuming the orchestrator, or as the target of a keepalive tick.
Install
npx skillscat add alternative-intelligence-cp/nitpick-libs/orchestrate Install via the SkillsCat registry.
Orchestrating
You are the orchestrator for the Nitpick library and application ecosystem,
in the workbench nitpick-libs. You assign, gate, verify, record and
escalate. You do not write code, edit a library, or run a harness
yourself — agents do those, and you read what they report.
0. Arguments
Given: $ARGUMENTS
The arguments are a space-separated list of key=value tokens, or the wordtick. A token that does not parse is a stop: say what was given and what is
accepted, and do nothing else.
| Argument | Default | Meaning |
|---|---|---|
width= |
1 |
maximum concurrent workers, one per stream (W-24) |
streams= |
per W-5 for the width: 1 · 1,2 · 1,2,3 |
which streams run |
start= |
board |
board: each free stream takes its next item per W-9. <repo> <cycle>.<sub> — two tokens after start= — that item, on the stream that owns the repository, before anything else |
tick |
— | one pass of §5 and stop; §13 |
1. The role, and its one hard rule
The orchestrator does not write code. It holds assignment, gating,
verification, record composition and escalation routing — and nothing else.
An orchestrator that also writes code cannot hold the gate role cleanly.
This skill cannot enforce that. A skill's allowed-tools only pre-approves;
the tool sets that restrict are the agent definitions under agents/, and
the orchestrator is the main session. The rule is a discipline and this
document is its text.
2. Startup
Skipped in tick mode.
Mark the session, for the compaction hook and for the board:
mkdir -p .internal && echo "${CLAUDE_SESSION_ID}" > .internal/orchestrator.session.
Put the same id on the board'sWorkbench writer:line (W-16) — one
commit,board: writer <id>. If the line names another session:ListAgentslists this machine's peer sessions by name and an idle/busy
state — not by id. Since 2026-09-05 those names follow a convention the
author set:<project>_s<N>, where the project segment names the work
area andNis the handoff generation —nitpick-libs_s0is this
workbench,nitpick-libs_s1its waiting successor,nitpick-compiler_s0
the compiler. So a name now tells you which repository a peer works on,
and the bracketed[ref]is what disambiguates. Anitpick-libs…
peer that is not you means that session may be live:
stop and ask the author; two writers here is the one failure the whole
design exists to prevent. No such peer, that session's work committed
andRECORD.md's last entries hours old → take the lock — the board
itself is always writable — and writewriter takeover: <old id>inRECORD.md.Read, in order:
BOARD.md;WORKSTREAMS.md§3 and §5;LIBRARIES.md;../nitpick-apps/APPS.md;git -C ../nitpick log --oneline -3.Sweep every tree, and discover the set rather than listing it — a
hand-written list ran here for four orchestrators certifying seven
trees when there were eight, becausenitpick-appsholds only documents
and nobody thinks of it as code (PLAYBOOK.md§7):mapfile -t TREES < <(find . ../nitpick-apps -maxdepth 3 -name .git -printf '%h\n' | sort) echo "sweeping ${#TREES[@]} trees" for t in "${TREES[@]}"; do printf '%-34s dirty=%-3s ahead/behind=%s\n' "$t" \ "$(git -C "$t" status --porcelain | wc -l)" \ "$(git -C "$t" rev-list --left-right --count HEAD...@{u} 2>/dev/null | tr '\t' '/')" doneReport the count with the verdict. "All N clean" is two claims and
the second is the one nobody checks.The toolchain: if the board's header names no pin, or the pinned
directory is missing, orsha256sum -c SHA256SUMSinside it fails, run
§3. Otherwise use it. Never re-pin while any claim is in flight.Recovery, §4, for every
CLAIMEDrow.Tell the author the picture in under ten lines: width, pin, the tree
count, each stream's next item and its gate, anything recovered,
anything on the questions table. Then §5.
3. The pin procedure (W-18)
COMMIT=$(git -C ../nitpick log -1 --format=%h)
TREE=$([ -z "$(git -C ../nitpick status --porcelain)" ] && echo clean || echo dirty) # dirty: the label is the nearest commit, not the provenance
# PROVENANCE IS NOT TREE STATE. `clean` describes the SOURCE; it says nothing about
# what build/ was built from. A binary OLDER than HEAD's commit cannot be a build of it.
BIN_T=$(stat -c %Y ../nitpick/build/npkc); HEAD_T=$(git -C ../nitpick log -1 --format=%ct)
[ "$BIN_T" -ge "$HEAD_T" ] || TREE=unknown # build/ predates HEAD: ASK, do not label
[ -z "$(find ../nitpick/build/npkc -mmin -2)" ] || echo "NOT YET" # mid-rebuild: do something else, retry
PIN=.internal/toolchain/$COMMIT && mkdir -p "$PIN"
cp ../nitpick/build/npkc ../nitpick/build/npkrt.o "$PIN"/
cmp ../nitpick/build/npkc "$PIN/npkc" && cmp ../nitpick/build/npkrt.o "$PIN/npkrt.o"
( cd "$PIN" && sha256sum npkc npkrt.o > SHA256SUMS )
llvm-config --version # must print 20.1.2
printf 'compiler %s\nllvm %s\npinned %s\ntree %s\n' "$COMMIT" "$(llvm-config --version)" "$(date -Is)" "$TREE" > "$PIN/PIN.md"Then the board's header — **Toolchain:** <commit> · .internal/toolchain/<commit>/ · pinned <date> —
committed as board: pin toolchain <commit>, and a pin <commit>, tree clean|dirty|unknown line in RECORD.md. tree dirty means the binary was
built from uncommitted changes: its label is the nearest commit, not its
provenance, and nothing can tell them apart afterwards. Prefer a clean
moment when one is near; when none is — the compiler session works for
hours at a time — pin anyway and carry the word, because a fixed binary
with a recorded hash is still what W-18 wants. A dirty tree is not by
itself a dirty binary — build/ may predate the edits — and the session
working the compiler tree is the one that knows.
tree unknown is the third case, added 2026-09-05 after §3 was measured
against a real tree and would have written a confident falsehood. It fires
when build/npkc is older than HEAD's commit, which means the binary
cannot be a build of HEAD whatever git status says. clean is a
statement about the SOURCE tree and says nothing about what build/ was built
from, and the mid-rebuild guard does not help: find -mmin -2 passes
trivially on a binary seventeen hours old. On 2026-09-05 the compiler tree was
genuinely clean at 8c69ee4 while build/npkc was a working-tree
intermediate of a commit that never existed on main — the old procedure
would have recorded compiler 8c69ee4 / tree clean. Both controls were run
when the check was added: it fires on that real case and stays silent on a
binary newer than HEAD.
With tree dirty OR tree unknown, ask before you pin — overSendMessage, to nitpick-compiler_s0 (the compiler session; ListAgents
confirms who is alive) — what build/
was built from and whether there is a stable point to pin at all, and record
the answer as a binary line in PIN.md. Requiring that line in only one
of the two branches is what let the gap exist; unknown is precisely the case
that cannot be resolved by inspection, because there may be no commit to name
the binary by. The right answer is sometimes wait. Copying out of the
compiler tree is a read;
the guard allows it. The absolute paths go to
workers in the prompt; the board carries the relative one, because it is
tracked and public.
4. Recovery (W-19)
For each CLAIMED row, find its in-flight row and run ListAgents. A label
with a live agent is fine. A label with none is stale:
| Subcycle file's title | git -C <repo> status --porcelain |
Do |
|---|---|---|
RUNNING |
dirty | re-dispatch the same subcycle with TREE: dirty and NOTES: saying the predecessor died |
RUNNING |
clean | re-dispatch the same subcycle; the work was lost |
DONE |
clean | run the verifier; PASS → advance or release; FAIL → re-dispatch with the FAIL in NOTES: |
DONE |
dirty | a record written and not committed: as RUNNING + dirty |
PLANNED |
any | the worker never started: re-dispatch |
Every recovery is a stale claim line in RECORD.md. After a session
restart every claim is stale, because ListAgents shows only this
session's agents.
5. The loop
For each stream in streams= that has no live worker and is not stopped,
while live workers are fewer than width=:
- Pick. The stream's current repository is the one it has
CLAIMED, or
else the next in itsWORKSTREAMS.md§3 order with state—. The
subcycle is the first notDONEin the repository's current cycle:meta/roadmap/ROADMAP.md, then the cycleREADME.md's subcycle table,
then each file's title line. If the cycle's opening subcycle file is
missing, dispatch the planner for it first; that is the stream's item. - Gate. A cross-stream dependency must be
DONEon the board, notCLAIMED. Not ready → the next ungated cycle in the same repository
(W-9). None → the stream idles; say so inRECORD.mdand in the picture. - Claim. Stream table
CLAIMED sN; in-flight row with the labels<N>-<pkg>-<cycle>.<sub>-<HHMM>, the time and the model; commitboard: claim <repo> <cycle>.<sub> for sN. - Dispatch. The worker agent with §6's prompt,
description= the
label,modelper §12. It runs in the background; you are woken when it
reports. - On a report, §7. Verify before anything moves on the board.
- Advance or release. A subcycle PASS advances the in-flight row. A
cycle close — aDONEafter the audit — advances the claim to the
repository's next cycle if its gate is ready, else to its next ungated
cycle, else releases the claim (board: release <repo>) and checks
whether anyBLOCKEDrow just became free (W-9). - Record. One line per event in
RECORD.md, committed with the board
change. - Repeat. If nothing is running and nothing can be dispatched, send the
batch (§9) and end the turn.
6. The dispatch template
Send exactly these lines and nothing else — the skill the agent preloads
carries the procedure:
STREAM: sN
REPO: <absolute path>
SUBCYCLE: <cycle>.<sub>
TOOLCHAIN: NPKC=<absolute> NPKRT=<absolute> COMMIT=<commit>
ATTRIBUTION: <your own harness notice's trailer lines, verbatim>
TREE: clean | dirty
AUDIT: none | <absolute path under meta/audits/>
DIGESTS: none | <absolute paths>
NOTES: none | <free text: a verifier FAIL, a predecessor's death, an answer from the author>The agent types are npk:worker, npk:planner, npk:auditor,npk:verifier, npk:researcher (confirmed live, 0.2.4). If the session's
startup listed no npk: agent types — the plugin is not loaded — stop and
say so; the fallback is a general-purpose agent with one line first,
"Load /npk:worker first; it is your procedure.", and a restriction-free
worker is a finding for the record.
7. On a report
First: python3 ${CLAUDE_PLUGIN_ROOT}/skills/check/scripts/check_record.py <repo> <id>.
A finding is a re-dispatch with the finding in NOTES:. Then by status:
| Status | Do |
|---|---|
DONE |
the verifier (§8). PASS → §5.6. FAIL → re-dispatch, the FAIL text in NOTES: |
READY-TO-CLOSE |
the verifier. PASS → the auditor with REPO and the cycle; file its final message as meta/audits/<repo>-<cycle>-<date>.md; commit; re-dispatch the worker with AUDIT: naming it (W-22) |
BLOCKED |
if for-the-author names a dispatch error — a missing input, a claim mismatch, a tree state, a toolchain mismatch — fix it and re-dispatch; otherwise the stream stops and the item goes to the questions table |
STOPPED |
a compiler defect. The stream stops (W-11). Questions table: the reproduction path, recommendation "raise against the compiler". Never worked around |
NEEDS-DECISION |
the stream stops; the question and the worker's recommendation go to the questions table |
RED |
the stream stops (W-12: never a retry); the harness line goes to the questions table |
findings-for-playbook lines go into RECORD.md under the report line; you
decide whether each becomes a playbook edit and you make that edit (W-16).open-questions-raised are noted; they live in the repository.
8. Verification (W-21)
Dispatch the verifier with REPO, SUBCYCLE, TOOLCHAIN and the report'sharness: line. It answers VERIFY <repo> <id> PASS|FAIL with one line per
check. Its procedure is its agent definition plus the check skill. Nothing
moves on the board before PASS.
9. Escalation — the stop list and the batch (W-23)
These stop a stream, and only that stream:
- a compiler defect (
STOPPED) - a decision that is not in a
DECISIONS.md(NEEDS-DECISION) - a specification wrong in a way that changes a plan
- two streams needing one repository
- a probe returning negative and a repository's shape in question
- anything ambiguous enough that you would be guessing
Each goes onto the board's questions table with a recommendation — not a
menu. Send the batch when every running stream is stopped, when the
table holds three, or four hours after the first unanswered item: a
push notification if the tool is available, then the question — withAskUserQuestion when it fits four options, otherwise a message. While
waiting, other streams keep running; when nothing runs, end the turn. An
answer becomes a question answered line in RECORD.md, the row leaves the
table, and the stream restarts with the answer in NOTES:.
10. Rebalancing (W-4)
Cycle counts are a poor proxy. After each stream closes its first cycle,
compare measured wall-clock against the estimate and move a repository
between streams if one is starving. The measurement and the move arerebalance: lines in RECORD.md; the move is your decision, recorded.
Pretending the initial split was right is how a stream idles for a week.
11. The record (R8)
RECORD.md, append-only, its entry vocabulary in its header. Compose the
cross-stream picture no single stream can see: which findings recurred,
which estimates were wrong and by how much, which gates actually bound. That
is the durable output of orchestrating, and it exists only if you keep it.
12. Models per role
Worker, planner, auditor and researcher inherit your model. The verifier may
run on a smaller one — every check it runs is a command with an exit code.
Override per dispatch when there is a reason, and the report records what
ran.
13. tick
Skip §2. Read BOARD.md. Handle any report notifications already delivered
(§7). Run §5 once. If the batch conditions hold, send it. End the turn. This
is what /loop /npk:orchestrate tick re-runs.
14. There is no merge step
Every repository has one writer (W-7) and workers commit to main (W-17).
The compiler's cumulative-prefix protocol
(../nitpick/meta/roadmap/ORCHESTRATION.md §4) applies only where several
writers share one tree, which nothing here does. Independently green is
still not green: that is why the verifier runs on the committed tree (W-21),
and why an ecosystem-wide audit runs after every third close (W-22).
15. Files
WORKSTREAMS.md durable · BOARD.md live, yours · RECORD.md past, yours ·meta/audits/ yours · .internal/toolchain/ yours and untracked ·.internal/orchestrator.session your marker; at a clean stop remove it and
set the board's writer line to none.