Originally published on hexisteme notes.
If you use AI agents daily, you have hit this: the agent gives a correct, well-reasoned answer on Monday. On Friday, in a fresh session, it gives a different answer to the same question. Not wrong — just different, shaped by whatever context happened to come up first.
That is drift. And it is not fixable with better prompts or more examples piled into a CLAUDE.md file. Those approaches scale poorly and they do not version. What actually works is treating each repeatable decision as a small, versioned artifact I call a Reusable Decision Unit (RDU).
The one-sentence version: encode each repeatable decision as a versioned markdown file with a grep-able trigger and a committed judgment, store it where the agent loads it at session start, and the rule fires automatically on trigger instead of relying on the model's memory. I run a library of 140+ of these across iOS deployment, trading, AI-agent design, game development, and real estate. This is how they are built and why they hold.
The problem with "just write it down"
The naive fix for drift is to document decisions: write a CLAUDE.md or AGENTS.md entry that says "when X happens, do Y." It fails for two reasons.
- Documentation requires reading. A rule buried in a long markdown file competes for attention with everything else in that file. It gets read once, and then it drifts as the file grows around it.
- There is no version control on the judgment itself. When you discover a better approach, you overwrite the old rule silently. There is no history of why the judgment changed, so the next time you question it you re-derive from scratch.
The fix is to treat decisions the way you treat code: versioned, trigger-driven, with explicit evolution conditions.
What an RDU looks like
Each RDU is a markdown file with YAML frontmatter. Here is a real one from the iOS-deployment domain:
---
id: RDU-047
title: "App Store Connect in-flight limits — 1 version / 2+5 reviewSubmissions"
status: active
domain: [ios, deploy, app-store-connect]
version: 3
created: 2026-04-28
last_applied: 2026-05-10
sources: ["ASC API docs", "direct curl validation 2026-05-10"]
---
## Trigger
- `app_versions_create` returns 409 STATE_NOT_SUITABLE
- User wants to ship CPP + PPO + new version simultaneously
## Situation
ASC enforces two independent concurrency limits:
- **Version**: only 1 in-flight (PREPARE/WAITING/IN_REVIEW) per platform
- **reviewSubmissions**: max 2 in-flight, max 5 total
READY_FOR_REVIEW orphans can't be deleted via API (403) or canceled (409).
They auto-expire after 7 days or require manual ASC web UI action.
## Judgment
**Check both limits before any submission attempt.**
Bundle CPP + PPO + appEvent into one reviewSubmission to minimize slot consumption.
Never assume a clean slate — always query current state first.
## Action
```bash
# Check in-flight versions
GET /v1/appStoreVersions?filter[appStoreState]=PREPARE_FOR_SUBMISSION,WAITING_FOR_REVIEW,IN_REVIEW
# Check reviewSubmission slots
GET /v1/reviewSubmissions?filter[state]=READY_FOR_REVIEW,WAITING_FOR_REVIEW,IN_REVIEW
```
The structural elements each exist to prevent a specific failure:
| Section | Purpose | Anti-pattern it prevents |
|---|---|---|
Trigger |
Grep-able condition that fires the rule | Rules that require the agent to "remember" to check |
Situation |
Why this judgment was needed | Rules that become obsolete without anyone noticing |
Judgment |
Committed decision, no hedging | "It depends" answers that push the decision back to the user |
version: N |
Explicit evolution tracking | Silent rewrites that erase why a decision was made |
sources |
Provenance of the judgment | Rules derived from hallucinated "facts" |
The four core principles
1. No absolutes
Every RDU is the best judgment at the time of writing. The meta-rule — RDU-000 — states it plainly: there are absolutely no absolutes. Every rule carries implicit evolution conditions, so if a better approach shows up you propose a version bump or a supersession; you never silent-rewrite. A rule that cannot be revised honestly is a rule that will quietly become wrong.
2. Trigger specificity
A trigger must be a grep-able token, not a vague description. "When working on iOS" is not a trigger. "When app_versions_create returns 409" is. If the trigger cannot be matched by a regex, the rule will not fire reliably — and a rule that fires unreliably is worse than no rule, because you stop trusting the whole system.
3. Proactive registration
When the agent notices it is re-deriving a judgment it has made before — or when the user signals "this is useful, save it" — it should propose an RDU draft immediately, not wait. The pattern is always propose → user approves → write file. No silent creation, because a rule you did not agree to is a rule you will not trust.
4. Domain-agnostic
The trigger–situation–judgment structure works across every domain. The 140+ RDU library spans:
- iOS deployment — ASC API limits, fastlane patterns, Swift 6 concurrency
- Trading — no LLM probability production (RDU-021), walkforward validation required (RDU-022)
- AI-agent design — council orchestration patterns, stop-hook architectures
- Game development — Godot platformer feel numbers (coyote time 100 ms, jump buffer 150 ms)
- Real estate — LTV computation, loan-headroom calculation
The same five-section skeleton carries a Swift concurrency rule and a jump-buffer timing without any change in form.
How it works in practice
At session start, the agent loads a single index file — one line per rule. When a trigger condition appears in the conversation, the agent reads the full RDU file. The index stays short; the full rules load only when they are relevant. This is the whole efficiency argument against dumping everything into CLAUDE.md: a global, always-loaded file pays for every rule on every turn, while an index-plus-trigger design pays for a rule only when it fires.
Evolution is explicit. When a newer approach is discovered, the agent proposes a diff rather than editing in place:
# RDU evolution example
- version: 2 → version: 3
- Judgment updated: fastlane now works in READY_FOR_SALE state (ASC MCP doesn't)
- History: v2 assumed ASC MCP parity — incorrect for Editable Properties
- Source added: direct fastlane validation 2026-05-10
The value is not in having 140 rules. It is in the trigger mechanism. A rule that fires automatically is worth 10× a rule that requires the agent to remember to check.
What RDUs don't solve
RDUs encode repeatable judgments. They do not handle novel situations, value questions (user preferences, risk appetite), or decisions that genuinely depend on runtime context. For those, the agent should ask. But every judgment you move into an RDU shrinks the surface area of what actually needs asking — which means the questions the agent does raise are the ones that genuinely need a human.
Getting started
The minimal viable setup is three things: a single directory for the rule files, an index file, and a template. You do not need 140 rules to start; you need one and the discipline to add the next. The signal to write one is concrete — when you catch yourself re-explaining the same judgment twice, or when you fix a bug caused by the agent forgetting a previous decision, that is an RDU waiting to be written.
Here is the template:
---
id: RDU-XXX
title: [one-line description]
status: active
domain: [list of domains]
version: 1
created: YYYY-MM-DD
last_applied: YYYY-MM-DD
sources: ["where did this judgment come from"]
---
## Trigger
[Specific, grep-able condition]
## Situation
[Why was this judgment needed? What was the failure mode without it?]
## Judgment
**[Committed decision — no hedging, no "it depends" without explicit conditions]**
## Action
[Concrete steps when trigger fires]
None of this is exotic. It is the same instinct that makes you write a test instead of manually re-checking a behavior every time: encode the judgment once, in a form that fires on its own, and stop paying the cost of remembering. The drift does not come back, because the rule is no longer in anyone's memory — it is on disk, versioned, waiting for its trigger.
More notes at hexisteme.github.io/notes.
Top comments (0)