Repository navigation
Description
Problem
AGENTS.md loads into every agent session, so its length is a cost paid on every
run. #921 (PR #925) took it from 9,677 to 8,603 words by rewriting prose, and
that lever is now spent — 50 of 96 rules were rewritten and further wordsmithing
would be scraping.
What remains is structural:
| words | share | |
|---|---|---|
| rule paragraphs | 7,488 | 87% |
— of which, rules that already carry a → decision record |
4,689 | 54% |
| — rules with no record | 2,799 | 32% |
| headings, code blocks, layout, prose | 1,115 | 12% |
Over half the file is narrative that already has a home. The file's own
preamble says a → link means "the narrative is behind that link in
docs/decisions/", yet those 48 rules average ~98 words each and carry the
reasoning inline as well as linking to it. The records are substantial, not
stubs: 530-multi-farm-tenancy is 4,079 words, 782-ci-job-gating 2,012,
271-single-serving-instance 1,479, 846-adapter-reach-ratchet 1,473.
This is not a repeat of #413, which moved rationale into the records. This
removes what stayed behind afterwards.
Scope
For each of the 48 rules carrying a → link, answer one question and edit to
match the answer:
What is the minimum an agent must know to avoid breaking this rule, without
opening the record?
Keep that inline. Move the rest into the record the rule already links to.
What must stay inline — this is the entire risk
An agent who never opens the record must still be unable to break the rule.
These never move, however long they leave the paragraph:
- the imperative — what to do and what never to do, including the negative form
- every identifier: command, path, file, env var, config key, class, test name
- every exception and opt-out (
Database:AllowInsecureConnection=true,
RateLimiting:AllowNoTrustedProxies=true, theIdentityProviderexemption, …) - every accepted risk and statement of what is not guaranteed —
"Nothing enforces this.", "only review catches them", "neither survives a merge
tomain", "never fail-open". A rule that loses its limit reads as a
stronger guarantee than the repo actually offers, which is worse than a
long rule. - any counter-example or wrong-fix warning that heads off a plausible mistake
What moves is incident narrative: which review round found it, what the earlier
broken version did, the measurement behind the decision.
Target
~6,300 words (−27%) if linked rules land near 50 words each. This is a
prediction, not a quota. A rule needing 150 words to stay safe keeps them, and
the PR names which and why. Going materially below ~6,300 means deleting rules,
which is a separate decision.
Acceptance criteria
- All 48 linked rules reviewed; the PR reports how many were shortened and
how many deliberately left, with reasons for the latter - Zero loss of decision links, issue references, runbook links, backticked
identifiers, exceptions, accepted risks or negative prohibitions —
verified by count before and after, not by reading - Text removed from a rule lands in that rule's record; nothing is dropped
outright without saying so in the PR - Each edited record still reads coherently after the insert
- Every rule remains one paragraph
- PR records before/after word counts for
AGENTS.mdand for every record
that received text - No rule changes meaning, and docs: /deslop AGENTS.md without weakening its rules #921's "do not track issue state here" rule
is respected - PR body stays short: this repo squashes with the PR title and a blank
body, and long accounting belongs in a PR comment
Non-goals
- Deleting rules or relaxing any policy
- Touching the 49 unlinked rules — with no record to move text into, shortening
those is deletion, and they carry the least written history - Creating new decision records
- Hitting a word target at the cost of an accepted risk
Known follow-ups already identified
Three code-versus-doc mismatches were found during #921 and deliberately left,
because each needs a precision edit rather than a deletion. Fix them here or
split them out:
gh pr editis documented as failing with a Projects-classic GraphQL
deprecation error. It works — tested on PR feat(web): redesign Sales with a MUI order desk and settlement rail #927, exit 0, gh 2.101.0.- Developer experience: add an Aspire AppHost for local orchestration and observability #565 says the AppHost is "deliberately not in CI";
ci.yml:189runs
- group: apphostas a matrix leg. The claim is really aboutaspire run's
config wiring, notAppHost.Tests, so the wording needs narrowing. - Sim harness (#243) rotted silently: 4 breakages, no CI ever ran it #370 says the simulation harness is "deliberately not in CI";
e2e-smoke.ymlandk6-baseline.ymlboth drivetools/simulation. The claim
is really about the boot-guard harness (bootstrap.sh,
docker-compose.sim.yml,verify-harness.sh), and Daily entry: require grading to reconcile sellable eggs before submit #394 in the same file
already says the Playwright specs run on every PR — so the file currently
contradicts itself.
Worklist
All 48 linked rules, heaviest inline first.
| words | decision record | rule |
|---|---|---|
| 284 | 530-multi-farm-tenancy |
Multi-tenancy: every tenant-owned entity has AccountId, enfo… |
| 272 | 782-ci-job-gating |
A documentation-only PR skips web and image, and the gate … |
| 219 | 732-farm-code-rename |
A farm code changes only through Account.Rename, reached by … |
| 213 | 846-adapter-reach-ratchet |
Every adapter declares its module reach, and shrinking it is f… |
| 209 | 271-single-serving-instance |
Do not extend that list from memory. Two previous derivations … |
| 200 | 775-ci-test-matrix |
The four test projects are legs of one fail-fast CI matrix (#7… |
| 188 | 819-business-record-chronology |
Business dates, row timestamps and list sequences are separate… |
| 174 | 688-i18n-help-label-pairing |
Help prose naming a control follows that control's LABEL, per … |
| 157 | 280-seed-and-simulation |
Seed / simulation data is never boot-seeded (#280, #284, #279)… |
| 157 | 514-module-ledger |
Declare cross-module references in the module ledger (#514/#84… |
| 145 | 843-adapter-tiers |
Declare a tier's otherwise-unavailable module privilege before… |
| 143 | ? |
Key a registry by what the code says about itself, never by wh… |
| 128 | ? |
AuditEvents carries a durable, GLOBAL monotonic ordering key… |
| 117 | 394-write-contract-callers |
A write-contract change must update its non-CI callers (#394).… |
| 108 | 565-aspire-local-orchestration |
Run the whole stack under Aspire: aspire run from the repo r… |
| 103 | 845-table-owners |
Every mapped table has one ledger owner (#514/#845). `TableOwn… |
| 96 | 776-backend-coverage |
Backend coverage is measured, not gated (#776). `tools/coverag… |
| 93 | 277-spa-e2e |
SPA E2E lives in tools/simulation/ui/ (#277/#385). Playwrigh… |
| 90 | 579-suspension-issuance-window |
Suspension is immediate for use, not for issuance (#579). … |
| 89 | 264-farm-timezone |
Farm timezone + tzdata/ICU (#264). A farm may be given its IAN… |
| 87 | 847-seam-surface-guard |
No persistence type crosses an Application seam (#514/#847). P… |
| 82 | 565-aspire-local-orchestration |
A new required config key must also be taught to the AppHost (… |
| 80 | 848-generated-coupling-matrix |
The coupling matrix is generated from the ledger and its three… |
| 79 | 729-owner-only-farm-configuration |
Farm configuration and identity are Owner-only (#729). GET a… |
| 67 | 269-transient-db-retry-boundary |
Transient-DB retry stops at unreplayable work (#269). Use `Ena… |
| 67 | 364-credential-epoch-revocation |
Credential epoch revocation (#364). Every access/refresh token… |
| 66 | 407-migration-freeze |
InitialCreate is frozen; one migration per change (#407). EF… |
| 66 | 370-sim-harness-boot-guards |
A new Production boot guard must be taught to the sim harness … |
| 65 | 260-proxy-trust |
Proxy-trust boot guard (#260). HSTS (#144) and the per-IP logi… |
| 65 | 347-process-role |
Process role, not statement order (#347). Declare a boot guard… |
| 64 | 261-postgres-tls-floor |
Production Postgres TLS floor (#261/#262). In Production the e… |
| 61 | 283-migrations-base-provisioning |
Base reference data ships as guarded raw-SQL migrations (#283)… |
| 60 | 510-jwt-key-boot-check |
Both JWT keys are checked at boot, and the check is serving-on… |
| 58 | 500-audit-actor |
Nothing writes an audit event without an actor (#500). `AuditW… |
| 57 | 404-production-logs |
Production logs: compact JSON on stdout; base layer argument-f… |
| 53 | 265-break-glass-recovery |
Break-glass: recover-admin (#265). Same run-then-exit shape … |
| 52 | 332-gss-kerberos |
GSS/Kerberos negotiation off by default (#332). `PostgresConne… |
| 47 | 263-migrate-command |
Migrate command + prod migration split (#263). `dotnet Cluckwo… |
| 47 | 283-first-run-admin-provisioning |
First-run admin: bootstrap-admin (#283). Creates an Owner wi… |
| 45 | 266-container-health-probe |
Container health probe: the healthcheck verb (#266). Before … |
| 44 | 505-audit-events-no-time-partition |
AuditEvents is not time-partitioned, on purpose (#505). The … |
| 36 | 318-design-time-migration-connection |
The design-time connection is fail-closed (#318). `AppDbContex… |
| 35 | 267-container-hardening |
Container image hardening (#267). Runtime stage runs non-root … |
| 34 | 417-schema-docs |
Schema docs are generated, committed, and regenerated with eve… |
| 31 | ? |
The SPA egg loop runs end to end (#13): daily entry (by grade)… |
| 26 | ? |
Run full stack (prod-like): `docker compose -f deploy/docker-c… |
| 16 | ? |
Run frontend dev: cd web && npm run dev → http://localhost:5… |
| 14 | ? |
Every rule here is one paragraph. A → link points to its his… |
Activity
docs: correct three false claims about CI coverage and gh pr edit
mforce commentedon Sep 25, 2026
The narrative move was built, reviewed, and dropped
PR #956 implemented this issue as written: for each of the 47 rule paragraphs
carrying a
docs/decisions/link, keep inline the minimum an agent needs toavoid breaking the rule, and move the incident narrative into the record it
already links to. It reached 8,705 -> 8,392 words (-3.6%), well short of the
~6,300 prediction, because the issue's own "what must stay inline" list already
covers most of the linked-rule text.
Two Codex review rounds found 10 rules that lost a load-bearing statement.
Round 1, both P1:
#819lost "Legacy audit backfills use only exact whitelisted row events,falling back to the documented unknown sentinel", an instruction for any future
backfill.
#845lost "Insights owns no tables". The table-owner scanner accepts a newInsights table the moment someone declares it under that owner, so this is a
design constraint with no guard behind it.
Round 2, all eight P1:
which the account-scoped unique indexes implement, and which the Phase context
line "identity is per-account email" does not state;
extraction, not
includes()or stem matching;AdapterTier.KnownSurfaces, whichis where
privilegeis validated;TableOwnerRealModelTests;AdapterReachRealTreeTests;SeamSurfaceRealAssemblyTests;843record, that "adding either makes that description stale, notred" is itself false, because
AdapterTierRealTreeTests.McpTierRow_IsDormantTodayasserts the row stays dormant.
A token-level preservation check passed all ten, because a dropped constraint
SENTENCE carries no unique identifier. A sentence-level audit built after round 1
caught the round 1 pair on replay, but the pattern had already cost two rounds.
The owner decided to ship only the corrections. Losing a rule silently
re-enables a shipped defect, and about 3% of a file is not worth that. PR #956 is
rebuilt to carry three genuine corrections and nothing else. Its
AGENTS.mddifftouches 4 of 252 lines, and every other rule paragraph is byte-identical to
main.This issue stays open for the owner to close as not planned, or to re-scope. If
it is re-scoped, the useful shape is probably per-rule and reviewed one rule at a
time, not a sweep: every sweep so far has been measured in rules lost rather than
words saved.
Update: the
#843finding is now fixed in PR #956, so it needs no separateissue. The rule said of the dormant
Cluckwork.Api.Mcptier row that "addingeither makes that description stale, not red".
AdapterTierRealTreeTests.McpTierRow_IsDormantTodayassertsreport.Dormantcontains
Cluckwork.Api.Mcp, so mappingMapMcpor adding a tool type under thatnamespace turns CI red. The rule now says so and tells whoever activates the
tier to drop that assertion in the same commit.
843-adapter-tiers.mdcarried thesame claim as "goes stale (not red)" and is corrected too. The owner approved
adding it to #956 after this comment was first posted.
mforce commentedon Sep 25, 2026
Closed as not planned (owner decision, 2026-09-26). The narrative move was tried in PR #956. Two review rounds found ten rules it dropped, against a saving of about 3%, so the owner decided to ship only the four factual corrections (merged in #956).