AGENTS.md · git:20261001.de76699 · 2026-10-01 · sha256 6211dd2b58763f60

AGENTS.md git:20261001.de76699A

Immutable. This exact content never changes and is served at /api/v1/blob/6211dd2b58763f60.

# workkit: Architecture Overview

> **This file is the ENTRY, not the book.** Every detail has one home (`docs/<topic>.md` or the folder's own README) and this file points at it. Budget: ≤250 lines and no line over 400 bytes, enforced by `docs:board-guard`.

## Identity

workkit is the issue-pipeline workflow system packaged as a Claude Code plugin: it installs into any repo's sessions and brings that repo to one standard: GitHub Issues as the work-item SSOT, labels as the pipeline, a CHANGELOG entry per shipped item.

`docs/project-state.md` is the spec it implements: the ONE normative text, and the only home of the rules the parts below enforce. This file is the entry to the architecture that executes them; `README.md` tells the same story in human voice.

## Repo structure

```
<repo>/
├── .claude-plugin/       # plugin.json + marketplace.json (this repo is its own marketplace)
├── hooks/                # hooks.json + the hook groups, resolved via ${CLAUDE_PLUGIN_ROOT}
│   ├── loader.sh         # name → path router (docs:board-guard → docs/board-guard/run.sh)
│   ├── _lib.sh           # shared helpers (sourced, never executed)
│   ├── lib/              # _lib.sh's helper groups, one file per concern (sourced by it, never a hook)
│   ├── docs/             # board-guard, changelog-guard, change-tracker, checkpoint, session, session-guard, state-check
│   ├── safety/           # vendor-guard, commit-gate, commit-language, release-taken, issue-guard, proof-guard, spec-guard, suite-guard, capture-guard, tree-guard, test-reminder
│   ├── manager/          # resolver, profile, spawn-guard, close-guard, addon + resources/ladder.json (the tier SSOT)
│   └── workflow/         # standards (the daily heal) + reload-guard + feature
├── agents/               # the crew: surface as workkit:<name> (roster + contract: docs/agents.md)
├── briefs/               # the role brief templates, one per crew job, the manager fills the slots: docs/agents.md § File-handoff convention
├── skills/               # the nine workflow skills: surface as workkit:<name>
├── workflow/             # the agent-agnostic engine (labels.json, standards.sh + standards/, workkit.sh + workkit/, home.sh + home/, publish.sh + publish/site-repos.js, lib.sh + lib/, changelog/, ship/, templates)
├── tower/                # mission control: api/ (the JSON API + its libs) + app/ (the OMEGA dashboard)
├── jobs/                 # scheduled work: the 9am daily brief, its launchd plist, and install.sh
├── scripts/              # four scripts, the only platform-touching commands the skills and agents have: the two markers the skills call (review, triage), red-proof.sh (the verifier's red run), review-covers.sh (the ship's review tier)
├── docs/                 # project-state.md (the spec) · agents.md (the crew contract) · hooks.md (the hook detail) · setup.md (the install walkthrough) · cloud.md (remote provisioning) · history-purge.md (the rewrite runbook)
│   └── assets/           # the README's hero and its mark in two inks, rendered by the showcase repo (ITW-Creative-Works/showcase, private)
├── tests/                # Node runner + hook/script/tower suites (npm test)
└── .workkit/             # settings.json is COMMITTED (this repo's own opt-in)
```

## Install

From zero: clone, then `./workflow/workkit.sh setup`. One pass, each step checked before it acts. The steps in order, the plugin-only install, the home repo, opting a repo in and the layout: `docs/setup.md`. Every mechanic: `workflow/README.md`.

A SHIP re-runs it: when the shipped diff touched the setup surface, the ship runs `workkit setup` itself once the release commit's CI is green, so the install follows the kit rather than waiting on the owner. The paths and the clause: `skills/ship/SKILL.md` Step 7.

The engine's stable filesystem address is `~/.claude/workkit` → this repo's `workflow/`; the hooks resolve the engine from their own location instead, so they never wait on it. Mechanics: `workflow/README.md` § How it is reached.

## Hooks

Registered in `hooks/hooks.json`, every command routed through `hooks/loader.sh` so settings reference a hook by `prefix:name` rather than a path. A LOADER-level failure fails open (exit 0); the hook's own exit code passes through untouched, which blocking hooks (exit 2) need.

The index of all twenty-six and what each one does: `docs/hooks.md`. Four carry a README beside the script as well: `tree-guard`, `release-taken`, `session-guard`, `change-tracker`.

## Agents

Five, namespaced `workkit:<name>`: `scout` (recon), `worker` (implementation), `verifier` (blind review), `advisor` (frontier consult), `reviewer` (compliance lens). The first four are CAPABILITY CLASSES, their model supplied per spawn by the `manager/resolver` hook. Roster, classes, crew sizing, the file-handoff convention and its `briefs/` role templates, the definition rules: `docs/agents.md`.

## Skills

Nine, namespaced `workkit:<name>`: `feature` · `interview` · `diagnose` · `review` · `triage` · `status` · `checkpoint` · `migrate` · `ship`. One `SKILL.md` each, which is that skill's own home: what it does, when it fires, and how it runs. Every SKILL.md body holds one bar, at most 120 non-blank lines and no line over 400 bytes, pinned by `tests/scripts/skills.test.js`.

## The engine (`workflow/`)

Agent-agnostic: shell + Node, no Claude Code knowledge, which is why the hooks call it rather than contain it. `workkit.sh` is the one command and the from-zero entry point: `setup [--token]` · `update [--auto]` · `doctor` · `publish` · `brief [--local]` · `tower` · `enable` · `decline` · `heal` · `note`.

Beside it live the label SSOT, the heal, the CHANGELOG linter in `changelog/`, the capture CLI, the ship's four helpers in `ship/` (`publish-plan.js`, `release.js`, `ship-items.sh`, `ci-watch.sh`), the templates a repo receives on enable, and the home repo's whole lifecycle. Every file and every step: `workflow/README.md`.

## The tower (`tower/`)

Mission control in two processes behind one command (`npm run tower`): the plain-Node JSON API on port 8693 (`tower/api/`, zero dependencies) and the OMEGA dashboard on 4300 that reads it cross-origin (`tower/app/`). Seven pages (Overview, Board, Crew, Usage, Brief, Health, Settings) over the board, the crew and its spend, the mornings, what is broken, and a published copy's token.

A view, never a second store, one focus per page: Overview surveys and points, Brief shows the mornings themselves, Health shows only what is broken and sits last in the nav. The pages, the dependency graph, the telemetry, the two write paths, and the published copy that speaks GitHub from the browser: `tower/README.md`.

## The jobs (`jobs/`)

ONE job, at 9am, in five steps (the summaries, the runner reconcile, the brief, the publish, the stale-brief marker) and ONE script that runs them: `morning.sh`, the entry point both schedulers invoke, this machine's launchd agent and the seeded `brief.yml` on a runner.

Each step is gated by what the environment it woke up in can do; the brief itself runs in the CLOUD, on the home repo, since that is where the sweep token and the roster live. Every step, both environments, the payloads, the two tokens and the handover: `jobs/README.md`.

## Tests

`npm test` runs `tests/run.js`, which discovers every `tests/**/*.test.js` through `tests/lib/suites.js`. A suite whose precondition this machine cannot meet calls `skipSuite()` and the runner names the skip rather than hiding it. Suites live under `tests/hooks/`, `tests/scripts/`, `tests/tower/`, `tests/jobs/`, and `tests/docs/`, plus the runner's own suite at `tests/runner.test.js`.

Every suite ends with `if (require.main === module) selfRun(<its export>);`, so `node tests/<path>.test.js` runs it alone; `tests/runner.test.js` fails naming a suite without the line.

Every suite runs on macOS and on Windows under Git Bash, or skips whole by name on the platform it cannot answer. Everything that differs (the shell a case spawns, a PATH it controls, the POSIX spelling a shell script sees, how a tool reaches a stub PATH) lives in `tests/lib/platform.js`, every export the identity on macOS and Linux.

A world a case spawns comes from the same seam: `homeEnv()` is the scratch home in every spelling a tool reads one by (HOME, USERPROFILE, `GH_CONFIG_DIR`, no ambient token, Node's compile cache off), so no child of a test reads the developer's own home or reaches GitHub as them, and `stubTool()` is the one writer of a PATH stub, which a shell starts on either platform and Node starts on neither.

A scratch folder has one writer as well: `mkTmp(prefix)` in `tests/lib/scratch.js`, which resolves the path and removes every folder it made when the process exits.

A case only one platform can answer names its skip through the harness's `skip()`. The Windows lane runs in CI: `checks.yml` runs the suite on a Windows runner on every push, beside Linux. On a Windows machine `workkit setup` builds the kit's script shell (`workflow/script-shell.cs`) and points npm at it, so a root `npm test` there records its tree.

Lanes per layer (`docs/project-state.md` § The proof):
- Unit: a module called directly.
- Integration: a script run whole with a stubbed `gh`.
- End to end: no lane here (a live session is the only surface), so a park names that layer as skipped.

A missing `Proof:` line is a hard gate: `safety/proof-guard` holds the flip to complete and the close, and `safety/commit-gate` check 6 holds the `Fixes #N` trailer.

## Conventions

- **Portable by default.** macOS, Windows (Git Bash) and Linux all run the kit; a spelling that differs branches once, in `workflow/lib/platform.sh` and `hooks/_lib.sh` (`docs/hooks.md` § Platforms). Nothing under `hooks/`, `agents/`, or `skills/` may carry a machine-specific absolute path. Hook commands resolve through `${CLAUDE_PLUGIN_ROOT}`; the engine's stable address is `~/.claude/workkit`.
- **Generic by construction.** No owner names and no personal paths anywhere in the kit; `~/.workkit` and `.workkit/` are the only filesystem anchors.
- **One mechanism, branching by environment.** Never two parallel copies of the same job: one entry point, each step gated on what its environment can do.
- **Idempotent.** Every heal checks before acting; running twice equals running once.
- **Comments are reminders and directions.** One to four lines saying the one thing the code cannot; a file header is one short paragraph. No issue numbers, dates or history in a comment: the why lives on the issue and in the commit. The owner's `code/lint` hook bounces a longer written run or history.
- **No em dashes, no exceptions.** Prose, comments, printed strings, tests and `CHANGELOG.md` use a colon, a comma, parentheses, a spaced hyphen or a new sentence. The CHANGELOG entry separator is a spaced hyphen.
- **The spec is the SSOT.** Rules live in `docs/project-state.md`; skills and hooks execute them and point at it rather than restating them.
- **One home per fact.** Every detail lands in its topic home (`docs/<topic>.md`, a folder's README, a `SKILL.md`) and is never restated where it is pointed at from.