Rules and the Learning Loop
Rules are the standing decisions your team carries into every workflow — the "always do it this way" that you would otherwise re-explain at the start of each run. As a harness engineer you author them in two ways: by editing a rule file directly, or by letting the learning loop promote an in-workflow correction into a durable rule for you. This chapter is the authoring-side companion to the Rules and the Learning Loop chapter in the User Guide, which covers the loop conceptually and walks the ANZ banking example end to end. Here the focus is operational: which file you edit for what, how the strict-additive model behaves when you stack a rule, and how to run the loop so a one-time fix becomes a standing rule.
Rules are the feedforward half of the control loop — prose the agent reads before it works. Sensors are the feedback half — deterministic checks that fire after a write. A rule can name a sensor as its companion; the two halves are designed to pair.
The five layers and which file you edit
Rules live as Markdown files under core/memory/ (the authored source for the space memory layer), one file per scope.
There is no scope: frontmatter field — this implementation derives scope from
the filename, so the file you pick is the scope you author at:
| You want a rule that applies to… | Edit | Scope |
|---|---|---|
| every project at your organization | org.md |
org (framework default) |
| every project your team runs | team.md |
team |
| this one project | project.md |
project |
| every stage in one phase | phases/<phase>.md |
phase |
The four phase files are phases/ideation.md, phases/inception.md,
phases/construction.md, and phases/operation.md (initialization is
bootstrap-only and ships no rule file). A fifth layer — per-stage rules
(aidlc-stage-<slug>.md) — is reserved for a future release; you cannot author
one yet.
Two judgment calls drive which file you reach for:
org.mdis framework-shipped. It carries the defaults every project inherits — trunk-based development, the walking-skeleton policy, the testing posture per scope. Treat it as upstream. Most harness engineers leave it alone and author at team or project.- Project scope is for durable deviation, not for everything. Reach for
project.mdonly when this project stably departs from team-wide practice — a monorepo that rebases where the team squashes, a legacy project that skips the test floor. If the rule would help every project your team runs, it belongs inteam.md.
Each file is plain prose under topical headings — ## Way of Working,
## Testing Posture, ## Deployment, ## Code Style, and so on. You add a rule
by adding a bullet under the heading it belongs to. The full filename-to-scope
table and the resolver mechanics are the normative contract:
Rule System § Filename-derived scope
and § Layout.
Strict-additive: layers stack, none silently overrides
The chain resolves through five layers at the start of every workflow:
org → team → project → phase → stage
The model is strict-additive. Every applicable rule appears in the agent's
context at once — nothing is dropped or suppressed at runtime. Org defaults, team
practices, and project specialization concatenate; the matching phase rule
attaches because the stage already declares its phase: in frontmatter (the same
pull-authoring direction a stage uses to import everything else). The resolved
chain is baked into each stage node once, at workflow start — the runtime never
re-walks it. See
Rule System § Strict-additive runtime model
and § Five-layer inheritance.
What this means for you as an author: there is no overrides: block and no
enforcement: keyword to set. A narrower layer does not quietly win over a
broader one. Instead, every layer you write is present in the agent's view
simultaneously. That changes how you author — you state a rule positively at the
scope where it should apply, and you trust it to stack with the layers above it
rather than reaching for a switch to suppress them.
Conflicts are rejected when you write, not resolved at runtime
Because nothing overrides at runtime, a rule that contradicts a broader-scope
rule would be a problem the resolver could not untangle. The framework forecloses
that by checking at write time, not run time. When a team-scope rule is being
added under a given ## Heading, an admission gate compares the proposed text
against org.md's same heading; if it finds a contradiction, the gate stops
the write and offers three choices — revise, skip, or escalate to the
org-rule owner. Project-tier writes check against org only, since team-versus-
project differences are legitimate project specialization, not a policy violation.
This check runs at the two admission gates the framework owns — the
practices-discovery affirmation gate and the learning gate (below) — so by the
time a rule reaches the resolver, it has already passed conflict-check. Two
read-only /aidlc --doctor rows surface state after the fact: a rule-drift
row flags headings where team or project content overlaps a populated org heading
(a candidate contradiction for a human to verify), and a paired-coverage row
reports how many rules name a sensor that actually resolves. Both are advisory and
never change the exit code. See
Rule System § Rule-drift detection.
Operating the learning loop
The other way a rule gets authored is that you never open a file — you correct an agent during a workflow, confirm the correction at a gate, and the framework writes it for you. That is the learning loop. Most stage runs add nothing, which is healthy; the loop fires only when something during the stage is worth keeping.
The mechanics are covered in the User Guide chapter. The harness-engineer view is what the loop produces and where it lands:
- The diary records during the stage. The conductor (the live
/aidlcsession running the active stage) keeps an observation log at<record>/<phase>/<stage>/memory.md(under the intent's record dir,aidlc/spaces/<space>/intents/<YYMMDD>-<label>/), with entries under four headings — Interpretations, Deviations, Tradeoffs, Open questions. It is auto-created and maintained for you; never hand-edit it. Writing the diary is the only job the language model has in this loop — everything after the stage (counting, surfacing, routing, writing) is deterministic tooling or your explicit pick at the gate. - The gate surfaces candidates. Before the approval gate the learning gate
reads
memory.mdand shows each non-blank diary line verbatim as a candidate, plus a free-text "anything to add for next time?" channel where you type an observation and pick which of the four headings it belongs under. - You confirm what to keep, and it writes. A confirmed learning lands as a dated entry — you never pick a file path; the heading determines the destination.
Where a kept learning lands
A confirmed learning is a practice: it lands in the same space memory files
practices-discovery affirms (aidlc/spaces/<active-space>/memory/team.md /
memory/project.md) — there is no separate *-learnings.md surface. The gate
routes it by topical heading:
| Heading at the gate | Lands in | Audit event |
|---|---|---|
| Interpretation / Deviation / Tradeoff | aidlc/spaces/<active-space>/memory/project.md (default) |
RULE_LEARNED |
| same, promoted one click | aidlc/spaces/<active-space>/memory/team.md |
RULE_LEARNED |
| Open question | nothing — research items don't promote | — |
The default scope is project, the narrowest. A one-click "promote to team"
affordance widens a learning to memory/team.md when the lesson applies
beyond this project. There is no widen-to-org path — org practices are framework-
shipped or organization-authored through a separate process, so the loop never
writes at org scope. Keeping the default narrow stops one project's surprise from
becoming an organization-wide rule by accident. The learning loop and
practices-discovery write into these same files by two different lifecycles: the
loop appends one dated entry at a time, practices-discovery affirms whole sections.
The resolver sorts the files on the clean integer chain org → team → project → phase.
When the learning is a check, not a rule (SENSOR_PROPOSED)
Sometimes the thing worth keeping is a recurring manual check — you keep eyeing
a stage's output for the same gap. When you confirm that kind of learning at the
gate, the framework treats it as a sensor binding rather than a rule. It does a
two-write install atomically: it scaffolds a sensor manifest under
.claude/sensors/ and appends the new sensor's id to the originating stage's
sensors: import list, leaving a SENSOR_PROPOSED audit row. That is the one
sanctioned edit to a stage file outside a framework release — only the import list
grows; the stage body is never touched. From there you flesh out the manifest by
hand using Sensors — the loop scaffolds the binding; you author
the check.
Learnings apply on the next workflow, not the one in flight
A learning captured at a gate does not change the rules for the rest of the
current run. You already corrected the agent in conversation for this workflow;
the rule is for next time. The new line is on disk, but the in-flight workflow
keeps the compiled view it started with. The next /aidlc you run recompiles,
the directory walk picks up the new file, and the rule applies from stage one
onward.
This matters when you author by hand too: editing team.md mid-workflow
will not retroactively change the run in progress. Rules take effect at the next
compile boundary. If you need a change to bite immediately, finish or restart the
workflow so the compile re-reads your edit.
Next
Sensors — author a deterministic check and bind it to the stages
that should run it, including the manifest the loop scaffolds when you confirm a
SENSOR_PROPOSED learning.