W Workflow Skills Wiki
Home Wiki Index GitHub

Spec Lifecycle (specs)

From Workflow Skills Wiki, the living architecture & domain knowledge base

> Provenance: .agents/skills/ws-spec-write/SKILL.md, .agents/skills/ws-spec-format/scripts/validate_spec.cjs, .agents/skills/ws-spec-organizer/scripts/resolve_spec_path.cjs, .agents/skills/ws-spec-index/SKILL.md, living synthesis of specs 0009, 0040, 0045, 0051, 0053, 0065, 0084, 0108-code-review-findings-fixes through 0121-us-395, 0135-spec-organizer-status-subfolders, 0145-us-439, 0146-us-436, 0157-spec-closure-strengthening, 0165-us-474.

Feature [edit]

Specifications are point-in-time delivery contracts. Operators author them in free text or reformulate tracker issues into testable acceptance criteria, then route them through planning, indexing, listing, explanation, drift repair, and archival. The ws-spec-manager router (/spec) dispatches to specialists without reimplementing their logic: ws-spec-write authors specs, ws-spec-format validates schema, ws-spec-organizer resolves paths and prefix ordering, ws-spec-index maintains index.PRD, ws-spec-list inventories both boards, ws-spec-explain provides read-only panorama, ws-spec-update fixes surgical drift, and ws-spec-archive harvests history and cleans shipped plan directories. The outbound companion ws-spec-to-issue reformulates a free-text idea into an anonymized tracker item (GitHub issue / ADO User Story) with no local spec and no git mutation, so a running workflow's tree stays clean; the item re-enters later through ws-spec-from-provider or a provider fetch-to-spec. Creation flows run ws-spec-write โ†’ ws-spec-organizer โ†’ ws-spec-format โ†’ ws-spec-index track. The human Spec Board ({specsDir}/*.spec.md) stays strictly separate from the transient Plan Board ({plansDir} runs), with gated destructive actions on the list skill.

How it works [edit]

Authoring mode requires sections for Out of Scope, Assumptions, Definition of Ready, Validation and Observation Notes, and Negative and Failing Test Scenarios. Every stated requirement maps to at least one acceptance criterion or an explicit out-of-scope row. Acceptance criteria must be EARS-shaped: every - ACn: bullet matches one documented pattern (ubiquitous The <system> shall <response>, event-driven When <trigger>, the <system> shall <response>, state-driven, optional-feature, or unwanted-behavior), and authoring validation rejects free-form bullets naming the offending AC id. The ## Out of Scope table must carry at least one substantive data row, and its finder reads the canonical section so verbatim bullet lists or tables pasted into other sections can neither satisfy nor shadow it. Compat mode keeps its legacy behavior so historical specs validate unchanged. ws-spec-write never blind-copies a tracker issue or pasted description: it reformulates the requirement into explicit, testable acceptance criteria, keeps public-issue context verbatim, and strips private consumer names, paths, hostnames, and customer data per the hub's Source anonymization rule. validate_spec.cjs --mode=authoring must exit zero before registration proceeds. Gray areas with two or more product options get a {slug}.context.md companion that is never empty.

Plans are interrogated against DoR during the plan interview step. Implementation follows failing-tests-first discipline with positive and negative scenario verification. ws-spec-update (delta spec drift) and ws-spec-index sync (phase status) serve different purposes and must not be interchanged. Prefix ordering is opt-in via plans.enforceSpecPrefixOrdering; when true, specs-of-record use NNNN-{slug}.spec.md ordered by specDate, git first-add, or mtime, while frontmatter slug and {plansDir}/{slug}/step-00 paths stay unprefixed. Reorder runs only through explicit organize_specs.cjs --apply with git mv; install never auto-renames existing files.

Large boards can be filed into status subfolders under {specsDir}: pending/ (draft, todo, in progress), completed/ (delivered or closed), and archived/ (cancelled, superseded, retired). plans.statusSubfolders (default false) makes new specs resolve under pending/; --status=<status> does the same per call. Path resolution finds an existing spec anywhere under the root or the three subfolders, and prefix numbering scans all of them so NNNN stays globally unique. organize_specs.cjs --by-status reads frontmatter status: first, then index.PRD (Archive, Done log, and [x] checkboxes), then issueState:. An index completion mark files the spec under completed/ even when issueState is still open. plans.autoOrganizeByStatus (default false) makes a run with no --slug and no --dry-run behave as --by-status --apply when plans.statusSubfolders is also true. The move includes *.context.md and *.assets/ companions and rewrites index.PRD links. Tracked files move with git mv, and the run fails closed when dirty tracked files overlap. A spec that reaches completion through ws-spec-index sync moves from pending/ to completed/.

Close-time filing is deterministic rather than best-effort. sync_index.cjs files the shipped spec into completed/ with its sidecars first (rewriting spec: refs in the same apply) and only then updates the index rows; when filing cannot happen the result reports outstanding with a non-zero exit instead of succeeding. verify_close_filing.cjs fails the close when a stale pending/ path is still referenced, and the check_spec_filing.cjs Phase 5a harness gate flags index [x] rows that point under pending/.

ws-spec-translate-to-human writes a human runbook beside the agent spec. The companion uses numbered Implementation, UI Test, and Out of scope steps. It does not replace the spec, and a refinement hook that offers it is non-blocking when the companion is absent.

The dispatcher never reimplements specialist scripts. Slice specs for task-lifecycle work never create {plansDir} trees. Recent boards keep the same discipline at higher volume: review-fix rounds, coverage pushes, monitor and queue hardening, hub relocation, and generator work all land as prefixed specs-of-record with context companions where the gray area needs two or more product options, and shipped items archive with their plan trees harvested rather than left behind.

Backend [edit]

Path authority lives in resolve_spec_path.cjs, which reads plans.specsDir (default .agents/specs) and the prefix flag. organize_specs.cjs supports --dry-run and --apply with index backtick updates after moves. Tracking order for index operations walks FEATURES.md โ†’ PLAN.md โ†’ PRODUCT.PRD โ†’ index.PRD, skipping missing files with a note.

Validation tooling includes validate_spec.cjs --mode=authoring|compat, covered by test/test-spec-dor-tdd.js and test/test-ws-spec-manager.js. The EARS and closure-finder rules are pinned by test/test-spec-closure-ears.js, and heading-variant open-question detection by test/test-classify-open-questions.js. Local registration copies specs into {plansDir}/{slug}/step-00-*.spec.md through register_local_spec.cjs after the specs-of-record file exists under {specsDir}.

The outbound ws-spec-to-issue flow resolves the active tracker from providers.active (falling back to an enabled tracker or the repo host, and STOPs when only local resolves), validates provider auth before any mutating call, and creates the item through the provider create-issue intent with an anonymized spec-shaped body passed via --body-file. A successful run leaves git status byte-identical, and --dry-run prints the resolved tracker, title, and body without creating anything.