Field Notes · The Precheck Cut
02 / 08

One Concern Per Worker, One Decision Per ADR

Single-concern workers aren't about merge conflicts. They're about producing self-contained outputs that the next session — or the next agent — can pick up cold.

There was a Console.tsx file in Precheck that got to 2,316 lines. Six tab panels. Ninety state variables. Two phase modes. Every feature had a tab conditional wrapped around it. Every new capability required touching the same file.

I knew it was wrong. I built it that way anyway. This post is about the ADR that got written after I had to revert most of it.

The Mech Suit Methodology article has a rule that shows up as an offhand line in the Phase 6 section: one concern per worker. One worker touches one concern per session. No mixing UI changes and server code in the same context window. I phrased it as discipline — the kind of thing you say in a retrospective when you want to sound wise. In practice, it is not discipline. It is the difference between a build session that ships and a build session that has to be partly reverted.

The receipt for that rule is ADR-010 in the Precheck repo. It exists because an earlier version of the UI got so tangled that the only fix was to decompose it. The ADR is the record of that decomposition. And because it was written immediately after the work — by the agent, while the context was still hot — it preserves the exact shape of what went wrong. That is the thing I want to show you in this post.

The rule, stated plainly

One concern per worker means exactly what it says. In any given build session, the agent doing the work is allowed to touch one category of thing: UI, or server, or database, or infrastructure, or tests. Not two. Not "mostly UI with a small server change." One.

The reason is not branch hygiene. It is not merge conflicts. Those things happen, but they are the minor consequence. The major consequence is that context windows are finite, and the moment you mix concerns you start trading attention between them. The agent that is mid-implementation on a UI refactor cannot pause to "just quickly" fix a server bug without losing fidelity on both. The context pollution is asymmetric — the UI state leaks into the server reasoning and the server state leaks into the UI reasoning, and both deliverables come out worse than either would have been alone.

The second, subtler reason is that single-concern work produces single-concern output. When an ADR describes a decision that was made inside a single-concern session, the ADR is legible. It has one subject, one decision, one set of consequences. When an ADR tries to describe a decision that emerged from a mixed session, it either loses information (because the decision was actually two entangled decisions and only one of them gets written down) or it becomes a tangled document that nobody wants to read.

The ADR is the receipt. If the receipt is messy, the work was messy. If the receipt is clean, the work was clean.

What the clean version looks like

ADR-001 in the Precheck repo is the first architectural decision the rebuild committed to. It is also the shortest one in the folder. That is not an accident.

Receipt docs/system-foundation-v2/adr/001-single-planner-single-loop.md
# ADR 001: Single Planner Single Loop ## Status Accepted ## Context The foundation rebuild needed a runtime model that stays replayable, explainable, and small enough to validate without distributed coordination. The repo already implements a single-loop runtime that advances one work item attempt at a time through explicit decisions. ## Decision Keep one planner and one runtime loop with explicit attempt, event, outcome, suggestion, and transition records. The loop remains `SELECT -> REFINE -> ASSESS`, and retries stay structured rather than hidden. ## Alternatives considered - Multiple cooperating agents: rejected because it increases causal ambiguity before the archive and replay model are mature. - Ad hoc recursive loops: rejected because replay and stop semantics become harder to explain. ## Consequences The runtime stays easier to reason about, test, and replay. Scaling behavior is deferred until the canonical contracts and operator tooling are stronger. ## Follow-up work Keep retry and stop semantics explicit in golden runs, replay tools, and runtime tests as provider handling expands.

That is the whole document. Twenty lines. One decision. One context paragraph. Two alternatives explicitly rejected. Two sentences of consequences. One follow-up. An agent reading this file a year from now will know exactly what was decided, why it was decided, what it rejected, and what it committed the system to. A new contributor can read it in thirty seconds and understand why there is no cooperating-agents architecture in Precheck, even though cooperating agents are the fashionable answer in 2026.

The discipline that produced this file is single-concern. I did not ask the agent to "rebuild the runtime loop and also update the docs and also refactor the provider adapters." I asked it to make one decision about the runtime model and write it down. The decision and the write-up were the same atomic task. The ADR came out clean because the work was scoped tight enough for the ADR to have one subject.

What the messy version looks like

Now the contrast. ADR-010 exists because an earlier version of the Precheck UI — Console.tsx — was built the wrong way, and I had to decompose it after the fact. This is the ADR that records the corrective.

Receipt docs/system-foundation-v2/adr/010-route-based-ui-over-monolithic-tabs.md
# ADR 010: Route-Based UI Over Monolithic Tabs ## Context The UI was a single 2316-line Console.tsx component with 6 tab panels, ~90 state variables, and two phase modes. Every new capability required touching one file. Features built earlier (Projection, learning surfaces, story backlog) were buried in tab conditionals and effectively invisible to users because no navigation path exposed them. The product shape had drifted toward "prompt launcher" — users could compose and start runs but couldn't navigate to the interpretation surfaces that make runs valuable: learnings, suggestions, guardrail pressure, and real-time execution feel. ## Decision Decompose Console.tsx into route-based pages using React Router (already a dependency, previously unused). ... ## Consequences - Console.tsx deleted. No single file exceeds ~530 lines. - Each page owns its local state and data fetching. No tab-gating effects. - `consolePhase` state eliminated — URL is the source of truth for which view is active.

