quickreview
AGENTS.md9 starsChanged 29 days agoMITView on GitHub
QuickReview is a 24/7 AI code reviewer for a GitHub repository: a deterministic TypeScript daemon receives GitHub App webhooks, runs an authoritative reconcile poll, and drives sticky pi coding-agent sessions in tmux — one conversation per pull request, blank-slate single-concern "lens" passes per review round, and an adversarial validator before anything is posted. No language model runs in the daemon's own decision path: the daemon selects lenses, prepares worktrees, applies posting-time guards, and executes every GitHub write through one gate;…
- Reads credentials
- Deletes or force-pushes
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,439 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository36 other files it gives its agents.
- scripts/AGENTS.mdAGENTS.md
- tests/AGENTS.mdAGENTS.md
- scripts/CLAUDE.mdCLAUDE.md
- tests/CLAUDE.mdCLAUDE.md
- ask-mattSkill
- codebase-designSkill
- code-reviewSkill
- diagnosing-bugsSkill
- domain-modelingSkill
- grillingSkill
- grill-meSkill
- grill-with-docsSkill
- handoffSkill
- implementSkill
- improve-codebase-architectureSkill
- prototypeSkill
- researchSkill
- resolving-merge-conflictsSkill
- setup-matt-pocock-skillsSkill
- tddSkill
- teachSkill
- to-questionnaireSkill
- to-specSkill
- to-ticketsSkill
- triageSkill
- wait-whatSkill
- wayfinderSkill
- wizardSkill
- writing-for-agentsSkill
- adjudicateSkill
- evidenceSkill
- landSkill
- replaySkill
- setupSkill
- surveySkill
- selfimproveSkill
The file
We split this AGENTS.md into the rules it gives an agent
7 rules. About 0 are common, 1 is distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 26
Read README.md for what the system does for its user, and MANIFESTO.md for the beliefs it implements.
- Distinctive: line 59
All three must be green before you commit.
- Very specific: line 61
Never pass --reporter=basic.
- Very specific: line 164
One mutation gate: Every GitHub write goes through packages/pi-plugin/src/gate.ts, whose MUTATION_KINDS is a closed list.
- Very specific: line 216
One memory seam, one engine: Every read and write of reviewer memory goes through MemoryStore (src/memory/store.ts), whose single …
- Very specific: line 270
Add or change a lens rule: Add an entry to PASS_ADDENDA in packages/pi-plugin/src/include.ts (scope "all", "lenses", or a named lens list.
- Very specific: line 295
Add a gate kind: Add it to MUTATION_KINDS in packages/pi-plugin/src/gate.ts and give it a caller.
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
AGENTS.md — working in this repository
What this is
QuickReview is a 24/7 AI code reviewer for a GitHub repository: a deterministic TypeScript daemon receives GitHub App webhooks, runs an authoritative reconcile poll, and drives sticky pi coding-agent sessions in tmux — one conversation per pull request, blank-slate single-concern "lens" passes per review round, and an adversarial validator before anything is posted. No language model runs in the daemon's own decision path: the daemon selects lenses, prepares worktrees, applies posting-time guards, and executes every GitHub write through one gate; the sessions produce findings and a verdict file the daemon validates. Around that core sit an operator TUI, a Slack concierge, an OpenViking-backed memory engine, and a guided onboarding wizard, each of which degrades cleanly when it is not configured.
The repository publishes two npm packages. The root is
@quickchatai/quickreview, the whole app with the qcr bin (TUI, daemon,
companion, every verb); the release image runs this same package.
packages/pi-plugin is @quickchatai/pi-quickreview, the review core and
the pi plugin over it (/qcr-review, /qcr-scan, /qcr-setup, each also a
skill), installable on its own with pi install npm:@quickchatai/pi-quickreview.
The app depends on the core at its exact version, and its own pi (qcr pi)
adds /selfimprove.
Read README.md for what the system does for its user, and
MANIFESTO.md for the beliefs it implements. This file is the
map for changing it.
Architecture decisions
The decisions that shape the repository, each with its reasons and the
options it rejected, are recorded in docs/adr/. Read
the index before a change that moves code between packages or adds a surface.
- ADR 0001: the plugin and the app
nest. The plugin (
packages/pi-plugin) is what plain pi needs to review. The app is the managed product around it: the home, setup, auto-review and the TUI. Each works without the one outside it. - ADR 0002: the model route. A review
runs on any provider pi supports, on the credential the person has. The
shipped config names no model, and no provider or model name decides
anything in product code (
tests/fences-modelnames.test.ts). The route is resolved once per run where pi is (packages/pi-plugin/src/modelroute.ts, from the inventorypackages/pi-plugin/src/engine/pisdk.tsbuilds), a spec that names no model means "the run's route", and the tiers arelight,standardanddeep. Read it before a change that names a model, reads a credential or prices a run.
Build, test, typecheck
npm ci
timeout 900 npm test # vitest; the whole suite, offline, no secrets
npm run build # tsc -b (packages/pi-plugin, then the app), verify-dist, the build stamp
npm run typecheck:tests # tsc -p tsconfig.tests.json (tests/ is not in the build)All three must be green before you commit. Notes that will bite otherwise:
- Never pass
--reporter=basic; run a single file astimeout 120 npx vitest run tests/<file>.test.ts. tests/is excluded fromtsconfig.json(it emits nothing), so the build does not typecheck it.tests/fences-gate.test.tsrunstsc -p tsconfig.tests.jsonas part of the suite, which is why a type error in a test failsnpm test.npm run buildchainsscripts/verify-dist.mjsbecauseincremental+noEmitOnErrormean tsc decides what to emit from its build info, not from what is on disk — a targetedrminsidedist/will not be rebuilt. The reliable cold build isrm -rf dist packages/*/dist node_modules/.cache && npm run build.tsc -bbuilds two projects in order: the core intopackages/pi-plugin/dist/, then the app intodist/.noEmitOnErrorholds per project, so a core edit that breaks the app still rewrites the core'sdist/. That is why "a whole build finished" is its own signal: every build route (npm run build,scripts/tsc-watch.mjsafter each clean pass,scripts/dev-tui.sh) runsscripts/build-stamp.mjs, which touchesstate/build-completeonly after a whole pass succeeded. The TUI's hot reload (src/tui/reload.ts) watches the app'sdist/and that stamp, never the core'sdist/.vitest.config.tsrestricts collection totests/**on purpose: the default globs also collect agent worktrees under.claude/worktrees/, which then judge this checkout's seeds against another branch's expectations.
Conventions
- TypeScript strict, plus
noUncheckedIndexedAccessandnoImplicitOverride(tsconfig.json). Node ≥ 22.19 (both packages'engines; CI's test job runs 22, 24 and 26, and the images carry 26). - ESM,
module: NodeNext— local imports carry the.jssuffix even from.tsfiles. - Two layers, fenced. The review core is its own package, the npm
workspace
packages/pi-plugin(@quickchatai/pi-quickreview), with its ownsrc/andextensions/. Core is everything underpackages/pi-plugin/; app is the rest of the repo. The app imports the core by package specifier,@quickchatai/pi-quickreview/src/<file>.js; the core imports only itself, pi's root specifiers,smol-tomlandnode:*, never the app. The core never owns the process either: noprocess.exit,process.cwd,console,homedirornode:readline(a host passes those facts in), except the plugin's entry,src/plugin/index.ts, which may read the home. Which package a responsibility belongs in is ADR 0001.tests/layers.test.tsholds both rules on the real tree (tests/helpers/layers.ts), and its ratchet,tests/helpers/layer-allowlist.ts, is empty and stays empty: a violation is fixed by moving code, never by an allowlist entry. A test resolves the specifier to the core's sources (vitest.config.ts,tsconfig.tests.json), and a fence that walks the source walks both trees (tests/helpers/source-trees.ts). - The core's data stays at the root.
prompts/,agents/,seeds/,schemas/,profiles/,deploy/reviewer/, the seed manifest andconfig.example.tomlhave one source, the repo root. The plugin'sprepackcopies them intopackages/pi-plugin/assets/(scripts/plugin-assets.mjs), checks every copy's sha256 against its source, and itspostpackremoves the copy again;assets/is gitignored and never edited. The core finds its data throughassetRoot(packages/pi-plugin/src/assetroot.ts):assets/in an installed package, the repo root in a checkout or the image. The seeds sit underassets/, not at the package root, because pi turns a package'sprompts/into slash commands. - Minimal dependencies. Runtime:
smol-toml, andbetter-sqlite3as an optional dependency: it is the state store's native driver, so an install where it cannot build still installs, and the verbs that never open the store (SQLITE_FREE_VERBSinsrc/sqlitehint.ts, and the home) still run; their routes are fenced bytests/sqlite-free-routes.test.ts. Dev:typescript,vitest,@types/*,eslint+@eslint/js+typescript-eslint,prettier. Adding one is a decision, not a convenience. - Tests mock all I/O: fake fetch and adapters,
StateStore(":memory:"), temp directories. The suite must stay green offline, with no secrets, on a fresh clone — a stranger's first command is the suite. A handful of suites spawn real subprocesses because the subprocess is the thing under test (the worktree manager against a scratch git repo, the shell posting scripts against a loopback HTTP stub, the CLI and deploy preflight); the current set isPROCESS_SUITESintests/helpers/suite-timeouts.ts. - Extensions are factories over explicit config. Nothing under
extensions/readsprocess.env,process.cwd(),process.argvoros.homedir()(an ESLint ban,tests/extension-lint-ban.test.ts). A pi that loads an extension by path loads its env shim,extensions/cli/<name>.ts, the one place the process becomes config; an SDK session builds the factory (createX(config)) itself. - Every behaviour change needs a test at the seam it changes. See
tests/AGENTS.mdfor the house style. - Fences are verified by mutation. A structural test that cannot be made to
fail is not a fence, so each one names, in its own comment, the exact mutation
that proves it bites (
MUTATION: add "retract_review" to MUTATION_KINDS — this fails). Two more rules go with it, both learned from fences that looked right and proved nothing: grep for a reader, never for a name (a name appears in its own declaration), and a derived set can be satisfied by a self-loop (so reachability fences do a real traversal from the initial state). The rules are stated at the top oftests/fences.test.ts. - Legacy input folds are load-bearing. Retired spellings stay accepted on
input forever and are pinned by
tests/legacy-acceptance.test.ts. Do not "clean them up" — a container somewhere is still running the older prompt. The one exception is a record underdocs/adr/that rules otherwise: ADR 0001 drops the plugin's unprefixed pi commands.
Invariants an agent must never break
These are structural: each is enforced by code shape or by a pinned test, not by convention. If your change trips one, the change is wrong — fix the code, never the fence.
- One mutation gate. Every GitHub write goes through
packages/pi-plugin/src/gate.ts, whoseMUTATION_KINDSis a closed list; an unknown kind throws. Reads go direct (src/github.ts). The gate reads the caller's intent once into a frozen snapshot and admits that snapshot; guards receive it and may soften a mutation (body, note) but can never change which mutation it is, and the result is re-admitted in full before anything executes. Never add a write path around the gate. - No review dismissal exists. There is no mutation kind that dismisses,
retracts, withdraws or revokes a review, and
tests/fences-gate.test.tsfences the name shape, not a list of four names people thought of. Guards only refuse or soften. - Frozen prompt seeds.
prompts/andagents/seeded content is canonical and byte-pinned againstseeds/bytests/seed-integrity.test.ts(sha256 manifest + provenance comparison). A genuine reseed is the owner's to ratify and is recorded intests/fixtures/ratified-divergence.json, which stays hash-pinned so further drift still fails. Inside the files that are ours to edit, ratified passages sit between<!-- RATIFIED … -->markers whose contents are hashed bytests/invariant-fences.test.ts— edit around a fence, never inside one, and deleting the marker fails just as loudly. - Pass behaviour changes go through the addendum registry.
PASS_ADDENDAinpackages/pi-plugin/src/include.tscomposes binding rules onto the built pass bodies instate/agents/; repo-specific house rules go in a profile pack (profiles/<name>/addenda/, loaded bypackages/pi-plugin/src/profiles.ts). Both are the same mechanism — a pack extends the registry rather than bypassing it. There is no third path. - The reviewer reads code; it never executes it. Five independent layers
hold that boundary — a git config-scope pin on the session argv
(
src/sessions.ts), a command guard (packages/pi-plugin/extensions/bash-guard.ts), a write veto over git's configuration surface and this system's own policy surface, a round-boundary integrity fingerprint (src/execboundary.ts), and an ungated-review outcome detector. None may be traded for another; the command-string layers are explicitly not claimed to be closed. Readdocs/exec-boundary.md— including its review discipline — before touching any of them, and extend theCORPUSintests/exec-boundary.test.ts. dry_runis the operator's flag alone. It shipstrue; in dry-run the gate writes each intended call tostate/dryrun/instead of executing it. Nothing in the codebase may move it in either direction. The one class that executes anyway isAPP_SELF_CONFIG_KINDSinpackages/pi-plugin/src/gate.ts— a closed set pinned by deep equality, so adding a member failstests/gate.test.ts. Seedocs/ownership.mdfor every owner-only surface.- Single shepherd. Two daemon-owned session kinds exist: one main session
per PR and one global overseer. The daemon nudges (round order, wake file)
and never schedules an agent; lens passes are leaves, enforced at agent build
time by
packages/pi-plugin/src/include.tsstripping spawn tools. Anything wanting a session's attention mid-round goes throughwakeOrHoldinsrc/wakes.ts. Pinned bytests/single-shepherd.test.ts; the topology isdocs/single-shepherd-invariant.md. - One memory seam, one engine. Every read and write of reviewer memory goes
through
MemoryStore(src/memory/store.ts), whose single implementation issrc/openviking/store.ts. Two absolutes at every call site: memory never blocks or delays a round (a dead server is a refusal, never a throw), and no caller ever holds an admin or root key. - Honest-unknown rendering. A surface that cannot read its source renders
unknown — never a zero, never a default. It has two halves and fixing only
the first is the trap: the rendered value and every action or published
fact derived from the same missing data (an unknown container state means no
click target, and the companion-facing field is
nullrather than absent, so a dropped line cannot read as "nothing to report"). The TUI's first frame is painted before any network, docker or filesystem call —tests/tui-startup.test.tsasserts that structurally, with a wall-clock budget as a warning only. - The TUI has one layout: the tab on the left, the companion column on the
right, on every frame and in every configuration. No full-screen takeover, no
collapsed spine, no no-column mode; configuration decides what runs and
what the column says, never whether it is there. Fenced structurally by
tests/tui-fixed-layout.test.ts(a module that must not exist, a hook with no name, a state field not on the class).
Where things live
| Concern | Modules |
| Ingress | src/webhook.ts (HMAC, delivery dedupe), src/reconcile.ts (the authoritative poll — GitHub always wins) |
| Lifecycle | src/statemachine.ts (transition table in the module header), src/state.ts (SQLite + status.json snapshot) |
| Round planning | src/dispatch.ts (FIFO, concurrency cap), packages/pi-plugin/src/planner.ts (the lens plan, follow-up rounds, the round order), packages/pi-plugin/src/heuristics.ts (lens selection), packages/pi-plugin/src/securitytriggers.ts and packages/pi-plugin/src/statetriggers.ts (the content triggers of the two lens families, pass-sec-* and pass-sm-*), packages/pi-plugin/src/ecosystem.ts + packages/pi-plugin/src/migrations.ts (framework pre-checks), packages/pi-plugin/src/profiles.ts |
| Prompt assembly | packages/pi-plugin/src/include.ts ({{include:}}, tier rewriting, PASS_ADDENDA), packages/pi-plugin/src/templates.ts (round templates) |
| Sessions | src/sessions.ts, src/tmux.ts, src/worktrees.ts, src/wakes.ts, extensions/inject-bridge.ts |
| Verdict + posting | src/verdicts.ts, packages/pi-plugin/src/gate.ts, packages/pi-plugin/src/guards.ts, packages/pi-plugin/src/cigate.ts, packages/pi-plugin/src/standing-review.ts, src/posting.ts, packages/pi-plugin/src/anchors.ts, src/postops.ts, the session shims scripts/post-review.sh, approve-pr.sh, reply-thread.sh |
| Review judgment | packages/pi-plugin/src/severity.ts (severity × confidence × reachability → tag), src/recheck.ts, src/pingpong.ts, src/escalation.ts |
| Security review | packages/pi-plugin/src/passes.ts (SECURITY_FAMILY, the fourteen pass-sec-* lenses), packages/pi-plugin/src/securitytriggers.ts (what plans a family lens), packages/pi-plugin/src/threatmodel.ts (the threat model, read from the base), packages/pi-plugin/src/secfacts.ts (deterministic facts through read-only git), packages/pi-plugin/src/secevidence.ts (a chain's outcome from its hop labels), src/securityrequest.ts (the label, @<bot> security, --security), profiles/security/ |
| Learning | src/feedback.ts, src/eod.ts, src/digest.ts, packages/pi-plugin/src/findingsrecord.ts, src/improvements.ts |
| Memory | src/memory/ (the seam), src/openviking/ (the engine, supervised on loopback) |
| On-demand reviews | src/securityrequest.ts (a label, an @<bot> comment or review-now flag asks for a lens family on a PR; one row per PR and kind), src/staterequest.ts (the state-machine kind over the same machinery) |
| Operator surfaces | src/tui/ (board, tabs, companion column), src/cli.ts (qcr), src/slack/, src/commands.ts + src/levers.ts |
| Assembly | src/daemon.ts (stub-wired core, test-friendly) vs src/wire.ts (production wiring) |
| Onboarding | src/onboard/, extensions/onboarding.ts, docs/onboarding.md |
The home (a bare qcr) | src/home/launch.ts (the pinned pi, quiet, on the person's own pi home), extensions/home.ts (createHome over the ports; every action a key and a home_* tool), src/home/model.ts (state and actions, the review button), src/home/panel.ts (the overlay), src/home/review.ts (a review's progress model), src/home/ports.ts + src/home/adapters.ts + src/home/reviews.ts (the reviews port: preview, the detached registry's start/status/stop, the review state, the daemon's rounds, the post of a kept review) |
| The pi plugin | packages/pi-plugin/src/plugin/ (the entry index.ts, the commands, versiongate.ts for the ^1.4.0 pi range, login.ts for the borrowed host login), packages/pi-plugin/src/reviewapi.ts, packages/pi-plugin/README.md |
| Host plugins | plugins/qcr/templates/ (the one skill source), plugins/qcr/render.mjs (its renderer, for every host and the Plugin's gitignored skills/), src/plugincli.ts (qcr plugin: a host's render in the data home, src/homes.ts dataHome, pointed at through the host's own verbs, or copied, with a record of each file written, where Cursor and opencode have none), docs/plugin.md |
| The npm CLI | bin/qcr.js → src/qcrbin.ts (the bin's front door: bare qcr, review, scan, login, pi, update, version, plugin), src/schemaflag.ts (qcr --schema N, a host plugin's contract, CONTRACTS in packages/pi-plugin/src/contract.ts), src/npmverbs.ts, src/updatenotice.ts; every other verb goes to src/cli.ts |
agents/, prompts/, skills/companion/ and extensions/ are shipped bot
content (what the reviewer and companion sessions load); .claude/ is
contributor tooling for working on this repo.
Per-concern architecture documents — one mechanism per page, each naming the
modules it describes — live under
docs/architecture/.
How to make the four common changes
Add or change a lens rule. Add an entry to PASS_ADDENDA in
packages/pi-plugin/src/include.ts (scope "all", "lenses", or a named lens list; declaration
order is composition order), or, for house rules about one repository, a file
under profiles/<name>/addenda/. Do not edit a seed under agents/ or
prompts/. Fences that will fail if you do: tests/seed-integrity.test.ts
(byte identity), tests/invariant-fences.test.ts (a ratified passage), and
tests/include.test.ts (the composed result). See
docs/profiles.md.
Add a config knob. Parse it in src/config.ts, document it in
config.example.toml, and consume it somewhere else.
tests/fences-config.test.ts fails a knob that
is parsed and never read — the check deliberately excludes the parser, because a
name appears in its own declaration. tests/config.test.ts and the
publishability lint both parse config.example.toml, so it must stay valid.
Add a TUI surface. Register its keys in the surface's vocabulary and name
them in the help overlay: tests/fences-tui.test.ts asserts both directions,
because a registered key with no reachable effect does not merely do nothing —
App.handleInput routes any unregistered printable to the companion column, so
a dead registration steals the keystroke. The same file fails an action-union
variant that nothing constructs (a click target that cannot be clicked), and
tests/tui-fixed-layout.test.ts fails anything that takes the terminal or folds
the column. Contract: docs/tui.md.
Add a gate kind. Add it to MUTATION_KINDS in packages/pi-plugin/src/gate.ts and give it a
caller. It must be ordinary: suppressed in dry-run, refused for forceLive, and
outside APP_SELF_CONFIG_KINDS. tests/fences-gate.test.ts rejects any name
suggesting dismissal; tests/gate.test.ts pins the two closed sets by deep
equality. If the new kind posts to GitHub from a session, it is a new op on
the daemon (src/postops.ts), never a shell route: the session shims only
hand the daemon their result files (scripts/AGENTS.md),
and the guard cases for it go in tests/guards.test.ts, which runs them
through the real gate.
Two more things to know
- The publishability lint (
tests/publishability.test.ts) walks every tracked file — the repository is public in full — and fails on private tokens: personal names, private hosts, absolute home paths, internal tracker paths. The tokens come from a denylist kept outside the repository (seetests/AGENTS.md), so the lint names none of them, and without it those checks skip; credential shapes are checked always. It carries a negative control that plants a canary and proves the scanner bites. If your change trips it, fix the content; an allowlist entry needs a named file, the kind of token and a real reason. - Decisions are recorded before they are built; comments state the rule, not
who decided it (
docs/provenance.md).
Contributor-facing rules — what a pull request needs — are in
CONTRIBUTING.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

