spec-author · diff

git:20260926.5e0248f to git:20261001.0eab749

27 added, 1 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."
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.8.0"
+ version: "1.9.0"
---
# 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.
## 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.
```
## 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.sh`. 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
bash scripts/reserve-spec-id.sh --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.sh` 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.
## 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)
→ *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.