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.
Files changed (133) hide show
  1. package/.claude-plugin/plugin.json +17 -0
  2. package/.codex-plugin/plugin.json +17 -0
  3. package/.plugin/plugin.json +17 -0
  4. package/README.md +159 -0
  5. package/agents/sdd-automaton.md +97 -0
  6. package/agents/sdd-impl-judge.md +214 -0
  7. package/agents/sdd-scanner.md +120 -0
  8. package/agents/sdd-spec-judge.md +224 -0
  9. package/agents/sdd-warden.md +101 -0
  10. package/package.json +24 -0
  11. package/skills/align-spec/README.md +20 -0
  12. package/skills/align-spec/SKILL.md +111 -0
  13. package/skills/align-spec/scripts/align-spec.mts +187 -0
  14. package/skills/architect-impl-governance/README.md +46 -0
  15. package/skills/architect-impl-governance/SKILL.md +45 -0
  16. package/skills/architect-spec-governance/README.md +48 -0
  17. package/skills/architect-spec-governance/SKILL.md +59 -0
  18. package/skills/blast-estimate/README.md +47 -0
  19. package/skills/blast-estimate/SKILL.md +133 -0
  20. package/skills/blast-estimate/scripts/blast-estimate.mts +583 -0
  21. package/skills/builder-impl-governance/README.md +47 -0
  22. package/skills/builder-impl-governance/SKILL.md +47 -0
  23. package/skills/builder-spec-governance/README.md +49 -0
  24. package/skills/builder-spec-governance/SKILL.md +36 -0
  25. package/skills/check-partition-quality/README.md +22 -0
  26. package/skills/check-partition-quality/SKILL.md +51 -0
  27. package/skills/check-partition-quality/scripts/check-partition-quality.mts +336 -0
  28. package/skills/check-plan-safety/README.md +17 -0
  29. package/skills/check-plan-safety/SKILL.md +60 -0
  30. package/skills/check-plan-safety/scripts/check-plan-safety.mts +145 -0
  31. package/skills/check-project-specs/README.md +19 -0
  32. package/skills/check-project-specs/SKILL.md +69 -0
  33. package/skills/check-project-specs/scripts/check-project-specs.mts +217 -0
  34. package/skills/check-scenario-overlap/README.md +19 -0
  35. package/skills/check-scenario-overlap/SKILL.md +74 -0
  36. package/skills/check-scenario-overlap/scripts/check-scenario-overlap.mts +249 -0
  37. package/skills/check-spec-structure/README.md +17 -0
  38. package/skills/check-spec-structure/SKILL.md +66 -0
  39. package/skills/check-spec-structure/scripts/check-spec-structure.mts +346 -0
  40. package/skills/collision-ladder/README.md +18 -0
  41. package/skills/collision-ladder/SKILL.md +83 -0
  42. package/skills/collision-ladder/scripts/collision-ladder.mts +657 -0
  43. package/skills/combat-log-governance/README.md +13 -0
  44. package/skills/combat-log-governance/SKILL.md +257 -0
  45. package/skills/concept-index/README.md +13 -0
  46. package/skills/concept-index/SKILL.md +38 -0
  47. package/skills/concept-index/scripts/concept-index.mts +245 -0
  48. package/skills/discover-plans/README.md +16 -0
  49. package/skills/discover-plans/SKILL.md +74 -0
  50. package/skills/discover-plans/scripts/discover-plans.mts +212 -0
  51. package/skills/discover-specs/README.md +15 -0
  52. package/skills/discover-specs/SKILL.md +76 -0
  53. package/skills/discover-specs/scripts/discover-specs.mts +396 -0
  54. package/skills/doctrine-loop/README.md +15 -0
  55. package/skills/doctrine-loop/SKILL.md +97 -0
  56. package/skills/formation-loop/README.md +17 -0
  57. package/skills/formation-loop/SKILL.md +140 -0
  58. package/skills/gate-validation-governance/README.md +12 -0
  59. package/skills/gate-validation-governance/SKILL.md +87 -0
  60. package/skills/impl-producer-governance/README.md +48 -0
  61. package/skills/impl-producer-governance/SKILL.md +85 -0
  62. package/skills/init/README.md +27 -0
  63. package/skills/init/SKILL.md +68 -0
  64. package/skills/init/scripts/wire-statusline.mts +276 -0
  65. package/skills/lifecycle-governance/README.md +11 -0
  66. package/skills/lifecycle-governance/SKILL.md +168 -0
  67. package/skills/manage/README.md +9 -0
  68. package/skills/manage/SKILL.md +62 -0
  69. package/skills/manage-ignore/README.md +19 -0
  70. package/skills/manage-ignore/SKILL.md +52 -0
  71. package/skills/manage-ignore/scripts/manage-ignore.mts +294 -0
  72. package/skills/manage-scenario-bridge/README.md +20 -0
  73. package/skills/manage-scenario-bridge/SKILL.md +60 -0
  74. package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.mts +156 -0
  75. package/skills/manage-spec-anchors/README.md +18 -0
  76. package/skills/manage-spec-anchors/SKILL.md +56 -0
  77. package/skills/manage-spec-anchors/scripts/manage-spec-anchors.mts +328 -0
  78. package/skills/mission-graph/README.md +15 -0
  79. package/skills/mission-graph/SKILL.md +67 -0
  80. package/skills/mission-graph/scripts/mission-graph.mts +844 -0
  81. package/skills/oracle-spec-governance/README.md +45 -0
  82. package/skills/oracle-spec-governance/SKILL.md +45 -0
  83. package/skills/ownership-governance/README.md +65 -0
  84. package/skills/ownership-governance/SKILL.md +104 -0
  85. package/skills/pause-mission/README.md +18 -0
  86. package/skills/pause-mission/SKILL.md +112 -0
  87. package/skills/place-node/README.md +12 -0
  88. package/skills/place-node/SKILL.md +47 -0
  89. package/skills/place-node/scripts/place-node.mts +157 -0
  90. package/skills/plan-retirement/README.md +32 -0
  91. package/skills/plan-retirement/SKILL.md +90 -0
  92. package/skills/plan-retirement/scripts/retire-plans.mts +196 -0
  93. package/skills/plugin-contract-governance/README.md +12 -0
  94. package/skills/plugin-contract-governance/SKILL.md +112 -0
  95. package/skills/remediation-governance/README.md +46 -0
  96. package/skills/remediation-governance/SKILL.md +78 -0
  97. package/skills/resolve-governances/README.md +18 -0
  98. package/skills/resolve-governances/SKILL.md +50 -0
  99. package/skills/resolve-governances/scripts/resolve-governances.mts +515 -0
  100. package/skills/resolve-tracking/SKILL.md +64 -0
  101. package/skills/resolve-tracking/scripts/resolve-tracking.mts +213 -0
  102. package/skills/resume-mission/README.md +12 -0
  103. package/skills/resume-mission/SKILL.md +53 -0
  104. package/skills/scaffold-project-spec/README.md +7 -0
  105. package/skills/scaffold-project-spec/SKILL.md +192 -0
  106. package/skills/sdd/README.md +7 -0
  107. package/skills/sdd/SKILL.md +92 -0
  108. package/skills/solution-producer-governance/README.md +9 -0
  109. package/skills/solution-producer-governance/SKILL.md +44 -0
  110. package/skills/spec-format-governance/README.md +73 -0
  111. package/skills/spec-format-governance/SKILL.md +114 -0
  112. package/skills/spec-gate/README.md +26 -0
  113. package/skills/spec-gate/SKILL.md +201 -0
  114. package/skills/spec-gate/scripts/check-spec-state.mts +601 -0
  115. package/skills/spec-gate/scripts/check-suite.mts +501 -0
  116. package/skills/spec-gate/scripts/classify-edit-class.mts +411 -0
  117. package/skills/spec-producer-governance/README.md +7 -0
  118. package/skills/spec-producer-governance/SKILL.md +86 -0
  119. package/skills/spec-structure-governance/README.md +40 -0
  120. package/skills/spec-structure-governance/SKILL.md +169 -0
  121. package/skills/ssa-lowering/README.md +26 -0
  122. package/skills/ssa-lowering/SKILL.md +181 -0
  123. package/skills/start-mission/README.md +7 -0
  124. package/skills/start-mission/SKILL.md +115 -0
  125. package/skills/suite-format-governance/README.md +75 -0
  126. package/skills/suite-format-governance/SKILL.md +299 -0
  127. package/skills/suite-format-governance/references/rubric.md +313 -0
  128. package/skills/touch-set-correction/README.md +16 -0
  129. package/skills/touch-set-correction/SKILL.md +67 -0
  130. package/skills/touch-set-correction/scripts/touch-set-correction.mts +418 -0
  131. package/skills/verify-scenarios/README.md +17 -0
  132. package/skills/verify-scenarios/SKILL.md +109 -0
  133. package/skills/verify-scenarios/scripts/verify-scenarios.mts +386 -0
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: plan-retirement
3
+ description: "Partial Skill: invoke by name only — the SDD Doctrine loop's last retro step — the gated, idempotent tracked deletion of a retired mission plan. Invoked by the doctrine-loop Scanner, not user-triggered; the clearance contract lives in the body + README."
4
+ user-invocable: false
5
+ metadata:
6
+ internal: true
7
+ ---
8
+
9
+ # SDD Plan Retirement
10
+
11
+ The Doctrine loop's **last retro step** (`sdd:doctrine-loop`; the provenance shape is
12
+ `sdd:combat-log-governance`). Because plans are **tracked** (committed with
13
+ the work, not gitignored), a retired plan leaves the tree by a deliberate **tracked deletion** —
14
+ never a gitignore side effect. This skill carries a self-contained `.mts` sweep that, for each
15
+ cleared `<cr-ref>`, deletes that CR's whole **transient artifact set**: the plan pair
16
+ (`<cr-ref>.plan.md` + `<cr-ref>.log.jsonl`) plus its **transient CR-level planning briefs**
17
+ (`<cr-ref>.design.md`, `<cr-ref>.operations.md`, `<cr-ref>.evidence.md`) from `.agents/plans` —
18
+ so a retired CR leaves no orphan behind.
19
+
20
+ ## The transient briefs ride along, never gate
21
+
22
+ `design.md` / `operations.md` / `evidence.md` are optional, cr-ref-scoped planning briefs that
23
+ share the plan's lifetime. They retire alongside the plan pair but do not participate in the
24
+ retirement decision. Two boundaries:
25
+
26
+ - **They do not widen the distilled gate.** The gate keys on the combat log's presence only — a
27
+ cr-ref with briefs but no `log.jsonl` still retires without a distilling `strategy` entry. A
28
+ brief owes no distillation (its content was consumed by the mission itself).
29
+ - **They do not anchor presence.** `<cr-ref>.plan.md` is the sole presence signal. A brief
30
+ without a `plan.md` is left untouched.
31
+
32
+ ## Distill and delete are decoupled
33
+
34
+ - **Distill (early).** At `→ implemented`, the Scanner reads the concluded combat log and distills
35
+ recurring `cause`s into the ledger's `strategy` lines (`sdd:doctrine-loop`).
36
+ - **Delete (late).** This sweep runs as a **separate, later** step, gated on source = `done`/merged
37
+ **and** the plan distilled. Never delete an un-distilled plan (the retro never ran).
38
+
39
+ ## The clearance boundary — split by verifiability
40
+
41
+ The two gating signals split by what the sweep can check itself:
42
+
43
+ - **source = `done`/merged** — the **caller's judgment**: query the source natively (`github-NN` → GH
44
+ issue, `asana-<gid>` → Asana, `local-<slug>` → the local store); needs network/`gh`. The caller
45
+ passes the source-cleared set via `--retire`.
46
+ - **distilled** — **verified mechanically by the sweep**: a `strategy` entry with `distills ==
47
+ <cr-ref>` must exist in the project ledger (`--ledger`). The sweep keys on the structured
48
+ `distills` field, **never** a `<cr-ref>` that appears only in a strategy's `evidence`
49
+ cross-references, and an **unratified** distilling entry still counts
50
+ (`sdd:combat-log-governance`). Its absence is **fail-closed** — but only when a combat log
51
+ **exists**: a cr-ref whose `<cr-ref>.log.jsonl` was never written (a non-gated mission — hand-run,
52
+ chore-tracked, investigation — runs no gate cycle and emits no correction) has **nothing to
53
+ distill**, so it retires on clearance + presence alone. The fail-closed leaves an existing,
54
+ undistilled log's plan intact so its distillation can still be drafted.
55
+
56
+ Leaving the distilled half to the caller once let a plan + combat log be deleted before any
57
+ distillation existed (the evidence the distill was meant to preserve). Because the check is local,
58
+ the sweep does it itself. Only the genuinely non-local judgment (source status) stays with the caller.
59
+
60
+ ## Run the sweep
61
+
62
+ ```bash
63
+ node "<skill>/scripts/retire-plans.mts" \
64
+ --root .agents/plans \
65
+ --ledger .agents/specs/<project>/ledger \
66
+ --retire github-34,asana-7 [--dry-run]
67
+ ```
68
+
69
+ - **`--ledger <dir>`** points at the project's ledger directory (the `ledger/` sibling of the root
70
+ `spec.md`). **Required for any deletion** — omit it (or an unreadable dir) and the sweep
71
+ fail-closes: nothing is deleted (the no-log branch only applies once a ledger is present to consult).
72
+ - Deletes the transient artifact set (`<cr-ref>.plan.md`, `<cr-ref>.log.jsonl`,
73
+ `<cr-ref>.design.md`, `<cr-ref>.operations.md`, `<cr-ref>.evidence.md`) only for a `<cr-ref>` that
74
+ is cleared (`--retire`) **and** present on disk (`<cr-ref>.plan.md` exists) **and** either
75
+ distilled (a `strategy` with `distills == <cr-ref>` in `--ledger`) **or** has no combat log to
76
+ distill (no `<cr-ref>.log.jsonl` on disk). Each brief is deleted only if present; an absent one is
77
+ a no-op, same as the missing log half.
78
+ - **Fail-closed** — a plan not named in `--retire`, or whose combat log **exists** but has no
79
+ distilling ledger entry, is never touched (and neither are its briefs).
80
+ - **Idempotent** — a cleared `<cr-ref>` with no plan on disk (already retired, or an open CR the
81
+ caller declined to clear) is a no-op, even if a brief for it exists; the sweep is safe to re-run.
82
+ - `--dry-run` prints the planned deletions without touching the tree.
83
+
84
+ When `node` is absent, an agent performs the same decision by hand: for each cleared `<cr-ref>`, if
85
+ its `<cr-ref>.log.jsonl` **exists**, **first confirm a `strategy` entry with `distills == <cr-ref>`
86
+ exists in the project ledger** (not a mere `evidence` mention; unratified still counts) — if none,
87
+ skip it. A cr-ref with **no** `log.jsonl` has nothing to distill and needs no such entry. Only then
88
+ delete `<cr-ref>.plan.md`, `<cr-ref>.log.jsonl`, `<cr-ref>.design.md`, `<cr-ref>.operations.md`, and
89
+ `<cr-ref>.evidence.md` if present, touching nothing else — and only if `<cr-ref>.plan.md` is
90
+ present in the first place (a brief alone never triggers deletion).
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+ // Plan retirement — the Doctrine loop's last retro step (see sdd:combat-log-governance,
3
+ // "Plan retirement"). A retired plan leaves the tree by a deliberate, gated TRACKED
4
+ // DELETION, never a gitignore side effect: for each cleared <cr-ref>, the sweep deletes that
5
+ // CR's whole TRANSIENT ARTIFACT SET — the plan pair (<cr-ref>.plan.md + <cr-ref>.log.jsonl)
6
+ // plus its transient CR-level planning briefs (<cr-ref>.design.md, <cr-ref>.operations.md,
7
+ // <cr-ref>.evidence.md) — from `.agents/plans`.
8
+ //
9
+ // The two gating signals split by verifiability. Source = done/merged stays the CALLER's
10
+ // judgment: the source-status query (github-NN -> GH issue, asana-<gid> -> Asana,
11
+ // local-<slug> -> the local store) needs network/gh, so the Scanner (doctrine-loop delegate)
12
+ // determines it and passes the CLEARED set via --retire. Distilled is local and mechanically
13
+ // checkable, so the sweep VERIFIES IT ITSELF against the project ledger (--ledger <dir>): a
14
+ // `strategy` entry whose `distills` field equals the <cr-ref> must exist. The distilled gate
15
+ // only guards a cr-ref whose combat log EXISTS — there is something on disk it could still
16
+ // distill from, so retirement without a distilling entry would be data loss. A cr-ref whose
17
+ // <cr-ref>.log.jsonl was never written has nothing to distill from in the first place, so it
18
+ // retires on clearance + presence alone, no distilling entry required. This is the
19
+ // mechanical, fail-closed gate + filesystem act:
20
+ // - it deletes the transient artifact set for a cr-ref that is cleared AND present on disk
21
+ // AND (distilled per the ledger OR has no log.jsonl to distill from);
22
+ // - a missing/unreadable --ledger skips retirement for ALL cr-refs (the no-log branch only
23
+ // applies once a ledger is actually present to consult);
24
+ // - anything not cleared, not present, or already gone is a no-op (idempotent, safe to
25
+ // re-run);
26
+ // - it never touches a plan it was not explicitly cleared to retire (fail-closed).
27
+ //
28
+ // Two invariants on the transient briefs (design.md / operations.md / evidence.md), both
29
+ // deliberate:
30
+ // - THEY DO NOT WIDEN THE DISTILLED GATE. The gate keys on the combat log's presence only
31
+ // (discoverLogs / logPresent) — a cr-ref with briefs but no log.jsonl still retires
32
+ // without a distilling strategy. A brief owes no distillation (its content was consumed
33
+ // by the mission itself, not extracted into the ledger), so gating on one would re-strand
34
+ // the no-log mission class the no-log branch exists to rescue.
35
+ // - THEY DO NOT ANCHOR PRESENCE. discoverPlans (keyed on .plan.md) stays the sole presence
36
+ // signal. A cr-ref with a design.md but no plan.md is a no-op — the idempotency contract
37
+ // (a cleared cr-ref with no plan on disk deletes nothing) is the stronger guarantee.
38
+ //
39
+ // Pure functions are exported for node:test; running the file directly drives the CLI.
40
+ // No dependencies. Use --dry-run to print the planned deletions without touching the tree.
41
+
42
+ import { existsSync, readdirSync, readFileSync, unlinkSync } from 'node:fs'
43
+ import { join } from 'node:path'
44
+
45
+ const PLAN_SUFFIX = '.plan.md'
46
+ const LOG_SUFFIX = '.log.jsonl'
47
+ const DESIGN_SUFFIX = '.design.md'
48
+ const OPERATIONS_SUFFIX = '.operations.md'
49
+ const EVIDENCE_SUFFIX = '.evidence.md'
50
+
51
+ // The full transient artifact set a retired cr-ref owns, in deletion order: the plan pair
52
+ // (plan.md, log.jsonl) then the optional transient CR-level planning briefs (design.md,
53
+ // operations.md, evidence.md). The briefs are optional — the per-file existsSync guard in
54
+ // main() no-ops any that are absent, which is what lets a cr-ref with only some briefs retire
55
+ // cleanly.
56
+ export function transientArtifactFiles(crRef: string): string[] {
57
+ return [
58
+ `${crRef}${PLAN_SUFFIX}`,
59
+ `${crRef}${LOG_SUFFIX}`,
60
+ `${crRef}${DESIGN_SUFFIX}`,
61
+ `${crRef}${OPERATIONS_SUFFIX}`,
62
+ `${crRef}${EVIDENCE_SUFFIX}`,
63
+ ]
64
+ }
65
+
66
+ // Parse a --retire value (comma-separated cr-refs) into a clean, de-duplicated list.
67
+ export function parseCleared(value: string | undefined): string[] {
68
+ if (!value) return []
69
+ const seen = new Set<string>()
70
+ for (const raw of value.split(',')) {
71
+ const ref = raw.trim()
72
+ if (ref) seen.add(ref)
73
+ }
74
+ return [...seen]
75
+ }
76
+
77
+ // The cr-refs that have a <cr-ref>.plan.md on disk under `root`.
78
+ export function discoverPlans(root: string): string[] {
79
+ let entries: string[]
80
+ try {
81
+ entries = readdirSync(root)
82
+ } catch {
83
+ return []
84
+ }
85
+ return entries.filter((f) => f.endsWith(PLAN_SUFFIX)).map((f) => f.slice(0, -PLAN_SUFFIX.length))
86
+ }
87
+
88
+ // The cr-refs that have a <cr-ref>.log.jsonl on disk under `root`.
89
+ export function discoverLogs(root: string): string[] {
90
+ let entries: string[]
91
+ try {
92
+ entries = readdirSync(root)
93
+ } catch {
94
+ return []
95
+ }
96
+ return entries.filter((f) => f.endsWith(LOG_SUFFIX)).map((f) => f.slice(0, -LOG_SUFFIX.length))
97
+ }
98
+
99
+ // The cr-refs the project ledger records as DISTILLED — a `strategy` entry whose `distills`
100
+ // field equals the cr-ref exists in some *.jsonl shard under `ledgerDir`. Keys on the
101
+ // structured `distills` field only: a cr-ref named merely inside a strategy's `evidence`
102
+ // array (a cross-reference) does NOT count, and an unratified entry (`ratified: false`, the
103
+ // Scanner's default) still counts — the gate is about what was distilled, not sign-off.
104
+ // Malformed lines and a missing/unreadable ledger dir are tolerated: fail-closed means an
105
+ // unverifiable ledger yields an EMPTY set, never a thrown error.
106
+ export function distilledCrRefs(ledgerDir: string): Set<string> {
107
+ const distilled = new Set<string>()
108
+ let entries: string[]
109
+ try {
110
+ entries = readdirSync(ledgerDir)
111
+ } catch {
112
+ return distilled
113
+ }
114
+ for (const entry of entries) {
115
+ if (!entry.endsWith('.jsonl')) continue
116
+ let text: string
117
+ try {
118
+ text = readFileSync(join(ledgerDir, entry), 'utf8')
119
+ } catch {
120
+ continue
121
+ }
122
+ for (const line of text.split('\n')) {
123
+ const trimmed = line.trim()
124
+ if (!trimmed) continue
125
+ let record: unknown
126
+ try {
127
+ record = JSON.parse(trimmed)
128
+ } catch {
129
+ continue
130
+ }
131
+ if (record === null || typeof record !== 'object') continue
132
+ const { kind, distills } = record as { kind?: unknown; distills?: unknown }
133
+ if (kind === 'strategy' && typeof distills === 'string' && distills.length > 0) distilled.add(distills)
134
+ }
135
+ }
136
+ return distilled
137
+ }
138
+
139
+ // Fail-closed, idempotent decision: retire exactly the cr-refs that are cleared by the caller,
140
+ // present on disk, AND either distilled per the ledger OR have no combat log to distill from
141
+ // (log.jsonl absent). An uncleared or absent plan is never retired; a present plan whose log
142
+ // DOES exist still requires a distilling entry (data-loss guard, unchanged). Order follows the
143
+ // cleared list for stable reporting.
144
+ export function decideRetirements(
145
+ cleared: string[],
146
+ existing: string[],
147
+ distilled: Set<string>,
148
+ logPresent: Set<string>,
149
+ ): string[] {
150
+ const present = new Set(existing)
151
+ const seen = new Set<string>()
152
+ const out: string[] = []
153
+ for (const ref of cleared) {
154
+ if (present.has(ref) && (distilled.has(ref) || !logPresent.has(ref)) && !seen.has(ref)) {
155
+ seen.add(ref)
156
+ out.push(ref)
157
+ }
158
+ }
159
+ return out
160
+ }
161
+
162
+ export function main(argv: string[]): number {
163
+ const root = argv.includes('--root') ? argv[argv.indexOf('--root') + 1] : '.agents/plans'
164
+ const ledgerArg = argv.includes('--ledger') ? argv[argv.indexOf('--ledger') + 1] : undefined
165
+ const cleared = parseCleared(argv.includes('--retire') ? argv[argv.indexOf('--retire') + 1] : undefined)
166
+ const dryRun = argv.includes('--dry-run')
167
+
168
+ let retiring: string[]
169
+ if (!ledgerArg || !existsSync(ledgerArg)) {
170
+ process.stdout.write(
171
+ `no verifiable ledger (${ledgerArg ? `--ledger ${ledgerArg} unreadable` : '--ledger not given'}): distillation cannot be checked, so retirement is skipped for all cr-refs\n`,
172
+ )
173
+ retiring = []
174
+ } else {
175
+ const distilled = distilledCrRefs(ledgerArg)
176
+ const logPresent = new Set(discoverLogs(root))
177
+ retiring = decideRetirements(cleared, discoverPlans(root), distilled, logPresent)
178
+ }
179
+ const deleted: string[] = []
180
+
181
+ for (const ref of retiring) {
182
+ for (const file of transientArtifactFiles(ref)) {
183
+ const path = join(root, file)
184
+ if (!existsSync(path)) continue // the log may be absent; delete what is there
185
+ if (!dryRun) unlinkSync(path)
186
+ deleted.push(path)
187
+ }
188
+ }
189
+
190
+ const verb = dryRun ? 'would delete' : 'deleted'
191
+ for (const path of deleted) process.stdout.write(`${verb} ${path}\n`)
192
+ process.stdout.write(`${dryRun ? 'dry-run: ' : ''}retired ${retiring.length} plan(s), ${deleted.length} file(s)\n`)
193
+ return 0
194
+ }
195
+
196
+ if (import.meta.main) process.exit(main(process.argv.slice(2)))
@@ -0,0 +1,12 @@
1
+ # plugin-contract-governance
2
+
3
+ Internal SDD governance (`user-invocable: false`). The **plugin contract** — what an SDD plugin must
4
+ implement: the five delegate roles (closed set), the per-role governance loadout (the Model-B
5
+ `(actor, gate)` bars + the fixed-universal set), and the `sdd-plugins[]` registry entry shape +
6
+ resolution by `artifact-type`.
7
+
8
+ A **single-owner** governance — its consumer family is the plugin/conductor resolution surface, so it
9
+ lives under `plugin/`, not `common-governances/`. Loaded by the conductor and by plugin authors. The
10
+ universal-plugin *format* is `plugin-design` (out of scope); resolution/composition mechanics are the
11
+ `resolve-governances` skill; the actor bars are the shipped
12
+ `sdd:{oracle,builder,architect}-{spec,impl}-governance` skills. Not triggered by users directly.
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: plugin-contract-governance
3
+ description: "Partial Skill: invoke by name only — the SDD plugin contract for what a plugin implements. Loaded by the conductor and by plugin authors, not user-triggered."
4
+ user-invocable: false
5
+ ---
6
+
7
+ # SDD Plugin Contract Governance
8
+
9
+ What an SDD plugin must implement and what each part loads. The conductor resolves delegates against
10
+ this contract; a plugin author builds to it. The universal-plugin format itself is `plugin-design`
11
+ (`governance show universal-plugin`); this skill is the SDD-role layer on top.
12
+
13
+ ## The five delegate roles (closed set)
14
+
15
+ A plugin covers a set of artifact-types by providing agents for these role keys. Any role may be
16
+ `null` (degenerates to the SDD default) or omitted (falls back to the convention name
17
+ `<plugin>-<role>`). A **producer** role may also name a model-tuned agent to run at its own
18
+ model/effort — the model-tuning escape valve; naming any agent (plugin delegate or model-tuned)
19
+ means the conductor **spawns** it.
20
+
21
+ | Role key | Acts | SDD default |
22
+ |---|---|---|
23
+ | `spec-producer` | writes the `spec.md` body + the `.feature` | conductor loads `spec-producer-governance`, authors inline (`sdd:automaton`) |
24
+ | `solution-producer` | writes `<unit>.solution.md` (the durable, ungated design fork) | conductor loads `solution-producer-governance`, authors inline (`sdd:automaton`) |
25
+ | `spec-judge` | judges `spec.md` + the `.feature` at the spec gate | `sdd-spec-judge` — spawned cold agent |
26
+ | `impl-producer` | builds the artifact **and** its verification | conductor loads `impl-producer-governance`, dispatches a generic builder (`sdd:automaton`) |
27
+ | `impl-judge` | runs the verification against the frozen `.feature` | `sdd-impl-judge` — spawned cold agent |
28
+
29
+ **Producers run inline (or via a mechanical builder), judges spawn cold** ("conductor writes, cold
30
+ judges grade"): an SDD-default spec/solution-producer is a governance the conductor loads and runs in
31
+ its own warm main-session context (recorded `produced-by.<role>: sdd:automaton`); the SDD-default
32
+ impl-producer is mechanical and spawned via a generic builder; an SDD-default judge is a cold agent
33
+ the conductor spawns, because a grader must not share the author's context. A plugin delegate — or a
34
+ model-tuned producer agent named for the slot — is always spawned.
35
+
36
+ Any of the spawns above may instead be realized through a general-purpose dispatch capability's
37
+ `subagent | channel` seam (ADR-0023, referenced by intent only) — an alternative realization of the
38
+ same spawn, not a change to which roles spawn or how they are graded.
39
+
40
+ > The legacy role key was `plan-producer` (writing `plan.md` + `tasks.md`); it is renamed
41
+ > **`solution-producer`** writing `<unit>.solution.md` (`sdd:combat-log-governance`). A live registry
42
+ > still carrying `plan-producer` is migrated on encounter.
43
+
44
+ ## Which governances each role loads
45
+
46
+ Bars are the Model-B `(actor, gate)` governances (matched by the `resolve-governances` skill; the
47
+ actor bars are the shipped `sdd:{oracle,builder,architect}-{spec,impl}-governance` skills); a producer
48
+ self-aligns to exactly the bars its judge grades. The lens sets are spec gate `{oracle, builder,
49
+ architect}`, impl gate `{builder, architect}`, solution `{architect}` (ungated).
50
+
51
+ | Role | Loads |
52
+ |---|---|
53
+ | spec-producer | `spec-format`, `suite-format`, `ownership`, the resolved `oracle-spec` + `builder-spec` bars |
54
+ | solution-producer | `ownership`, the resolved `architect-spec` bar |
55
+ | spec-judge | `spec-format`, `suite-format`, `lifecycle`, `gate-validation`, the resolved `oracle-spec` + `builder-spec` + `architect-spec` bars |
56
+ | impl-producer | `ownership`, the resolved `builder-impl` + `architect-impl` bars |
57
+ | impl-judge | `ownership`, `gate-validation`, the resolved `builder-impl` + `architect-impl` bars |
58
+
59
+ For an **SDD-default producer** role, the conductor additionally loads the matching
60
+ `spec-producer-governance` / `solution-producer-governance` / `impl-producer-governance` — the
61
+ procedure it runs — which itself references the bars above; a plugin delegate carries its own
62
+ procedure and loads these bars directly.
63
+
64
+ The `sdd` gateway loads **no** governance (it only classifies and routes). The gate skill
65
+ `spec-gate` loads `lifecycle`, `ownership`, `gate-validation`, `combat-log` (+ `spec-format` /
66
+ `suite-format` at the spec gate). A plugin's agents inherit the universal loads — e.g. `aced`/`quill`
67
+ spec-producers load `sdd:spec-format-governance` + `sdd:ownership-governance`, and their judges load
68
+ `sdd:gate-validation-governance`.
69
+
70
+ ## Registry shape
71
+
72
+ The conductor reads **only** `.agents/universal-plugin.json` (top-level `sdd-plugins[]`) — it does
73
+ not scan plugin directories. Each entry:
74
+
75
+ ```json
76
+ {
77
+ "name": "<plugin>",
78
+ "version": "<semver>",
79
+ "squads": [
80
+ {
81
+ "artifact-types": ["<artifact-type>", "..."],
82
+ "roles": {
83
+ "spec-producer": "<agent | null>",
84
+ "solution-producer": "<agent | null>",
85
+ "spec-judge": "<agent | null>",
86
+ "impl-producer": "<agent | null>",
87
+ "impl-judge": "<agent | null>"
88
+ },
89
+ "governances": {
90
+ "oracle-spec": "<name | null>",
91
+ "builder-spec": "<name | null>",
92
+ "builder-impl": "<name | null>",
93
+ "architect-spec": "<name | null>",
94
+ "architect-impl": "<name | null>"
95
+ }
96
+ }
97
+ ]
98
+ }
99
+ ```
100
+
101
+ A plugin declares one or more **squads**, each serving a **set of artifact-types** with one
102
+ production chain; a type appears in at most one squad per plugin (the plugin's served set = the union
103
+ of its squads' `artifact-types`).
104
+
105
+ Resolution: match each file's **`artifact-type`** (the squad key, **not** the folder name) against
106
+ each plugin's `squads[]` — the squad whose `artifact-types` contains it serves the file (e.g. ACED's
107
+ one squad covers `skill`, `subagent`, `command`, `agents-section`). An absent or unmatched
108
+ `artifact-type` → all roles degenerate to SDD defaults. One matching squad → resolve each role and
109
+ governance key (name = use it; `null` = SDD default; missing role key = `<plugin>-<role>`). Two or
110
+ more plugins claiming the type → return `STATUS: needs-input` for the skill to ask which plugin owns
111
+ it; the choice is recorded as `.agents/sdd/` resolution state (**distinct from `produced-by`**),
112
+ decisive on resume.
@@ -0,0 +1,46 @@
1
+ # remediation-governance
2
+
3
+ This is an internal SDD governance about **what a producer does when a gate sends work back**.
4
+
5
+ A gate returns one of three verdicts. `approve` and `reject` end the round. **`change`** sends the
6
+ work back with findings — and this bar governs what happens next.
7
+
8
+ Its single claim: **the findings are evidence, not a work order.** The tempting response is to read
9
+ them as a task list and edit each cited line. That fixes the lines and leaves the defect, because a
10
+ judge can only ever name the instances it happened to see.
11
+
12
+ ## What it requires — the four rules
13
+
14
+ | Rule | What it means |
15
+ | --- | --- |
16
+ | **Substantiate first** | A finding is a claim about the artifact, and claims can be wrong. Check it before changing anything. One that does not hold is **contested** — send back the evidence and change nothing. |
17
+ | **State the rule, then sweep** | The finding names one instance; the actual defect is the rule it breaks. Name that rule, then search for every other place it is broken — in a script, so anyone can re-run it. Report what you looked at and **ruled out**, not only what you found. |
18
+ | **Re-derive against the governing rule** | Check the fix against the rule that governs the artifact, not just against the finding. "Does it still trip the finding?" is weak. "Is what it now says **true**?" is the question — a fix that satisfies the finding while breaking a rule the artifact is bound by is worse than the original defect. |
19
+ | **Account for provenance** | For each finding, ask whether the artifact it names was changed by the **last** round of fixes. If so it is a **regression** — the fixes are creating work rather than finishing it, and the loop stops for a re-plan instead of running again. |
20
+
21
+ ## Sweeping is not string-matching
22
+
23
+ The most common way rule 2 goes wrong is a blanket search. A term retired in one place may be
24
+ correct in another, and the same word may name a different thing in a sibling project. Three
25
+ separations matter: **use vs mention** (deploying a retired term versus naming it *to say* it is
26
+ deprecated), **scope** (the live spec is bound; ADRs and ledger lines are history and are never
27
+ rewritten), and **word boundaries** (a substring is not an instance).
28
+
29
+ ## Usage
30
+
31
+ - **spec-producer:** responding to a `change` verdict at the **spec gate**
32
+ - **impl-producer:** responding to a `change` verdict at the **impl gate**
33
+
34
+ Both return the trace — rule, sweep hits, ruled-out candidates, provenance — in their `Output`, which
35
+ is what lets the next judge check the remediation instead of taking it on trust.
36
+
37
+ ## Related governances
38
+
39
+ - **`spec-producer-governance`** / **`impl-producer-governance`** — the producers that load this bar;
40
+ they own *how the work is done*, this bar owns *how a returned verdict is answered*.
41
+ - **`gate-validation-governance`** — which gate states are legal; this bar is silent on state and
42
+ governs only the response.
43
+ - **`suite-format-governance`** / **`spec-format-governance`** — the rules a correction is re-derived
44
+ against under rule 3.
45
+
46
+ Internal SDD governance (`user-invocable: false`). Not triggered by users directly.
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: remediation-governance
3
+ description: "Partial Skill: invoke by name only — the SDD remediation bar: how a producer responds to a `change` verdict at either gate. Loaded by the spec-producer and the impl-producer, not user-triggered."
4
+ user-invocable: false
5
+ ---
6
+
7
+ # Remediation Governance — responding to a `change` verdict
8
+
9
+ A gate verdict's findings are **evidence to reason from**, never a task list to execute. Working down
10
+ the list edit-by-edit fixes cited lines while leaving the defect, and can introduce defects the next
11
+ round then reports.
12
+
13
+ This bar applies at **both** gates — the spec gate and the impl gate — and is loaded by whichever
14
+ producer is responding.
15
+
16
+ ## The four rules
17
+
18
+ 1. **Substantiate each finding first.** A finding is a **hypothesis**. Verify it against the artifact
19
+ before touching anything. One you cannot substantiate is **contested** — return your evidence and
20
+ edit nothing. Fixing an unverified finding is how a vague line becomes a wrong one.
21
+ 2. **State the rule, then sweep.** A judge names an **instance**; the defect is the **rule**. Name
22
+ the rule the finding instantiates and sweep for every other instance — in a script, so the result
23
+ is reproducible. Return the sweep's **negative** half too: the candidates inspected and excluded,
24
+ so the next reader need not re-run it.
25
+ 3. **Re-derive the correction against the rule governing the artifact**, not merely against the
26
+ finding. "Does this still trip the finding?" is the weak question. "Is what it now says **true**?"
27
+ is the one that matters — a correction that clears the finding while contradicting a governance
28
+ the artifact is bound by is a worse defect than the one it replaced.
29
+ 4. **Account for each finding's provenance.** A finding is a **regression** when the artifact it
30
+ names was changed by the **previous remediation round's commits**; it is **pre-existing** when the
31
+ artifact predates them. Any regression means the loop is **no longer converging**: stop, report
32
+ it, and re-plan. Do not open another remediation round on a regressing loop.
33
+
34
+ ## A sweep is scope-aware, never a blanket match
35
+
36
+ Rule 2's sweep answers "every instance of the rule", which is **not** "every occurrence of a string".
37
+ Before acting on a sweep, separate:
38
+
39
+ - **use vs mention** — a retired term deployed as if current is the defect; the same term named *in
40
+ order to* mark it deprecated, or preserved in an append-only record, is correct.
41
+ - **scope** — the live project spec is bound by the rule; ADRs and ledger lines are history and are
42
+ never rewritten to match current vocabulary; a sibling tree may use the same word for a different
43
+ concept.
44
+ - **word boundaries** — a substring hit is not an instance.
45
+
46
+ Report the excluded candidates with the reason each was excluded. A sweep that reports only its hits
47
+ cannot be checked, and invites the next producer to re-run it.
48
+
49
+ ## What the producer returns
50
+
51
+ Remediation is **verifiable only if it leaves a trace**. Each finding answered carries, in the
52
+ producer's `Output`:
53
+
54
+ ```
55
+ REMEDIATION:
56
+ <finding>: verdict=<remediated | contested>
57
+ rule=<the rule the finding instantiates>
58
+ swept=<the other instances found, or none>
59
+ ruled-out=<candidates inspected and excluded, with the reason>
60
+ provenance=<pre-existing | regression>
61
+ ```
62
+
63
+ A `contested` finding carries the evidence against it and **no edit** to the artifact it named.
64
+
65
+ ## Key points (read-check)
66
+
67
+ 1. **A verdict is evidence, not a work order** — remediating cited lines one at a time is the defect
68
+ this bar exists to prevent.
69
+ 2. **Substantiate before acting** — an unsubstantiated finding is contested with evidence, not edited
70
+ away.
71
+ 3. **A finding names an instance; the defect is the rule** — sweep for every instance, and report the
72
+ ruled-out candidates as well as the hits.
73
+ 4. **A sweep is scope-aware** — use vs mention, scope, and word boundaries; a string match is not an
74
+ instance.
75
+ 5. **Re-derive the correction against the rule governing the artifact**, not against the finding
76
+ alone.
77
+ 6. **Provenance is derived from the diff** — an artifact changed by the previous round's commits
78
+ makes its finding a **regression**, which stops the loop for a re-plan rather than another round.
@@ -0,0 +1,18 @@
1
+ # resolve-governances
2
+
3
+ The concrete engine for **SDD governance resolution**.
4
+ A non-user-invocable skill carrying a self-contained `.mts` script that, for a touched file's
5
+ artifact-type, resolves each production-chain role to its agent plus the resolved-actor bars it loads
6
+ — matching candidates across the project's `.agents/governances/` anchors, the matched plugin squad,
7
+ and the sdd defaults.
8
+
9
+ - **Skill contract:** [`SKILL.md`](./SKILL.md)
10
+ - **Script:** [`scripts/resolve-governances.mts`](./scripts/resolve-governances.mts)
11
+ - **Tests:** [`scripts/resolve-governances.test.mts`](./scripts/resolve-governances.test.mts) (`node:test`)
12
+
13
+ ```bash
14
+ node scripts/resolve-governances.mts --root . --artifact-type skill
15
+ ```
16
+
17
+ Consumed by the conductor (`start-mission`) and the cold judges (`sdd-spec-judge`, `sdd-impl-judge`)
18
+ to load the right bars without hand-enumerating.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: resolve-governances
3
+ description: "Partial Skill: invoke by name only — the SDD governance matcher, resolving which actor-bar governances apply to a touched file — run by the conductor and the cold spec/impl judges, not triggered by users directly."
4
+ user-invocable: false
5
+ metadata:
6
+ internal: true
7
+ ---
8
+
9
+ # Resolve Governances
10
+
11
+ The concrete engine for **SDD governance resolution**. For a
12
+ touched file's **artifact-type** it names, per production-chain role, **which agent runs it** and
13
+ **which resolved-actor bar candidates it loads** — matching governances across the caller-passed
14
+ project anchors, the matched plugin squad (from the project registry
15
+ `.agents/universal-plugin.json`), and the sdd defaults. It is a **dumb matcher**: it returns each
16
+ bar's candidates **bucketed by tier** and does **not** order by precedence or apply `compose` — the
17
+ consuming agent composes. The conductor and the cold judges run it so they **never hand-enumerate**
18
+ the bars. It carries a self-contained `.mts` script (the repo's node-≥23.6 / no-deps convention).
19
+
20
+ ## Run the resolution
21
+
22
+ ```bash
23
+ node "<skill>/scripts/resolve-governances.mts" --root . --artifact-type <type> --project <path> [--project-root <path>]
24
+ ```
25
+
26
+ - `--root` is the **registry** location (default `.`) — `.agents/universal-plugin.json`.
27
+ - `--project <path>` is the file's own project anchor (defaults to `--root`); `--project-root <path>`
28
+ is the outer shared layer in a **monorepo** (omit for a single-project repo). Anchors are
29
+ **caller-passed**, never discovered — the conductor knows the project from `discover-specs`'
30
+ `project-path` or context.
31
+ - `--artifact-type <type>` emits the per-role plan as JSON: each role carries its resolved `agent`
32
+ and the **resolved-actor `bars`** only. Each bar's `candidates` are **bucketed by tier** —
33
+ `project` / `project-root` (direct-read file paths) and `plugin` / `sdd` (`<plugin>:<bar>` /
34
+ `sdd:<name>` harness-load refs). The **fixed-universal** governances are invariant per role and
35
+ stay declared in the role/agent definition — the matcher does not emit them.
36
+ - `--path <file>` (no `--artifact-type`) consults the optional tiebreaker map
37
+ `.agents/sdd/artifact-types.toml`; a no-match prints a classify-by-convention note.
38
+ - No `--artifact-type` and no `--path` → validates the registry is well-formed + unambiguous
39
+ (`governance registry OK`, or per-line violations).
40
+
41
+ When `node` is absent, an agent performs the same matching by hand: read the registry, match each
42
+ touched file's artifact-type to a squad, and name each role's agent + bar candidates per tier.
43
+
44
+ ## Boundaries
45
+
46
+ It owns no lifecycle state and writes nothing — it **names** candidates; the consuming agent loads
47
+ each (direct-read for project files, harness-load for plugin/sdd skills), reads each governance's own
48
+ `compose`, and composes by precedence `sdd-default < plugin < project-root < project` (most-specific
49
+ wins; `replace` supersedes). Registry matching is deterministic; **disambiguating** an artifact-type
50
+ claimed by two plugins is the consumer's agentic step (the plan returns `status: needs-input`).