cyber-sdd 0.0.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.plugin/pins.json +3 -0
- package/LICENSE +21 -0
- package/agents/sdd-automaton.md +13 -2
- package/agents/sdd-scanner.md +85 -0
- package/agents/sdd-spec-judge.md +32 -2
- package/agents/sdd-warden.md +9 -0
- package/package.json +30 -23
- package/skills/align-spec/scripts/align-spec.mts +3 -2
- package/skills/architect-spec-governance/README.md +1 -0
- package/skills/architect-spec-governance/SKILL.md +12 -1
- package/skills/blast-estimate/README.md +3 -5
- package/skills/blast-estimate/SKILL.md +2 -2
- package/skills/blast-estimate/scripts/blast-estimate.mts +7 -4
- package/skills/builder-impl-governance/SKILL.md +9 -1
- package/skills/builder-spec-governance/README.md +1 -0
- package/skills/builder-spec-governance/SKILL.md +31 -3
- package/skills/check-partition-quality/scripts/check-partition-quality.mts +3 -1
- package/skills/check-plan-safety/scripts/check-plan-safety.mts +5 -2
- package/skills/check-project-specs/scripts/check-project-specs.mts +67 -5
- package/skills/check-retired-terms/README.md +18 -0
- package/skills/check-retired-terms/SKILL.md +81 -0
- package/skills/check-retired-terms/scripts/check-retired-terms.mts +293 -0
- package/skills/check-scenario-overlap/scripts/check-scenario-overlap.mts +3 -2
- package/skills/check-spec-structure/scripts/check-spec-structure.mts +3 -2
- package/skills/collision-ladder/README.md +3 -5
- package/skills/collision-ladder/scripts/collision-ladder.mts +7 -4
- package/skills/combat-log-governance/SKILL.md +43 -4
- package/skills/concept-index/scripts/concept-index.mts +3 -2
- package/skills/discover-plans/scripts/discover-plans.mts +5 -2
- package/skills/discover-specs/scripts/discover-specs.mts +5 -2
- package/skills/doctrine-loop/README.md +6 -0
- package/skills/doctrine-loop/SKILL.md +136 -2
- package/skills/formation-loop/SKILL.md +21 -1
- package/skills/gate-validation-governance/SKILL.md +2 -2
- package/skills/impl-producer-governance/SKILL.md +10 -1
- package/skills/init/scripts/wire-statusline.mts +5 -2
- package/skills/lifecycle-governance/SKILL.md +1 -1
- package/skills/manage-ignore/scripts/manage-ignore.mts +5 -2
- package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.mts +5 -2
- package/skills/manage-spec-anchors/scripts/manage-spec-anchors.mts +5 -2
- package/skills/mission-graph/README.md +3 -5
- package/skills/mission-graph/SKILL.md +72 -5
- package/skills/mission-graph/scripts/mission-graph.mts +505 -16
- package/skills/oracle-spec-governance/README.md +7 -2
- package/skills/oracle-spec-governance/SKILL.md +21 -4
- package/skills/place-node/scripts/place-node.mts +3 -2
- package/skills/plan-retirement/README.md +5 -2
- package/skills/plan-retirement/SKILL.md +5 -1
- package/skills/plan-retirement/scripts/retire-plans.mts +5 -2
- package/skills/plugin-contract-governance/SKILL.md +7 -1
- package/skills/remediation-governance/SKILL.md +36 -1
- package/skills/resolve-governances/scripts/resolve-governances.mts +5 -2
- package/skills/resolve-tracking/SKILL.md +2 -2
- package/skills/resolve-tracking/scripts/resolve-tracking.mts +5 -4
- package/skills/sdd/SKILL.md +1 -1
- package/skills/spec-format-governance/README.md +1 -1
- package/skills/spec-format-governance/SKILL.md +76 -8
- package/skills/spec-gate/SKILL.md +18 -2
- package/skills/spec-gate/scripts/check-spec-state.mts +47 -11
- package/skills/spec-gate/scripts/check-suite.mts +53 -17
- package/skills/spec-gate/scripts/classify-edit-class.mts +10 -7
- package/skills/spec-producer-governance/README.md +1 -1
- package/skills/spec-producer-governance/SKILL.md +7 -3
- package/skills/ssa-lowering/README.md +3 -5
- package/skills/start-mission/README.md +1 -1
- package/skills/start-mission/SKILL.md +9 -5
- package/skills/suite-format-governance/SKILL.md +43 -4
- package/skills/touch-set-correction/README.md +3 -5
- package/skills/touch-set-correction/scripts/touch-set-correction.mts +7 -5
- package/skills/verify-scenarios/SKILL.md +10 -3
- package/skills/verify-scenarios/scripts/verify-scenarios.mts +92 -8
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 unional
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/agents/sdd-automaton.md
CHANGED
|
@@ -75,6 +75,17 @@ your position **from the artifacts** — `spec.md`, the `.feature`, frontmatter,
|
|
|
75
75
|
survived. The plan brief is the portable handoff: read it on entry, update todo statuses and the
|
|
76
76
|
`## NEXT` anchor as you go, so the next segment (yours or an in-session resume) picks up clean.
|
|
77
77
|
|
|
78
|
+
**At finalize, reconcile the brief — do not rely on having kept it current.** When a mission
|
|
79
|
+
**lands**, set every todo to its true terminal state and rewrite `## NEXT` to say what landed, naming
|
|
80
|
+
no remaining resume action, **in the same change as the work**. This is a backstop, so it runs in one
|
|
81
|
+
pass over the whole brief even if nothing updated it mid-flight — the case a stateless segment is
|
|
82
|
+
most likely to hit. Reconcile means *to the landed state*, never *mark everything done*: a todo whose
|
|
83
|
+
work was held out of scope stays un-completed and rides the follow-up record instead. Write the brief
|
|
84
|
+
and nothing else — no `spec.md` field, and **no terminal value in the plan-level `status` dispatch
|
|
85
|
+
flag** (it stays `active | approved`; terminal-ness is derived). A mission that **halts** is
|
|
86
|
+
checkpointed at its true in-progress state, not reconciled as landed. Full rule: `start-mission`'s
|
|
87
|
+
"Plan-brief durability" bullet.
|
|
88
|
+
|
|
78
89
|
## Spawn depth
|
|
79
90
|
|
|
80
91
|
You realize the conductor's spawns (the impl-producer builder, the cold spec-judge, the cold impl-judge)
|
|
@@ -93,5 +104,5 @@ depth-2 behavior described here.
|
|
|
93
104
|
when a capability is available, prefer its **warm** unit over a cold one-shot, else fall back to a
|
|
94
105
|
portable cold subagent. context-clear a warm judge (`npx cyberlegion@<version> unit clear <ref>`) to a fresh context before **each** judgment; a warm
|
|
95
106
|
impl-producer builder **keeps** its context across the mission. Reset or tear down every warm unit at
|
|
96
|
-
handoff. Full model: `start-mission`'s "Dispatch transport" note and
|
|
97
|
-
|
|
107
|
+
handoff. Full model: `start-mission`'s "Dispatch transport" note and the `design/harness-spawning`
|
|
108
|
+
node of the SDD project spec (repo-only).
|
package/agents/sdd-scanner.md
CHANGED
|
@@ -37,6 +37,26 @@ count** maintained in the ledger.
|
|
|
37
37
|
strategy is draftable from it **alone** for every categorical dimension. Raw `.jsonl` transcripts
|
|
38
38
|
are **optional enrichment**, never the contract — depending on a harness-specific transcript
|
|
39
39
|
format would couple doctrine to a harness, and the transcripts may be **absent post-merge**.
|
|
40
|
+
- **Validate before drafting — a plan/log is a hypothesis, not present truth.** Every candidate
|
|
41
|
+
improvement a persisted plan or combat log surfaces is a **hypothesis about the current codebase**,
|
|
42
|
+
not a fact (*a source-read is a hypothesis, refuted by a repro against current code*). **Before you
|
|
43
|
+
draft**, read the CURRENT code and decide: does the flagged gap still exist, or has it been
|
|
44
|
+
**built / fixed / superseded** since? A candidate current code already resolves is **cut** — draft
|
|
45
|
+
no build-or-fix strategy, emit no issue, and record the cut durably as a `disposition: resolved`
|
|
46
|
+
tombstone carrying the resolving current-code evidence (below). A still-open candidate is drafted
|
|
47
|
+
(`disposition: open`) and emitted as an issue. This gate stops you reinforcing a **stale cache** —
|
|
48
|
+
drafting "build X" for an X a later mission already built. A pure retro **lesson** (not a gap claim)
|
|
49
|
+
carries no gap and needs no check.
|
|
50
|
+
- **Cold-instrument evidence — non-author measurement + ablation before a rule-level recommendation.**
|
|
51
|
+
Draft a recommendation to **adopt a rule, drop a rule, or set a threshold** only when its grounding
|
|
52
|
+
measurement was **produced by, or independently reviewed by, a party other than the proposer, or
|
|
53
|
+
ablated against a freshly and adversarially constructed case** — a measurement on the proposer's
|
|
54
|
+
**own generator/harness alone is withheld** (draft nothing; record that it needs non-author or
|
|
55
|
+
fresh-adversarial evidence). Draft a candidate **reviving a dead measurement dimension** to land only
|
|
56
|
+
when an ablation against a control shows it **loseable**, stating the revived rule **abstractly with
|
|
57
|
+
no worked example** (a lifted probe apparatus is absorption); a Δ=0 revival is **cut as dead weight**.
|
|
58
|
+
A measurement grounding no rule-level decision is unconstrained. (Canonical statement:
|
|
59
|
+
`sdd:doctrine-loop`.)
|
|
40
60
|
- **Detect and draft cheaply and continuously; never block.** You draft strategy without blocking
|
|
41
61
|
any mission in progress — drafting is off the mission's critical path. You **accumulate** strategy
|
|
42
62
|
and surface it **episodically** (a retro, on demand, or when pending strategy piles up at the
|
|
@@ -57,6 +77,18 @@ count** maintained in the ledger.
|
|
|
57
77
|
distinct from the cross-referenced cr-refs in `evidence`. It is the hook `sdd:plan-retirement` keys
|
|
58
78
|
on to confirm a plan was distilled before deleting its combat log. Milestone / drift / token-waste
|
|
59
79
|
strategy has no single subject mission and **omits** `distills`.
|
|
80
|
+
- **Idempotent Ship/Kill — detect an already-distilled mission by parsing the ledger, never grepping.**
|
|
81
|
+
Because you can meet the same terminal transition more than once, the Ship/Kill triggers are
|
|
82
|
+
idempotent: **before drafting**, detect whether the mission was **already distilled** (a prior
|
|
83
|
+
`strategy` entry whose `distills` equals its `<cr-ref>`) and if so **draft nothing**. Decide this by a
|
|
84
|
+
**structured JSONL parse, never a substring/regex text match** — **reuse** the exported
|
|
85
|
+
`distilledCrRefs` engine from `../skills/plan-retirement/scripts/retire-plans.mts` over the project
|
|
86
|
+
`ledger/` directory and membership-test the `<cr-ref>`. It parses each line with `JSON.parse`, so it
|
|
87
|
+
is correct against a **pretty-printed** entry (a space after the colon), a wrapped or reordered
|
|
88
|
+
object — a naive no-space grep (`"distills":"<cr-ref>"`) silently under-counts and re-drafts an
|
|
89
|
+
already-distilled mission (the *grep is blind to wrapped terms* defect class). Reuse inherits its
|
|
90
|
+
semantics: keys on the structured `distills` field only (an `evidence`-only cross-ref never counts),
|
|
91
|
+
tolerates malformed/blank lines, and counts an unratified distilling entry the same as a ratified one.
|
|
60
92
|
- **Detection is yours; keep-or-cut is the Council's.** You detect and draft; the human Council
|
|
61
93
|
holds keep-or-cut. Ratified strategy re-enters as a CR that re-tunes the **doctrine** and grows
|
|
62
94
|
the **corpus** (skills, governances, conventions); unratified strategy does neither.
|
|
@@ -81,6 +113,29 @@ re-scan of many missions' raw logs (those are deleted with each plan at retro).
|
|
|
81
113
|
by `cause`** (the matchable field owned by `combat-log-governance`). A `cause` recurring across the
|
|
82
114
|
corpus is the pattern — draft a strategy to codify it, carrying the recurrence count as its evidence.
|
|
83
115
|
|
|
116
|
+
## Validate-before-draft, the cut disposition, and issue emission
|
|
117
|
+
|
|
118
|
+
Run the validation gate on each candidate a plan or log surfaces, then record and emit:
|
|
119
|
+
|
|
120
|
+
- **The cut disposition (`open | resolved`).** A drafted still-open improvement is `disposition: open`
|
|
121
|
+
(the default; a legacy line without the field grandfathers as open) and **counts toward pending
|
|
122
|
+
strategy**. A cut is `disposition: resolved` — a **tombstone** carrying the resolving current-code
|
|
123
|
+
evidence in `evidence`, emitting **no** issue and **not counted** toward pending strategy (the
|
|
124
|
+
gateway excludes it). Set the disposition **once at write**, never flip it (append-only). The field's
|
|
125
|
+
shape is owned by `combat-log-governance`; the gateway's pending count is `kind: strategy`,
|
|
126
|
+
`ratified: false`, `disposition: open`-or-absent.
|
|
127
|
+
- **Emit each validated-open improvement as a tracked issue** (`gh issue create`) — one titled, bodied
|
|
128
|
+
issue per real improvement, cross-linking its evidence. The issue is the **actionable output**; the
|
|
129
|
+
`strategy` line stays the **provenance**. **Dedupe first** against the forge's existing issues (open
|
|
130
|
+
and closed, ≥2 keyword combinations); on a mixed set file only the unmatched.
|
|
131
|
+
- **Emit is not dispatch.** Emitting an issue leaves the strategy **unratified** and spawns **no**
|
|
132
|
+
mission — it opens no CR and admits nothing to the mission graph. Keep-or-cut stays the Council's; a
|
|
133
|
+
filed issue re-enters SDD only when a **later** mission is started from it.
|
|
134
|
+
- **Outward-publish floor.** Compose the issue body to the same floor the handoff follow-up issues
|
|
135
|
+
meet (owned by the handoff unit, not restated here): **self-contained**, **no production-internal
|
|
136
|
+
artifact reference**, plus the committed-record bans; carry an **agent-filed marker** and name the
|
|
137
|
+
evidence it was distilled from.
|
|
138
|
+
|
|
84
139
|
## Drift / staleness
|
|
85
140
|
|
|
86
141
|
Detect a convention in the doctrine that is **now false**, or a contradiction between governances.
|
|
@@ -114,6 +169,36 @@ the Council re-enters — that is how detection meets keep-or-cut:
|
|
|
114
169
|
|
|
115
170
|
You neither ratify nor prune the corpus yourself — both are the Council's positional act.
|
|
116
171
|
|
|
172
|
+
## Stale plan frontmatter — deriving the retirement clearance set, never autofixing status
|
|
173
|
+
|
|
174
|
+
During your pass, for each brief under `.agents/plans/`, cross-check two independent signals — this
|
|
175
|
+
is separate from strategy-drafting above; you draft nothing and write nothing to the ledger for it:
|
|
176
|
+
|
|
177
|
+
- **`todos-all-done`** — every `todos[].status` in the brief's frontmatter reads `completed`.
|
|
178
|
+
- **`source-closed`** — the brief's declared `source` queried natively, the same way
|
|
179
|
+
`plan-retirement`'s own clearance check queries it (`github-NN` → GH issue, `asana-<gid>` →
|
|
180
|
+
Asana, `local-<slug>` → the local store).
|
|
181
|
+
|
|
182
|
+
- **Both agree terminal** → include the brief's cr-ref in the **retirement clearance set** you pass
|
|
183
|
+
as `plan-retirement`'s existing `--retire` clearance-set input. This is not a new deletion
|
|
184
|
+
mechanism — `plan-retirement` still runs its own gated, idempotent sweep (presence +
|
|
185
|
+
distilled-or-no-log) before anything leaves the tree; you are only supplying its `--retire` set,
|
|
186
|
+
cross-checked rather than source-only.
|
|
187
|
+
- **Both agree non-terminal** → leave the brief alone; no clearance, no finding.
|
|
188
|
+
- **The two signals disagree** (source closed but the brief's own todos are not all done, or the
|
|
189
|
+
reverse) → do **not** autofix anything, and do **not** include the cr-ref in the clearance set.
|
|
190
|
+
Name the brief's cr-ref and the disagreement in your **pass summary** — the only channel you
|
|
191
|
+
have, since you return only your final message. A disagreement is **never** a `kind: strategy`
|
|
192
|
+
entry (it names no doctrine improvement) and **never** a `kind: report` ledger line (reserved to
|
|
193
|
+
the conductor and the gate; you still never write `report` / `correction` / `gate`) — it is a
|
|
194
|
+
distinct, ephemeral finding, re-derived every pass from the two cheap signals above, never
|
|
195
|
+
persisted, and never conflated with the validated-open-improvement finding that becomes a
|
|
196
|
+
tracked issue.
|
|
197
|
+
- There is **no legal terminal value** for a plan brief's `status` field to autofix into — the
|
|
198
|
+
contract's own answer to "this mission is over" is retirement (a tracked deletion), not a status
|
|
199
|
+
flag (`design/provenance-model.md` reserves the plan-level `status` to the two-value dispatch
|
|
200
|
+
flag `active | approved`). You never write a plan brief's `status`.
|
|
201
|
+
|
|
117
202
|
## Boundaries
|
|
118
203
|
|
|
119
204
|
You own the **process** only. Route out-of-loop requests: a build-or-deprecate request → the
|
package/agents/sdd-spec-judge.md
CHANGED
|
@@ -69,6 +69,32 @@ before reading spec.md for content:
|
|
|
69
69
|
3. **A superset raises no finding.** A declared set covering every expected governance — with or
|
|
70
70
|
without extras — passes; report `PREFLIGHT: { result: pass }` and proceed to the lenses below.
|
|
71
71
|
|
|
72
|
+
## Spec-format conformance read — a non-blocking warning
|
|
73
|
+
|
|
74
|
+
`sdd:spec-format-governance` (a fixed-universal you already loaded) sets the required sections of a
|
|
75
|
+
**behavioral** `spec.md`: `## What`, `## Use Cases`, `## Control Flow` (the control-flow graph,
|
|
76
|
+
**CFG**), and `## Scenario map`. Read that bar backward here: for each touched **behavioral**
|
|
77
|
+
`spec.md`, check those sections are present and **emit a conformance warning naming any that are
|
|
78
|
+
missing** — **especially `## Use Cases`, `## Control Flow` (CFG), and `## Scenario map`**, the three
|
|
79
|
+
a backfill most often skips.
|
|
80
|
+
|
|
81
|
+
- **Behavioral only — key off `spec-type`, never a blind heading scan.** A `reference` node carries
|
|
82
|
+
`## Subject` in place of the four sections and a `descriptive` index carries none, so **neither
|
|
83
|
+
raises a conformance warning**: check a node's `spec-type` first and read the required sections
|
|
84
|
+
only for a behavioral one. A reference/descriptive node's result is always `pass`.
|
|
85
|
+
- **A warning, not a lens failure.** The conformance read is **distinct from the three lenses and
|
|
86
|
+
from the deterministic checks**. A missing section is a `warn`, surfaced for the gate to report —
|
|
87
|
+
it does **not** fail a lens, does **not** set `ALIGNED: false`, and does **not** short-circuit the
|
|
88
|
+
lenses the way a failed `PREFLIGHT` does. Grade the lenses regardless.
|
|
89
|
+
- **The normal-flow overlap.** In the gate's normal flow a behavioral node missing `## Use Cases` is
|
|
90
|
+
already caught by the deterministic `check-spec-state` fail-closed **before you are spawned**, so
|
|
91
|
+
the warning's **load-bearing** contribution is the CFG and the scenario map — but name `## Use
|
|
92
|
+
Cases` too whenever you do read a behavioral `spec.md` that lacks it.
|
|
93
|
+
|
|
94
|
+
Carry the result on the `CONFORMANCE` output field: `{ result: pass | warn, missing: [ <each
|
|
95
|
+
required behavioral spec-format section absent from a touched behavioral spec.md> ] }`. All sections
|
|
96
|
+
present (or a reference/descriptive node) ⇒ `{ result: pass, missing: [] }`.
|
|
97
|
+
|
|
72
98
|
## Split the work
|
|
73
99
|
|
|
74
100
|
- **Optional deterministic step** — two NodeJS static-analysis CLIs for the mechanical checks
|
|
@@ -207,6 +233,7 @@ narrowing that fires **Clearance**, so never demand it of one.
|
|
|
207
233
|
```
|
|
208
234
|
STATUS: complete | needs-input | blocked
|
|
209
235
|
PREFLIGHT: { result: pass | fail, finding-kind: governance-preflight-missing | null, missing: [ ... ] }
|
|
236
|
+
CONFORMANCE: { result: pass | warn, missing: [ <required behavioral spec-format sections absent — e.g. Use Cases, Control Flow, Scenario map> ] }
|
|
210
237
|
LENS: { oracle: pass | fail, builder: pass | fail, architect: pass | fail }
|
|
211
238
|
ALIGNED: true | false # false ⇒ which artifacts are out of sync
|
|
212
239
|
SCENARIOS_PASSING: [ titles ]
|
|
@@ -218,7 +245,10 @@ OBSERVATIONS: [ { owner: architect | strategist, note, evidence } ]
|
|
|
218
245
|
```
|
|
219
246
|
|
|
220
247
|
`PREFLIGHT.result: fail` short-circuits everything below it — `LENS` is omitted, `ALIGNED` is `false`,
|
|
221
|
-
and `BLOCKER` names the missing governances (see "Governance pre-flight check" above).
|
|
222
|
-
`
|
|
248
|
+
and `BLOCKER` names the missing governances (see "Governance pre-flight check" above).
|
|
249
|
+
`CONFORMANCE.result: warn` **short-circuits nothing** — the lenses still run, and it never on its own
|
|
250
|
+
sets `ALIGNED: false` or blocks the advance (see "Spec-format conformance read" above); the gate
|
|
251
|
+
surfaces it as a warning. Otherwise `ALIGNED` is `true` only when all three lenses pass and no open
|
|
252
|
+
marker remains. The conductor
|
|
223
253
|
synthesizes the gate verdict and the leash from this rollup — never advance with any lens failing,
|
|
224
254
|
any open marker, a failed preflight, or `ALIGNED: false`.
|
package/agents/sdd-warden.md
CHANGED
|
@@ -40,6 +40,15 @@ marker shape you leave — their fields and schema are owned there; never restat
|
|
|
40
40
|
a scenario-overlap candidate you judge (`@rubric`) whether it is **real** behavioral overlap and
|
|
41
41
|
**assign a single owning node** (one behavior = one scenario in one node). A finding **names** the
|
|
42
42
|
nodes or artifacts it concerns.
|
|
43
|
+
- **Check a split's organizing axis, not just its granularity.** An oversized node is a granularity
|
|
44
|
+
signal, but a split carves it along a proposed **organizing axis** — and the wrong axis produces a
|
|
45
|
+
split CR that gets superseded rather than landed. Before you propose a split (self-clear it or
|
|
46
|
+
escalate its CR), **check the proposed axis against a real capability/command boundary**: each side
|
|
47
|
+
must map to a **distinct capability, command, or lifecycle phase**, not merely an internal
|
|
48
|
+
implementation grouping sharing one boundary. An axis that passes → the split is **proposed on that
|
|
49
|
+
axis**; an axis that only regroups internal implementation → **fails**, so you do **not** carve
|
|
50
|
+
sub-nodes on it and instead raise the oversize as a **wrong-axis reorganization**. An oversize can
|
|
51
|
+
be a symptom of the wrong axis, not just wrong granularity.
|
|
43
52
|
- **Judge against the declared strategy.** Read each project spec's root `spec.md` **placement map**
|
|
44
53
|
for the layout strategy it chose, and judge structural fit against *that*, never a default or the
|
|
45
54
|
shape the tree happens to have. Inferring the strategy makes the audit circular. A map naming no
|
package/package.json
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
2
|
+
"name": "cyber-sdd",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Spec-Driven Development. Scaffold, validate, and maintain behavioral specs (spec.md + .feature files) for software features.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/cyberuni/cyberplace.git",
|
|
8
|
+
"directory": "plugins/sdd"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"bin": {
|
|
13
|
+
"sdd-check-specs": "./skills/check-project-specs/scripts/check-project-specs.mts"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"skills",
|
|
17
|
+
"agents",
|
|
18
|
+
".plugin",
|
|
19
|
+
".codex-plugin",
|
|
20
|
+
".claude-plugin",
|
|
21
|
+
"!skills/**/*.test.mts"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"gherkin-cli": "0.0.2"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"check:spec": "sdd-check-specs",
|
|
28
|
+
"test": "node --test \"skills/*/scripts/*.test.mts\"",
|
|
29
|
+
"typecheck": "tsc -p tsconfig.json"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
// No dependencies (the repo's node-≥23.6 / no-deps convention). Pure functions are exported for
|
|
25
25
|
// node:test; running the file directly drives the CLI.
|
|
26
26
|
|
|
27
|
-
import { existsSync, readdirSync } from 'node:fs'
|
|
27
|
+
import { existsSync, readdirSync, realpathSync } from 'node:fs'
|
|
28
28
|
import { isAbsolute, join, relative } from 'node:path'
|
|
29
|
+
import { pathToFileURL } from 'node:url'
|
|
29
30
|
import { type NodeRecord, scanProjectSpec } from '../../check-spec-structure/scripts/check-spec-structure.mts'
|
|
30
31
|
import { classifyFile } from '../../spec-gate/scripts/classify-edit-class.mts'
|
|
31
32
|
|
|
@@ -182,6 +183,6 @@ export function main(argv: string[]): number {
|
|
|
182
183
|
return 0
|
|
183
184
|
}
|
|
184
185
|
|
|
185
|
-
if (import.meta.url ===
|
|
186
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
|
|
186
187
|
process.exit(main(process.argv.slice(2)))
|
|
187
188
|
}
|
|
@@ -22,6 +22,7 @@ asked of the implementation instead of the spec.
|
|
|
22
22
|
| **Placement matches the declared layout** | The project declares its layout strategy in its root `spec.md` placement map; placement is judged *within* that declaration, not against a preferred one. Under the screaming-architecture default a capability lives in a folder named for its intent; a project that declared `mirror-source` is correctly placed when it mirrors its source. |
|
|
23
23
|
| **…and the layout preserves the partition** | The declaration is not a licence. Layouts are ranked by whether they keep node ↔ capability one-to-one, because the mission scheduler cuts one mission per node and a scattered capability degrades the schedule toward serial (ADR-0025). **One capability per node, never smeared across nodes** holds under every strategy, and a layered / framework-first top level stays discouraged however it is declared. |
|
|
24
24
|
| **A well-formed CFG** | The capability's control-flow graph connects — every decision reachable, no dangling branch — and the suite's sections mirror it. |
|
|
25
|
+
| **The CFG reaches every stated extension** | A use case's **extensions** are its divergence paths (`spec-format-governance`), so each is an edge the graph must actually contain. An extension named in `## Use Cases` with no path to it in `## Control Flow` is a **dangling branch read from the other side** — the prose claims a divergence the drawn graph cannot take. A **forbidden combination** is the same defect in guard form: the graph carries the decision that refuses the pair, or the constraint is unenforceable and the prose is decoration. Judge it **both ways** — an edge with no extension is the ordinary uncovered-edge case; an extension with no edge is this one. |
|
|
25
26
|
| **An orthogonal axis** | Structural fit judges a property the builder was not optimizing — a real independent check even from the same hand. |
|
|
26
27
|
| **Structural concerns are deferred** | A structural problem in *another* capability is an observation that spawns a new spec — never a marker in the one being built. |
|
|
27
28
|
|
|
@@ -37,6 +37,15 @@ unbound.
|
|
|
37
37
|
level stays discouraged however it is declared.
|
|
38
38
|
- **A well-formed CFG.** Its control-flow graph connects — every decision reachable, no
|
|
39
39
|
dangling branch — and the suite's sections mirror it.
|
|
40
|
+
- **The CFG reaches every stated extension.** A use case's **extensions** are its divergence paths
|
|
41
|
+
(`sdd:spec-format-governance`), so each is an edge the graph must actually contain. An extension
|
|
42
|
+
named in `## Use Cases` with no path to it in `## Control Flow` is a **dangling branch read from
|
|
43
|
+
the other side** — the prose claims a divergence the drawn graph cannot take. A **forbidden
|
|
44
|
+
combination** of surface elements is the same defect in guard form: if two elements may not be
|
|
45
|
+
combined, the graph carries the decision that refuses them, or the constraint is unenforceable and
|
|
46
|
+
the prose is decoration. Judge the graph against the stated extensions in **both** directions —
|
|
47
|
+
an edge with no extension is the ordinary uncovered-edge case; an extension with no edge is this
|
|
48
|
+
one.
|
|
40
49
|
- **An orthogonal axis.** Structural fit judges a property the builder was not optimizing — a real
|
|
41
50
|
independent check even from the same hand.
|
|
42
51
|
- **Structural concerns are deferred.** A structural problem in another capability is an observation
|
|
@@ -55,5 +64,7 @@ from `spec.md` + the suite only — the solution is out of view (grader independ
|
|
|
55
64
|
boundaries.
|
|
56
65
|
2. **Placement matches the *declared* layout** (`sdd:spec-structure-governance`), not a preferred
|
|
57
66
|
one; one capability per node either way, never smeared across nodes.
|
|
58
|
-
3. **A well-formed CFG** the suite's sections mirror
|
|
67
|
+
3. **A well-formed CFG** the suite's sections mirror — and it reaches every stated extension;
|
|
68
|
+
an extension with no edge is a dangling branch read from the prose side, a forbidden combination
|
|
69
|
+
with no guard is unenforceable.
|
|
59
70
|
4. **Structural concerns in another capability are deferred** — an observation that spawns a new spec.
|
|
@@ -24,11 +24,9 @@ node scripts/blast-estimate.mts --root <corpus> --touch-set sdd/mission-graph,sd
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Read-only, deterministic, reports-never-writes — the mission-graph's single writer records the
|
|
27
|
-
computed level. See [`SKILL.md`](./SKILL.md) for the full contract
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
[`blast-estimate.feature`](../../../../.agents/specs/sdd/blast-estimate/blast-estimate.feature) for
|
|
31
|
-
the frozen 21-scenario contract.
|
|
27
|
+
computed level. See [`SKILL.md`](./SKILL.md) for the full contract; the `blast-estimate` node of
|
|
28
|
+
the SDD project spec (in the cyberplace repository, not shipped in this package) carries the
|
|
29
|
+
authoritative behavior description and the frozen 21-scenario contract.
|
|
32
30
|
|
|
33
31
|
Fan-in counts the reference forms the corpus really uses — the bare id, a `sdd:spec-gate` skill ref,
|
|
34
32
|
a path under any **declared root** at any depth, and a same-project relative link — not the bare id
|
|
@@ -12,8 +12,8 @@ The concrete engine for **blast-estimate** — a read-only derivation that works
|
|
|
12
12
|
project a Mission could disturb, instead of trusting the hand-typed guess. Given a Mission's
|
|
13
13
|
**touch-set** (the work areas it names) and the project corpus, it computes a **blast** level
|
|
14
14
|
(`low` / `medium` / `high`) from three measured inputs and lines that level up against the Mission's
|
|
15
|
-
hand-typed **declared** blast (`agrees` / `under-called` / `over-called`).
|
|
16
|
-
|
|
15
|
+
hand-typed **declared** blast (`agrees` / `under-called` / `over-called`). The `blast-estimate` node
|
|
16
|
+
of the SDD project spec (repo-only) carries the full contract and vocabulary.
|
|
17
17
|
|
|
18
18
|
## The three inputs
|
|
19
19
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// blast-estimate — the concrete engine for blast-estimate's derivation: compute a Mission's BLAST
|
|
3
3
|
// (low/medium/high) from its touch-set + the project corpus instead of trusting the hand-typed guess,
|
|
4
|
-
// then line the computed level up against the declared one (agrees / under-called / over-called).
|
|
5
|
-
//
|
|
4
|
+
// then line the computed level up against the declared one (agrees / under-called / over-called). The
|
|
5
|
+
// blast-estimate node of the SDD project spec (repo-only) carries the full contract this mirrors.
|
|
6
6
|
//
|
|
7
7
|
// Three inputs, each measured — never inferred — from the corpus:
|
|
8
8
|
// - count — how many of the touch-set's areas resolve to a known work area
|
|
@@ -32,8 +32,9 @@
|
|
|
32
32
|
// mission-graph's single writer records it. Pure functions are exported for node:test; running the
|
|
33
33
|
// file directly drives the CLI.
|
|
34
34
|
|
|
35
|
-
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
35
|
+
import { readdirSync, readFileSync, realpathSync, statSync } from 'node:fs'
|
|
36
36
|
import { join, relative } from 'node:path'
|
|
37
|
+
import { pathToFileURL } from 'node:url'
|
|
37
38
|
import {
|
|
38
39
|
discoverLayouts,
|
|
39
40
|
fileToNode,
|
|
@@ -580,4 +581,6 @@ export function main(argv: string[]): number {
|
|
|
580
581
|
return result.error ? 1 : 0
|
|
581
582
|
}
|
|
582
583
|
|
|
583
|
-
if (import.meta.
|
|
584
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
|
|
585
|
+
process.exit(main(process.argv.slice(2)))
|
|
586
|
+
}
|
|
@@ -25,6 +25,13 @@ unbound.
|
|
|
25
25
|
paths that matter, **boundary** (the external mocked) as the honest substitute where e2e is
|
|
26
26
|
infeasible or unsafe. **Record the level and why.** The suite's overall pyramid shape is the
|
|
27
27
|
architect's call (`sdd:architect-impl-governance`).
|
|
28
|
+
- **Instrument subject → mutation-sweep-first.** When the **subject is itself a measurement or
|
|
29
|
+
verification instrument** — a fixture, mutation set, ablation generator, falsifier, judge, or check —
|
|
30
|
+
a **mutation sweep is the default verification method** and reading is **supplementary**. This
|
|
31
|
+
**overrides the verify-as-high default above for an instrument subject only**; a non-instrument
|
|
32
|
+
subject keeps that default. Reading an instrument's own blind spot back to itself is a weak oracle —
|
|
33
|
+
a "cannot-fail" defect survives every read and dies to the first mutation the instrument fails to
|
|
34
|
+
catch (the cold-instrument doctrine, stated in full on the impl-producer node).
|
|
28
35
|
- **A graded subject still yields a boolean.** Reach the per-scenario boolean through a rubric +
|
|
29
36
|
threshold over N runs; the rubric stays out of the `.feature`.
|
|
30
37
|
- **No green-by-tampering.** Passing means the behavior holds, not that a check was edited to pass;
|
|
@@ -40,7 +47,8 @@ unbound.
|
|
|
40
47
|
1. **The bar is not self-set** — checks derive from the frozen suite, one per scenario; the judge
|
|
41
48
|
re-derives the oracle independently.
|
|
42
49
|
2. **Verify as high as it doesn't hurt** — cheap base, thin e2e cap, boundary as substitute where e2e
|
|
43
|
-
is infeasible/unsafe; record level and why.
|
|
50
|
+
is infeasible/unsafe; record level and why. **An instrument subject inverts this: mutation-sweep-first,
|
|
51
|
+
reading supplementary.**
|
|
44
52
|
3. **No green-by-tampering** — passing is the behavior holding, never an edited check or a modified
|
|
45
53
|
suite.
|
|
46
54
|
4. **Deterministic combinatorics go to units** (the pyramid base); missing that coverage withholds the
|
|
@@ -19,6 +19,7 @@ contract?" at the impl gate.
|
|
|
19
19
|
| Requirement | What it means |
|
|
20
20
|
| --- | --- |
|
|
21
21
|
| **Every branch is covered** | Each edge of the capability's CFG has its scenario, and every guard/negative edge is paired with a positive companion. The scenario map is 1:1 in both directions — no orphan scenario, no uncovered edge. |
|
|
22
|
+
| **Every stated extension is a path in the CFG** | A use case's **extensions** are its divergences (`spec-format-governance`), and the CFG is the **single source** scenarios derive from — so an extension earns its scenario by being a path in the graph, never as a second rule beside the edge coverage above. An extension with no path is a hole in the *graph*: fix it there, and the 1:1 edge coverage supplies the scenario. Never derive a scenario from the prose directly — a suite drawn from a stated list is 1:1 with that list by construction and can no longer surface a hole. `extensions: none` is a claim to judge; a forbidden combination is the same rule in guard form. |
|
|
22
23
|
| **Every scenario is testable** | Each scenario asserts an observable outcome a check can confirm — a boolean, no "sometimes". A behavior the capability cannot expose cannot be specced. |
|
|
23
24
|
| **A graded subject is still a boolean** | A non-deterministic capability (one whose output varies run to run) still reaches a per-scenario boolean, through a rubric plus a threshold over N runs. The rubric form stays out of the boolean `.feature`, carried as a judge-only `@rubric` scenario. |
|
|
24
25
|
|
|
@@ -19,18 +19,46 @@ plugin may bind its own, and this loads when the registry leaves `builder`/`spec
|
|
|
19
19
|
|
|
20
20
|
- **Every branch of the capability is covered.** Each edge of its control-flow graph (CFG) has its
|
|
21
21
|
scenario, and every guard/negative edge is paired with a positive companion. The **scenario map
|
|
22
|
-
is 1:1** — no orphan scenario, no uncovered edge (`sdd:suite-format-governance`).
|
|
22
|
+
is 1:1** — no orphan scenario, no uncovered edge (`sdd:suite-format-governance`). For a **fold**
|
|
23
|
+
node whose rule combines two or more interacting sub-conditions, the CFG is drawn from that rule
|
|
24
|
+
stated in **closed form** (single-condition folds may be by example — demanding closed form of one
|
|
25
|
+
is over-firing), and its coverage is backed by a **mutation sweep** and a **safety dual**
|
|
26
|
+
(`sdd:suite-format-governance`).
|
|
27
|
+
- **Every stated extension is a path in the CFG.** A use case's **extensions** are its divergences
|
|
28
|
+
(`sdd:spec-format-governance`), and the CFG is the **single source** the scenarios derive from —
|
|
29
|
+
so an extension earns its scenario **by being a path in the graph**, never as a second rule
|
|
30
|
+
alongside the edge coverage above. The check is therefore: does the CFG contain a path to each
|
|
31
|
+
stated extension? An extension with no path is a hole in the **graph** — fix it there, and the
|
|
32
|
+
standing 1:1 edge coverage supplies the scenario. **Never derive a scenario from the prose
|
|
33
|
+
directly**: a suite drawn from a stated list is 1:1 with that list by construction and can no
|
|
34
|
+
longer surface a hole, which is the retrofit shape that has diverged in this corpus before. A use
|
|
35
|
+
case declaring `extensions: none` asserts nothing can diverge — judge that claim against the
|
|
36
|
+
graph. A **forbidden combination** is the same rule in guard form: it is a decision the CFG must
|
|
37
|
+
carry, and its refusal scenario comes from that guard's edge.
|
|
23
38
|
- **Every scenario is testable.** Each asserts an observable outcome a check can confirm — a boolean,
|
|
24
39
|
no "sometimes". A behavior the capability cannot expose cannot be specced.
|
|
25
40
|
- **A graded subject is still a boolean.** For a non-deterministic capability the contract reaches a
|
|
26
41
|
per-scenario boolean through a rubric + threshold over N runs; the rubric form stays out of the
|
|
27
42
|
boolean `.feature`, carried as a judge-only `@rubric` scenario.
|
|
43
|
+
- **A dimension or cut is grounded on non-author evidence.** When a `@rubric` dimension or its cut is
|
|
44
|
+
justified by a **measurement** (an ablation Δ, a discrimination count), that measurement is admissible
|
|
45
|
+
only if it is **not solely the author's own** — independently produced/reviewed by a non-author, or a
|
|
46
|
+
fresh-adversarial ablation. An author's own instrument silently assumes the property under test. The
|
|
47
|
+
standard is stated canonically at `sdd:doctrine-loop`; this bar requires it be met, not re-listed
|
|
48
|
+
(the cold-instrument doctrine).
|
|
28
49
|
|
|
29
50
|
## Key points (read-check)
|
|
30
51
|
|
|
31
52
|
1. **Every branch of the capability is covered** — every edge has its scenario, guards paired with
|
|
32
53
|
positives, the scenario map 1:1.
|
|
33
|
-
2. **Every
|
|
54
|
+
2. **Every stated extension is a path in the CFG** — the graph is the single source scenarios derive
|
|
55
|
+
from, so an extension earns its scenario by being a path, never as a second rule alongside edge
|
|
56
|
+
coverage; a divergence with no path is a hole in the *graph*. Never derive a scenario from the
|
|
57
|
+
prose directly. `extensions: none` is a claim to judge; a forbidden combination is a guard the
|
|
58
|
+
graph carries.
|
|
59
|
+
3. **Every scenario is testable** — an observable boolean outcome; behavior the capability cannot
|
|
34
60
|
expose cannot be specced.
|
|
35
|
-
|
|
61
|
+
4. **A graded subject still reaches a per-scenario boolean** via rubric + threshold; the rubric stays
|
|
36
62
|
out of the `.feature`.
|
|
63
|
+
5. **A dimension or cut is grounded on non-author evidence** — a measurement justifying it must be not
|
|
64
|
+
solely the author's own (canonical standard: `sdd:doctrine-loop`); the cold-instrument doctrine.
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
// node:test; running the file directly drives the CLI.
|
|
21
21
|
|
|
22
22
|
import { execFileSync } from 'node:child_process'
|
|
23
|
+
import { realpathSync } from 'node:fs'
|
|
24
|
+
import { pathToFileURL } from 'node:url'
|
|
23
25
|
|
|
24
26
|
/** Below this many usable multi-file commits, a rate would be noise — report, never score. */
|
|
25
27
|
export const DEFAULT_FLOOR = 20
|
|
@@ -331,6 +333,6 @@ export function main(argv: string[], context: Context = { readHistory }): number
|
|
|
331
333
|
return 0
|
|
332
334
|
}
|
|
333
335
|
|
|
334
|
-
if (import.meta.url ===
|
|
336
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
|
|
335
337
|
process.exit(main(process.argv.slice(2)))
|
|
336
338
|
}
|
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
// Default output is TOON (the token-efficient tabular form); --format json for a flat array.
|
|
19
19
|
// --check is the CI guard: exit non-zero iff any leak is found.
|
|
20
20
|
|
|
21
|
-
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
21
|
+
import { existsSync, readdirSync, readFileSync, realpathSync } from 'node:fs'
|
|
22
22
|
import { join } from 'node:path'
|
|
23
|
+
import { pathToFileURL } from 'node:url'
|
|
23
24
|
|
|
24
25
|
export interface Leak {
|
|
25
26
|
/** Repo-relative path of the file the leak sits in. */
|
|
@@ -142,4 +143,6 @@ export function main(argv: string[]): number {
|
|
|
142
143
|
return check && leaks.length > 0 ? 1 : 0
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
if (import.meta.
|
|
146
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
|
|
147
|
+
process.exit(main(process.argv.slice(2)))
|
|
148
|
+
}
|