---
name: tech-spec
description: '[Documentation] Use when (re)generating the DERIVED technical spec view over code + tests, or reporting canonical §8 TC/test-code drift. A GENERATOR — it projects code + tests into a regenerable per-component view and NEVER authors business content. Modes generate|audit|sync. Per-mode procedure lives in references/{author,sync}.md.'
---

> Codex compatibility note:
>
> - Invoke repository skills with `$skill-name` in Codex; this mirrored copy rewrites legacy Claude `/skill-name` references.
> - Task tracker mandate: BEFORE executing any workflow or skill step, create/update task tracking for all steps and keep it synchronized as progress changes.
> - User-question prompts mean to ask the user directly in Codex.
> - Ignore Claude-specific mode-switch instructions when they appear.
> - Strict execution contract: when a user explicitly invokes a skill, execute that skill protocol as written.
> - Subagent authorization: when a skill is user-invoked or AI-detected and its protocol requires subagents, that skill activation authorizes use of the required `spawn_agent` subagent(s) for that task.
> - Do not skip, reorder, or merge protocol steps unless the user explicitly approves the deviation first.
> - For workflow skills, execute each listed child-skill step explicitly and report step-by-step evidence.
> - If a required step/tool cannot run in this environment, stop and ask the user before adapting.

<!-- CODEX:PROJECT-REFERENCE-LOADING:START -->

## Codex Project-Reference Loading (No Hooks)

Codex uses static project-reference loading instead of runtime-injected project docs.
When coding, planning, debugging, testing, or reviewing, open project docs explicitly using this routing.

**Always read:**

- `docs/project-config.json` (project-specific paths, commands, modules, and workflow/test settings)
- `docs/project-reference/docs-index-reference.md` (routes to the full `docs/project-reference/*` catalog)
- `docs/project-reference/lessons.md` (always-on guardrails and anti-patterns)

**Missing/stale context route:** If `docs/project-config.json`, the docs index, `lessons.md`, `CLAUDE.md`, `AGENTS.md`, or any task-required reference doc is missing or stale, auto-run `$project-init` or the narrow setup route (`$project-config`, `$docs-init`, `$scan-all`, `$scan --target=<key>`, `$claude-md-init`) before ordinary project-specific work. If Codex mirrors or `AGENTS.md` are missing/stale, ask the user to run `$sync-codex`; do not auto-run it.

**Situation-based docs:**

- Project structure/architecture/tech-stack/deployment/setup (any layer — backend, frontend, or infra): `project-structure-reference.md`
- Backend/CQRS/API/domain/entity changes: `backend-patterns-reference.md`, `domain-entities-reference.md`
- Frontend/UI/styling/design-system: `frontend-patterns-reference.md`, `scss-styling-guide.md`, `design-system/README.md`
- Spec authoring, `docs/specs/` pathing, or TC format: `feature-spec-reference.md`, `spec-system-reference.md`, `spec-principles.md`
- Behavior/public-contract changes or spec-test-code sync: `workflow-spec-test-code-cycle-reference.md` plus the spec docs above
- Derived spec indexes/ERDs/reimplementation guides: `spec-system-reference.md` and source Feature Specs under `docs/specs/`
- Integration test implementation/review: `integration-test-reference.md`
- E2E test implementation/review: `e2e-test-reference.md`
- Code review/audit work: `code-review-rules.md` plus domain docs above based on changed files

Do not read all docs blindly. Start from `docs-index-reference.md`, then open only relevant files for the task.

<!-- CODEX:PROJECT-REFERENCE-LOADING:END -->

<!-- PROMPT-ENHANCE:STEP-TASK-ANCHOR:START -->

> **[BLOCKING]** Execute skill steps in declared order. NEVER skip, reorder, or merge steps without explicit user approval.
> **[BLOCKING]** Before each step or sub-skill call, update task tracking: set `in_progress` when step starts, set `completed` when step ends.
> **[BLOCKING]** Every completed/skipped step MUST include brief evidence or explicit skip reason.
> **[BLOCKING]** If Task tools are unavailable, create and maintain an equivalent step-by-step plan tracker with the same status transitions.

