# Chiltepin — the authoring skill (chiltepin 0.47.0) This is the complete skill `npx skills add jdiejim/chiltepin` installs into an AI agent: SKILL.md first, then every reference file. Site: https://chiltepin.dev · Docs: https://chiltepin.dev/docs ======================================================================== # FILE: SKILL.md ======================================================================== --- name: chiltepin description: >- Write, edit, validate, and render Chiltepin docs: Markdown with typed YAML blocks for diagrams, API references, ADRs, runbooks, and slides. Use when the user requests Chiltepin or the chiltepin CLI, or edits typed-block docs in a Chiltepin project (chiltepin.config.*). Preserve an explicitly requested format; installing this skill alone does not make every Markdown task a Chiltepin task. --- # Chiltepin — docs as Markdown with typed YAML blocks A doc is plain Markdown. Anything structured — a diagram, a table, a plan — is a fenced block whose info-string is the block type and whose body is YAML. The `.md` file is the only source of truth. Never paste HTML or SVG. Never place pixels: the renderer owns layout, you own content. ```` ## Request flow ```sequence actors: - { id: Client, name: Client } - { id: API, name: Orders API } messages: - Client -> API: POST /orders - API --> Client: 201 Created ``` ```` ## Fast path Use the project's installed CLI (`pnpm exec chiltepin` or `npx --no-install chiltepin`) to match its dependency version. Otherwise, `npx -y chiltepin …` downloads and runs the published CLI. In the Chiltepin source repo, use the built `node packages/cli/dist/bin.js`. The commands below show the fallback form. Detailed references live beside this file — read them on demand; the table at the end explains which reference each task needs. 1. **Pick the blocks from the reader's question**, not from the words in the request. Use the table below. For a full doc, two to five structural blocks often suffice. A single diagram request needs only that diagram. Use prose when a small list communicates the same information more clearly. Unsure which block exists: `npx -y chiltepin block` lists all 107 block types. 2. **Look up each block you will write**: `npx -y chiltepin block `. It prints the fields, enums, terse one-line forms, and a validating example. Read a family selection sheet only when the choice remains unclear. 3. **Write the doc.** For a full doc, put `meta` first (title, subtitle, tag). A `##` heading above a block is its title. Prose carries why and consequence, never a description of the block below it. Rules in the two sections after the table. 4. **Check**: `npx -y chiltepin check --json`. Every diagnostic carries a stable code and the failing value; `reference/check.md` maps each code to its fix. Fix errors and rerun while you make progress. Stop and report the blocker if a diagnostic repeats without a new fix or needs missing facts. Warnings do not fail the check by default; review them and report relevant ones. A non-zero exit is never "done". If the CLI is unavailable, report validation as unverified. Never invent a successful check. 5. **Render when asked**: `npx -y chiltepin html -p` (page) or `slides -p` (deck). Handoff: the file path, actual check result, and any unresolved diagnostics. Explain block selection only when the user asks or a tradeoff needs explanation. Editing an existing doc: read it whole first. Change the one block, and carry the fact into related blocks within the requested scope. Preserve unrelated content. Rewrite the whole document only when the user requests a rewrite. ## Pick the block by the reader's question | Reader question | Blocks | Choose by | |---|---|---| | What calls what? | `sequence` · `graph` · `c4` | ordered messages → sequence; topology at rest → graph or c4 | | What path does a request take through the infrastructure? | `block` · `c4` · `cluster` | tiers and hops → block; system context for a stakeholder → c4; namespaces and replicas → cluster | | What happens when this fails? | `flow` · `saga` · `state` · `sequence` | branching decisions → flow; multi-service undo → saga; one object's lifecycle → state | | Where did the time go? | `spans` · `sequence` | measured durations → spans; call order only → sequence | | What does the event carry, who emits and consumes it? | `eventcontract` · `table` | one event → eventcontract; a catalog → table | | Who publishes, who subscribes, how does work fan out? | `block` (`preset: event`) · `dfd` · `sequence` | topology of producers, topics, queues, consumers → block; the hop order with the failure branch → sequence; `reference/patterns.md` names the stack per pattern | | How does this ship, and what stops it? | `rollout` · `steps` · `timeline` | staged traffic with gates → rollout; manual procedure → steps; dated milestones → timeline | | What lives inside what? | `c4` · `cluster` · `block` · `layers` · `archmap` · `tree` · `composition` · `treemap` | runtime boundaries → c4/cluster/block; conceptual tiers → layers; capability landscape → archmap; part-of → tree/composition; area budget → treemap | | What changes over time? | `timeline` · `gantt` · `changelog` · `chart` · `slopegraph` · `state` | events → timeline; scheduled work → gantt; released work → changelog; a measured quantity → chart; two snapshots → slopegraph | | How do these options compare? | `options` · `proscons` · `matrix` · `scorecard` · `benchmark` · `quadrant` · `harvey` | criteria × candidates → options; one option → proscons; numbers → benchmark; two axes → quadrant | | Where does data go? | `dfd` · `sankey` · `erd` | processes and stores → dfd; volumes → sankey; shape at rest → erd | | Who does what, when? | `swimlane` · `journey` · `agenda` · `team` · `kanban` | ownership across steps → swimlane; experience over stages → journey; work in flight → kanban | | What are the exact steps? | `steps` · `flow` | linear → steps; branches or retries → flow | | What do we build, in what order? | `storymap` · `timeline` · `gantt` | scope per journey step by release → storymap | | How big, how fast, how much? | `bignumber` · `stats` · `chart` · `envelope` · `benchmark` | one headline → bignumber; a set → stats; napkin math → envelope | | What is this made of? | `anatomy` · `composition` · `erd` · `layers` | labeled parts of a string → anatomy; proportions → composition | | What causes this? | `fishbone` · `matrix` | one effect, branching causes → fishbone | | Why did we decide this? | `options` · `proscons` · `scqa` · `takeaways` · `callout` | the ADR shape → `reference/recipes.md`; the decision alone → callout | | What does the API accept and return? | `endpoint` · `code` · `packet` · `table` | HTTP surface → endpoint; wire format → packet; error codes → table | | How does the agent behave? | `agentloop` · `trace` · `prompt` · `context` | the loop → agentloop; one real run → trace; the contract → prompt; window contents → context | | What did the review find, and are we ready? | `audit` · `checklist` · `risk` | defects found with evidence → audit; a standard applied once → checklist; what might go wrong → risk | | Are we within budget, and how slow is the tail? | `perfbudget` · `percentiles` · `slo` · `benchmark` | targets with a pass line → perfbudget; p50…p99 per endpoint → percentiles; targets over time → slo | | Where can this be attacked? | `threatmodel` · `dfd` · `audit` | STRIDE on a data flow with trust boundaries → threatmodel; the flow alone → dfd | | Who uses the system for what, and which module may depend on which? | `usecase` · `pkg` · `uml` · `timing` | actors and cases → usecase; module dependencies → pkg; classes → uml; states over time with durations → timing; `reference/patterns-design.md` maps the GoF and distributed patterns to blocks | | What is the model's shape, and what may it be used for? | `neuralnet` · `modelcard` · `chart` | layers → neuralnet; the card → modelcard; loss curves → chart line | | What ships when, by theme, and where are we in the process? | `roadmap` · `chevrons` · `gantt` · `mindmap` | quarters × themes → roadmap; phases with the current one → chevrons; dated tasks → gantt; unordered ideas around a topic → mindmap | | What must always hold? | `spec` · `slo` · `glossary` · `callout` | invariants → spec; service targets → slo; terms → glossary | | What does the user see? | `wireframe` · `frontend` · `felogic` | screens → wireframe; component tree → frontend; module graph with edges → felogic | | How does the algorithm move through the data? | `array` · `linkedlist` · `bintree` · `hashmap` · `graph` · `code` | pointers, a window, or binary search over cells → array; pointer rewiring → linkedlist; a tree shape → bintree (never `tree`, that is a file hierarchy); hashing → hashmap; visit order → graph with node `state`; the reference implementation → code. A `flow` or `table` is the keyword trap here. | The type name is a hint, not a cage: a `quadrant` is any two-axis 2×2, a `journey` any staged progression, a `cvt` any before → after. Relabel every axis, column, and unit in the user's own nouns. Twelve old names still work as aliases (`infra` `event` `ddd` `network` → `block`, `belogic` → `felogic`, `dag` → `flow`, `waterfall` `funnel` → `chart`, `diff` `terminal` → `code`, `mece` → `tree`, `tracker` → `statustable`). Write the canonical name in new blocks; never rewrite an existing fence only to silence the `W_ALIAS_TYPE` warning. ## Writing rules - Use only the fields `chiltepin block ` prints. Schemas are strict: an unknown field is an error. - **Quote any YAML value that contains `,` `:` `#` `{` `}` or starts with a special character.** Inside `{ a: b, c: d }` an unquoted comma splits a phrase into keys. Numbers that must be strings (`version: "1.0"`, `delta: "0"`) get quotes. Prose fields (`desc`, `note`, `summary`, `description`) are always quoted. When unsure, write the body as JSON — it is valid YAML. - Prefer the terse one-line item forms the contract prints (`a -> b: label`, `Term — definition`). Switch to the object form only for a field the grammar cannot say. - Give a block an `id:` when another block references it; reference it as `doc#id`, or `#id` inside the same doc. A ref to a missing id fails the check. - Use the user's nouns verbatim, and the same name for the same thing in every block. Headings say what the reader sees, never the block type. - **Vary the lens.** One `callout` per doc (the assumptions), never a row of them: several points are a `list`, a `spec`, a `faq`, or `takeaways`. A third block of the same type is a warning (`W_LENS_REPEAT`). Reach past the habitual four (`callout`, `table`, `sequence`, `flow`): ownership across steps is a `swimlane`; code the reader will copy or diff is a `code` block (`kind: compare` for before / after); terms are a `glossary`; questions a reader will ask are a `faq`; a runbook is `steps`; side-by-side snippets or nested diagrams are a `gallery`. - Diagram data (node names, messages, labels, values) is never trimmed to fit. Split a dense diagram into two; `chiltepin check` warns at the caps. - Every arrow says what crosses it, as a verb phrase, never "uses". A `c4` edge without a label is a warning; at container level add `tech` too. Solid is a call, dashed is async or optional. Flow runs left to right or top to bottom, one direction per diagram. - `sequence`, `flow`, `erd`, `state`, and pie `chart` also accept a ```mermaid fence; `erd` accepts ```dbml and ```prisma. Subsets are in `reference/mermaid.md`. ## Prose rules `reference/style-ste.md` is the authority. Between blocks: three sentences per paragraph by default, five at most. Every sentence carries a fact, a decision, or a consequence. Banned openers: "In this section", "This diagram shows", "It's important to note", "At a high level". Block text fields keep every fact in short active sentences. Diagram data is untouchable. ## Read more only when the task needs it | File | When | |---|---| | `reference/blocks/INDEX.md` | Scanning every block with a one-line description (same as `chiltepin block`). | | `reference/blocks/.md` | Choosing between neighbours in one family — discriminators and hard rules the schema cannot express. | | `reference/writing.md` | The full terse-form table, every YAML trap, `doc#id`, naming. | | `reference/check.md` | A diagnostic code you do not recognise. | | `reference/recipes.md` | Composing a whole document: architecture, ADR, API reference, incident, pipeline, agent system. | | `reference/patterns.md` | Anything with events, queues, streams, fan-out, outbox, CQRS, sagas, retries: which blocks draw each pattern and the trap. | | `reference/patterns-design.md` | A GoF or architectural design pattern (Strategy, Observer, CQRS, Circuit breaker …): the block stack that documents it. | | `reference/system-design.md` | Any "design an X" ask — the eight-step method. | | `reference/intake.md` | A new document with an unclear reader or scope — the questions to ask back. | | `reference/decks.md` | Any slides or deck ask. | | `reference/organizing.md` | Multi-doc work — file naming, splitting, index docs. | | `reference/exemplars/*.md` | Ten finished documents to model on. | ======================================================================== # FILE: reference/blocks/agentic.md ======================================================================== # Chiltepin blocks — AI & agents Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Structure & emphasis — four fixed frames for one LLM agent: the loop (`agentloop`), one real episode (`trace`), the contract (`prompt`), and the window budget (`context`). **Answers**: What does the loop do? What can it call? What is the model told? What fills the window? What did a real run look like? They compose — the AI / agent recipe in `reference/recipes.md` stacks all four. **Not this family**: the architecture around the agent (services, queues, vector stores) → `block` (architecture.md; `kind: llm` / `agent` gets the violet card); one turn's message timing between services → `sequence` (flows.md). ### AI & agents #### `agentloop` — the canonical agent-loop diagram Environment left, agent card centre, tools stacked right, a memory cylinder below. Answers: what does one loop turn do, and what can the agent call? The four numbered arrows are fixed (prompt → tool call → result → response). List only tools the agent can call; the render shows 5 and folds the rest. The memory cylinder draws only when `memory:` is present. `agentloop` for the loop itself; `block` for the deployment around it. #### `trace` — an agent / session execution transcript A vertical transcript: one card per turn with a role chip (user, assistant, tool, system). Answers: what did one real episode do, step by step? Quote `args` and `result`: JSON braces and colons are YAML syntax. Block scalars (`|`) keep line breaks. `trace`, not `sequence`, to follow one conversation; `sequence` for the timing between services. #### `prompt` — prompt anatomy with variable highlighting Stacked segment cards with role kickers; every `{{variable}}` highlights as a chip. Answers: what is the model told, and where does each value come from? Quote any `text` that contains `{{ }}`: bare braces are YAML flow syntax. List each variable in `vars` so the legend explains it. `prompt`, not `code`, for templates and system prompts; `code` for programs. #### `context` — context-window token budget One horizontal bar sized against `window`, segments left to right, free space dim. Answers: what fills the window, and how much is left? A sum past `window` draws red past a dashed boundary with an "over budget" chip. Use it to show the failure case on purpose. `context` for token budgets; a waterfall `chart` for latency and cost. #### `neuralnet` — layered network One column per layer with `units`, `kind` (input / conv / pool / dense / attention / output …) and `activation`; dense mesh between layers, an ellipsis when a layer is wider than `maxUnits`. Answers: what is the model's shape? List the layers a reader would name, not every repeated block: fold "12 × transformer block" into one `attention` layer with a `note`. `neuralnet` for the architecture; `flow` (`variant: dag`) for the training pipeline; `chart` line for loss curves. #### `modelcard` — model card Identity, intended use and out-of-scope, training data, metrics per split, limitations, ethics. Answers: what is this model, and what may it be used for? The endpoint card for a model: one per deployed model version. `modelcard`, not `spec`, for a model; `benchmark` to compare candidates. ======================================================================== # FILE: reference/blocks/algorithms.md ======================================================================== # Chiltepin blocks — Algorithms & data structures Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Structure & emphasis — one data structure frozen at one step (`array`, `linkedlist`, `bintree`, `hashmap`). One step per block; freeze a moment, don't animate. **Answers**: What does the structure hold at this step, and where do the pointers stand? **Not this family**: graph algorithms (BFS / DFS / Dijkstra) → `graph` with node `state` + edge `weight` (charts-overviews.md); a file hierarchy → `tree` (charts-overviews.md); control flow → `flow` (flows.md). ### Algorithms & data structures All four blocks share one `tone` enum: `active` is the element under examination, `visited` is processed, `target` is the goal, `muted` is out of play. Quote numeric values (`value: "19"`); they are strings. #### `array` — array cells for algorithm walkthroughs A row of square cells, indices above, pointer labels (`lo`, `mid`) below. Answers: where do the pointers stand at this step? `window` outlines a 0-based inclusive index range. `array`, not `table`, for binary search, two pointers, and sliding windows; `table` for tabular data. #### `linkedlist` — pointer-chain diagram Boxed nodes joined by arrows; the chain ends in a ground symbol. Answers: which node does each pointer hold during a reversal or insertion? `kind: doubly` adds a back-arrow per link. Pointer labels render above. `linkedlist`, not `flow`, for pointer manipulation; `flow` for control flow. #### `bintree` — binary tree Nodes placed by parent and side; a parent centres over its children, so an unbalanced chain slants. Answers: which path does a search or traversal take? Every node with a `parent` must set `side`. Two children on one side is a schema error. Several parentless nodes draw as side-by-side roots (rotations). `bintree`, not `tree`, for BSTs, heaps, and traversals; `tree` for file hierarchies. #### `hashmap` — buckets + chained entries A column of bucket slots; entries in one bucket chain rightward in entry order. Answers: where does each key land, and which keys collide? An entry whose `bucket` is outside `0..buckets-1` is skipped, not clamped. The render caps at 12 buckets; keep the count readable. `hashmap`, not `table`, for hashing and collision walkthroughs; `table` for a plain key-value listing. ======================================================================== # FILE: reference/blocks/api.md ======================================================================== # Chiltepin blocks — API reference Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Structure & emphasis — one contract card per operation (`endpoint`) or per event (`eventcontract`) — plus Exchange at the byte level (`packet`). **Answers**: What can I call, with what, and what comes back? Who emits this event, who consumes it, and what does the payload guarantee? What does the wire carry, bit by bit? **Not this family**: how calls compose over time → `sequence` (flows.md); an error-code listing → `table` (tables-data.md); the API already has an OpenAPI spec → generate the cards with `chiltepin sync openapi`. ### API reference #### `endpoint` — a Swagger-style API endpoint card One card per operation: method pill, path, parameters, body, responses, and example request and response. Answers: what can I call, with what, and what comes back? Only `method` and `path` are required. For a whole spec, run `chiltepin sync openapi` instead of writing cards by hand. `endpoint`, not `sequence`, for the contract of one call; `sequence` for how calls compose over time. #### `eventcontract` — an async event contract, the twin of endpoint One card per event: a producers → consumers strip, delivery facts as chips, and the payload table with the partition key marked. Answers: who emits this, who consumes it, and what does the payload guarantee? Only `name` is required. The card needs no title. `eventcontract`, not `endpoint`, when the channel is a topic or queue and the caller never waits for a reply. #### `packet` — a wire format, bit by bit A bit ruler across the top, then fields whose cell width is their bit count. A field that overflows its row wraps and is marked `(cont.)`. The footer totals bits and bytes. Answers: what does the wire carry, bit by bit? `packet`, not `table`, when position and width on the wire are the point; `table` for a field listing. ======================================================================== # FILE: reference/blocks/architecture.md ======================================================================== # Chiltepin blocks — Architecture Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Containment — boundaries and what lives inside them (`c4`, `block` + presets, `cluster`, `archmap`) — and Network for module, class, and actor graphs (`felogic`, `frontend`, `uml`, `usecase`, `pkg`). **Answers**: What lives inside which boundary? What depends on what, at rest? **Not this family**: the order of calls → `sequence` (flows.md); branching or a lifecycle → `flow` / `state` (flows.md); data shape → `erd` (data-model.md); tiers with no arrows → `layers`; area by number → `treemap`; a CI/CD pipeline → `flow` with `variant: dag`. #### `c4` — context / container / component Boxes with kind chips inside dashed boundaries; the frame tag shows the level. Answers one question per level. `c4`, not `block`, when C4 levels and system boundaries carry the message; `sequence` for the order of calls. - `level` is required, and one diagram holds one level. Never mix levels. - Context: who uses it and what does it talk to. `person`, `system` (usually one), and `external` only. 4–8 nodes. - Container: the deployable pieces. One `boundary` (or `boundaries[]`) is the system; every container carries `tech`; persons and externals sit outside. 5–9 nodes; past that, split the diagram. - Component: inside ONE container, named in the title; `family` codes the layer. - Every edge is a sentence: `label` an active verb phrase, `tech` the protocol. One arrow from the caller; the reply is implied. Async edges are `dashed`. - Externals are things you do not deploy. If your team owns it, it is a container. - Omit `col`/`row` on every node for auto-layout; `dir: TB` flips it. #### `block` — grid architecture with optional groups Boxes and arrows on a grid. Known `kind`s (db, queue, cache, gateway, cdn, and vendor names like postgres, s3, kafka, redis) get a glyph and a shape; an unknown kind draws a plain box. Answers: what talks to what, at rest? `block`, not `c4`, for free kinds, nested zones, and presets. - `gateway`, `lb`, `proxy`, and `ingress` draw as the tall vertical bar of system-design diagrams. It spans the rows of the services it fans out to on its own; set `h` to choose the span. Put the bar in its own column. - `preset` (infra, event, ddd, network, k8s) changes only the framing: the tag, the eyebrow, and which kind is the accent entry. The YAML is the same. - Omit `col`/`row` on every node for auto-layout. Use coordinates for a deliberate shape, and always with `groups`. - `layers:` switches to horizontal bands; nodes then use `layer`, not `col`/`row`. Do not mix the two modes. - `groups` nest by overlap (the larger paints first) or by `parent`. A child's cells must lie inside its parent's range (`W_GROUP_NESTING`). - `replicas: N` (2 or more) draws a stacked card. For a database replica set use two nodes and a dashed `replicates` edge. - `preset: k8s`: a namespace is a group and `ingress` is the entry; nest namespaces inside a cluster with `parent`. ```block groups: - { id: vpc, col: 1, row: 1, cols: 2, rows: 2, label: VPC } - { id: pub, parent: vpc, col: 1, row: 1, cols: 2, rows: 1, label: Public subnet } - { id: priv, parent: vpc, col: 1, row: 2, cols: 2, rows: 1, label: Private subnet } nodes: - { id: alb, col: 1, row: 1, kind: gateway, name: ALB } - { id: api, col: 1, row: 2, kind: service, name: orders-api, replicas: 3 } edges: - alb -> api ``` #### `cluster` — k8s-style nested boxes with services Namespace boxes holding service cards with replica bars; a single `gateway` service takes the accent. Answers: which services run in which namespace? `block` with `preset: k8s` for nested namespaces or a mixed cloud + cluster map. #### `archmap` — target-architecture capability map A mosaic of tinted domain areas packed with capability tiles. A plain string is a current capability; `status` marks target, new, gap, or deprecated. Answers: what lives in each domain? `block` when the arrows between systems matter. #### `felogic` — frontend / backend module graph Module boxes with UML stereotype banners (interface, controller, service, repository) and typed edges. `variant: be` changes only the framing. Answers: which module uses or implements which? Omit `col`/`row` and `groups` for auto-layout. `felogic`, not `uml`, for a module graph. #### `frontend` — top-down component tree Parents above children with link paths, one `root`. Answers: how do the components nest? `frontend`, not `tree`, for a UI component tree with kinds (layout, page, hook, store). #### `uml` — class diagram Class boxes with attributes and methods; the relation `kind` drives the arrow marker. Answers: which classes inherit, implement, or depend on which? `uml`, not `erd`, for classes with behaviour; `erd` for tables and cardinality. #### `usecase` — UML use-case diagram Actors outside the `system` boundary, cases (verb phrases, ≤ 12) inside, `links` as `actor -> case`, `relations` `kind: include | extend | generalize`. Answers: who uses it for what? #### `pkg` — UML package diagram Tabbed folders with `contains` members, `parent` to nest, `deps` dashed with `kind: import | use | access | merge`. Answers: which module may depend on which? ======================================================================== # FILE: reference/blocks/business.md ======================================================================== # Chiltepin blocks — Business, decisions & access Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Grid — compare, score, locate (`matrix`, `scorecard`, `harvey`, `swot`, `scenarios`); cards for decisions, strategy, and access (the rest). **Answers**: How do the options compare, and which won? Who may do what? What forces shaped this design? Do the numbers pencil out? **Not this family**: measured numbers → `benchmark` (tables-data.md); one option's tradeoffs → `proscons` (planning.md); the decision in one line → `callout` (narrative.md); day-to-day task state → `statustable` (planning.md). #### `matrix` — a role × resource capability grid Rows are roles, columns resources, each cell a permission level tinted by meaning. Answers: who may do what? `matrix`, not `table`, when every cell is a permission. #### `anatomy` — the parts of a structured string (e.g. a permission) The full string with each segment coloured, then one card per segment. Answers: what does each part of this identifier mean? One string per block. #### `composition` — effective access as intersected gates Renders gate ∩ gate ∩ gate = result. Answers: which independent checks must all pass? `composition`, not `flow`, when access is an AND of checks rather than an ordered sequence. #### `drivers` — the forces that shaped a design A card grid, one card per driver with an icon and a tag; each is a real requirement with its consequence. Answers: why is the design like this? `list` for plain points. #### `team` — people cards (who owns what) Compact cards: initials avatar, name, role, one-line focus; set `initials` for a group. Answers: who owns what? `team` for real people; `persona` for user archetypes. #### `options` — approaches explored, with a verdict One card per option: how, pros, cons, verdict; `tone: chosen` marks the winner. Answers: which approaches did we weigh, and which won? `options`, not `proscons`, for several candidates; `proscons` weighs one. #### `scorecard` — a weighted decision matrix Criteria as rows, options as columns, a weighted TOTAL row; the winner is derived. Answers: which option scores highest? `scorecard` when the decision was scored; `options` for qualitative verdicts; `harvey` for judgements. #### `spec` — a labelled spec sheet A fact sheet for one approach or component; a row with `steps` draws a pill flow. Answers: what are the facts of this one thing? `spec`, not `table`, for one subject. #### `envelope` — back-of-envelope capacity math Givens, one derivation row per step, then a highlighted bottom line. Every value is a string; write units and `≈` freely. Answers: do the numbers pencil out? `envelope` for the estimate that justifies a design; `stats` for KPIs. #### `swot` — strengths / weaknesses / opportunities / threats The 2×2 draws itself from four string lists; an empty quadrant still draws. Answers: where do we stand? `swot` for a position; `quadrant` to plot items. #### `okr` — objectives + key results One card per objective, a progress bar per key result coloured by status. Answers: how far are we on each goal? `slo` for reliability; `statustable` for tasks. #### `persona` — user persona cards Cards with an avatar, role, quote, goals, frustrations, and tool chips. Answers: who do we build for? `persona` for archetypes; `team` for people. #### `wardley` — value chain against evolution Components plotted by user visibility (up) and evolution (right), both 0–1; `movement` draws where one is heading. Answers: what do we build, and what do we buy? A position on the map replaces an opinion. #### `harvey` — the rated comparison Options across, criteria down, a filled ball per judgement (0–4), a weighted footer. A short `ratings` row means "not assessed", not zero. Answers: which option fits best? `harvey` for judgements; `benchmark` for measured numbers. #### `scqa` — the executive summary, in Minto order Situation, complication, question as a ladder; the answer as the filled card with its support. Answers: what is the recommendation, and why? The order is fixed; keeping it is the block's job. #### `scenarios` — base, upside and downside Cases as columns, drivers as rows, the outcome in its own row; the base case is badged. A missing value renders `·`, not zero. Answers: how much of the outcome hangs on each assumption? ======================================================================== # FILE: reference/blocks/charts-overviews.md ======================================================================== # Chiltepin blocks — Charts & overviews Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: the widest family. Time (`chart`, `gantt`, `journey`, `slopegraph`); Grid (`heatmap`, `quadrant`); Flow (`sankey`); Containment (`treemap`, `venn`); Network (`graph`, `mindmap`); hierarchy and proportion (`tree`, `pyramid`, `chart` kinds); causes behind one outcome (`fishbone`). **Answers**: What changes over time? How does the whole split? Where does the volume go? Where do items sit on two axes? What causes this? **Not this family**: one headline number → `bignumber`; a few KPIs with trends → `stats`; exact values → `table`; boundaries → architecture.md. #### `graph` — node-link graph Nodes and edges on a grid, no nesting; `weight` renders on the edge pill. Answers: what connects to what? For BFS / DFS / Dijkstra walkthroughs set node `state` (visited, current, frontier, target). `graph`, not `flow`, with no start or end. #### `tree` — indented hierarchy (HTML, not SVG) Nodes by `parent`. Plain: an indented outline. `variant: issue`: a MECE issue tree, left to right. `variant: org`: a top-down org chart with `role` under each name; more than 6 reports stack in two columns. `value` on nodes makes a driver tree: each node shows its number and its share of its parent. Answers: how does this break down? `tree`, not `bintree`, for hierarchies; `fishbone` for causes. #### `mindmap` — radial idea map One `center`, branches right and left, children on each, `accent` per branch. Answers: what belongs to this topic? `tree` for a directed hierarchy; `fishbone` for causes. #### `gantt` — schedule bars Task bars across named periods, tinted by `kind`. Answers: what runs when? `gantt`, not `timeline`, when bars span periods. #### `chart` — a data chart (bar / stacked / line / area / scatter / donut / pie / gauge / radar / waterfall / funnel / pareto / histogram / bell / boxplot / bullet) `labels` + `series` drive bar, stacked, line, area, radar, and category scatter; `items` drive donut, pie, gauge, waterfall, funnel, pareto; `points` drive a numeric scatter with `guides`; `values` drive histogram and bell (or give `mean` + `sd` and `markers`); `boxes` drive boxplot; `bullets` drive bullet. Answers: how does the number move or split? Pick the kind by the question: stacked when the total matters as much as the split; gauge for one number against a ceiling; donut for a whole that sums; waterfall for parts against a `budget`; funnel for drop-off between ordered stages; radar needs 3+ labels; pareto for the few causes behind most of the effect (80% rule drawn); histogram for how raw values spread; bell for a normal curve with named points; boxplot to compare spreads; bullet for a measure vs a target. #### `sankey` — how much moves between stages Node height and ribbon width share one scale; nodes are inferred from the links. Answers: where does the volume go? Declare `nodes` only for a label, an accent, or a pinned `col`. `sankey` for volumes; `flow` for the path; funnel `chart` for drop-off. #### `treemap` — proportional composition Squarified tiles, area = value, biggest first. Answers: what dominates the whole? `treemap`, not donut, past six slices. #### `venn` — two or three overlapping sets Fixed circles; `shared.sets` names set labels and puts a label in that lens. Answers: what do two groups share? A Venn names regions; it never measures. #### `fishbone` — cause & effect (Ishikawa) One effect at the head, cause categories as bones, specific causes along each bone. Keep 1–8 bones and up to 8 short items per bone. Answers: what causes this? `fishbone`, not `tree`, for suspected causes behind one outcome. #### `slopegraph` — ranked before / after One line per item between two value columns on a shared linear scale; crossings are the story. Quote `left` / `right` years. Give `accent` to the one or two lines that matter. Answers: what rose, what fell, what held? `slopegraph` for many items at two points; `chart` line for a few series over many points. #### `heatmap` — a numeric grid with an intensity ramp Cells tinted light-to-deep on one ramp, normalised to the data (or `min` / `max`). Answers: where is it hot? `heatmap` for a dense value grid; `matrix` for categorical cells; `table` when the reader needs exact rows. #### `pyramid` — stacked hierarchy (top → bottom widening) Levels that widen downward, each with a description. Answers: what rests on what? `pyramid`, not `layers`, when the widening shape is the message. #### `quadrant` — 2×2 matrix Items plotted at `x` / `y` (0–1) on two labelled axes. Answers: where does each item sit? `quadrant` for placement by judgement; scatter `chart` with `points` for data. #### `journey` — user journey map with optional emotion curve Stages across the top, rows of cells beneath, an optional emotion curve (0–1 per stage). Answers: what does the user experience at each stage? `journey` for experience; funnel `chart` for drop-off numbers; `storymap` for scope per activity. ======================================================================== # FILE: reference/blocks/data-model.md ======================================================================== # Chiltepin blocks — Data model Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Network — entities joined by cardinality edges, no nesting (`erd`). **Answers**: What shape is the data at rest, and how do the entities relate? **Not this family**: data in motion → `dfd` (flows.md) or `sankey` (charts-overviews.md); classes with behavior → `uml` (architecture.md); example rows the reader should scan → `table` (tables-data.md). ### Data model #### `erd` — entities and relations Entity cards with columns and key markers, joined by crow's-foot edges. The renderer centres the aggregate root (the "one" side of most relations), fans neighbours out by relation depth, and never truncates a card. Answers: what shape is the data at rest, and how do the entities relate? Write columns and relations in the terse forms. Use `fromCol` / `toCol` only when the FK cannot be inferred from `ref` or the column name. `schema` on an entity, or `groups`, draws a panel around the entities that share it. Budget: 20 entities or 60 columns per block; `chiltepin check` warns past that. Split the model by domain. `erd`, not `uml`, for data at rest; `uml` for classes with behaviour; `dfd` for data in motion. ### Other ways to write it A ` ```dbml ` or ` ```prisma ` fence, or a ` ```mermaid ` fence with `erDiagram`, parses into an `erd`. `chiltepin sync sql schema.sql --out docs/data-model.md` (or `sync dbml` / `sync prisma`) converts a schema file. The dialect subsets are in `reference/mermaid.md` (Input dialects). ======================================================================== # FILE: reference/blocks/design-system.md ======================================================================== # Chiltepin blocks — Design system & UI mockups Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Structure & emphasis — token specimens, usage rules, and low-fi screens (`palette`, `typescale`, `dodont`, `inventory`, `wireframe`). **Answers**: What does the UI look like before it exists? What tokens and styles exist, and what does correct use look like? **Not this family**: a real screenshot → `figure` (narrative.md); the component tree → `frontend` (architecture.md); component code → `code` (tables-data.md); shipped history → `changelog` (planning.md). ### Design system #### `palette` — color-token swatches A card grid of swatches: the hex in mono, the token name, its usage. Text contrast on each swatch is automatic. Answers: which colour tokens exist, and what is each for? Always quote hex values (`"#0E54A1"`): an unquoted `#` starts a YAML comment. An invalid colour falls back to gray. `palette` for colour tokens; `stats` for numbers. #### `typescale` — a live type specimen One row per style; the sample text renders live at that size, weight, and font. Answers: what does each text style look like? Sizes over 64px render clamped at 64 but keep the true label. `typescale` when the visual matters; `table` for a token list with no visual payoff. #### `dodont` — do / don't guideline cards Two cards side by side, DO green and DON'T red; both lists are required. Answers: what does correct use look like? An item's `example` renders beneath it as a mono chip, good for label copy. `dodont` for usage rules; `proscons` to weigh a decision; `callout` for one warning. #### `inventory` — component / feature status board Hairline rows, each with a name, a tag chip, an optional note, and a colour-coded maturity chip. Answers: how mature is each component? `inventory` for maturity; `statustable` for task work; `changelog` for shipped history. #### `wireframe` — low-fi screen mockups (desktop / browser / phone) Device frames left to right, each a top-to-bottom stack of gray elements. Answers: what does the UI look like before it exists? `nav` and `tabs` read their items from a comma-separated `label`; quote it. `rows` repeats a list or card and sizes text or a spacer. Keep it low fidelity: a wireframe, not a comp. `figure` for a real screenshot; `frontend` for the component tree. ======================================================================== # FILE: reference/blocks/flows.md ======================================================================== # Chiltepin blocks — Flows, sequences & state Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Exchange — actors trading messages over time (`sequence`) and one request's time split across services (`spans`); Flow — steps and branches through a graph (`flow`, `dfd`, `swimlane`, `cycle`, `gitgraph`, `saga`); Modes — one object, discrete states (`state`); plus one Structure block for linear procedures (`steps`). **Answers**: What calls what, in what order? What happens when this fails? What states can it be in? What are the exact steps? **Not this family**: topology at rest → `c4` / `block` (architecture.md) or `graph` (charts-overviews.md); how much moves → `sankey` (charts-overviews.md); tasks with owner and status → `statustable` (planning.md). #### `sequence` — interaction over time (rich SVG + step list + footer) Lifelines, numbered arrows, frames, activation bars, a step list under the SVG. Answers: who calls whom, in what order? `sequence`, not `flow`, when the question is message order between actors. Short `label` on the arrow; detail in `summary`. `kind: note` is a box, not a message. Close every frame with `end` (`W_SEQ_FRAME`). Activation: `-> +B` opens a bar on B; `--> -A` closes the SENDER's bar. Only the first `-` closes, so inside an `alt` put the sign on the LAST branch's reply. #### `spans` — distributed-trace waterfall (where did the time go?) One lane per service, one bar per span on a shared time axis, nested by `parent`; the critical path takes the accent. Bars sit exactly where the span ran. Answers: how long did each call take, and which one did the response wait on? One request per block; the density check warns past 40 spans. `sequence` for order, not duration. #### `state` — state machine (+ transition table) States on a grid joined by event arrows, plus a transition table. Answers: what states can it be in, and what moves it? Give it one `kind: start` state and mark `terminal` states. `state`, not `flow`, for one object's discrete modes. #### `flow` — flowchart with decisions Start, process, decision, and end nodes with labelled edges; `variant: dag` frames it as a pipeline. Answers: what happens next, and what if the check fails? Flows run across, not down: the main path on `col` 1, 2, 3, branches on `row: 2`. Omit `col`/`row` for auto-layout (`dir: TB` works only there). A label that starts with no / fail / error / reject renders red. `groups` draw dashed zones over cell ranges. `flow`, not `sequence`, for branching. #### `dfd` — data-flow diagram External entities, numbered processes, and stores joined by labelled data flows. Answers: where does the data come from, and where does it land? `dfd`, not `flow`, when the arrows carry data rather than control. #### `gitgraph` — the branching and release model Commit dots on branch lanes, in the order the history happened. The first commit on a branch opens its lane; `merge: ` closes that branch into the commit's branch; `tag` marks a release. Answers: how do branches fork, merge, and ship? `gitgraph` for branches; `timeline` for phases. #### `swimlane` — who does which step, in what order One lane per owner; a step names its lane by label (`lane: Sales`) and takes its column from the links (`col` only to pin one). `phases` bands the columns; `accent: true` marks the focal step. `flow` for one object's decisions; `sequence` for messages. #### `saga` — a distributed transaction and what runs backwards Forward steps left to right, the compensation under each, and the compensating flow drawn back from the failing step. Answers: what happens when step 3 fails? `failAt` derives every status (failed, compensated, skipped); set `status` only to override. Without `failAt` it draws the happy path. Keep it to 12 steps. `saga` for the transaction as a whole; `sequence` for the messages of one step. #### `steps` — a numbered how-to / runbook stepper A vertical stepper: title, body, an optional command on the dark surface, a note. Answers: what are the exact steps, in order? `steps` for a linear procedure a person runs; `flow` / `swimlane` when it branches; `statustable` when items carry status. #### `cycle` — a closed loop of stages arranged in a circle Stages clockwise from 12 o'clock, numbered, the last feeding the first; `center` labels the hub. 2–8 stages. Answers: what repeats? `cycle` when the process loops; `flow` when it branches and ends; `steps` for a one-shot procedure. #### `timing` — UML timing diagram One lane per lifeline stepping through `states` over a shared time axis (`from` … `to` in `unit`), `events` as instants, `constraints` as duration brackets. Answers: what state is each part in at time t, and how long does a phase last? `timing` when durations and overlaps are the point (a circuit breaker, a lease, a handshake); `state` for the transitions without time; `sequence` for message order; `spans` for measured traces. ======================================================================== # FILE: reference/blocks/INDEX.md ======================================================================== # The 107 block types — by family Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). One line per block, mapped to the family file that holds its selection guidance. For a block's fields and a validating example, run `npx -y chiltepin block `; bare `chiltepin block` prints this same list. Twelve old block names remain valid as permanent aliases — see the table at the bottom. | Block | Family file | What it represents | |---|---|---| | `meta` | `narrative.md` | Document header — title, subtitle, tag pill. Always the first block. | | `callout` | `narrative.md` | A single aside: note / tip / warn / danger. | | `table` | `tables-data.md` | Genuinely tabular data (rows × columns of values); cells can carry tone. | | `sequence` | `flows.md` | Messages between actors **over time** (lifelines, returns); optional step list + endpoint pill. | | `erd` | `data-model.md` | Entity-relationship diagram — tables, views, enums, columns with key markers, schema groups, crow's-foot cardinality; also written as ```dbml / ```prisma. | | `userstory` | `planning.md` | An agile story: role / want / soThat + acceptance criteria + links. | | `timeline` | `planning.md` | Phases in order with status dots (done / current / next / future). | | `kanban` | `planning.md` | Flexible named columns (e.g. Now / Next / Later) of cards. | | `prose` | `narrative.md` | Structured prose (headings, paragraphs, lists, quotes) carried as data. | | `glossary` | `narrative.md` | Term → definition rows. | | `proscons` | `planning.md` | Two columns weighed against each other: pros vs cons. | | `cvt` | `planning.md` | Current → target (before / after) as two side-by-side panels. | | `stats` | `tables-data.md` | KPI cards — a value with a delta and an up/down/flat trend. | | `code` | `tables-data.md` | Code the reader will copy or diff — `highlight` line bands, `lines`, a `cols` grid; `kind: compare` before / after, `kind: diff` a unified diff, `kind: terminal` a shell session. | | `agenda` | `planning.md` | Meeting agenda — time, duration, owner, topic per row. | | `tree` | `charts-overviews.md` | An indented file/folder hierarchy (HTML, not SVG); `variant: issue` draws a MECE issue tree. | | `pyramid` | `charts-overviews.md` | A layered pyramid (strategy / hierarchy), widening top → bottom. | | `flow` | `flows.md` | A decision flowchart — start / process / decision / end nodes, with `error` exits; `variant: dag` frames it as a pipeline / DAG. | | `state` | `flows.md` | A state machine — states + event transitions (+ a transition table). | | `dfd` | `flows.md` | Data-flow — processes, external entities, and datastores. | | `journey` | `charts-overviews.md` | A user journey across stages, with an optional emotion curve. | | `gantt` | `charts-overviews.md` | A schedule — tasks as bars across date columns. | | `graph` | `charts-overviews.md` | A generic node-link graph with colour-cycled groups. | | `quadrant` | `charts-overviews.md` | A 2×2 matrix (e.g. effort vs impact) with plotted items. | | `swimlane` | `flows.md` | A cross-functional process with one horizontal lane per role. | | `c4` | `architecture.md` | C4 model (context / container / component) — people, systems, containers, stores. | | `uml` | `architecture.md` | A class diagram — attributes, methods, UML relationships. | | `frontend` | `architecture.md` | A top-down component tree — root / layout / page / component / provider / hook / store. | | `cluster` | `architecture.md` | Kubernetes-style namespaces holding services, with replica counts. | | `block` | `architecture.md` | Generic boxes-and-arrows architecture — grid **or** horizontal `layers`, `groups` zones that nest by `parent`, node `replicas`; `preset: infra \| event \| ddd \| network \| k8s` re-frames it for cloud, pub/sub, DDD, security-zone, or Kubernetes maps. | | `felogic` | `architecture.md` | Frontend module/logic graph — components, hooks, interfaces, strategies; group zones + egress edges; `variant: be` re-frames it for the backend (controller / service / repository / adapter). | | `wireframe` | `design-system.md` | Low-fi UI mockups inside device frames — desktop / browser / phone screens. | | `endpoint` | `api.md` | A Swagger-style API endpoint card — method, path, params, request body, responses, examples. | | `pullquote` | `narrative.md` | A standout pull-quote with optional attribution. | | `layers` | `narrative.md` | A layered explanation — N numbered layers, each a kicker / title / source / question + body. | | `matrix` | `business.md` | A role × resource capability grid; cells tint by permission level. | | `anatomy` | `business.md` | The labelled parts of a structured string (e.g. `app:feature:action`). | | `composition` | `business.md` | Effective access as intersected gates — `gate₁ ∩ gate₂ ∩ … = result`. | | `drivers` | `business.md` | A grid of factor/driver cards — icon + title + body + tag, the forces that shaped a design. | | `options` | `business.md` | Approaches explored — cards with pros / cons / verdict; the chosen one is highlighted. | | `spec` | `business.md` | A labelled spec sheet — `label → value` rows (a value can be an inline step-flow). | | `list` | `planning.md` | A fancy bullet list — bold lead + supporting line per row, in one of four marker styles (accent bar / check / icon / number). | | `stories` | `planning.md` | A collapsible backlog of user stories — many stories as `
` accordions in one section. | | `pattern` | `planning.md` | A design-pattern reference card — intent · forces · participants · consequences. | | `gallery` | `planning.md` | A responsive grid of cards — code snippets or notes (a bug gallery, a comparison grid). | | `chart` | `charts-overviews.md` | A data chart in pure SVG — `kind:` bar / line / area / donut / radar, plus `waterfall` (budget cascade) and `funnel` (conversion bands). | | `figure` | `narrative.md` | An image with a caption in a bordered card (optional pixel width cap). | | `steps` | `flows.md` | A numbered how-to / runbook stepper — title + body + optional command + note per step. | | `cycle` | `flows.md` | A closed loop of stages arranged in a circle — the last step feeds the first; descriptions become a numbered legend. | | `faq` | `narrative.md` | Q&A accordions — native `
`, question in the summary, answer expands. | | `envelope` | `business.md` | Back-of-envelope capacity math — assumptions, derivation rows, a highlighted bottom line. | | `slo` | `tables-data.md` | Service-level objectives — SLI, target vs current, and an error-budget burn bar. | | `benchmark` | `tables-data.md` | Measured results side by side — subject columns × metric rows, the best number in each row derived and highlighted; one column can be outlined as the focus. | | `swot` | `business.md` | A classic SWOT 2×2 — strengths / weaknesses / opportunities / threats as tinted quadrant cards. | | `okr` | `business.md` | Objectives + key results — one card per objective, a status-coloured progress bar per KR. | | `persona` | `business.md` | User persona cards — avatar, role, quote, goals, frustrations, tools. | | `changelog` | `planning.md` | Release history on a vertical rail — version pills, dates, and typed change chips. | | `team` | `business.md` | Compact people cards — initials avatar, name, role, focus area. | | `heatmap` | `charts-overviews.md` | A numeric grid with an intensity ramp — rows × columns of tiles tinted by value. | | `sankey` | `charts-overviews.md` | Flow volumes between stages — node height and ribbon thickness are the value, so the widest ribbon is where the volume goes. | | `gitgraph` | `flows.md` | A branching and release model — lanes for branches, dots for commits, curves where one forks and merges back; tags mark releases. | | `treemap` | `charts-overviews.md` | Proportional composition as nested tiles — area is the value, so thirty items stay readable where a donut gives up at six. | | `packet` | `api.md` | A wire format laid out bit by bit — fields wrap across rows of `width` bits, the way an RFC header diagram reads. | | `venn` | `charts-overviews.md` | Two or three overlapping sets with the shared regions labelled — scope, ownership, responsibility. | | `wardley` | `business.md` | A Wardley map — components placed by visibility to the user and by evolution (genesis → commodity), joined into a value chain. | | `harvey` | `business.md` | A rated comparison — options across the top, criteria down the side, a Harvey ball per judgement, and the recommended column marked. | | `scqa` | `business.md` | An executive summary in Minto order — situation, complication, question, and the answer the deck exists to deliver. | | `scenarios` | `business.md` | Base, upside and downside against the same drivers — assumptions in columns, the outcome each produces on its own row. | | `scorecard` | `business.md` | A weighted decision matrix — criteria rows × option columns, weighted totals, winner highlighted. | | `risk` | `planning.md` | A risk register — severity derived from likelihood × impact, with mitigation, owner, status. | | `palette` | `design-system.md` | Color-token swatches — name, hex value, and usage per color, on a card grid. | | `typescale` | `design-system.md` | A live type specimen — each row renders the sample text at its real size / weight / font. | | `dodont` | `design-system.md` | Do / don't guideline cards — what to do (green ✓) vs what to avoid (red ✕), with optional mono examples. | | `inventory` | `design-system.md` | A component / feature status board — name + color-coded status chip (stable · beta · experimental · deprecated · planned) per row. | | `array` | `algorithms.md` | Array cells for algorithm walkthroughs — tones, pointer labels below cells, a dashed index-window highlight. | | `linkedlist` | `algorithms.md` | A pointer-chain diagram (singly or doubly) — boxed nodes with next/prev arrows, markers like `head`/`curr`, a ∅ terminator. | | `bintree` | `algorithms.md` | A binary tree — nodes placed by `parent` + `side`, tinted to show search paths, traversals, heap shapes. | | `hashmap` | `algorithms.md` | Hash buckets with chained entries — collision chains read left → right as key/value pills. | | `agentloop` | `agentic.md` | The canonical LLM agent loop — environment → agent (model chip) → tools column, memory cylinder, numbered loop arrows, stop condition. | | `trace` | `agentic.md` | An agent / session execution transcript — user / assistant / tool / system turns, with `thinking` and tool `args` → `result`. | | `prompt` | `agentic.md` | Prompt anatomy — stacked role segments (system / user / assistant / tool) with `{{variable}}` chips and a variable legend. | | `context` | `agentic.md` | A context-window token budget — one stacked bar sized against the window, with free space and over-budget overflow. | | `archmap` | `architecture.md` | A target-architecture capability map — a mosaic of tinted domain areas packed with small status-coded capability tiles (current · target · new · gap · deprecated). | | `divider` | `narrative.md` | A full-width section break — kicker ("PART 2"), display title, optional subtitle on an accent-washed band; a clean interstitial slide in decks. | | `bignumber` | `narrative.md` | One hero metric at presentation scale — a display-size value with an optional delta + neutral trend arrow, a one-line claim, and a context line. | | `takeaways` | `narrative.md` | The 2-6 things to remember — numbered rows at presentation scale, each a bold one-liner with an optional detail; a deck's closing slide. | | `statustable` | `planning.md` | A task table — free columns (task / update) plus a colored status pill per row, from a user-defined label → color vocabulary; rows can nest one level of subtasks. | | `fishbone` | `charts-overviews.md` | Cause & effect (Ishikawa) — one effect at the head, cause categories as bones off the spine, specific causes along each bone. | | `storymap` | `planning.md` | User story map — the ordered backbone of activities across the top, release slices as rows of cards under each step. | | `eventcontract` | `api.md` | An async event contract card — name, version, channel, producers → consumers, delivery / ordering / retention, payload fields with the partition key marked; the twin of `endpoint`. | | `saga` | `flows.md` | A distributed transaction — forward steps left to right, the compensation under each, and the compensating flow drawn back from the step that fails (`failAt`). | | `slopegraph` | `charts-overviews.md` | Ranked before / after — one line per item between two labeled columns; the slopes show what rose, fell, or held. | | `spans` | `flows.md` | A distributed-trace waterfall — one lane per service, each span a bar on a shared time axis, nested by parent; the critical path is marked. | | `rollout` | `planning.md` | A progressive-delivery plan — stages left to right with their traffic share, hold time, and the gate each must pass; the rollback move as the footer. | | `neuralnet` | `agentic.md` | A layered neural network — one column per layer with unit counts, kinds, activations; dense mesh between layers. | | `modelcard` | `agentic.md` | An ML model card — identity, intended use, training data, metrics per split, limitations. | | `mindmap` | `charts-overviews.md` | A radial mind map — one centre, branches left and right, sub-branches per branch. | | `audit` | `quality.md` | An audit findings register — severity-ranked rows with evidence, fix, owner, status; counts per severity. | | `checklist` | `quality.md` | A pass / fail checklist with evidence per item; pass rate derived. `"[pass] item — evidence"`. | | `perfbudget` | `quality.md` | Performance budgets vs measured — one bar per metric with the budget mark; over / near / ok derived. | | `percentiles` | `quality.md` | Latency percentiles per row — p50 … p99 · max on one axis with the SLO rule. | | `threatmodel` | `quality.md` | A STRIDE threat model — data-flow shapes inside trust boundaries plus a threats table. | | `usecase` | `architecture.md` | A UML use-case diagram — actors, system boundary, use cases, include / extend / generalize. | | `pkg` | `architecture.md` | A UML package diagram — tabbed folders with members, dashed import / use dependencies. | | `timing` | `flows.md` | A UML timing diagram — lifelines stepping through states over time, with events and constraints. | | `chevrons` | `planning.md` | A process chevron strip — phases left to right, the current one highlighted. | | `roadmap` | `planning.md` | A roadmap — themes × periods with status chips spanning their periods and a "now" rule. | ## Old names → canonical — the permanent aliases These 12 former block types merged into a canonical block. **The old spelling keeps working forever.** An alias fence parses to the canonical type with the listed fields injected (only for keys the body doesn't set). It renders exactly as it always did, and `chiltepin check` notes the mapping as a `W_ALIAS_TYPE` warning (warnings never fail a check). Write the canonical spelling in new blocks; don't rewrite existing fences just to silence the warning. | Alias | Canonical | Injected fields | |---|---|---| | `infra` | `block` | `preset: infra` | | `event` | `block` | `preset: event` | | `ddd` | `block` | `preset: ddd` | | `network` | `block` | `preset: network` | | `belogic` | `felogic` | `variant: be` | | `dag` | `flow` | `variant: dag` | | `waterfall` | `chart` | `kind: waterfall` | | `funnel` | `chart` | `kind: funnel` | | `diff` | `code` | `kind: diff` | | `terminal` | `code` | `kind: terminal` | | `mece` | `tree` | `variant: issue` | | `tracker` | `statustable` | `variant: tracker` | ======================================================================== # FILE: reference/blocks/narrative.md ======================================================================== # Chiltepin blocks — Narrative & prose Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Structure & emphasis — text that must stand out from the page (`callout`, `pullquote`, `bignumber`, `takeaways`), plus the document frame (`meta`, `divider`, `prose`, `figure`) and one Containment block (`layers`) for ordered conceptual tiers. **Answers**: What must the reader notice or remember? What does this term mean (`glossary`, `faq`)? **Not this family**: a row of KPIs → `stats` (tables-data.md); a procedure → `steps` (flows.md); weighing a choice → `options` (business.md) or `proscons` (planning.md); an ordinary bullet list → `list` (planning.md). ### Narrative & prose #### `meta` — document cover (first block only) Title, subtitle, tag pill, and an optional logo (absolute https URL). Answers: what is this document? One `meta` per doc, always first; `divider` for a cover inside the doc. #### `prose` — structured prose (heading / paragraph / list / quote) Headings, paragraphs, lists, and quotes carried as data. Answers: what is the context? Plain Markdown outside blocks does the same job; use `prose` when the text must live inside a block, such as a `gallery` cell. #### `callout` — note / tip / warning / danger One aside with a tone band. Bare text with no `field:` lines is the body. Answers: what must the reader notice here? `callout` for one aside; `takeaways` for the closing list; `faq` for several questions. #### `glossary` — term / definition rows Term → definition rows. Answers: what does this word mean in this doc? The object form adds `avoid`, the words the doc must not use instead; `chiltepin check` flags an avoided word anywhere in the doc's prose (`W_PROSE_TERM_DRIFT`). This makes the glossary the approved term list. #### `figure` — an image with a caption A real image with alt text and a caption. Answers: what did it look like? `figure` only for screenshots, photos, and exports from other tools. Anything the renderer can draw belongs in a typed diagram block. #### `faq` — Q&A accordions (native details, no JS) One accordion per question; `open: true` starts one expanded. Blank lines in an answer become paragraphs. Answers: what do readers ask? `faq`, not `glossary`, for questions; `callout` for a single aside. #### `divider` — a full-width section break ("PART 2") A band with a mono kicker, a title, and a subtitle. Answers: where does the next part start? In a deck, put a `divider` alone under its own `##` heading and it becomes an interstitial slide. #### `bignumber` — one hero metric that carries the slide One value with a label, context, and a delta. Quote numeric-looking values (`"-75%"`). Answers: what is the one number? The trend arrow is neutral gray on purpose: "down" is often good. `bignumber` for ONE number; `stats` for a row of KPIs. #### `takeaways` — the 2-6 things to remember Numbered bold one-liners, each with an optional detail line. Answers: what should the reader remember? The natural closing slide of a deck. `takeaways` to close; `list` for ordinary bullets inside a document. #### `pullquote` — a standout quote Bare text is the quote; lead with `text:` / `attribution:` for fields. Answers: whose words frame this section? `pullquote` for a quote; `callout` for an aside. #### `layers` — a layered explanation (N numbered layers) Numbered tiers, each with a kicker, a source, a question, and a body. Answers: which tier answers which question? `layers`, not `table`, when the content reads as ordered tiers (an L1 / L2 / L3 model); `block` with `layers:` when arrows join the tiers. ======================================================================== # FILE: reference/blocks/planning.md ======================================================================== # Chiltepin blocks — Planning, lists & backlogs Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Time — what happened or is planned (`timeline`, `changelog`, `rollout`, `roadmap`, `chevrons`); Grid — one option weighed (`proscons`); work items and cards (`userstory`, `stories`, `kanban`, `storymap`, `statustable`, `risk`, `list`, `cvt`, `agenda`, `pattern`, `gallery`). **Answers**: What work exists, in what state, owned by whom? What shipped when? **Not this family**: bars against dates → `gantt`; verdicts → `options`; targets → `slo`. #### `userstory` — agile story + acceptance criteria + links One story as its own section: role / want / soThat, criteria, links. Use a short stable `id` (`US-142`); other docs reference it. `links[].ref` (`doc#id`) is a real cross-reference that `chiltepin check` verifies. Answers: what does done mean? `stories` for many. #### `timeline` — phases / roadmap Phases in order with a status dot each. Answers: what happens in which phase? `timeline` for plans ahead; `changelog` for history; `gantt` for bars. #### `changelog` — release history A rail with a dot per release (red for `tag: breaking`), a version pill, a date, and typed items. Newest first. Answers: what shipped when? #### `kanban` — flexible columns Named columns of cards (Now / Next / Later). Answers: what is in flight? `kanban` for work in flight; `storymap` for scope; `statustable` for status. #### `storymap` — user story mapping (backbone + release slices) Activities across the top; each release slice is a band of cards under the step they belong to. Each slice's `cells` carries exactly one entry per backbone step, in order; write `[]` for an empty step. Answers: what do we build, in what order? #### `rollout` — how a change ships, and what stops it Stages left to right with traffic share, hold time, and the gate that must pass before the next stage; the gate belongs to the stage it closes. `rollback` is the footer: the move, not the wish. Answers: what condition starts the next stage? #### `statustable` — task table with an update column + colored status pills Free cells under `columns`, then a Status pill per row; one level of `subtasks`. `statuses` is your label → colour vocabulary; built-in defaults are in progress, blocked, completed, todo, done. Any other status fails `chiltepin check`. A parent's status never rolls up. Answers: what state is each task in? `list` when items carry no status. #### `risk` — a risk register One row-card per risk; severity derives from likelihood × impact. Answers: what could go wrong, and who owns it? `swot` for strategic position. #### `cvt` — current vs target (before / after) Two side-by-side panels of items, today and target, with a note. Answers: what changes between now and the target? `options` when several targets compete. #### `proscons` — pros vs cons (two columns) Two columns weighing ONE option. Answers: is this one option worth it? `options` for several candidates with verdicts; `gallery` for side by side. #### `agenda` — meeting agenda Timed rows with duration, title, owner, and description. Answers: what happens when in this meeting? `agenda`, not `timeline`, for one meeting. #### `list` — a fancy bullet list (four marker styles) A bold lead plus text per item; `style` picks accent, check, icon, or number markers. Answers: what are the points? `statustable` when items carry status; `takeaways` to close. #### `stories` — a collapsible user-story backlog Every story as an accordion in one section; `open: true` expands one. Answers: what is in the backlog? `userstory` for one with its own section. #### `pattern` — a design-pattern reference card A GoF-style card: intent, forces, participants, consequences; only `name` is required. Answers: what does this pattern do and cost? Start from the pattern library in `reference/system-design.md`; pair with `felogic` (structure) and `sequence` (runtime). #### `gallery` — a responsive grid of cells A real grid (2 columns by default, `cols` up to 4). A cell is a note, a `code` snippet, or a nested `block`: a whole diagram checked against its own schema. Answers: how do these compare side by side? When the user says "compare X vs Y", put each side in a cell as a nested block (a `pattern` card or a diagram), not prose or a table. `gallery`, not `code` with `blocks[]`, for a grid. #### `chevrons` — process chevron strip 2–8 chevrons left to right, `current` highlighted, a `desc` under each. Answers: what are the phases, and where are we? `steps` to execute them; `cycle` when it loops. #### `roadmap` — themes × periods `themes` as rows, `periods` as columns, items as status chips spanning `from` … `to`, `now` as a rule. Answers: what ships when, by theme? Coarser than `gantt` (no days, no dependencies); `kanban` when nothing is dated. ======================================================================== # FILE: reference/blocks/quality.md ======================================================================== # Chiltepin blocks — Quality & audits Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Verdicts with evidence. What is wrong now (`audit`), what passes and fails a standard (`checklist`), what is over budget (`perfbudget`), how long the tail is (`percentiles`), and what an attacker can do (`threatmodel`). **Answers**: What did the review find? Are we ready? Are we within budget? How slow is the slow path? Where can this be attacked, and what stops it? **Not this family**: what MIGHT go wrong → `risk` (planning.md); service targets over time → `slo` (tables-data.md); measured results side by side → `benchmark` (tables-data.md); where the time goes in one request → `spans`. #### `audit` — findings register Severity-ranked rows with evidence, fix, owner, status, and a count strip per severity. Answers: what did the review find, and how bad is it? One finding per row; `evidence` is what was observed (a path, a query, a log line), `fix` the change. `audit`, not `risk`, for defects found; `risk` for possibilities. `audit`, not `table`: the severity order and the counts are derived. #### `checklist` — pass / fail with evidence Items, or `groups` of items, each with a verdict and the evidence behind it; the footer derives the pass rate. Terse: `"[pass] item — evidence"` (quote it: the bracket is YAML flow syntax). Answers: are we ready, and what is missing? `checklist` for a standard applied once; `statustable` for work in flight; `list` with `check` markers when nothing is being verified. #### `perfbudget` — budgets vs measured One bar per metric against its budget mark; over / near / ok derived (`lowerIsBetter: false` for scores and throughput). Answers: are we within budget, and by how much? `perfbudget` for targets with a pass line; `benchmark` for candidates against each other; `stats` for KPIs with trends. #### `percentiles` — latency distribution per row p50 · p90 · p95 · p99 · max per endpoint on one axis, the SLO as a rule; `scale: log` for a long tail. Answers: how slow is the slow path? A p99 past the SLO is marked. `percentiles` for the tail; `chart` line for latency over time; `spans` for where the time goes inside one request. #### `threatmodel` — STRIDE on a data flow The dfd shapes inside dashed trust `boundaries`, `channel: plain` hops marked, and a `threats` table keyed to nodes or edges with the STRIDE letter. Answers: where can this be attacked, and what stops it? One trust boundary per block; put the mitigations in the table, not in prose. `threatmodel`, not `dfd`, when threats are the question; `audit` for what a review found. ======================================================================== # FILE: reference/blocks/tables-data.md ======================================================================== # Chiltepin blocks — Tables, metrics & code Part of the **chiltepin** skill (the hub is `SKILL.md`, two folders up). Run `chiltepin block ` for the fields and an example; block → family map: `INDEX.md`. Schemas reject unknown fields. **Shape**: Grid — two axes of exact values (`table`, `benchmark`) — plus Structure & emphasis for headline numbers, targets, and code as evidence (`stats`, `slo`, `code`). **Answers**: What are the exact values? How big, fast, or reliable is it, as measured? **Not this family**: - cells are permission levels → `matrix` (business.md) - a value grid read by intensity → `heatmap` (charts-overviews.md) - the numbers move over time → `chart` (charts-overviews.md) - scores you invented rather than measured → `scorecard` or `harvey` (business.md) ### Tables & metrics #### `table` — comparison table Rows × columns of exact values; a cell can carry a tone and emphasis. Answers: what are the exact values? `table`, not `matrix`, when cells are data rather than permissions; not `heatmap` when the reader needs the numbers rather than the pattern. #### `stats` — KPI / metric cards A row of cards: value, label, delta, and a trend arrow. Answers: how big is it right now? `stats` for a few KPIs with trends; `bignumber` for one hero number; `chart` when the numbers move over time; `envelope` for an estimate. #### `slo` — service-level objectives with error budgets One row-card per objective: SLI, target, current, window, and a burn bar. Answers: are we inside the error budget? `budget` is the fraction consumed (0–1): the bar turns amber past 0.5, red past 0.8, "exhausted" at 1. Omit it to skip the bar. `slo` for reliability targets; `okr` for goals; `stats` for plain KPIs. #### `benchmark` — measured results, side by side Subject columns × metric rows; the best cell per row is derived from the numbers and highlighted. Never bold a winner yourself. `better: low` flips a row (latency, cost); `better: none` turns the highlight off; `best: true` forces it for a tie. `variants` on a row stacks one value per condition and compares each condition on its own line. Answers: what did we measure? `benchmark` for measured numbers; `scorecard` or `harvey` for scores you gave. #### `code` — code the reader will copy or diff When the reader will copy or diff it, it is a `code` block, not prose or a table; when the change is the point, `kind: compare` or `kind: diff`. `highlight: "3-5, 8"` bands the lines that matter; `lines: true` numbers them; `cols: 2` sets snippets side by side (request / response); `kind: compare` is before / after under eyebrows; `kind: terminal` is a `session`. `steps` for a runbook with prose between commands; `gallery` for a card grid. ======================================================================== # FILE: reference/check.md ======================================================================== # Check and fix — step 7 of the procedure After you create or edit any doc, run the CLI and fix everything it reports. **A change is not done until `chiltepin check` passes.** ``` chiltepin check # validate all docs: schema + dangling refs + dup ids chiltepin check docs/orders-api.md # validate one file chiltepin check --json # machine-readable: every diagnostic with its code, file, line, value chiltepin check --strict-prose # prose warnings become errors chiltepin block # the fields, enums, terse forms, and example for one block chiltepin html docs/orders-api.md -p # render and open it (slides … -p for a deck) chiltepin new # scaffold a whole doc (adr, runbook, …) or one block chiltepin build # static site (index + nav + cross-doc links) → dist/ chiltepin sync openapi spec.yaml --out docs/api.md # doc from an OpenAPI spec ``` Prefix every command with `npx -y chiltepin` when `chiltepin` is not on PATH. `chiltepin check` exits non-zero on any error and names the file, line, and offending value. Warnings never fail the check, but read each one: most name a real problem. ## Error codes Every diagnostic carries a stable code. Apply the matching fix: | Code | Meaning | First check | |---|---|---| | `E_PARSE_YAML` | YAML body failed to parse. | Re-read *YAML pitfalls* in `writing.md`. Unquoted `,`/`:`/`#` in a `desc` is the usual cause. | | `E_PARSE_MERMAID` | A ` ```mermaid ` body has a line outside the supported subset; the message names the line. | Compare against `mermaid.md`. Fix the line, or write the block as typed YAML. | | `E_PARSE_DBML` / `E_PARSE_PRISMA` | A ` ```dbml ` / ` ```prisma ` body has a line outside the supported subset; the message names the line. | Compare against `mermaid.md` (Input dialects). Fix the line, or write the block as an `erd` in YAML. | | `E_SCHEMA` | A field is missing, wrong-typed, or unknown; the message contains the path. | Compare against `chiltepin block `. Do not add undocumented fields — the schema is strict. | | `E_DANGLING_REF` | A `ref` points at an id that exists nowhere. | Fix the ref string, or add the missing `id:` to the target block. | | `E_SWIMLANE_LANE` | A `swimlane` step's `lane` matches no lane label, id, or index. | Use one of the lane labels the hint lists, exactly as written (case does not matter). | | `E_DUP_ID` | The same `id:` in two blocks; the message names both. | Ids are repo-global. Rename one. | | `E_BAD_REF_FORMAT` | A `ref:` is not `doc#id` or `#id` shape. | Match the format; the id slug is `[\w-]+`. | | `E_UNKNOWN_BLOCK` | A segment claims an unknown block type (rare — unknown fences normally fall through to plain code). | Use exactly one of the types `chiltepin block` lists. | Common `E_SCHEMA` shapes: `Expected string, received number` → quote the value (`tech: "16"`). `Invalid enum value` → use the documented enum only. `Unrecognized key(s)` → you added an undocumented field, or an unquoted comma in a flow-style mapping split a phrase into keys. `meta` fails → it must be the first block in the file. ## Warning codes | Code | Meaning | What to do | |---|---|---| | `W_EMPTY_BLOCK` | A typed block had an empty body. | Add fields or remove the block. | | `W_SUSPECT_BLOCK` | A fence tag is within typo distance of a real type (e.g. ` ```sequnce `); it rendered as plain text. | Rename the fence to the suggested type. | | `W_ALIAS_TYPE` | The fence uses one of the 12 old merged names. It parsed and rendered fine. | Nothing — both spellings work forever. Use the canonical name in new blocks; do not churn existing fences. | | `W_DENSE_BLOCK` | A diagram is past its density budget. | Split it into two focused blocks. A diagram past its budget reads worse than two. | | `W_EDGE_LABEL` | A `c4` relationship has no `label`. | Add the verb phrase that crosses the line; at container level add `tech` (the protocol). | | `W_LENS_REPEAT` | A third `callout`, or a fourth block of any other structural type, in one doc. | Merge them, or answer the third question with a different lens — the hint names the usual one. | | `W_PROSE_*` | A prose rule broke: a paragraph too long, a banned opener, a sentence that restates the block. | Apply the prose rules in `SKILL.md` and `style-ste.md`. Block text fields are exempt from the length cap. | | `W_DOC_CONVENTION` | The file path breaks the on-disk convention. | See `organizing.md`. Never rename a file only to silence this. | ## The final read After the check passes, reread only the headings and block titles. The skim must still tell the story from step 1. Cut any section that reads as filler. ======================================================================== # FILE: reference/decks.md ======================================================================== # Slide decks — `chiltepin slides` Part of the **chiltepin** skill (the hub is `SKILL.md`, one folder up). Read this for any slides or deck ask. ## Slide decks (`chiltepin slides`) Any document renders as a deck with `chiltepin slides`. **Each top-level heading (`#`/`##`) starts a new slide and is its title.** Everything until the next heading — prose *and* every block — stays on that slide, so a slide can hold several blocks. (`###`+ headings stay in the slide body; to keep things on the same slide, just don't add a new `#`/`##`.) ````md # Why now A sentence of context, then any blocks under this heading. ```drivers items: - { title: Slow, body: "p95 hit 2.4s.", icon: clock, accent: amber } ``` # The fix Next heading → next slide. This one stacks two blocks. ```stats stats: - { value: "800ms", label: New p95 target, trend: flat } ``` ```callout tone: success body: Both blocks land on "The fix" slide. ``` ```` - This means a normal Chiltepin doc (sections under `##` headings) already presents cleanly — no special markup needed. To author *for* slides, write one `##` heading per slide and keep each to **one idea**. A heading plus one strong visual (a diagram, `drivers`, `stats`, `pyramid`, `quadrant`, `timeline`) reads better than dense prose. - **Vertical alignment is automatic** — light slides (one block, little prose) center; heavier slides (stacked blocks or lots of prose) top-align. To force it, add a marker to the heading: `## Title {top}`, `## Title {center}`, or `## Title {bottom}` (the marker is stripped from the displayed title). A fourth marker, `## Title {split}`, switches the slide to the consulting layout — prose left, exhibit right (see *Consulting-style decks* below). - **Builds are automatic.** A diagram with a natural order — `sequence`, `flow`, `state`, `saga`, `spans`, `steps`, `timeline` — reveals one item per → press before the deck moves on; earlier items stay, the newest takes the accent, and ← walks back. Add `## Title {nobuild}` to show that slide whole. The page and print always show everything. - Every non-cover slide automatically gets a footer (deck title · page number). - **`chiltepin build` — and the studio's Site link — emit both views of every doc**: the page plus a companion deck at `.slides.html`. A Doc | Slides toggle links the two on each page, so a doc is a deck with no extra command. Studio's Present mode shows the current doc's deck without even saving. - **Long sections paginate automatically** — each slide has a content budget weighted by block item counts. A hero-scale block is one heavy enough to fill a slide on its own — a big diagram, a many-card grid. It splits onto its own slide with the same section title instead of sharing the stage with its section's prose. - **Two-part slides auto-split.** Substantial prose plus a medium exhibit that would overflow stacked lays out side by side automatically. Prose becomes the left message column, the exhibit the right — the same layout `{split}` forces. Write the section naturally; the deck picks the layout. - The `meta` block is the cover slide. A doc with **no headings at all** falls back to one slide per block (legacy behavior). ### Consulting-style decks For an executive or consulting-grade deck, hold every slide to the formula **assertion → exhibit → takeaway**: - **Action titles.** Each `##` is a full-sentence assertion the slide proves ("Checkout latency costs us conversions"), never a topic label ("Latency"). Someone flipping through only the titles should get the whole argument. - **`{split}` layout.** `## Title {split}` puts the slide's prose in a left *message* column and its blocks in a right *exhibit* column — the classic consulting slide. Write 1-3 short punchy paragraphs, then exactly one strong block. - **One exhibit per slide.** A `chart`, `scorecard`, `heatmap`, or a diagram — the block *is the evidence* for the title's claim. Two exhibits means two slides. - **Open each part with a `divider`.** A deck with 3+ parts gets an interstitial per part — a `divider` alone under its own `##` heading (`kicker: PART 2`, an assertion as the `title`). It renders as a clean full-band break slide. - **The money slide is a `bignumber`.** When one metric carries the whole argument ("-75% checkout p95"), give it its own `{split}` slide: message left, the `bignumber` as the exhibit right. Do not use a one-item `stats` row. - **Close the argument with `takeaways`**: the 2-4 things the room must remember, numbered; follow with a `callout` (`tone: success`) only if there's a separate ask. - **No thin slides.** A heading floating over one small block reads empty — merge it into a neighbour, or give it a message column with `{split}`. A three-part skeleton using all three: ````md ## Part 1 — checkout is bleeding conversions ```divider kicker: PART 1 title: Checkout is bleeding conversions accent: navy ``` ## One number tells the story {split} The async capture change removed the 1.7s synchronous call from the request path. Nothing else moved. ```bignumber value: "-75%" label: Checkout p95 after the change trend: down accent: green ``` ## What to remember ```takeaways items: - text: The synchronous capture call was the bottleneck - text: Moving it to a queue cut p95 by 75% - text: Conversion recovered within two weeks ``` ```` ````md ## Checkout latency costs us conversions {split} Every 100ms of checkout latency costs ~0.6% conversion. Our p95 has drifted to 2.4s — the synchronous capture call is 71% of it. ```chart kind: bar title: Where the 2.4s goes labels: [Gateway, Fraud, Capture, Persist, Render] series: - { label: p95 ms, values: [120, 260, 1700, 180, 140] } ``` ```` ### The design-review arc — the structural exemplar For a full design-review deck, follow this **arc**. The structure is the template — swap every exhibit and every title for the system at hand; nothing about the topic carries over. | # | Slide (always an action title) | Layout | Exhibit | |---|---|---|---| | 1 | Cover | `meta` | — | | 2 | PART 1 · the problem | `divider` | — | | 3 | *The pain, stated as a claim* | `{split}` | `chart` — the evidence | | 4 | *The scale is real* | — | `envelope` — the math that sets the target | | 5 | PART 2 · the design | `divider` | — | | 6 | *Who touches the system* | — | `c4` (context) | | 7 | *The design, stated as a claim* | `{split}` | `block` — the shape | | 8 | *The decision that mattered* | `{split}` | `options` — chosen vs rejected | | 9 | *The budget holds* | — | a waterfall `chart` against the target | | 10 | *One request, end to end* | — | `sequence` | | 11 | *When X degrades, …* | — | `swimlane` — the ops story | | 12 | PART 3 · the commitment | `divider` | — | | 13 | *What we're measured on* | — | `slo` | | 14 | *The number that matters* | — | `bignumber` | | 15 | Takeaways | — | `takeaways` — the close | What the arc encodes (keep these even when you reshape it): - **Three parts, opened by dividers**: problem → design → commitment. The reader always knows where they are. - **Evidence before design**: slides 3-4 earn the right to propose anything — a complaint chart, a metric, then the envelope math. That math turns pain into a numeric target the rest of the deck answers to. - **One decision slide** (8): every real design had a fork; show the rejected option honestly or the deck reads as a sales pitch. - **Slides 9-11 are chosen by YOUR bottleneck**, not by this table: a fan-out system shows `sequence` + `swimlane`; a storage system might show `erd` + `heatmap`; an agent system `agentloop` + `trace`. Two or three deep dives, never a fixed list. - **The commitment close never changes**: objectives (`slo`) → the money number (`bignumber`, echoing slide 4's target) → `takeaways`. The last takeaway carries the ask (effort, flag, rollback). ## Provenance and position `## Title {source: production traces, 14 Oct 2026}` puts a source line in the slide footer, where every consulting exhibit carries one. It sits in the footer rather than under the block on purpose. The fitter scales the exhibit, and a source line that shrinks with it stops being readable. A deck with two or more `divider` bands also grows a **tracker** in the slide header — the parts of the deck with the current one lit. The room always knows where it is. One divider draws nothing: a strip of one says nothing. ======================================================================== # FILE: reference/exemplars/adr.md ======================================================================== ```meta title: ADR-014 — Sync engine for co-writing subtitle: Why Plotline replaced last-writer-wins with CRDT sync, and what it measurably changed. tag: ADR · Accepted ``` In March, 23% of active manuscripts had two or more writers in the same hour, and last-writer-wins overwrote 31 reported edits that month. "Lost my changes" became the top support tag. A ghostwriter and an editor routinely work the same scene at the same time, so the fix had to allow concurrent edits — not serialize them. ## Options ```options id: ex-adr-options items: - kicker: Option 1 title: Section locking how: One writer holds a scene at a time; others wait or fork. pros: [No merge logic at all, Ships in two weeks] cons: ["Blocks the ghostwriter + editor pair — our core workflow", Stale locks need a timeout policy] verdict: "REJECTED — solves the symptom by forbidding the use case" tone: rejected - kicker: Option 2 title: OT server how: A central server transforms and orders every operation. pros: [Proven at scale by Google Docs, Server log makes debugging linear] cons: [Every keystroke round-trips — offline writing stops working, The sequencer is a single point of failure] verdict: "VIABLE — fallback if CRDT storage costs blow up" tone: viable - kicker: Option 3 title: CRDT (Yjs) how: Every client merges; the server only relays and persists updates. pros: [Offline edits merge on reconnect, "80 ms p50 merge in the prototype", Relay server is stateless] cons: ["Stored docs grow ~1.6× from tombstones", Merge output is harder to debug than a server log] verdict: "CHOSEN" tone: chosen ``` ## Decision ```callout id: ex-adr-decision tone: note title: Decision body: "Plotline syncs manuscripts with Yjs CRDTs. The server relays and persists updates; it never resolves them." ``` ## What we accepted ```proscons id: ex-adr-consequences pros: - Concurrent edits merge without a lock or a round-trip - Writers keep working through the full offline flight test - Relay servers scale horizontally — no sequencer to shard cons: - "Stored manuscript grows ~1.6× — tombstones never leave the doc" - Deletion GC needs a weekly compaction job we now own - A bad merge has no single log to replay ``` The trade we made explicit: 83 KB more per manuscript is about 10 GB across Plotline's 120,000 manuscripts — under $1 a month at $0.02/GB. Each lost-edit ticket cost a churn-risk conversation. We bought reliability with pennies of disk. ## Measured outcome ```benchmark id: ex-adr-outcome metricLabel: Metric subjects: - { label: Before, sub: last-writer-wins, tone: muted } - { label: After, sub: Yjs rollout, featured: true } rows: - { label: Lost-edit tickets / month, better: low, cells: ["31", "2"] } - { label: Sync latency p50, better: low, cells: ["140 ms", "80 ms"] } - { label: Doc load p95, better: low, cells: ["410 ms", "460 ms"] } - { label: Storage per manuscript (median), better: low, cells: ["148 KB", "231 KB"] } note: "Four weeks either side of the 100% rollout, June; same manuscript cohort." ``` Load p95 and storage went the wrong way, as the options card predicted. Both stayed inside the budget we set before rollout (500 ms, 300 KB), so the decision stands without amendment. ======================================================================== # FILE: reference/exemplars/agent-system.md ======================================================================== ```meta title: Reconciliation agent subtitle: How Caravel Freight's agent matches carrier invoices to shipments — the loop, the window, one real run. tag: Agent system · v2 ``` Caravel Freight books about 9,000 ocean and air shipments a month. Carriers invoice 1,400 line items a week, and roughly 30% disagree with the quoted rate. The agent proposes a match and a verdict per line; a billing clerk approves every proposal. The agent has no path to the ledger. ## The loop ```agentloop id: ex-agent-system-loop agent: name: Reconciliation agent model: claude-sonnet-4-6 note: One invoice line per episode. env: Invoice queue tools: - { name: get_shipment, desc: "Booking, lane, and quoted rate" } - { name: search_rate_card, desc: Contract rate for a lane and sail date } - { name: get_invoice_history, desc: "The carrier's past corrections" } - { name: post_proposal, desc: Write match + verdict to the review queue } - { name: escalate, desc: Send the line to a clerk with a reason } memory: - carrier correction history - verdicts on earlier lines stop: proposal posted or line escalated ``` Three conditions force `escalate` instead of a proposal: no booking reference resolves, the rate delta exceeds $400, or the invoice currency differs from the contract currency. Everything else — 68% of lines — ends in a posted proposal. ## Window budget ```context id: ex-agent-system-window window: 100000 segments: - { label: system prompt, tokens: 3500, accent: navy } - { label: tool schemas, tokens: 4500, accent: teal } - { label: invoice line + OCR text, tokens: 9000, accent: amber } - { label: shipment record, tokens: 6000, accent: blue } - { label: rate-card excerpt, tokens: 12000, accent: purple, desc: top-3 matching lanes only } - { label: carrier history, tokens: 15000, accent: green } ``` The free 50,000 tokens are the episode's working room — tool results and retries append there. Carrier history is evicted first when a long episode fills the window. That is safe because history only tunes the verdict wording; the match depends on the shipment record and the rate card, which are never evicted. ## One real run ```trace id: ex-agent-system-run turns: - role: user text: "Invoice CMA-88231-04 · Shanghai → Rotterdam · 2 × 40HC · $4,120" - role: assistant thinking: The quoted rate lives on the booking. Fetch the shipment before touching the rate card. text: Resolving booking CV-59107 for this line. - role: tool tool: get_shipment args: '{ "booking": "CV-59107" }' result: "2 × 40HC · lane CNSHA→NLRTM · quoted $3,890 all-in · sailed 2026-07-14" - role: tool tool: search_rate_card args: '{ "lane": "CNSHA-NLRTM", "date": "2026-07-14" }' result: "timeout after 10s" - role: assistant thinking: One retry is allowed on timeout; escalating on a transient error wastes a clerk's time. text: Rate-card lookup timed out — retrying once. - role: tool tool: search_rate_card args: '{ "lane": "CNSHA-NLRTM", "date": "2026-07-14" }' result: "contract $3,890 per pair · valid through 2026-09-30 · no peak surcharge clause" - role: assistant text: "Invoice exceeds contract by $230 with no surcharge clause in force. Proposing: match to CV-59107, dispute $230." - role: tool tool: post_proposal args: '{ "booking": "CV-59107", "verdict": "dispute", "amount": 230 }' result: "queued for review · PR-20441" ``` ## Eight weeks in production ```stats id: ex-agent-system-outcomes stats: - { value: 68%, label: Lines auto-proposed, delta: "+68pp vs manual", trend: up } - { value: 45s, label: Clerk time per line, delta: "-87%", trend: down } - { value: 0.4%, label: Proposals reversed after approval, trend: flat } - { value: $18k, label: Disputed overcharges recovered / month, trend: up } ``` Reversals are the number to watch: each one means a clerk approved a wrong verdict. That makes 0.4% the agent's real error rate as the business sees it. ## The boundary ```callout id: ex-agent-system-boundary tone: danger title: Propose, never post body: "`post_proposal` writes to the review queue only. Ledger writes require a clerk's approval click, and the agent's service account holds no ledger credential — the boundary is IAM, not prompt text." ``` ======================================================================== # FILE: reference/exemplars/api-reference.md ======================================================================== ```meta title: Skerry API subtitle: Render any URL to PNG, WebP, or PDF in three calls. tag: API · v1 ``` The base URL is `https://api.skerry.dev/v1`; authenticate every call with `Authorization: Bearer sk_live_…`. Captures run asynchronously — a POST returns `202` with an id, and the file arrives seconds later via webhook or polling. A capture spends one credit only when it succeeds; rate limits are 10 requests per second with bursts to 50. ## Create a capture ```endpoint id: ex-api-create method: POST path: /captures auth: Bearer sk_live_… body: - { name: url, type: string, required: true, desc: "Page to render; must be reachable from the public internet" } - { name: format, type: string, desc: "png | webp | pdf — default png" } - { name: width, type: integer, desc: "Viewport width in px; default 1280" } - { name: full_page, type: boolean, desc: Capture the full scroll height } - { name: webhook_url, type: string, desc: Receives capture.finished / capture.failed } responses: - { status: 202, desc: Capture queued } - { status: 402, desc: Credit balance is zero } - { status: 422, desc: URL malformed or scheme not http(s) } request: | { "url": "https://example.com/pricing", "format": "png", "full_page": true } response: | { "id": "cap_8fk2", "status": "queued" } ``` ## Fetch a capture ```endpoint id: ex-api-fetch method: GET path: /captures/{id} description: result_url is a signed link that expires 24 hours after the capture finishes. params: - { name: id, in: path, type: string, required: true, desc: Capture id from the create call } responses: - { status: 200, desc: Capture in any status } - { status: 404, desc: Unknown or expired id } response: | { "id": "cap_8fk2", "status": "done", "result_url": "https://files.skerry.dev/cap_8fk2.png?sig=…" } ``` ## List captures ```endpoint id: ex-api-list method: GET path: /captures params: - { name: cursor, in: query, type: string, desc: Opaque cursor from the previous page } - { name: limit, in: query, type: integer, desc: "1–100, default 25" } - { name: status, in: query, type: string, desc: "Filter: queued | rendering | done | failed" } responses: - { status: 200, desc: "Newest first, with next_cursor when more exist" } ``` ## One capture, end to end ```sequence id: ex-api-seq actors: - { id: Client, name: Your server } - { id: Skerry, name: Skerry API } - { id: Hook, name: Your webhook, sub: webhook_url, external: true } messages: - Client -> Skerry: POST /captures - Skerry --> Client: 202 · cap_8fk2 queued - { from: Skerry, to: Skerry, kind: note, label: "render, 2–8 s typical" } - { from: Skerry, to: Hook, label: POST capture.finished, kind: async, summary: "Signed with X-Skerry-Signature; non-2xx responses are redelivered 5 times over 30 minutes." } - Hook --> Skerry: 2xx ack - Client -> Skerry: GET /captures/cap_8fk2 - Skerry --> Client: 200 · done + result_url foot: - { label: Webhook delivery, value: at-least-once } - { label: result_url TTL, value: 24 h } ``` Webhook delivery is at-least-once, so make the handler idempotent on the capture id. Polling is the fallback, not a race: `result_url` appears on `GET /captures/{id}` the moment status is `done`, whether or not any webhook was delivered. ## Errors ```table columns: [Status, Meaning, What to do] rows: - [401, Missing or revoked key, "Rotate the key in the dashboard; do not retry."] - [402, Credit balance is zero, "Top up; already-queued captures still finish."] - [422, URL invalid or scheme not http(s), Retrying identical input fails identically.] - [429, Rate limit exceeded, "Back off for Retry-After seconds, then retry."] - [500, Skerry fault, "Retry with backoff; the capture id stays valid."] note: "A render that fails is not an HTTP error: the capture ends as status failed with a failure_reason, and spends no credit." ``` ```callout tone: warn title: Verify X-Skerry-Signature body: "Every webhook carries an HMAC-SHA256 of the raw body, keyed with your signing secret, plus a timestamp. Reject anything unsigned or older than five minutes — an unverified handler lets anyone mark your captures done." ``` ======================================================================== # FILE: reference/exemplars/backend-arch.md ======================================================================== ```meta title: Marram — payout engine subtitle: How Fenwick moves seller money from captured charge to bank account. tag: Backend · v2 ``` Fenwick is a marketplace for used camera gear; buyers pay Fenwick, and Fenwick pays its sellers once a day. Marram owns that gap. It holds seller balances between capture and payout and splits each charge into seller net plus an 8% platform fee. Every afternoon it executes about 38,000 SEPA transfers. The constraint that shaped the design is auditability: a regulator can ask for any seller's position on any past date. So every money movement is a double-entry journal, and balances are derived, never stored. ## Boundaries ```c4 id: ex-backend-c4 level: container boundary: { label: Marram } nodes: - { id: core, kind: external, name: marketplace-core, desc: Checkout and charge capture. } - { id: settler, kind: container, family: service, name: settler, tech: Go, desc: Turns captures into ledger journals. } - { id: api, kind: container, family: service, name: payout-api, tech: Go, desc: Balances and payout status. } - { id: batcher, kind: container, family: service, name: batcher, tech: Go, desc: Runs the daily payout batch. } - { id: ledger, kind: store, name: ledger-db, tech: Postgres 16, desc: Journals and entries. } - { id: vaultic, kind: external, name: Vaultic, desc: Acquirer; executes SEPA transfers. } edges: - { from: core, to: settler, label: publishes charge.captured, tech: Kafka, kind: dashed } - { from: settler, to: ledger, label: posts capture journals, tech: SQL } - { from: api, to: ledger, label: reads balances, tech: SQL } - { from: batcher, to: ledger, label: posts payout journals, tech: SQL } - { from: batcher, to: vaultic, label: creates transfers, tech: REST } - { from: vaultic, to: api, label: transfer status webhooks, tech: HTTPS, kind: dashed } ``` Charge capture stays in marketplace-core, so a Marram outage delays payouts but never blocks checkout. Vaultic's webhooks land on payout-api, not the batcher — a batch crash therefore never loses a settlement status. ## The ledger ```erd id: ex-backend-erd entities: - name: journals columns: - { name: id, type: uuid, pk: true } - { name: kind, type: text } - { name: created_at, type: timestamptz } - name: entries columns: - { name: id, type: uuid, pk: true } - { name: journal_id, type: uuid, fk: true } - { name: account, type: text } - { name: amount_cents, type: bigint } - name: transfers columns: - { name: id, type: uuid, pk: true } - { name: seller_id, type: uuid } - { name: journal_id, type: uuid, fk: true } - { name: status, type: text } relations: - journals ||--o{ entries: contains - journals ||--o{ transfers: funds ``` A seller's balance is `SUM(amount_cents)` over their entries — there is no balance column to drift. The read costs about 30 ms across the 90-day hot partitions, and payout-api pays that price on every balance call. ## The daily payout run ```sequence id: ex-backend-seq endpoint: { method: POST, path: /transfers } actors: - { id: Batcher, name: batcher, sub: "daily 14:00 UTC" } - { id: PG, name: ledger-db, sub: Postgres } - { id: Vaultic, name: Vaultic, sub: acquirer, external: true } messages: - { from: Batcher, to: PG, label: SELECT due balances, summary: "Sellers with settled balance of 10 EUR or more; about 38k rows." } - { from: PG, to: Batcher, label: due sellers, kind: response } - { from: Batcher, to: Batcher, kind: note, label: derive transfer_id, summary: "transfer_id = date + seller_id, so every retry names the same transfer." } - { from: Batcher, to: Vaultic, label: POST /transfers, summary: "One transfer per seller, transfer_id as the external reference." } - { from: Vaultic, to: Batcher, label: 504 timeout, kind: error, summary: "On timeout the batcher retries the same transfer_id; Vaultic dedupes on it." } - { from: Batcher, to: Vaultic, label: retry POST /transfers } - { from: Vaultic, to: Batcher, label: 201 accepted, kind: response } - { from: Batcher, to: PG, label: post payout journal, summary: "The journal posts only after Vaultic accepts — the ledger never claims money that did not move." } foot: - { label: Batch window, value: "14:00–14:40 UTC" } - { label: Idempotency, value: "transfer_id, replay-safe" } ``` Retries reuse the transfer_id and Vaultic deduplicates on it, so a seller receives at most one transfer per day. A crashed batch is rerun whole, with no reconciliation step. ## Who owns what ```table columns: [Service, Responsibility, On-call] rows: - [payout-api, "Balance reads, payout status, Vaultic webhooks", Payments Core] - [settler, "charge.captured events → capture journals", Payments Core] - [batcher, "Daily selection, transfer execution, payout journals", Money Movement] - [ledger-db, "Journals and entries — the source of truth", Payments Core] ``` ## The invariant ```callout tone: danger title: Journals balance; entries never change body: "Every journal's entries must sum to zero — a trigger rejects the whole insert otherwise. Posted entries (#ex-backend-erd) are never updated or deleted; a correction is a new reversing journal. Any code path that edits an entry in place is a bug, whatever it fixes." ``` ======================================================================== # FILE: reference/exemplars/data-pipeline.md ======================================================================== ```meta title: Trips pipeline subtitle: How 2.1 billion daily tracker pings become the trips Loxley bills and scores. tag: Data · v3 ``` Loxley sells fleet telematics: 140,000 delivery vehicles carry a tracker that reports position and speed every few seconds. Trackers buffer offline — tunnels, depots, dead zones — so pings arrive up to 48 hours late. The pipeline therefore streams with a 48-hour dedupe window keyed on `(vehicle_id, recorded_at)`. Staleness has a user-facing price: fleet managers review yesterday's trips at 07:00 local, and per-mile invoices draw on the same rows. ## From pings to trips ```dfd id: ex-pipeline-dfd nodes: - { id: fleet, col: 1, row: 1, kind: external, name: Tracker fleet } - { id: ingest, col: 2, row: 1, kind: process, name: Ingest gateway, num: 1 } - { id: raw, col: 3, row: 1, kind: store, name: pings.raw · Kafka } - { id: dlq, col: 2, row: 2, kind: store, name: pings.dlq } - { id: dedupe, col: 4, row: 1, kind: process, name: Deduper, num: 2 } - { id: sess, col: 5, row: 1, kind: process, name: Sessionizer, num: 3 } - { id: trips, col: 6, row: 1, kind: store, name: trips-db · Postgres } edges: - { from: fleet, to: ingest, label: pings } - { from: ingest, to: raw } - { from: ingest, to: dlq, label: malformed } - { from: raw, to: dedupe } - { from: dedupe, to: sess, label: unique pings } - { from: sess, to: trips, label: closed trips } ``` The sessionizer is the lossy stage by design: it closes a trip after five idle minutes. That collapses 2.1 billion pings into about 9.4 million trips a day. Raw pings expire after 30 days; safety scores, invoices, and dashboards all read trips, never pings. ## Trips at rest ```erd id: ex-pipeline-erd entities: - name: vehicles columns: - { name: id, type: uuid, pk: true } - { name: fleet_id, type: uuid } - { name: tracker_serial, type: text } - name: trips columns: - { name: id, type: uuid, pk: true } - { name: vehicle_id, type: uuid, fk: true } - { name: started_at, type: timestamptz } - { name: ended_at, type: timestamptz } - { name: distance_m, type: int } - name: trip_points columns: - { name: trip_id, type: uuid, fk: true } - { name: recorded_at, type: timestamptz } - { name: speed_kph, type: smallint } relations: - vehicles ||--o{ trips: drives - trips ||--o{ trip_points: samples ``` `distance_m` is computed once, when the trip closes, and never recomputed — an invoice printed in March must match the trip row it was billed from. ## Replaying a gap ```steps id: ex-pipeline-replay items: - title: Size the gap body: Compare the ingest tally against the deduper's output for the affected hours. code: lox lag --topic pings.raw --by hour lang: bash - title: Replay inside the window body: The deduper drops every ping it has already seen, so a bounded replay is safe. code: lox replay --topic pings.raw --from 2026-08-21T06:00Z --to 2026-08-21T09:00Z lang: bash note: Give both bounds; the tool refuses an unbounded replay. - title: Verify counts body: Close the incident only when trip counts match the ingest tally within 0.01%. code: lox verify trips --day 2026-08-21 lang: bash ``` ```callout tone: danger title: Never replay past 48 hours body: "Beyond the dedupe window the deduper has forgotten the pings, so a replay re-creates trips that invoices already used — miles get billed twice. For older gaps run `lox rebuild --day`, which deletes and re-sessionizes whole days atomically." ``` ## Freshness commitments ```slo id: ex-pipeline-slo items: - { name: Freshness, sli: Ping visible as trip data within 15 min, target: 99%, current: 99.4%, window: 30d, budget: 0.6 } - { name: Completeness, sli: Trip closed within 48 h of its last ping, target: 99.9%, current: 99.95%, window: 30d, budget: 0.5 } - { name: Scoring latency, sli: Safety score updated within 24 h of trip close, target: 99.5%, current: 99.1%, window: 7d, budget: 1.8 } ``` The scoring objective is breached at 1.8× budget: the scorer re-shards this week, and feature work on it stays frozen until the budget recovers. ======================================================================== # FILE: reference/exemplars/frontend-arch.md ======================================================================== ```meta title: Terrastride field app subtitle: The offline-first inspection app wind-turbine technicians run on tower phones. tag: Frontend · SPA ``` Terrastride is a React SPA behind a service worker, not an SSR app. Technicians spend up to 9 hours offline inside a tower, and 60% of sessions touch the network zero times. Server rendering buys nothing when there is no server to reach. The costs we accept: a 180 KB gzip bundle budget on first load, and every read and write goes through IndexedDB. ## Module tree ```frontend id: ex-frontend-arch-tree nodes: - { id: app, kind: root, name: App } - { id: sync, parent: app, kind: provider, name: SyncProvider, note: owns the network } - { id: shell, parent: app, kind: layout, name: Shell } - { id: turbines, parent: shell, kind: page, name: TurbineListPage } - { id: turbine, parent: shell, kind: page, name: TurbineDetailPage } - { id: inspections, parent: shell, kind: page, name: InspectionListPage } - { id: inspection, parent: shell, kind: page, name: InspectionPage } - { id: syncpage, parent: shell, kind: page, name: SyncPage } - { id: checklist, parent: inspection, kind: component, name: ChecklistForm } - { id: photo, parent: inspection, kind: component, name: PhotoCapture } - { id: outbox, parent: sync, kind: store, name: outboxStore, note: IndexedDB queue } - { id: useoutbox, parent: checklist, kind: hook, name: useOutbox } ``` The rule the tree must keep: pages read IndexedDB and nothing else. `SyncProvider` is the only module that imports the network layer, so "does this work offline?" is answered by the import graph, not by testing every screen. ## The inspection screen ```wireframe id: ex-frontend-arch-screen screens: - device: phone title: "T-114 · Gearbox" label: InspectionPage — offline, 4 writes queued elements: - { type: header, label: "Gearbox inspection" } - { type: badge, label: "offline · 4 queued", tone: muted, align: r } - { type: card, rows: 3 } - { type: input, label: Torque reading } - { type: button, label: Add photo } - { type: button, label: Complete inspection, tone: accent } - { type: tabs, label: "Turbines, Inspections, Sync" } ``` The queued badge is the only sync UI on this screen. Sync state stays ambient, and the technician never leaves the checklist to check on it. ## Record lifecycle ```state id: ex-frontend-arch-lifecycle states: - { id: s0, col: 1, row: 1, kind: start } - { id: draft, col: 2, row: 1, kind: active, name: DRAFT } - { id: queued, col: 3, row: 1, kind: wait, name: QUEUED } - { id: syncing, col: 4, row: 1, kind: active, name: SYNCING } - { id: synced, col: 5, row: 1, kind: terminal, name: SYNCED } - { id: conflict, col: 4, row: 2, kind: wait, name: CONFLICT } transitions: - { from: s0, to: draft, event: open inspection } - { from: draft, to: queued, event: technician taps Complete } - { from: queued, to: syncing, event: radio regained, guard: outbox not empty } - { from: syncing, to: synced, event: server ack } - { from: syncing, to: conflict, event: server version newer } - { from: conflict, to: queued, event: technician merges on SyncPage } ``` Records sit in QUEUED longest — median 3.4 hours, the rest of the tower visit. The UI treats QUEUED as success: the badge counts quietly, and a queued record never blocks starting the next inspection. ## Routes ```table id: ex-frontend-arch-routes columns: [Route, Screen, Offline behavior] rows: - ["/turbines", TurbineListPage, Served from cache — never blocks on network] - ["/turbines/:id", TurbineDetailPage, "Cache first, silent refetch when radio returns"] - ["/inspections", InspectionListPage, "Local list from cache + outbox — queued records included"] - ["/inspections/:id", InspectionPage, All writes go to the outbox] - ["/sync", SyncPage, "Queue, conflicts, manual retry — the only network-aware UI"] ``` ======================================================================== # FILE: reference/exemplars/incident-postmortem.md ======================================================================== ```meta title: SEV-1 · 2026-07-14 — duplicate campaign sends subtitle: A 60-second visibility timeout met a 90-second batch job; 1.9M subscribers got the same email twice. tag: Postmortem · SEV-1 ``` Quillfeed sends about 62 million campaign emails a day through workers that consume send-batch jobs from a queue. On 14 July a config refactor silently dropped the send queue's visibility-timeout override, and the broker began redelivering jobs that were still in flight. All times are UTC. ## Timeline ```timeline id: ex-incident-timeline items: - "14:12 · Deploy ships · The queue-defaults refactor drops the send queue's 15-minute visibility override to the new 60 s default" - "14:22 · First duplicates · The broker redelivers in-flight batch jobs; second workers re-send them" - "14:26 · Support signal · Duplicate-email tickets spike; no alert has fired" - "14:29 · Pager · The esp-accept-rate anomaly alert fires at 2.3× forecast" - "14:37 · Mitigation · On-call correlates with the 14:12 deploy and pauses all send queues" - "14:41 · Rollback · Config revert restores the 15-minute timeout" - "14:52 · Resume · Sends restart after in-flight jobs are checked against the send ledger" - "15:03 · Resolved · Send rate returns to forecast; SEV closed" ``` Detection was the slow half: eleven minutes passed between the first customer ticket and the queue pause, because no dashboard tied send-rate anomalies to deploys. Support saw the incident three minutes before the pager did. ## The mechanism ```sequence id: ex-incident-seq actors: - { id: Broker, name: Broker, sub: send queue } - { id: A, name: Worker A } - { id: B, name: Worker B } - { id: ESP, name: ESP, sub: email provider, external: true } messages: - { from: Broker, to: A, label: deliver batch 4411, summary: "500 recipients; a batch takes about 90 seconds end to end." } - { from: A, to: A, kind: note, label: ledger check passes, summary: "None of the 500 recipients has a send record yet." } - { from: A, to: ESP, label: send 500 emails } - { from: Broker, to: B, label: redeliver 4411, kind: error, summary: "No ack after 60 seconds — the new default timeout — so the broker assumes Worker A died." } - { from: B, to: B, kind: note, label: ledger check passes again, summary: "Worker A records sends only after the ESP accepts, so the ledger still shows nothing." } - { from: B, to: ESP, label: send the same 500 } - { from: A, to: Broker, label: ack — 30 s late, kind: response } foot: - { label: Root cause, value: timeout 60 s < batch 90 s } - { label: Amplifier, value: ledger written after the send } ``` The ledger made sends look idempotent without making them idempotent. Workers checked it before the send but wrote it only after the ESP accepted — a 90-second window in which the check lies. The timeout change did not create that window; it built a machine that hit it on every batch. ## Impact ```stats id: ex-incident-impact stats: - { value: 1.9M, label: Duplicate emails, delta: "3.1% of daily volume" } - { value: 41 min, label: Impact to resolution, delta: "detection took 7 of them" } - { value: "214", label: Campaigns affected } - { value: 3.1×, label: Unsubscribe rate on affected campaigns, trend: up, delta: vs. baseline } ``` ## Remediation ```statustable id: ex-incident-remediation columns: [Action, Owner, Done / due] statuses: - { label: shipped, color: success } - { label: scheduled, color: neutral } rows: - { cells: ["Send ledger written before the ESP call; failed writes reconciled hourly", Delivery, 2026-07-16], status: shipped } - { cells: ["Visibility timeouts pinned per queue by a config test that fails on defaults", Platform, 2026-07-21], status: shipped } - { cells: [Deploy markers on every send-rate dashboard, Observability, 2026-08-01], status: in progress } - { cells: ["Duplicate-send canary — pages when the duplicate rate passes 0.1%", Delivery, 2026-08-15], status: scheduled } ``` ## Takeaways ```takeaways id: ex-incident-takeaways items: - Record intent before the side effect — a ledger written after the send is a race, not a guarantee. - Defaults are code — the 15-minute override lived in config nobody tested, and a refactor deleted it silently. - text: Users out-detect dashboards on duplicates detail: Support tickets led the pager by three minutes; a duplicate-rate canary closes that gap. ``` ======================================================================== # FILE: reference/exemplars/migration-plan.md ======================================================================== ```meta title: Orders store migration subtitle: Moving 480M orders off the monolith database, with a rollback path at every phase. tag: Plan · Q3–Q4 ``` Hollybank's `orders` table is 2.3 TB inside the monolith's shared Postgres. Write p95 is 210 ms and climbs about 8 ms a month; autovacuum now runs 11 hours and blocks schema changes. We move orders to a dedicated cluster, partitioned by month, behind the existing `OrdersRepo` interface — application code does not change. Every phase is reversible until the old copy is destroyed, and that happens no earlier than 30 days after cutover. ## Today and target ```cvt id: ex-migration-cvt current: label: Monolith DB items: - orders plus 61 other tables in one Postgres - "2.3 TB, 480M rows" - "write p95 210 ms, rising" - autovacuum runs 11 h and blocks DDL target: label: orders-db items: - dedicated cluster, partitioned by month - write p95 under 40 ms - DDL touches one partition at a time - old copy kept warm for 30 days after cutover note: OrdersRepo stays the only entry point; just its wiring changes. ``` ## Cutover as a state machine ```state id: ex-migration-state states: - { id: s0, col: 1, row: 1, kind: start } - { id: dual, col: 2, row: 1, kind: active, name: DUAL_WRITE } - { id: backfill, col: 3, row: 1, kind: active, name: BACKFILL } - { id: shadow, col: 4, row: 1, kind: active, name: SHADOW_READ } - { id: readnew, col: 5, row: 1, kind: active, name: READ_NEW } - { id: writenew, col: 6, row: 1, kind: active, name: WRITE_NEW } - { id: done, col: 7, row: 1, kind: terminal, name: DONE } transitions: - { from: s0, to: dual, event: flag on } - { from: dual, to: backfill, event: writes verified 24 h } - { from: backfill, to: shadow, event: history copied, guard: row counts match } - { from: shadow, to: readnew, event: 7 clean days, guard: "mismatch < 0.001%" } - { from: readnew, to: writenew, event: 7 clean days, guard: read p95 at or under old } - { from: writenew, to: done, event: 30 quiet days } - { from: shadow, to: dual, event: mismatch spike } - { from: readnew, to: shadow, event: read errors or drift } - { from: writenew, to: readnew, event: rollback flag, guard: "reverse replication lag < 60 s" } ``` The state names are literal values of the `orders_migration_phase` flag, so the diagram, the flag, and the dashboards share one vocabulary. WRITE_NEW is not the point of no return — the old database follows through reverse replication and can resume as primary in minutes. The only irreversible transition is into DONE. ## Write-cutover runbook ```steps id: ex-migration-runbook items: - title: Freeze schema changes body: "The freeze flag rejects DDL on orders in both stores; announce it in #eng first." code: hb flags set orders_ddl_freeze on lang: bash - title: Confirm reverse replication body: Rollback depends on it; lag must stay under 60 s before and during the flip. code: hb repl status orders-reverse --watch lang: bash - title: Flip the write primary code: hb flags set orders_migration_phase WRITE_NEW lang: bash note: The flag drains in-flight transactions for up to 5 s — expect a latency blip, not errors. - title: Hold the exit criteria for 30 minutes body: "Write p95 under 40 ms, zero dual-write mismatches, reverse lag under 60 s. On any breach, flip back to READ_NEW and debug offline — never in place." ``` ## Risks ```risk id: ex-migration-risks items: - { risk: "Three cron jobs write orders with raw SQL, bypassing OrdersRepo and the dual-write layer", likelihood: high, impact: high, mitigation: "Two rewritten, one deleted; the bypass audit re-runs weekly until DONE.", owner: Monolith, status: mitigating } - { risk: Reverse replication breaks silently and the rollback path becomes fiction, likelihood: med, impact: high, mitigation: "Lag over 60 s pages at production severity, day and night.", owner: Storage, status: mitigating } - { risk: Diff sampler misses drift inside JSON columns, likelihood: low, impact: high, mitigation: Rows are canonicalized before hashing., owner: Storage, status: closed } - { risk: Backfill competes with month-end order load, likelihood: med, impact: med, mitigation: Copy is rate-limited and pauses itself when monolith write p95 passes 150 ms., owner: Storage, status: open } ``` ======================================================================== # FILE: reference/exemplars/onboarding.md ======================================================================== ```meta title: Ingest team — week one subtitle: What a new engineer sets up, reads, and ships in the first five days. tag: Onboarding ``` Nocturne's ingest tier accepts 2.1M spans per second across three regions. This team owns the gateway, the sampler, and the Kafka topics between them. The week has one goal: ship a guarded sampler-config change to staging by Friday, with your own hands on every step. ## Who to ask ```team id: ex-onboarding-people members: - { name: Priya Nair, role: Tech lead, focus: "Sampler, capacity planning", accent: navy } - { name: Jonas Weber, role: SRE, focus: "Gateway, on-call rotation, staging access", accent: teal } - { name: Mel Torres, role: Backend, focus: "Kafka topics, schema registry", accent: purple } - { name: "#ingest-help", initials: IH, role: Slack channel, focus: First stop for any question — median answer 11 minutes, accent: green } ``` ## Day one — local stack ```steps id: ex-onboarding-setup items: - title: Clone and bootstrap body: Bootstrap installs the pinned toolchain and takes about 15 minutes on first run. code: git clone git@github.com:nocturne/ingest.git && make bootstrap lang: bash - title: Start the local stack body: "Gateway, sampler, and a single-broker Kafka run in Docker." code: make stack-up lang: bash - title: Run the smoke test body: The test pushes 400 synthetic spans end to end and checks the drop-rate counter. code: make smoke lang: bash note: "A red smoke test on a clean clone is a bug — report it in #ingest-help the same day." - title: Request staging access body: Request the "ingest-staging-rw" role in the access portal. Jonas approves same day. ``` ```callout id: ex-onboarding-replay tone: warn title: Replay targets staging only body: "`make replay` re-sends captured traffic. A production broker accepts replayed spans as real tenant data, and they land in customer dashboards. Check the `KAFKA_BROKERS` value before every replay." ``` ## The week ```timeline id: ex-onboarding-week items: - "[done] Day 1 · Local stack · Smoke test green; staging access requested" - "[done] Day 2 · Read path · Pair with Mel and follow one span from gateway to sampler" - "[current] Day 3 · Shadow on-call · Sit in on Jonas's handover; read the two latest incident docs" - "[next] Day 4 · First change · Raise tail sampling for the demo tenant behind ingest.sampler.demo_rate" - "[next] Day 5 · Ship it · Flag on in staging; watch the drop-rate dashboard for one hour" ``` Day 4's change is deliberately trivial. The point is the path — flag, review, staging deploy, dashboard — not the diff, so that your first urgent change is not also your first deploy. ## Vocabulary ```glossary id: ex-onboarding-terms terms: - Span — one timed operation; the unit everything on this team counts. - Drop rate — spans the sampler discards as a share of spans received; the team's headline SLI. - Head sampling — keep/drop decided at the gateway, before the trace is complete. - Tail sampling — decided in the sampler once the full trace arrives; costs memory, saves storage. - Tenant — one customer's isolated stream; every dashboard and quota is per-tenant. ``` ======================================================================== # FILE: reference/exemplars/product-spec.md ======================================================================== ```meta title: Plot transfer & waitlist subtitle: How a Loamly garden plot moves from a leaving member to the next person in line. tag: Spec · v1 ``` Loamly manages 140 community gardens, and the median waitlist is 23 people deep. Today a plot changes hands by email between the coordinator and whoever answers first — position in line is a suggestion. This feature makes the handover self-serve for the member and automatic for the waitlist, with the coordinator as an approval gate only. ## The story ```userstory id: ex-product-spec-story role: plot holder who is moving away want: hand my plot back without emailing the coordinator soThat: the next person in line gets it before planting season priority: High points: 5 criteria: - { given: I hold an active plot, when: I start a transfer, then: the plot enters the offer flow and I keep access for up to 14 days } - { given: I am first on the waitlist, when: I receive an offer, then: I have 72 hours to accept before it moves on } - { given: I decline an offer, when: the next offer round starts, then: my waitlist position is unchanged } links: - { ref: "#ex-product-spec-flow", mode: flow, label: Offer flow } ``` ## Offer flow ```flow id: ex-product-spec-flow nodes: - { id: start, col: 1, row: 1, kind: start, label: Transfer started } - { id: approve, col: 2, row: 1, kind: decision, label: Coordinator approves? } - { id: keep, col: 2, row: 2, kind: end, label: Plot stays with holder } - { id: offer, col: 3, row: 1, kind: process, label: Offer to next in line } - { id: accept, col: 4, row: 1, kind: decision, label: "Accepted within 72 h?" } - { id: assign, col: 5, row: 1, kind: end, label: Plot assigned } - { id: more, col: 4, row: 2, kind: decision, label: More on waitlist? } - { id: dormant, col: 5, row: 2, kind: end, label: Dormant — coordinator review } edges: - start -> approve - approve -x-> keep: "no" - approve -> offer: "yes" - offer -> accept - accept -> assign: "yes" - accept -> more: "no / expired" - more -> offer: "yes" - more -x-> dormant: "no, or 3 offers made" ``` The 72-hour window and the three-offer cap exist for the same reason. A transfer that drags past two weeks straddles planting season, and a plot idle in April is the outcome every party loses on. ## Invariants ```spec id: ex-product-spec-invariants accent: green rows: - { label: One plot per member, value: "A member holds at most one active plot per garden. A transfer that would create a second is refused at start, not at assignment." } - { label: Sticky position, value: "Declining an offer keeps your waitlist position. Only accepting a plot removes the entry." } - { label: Offer window, value: "72 hours per offer, at most 3 offers per transfer, then the plot goes dormant for coordinator review." } - { label: Access overlap, steps: [Transfer starts, "Holder keeps access ≤ 14 days", Access ends at assignment] } ``` ## Scope edges ```table id: ex-product-spec-scope columns: [Capability, v1, Why] rows: - [Member-initiated transfer, { v: In, tone: pos }, The core loop above] - [Coordinator-forced reassignment, { v: In, tone: pos }, "Abandoned plots exist; coordinators need the same flow without the member"] - [Plot swaps between two members, { v: Out, tone: muted }, "2% of requests — stays manual"] - ["Priority rules (seniority, household size)", { v: Out, tone: muted }, "FIFO only in v1; priority differs per garden's bylaws and needs its own spec"] - [Plot fees and payments, { v: Out, tone: muted }, Handled off-platform today and out of this feature's blast radius] ``` ======================================================================== # FILE: reference/intake.md ======================================================================== # Intake checklists — what to ask before writing Part of the **chiltepin** skill (the hub is `SKILL.md`, one folder up). Use this in move 1 (*Understand the ask — and ask back*) of every new document. ## The ask-back protocol 1. **Identify the document type.** Match the ask to a checklist below (they mirror the *Document playbooks* table in `SKILL.md`). No match → fall back to the four generic questions: reader & moment · job · scope · form. 2. **Collect the checklist and diff it against the ask.** Note which items the user already answered — never ask for something they told you. 3. **Ask everything at once.** Ask for every missing **CRITICAL** item in ONE batched message of 2-6 pointed questions — never drip one question per turn. Fold in a nice-to-have only when its answer would change the outline. 4. **If the user is unavailable** (or the gaps are minor), proceed with explicit assumptions. Add a `callout` (`tone: note`, title *Assumptions*) near the top of the doc, listing each guess so every one is visible and correctable. Each checklist marks items **CRITICAL** (the document is wrong without them) vs *nice-to-have* (improves it, but safely assumable). > **Raw tabular data?** When the source material is a spreadsheet/CSV export, > don't hand-transcribe it into YAML — import it. `chiltepin sync csv ` prints > a ready-made `table` / `statustable` / `chart` fence (it auto-picks the > block and says why), and dropping the `.csv` onto the studio canvas inserts > the block in place. Ask for the export, not a screenshot of it. ## API / endpoint spec - **CRITICAL** — the routes + methods to document (all of them, or which subset?). - **CRITICAL** — the auth model: scheme, token type, scopes/permissions per route. - Request/response examples — real payloads beat invented ones. - Error codes and what each means to the caller. - Rate limits (per key? per IP? headers exposed?). - Versioning scheme (URL, header, none). ## System design - **CRITICAL** — functional requirements: what must the system do, for whom? - **CRITICAL** — scale numbers: DAU, writes/day, reads/day, data size and growth. - **CRITICAL** — latency and consistency targets (p95/p99; strong vs eventual, where). - Existing stack and constraints (languages, cloud, what's already built). - Budget envelope (infra spend, buy-vs-build appetite). - Team size and skill mix (shapes how much operational complexity is affordable). ## Agent system - **CRITICAL** — the model(s) used, and where in the loop. - **CRITICAL** — the tools: each tool's name and what it actually does. - **CRITICAL** — stop conditions: when does the loop end, and what bounds it? - Memory strategy (none, scratchpad, vector store, summaries — and eviction). - One real transcript of a representative episode (for the `trace` block). - Eval criteria — how is "it works" measured? - Guardrails: input/output filtering, permissioning, human-in-the-loop points. ## Architecture overview / onboarding - **CRITICAL** — the system inventory: the services/components and one line each. - **CRITICAL** — the one key request path a new joiner will touch most. - Owners — which team/person owns each piece. - Deploy topology (where it runs: regions, clusters, environments). ## Runbook - **CRITICAL** — the trigger: which alert/symptom puts you in this runbook? - **CRITICAL** — the exact commands to run, verbatim (no "restart the service" hand-waving). - **CRITICAL** — the verification step: how do you know it worked? - Access needed (VPN, roles, break-glass credentials) before you start. - Escalation path — who to page when the runbook doesn't resolve it. - Rollback — how to undo the intervention if it makes things worse. ## ADR - **CRITICAL** — the options actually considered (not a padded strawman list). - **CRITICAL** — the deciding constraint: which force actually picked the winner? - **CRITICAL** — status: proposed, accepted, superseded? - Consequences observed so far (if the decision already shipped). ## Roadmap - **CRITICAL** — the horizon (quarter? half? year?). - **CRITICAL** — the milestones and what "done" means for each. - **CRITICAL** — current status per milestone/phase. - Owners per workstream. - Dependencies between items (and on other teams). ## Design system - **CRITICAL** — the token source: actual color values, type sizes/weights, spacing. - **CRITICAL** — the component list with a status per component (stable/beta/…). - Usage rules — the do/don't guidance per token or component. - Contribution process — how a new component gets in. ## Deck - **CRITICAL** — the audience (engineers? leadership? customers?). - **CRITICAL** — the time slot (5 minutes and 30 need different decks). - **CRITICAL** — the decision sought — what should the room say yes to? - The 3 numbers that matter (the exhibits hang off them). - Appendix depth — how much backup material to prepare. ## Data model - **CRITICAL** — the entities and their relationships (with cardinality). - **CRITICAL** — lifecycle states: which records have a state machine, and what is it? - Volumes per table (rows now, growth rate). - Retention — what gets deleted/archived, and when. ## Postmortem / incident - **CRITICAL** — the timeline: detection → mitigation → resolution, with times. - **CRITICAL** — the impact: who/what was affected, how badly, for how long. - **CRITICAL** — the root cause (the real one, not the first symptom). - Action items with owners (and due dates if agreed). ## Reviewing an existing doc When asked to review (not write) a doc, walk this checklist top to bottom and report findings per item — worst first. Fix only what you were asked to fix. 1. **Skim test.** Read only the `meta` title and the `##` headings: do they tell one story (orient → big picture → detail → plan)? Headings that could sit on any document mean it was templated. 2. **One lens per beat.** No two blocks drawing the same boxes; no three tables in a row where one wants to be a `matrix`, `list`, or diagram. 3. **Thin blocks.** A block with fewer than ~3 rows/nodes should fold into prose or a `callout`. An empty block is a `W_EMPTY_BLOCK` waiting to fire. 4. **YAML pitfalls scan.** Unquoted `,` `:` `#` in `desc`/`note`/`summary` fields, unquoted hex colors, unquoted `1:N` cardinality, numeric-looking strings (`version: 1.0`, `delta: 0`) — the usual parse traps. 5. **Refs.** Every `ref:` points at an id that exists; same-doc refs use bare `#id`; blocks other docs might need carry an `id:`. 6. **Title/heading agreement.** Each block's `title` and its `##` heading sound like one author; the `meta` cover matches the doc's actual content. 7. **Stale facts.** Counts, versions, dates, and status fields (`timeline`/`statustable`/`inventory`) that no longer match reality — flag them even when you can't verify the correction. 8. **Close with `chiltepin check`.** A review isn't done until the doc validates clean — report any diagnostic verbatim. ======================================================================== # FILE: reference/mermaid.md ======================================================================== # Input dialects — Mermaid, DBML, Prisma Part of the **chiltepin** skill (the hub is `SKILL.md`, one folder up). Three fence tags hold a body that is not YAML. The parser converts the body into a typed block at read time; validation and rendering are the same as for a YAML block. The Markdown file keeps the dialect text until an editor changes the block; an edit writes the block back as YAML under its canonical tag. | Fence | Block | Subset | |---|---|---| | ` ```mermaid ` + `sequenceDiagram` | `sequence` | below | | ` ```mermaid ` + `flowchart` / `graph` | `flow` | below | | ` ```mermaid ` + `erDiagram` | `erd` | below | | ` ```mermaid ` + `stateDiagram` / `stateDiagram-v2` | `state` | below | | ` ```mermaid ` + `pie` | `chart` (`kind: donut`) | below | | ` ```dbml ` | `erd` | [DBML](#dbml) | | ` ```prisma ` | `erd` | [Prisma](#prisma) | SQL DDL is **not** a fence dialect (a ` ```sql ` fence is usually a code sample). Convert a schema file with `chiltepin sync sql schema.sql --out docs/data-model.md`; `chiltepin sync dbml` and `chiltepin sync prisma` do the same for files. ## The rule Write a dialect fence when you already have the text in that grammar and the diagram needs only what the subset can say. Switch to the typed YAML block when you need a field the dialect cannot say: `endpoint`, `foot`, `summary` / `code` / `note` on a message, `groups`, `guard`, node `kind` overrides, `accent`, `description`, `lede`, or an erd `dir` / relation `label`. Never mix: one fence is either dialect text or YAML. # Mermaid A ` ```mermaid ` fence whose first non-comment line is one of the five keywords above converts. Any other first line (`gantt`, `classDiagram`, `mindmap`, `gitGraph`, …) is not converted: the fence stays prose and renders as a plain code block, with no diagnostic. `%%` comment lines are ignored in every grammar. ## sequenceDiagram - `participant A`, `participant A as Name`, `actor A as Name` → an actor `{ id, name }`. Without `as`, the id is the name. An id used in a message but never declared is added at its first use, in order, as Mermaid does. - Messages: `A->>B: text` sync · `A-->>B: text` response · `A-)B: text` async · `A--)B: text` response · `A-xB: text` and `A--xB: text` error · `A->B:` sync · `A-->B:` response. The text after `:` is the label. - `Note over A,B: text`, `Note right of A: text`, `Note left of A: text` → a message `{ from: A, to: B, kind: note }` (`to` is `A` for a one-actor note). - `title Text` → `title`. - Fragments: `alt text` / `opt text` / `loop text` / `par text` / `critical text` / `break text` → a frame open `{ frame, label }`; `else text`, `and text`, `option text` → `{ else: text }`; `end` → `{ end: true }`. The frame renders as a UML frame around its messages. - `A->>+B: text` sets `activate: true` on the message (a bar opens on B); `B-->>-A: text` sets `deactivate: true` (the bar on B, the sender, closes). - Ignored: `autonumber`, standalone `activate X` / `deactivate X` lines, and `rect` / `box` … `end` (the messages inside are kept; the coloured box is lost). - Any other line is an error (`E_PARSE_MERMAID`, with the line number). ## flowchart / graph - Direction: `TD` and `TB` → `dir: TB`; `LR`, `RL`, `BT` → `dir: LR`; none → `TB`. The renderer lays the nodes out; Mermaid positions are not kept. - Node shapes: `A[text]`, `A(text)`, `A[/text/]`, `A[\text\]`, `A>text]`, `A[(text)]`, `A[[text]]` → process. `A{text}`, `A{{text}}` → `decision`. `A([text])`, `A((text))` → `start` when the node has no incoming edge, `end` when it has no outgoing edge, otherwise process. A bare `A` uses the id as its label. Quotes around a label are removed. Ids keep their Mermaid spelling. - Edges: `A --> B`, `A -->|label| B`, `A -- label --> B`, `A -.-> B`, `A -. label .-> B`, `A ==> B`, `A --- B`. A dotted edge (`-.->`) becomes `kind: dashed`; a thick edge (`==>`) renders as a plain edge. `A --x B` → `kind: error`. `A <--> B` is one edge from A to B. - Chains `A --> B --> C` and fans `A & B --> C` expand to one edge per pair. `;` separates statements on one line. - Lost: `subgraph … end` framing. The nodes inside are kept; the box is dropped, because `flow` groups need grid coordinates and the auto layout has none. Add `groups` with `col` / `row` in a YAML `flow` block instead. - Ignored: `style`, `classDef`, `class`, `click`, `linkStyle`, `direction` inside a subgraph, and `:::class` suffixes. ## erDiagram - `A ||--o{ B : label` → a relation. Left ends `||` `|o` (one) and `}o` `}|` (many); right ends `||` `o|` (one) and `o{` `|{` (many). One–one → `1:1`, one–many → `1:N`, many–one → `N:1`, many–many → `N:M`. A `..` body sets `identifying: false` (dashed); `--` is identifying. The label loses its quotes; `""` means no label. - `A { type name PK "comment" }` → an entity with `columns: [{ name, type, pk }]`. `FK` → `fk: true`, `UK` → `unique: true`, the `"comment"` → `note`. An entity named only in a relation is added with no columns. Names may be quoted: `"Order Line"`. - Ignored: `direction`. ## stateDiagram / stateDiagram-v2 - `[*]` becomes a pseudo-state node: `_start` (`kind: start`, the filled dot) or `_end` (`kind: terminal`, the bullseye). `[*] --> A : event` and `A --> [*]` are ordinary transitions from or to that node. - `A --> B : event` → `{ from, to, event }`. Without `: event` the event is the empty string, which renders as an unlabelled arrow. - `state "Long name" as A` → `{ id: A, name: "Long name" }`. `A : text` sets the name of A. A bare `state A` or an id used only in a transition is named by its id. - `direction LR|TB` at the top level → `dir`. - Lost: composite states `state A { … }` are flattened. The inner states and transitions are kept; the container box is dropped. An inner `[*]` gets its own pseudo-state, `_start_A` / `_end_A`. - Ignored: `note … end note` blocks, one-line `note right of A : text`, `--` concurrency separators, `<>`-style stereotypes, `classDef`, `class`. ## pie - `pie`, optionally followed by `showData` and/or `title Text` on the same line. `title Text` on its own line also works. - `"Label" : 42` → an item `{ label, value }`. Decimals are accepted. - Result: `{ kind: donut, title?, items }`. # DBML A ` ```dbml ` fence always converts to an `erd`. The subset: - `Table [schema.]name [as Alias] { … }` → an entity. A `schema.` prefix sets `schema` (the renderer draws a panel per schema). Header settings (`[headercolor: …]`) are dropped. - Column line `name type [settings]`. Settings: `pk` / `primary key` → `pk`; `unique` → `unique`; `not null` → `nullable: false`; `null` → `nullable: true`; `default: value` → `default` (quotes and backticks removed: `` `now()` `` → `now()`); `note: '…'` → `note`; `ref: > t.c` / `< t.c` / `- t.c` / `<> t.c` → a relation (below). `increment` and any other `key: value` setting are dropped. A type with spaces must be quoted (`"double precision"`); `varchar(255)` and `decimal(10, 2)` work. - `Note: '…'` (or a `Note: '''…'''` block) inside a table → the entity `note`. - `indexes { (a, b) [unique, name: '…'] }` → `indexes` on the entity; a single-column index sets `index` (or `unique`) on the column; `[pk]` marks a composite primary key. - `Ref [name]: a.b > c.d` and the `Ref { … }` block: `>` many-to-one (`a.b` is the foreign key, relation `N:1` from `a` to `c`), `<` one-to-many (the key is on `c.d`), `-` one-to-one (`1:1`; the key goes on the side whose column is not the primary key), `<>` many-to-many (`N:M`, no key). Composite ends: `t.(a, b)`. Ref settings (`[delete: cascade]`) are dropped. A referenced table that is never declared becomes an `external` entity. - `Enum [schema.]name { value [note: '…'] }` → `enums` (value notes are dropped). - `TableGroup name { t1 t2 }` → `groups`. - Skipped: `Project { … }`, sticky `Note x { … }`, `TablePartial`; `//` and `/* … */` comments. - Any other line is an error (`E_PARSE_DBML`, with the line number). # Prisma A ` ```prisma ` fence always converts to an `erd`. The subset: - `model X { … }` → an entity; `view X { … }` → `kind: view`. A `///` doc comment before the block → the entity `note`. - Field line `name Type[?|[]] @attrs`. A scalar or enum type → a column (`String`, `Int`, `DateTime`, … as written; `String[]` keeps the `[]`; `Unsupported("x")` → `x`). `?` → `nullable: true`. `@id` → `pk`; `@unique` → `unique`; `@default(v)` → `default` (`uuid()`, `now()`, `autoincrement()`, `"str"` unquoted, `dbgenerated("…")` unwrapped). A `///` doc comment before the field → `note`. - A field whose type is another model is a relation field, never a column. `@relation(fields: [a], references: [b])` marks `a` as the foreign key (`ref: Model.b`) and adds the relation `N:1` from this model — `1:1` when the key columns are unique. An optional relation field (`User?`) makes the key column nullable. A relation `"name"` becomes the `label`. A list on both sides with no `fields` (implicit many-to-many) → one `N:M`. The back side of an explicit relation adds nothing. - `@@id([a, b])` → composite `pk`; `@@unique([...])` / `@@index([...])` → `indexes` (single-column ones set `unique` / `index` on the column); `@@schema("x")` → `schema`. - Dropped: `@map` / `@@map`, `@db.*`, `@updatedAt`, `@ignore` / `@@ignore`, `onDelete` / `onUpdate`; `datasource`, `generator` and composite `type` blocks; `//` comments. - Any other line is an error (`E_PARSE_PRISMA`, with the line number). # SQL DDL (`chiltepin sync sql`) Not a fence. `chiltepin sync sql schema.sql` prints an ` ```erd ` fence; add `--out docs/data-model.md` to write a doc and validate it. The subset: - `CREATE TABLE [IF NOT EXISTS] [schema.]name ( … )` with column definitions: `name type` (multi-word and parenthesised types work: `timestamp with time zone`, `numeric(10, 2)`, `int unsigned`, `text[]`), then `NOT NULL` / `NULL`, `PRIMARY KEY`, `UNIQUE`, `DEFAULT expr` (kept as text: `now()`, `'open'::order_status`, `CURRENT_TIMESTAMP`), `REFERENCES t (c) [ON DELETE …]`, MySQL `COMMENT '…'` → `note`, MySQL `ENUM('a','b')` → `enum`. Table constraints: `[CONSTRAINT n] PRIMARY KEY (…)`, `UNIQUE (…)`, `FOREIGN KEY (…) REFERENCES t (…)`, MySQL `KEY` / `INDEX (…)`. A table `COMMENT = '…'` option → `note`. - `CREATE [UNIQUE] INDEX … ON t (cols)`, `ALTER TABLE t ADD [CONSTRAINT] PRIMARY KEY | UNIQUE | FOREIGN KEY …`, `CREATE TYPE t AS ENUM (…)` → `enums`, `CREATE [MATERIALIZED] VIEW v [(cols)] AS SELECT …` → a `view` entity (columns from the list or a simple select list), `COMMENT ON TABLE | COLUMN … IS '…'` → `note`. - Postgres `"x"`, MySQL `` `x` `` and SQL Server `[x]` quoting; `--`, `#` and `/* … */` comments; `$$` bodies. The `public` / `dbo` schema prefix is dropped; any other prefix sets `schema`. - Dropped: `CHECK`, `AUTO_INCREMENT`, `GENERATED … AS IDENTITY`, `COLLATE`, `CHARACTER SET`, `ON DELETE` actions, storage options. Every other statement (`INSERT`, `GRANT`, `CREATE FUNCTION`, …) is skipped. - A `CREATE TABLE` the subset cannot read fails with its line. # Errors `E_PARSE_MERMAID`, `E_PARSE_DBML` and `E_PARSE_PRISMA` name the body line the subset cannot read. Fix the line to match the subset above, or rewrite the block as typed YAML. A dialect fence never produces `W_ALIAS_TYPE`. ======================================================================== # FILE: reference/organizing.md ======================================================================== # Organizing a documentation set Part of the **chiltepin** skill (the hub is `SKILL.md`, one folder up). One doc is a story; a docs folder is a *library*. Use this when a project outgrows a single file — or when you're deciding whether it has. ## Where files live | Path | What it holds | | --- | --- | | `docs/` (or the configured `docsDir`) | All docs. One group level: `docs//.md`. Deeper nesting is drift. | | `dist/` (the build default) | Generated output. Never commit it. | | `.chiltepin/` | Tooling state — `skill/` is the authoring skill. | | `chiltepin.config.json` | Project config, at the root. | | `resources/` (or any folder outside `docsDir`) | Demo and fixture docs. Not part of the built site. | Doc filenames are kebab-case slugs: lowercase a-z, 0-9, hyphens, `.md`. The path is the reference prefix (`doc#id`), so slugs are load-bearing. Put a new doc at `docs//.md` — `chiltepin check` warns (`W_DOC_CONVENTION`) when a name is not kebab-case or a doc sits deeper than one group level. ## Four kinds of document Readers arrive with one of four needs, and a page that serves two of them serves neither (the Diátaxis split, used by Canonical, Python, and Cloudflare). Name the kind in `meta.tag` and keep each doc to one: | Kind | Reader need | Typical blocks | | --- | --- | --- | | Tutorial | learn by doing, start to finish | `steps`, `code`, `callout` | | How-to | get one task done now | `steps`, `flow`, `checklist` items, `table` | | Reference | look a fact up | `endpoint`, `table`, `erd`, `spec`, `glossary` | | Explanation | understand why it is this way | `c4`, `sequence`, `options`, `scqa`, `timeline` | A runbook is a how-to. An architecture overview is an explanation. An API doc is a reference; its "getting started" section is a tutorial and wants its own page once it passes three steps. ## When to split into multiple docs One document = **one system (or one job) for one audience**. Split when any of these hold; otherwise stay in one file — a 4-block doc doesn't need a folder. - **Two audiences.** An integrator reference and a new-joiner explainer about the same service are two docs, not two halves of one. - **Two systems.** The orders service and the notification pipeline each get a doc, even if they talk to each other — connect them with refs, not by merging. - **Two jobs.** "How it works" (overview) and "what to do at 3am" (runbook) read at different speeds. A runbook buried in an architecture doc won't be found during the incident. - **The skim breaks.** If reading only the `##` headings no longer tells one story (see move 2 in `SKILL.md`), the extra beats want their own doc. ## Slugs — the path *is* the reference prefix A doc's **slug** is its path under the docs root without `.md`: `docs/payments/api.md` → slug `payments/api` → its blocks are referenced as `payments/api#some-id`. Renaming a file renames every ref to it, so: - **kebab-case** file and folder names (`getting-started.md`, not `GettingStarted.md`). - **Folders are domains**, not types: `docs/payments/`, `docs/identity/` — never `docs/diagrams/` or `docs/misc/`. - Name for the subject, not the format: `orders-api.md`, not `api-doc-v2.md`. - Avoid ids named like `section-*` — the renderer uses those for its own section anchors. ## The index / overview doc Give a multi-doc set a landing page — `docs/overview.md` (or `docs//overview.md` per domain). It holds a `meta` block, 2-4 sentences of prose on what the set covers, and one big-picture block (`c4` context or `archmap`). When the set is large, add a `table` or `list` of the other docs and the job each does. It's the doc a new reader opens first and the natural home for ids that many docs reference. ## Cross-doc references - Ids are **repo-global unique** — `id: seq-place-order` can exist once across the whole docs tree, so a ref always has exactly one target. - **Same doc → prefer `#id`** (survives file renames). Other doc → `slug#id` (`payments/api#seq-charge`). - Point stories at the diagrams that realize them (`userstory.links[].ref` / `stories.items[].links[].ref` — the ref-bearing fields), rather than redrawing the diagram in the second doc. - Draw each diagram in the doc that *owns* it; every other doc links. When a system changes, one block changes. - `chiltepin check` fails on dangling refs and duplicate ids across the whole set — run it after any rename or move. ## How `chiltepin build` and `chiltepin studio` (Site mode) consume the set The layout above is exactly what the site generator reads: - **`chiltepin build`** renders every doc under the docs root into a static site. `index.html` is a card grid built from each doc's `meta` (title · subtitle · tag — another reason `meta` is never optional). Each doc becomes `.html` (folders keep their nesting), and a sidebar lists every doc with the current doc's sections expanded. - **Refs become links.** A `userstory`/`stories` link chip navigates to its target block — same page or `other-doc.html#id` across pages. A dangling ref degrades to a plain chip (and `chiltepin check` will name it). - **`chiltepin studio` Site mode** is the authoring loop: the studio mounts the same site from memory under `/site/…` and rebuilds + reloads it on every save. Switch the top bar to Site to browse it while you edit. So the organizing rules pay rent twice: a tidy tree reads well in the repo *and* ships as a navigable site with no extra configuration. ## Importing instead of transcribing When a doc's data-table already exists elsewhere, import it rather than retype it. `chiltepin sync csv ` turns a CSV export into a ready-made `table` / `statustable` / `chart` block (`--out docs/.md` wraps it in a new doc). `chiltepin sync openapi -o docs/api.md` generates a whole API doc — with `--check` keeping it drift-free in CI. The studio accepts the same files by drag-drop. The imported result is a normal doc on disk: edit it, ref it, `chiltepin check` it like anything hand-written. ======================================================================== # FILE: reference/patterns-design.md ======================================================================== # Design patterns — which blocks document them Part of the **chiltepin** skill (the hub is `SKILL.md`, one folder up). Read this when the request names a design pattern or asks "how is X structured". Every pattern here is the same stack, in this order: 1. `pattern` — the card: intent, forces, participants, consequences. One per pattern, always first. 2. `uml` — the structure: participants as classes, the relation `kind` carrying the meaning (`implements` for the interface, `composition` for ownership, `dependency` for a call). 3. `sequence` — the behaviour, only when the order of calls IS the pattern. 4. `code` — the smallest real example, when the doc is for implementers. Fields: `chiltepin block pattern`, `chiltepin block uml`. Never draw a pattern as a `flow`: a pattern is a set of roles and their relations, not a procedure. ## Gang of Four | Pattern | The structural fact `uml` must show | Add | |---|---|---| | Singleton | one class, a private constructor, a static `instance()` | — | | Factory Method | creator ↔ product interfaces, concrete pairs `implements` | — | | Abstract Factory | one factory interface, one family per concrete factory | `table` of families × products | | Builder | director `dependency` builder; builder `implements`; product built | `sequence` (build steps) | | Prototype | `clone()` on the interface, concretes `implements` | — | | Adapter | client → target interface; adapter `implements` target, `composition` adaptee | — | | Bridge | abstraction `composition` implementor; both have hierarchies | — | | Composite | component interface; leaf and composite `implements`; composite `composition` component (the self-reference) | `tree` of a real instance | | Decorator | decorator `implements` component AND `composition` component | `sequence` (the wrapping chain) | | Facade | facade `dependency` on each subsystem class; client sees one | `c4` component when it is a service boundary | | Flyweight | factory returns shared intrinsic state; extrinsic passed in | `envelope` (memory saved) | | Proxy | proxy `implements` subject, `composition` real subject | `sequence` (lazy load / access check) | | Chain of Responsibility | handler `composition` next handler (self) | `sequence` (one request through the chain) | | Command | command interface; invoker `composition` command; receiver | `sequence` (undo) | | Interpreter | expression interface; terminal / non-terminal `implements` | `tree` (a parsed expression) | | Iterator | iterator interface; aggregate creates it | — | | Mediator | colleagues `dependency` mediator, never each other | `sequence` | | Memento | originator creates memento; caretaker `composition` memento | `sequence` (save / restore) | | Observer | subject `composition` observers; `notify()` | `sequence` (one change, N updates); at system scale → `reference/patterns.md` pub/sub | | State | context `composition` state; concretes `implements` | `state` (the machine itself) | | Strategy | context `composition` strategy interface; concretes `implements` | `options` when the choice of strategy is the decision | | Template Method | abstract class with the skeleton; hooks overridden in subclasses | `steps` (the fixed order) | | Visitor | visitor interface with one `visit` per element; elements `accept` | `matrix` of visitors × elements | ## Architectural and distributed | Pattern | Stack | Trap | |---|---|---| | Layered / Clean / Hexagonal | `pkg` (allowed `deps` between layers, `dependency` arrows only inward) → `block` for the runtime | `c4` alone hides the dependency rule | | Repository / Unit of Work | `uml` (interface + implementation) → `sequence` for one transaction | `erd` (that is the data, not the pattern) | | CQRS, Event sourcing, Saga, Outbox | `reference/patterns.md` | — | | Circuit breaker, Retry, Bulkhead | `state` (closed → open → half-open) or `timing` (breaker vs downstream over time) → `spec` for the numbers | prose only | | Cache-aside / Read-through | `sequence` with an `alt` (hit / miss) → `spec` for TTL and invalidation | `flow` | | Sidecar / Ambassador | `cluster` or `block` (`preset: k8s`) with the sidecar in the pod | — | | Strangler fig | `block` (facade in front of legacy + new) → `roadmap` for the migration by theme | `timeline` | | BFF (backend for frontend) | `c4` container view with one BFF per client → `endpoint` per BFF | — | | Microkernel / Plugin | `pkg` (core + plugin packages, `deps` inward) → `uml` for the plugin interface | — | | Pipes and filters | `flow` (`variant: dag`) → `dfd` when data shape matters | `sequence` | ======================================================================== # FILE: reference/patterns.md ======================================================================== # Messaging and event patterns — which blocks draw them Part of the **chiltepin** skill (the hub is `SKILL.md`, one folder up). Read this when the request is about events, queues, streams, fan-out, or making a write reliable across two systems. Each pattern names the reader question, the block stack that answers it, and the trap. Fields: `chiltepin block `. Three rules hold for every pattern here: - Topology at rest is a `block` with `preset: event`. Producers are `kind: producer`, subscribers `kind: consumer`, brokers `kind: topic` (fan-out) or `kind: queue` (one taker), streams `kind: stream`. The single `producer` takes the accent; put the fan-out in one column so the topic reads as the hub. - One event's contract is an `eventcontract`, never a `table` row: it carries producers, consumers, delivery, ordering, the partition `key`, and retention, which is exactly what a consumer needs to be safe. - Time and failure are `sequence` (async arrows `-->` for the broker hop, an `alt` frame for the failure branch) or `flow` (retry loops, decisions). A `block` shows who is wired to whom; it cannot show a retry. ## Publish / subscribe (fan-out to many) Question: when X happens, who reacts, and does the producer know them? Stack: `block` (`preset: event`) → `eventcontract` for the event → prose on delivery and ordering. Trap: a `sequence` with one arrow per subscriber hides the point, which is that the publisher has no arrows to them. ```block preset: event groups: - { id: subs, col: 3, row: 1, cols: 1, rows: 3, label: Subscribers } nodes: - { id: orders, col: 1, row: 2, kind: producer, name: Orders, tech: order.placed } - { id: t, col: 2, row: 2, kind: topic, name: order-events, tech: Kafka } - { id: mail, col: 3, row: 1, kind: consumer, name: Email } - { id: search, col: 3, row: 2, kind: consumer, name: Search index } - { id: audit, col: 3, row: 3, kind: consumer, name: Audit log } edges: - orders -> t: publish - t -> mail - t -> search - t -> audit ``` ## Competing consumers (one taker wins) Question: how does work spread across N workers, and how many times is a message handled? Stack: `block` (`preset: event`, a `queue` not a `topic`, the workers in one group, dashed edges to the workers that did not take the message) → `spec` for at-least-once and idempotency rules. Trap: drawing it as pub/sub; a queue delivers each message once. ## Partitioned log and consumer group (streams) Question: how does the stream scale, and what keeps order? Stack: `block` (partitions as `queue` nodes inside a `Topic` group, consumers in a `Consumer group` group, the partition key on the producer edge) → prose on what the key is and what happens when a consumer joins or leaves. Trap: a `sequence`; the question is placement, not time. ```block preset: event groups: - { id: t, col: 2, row: 1, cols: 1, rows: 3, label: orders topic (3 partitions) } - { id: cg, col: 3, row: 1, cols: 1, rows: 3, label: billing consumer group } nodes: - { id: prod, col: 1, row: 2, kind: producer, name: Orders API } - { id: p0, col: 2, row: 1, kind: queue, name: partition 0 } - { id: p1, col: 2, row: 2, kind: queue, name: partition 1 } - { id: p2, col: 2, row: 3, kind: queue, name: partition 2 } - { id: c0, col: 3, row: 1, kind: consumer, name: billing-1 } - { id: c1, col: 3, row: 2, kind: consumer, name: billing-2 } - { id: c2, col: 3, row: 3, kind: consumer, name: billing-3 } edges: - prod -> p0: "key = order_id" - prod -> p1 - prod -> p2 - p0 -> c0 - p1 -> c1 - p2 -> c2 ``` ## Event-driven backbone (many producers, many consumers) Question: what is the shape of the whole event system? Stack: `block` in `layers` mode (Producers · Backbone · Consumers) with one `topic` node in the middle band → `table` of topics × producer × consumers × retention. Trap: drawing every topic as a node; past four topics the table carries it and the diagram shows the bands. ## Outbox (reliable publish after a commit) Question: how do we never lose an event when the commit succeeds and the publish fails? Stack: `sequence` (API → DB writes row and outbox in one transaction; relay polls outbox → broker; `alt` for the broker being down) → `state` for the outbox row (pending → published → failed) → `spec` for the invariants (same transaction, at-least-once, consumer idempotent). Trap: a `block` alone; the pattern is an ordering of writes, so it needs time. ## Dead-letter queue and retry (poison messages) Question: what happens to a message that keeps failing, and who looks at it? Stack: `flow` (consume → process → ok / retry with backoff / after N to the DLQ, `kind: error` on the DLQ edge) → `block` (`queue` → consumer → `DLQ` queue) → `table` of failure classes × action × owner. Trap: a `sequence`; the loop and the threshold are decisions, not messages. ## CQRS (separate read and write models) Question: why are reads and writes different shapes, and how does a write reach the read side? Stack: `block` (command side → write store → events → projector → read store → query side, `dfd` also works) → `sequence` for one write and the eventual read → prose on the consistency lag and what the UI does about it. Trap: an `erd` of the read model; the reader asked for the split, not the columns. ## Event sourcing (append-only log) Question: where is the truth, and how is current state rebuilt? Stack: `block` (commands → aggregate → event store, `replay` dashed back, projections subscribing) → `eventcontract` for one event → `spec` for replay, snapshots, and schema evolution. Trap: a `state` machine of the aggregate; the pattern is about storage, not lifecycle. ## Saga (multi-service undo) Question: what happens when step three of five fails after steps one and two committed? Stack: `saga` (forward steps, compensation under each, `failAt`) → `sequence` only if the message order between services is the question → `table` of steps × compensation × idempotency key. Trap: a `flow`; a saga's shape is steps with their undo, which `flow` cannot say. ## Scatter-gather Question: how does one request fan out to N workers and come back as one answer? Stack: `block` (coordinator → workers group → aggregator) → `sequence` with a `par` frame for the parallel calls and the timeout branch → `spec` for the partial-result rule. Trap: `flow`; the parallelism is the point and `par` draws it. ## Backpressure, retry, and circuit breaker Question: what does the system do when a downstream slows or fails? Stack: `state` for the breaker (closed → open → half-open) or `flow` for retry with backoff and the give-up exit → `spec` for the numbers (timeout, attempts, backoff, jitter, trip threshold) → `sequence` only for the one call that shows the breaker opening. Trap: prose only; the numbers are the contract, and `spec` holds them. ## Change data capture and webhooks (events out of a store or to a partner) Question: how do changes leave the database, or reach a partner, in order and exactly once as far as they can tell? Stack: `dfd` (table → log → connector → topic → consumers) or `sequence` (producer → partner endpoint, signed, retried, `alt` for 5xx) → `eventcontract` for the payload → `spec` for signing, retry, and replay. Trap: an `endpoint` block for a webhook; the partner's endpoint is not ours to document. ## Idempotency Question: what happens when the same message or request arrives twice? Stack: `sequence` with the duplicate as a second message and an `alt` frame (key seen → return stored result) → `spec` for the key, its scope, and its TTL → `state` when the record itself moves (received → processed). Trap: a `callout` only; the reader needs the exact key and window. ======================================================================== # FILE: reference/recipes.md ======================================================================== # Composition recipes Part of the **chiltepin** skill (the hub is `SKILL.md`, one folder up). These are worked examples of composition, not forms to fill in. Two different systems must not produce structurally identical docs. Start from the reader questions, not from a recipe. Keep a block only if your system raises its question, and drop it if not. Add a block these stacks never mention when your system needs it. Fields: `chiltepin block `; discriminators: the family files. Each recipe: the reader questions → the block stack in document order → the alternatives rejected, and why. The stack notes what each block carries and what the prose around it carries. Prose carries why, tradeoff, and consequence — never a description of the block beside it. ## Backend architecture Reader questions: What are the boundaries? · What calls what on the critical path? · Who owns what? 1. `meta` — the system's name and its one-line job. 2. Prose — why the system exists; the one constraint that shaped the design. 3. `c4` — ours vs external, one level only. Prose after: what the boundary decision costs, not what the picture already shows. 4. `cluster` or `block` (`preset: infra`) — the deployment topology. Prose after: which parts fail independently. 5. `sequence` — the one request path that pays the bills, with its failure branch. Prose after: the consequence a client can rely on. 6. `table` — service × responsibility × owner. 7. `callout` — the invariant that must not break. Rejected: `uml` (class detail is the code's job) · `graph` (no boundaries — a backend doc is about what contains what). ## AI / agent architecture Reader questions: What does the loop do? · What can it call? · What fills the window? · What does a real run look like? 1. `meta` — the agent's name and the task it owns. 2. Prose — the task delegated to the agent; where a human stays in the loop. 3. `agentloop` — environment, tools, memory, stop condition. 4. `context` — the window budget. Prose after: what gets evicted first, and why that is safe. 5. `prompt` — the contract the model is held to. 6. `sequence` — one turn end-to-end, including a tool failure. 7. `trace` — one real transcript, evidence the loop behaves as drawn. 8. `callout` — the safety boundary the agent cannot cross. Rejected: `flow` (the loop is the primitive here, not a branch chart). ## Frontend architecture Reader questions: What are the modules? · What states can the UI be in? · Where does data come from? 1. `meta` — the app and its rendering model in one line. 2. Prose — the rendering-model decision (SSR/SPA/islands) and its cost. 3. `frontend` or `felogic` — the module graph. Prose after: the dependency rule the graph must keep. 4. `wireframe` — the one screen that matters. 5. `state` — the UI states, error and empty included. Prose after: which state users actually sit in most. 6. `sequence` — the data-fetch path. 7. `table` — the routes. Rejected: `c4` (usually one container — a boundary diagram with one box says nothing). ## Data flow / pipeline Reader questions: Where does data come from and go? · What shape is it at rest? · How fresh is it? 1. `meta` — the pipeline and what depends on its output. 2. Prose — why batch or stream; the cost of staleness in user terms. 3. `dfd` (processes and stores) or `sankey` (when volumes are the story). Prose after: the stage that loses or transforms data. 4. `erd` — the shape at rest. 5. `steps` — the backfill / replay procedure an operator runs. 6. `slo` — the freshness targets the team commits to. Rejected: `sequence` (a pipeline has no request/response pairing to draw). Zone patterns compose — they are not block types. A medallion view (bronze → silver → gold) is `block` with `layers`: one layer per zone, one node per dataset with `tech` for format and retention, edges for the promotions. A deployment topology is `block` with `preset: infra` and `groups` for the account and network boundaries. ## State machine Reader questions: What states exist? · What forces a transition? · What is illegal? 1. `meta` — the object whose lifecycle this is. 2. Prose — why these states exist; the invariant the machine protects. 3. `state` — states and transitions. 4. `table` — transition × guard × side effect. 5. `sequence` — one path that exercises the risky transition. Prose after: what a caller observes while it runs. 6. `callout` — the illegal states, and why they must stay illegal. Rejected: `flow` (flows end; lifecycles loop back). ## Incident writeup Reader questions: What happened, when? · Why did it break? · What did it cost? · What prevents recurrence? 1. `meta` — incident id, date, severity. 2. `timeline` — detection to resolution. Prose after: where the response was slow, and why. 3. `sequence` or `flow` — the failure mechanism, not the happy path. 4. `stats` — the impact in numbers. 5. `steps` — remediation, each step with an owner. 6. `takeaways` — what the organization keeps from the incident. Rejected: `scqa` (a postmortem argues with evidence, not narrative). ## ADR Reader questions: What forced a decision? · What were the options? · What did we accept by choosing? 1. `meta` — the decision's id and title. 2. Prose (Context) — the forcing fact, with a number in it. 3. `options` — candidates against the criteria, verdict per card. 4. `callout` (title "Decision") — the decision in one sentence. 5. `proscons` — the consequences of the winner, both directions. Prose after: the tradeoff the team explicitly accepts. 6. `statustable` — the follow-up work the decision creates. Rejected: `harvey` (only when criteria resist numbers) · `scorecard` (weights imply a precision most ADRs do not have). ## API reference Reader questions: What can I call? · What do errors look like? · How do calls compose? 1. `meta` — the API and its version. 2. Prose — auth model, versioning, base URL: what endpoint cards cannot carry. 3. `endpoint` × N — one card per operation, examples included. 4. `sequence` — a multi-call workflow. Prose after: the ordering rule the workflow depends on. 5. `table` — the error codes and what the client should do about each. 6. `glossary` — the domain terms the paths use. Rejected: `packet` (binary protocols only) · standalone `code` (snippets live inside the endpoint cards). ======================================================================== # FILE: reference/style-ste.md ======================================================================== # STE discipline — style rules for Chiltepin text Aerospace maintenance manuals follow rules like these so that a technician who reads English as a second language cannot read a step in two ways. Our failure mode is the same: text that looks fluent but means several things. These rules adapt ASD-STE100 Simplified Technical English to software documentation. They trade style for one property: each sentence has exactly one reading. ## Vocabulary and terms - One word, one meaning. If `build` names a command, do not also use it for the compiled output. - One concept, one word. If the doc says `endpoint`, it never says `route`, `path`, or `handler` for the same thing. - Project terms live in a `glossary` block. That block is the approved term list for the doc. For every term outside it, use ordinary English. - Prefer the short common word: use, not utilize; before, not prior to; end, not terminate; about, not approximately. ## Sentences - Length limits: instructions, 20 words at most; descriptive sentences, 25. - Write one instruction per sentence. - Instructions are always active voice: "Run `chiltepin check`", never "`chiltepin check` should be run". Passive voice is allowed only in descriptive text, and only when the actor is truly unknown or irrelevant. - Use simple tenses only: past, present, future. No perfect or continuous forms. - Put the main action first and the condition after it: "Rerun the build if the check fails." - Keep articles and relative pronouns: "Run the check that validates the doc", not "Run check validates doc". When you drop these small words, the sentence becomes ambiguous. - Keep noun clusters to 3 words at most. Write "the handler that refreshes authentication tokens", not "authentication token refresh handler". - Do not use an -ing word as a noun or modifier where a verb or clause works. Write "when the parser fails", not "on parsing failure". ## Paragraphs - Descriptive paragraphs hold 6 sentences at most; procedural paragraphs, 3. - Give each paragraph one topic. Announce the topic in the first sentence. - Put warnings and prerequisites before the step they apply to, never after. ## Where each level applies **Full STE** — procedural and machine-adjacent text: `steps` blocks, CLI help, `chiltepin check` diagnostics, error messages, MCP tool descriptions, and the skill's own instructions. A reader parses this text under pressure, and the reader is sometimes another model. Every rule above applies. **STE-lite** — `prose`, `callout`, and `pullquote` content, and plain Markdown paragraphs. Every rule applies except the restricted vocabulary. Prose carries tradeoffs and consequences; a controlled word list flattens that. Keep the sentence limits, active voice, and simple tenses, and keep one word per concept. **STE-lite, completeness first** — block text fields (`description`, `lede`, `body`, `note`, `subtitle`, `summary`). The form rules apply, but never delete a fact to satisfy a length rule. Split a long sentence into two sentences. Keep every component, value, and condition the field states. **Not applied** — marketing copy, changelog voice, code comments. Diagram data (node names, messages, edge labels, states, values) is never edited for style: the diagram is the data, and it must stay complete. ## Worked pairs **Pair 1 — a restated diagram (docs/showcase.md:512).** ``` Before: A controller calls OrderService, which loads via an OrderRepository, charges through a PaymentGateway interface (Stripe/Adyen adapters), and egresses to Postgres, the event bus, and external gateways. After: Payment providers sit behind one interface, so a Stripe outage is a config change, not a code change. ``` The before repeats what the diagram already shows. The after states the consequence that the diagram cannot show. **Pair 2 — a lede with three jobs (resources/orders-api.md:29).** ``` Before: Time runs downward. Solid arrows are synchronous requests; dashed are responses. The order row exists as PENDING only inside the transaction — it is CONFIRMED before commit, or rolled back to CANCELLED on decline. After: An order is never visible as PENDING outside the transaction: it commits as CONFIRMED or rolls back to CANCELLED. Clients can treat every read as final. ``` The before teaches notation, describes arrows, and buries the invariant. The after does one job: the guarantee, and what it lets clients do. **Pair 3 — the wrapper opener (docs/showcase.md:9).** ``` Before: The blocks below are rendered from typed YAML fences. Edit the source .md file, rerun `chiltepin html`, and the HTML updates accordingly. After: Edit a YAML block to change its diagram. Then run `chiltepin html`. The .md file is the only source; there is no rendered state to fix by hand. ``` The before is passive, restates the page, and stacks two instructions into one sentence. The after gives one instruction per sentence, plus the fact that makes the edit safe. ## Constraints on how we use STE - ASD-STE100 is free to obtain but not free to redistribute. Never copy the specification text or its ~900-word approved dictionary into this repo. We apply the rules and keep our own term list. - Never claim Chiltepin output "is STE" or "is STE-compliant". Write "STE-informed" or "follows STE writing discipline". Certified compliance requires the real dictionary, and we do not ship it. ======================================================================== # FILE: reference/system-design.md ======================================================================== # Designing systems — the design method & the architecture blocks Part of the **chiltepin** skill (the hub is `SKILL.md`, one folder up). Read this for any architecture or design ask. ## Designing a system — reason it, don't template it "Design an X" asks (a notification system, a rate limiter, "how would you build Y at scale") are where templating shows worst. Every real system's document is shaped by *its* bottleneck. Do the design reasoning; each step emits the block that carries it: 1. **Requirements — ask, then pin them down.** Functional (what it does) and non-functional (scale, latency, consistency, durability). If the user gave no scale or constraints, **ask back** (move 1 of the method in `SKILL.md`; checklists in `intake.md`). Emit `drivers` — each driver a real requirement with its consequence, never a platitude. 2. **Envelope math.** Users × actions × fan-out → QPS, storage/day, peak factor. Emit `stats` with the numbers that justify the architecture — skip when scale genuinely isn't the story. 3. **Contract.** The API surface (`endpoint` per route that matters) and the data model (`erd`). These fix the names every later block reuses. 4. **High level, shaped by the dominant motion.** Request/reply system → `c4`; things flowing through stages → `block` (`preset: event`) or `dfd`; deployment/regions/zones → `block` (`preset: infra`); a k8s estate → `cluster`. One overview diagram of the whole system, using the real names from step 3. 5. **Deep-dive the bottleneck — this is where documents differ.** Work out what actually breaks at the stated scale, and design *that* section: - hot reads → caching + invalidation (a `sequence` of the miss path) - write spikes → queue + backpressure (a `flow` with the shed path) - fan-out → push vs pull (`options`, then the chosen `sequence`) - cross-service consistency → outbox/saga (a `state` of the saga) - geo-latency → replication + CDN (a `preset: infra` `block` per region) One or two deep dives, chosen by the numbers from step 2 — never a fixed list. 6. **Trade-offs on the record.** The genuine alternatives as `options` (with `tone: chosen` on the winner), or `proscons` when only one option's tension matters. A design doc with no rejected alternative wasn't a decision. 7. **Failure & operations.** What breaks, the blast radius, how it degrades: a `table` of failure modes → responses, `kind: error`/`forbidden` edges on the diagrams, a `flow` of the degradation path. 8. **Plan.** `timeline` for phasing, `statustable` for the open questions you asked in step 1 but didn't get answered. **Patterns are ingredients, not the meal.** When a named pattern is load-bearing (pub-sub, saga, circuit breaker, CQRS, …), write it as a `pattern` card plus one structure diagram that fits it (`block` for system patterns, `felogic` or `uml` for code patterns, `flow`/`state`/`sequence` for agent patterns). Then **name every node in the user's domain**: a card whose participants are still "ServiceA" was pasted, not designed. Comparing patterns side by side → a `gallery` with a nested `pattern`/diagram per cell. The outline that falls out of steps 1-8 differs per system. A rate limiter's doc is mostly steps 4-6 with algorithmic depth (`state`, `code`). A social feed's is dominated by step 5 fan-out math, a payments integration by step 7 failure semantics. **If two of your design docs share the same section list, you skipped step 5.** ## Architecture and topology (which one when?) | If you want to show… | Use | Notes | |---|---|---| | Who uses the system + which external systems it depends on | `c4` (level: context) | One node per actor / system | | Containers inside a system, with optional boundary box | `c4` (level: container) | Use `family` to colour-code (client / service / data / store) | | Components inside one container | `c4` (level: component) | Same shape, finer granularity | | Generic boxes-and-arrows architecture | `block` | Grid layout; add `groups` for dashed zones; add `layers` to switch to horizontal-band layout | | Cloud deployment (CDN, gateway, compute, DB, …) | `block` (`preset: infra`) | Same engine; the preset frames it for cloud topology | | Pub/sub event topology (producers → topics → consumers) | `block` (`preset: event`) | Same engine; framed for choreography | | Bounded-context map for DDD | `block` (`preset: ddd`) | Same engine; framed for context maps | | Security zones with trust boundaries | `block` (`preset: network`) | Same engine; supports `kind: forbidden` edges (red) | | Kubernetes-style namespaces with services inside | `cluster` | Has its own nested-box engine; supports `replicas` count | > Every `block` preset shares **one renderer** — presets differ only by the > colored tag pill above the diagram (ARCH / INFRA / EVENT / DDD / ZONES) and > the section eyebrow. Pick the preset that best signals intent to a reader; > the YAML grammar is identical. (The old type names `infra` / `event` / `ddd` > / `network` still work as permanent aliases.) **Quick mode — no coordinates.** Every architecture diagram can be written as just nodes + edges. Omit `col`/`row` on **all** nodes and the renderer computes a clean left-to-right layered layout from the edges. This is the default way to sketch a system fast. Add explicit coordinates only when you want a deliberate shape — and always when you use `groups`. Zones are anchored to grid cells, so they need placed nodes. If *any* node has coordinates but others don't, the auto-layout replaces all of them — place all or none. **Edge labels — dense diagrams renumber themselves.** On every edge-bearing diagram (`block` — any preset, `flow`, `dfd`, `graph`, `swimlane`, `uml`, `cluster`, `felogic`), up to three labelled edges render as text pills riding the arrows. At **four or more labelled edges** the renderer switches to circled step numerals and moves the label text to a numbered legend under the diagram. (`state` is the one exception — its numerals point at the transition table's rows instead of a second legend.) You don't opt in or lay anything out. Just keep edge labels short (a verb phrase, a couple of words) so they read well as a pill or a legend entry. **Node kinds** (block family; free strings — known ones get a colour + glyph): `client` · `service`/`microservice`/`compute`/`container` · `worker`/`etl` · `db`/`store`/`database` · `bucket`/`blob` · `queue`/`mq`/`broker` · `stream` · `cache` · `gateway`/`lb`/`proxy` · `function`/`lambda` · `cdn` · `dns` · `waf`/`firewall`/`shield` · `auth`/`idp`/`iam`/`oauth`/`sso` · `secrets`/`vault`/`kms` · `monitor`/`metrics`/`logs`/`tracing` · `scheduler`/`cron`/`job` · `warehouse`/`lake` · `analytics`/`bi` · `search`/`index` · `ml`/`model`/`llm`/`agent` · `vm`/`server`/`host` · `user`/`person`/`browser`/`mobile` · `users`/`crowd` · `device`/`iot` · `notification`/`webhook` · `email`/`sms` · `ci`/`cicd`/`pipeline` · `git`/`repo` · `registry` · `config` · `shard`/`sharded` · `replica`/`replicaset` · `region`/`geo`/`globe` · `producer`/`topic`/`consumer` · `context` · `external`. **Vendor names work too**: `postgres`/`mysql`/`mongo`/`dynamo` → db, `s3` → bucket, `sqs`/`rabbitmq` → queue, `kafka`/`kinesis` → stream, `redis`/`memcached` → cache, `elasticsearch`/`opensearch` → search. Pick the closest kind — an unknown kind renders as a neutral box. **Kinds also pick the shape** (the canonical system-design silhouettes — you get the right one automatically by choosing the right kind): | Shape | Kinds | |---|---| | cylinder | `db` `database` `store` `postgres` `mysql` `mongo` `dynamo` | | tiered cylinder | `warehouse` `lake` | | pail (bucket) | `bucket` `blob` `object` `s3` | | **sharded trio** (3 small cylinders) | `shard` `shards` `sharded` | | **replica set** (stacked cylinders) | `replica` `replicas` `replicaset` | | horizontal cylinder (pipe) | `queue` `topic` `stream` `mq` `broker` `sqs` `rabbitmq` `kafka` `kinesis` | | cloud | `cdn` `external` | | hexagon | `gateway` `proxy` | | octagon | `lb` | | instance stack (receding cards) | `cache` `redis` `memcached` `worker` `etl` | | server rack (stacked slabs) | `vm` `server` `host` | | shield | `waf` `firewall` `shield` | | actor figure (boxless) | `user` `person` `actor` | | crowd (overlapping figures) | `users` `crowd` | | browser window | `browser` `web` | | phone frame | `mobile` | | circle with ƒ | `function` `lambda` | | calendar with clock badge | `scheduler` `cron` `job` | | padlock | `secrets` `vault` `kms` | | globe (boxless) | `region` `geo` `globe` | | clean rounded card + glyph | everything else | The same silhouettes apply inside `felogic` (db/store → cylinder, queue/bus/broker → pipe, cache → stack, external/backend/api → cloud), in `c4` (`kind: store` → cylinder), and in `cluster` (db services → cylinder). A database looks like a database in every diagram. **C4 extras.** Edges take `tech:` — rendered as `label [tech]`, the C4 convention for the protocol. `boundaries[]` draws several named dashed boxes, each fitted around an explicit `nodes: [ids]` list (optional `color`) — so one diagram shows your platform and a partner's estate side by side. The single auto-fit `boundary:` still works for one system. **Mixing architecture views — one overview + one zoom.** Show the whole system once (`c4` context, or a `block` landscape), then zoom into the one or two places the doc is actually about. Zoom with `felogic` (or its `variant: be`) for a module's internals, a `preset: infra` `block` or `cluster` for deployment, and a `sequence` for the runtime of one path. Never redraw the same boxes in a second engine — pick one block per level of zoom and stitch them with prose ("inside the `api` container: …"). ======================================================================== # FILE: reference/writing.md ======================================================================== # Writing blocks — step 6 of the procedure Read this file when a YAML question is not answered by `chiltepin block `. It holds the block grammar, the full terse-item table, the YAML traps, the reference scheme, and the naming rules. The field contract and an example for every block come from `npx -y chiltepin block `. ## How a block looks ```` ## Request flow ```sequence id: seq-place-order endpoint: { method: POST, path: /orders } actors: - { id: Client, name: Client } - { id: API, name: Orders API } messages: - Client -> API: POST /orders - API --> Client: 201 Created ``` ```` Rules: - The info-string is exactly one of the block types listed by `chiltepin block` (`blocks/INDEX.md` is the same list). Never invent a type. The 12 old merged names remain valid as permanent aliases. - The body is **YAML**. JSON is valid YAML and also parses — write JSON when a value is full of commas or colons. A `mermaid` fence is also accepted for five diagram grammars — see `mermaid.md`. - Use only the fields documented for that block. The schemas are strict: an unknown field is an error. Keep prose outside blocks. - A block MAY carry a top-level `id:` (a slug) so other blocks can reference it. - Most diagram blocks accept optional `title`, `description`, and `lede`. A `##` heading directly above a block IS its title — omit the block `title` unless it must say something the heading does not. - A `description` is at most 2 sentences. Longer narrative goes in prose. - Never paste raw HTML, ``, or `