adopt · install · one repo
Install
The cost before the commands: two GitHub accounts (a bot and a human), nine GitHub Apps for the desk identities, and a macOS, Linux, or Windows host. Then two ways in. With Claude Code, add the Assay plugin and run one skill, assay:install. It scaffolds the board (the generated status page), pins and hash-verifies statusgen, wires CI, and proves the install, stopping where a person must decide. Without Claude Code, the manual path below stands up the same thing by hand from the pinned release binary in the public medici-finance/assay repository. Same destination; the skill spares you the transcription.
Before you start: two GitHub accounts
Assay runs on a bot account and a human account, and they must be different logins. The bot is what the fleet runs as: it opens the pull requests, runs CI, and owns the reviewer GitHub App. The human is you: you bless the roster, sign off the human-gated work, add the 👍 that clears a public change, and press merge.
Keep them separate. If the bot and the human are one account, the bot can approve and merge its own work and every gate becomes self-satisfiable. Two logins are what make “a human approved this” a claim posted by an identity the fleet cannot post as.
One person, one repository? This full setup is fleet-sized, and it is the wrong size for you. The smaller supported path is Solo mode: your own login and no GitHub Apps at all. Read it before the App section below rather than after. It is a different install, and it gives things up.
Which harness, and which platform
Assay is built to sit under whatever agent harness you already run, because the method is what matters, not the model. Here is how far that has been carried, and how far it has been proven, so you can answer “can I run this on my harness?” before you install rather than after. Each cell is in one of three states, and a built integration is not the same claim as a proven one: supported means it runs and the proof is on the record; integration built means the packaging and bindings have landed and been verified, but the live end-to-end desk run is not yet proven; not supported means what it says.
| Harness | State | What is proven (and when) | What is not yet proven |
|---|---|---|---|
| Claude Code | supported | The reference harness. The whole methodology, this site and the Assay repository included, is authored and run on it continuously. Everything else is measured against it. | — |
| Codex OpenAI | integration built | Harness-neutral skill bodies, the Codex binding file, and the generated Codex packaging (the .codex-plugin manifest and the AGENTS.md fragment the adopt flow installs) have landed, and were independently verified in the stream's source tree. The public record describes that packaging and its verification, but the dated run behind it is not yet on the public record, so Codex is reported as built rather than given a proof date you cannot yet follow to a run. |
A full desk loop run end-to-end in a live Codex session. That smoke run is held pending a live Codex environment; until it runs, Codex is packaged, not yet proven end-to-end. |
| Cursor | integration built | The third harness column (the generated always-apply .cursor rule, the drift-checked packaging, and the capability bindings) landed and was verified on 2026-08-26, with the dated run on the public record. |
A full desk loop run end-to-end in a live Cursor session. That run is tracked and in progress; until it lands, Cursor is packaged, not yet proven end-to-end. |
| Windows operating system | supported amd64 | Native Windows, not WSL. Every release ships statusgen-windows-{amd64,arm64}.exe, desk-tools-windows-{amd64,arm64}.tar.gz and qualgen-windows-{amd64,arm64}.exe, checksummed with the rest; a PowerShell bootstrap and the Go-native deskinstall download and sha256-verify them with no Go toolchain and no gh. A Windows CI leg runs statusgen --lint and a version smoke on a windows-latest runner on every change. |
The native windows/arm64 smoke, held for want of an arm64 Windows runner: the arm64 binaries are published and checksummed, not yet CI-run. Two surfaces stay POSIX and say so: the Claude Code SessionStart hooks need bash and jq (Git-Bash or WSL), and the cellctl cockpit is a bash script that drives tmux, so on Windows the desk sessions are started directly rather than through it. |
Harness and operating system are two different questions: the first three rows are agent harnesses; the last is an operating system. On a supported harness the guarantees that never degrade are isolation, evidence, and the review gates; a harness that cannot isolate an implementer refuses the work rather than running it in a shared checkout. The work behind the two “integration built” rows, and the live-session runs that would turn them into supported, are tracked in the public harness-portability stream (a stream is a named body of related briefs, the units of work); the Windows port, its CI leg, and the held arm64 smoke are recorded in the windows-port stream. A built integration is reported as built, and only a run on the record is reported as proven. Where a cell names a date it links to that run; a proof not yet on the public record is reported as built, not dressed as a date you cannot follow.
With Claude Code, the turnkey path
Assay ships as a Claude Code plugin, and the plugin carries an installer skill. You add the marketplace, install the plugin, and invoke assay:install. The skill detects the repo, scaffolds the streams tree, acquires the version-pinned statusgen binary and checks its sha256, wires the CI workflow, and proves the result. It is the same runbook the manual path walks, executed instead of transcribed.
Prerequisites. Claude Code installed and running in the repo you want to adopt; the GitHub CLI (gh) installed and authenticated (the skill downloads the pinned release with it); and a macOS, Linux, or Windows host. On Windows the pinned release binaries are acquired by the PowerShell bootstrap and the Go-native deskinstall rather than through gh, sha256-verified the same way; the two surfaces that still want Git-Bash or WSL are named in the harness table above.
Three commands. Add the plugin marketplace from the public repository, install the assay plugin from it, then invoke the installer skill:
# in Claude Code, inside the repo you're adopting
/plugin marketplace add medici-finance/assay
/plugin install assay@assay
# then invoke the installer skill — or just ask: "install Assay here"
assay:install
What assay:install does, in order, and what it refuses:
- Confirms the target
Before writing anything: it names the absolute repo path and the detected platform back to you, and will not scaffold a repo it only inferred.
- Scaffolds with
statusgen initRather than hand-rolling a second copy of the tree: the streams directory, the three registers (the append-only logs), and a bootstrap-safe CI workflow.
- Pins and verifies
statusgenIt resolves the statusgen release paired with the plugin version (never
latest, never “the newest release”), writes the channel-E line into.assay-versions, downloads the asset, and refuses on a hash mismatch. A pinnedsha256is the one thing a re-tagged release cannot silently swap. - Wires CI and the optional main-guard
Confirming the two-half workflow is present and correct rather than authoring a competing copy.
- Proves the install
It is not done until
statusgen --root . --lintexits0andstatusgen --versionprints the pinned tag. If either fails, the skill says the install is not proven and stops. No fabricated success. - Idempotent, and it refuses to clobber
Re-invoking is safe: an already-adopted repo is reported and left untouched (a first-class outcome, not an error); a partially-installed one advances only the unmet steps.
The skill authors branches and opens draft PRs only. It never merges. It stops and hands you the exact values at every step that stays a human decision: private-repo CI auth, creating and installing the reviewer GitHub App (the identity that posts approvals, which a worker session cannot stand in for), and any merge, push, tag, or first ready-flip on main. It hands you the values and waits; it does not fabricate the outcome.
Manual, without Claude Code
No Claude Code, or you want to run each step yourself? Everything below stands up the same install by hand. It is the ground the turnkey skill covers for you, and the reference for what that skill does. There is one supported acquisition channel, the pinned release binary, and it is described here.
Take the binary, not the source
statusgen is the single writer of your STATUS.md board, a repo-agnostic Go tool with no product knowledge. The obvious way to adopt a small self-contained tool is to copy its directory into your repository. That recommendation has been retired. A vendored copy is a fork, and forks rot in silence: a copied generator, frozen at the commit where it landed, stays green and keeps gating another repository's pull requests weeks later while missing every check added upstream since. A gate that has quietly stopped checking what you believe it checks is worse than no gate.
So the supported path is a pinned release binary, verified by sha256. A one-line-per-platform pin file names the release tag and the expected hash; installation downloads the asset, checks its digest, and refuses on a mismatch; upgrading is a one-line change reviewed like any other. One binary, one pin, one place to bump, and drift becomes a diff instead of a silence.
Everything else is the standard adoption runbook; this page is the getting-started slice of it. The authority is docs/adopting-assay.md in the public repository, written as a step-by-step runbook a coding agent can execute.
-
Pin the release
Commit a
.assay-versionsfile at your repository root with one line per platform you install on, in the formstatusgen-<platform> <tag> <sha256>. The current release is v1.0.7. Itsstatusgenassets and digests (the same release carriesdesk-tools-<platform>.tar.gzandqualgen-<platform>, listed in itschecksums.txt):Platform Asset name sha256 macOS Apple silicon statusgen-darwin-arm648abdee4486a141b0345476a6ccc31b96801abf04b7606d65e92f4ac7d6f8d99emacOS Intel statusgen-darwin-amd6470f29be625c256fc45a2094b55adf6ffbcdcb4b4425a5b61875bebb8b68ba456Linux x86-64 statusgen-linux-amd640acdf52e78470befb2c93caacace9f700fa4cd92879640c90e120c5643153dbbWindows x86-64 statusgen-windows-amd64.exee3d8db8f51037b78c37ac8d47a04766e25f3acd7b9275a99d7e7b883b1024241Windows arm64 statusgen-windows-arm64.exe1ab5272fde07696b3ede66aa8ea116ccff3a8073dc6f3d22f804371c4cbc6629The pin file, carrying only the platforms you actually run on:
# .assay-versions statusgen-darwin-arm64 v1.0.7 8abdee4486a141b0345476a6ccc31b96801abf04b7606d65e92f4ac7d6f8d99e statusgen-darwin-amd64 v1.0.7 70f29be625c256fc45a2094b55adf6ffbcdcb4b4425a5b61875bebb8b68ba456 statusgen-linux-amd64 v1.0.7 0acdf52e78470befb2c93caacace9f700fa4cd92879640c90e120c5643153dbb statusgen-windows-amd64.exe v1.0.7 e3d8db8f51037b78c37ac8d47a04766e25f3acd7b9275a99d7e7b883b1024241Four rules carry their weight, and each has burned someone who skipped it. Pin the full platform, operating system and architecture, never the family, or a
darwin-amd64-only file quietly passes on an Apple-silicon host. Refuse rather than guess when the line for the detected platform is absent. Re-pin, never edit in place on an upgrade, so the bump shows up in a diff and gets reviewed. And keep each pinned asset name distinct from any CI job name. -
Install and verify
Detect the platform, read the pinned line, download the asset, and compare the digest before installing. Refuse on a missing line and refuse on a hash mismatch. The check is the point, not decoration.
$ plat="$(uname -s | tr A-Z a-z)-$(uname -m | sed 's/^x86_64$/amd64/; s/^aarch64$/arm64/')" $ grep "^statusgen-$plat " .assay-versions # refuse if absent statusgen-darwin-arm64 v1.0.7 8abdee4486a141b0345476a6ccc31b96801abf04b7606d65e92f4ac7d6f8d99e $ read -r _ tag want < <(grep "^statusgen-$plat " .assay-versions) $ gh release download "$tag" --repo medici-finance/assay --pattern "statusgen-$plat" $ got="$(shasum -a 256 "statusgen-$plat" | cut -d' ' -f1)" $ [ "$got" = "$want" ] || { echo "sha256 mismatch — refusing"; exit 1; } sha256 verified — statusgen-darwin-arm64 $ install -m 0755 "statusgen-$plat" /usr/local/bin/statusgenThen prove the install is what the pin claims, and prove the hash check is live rather than ceremonial:
$ statusgen --version statusgen v1.0.7 $ # re-run the install against a deliberately corrupted digest sha256 mismatch — refusing -
Stand up the board
With the binary on
PATH, the rest of the day-one install is scaffolding the sourcesstatusgenreads. The adopter scaffold in the public repository is a populated, read-only worked example. Copy its shape, do not vendor it:- Streams. Create
docs/streams/<stream>/README.mdwith the required frontmatter and brief table, and author the first brief (one self-contained unit of work an agent can execute) by copying docs/brief-template.md. Fill every frontmatter field; an emptysources:or a missingrisk:answer is a gap, not a shortcut. The reviewgateis derived (anyrisk: yesmakes ithuman), never chosen. - Registers. Create the three append-only logs
docs/streams/FINDINGS.md,INTAKE.md, andRETRO.md(formats in docs/registers.md). Numbering is gap-free; withdraw an entry with a tombstone, never by deleting a heading. - CI. Author a two-half workflow at
.github/workflows/statusgen.yml; this is house-specific and is not shipped in the bundle. The pull-request half runsstatusgen --lintonly and blocks any diff that touchesSTATUS.md; the push-to-main half regenerates and commits the board. Only that one job ever writes the file.
The first board is the proof that the sources are well-formed:
$ statusgen --root . --lint LINT: OK — 1 stream, 1 brief, 3 registers, no problems $ statusgen --root . # writes STATUS.md locally; the main-side CI job commits it--lintis what pull-request CI runs: it builds the view internally but never reads or writesSTATUS.md, so the board has exactly one writer and drift between sources and status stays visible. See statusgen for the full behaviour and the lifecycle for what the status cells mean. - Streams. Create
-
Create the desk identities
Each desk role (a standing role window in the pipeline) posts under its own GitHub App, nine Apps in all, counting the board-writer, the promote identity, and a read-only auditor that holds no write permission at all, so that a review or a verification is attributed to an identity the author cannot post as. GitHub has no API for creating an App, but it accepts a pre-filled manifest, and the GitHub Apps page carries one per role: a click on each, your review on GitHub's page, and the key comes back to your own terminal. The manual permission table is on the same page for anyone who wants every toggle in view. One toggle there is easy to skip and expensive to skip: the reviewer App needs Administration at Read-only — a read of repository settings, no write to them — because that is how the ready-flip reads which status checks a branch requires. On a protected branch whose required checks are not expressed in a ruleset — classic branch protection, or a ruleset with no required-status-checks rule in it — the fallback the flip would otherwise use cannot see that list, so the gate fails closed and no pull request is ever flipped ready. Adding a required-status-checks rule to the branch's ruleset also clears it, one branch at a time; the permission is the durable fix. The boot preflight does not check this one, so it is not a gap the tools will report for you.
no API creates a GitHub App · the create click is a person's
One person, one repository: Solo mode
Everything above, two accounts and nine Apps, is built for a team that needs a review or a verification attributed to an identity the author cannot post as. For one person governing one repository it is the wrong size, and this page does not pretend otherwise. The smaller supported path is Solo mode: the installer creates zero GitHub Apps and writes a roster naming your own login. You run the whole workflow (briefs, the generated board, verifier sessions, the CI gate) as yourself, on one credential: your own user token from gh auth.
In Solo the six desk roles do not disappear; they become labels on what is posted rather than distinct GitHub identities. A verdict comment reads “role: reviewer” in its body, but the account that posted it is yours, and every action shows your login in the trail.
What Solo gives up
Solo is a reduction, not a lighter coat of the same thing. You give up:
- Per-role attribution. The audit trail shows one login, yours, for author, reviewer, and verifier alike. Where a reader needs “who reviewed this?” to resolve to someone other than the author, one login cannot answer it.
- A posted approval. GitHub will not let a login approve its own pull request, and in Solo you author every PR, so the reviewer verdict lands as a comment, not an
APPROVE, and says in one line that approval is withheld because GitHub does not permit a login to approve its own pull request. - The automatic ready-flip. With no bot able to approve, nothing keys a ready-flip off a review verdict; you read the comment and ready or merge by hand.
- Two preflight checks, by construction. The scopes-vs-duties and cold-mint checks report could-not-check: a user token has no recorded grant to compare against, and there is no App key to mint from. The boot line says so. It is could-not-check by design in this mode, never a green it did not earn.
And Solo refuses rather than degrades where one login cannot stand in for the separation: a repository whose branch-protection ruleset requires a specific bot identity for a required check is one Solo cannot drive, and it says so and stops rather than acting under a login the ruleset meant a distinct actor to satisfy. That refusal is also your signal that the repository has outgrown the tier.
What Solo keeps
The part that matters at this scale survives: the free toolkit, executable checks on each piece of work, and a status board derived from evidence rather than typed by hand. What it does not keep is a second person: in Solo you are author, reviewer, and verifier at once. The one structural thing GitHub still enforces is that you cannot formally approve your own pull request, so the verdict can only be a comment and you decide at the merge gate after reading it. That is a weaker control than a distinct reviewer identity, and Solo names it as weaker. It also means you do far more by hand here than in any other tier: every loop is started by you, and every verdict is a comment you post and re-read yourself.
Your path is the board install above; you skip “Create the desk identities.” Pin and verify the binary, stand up the board, and run as yourself. Then the tools you operate: the generated status board and the unit of work, with the lifecycle holding for one person exactly as it holds for a fleet. Moving up, when a repository outgrows Solo, is additive: your own login keeps merging and deciding exactly as before.
The reading order
The path for someone standing this up, each document in the public repository:
- README — what Assay is and the component inventory, in one screen.
- docs/adopting-assay.md — the install runbook. It defines the named primitives once and composes them across three scenarios: green-field, an existing suite, and carving a unit out of a larger project.
- docs/brief-template.md and docs/brief-rules.md — the unit of work: scope, typed dependencies, the risk-derived gate, and a Verify table that can actually run. See also Briefs.
- docs/lifecycle.md — the states a brief moves through, and why
verifiedis a distinct step a non-implementer runs. - docs/registers.md — the append-only logs and their contiguity and tombstone rules.
- examples/adopter-scaffold — the worked shape to copy from.
What you installed, stated exactly
The board is derived, not measured. statusgen parses markdown written by the same agents whose work it reports; it checks internal consistency (sequence gaps, missing evidence, unresolved findings, malformed gates) and is backstopped by independent re-verification. The value is that drift, missing evidence, and register tampering become machine-visible, not that agents become trustworthy.
Likewise, a review posted by the separate reviewer identity is attribution, not authorization: it names a distinct identity the author cannot post as, which is strictly more than a self-written checkmark, and not proof the review happened, was thorough, or was independent. When you report what you installed, claim that and no more.