<!-- PROMPT-ENHANCE:STEP-TASK-ANCHOR:END -->

## Quick Summary

> **Portability:** the technical root is read from `docs/project-config.json` → `specRoots.technical.path` (declared `authorship: "derived"`, `m1Policy: "exempt"`). NEVER hardcode a root. `{TechRoot}/{Service}/{Component}.md` is a **pattern** — `{Service}` and `{Component}` are placeholders resolved from the repo, never literal names.

**[IMPORTANT] task tracking** — Break ALL work into small tasks BEFORE starting (one task per emitted artifact).

**Goal:** Generate, on demand, a regenerable single-writer **technical view** — per-component derived specs (use-case inventory, TC↔test map, cross-service topology) — **projected FROM** code and test annotations (`TestSpec` for business TC joins, `TechnicalSpec` for technical-only joins when present), so an engineer can read a component's technical surface without ever forking a second, hand-maintained source of truth. **Code and tests are the source of truth; this skill only projects them.**

**Summary:**

- **Purpose:** a DERIVED-view **generator** ONLY — greps handlers/consumers/jobs/producers and test annotations to emit `{TechRoot}/{Service}/{Component}.md`; it **NEVER authors business content**. Code + tests stay the source of truth.
- **Main steps (run in order):** **Step 0** Scope Gate — ask the user directly (service/component + mode), BLOCKING before any read; no source to derive from → STOP. **Step 1** Derive facts — grep the use-case inventory, the `TestSpec`/`TechnicalSpec` joins, the topology. **Step 2** Instantiate templates — `references/author.md`'s fixed sections in pinned order. **Step 3** Stamp & Write — DERIVED banner + regenerate date, write each artifact immediately (never accumulate in context). **Step 4** Verify — no retired artifacts, banner present, no business artifact types, no canonical claims, no secrets.
- **Modes:** `generate` (default — regenerate the derived view) · `audit` (report which views are stale vs code/tests) · `sync` (report canonical §8 TC ↔ executing test-code drift — `references/sync.md`).
- Hard prohibition is the load-bearing rule: never emit the retired A-E engineering tree, `M##` dirs, `00-module-registry.md`, `01-domain-erd.md`, or `06-reimplementation-guide.md` under the technical root — why: an A-E bundle becomes a second source of truth competing with the Feature Spec, and a generator able to recreate it resurrects the retired tree on its next run (this has occurred once already, via rebase).
- Every generated file carries the `> DERIVED — regenerate with the tech-spec skill; do NOT hand-edit` banner + a regenerate date, anchors each fact back to its source, and makes **no canonical claim**.
- **This skill is M1-EXEMPT** (`specRoots.technical.m1Policy: "exempt"`) — its prose MAY name technology. That exemption is the whole reason this tree exists; it is NOT a licence to carry business content (see **Hard Prohibitions**).

> **[SCOPE]** This skill generates a **DERIVED** technical view over code + tests under `specRoots.technical.path`. It MUST NOT emit a per-module A-E engineering bundle (`A-domain-model`, `B-business-rules`, `C-api-contracts`, `D-events`, `E-user-journeys`), `M##` directories, `00-module-registry.md`, `01-domain-erd.md`, or `06-reimplementation-guide.md` — those are not part of the spec model. It MUST NOT author business content: the Feature Spec under `specRoots.business.path` owns §1–§8, and this skill neither writes nor amends it. Authority: [`docs/project-reference/spec-system-reference.md`](../../../docs/project-reference/spec-system-reference.md), [`.claude/skills/shared/sdd-artifact-contract.md`](../shared/sdd-artifact-contract.md).

**Inputs:** the code tree (command/query handlers, event consumers, background jobs, producers, sagas, outbox) and the test tree (`TestSpec` and `TechnicalSpec` annotations when present). **Code is the technical source of truth** — this skill reads it to **project** a view, never to populate a parallel _canonical_ layer.

