Session Kits
Session Kits
Section titled “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.
Where kits live
Section titled “Where kits live”| 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.
File shape
Section titled “File shape”{ "_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).
The six kits
Section titled “The six kits”| 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.
Applying a kit
Section titled “Applying a kit”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):
bob provision overlay apply --kit security # link the kit's items for this sessionbob provision overlay status # what is active, and from which sourcesbob 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.
Context cost (#2229)
Section titled “Context cost (#2229)”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:
bob provision status --context # tablebob 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.
Baseline: bigbrain (2026-09-30)
Section titled “Baseline: bigbrain (2026-09-30)”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.
Machine role defaults and exclusions
Section titled “Machine role defaults and exclusions”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 itThere 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
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.
node scripts/fleet/role-kits.js # this machine, one linenode scripts/fleet/role-kits.js --json --machine farm-01 # bob-role-kits/1bob-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.
The gate
Section titled “The gate”make check-kits # human outputnode scripts/checks/check-kits.js --json # bob-kits-check/1node 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.
Coverage report
Section titled “Coverage report”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.
make check-kits ARGS=--coverage # human listnode scripts/checks/check-kits.js --coverage --json # bob-kits-coverage/1kit 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).
Related
Section titled “Related”- Verification System — every
make checktarget. - Orchestrator — stack- and type-based recommendation, the axis kits complement.