cyber-sdd 0.4.0 → 0.4.2
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.plugin/plugin.json +1 -1
- package/agents/sdd-automaton.md +5 -1
- package/dist/align-spec/scripts/align-spec.js +102 -0
- package/dist/check-project-specs/scripts/check-project-specs.js +311 -0
- package/dist/check-scenario-overlap/scripts/check-scenario-overlap.js +175 -0
- package/dist/check-spec-references/scripts/check-spec-references.js +285 -0
- package/dist/check-spec-structure/scripts/check-spec-structure.js +241 -0
- package/dist/concept-index/scripts/concept-index.js +181 -0
- package/dist/discover-specs/scripts/discover-specs.js +318 -0
- package/dist/spec-gate/scripts/check-spec-state.js +420 -0
- package/dist/spec-gate/scripts/check-suite.js +317 -0
- package/dist/spec-gate/scripts/classify-edit-class.js +263 -0
- package/dist/spec-gate/vendor/gherkin-cli.js +6369 -0
- package/license +21 -0
- package/package.json +12 -3
- package/skills/check-project-specs/scripts/check-project-specs.mts +13 -2
- package/skills/concept-index/scripts/concept-index.mts +2 -2
- package/skills/spec-gate/README.md +9 -0
- package/skills/spec-gate/scripts/check-suite.mts +3 -2
- package/skills/spec-gate/scripts/classify-edit-class.mts +7 -7
- package/skills/spec-gate/vendor/gherkin-cli.d.mts +2 -0
- package/skills/spec-gate/vendor/gherkin-cli.mjs +6545 -0
- package/skills/start-mission/SKILL.md +1 -0
- package/skills/touch-set-correction/README.md +5 -0
- package/skills/touch-set-correction/scripts/touch-set-correction.mts +7 -7
- package/skills/verify-scenarios/README.md +6 -1
- package/skills/verify-scenarios/scripts/verify-scenarios.mts +2 -2
|
@@ -144,6 +144,7 @@ Once landed, **do not spawn** the formation Warden. Surface a **one-line nudge**
|
|
|
144
144
|
## Autonomy, provenance, and the hard floor (baked in)
|
|
145
145
|
|
|
146
146
|
- **Dispatch transport.** Every spawn beyond this session states a **dispatch intent** — role, brief, expected verdict schema — never a pinned command. When a harness-agnostic dispatch capability is available (detected at runtime; the concrete case is the Legate's `dispatch-governance` composing `cyberlegion` primitives — `agent resolve` + `unit spawn` + `mail await` — with **no** `dispatch` CLI verb, the seam named in the SDD project spec's `design/harness-spawning` node, repo-only), route through its intent seam and let it pick `subagent | channel | run-inline`, **preferring a warm unit** over a cold one-shot spawn; with no capability present, fall back to the portable cold subagent (depth-1) default — grader independence intact either way. **Warmth is a property of the unit/process; coldness of the context**: a judge's fresh-context guarantee (ADR-0016) is transport-agnostic — satisfied by a newly spawned cold subagent **or** a warm unit **context-cleared** to a fresh context before **each** judgment (re-deriving its oracle, carrying none of a prior round's context). Clear a warm unit with **`npx cyberlegion@0.3.1 unit clear <ref>`** (`<ref>` = unit id / handle / worktree branch or CR ref) — it injects the harness's own fresh-context command (`/clear` on Claude/Codex/Copilot, `/new-chat` on Cursor; fail-loud on a harness with no honest reset) so the **pane stays warm** while the **context goes cold**; it tears nothing down. The **impl-producer builder** instead stays warm and **keeps** its context across the explore spikes and the deliver build (never cleared between those uses). Warm units stay warm for **one mission** — reused within it, then **`unit clear`**'d or torn down at **handoff**, never carrying this mission's context into the next.
|
|
147
|
+
- **SDD's own judges go to the seam by file, not by name.** A dispatch capability may resolve a definition by name only in the project's own agent folder (cyberlegion's `agent resolve` does), so it cannot find one SDD ships. When the judge role resolves to SDD's own `sdd-spec-judge` or `sdd-impl-judge`, locate the definition yourself at **`agents/<name>.md` under the SDD plugin root** — two levels above this skill's own base directory (`<skill dir>/../../agents/<name>.md`) — and hand the capability **that path** (cyberlegion: `agent resolve --file <path>`, `unit spawn --agent-file <path>`), never the bare name. If no file is there, send the capability **no request for that judge** (a by-name one would miss) and take the no-capability route: spawn it as a portable cold subagent through the harness's own plugin-agent spawn (`sdd:sdd-spec-judge` / `sdd:sdd-impl-judge`), which knows the plugin. A plugin-delegated judge is outside this rule.
|
|
147
148
|
- **Initial strategy** (run start): assess blast radius + the other dimensions and emit a run-level `kind: leash` block to **your own ledger shard** (`ledger/<cr-ref>.<hash>.jsonl` — mint `<hash>` as 6 random hex **once per session** and reuse it for every line you append; `sdd:combat-log-governance`) — `leash` (`auto-none | auto-spec | auto-all`), `by: derived | user`, `approach[]`. It may be user-specified. This block is `kind: leash`, **not** `strategy` — `strategy` is the doctrine Scanner's alone. Ledger lines carry **no `ts`**.
|
|
148
149
|
- **Per-gate verdict.** At each gate, derive the leash against discovered state and either **self-assert within leash** (write `approval.<gate>: { verdict: approve, by: agent, why }`; the spec lands in the async review queue) or **stop** with a verdict packet for the human. **Never advance** when any judge fails, any open marker remains, or (at the impl gate) any frozen scenario's verification does not pass. Human ratification (`by: <name>`, advance `status`) is reserved to the in-session position holding the user channel — by default you, in-session; a headless `automaton` emits the verdict packet and stops, **even when a coordinator relays "the user approved."**
|
|
149
150
|
- **Combat log.** Append `report` / `correction` lines (and the halt that stopped you) to the plan's `*.log.jsonl` (these carry a UTC `ts`); your run-start `leash` block, self-asserted `gate` lines, and the handoff `followup` records go to **your own shard** in the durable `ledger/` directory sibling to `spec.md` — never another writer's shard, never a shared file (`strategy` there is the Scanner's alone). Free text is commit-message-grade — never code, prompts, secrets, or literal values.
|
|
@@ -12,3 +12,8 @@ and the frozen 21-scenario contract.
|
|
|
12
12
|
- **Script:** [`scripts/touch-set-correction.mts`](./scripts/touch-set-correction.mts)
|
|
13
13
|
- **Tests:** [`scripts/touch-set-correction.test.mts`](./scripts/touch-set-correction.test.mts)
|
|
14
14
|
(`node:test`) — one test per frozen scenario, titled `scenario: <verbatim frozen scenario name>`.
|
|
15
|
+
[`scripts/touch-set-correction-installed.test.mts`](./scripts/touch-set-correction-installed.test.mts)
|
|
16
|
+
runs the engine from a copy of the plugin's files, with no `node_modules`.
|
|
17
|
+
|
|
18
|
+
The engine imports gherkin-cli from the spec gate's committed bundle
|
|
19
|
+
(`../spec-gate/vendor/gherkin-cli.mjs`), so it runs from an installed plugin.
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// a live git diff or the live mission-graph store.
|
|
14
14
|
// - readChangedFiles / resolveArtifactType / changedScenarios / collectChangedFiles /
|
|
15
15
|
// discoverLayouts are the thin IO SEAM: they shell out to `git`, `resolve-governances.mts`, and
|
|
16
|
-
// the pinned `gherkin-cli
|
|
16
|
+
// the pinned `gherkin-cli` `diff` (the same differ classify-edit-class.mts uses —
|
|
17
17
|
// this tool never reimplements a differ). NOT unit-tested (binary/fs boundary) — the tested
|
|
18
18
|
// logic is everything downstream of the file list.
|
|
19
19
|
// - main() is a thin CLI: argv -> collectChangedFiles + assembleCorrection, rendering TOON by
|
|
@@ -30,7 +30,7 @@ import { execFileSync } from 'node:child_process'
|
|
|
30
30
|
import { readFileSync, realpathSync } from 'node:fs'
|
|
31
31
|
import { dirname, join, relative, resolve, sep } from 'node:path'
|
|
32
32
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
33
|
-
import {
|
|
33
|
+
import { diff as gherkinDiff, type ReadsGitDiff } from '../../spec-gate/vendor/gherkin-cli.mjs'
|
|
34
34
|
|
|
35
35
|
// ── Types ──
|
|
36
36
|
|
|
@@ -226,7 +226,7 @@ export function resolveArtifactType(path: string, root: string, cwd: string): st
|
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
// `
|
|
229
|
+
// `diff`'s default reader resolves paths against `process.cwd()` and derives git's own cwd
|
|
230
230
|
// from that resolved location (`git ls-files --full-name` to recover the repo-relative path) —
|
|
231
231
|
// this reader is that same algorithm, re-pointed at `cwd` (same seam classify-edit-class.mts
|
|
232
232
|
// uses), so it stays robust to a caller whose relative-path bookkeeping doesn't line up with its
|
|
@@ -234,7 +234,7 @@ export function resolveArtifactType(path: string, root: string, cwd: string): st
|
|
|
234
234
|
// reads as "absent" — the outer `changedScenarios` catch-all is this call site's real fail-open
|
|
235
235
|
// boundary, so a thrown `GitError` here is caught there rather than escalated.
|
|
236
236
|
const cwdReader =
|
|
237
|
-
(cwd: string):
|
|
237
|
+
(cwd: string): ReadsGitDiff['readDiff'] =>
|
|
238
238
|
(file, base) => {
|
|
239
239
|
const abs = resolve(cwd, file)
|
|
240
240
|
const dir = dirname(abs)
|
|
@@ -272,15 +272,15 @@ const cwdReader =
|
|
|
272
272
|
return { head, base: baseText }
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
/** The changed scenario names of a touched `.feature`, via the pinned `gherkin-cli
|
|
276
|
-
* `
|
|
275
|
+
/** The changed scenario names of a touched `.feature`, via the pinned `gherkin-cli`
|
|
276
|
+
* `diff` (same tool classify-edit-class.mts uses — never a reimplemented differ). Gated
|
|
277
277
|
* by isFeature — a non-.feature never calls out. On any failure returns []. Reads any `.feature`
|
|
278
278
|
* regardless of freeze — the freeze gate is a separate concern (spec-gate), not this tool's
|
|
279
279
|
* business. */
|
|
280
280
|
export function changedScenarios(base: string, path: string, cwd: string): string[] {
|
|
281
281
|
if (!isFeature(path)) return []
|
|
282
282
|
try {
|
|
283
|
-
const { files } =
|
|
283
|
+
const { files } = gherkinDiff([path], { base, full: true }, { readDiff: cwdReader(cwd) })
|
|
284
284
|
const fileResult = files.find((f) => f.file === path) ?? files[0]
|
|
285
285
|
return (fileResult?.scenarios ?? []).filter((s) => s.change !== 'unchanged').map((s) => s.name)
|
|
286
286
|
} catch {
|
|
@@ -8,7 +8,12 @@ script that unions one or more junit (today) result sources against the scenario
|
|
|
8
8
|
|
|
9
9
|
- **Skill contract:** [`SKILL.md`](./SKILL.md)
|
|
10
10
|
- **Script:** [`scripts/verify-scenarios.mts`](./scripts/verify-scenarios.mts)
|
|
11
|
-
- **Tests:** [`scripts/verify-scenarios.test.mts`](./scripts/verify-scenarios.test.mts) (`node:test`)
|
|
11
|
+
- **Tests:** [`scripts/verify-scenarios.test.mts`](./scripts/verify-scenarios.test.mts) (`node:test`);
|
|
12
|
+
[`scripts/verify-scenarios-installed.test.mts`](./scripts/verify-scenarios-installed.test.mts)
|
|
13
|
+
runs the engine from a copy of the plugin's files, with no `node_modules`
|
|
14
|
+
|
|
15
|
+
The engine imports gherkin-cli from the spec gate's committed bundle
|
|
16
|
+
(`../spec-gate/vendor/gherkin-cli.mjs`), so it runs from an installed plugin.
|
|
12
17
|
|
|
13
18
|
```bash
|
|
14
19
|
node scripts/verify-scenarios.mts --feature .agents/spec/identity/identity.feature --node cyberlegion/identity
|
|
@@ -49,7 +49,7 @@ import { execSync } from 'node:child_process'
|
|
|
49
49
|
import { existsSync, readFileSync, realpathSync } from 'node:fs'
|
|
50
50
|
import { isAbsolute, join } from 'node:path'
|
|
51
51
|
import { pathToFileURL } from 'node:url'
|
|
52
|
-
import {
|
|
52
|
+
import { parse } from '../../spec-gate/vendor/gherkin-cli.mjs'
|
|
53
53
|
|
|
54
54
|
// Resolves a path argument against `--root`: relative paths join beneath root (which defaults to the
|
|
55
55
|
// current directory); an absolute path is used verbatim, never double-prefixed under root.
|
|
@@ -132,7 +132,7 @@ export function scenarioKeysFromParse(parsed: GherkinParseOutput): ScenarioKey[]
|
|
|
132
132
|
// `--feature-root` — see main()).
|
|
133
133
|
export function getScenarioKeys(root: string, featurePath: string, featureRoot: string = root): ScenarioKey[] {
|
|
134
134
|
const abs = underRoot(featureRoot, featurePath)
|
|
135
|
-
return scenarioKeysFromParse(
|
|
135
|
+
return scenarioKeysFromParse(parse([abs]))
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// ── JUnit parsing (hand-rolled, no xml dep) ──
|