Appendix A - 3. Dynamic context injection
The Structure of Dynamic context injection — its shape at a glance:
One slicing operator runs in two directions. Forward maps target files to the constraints that will govern them and pushes them into the brief; reverse maps a diff's line ranges to the findings it introduced and feeds a self-heal gate.
flowchart LR
Files[/Target files/] -->|forward: files → constraints| Slicer{{Slicing operator}}
Reg[(Constraint registries)] --> Slicer
Slicer -->|forward| Brief[Injected into brief]
Diff[/git diff ranges/] -->|reverse: diff → findings| Slicer
Slicer -->|reverse| Heal[Self-heal gate]
Accessible description: a slicing operator reads the constraint registries and runs in two directions — forward, mapping target files to the constraints injected into the brief before the agent writes, and reverse, mapping a diff's line ranges to the findings it introduced, which feed a self-heal gate.
Projected from the catalogue entry context-and-dispatch / Dynamic context injection.
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — Map the files an agent is about to touch to the exact constraints that govern those files (lints, conventions, component boundaries, tests) and inject that subset into the agent's brief before it writes code, moving detection left of the cheapest CI gate.
Motivation
The recurring failure class is constraint under-specification at dispatch time. A coding agent lacks the tacit knowledge an experienced engineer has of which rules apply to a given change, so it makes plausible edits that violate them, then spends rounds (and tokens) discovering and repairing the violations. Call it "pinball." A layered validation hierarchy (pre-commit → merge check → deploy gate) makes it worse: context is lost between where the agent authored the change and where the failure surfaces. Across many concurrent agents, this wasted work multiplies.
Applicability
- File-addressable constraints. Every constraint declares its scope (file patterns / component tags) and lives in a registry you can query by file. Anything not addressable by file scope cannot be sliced.
- Self-documenting constraints. Each rule carries a name, docstring, and an actionable "How to fix" line. This is the decisive prerequisite: the value of an injected constraint is bounded by the agent's ability to act on it, and its inverse, if you can't clearly explain a rule to an agent, the rule's own defense value is questionable.
- A slicing operator at two granularities, file-scope (forward) and line-range (reverse), built as multiple adapters, one per registry (lint fleet, component registry, banned-API list, test corpus, doc index).
- An injection point in the dispatch pipeline (the brief-authoring step) plus the on-demand CLI.
Structure
The Structure diagram appears at the top of this page.
Sample Code
The forward slicer intersects each registered constraint's file-scope against the agent's target files and renders the matches (name, docstring, fix-hint) into the brief. The value of an injected constraint is bounded by the agent's ability to act on it, so the fix-hint is not optional.
import fnmatch
# each constraint declares the file globs it governs + how to fix it — a self-documenting registry
CONSTRAINTS = [
{"name": "no-raw-lib", "scope": ["model/**"], "fix": "route mutation through the typed seam"},
{"name": "typed-new-files", "scope": ["**/*.py"], "fix": "add the file to the strict type set"},
]
def constraints_for(files: list[str]) -> list[dict]:
"""Forward slice: which declared constraints govern *these* files?"""
hits = []
for c in CONSTRAINTS:
if any(fnmatch.fnmatch(f, pat) for f in files for pat in c["scope"]):
hits.append(c)
return hits
def render_block(files: list[str]) -> str:
lines = ["## Constraints governing your target files"]
for c in constraints_for(files):
lines.append(f"- **{c['name']}** — fix: {c['fix']}")
return "\n".join(lines)
if __name__ == "__main__":
import sys
print(render_block(sys.argv[1:])) # paste the block into the brief before dispatch
Consequences
- Garbage-in. The mechanism is only as good as the constraint declarations it slices: a rule with no scope tag can't be selected, and one with no actionable fix-hint can't be acted on. It depends on prerequisite 2 being true fleet-wide; it does not create that discipline.
- Advisory, not binding. Because forward injection is augmentation (see Enforcement), an agent can still ignore an injected constraint. It shifts the odds, raising salience at point-of-need, but a gate downstream is still what guarantees the rule; DCI reduces pinball, it does not eliminate violation.
- The relevance operator is itself fallible. Over-injection floods a brief with noise (lowering the salience the mechanism trades on); under-injection silently omits a governing rule. Precision/recall of
files → constraintsis a real, tunable failure surface, not a solved mapping. - Adapter maintenance. One adapter per registry (lint fleet, component registry, banned-API list, test corpus, doc index) must be kept in sync with its registry, or the sliced set drifts from truth.
Example use within DocAble
- A constraint-extraction tool (forward slicing / discovery-time pull).
- The diff-line-range attribution machinery (reverse) that powers CI self-heal.
- Consumes the component-zone model and the lint fleet's scope tags as its addressable registries (via the read-don't-hardcode discipline).
Related Patterns
- Bridge — the component-zone model (a models-bridge mechanism) supplies the file → component → checks mapping the forward slicer reads. DCI is the agent-facing consumer of that bridge model; the same model governs the product via the boundary lints, so DCI is one end of the bridge.
- See also (complement) — brief-linting: the structural check on the brief; this is the content injected into it.
- Temporal complement (the feed-back twin) — reflection-facet-substrate (built on lifecycle-hooks): injection is feed-forward, the constraints governing a task pushed into an agent before it acts, at dispatch. A tempo-gated reflection facet is the mirror image: it pulls the operator back to the same kind of repo policy while or after acting, at turn-tempo, so it is feed-back instead of feed-forward. Same move (meet the loop with the right policy at the right lifecycle moment), opposite direction in time. And just as injection slices one registry per file-set, a reflection substrate generalizes across policy facets (convert-a-recurrence, spot-a-second-copy, a stale runbook), consolidated into one paced emission so the facets don't compound into the alarm fatigue each was biased to avoid. The deeper unity: both are delivery modalities over the same policy source of truth. Push it in comprehensively at entry, or surface a slice of it selectively at tempo, so one policy-materials registry could feed both.
- Generalization: every meta-substrate authored for human discipline (the lint fleet, the component registry, the doc index, the banned-API registry, the test corpus) becomes a just-in-time constraint registry the moment you add a slicing operator over it. Lint pre-briefing is only the first adapter.