cyber-sdd 0.0.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/.claude-plugin/plugin.json +17 -0
- package/.codex-plugin/plugin.json +17 -0
- package/.plugin/plugin.json +17 -0
- package/README.md +159 -0
- package/agents/sdd-automaton.md +97 -0
- package/agents/sdd-impl-judge.md +214 -0
- package/agents/sdd-scanner.md +120 -0
- package/agents/sdd-spec-judge.md +224 -0
- package/agents/sdd-warden.md +101 -0
- package/package.json +24 -0
- package/skills/align-spec/README.md +20 -0
- package/skills/align-spec/SKILL.md +111 -0
- package/skills/align-spec/scripts/align-spec.mts +187 -0
- package/skills/architect-impl-governance/README.md +46 -0
- package/skills/architect-impl-governance/SKILL.md +45 -0
- package/skills/architect-spec-governance/README.md +48 -0
- package/skills/architect-spec-governance/SKILL.md +59 -0
- package/skills/blast-estimate/README.md +47 -0
- package/skills/blast-estimate/SKILL.md +133 -0
- package/skills/blast-estimate/scripts/blast-estimate.mts +583 -0
- package/skills/builder-impl-governance/README.md +47 -0
- package/skills/builder-impl-governance/SKILL.md +47 -0
- package/skills/builder-spec-governance/README.md +49 -0
- package/skills/builder-spec-governance/SKILL.md +36 -0
- package/skills/check-partition-quality/README.md +22 -0
- package/skills/check-partition-quality/SKILL.md +51 -0
- package/skills/check-partition-quality/scripts/check-partition-quality.mts +336 -0
- package/skills/check-plan-safety/README.md +17 -0
- package/skills/check-plan-safety/SKILL.md +60 -0
- package/skills/check-plan-safety/scripts/check-plan-safety.mts +145 -0
- package/skills/check-project-specs/README.md +19 -0
- package/skills/check-project-specs/SKILL.md +69 -0
- package/skills/check-project-specs/scripts/check-project-specs.mts +217 -0
- package/skills/check-scenario-overlap/README.md +19 -0
- package/skills/check-scenario-overlap/SKILL.md +74 -0
- package/skills/check-scenario-overlap/scripts/check-scenario-overlap.mts +249 -0
- package/skills/check-spec-structure/README.md +17 -0
- package/skills/check-spec-structure/SKILL.md +66 -0
- package/skills/check-spec-structure/scripts/check-spec-structure.mts +346 -0
- package/skills/collision-ladder/README.md +18 -0
- package/skills/collision-ladder/SKILL.md +83 -0
- package/skills/collision-ladder/scripts/collision-ladder.mts +657 -0
- package/skills/combat-log-governance/README.md +13 -0
- package/skills/combat-log-governance/SKILL.md +257 -0
- package/skills/concept-index/README.md +13 -0
- package/skills/concept-index/SKILL.md +38 -0
- package/skills/concept-index/scripts/concept-index.mts +245 -0
- package/skills/discover-plans/README.md +16 -0
- package/skills/discover-plans/SKILL.md +74 -0
- package/skills/discover-plans/scripts/discover-plans.mts +212 -0
- package/skills/discover-specs/README.md +15 -0
- package/skills/discover-specs/SKILL.md +76 -0
- package/skills/discover-specs/scripts/discover-specs.mts +396 -0
- package/skills/doctrine-loop/README.md +15 -0
- package/skills/doctrine-loop/SKILL.md +97 -0
- package/skills/formation-loop/README.md +17 -0
- package/skills/formation-loop/SKILL.md +140 -0
- package/skills/gate-validation-governance/README.md +12 -0
- package/skills/gate-validation-governance/SKILL.md +87 -0
- package/skills/impl-producer-governance/README.md +48 -0
- package/skills/impl-producer-governance/SKILL.md +85 -0
- package/skills/init/README.md +27 -0
- package/skills/init/SKILL.md +68 -0
- package/skills/init/scripts/wire-statusline.mts +276 -0
- package/skills/lifecycle-governance/README.md +11 -0
- package/skills/lifecycle-governance/SKILL.md +168 -0
- package/skills/manage/README.md +9 -0
- package/skills/manage/SKILL.md +62 -0
- package/skills/manage-ignore/README.md +19 -0
- package/skills/manage-ignore/SKILL.md +52 -0
- package/skills/manage-ignore/scripts/manage-ignore.mts +294 -0
- package/skills/manage-scenario-bridge/README.md +20 -0
- package/skills/manage-scenario-bridge/SKILL.md +60 -0
- package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.mts +156 -0
- package/skills/manage-spec-anchors/README.md +18 -0
- package/skills/manage-spec-anchors/SKILL.md +56 -0
- package/skills/manage-spec-anchors/scripts/manage-spec-anchors.mts +328 -0
- package/skills/mission-graph/README.md +15 -0
- package/skills/mission-graph/SKILL.md +67 -0
- package/skills/mission-graph/scripts/mission-graph.mts +844 -0
- package/skills/oracle-spec-governance/README.md +45 -0
- package/skills/oracle-spec-governance/SKILL.md +45 -0
- package/skills/ownership-governance/README.md +65 -0
- package/skills/ownership-governance/SKILL.md +104 -0
- package/skills/pause-mission/README.md +18 -0
- package/skills/pause-mission/SKILL.md +112 -0
- package/skills/place-node/README.md +12 -0
- package/skills/place-node/SKILL.md +47 -0
- package/skills/place-node/scripts/place-node.mts +157 -0
- package/skills/plan-retirement/README.md +32 -0
- package/skills/plan-retirement/SKILL.md +90 -0
- package/skills/plan-retirement/scripts/retire-plans.mts +196 -0
- package/skills/plugin-contract-governance/README.md +12 -0
- package/skills/plugin-contract-governance/SKILL.md +112 -0
- package/skills/remediation-governance/README.md +46 -0
- package/skills/remediation-governance/SKILL.md +78 -0
- package/skills/resolve-governances/README.md +18 -0
- package/skills/resolve-governances/SKILL.md +50 -0
- package/skills/resolve-governances/scripts/resolve-governances.mts +515 -0
- package/skills/resolve-tracking/SKILL.md +64 -0
- package/skills/resolve-tracking/scripts/resolve-tracking.mts +213 -0
- package/skills/resume-mission/README.md +12 -0
- package/skills/resume-mission/SKILL.md +53 -0
- package/skills/scaffold-project-spec/README.md +7 -0
- package/skills/scaffold-project-spec/SKILL.md +192 -0
- package/skills/sdd/README.md +7 -0
- package/skills/sdd/SKILL.md +92 -0
- package/skills/solution-producer-governance/README.md +9 -0
- package/skills/solution-producer-governance/SKILL.md +44 -0
- package/skills/spec-format-governance/README.md +73 -0
- package/skills/spec-format-governance/SKILL.md +114 -0
- package/skills/spec-gate/README.md +26 -0
- package/skills/spec-gate/SKILL.md +201 -0
- package/skills/spec-gate/scripts/check-spec-state.mts +601 -0
- package/skills/spec-gate/scripts/check-suite.mts +501 -0
- package/skills/spec-gate/scripts/classify-edit-class.mts +411 -0
- package/skills/spec-producer-governance/README.md +7 -0
- package/skills/spec-producer-governance/SKILL.md +86 -0
- package/skills/spec-structure-governance/README.md +40 -0
- package/skills/spec-structure-governance/SKILL.md +169 -0
- package/skills/ssa-lowering/README.md +26 -0
- package/skills/ssa-lowering/SKILL.md +181 -0
- package/skills/start-mission/README.md +7 -0
- package/skills/start-mission/SKILL.md +115 -0
- package/skills/suite-format-governance/README.md +75 -0
- package/skills/suite-format-governance/SKILL.md +299 -0
- package/skills/suite-format-governance/references/rubric.md +313 -0
- package/skills/touch-set-correction/README.md +16 -0
- package/skills/touch-set-correction/SKILL.md +67 -0
- package/skills/touch-set-correction/scripts/touch-set-correction.mts +418 -0
- package/skills/verify-scenarios/README.md +17 -0
- package/skills/verify-scenarios/SKILL.md +109 -0
- package/skills/verify-scenarios/scripts/verify-scenarios.mts +386 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// align-spec — project-spec/align-spec's concrete engine. Detects prose↔suite drift across the
|
|
3
|
+
// project spec's nodes and, on request, applies the mechanical write primitives a judge-directed
|
|
4
|
+
// reconcile step uses (see this skill's README.md / SKILL.md).
|
|
5
|
+
//
|
|
6
|
+
// The 12 frozen scenarios split two ways (align-spec.feature):
|
|
7
|
+
// - Mechanical (this engine ships code + a test for each):
|
|
8
|
+
// * a scenario-diff flags a narrowing of the frozen suite → Clearance
|
|
9
|
+
// * check mode exits non-zero on drift and writes nothing
|
|
10
|
+
// * check mode exits zero when there is no drift
|
|
11
|
+
// * detect runs over every node of the project spec (a chosen set)
|
|
12
|
+
// * reconcile never writes lifecycle state (writes only prose or scenarios)
|
|
13
|
+
// - Judge-orchestrated (SKILL.md documents the procedure; no engine code):
|
|
14
|
+
// coverage-gap detection, prose/scenario contradiction detection, the aligned-report-no-
|
|
15
|
+
// drift aggregate, and every reconcile *decision* (add a scenario / trim prose / align the
|
|
16
|
+
// losing side / escalate a Clearance) — there are no scenario IDs in prose, so prose↔suite
|
|
17
|
+
// alignment is judge-only. This engine feeds that judge the mechanical scan (node
|
|
18
|
+
// iteration + scenario-diff); it never itself judges coverage or contradiction.
|
|
19
|
+
//
|
|
20
|
+
// Node iteration and the scenario-diff reuse the existing engines rather than re-implementing
|
|
21
|
+
// them: check-spec-structure's scanProjectSpec (node discovery) and spec-gate's classify-edit-
|
|
22
|
+
// class (the gherkin-cli-backed structural diff against a frozen baseline).
|
|
23
|
+
//
|
|
24
|
+
// No dependencies (the repo's node-≥23.6 / no-deps convention). Pure functions are exported for
|
|
25
|
+
// node:test; running the file directly drives the CLI.
|
|
26
|
+
|
|
27
|
+
import { existsSync, readdirSync } from 'node:fs'
|
|
28
|
+
import { isAbsolute, join, relative } from 'node:path'
|
|
29
|
+
import { type NodeRecord, scanProjectSpec } from '../../check-spec-structure/scripts/check-spec-structure.mts'
|
|
30
|
+
import { classifyFile } from '../../spec-gate/scripts/classify-edit-class.mts'
|
|
31
|
+
|
|
32
|
+
export const DEFAULT_BASE = 'HEAD'
|
|
33
|
+
|
|
34
|
+
export type DriftKind = 'narrowing'
|
|
35
|
+
export type DriftSeverity = 'clearance'
|
|
36
|
+
|
|
37
|
+
export interface DriftFinding {
|
|
38
|
+
kind: DriftKind
|
|
39
|
+
severity: DriftSeverity
|
|
40
|
+
/** The node's display path (a folder for a README-rooted node). */
|
|
41
|
+
node: string
|
|
42
|
+
detail: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── Node selection — the "chosen set" a detect run scopes to ──
|
|
46
|
+
// No `--nodes` arg selects every node scanProjectSpec finds; an explicit list scopes the sweep
|
|
47
|
+
// to exactly those nodes (matched by display path or the README's relative path).
|
|
48
|
+
export function selectNodes(records: NodeRecord[], only?: string[]): NodeRecord[] {
|
|
49
|
+
if (!only || only.length === 0) return records
|
|
50
|
+
const wanted = new Set(only.map(normalizeNodeRef))
|
|
51
|
+
return records.filter((r) => wanted.has(normalizeNodeRef(r.display)) || wanted.has(normalizeNodeRef(r.relPath)))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function normalizeNodeRef(ref: string): string {
|
|
55
|
+
return ref
|
|
56
|
+
.replace(/\\/g, '/')
|
|
57
|
+
.replace(/\/README\.md$/, '')
|
|
58
|
+
.replace(/\/+$/, '')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Locate a node's sibling `.feature` file (same convention check-spec-structure scans by) ──
|
|
62
|
+
export function findFeaturePath(specDir: string, node: NodeRecord): string | null {
|
|
63
|
+
const nodeDir = join(specDir, node.relPath.replace(/README\.md$/, ''))
|
|
64
|
+
if (!existsSync(nodeDir)) return null
|
|
65
|
+
const entries = readdirSync(nodeDir, { withFileTypes: true })
|
|
66
|
+
const feature = entries.find((e) => e.isFile() && e.name.endsWith('.feature'))
|
|
67
|
+
return feature ? join(nodeDir, feature.name) : null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ── Mechanical scenario-diff: a modified/removed baseline scenario is a narrowing → Clearance ──
|
|
71
|
+
// Reuses spec-gate's classify-edit-class rather than hand-rolling a line-diff (a line-diff is
|
|
72
|
+
// fooled by a step reassigned off a frozen scenario onto a newly-added adjacent one).
|
|
73
|
+
export function detectNarrowing(node: NodeRecord, specDir: string, base: string, cwd = '.'): DriftFinding[] {
|
|
74
|
+
const featurePath = findFeaturePath(specDir, node)
|
|
75
|
+
if (!featurePath) return []
|
|
76
|
+
// classifyFile (and the `git show <base>:<path>` it drives) needs a path relative to cwd
|
|
77
|
+
// (the git repo root) — resolve it, since specDir/featurePath may already be absolute.
|
|
78
|
+
const relFeaturePath = isAbsolute(featurePath) ? relative(cwd, featurePath) : featurePath
|
|
79
|
+
const result = classifyFile(relFeaturePath, base, cwd)
|
|
80
|
+
if (result.classification !== 'narrowing' && result.classification !== 'mixed') return []
|
|
81
|
+
const narrowed = result.scenarios.filter((s) => s.change === 'modified' || s.change === 'removed')
|
|
82
|
+
return [
|
|
83
|
+
{
|
|
84
|
+
kind: 'narrowing',
|
|
85
|
+
severity: 'clearance',
|
|
86
|
+
node: node.display,
|
|
87
|
+
detail: `scenario-diff vs ${base} narrows: ${narrowed.map((s) => s.name).join(', ')} — escalate a Clearance CR, do not silently rewrite`,
|
|
88
|
+
},
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Runs the mechanical scan over exactly the chosen node set — reports drift per node.
|
|
93
|
+
export function detect(records: NodeRecord[], specDir: string, base: string, cwd = '.'): DriftFinding[] {
|
|
94
|
+
return records.flatMap((node) => detectNarrowing(node, specDir, base, cwd))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function hasDrift(findings: DriftFinding[]): boolean {
|
|
98
|
+
return findings.length > 0
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── The write boundary ──
|
|
102
|
+
// Reconcile decisions (add/trim/align/escalate) are judge-orchestrated (SKILL.md), but the
|
|
103
|
+
// mechanical write primitives the judge's verdict drives are engine code, and they are built to
|
|
104
|
+
// make writing lifecycle state structurally impossible: a README write splits frontmatter from
|
|
105
|
+
// body and only ever touches the body; a .feature write only ever appends a whole scenario block.
|
|
106
|
+
export interface SplitDocument {
|
|
107
|
+
frontmatter: string
|
|
108
|
+
body: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function splitFrontmatter(text: string): SplitDocument {
|
|
112
|
+
const m = /^(---\r?\n[\s\S]*?\r?\n---\s*(?:\r?\n|$))([\s\S]*)$/.exec(text)
|
|
113
|
+
if (!m) return { frontmatter: '', body: text }
|
|
114
|
+
return { frontmatter: m[1], body: m[2] }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Trims prose out of a node's README body — the frontmatter (and any status/approval/freeze
|
|
118
|
+
// field it carries) passes through byte-for-byte untouched.
|
|
119
|
+
export function trimProse(readmeText: string, proseToRemove: string): string {
|
|
120
|
+
const { frontmatter, body } = splitFrontmatter(readmeText)
|
|
121
|
+
return frontmatter + body.split(proseToRemove).join('')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Appends a whole scenario block to a `.feature` file. `.feature` files carry no frontmatter /
|
|
125
|
+
// lifecycle fields to begin with — this only ever appends, never rewrites an existing scenario
|
|
126
|
+
// (a rewrite of an existing frozen scenario is the narrowing path above, which escalates instead).
|
|
127
|
+
export function appendScenario(featureText: string, scenarioBlock: string): string {
|
|
128
|
+
const trimmed = featureText.replace(/\s+$/, '')
|
|
129
|
+
return `${trimmed}\n\n${scenarioBlock.trim()}\n`
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ── Render ──
|
|
133
|
+
export function renderFindings(findings: DriftFinding[], specDir: string): string {
|
|
134
|
+
const lines: string[] = [`align-spec: spec-dir=${specDir}`, `drift[${findings.length}]:`]
|
|
135
|
+
for (const f of findings) lines.push(` ${f.node} — ${f.kind} (${f.severity}): ${f.detail}`)
|
|
136
|
+
if (findings.length === 0) lines.push(' (none — mechanical scenario-diff found no narrowing)')
|
|
137
|
+
lines.push(
|
|
138
|
+
'note: coverage gaps and prose/scenario contradictions are judge-only (no scenario IDs in prose) — this engine reports only the mechanical scenario-diff',
|
|
139
|
+
)
|
|
140
|
+
return lines.join('\n')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── CLI ──
|
|
144
|
+
function parseNodesArg(value: string | undefined): string[] | undefined {
|
|
145
|
+
if (!value) return undefined
|
|
146
|
+
const parts = value
|
|
147
|
+
.split(',')
|
|
148
|
+
.map((s) => s.trim())
|
|
149
|
+
.filter(Boolean)
|
|
150
|
+
return parts.length > 0 ? parts : undefined
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function main(argv: string[]): number {
|
|
154
|
+
let specDir = '.'
|
|
155
|
+
let base = DEFAULT_BASE
|
|
156
|
+
let mode: 'detect' | 'check' = 'detect'
|
|
157
|
+
let format: 'toon' | 'json' = 'toon'
|
|
158
|
+
let only: string[] | undefined
|
|
159
|
+
for (let i = 0; i < argv.length; i++) {
|
|
160
|
+
const a = argv[i]
|
|
161
|
+
if (a === '--spec-dir') specDir = argv[++i] ?? '.'
|
|
162
|
+
else if (a === '--check') mode = 'check'
|
|
163
|
+
else if (a === '--base') base = argv[++i] ?? DEFAULT_BASE
|
|
164
|
+
else if (a === '--format') format = (argv[++i] as 'toon' | 'json') ?? 'toon'
|
|
165
|
+
else if (a === '--nodes') only = parseNodesArg(argv[++i])
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const records = selectNodes(scanProjectSpec(specDir), only)
|
|
169
|
+
const findings = detect(records, specDir, base)
|
|
170
|
+
|
|
171
|
+
if (mode === 'check') {
|
|
172
|
+
if (hasDrift(findings)) {
|
|
173
|
+
process.stderr.write(`align-spec: ${findings.length} drift finding(s) (mechanical scenario-diff)\n`)
|
|
174
|
+
return 1
|
|
175
|
+
}
|
|
176
|
+
process.stdout.write('align-spec: no mechanical drift\n')
|
|
177
|
+
return 0
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (format === 'json') process.stdout.write(`${JSON.stringify(findings)}\n`)
|
|
181
|
+
else process.stdout.write(`${renderFindings(findings, specDir)}\n`)
|
|
182
|
+
return 0
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
186
|
+
process.exit(main(process.argv.slice(2)))
|
|
187
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# architect-impl-governance
|
|
2
|
+
|
|
3
|
+
This is an internal SDD governance about structural fit at the impl gate.
|
|
4
|
+
|
|
5
|
+
It is the **Architect** actor's bar — the question it answers is: does the implementation fit the
|
|
6
|
+
existing structure, and is the suite's verification pyramid sound? Where its spec-gate twin judges
|
|
7
|
+
how a capability sits in the project, this bar judges how the code that realizes it sits in the
|
|
8
|
+
codebase.
|
|
9
|
+
|
|
10
|
+
This is the SDD default for the `architect` bar at the impl gate: a plugin may bind its own version,
|
|
11
|
+
and this one loads whenever the registry leaves `architect`/`impl` unbound. Its twin at the spec
|
|
12
|
+
gate is **`architect-spec-governance`** — same actor, same structural-fit question, asked of the
|
|
13
|
+
spec and solution instead of the implementation.
|
|
14
|
+
|
|
15
|
+
## What it requires — the bar
|
|
16
|
+
|
|
17
|
+
| Requirement | What it means |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| **No knowledge duplication** | A rule, contract, or piece of logic has **one home** in the implementation; whatever needs it calls that home. Two copies of one piece of knowledge drift — a change lands in one and misses the other. |
|
|
20
|
+
| **…but coincidental resemblance is not duplication** | Two units that look alike yet change for different reasons stay separate — a premature shared abstraction couples them, and the wrong abstraction costs more than the repetition. The test: does a change to one force the same change to the other? Only then converge. |
|
|
21
|
+
| **No conflict** | The implementation does not contradict conventions, module boundaries, or a spec's contract — including the project's **declared** layout, judged as declared (`spec-structure-governance`). The declaration is not a licence: node ↔ capability stays one-to-one under every strategy, because the scheduler cuts one mission per node (ADR-0025). |
|
|
22
|
+
| **Contained complexity** | Watch cyclomatic complexity and coupling; a unit that is hard to test is usually mis-structured — flag it. |
|
|
23
|
+
| **A sound verification pyramid** | Across the suite, the verification levels form a pyramid — a broad cheap base under a **thin e2e cap** that keeps the end-to-end path honest. An **all-e2e** suite is fragile and slow; a **capless** suite has no honesty check. The level of any *one* scenario is the builder's call (`builder-impl-governance`); this bar judges the overall shape. |
|
|
24
|
+
| **An orthogonal axis** | Structural fit judges a property the builder was not optimizing. |
|
|
25
|
+
| **Structural concerns are deferred** | A structural problem in *another* capability spawns a new spec — it does not block this one. |
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
One merged bar loaded by **both faces** at the **impl gate**:
|
|
30
|
+
|
|
31
|
+
- **impl-producer:** reads it forward to self-align while building.
|
|
32
|
+
- **impl-judge:** reads it backward to verify.
|
|
33
|
+
|
|
34
|
+
## Related governances
|
|
35
|
+
|
|
36
|
+
This bar owns the structural fit of the **implementation**. Its neighbors own everything around
|
|
37
|
+
that:
|
|
38
|
+
|
|
39
|
+
- **`architect-spec-governance`** — the same Architect bar at the **spec gate**: structural fit of
|
|
40
|
+
the capability as specified, where this bar judges the code that realizes it.
|
|
41
|
+
- **`builder-impl-governance`** — the per-scenario verification level is the builder's call under
|
|
42
|
+
that bar; this bar judges only the pyramid's overall shape.
|
|
43
|
+
- **`spec-structure-governance`** — the declared layout law the no-conflict requirement judges
|
|
44
|
+
against.
|
|
45
|
+
|
|
46
|
+
Internal SDD governance (`user-invocable: false`). Not triggered by users directly.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architect-impl-governance
|
|
3
|
+
description: "Partial Skill: invoke by name only"
|
|
4
|
+
user-invocable: false
|
|
5
|
+
metadata:
|
|
6
|
+
actor: architect
|
|
7
|
+
gate: impl
|
|
8
|
+
compose: union
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Architect-Impl Governance — the structural-fit bar (impl gate)
|
|
12
|
+
|
|
13
|
+
The **Architect** bar at the **impl gate**: does the implementation fit the existing structure, and
|
|
14
|
+
is the suite's verification pyramid sound? Loaded by both faces. The SDD default for the `architect`
|
|
15
|
+
impl bar; a plugin may bind its own, and this loads when the registry leaves `architect`/`impl`
|
|
16
|
+
unbound.
|
|
17
|
+
|
|
18
|
+
## The bar
|
|
19
|
+
|
|
20
|
+
- **No knowledge duplication.** A rule, contract, or piece of logic has **one home** in the
|
|
21
|
+
implementation; whatever needs it calls that home. Two copies of one piece of knowledge drift — a
|
|
22
|
+
change lands in one and misses the other. But **coincidental resemblance is not duplication**: two
|
|
23
|
+
units that look alike yet change for different reasons stay separate — a premature shared
|
|
24
|
+
abstraction couples them, and the wrong abstraction costs more than the repetition. Test: does a
|
|
25
|
+
change to one force the same change to the other? Only then converge.
|
|
26
|
+
- **No conflict.** The implementation does not contradict conventions, module boundaries, or a spec's
|
|
27
|
+
contract — including the project's **declared** layout, judged as declared
|
|
28
|
+
(`sdd:spec-structure-governance`). The declaration is not a licence: node <-> capability stays 1:1
|
|
29
|
+
under every strategy, because the scheduler cuts one mission per node (ADR-0025).
|
|
30
|
+
- **Contained complexity.** Watch cyclomatic complexity and coupling; a unit hard to test is usually
|
|
31
|
+
mis-structured — flag it.
|
|
32
|
+
- **A sound verification pyramid.** Across the suite the verification levels form a pyramid — a broad
|
|
33
|
+
cheap base under a **thin e2e cap** that keeps the end-to-end path honest. An **all-e2e** suite is
|
|
34
|
+
fragile and slow; a **capless** suite has no honesty check. The per-scenario level is the builder's
|
|
35
|
+
call (`sdd:builder-impl-governance`); this bar judges the shape.
|
|
36
|
+
- **An orthogonal axis.** Structural fit judges a property the builder was not optimizing.
|
|
37
|
+
- **Structural concerns are deferred.** A structural problem in another capability spawns a new spec.
|
|
38
|
+
|
|
39
|
+
## Key points (read-check)
|
|
40
|
+
|
|
41
|
+
1. **No knowledge duplication** — one home per rule; but coincidental resemblance is not duplication
|
|
42
|
+
(don't merge units that change for different reasons). No conflict; contained complexity.
|
|
43
|
+
2. **A sound verification pyramid** — cheap base, thin e2e cap; neither all-e2e nor capless.
|
|
44
|
+
3. **Per-scenario level is the builder's call; this bar judges the shape.**
|
|
45
|
+
4. **Structural concerns in another capability are deferred.**
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# architect-spec-governance
|
|
2
|
+
|
|
3
|
+
This is an internal SDD governance about structural fit at the spec gate.
|
|
4
|
+
|
|
5
|
+
It is the **Architect** actor's bar — the question it answers is: does this **capability** fit the
|
|
6
|
+
project's structure, and is its control-flow graph (CFG) well-placed? It judges the capability
|
|
7
|
+
itself, read from its spec and test suite — not the wording of the document. How the prose is
|
|
8
|
+
written is a different bar (`spec-format-governance`).
|
|
9
|
+
|
|
10
|
+
This is the SDD default for the `architect` bar at the spec gate: a plugin may bind its own version
|
|
11
|
+
per artifact-type, and this one loads whenever the registry leaves `architect`/`spec` unbound. Its
|
|
12
|
+
twin at the impl gate is **`architect-impl-governance`** — same actor, same structural-fit question,
|
|
13
|
+
asked of the implementation instead of the spec.
|
|
14
|
+
|
|
15
|
+
## What it requires — the bar
|
|
16
|
+
|
|
17
|
+
| Requirement | What it means |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| **No knowledge duplication** | A capability, concept, rule, or contract has **one home**; whatever needs it references that home. Two copies of one piece of knowledge drift — a change lands in one and misses the other. |
|
|
20
|
+
| **…but coincidental resemblance is not duplication** | Two capabilities that look alike yet change for different reasons stay separate — a premature shared abstraction couples them, and the wrong abstraction costs more than the repetition. The test: does a change to one force the same change to the other? Only then converge. |
|
|
21
|
+
| **No conflict** | The capability does not contradict established conventions, module boundaries, or an existing capability's contract. |
|
|
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
|
+
| **…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
|
+
| **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
|
+
| **An orthogonal axis** | Structural fit judges a property the builder was not optimizing — a real independent check even from the same hand. |
|
|
26
|
+
| **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
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
One merged bar with an **asymmetric loadout** — the two faces read different inputs:
|
|
31
|
+
|
|
32
|
+
- **solution-producer** (forward): self-aligns the ungated `<unit>.solution.md`, where structure is
|
|
33
|
+
chosen.
|
|
34
|
+
- **cold spec-judge** (backward, at the **spec gate**): grades structure from `spec.md` + the suite
|
|
35
|
+
only — the solution is out of view, which keeps the grader independent.
|
|
36
|
+
|
|
37
|
+
## Related governances
|
|
38
|
+
|
|
39
|
+
This bar owns the structural fit of the **capability**. Its neighbors own everything around that:
|
|
40
|
+
|
|
41
|
+
- **`architect-impl-governance`** — the same Architect bar at the **impl gate**: structural fit of
|
|
42
|
+
the implementation, where this bar judges the spec and solution.
|
|
43
|
+
- **`spec-format-governance`** — the document's prose: sections, tables, the plain-language bar.
|
|
44
|
+
This bar reads *through* the document at the capability; that one judges the document itself.
|
|
45
|
+
- **`spec-structure-governance`** — the layout law itself: the declared placement map this bar
|
|
46
|
+
judges placement against.
|
|
47
|
+
|
|
48
|
+
Internal SDD governance (`user-invocable: false`). Not triggered by users directly.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architect-spec-governance
|
|
3
|
+
description: "Partial Skill: invoke by name only"
|
|
4
|
+
user-invocable: false
|
|
5
|
+
metadata:
|
|
6
|
+
actor: architect
|
|
7
|
+
gate: spec
|
|
8
|
+
compose: union
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Architect-Spec Governance — the structural-fit bar (spec gate)
|
|
12
|
+
|
|
13
|
+
The **Architect** bar at the **spec gate**: does this **capability** fit the project's structure, and
|
|
14
|
+
is its control-flow graph (CFG) well-placed? Judges the capability (read from its spec + suite), not
|
|
15
|
+
the document's prose — that is `sdd:spec-format-governance`. The SDD default for the `architect` spec bar;
|
|
16
|
+
a plugin may bind its own per artifact-type, and this loads when the registry leaves `architect`/`spec`
|
|
17
|
+
unbound.
|
|
18
|
+
|
|
19
|
+
## The bar
|
|
20
|
+
|
|
21
|
+
- **No knowledge duplication.** A capability, concept, rule, or contract has **one home**; whatever
|
|
22
|
+
needs it references that home. Two copies of one piece of knowledge drift — a change lands in one
|
|
23
|
+
and misses the other. But **coincidental resemblance is not duplication**: two capabilities that
|
|
24
|
+
look alike yet change for different reasons stay separate — a premature shared abstraction couples
|
|
25
|
+
them, and the wrong abstraction costs more than the repetition. Test: does a change to one force the
|
|
26
|
+
same change to the other? Only then converge.
|
|
27
|
+
- **No conflict.** The capability does not contradict established conventions, module boundaries, or
|
|
28
|
+
an existing capability's contract.
|
|
29
|
+
- **Placement matches the declared layout — and the layout preserves the partition.** Judge
|
|
30
|
+
*placement within* the strategy the project **declared** in its root `spec.md` placement map
|
|
31
|
+
(`sdd:spec-structure-governance`): under the screaming-architecture default a capability lives in a
|
|
32
|
+
folder named for its intent, while a project that declared `mirror-source` is correctly placed when
|
|
33
|
+
it mirrors its source. But the declaration is **not** a licence — layouts are ranked by whether
|
|
34
|
+
they keep **node <-> capability 1:1**, because the mission scheduler cuts one mission per node and a
|
|
35
|
+
scattered capability degrades the schedule toward serial (ADR-0025). **One capability per node,
|
|
36
|
+
never smeared across nodes**, holds under every strategy, and a layered / framework-first *top*
|
|
37
|
+
level stays discouraged however it is declared.
|
|
38
|
+
- **A well-formed CFG.** Its control-flow graph connects — every decision reachable, no
|
|
39
|
+
dangling branch — and the suite's sections mirror it.
|
|
40
|
+
- **An orthogonal axis.** Structural fit judges a property the builder was not optimizing — a real
|
|
41
|
+
independent check even from the same hand.
|
|
42
|
+
- **Structural concerns are deferred.** A structural problem in another capability is an observation
|
|
43
|
+
that spawns a new spec, never a marker in the one being built.
|
|
44
|
+
|
|
45
|
+
## Faces — asymmetric loadout
|
|
46
|
+
|
|
47
|
+
The two faces read different inputs. **Forward (solution-producer):** self-aligns the ungated
|
|
48
|
+
`<unit>.solution.md`, where structure is chosen. **Backward (cold spec-judge):** grades structure
|
|
49
|
+
from `spec.md` + the suite only — the solution is out of view (grader independence).
|
|
50
|
+
|
|
51
|
+
## Key points (read-check)
|
|
52
|
+
|
|
53
|
+
1. **No knowledge duplication** — one home per concept; but coincidental resemblance is not
|
|
54
|
+
duplication (don't merge things that change for different reasons). No conflict with conventions or
|
|
55
|
+
boundaries.
|
|
56
|
+
2. **Placement matches the *declared* layout** (`sdd:spec-structure-governance`), not a preferred
|
|
57
|
+
one; one capability per node either way, never smeared across nodes.
|
|
58
|
+
3. **A well-formed CFG** the suite's sections mirror.
|
|
59
|
+
4. **Structural concerns in another capability are deferred** — an observation that spawns a new spec.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# blast-estimate
|
|
2
|
+
|
|
3
|
+
The concrete engine for **blast-estimate** — a read-only estimate of how much of the project a
|
|
4
|
+
Mission could disturb, computed from its touch-set and the project corpus instead of the hand-typed
|
|
5
|
+
guess: **breadth** (how many work areas, *and* whether the touch-set covers a project entirely),
|
|
6
|
+
`centrality` (dependency fan-in — how many other work areas reference a touched one), and
|
|
7
|
+
`sensitivity` (declared, never inferred, in the opt-in `.agents/sdd/sensitive-paths.toml`). Lines the
|
|
8
|
+
computed level up against the Mission's declared blast (`agrees` / `under-called` / `over-called`).
|
|
9
|
+
|
|
10
|
+
Breadth is `max(countScore, coverageScore)` — reach measured absolutely *and* relatively, whichever
|
|
11
|
+
says more — so a fully-covered project computes `high` at **any** project size (≥2 areas), not only
|
|
12
|
+
once the raw count crosses a bucket. See [`SKILL.md`](./SKILL.md) for the full rule and the
|
|
13
|
+
load-bearing ≥2-area guard that keeps the project-wide and single-peripheral scenarios disjoint.
|
|
14
|
+
|
|
15
|
+
Work-area recovery reuses [`touch-set-correction`](../touch-set-correction/SKILL.md)'s pure
|
|
16
|
+
`fileToNode` over `discoverLayouts`' declared `ProjectLayout[]` — never a path-shape guess. A work
|
|
17
|
+
area spans its **spec root and its impl root** (`sdd/mission-graph` lives at both
|
|
18
|
+
`.agents/specs/sdd/mission-graph/` and `plugins/sdd/skills/mission-graph/`), and fan-in is measured
|
|
19
|
+
across **both**, so centrality counts implementation dependency rather than spec prose alone.
|
|
20
|
+
Layouts are injected, so tests construct them as fixtures.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
node scripts/blast-estimate.mts --root <corpus> --touch-set sdd/mission-graph,sdd/blast-estimate --declared medium
|
|
24
|
+
```
|
|
25
|
+
|
|
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 and
|
|
28
|
+
[`.agents/specs/sdd/blast-estimate/README.md`](../../../../.agents/specs/sdd/blast-estimate/README.md)
|
|
29
|
+
for the authoritative behavior description and
|
|
30
|
+
[`blast-estimate.feature`](../../../../.agents/specs/sdd/blast-estimate/blast-estimate.feature) for
|
|
31
|
+
the frozen 21-scenario contract.
|
|
32
|
+
|
|
33
|
+
Fan-in counts the reference forms the corpus really uses — the bare id, a `sdd:spec-gate` skill ref,
|
|
34
|
+
a path under any **declared root** at any depth, and a same-project relative link — not the bare id
|
|
35
|
+
alone, which matched almost nothing on a real tree. It stays **mention-based** by design; symbol-level
|
|
36
|
+
dependency is `ssa-lowering` / `collision-ladder` territory.
|
|
37
|
+
|
|
38
|
+
- **Skill contract:** [`SKILL.md`](./SKILL.md)
|
|
39
|
+
- **Script:** [`scripts/blast-estimate.mts`](./scripts/blast-estimate.mts)
|
|
40
|
+
- **Tests:** [`scripts/blast-estimate.test.mts`](./scripts/blast-estimate.test.mts) (`node:test`) —
|
|
41
|
+
one test per frozen scenario, titled `scenario: <verbatim frozen scenario name>`.
|
|
42
|
+
- **Live-corpus smoke check:**
|
|
43
|
+
[`scripts/blast-estimate.smoke.test.mts`](./scripts/blast-estimate.smoke.test.mts) — **not** bound
|
|
44
|
+
to any frozen scenario. The frozen suite mandates constructed corpora, so it is structurally blind
|
|
45
|
+
to whether the engine measures real reach on a real tree; three defects reached an impl gate
|
|
46
|
+
green-on-all-21 for exactly that reason. This asserts live properties (never magic numbers) and is
|
|
47
|
+
the tripwire for that class.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blast-estimate
|
|
3
|
+
description: "Partial Skill: invoke by name only — blast-estimate's engine that computes a Mission's blast (low/medium/high) from its touch-set plus the project corpus and lines it up against the hand-typed declared blast — the engine for the blast-estimate node, consumed by the mission-graph's writer. Not triggered by users directly."
|
|
4
|
+
user-invocable: false
|
|
5
|
+
metadata:
|
|
6
|
+
internal: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Blast Estimate
|
|
10
|
+
|
|
11
|
+
The concrete engine for **blast-estimate** — a read-only derivation that works out how much of the
|
|
12
|
+
project a Mission could disturb, instead of trusting the hand-typed guess. Given a Mission's
|
|
13
|
+
**touch-set** (the work areas it names) and the project corpus, it computes a **blast** level
|
|
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`). See
|
|
16
|
+
`.agents/specs/sdd/blast-estimate/README.md` for the full contract and vocabulary.
|
|
17
|
+
|
|
18
|
+
## The three inputs
|
|
19
|
+
|
|
20
|
+
- **count** — how many of the touch-set's areas resolve to a known work area in the corpus, plus
|
|
21
|
+
**coverage**: whether the touch-set covers *every* work area of a touched project. Together these
|
|
22
|
+
are **breadth** — absolute reach and relative reach.
|
|
23
|
+
- **centrality** (dependency fan-in) — for each resolved area, how many *other* work areas hold at
|
|
24
|
+
least one file that **references** it, measured across the area's **full root set** —
|
|
25
|
+
implementation files count exactly as spec prose does. The `max` across the resolved areas is used.
|
|
26
|
+
See *The reference matcher* below: a bare id alone is **not** how real prose references an area.
|
|
27
|
+
- **sensitivity** — whether a resolved area is **marked** in the opt-in
|
|
28
|
+
`.agents/sdd/sensitive-paths.toml` — a `sensitive = [ "id", ... ]` string array, parsed
|
|
29
|
+
line-anchored and lenient exactly as `manage-spec-anchors` parses `anchors = [ … ]`, so a leading
|
|
30
|
+
comment, a trailing comment, or a neighbouring key are all ordinary TOML and parse. A file with no
|
|
31
|
+
`sensitive` array at all is malformed and fails loud. **Only an absent file (`ENOENT`) is benign** —
|
|
32
|
+
no area is sensitive, not an error. **Every other read failure fails loud** — unparseable,
|
|
33
|
+
unreadable (permissions), or not a regular file — returning `computed: null` and an `error` naming
|
|
34
|
+
the file. The narrow ENOENT test is deliberate: swallowing the rest into "nothing marked" fails in
|
|
35
|
+
the **dangerous direction**, silently under-calling blast on exactly the areas a project marked as
|
|
36
|
+
needing care. An unreadable marking is not evidence of no markings.
|
|
37
|
+
|
|
38
|
+
The arithmetic is bucketed and deliberately simple (the rubric fixes only the ordering properties,
|
|
39
|
+
not the exact numbers):
|
|
40
|
+
|
|
41
|
+
- **breadth** = `max(countScore, coverageScore)` — reach measured two ways, whichever says more:
|
|
42
|
+
- `countScore`: 1→0, 2-3→1, 4+→3 (absolute reach — touching many areas is broad even in a large
|
|
43
|
+
project the touch-set nowhere near covers)
|
|
44
|
+
- `coverageScore`: 3 iff the touch-set covers **every** work area of a touched project holding
|
|
45
|
+
**≥2** work areas, else 0 (relative reach — a 3-area project touched entirely *is* project-wide;
|
|
46
|
+
the barrier agreement must hold at every project size **≥2**, not only at 4+)
|
|
47
|
+
- **centrality**: 0→0, 1-2→1, 3-6→2, 7+→3 (a genuine hub: touching an area a large share of the
|
|
48
|
+
project leans on is project-scale reach even at count 1). Calibrated against **real** fan-in, which
|
|
49
|
+
spans roughly 0–17 on a corpus of this size.
|
|
50
|
+
- **sensitivity**: any marked area →+2
|
|
51
|
+
|
|
52
|
+
Sum the three, then `score>=3` → `high`, `score>=1` → `medium`, `score==0` → `low`. A touch-set that
|
|
53
|
+
resolves to **zero** known areas (including the empty touch-set) computes `unknown` — never `low`.
|
|
54
|
+
|
|
55
|
+
The **≥2 work areas** guard on coverage is load-bearing, and it is keyed per **project**, not per
|
|
56
|
+
corpus: a 1-area project inside a larger multi-project corpus is still never project-wide. The
|
|
57
|
+
contract states the precondition itself — the project-wide scenario requires a project holding more
|
|
58
|
+
than one work area, and *"a lone work area is its whole project but is not project-wide reach"* pins
|
|
59
|
+
the 1-area answer to `low` **for a lone area with no fan-in and no marking**. Coverage is reach
|
|
60
|
+
**relative** to a project, and a project of one has none to cover, so coverage never fires there and
|
|
61
|
+
**breadth** rests on absolute count alone — centrality and sensitivity still score, so a lone area
|
|
62
|
+
that is central or marked computes `medium` or `high`.
|
|
63
|
+
|
|
64
|
+
Two exclusions are structural, not just documented: there is no compatibility/breaking-change input
|
|
65
|
+
at all (no seam for it to enter the score), and centrality is **measured** fan-in only — a work
|
|
66
|
+
area's name never enters the computation.
|
|
67
|
+
|
|
68
|
+
## Work-area recovery — declared layouts, never a path shape
|
|
69
|
+
|
|
70
|
+
Node recovery is **not this engine's to invent**: it reuses `touch-set-correction`'s pure
|
|
71
|
+
**`fileToNode(path, layouts)`** over `discoverLayouts`' declared `ProjectLayout[]` (the same
|
|
72
|
+
cross-skill reuse `collision-ladder` does). This is load-bearing — a work area spans **multiple
|
|
73
|
+
declared roots**, a spec root *and* an impl root:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
project "sdd" -> roots [".agents/specs/sdd", "plugins/sdd/skills"]
|
|
77
|
+
.agents/specs/sdd/mission-graph/README.md -> sdd/mission-graph
|
|
78
|
+
plugins/sdd/skills/mission-graph/SKILL.md -> sdd/mission-graph # SAME node, two roots
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
A node's file set is every file under **any** of its roots, so fan-in measures the project's real
|
|
82
|
+
lean on an area rather than spec-prose cross-reference. `estimateBlast` takes layouts **injected**
|
|
83
|
+
(`fileToNode` is pure, so tests construct them as fixtures); the CLI sources them from
|
|
84
|
+
`discoverLayouts`, overridable with `--layout`.
|
|
85
|
+
|
|
86
|
+
## The reference matcher
|
|
87
|
+
|
|
88
|
+
Fan-in counts the forms the corpus **actually uses** to name an area — a bare id is what a touch-set
|
|
89
|
+
and the ledger write, not how prose references anything. For node `<p>/<cap>`:
|
|
90
|
+
|
|
91
|
+
| form | example |
|
|
92
|
+
|---|---|
|
|
93
|
+
| bare id | `sdd/spec-gate` |
|
|
94
|
+
| skill-style ref | `sdd:spec-gate` |
|
|
95
|
+
| path under any **declared root**, at any depth | `plugins/sdd/skills/spec-gate/`, `.agents/specs/sdd/authoring/spec-gate/` |
|
|
96
|
+
| relative sibling link (**same project only**) | `../spec-gate/`, `../../authoring/spec-gate/` |
|
|
97
|
+
|
|
98
|
+
Path forms are **derived from the project's declared roots**, never hardcoded, so a re-rooted project
|
|
99
|
+
keeps working; the any-depth segment matters because a node's spec can sit nested under its root. A
|
|
100
|
+
relative link carries no project, so it is only counted within the same project — otherwise two
|
|
101
|
+
projects sharing a capability name (`manage`, `design`) would cross-credit each other.
|
|
102
|
+
|
|
103
|
+
This stays **mention-based and cheap** by design. Real produced/consumed symbol dependency is
|
|
104
|
+
`ssa-lowering` / `collision-ladder` territory — the boundary is deliberate. Only the resolved areas
|
|
105
|
+
are scored, so a one-area touch-set costs one matcher pass over the corpus rather than one per area.
|
|
106
|
+
|
|
107
|
+
## Run it
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
node "<skill>/scripts/blast-estimate.mts" --root <repo> --touch-set a,b,c \
|
|
111
|
+
[--layout 'sdd:.agents/specs/sdd,plugins/sdd/skills']... \
|
|
112
|
+
[--declared low|medium|high|unknown] [--format toon|json]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- `--root` defaults to `.` (the repo root the layouts' roots and
|
|
116
|
+
`.agents/sdd/sensitive-paths.toml` resolve against).
|
|
117
|
+
- `--layout` is repeatable (`<project>:<root1>,<root2>`, the same shape `touch-set-correction`
|
|
118
|
+
accepts). Omit it to auto-discover via `discover-specs`.
|
|
119
|
+
- `--touch-set` is a comma-separated list of `project/capability` ids.
|
|
120
|
+
- `--declared` is optional; omitted or `unknown` lines up as `no-declared` — the computed level still
|
|
121
|
+
returns on its own.
|
|
122
|
+
- Default output is **TOON**; `--format json` emits the full `EstimateResult`: `resolved`,
|
|
123
|
+
`unresolved` (surfaced, never dropped), `computed`, `reasons` (`count`, `maxFanIn`,
|
|
124
|
+
`sensitiveAreas`, `projectWide`), and `lineUp`.
|
|
125
|
+
|
|
126
|
+
## Boundaries
|
|
127
|
+
|
|
128
|
+
Read-only: it composes a corpus scan and the touch-set it is handed, and **returns** an estimate — it
|
|
129
|
+
writes no file and mutates no store. It does **not** judge compatibility/breakage (a separate
|
|
130
|
+
dimension), rank by surface location, decide HITL vs AFK, render a self-clear-or-escalate verdict (it
|
|
131
|
+
only *modulates* a conductor's judgment), *produce* a touch-set (it consumes one — pre-work declared,
|
|
132
|
+
or `touch-set-correction`'s post-work corrected one), or **reimplement node recovery** (it imports
|
|
133
|
+
`fileToNode`; it never reinvents or shells out to it).
|