ss-sdd-writing-specs — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited ss-sdd-writing-specs (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Render the agreed understanding from the discovery stage into a structured spec document. No user interaction in this stage — discovery already happened. The coordinator already holds the agreed content; this skill is about how to lay it out on disk.
Core principle: The spec is a contract. It should be specific enough that a competent engineer (or a fresh subagent) can implement it without re-deriving intent from a chat log.
Announce at start: "I'm using the ss-sdd-writing-specs skill to render the spec document."
.sublime-skills/state.json. It is permanently gitignored. Do NOT bypass via git add -f, --force, git update-index, or any other mechanism. See state-schema.md "Git policy" for the full list.spec.md using the required structure (Spec Structure section below)Storage layout (fixed):
docs/specs/NNN-<short-name>/
spec.md # this stage writes (user-facing artifact, committed at Stage 7)
.sublime-skills/
state.json # SDD state file (created by preflight at Stage 0; this stage writes feature_id, short_name, work_type, spec_path into it) — gitignored, never committedNumbering:
NNN is sequential, three digits, zero-paddeddocs/specs/ for the highest used number; new feature is highest+1001Short name:
user-auth, add-export-csv, fix-payment-timeoutIf discovery (Stage 1) already Read these files in this session, you can skip re-Reading — but you MUST still have constitution + ADRs + glossary contents in your working context before writing the spec.
Otherwise, run the discovery script and Read every file it returns a non-null path for before composing the spec:
"${SUBLIME_SKILLS_HOME:?SUBLIME_SKILLS_HOME is not set; see Sublime-Skills README for setup}"/skills/spec-driven-development/framework/discover-context.shRequired reads when present (skip files the JSON returns as null):
constitution — non-negotiable principles the spec MUST comply with; violations get flagged CRITICAL by ss-sdd-reviewing-specs (Stage 3)adrs — prior decisions you must respect, not re-litigate; silently contradicting a settled ADR is CRITICALglossary / domain — canonical domain vocabulary; synonym proliferation is HIGH/MEDIUM (vocabulary drift)architecture — situates the feature within existing structurereadme — fallback for high-level project understandingThese reads are load-bearing, not padding — the next stage's auto-review checks the spec against them. A spec written without reading them will fail review.
Empty-context case: if every context field in the JSON comes back null (greenfield project, no bootstrap yet), that's a valid state — proceed without context. Do not halt; do not ask the user to produce files. Note the empty-context state in your final report and move on.
Use the structure in Spec Structure below. Omit any section that doesn't apply — don't leave "N/A" placeholders.
Required sections (in order): Goal, User Stories, Functional Requirements, Success Criteria, Edge Cases, Assumptions, Out-of-Scope.
Optional sections: Key Entities (only if data is involved), Open Questions (only if any remain), References (only if external docs/specs/ADRs are worth linking).
Write atomically. Compose the full spec content, write to <spec_path>.tmp, then mv <spec_path>.tmp <spec_path>. The atomic move prevents a half-written spec.md if the session dies mid-write. Apply the same pattern when editing the spec during the approval gate — never edit-in-place.
The state file already exists — preflight (Stage 0) wrote the shell. Read the current contents, merge in the feature-identifying fields produced by Stage 1 / this stage, and write back using the atomic pattern (write to .sublime-skills/state.json.tmp, then mv .sublime-skills/state.json.tmp .sublime-skills/state.json). See framework/state-schema.md for the full state schema (and the "Git policy (CRITICAL)" section — state.json is permanently gitignored and must never be committed).
Fields this skill writes:
| Field | Source | Notes |
|---|---|---|
feature_id | This skill (Step 1 dir resolution) | Format: NNN-<short-name>. |
short_name | This skill (Step 1) | Kebab-case. |
work_type | Coordinator's in-memory dict (captured at Stage 1 by ss-sdd-discovering-requirements) | "feature" or "fix". |
spec_path | This skill | docs/specs/<feature_id>/spec.md. |
updated_at | This skill | Touched on every write. |
All other fields (including started_at, current_stage, stages_completed) are owned by other skills or the coordinator — do NOT overwrite them.
Do NOT change `current_stage` or append to `stages_completed` here. The coordinator advances the stage and marks spec_written complete after this skill returns. (Avoids racing with the coordinator's stage-advancement logic.)
Do NOT commit spec.md. It stays uncommitted in the working tree. The ss-sdd-choosing-feature-branch skill at Stage 7 batch-commits spec.md alongside plan.md and ADRs. The state file at .sublime-skills/state.json is gitignored and is never committed at any stage.
Before reporting back:
Run the validator script:
"$SUBLIME_SKILLS_HOME/skills/spec-driven-development/framework/validate-spec.sh" docs/specs/NNN-<short-name>/spec.mdIf it fails (exit code 1): fix every CRITICAL issue it reports, then re-run. Don't proceed until the script returns PASS. Warnings can be left if they're acceptable for the spec's nature, but address them when easy.
The validator catches gross format issues; you check for semantic ones:
Fix issues inline. No need to re-review; just fix and move on. (A dedicated ss-sdd-reviewing-specs subagent will pass over this next.)
Return to the coordinator. The report must include the validator's PASS line verbatim — the coordinator uses this as proof that validation actually ran and succeeded.
Spec written: docs/specs/NNN-<short-name>/spec.md
Sections present: [list]
Open questions: [count]
State file: feature fields written (feature_id, short_name, work_type, spec_path).
Validator output (last line):
PASS — N warning(s), 0 critical issuesIf you cannot produce a PASS line from the validator (the script returned non-zero and you couldn't fix the issues), do NOT claim the spec is written. Report the failure instead with the validator's full output and which CRITICAL issues you couldn't resolve.
The coordinator will re-run the validator before committing — if your reported PASS doesn't match a fresh run, the coordinator aborts the stage. So there's no benefit to faking it.
# Spec: <Title>
**Feature ID:** NNN-<short-name>
**Created:** YYYY-MM-DD
**Status:** Draft
**Branch:** <branch-name>
## Goal
<One paragraph: what problem this solves and for whom. Use domain vocabulary.>
## User Stories
### Story 1 — <Brief title> (P1)
<Plain-language journey: who, what, why.>
**Why this priority:** <Reason — usually impact or dependency.>
**Independent test:** <How can this story alone be tested as an MVP increment?>
**Acceptance scenarios:**
1. **Given** <state>, **When** <action>, **Then** <outcome>
2. **Given** <state>, **When** <action>, **Then** <outcome>
---
### Story 2 — <Brief title> (P2)
<…same shape as Story 1…>
---
### Story 3 — <Brief title> (P3)
<…same shape as Story 1…>
## Functional Requirements
- **FR-001:** System MUST <capability>. _Stories: US1, US2_
- **FR-002:** Users MUST be able to <action>. _Stories: US1_
- **FR-003:** System MUST <data/behavior>. _Stories: US3_
(Each FR is testable. Each references the stories it supports.)
## Success Criteria
- **SC-001:** <Measurable outcome — time/percent/count/rate.>
- **SC-002:** <Measurable outcome.>
- **SC-003:** <User-experience or business metric.>
(All technology-agnostic, all measurable.)
## Key Entities
(Include only if data is involved. Otherwise omit.)
- **<Entity 1>:** <What it represents, key attributes (conceptual, no DB columns), key relationships.>
- **<Entity 2>:** <…>
## Edge Cases
- <What happens when [boundary condition]?>
- <How does the system handle [error scenario]?>
- <What's the behavior under [unusual load / network / data condition]?>
## Assumptions
- <Things we're treating as given that we should be explicit about.>
- <Defaults adopted from industry standards or project conventions.>
## Out-of-Scope
- <Adjacent feature explicitly deferred.>
- <Capability someone might reasonably expect but isn't included.>
## Open Questions
(Include only if some remain after discovery. Each should be answerable later — they're not blockers.)
- <Question 1>
- <Question 2>
## References
(Include only if external docs/specs/ADRs are worth linking.)
- ADR-NNNN — <title>
- <External doc URL or repo path>Default for "Acceptance scenarios": Given/When/Then. It's compact and readable.
EARS format is allowed when more precision is needed. Use only for FRs or for individual scenarios where ambiguity in the natural-language form is a real risk. Don't mix freely — pick a style per story and stick with it.
EARS templates:
WHEN <event> THEN <system> SHALL <response> (event-driven)IF <precondition> THEN <system> SHALL <response> (conditional)WHILE <state>, <system> SHALL <response> (state-driven)WHERE <feature>, <system> SHALL <response> (ubiquitous)<system> SHALL <response> (unconditional)If using EARS, mark the story with **Acceptance criteria (EARS):** instead of **Acceptance scenarios:**.
| Mistake | Fix |
|---|---|
| Leaving "TBD" or bracketed placeholders | Either fill it in from discovery context or move it to Open Questions |
| Implementation creeping in (file paths, code) | That belongs in the plan. Move it. |
| Multiple stories conflated as one | If two stories have different priorities or different tests, split them |
| Vague success criteria ("fast", "scalable") | Quantify: "p95 < 200ms", "10k concurrent users" |
| Acceptance scenarios that aren't testable | Restate so a tester (or a tester subagent) could verify with a clear pass/fail |
| Domain-noun drift (using "user" then "customer" then "account holder" for the same thing) | Canonical term from glossary; one term across the doc |
Force-adding state.json with git add -f | NEVER. Zero exceptions. |
Editing .sublime-skills/.gitignore mid-pipeline | NEVER. The ignore is permanent. |
git add -f .sublime-skills/state.json → STOP.sublime-skills/.gitignore → STOP~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.