Skip to content
v0.7.1

Spec-Driven Development. Scaffold, validate, and maintain behavioral specs (spec.md + .feature files) for software features.

cyber-sdd

0.7.1

Patch Changes

  • 0fbadde: fix(sdd): run the skill engines on Node 22.11 to 22.17

    Git and marketplace installs ran each engine's .mts source directly, which Node runs without a flag only from 22.18. The engines' sources now live in src/, and universal-plugin plugin build compiles each one into a self-contained skills/<skill>/scripts/<engine>.mjs that runs on every install channel and on Node 22.11 or later. The .mjs launchers, the .bundle.mjs files, and the separate dist/ build are gone. The sdd-check-specs bin now runs the compiled check-project-specs.mjs, and the package has no runtime dependencies. The canonical manifest moved from .plugin/plugin.json to plugin.json.

0.7.0

Minor Changes

  • a130c6a: Scope the two-level depth rule to capability nodes and give design/ its own shape: the current design at the top as one file per perspective, an optional project-named version folder (such as design/next/) holding a full copy of the next design, a one-commit cut, and a cut trigger recorded in the placement map. The formation Warden and the architect bars judge placement by that rule.

Patch Changes

  • 32b25db: Point the sdd Codex manifest $schema at the public Codex plugin manifest schema instead of the retired cyberuni/marketplace repo.
  • 522cbbc: Make the skill engines run when the package is installed from npm. Node will not strip types from a file under node_modules, so node <skill>/scripts/<engine>.mts failed with ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING. The skills now document node <skill>/scripts/<engine>.mjs, a launcher that runs the .mts source in a checkout and a self-contained bundle (built into the package at pack time, with sibling skills inlined) under node_modules. retire-plans gains --distilled <ledger-dir> so the doctrine loop no longer imports the .mts directly.

0.6.1

Patch Changes

  • a35b43d: Align the impl-judge with the plugin contract (it now also loads gate-validation-governance), resolve sdd:automaton to sdd:sdd-automaton, use one cyberlegion@<version> form, name the gherkin-cli pin once, and drop history asides and repo-only references from agent and skill bodies.

0.6.0

