Factory Log
Why the agent rule sheets say what they say — dated rulings, measurements and incidents moved out of the rules themselves
The coding-rule pages agents read every session hold current rules only. This page holds the reasoning and history those rules used to carry inline, so a rule can be traced to the incident or measurement that produced it without every agent re-reading the story. Add an entry here when a rule changes; never add the story back into the rule sheet.
In plain terms: the office manual says what to do; this is the file of memos that explains why the manual says it.
The rules for the rules (from the retired factory plan, 2026-09-05)
- A rule names its enforcer or its expiry. Every "never" or "must" in AGENTS.md or a coding rule either names the check that enforces it or carries a review date after which it is deleted unless its incident recurred. Memory-only rules cost reading and still get broken.
- Ceremony is budgeted per work item, not per session (Planning Workflow → Ceremony budget).
- Measure before pruning, and after. No cut lands on the argument that it feels heavy; the ledger reading before and after is the evidence.
The plan's ledger readings (first 2026-09-06) and session observations are in
Git history under plans/archived/2026-09-05-software-factory-continuous-improvement.md.
Plans retired — 2026-09-12
Ruling (owner, 2026-09-12). The structural-plan tier is retired. Eleven
active plans were audited against the tree: most of their content had landed;
the live remainder became Backlog tasks or open owner questions in
backlog/decisions/, and the unique reasoning became decision records.
plans/archived/, plans/references/ and plans/deprecated/ stay as
history only. Structural reasoning now goes in a decision record; sequencing
goes in a parent task and its subtasks; interrupted state goes in the task's
notes, never a handoff file.
Verification tiers — 2026-09-12
Ruling (owner, 2026-09-12). Small UI changes were paying for the whole
factory: a component plus story plus screen doc exceeded the two-file quick
fix, so it became a task with authority, label, dated comment, reading line
and final summary, then a mandatory fresh-context review. Verification tiers
now follow blast radius, computed by scripts/verification-rules.mjs, and
both the quick-fix door and the review requirement key off the tier.
Measured before the change, in a 4-core / 15GB cloud container on b3d1c40:
| Check | Time |
|---|---|
| Biome | 3s |
| TypeScript | 18s |
| Steiger and naming | 3s |
| Full unit suite (1,687 tests) | 36s |
| One story file in the browser | 10s wall, 5s of it start-up |
| Full story suite (91 files) | 8–16 min |
Story selection by import graph before the change: a widget edit reached 3 of
91 stories, a page section 7, an entity work bar 11, a shared molecule 62, and
any CSS edit or deleted file all 91 plus the full unit suite. Deleted files now
select nothing (TypeScript proves nobody imports them); CSS edits select one
smoke story per layer plus colocated stories; shared-UI changes run the
nearest twelve stories by import distance at landing and the rest under
--thorough, which is the default in a cloud session.
Frontend edits used to run vitest related across the Node and Convex
projects; the Convex project never imports src/, so it is no longer booted.
Shallow clones. Cloud sessions clone about fifty commits. The traceability
checker verifies deleted-plan slugs against Git history, so every cloud
preflight --close failed with 23 warnings on a clean tree, and one unit test
failed with it. The checker now reports the check as not performed in a
shallow clone instead of failing it.
Verify once, at landing
- 2026-08-16 (owner). The full Storybook browser run boots a headless Chromium per worker and takes 8–16 minutes. It is a pre-push gate, never a per-commit habit; "I touched shared UI" is not on its own a reason to run it.
- 2026-08-23 (owner). Verification is a finalisation phase, not an edit loop. Complete the implementation and story updates, then run the focused tests and gates once. Intermediate runs only to diagnose an unknown failure or when the owner asks for strict test-first iteration. Regression evidence is still collected: reverse the fix, watch the test fail, restore, watch it pass, once at the end.
- 2026-09-08 (owner). A design session spent most of its time re-running
tests. Batch related edits before each typecheck; run
test:changedrather than a hand-picked sweep of story directories; when a story's assertions break, read the whole play function once and fix every assertion in one pass. A leg needing repeated story runs belongs in a cloud session. - 2026-09-12 (owner). During a design round, inspect the changed UI and measure its DOM; batch edits before typechecking; no full suites after each visual adjustment. Superseded in mechanism by the tiers above.
Worker budget
- 2026-08-14 measurement, full story suite (51 files / 267 tests) in a 4-core / 15GB cloud container: 2 workers 973s, 4 workers 460s, identical results. Aggregate test time barely moved (1894s → 1789s): the run was worker-bound, not doing less work.
- 2026-08-16.
vitest.config.tsreads total memory: ≥12GB gets 4 browser workers, below that 2 (the 8-core/8GB laptop also hosts the dev server, Storybook and an editor; over-fanning caused swap thrash misread as five flaky stories). - 2026-09-06. The cap is also never more than cores minus one so the Vite server keeps a core: the 4-core container runs 3.
- GB-88. The Convex project shared the browser projects' CPU during the full gate but not their raised timeout, and failed on fixture setup under contention; its timeout now matches the browser projects.
- GB-229, 2026-09-11. Two uncapped local runs timed out in module-walk and subprocess tests; the same 1,705 tests passed with one worker. Node/Convex pools now default to one worker below 12GB. A failed run followed by the same uncapped retry is not a recovery strategy.
Story runs that fail in milliseconds
Vitest's browser mode, started seconds after an edit, picks up the Vite
reload mid-collection and reports Vite unexpectedly reloaded a test,
Preload assets timed out or a bare TypeError with whole files red in
15–30ms. Every such failure passed on an unchanged re-run. The tell is the
duration: a real assertion failure takes about as long as the story. This
repository has a documented history of sessions chasing story failures that
were never real. Separately, Failed to fetch dynamically imported module
under a running preview is a stale Storybook index in
node_modules/.cache/storybook; a browser reload does not clear it.
Two Storybook servers
bun run storybook serves port 6006 for manual development; the
storybook-preview launch config serves 6007 pinned to this checkout with
--config-dir ./.storybook. Diagnostics against one port say nothing about
the other. A worktree has no node_modules, so bun x resolves binaries
from the main checkout; when a served file and the file on disk disagree,
suspect the wrong checkout before a stale cache.
Biome scope
Biome's exclusions are files another tool owns: TanStack's route tree,
Convex's generated bindings, golden snapshots, the generated agent
instructions and skills-lock.json (two-space indentation from the skills
CLI). A tool-written file left inside the formatter's reach makes the gate
ship red and biome:fix rewrite a file nobody may commit. The upstream
landing zone is the one externally authored case and is disabled through an
override rather than an exclusion.
Git and concurrent sessions
- 2026-08-02. Twice, another session's staged files rode along in a
commit that did not describe them.
run/check-staged-scope.shwarns before a commit that spans three or more areas; it never blocks, because a guard that blocks gets disabled. - 2026-08-08. A guarded push of the shape
git merge-base --is-ancestor origin/main HEAD && git pushsilently short-circuited whenmainhad moved, and was reported as pushed. Confirm a push by reading the remote hash. git stashresets toHEAD, andHEADis a moving target in a shared checkout: a concurrent commit between edit and stash meansstash popcan drop work that was never captured.- A live PID is not evidence of an active edit; compare a file's mtime across two checks.
Convex environments
bunx convex dev --oncehas run in cloud sessions since 2026-09-04 via a deployment-scoped key; details in.claude/README.md.bunx convex logs --historyreturns nothing on this deployment and only tails live;bunx convex datatruncates wide cells. Read an export instead.
Planning ceremony
- Quick fix was two files (until 2026-09-12). The limit came from the coding-rules overview's fast track. It kept escalating three-file UI changes into full tasks; the tier now decides.
- Task filing debt (GB-124). Tasks filed without an authority, label and
dated comment failed
preflight --closefor everyone, every close; the debt reached 22 tasks missing an authority and 33 missing a label before GB-124 cleared it. That is why all three arrive with the task. - Reading line (2026-09-06). Tasks open their Implementation Plan with one line naming the authority and at most two rule sections, so nobody re-reads the manual to find them.
- Review trial (2026-09-06 to 2026-10-06, GB-192). Record whether review was required, requested or omitted and any located defects; unreviewed changes have unknown counterfactual findings, not zero. For the first three ordinary reviews, record candidate, actionable findings, false positives, rounds and observable cost before expanding specialist use.
- Ceremony budget (2026-09-06, R3). Paperwork rides in the work commit; at most one close-out commit per full-plan stage; none for task-plan work.
- Handoffs (GB-192, R4). One audit found a handoff with 3 of 5 items
already landed by concurrent sessions. One archived handoff held the only
record of the stale-Storybook-index trap and had to be rescued first. Mining
eval-results/answered in one pass what repeated runs would only have guessed at: a claimed "fails half the time" was three unrelated populations, two already fixed. - Backlog CLI (GB-129). A standalone
---inside--commenttext made the nexttask edit --commentmis-split the file and silently drop every later comment; two files were repaired.
Cloud sandbox
- 2026-08-14. The pre-installed Chromium revision can disagree with the
Playwright pin;
scripts/link-sandbox-browsers.mjsbridges it in the SessionStart hook. The fix is a symlink, never a committedexecutablePath, which would pin every developer to one machine's layout. - 2026-09-02. A cloud session opened with empty
node_modulesand spent its first gate run discovering why; the SessionStart hook now installs.
Duplicate Copies the Live Draft, Whole and Fresh
Duplicate on any card copies the working draft of that card and everything inside it, clears collected answers and nothing else authored, names the copy with the next free handle and " (copy)", records its lineage, and lands it immediately after the original. A whole document duplicates through the same door that creates one.
Folio About Is Schema-Bound YAML
Every Folio has one schema-bound YAML declaration that Goldie reads and writes and a person can download. Since 2026-09-10 the declaration has no face of its own.