---
name: bartsoj/SPEC.md
source: https://app.decimal.ai/s/bartsoj-spec-md@1/SKILL.md
source_sha256: 1b31fc58e9f8
---

# Task: Create SPEC.md for a Work Unit

## Objective

Produce a SPEC.md that serves as the single source of truth for implementing one work unit: a file manifest, every public symbol with a full signature, every behavioral path as ordered prose, every error as an `ERR_CODE` row bound to ERRORS.md § 3, every test as a setup/action/assertion triple, and every design-layer citation (invariants, events, state machines, sagas, metrics, SLOs, threats, mitigations, use cases, data aggregates) the unit touches. The defining discipline — and the definition of "done" — is that **the gap between SPEC and code is purely mechanical**: an implementing agent reading only this file (and the dependency SPECs it cites) produces the implementation without making any architectural, behavioral, or naming decisions of its own.

The commonest violation is silently picking an answer for a design-level question instead of surfacing it. When the design suite is silent, ambiguous, or contradictory on a question the unit must answer — a wire shape, an error code, a lock granularity, a field name — the question belongs in § 10 Open Questions with options, tradeoffs, and a recommendation. Do not invent. The SPEC must never introduce a design decision that is not already traceable to DOMAIN, INTERFACES, BEHAVIOR, ERRORS, QUALITY, SECURITY, DATA, or USE_CASES.

---

## Inputs

1. **Trigger artifact(s)** (required) — one or more `roadmap/<NNN>-<slug>/ROADMAP.md` (planned work) and/or `issues/<NNN>-<slug>/ISSUE.md` (defects in shipped code) that initiated this unit. Read the body and frontmatter end-to-end: the trigger states what the unit must deliver, its acceptance criteria, and the area it belongs to.
2. **Unit folder with stub frontmatter** (required) — the orchestrator has pre-created `units/<area>/u<NN>/SPEC.md` with frontmatter populated (`unit`, `kind`, `area`, `trigger`, `status: in-design`, and possibly initial `depends_on` / `supersedes` / `related` candidates). Empty list fields (`files: []`, `concepts: []`, `owns_ids: []`) are placeholders this skill fills in. The SPEC body is what this skill writes. Path: `units/<area>/u<NN>/SPEC.md`.
3. **Architecture and design suite** (required) — DOMAIN.md plus the design registries whose stable IDs the SPEC will cite: ARCHITECTURE.md, INTERFACES.md, BEHAVIOR.md, ERRORS.md, QUALITY.md, SECURITY.md, DATA.md, USE_CASES.md. The subset actually loaded is the read set the orchestrator passes — the registries whose IDs the unit touches given its trigger and area.
4. **Decision documents** (optional, auto-discovered) — any `D-NNN` referenced from the trigger or from any cited design artifact. Discovery mechanism: follow `D-NNN` citations and read each referenced decision at `decisions/D-NNN-slug/DECISION.md`.
5. **SPEC.md files of related existing units** (when applicable, supplied by orchestrator) — area peers and cross-area related units identified via the orchestrator's spec discovery (folder peers in `units/<area>/`, plus frontmatter grep on overlapping `concepts` and `files`, plus units named in the new unit's stub frontmatter `depends_on` / `supersedes` / `related`). These are the source of truth for the exact type names, function signatures, and interface shapes the new unit may import or rewrite. Read every supplied dependency SPEC end-to-end — truncated reads on dependencies cause silent signature drift.
6. **Prior SPEC.md for this unit** (optional, auto-discovered — only if refining) — read fully; preserve every resolved open question, every assigned internal-symbol name, and every `Size hint` already calibrated against earlier drafts. Re-run citations against the current registries — do not assume stale `INV-NN` or `ERR_CODE` are still valid.

Read-set size: trigger(s) + DOMAIN are always read; 3–6 further artifacts depending on the orchestrator-supplied read set (related-unit SPECs + the design-layer registries this unit actually cites). Read every supplied dependency SPEC end-to-end — truncated reads on dependencies cause silent signature drift.

