spec-author · diff

git:20261018.d250988 to git:20261018.1588e62

6 added, 463 removed. Audit A to A.

---
name: spec-author
- description: "Specification authoring skill for the SPECS stage of the ADR-0010 lifecycle. Activate as step 0 of any non-trivial ticket — before any architect, developer, or tester — to qualify the user intent and emit exactly one Markdown spec file under `/specs/` conforming to `docs/spec-format.md`. Mode-aware (FULL / INTERMEDIATE / MINIMAL / AUTO) and gated on resolved open questions."
+ description: "Specification authoring for the SPECS stage of the ADR-0010 lifecycle. Activate as step 0 of any non-trivial ticket, before architect, developer or tester, to qualify the intent and emit one spec under `/specs/` per `docs/spec-format.md`. Mode-aware (FULL/INTERMEDIATE/MINIMAL/AUTO)."
license: Apache-2.0
allowed-tools:
- Read
- Glob
- Write
- Bash
user-invocable: true
metadata:
provenance:
canonical: "https://github.com/crewrig/crewrig"
feedback: "https://github.com/crewrig/crewrig"
- version: "1.9.1"
+ version: "1.10.1"
---
# Spec Author
The `spec-author` skill turns a raw user intent into a draft specification
file under `/specs/` conforming to `docs/spec-format.md`. It owns the
*qualification* phase of the ADR-0010 lifecycle — answering "what does the
user actually want" — and emits exactly one artifact: a Markdown spec file.
It does not plan, design, or implement; those belong to downstream skills.
The skill is mode-aware (FULL / INTERMEDIATE / MINIMAL / AUTO per ADR-0010
→ *Interaction modes*) and adjusts interview depth accordingly. AUTO
authors the spec end-to-end with zero questions; the other three modes
escalate user gating.
## When to activate
1. **Explicit user invocation.** The user types `/spec` (or the equivalent
CLI activation phrase). Optional flags: `--mode=FULL|INTERMEDIATE|MINIMAL|AUTO`,
`--issue=<number>`. Absent `--issue`, the skill infers the parent ticket
from the current context.
2. **Orchestrator routing of a fresh ticket.** Any new ticket whose tier
is not `trivial` (per ADR-0010 → *Complexity tiers*) routes through
`spec-author` before any other team role. Trivial tickets bypass — the
orchestrator handles them inline.
3. **`spec`-class REVIEW finding** (per ADR-0010 → *Routing matrix*). The
retroactive routing engine re-invokes the skill to author a
delta-spec (`/specs/<NNNN>-<slug>.delta-<NN>.md`) per `docs/spec-format.md`
→ *Delta-spec convention*. The skill detects delta mode by the presence
of a parent spec for the ticket and switches its output template to the
three delta sections (`## ADDED` / `## MODIFIED` / `## REMOVED`).
## Inputs
The skill expects one of:
- A raw user intent in free-form prose (typical for `/spec` invocations
without a flag).
- `--issue <N>` — the skill reads the GitHub issue body, related comments,
and any pre-existing logbook context to derive the intent. Use `gh issue
view <N> --json title,body,comments` to retrieve it.
- A parent-ticket context provided by the orchestrator at step 0 of a
non-trivial template.
The skill SHALL pick the interaction mode in this order: (a) explicit
invocation flag, (b) the parent ticket's declared mode if one already
exists, (c) the framework default **INTERMEDIATE**.
## Interview script
- Each mode shares the same output contract (see *Output contract*) and
- differs only in how the skill gathers the information.
-
- ### AUTO — zero questions
-
- The skill SHALL ask the user no questions. It reads the ticket body,
- related comments, and any pre-existing logbook context, then drafts all
- five mandatory body sections itself. Every gap the skill cannot
- confidently close becomes a bullet in `## Open questions` prefixed with
- `[AUTO-PARKED]`. The user audits after the fact via the merged spec PR.
-
- ### MINIMAL — three questions
-
- Asked in order, one at a time, only when the answer is not already
- unambiguously derivable from the ticket:
-
- 1. **Intent confirmation.** "Confirm in one sentence the user-facing
- change. Anything missing from: *&lt;draft intent&gt;*?"
- 2. **Out-of-scope check.** "Is there a nearby behavior you do NOT want
- this spec to cover?"
- 3. **Acceptance signal.** "What single observable outcome will tell us
- the spec is satisfied?" (drives the happy-path scenario).
-
- The skill autonomously drafts requirements, scenarios, and complexity
- tier. Open questions are surfaced for the user to resolve before exit.
-
- ### INTERMEDIATE — default; six questions
-
- Extends MINIMAL with three more, asked after the first three (numbered
- 4–6 as a continuation of the MINIMAL list):
-
- - **Failure path.** "What should happen if &lt;the obvious failure
- condition&gt; occurs?" (drives the failure-path scenario).
- - **Complexity tier.** "Does this fit `trivial`, `small`, `standard`,
- or `large`? *&lt;skill's proposed tier with rationale&gt;*."
- - **Open-questions review.** "These points are unresolved — pick one:
- resolve now / park explicitly / drop." (one pass per unresolved item).
-
- ### FULL — INTERMEDIATE plus per-section validation
-
- After drafting each of the five mandatory body sections, the skill SHALL
- present the drafted section verbatim and request explicit sign-off
- ("approve / revise / reject") before moving on. The user gates exit on
- the same Open-questions discipline as INTERMEDIATE.
-
- ### Prose discipline for interactive batches
-
- The four sub-rules below realize R15 and R17 of
- [`specs/0002-spec-author-skill.md`](../../../specs/0002-spec-author-skill.md).
- The first three (preface anchors, acronym discipline, description
- self-sufficiency) apply uniformly to MINIMAL, INTERMEDIATE, and FULL —
- wherever the skill emits an `AskUserQuestion` (or the host CLI's
- equivalent interactive prompt) — and exist because the framework's
- interview text frequently renders through a side panel that strips
- prior chat context; the user sees the question and option descriptions
- in isolation. Treat each batch as if it were the first thing the user
- reads in this session. The fourth sub-rule (idiomatic language quality)
- extends to AUTO whenever AUTO emits any user-visible artifact in a
- language other than English — AUTO is not exempt simply because no
- interactive question batch is emitted.
-
- - **Preface anchors.** Before every interactive batch, emit a short
- one-paragraph preface that names, in this order: (1) the originating
- ticket identifier (issue number or spec id), (2) the current
- lifecycle stage and the artifact being authored, (3) the
- interview-pass position as "Question N of M — &lt;short label&gt;",
- and (4) a one-or-two-clause recap of the decisions already taken in
- this session. The preface compensates for the side-panel rendering;
- it is not a re-introduction of the skill. Concrete example for a
- MINIMAL pass at its third question: *"Issue #193 — SPECS stage,
- authoring `specs/0002-…-delta-03.md`. Question 3 of 3 (acceptance
- signal). So far: intent confirmed as the prose-discipline edit, and
- the `harness-report` skill is out of scope."*
- - **Acronym discipline.** Non-standard software-engineering vocabulary
- SHALL be spelled out at first use within the batch, including inside
- every option `description`. Treat the following as **illustrative**,
- not exhaustive. Spell out at first use: `OQ` (*Open Question*), `R1`
- / `R2` / … (*Requirement N*), `NNNN` (the four-digit spec id),
- `delta-NN` (the two-digit delta sequence number). Leave bare: `PR`,
- `CI`, `URL`, `ADR`, `CLI`. When in doubt, spell out — the cost of
- redundancy is negligible against the cost of an opaque question.
- - **Description self-sufficiency.** Each option's `description` field
- renders in a side panel disconnected from the question text and from
- prior chat history; the user must be able to decide from the
- description alone. Every `description` SHALL carry enough rationale
- to support the decision without re-reading earlier turns.
- Non-self-sufficient (bad): *"Use the same approach as before."*
- Self-sufficient (good): *"Reuse the `INTERMEDIATE` interview path
- (six questions, user-gated) — same gating model already chosen for
- spec 0002's parent ticket; lets the user audit each draft section
- before commit."*
- - **Idiomatic language quality.** When the user's preferred language
- is not English (declared explicitly via a memory record or a direct
- *"écris-moi en français"*-style instruction, inferred from the
- last three messages, or inherited from prior session prose without
- correction), produce the preface, the question text, the option
- `label` fields, the option `description` fields, inline progress
- messages, and any AUTO-mode user-visible artifact in **idiomatic**
- prose in that language. Direct calques of English software-
- engineering jargon — verb forms in `-er` derived from English
- verbs (`spawner`, `shipper`, `merger` as a verb, `amender` in the
- English sense), unprefixed `cold reviewer` instead of *"contrôle
- indépendant"*, `verdicter` instead of *"rendre un verdict"* or
- *"trancher"*, `en-branche` instead of *"directement sur la
- branche"*, bare `PR` without first-use expansion to
- *"pull-request"* — SHALL be avoided. The reference catalog is
- in spec 0002 R17 (sub-clause 2). The catalog is non-exhaustive;
- extrapolate from the listed patterns. When in doubt, prefer the
- longer idiomatic phrasing over the calque — verbosity in the
- target language is a smaller cost than the cognitive friction of
- parsing franglais.
-
- Reviewer enforcement: the three failure-path scenarios added by spec
- 0002 delta-02 in
- [`specs/0002-spec-author-skill.md`](../../../specs/0002-spec-author-skill.md)
- → `## Scenarios` codify the R15 contract; the two scenarios added by
- delta-04 codify the R17 contract. A spec-PR that ships an interview
- batch without a preface, with an opaque option `description`, with
- an undefined acronym, or with a non-English-language calque from the
- R17 catalog, is a `class: tech` finding in the retroactive review
- loop.
+ Read `references/interview-script.md` when qualifying the intent, before asking anything.
## Pre-write grounding
- Before writing the `## Requirements` section of a spec that qualifies
- a **verification, audit, or sanity-check tool** whose acceptance is
- file-level, run a short grounding pass against the codebase. The pass
- realizes R16 of
- [`specs/0002-spec-author-skill.md`](../../../specs/0002-spec-author-skill.md)
- (introduced by delta-03) and exists because the friction reported in
- issue #194 — spec 0007 mandated a `type` field that no built artifact
- ever exhibited, discovered only at DEV time — proves that a WHAT
- qualified in isolation from the actual code drifts. The grounding
- step is the cheap safety net that catches the drift at qualification
- time.
-
- **Mode applicability.** The grounding step runs in all four
- interaction modes — `MINIMAL`, `INTERMEDIATE`, `FULL`, and `AUTO`.
- In `AUTO` the user has no interactive surface to challenge the
- requirements list before the spec PR opens, so grounding is the only
- catcher; any anomaly bullet emitted in `AUTO` carries the
- `[AUTO-PARKED]` discipline of R10 and is audited post hoc via the PR
- diff. A residual risk remains in `AUTO` — a missed-in-scope
- classification produces no bullet at all and no `[AUTO-PARKED]`
- entry to audit — acknowledged in spec 0002 delta-03's `### Risks`
- section.
-
- ### Detection
-
- Classify the spec as in-scope for grounding when ANY of the four
- conditions below holds for any drafted requirement. Use the
- disjunction, not a conjunction — a single match flips the spec
- in-scope.
-
- - **a. Quantifier pattern.** The requirement contains *"every"* or
- *"each"* followed by a noun naming a built artifact class.
- Example: *"every built `SKILL.md` frontmatter SHALL contain a
- `type` field"* — `every` + `SKILL.md frontmatter` triggers.
- - **b. File-level assertion.** The requirement asserts a property at
- the file level: an `exit zero`-style claim, a field-presence claim,
- a layout claim, or a content-shape claim. Example: *"the linter
- SHALL exit zero on a clean checkout"* — file-level outcome triggers.
- - **c. Validation-verb on existing class.** The requirement uses a
- validation, rejection, refusal, or enforcement verb (*refuse*,
- *reject*, *validate*, *require*, *mandate*, *disallow*, *enforce*,
- *forbid*) acting on a property of an artifact class the codebase
- already produces. Example (the iter:1 reviewer counter-example
- that motivated this clause): *"the build script SHALL refuse to
- publish bundles whose `provenance` block is missing the `version`
- field"* — `refuse` + property of `bundles` (an existing class)
- triggers via condition c. Note that condition a does NOT fire here
- (no `every` / `each`); condition c is the catcher.
- - **d. Semantic catch-all.** The drafted requirement describes the
- shape, structure, or content of a class of artifacts the codebase
- already produces, regardless of surface wording. Example: *"the
- ADR header MUST carry the status badge in the second line"* — no
- quantifier, no validation verb, but the requirement targets the
- shape of an existing class.
-
- **False-positive preference.** When uncertain whether a requirement
- triggers any of the four conditions, treat it as in-scope and run the
- grounding step. Grounding ceremony in an out-of-scope case costs one
- extra file read; grounding skipped in an in-scope case re-introduces
- the original #194 friction. Bias accordingly.
-
- ### Inspection
-
- When the spec is in-scope:
-
- 1. **Pick the artifact class.** Read the noun in the drafted
- requirement (e.g. *"every built SKILL.md frontmatter"* →
- `artifacts/core/skills/*/SKILL.md`). When the requirement names
- multiple classes, pick one per class.
- 2. **Pick at least one real instance.** A safe default is the
- most-recently-modified file under the class's canonical path
- (`git log -1 --name-only` scoped to the path). Choose more
- instances when the class is heterogeneous (e.g. skills and
- agents both ship `metadata.provenance` — inspect one of each).
- 3. **Read the instance end-to-end** when the file is short, or read
- the relevant section (frontmatter, header block, target field)
- when the file is long. Use the same `Read` / equivalent tool the
- skill already uses for repository content; no new tooling
- required.
-
- ### Comparison
-
- Cross-check the observed shape against the drafted requirement. Look
- specifically for:
-
- - **Missing fields.** The requirement mandates a field that the
- observed instance does not exhibit. (The #194 origin case — spec
- 0007's `type` field.) Distinguish "absent from this instance" from
- "absent from every instance" — the latter is the *New field on
- existing class* path below.
- - **Layout divergence.** The requirement implies a structural layout
- (field nesting, section ordering, header level) the observed
- instance does not match.
- - **Path mismatch.** The requirement assumes a canonical path
- (`artifacts/core/skills/<name>/SKILL.md`) that does not match
- where the class actually lives in the codebase.
-
- The comparison is informational — the skill is checking that the
- WHAT it is about to qualify matches the WHAT already in the codebase,
- not refactoring the codebase to match the spec.
-
- ### Anomaly emission
-
- When the comparison surfaces an anomaly, emit a single bullet under
- `## Open questions` prefixed `[GROUNDING:]`. The bullet blocks skill
- exit per R10's open-question discipline (`MINIMAL` / `INTERMEDIATE` /
- `FULL`) or lands as `[AUTO-PARKED]` (`AUTO`). One bullet per anomaly,
- each one one line, naming the observed shape, the drafted requirement,
- and the reconciliation that the user (or AUTO-mode audit reader) must
- take.
-
- Worked example:
-
- ```text
- - [GROUNDING:] artifacts/core/skills/spec-author/SKILL.md frontmatter
- has no 'type' field; the drafted R8 mandates it. Reconcile before exit
- — either drop R8, retarget the requirement at a different artefact
- class, or scope the back-fill in `## Out of scope`.
- ```
-
- The bullet names enough context for the reader to act without
- re-reading the inspection trail: which file was inspected, what the
- delta is, what the next decision is.
-
- ### Conforming-case silence
-
- When the inspection confirms the observed shape matches the drafted
- requirements, emit **nothing**. No `[GROUNDING:]` bullet, no
- "grounding confirmed" line, no audit trail in the spec body. The
- absence of pushback is the only signal. This keeps the spec lean —
- the spec body documents the WHAT, not the qualification ceremony —
- and the grounding step is auditable from the absence of an open
- question, not from a manufactured marker.
-
- ### New field on existing class
-
- When the spec mandates a field, attribute, or property that no
- instance of the artifact class currently exhibits on `main` (the
- PR-189 scenario that originated this discipline), still run the
- inspection. The purpose is no longer to confirm field presence
- (absent by definition) but to confirm two adjacent facts:
-
- - The artifact class genuinely exists in the codebase under the
- path the spec assumes — no typo, no stale path.
- - The existing instances have a coherent shape into which the new
- field can be added without breaking the structure.
-
- Emit a `[GROUNDING:]` bullet under `## Open questions` stating
- explicitly that the field is absent from every existing instance and
- naming who is responsible for the back-fill: the implementation PR
- that realizes this spec, a migration PR scoped under `## Out of
- scope`, or a separate follow-up ticket. This forces the back-fill
- scope decision into the spec PR rather than deferring it to DEV time.
-
- Worked example:
-
- ```text
- - [GROUNDING:] No `artifacts/core/skills/*/SKILL.md` on main carries
- a `type: skill` frontmatter field; the drafted R8 mandates it.
- Back-fill responsibility: the implementation PR for this spec SHALL
- add `type:` to every existing skill source in the same diff.
- ```
+ Read `references/pre-write-grounding.md` before drafting the spec.
## Output contract
- The skill writes exactly one new file:
-
- ```text
- /specs/<NNNN>-<slug>.md
- ```
-
- The file SHALL conform to `docs/spec-format.md` (the normative format
- contract). Below is the skill-side summary; on any conflict, the format
- document wins.
-
- ### Ticket pickup check
-
- Before anything else — before the id is secured, and before any branch,
- filename, or frontmatter exists — the skill runs the cross-contributor
- ownership check of `docs/ticket-ownership.md`
- (`specs/0244-multi-contributor-assignment.md`, requirements 11 and 15), in
- spec and delta-spec mode alike:
-
- ```sh
- task -x ticket-pickup -- --issue <related-issue>
- ```
-
- Without `task`, run `node scripts/lib/node-floor-guard.js`, then — only if
- it exits `0` — `node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON
- scripts/ticket-pickup.ts --issue <related-issue>`, as two separate steps
- (never chained with `&&`).
-
- Only exit `0` lets the skill continue. On any other exit the skill SHALL
- stop before securing an id or creating a branch, and relay the tool's stderr
- sentence to the user: `3` names the owner and the permitted paths, `4` and
- `2` report what could not be determined, `5` reports that a maintainer was
- asked to assign the ticket. The skill SHALL NOT retry the check hoping for a
- different answer, SHALL NOT assign, unassign, or reassign anyone itself, and
- SHALL proceed without the check only on the user's explicit instruction in
- the same session.
-
- ### ID allocation
-
- `<NNNN>` is **secured on the reference repository before the spec file is
- written**, by `scripts/reserve-spec-id.ts`. The skill SHALL NOT compute it
- from the local working tree.
-
- What this replaces, and why (`specs/0112-spec-id-reservation.md`): an
- unsynchronised `max(existing) + 1` hands the same number to two sessions
- that pick up two tickets in the same second, and the collision is
- discovered by an unrelated third pull request *after* both offending specs
- have merged — by which point the id is carried by an issue, a branch name,
- a pull-request title and a logbook, and repairing it means renaming all of
- them.
-
- Run the tool FIRST, before the filename, the branch name, or the
- frontmatter exist (requirement 2 — so that all three agree from the first
- commit):
-
- ```sh
- node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON scripts/reserve-spec-id.ts --issue <related-issue>
- ```
-
- The first line of stdout is always the id. The exit code is the only thing
- the skill interprets:
-
- | Exit | Meaning | What the skill does |
- |---|---|---|
- | `0` | The id is secured. | Use it. Write no mark. |
- | `3` | The id is allocated locally but **not** secured — offline, unreachable remote, or no write access (the fork case). | Use it, AND copy the emitted `unsecured-id: true` line verbatim into the frontmatter. |
- | `1` | Genuine failure. | Stop. Relay the stderr reason to the user. |
-
- The marker is emitted on stdout line 2 in the exact frontmatter form, so the
- skill pastes it rather than composing it. The exit code already carries the
- boolean; the line exists to be copied.
-
- The `--issue <N>` invocation is idempotent per issue: if this issue already
- holds a secured reservation — from an earlier invocation of this exact
- command, or because a parent orchestrator secured a specific id on this
- issue's behalf via `--id <ID> --issue <N>` before spawning this session —
- the tool returns that existing id instead of computing a new one, and no
- second reservation is created (`specs/0112-spec-id-reservation.delta-02.md`,
- requirements 17-18). The skill does not need to know which of these
- happened, and this does not change its own contract below: it continues to
- never pass `--id` itself.
-
- Three obligations, each closing a tempting shortcut:
-
- - On exit `1` the skill SHALL NOT fall back to `max(existing) + 1`, and
- SHALL NOT re-run the tool hoping for a different answer. Exit `1` names a
- condition a retry cannot change: an invalid carrier setting, a
- non-representable identifier, an id already held by another ticket, or a
- retry budget already exhausted by lost races.
- - On exit `3` the mark is **not optional**. It is the machine-readable
- statement that the id may still be taken by another session, and it is
- what `scripts/check-spec-id-reserved.ts` reports at pull-request time —
- blocking for a pull request from the reference repository, non-blocking
- for one from a fork, where a maintainer secures the id and removes the
- mark before merge (requirement 10).
- - The skill never passes `--corpus`, never passes `--id`, and never sets
- `CREWRIG_SPEC_ID_CARRIER`. Securing a chosen identifier is the maintainer's path
- and the org-corpus path; changing the carrier is a pull request against
- `.crewrig/spec-id-carrier`, not something a session decides.
-
- Ids are never reused and a gap in the sequence is an accepted outcome (per
- `docs/spec-format.md` → *Naming convention*). An abandoned reservation is
- harmless by design: no expiry, no reclamation pass, no release protocol.
-
- **Delta-specs secure nothing.** A delta-spec reuses its parent's id by
- construction, so in delta-spec mode the skill does not call the tool at
- all.
-
- ### Frontmatter
-
- Populate every required field per `docs/spec-format.md` → *Frontmatter
- schema*:
-
- | Field | Source |
- |---|---|
- | `id` | Secured as above, quoted string. |
- | `slug` | Generated from the intent; kebab-case, ASCII, ≤ 40 chars. |
- | `status` | Always `draft` on first write. |
- | `complexity` | From the user (INTERMEDIATE/FULL) or skill-judged (AUTO/MINIMAL). |
- | `interaction-mode` | The mode the skill is running in. MAY be omitted in `draft`; the skill SHOULD write it explicitly to lock the choice. |
- | `related-issue` | The GitHub issue number the skill is invoked against. |
- | `version` | `1.0.0`. |
- | `max-iterations` | Omitted by default (inherits ADR-0010's 5). |
- | `superseded-by` | Omitted (only present for `superseded` status). |
- | `unsecured-id` | Omitted. Present, and copied verbatim from the tool's stdout, only on the exit-`3` path above. |
-
- The filename slug and the frontmatter slug SHALL match exactly.
-
- The `status` field is written `draft` on first write and flipped to
- `approved` in the spec-PR's own commit (the Merge mechanic). The subsequent
- `approved` → `implemented` transition is **not** this skill's job: it is
- recorded by the implementation team's `pr-logbook` on the implementation
- feature branch, folded into the implementation PR's own squash commit
- (`docs/spec-format.md` → *Recording a status transition*). This skill SHALL
- NOT open a separate, post-merge, metadata-only PR to record `implemented`.
-
- ### Body sections
-
- All five mandatory sections per `docs/spec-format.md` → *Mandatory body
- sections* SHALL be present, in order, with their headings verbatim:
-
- 1. `## Intent` — one paragraph, no HOW words.
- 2. `## Requirements` — numbered list, every line uses SHALL or MUST.
- 3. `## Scenarios` — at least one happy-path AND at least one failure-path
- scenario in Given/When/Then form.
- 4. `## Out of scope` — bullet list; MAY be empty only for `trivial` tier.
- 5. `## Open questions` — bullet list; MAY be empty.
-
- A section MAY be empty in `draft` (the linter checks header presence,
- not body content) but the skill SHOULD avoid emitting empty sections
- when content is derivable — empty sections in a draft are a smell the
- spec reviewer will flag.
-
- ### Delta-spec mode
-
- When the skill is invoked on a ticket that already has a parent spec
- (activation trigger 3), the output file is named
- `/specs/<NNNN>-<slug>.delta-<NN>.md` where `<NN>` is the next free
- two-digit delta number for the parent. The body replaces the five
- mandatory sections with the three delta sections defined in
- `docs/spec-format.md` → *Delta-spec convention* (`## ADDED`,
- `## MODIFIED`, `## REMOVED`), all three present even when empty.
-
- ### Self-validation (best-effort)
-
- Before writing, the skill SHALL re-read `docs/spec-format.md` and verify
- locally that: (a) all five headings are present in order, (b)
- frontmatter parses as YAML, (c) every required field has a value of the
- right type. Enforcement of the format is the spec linter's job; this
- self-check is a courtesy, not a substitute.
-
- In addition, per R18 of spec 0002, the skill SHALL enforce a mandatory
- null-case and permitted-path self-check during the authoring phase. For
- each drafted requirement, verify that the requirement explicitly answers
- the null/degenerate case and names a permitted path, rather than solely
- stating what is forbidden or describing only the happy path. If a
- requirement fails this check, revise it before finalizing the draft.
+ Read `references/output-contract.md` before writing the file (ticket pickup, ID allocation, frontmatter, body, delta mode, self-validation).
## Open-questions discipline
Any unresolved entry in `## Open questions` SHALL block skill exit in
MINIMAL, INTERMEDIATE, and FULL modes until either:
- Resolved (rewritten as a requirement, scenario, or out-of-scope bullet); or
- Explicitly parked with the user's recorded consent. Parked items remain
in `## Open questions` with the prefix `[USER-PARKED]`.
The skill is **forbidden** from silently dropping an unresolved question
— that pre-bakes a `spec`-class finding into the REVIEW loop, which
`docs/spec-format.md` → *Open questions* explicitly calls out as wasted
iteration.
In **AUTO** mode the same discipline applies with no user round-trip:
unresolved items land under the prefix `[AUTO-PARKED]` and the user
audits after the fact via the spec PR.
## Finding class taxonomy
This skill participates in the retroactive review loop on both ends
- (per [`specs/0005-retroactive-routing-engine.md`](../../../specs/0005-retroactive-routing-engine.md)
- R2, R6 and [`docs/retroactive-loop.md`](../../../docs/retroactive-loop.md)
+ (per [`docs/retroactive-loop.md`](../../../docs/retroactive-loop.md)
→ *Routing matrix*):
- **As reviewer.** When the skill reviews a spec-PR (originating or
delta), every finding it emits SHALL carry exactly one `class:`
field whose value is `tech`, `arch`, or `spec`. Untagged findings
are malformed and trigger a retag round-trip that does NOT
increment the iteration counter. The review runs on the
`specs/<ticket>` seat: the verdict carries a `seat:` line at the
placement its posting transport dictates, findings carry `s<N>-F<M>`
identifiers where
`<N>` counts the seat's passes across every artifact of that surface,
and a fresh delta-spec PR is a **replaced artifact** — examined in
full, with the seat's dossier retained for the prior-finding audit.
The contract is `docs/reviewer-seat.md`.
- **As re-spawn target.** When invoked on a `class: spec` REVIEW
finding (activation trigger 3 above), the skill operates in
**delta-spec mode only** — the original spec on `main` is
immutable per ADR-0010 and spec 0003. The `superseded` transition
is a new-ticket path outside the loop. The skill SHALL surface a
violation if the incoming routing request omits the `class:` tag
or asks for a non-delta re-author of an existing spec.
## Harness friction tagging
When a recognition signal fires (see `config/TOOLS.md` → *Friction
Reporting → Recognition signals*), invoke the `harness-report` skill
(`artifacts/library/skills/harness-report/SKILL.md`) rather than
reimplementing the protocol inline. The skill is the single canonical
implementation of the tagging contract.
In addition to the default recognition signals, the following spec-author
specific triggers SHALL fire a harness-report:
| Trigger | `room` | Notes |
|---|---|---|
| The user pushes back on the drafted intent twice in a row in the same session (rewrites it both times). | `prompt` | Signal that the interview's intent question is misleading. |
| A drafted spec fails the skill's own pre-write self-validation and the failure root-causes to ambiguous wording in `docs/spec-format.md`. | `format` | Subcategory: `spec-format`. Evidence: the failing spec path + the ambiguous sentence. |
| The skill cannot determine the next free `<NNNN>` due to a malformed existing filename in `/specs/`. | `process` | Subcategory: `spec-id-allocation`. |
| In AUTO mode, the skill is forced to `[AUTO-PARKED]` more than five Open questions on a single spec. | `behavior` | High signal that AUTO is being asked to solve an ill-defined ticket; the user should re-route through INTERMEDIATE. |
| A second `spec`-class REVIEW iteration on the same ticket cites a question the skill already attempted to resolve in the prior pass. | `prompt` | Subcategory: `delta-spec-interview`. The interview is missing the right question. |
Tagging is fire-and-forget; the skill SHALL NOT block the user's work
waiting for an acknowledgment.
## Not in scope
The following belong to sibling tickets and SHALL NOT be implemented
inside the `spec-author` skill:
- **Build-time multi-CLI distribution** of the skill and agent — tracked
in issue #174.
- **Retroactive routing engine** that re-invokes the skill on `spec`-class
REVIEW findings — tracked in issue #172. The skill declares the
activation trigger; wiring the trigger is #172.
- **Complexity-tier selection logic** beyond asking the user
(INTERMEDIATE/FULL) or proposing a reasonable default (AUTO/MINIMAL) —
tracked in issue #173.
- **Plan format and plan-review protocol** — tracked in issue #169. The
skill produces specs, never plans.
- **Spec linter** — tracked in issue #178. The skill self-validates
best-effort; enforcement is the linter's job.