Ctx spec
The agent context layer — converge messy multi-session AI-agent work into one lean, LIVING, spec-centered source of truth that doesn't rot. Organize docs by lifetime, keep the spec generative, and let an agent (or a human three months later) build from it.
npx -y skills add motiful/ctx --skill ctx-specAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Writes specs, ADRs, architecture and design docs that an AI coding agent can build directly from — the right granularity (constraints, not hand-written implementation), testable EARS acceptance criteria, and ADR discipline (record only decided choices, supersede in place). Use when writing or restructuring a spec, a decision record (ADR), an architecture doc, or a design doc in a ctx knowledge base, or when deciding how detailed a spec needs to be.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
23.9 KB, as published. Nobody here has run it
ctx-spec — Write Docs an Agent Can Build From
The lifetime model and shared conventions live in
../ctx; this is the spec / ADR / design how-to layer.
How a spec is written — the writing standard
A spec is an authored doc an agent reads once and builds from. Four orthogonal axes govern every line; each governs a different thing, so they compose without clashing:
- Status — normative vs informative. A spec states conclusions and constraints (normative). A note, example, or figure is never a requirement (informative). State a binding claim as a MUST/SHALL line — don't let a diagram imply it.
- Strength — RFC 2119.
MUST / SHOULD / MAY, all-caps, grep-able (all-caps only per RFC 8174). Lower-case "must" is prose, not a requirement. - Why-discipline (IRB) — which "why" may stay. Classify every "why" by what it does for the agent that reads the spec:
- INTENT — a one-clause so-that that scopes a rule (
X, so that Y) so the agent generalizes to cases the spec never enumerated. Stays inline with the rule. - RATIONALE — the justification of a choice among alternatives (
chose X over Y because Z). Goes to an ADR (Considered options+Decision outcome), never the spec body. - BACKGROUND — context the model already knows, or history that changes no action. Cut it.
- Delete-test: delete the clause — does an edge-case action change? Generalizes the rule → INTENT (keep). Only justifies a past choice → RATIONALE (→ ADR). Nothing → BACKGROUND (cut).
- INTENT — a one-clause so-that that scopes a rule (
- Granularity — specify the constraint, not the mechanism; testable (EARS), versioned. Its own section below (the most important one).
The one anti-pattern (mode bleed): a spec that teaches, compares options, or narrates what happened has let Explanation leak into a normative doc. Rationale → an ADR; comparison / review thinking → a report. The single "why" that belongs in a spec is INTENT — it carries normative force, because deleting it changes conformance at the edges.
Execution Procedure
write_spec_or_record(target) → build_grade_doc
kind = classify(target) # subsystem-spec | decision(ADR) | design | architecture(HLD) | theory
if kind == "theory": # psychology / research-evidence / a chosen framework
split(target): normative_extract → spec (principle) + decisions (why-this-framework) ; raw_body → scratch/
if kind == "subsystem-spec":
apply(granularity_rule) # specify constraint not mechanism; no hand-written LLD
fill(spec_template) # Tier A precise (EARS + interfaces + schemas + invariants) · Tier B loose (HLD sketch)
mark_open_questions() # [NEEDS CLARIFICATION] while draft
if kind == "decision":
assert decided(target) ∧ significant(target) # a MADE, significant choice — never a "proposed" parking lot for open options
n = next_adr_number() # NNNN, 4-digit, sequential, NEVER reused
write(f"decisions/{n}-title.md", adr_template) # rich enough that "why X not Y" is derivable later
if reverses(prev): set(prev.status, f"Superseded by {n}"); link_bidirectional(prev, n) # supersede in place, never delete
update("decisions/README.md") # index row: number · title · status · date
if kind == "design":
read("references/design-system.md") # flat-first structure · layer taxonomy · DTCG tokens · motion · content boundary · tool-free standard
if kind == "architecture":
place_hld() # C4 L1+L2 → overview.md ; L3 → per-subsystem Tier B ; L4 → agent generates
assert tierA_is_testable ∧ out_of_scope_stated ∧ no_hand_written_LLD # GATE
apply("../ctx/references/consistency.md") # cross-cutting rules + conventions — verify before commit
The granularity rule (the most important thing)
Specify the constraint, not the mechanism. Loosen the prose, harden the tests.
- Spec to: verifiable acceptance criteria (EARS / Given-When-Then) + named interfaces + data schemas + invariants + security constraints + explicit out-of-scope. Stop there.
- Do NOT hand-write low-level design (class/method internals) — the agent generates that in code. Big-company BRD→PRD→HLD→LLD collapses: BRD+PRD fold into one spec, HLD survives, LLD disappears as a human doc.
- Under-specify → the agent drifts/invents intent. Over-specify → you've hand-written pseudo-code (waterfall, brittle). Sweet spot: constraints + acceptance criteria; the agent picks the how. ("session lookup must be O(1)", not "use a HashMap".)
- Scale-dependent: loose specs for prototypes/recoverable work; hardened contracts for transactional/regulated systems.
- Traceable end-to-end. A user story threads forward through its requirement → design → task and stays followable (
story → REQ-### → design → T###, where theT###task rows live inprogress/and point back to aREQ-###); IDs are immutable and never reused. A criterion nobody can trace to a task won't get built; a task with no requirement is scope creep.
| MUST be machine-precise | SHOULD stay loose (agent decides) |
|---|---|
| interfaces / API contracts / signatures | internal implementation approach |
| data schemas + types | internal class/module structure |
| acceptance criteria (EARS/GWT, binary pass/fail) | algorithm choice where any correct one works |
| invariants & business rules | file organization, private naming |
| security / auth / privacy / compliance | micro-optimizations |
| error & edge taxonomy; non-functional limits | |
| out-of-scope / non-goals (agents can't infer from omission) |
spec.md template (per subsystem)
# <subsystem> Spec
status: draft|active · owner: <name> · last-verified: <YYYY-MM-DD>
## 1. Intent & why [MUST] problem + who + why now
## 2. Non-goals [MUST] what it explicitly does NOT do
## 3. User scenarios [MUST] As <role> I want <X> so that <Y> — P1/P2/P3
— TIER A · INTENT/BEHAVIOR (precise, verifiable) —
## 4. Requirements [MUST] one row per requirement, each with an immutable `REQ-###` id
e.g. `REQ-001` <the requirement> — traces from scenario §3
## 5. Acceptance criteria [MUST] EARS: WHEN <trigger> the system SHALL <response>; one clause = one test
each criterion cites the `REQ-###` it verifies
## 6. Interfaces & contracts [MUST] public signatures, endpoints, event schemas
## 7. Data models [MUST if data] entities, field types/constraints
## 8. Invariants & rules [MUST if domain logic]
## 9. Constraints [MUST] security/auth/privacy + non-functional limits
## 10. Error & edge behavior [MUST] failure taxonomy, retry/fallback
— TIER B · CURRENT STRUCTURE (descriptive, deliberately loose) —
## 11. Architecture sketch [OPT] HLD only — components + connections (Mermaid). NOT class-level
## 12. Existing code map [OPT] where it lives / files to touch
## 13. Implementation notes [OPT] hints; agent may override with rationale
## 14. Open questions [NEEDS CLARIFICATION] [MUST while draft]
## 15. Assumptions [OPT]
# Tasks are NOT part of the spec. The ordered T### task list + status live in progress/
# (ctx-progress); each T### points back to the REQ-### here that it implements.
The REQ-### (§4) fields make each requirement individually addressable. The traceability chain story (§3) → REQ-### (§4) → acceptance criterion (§5) / design (Tier B) → T### (in progress/) is followable end-to-end — the spec owns story→design; the T### task rows live in progress/, each pointing back to the REQ-### here that it implements. IDs are immutable and never reused (granularity rule above; a hard constraint — see § Hard constraints below).
Tasks live in progress, not the spec. The spec ends at requirements + design (§1–§13, §14–§15). The ordered task list — the
T###rows, their status (done / active / blocked), and the working construction plan — lives inprogress/(ctx-progress); eachT###points back to theREQ-###it implements. Rule of thumb: spec defines what must be true; progress tracks the doing. This keeps the durable contract (spec, never archived while the product lives) cleanly separate from the transient plan (progress, archived when done). A one-off chore with no durable product-truth delta gets no spec at all — the whole plan is justprogress/.
EARS keywords: Ubiquitous the system SHALL …; Event WHEN <trigger> …; State WHILE <state> …; Unwanted IF <cond> THEN … SHALL …; Optional WHERE <feature> …. Review Tier A like a contract (precise/testable); Tier B like a map (orient, not binding). If you're writing Tier B to method level, move that constraint UP into Tier A as an acceptance criterion or interface.
Truth arbiter
- Intent (what should be) → the spec is authoritative; if spec says X and code does Y, the code is wrong.
- Behavior (what actually is) → the running code + tests are authoritative; a doc that disagrees is stale.
- Bridge: compile EARS/GWT acceptance criteria into CI tests so the two stay aligned.
- Recovering from drift — when the agent builds the wrong thing, fix the spec first, then re-run; never patch code past a stale spec. The spec is the durable artifact, code is its current expression; treat the spec as a living doc, not a one-shot prompt. (Stale specs are the #1 spec-driven-dev failure — Kiro / Spec-Kit; updating the spec first is how you keep it alive.)
- When the drift is code-vs-doc (behavior changed but the spec didn't) rather than agent-vs-spec, keeping the two in truth-sync in the same change is
../ctx/references/consistency.mdRule 2 (same change, no drift) — this section states the authority model; that rule enforces it on a behavior-changing commit.
Theory & evidence — conceptual content CAN be normative
A theory / psychology model / research-evidence input is not disposable by default (normative = "binding on what we build", not "concrete vs abstract"). Route it by splitting:
- Normative extract → the SOT. Which paradigm and how it's applied → a spec design principle. Why this framework, not others → an ADR.
- Raw literature / evidence body →
scratch/(disposable), cited by the spec/decision. Reliability flows from the SOT link, not from the file sitting there. - Classic theory → just name it (the model already knows it). New (2026) theory → cite the URL + a one-line summary in the spec. A theory you designed yourself IS a spec (you authored it — write it as a principle).
ADR (decision record) format, numbering, and how it's read
# NNNN. <short noun-phrase title>
* Status: proposed | accepted | deprecated | superseded by NNNN — see status lifecycle below
* Date: YYYY-MM-DD · Deciders: <names>
## Context and problem statement — the forces in tension
## Decision drivers (optional)
## Considered options — real alternatives (not straw men)
## Decision outcome — chosen option, because …
### Consequences — good AND bad
## Confirmation (optional) — how it's enforced (review / test / lint)
What belongs IN an ADR (the content boundary)
An ADR records a decision that was actually MADE, rich enough that "why X, not Y" is DERIVABLE from it later — this derivability is exactly why the collection stores no long-term explanation docs. It holds:
- the decision made + its real considered alternatives (not straw men),
- the outcome (chosen because…) + consequences (good AND bad), + optional confirmation.
What stays OUT:
| Not an ADR | Goes to |
|---|---|
| Undecided deliberation (options still open) | progress/ or the live session — NEVER a "proposed" parking-lot ADR |
| Mutable current-state fact | spec/ |
| Implementation detail | code / spec/ |
| An event log ("what happened") | git history |
Significance bar: write an ADR when a decision is architecturally significant / costly-to-reverse / non-obvious / would-otherwise-be-relitigated. Trivial reversible choices get no ADR — they're noise.
Rejected-alternative clause (soft norm). When a choice meaningfully turned away from an option, capture it — even a one-line decision-log entry should read — rejected X because Y where relevant. This feeds the reject-log that the read-side scan consults, so the same option isn't reintroduced later. (In a full ADR this is already carried by Considered options + Decision outcome; the clause matters most for the lightweight one-liners.)
Before deciding — read the trail (read-side scan). Before an architecturally-significant choice, SCAN decisions/ + the reject-log so you don't re-litigate a settled question or reintroduce a rejected concept. This is the write-side payoff of the append-only trail — a global habit (see ../ctx/SKILL.md § the read-side habit; a MUST in § Hard constraints below).
Status lifecycle (precise)
| Status | Meaning |
|---|---|
proposed | Transient, awaiting acceptance. NOT a place to park undecided options — a proposed ADR is a decision about to land, not an open debate. |
accepted | Current; the decision is in force. |
deprecated | The decision was retired and NOTHING replaces it — it no longer applies. (Distinct from superseded.) |
superseded by NNNN | Replaced by a specific new ADR. Bidirectional link; old body left untouched. |
rejected | A choice that was considered and declined before ever being adopted — distinct from deprecated (which was accepted, then retired). Feeds the reject-log read-side scan (see ctx-merge) so a declined concept isn't quietly reintroduced under a new label. |
Live-log tag mapping. A real project often tracks decisions inline with lightweight tags before they become formal ADRs. Map them: [LOCKED] = accepted; [DRAFT] = proposed (a decision about to land, not an open debate); [OPEN] = not yet a decision at all → it belongs in progress/ (or a spec [NEEDS CLARIFICATION] block), NOT in decisions/. Only [LOCKED]/[DRAFT] graduate to an ADR; [OPEN] never does until it's decided.
- One decision per file —
decisions/NNNN-title-with-dashes.md. NOT many-in-one-file: per-file numbering, per-file status, and per-file supersede links all assume it, and an agent can open exactly the one it needs (cheaper than loading a monolith). - Numbering: sequential, 4-digit zero-padded (
0001), monotonic, never reused — Nygard's original rule. - Immutability: a decided ADR is NEVER rewritten. You only ever APPEND a supersede pointer and flip the old status — the original clause text stays exactly as written. Reversing a decision = supersede in place, NEVER edit-the-old / delete. Write a NEW ADR with
Supersedes: NNNN; flip the old toSuperseded by NNNN(bidirectional). The old file stays, body untouched — the truth is the whole chain. This is a strong, documented CONVENTION (Nygard, Fowler, AWS, Microsoft, adr-tools), not an ad-hoc choice; the append-only trail ("we thought X, then Y") is exactly what prevents re-litigating settled decisions. - The index carries the token cost, not a forced read.
decisions/README.mdis a table (number · title · status · date · supersedes/superseded-by), generatable by adr-tools/adr-log. An agent reads the index first, readsspec/for "what to build", and opens a specific ADR only for "why is this like this / can I change it". A superseded ADR costs one index row, not a full read — so keeping history is cheap. - ADRs are never archived (unlike specs) — see archive rule below.
Worked example — the supersede chain (bidirectional, in place)
decisions/0003-session-store.md (flipped when 0007 lands — body below the header untouched):
# 0003. Session store in Postgres
* Status: Superseded by 0007
* Date: 2026-02-10
Chose Postgres for session rows: one datastore, transactional with the user table.
decisions/0007-session-store-redis.md:
# 0007. Move session store to Redis
* Status: accepted · Supersedes: 0003
* Date: 2026-06-30
p99 session lookup missed the O(1) target under load; moved to Redis. Trade-off: a second datastore to operate.
The chain (0003 ⇄ 0007) reads in place — no deletion, no archive; the index carries both rows.
Worked example — minimal spec skeleton (Tier A vs Tier B headers)
# Auth Spec — status: draft · last-verified: 2026-06-30
## 4. Requirements [Tier A] REQ-001 an expired token must be rejected
## 5. Acceptance criteria [Tier A] WHEN a token expires the system SHALL return 401 (verifies REQ-001) ← one clause = one test
## 6. Interfaces [Tier A] POST /session → {token, exp}
## 11. Architecture sketch [Tier B] gateway → auth-svc → session store (Mermaid, not class-level)
# the T001 task (implement token-expiry check → REQ-001) lives in progress/, NOT the spec
HLD / architecture (C4) — placement
- System level (C4 Context + Container) → ONE top-level
ctx/overview.md. Whole-system shape, tech choices, inter-service comms. Global, slow-changing. - Subsystem level (C4 Component) → inside each
spec/<subsystem>.mdTier B. Co-evolves with its spec. - Code level (C4 L4) → not drawn; the agent generates it.
- Diagrams: Mermaid-as-code (
flowchart/sequenceDiagram/erDiagramrender natively on GitHub; C4 Mermaid blocks do NOT render on GitHub — use PlantUML+C4-PlantUML if true C4 is needed).
spec/design/ — design is a spec subtype
Design (layout / color / type / motion) can't be expressed precisely in Markdown, so under spec/design/ the .json (tokens) and .html (comps) files carry the truth directly — but the two differ: token JSON is the diffable contract (the true SOT), an .html comp is reference-grade intent (not pixel-truth). The structure (flat, graduating to subfolders when it grows), the layer taxonomy, the DTCG token model, motion (global tokens + local choreography), the tool-free design standard, and the content boundary are in references/design-system.md. Read it before writing any design doc.
Archive rule (spec vs decision — they differ)
Follows the lifetime class (global rule in ../ctx/references/consistency.md § Archive mechanics):
- spec (LIVING, detailed): localized change → edit in place; foundational rewrite → move the WHOLE old spec to
spec/archive/under the canonical archive filename (defined once in../ctx/references/consistency.md § Archive mechanics) and write ONE clean new<name>.mdreusing the canonical name (the active spec stays a single elegant current-truth doc — never a pile of deprecated sections). Record the why we rewrote as an ADR. Ripple-fix references in the same commit. - decisions (APPEND-ONLY): never archived — supersede in place (above). The trail is the value.
- Archive folder is per-folder, plain name
archive/, verbatim move + pointer, never delete by default.
Hard constraints (spec authoring)
The MUST/NEVER lines below are the domain constraints for spec / ADR / design authoring — binding, load-before-act; they fire when a spec, decision, or design doc is written. The cross-cutting rules (single-source, same-change, verify-canonical, the gate) live in
../ctx/references/consistency.mdand apply on top. The formats/templates above teach; these lines bind.
Status & why-discipline
- MUST write a spec as conclusions + constraints (normative content). NEVER embed teaching, tutorials, discursive narrative, or option-comparison in a spec — that is Explanation bleeding into a normative doc; move it to a
reports/doc (for review) or an ADR (the why). - MAY carry INTENT inline — a one-clause so-that that scopes a rule so the agent generalizes correctly (
X, so that Y); this is the one "why" that stays, because deleting it changes edge-case conformance. MUST NOT embed RATIONALE ("why we chose X over Y") → that belongs in the ADR'sConsidered options+Decision outcome. MUST cut BACKGROUND (context the model already knows / history that changes no action). - NEVER treat a figure, example, or note as a binding requirement — illustrative content is informative, not normative. State the normative claim as a MUST/SHALL line; do not let a diagram imply it.
- MUST run a term-operation consistency check before naming a new concept in a spec: if the term's full established meaning conflicts with the requirement it's attached to, drop the term and keep only the requirement (same gate as
skill-forge/references/skill-format.md § Term Usage's Consistency gate, adopted fromctx-report'sterm-operation-consistency-gate).
Testable, versioned, machine-readable
- MUST use testable acceptance criteria (EARS
WHEN <trigger> the system SHALL <response>/ Given-When-Then) with quantified thresholds — one clause = one test, binary pass/fail. NEVER an unfalsifiable criterion ("should be fast" → "P95 < 200 ms"). - MUST give versioned tech choices (
Postgres 16, not "a database") and machine-readable contracts (interfaces / signatures / endpoints / event schemas / data models with types). - MUST specify the constraint, not the mechanism — no hand-written low-level design ("session lookup must be O(1)", not "use a HashMap"). State non-goals / out-of-scope explicitly (an agent can't infer them from omission).
Traceability & drift recovery
- MUST keep the ID chain traceable end-to-end — story → requirement (
REQ-###) → design (spec) → task (T###, inprogress/), immutable, never reused, followable. NEVER ship a requirement whose acceptance criterion no task will implement, nor (inprogress/) aT###task that traces to noREQ-###. - MUST, on agent misbehavior, fix the spec first then re-run — the spec is the artifact, code is its current expression; NEVER patch code past a stale spec or treat the spec as a one-shot prompt.
No dangling pointers
- MUST NOT leave a dangling pointer — e.g. "see Q1–Q5" / "per §4" / "→ ADR-0007" where the target is undefined, unwritten, or unresolved. Every forward reference in a spec resolves to a defined, current target in the same commit. (Ties to consistency.md Rule 1's stale-pointer gate.)
ADR discipline
- MUST, before an architecturally-significant choice, scan
decisions/+ the reject-log — do not re-litigate a settled decision or reintroduce a previously-rejected concept. (The read-side payoff of the append-only trail; global habit in../ctx/SKILL.md § the read-side habit.) - MUST have an ADR record only DECIDED choices — a made choice + its real alternatives + consequences. NEVER park undecided options as a "proposed" ADR to be filled in later; undecided deliberations stay in
progress/(or a spec's[NEEDS CLARIFICATION]block) until decided, then land as anacceptedADR. - MUST number ADRs
NNNN(4-digit, sequential, monotonic, never reused); one decision per file; reverse only by supersede-in-place (bidirectional status flip, old body untouched — mechanics above).