diff --git a/docs/architecture/07-deployment-view.md b/docs/architecture/07-deployment-view.md index 56daaf37..edfe9005 100644 --- a/docs/architecture/07-deployment-view.md +++ b/docs/architecture/07-deployment-view.md @@ -49,9 +49,9 @@ checks, with hooks only. ## 7.4 Version updates -The team moves the pin, by hand, when a release holds a rule it wants -(chapter 6.5). A fork may add an updater, a bot that watches releases and -proposes the move to its projects. Any updater follows these rules. +The team moves the pin, by hand, when a release holds a rule it wants. A +fork may add an updater, a bot that watches releases and proposes the move +to its projects. Any updater follows these rules. | Rule | Source | |------|--------| diff --git a/docs/architecture/08-crosscutting-concepts.md b/docs/architecture/08-crosscutting-concepts.md index a33aa1fd..bd6f8479 100644 --- a/docs/architecture/08-crosscutting-concepts.md +++ b/docs/architecture/08-crosscutting-concepts.md @@ -1,5 +1,10 @@ # 8. Crosscutting concepts +The concepts that hold across every template, tool and fork. They cover +how sections combine, which rule wins, how far a reference reaches, what a +fork may change, what 3.x freezes, how rules are written, and what a root +costs in context. + ## 8.1 Sections, IDs and directives A section that something references carries a unique ID. A later template @@ -31,13 +36,13 @@ A heading may carry several tag lines: - `[DEPENDS ON:]` declares file-level dependencies; it is metadata, not content, and may be split over several lines to keep the width - `[EXTEND:]` or `[OVERRIDE:]` declares the relationship to a parent -- `[LOAD:]` says where the heading's rules load: a kind of task the - manifest's `tasks:` lists, such as `release`, or `check` for rules a +- `[LOAD:]` says where the heading's rules load, either a kind of task + the manifest's `tasks:` lists, such as `release`, or `check` for rules a check enforces. A heading without one loads on every turn, and a - heading's subheadings take its load (ADR-058) -- A heading carries one load at most, among its tags; a template that - guards data, credentials or authentication carries none, and SYS-25 - checks all three + heading's subheadings take its load (ADR-058, proposed) +- A heading carries one load at most, among its tags, and none under a + heading that has one. A template that guards data, credentials or + authentication carries none. SYS-25 checks each of these - A section runs from its tags to the next `[ID:]` or the end of the file, and is never empty - Shipped IDs are frozen: never removed, renamed or moved to another file @@ -61,15 +66,17 @@ extends it, and the interview answer "use BDD-style names" overrides both. ## 8.3 Conflicts -| Situation | Resolution | -|-----------|------------| -| One template extends, another overrides one ID | The override replaces the base; the extension applies on top | -| Two templates override one ID | Error: the agent shows both and asks which applies | -| Two templates extend one ID | Both apply, in the stack's declared dependency order | +When two templates in one chain name the same ID, the kind of each +settles which rule applies. + +| Situation | Resolution | Source | +|-----------|------------|--------| +| One template extends, another overrides one ID | The override replaces the base, and the extension applies on top | FR-04-03 | +| Two templates override one ID | The chain is refused, and TPL-10 fails it | FR-04-04, ADR-039 | +| Two templates extend one ID | Both apply, in the stack's declared dependency order | FR-04-03 | -A chain never overrides one ID twice, and `SAIT-SMK-TPL-10-001A` fails one -that does (ADR-039). To specialise across three levels, each override -declares an ID of its own and the next level overrides that: +To specialise across three levels, each override declares an ID of its own +and the next level overrides that: ```text frontend/content.md [ID: static-site-content] @@ -77,81 +84,77 @@ stack/static-site-astro.md [ID: astro-content] [OVERRIDE: static-site-cont stack/static-site-tutorial.md [ID: tutorial-content] [OVERRIDE: astro-content] ``` -A conflict the rules cannot settle goes to the user: show both rules, ask -which wins, and record the answer in the generated file. - -## 8.4 Reach +## 8.4 Chain reach A rule that names another file's section in running prose is not a -directive and is not resolved. It reads correctly only in chains that carry -both files. Never name a section from a file that is absent from some chain -carrying yours; state the substance inline. SYS-11 checks this per chain -(ADR-028). +directive, and the resolver does not resolve it. It reads correctly only in +chains that carry both files. Never name a section from a file that is +absent from some chain carrying yours; state the substance inline. SYS-11 +checks this per chain (FR-01-03, ADR-028). -An orthogonal template is the sharpest case: its guaranteed context is the +An orthogonal template is the sharpest case. Its guaranteed context is the core tier plus its own `depends_on` tree, so every `[EXTEND]` and prose reference in it resolves there (ADR-035). When a rule could live in either of two templates, measure both files' chain reach and put it in the wider one. -## 8.5 Governed tiers +## 8.5 Opt-in tier membership Two opt-in tiers are not left to what the dependency declarations produce. Membership is stated by stack category, the `layer` a stack declares, with -a reason per exempt category, and a smoke check enforces it both ways -(ADR-044). - -| Tier | Exempt categories | Reason | Check | -|------|-------------------|--------|-------| -| Security | library, embedded | A library's caller owns the trust boundary; an embedded target has no HTTP surface, sessions or TLS | SYS-15 | -| Quality gates | Named by a fork in `tests/fork_checks.py` | A category at the most constrained context tier cannot admit the tier without changing which models can run it | SYS-18 | - -A list of stack IDs is what this replaces: correct the day it is written, +a reason for each exempt category, and a smoke check enforces it both +ways. A list of stack IDs would be correct the day it is written and silently wrong at the next stack. +| Tier | Exempt categories | Reason | Check | Source | +|------|-------------------|--------|-------|--------| +| Security | library, embedded | A library's caller owns the trust boundary; an embedded target has no HTTP surface, sessions or TLS | SYS-15 | FR-02-02, ADR-044 | +| Quality gates | Named by a fork in `tests/fork_checks.py` | A category at the most constrained context tier cannot admit the tier without changing which models can run it | SYS-18 | ADR-044 | + ## 8.6 Fork contract -| Mechanism | Rule | -|-----------|------| -| Sync by merge | A fork stays current with `git merge upstream/main`, never by copying files | -| Keys of its own | A fork appends under `stacks:`, `platform:` and keys of its own, never `core:`, `base:`, `backend:` or `frontend:`. SYS-20 checks the upstream keys against `SAIT_UPSTREAM_REF` | -| `fork_core` | Joins every chain's core tier after `core:`; an entry there depends only on core-tier IDs. SYS-21 checks it | -| Namespaces | Upstream holds `base-*`, `backend-*`, `frontend-*` and its template directories; forks hold `stack-*`, `platform-*` and `-*`. A duplicate ID fails MNF-01 and stops the resolver | -| `Fork-merge: ours` | Marks an upstream commit that removes what forks carry; ADR-049, "Merging the split", gives the commands | -| Fork-owned files | `README.md`, `AGENTS.md`, `CLAUDE.md` and `CHANGELOG.md`; the fork keeps its side on every merge | -| Fork-owned modules | `tests/fork_checks.py`, `tests/fork_dispositions.py` and `tools/fork_examples.py`, loaded when present. `fork_examples.py` may set `CHAIN_EXAMPLES_DOC`, default `docs/architecture/06-runtime-view.md` | +What a fork may change, and how it stays current with upstream. + +| Mechanism | Rule | Source | +|-----------|------|--------| +| Sync by merge | A fork stays current with `git merge upstream/main`, never by copying files | FR-08-01, ADR-049 | +| Keys of its own | A fork appends under `stacks:`, `platform:` and keys of its own, never `core:`, `base:`, `backend:` or `frontend:`. SYS-20 checks the upstream keys against `SAIT_UPSTREAM_REF` | FR-08-02 | +| `fork_core` | Joins every chain's core tier after `core:`; an entry there depends only on core-tier IDs. SYS-21 checks it | FR-03-02, FR-08-03 | +| Namespaces | Upstream holds `base-*`, `backend-*`, `frontend-*` and its template directories; forks hold `stack-*`, `platform-*` and `-*`. A duplicate ID fails MNF-01 and stops the resolver | FR-03-01 | +| `Fork-merge: ours` | Marks an upstream commit that removes what forks carry. The fork merges it with `-s ours`, keeping its own files | ADR-049 | +| Fork-owned files | `README.md`, `AGENTS.md`, `CLAUDE.md` and `CHANGELOG.md`; the fork keeps its side on every merge | ADR-050 | +| Fork-owned modules | `tests/fork_checks.py`, `tests/fork_dispositions.py` and `tools/fork_examples.py`, loaded when present. `fork_examples.py` may set `CHAIN_EXAMPLES_DOC`, default `docs/architecture/06-runtime-view.md` | ADR-050 | ## 8.7 Frozen surface -This is the one list of what 3.x freezes; other documents point here. From -3.0.0 no release removes, renames or changes the meaning of an element below -before 4.0.0, except for a security fix (ADR-051). A minor may add elements -and deprecate one by naming its replacement (8.8). +From 3.0.0 no release removes, renames or changes the meaning of an +element below before 4.0.0, except for a security fix. A minor may add +elements, and deprecate one by naming its replacement. | Element | What is frozen | Source | |---------|----------------|--------| | Template paths | The file path of every shipped template | ADR-051 | | Template IDs | The `id` of every manifest entry | ADR-051 | -| Section IDs | Every `[ID:]`, in the file that holds it, and the headings its section holds: a section does not move to another file, and a minor adds an ID only on new content, never in front of lines a shipped section holds. SYS-24 checks it against the newest 3.x tag | ADR-051 | +| Section IDs | Every `[ID:]`, in the file that holds it, and the headings its section holds. A section does not move to another file, and a minor adds an ID only on new content, never in front of lines a shipped section holds. SYS-24 checks it against the newest 3.x tag | ADR-051 | | Manifest schema | The fields the header comment of `templates/manifest.yaml` lists, and their meaning; a minor adds only optional fields | ADR-051 | | Core-tier membership | The IDs under `core:`; a new template is reached by declaring it, never by joining the core | ADR-051 | | Check exit codes | 0 passes, 1 fails, 3 does not apply | ADR-051 | -| Task kinds | The kinds under the manifest's `tasks:`, which a load directive names | ADR-058 | +| Task kinds | The kinds under the manifest's `tasks:`, which a load directive names | ADR-058, proposed | | Hooks | A shipped hook ID, its direct exit codes, its `not applicable` line and its violation codes | ADR-055 | | Hook arguments | The name and default of each argument a consumer writes into its config, such as `--path`, `--forbid` and `--base`; a minor adds only arguments whose default keeps the hook's behaviour | ADR-051, applied to hooks | | Fork entry points | The fork-owned module names (8.6), and what upstream reads from them: `bind`, `CHECKS`, `TEMPLATE_DIRS`, `CORE_DIRS` and `QUALITY_GATES_EXEMPT_LAYERS` from `fork_checks.py`; `CHECKS` and `SKIP_BUDGET` from `fork_dispositions.py`; `SPEC_CHAIN_EXAMPLES` and `CHAIN_EXAMPLES_DOC` from `fork_examples.py`. The disposition words `tests/conformance` defines for a fork to import | ADR-051, applied to forks | | Fork baseline | The `SAIT_UPSTREAM_REF` variable smoke reads | ADR-051, applied to forks | | Resolver CLI | `tools/resolve_chain.py ` and its `--concat`, `--generate`, `--list`, `--roots` and `--check` flags | ADR-051, applied to forks | -What is not frozen: +These elements are not frozen. -| Element | Why | -|---------|-----| -| The wording inside a section | Prose is free while the section keeps its ID (ADR-051) | -| The declaration block | A target with no producer and no reader (ADR-053) | -| The fixture: `stack-fixture`, `platform-fixture` | Test input; no listing offers it and no project resolves it | -| The helpers `bind()` hands `fork_checks.py` | Every name in the smoke modules; a change to one is listed in the changelog | +| Element | Why | Source | +|---------|-----|--------| +| The wording inside a section | Prose is free while the section keeps its ID | ADR-051 | +| The declaration block | A target with no producer and no reader | ADR-053 | +| The fixture, `stack-fixture` and `platform-fixture` | Test input; no listing offers it and no project resolves it | - | +| The helpers `bind()` hands `fork_checks.py` | Every name in the smoke modules; a change to one is listed in the changelog | - | ## 8.8 Deprecation @@ -179,7 +182,7 @@ the changelog lists it under `Deprecated`. It keeps its frozen behaviour A rule is a plain imperative, read as MUST or MUST NOT, or carries an RFC 2119 keyword where strength matters (`templates/core/docs.md`, "Rule language"). A mechanically checkable rule names its command in a fence and -its pass condition beside it (ADR-031). +its pass condition beside it (FR-01-01, FR-07-01, ADR-031). ```text templates//.md # cross-cutting: core, security, infra, data @@ -188,24 +191,23 @@ templates/backend/.md # backend layer templates/fixture/.md # test input ``` -A fork names its own files; the Imbra fork names a stack -`-.md`, such as `python-fastapi.md`. +A fork names its own files. ## 8.10 Context cost Every rule in a widely resolved file is read on every turn by every project -on that chain. A change that moves a stack category across a context-window -tier must record it in `tests/smoke/context-tiers.txt` in the same change -(SYS-16). Two design documents go deeper: - -- [agent-context-tradeoffs.md](../design/agent-context-tradeoffs.md): how - context size and startup design affect latency and quality -- [template-content-quality.md](../design/template-content-quality.md): the - measured criteria `tools/score_templates.py` scores - -What each root puts in context, as `resolve_chain.py --emit` builds it: -the root file an agent reads on every turn, and the skills that load only -when their task starts (ADR-058). The root file counts its fact slots too. +on that chain. A stack category's context-window tier moves only on +purpose, recorded in `tests/smoke/context-tiers.txt`, and SYS-16 checks it +(QG-02-02). Two design documents go deeper: + +- [agent-context-tradeoffs.md](../design/agent-context-tradeoffs.md), on + how context size and startup design affect latency and quality +- [template-content-quality.md](../design/template-content-quality.md), on + the measured criteria `tools/score_templates.py` scores + +The table shows what each root puts in context, as the emitter builds it. +The root file is read on every turn, and the skills load only when their +task starts. The root file's count includes its fact slots. | Root | Every turn, words | Skills | Skill words | diff --git a/docs/requirements.md b/docs/requirements.md index ae894010..4f72d15f 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -56,7 +56,7 @@ Status: skeleton. Each cluster holds a few requirements until its fill |----|-------------|--------|------------| | FR-05-01 | The system shall let an agent generate a context file by reading Markdown, with no build step between the fork and the file. | Chapter 6.1 | - | | FR-05-02 | The system shall have the agent write the file to the output contract in `templates/core/agents.md`. | Chapter 6.1 | - | -| FR-05-03 | The system shall send a conflict the rules cannot settle to the user, and record the answer in the generated file. | Chapter 8.3 | - | +| FR-05-03 | The system shall send a conflict the rules cannot settle to the user, and record the answer in the generated file. | Chapter 6.1 | - | | FR-05-04 | The system shall produce a context file that any agent reading Markdown can use, written to the file each agent reads. | `templates/core/docs.md`, "Output file by agent" | - | ### 06. Context file and task skills