---

## Rules

### Completeness rules

- Every public symbol must have a full signature: name, parameters (with names and types), return type, async/sync.
- Every error condition must be enumerated with its trigger, error type, and user-facing consequence (HTTP status, exit code, message format).
- Every constant, default value, environment variable name, timeout, size limit, or magic number must be stated explicitly with its value.
- Every file must be described at the level where an agent can produce it without referring to other sections — each file entry is a self-contained mini-spec.
- Every behavioral path (happy path, each error path, edge cases) must be described step by step in prose.
- The Open Questions section must be empty. If you cannot resolve a question, surface it to the user rather than leaving it open.

### Precision rules

- Use exact names — function names, type names, field names, file paths, route paths, crate/package names. Do not use placeholders like "appropriate error" or "relevant fields."
- When referencing something from a dependency unit, use its exact exported name and the file it comes from.
- Describe behavior as ordered steps ("1. Acquire write lock on repo_id. 2. Open repository. 3. ..."), not as vague summaries ("handles the request appropriately").
- Distinguish between "returns error" and "returns empty/default" — state which one, and the exact value.

### Scope rules

- The spec defines the unit's contract regardless of work type — new feature, bug fix, refactor, or codebase maintenance. It states what must be true of the unit's public surface, behavior, errors, and tests after implementation; it does not prescribe implementation steps, code, hard implementation rules, or edits to top-level design artifacts.
- The spec operates at the declarative-semantic level: file paths, module / class / namespace names, symbol names and signatures, library and convention names cited from design artifacts and dependency specs. Source code is not an input; do not cite source-line positions, do not embed code fragments, and do not describe transformations of existing code (PLAN sequences the work; IMPLEMENT performs the edits). For "modifies" units, describe the target state of the named symbols, not the transformation that gets there.
- The spec must explicitly state what is OUT of scope. Closely related functionality that belongs to other units must be named and excluded.
- Do not spec anything that is not part of this unit's deliverable. If a function will be implemented by a future unit, do not describe its behavior — only reference its expected signature if needed for type-checking.
- Do not add features, optimizations, or error handling beyond what the work unit definition calls for.
- When the unit's contract requires changes to entries defined in top-level design artifacts (DOMAIN invariants, INTERFACES endpoints, BEHAVIOR state machines, ERRORS codes, QUALITY metrics, SECURITY threats/mitigations, DATA tables, USE_CASES entries, IA pages/commands), list each change in the §1 "Design-suite changes" sub-section by stable ID and one-line description. Reconcile applies these to the top-level artifacts after implementation; this spec does not edit those artifacts directly.

### Consistency rules

- All type names, function signatures, and route paths must be consistent with dependency units' SPECs. If a dependency exports `TreeEntry { path: String, sha: String, kind: EntryKind }`, use those exact field names.
- If the architecture documents specify a convention (e.g., vertical modules, error mapping pattern), the spec must follow it without restating the convention — just apply it.

### Interface contract rules

When the unit implements code on either side of an HTTP boundary (client that sends requests/deserializes responses, or server that defines route handlers/serializes responses):

- The spec must reference the specific INTERFACES.md entries (or equivalent contract document) that define the wire format for the endpoints this unit touches. If no interface contract exists, derive wire formats from architecture documents and state them explicitly in the spec — do not leave them implicit.
- Request/response types in the spec must use the exact field names and types from the interface contract. Do not independently derive wire formats from English prose descriptions.
- The spec must explicitly state the serialization convention the unit must follow (e.g., "Response struct uses `#[serde(rename_all = "camelCase")]` per interface contract wire convention" or "Route handler returns camelCase JSON per interface contract").
- When the unit is one side of a client-server pair, the spec must acknowledge the other side exists and reference the interface contract entry that both sides must conform to. This prevents each side from making independent naming choices.

### Design-layer citation rules

