Skip to content

AI Workflow

Halls and Rooms: How to Structure AI Context Files That Don't Rot

A Knowledge Tree, Not a Wall of Text

🏛️ 🚪 🧹

Every AI coding setup hits the same wall. You start with a small project-rules file, it works, so you add to it. Six months later it's 4,000 lines, and the model follows roughly none of it.

The instinct is that the model is ignoring you. It isn't, exactly — you've handed it a document where every line has equal weight and none of it is scoped to the task at hand. Here's the structure that fixed it for me, and the maintenance problem nobody warns you about.

Why the flat file fails

Three separate failure modes, all of which look like "the model ignored my rules":

Dilution. A rule about button padding sits beside a rule about database migrations. Neither is more prominent, so the model can't tell which matters for the current edit. Signal-to-noise collapses as the file grows.

Cost. That file loads on every single session, and re-enters the billed context on every turn. You're paying continuously for payment-webhook rules while editing a marketing page.

Rot. A file that big is impossible to keep accurate, and — this is the important bit — outdated docs are worse than no docs, because they actively mislead rather than merely failing to help.

The structure: halls and rooms

Halls are one file per domain. Rooms are sections inside a hall covering one specific component or flow. The root file is an index and nothing else — it says what exists and when to load it.

CLAUDE.md                      # index only. loads every session. ~40 lines.
halls/
  general-app-design.md        # rooms: Buttons, Cards, Sliders, Modals
  databasing.md                # rooms: Users, Licenses, Templates, Audit
  payments.md                  # rooms: Checkout, Dunning, Webhooks
  controller-input.md          # rooms: MIDI Mapping, Latency, Device Detect

And the root is genuinely just pointers:

# Project — index

## Halls (load on demand)
- halls/general-app-design.md — any UI component work
- halls/databasing.md         — schema, queries, RLS
- halls/payments.md           — checkout, subscriptions, webhooks

## Always true
- Tailwind 3.3.3, not 4. Don't assume v4 features.
- Commit to main. Never branch.

Root loads every session. Halls load only when the task touches that domain. Rooms get read on demand inside a hall. The always-on surface stays tiny while the deep rules live exactly where an agent will look when it actually needs them.

What belongs in a room (the part people get wrong)

The most common mistake is filling rooms with things the code already says. That's pure cost with no benefit — the model can read the code.

A room earns its tokens only if it holds knowledge that isn't derivable from the repo:

  • Decisions and their reasons. "We use a JSONB blob here instead of columns because the shape varies per tenant." The code shows the blob; only the room explains why, which is what stops someone helpfully normalising it.
  • Landmines. "This table has a trigger that rewrites updated_at — don't set it manually." Invisible until it bites.
  • Conventions with a preferred default. "Side panels, not modals, for data entry." The code has examples of both because of history; the room says which one is right going forward.
  • Cross-system contracts. "This field maps to a Pipedrive custom field ID that isn't in this repo." Genuinely unknowable locally.

What does not belong: file listings, component inventories, restatements of the type definitions, or anything a grep answers faster. If the model can find it in two tool calls, don't pay for it on every turn.

Frontend halls tend to get a room per recurring component — Buttons Room, Cards Room, Sliders Room — each proposing the standard so new components match. Backend halls get a room per data flow — Users Room, Licenses Room — each documenting the gnarly rules about how the app talks to that part of the schema.

The catch: rooms rot, and rot is worse than silence

This is the failure that actually cost me money, so it gets its own section.

A line in one of my context files said "GitHub pushes do NOT auto-deploy." It had been true. It stopped being true when the repo was connected to CI. The agent read it, believed it over the live system, told me a fix wasn't deployed when it had already shipped twice, and then suggested I'd tested it wrong. An hour gone and my temper with it.

The mechanism has a name: documentation bias — favouring a written source while discounting contradictory evidence. And the dangerous zone is exactly where a source is good enough to trust but imperfect enough to fail. A mostly-correct context file is the perfect trap: it's right often enough to earn trust, so the one wrong line gets believed too.

Three rules that keep it survivable:

1. Treat a stale line as a bug, fixed in the same pass. Not noted, not queued. When you catch a room lying, you fix it before moving on. Anything else and the queue becomes the graveyard.

2. Never state environment facts in a doc — state where to check them. This is the big structural fix. Deploy behaviour, current URLs, which account owns what: all volatile. So the room shouldn't say "pushes don't auto-deploy", it should say:

# BAD — a fact that expires silently
Deploys: GitHub pushes do NOT auto-deploy. Deploy with `netlify deploy --prod`.

# GOOD — a pointer that can't go stale
Deploys: check the live wiring before claiming anything about deploy state.
  git remote -v                    # where does this actually push
  netlify status                   # is the site CI-linked
  netlify deploy:list --json | head # did the last push deploy

A doc that tells the agent how to look stays true forever. A doc that tells it what's true starts decaying the moment you save it.

3. Date the volatile claims. Anything genuinely time-bound gets an explicit date, so a reader can weigh it: "As of 2026-07-09, Sol is ~half Fable's price." A dated claim invites a re-check. An undated one masquerades as permanent.

The money pattern: specific beats general, always

The last piece is layering. A global file holds what's true everywhere — git policy, tone, deploy accounts. A project file holds what's true only here. The agent reads the narrow one last, so specific always wins.

This matters more than it sounds. My global stack default is Tailwind 4. One site predates it and runs 3.3.3, where v4 syntax silently breaks. That single project-level override has prevented more damage than any other line I've written, because without it the general rule is confidently wrong in one specific place — the worst kind of wrong.

The verdict

Split by domain, index at the root, load on demand, and only write down what the code can't tell you. Then accept that the whole structure is only as good as your discipline about pruning it, because the tree's failure mode isn't getting too big — it's quietly going out of date while still sounding authoritative.

Write pointers, not facts. Date anything volatile. Fix a lying room the moment you catch it.

A stale doc is a bug with better formatting.

Want your project's AI context architecture designed properly? Get in touch, or browse the work first.

Let us make some quick suggestions?
Please provide your full name.
Please provide your phone number.
Please provide a valid phone number.
Please provide your email address.
Please provide a valid email address.
Please provide your brand name or website.
Please provide your brand name or website.