Minor Changes

  • f4df49c: check-field-mandates now catches a field spelled in two cases. A field token is UPPER_CASE or snake_case, and both spell the same field. When a definition's prose names a field in one case and its own block declares it in the other, the check reports it as miscased. A lowercase word counts as a field token only if it contains an underscore, so ordinary words in code spans are never fields.

    Fixed the mismatch this was widened to catch (#29). The spec-producer's Output block now spells its field governances_loaded, the spelling its prose, the conductor, the spec-judge and the producer suite already use. Quill's spec-writer implements the same producer contract, and its block and prose now use the same spelling.

0.5.0

Minor Changes

  • 7c08292: New check-field-mandates check: a skill or agent definition's structured blocks and its prose must agree on their fields.

    A definition ships blocks — an Input, an Output, a dispatch payload — whose fields the prose also mandates. The check reads every plugins/<plugin>/skills/**/SKILL.md and plugins/<plugin>/agents/*.md and reports both directions with file, line and token:

    • unexplained — a block declares a field that carries no gloss and the prose never names.
    • undeclared — the prose names a known field in a code span that none of the file's blocks declare.

    A field is explained by a gloss on its declaration or a mention in the prose. A prose line that names another agent's field on purpose is excused by a reasoned marker beside it: <!-- field-mandate-ignore: <reason> -->. There is no allow-list and no report-only mode — a finding always exits non-zero.

    Three shipped definitions (impl-producer-governance among them) mandated returning a BLOCKER their Output block had no line for; each now carries it. The bare path fields in the producer and judge Input blocks now carry a gloss.

0.4.2

Patch Changes

  • f4a72e2: The spec gate's check-suite engine now runs from an installed plugin. It imported gherkin-cli as a package, and an installed plugin has no node_modules, so the producer's suite self-check failed with module-not-found. The engine now imports a self-contained gherkin-cli bundle shipped beside it.
  • 770bad2: classify-edit-class, touch-set-correction and verify-scenarios now run from an installed plugin. Each imported gherkin-cli as a package, and an installed plugin has no node_modules, so each failed with module-not-found. They now import the same self-contained gherkin-cli bundle the spec gate's check-suite uses.

0.4.1

Patch Changes

  • f3a7f14: concept-index keeps whole node paths when --spec-dir is ., starts with ./, or ends with /. Before, it cut characters off the front of each path, so cli/ showed as i/.
  • c6eaee4: Adapt to gherkin-cli 0.2: use its renamed diff, parse and validate entry points and injectable git reader.
  • 05d4858: The conductor now hands SDD's own judges (sdd-spec-judge, sdd-impl-judge) to a dispatch capability by their definition file path, agents/<name>.md under the SDD plugin root, instead of by name. A capability that looks up definitions only in the project's own agent folder could not find them, so routing a judge through it failed. When the file is not there, the conductor spawns the judge as a portable cold subagent.
  • f069d67: Fix the sdd-check-specs bin from an npm install. It pointed at the .mts source, which Node refuses to run under node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING). The bin and every engine it spawns now ship compiled to dist/; the skill still runs the .mts source in place.

0.4.0

Minor Changes

  • cabf38b: start-mission now splits its governance bars into an entry set the conductor reads before intake and a deferred set it reads at the decision that invokes them, and requires the conductor to declare the set it actually loaded.

    The entry set — lifecycle, ownership, spec-format, suite-format, spec-producer, combat-log, gate-validation — governs every mission, so reaching intake without it means acting unbound. Remediation (on a change verdict) and impl-producer (at deliver) stay deferred: naming a governance is not loading it. The split is by reach, not phase — gate-validation fires only at a gate yet belongs to the entry set, because the spec-judge's pre-flight derives its expected set from its own fixed-universal floor and checks it against the relayed producer declaration, so deferring it would fail that pre-flight by construction.

    The declaration makes the load visible: a skipped governance and a correctly loaded one no longer look identical from outside. It is the conductor's own declaration, recorded separately from the producer_governances_declared set it relays.

0.3.1

Patch Changes

  • 3f824e5: The plugin manifests carry the package version. They said 0.0.0 since the plugin was scaffolded, so plugin update could not tell a newer release apart.

0.3.0

Minor Changes

  • 9bb913b: A new check-plugin-manifests guard: a plugin manifest may declare only components the package actually ships.

    A manifest pointer naming something the package does not carry is valid JSON, satisfies the schema, and is copied into every generated vendor manifest — so it fails only on an installer's machine, after publish, as a component the host runtime cannot load. Nothing in the repo compared a manifest's pointers against what ships.

    The guard resolves every ./-prefixed pointer two ways, because a component can fail to arrive in two independent ways: against disk (does the path exist?) and, for a package that publishes, against its files allowlist (will the tarball carry it?). A directory can exist and be excluded; a files entry can name a directory nobody created.

    Keyed on the value shape rather than a component-key list, so a key the manifest format adds later is covered without editing the engine. Pointers resolve against the plugin root, not the manifest's own directory, so a generated vendor manifest one level down is read correctly.

    It joins the root chain as check:plugins, so it runs on every pnpm verify and in CI. A finding always exits non-zero — there is deliberately no report-only mode, because a chain that reports green over a defect is treated as clearance to commit.

  • 4a0230a: check-project-specs gains a total corpus scope and refuses flags it does not define.

    --corpus runs the coverage guard and the engine set over every project-spec in the corpus, so a repo's commit floor checks the whole tree rather than one project. The two sub-checks compose deliberately: the sweep only visits the specs discovery recognizes, so a spec whose lifecycle status is a typo is invisible to it, while the coverage guard sees that file on disk but says nothing about whether the engines pass.

    Breaking for callers of --check-coverage: that flag is removed, and an unrecognized flag is now an error instead of falling through to project scope. The fall-through was silent — project scope at a repo root resolves no governing spec and exits 0 — so a coverage-only flag could guard every commit and every CI run while running no engine at all. Replace --check-coverage with --corpus.

0.2.1

Patch Changes

  • 405b8de: Spec discovery no longer descends into a nested checkout.

    The filesystem walk was guarded by a name blocklist — node_modules, .git, dist, .turbo, .next, coverage. A name list can say "this is called X"; it cannot say "this is where the tree stops". A nested git checkout is a full copy of the repository inside the repository, and nothing stopped the walk from descending into one, so every spec in the corpus was discovered once per nested checkout.

    The guard was also one level off from where it was needed: .git was already in the blocklist, so the walk skipped the metadata directory while descending straight into the checkout that directory marks.

    Agent harnesses that isolate work in a git worktree check the repo out inside itself, which is how this surfaced: seven such worktrees turned a 10-spec corpus into 38 discovered spec files. The loud symptom was check-project-specs failing with "packages/x is claimed by 8 specs — a project has exactly one spec", breaking pnpm verify. The quiet symptom was worse: check-suite, concept-index, check-scenario-overlap, check-spec-structure, and the formation Warden all reported green over a corpus nearly 4x duplicated. A guard passing over a corpus nobody has is worse than one that fails.

    Discovery now skips any directory that is itself a checkout — one carrying its own .git, whether a directory (a clone) or a file holding a gitdir: pointer (a worktree or a submodule). Both forms matter: guarding only on the directory form would miss the worktree case that caused this.

    The rule is structural rather than a name, so it also covers submodules, vendored clones, and whatever the next harness invents, without naming any of them. A directory that merely resembles a checkout by name but carries no .git of its own is still part of this tree and is still scanned.

    All three walk sites are guarded: the fixed-convention scan, the ** any-depth expansion behind extra anchors, and the <project> glob frontier.

0.2.0

Minor Changes

  • 3b8b39a: The ## Use Cases section now carries actor, goal, and extensions — not just entry points.

    A use case was defined as an entry point: one row per way the capability is invoked, given as trigger / inputs / outcome and named to its implementation surface. That definition had stood since the section was introduced. It never drifted; it was narrow from the origin.

    The requirements-engineering definition is actor + goal + main success path + extensions — the alternate, error and divergence paths. Extensions is where "what can go wrong for this actor" and "which of these inputs may be combined" live, and a trigger / inputs / outcome row has nowhere to put any of it. The practical cost: a spec could satisfy the bar completely and still carry no analysis of why each element of a designed surface exists, what breaks it, or which combinations are valid. A judge would then confirm the section existed and each row mapped to a scenario, and pass it — coverage of what is present cannot detect what should not be present.

    What changes for spec authors:

    • Use cases are enumerated by actor, never by entry point. Walking the interface returns only the use cases that interface already implies, and is structurally blind to the one nobody built. List the actors first — including whoever is affected by the outcome without invoking it — then their goals, then map goals to entry points. Both mismatches are findings: a goal no entry point serves, and an entry point no listed goal reaches. The enumeration is checkable in both directions. On a backfill, source yields only the served use cases by construction, and an inferred set is never reported as complete.
    • A use case carries four parts — actor / goal (one line each, not a persona), the entry point (trigger / inputs / outcome, unchanged), and its extensions. An extension is any path from the trigger that does not reach the success outcome, stated with its cause and outcome. The recurring kinds (refusal, error, boundary, partial result, contended or absent input) are a prompt to search with, not a closed set.
    • extensions: none — <why> is written explicitly when a use case has no divergence, so the claim is contestable rather than silently absent.
    • Every element of the public surface traces to a use case that needs it — each flag, option, parameter, prop, or event names the use case requiring it and the elements it may not be combined with. An element no use case needs is an orphan: cut it or name the use case. This is the same orphan-detection discipline as ## Scenario map, one level up.
    • Degenerate surfaces stay cheap. A capability with one entry point and no optional elements records the trace in a line, never a table — the obligation is that nothing on the surface is unaccounted for, not that a table exists.

    The three spec-gate actor bars each take a duty in their own domain rather than a shared copy: Oracle rules cut-or-justify on unbought surface and treats an actor or goal that restates the mechanism as an unanswered Why; Builder requires every stated extension to be a path in the control-flow graph, so the graph supplies the scenario rather than the prose, and judges extensions: none as a claim; Architect requires the graph to reach every stated extension, since an extension with no edge is a dangling branch read from the prose side. Oracle additionally grades the actor enumeration in both directions.

    The CFG remains the single source scenarios derive from. Extensions are a discovery instrument, not a parallel specification: a graph drawn from an implementation reproduces what the code already does and can never say a branch is missing, whereas asking what can go wrong for this actor finds it. So an extension earns its scenario by being a path in the graph — never by being drawn from the stated list, which would make the suite 1:1 with that list by construction and unable to surface a hole. A use case is therefore not 1:1 with a scenario.

    Existing specs are not swept — the restored shape applies to new and revised nodes, so no in-flight change inherits a bar its node has not adopted. Backfilling the existing corpus one node at a time is tracked separately.

0.1.0

Minor Changes

  • 4f6dd97: Rename the package from @cyberplace/sdd-plugin to cyber-sdd and make it self-sufficient for a standalone install: gherkin-cli is now a real dependency (imported directly, no longer shelled out via npx), and package.json#files ships the right surface (skills, agents, plugin manifests) for npm install.

    Fixes a bug where concurrent .feature checks could corrupt the shared npx install cache under load, intermittently breaking check:spec.

  • 1c91b5c: Add check-retired-terms — a declared registry of retired paths and conventions (.agents/sdd/retired-terms.toml) plus a verify-time sweep over every git-tracked file that reports each surviving occurrence as file:line:term with its replacement and exits non-zero. Narrowed by built-in exclusions (the guard's own definition, durable provenance), a per-entry scope, and a two-form allow list. A malformed registry fails loud rather than falling back — the registry is the check.

  • a9c5528: The doctrine-loop Scanner now cross-checks each plan brief's own todos-all-done against its declared source-closed during its pass, and derives a retirement clearance set from agreement — feeding it to plan-retirement's existing --retire clearance-set input instead of a human hand-assembling it. It never autofixes a plan brief's frontmatter status (no legal terminal value exists for that field); a disagreement between the two signals is excluded from the clearance set and surfaced as a flagged finding in the Scanner's pass summary instead.

  • 1884751: Add the plan-brief finalize backstop to the SDD conductor: a mission that lands now reconciles its plan brief's todos and ## NEXT anchor to the landed state, in the same change as the work, rather than leaving the drift to a later retro.

    The reconcile is a backstop — it runs in one pass over the whole brief even when nothing updated it mid-flight — and it reconciles to the landed state, never marks everything done: a todo whose work was held out of scope stays un-completed and rides the follow-up machinery. It writes the brief and nothing else, including no terminal value in the plan-level status dispatch flag, which stays active | approved with terminal-ness derived. A mission that halts is checkpointed at its true in-progress state, not reconciled as landed.

  • b9ee04d: The SDD spec-judge now emits a non-blocking spec-format conformance warning when a behavioral spec.md is missing a required section — especially ## Use Cases, ## Control Flow (CFG), or ## Scenario map. The warning is surfaced in the spec-gate report and never blocks the gate, sets ALIGNED: false, or short-circuits the lenses on its own. Reference and descriptive nodes raise no warning.

Patch Changes

  • 05e1f1a: SDD combat-log: off-enum cause visibility nudge (cause-candidate flag) + reconcile gate stop-cause enum to dimension|clearance|ceiling (discharges 263-op6-m2).

  • ebee17d: SDD cold-instrument doctrine: mutation-sweep-first for instrument subjects; non-author evidence for rule-level decisions; new/revived rules stated abstractly + ablation-tested before landing.

  • 9d3ff29: Fix — SDD skill-script CLIs silently no-opped on the supported Node floor. The entrypoint guard used import.meta.main, added only in Node 24.2.0, while the engines floor is >=22; on Node 22/23 the guard was undefined, so the bin ran nothing and exited 0. Switched all 19 affected scripts — and 7 more that used the file://${process.argv[1]} concat, which breaks on any install path holding a space, #, ?, or %, and under the symlink npm creates for a bin — to the portable pathToFileURL(realpathSync(process.argv[1])).href === import.meta.url entrypoint check. Adds a guard test so no shipped script can regress to either form. Fixes #272.

  • ff85de9: verify-scenarios: bind a scenario whose test title differs only by punctuation or whitespace

    A curly apostrophe pasted where the frozen .feature has a straight one used to land the same scenario in BOTH the UNBOUND list and the EXTRA list, with nothing saying the two were the same binding — so a real signal read as an unbound scenario and got hand-judged at every impl gate. The fold now retries an unmatched key against unclaimed results on a punctuation- and whitespace-folded comparison key (curly quotes/apostrophes, dashes, ellipsis), and reports the bind as a probable title mismatch naming both verbatim titles so the typo still gets fixed. Titles are never rewritten, an exact match always wins, case is not folded, and an ambiguous fold stays UNBOUND.

    Closes #312.

  • a3c25a6: SDD spec-gate checks escalate malformed input instead of failing open. A root spec.md whose status is missing or outside the lifecycle enum is now a check-spec-state violation, and a per-project check-project-specs run that finds such a spec escalates instead of reporting "no spec governs — skipped" with exit 0 (#316). A ## Use Cases data row whose Scenario cell is empty or absent is reported like an un-backticked one, rather than skipped (#364). Also drops the dangling spec-tree pointers shipped scripts and skills cited — a repo-only path is a broken link for every installed user (#290).

  • 59e08d9: Fix — plugin-contract-governance's role-loads table dropped architect-spec from the spec-producer row, so plugin authors built agents that loaded three bars and were graded against four.

    The table contradicted its own preamble two lines above it, which states the spec-gate lens set as {oracle, builder, architect} and that "a producer self-aligns to exactly the bars its judge grades". The impl-producer row one line below correctly carried its architect-impl bar against its own {builder, architect} set, which is what identifies the spec-producer row as a dropped cell rather than a deliberate narrowing. spec-producer-governance — the procedural authority — and design/specialists-and-squads.md — the table's declared owner — both name all three bars.

    The cost was measured, not theoretical: six of six cold spec-judges blocked at governance pre-flight on a plugin built to this row, and when the affected nodes were re-authored with the missing bar loaded, six of six carried a real defect only that bar caught.

    Also notes in the table's preamble that it is a shipped copy of a spec-owned table, restated because a governance loads standalone and cannot reach the spec tree — so a row missing part of its lens set should be read as a transcription slip.

  • b9260f8: Scanner detects an already-distilled mission by parsing the ledger as JSONL, never a substring grep (reuses distilledCrRefs; no engine change).

  • 1acc068: Codify Rule 1 (doctrine keep): a fold/aggregation node whose rule combines two or more interacting conditions states its rule in closed form — and re-derives its soundness against the real data model — before deriving scenarios; single-condition folds may be by example. Buys convergence, not coverage: pair with a mutation sweep and a safety dual. Includes the matrix corollary (draw every independent cell as a CFG branch, exclude degenerate cells). Realized in suite-format-governance, spec-producer-governance (fold instruction before scenario authoring), and builder-spec-governance.

  • 946429d: SDD doctrine: sanity-check a split's organizing axis against a real capability boundary (#388); prove a Clearance repair isn't a back-fit via pre-repair-draft failure (#389).