The SPEC is the bridge between the design suite (DOMAIN, INTERFACES, BEHAVIOR, ERRORS, QUALITY, SECURITY, DATA) and code. Every design layer the unit touches must be cited by stable ID in section 1 "Design References" — never by prose, never by re-stated content. Citations resolve to the authoritative registry; the implementer does not have to guess what `INV-7` or `METRIC-push-duration-ms` means.

- **Domain invariants (`INV-NN` from DOMAIN.md § 9).** If the unit's behavior preserves, enforces, or potentially threatens any invariant, list the IDs in section 1 and name the mitigation in section 4 wherever a behavioral step crosses the risk.
- **Domain events (`EVT-name` from DOMAIN § 7 / INTERFACES § 7).** If the unit emits or consumes any event, list it in section 1 with the emission or consumption point and specify the exact trigger in section 4.
- **State machines and sagas (`SM-entity: {from} → {to}` and `SAGA-name` from BEHAVIOR §§ 1–2).** If the unit implements a state-machine transition or a saga step, cite the exact transition or step ID in section 1 and match the transition's guards / effects / invariants in section 4.
- **Observability signals (`METRIC-*`, `SLO-*`, `SPAN-*` from QUALITY §§ 3–5).** If the unit emits metrics, participates in an SLO, or owns spans, list the IDs in section 1 and state emission locations (which function, which handler, which saga step) in section 4.
- **Security threats and mitigations (`THREAT-NN`, `MIT-NN` from SECURITY §§ 5–6).** If the unit is cited as the implementation location for any `MIT-NN`, or faces any `THREAT-NN` relevant to its surface, list both in section 1 and name the control implementation in section 4.
- **Error codes (`ERR_CODE` from ERRORS.md § 3).** Every error the unit may emit, map, or propagate uses the exact `ERR_CODE` UPPER_SNAKE string from the registry. Do not invent new codes in the SPEC. If a condition the unit must signal has no registered code, surface it as an open question for `/ERRORS.md` to resolve — then re-run this SPEC.

Layers that do not apply to this unit get an explicit `None — {reason}` line in section 1, not silent omission.

### Single YAML frontmatter block

- Exactly one YAML frontmatter block at the top of the SPEC.md output, never two — even when refining an existing SPEC, merge into a single block. The block carries every field enumerated in the Output Format template (`artifact`, `last_updated`, `status`, `unit`, `kind`, `area`, `files`, `concepts`, `owns_ids`, `trigger`, `supersedes`, `superseded_by`, `depends_on`, `related`, `files_specified`, `tests_specified`, `errors_specified`, `estimated_loc_prod`, `estimated_loc_test`, `open_questions`).
- The orchestrator pre-populates the stub frontmatter with `unit`, `kind`, `area`, `trigger`, and any pre-declared `supersedes` / `depends_on` / `related` candidates before invoking this skill. Preserve those values; populate the remaining fields based on the SPEC body you author. **`concepts`** is 3–8 broad tags drawn from the unit's scope. **`owns_ids`** is every stable ID whose authoritative definition is in this SPEC body — extracted from §§ 3–7 (EP-name in route headers, EVT-name in event refs, SAGA-name / SM-entity-state in behavior, ERR_CODE in § 7, INV-NN / METRIC-name / SLO-name / THREAT-NN / MIT-NN / CFG_NAME if defined here, J-name / T-name for jobs/telemetry).
- `status` is the unit lifecycle state — set to `in-design` when authoring a fresh SPEC. The orchestrator advances it (`in-progress` after G2 SPEC_REVIEW passes, `implemented` after G7 VERIFICATION passes, `superseded` / `archived` / `abandoned` later). The skill itself does not set `in-progress` or beyond. SPEC-write completion is signalled by `open_questions: 0`; if a missing input (unregistered `ERR_CODE`, ungranted dependency SPEC) prevents authoring, surface it in § 10 and leave `open_questions` non-zero.
- Every count in the frontmatter matches the body exactly: `files_specified` is the number of file entries in § 9; `tests_specified` is the number of tests in § 8; `errors_specified` is the number of rows in § 7's table; `open_questions` is the number of unresolved questions in § 10 (zero when § 10 reads "All questions resolved."); `estimated_loc_prod` and `estimated_loc_test` are the sums of per-file `Size hint` values for production and test files respectively.