**Modes:**

| Mode       | Trigger                                    | Input                                       | Output                                                                                      |
| ---------- | ------------------------------------------ | ------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `generate` | default — refresh the derived view         | code + test annotations                     | `{TechRoot}/{Service}/{Component}.md`, all DERIVED (`references/author.md`)                 |
| `audit`    | explicit request — staleness check         | code/test mtimes or git vs derived-view age | Stale-list report (which views lag their source). Never mutates                             |
| `sync`     | "sync tests" / "reconcile tests" / harvest | canonical §8 TCs + test code                | §8/test drift report + route-only `CoveredBy:`/orphan reconciliation (`references/sync.md`) |

**Tooling:**

- `npm run tech-spec:generate` — regenerate the derived technical views from code/test annotations.
  Equivalent direct invocation, for projects that copy `.claude/` without a `package.json`:
  `node .claude/skills/tech-spec/scripts/generate-tech-specs.mjs`

**Mode resolution (do this before any work):**

1. Parse the mode from the invocation: explicit `[mode=<x>]` arg wins; else infer ("regenerate tech specs", "technical spec for {Component}" → `generate`; "stale", "audit" → `audit`; "sync tests", "reconcile tests", "reverse sync", "harvest" → `sync`).
2. If ambiguous, present the detected mode by asking the user directly before proceeding — NEVER auto-start a mutating mode.
3. **Read the matching `references/` body** — it is the single source of truth for that mode's procedure, gates, and output contract. Do not run a mode from memory.

**Workflow:** `$scout` (locate the component) → `$tech-spec` (project the view) → `$changes-review` → `$watzup`

---

## The Generator Contract (NON-NEGOTIABLE)

This skill is a **generator**, not an author. Every clause below is structural — none is a preference a future maintainer may relax for convenience.

| #      | Clause                                                                                                                                                                                                                                  | Why                                                                                                                                                                                              |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **C1** | **[BLOCKING]** Output is **DERIVED and regenerable** — every generated file carries a `> DERIVED — regenerate with the tech-spec skill; do NOT hand-edit` banner + a regenerate date. It is **NEVER a second source of truth**.         | A derived view makes no truth claim, so it cannot compete for canonical status. A hand-editable tree makes one — and then "which is right, the doc or the code?" becomes askable.                |
| **C2** | **[BLOCKING]** **Never authors business content.** No user story, no acceptance criterion, no business rule, no §1–§7 prose, no §8 TC is written by this skill — in any mode. The Feature Spec stays the source of truth for all of it. | Carrying the Feature Spec's own artifact types is exactly how a rival tree competes on the Feature Spec's turf.                                                                                  |
| **C3** | **[BLOCKING]** **Never claims to be a source of truth.** The generated files never assert canonical authority. When the view disagrees with code, **code is right by construction and the view is stale — regenerate it.**              | A derived aid that asserts canonical authority corrupts the single-writer contract.                                                                                                              |
| **C4** | **[BLOCKING]** **Write each artifact immediately** after instantiating it; do NOT accumulate large outputs in context.                                                                                                                  | A `{Service}/{Component}` fan-out is exactly the case that exhausts context mid-run and loses every unwritten artifact.                                                                          |
| **C5** | **[BLOCKING]** **Single writer.** This skill is the **sole writer** under `specRoots.technical.path`. Nothing else writes there; it writes nowhere else.                                                                                | Two writers is how a view becomes a sibling.                                                                                                                                                     |
| **C6** | **[BLOCKING]** **Regeneration is idempotent** — regenerating over unchanged source produces an **empty diff**.                                                                                                                          | This is the tree's flagship oracle: it proves the artifact can be thrown away and rebuilt from its source. If it cannot, the tree holds content of its own — it claims truth, and it is a rival. |
| **C7** | **[BLOCKING]** **A-E filenames are never emitted.** See **Hard Prohibitions**.                                                                                                                                                          | An A-E bundle becomes a second source of truth competing with the Feature Spec.                                                                                                                  |

