# Wolf Pack — how this platform is built and maintained with AI

A four-component guild platform (Discord bot, website, log-parsing agent on
each user PC, desktop overlay app) built and maintained almost entirely by AI
coding sessions that cannot see each other’s conversations. What holds it
together is written rules — each adopted the day a specific failure made it
necessary — plus a verification gate that runs before anything ships.

- Human view: https://wolfpack.quest/ai
- Structured: https://wolfpack.quest/ai.json
- Repository: https://github.com/davehess/QuarmBossTracker

Authoritative sources (this document is an index into them, not a replacement):
- CLAUDE.md — architecture, release routing, scope boundaries. https://github.com/davehess/QuarmBossTracker/blob/main/CLAUDE.md
- docs/GEMINI-SPARK-HELPER.md — working guide for an agent with repo access. https://github.com/davehess/QuarmBossTracker/blob/main/docs/GEMINI-SPARK-HELPER.md
- docs/AI-CONTRIBUTOR-BRIEF.md — brief for a chat assistant with no repo access. https://github.com/davehess/QuarmBossTracker/blob/main/docs/AI-CONTRIBUTOR-BRIEF.md

Measured 2026-08-23: 2088 commits, 2284 tests across
151 files, 79 documents, 7 decision
records, 21 automatic branch syncs. First commit 2026-04-21.
Counted, not estimated.

CAVEAT: this method suits a repo with two very large single files, few
readers, and consequences that land on a schedule. Adopt the shape; derive
your own rules from your own incidents.

## 1. Standing rules

Each rule states what it requires and the incident that produced it.