### No-code rule

- The spec must not contain code or pseudocode unless a specific algorithm is non-obvious and cannot be described unambiguously in prose (e.g., a specific hash partitioning scheme, a binary format layout). In that case, minimal pseudocode is permitted with a note explaining why prose was insufficient.

---

## Output Format

The SPEC.md file must follow this exact structure. Top-level sections (§1–§10) are mandatory; if a section has no content, include the heading with "None." underneath. Sub-blocks within a section may be omitted when empty (do not stub them).

```markdown
---
artifact: SPEC
last_updated: {YYYY-MM-DD}
status: {in-design | in-progress | implemented | superseded | archived | abandoned}   # lifecycle state; orchestrator-managed. Set to in-design when authoring a fresh SPEC.
unit: u{NN}
kind: {feature | fix | refactor | refinement | chore}
area: {bounded-context}                           # matches units/<area>/ folder name; one of DOMAIN.md's bounded contexts
files: [{repo-relative-path}, ...]                # production + test files this unit creates or modifies
concepts: [{tag}, ...]                            # 3–8 broad concept tags (canonicalized vocabulary)
owns_ids: [{stable-id}, ...]                      # every stable ID (EP-, EVT-, SAGA-, SM-, ERR_, INV-, METRIC-, SLO-, THREAT-, MIT-, CFG_, J-, T-) authoritatively defined in this SPEC body
trigger:
  roadmap: [{id}, ...]
  issues: [{id}, ...]
  fresh_intent: {bool}
supersedes: [{unit-id}, ...]
superseded_by: [{unit-id}, ...]                   # orchestrator-maintained; leave [] when authoring
depends_on: [{unit-id}, ...]
related: [{unit-id}, ...]
files_specified: {N}
tests_specified: {N}
errors_specified: {N}
estimated_loc_prod: {N}
estimated_loc_test: {N}
open_questions: {N}
---

# SPEC: {Unit ID} — {Unit Name}

## 1. Identity & Context

**Unit:** {ID}
**Name:** {name}
**Repo:** {repository name}
**Concept:** {one-paragraph purpose, ≤ 8 sentences — why this unit exists, what problem it solves, where it sits in the system. Push longer rationale into §4 or §5.}

### Dependencies

For each dependency, state:
- Unit ID and name
- What specifically is consumed (list exact type names, function signatures, config values)
- Where it is imported from (file path)

If no dependencies: "None — this is a foundation unit."

### Design References

Cite the stable IDs from the design suite that this unit touches. For layers that do not apply to this unit, state "None — {one-line reason}" — silent omission is forbidden.

- **Domain invariants preserved:** `INV-{NN}` — one-line statement recalled from DOMAIN § 9 for each; how the unit preserves it. (From DOMAIN.md § 9.)
- **Domain events produced / consumed:** `EVT-{name}` produced at `{function / step}`; `EVT-{name}` consumed by `{handler}`. (From DOMAIN § 7 / INTERFACES § 7.)
- **State machines / sagas:** `SM-{entity}: {from} → {to}` implemented by this unit; `SAGA-{name}` step `{N}` implemented. (From BEHAVIOR §§ 1–2.)
- **Observability signals emitted:** `METRIC-{name}` (counter / histogram / gauge) at `{emission point}`; owns span `SPAN-{name}`; participates in `SLO-{name}` as a backend of its SLI. (From QUALITY §§ 3–5.)
- **Security threats faced / mitigations implemented:** faces `THREAT-{NN}` at `{surface}`; implements `MIT-{NN}` at `{function / middleware / guard}`. (From SECURITY §§ 5–6.)
- **Use cases realized:** `UC-{NN}`, `UC-{NN}`. (From USE_CASES.md.)
- **Data aggregates / tables touched:** `{table_name}` (represents `{AggregateName}`); access patterns used: `{query summary → idx_name}`. (From DATA §§ 3–5.)

### Design-suite changes

When the unit's contract requires changes to top-level design-artifact entries, list each change here by stable ID, one-line description, and target artifact. Reconcile applies these to the top-level artifacts after implementation; this spec does not edit them directly.

- `{STABLE-ID}` (`{ARTIFACT.md}` § {section}) — {one-line description of the change}

If the unit changes no top-level entries: "None."

---

## 2. Scope Boundary

### In scope

Exhaustive bulleted list of what this unit delivers. Each item should be concrete and verifiable (e.g., "a function that...", "an HTTP route that...", "a type definition for...").

### Out of scope

Bulleted list of closely related things this unit does NOT do. Name the unit that owns each excluded item where applicable. This prevents the implementing agent from adding helpful but out-of-scope functionality.

---

## 3. Public Interface Contract

For each exported symbol, grouped by file:

### Functions

For each function:
- Full signature: `fn name(param: Type, ...) -> ReturnType`
- Async: yes/no
- Brief description of what it does (one line)
- Reference to Behavioral Specification section for detailed behavior

### Types / Structs / Enums

For each type:
- Full definition with every field/variant
- Each field: name, type, description, and whether optional
- Any trait implementations required (Display, Serialize, etc.)
- When the type has ≥ 3 fields, render the field list as a markdown table (`Field | Type | Required | Description`). Do not use code blocks.

### HTTP Routes (if applicable)

For each route:
- Method and path: `GET /repos/{id}/tree`
- Auth requirement: authenticated/public
- Request: path params, query params, body schema (field names, types, required/optional, constraints)
- Response: status code(s), body schema for each status
- Reference to Behavioral Specification section for detailed behavior

---

## 4. Behavioral Specification

For each public function or route, a step-by-step prose description. When the unit has no runtime behavior (types-only, config-only, scaffold), this section may read: "Behavior is declarative — see §3 / §9."

### `function_name` / `METHOD /path`

**Happy path:**
1. Step one (be precise about what happens, what is called, what is checked)
2. Step two
3. ...
n. Return value / response

**Error paths:**
- **{Error condition}:** {what triggers it} → {what is returned/thrown, with exact error variant and message format}
- ...

**Edge cases:**
- {Description of edge case} → {expected behavior}
- ...

**Concurrency:**
- What locks are acquired/released and when (if applicable)
- What can safely run in parallel

---

## 5. Internal Design Decisions

Decisions that an implementer would otherwise have to make arbitrarily. Render as Decision/Rationale bullet pairs when ≤ 5 decisions; render as a 3-column table (`# | Decision | Rationale`) when ≥ 5 decisions, with stable IDs `D1`, `D2`, … Cite precedent units when applicable (e.g., "Matches U141's `*_routes` convention").