Read that context paragraph again. A 2,316-line component. Ninety state variables. Six tab panels. Two phase modes. Every new capability required touching one file. Features built earlier were buried in tab conditionals and effectively invisible to users because no navigation path exposed them.

That was not what I intended to build. At no point did I sit down and say "let me make a two-thousand-line component with ninety pieces of state." Console.tsx got there one session at a time, because multiple sessions added features to the same file without ever stopping to ask whether the file still had one concern. Each individual session looked reasonable. Each one was adding one feature. None of them were checking whether the file's concern was still single.

By the time I noticed, the reverts had started. A UI change that looked innocent in isolation would break a tab conditional four hundred lines away. A state variable that looked local would turn out to be read by an effect hook in a different tab panel. The component had become a distributed system living inside a single file, and every builder session that touched it was implicitly fighting every prior session's assumptions.

When the decomposition finally happened, it was not a fun session. It took the better part of a day, it broke several things in the process, and the ADR captures the final state (Console.tsx deleted, no file exceeds ~530 lines) but it does not capture the hours of pain that came between the deleted file and the route-based pages that replaced it. That pain is the cost of having ignored the one-concern rule for six sessions in a row.

The Honest Part

I have reverted entire UIs because I did not respect this rule. That line from the parent article is not a metaphor. ADR-010 is the receipt for one of those reverts. The corrective is in the repo because the mistake was in the repo first.

Why the ADR matters more than the fix

Here is the part that took me a while to understand. The fix — decomposing Console.tsx into route-based pages — is a one-time event. It happened in an afternoon. The ADR, on the other hand, is a permanent artifact. It will be in the repo for as long as Precheck exists. And its job is not to document the fix. Its job is to prevent the mistake from happening again.

If I am two months from now, mid-session, and I am tempted to add a new page to the UI by dropping it into an existing file "just for now," ADR-010 is what stops me. Not because I remember the rule. Because the next agent that loads the repo context will read ADR-010 before it writes code, see the explicit decision that "URL is the source of truth for which view is active," and will refuse to introduce a tab conditional. The ADR is a load-bearing constraint on future work, not a post-mortem on past work.

This is why the one-concern rule and the ADR discipline are the same rule said two ways. A single-concern work session produces one clean decision. A clean decision fits in a short ADR. A short ADR is actually read by the next session. A read ADR prevents the next session from making the same mistake. The chain breaks if any step in it is sloppy. If the session is multi-concern, the decision is tangled. If the decision is tangled, the ADR is long and nobody reads it. If nobody reads it, it might as well not exist, and the mistake gets made again.

The cost of keeping sessions single-concern is mostly psychological. You will constantly be tempted to "just fix one small thing" in an adjacent file while you have the context loaded. The discipline is to refuse. The small thing goes into a note. The note becomes its own session later. That session produces its own small, clean ADR or its own small, clean plan update. The repo accumulates clean artifacts instead of tangled ones.

What single-concern enables

A side effect I did not expect: when sessions are single-concern, parallel pipelines become possible. The parent article's Phase 5 — running three to five builder pipelines in parallel — depends on this. You cannot parallelize work that mixes concerns, because the mixed sessions will collide with each other on shared files. You can parallelize work that stays in its lane. Three single-concern sessions can run simultaneously against the same repo without stepping on each other because they are each touching a different category of file. One is in ui/. One is in precheck-api/Precheck.Engine/. One is in tests/. They commit independently. They merge independently.

Single-concern sessions also produce more useful retrospectives. A retrospective from a mixed session is a catalog of unrelated observations. A retrospective from a single-concern session is a coherent story about one thing. The story is shorter. The lessons are sharper. The next session that reads it actually benefits from having read it, instead of wading through paragraphs of context that are irrelevant to the work at hand.

Unlock

One concern per session is the foundational discipline that makes every other Phase 6 habit possible. ADRs work because sessions are single-concern. Retrospectives work because sessions are single-concern. Parallel pipelines work because sessions are single-concern. The rule is load-bearing. Break it, and the rest of the methodology degrades.

The part I'm still working on

The honest limitation: I am not yet reliably catching myself when I drift. The strongest signal I have is when I realize mid-session that I am thinking about two files in two different categories, and by the time that thought arrives, the drift has already started. I have a few working heuristics — if the todo list has items from two categories, stop and split the session; if the agent's proposed diff spans two categories, push back — but they catch the drift reactively, not preventively.

The thing I want to build next is a pre-commit check that looks at the file paths being touched in a session and refuses to commit if they span categories without an explicit override. That is not a full solution — sometimes a single concern genuinely spans categories, like a contract change that touches the engine and the API at once — but it would catch the easy cases automatically and force the hard cases to be explicit.

In the meantime, the rule is still the rule. One concern per worker. The ADR is the receipt. When the receipt gets long or tangled, the work was long or tangled, and the next session has to pay the cost.

Back to the arc

The parent article calls this phase "the mature system" and lists single-concern workers as one of the foundational disciplines. This post is one receipt for that discipline — a clean ADR next to a messy corrective, both sitting in the same folder, both written by the same workflow, both teaching the same lesson from opposite sides. The next post in this series is about what happens in the refine step, when a design gets stress-tested hard enough that it dies before a single line of code is written. That is the corollary: single-concern sessions work best when the concern itself has been refined to the point where the builder does not need to improvise.