Spec write
Register a spec file in the manifest and validate itFrom its SKILL.md
npx -y skills add telefrek/vallorcine --skill spec-writeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
13.2 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it
/spec-write <feature-id> <title>
Register a spec file in the manifest, validate its structure, and update
indexes. This is the mechanical registration step — run it after
/spec-author has produced the hardened spec content, or when registering
a prerequisite stub.
For authoring hardened specs with adversarial review, use /spec-author
first, then /spec-write to register the output.
Canonical frontmatter schema: .spec/_refs/frontmatter.md is the single
authoritative reference for what fields a spec carries, what enums each
field accepts, and how state and status differ. Read it once at the
start of any spec authoring session — it is short and exhaustive.
Validation (spec-validate.sh) and corpus drift detection (/curate
analyses 20–21) reconcile against this schema; deviations surface there.
Inputs expected in context
- The feature ID and title passed as arguments
- A complete spec file (from /spec-author or manual creation)
- Optionally, a resolved context bundle from /spec-resolve
Step 1 — Resolve context and check for conflicts
If no context bundle is already in your context, run the resolver:
bash .claude/scripts/spec-resolve.sh "<feature description>" 25000
Read the resolved bundle. For each existing spec in the bundle:
- Check whether this feature's behavior contradicts any existing requirement
- If a conflict exists, you MUST either:
(a) Add the conflicting requirement to
invalidates(explicit override), or (b) Adjust this feature's requirements to avoid the conflict - If you override with
invalidates, note in the Design Narrative why the prior requirement no longer holds
This is not optional. A spec that silently contradicts an existing requirement is a defect in the spec corpus.
Step 2 — Identify prerequisites
For each assumption this feature makes about existing components (behaviors, contracts, invariants that must be true for this feature to work), check whether a spec exists for that component:
jq -r '.features | keys[]' .spec/registry/manifest.json
If a spec exists: Add it to requires. Verify that the existing spec's
requirements cover what this feature needs. If not, add the missing
requirements to the existing spec (increment its version) or note the gap
in open_obligations.
If no spec exists: Create a prerequisite stub spec for the component. This is a minimal spec covering only the requirements this feature depends on:
- Verify each assumed requirement holds in the current code (read the relevant implementation)
- For requirements that hold: write them as numbered requirements
- For requirements you cannot verify: annotate with
[UNVERIFIED: assumes X — source needed before implementation] - Write the stub spec with
state: "APPROVED"for verified requirements (you just verified them), orstate: "DRAFT"if any requirement is UNVERIFIED - Add a verification note recording what you checked and when
- Register the stub in the manifest
- Add the stub's ID to this feature's
requiresarray
Prerequisite stubs are intentionally minimal — they cover only what the current feature needs, not the full contract of the component. Future features that depend on the same component will add their own requirements, growing the spec incrementally.
Step 3 — Determine domain assignment
From the context bundle header Domains matched:, take those as the
primary domains. If the implementation touched interfaces from additional
domains not in the bundle, add them.
If no context bundle is available, read .spec/CLAUDE.md Domain Taxonomy
and assign domains based on the feature description.
Step 4 — Identify cross-references
amends: Feature IDs whose requirements this feature changes or supersedes (partial overlap allowed).
requires: Feature IDs whose interfaces this feature depends on at runtime (not just at build time). Includes prerequisite stubs created in Step 2.
invalidates: Specific requirement IDs this feature makes obsolete.
Format is <spec-id>.RN where <spec-id> is either legacy FXX
(e.g., F01.R3) or new domain.slug (e.g., schema.field-access.R3).
Match the format your project uses — check existing specs in .spec/domains/
to determine which is in use. Cross-check against the context bundle
requirements to confirm they exist before writing them. MUST include any
conflicts identified in Step 1.
decision_refs: ADR slugs from .decisions/ that informed this feature.
Check that .decisions/<slug>/adr.md exists.
kb_refs: KB paths from .kb/ that informed this feature.
Check that .kb/<path>.md exists.
displaced_by: Set by the pipeline during displacement finalization — do not populate manually. Array of spec IDs (FXX or domain.slug) that caused this spec to be INVALIDATED.
revived_by: Set by the pipeline when a new spec revives this one — do not populate manually. Array of spec IDs.
revives: Set when authoring a spec informed by an INVALIDATED predecessor.
Array of spec IDs whose behavioral intent this spec restores (in whole or
part). Each target must have state: "INVALIDATED".
displacement_reason: Set during displacement finalization. Free text explaining why this spec was displaced — captures the what/why for future inspection.
Step 5 — Determine spec ID, version, and file path
Spec ID format: Two conventions are supported. Use whichever matches
the project's existing specs (check .spec/domains/ to see what's in use).
domain.slug(recommended for new projects) — e.g.,schema.field-access,query.full-text-index. Self-describing, greppable, dimensional. The spec lives at.spec/domains/<domain>/<slug>.md.FXX(legacy) — e.g.,F07,F13. Numeric feature ID. The spec lives at.spec/domains/<domain>/F<nn>-<slug>.md(filename has both ID and slug).
Check the registry for existing versions:
# Try v2 manifest schema (specs[] array) first; falls back to v1 (features{}).
jq -r --arg id "$SPEC_ID" '
if .specs then
((.specs[] | select(.id == $id) | .path) // "")
else
(.features[$id].latest_file // "")
end
' .spec/registry/manifest.json
If no entry exists: version = 1.
- For domain.slug: filename =
<slug>.md - For FXX: filename =
F<nn>-<slug>.md
If entry exists: increment version. Filename gains .v<N> suffix
(e.g., field-access.v2.md or F07-compaction-engine.v2.md).
The slug is the title lowercased with spaces replaced by hyphens, truncated to 30 chars.
Step 6 — Determine target domain shard directory
Primary domain = first entry in domains array.
Directory = .spec/domains/<primary-domain>/
Step 7 — Write the spec file
Use this exact structure:
---
{
"id": "<feature-id>",
"version": <N>,
"status": "ACTIVE",
"state": "DRAFT",
"domains": [<domain list>],
"amends": [<feature ids or empty>],
"amended_by": [],
"requires": [<feature ids or empty>],
"invalidates": [<spec.RN refs or empty — FXX.RN or domain.slug.RN>],
"displaced_by": [],
"revives": [<spec ids of INVALIDATED specs this revives, or empty>],
"revived_by": [],
"displacement_reason": "",
"decision_refs": [<ADR slugs or empty>],
"kb_refs": [<KB paths or empty>],
"open_obligations": [<obligation strings or empty>]
}
---
# <feature-id> — <Title>
## Requirements
R1. <single falsifiable claim with explicit subject and measurable condition>
R2. ...
---
## Design Narrative
### Intent
<Why this feature exists — not what it does>
### Why this approach
<Rationale for the chosen design>
### What was ruled out and why
<Rejected alternatives with their disqualifying constraints>
<If none known: "Not captured at authoring time.">
### Invalidated requirements
<If any invalidates entries: explain why each prior requirement no longer holds>
<If none: omit this section>
## Verification Notes
Requirement writing rules:
- One falsifiable claim per requirement
- Explicit subject: "The vector index must..." not "Must..."
- Measurable condition where applicable
- No compound requirements: no "and" joining two obligations
- Present tense, active voice
- Unverified claims annotated:
[UNVERIFIED: assumes X — source needed] - Behavioral, not structural: requirements must be verifiable by observing inputs and outputs, not by reading source code. Never reference specific class names, method names, file paths, or call chains.
State assignment rules:
The state field determines whether a spec enters resolved context bundles
consumed by downstream pipeline stages (feature-plan, feature-test, etc.).
- If all requirements are verified and no conflicts remain from the
arbitration phase: set
state: "APPROVED" - If any conflict was deferred, dropped, or left unresolved during
arbitration: set
state: "DRAFT"and add each unresolved conflict toopen_obligations— for example:"open_obligations": ["resolve conflict between R3 and F02.R1"] - A DRAFT spec with
open_obligationsentries or[UNRESOLVED]/[CONFLICT]markers in its requirements is excluded from resolved bundles. It will not be consumed by downstream stages until the conflicts are resolved and the state is changed to APPROVED.
Step 8 — Normalize and validate
sed -i 's/\r//' "<spec-file-path>"
bash .claude/scripts/spec-validate.sh "<spec-file-path>"
If validation FAILS, fix all reported errors before proceeding. Do not proceed past this step with a failing spec.
Step 8a — Ambiguity score
Run the quantitative ambiguity gate to confirm the spec is ready for downstream consumers:
bash .claude/scripts/spec-ambiguity-score.sh "<spec-file-path>"
The script computes score = ([UNVERIFIED] + [UNRESOLVED] + [CONFLICT]) / total_requirements and reports PASS if score <= 0.20.
Interpretation:
- Score 0.00 (PASS): clean — all requirements are resolvable from the
current context. Proceed with
state: "APPROVED"unless Step 7 state rules already assigned DRAFT for another reason. - Score between 0.01 and 0.20 (PASS): one or two lingering markers
against a well-sized spec. The spec can go APPROVED if the markers are
on non-load-bearing requirements; otherwise set DRAFT and use
open_obligationsto track each unresolved claim. - Score above 0.20 (FAIL): too many unresolved claims for
downstream consumption. Set
state: "DRAFT", record each marker inopen_obligations, and surface the score in your completion report so the caller sees it.
The score is a signal, not a hard gate — Step 7 state rules remain authoritative for state assignment. The score adds transparency: authors see why a spec is DRAFT instead of inferring from the absence of explicit feedback.
Do not proceed to registration while the score indicates FAIL and the spec is being registered as APPROVED. These two states are inconsistent — resolve them (lower score by resolving markers, or acknowledge DRAFT state) before continuing.
Step 9 — Update shard INDEX.md
Add a row to the Feature Registry table in the domain shard's INDEX.md:
| <ID> | <Title> | ACTIVE | <amends or —> | <decision_refs or —> |
Step 10 — Register in manifest
Update the manifest with the new spec:
tmp=$(mktemp)
jq --arg id "<feature-id>" \
--arg lf "domains/<primary-domain>/<filename>" \
--arg st "DRAFT" \
--argjson doms '["<domain1>","<domain2>"]' \
'.features[$id] = {"latest_file": $lf, "state": $st, "domains": $doms}' \
.spec/registry/manifest.json > "$tmp" \
&& mv "$tmp" .spec/registry/manifest.json
Increment the domain feature_count:
tmp=$(mktemp)
jq --arg d "<primary-domain>" '.domains[$d].feature_count += 1' \
.spec/registry/manifest.json > "$tmp" \
&& mv "$tmp" .spec/registry/manifest.json
Step 11 — Update .spec/CLAUDE.md
Add a row to the Recently Added table:
| <date> | <ID> | <primary-domain> | <Title> |
Keep only the 10 most recent rows. Move older rows off the table.
Step 12 — Confirm
Report:
Spec written: .spec/domains/<domain>/<filename>
Validation: PASS
Registry: updated
Prerequisites created: <count or "none">
<list of stub spec IDs if any>
Conflicts resolved: <count or "none">
<list of invalidated requirements if any>
Reverse dependencies (specs requiring this feature's prerequisites):
<query manifest for specs that require the same IDs>
Next: run /spec-verify <feature-id> after implementation review
Hard constraints
- Never skip the conflict check (Step 1) — silent contradictions are defects
- Never write a prerequisite stub without verifying requirements against code
- Prerequisite stubs with all requirements verified are APPROVED immediately
- Never skip spec-validate.sh — fix all errors before proceeding
- Never use YAML syntax in front matter — JSON only
- Never omit the human narrative separator (bare --- line)
- Requirements must be numbered R1, R2, R3 — never lettered or nested
- If invalidates references cannot be confirmed against context bundle, leave the array empty and note it in open_obligations
- Never set state to APPROVED if unresolved conflicts exist — use DRAFT with open_obligations entries so the spec is excluded from resolved bundles
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.