Skip to content

Session Kits

What a kit is, where kits are declared, and how the declaration is checked (#2221, #2225). (Back to reference index.)

A kit is the set of skills, commands and agents one activity needs — a review, a security pass, a release. Provisioning already groups items by stack and by lifecycle phase; kits add the axis a single afternoon actually has: what you are doing right now. Every trigger that selects a kit — an explicit flag, a flight plan’s labels, a ritual, a machine role’s default — reads the same file, so “a security session” loads the same items on every machine.

Per the Prime Directive, kit membership lives in exactly one place and is validated on every make check.

What Path
Declaration registry/kits.json (BOB_SOURCE)
Schema schemas/kits.schema.json
Runtime copy ~/.claude/registry/kits.json — synced by deploy.sh with the rest of registry/
Gate scripts/checks/check-kits.js, run by make check-kits (part of make check)

Why registry/ and not provisions/. provisions/ holds one manifest per project (and the _bob-home / _default manifests), and check-schemas validates every provisions/*.json as a project manifest. A kit file there would either be mis-validated or need a carve-out. registry/ is where the items a kit names already live, and deploy.sh syncs it to BOB_HOME, so the runtime overlay can read the kit file next to the items it links. Recorded as decision #2359.

{
"_meta": { "version": 1, "convention": "…" },
"kits": [
{
"name": "security",
"purpose": "Threat-model, audit and harden a surface: vulnerabilities, dependencies, secrets and HTTP headers.",
"skills": ["dependency-auditing", "secrets-management", "web-security-hardening", "drupal-security"],
"commands": ["threat-model"],
"agents": ["security-auditor"],
"runbooks": ["cloudflare-api-token-rotation", "github-pat-rotation", "bridge-secret-rotation", "tailscale-auth-key-rotation", "tellbob-token-lifecycle", "bob-attack-surface-review"]
}
]
}
Field Rule
name lowercase-kebab, unique across the file
purpose one line — the activity the kit is for
labels optional — issue labels that select the kit when a flight plan’s scope is derived; a label selects at most one kit across the file (see Labels)
skills directory names under registry/skills/ or skills/
commands <name>.md basenames under registry/commands/ or commands/
agents <name>.md basenames under registry/agents/ or agents/
runbooks <name>.md basenames under registry/runbooks/ only — universal runbooks/ are referenced by absolute path and never linked per project, so a kit cannot name one (#2239)

Each list is optional and duplicate-free; a kit must list at least one item. An item may belong to several kits (code-debugger is in both incident and bugfix).

Kit Purpose
review Review a diff, branch or PR for correctness, simplicity, accessibility and search health. Carries docs-review-checklist, the versioned checklist the docs-review ritual walks (rituals.md → Checklists, #2625).
vision-planning Shape the vision and turn it into business cases, capabilities, requirements, solutions and risks.
security Threat-model, audit and harden a surface: vulnerabilities, dependencies, secrets and HTTP headers. Carries the credential-rotation runbooks (#2239) and bob-attack-surface-review, the versioned checklist bigbrain’s security-review ritual walks (rituals.md → Checklists, #2241).
release Cut, version, promote and deploy a release, with its change record and QA sign-off.
incident Diagnose and recover a live failure across machines, then capture the retrospective and runbook. Carries the three drill runbooks — drill-restore, drill-lost-laptop, drill-farm-compromise — since it is the kit every drill ritual applies (rituals.md → Drills, #2240).
bugfix Reproduce a bug as a failing test, fix the root cause and keep the regression test.

registry/kits.json is the membership list; this table only repeats each kit’s purpose.

A kit is loaded into a session as a provision overlay — the same lifecycle a flight plan’s items use (#1133) — so the project’s manifest is never edited (#2226):

Terminal window
bob provision overlay apply --kit security # link the kit's items for this session
bob provision overlay status # what is active, and from which sources
bob provision overlay restore # remove the overlay links
Behaviour Rule
What is linked The kit’s skills, commands, agents and runbooks (into .claude/runbooks/) minus those the project manifest already declares and those BOB_HOME already provides (global or universal). The overlay only owns links it alone created.
Where the kit is read ~/.claude/registry/kits.json — the deployed copy, next to the items it links.
State .claude/.provision-overlay.json records kit, the per-source item lists under sources, and their union under items.
With a flight plan --kit composes with --plan <N> (or --file <overlay.json>), in one call or applied one after the other. The overlay is the de-duplicated union, and overlay status names both sources (plan #2311 + kit security).
Re-apply Applying the same kit again is idempotent. A different kit (or plan) while one is active exits 2 and names overlay restore.
Unknown kit Exits 2 with the list of declared kits; nothing is written.
Restore Removes every overlay link (plan and kit together), deletes the state record and rebuilds the managed ignore block from the manifest. The project tree ends up byte-identical to before the apply, and a second restore is a no-op (exit 0).
From the shell claude --kit <name> (or --kit=<name>) runs overlay apply --kit <name> on the current directory, then starts Claude Code without the flag. It sits alongside the wrapper’s -a1–-a4 and -rdb flags in bin/bob-shell.zsh. A refused kit stops the launch with the overlay’s exit code, and --kit with no name exits 2.
Crash safety The state record is written atomically, before any link. An interrupted apply therefore leaves either no record or a whole one naming every link it may have made. bob provision status surfaces it, and overlay restore (or re-applying the same kit) recovers it.
Ritual run stamp bob ritual run applies the ritual’s kit with --ritual-run <owner/repo#N>, which records the run on the kit source and as ritual_run on the record (#2234). overlay status names it, for example kit review (ritual run paulirv/bigbrain#2372), plus the bob ritual close that restores it, so a run abandoned without a close is visible. bob ritual close restores only an overlay stamped with its own run, and never one that also carries a flight plan. Re-applying the same kit without a stamp keeps the stamp. The same kit stamped for a different run exits 2, as does --ritual-run without --kit or not in owner/repo#N form.

Labels: a flight plan selects kits (#2227)

Section titled “Labels: a flight plan selects kits (#2227)”

A kit’s labels are the issue labels that pull the whole kit into a flight plan’s overlay. plan-scope.js derive <N> (what bob provision overlay apply --plan <N> runs) checks every issue in the plan: when one carries a label a kit declares, that kit’s items join the items the plan’s other signals derived.

Label Kit
security security
release release
incident incident
cap, business-case, use-case, vision vision-planning
bug bugfix

review declares no labels: it is only ever applied by name (--kit review, a ritual).

Rule Behaviour
What joins the overlay The kit’s registry items, required_with expanded, minus what the project manifest already declares. Universal items are left out — bob init already links them.
Provenance derived_from.kits lists each selected kit with the labels and issue numbers that selected it. sources maps every overlay item (skills/tdd-flow) to what put it there: kit:<name> for each selecting kit, signal for the stack/label/area signals.
Determinism Same plan, same issue labels, same kit file and registry → byte-identical output.
One label, one kit make check-kits fails duplicate-label when two kits claim the same label.
Reviewing it plan-scope.js diff <N> prints the usual + list, then an Added by block: one group per selecting kit, headed with the label and issues that selected it, then the signal-sourced additions. --json carries the same split as sources: {kits: {<kit>: ["type/name", …]}, signal: [...]}. An item both selected is listed under each.
A kit the machine forbids A plan’s labels can select a kit the machine’s role forbids — a cap issue selects vision-planning, which farm workers forbid. overlay apply --plan does not refuse the plan: it leaves out every item only that kit contributed, names the kit and the dropped items, and keeps any item a signal or an allowed kit also selected. A plan doc without sources (written before #2227, or by hand) is applied as it is.

The map is edited here, in registry/kits.json; plan-scope.js has no label→kit table of its own.

Every skill, command and agent a session can see loads its description into the context, and each one dilutes triggering for the rest. bob provision status --context measures what a session here carries, so a kit’s saving is a number rather than a guess:

Terminal window
bob provision status --context # table
bob provision status --context --json # bob-context-cost/1
Row What it counts
global set the machine’s resolved global manifest (_bob-home*.json, the #414 chain) — the universal set every BoB session carries
project manifest (extra) the project manifest’s items the global set does not already provide
loaded today global + project: what a session in this project carries now
kit, + on manifest what applying the kit adds on top of the manifest — the overlay’s rule, kit items minus global and manifest
kit, global + kit alone what a session carrying only the global set and the kit would load, and its reduction against loaded today

The estimate. An item’s loaded text is <name>: <description>, the description read from its frontmatter with runs of whitespace collapsed to one space. Its cost is ceil(characters / 4) — the common characters-per-token rule of thumb. It approximates the real tokenizer and never calls it, so the same files always give the same numbers. A missing description costs its name alone; a declared item with no file is listed under missing and costs nothing. Runbooks are not counted: they are read by path when a step needs them, never loaded into the context. Items and kits are read from BOB_SOURCE (registry/<type>/ first, then the universal <type>/ directory); scripts/orchestrator/context-cost.js owns the method.

bob-context-cost/1. method (the rule, restated), global and project (each {manifest, types: {skills|commands|agents: {count, tokens}}, total}; project.costliest lists its ten most expensive items), loaded ({count, tokens}), kits[] ({kit, adds: {types, total}, with_manifest, alone, reduction_vs_manifest: {tokens, percent}}) and missing[]. Exit 0, or 2 when a manifest cannot be read. The global set differs per machine role, so run it on the machine whose sessions you are measuring.

Measured on farm-01 (role farm-worker, global set _bob-home.farm-worker.json) at commit 47d8e704, with bob provision status --context:

Set Skills Commands Agents Tokens
global set 9 17 3 1,623
project manifest (extra) 16 18 11 5,313
loaded today 25 35 14 6,936
Kit Adds on top of the manifest Global + kit alone Reduction vs loaded today
review +964 3,795 3,141 (45.3%)
vision-planning +508 2,642 4,294 (61.9%)
security +605 2,629 4,307 (62.1%)
release +107 2,038 4,898 (70.6%)
incident +987 3,259 3,677 (53.0%)
bugfix +0 2,858 4,078 (58.8%)

The bugfix-kit reduction is 4,078 tokens, 58.8%. A bugfix session that carries the global set and the bugfix kit instead of bigbrain’s full manifest loads 2,858 description tokens instead of 6,936. Applied on top of the manifest the kit adds nothing, because bigbrain already declares every bugfix item — its saving comes from loading it instead of the manifest. The four agents with example-laden descriptions (lead-dev-architect, unit-test-generator, code-debugger, code-reviewer) cost 1,669 tokens between them, almost a third of the project’s extra items. The farm-worker global manifest also declares two commands no longer in the tree (pr, research); they are listed under missing and cost nothing.

bob tour uses the same estimate: every skill, command and agent on the tour carries its tokens, and the never-invoked grades list the costliest first, so the items that load the most for no use lead the list.

A machine’s role sets a default kit and the kits it forbids (#2228). Role is a default and a constraint, not the primary key: a farm worker runs unattended, so it must never load a kit that needs a human.

The kits are declared in a global manifest, next to the item lists:

{
"_meta": { "project": "_bob-home", "path": null, "stack": ["bob-home"] },
"skills": ["…"],
"kits": {
"default": "bugfix",
"forbid": ["vision-planning", "security"],
"why": "farm-worker machines run unattended …"
}
}
Field Rule
default A declared kit name, or null for no default.
forbid Declared kit names that overlay apply --kit refuses on this machine.
why Optional one line explaining the choice.

Resolution. A machine reads its kits from the same file its global item lists come from. That is the #414 chain: _bob-home.<machine-id>.json, then _bob-home.<role>.json, then _bob-home.json. The most specific file that exists wins as a whole. A machine-id file with no kits block therefore means “no default, nothing forbidden”; it is not merged with the role file. A role file must also carry the full global item lists, because it replaces the base manifest for that machine.

Machine role File Default Forbids
farm-worker (farm-01) provisions/_bob-home.farm-worker.json bugfix vision-planning (a human shapes intent), security (its secrets-management skill covers credential rotation, which needs a human at the provider)
dev-laptop provisions/_bob-home.json (base) none nothing
utility-host (fubaz) provisions/_bob-home.json (base) — no role file, decided on #2543 none nothing

Refusal. bob provision overlay apply --kit <name> with a forbidden kit exits 3 and writes nothing:

overlay: kit 'vision-planning' is forbidden on this machine (farm-01, role farm-worker) by provisions/_bob-home.farm-worker.json — there is no override; apply it on a machine whose global manifest allows it

There is no --force-kit. To allow the kit, change the global manifest, which goes through review like any other manifest edit. An unknown kit is still exit 2, checked before the role rule. claude --kit stops the launch with the same exit code.

Default kit. When a session names no kit, the machine’s default applies through the same overlay lifecycle. There is no new warp-drive phase: the existing prerequisites overlay step runs

Terminal window
bob provision overlay apply --default-kit [--plan <N>]
Situation Result
The machine declares kits.default That kit is applied (subject to forbid), and the default is named with the manifest it came from. With --plan it composes into one plan + kit union.
No default declared Clean no-op, exit 0. Nothing is written unless --plan was also given.
A kit is already applied (claude --kit <name>, or an earlier segment of the run) That kit stays; the default is not applied over it.
--default-kit together with --kit <name> Exit 2 — they are exclusive.

session_ending restores the overlay exactly as it does for a plan.

Reporting. Every machine’s resolved kits are visible without SSH:

Surface What it shows
bob doctor One informational line, never graded: INFO kits: role farm-worker — default bugfix, forbid vision-planning, security (provisions/_bob-home.farm-worker.json) (#2228). The bob-host-drift/1 document carries it as kits (bob-role-kits/1).
audit.js snapshot summary.kits: {declared, default, forbid, manifest, error} for the publishing machine.
audit.js view A Session kits table: MACHINE, ROLE, DEFAULT, FORBID, MANIFEST, with one row per machine whose snapshot carries the key.

Asking what a machine resolves.

Terminal window
node scripts/fleet/role-kits.js # this machine, one line
node scripts/fleet/role-kits.js --json --machine farm-01 # bob-role-kits/1

bob-role-kits/1 carries machine, role, manifest (the resolved provisions/ file), declared, default, forbid and error. BOB_MACHINE_ID=<id> names the machine instead of matching the hostname. The provisioning engine honours it too, so a test can play any fleet machine.

Terminal window
make check-kits # human output
node scripts/checks/check-kits.js --json # bob-kits-check/1
node scripts/checks/check-kits.js --root DIR # grade another checkout
Exit Meaning
0 every kit is valid
1 at least one problem (listed)
2 the kit file or schema is missing or unparseable, or a usage error
Problem kind Raised when
schema the file does not validate against schemas/kits.schema.json
duplicate-kit two kits share a name
unknown-item a kit names an item that exists in neither registry/<type>/ nor <type>/ (a runbook: not in registry/runbooks/)
duplicate-label two kits declare the same label, so a flight plan’s kit selection would be ambiguous (#2227)
empty-kit a kit lists no skills, commands, agents or runbooks
unknown-role-kit a global manifest’s kits.default or kits.forbid names a kit registry/kits.json does not declare (#2228)
forbidden-default a global manifest defaults to a kit it also forbids
kits-outside-global-manifest a project manifest declares kits — only _bob-home*.json may

The bob-kits-check/1 document carries verdict (ok / fail / error), kits (the declared names), problems (each with kind, kit, and for unknown-item the type and item) and, on error, an error string.

Every registry item in the review, security, process and governance categories (the category: frontmatter key) should belong to at least one kit. The coverage report lists the ones that don’t. It only reports, so it exits 0 whatever it finds, as long as the kit file can be read.

Terminal window
make check-kits ARGS=--coverage # human list
node scripts/checks/check-kits.js --coverage --json # bob-kits-coverage/1
kit coverage (review, security, process, governance): 33/44 registry item(s) in a kit, 11 uncovered — report only
[uncovered] skill agent-builder (process)
…
Rule Why
Registry items only Universal items load in every session, so no kit needs to list them.
.disabled/ items are skipped They are not provisionable.
Other categories are skipped Design, docs, ops and similar items are grouped by stack and phase today; kits do not have to cover them.

An uncovered item raises a question, not a defect: which activity is it for? Answer it by adding the item to a kit, or leave it out on purpose (for example skill-creator, which is for authoring BoB itself, not a session activity). The bob-kits-coverage/1 document carries verdict, categories, considered, covered and uncovered (each entry with type, name and category). A missing or unparseable kit file gives verdict: "error" and exit 2.

Tests: make test-kits (tests/test-kits.js, fixtures in tests/fixtures/kits/ — a valid kit file, an unknown item, a duplicate kit name).