Skip to content

Commit 3a1ca37

Browse files
committed
docs(architecture): ADR-0004 -- engine anchor files as instructions spine
Captures the pattern that emerged from PR #577 (gender anchor) and PR #581 (wow anchor): - Every engine domain subfolder has exactly one anchor file -- its single clear entry point. - Two anchor flavours, picked by what the axis is: position-anchor (HOLD: Has/Orders/Loses/Drives) for axes the room *reads* statically on the persona process-anchor (IDLE: Initiated by/Direction/Lever/Echo) for axes that *happen* dynamically - The anchor enumerates its expressions in prose (with sibling links) so zip-deployed readers can navigate the axis without the README (engine READMEs are excluded from shipped zips by scripts/build_zips.py). - engine/stack/instructions.md is the registry: every anchor is declared there with an invocation mode -- "global" (every persona, e.g. language, stress, gender, wow) or "per-khai-type" (when a specific khai entity is in scope, e.g. group, org, adoption). Status: Proposed. The implementation work to update instructions.md with the full engines registry is left as Open follow-up #1 -- this ADR documents the contract; the registry update is a separate culture/engine PR. The pattern is architecture-relevant at the khai-spine level (a KAIHACKS concern), not Cultures-specific. Open follow-up #4 calls for mirroring the contract in KAIHACKS' ARCHITECTURE.md so other khai projects inherit the same engine-entry pattern. Files: docs/architecture/adrs/0004-engine-anchor-as-instructions-entry.md NEW docs/architecture/README.md +1 line (added ADR-0004 to the ADR index table) Refs: #577 (gender precedent), #581 (wow precedent), engine/org/ process_operate_org.md (existing process-anchor convention).
1 parent 45921cf commit 3a1ca37

3 files changed

Lines changed: 298 additions & 1 deletion

File tree

.validation-stamp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
89dcca5304caa327de78786d847d51703547770c
1+
286a859fbdfc43d8047923990efe602b73392002

docs/architecture/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ Decision Records under [`adrs/`](adrs/):
211211
| [0001](adrs/0001-subnational-cultures.md) | Sub-national cultures | Proposed |
212212
| [0002](adrs/0002-language-as-projection-subchapter.md) | Language as a Projection subchapter | Proposed |
213213
| [0003](adrs/0003-multi-variety-language-files.md) | Multi-variety language files and personas | Proposed |
214+
| [0004](adrs/0004-engine-anchor-as-instructions-entry.md) | Engine anchor files as instructions spine | Proposed |
214215