### Minimal diff
- Adopted: 2026-07-18
- Rule: Change only what the task requires. If it looks like adjacent code must change too, stop and say so before proceeding.
- Because: The bot is one ~18k-line file and the agent one ~35k-line file, so a small line count is a bad proxy for a small blast radius. Reaching into unrelated behaviour is the structural hazard, not diff size.
- Lives in: CLAUDE.md (https://github.com/davehess/QuarmBossTracker/blob/main/CLAUDE.md)

### Answer "do we already have X?" from the index
- Adopted: 2026-07-19
- Rule: Read docs/HOW-ITS-BUILT.md first, then grep all four surfaces — bot, web, agent dashboard, Mimic. Never conclude "we do not have that" from one file.
- Because: A feature can span four surfaces. A single grep produced a confident "we do not have that" for the eqclient/Zeal "Set up for me" writer, which already existed. The failure-prone direction is always the negative answer.
- Lives in: CLAUDE.md (https://github.com/davehess/QuarmBossTracker/blob/main/CLAUDE.md)

### Decisions get written down, same session
- Adopted: 2026-08-08
- Rule: When the guild lead makes a call — a default, a threshold, a policy, a "we do not do that" — append it to docs/DECISIONS-<date>.md before the session ends, with the reasoning and where it landed.
- Because: Sessions cannot share a conversation and a container reset takes the scratchpad with it. A decision that lives only in chat is lost. Retrieval was never the weak link — writing was.
- Lives in: CLAUDE.md (https://github.com/davehess/QuarmBossTracker/blob/main/CLAUDE.md)

### Shipping updates the docs in the same change
- Adopted: 2026-08-11
- Rule: A change updates its ledger entry and design doc in the same commit, and again when it graduates from beta to stable. If the doc edit is not in the diff, the ship is not done.
- Because: A ledger that lags its code made the recall tool report a feature as "blocked on the call" the day after it shipped.
- Lives in: CLAUDE.md (https://github.com/davehess/QuarmBossTracker/blob/main/CLAUDE.md)

### Attribution is explicit
- Adopted: 2026-08-09
- Rule: Most requests here come from one person under several character names, and are credited to them — but the list of those names is checked with them rather than inferred, and anyone not on it keeps their own name.
- Because: The list was wrong for three weeks: it carried a name belonging to a different member, and was missing one that did belong. A rule that folds many names into one person fails by erasing other people's contributions, and it fails silently — a wrong credit reads exactly like a right one, and only the person themselves ever notices. So an unrecognised name now keeps its own credit by default.
- Lives in: CLAUDE.md (https://github.com/davehess/QuarmBossTracker/blob/main/CLAUDE.md)

### Raid-night deploy freeze
- Adopted: 2026-07-13
- Rule: No pushes to main on Sun/Wed/Thu between 19:30 and 00:30 Eastern. Mid-raid fixes ship with [hotfix] in the commit message.
- Because: A push restarts the production surfaces the raid depends on. Mid-raid restarts amplified a queue backup and announcer spam on 2026-07-13.
- Lives in: .github/workflows/raid-freeze.yml (https://github.com/davehess/QuarmBossTracker/blob/main/.github/workflows/raid-freeze.yml)

### Fail open
- Adopted: 2026-07-18
- Rule: On missing data, an unknown value, or a dependency being down, degrade to safe defaults. Never crash, never hide data that exists.
- Because: The consumers are raiders mid-pull. A surface that disappears is worse than one that shows less.
- Lives in: docs/AI-CONTRIBUTOR-BRIEF.md (https://github.com/davehess/QuarmBossTracker/blob/main/docs/AI-CONTRIBUTOR-BRIEF.md)

### Private channels never leave the machine
- Adopted: 2026-05-30
- Rule: Officer chat, tells, group and custom channels are dropped at byte level before parsing, on the user PC. Every log-derived stat declares a visibility scope, and opt-out flags are honoured by every consumer.
- Because: The agent reads a player’s whole log file. Filtering after upload would mean the data had already left, so the filter has to run before the parse.
- Lives in: docs/PRIVACY.md (https://github.com/davehess/QuarmBossTracker/blob/main/docs/PRIVACY.md)

### Never deduplicate a per-observer stream
- Adopted: 2026-07-20
- Rule: Live state, threat, casting, target-casts and encounter uploads are per observer. Each one is a distinct fact and is merged, never collapsed.
- Because: Two agents reporting the same fight are two viewpoints, not a duplicate. Collapsing them silently discards the coverage that makes merged parses work.
- Lives in: docs/AI-CONTRIBUTOR-BRIEF.md (https://github.com/davehess/QuarmBossTracker/blob/main/docs/AI-CONTRIBUTOR-BRIEF.md)

### Count adoption in players, never characters
- Adopted: 2026-08-16
- Rule: Fleet and adoption numbers count distinct uploading humans, each at their most recent version.
- Because: One person runs 3–12 characters, so character counts inflate roughly tenfold: a "178 characters on the new build" fleet was 16 people.
- Lives in: CLAUDE.md (https://github.com/davehess/QuarmBossTracker/blob/main/CLAUDE.md)

### One paginated reader per runtime
- Adopted: 2026-08-16
- Rule: All database reads go through a single paginator per runtime. A second paginator, or a new over-cap .limit(), fails CI.
- Because: PostgREST silently caps every response at 1000 rows, and asking for 5,000 does not lift it — the cap is applied on top, so you get a short array and a success code. The same footgun was rediscovered independently four times, each rediscovery writing its own paginator.
- Lives in: test/db-read-discipline.test.js (https://github.com/davehess/QuarmBossTracker/blob/main/test/db-read-discipline.test.js)

### Postgres is the home; Discord is a projection
- Adopted: 2026-08-16
- Rule: No new durable state goes into Discord messages or the local state file. Postgres holds it; Discord renders it.
- Because: The state file does not persist across deploys, and treating Discord as a source of truth once posted the same raid review eleven times in one night.
- Lives in: CLAUDE.md (https://github.com/davehess/QuarmBossTracker/blob/main/CLAUDE.md)

### When main gets something, beta gets it too
- Adopted: 2026-08-10
- Rule: A workflow merges main into beta on every push to main. Real conflicts fail the run loudly instead of auto-resolving.
- Because: Nothing flowed main to beta for months. Beta drifted 79,199 lines behind, carried 35 test files against main’s 90, and a priority-one parser bug rode through nine agent releases unseen because the test that would have caught it did not exist on that branch.
- Lives in: .github/workflows/sync-beta.yml (https://github.com/davehess/QuarmBossTracker/blob/main/.github/workflows/sync-beta.yml)

### Deployment decisions are recorded as they are made
- Adopted: 2026-08-12
- Rule: Any decision that changes how the platform is deployed, what it stores, or what it costs to run gets a line in the self-host design doc at the time it is made.
- Because: The goal is a wizard that stands the whole platform up for another guild. It can only be built from decisions recorded as they happened; a choice captured only in a runbook is written for one specific box.
- Lives in: CLAUDE.md (https://github.com/davehess/QuarmBossTracker/blob/main/CLAUDE.md)

### Every control exists on more than one surface
- Adopted: 2026-08-19
- Rule: A control that ships in the tray menu ships on the dashboard in the same change, driving the same internals — never a parallel path.
- Because: A control that exists in only one place is a control people forget exists. Per-character layout saves sat tray-only for four minor versions before the guild lead discovered them.
- Lives in: CLAUDE.md (https://github.com/davehess/QuarmBossTracker/blob/main/CLAUDE.md)

### Any assistant can pick up the work
- Adopted: 2026-08-23
- Rule: Two onboarding docs are maintained: one for an assistant with repo and shell access, one for a chat with neither. Each says which it is and points at the other.
- Because: Sessions run on different tools and cannot share a conversation. What is not written down for the next one does not survive.
- Lives in: docs/GEMINI-SPARK-HELPER.md (https://github.com/davehess/QuarmBossTracker/blob/main/docs/GEMINI-SPARK-HELPER.md)

## 2. The task workflow

### 1. Load the committed context
CLAUDE.md (architecture and rules; outranks the README), docs/STATUS.md (ledger and queue), docs/HOW-ITS-BUILT.md (feature → file index), then the newest docs/DECISIONS-*.md. Then the design or runbook doc for the area you are touching.

Guards against: Sessions cannot share a conversation. Anything not in a committed file does not reach the next one.

### 2. Find the real code before deciding it does not exist
A feature can live on four surfaces and often spans several: the bot, the web app, the agent dashboard, and the desktop overlays. Check the index, then grep all four.

Guards against: The negative answer is the failure-prone one. It has been wrong before.

### 3. Route the change
What you touched decides both the branch and which version file moves. Versions live in package.json and nowhere else.

- IF the Discord bot and its HTTP API → main · bump the root package.json
- IF the website → main · bump web/package.json
- IF the log agent → beta · bump the agent package.json only
- IF the desktop app → beta · its version stays parked; the release workflow increments the prerelease tag
- IF a database migration → main, as a timestamped idempotent file — and if the column is needed now, apply it AND commit the identical file
- IF documentation only → main · no version bump

Guards against: A change spanning bot and agent is two commits on two branches. Cherry-pick between them; never merge the whole branch, because the beta branch carries a deliberately-ahead version and in-flight work.

### 4. Check the clock before touching production
Raids run Sunday, Wednesday and Thursday evenings Eastern. The freeze window is 19:30 to 00:30.

- IF outside the window → ship normally
- IF inside it, and something is broken right now → ship with [hotfix] in the commit message
- IF inside it, and it can wait → stage on a working branch and land it after midnight

Guards against: A push restarts the production surfaces the raid depends on.

### 5. Make the smallest change that does the job
Extract the decision into a small pure module where you can; leave the plumbing in place. Fail open on missing data. Never collapse a per-observer stream.

Guards against: Two files here are 18k and 35k lines. Line count is a bad proxy for blast radius.

### 6. Prove it
Pure module → import and test it directly. Logic trapped in a monolith → slice the real function out of the shipped file and evaluate it, so the test tracks shipped code. A rule rather than a function → a guard test, and read the header of any guard test that goes red before changing it.

Guards against: Use real fixtures. An invented fixture that is subtly unlike production has shipped bugs here.

### 7. Run the whole gate
All of it, not the parts that seem relevant. Two of these are not in CI, which means the only other place they fail is the deploy.

```
npm run lint
npm run check:dashboard
npm test
cd web && npx tsc --noEmit   # if the web app changed
npm run golden:check         # if the log parser changed
```

### 8. Write the docs into the same commit
The ledger entry, the feature index row, the design doc, a dated decision record for any call that was made, and a plain-language release note for anything user-facing. Deployment-shaped decisions also go in the self-host design doc.

Guards against: If the doc edit is not in the diff, the ship is not done. A ledger that lags its code once reported a shipped feature as blocked.

### 9. Commit, push, and say what actually happened
Commit subject is "<component> vX.Y.Z — short reason"; the body explains why, not which files. Push to the branch the routing step named. Then report honestly: failures with their output, skipped scope named as skipped, uncertainty left open stated as uncertainty.

Guards against: Containers here come up shallow and with stale refs. Fetch and check the real remote head before concluding work was lost, and never force-push on a stale ref.

## 3. The verification gate

- `npm run lint` — blocks CI. A single no-undef tripwire over the two monoliths. An undeclared global in an 18k-line file throws only when that line executes; this catches it statically. Deliberately the only rule — the value is that it is zero-noise.
- `npm run check:dashboard` — blocks CI. The agent dashboard is one template literal with two layers of escaping; one wrong character blanks the whole page with no partial degradation. This parses every script block it emits, checks a required helper on every collapsible element, and fails if an embedded copy of an overlay has drifted from its source file.
- `npm test` — blocks CI. 151 files, 2,284 tests. Includes guard tests that hold a rule rather than cover a function — the read-layer ratchet, workflow validity, the expansion lock, and the parser golden.
- `cd web && npx tsc --noEmit` — NOT in CI, run it yourself. Type errors in the web app. Not in CI — the only other place this fails is the deploy, i.e. after the push.
- `npm run golden:check` — NOT in CI, run it yourself. That the committed parser expectations still describe the parser. Changing them on purpose means regenerating and reading every changed number, because each one is a change in what the raid sees.

## 4. How the work is written

### Direct push, not pull requests
This repo ships by pushing to a branch, not by opening pull requests, so commits are the durable unit of history. Bot, web and docs go to main; the desktop app and its bundled log agent go to a beta branch and graduate by file-level promotion, never by merging the whole branch. Version numbers live in package.json and nowhere else — never in a document.

### Write the reason, not the change
Every rule here records the incident that produced it. "The threshold is 0.5" is worthless in six weeks; "0.5 because our raids measure 0.75–0.89 and outside raids 0.14–0.22, and it matches the constant the other surface already uses" is the whole point. Comments in this codebase carry reasons, not descriptions.

### Tests are the record of why
Tests are named after the behaviour that was asked for and quote the request. Pure logic is extracted into small modules so it can be tested directly. Logic trapped inside a monolith is tested by slicing the real function out of the shipped file and evaluating it, so the test tracks the shipped code and fails loudly if the function is renamed.

### Report honestly
If a test failed, say so with the output. If part of the scope was skipped, say which part and why. Scaling work down is the requester’s call, not the assistant’s. Uncertainty that was not closed gets stated as uncertainty — several rules here exist because a confident wrong answer was cheaper to give than a checked one.

### Verify git state before concluding anything is lost
Cloud containers here come up shallow and with stale refs, so local branch pointers can look dozens of commits behind while the remote is fine. Fetch and check the real remote head before concluding work vanished, and never force-push on the basis of a stale ref.

## 5. Adoption timeline

Oldest first. Every entry is a real commit; this repo ships by direct push,
so commits rather than pull requests are the durable unit of history.

### 2026-04-25 — The instructions become a file
What forced it: The project was a single Discord respawn timer. Context for each session was retyped by hand, so it varied every time.
What changed: CLAUDE.md is committed alongside the deployment config — one file describing the architecture and the rules, versioned with the code it governs.
Measured after: Every rule below is an edit to this one file. It now outranks the README where they disagree.
Commit: 44ca9487 Add Railway deployment config and CLAUDE.md — https://github.com/davehess/QuarmBossTracker/commit/44ca9487

### 2026-05-30 — Privacy becomes a constraint, not a feature
What forced it: The agent reads a raider’s entire EQ log — officer chat, tells, group, custom channels. Filtering after upload would mean the data had already left their machine.
What changed: A written privacy statement, and byte-level filtering before the parse so private channels never enter the pipeline. Visibility scopes and opt-out flags become mandatory on every log-derived stat.
Measured after: Later exceptions are narrow and documented: a single carve-out for public "Hail" lines was added for expansion flagging, in the privacy doc and on the member-facing page.
Rules introduced: Private channels never leave the machine
Commit: b1009ecf privacy statement draft + QoL north star + PoP flagging in queue — https://github.com/davehess/QuarmBossTracker/commit/b1009ecf
Commit: d3c9d9a6 build: guard against the agent-dashboard escape bug (blank-page class) — https://github.com/davehess/QuarmBossTracker/commit/d3c9d9a6

### 2026-07-07 — A feature index, so the answer is not one grep
What forced it: A session answered "we do not have that" about a feature that already existed, because it checked one of the four surfaces a feature can live on.
What changed: HOW-ITS-BUILT.md maps every feature to its file and surface, and the rule becomes: read the index before answering a does-this-exist question. Twelve days later the rule is written into CLAUDE.md explicitly.
Measured after: Now 79 documents. A stale index causes exactly the wrong negative answer, so shipping a feature refreshes its row.
Rules introduced: Answer "do we already have X?" from the index
Commit: 9dbb9a07 docs — HOW-ITS-BUILT.md: per-feature 'how X is built' reference — https://github.com/davehess/QuarmBossTracker/commit/9dbb9a07
Commit: 05bd892d docs — feature-index discipline: CLAUDE.md rule for 'do we have X' questions — https://github.com/davehess/QuarmBossTracker/commit/05bd892d

### 2026-07-13 — The deploy freeze
What forced it: A mid-raid deploy restarted production surfaces during a raid, amplifying a queue backup and announcer spam.
What changed: No pushes to main inside the raid window. A workflow turns any such push red — advisory, because Railway and Vercel deploy on push regardless. [hotfix] is the escape hatch, because a mid-raid fix is exactly what should ship mid-raid.
Measured after: A tripwire, not a gate. The enforcement that matters is in the instructions; the workflow catches everyone who has not read them.
Rules introduced: Raid-night deploy freeze
Commit: 2f9c32c6 bot v3.0.167 — pre-raid health check, raid-night deploy freeze, mid-raid shed switches — https://github.com/davehess/QuarmBossTracker/commit/2f9c32c6

### 2026-07-17 — One ledger instead of scattered queues
What forced it: Work-in-flight was tracked in several competing documents, so no single one could be trusted.
What changed: STATUS.md becomes the single index: done, queued, abandoned, and items that need a session with local-machine access.
Commit: 0225ad10 docs: consolidate scattered queues into STATUS.md + archive superseded — https://github.com/davehess/QuarmBossTracker/commit/0225ad10

### 2026-07-18 — The first real gates
What forced it: An undeclared global shipped and took a surface down. In an 18k-line file, that class of bug throws only when the offending line executes.
What changed: A deliberately narrow lint tripwire (one rule: no-undef, nothing stylistic), a blocking CI workflow, and the first characterization tests. The minimal-diff rule enters CLAUDE.md the same week.
Measured after: 6 test files at introduction. 151 files and 2,284 tests today.
Rules introduced: Minimal diff; Fail open; Never deduplicate a per-observer stream
Commit: 243888e8 bot v3.0.204 — ESLint no-undef gate + blocking test workflow — https://github.com/davehess/QuarmBossTracker/commit/243888e8
Commit: 7d91a4dc bot v3.0.205 — vitest characterization suite + CI test step — https://github.com/davehess/QuarmBossTracker/commit/7d91a4dc
Commit: 944a87f1 docs — commit .claude settings + minimal-diff rule — https://github.com/davehess/QuarmBossTracker/commit/944a87f1

### 2026-07-20 — Onboarding for an assistant that cannot read the repo
What forced it: Work was being drafted in chat tools with no repo access, against guesses about the architecture.
What changed: A self-contained brief with the architecture, the guardrails and the review bar, written to be pasted into a fresh chat.
Commit: d6786ade docs — AI-CONTRIBUTOR-BRIEF.md: self-contained brief for chat AIs that can't browse the repo — https://github.com/davehess/QuarmBossTracker/commit/d6786ade

### 2026-08-02 — A regression net for the numbers the raid sees
What forced it: The log parser produces every figure on a parse card, a damage meter and a kill credit. A silent regression there is forty raiders with wrong numbers on a Sunday night.
What changed: A committed synthetic log is replayed through the shipped parser and diffed against a committed known-good result. Changing it on purpose means regenerating and reading every changed number.
Measured after: The privacy assertions read the live parser rather than the expectation file, so regenerating the golden can never launder a privacy hole.
Commit: 8dac1be5 test: #75 golden-log CI + pre-raid drill (agent parser regression net) — https://github.com/davehess/QuarmBossTracker/commit/8dac1be5

### 2026-08-08 — Project memory
What forced it: Sessions run on different machines and cannot share a conversation. Decisions made in chat were being re-litigated a week later.
What changed: Dated decision records, a session-start digest that prints open items and live versions, and a recall command that answers questions from the committed docs with citations.
Measured after: 7 decision records so far. Retrieval was never the weak link — the writing discipline is what this milestone bought.
Rules introduced: Decisions get written down, same session; Shipping updates the docs in the same change
Commit: 45629403 docs — project memory: decision records, a SessionStart digest, and /recall — https://github.com/davehess/QuarmBossTracker/commit/45629403

### 2026-08-09 — Attribution stops being guesswork
What forced it: Credit was being inferred from names in code comments — which is exactly how the wrong credits got there in the first place.
What changed: One rule with one explicit exception, and a named table of who the exception covers. Character names in fixtures are excluded from the rule by construction.
Rules introduced: Attribution is explicit
Commit: 6dae2bdd attribution — everything is Hitya unless it came through the feedback form — https://github.com/davehess/QuarmBossTracker/commit/6dae2bdd

### 2026-08-10 — Branch parity becomes automatic
What forced it: Nothing had ever flowed from main to beta. Beta reached 79,199 lines behind and carried 35 test files against main’s 90 — which is why a priority-one parser bug rode through nine agent releases unseen.
What changed: A workflow merges main into beta on every push to main. Only two deliberately-ahead version files are excluded; any other conflict fails the run loudly rather than picking a side.
Measured after: 21 automatic syncs so far. The earlier practice — re-syncing by hand at each release — was a snapshot, not a link, and main moves 12–42 commits a day.
Rules introduced: When main gets something, beta gets it too
Commit: e0ee9cd7 ci — when main gets something, beta gets it too — https://github.com/davehess/QuarmBossTracker/commit/e0ee9cd7

### 2026-08-12 — Deployment decisions are written for the next deployment
What forced it: Retention windows, hosting mix and Discord layout were being decided per-choice and recorded, if at all, in runbooks written for one specific machine.
What changed: Every decision that changes how the platform is deployed, what it stores, or what it costs to run gets a line in the self-host design doc at the moment it is made.
Measured after: Retention is treated as a hosting-bill question, not a data-modelling one — hosted storage bills on egress, an on-premises box costs electricity, and the answer differs per deployment.
Rules introduced: Deployment decisions are recorded as they are made
Commit: d017c3b3 scripts+docs — the local box becomes an archive that never loses history — https://github.com/davehess/QuarmBossTracker/commit/d017c3b3

### 2026-08-16 — A ratchet instead of a ban
What forced it: PostgREST silently caps every response at 1000 rows. The same footgun was rediscovered four separate times, and each rediscovery wrote its own paginator — three paginators for one problem.
What changed: One paginator per runtime, enforced by test. Existing over-cap call sites are counted into a baseline that may only shrink: converting one lowers it, adding one fails CI.
Measured after: A ratchet rather than a ban because 85 pre-existing sites could not be converted blind — each needs its ordering key checked by hand.
Rules introduced: One paginated reader per runtime
Commit: 92447856 bot v3.1.49 + web v1.1.58 — one paged reader per runtime, award identity in the schema — https://github.com/davehess/QuarmBossTracker/commit/92447856

### 2026-08-16 — Metrics have to be honest to be useful
What forced it: Adoption was being reported in characters. One person runs 3–12, so a "178 characters on the new build" fleet was 16 people — a tenfold overstatement in every graduation argument.
What changed: Adoption counts distinct humans. In the same period, durable state stops going into Discord messages: Postgres is the home, Discord is a projection.
Rules introduced: Count adoption in players, never characters; Postgres is the home; Discord is a projection
Commit: 9b579015 docs — fleet counts are PLAYERS not characters (Hitya’s rule) — https://github.com/davehess/QuarmBossTracker/commit/9b579015

### 2026-08-19 — A control in one place is a control nobody finds
What forced it: Per-character layout saves existed for four minor versions in the tray menu only. The guild lead discovered them by accident.
What changed: Anything available from the tray is available from the dashboard in the same change, driving the same internals rather than a parallel path.
Rules introduced: Every control exists on more than one surface
Commit: 99f48a98 web v1.1.71 + docs — tray/dashboard parity rule, ledger + roadmap for the parity batch — https://github.com/davehess/QuarmBossTracker/commit/99f48a98

### 2026-08-23 — The method becomes portable
What forced it: A different assistant was going to work in this repo, and would get none of the standing instructions automatically — no session-start hook, no accumulated conversation.
What changed: A working guide for any agentic session with repo access: boot order, the per-task loop, branch routing, the full verification gate including the two checks CI does not run, the three test tiers, and the footguns that have each already shipped a bug. Then this page, so the method is legible without a checkout at all.
Rules introduced: Any assistant can pick up the work
Commit: 4dd1385b docs — a working guide for non-Claude agentic sessions (Gemini Spark) — https://github.com/davehess/QuarmBossTracker/commit/4dd1385b
