ss-sdd-writing-plans — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited ss-sdd-writing-plans (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.
Convert an approved spec into an implementation plan that a per-task implementer subagent can execute without re-deriving intent. Tasks are bite-sized (2-5 minutes each), strictly ordered, and organized by user story to enable MVP-first delivery.
Core principle: Every step contains the actual content the implementer needs — exact file paths, complete code, exact commands with expected output. No placeholders, no "similar to above," no "TODO".
Announce at start: "I'm using the ss-sdd-writing-plans skill to render the implementation plan."
.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.[T###], optional [P], [US#], file paths, and concrete TDD steps with code + commands + commitsdocs/specs/NNN-<short-name>/plan.mdRead docs/specs/NNN-<short-name>/spec.md in full. Note:
**Requirements:** FR-..., SC-...Run the discovery script (skip re-Reads if these files are already in your working context from an earlier stage) and Read every file it returns a non-null path for before decomposing into tasks:
"${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 — tasks MUST comply; treat a violation as a CRITICAL issue to catch in your Step 6 self-reviewadrs — especially the newly accepted ones from Stage 4 — tasks must reflect the chosen approaches; silent contradiction is CRITICALarchitecture — fit the plan into the existing structure; follow established file/module patternsglossary / domain — use canonical terms in task descriptions and code identifiersPlans written without these reads typically fail review on constitution/ADR alignment, or surface as NEEDS_CONTEXT from the per-task implementers asking questions the convention files would have answered.
Empty-context case: if every context field in the JSON comes back null (greenfield project; no constitution, ADRs, architecture, or glossary), that's a valid state — proceed without them. Do not halt; do not ask the user to produce files. The plan can still be written; the auto-review just won't have alignment checks to run against.
See the Plan Structure section below for the full header template. The header includes title, feature ID, spec link, status, goal, architecture (2-3 sentences referencing ADRs), and tech stack. This is the first content written to the file; later steps add file structure, then tasks.
Before defining tasks, list every file that will be created or modified, with a one-line responsibility:
## File Structure
**New:**
- `src/auth/jwt.ts` — issue & verify JWTs
- `src/auth/middleware.ts` — Express auth middleware
- `tests/auth/jwt.test.ts` — JWT unit tests
- `tests/auth/middleware.test.ts` — middleware unit tests
**Modified:**
- `src/server.ts` — wire in auth middleware
- `src/routes/users.ts` — protect endpoints
**Dependencies:**
- Add: `jsonwebtoken`, `@types/jsonwebtoken`This locks in decomposition decisions. Each file should have one clear responsibility. Files that change together live together — split by responsibility, not by technical layer.
Tasks are organized into phases:
[US#])[US1], [US2], etc.Each story phase should be a complete, independently testable increment. Completing only Phase 3 (US1) must yield a working MVP.
### Task T012 [P] [US1]: Implement JWT issue/verify
**Files:**
- Create: `src/auth/jwt.ts`
- Test: `tests/auth/jwt.test.ts`
**Requirements:** FR-002, FR-003[P] — parallel marker, only include when this task is parallelizable (different files from other [P] tasks in the same phase, no dependency on incomplete tasks)[US#] — story label, only for Phase 3+ story tasks[NO-TDD] — TDD opt-out marker. Strict criteria — see [NO-TDD] Criteria section below. A reviewer will flag misuse as CRITICAL. Include a one-line reason on the line immediately after the task header.Every task uses Red-Green-Refactor steps unless marked [NO-TDD]:
- [ ] **Step 1: Write the failing test**
// tests/auth/jwt.test.ts import { issueToken, verifyToken } from '../../src/auth/jwt';
test('issueToken produces a verifiable token', () => { const token = issueToken({ userId: 'u1' }); const claims = verifyToken(token); expect(claims.userId).toBe('u1'); });
- [ ] **Step 2: Run test to verify it fails**
Run: `npx vitest run tests/auth/jwt.test.ts`
Expected: FAIL — "Cannot find module '../../src/auth/jwt'"
- [ ] **Step 3: Write minimal implementation**
// src/auth/jwt.ts import jwt from 'jsonwebtoken'; const SECRET = process.env.JWT_SECRET || 'dev-secret';
export function issueToken(claims: object): string { return jwt.sign(claims, SECRET, { expiresIn: '24h' }); }
export function verifyToken(token: string): any { return jwt.verify(token, SECRET); }
- [ ] **Step 4: Run test to verify it passes**
Run: `npx vitest run tests/auth/jwt.test.ts`
Expected: PASS — 1/1 tests passing
- [ ] **Step 5: Commit**
git add src/auth/jwt.ts tests/auth/jwt.test.ts git commit -m "feat(auth): JWT issue/verify (T012)"
[NO-TDD] is allowed ONLY when the task falls entirely into one of these categories. The reason line must match one of these labels (verbatim or close):
| Allowed category | Examples |
|---|---|
docs-only | README updates, CHANGELOG, ADR text fixes, code comments only |
config-only | JSON/YAML/TOML/INI data changes without logic — e.g., bumping a timeout value, adding a feature flag entry |
asset-addition | Images, fonts, static files, fixtures with no consuming code |
dependency-bump | package.json / Cargo.toml / requirements.txt version bumps without API changes |
mechanical-rename | File renames or moves with all callers updated mechanically; no behavior change |
lint-only | Whitespace, import sorting, formatter-driven changes |
[NO-TDD] is NOT allowed for:
If you find yourself reaching for [NO-TDD] because the test would be "tedious", you're in TDD territory; write the test.
Your Step 6 self-review checks [NO-TDD] usage — treat any misuse as a CRITICAL fix before saving.
[NO-TDD] applies)- [ ] **Step 1: <Action>**
<Show the exact change — code/config/command. Same precision standard as TDD steps.>
- [ ] **Step 2: Verify**
Run: <command>
Expected: <output or behavior>
- [ ] **Step 3: Commit**
git add ... ; git commit -m "..."Run the validator script:
"$SUBLIME_SKILLS_HOME/skills/spec-driven-development/framework/validate-plan.sh" docs/specs/NNN-<short-name>/plan.mdIf it fails (exit code 1): fix every CRITICAL issue, then re-run until PASS. Common failures: missing required sections, T### IDs not on task headers, [NO-TDD] markers without a reason on the next line, placeholders, forbidden diagram syntax.
After the validator passes, read the plan with fresh eyes:
clearLayers() in T003 and clearFullLayers() in T007 = bug.[NO-TDD] marker has a reason matching one of the allowed categoriesFix issues inline before saving. There is no separate plan-review stage — this self-review is the plan's last check before it goes to the branch-settle and implementation stages.
Save to docs/specs/NNN-<short-name>/plan.md. Use the same NNN-<short-name> directory established by the spec.
Write atomically. Compose the full plan content, write to <plan_path>.tmp, then mv <plan_path>.tmp <plan_path>. The atomic move prevents a half-written plan.md if the session dies mid-write.
Update .sublime-skills/state.json using the atomic pattern (write to .sublime-skills/state.json.tmp, then mv .sublime-skills/state.json.tmp .sublime-skills/state.json):
{
"plan_path": "docs/specs/NNN-<short-name>/plan.md",
"current_stage": "plan_writing",
"updated_at": "<ISO-8601 timestamp>"
}Leave current_stage as "plan_writing" and DO NOT append "plan_written" to stages_completed here. The coordinator advances and marks completion after this skill returns.
Do NOT commit plan.md. It stays uncommitted in the working tree. The ss-sdd-choosing-feature-branch skill at Stage 7 batch-commits plan.md alongside spec.md and ADRs. The state file at .sublime-skills/state.json is gitignored and is never committed at any stage.
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.
Plan written: docs/specs/NNN-<short-name>/plan.md
Phases: <N>
Tasks: <N>
Spec coverage: <FRs covered / total>
Validator output (last line):
PASS — N warning(s), 0 critical issuesIf you cannot produce a PASS line from the validator, do NOT claim the plan is written. Report the failure with the validator's full output and which CRITICAL issues you couldn't resolve.
The coordinator will re-run the validator before committing — a mismatch aborts the stage.
# Plan: <Title>
**Feature ID:** NNN-<short-name>
**Spec:** [spec.md](./spec.md)
**Created:** YYYY-MM-DD
**Status:** Draft
## Goal
<One sentence — what this builds.>
## Architecture
<2-3 sentences on the approach. Reference ADRs that govern key choices.>
## Tech Stack
<Key technologies/libraries used, in bullet form.>
---
## File Structure
**New:**
- `path/to/file.ext` — one-line responsibility
**Modified:**
- `path/to/existing.ext` — what changes about it
**Dependencies:**
- Add/remove: <package names>
---
## Phases
### Phase 1 — Setup
(Tasks with no `[US#]` label. Project init, dep installs, config.)
### Phase 2 — Foundational
(Tasks blocking multiple stories. No `[US#]` label.)
### Phase 3 — <Story 1 title> (US1)
(Tasks tagged `[US1]`. Completing this phase alone yields a working MVP increment.)
### Phase 4 — <Story 2 title> (US2)
(Tasks tagged `[US2]`.)
### ...
### Final Phase — Polish
(Cross-cutting concerns, integration tests, docs. No `[US#]` label.)Required sections (in order): Header (title, feature ID, spec link, created, status), Goal, Architecture, Tech Stack, File Structure, Phases (1, 2, 3+, Final Polish).
Optional sections (include only if applicable, append after Phases): Open Questions, Risk Notes, Dependencies & Sequencing (only if cross-task ordering needs explaining beyond what the phase structure already implies).
If a section doesn't apply, omit it entirely — don't leave "N/A" placeholders.
| Acceptable | Forbidden |
|---|---|
| Complete code block for the file or change being made | Code stub with // implementation here |
Exact pytest tests/x.py::test_y -v command with expected output | "Run the tests" |
git add a.py b.py && git commit -m "..." with the actual message | "Commit your changes" |
| "Reference: FR-005, FR-006" | "See spec" |
| Type signatures and imports | "Use the right types" |
| Mistake | Fix |
|---|---|
| Vague steps ("add error handling") | Show the exact error-handling code |
| "Similar to Task 3" without repeating the content | Repeat the code; implementers may read tasks out of order |
| Tasks that span multiple files when they should be split | One file's worth of change per task is the right granularity in most cases |
| Phase 3 (US1) doesn't produce a working increment | Re-decompose — MVP-first means Story 1 must stand alone |
[P] on tasks that actually touch the same file | Drop [P] and sequence them |
Forgetting **Requirements:** on tasks | Add traceability — reviewers and the coordinator both use it |
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.