215216
ADRs follow the convention `NNNN-kebab-case.md`, numbered in
216217
acceptance order. An accepted ADR is immutable; a new decision that
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
# ADR-0004: Engine Anchor Files as Instructions Spine
2+
3+
**Status:** Proposed
4+
**Date:** 2026-05-27
5+
**Author:** KAI HACKS AI (LLM-with-maintainer collaboration)
6+
**Supersedes:** --
7+
**Superseded by:** --
8+
9+
## Context
10+
11+
The engine domain (`engine/`) carries the universal scaffold every culture
12+
uses. It has eight subfolders today, each modelling one universal aspect
13+
of personas and scenes:
14+
15+
```
16+
engine/adoption/ how an idea moves across a system
17+
engine/gender/ how a body is read before it speaks
18+
engine/group/ how a configuration holds people together
19+
engine/language/ how language passes between persona and room
20+
engine/org/ how an organisation refracts work through a claim
21+
engine/stress/ how demand exceeding capacity discharges
22+
engine/wow/ how a work life enters a room
23+
engine/stack/ (integration layer -- not a domain axis)
24+
```
25+
26+
Each subfolder ships position files (HOLD-shaped: Has / Orders / Loses /
27+
Drives) and / or process files (IDLE-shaped: Initiated by / Direction /
28+
Lever / Echo) plus a README. As axes were added one at a time, no formal
29+
contract existed for "what is the single entry point into this engine".
30+
Three problems followed:
31+
32+
1. **Inconsistent anchoring.** Some axes had a natural entry that named
33+
the axis itself (`engine/group/position_group.md`,
34+
`engine/org/process_operate_org.md`, `engine/adoption/process_adopt_idea.md`).
35+
Others didn't (gender had only `position_male.md` + `position_female.md`
36+
with no axis-level file; wow had 14 sibling positions with no anchor).
37+
38+
2. **`instructions.md` drift.** The runtime spine
39+
(`engine/stack/instructions.md`) references engine entries in prose to
40+
tell agents what to invoke -- but only five of seven engine axes were
41+
referenced (language, stress, adoption, group, org). gender and wow
42+
were structurally invokable but operationally invisible: the runtime
43+
had no canonical pointer to them.
44+
45+
3. **Discovery inside zips.** Engine READMEs are excluded from the
46+
shipped zips by `scripts/build_zips.py`. Readers (deployers, downstream
47+
LLM agents) inside an extracted culture pack cannot navigate via the
48+
README; they need a per-axis entry file they can find from its name
49+
alone.
50+
51+
Without a contract, every new engine axis re-litigates "what's the
52+
entry", `instructions.md` drifts further from completeness, and zip
53+
deployment can't reliably enumerate the engine surface.
54+
55+
PR #577 (gender anchor) and #581 (wow anchor) closed the immediate
56+
gaps, but the underlying pattern was implicit. This ADR makes it
57+
explicit so future engine axes follow it by default and
58+
`instructions.md` becomes a complete registry of invokable engines.
59+
60+
## Decision
61+
62+
1. **One anchor per engine axis.** Every domain subfolder under
63+
`engine/<axis>/` carries exactly one anchor file -- its single clear
64+
entry point. The integration layer (`engine/stack/`) is exempt: it
65+
has `stack.md` + `instructions.md` and is structurally distinct.
66+
67+
2. **Two anchor flavours, picked by what the axis is.**
68+
69+
| Flavour | File pattern | Shape | When |
70+
|---|---|---|---|
71+
| Position-anchor | `position_<axis>.md` | HOLD (Has / Orders / Loses / Drives) | The axis is something the room *reads* on the persona -- static, held continuously. |
72+
| Process-anchor | `process_<verb>_<axis>.md` or `process_<axis>.md` | IDLE (Initiated by / Direction / Lever / Echo) | The axis is something that *happens* -- dynamic, with an Initiated-by trigger and an Echo. |
73+
74+
The choice is editorial, made when the axis is introduced, and
75+
documented in the axis README.
76+
77+
3. **Anchor enumerates its expressions in prose.** The anchor file names
78+
every sibling expression in its body (Initiated by for processes,
79+
## Expressions / Has for positions) with inline sibling links, so a
80+
reader inside a zip can navigate from the anchor to every expression
81+
without depending on the README. Following the
82+
`process_operate_org.md` precedent (which links all four CVF
83+
positions) and `position_gender.md` README convention (which lists
84+
the male/female expressions).
85+
86+
4. **`engine/stack/instructions.md` is the registry.** Every engine
87+
anchor is referenced in `instructions.md` with an invocation mode:
88+
89+
| Mode | Meaning | Examples |
90+
|---|---|---|
91+
| **global** | The engine is invoked for every persona / every scene. | language, stress, gender, wow |
92+
| **per-khai-type** | The engine is invoked when a specific khai entity (other than `persona`) appears in scope. | group (when a group exists), org (when an organisation exists), adoption (when an idea-adoption arc is active) |
93+
94+
The two modes are mutually exclusive at the engine level. A persona
95+
always carries the global engines; the per-khai-type engines attach
96+
when their triggering entity is present.
97+
98+
## Architecture
99+
100+
### Anchor file shape
101+
102+
Frontmatter:
103+
104+
```yaml
105+
---
106+
khai: position # or "process"
107+
license: CC-BY-NC-4.0
108+
stamp:
109+
owner: KAI HACKS AI
110+
version: vX.Y.Z
111+
date: 'YYYY-MM-DD'
112+
---
113+
```
114+
115+
Body:
116+
117+
```
118+
# Position: <Axis> (or "# Process: <Verb>")
119+
120+
## Title
121+
<Axis> (or "<Verb>" for processes)
122+
123+
## Owner
124+
- Project: Cultures
125+
- Scope: Universal
126+
127+
---
128+
129+
## Has / Orders / Loses / Drives (HOLD, positions)
130+
## Initiated by / Direction / Lever / Echo (IDLE, processes)
131+
```
132+
133+
The `## Title` section is required by
134+
`khai_tests.components.test_has_title` for newly-added position files
135+
and recommended (consistency) for process files.
136+
137+
### Anchor → expressions linking
138+
139+
The anchor's prose body names every expression with a sibling link.
140+
The link surface lives in the section most natural to the flavour:
141+
142+
- **Position-anchor** (e.g. `position_gender.md`): the README's
143+
`## Expressions` section lists siblings; the anchor file itself may
144+
reference expressions abstractly without enumerating them.
145+
- **Process-anchor** (e.g. `process_operate_org.md`,
146+
`process_wow.md`): the **Initiated by** section enumerates the
147+
sub-axes / quadrants / channels the process refracts through, each
148+
with a sibling link.
149+
150+
Expressions are not required to backlink the anchor in their own prose
151+
(matches the existing convention: `position_clan_org.md` does not link
152+
`process_operate_org.md`). If the codebase later adopts comprehensive
153+
two-way navigation, that's a separate refactor across every engine
154+
axis.
155+
156+
### `instructions.md` registry
157+
158+
`engine/stack/instructions.md` carries a section that lists every
159+
engine anchor with its invocation mode. Shape (to be finalised in the
160+
implementation PR):
161+
162+
```markdown
163+
# Engines
164+
165+
Each engine declares one entry. The agent invokes it according to
166+
the engine's mode.
167+
168+
| Engine | Entry | Mode | Triggered by |
169+
|---|---|---|---|
170+
| gender | [../gender/position_gender.md](../gender/position_gender.md) | global | every persona |
171+
| language | [../language/process_using_language.md](../language/process_using_language.md) | global | every persona |
172+
| stress | [../stress/process_stress.md](../stress/process_stress.md) | global | every persona |
173+
| wow | [../wow/process_wow.md](../wow/process_wow.md) | global | every persona |
174+
| adoption | [../adoption/process_adopt_idea.md](../adoption/process_adopt_idea.md) | per-khai-type | `khai: idea` in scope |
175+
| group | [../group/position_group.md](../group/position_group.md) | per-khai-type | `khai: group` in scope |
176+
| org | [../org/process_operate_org.md](../org/process_operate_org.md) | per-khai-type | `khai: organisation` in scope |
177+
```
178+
179+
The existing prose in `instructions.md` (Human / Agent / Collaboration
180+
/ Knowledge / System) is preserved; the engine registry is an
181+
additional section that makes the global/per-khai-type contract
182+
explicit.
183+
184+
### Relationship to KAIHACKS
185+
186+
This pattern lives at the **khai-spine** level, not the Cultures
187+
project level. The Cultures repo holds the concrete instance:
188+
`engine/stack/instructions.md` is the Cultures-flavoured spine.
189+
Other projects building on khai (e.g. a different culture stack)
190+
follow the same anchor + mode contract for their own engines.
191+
192+
KAIHACKS' `ARCHITECTURE.md` documents the khai spine; this ADR is the
193+
Cultures-side concrete description of how engine anchors plug into
194+
that spine.
195+
196+
## Alternatives considered and rejected
197+
198+
- **One anchor flavour for all axes (always position, or always process).**
199+
Rejected: gender and group are read-shaped (static); adoption and
200+
stress are happening-shaped (dynamic). Forcing one flavour onto both
201+
loses the distinction the codebase already uses naturally. Picking
202+
per axis is an editorial cost paid once at introduction.
203+
204+
- **README as the anchor.** Rejected: engine READMEs are excluded from
205+
shipped zips by `scripts/build_zips.py`. A reader inside an
206+
extracted pack cannot reach the README; the anchor must be a `.md`
207+
file that ships.
208+
209+
- **Multiple anchors per axis.** Rejected: violates "one clear entry".
210+
Without a single canonical entry, `instructions.md` cannot reference
211+
the engine unambiguously.
212+
213+
- **Machine-readable anchor declaration (frontmatter field
214+
`engine_anchor: true`).** Rejected for now: the file-name convention
215+
(`position_<axis>.md` or `process_<verb>_<axis>.md` matching the
216+
containing directory name) is sufficient for the validator to find
217+
the anchor by inspection. A frontmatter field would add validator
218+
surface without giving the runtime new capability. Reconsider if a
219+
future need demands programmatic detection that can't rely on
220+
filenames.
221+
222+
- **Registry file separate from `instructions.md`.** Rejected:
223+
`instructions.md` is already the spine and the natural home for "what
224+
the agent invokes". Splitting the registry into a second file creates
225+
a synchronisation duty between two files for no clear gain.
226+
227+
- **Comprehensive expression -> anchor backlinks.** Rejected for this
228+
ADR: not done in any existing engine axis (e.g.
229+
`position_clan_org.md` does not link `process_operate_org.md`). If
230+
adopted, it would be a separate refactor touching every position and
231+
process file in `engine/`. Out of scope here.
232+
233+
## Consequences
234+
235+
**Positive:**
236+
237+
- Every engine axis has a discoverable entry. New axes follow a known
238+
pattern; reviewer cost on "what's the entry" drops to zero.
239+
- `instructions.md` becomes a complete, machine-readable-ish registry
240+
of invokable engines. The global vs per-khai-type mode is explicit;
241+
the agent can load global engines eagerly and gate per-khai-type
242+
engines on entity presence.
243+
- Zip deployment carries every anchor (anchors are not README-style;
244+
they ship). A downstream LLM agent reading the culture pack can find
245+
the entries by name.
246+
- The position/process distinction at anchor level mirrors the
247+
position/process distinction at expression level -- one shape, two
248+
scopes (anchor + expression).
249+
250+
**Negative:**
251+
252+
- The position/process choice is editorial: a new engine axis still
253+
needs a human (or LLM-plus-human) call on which flavour fits. The
254+
ADR offers heuristics but not a mechanical rule.
255+
- Every engine README needs an `## Anchor` section. Gender (#577) and
256+
wow (#581) added theirs; adoption / language / stress / org / group
257+
pre-date this ADR and have their entries but not the formal `## Anchor`
258+
framing. Bringing them to convention is follow-up work, not blocking.
259+
- The global / per-khai-type mode declaration only lives in
260+
`instructions.md` -- if the file is missing or stale, the runtime
261+
has no source of truth. The ADR accepts this trade-off in exchange
262+
for keeping the registry in one place.
263+
264+
## Open follow-ups
265+
266+
1. **Implementation PR for `instructions.md`.** Add the engines
267+
registry section listing all seven domain axes with their modes
268+
(governance branch -- the file is engine/stack/, which is engine/
269+
territory under `culture/engine` scope).
270+
271+
2. **Backfill `## Anchor` sections.** adoption, language, stress, org,
272+
group READMEs already have entries in practice (e.g.
273+
`process_operate_org.md` is the wrapper root) but no explicit
274+
`## Anchor` section. A follow-up culture/engine PR adds the
275+
convention uniformly.
276+
277+
3. **Validator: one anchor per axis.** Optional governance follow-up:
278+
a test in `tests/` that walks `engine/*/` and asserts exactly one
279+
anchor file exists per domain subfolder, named after the directory.
280+
Catches the next "we forgot the anchor" regression at CI time.
281+
282+
4. **KAIHACKS-side spec.** Mirror this contract in KAIHACKS'
283+
`ARCHITECTURE.md` at the khai-spine level, so non-Cultures khai
284+
projects inherit the same engine-entry pattern. The Cultures-side
285+
ADR is the concrete instance; the KAIHACKS-side spec is the
286+
abstract spec.
287+
288+
5. **Per-khai-type trigger formalisation.** "Triggered by `khai: X` in
289+
scope" is descriptive prose. If the runtime grows a formal scope
290+
model (e.g. "what khai entities exist in the current scene"), the
291+
trigger column in `instructions.md` could become machine-checkable.
292+
Deferred until that runtime exists.
293+
294+
---
295+
296+
*ADR-0004 -- v0.1.0 -- KAI Worlds*

0 commit comments

Comments
 (0)