A File-Based Context Layer for Coding Agents

aicoding-agentscontext-engineeringarchitecture

This is the context layer I built between my coding agent and the data platform I work on: a multi-tenant ELT system spread across a large estate of repositories. It is one git repository of markdown files, run in Cursor, and it is not backed by a vector index, a service, or a database. (I called it the Harness for most of its life, until it became clear that word now names something bigger; the collision is covered at the end.)

I open this repo to work, and the agent starts here too: it reads the right sibling repo, queries the warehouse directly, and commits changes back into the other repos, without me switching windows. (The one standing exception is live debugging of the C#/.NET services: Microsoft licenses its .NET debugger only for its own editors, so a VS Code fork like Cursor can't ship it, and stepping through service code still happens in an IDE that has one.)

By the field's survey data, this shape is unusual. Galster et al.'s empirical study of 2,853 GitHub repositories across Claude Code, Copilot, Cursor, Gemini, and Codex (arXiv:2602.14690) finds context files are often the sole mechanism a repository carries; skills appear in 158 of those repositories (5.5%), subagents in 131 (4.6%). The survey counts public repositories; a private internal layer like this one is exactly what its denominator can't see. The survey is still the adoption baseline that exists. This system leans on all three mechanisms at once: context files for grounding, workflow skills for the ticket lifecycle, and subagent delegation for retrieval.

Some of this is topology-specific: the dual Mongo instances and the per-developer path map are artifacts of one shop's setup. The portable parts are trust tiers with a freshness gate, routing that holds pointers instead of facts, temperature budgeting, and the delegation threshold.

What follows is the system as it runs: the layout, the file formats, and the loading model.

The problem

The platform is a multi-tenant ELT system: C#/.NET services, YAML transformation configs, a Snowflake warehouse, MongoDB for entity configuration, CDC streams, and a financial data schema no model was trained on. It spans roughly forty repositories (about fifty exist, some now falling out of use), and no single repo explains how it works. Open any one of them in Cursor or Copilot and ask a question: the agent cannot know that the answer involves a service in multiple repos, a transformation config in another, and a warehouse view downstream.

This is not an access problem. Every one of those systems ships an official MCP server now; the agent can already query Snowflake, read Mongo, and pull Jira tickets. (I built a custom MCP server across those three in early 2025, before the official ones existed. It is now obsolete; access became commodity within about a year.)

The context layer holds what an agent with ten MCP connections still lacks: which repo to read first, what the domain terms mean, and whether the file it just read is still true.

I'd been solving this manually since the GPT-3.5 days: pre-written "this is what our ELT does" preambles pasted into every fresh chat, compressed to fit back when character counts still mattered. The discipline has a name now, context engineering, and this layer is that preamble habit built out into a maintained system.

What it is

The tree, abridged (the running system carries more topics, rules, and workflow files than shown):

context-layer/
├── .rules/                     # agent runtime rules (hot/warm/cool)
│   ├── entry.md                #   alwaysApply: true
│   ├── trust-tiers.md          #   alwaysApply: true
│   ├── delegation.md           #   alwaysApply: true
│   └── configs.md              #   globs: ["configs/**"]
├── index/
│   └── trigger-table.md        # topic → files + live pointer + vocabulary
├── knowledge/                  # curated, human-reviewed
│   └── <topic>/<subject>.md
├── extractions/                # agent-written session findings, PR-gated
│   └── <date>-<topic>.md
├── workflows/                  # the ticket implementation workflow + its skills
├── working/
│   └── <ticket>/               # plan, investigation, verification queries (local, not committed)
└── .local/
    └── repo-paths.json         # gitignored, per-developer

repo-paths.json maps repo names to that developer's local clones (about twenty, on my machine):

{
  "service-ingest": "/Users/<dev>/src/service-ingest",
  "config-transforms": "/Users/<dev>/src/config-transforms"
}

The path map is per-machine, and the ticket folders under working/ are working state; neither is committed. The shared surface (the rules, the index, knowledge/, extractions/, workflows/) is what the repo versions, and extractions don't land by direct commit: the end-of-session skill that writes one also opens its pull request, so agent-written knowledge enters the shared repo through review.

Two axes

Everything below is a cost mechanism as much as a grounding one. Hot context is a per-message tax and gets policed like one; bulk retrieval is delegated so a tool result is paid for by a subagent on a cheaper model instead of by the orchestrator; the trigger table exists so the expensive context loads three files instead of thirty. The design goal throughout is that the orchestrating session spends its tokens on reasoning, not retrieval, and each task runs on the cheapest model that can do it.

The mechanisms sort along two independent axes. Trust is how much the agent should believe a thing, graded by distance from the running system. Temperature is how often the tokens for it are paid: from hot, injected into every message, down to cold, not resident at all until something pulls it.

The axes do not correlate, and cold is where that's easiest to misread: cold means not resident, not not trusted. A live Snowflake query is Tier 1, the most trusted thing in the system, and cold: nothing about it sits in context until the agent runs it, and it's re-fetched every time because that's what makes it Tier 1. Conversely a routing hint is Tier 2, believed provisionally, and hot, paid for on every single turn.

artifact trust temperature
a Snowflake query executing now Tier 1 cold
the trigger table Tier 2 hot
a knowledge/ topic file Tier 2 cold
a Confluence page Tier 3 cold

One combination is impossible by construction: nothing in the system is both Tier 1 and hot, because Cursor gives no way to make live state resident per message. That absence comes back below.

The axes get a section each below, with the trigger table between them.

Trust tiers

The ordering is by distance to the running system, because files go stale and running systems do not; it's written into a rule the agent loads on every message.

Tier 1 — live state. A Snowflake query executing now. Mongo's current entity config. Source code in a sibling repo, once the freshness gate (run at read time; see Delegation) confirms the local clone is current.

Tier 2 — cached knowledge. knowledge/ and extractions/. The rule text describes these as navigational maps into the live system, not substitutes for reading it. On conflict, live wins and the file gets corrected in the same session.

Tier 3 — external human docs. Confluence, READMEs, design docs. Treated as what someone believed at some point. Validated against Tier 1 before anything from them is promoted into Tier 2.

The routing policy for the most common case is stated as an ordered list, not a paragraph:

schema questions:
  1. load the cached topic map        # Tier 2 — orient cheaply
  2. query the live source            # Tier 1 — verify what matters
  3. supplement from external docs    # Tier 3 — context only
  4. model memory                     # last resort; must be flagged unverified

One refinement that matters if you port this: the tiers are a preference ordering given what the agent can reach, not an absolute ranking. An agent running without live adapters is instructed to re-grade the cache as effectively Tier 1, the best available truth, rather than treat it as second-class and guess instead.

Within Tier 2, directory placement is the coarse grade (knowledge/ vs extractions/); frontmatter carries the rest:

---
source: extraction          # human-authored | extraction | external
created: 2026-04-14
last-verified: 2026-06-02
verified-by: live-query     # developer | live-query | unverified
status: confirmed
---

status has one rule attached to it: agent-written statuses are annotations, never control signals. An agent may write hypothesis or confirmed-by-agent as a breadcrumb, but only a developer-stated confirmation gates downstream behavior. The rule exists because agents kept writing confirmed on findings they had reasoned their way to and never verified against anything.

The trigger table

One row per topic: fifteen platform topics plus five for the layer's own meta cluster. Each row holds exactly three kinds of thing: pointers to the highest-value files, the live pointer, and vocabulary anchors for matching task language to topic. A lightly fictionalized row, compressed to a schematic:

| topic      | files (highest value first)                    | live pointer                             | vocabulary                          |
| cdc-ingest | overview.md · stream-map.md · failure-modes.md | warehouse ingest.v_cdc_latest, read-only | tombstone, watermark, late-arriving |

The schematic undersells the density. A real row is a topic section, ranging from about 250 tokens for a sparsely covered topic (two file pointers, four vocabulary terms) to about 1,200 for the densest, which carries thirty-four file pointers, three sub-groups, and twenty-two vocabulary terms. Twenty rows plus roughly 365 tokens of legend and preamble is how the index comes to 9.5K, and the bulk is pointers, one-line pointer annotations, and vocabulary; the rule below constrains what a row may hold, not how much.

A row must not contain a fact. The table is always loaded, so anything asserted in it reads as the system's own voice and gets absorbed without cross-checking. When it's wrong, every agent that read the index inherits the error. (The pointer annotations breach this rule, and the breach is bought for routing: one-line headlines are what let an agent choose among thirty-four pointers without opening them all. A wrong headline gets absorbed like anything else always-loaded; the attached file makes verification possible, not automatic. So the annotations accept the absorption risk rather than solve it.) The shape constraint is checked by a regex rather than left to convention, because shape drift is invisible until someone reads the index for content.

The topics are deliberately coarse. A task doesn't have to fit a topic exactly; it has to be pulled toward the right neighborhood. Sub-grouping lives only in the trigger table; file frontmatter stays single-axis, because a taxonomy maintained in two places eventually disagrees with itself.

The context layer's own documentation lives in a separate cluster, and the gate between them is absolute: ticket work reads platform topics only. An agent working a ticket that finds itself wanting the meta-docs has hit a routing failure: it logs a one-line backlog entry and stays on the ticket. That single boundary handles the two context-pollution paths I've hit: task agents rabbit-holing into knowledge-base cleanup, and meta-work leaking into task reasoning.

Temperature: hot, warm, cool, cold

Temperature is the loading model: the band a thing lives in decides when its tokens are paid.

Temperature Loads Contents
Hot Injected into every message by the runtime A small always-on rule set + the trigger table + tool schemas from every connected MCP adapter
Warm Agent elects it when the description matches the task Operation-specific rules
Cool Fires when a matching file path is touched Rules scoped to a subsystem
Cold Not preloaded at all; pulled explicitly Topic files, extractions, live queries

Hot is the band with a hard dependency. In Cursor that's an alwaysApply flag on a rule file; Claude Code has an equivalent; plenty of setups have none at all, in which case there is no hot tier and the whole budget below collapses.

Tool definitions are hot whether you plan for them or not: every connected adapter's schemas are injected into every message, so each new MCP server is a standing per-message cost, not a free capability.

As it runs today, the full hot set (the always-on rules, the trigger table, and every adapter's tool schemas) costs about 22K tokens per message, measured from a fresh session before any conversation: 19K of rules and index, 3.4K of MCP schemas. That total is what the promotion rule below polices.

The 19K is not split the way a reader might guess. The one heavy item is the trigger table at roughly 9.5K, because the index is where twenty topics' file pointers and vocabulary anchors live. The named rules are smaller: the entry rule around 0.3K, trust tiers around 2K, the delegation rules around 2.3K. The remaining 4.9K is a long tail of smaller always-on rules, which together outweigh the three named ones.

That arithmetic also names the next cut. Half of that 19K is the index, and its five meta rows ride along on every ticket message even though ticket work is forbidden to route to them. By the promotion test below, a row the agent may not use cannot clear the bar. Splitting the index (platform rows staying hot, meta rows dropping out of the always-loaded set) is the cut the arithmetic argues for; as of this writing it has not been made.

Overhead sits on top of all of it: the IDE's system prompt and built-in tool definitions, plus skill and subagent listings. The standing preamble comes to roughly 55K of a 300K window, 18% spent at message one. (300K is the window Cursor provisions for the session, not the model's advertised maximum.) Even so, it is cheaper context than what it replaced: hand-pasting the orientation preamble and every relevant file into each fresh chat.

In this IDE, hot holds static text only. Cursor re-injects authored rule files every message, and its hooks can add context at session start and after a tool call, but nothing makes live state (a fresh query result, current ticket state) resident on every message, which is why live truth ends up Tier 1 but cold in the table above. Hot is the only part of this section that does not degrade gracefully.

Both limits sit in the runtime, not the model: whether a hot tier exists at all, and whether it can hold anything but static text. Hot exists wherever something controls prompt assembly. In Cursor you rent that control through a flag; in a scripted multi-agent orchestrator, the script is the runtime. It composes each agent's messages, so it can pin the same rule files into every subagent's prompt directly, and it can do what no flag can: make live state resident, re-injecting a fresh query result each turn. The temperature bands are properties of prompt assembly, not of Cursor; they port to any runtime that controls what enters the window.

Warm and cool are the same file format with a different trigger (a description the agent matches against, or a path pattern):

---
description: <when the agent should elect to load this rule>   # warm
globs: ["configs/**"]                                          # cool — path-fired
alwaysApply: true                                              # hot
---

Hot memory is a recurring cost, so promotion is a reviewed act: every hot rule carries a written justification for why it earns always-on, and the argument it has to make is

promote to hot iff:
  (tokens saved across future sessions)
    > (hot tax paid every message, every session)
    + (one-time cost of the change)

Every session ends with a required hot-memory verdict on the always-loaded index: either "updated, here's what and why" or "reviewed, no change, here's why not." Most end with the second.

Live access

MCP adapters for Snowflake, MongoDB, and Jira (which reaches Confluence). Read-only is enforced in the adapter config (a SELECT/describe allowlist) rather than requested in the rule text. The workflow rules also say don't write, but enforcement lives in the allowlist.

Mongo exists in two instances, local and remote, whose contents diverge by design. Rather than a stateful environment switch the agent could silently get wrong, there are two named read-only adapters and a rule requiring the agent to state which one it used in every answer. A mis-route gets caught in the transcript instead of prevented with more machinery.

Delegation

MCP made it trivial to pull an enormous tool result into the main context and poison it. Nothing in the protocol budgets bytes, so the budget lives here:

delegate to a subagent when ANY of:
  - expected result rows ≥ 20
  - independent queries ≥ 2
  - any inventory scan (files, tables, topics)
  - any large-JSON read

every subagent prompt is fully self-contained:
  exact paths, the literal task, no conversation history — no routing step

subagent returns a digest only:
  counts, 3–5 observations, the specific rows that answer the question

never: the raw result set in the orchestrator's context

Sibling repos are worked the same way, with one addition in front: a freshness gate. A stale local clone is worse than no clone: it looks like Tier 1 and behaves like Tier 3. So every read of a sibling repo runs this preamble first:

before reading any sibling repo:
  1. git fetch                    (never pull)
  2. confirm the expected branch
  3. dirty-tree check             — uncommitted changes? stop and surface
  4. ahead/behind check vs remote
  5. stale or diverged → STOP, report state, wait for the developer

  never: auto-pull, auto-stash, auto-checkout

Auto-pull is banned because the agent doesn't know what the developer was mid-way through; surfacing the state instead costs one message, and recovering from a silent stash can cost an afternoon. If a cached tier can go stale without anyone noticing, the staleness check has to be wired into the retrieval path itself, and delegation is the retrieval path. Otherwise the tier rating is decorative.

Past the gate, the subagent does the bulk read on a cheaper model and returns a summary; when the work writes, the subagent carries the branch and commits into the sibling repo, and the pull request is the human gate. The orchestrating session does the reasoning while the subagents handle retrieval.

The model split is part of the budget. The orchestrating session runs a frontier reasoning model: Opus 4.6 and then 4.8 as the daily drivers at the time of writing, swapped up or across high-reasoning models when a ticket warrants it. Subagents default to the cheapest model that can do the job, with the orchestrator having a hand in the pick per task: a bulk file scan does not need what a root-cause derivation needs.

Advisory-board fan-outs (the same question put to a panel of different models) draw on whatever Cursor exposes: Claude Opus, Sonnet, and Haiku, GPT and Codex versions, Gemini, Grok, Composer. The boards are the deliberate exception to cheapest-model, and a posture rule scopes when they fire: high-stakes or hard-to-reverse decisions, genuine design trade-offs, anchoring risk, or a stuck loop — and explicitly not decisions that are cheap to make or cheap to reverse. The ticket workflow itself never fans out to design a fix; its one diversity mechanism is the single cold re-derivation described under Session commands.

Independent needs fan out in parallel. On a recent null-column investigation, one subagent scanned transformation configs across two repos while another pulled recent change history; the orchestrator received two digests totaling a few hundred tokens instead of two dumps totaling tens of thousands.

Anything that changes state in a data system (a write, a migration, DDL) is emitted as a copyable block, and the agent stops. Execution is the developer's action. Credentials live in environment variables and the OS keychain, referenced by name only; nothing secret enters the tracked repo.

Session commands

Context runs out before a ticket does, so state moves through files rather than through the conversation. Everything a ticket generates (the plan, investigation notes, verification queries, hand-off blocks) lives in that ticket's folder under working/.

  • /start-ticket — pull the ticket, create its folder under working/, route through the trigger table, investigate. A wrong route chosen here gets caught at the plan stage for one paragraph, or three commits later.
  • /create-plan — write the plan as a rich markdown file in the ticket's folder. Not the IDE's plan mode: plan mode relegates the plan to a hidden file a fresh agent won't intuitively find, and the agent has to be in agent mode to write its investigation files anyway. A plan that must survive sessions has to be an ordinary file.
  • /build-plan — execute the plan file, typically handed to a cheap model or a subagent. The asymmetry with retrieval delegation is a judgment, not an oversight: by this stage the plan is the settled fix made mechanical (written by the frontier session from confirmed facts, decisions already made), and the output still passes the same gates as any other write (the review passes and the pull request). Design never runs on the cheap tier; execution does.
  • /continue-ticket — load the ticket folder, not the transcript: current state, confirmed facts, what remains.
  • /end-session — write findings to extractions/, index them into the trigger table, open the extraction PR, render the hot-memory verdict.

/continue-ticket is not a resume-after-implementation command; it works at any stage boundary. A single ticket might run investigation → second investigation session → plan-writing session → plan-review session → first implementation session, each one started fresh from the files. That is what keeps context light: a session gets cut wherever the current stage ends instead of stretched toward the ticket's end. The cut itself is cheap because hand-offs are explicit: these skills, /continue-ticket included, produce a copy-pastable hand-off block when asked or when they finish, so seeding the next session is a paste rather than a re-derivation.

/continue-ticket deliberately does not carry forward the draft solution. The original implementation passed the in-progress fix forward, and each session refined what it inherited; after four or five hand-offs the fix was right in direction and badly scoped, carrying assumptions nobody could trace to an origin. A fresh session given only the confirmed facts, with the draft withheld, derived a tighter fix. That is a single comparison with me as the judge of "tighter" (n=1, not a benchmark), but it changed the default: the resuming session re-derives the fix cold, and only then is the withheld draft surfaced and diffed against the new approach. When the two agree, that's convergent evidence; when they differ, the difference is what gets examined.

Lineage and neighbors

Two papers are worth reading alongside this. Mei et al., A Survey of Context Engineering for Large Language Models (arXiv:2507.13334), is the formal treatment of the general problem. Vasilopoulos, Codified Context: Infrastructure for AI Agents in a Complex Codebase (arXiv:2602.20478), describes a related three-part system built alongside a 108,000-line C# codebase: a hot-memory constitution, a set of specialized domain-expert agents, and a cold-memory knowledge base.

The Codified Context paper is where this started. Its infrastructure and its codebase grew together; the question was whether its trigger table could work as a routing layer across an entire existing stack. The first build step was small: a reusable extraction prompt run over old Claude and Cursor chats, producing the first files in extractions/. I took some of the paper's concepts and extended them rather than implementing the system, because this one is retroactive and spans a large multi-repo estate. That is where most of the staleness machinery above comes from: a system that already exists is carrying years of decisions nobody wrote down.

The scripted-orchestrator route I know from outside this job: the Weirwood Network, a knowledge-graph project of mine, ran its extraction and enrichment passes as orchestrated agent fleets, each worker grounded by what the orchestrator pinned into its prompt, with no IDE in the loop. I considered porting that model here for the heavier workflows and decided against it, for now, for an unglamorous reason: an orchestrator is one more running system, and this layer has to stay maintainable inside the margins of a day job. The IDE-hosted version is the one that survives a ticket queue.

McMillan, Structured Context Engineering for File-Native Agentic Systems (arXiv:2602.05447), is the closest neighbor I've found: file-native context for structured-data agents, evaluated across 9,649 experiments. This system was built without knowledge of it; the arrival at similar shapes was independent and concurrent. Three of its findings bear on choices made here:

  • Format barely matters. Markdown, YAML, JSON, and TOON (Token-Oriented Object Notation) showed no significant difference in aggregate accuracy (chi-squared 2.45, p=0.484); how content is partitioned and navigated is what moves it. That is the one-sentence justification for a layer that is all markdown.
  • Partitioning scales. File-native agents held high navigation accuracy at 10,000-table scale when schemas were domain-partitioned: the principle the trigger table's coarse topics rely on, tested at a scale this system can't reach. (The delegation threshold is this system's inference from the same result, not something the paper tested.)
  • The capability finding is adjacent, not exact. File-based retrieval helped frontier models (+2.7%, p=0.029) and hurt open-source models in aggregate (−7.7%, p<0.001), with per-model deltas running from −21.9% (Qwen3-32B) to +0.5% (Llama 4 Scout). The subagents here run on cheap commercial models, not the open-source tier the paper measured, so the closest finding in the literature tests an axis next to this design rather than against it.

The design still assumes the split McMillan's closing recommendation names: tailor architecture to model capability. Subagents are never handed the file layer: prompts are fully self-contained (the Delegation contract above), routing stays in the frontier orchestrator, and what comes back is a digest. Nothing prohibits a subagent from reading the index; it is simply never given the routing step.

About the name

While this was being built, harness came to mean something larger. Galster et al. (arXiv:2602.14690) define the harness as the software layer around the model that assembles context for each call, exposes tool schemas, and manages turn-by-turn state, and harness engineering as customization of that whole layer, not only the context. (That paper is the repository survey from the introduction; it began as Configuring Agentic AI Coding Tools and was retitled Harness Engineering for Agentic AI Coding Tools in later revisions.) Agent = model + harness is the shorthand that stuck.

Böckeler's harness-engineering article supplies the taxonomy I'd now use to place this system: harness components split into guides (feed-forward, steering the agent before it acts) and sensors (feedback, observing after it acts so it can self-correct), each either computational or inferential. On that grid, almost everything here is an inferential guide: trust tiers, the trigger table, the temperature budget, and the delegation threshold all shape what the agent believes and reads, through text the model interprets. The deterministic pieces are the freshness gate, the read-only allowlist, and the trigger table's shape regex; the sensor half is the manually triggered review passes and a human reading the PR. That is the context-layer cell of a harness, not the whole harness; that is why this article stopped calling the system one. The literature grew up alongside this build, not before it.

What still breaks

Most of the rules above are scar tissue, and it's worth saying which failures produced them, and which are still open.

  • Routing fails. Wrong routes happened often enough that the ticket workflow writes a plan before implementing anything; the cheap catch at the plan stage is the point of the gate.
  • Agents overclaim. The status rule is the response: agents graded their own conclusions as confirmed without checking them against the live system, so a developer's statement became the only confirmation that gates behavior.
  • The index drifts. The shape regex earned its place after rows quietly stopped matching the format, and nothing surfaced that until a human read the table itself instead of routing through it.
  • Rows thicken instead of topics splitting. The densest row, described under The trigger table, has grown internal sub-groups: sub-structure that, by the coarseness principle, wants to be a topic boundary. The table absorbs growth by thickening rows, and nothing in the maintenance loop catches the moment a row should have become two topics.
  • The sensor half is manual. There are LLM review passes (reviewers that feed findings back for the agent to correct), but no hook can run an LLM pass and feed its verdict back to the agent. I trigger the passes by hand, and the outer feedback loop is a human reading a pull request.

That last one is the determinism ceiling. Cursor does have lifecycle hooks; this repo runs one, a secrets scan that fires on every prompt submission and can block it. The limit is what a hook may do. afterFileEdit can fire a script after every edit, but the script only observes: it cannot gate the edit on its result or feed a pass or fail back to the agent. Context injection is limited to the two points named under Temperature, never into every message.

And a hook is a deterministic script, not an LLM pass. Checks can fire on their own; their findings still wait on a person. The ceiling is Cursor's, not the design's: the same files under a runtime whose post-edit hooks feed results back to the agent (Claude Code's can), or under a scripted orchestrator, would close most of that gap.

One gap is an absence rather than a failure: the input side of this system is measured (hot-set size, thresholds, model tiers), and the output side is not. No rework rate, no tickets-per-session, no baseline from before the layer existed to compare against; that data was never collected and is not recoverable retroactively. On outcomes, this article is testimony and says so.

The testimony has an extent, at least: about four and a half months of daily use since the first extraction landed in late March 2026, and 95 extraction files across 33 ticket folders on this machine. Read together, the numbers do a second job. A session closes through /end-session, and a session writes at most one extraction, so the file count is a floor on the session count: just under three sessions per ticket, before the sessions that had nothing to extract (plan-writing and plan-review among them) push the real figure higher. That floor is the stage-boundary cut working as designed, and ninety-five files in four and a half months is the accretion rate that turns the pruning policy under When not to build this from hypothetical to due.

When not to build this

Each mechanism above has a maintenance cost, and it's only worth paying once the simpler thing has already failed.

  • Small, stable codebase the agent can hold — read the code. The code is Tier 1. Developer count is not the variable; size and drift are.
  • A handful of docs — load all of them. Six files don't need a taxonomy.
  • A dozen notes — one folder and honest frontmatter. No two-grade store.
  • One agent tool — skip rule-format portability until a second runtime shows up.

What it actually costs to run: the hot set needs active policing, the trigger table drifts without the regex and periodic audits, the taxonomy needs upkeep as the platform changes, extractions accrete and need a pruning policy, and every live adapter is one more running system to operate: credentials to rotate, an allowlist to keep current, and tool schemas riding in the hot band on every message.

Build the grounding layer when the agent has to reason over truth it can't fully load and that drifts faster than you can document it. Below that line, the simpler thing is the correct one.