- **Decision:** {what was decided}
- **Rationale:** {one-line why}

Examples of what belongs here:
- Choice of data structure when multiple would work
- Error handling strategy (propagate vs. map vs. wrap)
- Whether to use a helper function or inline logic
- Ordering of operations when multiple orderings are correct
- Naming of private/internal symbols

---

## 6. Dependencies & Integration

Include only the sub-blocks that apply to this unit; omit (do not stub) those with no content.

### External packages/crates

| Package | Version constraint | Features used | Purpose |
|---------|-------------------|---------------|---------|
| ... | ... | ... | ... |

### Registration / Mounting

How this unit is integrated into its parent:
- Module declaration (e.g., `pub mod X;` added to which file)
- Route mounting (e.g., `router.merge(X_routes())` in which file)
- Any other wiring required

### HTTP Contract References

(Include this subsection when the unit implements code on either side of an HTTP boundary. If the unit has no HTTP boundary involvement: "Not applicable — this unit does not interact with an HTTP boundary.")

For each INTERFACES entry this unit implements:

| Endpoint | Role | Registry entry | Wire convention | Transformation |
|----------|------|----------------|-----------------|----------------|
| `METHOD /path` | client / server | INTERFACES.md § {section} | {e.g., camelCase JSON} | {e.g., "Server transforms snake_case from internal service to camelCase for client" or "None"} |