### C8 — Mechanical detect + route · never judge · never write business content

> **A generator MUST NEVER apply RIT — or any judgment test — at generation time.** Judgment produces data; generators consume data.
> Canonical formulation: [`.claude/skills/shared/sdd-artifact-contract.md`](../shared/sdd-artifact-contract.md) (beside RIT). **Cited, not restated.**

|                                                                            | Who                                          | When                                       | Output                                            |
| -------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------ | ------------------------------------------------- |
| **Judgment** (RIT, business-invariant verdicts, visibility classification) | a human, or an AI **outside** this generator | **once**, at authoring/classification time | a **persisted verdict — data**                    |
| **Generation**                                                             | `$tech-spec`                                 | every run                                  | output **mechanically re-derived** from that data |

**Idempotency holds because nothing is re-judged.** The generator reads a verdict it did not make and cannot revise. Same source + same verdicts ⇒ same output, every run. **A generator that judges is not idempotent — it fails C6 against its own tree, permanently, and the failure reports as `hand-edited`, which is the wrong cause and undiagnosable.**

**Two resolutions that are FORBIDDEN, because both look like fixes:**

| Rejected                                              | Why it fails                                                                                                                                                                                                 |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Scope idempotency to only the mechanical sections** | Reintroduces a **per-section carve-out** — an exemption at a new address. A carve-out is this framework's characteristic failure; do not re-mint one inside the oracle.                                      |
| **Cache judgments inside the generator**              | The generator then owns a **staleness problem**: a cached verdict that outlives the code it judged, invisible to review and to `git`. **Persisted verdicts belong in the artifact, not in generator state.** |

### C9 — The harvest detector REPORTS; it never gates

> **A structural proxy for a semantic property REPORTS; it never blocks.**
> Canonical formulation: [`.claude/skills/shared/sdd-artifact-contract.md`](../shared/sdd-artifact-contract.md) (beside RIT). **Cited, not restated.**

Harvest **detection** is a structural signal — _an invariant enforced at ≥2 points with no business rule citing it_ (the countable property `references/sync.md` step 2 already asks for). It is **mechanical**, so this skill may perform it. Its output is a **candidate list** for human or `$spec [mode=update]` adjudication.

- **NEVER an `error`. NEVER a build gate. NEVER a precondition on regeneration. NEVER a fixture that fails CI.**
- The detector is a **structural proxy**: it infers a semantic property from a correlate, and the rule-citation link it reads is **prose, not a key**. It has false positives (a covered invariant can present as uncovered) **and** false negatives (an invariant enforced at one chokepoint is invisible to a ≥2-point counter).
- **Why it may not gate:** a false positive that blocks a build gets suppressed — **and a suppressed detector is a dead detector.** A `report` cannot be suppressed, because it never blocked anything worth suppressing it for.
- **A detection changes the report, never the tree** — so it cannot break C6.

**The boundary — C9 does NOT license softening anything else:**

| Rule                                                | Operationally defined?                       | Severity                 | Why                                                                                    |
| --------------------------------------------------- | -------------------------------------------- | ------------------------ | -------------------------------------------------------------------------------------- |
| **C1** — DERIVED banner                             | **Yes** — a literal string test              | **`error`**              | The banner check **is** the rule                                                       |
| **C6** — regeneration-idempotency                   | **Yes** — regenerate, diff empty             | **`error`**              | **An ORACLE, not a proxy — it re-runs the transform and compares. It does not infer.** |
| **C7** — no A-E resurrection                        | **Yes** — a **closed** filename set          | **`error`**              | The filename list **is** the rule                                                      |
| **C2** — no `US-`/`AC-`/`BR-` in the technical tree | **Yes** — a static prefix denylist           | **`error`**              | The prefix list **is** the rule                                                        |
| **M1 / tech-token bans on the business tree**       | **Yes** — a token denylist                   | **`error`**              | Opera