gstack/docs/SYSTEM_MD.md

130 lines
4.9 KiB
Markdown

# SYSTEM.md — the semantic contract graph
`SYSTEM.md` is an optional repo-root file declaring what each component
*is*, what it *owns*, and the role-level contracts between components.
Its consumer is `/plan-rollout`. The skill reads it to sharpen slice
ordering and surface coordinated-deploy edges; it falls back to path
heuristics when absent.
Not a package manifest. Not an import graph. Those are discovered at
runtime (AST, grep, manifests) and would go stale within a week if
declared. SYSTEM.md captures only what a human knows: "auth mints
session tokens middleware enforces; session-format change without
middleware redeploy breaks sessions."
## Schema (v1 — intra-repo)
```yaml
---
version: 1
components:
- name: <unique identifier>
path: <repo-relative path>
kind: component # or: leaf-util | types-only (default: component)
role: <one-line job description>
owns:
- <data surface, table, API, or feature this component owns>
contracts:
- with: <other component name>
nature: <plain-English relationship>
breaks-if: <specific human action that violates the contract>
rollout-edge: <hard | soft>
note: <runtime-only | types-only | legacy | free text> # optional
rollout-order: <integer; lower ships first; equal = parallel>
---
# System Map
<Free-form human notes: stability, anti-patterns, deploy-edge lessons
from past incidents. Not parsed.>
```
### Field reference
| Field | Meaning |
|-------|---------|
| `name` | Unique identifier referenced by other components' contracts. Renames cascade. |
| `path` | File or dir under the repo root. Used for component-membership lookups. |
| `kind` | `component` (default), `leaf-util` (utils dirs, no contracts), `types-only` (interfaces, no runtime). leaf/types are skipped during reconciliation. |
| `role` | One sentence: what this component does in the system. Not what it contains. |
| `owns` | Data surfaces / tables / APIs / features this component is source-of-truth for. Dual ownership is a design smell. |
| `contracts[].with` | Other component's name. Must match a declared component. |
| `contracts[].nature` | Plain-English description of the relationship. |
| `contracts[].breaks-if` | Specific human action that violates the contract. This is what `/plan-rollout` reads to detect coordinated-deploy stages. |
| `contracts[].rollout-edge` | `hard` = must deploy together. `soft` = can lag. |
| `contracts[].note` | `runtime-only` (DB/HTTP/bus coupling — no import edge expected), `types-only`, `legacy`, or free text. |
| `rollout-order` | Integer. Lower ships first. Equal values can ship in parallel. |
## Example
```yaml
---
version: 1
components:
- name: auth
path: src/auth
role: authentication + session lifecycle
owns: [user table, session table, JWT minting]
contracts:
- with: middleware
nature: middleware enforces session tokens auth mints
breaks-if: session payload schema changes without middleware redeploy
rollout-edge: hard
rollout-order: 1
- name: middleware
path: src/middleware
role: request routing + auth enforcement
owns: [request context shape]
contracts:
- with: gateway
nature: gateway consumes req.user set by middleware
breaks-if: req.user shape changes without gateway redeploy
rollout-edge: hard
rollout-order: 2
- name: gateway
path: src/gateway
role: external HTTP surface
owns: [public API schema]
contracts: []
rollout-order: 3
- name: utils
path: src/utils
kind: leaf-util
role: shared helpers — imported freely without contracts
owns: []
contracts: []
rollout-order: 0
---
# System Map
auth and middleware are the security boundary. Session-format or
user-context shape changes are coordinated deploys (rollout-edge: hard).
We learned this in Feb 2025 when a session serializer change shipped
40 minutes ahead of middleware and logged everyone out.
```
## How /plan-rollout uses it
1. **File → component mapping.** Changed files are bucketed into the
slice for their component (matched by `path`, longest-match wins).
Without SYSTEM.md, the skill falls back to top-level-dir bucketing.
2. **Slice ordering.** Slices are sorted by `rollout-order`. `leaf-util`
and `types-only` components float to slice 0.
3. **Hard-edge enforcement.** When a changed-file set spans both sides
of a `rollout-edge: hard` contract, those files merge into one slice
tagged "coordinated deploy required — \<breaks-if reason\>".
4. **Reconciliation flags (informational).** Mismatches between declared
contracts and discovered imports surface in the output:
`import-without-contract`, `contract-without-imports`,
`rollout-order-inversion`. Never blocking.
## Out of scope (v1)
No scaffolder. Write SYSTEM.md by hand or copy the example. Scaffolding
that walks top-level dirs and infers `role` from README/`package.json`
is a v2 follow-up.