---

## 7. Error Catalog

| `ERR_CODE` | Language-level error type | Trigger condition | HTTP status | CLI exit | Source |
|------------|--------------------------|-------------------|-------------|----------|--------|
| `ERR_CODE_FROM_REGISTRY` | `RustErrorVariant` / `TsExceptionClass` / … | ... | `{3-digit or —}` | `{int or —}` | this unit / propagated from `U{NN}` |

Every `ERR_CODE` must exist in ERRORS.md § 3 — do not invent codes in the SPEC. If a condition the unit must signal has no registered code, surface it as an open question in section 10 and re-run `/ERRORS.md` before regenerating this SPEC.

For propagated errors: state whether they are re-wrapped (into which code), mapped to a different code, or passed through unchanged. The mapping, if any, is recorded in ERRORS.md § 4 Wrapping Rules; cite it here.

---

## 8. Test Specification

For each test:

### Test: {descriptive name}

- **Setup:** What state/fixtures must exist before the test runs. Be specific (e.g., "a bare git repo initialized at a temp directory", not "appropriate setup").
- **Action:** What function/route to call, with what arguments.
- **Assertion:** Exact expected outcome — return value, status code, side effects to verify, state changes.
- **Teardown:** Cleanup required (if any beyond default temp directory cleanup).

**Mock fidelity requirement:** When tests use HTTP mocks (wiremock, MSW, nock, test doubles that simulate HTTP responses), mock response bodies MUST use wire-format field names and casing as defined in INTERFACES.md (or the contract source of truth for the project), not the implementing language's native convention. This ensures tests verify against realistic wire data, not fantasy responses that happen to match the client's struct layout.

---

## 9. File Manifest

For each file this unit creates or modifies:

### `{full/path/to/file.ext}` {creates | modifies}

**Purpose:** Why this file exists as a separate file (one line).

**Exports:**
- List exported symbol names only; signatures live in § 3 — do not restate them here.

**Internals:**
- Private functions, types, constants — not their implementation but their existence and role
- Description of internal logic organization

**Imports:**
| Symbol | From |
|--------|------|
| ... | ... |

**Invariants:**
- Conditions that must always hold within this file (e.g., "every public function validates input before processing", "all filesystem access goes through the lock manager")

**Constants & Configuration:**

| Name | Value | Configurable | Source |
|------|-------|-------------|--------|
| ... | ... | hardcoded / env var / config | ... |

If no constants: "None."

**Size hint:** ~{N} lines

---

## 10. Open Questions

This section must be EMPTY before implementation begins. If any questions remain unresolved, the spec is not ready. The user will resolve open questions after reviewing the spec — do not ask during generation.

When drafting the spec, you will encounter decisions where multiple approaches are defensible and the input documents do not clearly favor one. Do NOT silently pick an answer to keep this section empty. Instead:

- If a question has one obviously correct answer given the input documents, resolve it yourself and write the decision into the appropriate section. Do not list it here.
- If a question has no clear answer — multiple valid approaches exist, or the input documents are ambiguous or silent — list it here with proposed options and tradeoffs so the user can make an informed decision.

Format for open questions:

- [ ] {Question}
  - **Option A:** {description} — {tradeoff}
  - **Option B:** {description} — {tradeoff}
  - **Recommendation:** {if you lean one way, say so and why — but leave the decision to the user}

Once the user resolves all questions, move each resolution into the appropriate section and replace this section with:

"All questions resolved."
```