Building
Reference notes on running Groupthink with Claude Code agents. What the agents read, how a change moves from an issue to production, what checks production afterward, and what I do myself. The throughput figures are on the Groupthink page. This page is the configuration. Each item states the mechanism and the incident that produced it, with the date.
Summary
Each repository has a CLAUDE.md at its root, plus one global file above them. Claude Code loads them at the start of every session, before anything else.
Every rule is the instruction followed by a Why line carrying the date and the incident. Two reasons. An undated rule cannot be retired, because nobody can establish whether the condition that produced it still holds. And the reasoning is only recoverable at write time. Adopted 2026-08-07 and applied to the configuration, the workflow documents, the memory files, and the repository documentation.
The global file holds behavioural rules and current state. Procedure lives in separate documents, indexed by a table that maps each one to the task that should trigger reading it. Currently 28 documents. Incident, 2026-08-26: the global file reached 9,540 words, growing about 200 a day, because every new rule was appended to it. Behavioural rules were outnumbered thirteen to one by procedure. The frontend repository hit 12,816 words the same week, with branching rules at a third of the total. Nothing was deleted. The test when adding a rule: does this apply every turn, or only to one kind of task.
A rule with a limited lifespan gets an expiry comment on its heading. A script checks for passed dates and fails, wired into the morning routine. Incident, 2026-08-26: a section instructed in prose that it be removed after July 1. It stayed in context for eight weeks past that date, routing code review to a role that no longer existed. Prose is not an enforcement mechanism.
One file describes what I optimize for, how I write, how I decide, and where its model of me is reliable. Engineering scope and voice rules are marked reliable. Pricing, anything affecting paying customers, and strategic direction are marked unreliable and get escalated instead of simulated. Consulted in place of asking when asking would not add information. Updated on redirect.
The configuration repository dates from November 2025 and holds 28 workflow documents, five hooks, eight routines, and one file per incident. Most of it postdates May 2026, when an audit of every repository on this machine found no hooks, no subagents, and no skills in any of them. The repositories were documented and not yet programmable.
One issue, one agent, one worktree, one pull request. Steps run in this order.
An engineering issue names the product issue it implements, which names the goal. A milestone is one dated title created in all five repositories at once, so the board sorts as a timeline, and it carries a launch tier that determines which marketing work is created alongside the engineering work. A separate file per repository holds what an issue tracker structurally cannot: which issue bodies are factually wrong, what is blocked and on which trigger, what was investigated and ruled out, and why the order is the order. It carries a last-verified date. Older than seven days means rewrite before acting.
Every session authenticates as the same GitHub account, so assignment cannot disambiguate. The claim is a label plus a comment carrying a session id and a UTC timestamp. Earliest timestamp wins. A claim older than three hours with no pull request is stale and reclaimable. The durable claim is an open pull request that closes the issue. Claiming runs on the whole batch before any agent is dispatched. Incident, 2026-06-24: two sessions built the same two issues, one of which was assigned and still double-built.
Each session works in its own git worktree branched from the integration branch. A pre-tool hook blocks branch-mutating git commands in a shared primary checkout and allows them in a worktree. Detection is structural: a worktree has .git as a file, a primary checkout has it as a directory, so new clones are covered without a list. Enforcement has failed three times, on 2026-08-04, 2026-08-13, and 2026-09-02, and in each case the documentation, the repository, and the settings all read as protection while nothing was enforced. Install by symlink, never by copy. A script now exercises the installed hook daily and asserts it denies what it should.
The milestone is inherited from the parent issue. The body opens in plain language, then states what the change enables, then engineering detail, with every issue reference glossed inline so the body reads without following links. Each closed issue gets its own closing keyword, because a comma-separated list closes only the first. A post-deploy checklist section is mandatory; low-risk changes write not applicable with a reason rather than omitting it, because the release aggregates these sections. An audit table is posted as a comment: issue alignment, duplicate-system check, whether a zero was treated as evidence, commit hygiene, branch freshness, deferred items lifted to issues. Incident, 2026-08-14: a sweep of 159 open issues found six already shipped and still open.
The session that wrote the change requests the review, and the review is a comment. A session reading its own diff is not a review, and green CI is not one either, since CI only demonstrates that existing tests still pass. The reviewer runs on the strongest available model, on the principle that a reviewer should not be weaker than the sessions writing the code, and has a read-only shell so it can check findings against source rather than inferring from a diff. Policy is default-to-approve, blocking on security, broken functionality, breaking changes, and migrations. Findings are treated as claims: both the premise and the proposed fix are verified against source before applying. In one August release, three findings would have broken the feature if applied as written. A disposition comment then records every finding as fixed with a commit, deferred with an issue, or declined with a reason. The last human reviewer left in May 2026.
An agent reporting merge-ready is not evidence. Agents work in worktrees where local tooling can mislead, so they report CI as unverified and the merging session reads the checks on the head commit, re-reading after every push. A path-gated job that reports success in seconds means nothing was tested, not that the suite passed. No check run at all is not a passing run. Required status checks on the protected branch make the platform enforce this. Incident, 2026-08-26: an auto-merge fired mid-CI because the branch had no required checks and therefore nothing to wait for.
The integration branch merges to the release branch roughly daily. The release body is written in plain language, aggregates every merged pull request's post-deploy checklist into one section, names rollback signals, and opens a paired 24-hour monitoring issue. Since 2026-08-26 the agent merges on observed green. Carve-outs still route to me: migrations against existing rows, authentication, billing, queue and scheduler changes, and changes to API response shape. That list has been narrowed twice, deliberately, because routing zero-risk changes to a human trains rubber-stamping and degrades the signal on the ones that need judgment.
There is no staging environment. The checklist runs against production: deploy landed, new routes respond, the error tracker shows nothing new since the deploy timestamp, the failed-jobs table is empty. Read-only production queries run through a documented pattern. Anything an agent structurally cannot check, which is anything visual, is labelled for human verification, and since 2026-09-02 a merged release carries that label on GitHub until the walk is done. Incident: a release sat merged and deployed for an hour with an untouched checklist and no signal anywhere that it was outstanding.
Building is the tractable half. The system became trustworthy when a loop started reading production every morning.
About thirty numbered checks, specified in one markdown file, run every morning. The deterministic subset is a single script taking roughly ninety seconds; the rest needs a session with access to mail, a browser, and read-only production queries. Output: one dated issue with a summary comment, a row appended to a dashboard, and a separate issue per real problem.
Most checks fire on something missing. Missing can mean broken or it can mean nothing was due. Every absence-shaped check therefore specifies a second query that distinguishes the two, and it runs before escalation. Written after a single session produced four false alarms in one day. The corollary is that flattering readings receive less scrutiny than alarming ones: a stale configuration once reported a month at 389 percent of target and went unquestioned.
Each metric is evaluated against its own sixteen-week band rather than a fixed threshold. One metric read red for three weeks because its threshold had been set below its historical floor.
A notification type that stopped sending for about eight months with no error anywhere, which produced a zero-volume canary check. Meeting-bot joins failing for weeks, invisible because the failures wrote rows that every other check filtered as noise. A release merged but not deployed for two days behind a hung deploy that reported cancelled rather than failed, which produced a check comparing the running commit against the release branch.
An append-only file, newest first. Schema per entry: question, decision, alternatives considered, why, predicted outcome, revisit-when. Sessions append entries when a decision is made, without being asked. Two integrity rules: never infer a why, mark it as needing confirmation instead; never edit a past entry, write a new one linking back. Roughly 350 entries since May 2026.
The morning routine reads dates out of markdown logs to decide what to run. Incident: one entry sat in an unmerged pull request for six days, so the log on the default branch still showed the previous run. The next session computed a thirteen-day gap that had not occurred and started work that was not needed. A file consulted to decide whether a routine should run gets merged in the session that writes it.
Total recurring burn divided by merged engineering pull requests, with each anchor marked verified or estimated. Agent token spend is logged daily in API-equivalent dollars, because raw token counts are dominated by cache reads and misstate the trend. The figures and their sources are on the Groupthink page.
The system is bounded by what I specify and what I accept back. These are the constraints I hold myself to.
Every request ends in exactly one of: done and verified; done with judgment calls documented where I will see them; escalated as a single packet containing context, options, and a recommendation; or parked with a durable carrier, meaning an issue, a calendar event, or a scheduled trigger created before the session ends. There is no fifth state. I send a request and drop it, so my memory is never the persistence layer. If I have to remind the system of anything, that is a defect: the task gets handled, then the class of failure gets a rule, a routine step, or a hook.
When the request names an artifact, the deliverable is the artifact existing, not its contents rendered in conversation. A preview doubles the reading and converts a completed task back into a review task. Previews are reserved for irreversible actions. Adopted 2026-08-26.
Default is to merge. When a question surfaces, it comes to me as the question, with options and a recommendation, and I do not read the diff. Releases, outbound sends, spending, and irreversible actions remain my click. Adopted 2026-08-14, after a day of a dozen green pull requests that I could not meaningfully review. The rule depends on the automated review's measured precision, which is tracked in its own log; if that log goes stale the rule is revisited.
The first two lines carry the outcome and any decision required of me. Anything needing 500 words becomes a document or an issue, and I get the link instead.
An audit of 215 session closes in July 2026 found about one open item in nine leaked: half were finishable and parked anyway, half were blocked with nothing but a sentence recording it. The close now requires finishing finishable work rather than offering it, and forbids closing a blocked item on prose alone, since a summary line will not resurface anything. It then sweeps for anything a future session would have to be told again and writes it into the appropriate file, and appends decisions to the decision log.
A session ends its turn when it stops emitting tool calls, and nothing re-invokes it across a wall-clock gap. Overnight work therefore requires an external driver: a self-pacing loop, a scheduled job, or a workflow script. Prerequisites: the host stays awake, the permission mode will not block on a prompt nobody will answer, the prompt is self-contained enough to re-enter cleanly, and each item is time-boxed. Incident, 2026-06-04: an instruction to work autonomously overnight was acknowledged, a worktree was created, and the session sat idle for eight hours. Test the driver in a short in-session run before relying on it.
Good morning, goodnight, Monday setup, Friday wrap. Each maps to a script plus a written guide, and the guide specifies tone as well as steps. The morning one reports no throughput figures by design.
Most rules written this summer address one gap: the distance between a thing reporting done and a thing being done.
Before a launch, the agent uses the live product in a real browser with the public documentation as its only reference, suppressing what it knows about the implementation, and logs every divergence between what the documentation implied and what happened. CI validates code against our model of the user. This validates the product against what the documentation promised. Incident, 2026-08-27: a launch had passed CI, two review rounds, API smoke tests, and a claim-by-claim documentation audit. The first walk produced five findings in under an hour, one of them a launch blocker where the headline feature worked in the database and was not visible to a user checking for it. The walk ends with a briefing on what the product can now do, which repays the cost of delegating verification.
A new test must be observed failing when the behaviour it guards is removed. Incident, 2026-08-13: three pull requests shipped tests that asserted nothing, and two survived both the automated review and a human one.
Incident, 2026-08-29: a clean check meant an entire milestone had received no production data. Absence of failure and absence of data are indistinguishable at the check level. Verification asserts positive evidence, and a zero result requires establishing what the total was.
Any description of product behaviour intended for someone outside the room is checked claim by claim against a freshly fetched ref, citing a file and line per claim. My own description of the product is an input to that process, not an authority. Incident, 2026-08-19: a help document asserted the product did not record something that the production code path in fact requested. It would have reached a paying customer as fact.
Seeding ends with a call that exercises the credential against the thing it is for, because exit status and string length are not validity signals. Incident, August 2026: a correctly encrypted key with insufficient permissions disabled desktop transcription for days. Critical credentials now carry a scheduled probe.
Any change writing to a user's external account ships with a read-only production dry run first: affected row count plus before-and-after under the new logic, reported as counts and aggregates only, never rows. Adopted 2026-08-25. The rule has since stopped two further fixes that would have changed nothing.
All of the above is files. Below are the file shapes, trimmed of specifics, and prompts that produce them. This page is also served as plain markdown, so the whole thing can be handed to an agent as a single URL.
/building/claude-code.md This page as markdown, for handing to an agent.
One private configuration repository, symlinked into the paths the tools read, plus a few files per working repository.
~/.claude/CLAUDE.md symlink to claude-config/CLAUDE.md
claude-config/
CLAUDE.md behaviour and current state only
mind-theory.md model of the principal, and where it is wrong
workflows/ 28 procedures, loaded on demand
routines/ morning, goodnight, Friday wrap: script + guide
hooks/ Claude Code hooks, symlinked to ~/.claude/hooks
git-hooks/ protected branches, no force push, no secrets
scripts/ claim.sh, check-expired-rules.sh
incidents/ one file per incident, dated
<working repository>/
CLAUDE.md every-turn instruction + load-on-demand table
docs/ PR checklist, release process, verification
NEXT-UP.md what the tracker cannot hold, with a date
~/.claude/projects/<repo>/memory/
MEMORY.md one line per memory, loaded every session
*.md one fact per file, with why and how-to-apply
<product repository>/
DECISION-LOG.md append-only, newest first
PRODUCT-LOOP.md idea to shipped, numbered phases
MILESTONES.md one dated title, mirrored across repositories
<company repository>/
daily-check-process.md every production check, with its second query
process/ incident protocol, and the rest
The shape of every rule, wherever it lives.
## Green means observed green
Before merging, read the checks on the head commit and confirm they
concluded successfully. Do not merge while checks are pending, and do
not treat --auto as a substitute for reading them.
**Why** (2026-08-26): a release merged to main mid-CI under a "merge
when tests pass" instruction. The branch had no required status checks,
so --auto had nothing to wait for and merged immediately. It landed on
green code by luck. Durable fix: required checks on the protected
branch, so the platform enforces the condition instead of a session
remembering to poll.
The always-on file, in skeleton. Behaviour and current state, then a table routing tasks to procedure documents. Mine runs about 1,500 words; it was 9,540.
# CLAUDE.md
Behaviour and current state. Procedure lives in workflows/ and is read
when the task calls for it. When adding a rule, decide which half it is.
## Working rules
- Produce the artifact, do not preview it. When I name an artifact, the
deliverable is the artifact existing. (2026-08-26: a preview doubles
my reading and converts a finished task into a review task.)
- 150 words by default. First two lines carry the outcome and any
decision required of me.
- Every request ends in one of four states: done and verified, done
with judgment calls documented, escalated as one packet, or parked
with a durable carrier. A reminder from me is a defect; patch the
class, not the instance.
- Every rule carries a dated why.
## Current state
| Mode | State |
|-------------|-------|
| Travel Mode | OFF |
## Load on demand
| Document | Read it when |
|----------------------------------|---------------------------------------|
| workflows/git-safety.md | before any branch, commit, or PR work |
| workflows/session-close.md | on "ready to close?" |
| workflows/release-management.md | staging or verifying a release |
| workflows/doc-led-walkthrough.md | verifying a user-facing feature |
A comment a script can parse. The morning routine fails once the date passes.
## Reviews go to the automated reviewer only <!-- EXPIRES: 2026-10-01 -->
Until the new review workflow is proven, request every review from the
bot and none from a person.
**Why** (2026-09-01): the human reviewer role ended, and the transition
needs one gate rather than two.
One fact per file. Frontmatter for relevance matching, then the fact, the why, and how to apply it. The index holds one line per memory and never the content.
---
name: worktree-setup-here
description: What a fresh worktree needs before its tests can be trusted
metadata:
type: feedback
---
A fresh worktree needs a real dependency install and an application
key before the suite runs. Rebuild assets after adding a view, then
hard reload the browser.
**Why:** on 2026-09-02 a new panel rendered with invisible text. The
stylesheet had been built before the view existed, and the browser
served the cached file under an unchanged name.
**How to apply:** set up in that order. If a utility class appears to
do nothing, grep the built CSS before editing the view.
Newest first, never edited. Written at session close without being asked.
## 2026-08-26: Who merges release pull requests
**Question:** Should the agent merge the integration branch into the
release branch on green CI, or wait for my click on every release?
**Decision:** Agent merges on observed green. Migrations against
existing rows, billing, and auth changes still route to me.
**Alternatives considered:** Keep every release on my click. Rejected:
it made each release wait on my attention, and the post-deploy checklist
is the backstop in either case.
**Why:** release cadence was bounded by my availability, not by risk.
**Predicted outcome:** releases ship the same day the work merges.
**Revisit when:** a release ships broken, or the automated review's
measured precision drops.
What an issue tracker cannot represent: ordering rationale, known-wrong issue bodies, ruled-out approaches, and items awaiting a decision rather than an engineer.
# NEXT-UP.md
Last verified: 2026-09-02. Older than 7 days: reseed before acting.
1. **#412 Acceptance label on releases.** First because a release sat
merged and unverified for an hour with no signal on GitHub.
Blocked on: nothing.
2. **#418 Dry run before third-party writes.** Issue body is wrong: the
row count predates the backfill. Ruled out: a migration. The data is
correct and the logic is not.
Needs a decision: whether to write to accounts that never opted in.
3. **#403 Calendar reconnect prompt.** Real, lower priority. Ordered
after #412 because both modify the settings page.
One of about thirty. Query, firing condition, and the second query that distinguishes broken from nothing-was-due.
### Check 7: Transactional email volume
Query: count of emails sent in the last 24 hours, grouped by type.
Fires when: any type returns zero.
An absence has at least two causes. Before escalating, run the second
query: how many of that type were due in the window? Zero due and zero
sent is healthy. Some due and zero sent is a fault.
Evaluate each type against its own 16-week band, not a fixed threshold.
Writes: one line in the dated issue, one row on the dashboard.
What "ready to close?" executes. The constraint is that nothing exits the session as prose.
## Ready to close?
1. Commit and push anything this session changed.
2. Completion check. Every open item ends in one of four states: done
and verified; done with judgment calls documented; escalated as one
packet with options and a recommendation; or parked with a durable
carrier (issue, calendar event, memory file). A line in the summary
is not a carrier.
3. Sweep for anything a future session would have to be told again and
write it into the relevant CLAUDE.md or workflow document.
4. Append decisions to DECISION-LOG.md. Never infer a why.
5. Save memories for corrections and non-obvious patterns. Prune stale
ones. Keep the index short.
Paste these into a session in the repository you want to change. Each one produces a file rather than a plan, so read what it wrote and argue with it.
Create a CLAUDE.md at the root of this repository, under 500 words. Include only what must be considered on every turn: how to work with me, the branch and pull request rules, and what to do before opening a pull request. Move anything longer into a file under docs/ and add a row to a "Load on demand" table in CLAUDE.md stating when to read it. Give every rule a Why line with today's date and the reason. When you finish, list the three rules you are least confident I actually want.
Add this to CLAUDE.md and follow it from now on: when I correct the same class of mistake a second time, do not apologize. Add a rule to the relevant CLAUDE.md or docs file recording the correction, today's date, and my wording as the why. Then tell me where you put it.
Read my CLAUDE.md and count the words. Classify every section as behaviour (applies every turn) or procedure (applies to one kind of task). Move each procedure section into its own file under workflows/, with a header noting the extraction date and the reason, preserving each rule's why. Replace each in CLAUDE.md with a row in a "Load on demand" table stating the trigger. Report the word count before and after.
I run multiple sessions against this repository concurrently. Write a Claude Code PreToolUse hook for Bash that blocks branch-mutating git commands (checkout, switch, commit, merge, rebase, reset, push, stash) when the working directory is a primary checkout, and permits them in a linked worktree. Detect the difference structurally: .git is a directory in a primary checkout and a file in a worktree. Install by symlink, never by copying. Then write verify-install.sh, which executes the installed hook to prove it denies a commit in the primary checkout and allows one in a worktree, and add it to whatever runs at the start of my day.
Every session here authenticates as the same GitHub account, so assignment cannot identify which session holds an issue. Write a script that claims an issue with a label and a comment carrying a session id and a UTC timestamp, skips issues already claimed by another session, treats a claim older than three hours with no open pull request as stale, and can report the current holder. Add a rule to CLAUDE.md that the whole batch is claimed before any agent is dispatched, and that an open pull request closing the issue supersedes the label.
Create DECISION-LOG.md, append-only, newest first. Each entry has: Question, Decision, Alternatives considered, Why, Predicted outcome, Revisit when. Add a rule to CLAUDE.md: when a decision is made in a session, append an entry without asking me. Never infer a why; write "(needs confirmation)" instead. Never edit a past entry; write a new one linking back to it.
Write daily-check-process.md: a numbered list of every check we can run against production each morning. Start with signups, transactional email volume, deploy failures, queue errors, the error tracker, cancellations, and the support inbox. For each, specify the query and the firing condition, and where a check fires on an absence, specify the second query that distinguishes a fault from nothing being due. Put the deterministic checks in one script. Then write the routine: run the script, perform the rest, file one dated issue with a summary comment, and open a separate issue per real problem.
Add a "Ready to close?" section to CLAUDE.md with these steps. Commit and push this session's changes. List every open item and assign each to one of four states: done and verified; done with judgment calls documented; escalated as one packet with options and a recommendation; or parked with an issue, calendar event, or memory file that will resurface it. A line in the summary does not qualify. Record anything a future session would have to be told again in the appropriate document. Append decisions to DECISION-LOG.md. Save memories for corrections.
Before we launch, use the product in my browser with the public documentation as your only reference. Do not read the code or internal notes during the walk. Follow each documentation page top to bottom, do exactly what it says, and log every step where the result differed from what the documentation implied. Continue until you are blocked, then stop. Classify findings as documentation wrong, product gap, or worked as documented. Finish by stating, in plain language, what the product can now do that it could not before.
Read https://daniellemorrill.com/building/claude-code.md. Then examine this repository and report which of those mechanisms already exist here, which are missing, and which do not apply. Propose an order for adding the missing ones, cheapest and most reversible first. Do not implement anything yet.
Always-on hosts, authentication that outlives a trip, the escalation list, and batched contact.
The commands, and what the pre-flight checklist keeps finding.
The same mechanisms at daily scale.