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,45 @@
|
|
|
1
|
+
# oracle-spec-governance
|
|
2
|
+
|
|
3
|
+
This is an internal SDD governance about scope and the kill-or-ship call at the spec gate.
|
|
4
|
+
|
|
5
|
+
Before a capability's spec is approved, someone has to ask the uncomfortable questions: is this one
|
|
6
|
+
thing or two things glued together? Is it worth building at all? Does every scenario in its suite
|
|
7
|
+
actually belong to it? This governance is that question list — the **Oracle** bar. It judges the
|
|
8
|
+
**capability itself**, read from its spec and suite together — not how the document is written,
|
|
9
|
+
which is a different bar (`spec-format-governance`).
|
|
10
|
+
|
|
11
|
+
## What it requires — the bar
|
|
12
|
+
|
|
13
|
+
| Check | What it demands |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| **One coherent intent** | The capability delivers a single nameable outcome. Two concerns are two capabilities — split them, each into its own node. Test: can you name the outcome without "and"? |
|
|
16
|
+
| **Bounded, stated scope** | What is out of scope is named. A capability that keeps absorbing adjacent problems is scope creep — cut it back. |
|
|
17
|
+
| **The node owns its decisions** | Every scenario tests a decision the node owns. A property co-owned across a seam — activation/routing, a sibling's behavior, harness wiring — is out of scope: relocate it to the node that owns it, or kill it. |
|
|
18
|
+
| **Strict — non-decisions are killed** | An invariant that always holds is not acceptance and does not enter the suite. The one exception is a user `@pinned` scenario, kept whatever strict prunes. |
|
|
19
|
+
| **Worth shipping, or kill** | The Why names a real problem and who feels it. If the value does not clear the cost of building, the verdict is kill. |
|
|
20
|
+
| **Kill-or-revert** | A capability that passes every check but proves fatal goes back to Draft — surface the deal-breaker. |
|
|
21
|
+
| **No premature commitment** | A decision that need not be made yet is deferred to the last responsible moment. |
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
- **spec-producer:** self-aligns on scope against this bar before writing the spec
|
|
26
|
+
- **spec-judge:** grades kill-or-ship cold at the **spec-gate**
|
|
27
|
+
|
|
28
|
+
The same bar is loaded by both faces, but producer and judge stay separate agents. Oracle has no
|
|
29
|
+
impl-gate face, so this is the only Oracle bar. It is SDD's default for the `oracle` bar — a plugin
|
|
30
|
+
may bind its own per artifact-type, and this one loads only when the registry leaves `oracle`
|
|
31
|
+
unbound.
|
|
32
|
+
|
|
33
|
+
## Related governances
|
|
34
|
+
|
|
35
|
+
This bar owns whether the capability is the right thing to commit to. Its neighbors own everything
|
|
36
|
+
around that:
|
|
37
|
+
|
|
38
|
+
- **`spec-format-governance`** — how the `spec.md` document itself is structured and written; this
|
|
39
|
+
bar deliberately does not read the prose.
|
|
40
|
+
- **`builder-spec-governance`** — the Builder bar at the same gate: testability and coverage of the
|
|
41
|
+
`.feature`, not scope.
|
|
42
|
+
- **`architect-spec-governance`** — the Architect bar at the same gate: structural fit of the spec
|
|
43
|
+
and solution, not whether it should exist.
|
|
44
|
+
|
|
45
|
+
Internal SDD governance (`user-invocable: false`). Not triggered by users directly.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oracle-spec-governance
|
|
3
|
+
description: "Partial Skill: invoke by name only"
|
|
4
|
+
user-invocable: false
|
|
5
|
+
metadata:
|
|
6
|
+
actor: oracle
|
|
7
|
+
gate: spec
|
|
8
|
+
compose: union
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Oracle-Spec Governance — the scope & kill-or-ship bar
|
|
12
|
+
|
|
13
|
+
The **Oracle** bar at the **spec gate**: is this **capability** worth committing, and is its scope the
|
|
14
|
+
node's to hold? Judges the capability (read from its spec + suite), not the document's prose — that is
|
|
15
|
+
`sdd:spec-format-governance`. Loaded by both faces — the spec-producer self-aligns on scope before
|
|
16
|
+
writing, the cold spec-judge judges kill-or-ship. Oracle has no impl face. The SDD default for the
|
|
17
|
+
`oracle` bar; a plugin may bind its own per artifact-type, and this loads when the registry leaves
|
|
18
|
+
`oracle` unbound.
|
|
19
|
+
|
|
20
|
+
## The bar
|
|
21
|
+
|
|
22
|
+
- **One coherent intent.** The capability delivers a single nameable outcome. **Two concerns are two
|
|
23
|
+
capabilities** — split them, each into its own node. Test: can you name the outcome without "and"?
|
|
24
|
+
- **Scope is bounded and stated.** What is out of scope is named. A capability that keeps absorbing
|
|
25
|
+
adjacent problems is scope creep — cut it back.
|
|
26
|
+
- **The suite's decisions are the node's to hold.** Every scenario tests a **decision the node owns**.
|
|
27
|
+
A property **co-owned** across a seam — activation/routing, a sibling's behavior, harness wiring —
|
|
28
|
+
is out of scope: relocate it to the node that owns it, or kill it.
|
|
29
|
+
- **Strict — a non-decision is killed.** An **invariant** that always holds is not acceptance and does
|
|
30
|
+
not enter the suite. The one exception is a user **`@pinned`** scenario, kept whatever strict prunes.
|
|
31
|
+
- **Worth shipping, or kill.** The Why names a real problem and who feels it. If value does not clear
|
|
32
|
+
the cost of building, the verdict is **kill**.
|
|
33
|
+
- **Kill-or-revert is allowed.** A capability that passes every check but proves fatal goes back to
|
|
34
|
+
Draft — surface the deal-breaker.
|
|
35
|
+
- **No premature commitment.** Defer a decision that need not be made yet to the last responsible
|
|
36
|
+
moment.
|
|
37
|
+
|
|
38
|
+
## Key points (read-check)
|
|
39
|
+
|
|
40
|
+
1. **One coherent intent** — two concerns are two capabilities; bounded and stated scope.
|
|
41
|
+
2. **Every scenario tests a decision the node owns** — a co-owned seam property is out of scope
|
|
42
|
+
(relocate or kill).
|
|
43
|
+
3. **Strict** — an invariant / non-decision does not enter the suite; only a user `@pinned` scenario
|
|
44
|
+
escapes.
|
|
45
|
+
4. **Worth shipping or kill** — value must clear the build cost; a fatal proof reverts to Draft.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# ownership-governance
|
|
2
|
+
|
|
3
|
+
This is an internal SDD governance about write ownership.
|
|
4
|
+
|
|
5
|
+
It answers one question for every artifact in the SDD workflow: **who may write it — and who never
|
|
6
|
+
may.** Every act in the workflow has a write leash, and this skill is the canonical matrix. One
|
|
7
|
+
writer per field or artifact; no role writes outside the spec it owns or spawns specs on its own.
|
|
8
|
+
|
|
9
|
+
## Who writes what
|
|
10
|
+
|
|
11
|
+
The full matrix lives in the skill body; condensed, the ownership falls into a few hands:
|
|
12
|
+
|
|
13
|
+
| Artifact | Written by | Notable exclusions |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `spec.md` body + the `.feature` | the **spec-producer** | the conductor, judges, other producers |
|
|
16
|
+
| A `@pinned` scenario | the **user** (in-session) | every agent role — it proposes, never executes |
|
|
17
|
+
| `<unit>.solution.md` | the **solution-producer** | the spec-producer, judges |
|
|
18
|
+
| Implementation + its verification | the **impl-producer** | the impl-judge (it *runs*, never authors) |
|
|
19
|
+
| `status` + human ratification of `approval` | the **gate skill** (`spec-gate`), in-session position only | the conductor, any producer |
|
|
20
|
+
| Coordination state — `<!-- open: -->` markers, `produced-by`, run-level leash, self-asserted approvals, plan brief, combat-log and ledger lines | the **conductor** | producers, judges, the gate skill |
|
|
21
|
+
| Ledger `strategy` lines | the doctrine-loop **Scanner** | everyone else |
|
|
22
|
+
|
|
23
|
+
Appended log and ledger lines carry a pseudonymous `handle`; the in-file attribution is advisory —
|
|
24
|
+
the git commit signature is the attestation.
|
|
25
|
+
|
|
26
|
+
## The three standing rules
|
|
27
|
+
|
|
28
|
+
- **Producer write boundary.** A spec-producer writes the `spec.md` body and the `.feature` only —
|
|
29
|
+
never the control frontmatter or the resolution state. A required input it cannot supply comes
|
|
30
|
+
back as a `CONTENT_GAP`; the conductor turns it into an `<!-- open: -->` marker. Producers never
|
|
31
|
+
write markers directly.
|
|
32
|
+
- **Ratification authority is positional.** A human-attributed gate write belongs to the in-session
|
|
33
|
+
position holding the real user channel. A headless conductor — a spawned subagent with no user
|
|
34
|
+
channel — writes only self-assertions and pauses; it never writes a human ratification, even when
|
|
35
|
+
a coordinator relays "the user approved". A relayed claim is not user confirmation.
|
|
36
|
+
- **Never write a frozen `.feature`.** Once a suite carries `@frozen`, no role may add, remove, or
|
|
37
|
+
rewrite its scenarios. A gap that requires changing specified behavior is a `BLOCKER` returned
|
|
38
|
+
upward, never an in-place edit. The freeze binds the contract only — the combat log and ledger
|
|
39
|
+
shards stay writable. Judges never patch: they report.
|
|
40
|
+
|
|
41
|
+
`@pinned` scenarios sit outside freeze entirely: they are grounded in **ownership**, so the user's
|
|
42
|
+
authority over them holds at `draft` and survives a re-open.
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
- **Every producer:** knows the boundary of what it owns and returns `CONTENT_GAP` for what it
|
|
47
|
+
cannot supply.
|
|
48
|
+
- **Every judge:** reports findings, never patches `spec.md` or the suite.
|
|
49
|
+
- **The conductor:** writes the coordination state — markers, `produced-by`, the run-level leash,
|
|
50
|
+
self-asserted gate entries, and its append-only log and ledger lines.
|
|
51
|
+
- **`start-mission` / `spec-gate`:** the gate skill is the sole writer of `status` and human
|
|
52
|
+
ratifications, from the in-session position only.
|
|
53
|
+
|
|
54
|
+
## Related governances
|
|
55
|
+
|
|
56
|
+
This contract owns *who writes*. Its neighbors own the surrounding definitions:
|
|
57
|
+
|
|
58
|
+
- **`lifecycle-governance`** — the field definitions, and what freezing *means* as a state; this
|
|
59
|
+
bar carries only the write constraint.
|
|
60
|
+
- **`gate-validation-governance`** — the legality of the resulting state after a write.
|
|
61
|
+
- **`combat-log-governance`** — the plan/ledger write split and the record shapes.
|
|
62
|
+
- **`suite-format-governance`** — the `@pinned` marker itself and its seed-growth role; this bar
|
|
63
|
+
carries only its ownership.
|
|
64
|
+
|
|
65
|
+
Internal SDD governance (`user-invocable: false`). Not triggered by users directly.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ownership-governance
|
|
3
|
+
description: "Partial Skill: invoke by name only"
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# SDD Ownership Governance
|
|
8
|
+
|
|
9
|
+
Who may write what. Every act in the SDD workflow has a write leash; this skill is the canonical
|
|
10
|
+
matrix. The field definitions are in `sdd:lifecycle-governance`; the legality of the resulting state
|
|
11
|
+
is in `sdd:gate-validation-governance`; the plan/ledger write split is in `sdd:combat-log-governance`.
|
|
12
|
+
|
|
13
|
+
## Write-ownership matrix
|
|
14
|
+
|
|
15
|
+
| Field / artifact | Written by | Never written by |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `status` | the gate skill (`spec-gate`) — on a human verdict, or to match a conductor self-assertion within leash | the conductor, any producer |
|
|
18
|
+
| `project-path` | the **conductor** (at scaffold; `scaffold-project-spec`) | producers, the gate skill |
|
|
19
|
+
| run-level leash + approach — the `kind: leash` ledger line (session-local; to the conductor's own ledger shard, **not** spec.md frontmatter — the conductor's autonomy bar, `start-mission`) | the **conductor** (initial evaluation) | producers, the gate skill |
|
|
20
|
+
| `approval` **self-assertion** (`verdict: approve`/`pause` + `by: agent`/none + `why`) | the **conductor** (synthesis only) | producers, the gate skill |
|
|
21
|
+
| `approval` **human ratification** (`verdict: approve`/`reject` + `by: <name>`) | the gate skill (`spec-gate`), **in-session position only** | the conductor, any producer, any spawned delegate |
|
|
22
|
+
| `<!-- open: -->` markers | the **conductor** | producers (they *emit gaps*, not markers) |
|
|
23
|
+
| `produced-by` map | the **conductor** (records the resolved producer per role at production) | producers, judges, the gate skill |
|
|
24
|
+
| contested-type → chosen-plugin state (`.agents/sdd/`) | the **conductor** (or `start-mission` for a contested artifact-type); **distinct from `produced-by`** | producers, judges, the gate skill |
|
|
25
|
+
| combat-log `report` / `correction` / `halt` lines | the **conductor** (append-only, to the plan's `*.log.jsonl`) | producers, judges, the gate skill |
|
|
26
|
+
| ledger `gate` line — self-asserted (`by: agent`) | the **conductor** (append-only, to its own ledger shard) | producers, judges |
|
|
27
|
+
| ledger `gate` line — human-ratified (`by: <name>`) | the gate skill (`spec-gate`), **in-session position only** | the conductor, producers, judges |
|
|
28
|
+
| ledger `followup` line — the durable follow-up record (`class: blocking`/`backlog`) | the **conductor** (append-only, to its own ledger shard, at handoff, **unconditionally** — no permission, no forge, no human) | producers, judges, the gate skill |
|
|
29
|
+
| ledger `strategy` lines | the doctrine-loop Scanner (append-only) | the conductor, producers, judges |
|
|
30
|
+
| `spec.md` body + the `.feature` | the **spec-producer** | the conductor, judges, solution/impl producers |
|
|
31
|
+
| a **`@pinned` scenario** (user-owned) | the **user** (in-session) | every agent role — may **propose**, never **executes** a change/removal without in-session user authorization |
|
|
32
|
+
| `<unit>.solution.md` | the **solution-producer** | the spec-producer, judges |
|
|
33
|
+
| plan brief + `todos` | the **conductor** | producers, judges |
|
|
34
|
+
| implementation + its verification | the **impl-producer** | the impl-judge (it *runs*, never authors) |
|
|
35
|
+
|
|
36
|
+
Each appended `*.log.jsonl` / ledger-shard line carries its writer's pseudonymous `handle`
|
|
37
|
+
(`SDD_HANDLE` if set, else omitted — attribution falls back to the git commit author; **never**
|
|
38
|
+
`user.email`, never a `git config` read); combat-log lines additionally carry a write-time UTC `ts`,
|
|
39
|
+
while ledger lines carry none (`sdd:combat-log-governance`). The in-file `handle` / `by` is
|
|
40
|
+
**advisory** — the git commit signature is the attestation.
|
|
41
|
+
|
|
42
|
+
## Producer write boundary
|
|
43
|
+
|
|
44
|
+
A **spec-producer** writes the `spec.md` body and the `.feature` only. It must **not** write the
|
|
45
|
+
control frontmatter (`status`, `project-path`, `approval`, `produced-by`) or the `.agents/sdd/`
|
|
46
|
+
resolution state. A required input it cannot supply or infer is returned as a `CONTENT_GAP` — the
|
|
47
|
+
conductor turns it into an `<!-- open: -->` marker. Producers do not write markers directly.
|
|
48
|
+
|
|
49
|
+
The **conductor** writes `<!-- open: -->` markers, the `produced-by` map, the run-level leash (the
|
|
50
|
+
`kind: leash` line to its own ledger shard, session-local — not a spec.md frontmatter field), and — when it
|
|
51
|
+
self-asserts a gate within the effective leash — the provisional `approval.<gate>` entry
|
|
52
|
+
(`verdict: approve` + `by: agent` with the `why` derivation; a halt is `verdict: pause` with its
|
|
53
|
+
`why` and no `by`). There is no `leash` field in the `approval` entry — the leash is the run-level
|
|
54
|
+
record on the ledger. The **gate skill** writes `status` (on a human verdict or to match the
|
|
55
|
+
conductor's in-leash self-assertion) and the human ratification of `approval` (rewriting `by: agent`
|
|
56
|
+
→ `by: <name>`).
|
|
57
|
+
|
|
58
|
+
**Ratification authority is positional.** A human-attributed gate write — `status → approved |
|
|
59
|
+
implemented`, a verdict carrying `by: <name>`, the human-ratified ledger `gate` line, and the freeze
|
|
60
|
+
— belongs to the **in-session position** that holds the real user channel. By default this is
|
|
61
|
+
trivially satisfied: the **conductor is the main session**, so the position that grills *is* the
|
|
62
|
+
position that ratifies. The rule bites only in the **headless / fan-out fallback**, where the
|
|
63
|
+
automaton runs as a **spawned subagent** with no user channel: it then writes only `by: agent`
|
|
64
|
+
self-assertions and `pause` halts, and on a human gate emits a verdict packet and stops — it never
|
|
65
|
+
writes a human ratification, **even when a coordinator relays "the user approved"** (a relayed claim
|
|
66
|
+
is not user confirmation). This is positional, not definitional: the same conductor definition run
|
|
67
|
+
in-session may perform the write. A self-assertion is **provisional**: the act is delegable, the
|
|
68
|
+
accountability is not — the human ratifies the trail. No role writes outside the spec it owns or
|
|
69
|
+
spawns specs on its own.
|
|
70
|
+
|
|
71
|
+
## Freeze (write constraint)
|
|
72
|
+
|
|
73
|
+
**Never write a frozen `.feature`.** Once a `.feature` carries its `@frozen` tag, no role — producer,
|
|
74
|
+
judge, solution-producer, or conductor — may add, remove, or rewrite its scenarios. A discovered gap
|
|
75
|
+
that requires changing specified behavior is a `BLOCKER` returned upward (the file must unfreeze and
|
|
76
|
+
its layer revert to `draft` — the gate/skill decides), never an in-place edit. The matching lifecycle
|
|
77
|
+
rule (what freezing *means* as a state) is in `sdd:lifecycle-governance`.
|
|
78
|
+
|
|
79
|
+
The freeze binds the **contract only** (`spec.md` + the suite). The combat log (the plan's
|
|
80
|
+
`*.log.jsonl`) and the durable `ledger/` shards are **exempt**: they are operational provenance, never
|
|
81
|
+
frozen, and the conductor and Scanner keep appending to their own shards within their boundaries above
|
|
82
|
+
even while a file sits `@frozen`.
|
|
83
|
+
|
|
84
|
+
A judge — spec-judge or impl-judge — must not modify `spec.md` or the suite: it reports, it does
|
|
85
|
+
not patch.
|
|
86
|
+
|
|
87
|
+
## User-owned scenarios (`@pinned`)
|
|
88
|
+
|
|
89
|
+
A `@pinned` scenario is **user-owned** — the one scenario the spec-producer does **not**
|
|
90
|
+
own. Any agent role may **propose** changing or removing it, but **never executes** the change
|
|
91
|
+
without **in-session user authorization** — the authority of a human ratification (positional, not
|
|
92
|
+
relayable, not self-assertable within leash). Only the user applies `@pinned`. This is grounded in
|
|
93
|
+
**ownership, not freeze**: it holds at `draft` and survives a re-open, since ownership does not lapse
|
|
94
|
+
when a file unfreezes. The marker and its seed-growth role are `sdd:suite-format-governance`.
|
|
95
|
+
|
|
96
|
+
## Key points (read-check)
|
|
97
|
+
|
|
98
|
+
1. **One writer per field/artifact** (the matrix) — no role writes outside the spec it owns or spawns.
|
|
99
|
+
2. **Never write a frozen `.feature`** — a behavior-changing gap is a `BLOCKER` returned upward, never
|
|
100
|
+
an in-place edit.
|
|
101
|
+
3. **Human ratification is positional** — the in-session channel only; never relayed, never
|
|
102
|
+
self-asserted within leash.
|
|
103
|
+
4. **A `@pinned` scenario is user-owned** — the agent proposes, never executes a change or removal
|
|
104
|
+
without in-session user authorization.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# pause-mission
|
|
2
|
+
|
|
3
|
+
Project-private workflow skill for **checkpointing an in-progress SDD mission into its plan
|
|
4
|
+
brief** (`.agents/plans/<cr-ref>.plan.md`). Invoke it when stopping mid-mission ("pause",
|
|
5
|
+
"checkpoint", "save state to the plan", "stop here") to update the todo statuses and rewrite
|
|
6
|
+
the `## NEXT — resume here` anchor, then commit — so the working tree is clean and the next
|
|
7
|
+
session resumes without rediscovery.
|
|
8
|
+
|
|
9
|
+
Pass **`--approve`** to also clear the mission for **headless dispatch** — it sets the brief's
|
|
10
|
+
top-level `status: approved` (a human review act; a headless automaton never self-approves), the
|
|
11
|
+
go-signal the gateway's dispatch loop selects on.
|
|
12
|
+
|
|
13
|
+
The checkpoint is **self-sufficient**: any later session that opens the plan can continue from
|
|
14
|
+
it — [`resume-mission`](../resume-mission/README.md) is one convenient reader, not a required
|
|
15
|
+
pair. Modeled after the handoff-skill pattern (reference artifacts instead of restating, keep it
|
|
16
|
+
commit-message-grade, lead the `## NEXT` anchor with the next action *and the skill to invoke for
|
|
17
|
+
it*) but targeted at the durable tracked plan rather than a temp file. `internal: true` —
|
|
18
|
+
contributor tooling.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pause-mission
|
|
3
|
+
description: "Checkpoint an in-progress SDD mission INTO its plan brief so any later session can pick it up. Use this skill when asked to 'pause', 'wrap up for now', or 'stop here' mid-mission — update the plan's todo statuses and rewrite its ## NEXT anchor so a fresh session continues exactly where you left off. Pass --approve to also clear the mission for headless dispatch (sets the brief's status: approved)."
|
|
4
|
+
argument-hint: "[what the next session should focus on] [--approve]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Pause an SDD mission into its plan
|
|
8
|
+
|
|
9
|
+
An SDD mission's durable handoff is its **plan brief** at `.agents/plans/<cr-ref>.plan.md`. A
|
|
10
|
+
pause writes this session's state back into that plan so **any** next session can pick the
|
|
11
|
+
mission up by reading it — no special tool required (`resume-mission` is one convenient reader;
|
|
12
|
+
the plan stands on its own). Capture *enough to continue, nothing to relitigate.*
|
|
13
|
+
|
|
14
|
+
## Procedure
|
|
15
|
+
|
|
16
|
+
1. **Honor the focus.** If the user named what the pause should emphasize, scope the checkpoint
|
|
17
|
+
to it; otherwise checkpoint the whole live frontier.
|
|
18
|
+
|
|
19
|
+
2. **Locate the plan — scaffold it if missing.** Find `.agents/plans/<cr-ref>.plan.md`. If none
|
|
20
|
+
exists, create a minimal one (frontmatter `todos` + a `## NEXT` anchor) so the checkpoint has
|
|
21
|
+
a durable home; a pause always leaves a plan behind.
|
|
22
|
+
|
|
23
|
+
3. **Update the todo statuses.** In the frontmatter `todos`, set each touched todo to its true
|
|
24
|
+
`status` (`pending | in_progress | completed`). Mark the one you were on `in_progress`; never
|
|
25
|
+
mark a todo `completed` unless it truly is — partial work stays `in_progress` with a one-line
|
|
26
|
+
note of what remains. Add a todo for any new work or decision that surfaced this session.
|
|
27
|
+
|
|
28
|
+
4. **Rewrite the `## NEXT — resume here` anchor** (create it if absent) so a cold reader can
|
|
29
|
+
start in under a minute. Order it for pickup, **action first**:
|
|
30
|
+
- **The next action — first, concrete, runnable.** One line the resumer can act on
|
|
31
|
+
immediately: the file or unit to touch *and the skill or command to invoke for it* — e.g.
|
|
32
|
+
"build `<unit>`'s spec + suite via `start-mission`, then run the project's spec check," never
|
|
33
|
+
a vague "continue authoring."
|
|
34
|
+
- **Blocking decisions** — open scope calls and unresolved `<!-- open: -->` markers, each with
|
|
35
|
+
its options, so the next session *resolves* rather than rediscovers them.
|
|
36
|
+
- **Findings the commits won't show** — only what isn't obvious from the diff and changes the
|
|
37
|
+
next move. Skip the play-by-play.
|
|
38
|
+
- **A pointer to the working method / resolved decisions** already in the plan body ("do not
|
|
39
|
+
relearn — see `## Resolved decisions`").
|
|
40
|
+
|
|
41
|
+
Lead with the action; put history and findings *below* it. A resumer needs *what to do* first
|
|
42
|
+
and *what happened* second.
|
|
43
|
+
|
|
44
|
+
5. **Reference, don't restate.** Point to commits (SHAs), files (**repo-relative paths**), design
|
|
45
|
+
rules, and issues by reference — never paste their contents. The plan is an index to the work,
|
|
46
|
+
not a copy of it. A file the brief depends on must live **in the repo**: if the source is a
|
|
47
|
+
machine-local doc (a plan-mode plan under `~/.claude/plans/…`), bring it in as a sibling
|
|
48
|
+
`<cr-ref>.design.md` and reference that, never the external absolute path.
|
|
49
|
+
|
|
50
|
+
6. **Keep it commit-message-grade — the safe-to-publish floor.** The plan is tracked and committed,
|
|
51
|
+
so it carries the same floor as the combat log (`combat-log-governance`): **never committed** —
|
|
52
|
+
secrets, keys, prompts, PII, **absolute paths, OS usernames/hostnames, `$HOME`/`$USER`**, or any
|
|
53
|
+
machine-local reference outside the repo. Describe the decision or its class, not literal payloads
|
|
54
|
+
or machine-local locations.
|
|
55
|
+
|
|
56
|
+
7. **Commit the checkpoint.** Before committing, run the plan-brief leak guard over the brief —
|
|
57
|
+
`node "<sdd-skills>/check-plan-safety/scripts/check-plan-safety.mts" --path <brief> --check` (the
|
|
58
|
+
`check-plan-safety` engine). A hit **blocks the commit**: scrub the machine-local reference (bring
|
|
59
|
+
the content in-repo, reference it repo-relative) and re-run. Then commit the plan update (`docs:`)
|
|
60
|
+
so the pause is durable in git and the working tree is clean. Leave no uncommitted work behind.
|
|
61
|
+
|
|
62
|
+
## Reconcile-forward checkpoint
|
|
63
|
+
|
|
64
|
+
When the mission's live state is "this CR's work is already merged, not something this session
|
|
65
|
+
ran live" (e.g. resuming a plan and finding its commits already landed on `main`), treat it as a
|
|
66
|
+
checkpoint whose input is git/ledger evidence instead of a live conductor session — same
|
|
67
|
+
procedure above, plus one gate before declaring the plan retirement-ready:
|
|
68
|
+
|
|
69
|
+
- **Only for a CR-bearing mission.** A mission that opened a real CR and reached
|
|
70
|
+
`status: approved` or `implemented` gets this check. A non-CR mission (the `start-mission`
|
|
71
|
+
escape hatch — no suite-relevant behavior, no CR opened, no gate invoked) needs none; mark it
|
|
72
|
+
complete on its own merits.
|
|
73
|
+
- **Run the gate floor.** Run `checkGateFloor` (`plugins/sdd/skills/spec-gate/scripts/
|
|
74
|
+
check-spec-state.mts`, wired into `pnpm verify:specs`) before marking the plan
|
|
75
|
+
retirement-ready. A clean floor → mark it. A violation (the status is approved/implemented but
|
|
76
|
+
the ledger has no matching gate approve line) → do **not** mark the plan retirement-ready on
|
|
77
|
+
git evidence alone; record the violation explicitly in `## NEXT` so a human resolves it (either
|
|
78
|
+
the ledger line was genuinely never written, or it needs relocating/re-checking).
|
|
79
|
+
|
|
80
|
+
**Write scope.** A checkpoint writes **only the plan brief** — the `todos`, the `## NEXT` anchor,
|
|
81
|
+
and (with `--approve`) the top-level `status`. It never touches `spec.md`'s `status` or `approval`:
|
|
82
|
+
the mission's contract lifecycle is the gates', not the checkpoint's.
|
|
83
|
+
|
|
84
|
+
## Clear for headless dispatch — `--approve`
|
|
85
|
+
|
|
86
|
+
A plain pause never touches the brief's top-level `status`. When invoked with **`--approve`**, the
|
|
87
|
+
checkpoint additionally sets the brief's **`status: approved`** — the human review act that clears
|
|
88
|
+
the mission for the gateway's headless dispatch queue (the go-signal `dispatch` selects on; the
|
|
89
|
+
enum + the three-way `status` distinction live in the SDD `provenance-model`). Rules:
|
|
90
|
+
|
|
91
|
+
- **Human act only.** Setting `approved` is a review decision — a person has read the brief (todos,
|
|
92
|
+
`## NEXT`, the run-level leash) and cleared it. A **headless automaton never self-approves**: with
|
|
93
|
+
no user channel it may checkpoint progress but must **refuse `--approve`** and leave `status`
|
|
94
|
+
unchanged (the same positional-authority rule that reserves a human-ratified gate verdict).
|
|
95
|
+
- **Idempotent.** Approving an already-`approved` brief leaves it `approved`.
|
|
96
|
+
- **Flag-only.** It writes the one frontmatter field; it does not dispatch the mission (that is the
|
|
97
|
+
gateway) and does not touch the run-level leash — `approved` says *run it*, the leash says *how far*.
|
|
98
|
+
|
|
99
|
+
## What a good pause is not
|
|
100
|
+
|
|
101
|
+
- **Not a transcript.** Capture decisions and the next action, not a play-by-play.
|
|
102
|
+
- **Not a duplicate.** If it already lives in a commit, an ADR, the spec, or an issue, link it.
|
|
103
|
+
- **Not buried.** The next action leads the anchor; if a resumer has to hunt for where to start,
|
|
104
|
+
the pause failed.
|
|
105
|
+
- **Not self-referential.** The plan never tells its reader to "invoke `resume-mission`" or
|
|
106
|
+
explains how to resume — a session reading the plan is already past that, and the
|
|
107
|
+
`## NEXT — resume here` heading orients a human on its own. Open `## NEXT` with the *work*.
|
|
108
|
+
- **Not final-sounding.** A pause is a *resumable* checkpoint — leave the live frontier and the
|
|
109
|
+
open questions explicit, not smoothed into a summary that hides where to begin.
|
|
110
|
+
|
|
111
|
+
The plan is self-sufficient: any session that opens it can continue. `resume-mission` reads this
|
|
112
|
+
back conveniently, but is not required.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# place-node
|
|
2
|
+
|
|
3
|
+
Internal SDD skill — the concrete engine for the **place-node** step. Given a new node's `concept`
|
|
4
|
+
(and optional name), suggests a **provisional** capability home (derived from where that concept
|
|
5
|
+
already lives) and catches possible duplicates, so explore places a node in one lookup.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
node scripts/place-node.mts --spec-dir <spec> --concept resolution --name leash
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Read-only and advisory; placement is finalized at handoff. See [`SKILL.md`](./SKILL.md) for the full
|
|
12
|
+
contract. Not user-invocable.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: place-node
|
|
3
|
+
description: "Partial Skill: invoke by name only — project-spec/place-node's engine that suggests a capability home and surfaces duplicates for a new node — used by explore, not triggered by users directly."
|
|
4
|
+
user-invocable: false
|
|
5
|
+
metadata:
|
|
6
|
+
internal: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Place Node
|
|
10
|
+
|
|
11
|
+
The concrete engine for the **place-node** step. Given a new
|
|
12
|
+
node's `concept` (and optional name), it suggests a **provisional** capability home and catches possible
|
|
13
|
+
duplicates, so explore places a node without holding the whole tree in its head (it scans the project-spec's
|
|
14
|
+
`concept:` frontmatter and ranks the capability folders those facets already sit in). Self-contained `.mts`
|
|
15
|
+
(the repo's node-≥23.6 / no-deps convention).
|
|
16
|
+
|
|
17
|
+
## Run it
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
node "<skill>/scripts/place-node.mts" --spec-dir <spec> --concept <c> [--name <n>]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `--concept` — suggests the capability folders where that concept's facets already sit, ranked by count
|
|
24
|
+
(the provisional home). A concept no node yet carries → an empty suggestion: pick any plausible
|
|
25
|
+
capability, finalized at handoff.
|
|
26
|
+
- `--name` — surfaces existing nodes whose path overlaps the name ("belongs near X" duplicate-catch).
|
|
27
|
+
|
|
28
|
+
The home is **derived** from the project-spec's own `concept:` tags — where that concept's facets
|
|
29
|
+
already sit — never a stored routing list (the `corpus/discovery` no-drift rule). For genuinely
|
|
30
|
+
contested overlaps, the human tie-break rules live in the placement-map routing table (root
|
|
31
|
+
`spec.md`), which this tool points to rather than re-deriving.
|
|
32
|
+
|
|
33
|
+
**Strategy-neutral by construction — and not this tool's call.** The derivation follows the tree as
|
|
34
|
+
it actually is, so it returns a correctly-shaped home under whatever layout the project declared: a
|
|
35
|
+
`mirror-source` project's concepts sit in mirror-shaped folders, and "where the siblings already
|
|
36
|
+
are" answers that as well as it answers capability-first. This tool therefore neither reads nor
|
|
37
|
+
recommends a strategy. Choosing one belongs to `scaffold-project-spec`, recording it to the root
|
|
38
|
+
`spec.md` placement map, and judging fit against it to the formation Warden
|
|
39
|
+
(`sdd:spec-structure-governance`).
|
|
40
|
+
|
|
41
|
+
## Boundaries
|
|
42
|
+
|
|
43
|
+
Read-only and advisory — it **writes nothing** (no scaffold, no relocation, no frontmatter); placement is
|
|
44
|
+
provisional and finalized at handoff (the `start-mission` handoff step), so a
|
|
45
|
+
suggestion never has to be correct — only useful. Frontmatter only — no node body is read. When `node` is
|
|
46
|
+
absent, an agent does the same by hand: read the `concept:` tags, see where that concept already lives,
|
|
47
|
+
and place near them.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// place-node — project-spec/place-node's concrete engine. Given a new node's concept (and optional name),
|
|
3
|
+
// suggests a provisional capability home and surfaces possible duplicates, so explore places a node in
|
|
4
|
+
// one lookup instead of holding the tree in its head.
|
|
5
|
+
//
|
|
6
|
+
// Derivation, not a registry: the home for a concept is where that concept's facets already sit — read
|
|
7
|
+
// live from the project-spec's `concept:` tags, never a stored routing list (the corpus/discovery no-drift
|
|
8
|
+
// rule). Read-only and advisory: it writes nothing, and placement is finalized at handoff.
|
|
9
|
+
//
|
|
10
|
+
// No dependencies (the repo's node-≥23.6 / no-deps convention). Pure functions are exported for
|
|
11
|
+
// node:test; running the file directly drives the CLI.
|
|
12
|
+
|
|
13
|
+
import { readdirSync, readFileSync } from 'node:fs'
|
|
14
|
+
import { join } from 'node:path'
|
|
15
|
+
|
|
16
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', '.turbo', '.next', 'coverage'])
|
|
17
|
+
|
|
18
|
+
export interface NodeRecord {
|
|
19
|
+
/** Path relative to the spec directory (POSIX). */
|
|
20
|
+
relPath: string
|
|
21
|
+
/** The top-level folder — the capability. */
|
|
22
|
+
capability: string
|
|
23
|
+
/** Display form — a README.md node shows its folder, a design doc shows the file. */
|
|
24
|
+
display: string
|
|
25
|
+
concepts: string[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface HomeSuggestion {
|
|
29
|
+
capability: string
|
|
30
|
+
count: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Frontmatter parse (concept only — the placement signal) ──
|
|
34
|
+
export function parseConcepts(text: string): string[] {
|
|
35
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n|$)/.exec(text)
|
|
36
|
+
if (!m) return []
|
|
37
|
+
const lines = m[1].split('\n').map((l) => l.replace(/\r$/, ''))
|
|
38
|
+
for (let i = 0; i < lines.length; i++) {
|
|
39
|
+
const line = lines[i]
|
|
40
|
+
if (line.length - line.trimStart().length !== 0) continue
|
|
41
|
+
const [key, ...rest] = line.trim().split(':')
|
|
42
|
+
if (key !== 'concept') continue
|
|
43
|
+
const value = rest.join(':').trim()
|
|
44
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
45
|
+
return value
|
|
46
|
+
.slice(1, -1)
|
|
47
|
+
.split(',')
|
|
48
|
+
.map((s) => unquote(s.trim()))
|
|
49
|
+
.filter((s) => s.length > 0)
|
|
50
|
+
}
|
|
51
|
+
if (value !== '') return [unquote(value)]
|
|
52
|
+
// block list
|
|
53
|
+
const out: string[] = []
|
|
54
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
55
|
+
const item = lines[j]
|
|
56
|
+
if (item.trim() === '') continue
|
|
57
|
+
if (item.length - item.trimStart().length === 0) break
|
|
58
|
+
const dash = /^\s*-\s+(.*)$/.exec(item)
|
|
59
|
+
if (dash) out.push(unquote(dash[1].trim()))
|
|
60
|
+
}
|
|
61
|
+
return out
|
|
62
|
+
}
|
|
63
|
+
return []
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function unquote(v: string): string {
|
|
67
|
+
return v.replace(/^["']|["']$/g, '')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function displayPath(relPath: string): string {
|
|
71
|
+
const p = relPath.replace(/\\/g, '/')
|
|
72
|
+
return p.endsWith('/README.md') ? p.slice(0, -'README.md'.length) : p
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── Scan the project-spec — every concept-tagged node ──
|
|
76
|
+
export function scanProjectSpec(specDir: string): NodeRecord[] {
|
|
77
|
+
const records: NodeRecord[] = []
|
|
78
|
+
walk(specDir, specDir, records)
|
|
79
|
+
return records
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function walk(dir: string, specDir: string, out: NodeRecord[]): void {
|
|
83
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
84
|
+
if (entry.name.startsWith('.') || SKIP_DIRS.has(entry.name)) continue
|
|
85
|
+
const full = join(dir, entry.name)
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
walk(full, specDir, out)
|
|
88
|
+
} else if (entry.name.endsWith('.md')) {
|
|
89
|
+
const concepts = parseConcepts(readFileSync(full, 'utf8'))
|
|
90
|
+
if (concepts.length === 0) continue
|
|
91
|
+
const relPath = full.slice(specDir.length + 1).replace(/\\/g, '/')
|
|
92
|
+
out.push({ relPath, capability: relPath.split('/')[0], display: displayPath(relPath), concepts })
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── Suggest a home — where the concept's facets already sit, ranked by count ──
|
|
98
|
+
export function suggestHomes(records: NodeRecord[], concept: string): HomeSuggestion[] {
|
|
99
|
+
const counts = new Map<string, number>()
|
|
100
|
+
for (const rec of records) {
|
|
101
|
+
if (!rec.concepts.includes(concept)) continue
|
|
102
|
+
counts.set(rec.capability, (counts.get(rec.capability) ?? 0) + 1)
|
|
103
|
+
}
|
|
104
|
+
return [...counts.entries()]
|
|
105
|
+
.map(([capability, count]) => ({ capability, count }))
|
|
106
|
+
.sort((a, b) => b.count - a.count || a.capability.localeCompare(b.capability))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── Duplicate-catch — existing nodes whose path/name overlaps the candidate name ──
|
|
110
|
+
export function findNear(records: NodeRecord[], name: string): NodeRecord[] {
|
|
111
|
+
const needle = name.trim().toLowerCase()
|
|
112
|
+
if (needle === '') return []
|
|
113
|
+
return records
|
|
114
|
+
.filter((rec) => rec.relPath.toLowerCase().includes(needle))
|
|
115
|
+
.sort((a, b) => a.display.localeCompare(b.display))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── Render ──
|
|
119
|
+
function render(homes: HomeSuggestion[], near: NodeRecord[], concept: string, name: string): string {
|
|
120
|
+
const lines: string[] = [`place-node: concept=${concept || '(none)'} name=${name || '(none)'}`]
|
|
121
|
+
if (homes.length > 0) {
|
|
122
|
+
lines.push(`homes[${homes.length}]{capability,facets}:`)
|
|
123
|
+
for (const h of homes) lines.push(` ${h.capability},${h.count}`)
|
|
124
|
+
} else {
|
|
125
|
+
lines.push('homes[0]: (new concept — pick any plausible capability; finalized at handoff)')
|
|
126
|
+
}
|
|
127
|
+
if (near.length > 0) {
|
|
128
|
+
lines.push(`near[${near.length}]:`)
|
|
129
|
+
for (const n of near) lines.push(` ${n.display}`)
|
|
130
|
+
} else {
|
|
131
|
+
lines.push('near[0]: (no name overlap)')
|
|
132
|
+
}
|
|
133
|
+
lines.push('note: placement is provisional — finalized at handoff')
|
|
134
|
+
return lines.join('\n')
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── CLI ──
|
|
138
|
+
export function main(argv: string[]): number {
|
|
139
|
+
let specDir = '.'
|
|
140
|
+
let concept = ''
|
|
141
|
+
let name = ''
|
|
142
|
+
for (let i = 0; i < argv.length; i++) {
|
|
143
|
+
const a = argv[i]
|
|
144
|
+
if (a === '--spec-dir') specDir = argv[++i] ?? '.'
|
|
145
|
+
else if (a === '--concept') concept = argv[++i] ?? ''
|
|
146
|
+
else if (a === '--name') name = argv[++i] ?? ''
|
|
147
|
+
}
|
|
148
|
+
const records = scanProjectSpec(specDir)
|
|
149
|
+
const homes = concept ? suggestHomes(records, concept) : []
|
|
150
|
+
const near = name ? findNear(records, name) : []
|
|
151
|
+
process.stdout.write(`${render(homes, near, concept, name)}\n`)
|
|
152
|
+
return 0
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
156
|
+
process.exit(main(process.argv.slice(2)))
|
|
157
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# plan-retirement
|
|
2
|
+
|
|
3
|
+
Internal, non-user-invocable SDD skill carrying the Doctrine loop's **last retro step** — the
|
|
4
|
+
gated, idempotent **tracked deletion** of a retired mission plan. It holds a self-contained
|
|
5
|
+
`scripts/retire-plans.mts` (node ≥23.6, no deps; an agent fallback when `node` is absent).
|
|
6
|
+
|
|
7
|
+
**Distill and delete are decoupled.** The Scanner distills the combat log into ledger `strategy`
|
|
8
|
+
early (at `→ implemented`); this sweep deletes the plan **later**, gated on source = `done`/merged
|
|
9
|
+
**and** distilled. The two signals **split by verifiability**: **source** stays the caller's
|
|
10
|
+
judgment (`sdd:doctrine-loop` Scanner passes the source-cleared set via `--retire`; it needs
|
|
11
|
+
network/`gh`), while **distilled** is **verified mechanically by the sweep** against the project
|
|
12
|
+
ledger (`--ledger <dir>`) — a `strategy` entry with `distills == <cr-ref>` must exist (keyed on the
|
|
13
|
+
`distills` field, never an `evidence` mention; unratified still counts). The distilled gate guards
|
|
14
|
+
an **existing** combat log: a cr-ref whose `<cr-ref>.log.jsonl` was **never written** (a non-gated
|
|
15
|
+
mission — hand-run, chore-tracked, investigation) has nothing to distill, so it retires on clearance
|
|
16
|
+
+ presence alone, no distilling entry required. The sweep is the mechanical, **fail-closed**
|
|
17
|
+
filesystem act — it deletes a cr-ref's whole **transient artifact set** (`<cr-ref>.plan.md` +
|
|
18
|
+
`<cr-ref>.log.jsonl` plus the optional transient CR-level planning briefs `<cr-ref>.design.md`,
|
|
19
|
+
`<cr-ref>.operations.md`, `<cr-ref>.evidence.md`) only for a cr-ref that is cleared, present
|
|
20
|
+
(`<cr-ref>.plan.md` on disk), **and** (distilled **or** has no log to distill); a cleared, present
|
|
21
|
+
cr-ref whose log **exists** but is undistilled fails closed, taking its briefs with it. Anything not
|
|
22
|
+
cleared, not present, missing, or already gone is a no-op (idempotent, safe to re-run). Omitting
|
|
23
|
+
`--ledger` fail-closes to deleting nothing (the no-log branch only applies once a ledger is present
|
|
24
|
+
to consult).
|
|
25
|
+
|
|
26
|
+
The briefs **ride along, never gate**: they do not widen the distilled gate (which keys on the
|
|
27
|
+
combat log's presence only — a brief owes no distillation, so a cr-ref with briefs but no
|
|
28
|
+
`log.jsonl` still retires clearance-only), and they do not anchor presence (`<cr-ref>.plan.md`
|
|
29
|
+
alone does — a brief without a `plan.md` is untouched). Each brief is deleted only if present; an
|
|
30
|
+
absent one is a no-op, same as the missing log half.
|
|
31
|
+
|
|
32
|
+
Tests (`scripts/retire-plans.test.mts`) run under `pnpm verify:specs`.
|