agentic-sdd-framework 1.4.0 → 1.5.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/.agents/AGENTS.template.md +18 -10
- package/.agents/ENTRYPOINT.template.md +17 -0
- package/.agents/skills/fix-and-verify/SKILL.md +66 -0
- package/.agents/skills/no-ai-slop/SKILL.md +1 -0
- package/CHANGELOG.md +12 -1
- package/README.md +112 -118
- package/docs/architecture/spec-integrity.md +84 -0
- package/package.json +4 -2
- package/scripts/check-constitution.js +97 -0
- package/scripts/lib/constitution-rules.js +25 -0
- package/scripts/lib/provision.js +35 -5
- package/scripts/quality-gate.js +1 -0
- package/scripts/sdd-add-rule.js +108 -0
- package/scripts/sdd-init.js +15 -4
- package/sdd.config.json +1 -1
- package/scripts/dev/set-npm-publish-token.sh +0 -40
|
@@ -4,56 +4,64 @@ This document establishes the non-negotiable operating rules for AI coding agent
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
<!-- sdd:rule id="discovery-first" tier="moderate" -->
|
|
7
8
|
## 1. Discovery First (No Premature Assumptions)
|
|
8
9
|
* **Rule:** Before recommending architectures, selecting frameworks, or generating code on a new initiative, the agent must execute the 4-Pillar Discovery Interview (Scale/Concurrency, Hardware/Deployment, Workload/Compute, Modularity).
|
|
9
10
|
* **Why this rule exists:**
|
|
10
|
-
>
|
|
11
|
+
> A stack or architecture chosen before the actual scale, deployment target, and workload are known tends to be either over-built or wrong for the job; a few minutes of discovery costs less than unwinding that choice later.
|
|
11
12
|
|
|
12
13
|
---
|
|
13
14
|
|
|
15
|
+
<!-- sdd:rule id="evidence-driven-debugging" tier="moderate" -->
|
|
14
16
|
## 2. Evidence-Driven Debugging & Diagnostics
|
|
15
17
|
* **Rule:** Never guess root causes or apply speculative patches. Inspect log files, inspect command output, and run diagnostics before altering code.
|
|
16
18
|
* **Why this rule exists:**
|
|
17
|
-
>
|
|
19
|
+
> A fix aimed at a guessed cause, instead of the one diagnostics actually show, risks patching a symptom while the real defect -- and a regression alongside it -- stays hidden.
|
|
18
20
|
|
|
19
21
|
---
|
|
20
22
|
|
|
23
|
+
<!-- sdd:rule id="mandatory-verification" tier="critical" -->
|
|
21
24
|
## 3. Mandatory Verification Before Certification
|
|
22
|
-
* **Rule:** A task or phase is not complete until its explicit verification command exits with code 0. Reading code visually is never a substitute for running the code.
|
|
25
|
+
* **Rule:** A task or phase is not complete until its explicit verification command exits with code 0. Reading code visually is never a substitute for running the code. When the change touches anything a user perceives directly -- rendered markup, CSS, a screen transition, an interactive flow -- a green test suite is not sufficient evidence: verify by actually driving the interface the way a user would, since a unit test does not render CSS or exercise the DOM's `hidden`/cascade behavior. Any claim of having stopped, cleaned up, or removed a process or resource must name the exact identifier (PID, handle, container id) the agent itself created; never certify a resource as handled without confirming it is the one you spawned.
|
|
23
26
|
* **Why this rule exists:**
|
|
24
|
-
>
|
|
27
|
+
> Clean-looking code is not proof of working code. A visual review cannot catch a broken runtime path, a failing integration, or a regression an agent introduced while editing; running the actual command is the only evidence this framework accepts, which is why `sdd-verify` exists. A full test suite has passed while a CSS rule silently defeated the `hidden` attribute and left two screens stacked on top of each other, invisible to any test that never renders a page -- the tests were evidence of the backend logic, not of what a user would actually see. The same gap extends to claims about system actions: an agent reported having stopped "its own" test process while actually terminating a different, pre-existing one, misidentifying the PID in its own success report. Certifying either kind of claim requires evidence tied to the specific thing claimed, not prose that merely sounds like evidence.
|
|
25
28
|
|
|
26
29
|
---
|
|
27
30
|
|
|
31
|
+
<!-- sdd:rule id="closed-network-testing" tier="moderate" -->
|
|
28
32
|
## 4. Closed-Network Testing Isolation
|
|
29
33
|
* **Rule:** Automated test suites must never contact external internet hosts. All external integrations must be mocked or gated on environment variables. Loopback testing is permitted for local servers.
|
|
30
34
|
* **Why this rule exists:**
|
|
31
|
-
>
|
|
35
|
+
> A test suite that reaches a real third-party API is one flaky network call away from a false failure, and one bad run away from a rate limit or a bill nobody asked for.
|
|
32
36
|
|
|
33
37
|
---
|
|
34
38
|
|
|
39
|
+
<!-- sdd:rule id="zero-trust-secrets" tier="critical" -->
|
|
35
40
|
## 5. Zero-Trust Secrets Management
|
|
36
41
|
* **Rule:** Agents must never request API keys or credentials in chat prompts. Secrets must be read directly from the Tier 3 Vault (`~/secrets/<app>/.vault`) or environment variables. Never commit secrets to Git. Tier model: `docs/guides/AGENT_CREDENTIALS.md`.
|
|
37
42
|
* **Why this rule exists:**
|
|
38
|
-
>
|
|
43
|
+
> A credential typed into a chat prompt or pasted into a diff is permanently recorded in logs and git history. Treat every prompt and every commit as effectively public, and never put a secret where either can capture it.
|
|
39
44
|
|
|
40
45
|
---
|
|
41
46
|
|
|
47
|
+
<!-- sdd:rule id="scope-bounding" tier="critical" -->
|
|
42
48
|
## 6. Scope Bounding & Atomic Progression
|
|
43
49
|
* **Rule:** Execute one task at a time in strict sequence. Do not refactor unrelated files or perform out-of-scope cleanups without explicit Auditor authorization.
|
|
44
50
|
* **Why this rule exists:**
|
|
45
|
-
>
|
|
51
|
+
> An agent that touches files outside the current task makes the change hard to attribute, review, and revert, and breaks the handoff when a different session or agent picks up the work next.
|
|
46
52
|
|
|
47
53
|
---
|
|
48
54
|
|
|
55
|
+
<!-- sdd:rule id="factual-copy" tier="moderate" -->
|
|
49
56
|
## 7. Factual Technical Copy (No AI Slop)
|
|
50
57
|
* **Rule:** All documentation, user-facing copy, and commit messages must be factual, concise, and dense, following the `no-ai-slop` skill. The quality gate enforces its banned patterns.
|
|
51
58
|
* **Why this rule exists:**
|
|
52
|
-
>
|
|
59
|
+
> Generic, promotional phrasing in docs and commit messages buries the one technical fact a future reader actually needs.
|
|
53
60
|
|
|
54
61
|
---
|
|
55
62
|
|
|
63
|
+
<!-- sdd:rule id="author-attribution" tier="moderate" -->
|
|
56
64
|
## 8. Author Attribution & Integrity
|
|
57
|
-
* **Rule:** Never attach `Co-Authored-By:` trailers crediting AI assistants to Git commits. The repository owner is the sole author.
|
|
65
|
+
* **Rule:** Never attach `Co-Authored-By:` trailers crediting AI assistants to Git commits. The repository owner is the sole author. Before the first commit in a repository you have not committed to before, run `git config user.email` (and `user.name`) and confirm the result is the identity this project actually wants -- do not assume whatever the global default resolves to is correct, since it may be a different project's or a personal identity.
|
|
58
66
|
* **Why this rule exists:**
|
|
59
|
-
>
|
|
67
|
+
> The project owner is the sole author of record; a Co-Authored-By trailer credits work no tool did unsupervised, and pollutes the commit graph for everyone after. A commit's author is never wrong by accident -- it is either the identity someone actually configured for this project, or nobody checked.
|
|
@@ -15,9 +15,26 @@ Load a skill from `.agents/skills/<name>/SKILL.md` only when its trigger applies
|
|
|
15
15
|
| :--- | :--- |
|
|
16
16
|
| `strategic-cto` | Choosing a stack or architecture, or answering "can this be improved?" |
|
|
17
17
|
| `auditor-executor-protocol` | Work spans several phases or sessions, or another agent implements the plan |
|
|
18
|
+
| `fix-and-verify` | Fixing a reported bug, including a vague one with no repro steps, in a single session |
|
|
18
19
|
| `no-ai-slop` | Writing documentation, user-facing copy, or commit messages |
|
|
19
20
|
| `ast-navigator` | Exploring code. Active adapter: `{{AST_ADAPTER}}` (`.agents/skills/ast-navigator/adapters/{{AST_ADAPTER}}.md`) |
|
|
20
21
|
|
|
22
|
+
## Adding a Rule
|
|
23
|
+
|
|
24
|
+
`.agents/AGENTS.md` is the only place a project rule lives. If the user asks to add one
|
|
25
|
+
("always do X", "never do Y", "remember this constraint"), do not write it to memory, to
|
|
26
|
+
CLAUDE.md, or to a temporary or unrelated doc -- none of those are read by every agent, or
|
|
27
|
+
checked by the gate. Run:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
{{ADD_RULE_COMMAND}} --title "<short title>" --rule "<the operating rule>" --why "<why this rule exists>"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
It appends a correctly numbered, tagged rule block; it refuses empty fields and bracket
|
|
34
|
+
placeholders. If the rule replaces one of the 8 defaults with an equivalent of your own,
|
|
35
|
+
add `--fulfills=<id>` (see the tag above the rule you are replacing in `.agents/AGENTS.md`)
|
|
36
|
+
so the constitution check still recognizes it as fulfilling that slot.
|
|
37
|
+
|
|
21
38
|
## Project Facts
|
|
22
39
|
|
|
23
40
|
* Runtime: {{RUNTIME}}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fix-and-verify
|
|
3
|
+
description: "Investigate and resolve a bug report, including a vague one ('this doesn't work', 'it looks wrong', a one-line client complaint with no repro steps), with reproduction before diagnosis and independent, evidence-backed verification before claiming done. Use for any single-session bug fix, whether or not the project has adopted the full spec lifecycle -- a quick 'fix this' ask inherits the same evidence standard as a tracked task. Triggers: 'this doesn't work', 'fix this bug', 'it looks wrong', 'the client/user says', a complaint with no repro steps, being asked to fix something another agent already touched and reported as done. NOT for multi-phase or multi-session work handed to a different implementing agent -- see auditor-executor-protocol for that."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fix and Verify
|
|
7
|
+
|
|
8
|
+
A vague bug report is not an invitation to guess. This exists because the two most
|
|
9
|
+
expensive failures in a bug-fix task both look identical to a rushed, confident report:
|
|
10
|
+
a fix that patches the wrong thing, and a fix that's real but never actually checked.
|
|
11
|
+
|
|
12
|
+
## 1. Reproduce before you diagnose
|
|
13
|
+
|
|
14
|
+
Never fix a symptom you haven't seen yourself. A one-line complaint ("se ve mal", "this
|
|
15
|
+
doesn't work") describes what the user perceived, not the mechanism -- go observe the
|
|
16
|
+
actual failure (run the app, read the log, load the actual page) before touching a
|
|
17
|
+
single file. If a previous engineer or agent already touched this and reported it fixed,
|
|
18
|
+
do not trust that report either: reproduce the *original* complaint yourself first, on
|
|
19
|
+
the current state of the code, before deciding whether their fix actually closed it.
|
|
20
|
+
|
|
21
|
+
## 2. Read the full source of anything load-bearing
|
|
22
|
+
|
|
23
|
+
Grep finds a string; it does not tell you whether a class is used elsewhere, whether a
|
|
24
|
+
rule is overridden further down the file, or whether a fix for one element also covers a
|
|
25
|
+
sibling that has the same shape. Read the actual file for anything the fix depends on.
|
|
26
|
+
|
|
27
|
+
## 3. Tests passing is not evidence for a class of bug tests can't see
|
|
28
|
+
|
|
29
|
+
A backend/unit test suite exercises logic, not rendering. If the complaint is about
|
|
30
|
+
anything a user perceives visually or interactively -- a layout, a form, a screen
|
|
31
|
+
transition, an overlapping element -- a green suite tells you nothing about whether the
|
|
32
|
+
complaint is resolved, because no unit test renders CSS or exercises the DOM's `hidden`
|
|
33
|
+
attribute against the cascade. Confirm the fix the same way the complaint was raised:
|
|
34
|
+
drive the actual interface, the same flow the user described.
|
|
35
|
+
|
|
36
|
+
## 4. Minimal, scoped fix
|
|
37
|
+
|
|
38
|
+
Fix the reported failure. Do not refactor adjacent code, rename things, or "while I'm
|
|
39
|
+
here" clean up unrelated issues -- note them separately if they're worth flagging, but
|
|
40
|
+
scope creep on a bug-fix task is itself a defect (see Rule 6, Scope Bounding, in
|
|
41
|
+
`AGENTS.md`).
|
|
42
|
+
|
|
43
|
+
## 5. Verify by re-running the exact repro, not a nearby one
|
|
44
|
+
|
|
45
|
+
Re-run the precise steps that surfaced the complaint after the fix, in a fresh session/
|
|
46
|
+
page load, not a variation that happens to look clean. Fixing one visible symptom (e.g.
|
|
47
|
+
two forms toggling correctly) is not the same as fixing the complaint (e.g. the screen
|
|
48
|
+
staying stacked after login) if the two share a root cause but not a code path. Before
|
|
49
|
+
reporting done, ask: does this exact repro, run again, now show the correct result? If
|
|
50
|
+
you can't answer that from something you just ran, you don't have a verified fix yet.
|
|
51
|
+
|
|
52
|
+
## 6. Never claim credit for cleaning up a resource you didn't create
|
|
53
|
+
|
|
54
|
+
If you started a server, process, or container to test the fix, stop only that one, and
|
|
55
|
+
report the specific identifier (PID, port, handle) you yourself created it with. A
|
|
56
|
+
process that predates your task, or one you didn't verify the origin of, is out of scope
|
|
57
|
+
to touch and out of scope to claim you stopped -- "confirmed nothing is listening" is not
|
|
58
|
+
the same claim as "confirmed *the process I started* is gone," and conflating the two in
|
|
59
|
+
a report has caused a pre-existing, unrelated process to be killed without authorization
|
|
60
|
+
while the report described it as the agent's own test server.
|
|
61
|
+
|
|
62
|
+
## 7. Report what you found, not how confident you feel
|
|
63
|
+
|
|
64
|
+
State: what was still broken, the root cause, exactly what changed, and the repro output
|
|
65
|
+
that confirms it's fixed now. "Should be fixed" or "this looks correct now" is not a
|
|
66
|
+
substitute for pasting the result of re-running the repro.
|
|
@@ -40,6 +40,7 @@ Do not use decorative buzzwords that inflate importance without adding technical
|
|
|
40
40
|
1. **Active Voice & Factual Density:** Explain what the code does, what parameters it receives, and what command validates it.
|
|
41
41
|
2. **State Measurable Facts:** Replace "ultra-fast performance" with measured latency or memory consumption (for example: "< 30MB RAM, < 50ms startup").
|
|
42
42
|
3. **No Unverifiable Claims:** If a metric has not been empirically benchmarked, do not state it as fact.
|
|
43
|
+
4. **Changelog Entries, One Line Each:** One bullet per change, present tense, user-facing ("Fixed X.", "Added Y."). No mechanism internals, no multi-sentence rationale, no nested sub-bullets explaining how it works -- that belongs in code comments, tests, or the PR description, not in CHANGELOG.md. See this file's own `## [1.3.0]` and `## [1.4.0]` entries for the target density.
|
|
43
44
|
|
|
44
45
|
---
|
|
45
46
|
|
package/CHANGELOG.md
CHANGED
|
@@ -6,7 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Install a specific release
|
|
7
7
|
with `npx github:tBeltty/agentic-sdd-framework#v<version>`.
|
|
8
8
|
|
|
9
|
-
## [
|
|
9
|
+
## [1.5.0] - 2026-09-27
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Constitution check: a non-blocking gate step that warns when a critical constitution rule is missing or the constitution is empty.
|
|
13
|
+
- `sdd-init --rules=all|critical|none` to choose which default constitution rules the wizard writes.
|
|
14
|
+
- `sdd-add-rule` to add a constitution rule correctly after day-0, with `--fulfills=<id>` for a custom equivalent of a default rule.
|
|
15
|
+
- The constitution's Author Attribution rule now also requires confirming `git config user.email`/`user.name` before a repository's first commit, instead of trusting whatever identity the global git default resolves to.
|
|
16
|
+
- `fix-and-verify` skill: reproduce a bug report before diagnosing it, verify a UI-facing fix by driving the interface instead of trusting a green test suite, and never claim credit for stopping a process the agent didn't itself spawn.
|
|
17
|
+
- The Mandatory Verification rule now also covers UI-render evidence and requires naming the exact identifier (PID, handle, container id) behind any claim of having stopped, cleaned up, or removed a process or resource.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- `sdd-init`'s default `sdd.config.json` no longer claims "clean-architecture" for every new project; it says the style is not yet decided until the discovery interview records one.
|
|
10
21
|
|
|
11
22
|
## [1.4.0] - 2026-09-25
|
|
12
23
|
|
package/README.md
CHANGED
|
@@ -1,92 +1,68 @@
|
|
|
1
1
|
# Agentic SDD Framework
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **Deterministic governance for AI coding agents.** Stop Claude Code, Cursor, Antigravity, and Codex from hallucinating completed tasks and drifting out of scope.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/agentic-sdd-framework)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://nodejs.org/)
|
|
6
8
|
[](https://github.com/tBeltty/agentic-sdd-framework/actions)
|
|
7
9
|
|
|
8
10
|
---
|
|
9
11
|
|
|
10
|
-
##
|
|
12
|
+
## The Problem
|
|
11
13
|
|
|
12
|
-
AI coding agents
|
|
14
|
+
AI coding agents write code fast, but uncontrolled:
|
|
15
|
+
- They check off `[x] Done` without actually running the code.
|
|
16
|
+
- They silently guess the stack, add dependencies, and drift from what was asked.
|
|
17
|
+
- They pass a manual review because reading terminal output line by line is tedious, so nobody does it every time.
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
2. **A specification with evidence.** Tasks are checked off with recorded command output, and a spec is completed only with a recorded verification run tied to the exact content it verified.
|
|
16
|
-
3. **A quality gate.** A pre-push hook checks the commits being pushed for secrets, prose rules, file size limits, and specification evidence.
|
|
19
|
+
## The Solution
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
V2 --> V3[Unverified Multi-File Edits]
|
|
23
|
-
V3 --> V4[Regression Cascade]
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
subgraph AgenticSDD [With Agentic SDD]
|
|
27
|
-
S1[Constitution and Discovery] --> S2[Specification]
|
|
28
|
-
S2 --> S3[Atomic Tasks with Evidence]
|
|
29
|
-
S3 --> S4[Recorded Verification Gate]
|
|
30
|
-
end
|
|
31
|
-
```
|
|
21
|
+
Agentic SDD adds a specification lifecycle to your repository that an agent cannot talk its way around:
|
|
22
|
+
1. **Rules that load without a prompt.** `AGENTS.md` and `CLAUDE.md` point every supported agent at a constitution before it writes a line of code.
|
|
23
|
+
2. **Evidence, not claims.** A task cannot be checked off by hand: `sdd-verify` runs the command and stamps the result with a hash tied to the exact file state it verified.
|
|
24
|
+
3. **A gate that actually blocks.** A pre-push hook fails the push, before it reaches `origin`, if evidence is missing, faked, or stale.
|
|
32
25
|
|
|
33
26
|
---
|
|
34
27
|
|
|
35
|
-
##
|
|
28
|
+
## See It Work
|
|
36
29
|
|
|
37
|
-
|
|
30
|
+
```text
|
|
31
|
+
$ git push
|
|
32
|
+
❌ T1 is checked but has no evidence.
|
|
33
|
+
Quality gate failed — push rejected.
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
B -->|Solo Dev / Lightweight MVP| C[Lite Mode - Default]
|
|
43
|
-
C --> C1[Single File: docs/SPEC.md]
|
|
44
|
-
C1 --> C2[Recorded Verification Gate]
|
|
45
|
-
B -->|Multi-Agent / Enterprise System| D[Rigor Mode]
|
|
46
|
-
D --> D1[Plan, Execution Guide, Compliance Log, Annexes]
|
|
47
|
-
D1 --> D2[Negative Control Gates]
|
|
48
|
-
```
|
|
35
|
+
$ sdd-verify --task T1 -- npm test
|
|
36
|
+
$ 42 passed
|
|
37
|
+
✅ T1: exit 0, evidence recorded in docs/SPEC.md.
|
|
49
38
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* **Best For:** Solo developers, utilities, early-stage MVPs.
|
|
39
|
+
$ git add -A && git commit -m "T1: parser" && git push
|
|
40
|
+
✅ Quality gate passed.
|
|
41
|
+
```
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
* **The Auditor-Executor document set** in `docs/roadmap/`, in the format of [auditor-executor-protocol](https://github.com/tBeltty/auditor-executor-protocol):
|
|
57
|
-
* `plan-of-record.md`: the "what" and "why" (phases and trade-offs).
|
|
58
|
-
* `execution-guide.md`: the "how" (numbered tasks `P<phase>-T<n>` and gates `P<phase>-G<n>`).
|
|
59
|
-
* `compliance-log.md`: the ledger of pasted command output and verdicts.
|
|
60
|
-
* `annexes/`: self-contained remediation orders issued after a verdict that is not a clean `APPROVED`.
|
|
61
|
-
* **Mechanical Checks:** the quality gate runs `auditkit lint` (0.3.9 or newer), which rejects missing or orphaned task entries, gates without a negative control, and `DONE` reports without pasted verify output.
|
|
62
|
-
* **Best For:** Multi-agent handoffs, asynchronous work, and regulated domains.
|
|
43
|
+
Nobody edits the spec by hand to mark a task done: `sdd-verify` is the only thing allowed to write evidence, and it only writes what actually happened.
|
|
63
44
|
|
|
64
45
|
---
|
|
65
46
|
|
|
66
|
-
##
|
|
67
|
-
|
|
68
|
-
**Requirements:** Git and Node.js 22 LTS or newer (24 LTS recommended), for projects in any language. Rigor mode also needs Python 3.9+ for `auditkit`:
|
|
47
|
+
## Quickstart
|
|
69
48
|
|
|
70
49
|
```bash
|
|
71
|
-
|
|
50
|
+
cd your-project
|
|
51
|
+
npx github:tBeltty/agentic-sdd-framework#v1.4.0
|
|
72
52
|
```
|
|
73
53
|
|
|
74
|
-
|
|
75
|
-
Run the wizard from the root of a new or existing Git repository. Pin a release tag so a later change to `main` never reaches you unannounced (see [CHANGELOG.md](CHANGELOG.md)):
|
|
54
|
+
The interactive wizard asks a few questions (specification mode, coding agent skills, runtime) and sets up agent rules, a starter spec, and the pre-push hook. Non-interactive:
|
|
76
55
|
|
|
77
56
|
```bash
|
|
78
|
-
|
|
79
|
-
npx github:tBeltty/agentic-sdd-framework#v1.4.0
|
|
57
|
+
npx github:tBeltty/agentic-sdd-framework#v1.4.0 --express --mode=lite --runtime=node-24-lts
|
|
80
58
|
```
|
|
81
59
|
|
|
82
|
-
|
|
60
|
+
**Requirements:** Git and Node.js 22 LTS or newer (24 LTS recommended), for a project in any language. That's it. [Rigor mode](#progressive-modes-lite-vs-rigor) needs one more tool; see below.
|
|
83
61
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
```
|
|
62
|
+
<details>
|
|
63
|
+
<summary>Other ways to install</summary>
|
|
87
64
|
|
|
88
|
-
|
|
89
|
-
Use the framework repository itself as the starting point of a new project:
|
|
65
|
+
**Start from a clone:** use the framework repository itself as the starting point of a new project.
|
|
90
66
|
|
|
91
67
|
```bash
|
|
92
68
|
git clone --branch v1.4.0 https://github.com/tBeltty/agentic-sdd-framework.git my-project
|
|
@@ -94,7 +70,37 @@ cd my-project
|
|
|
94
70
|
node scripts/sdd-init.js
|
|
95
71
|
```
|
|
96
72
|
|
|
97
|
-
|
|
73
|
+
**Pin a release tag.** `#v1.4.0` above pins the exact release, so a later change to `main` never reaches you unannounced. See [CHANGELOG.md](CHANGELOG.md) for what changed in each version.
|
|
74
|
+
|
|
75
|
+
</details>
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## How It Works: The 3 Pillars
|
|
80
|
+
|
|
81
|
+
1. **Constitution (`AGENTS.md`, `CLAUDE.md`):** 8 non-negotiable rules, each with a "why this rule exists" field, loaded automatically by every supported agent. No prompt engineering required to keep an agent inside its lane.
|
|
82
|
+
2. **Evidence-Based Spec (`sdd-verify`):** `docs/SPEC.md` holds atomic tasks; `sdd-verify --task <ID> -- <command>` runs a command and records its output as that task's evidence, and `sdd-verify --record` does the same for the spec's overall verification command. See [docs/architecture/spec-integrity.md](docs/architecture/spec-integrity.md) for exactly how that evidence is hashed and checked.
|
|
83
|
+
3. **Quality Gate (pre-push hook):** before a push reaches `origin`, it's checked for secrets, low-effort AI prose, oversized files, and specification evidence, in one process with one exit code.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Progressive Modes: Lite vs. Rigor
|
|
88
|
+
|
|
89
|
+
Projects start simple and scale as complexity grows. The mode is set in `sdd.config.json`.
|
|
90
|
+
|
|
91
|
+
| | 🟢 Lite (default) | 🔴 Rigor (opt-in) |
|
|
92
|
+
| :--- | :--- | :--- |
|
|
93
|
+
| **Use it for** | Solo developers, utilities, early-stage MVPs | Multi-agent handoffs, asynchronous work, regulated domains |
|
|
94
|
+
| **Spec lives in** | One file: `docs/SPEC.md` | `docs/roadmap/`: a plan, an execution guide, a compliance log, and remediation annexes |
|
|
95
|
+
| **Format** | Tasks with recorded evidence and a verification gate | The [auditor-executor-protocol](https://github.com/tBeltty/auditor-executor-protocol) document set, with negative-control gates |
|
|
96
|
+
| **Extra requirement** | None | Python 3.9+ and `auditkit`: `pipx install git+https://github.com/tBeltty/auditor-executor-protocol` |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Commands & CLI Reference
|
|
101
|
+
|
|
102
|
+
<details>
|
|
103
|
+
<summary><strong>Wizard flags</strong> (<code>sdd-init</code>)</summary>
|
|
98
104
|
|
|
99
105
|
Flags take `--flag=value` or `--flag value`. Unknown flags are an error.
|
|
100
106
|
|
|
@@ -105,82 +111,71 @@ Flags take `--flag=value` or `--flag value`. Unknown flags are an error.
|
|
|
105
111
|
| `--name=<name>` | Project name | Target directory name |
|
|
106
112
|
| `--runtime=<id>` | `node-24-lts`, `go-1.23`, `python-3.12`, ... | `node-24-lts` |
|
|
107
113
|
| `--mode=<mode>` | `lite`, `rigor` | `lite` |
|
|
114
|
+
| `--rules=<selection>` | `all`, `critical`, `none` (constitution rules to include; see the Constitution check row below) | `all` |
|
|
108
115
|
| `--ast=<adapter>` | `ast-grep`, `graphify`, `ripgrep`, `lsp` | `ast-grep` |
|
|
109
116
|
| `--concurrency=`, `--hardware=`, `--workload=` | Discovery answers recorded in ADR-0001 | Small internal service |
|
|
110
117
|
| `--i18n`, `--pwa` | Enable the capability flags | Disabled |
|
|
111
118
|
| `--force` | Refresh copied skills, templates, and `.claude/skills/` copies | Keep existing copies |
|
|
112
119
|
| `--help` | Print usage | |
|
|
113
120
|
|
|
114
|
-
Rerunning the wizard is safe
|
|
121
|
+
Rerunning the wizard is safe: existing documents are kept, `sdd.config.json` is merged and validated (`x-` prefixed keys are free-form), and `AGENTS.md` / `CLAUDE.md` are only rewritten while they carry the `sdd:managed` marker.
|
|
122
|
+
|
|
123
|
+
</details>
|
|
115
124
|
|
|
116
|
-
|
|
125
|
+
<details>
|
|
126
|
+
<summary><strong>What the wizard generates</strong></summary>
|
|
117
127
|
|
|
118
128
|
| Path | Purpose |
|
|
119
129
|
| :--- | :--- |
|
|
120
130
|
| `AGENTS.md` | Entry point read automatically by Codex, Cursor, and other AGENTS.md-aware agents |
|
|
121
131
|
| `CLAUDE.md` | Imports the entry point, constitution, and context into Claude Code |
|
|
122
|
-
| `.claude/skills/` | Symlinks to `.agents/skills/` (copies where symlinks are unavailable)
|
|
123
|
-
| `.agents/AGENTS.md` | Constitution: 8 non-negotiable rules
|
|
132
|
+
| `.claude/skills/` | Symlinks to `.agents/skills/` (copies where symlinks are unavailable) |
|
|
133
|
+
| `.agents/AGENTS.md` | Constitution: 8 non-negotiable rules |
|
|
124
134
|
| `.agents/CONTEXT.md` | Project facts, incident registry, technical debt, and non-goals |
|
|
125
135
|
| `docs/SPEC.md` (Lite) or `docs/roadmap/` (Rigor) | Active specification documents, checked by the quality gate |
|
|
126
136
|
| `docs/decisions/ADR-0001-stack-and-architecture.md` | Stack decision record seeded with the discovery answers |
|
|
127
137
|
| `sdd.config.json` | Configuration, validated against [`scripts/lib/sdd.config.schema.json`](scripts/lib/sdd.config.schema.json) |
|
|
128
|
-
| `.sdd/scripts/`, `.sdd/VERSION` | Quality gate tooling and its version (install mode only) |
|
|
129
138
|
| Git `pre-push` hook | Runs the quality gate on the pushed commits; an existing hook is kept as `pre-push.local` and runs first |
|
|
130
139
|
|
|
131
|
-
When `core.hooksPath` is set (Husky, lefthook, or a shared hooks directory), the wizard installs nothing there and prints the command to add
|
|
140
|
+
When `core.hooksPath` is set (Husky, lefthook, or a shared hooks directory), the wizard installs nothing there and prints the command to add instead.
|
|
132
141
|
|
|
133
|
-
|
|
142
|
+
</details>
|
|
134
143
|
|
|
135
|
-
|
|
144
|
+
<details>
|
|
145
|
+
<summary><strong>Quality gate</strong> (<code>quality-gate.js</code>)</summary>
|
|
136
146
|
|
|
137
|
-
|
|
147
|
+
Runs every check in one process, exits 1 if any fails. Each run reads files from one source:
|
|
138
148
|
|
|
139
149
|
| Invocation | Checks |
|
|
140
150
|
| :--- | :--- |
|
|
141
151
|
| *(no flag)* | Tracked files in the working tree |
|
|
142
152
|
| `--staged` | The index: what the next commit contains |
|
|
143
|
-
| `--ref=<commit>`
|
|
144
|
-
| `--push [remote]` | Pre-push mode
|
|
145
|
-
|
|
146
|
-
Unknown flags are errors, so a typo never falls back to checking the working tree. A check that cannot read the repository (not a Git repository, Git error, unreadable file) fails; it never reports "0 files, all clean". An invalid `sdd.config.json` (unknown key, wrong type, unknown value) fails every check with the exact problem.
|
|
147
|
-
|
|
148
|
-
| Check | Script | Configuration (`sdd.config.json`) |
|
|
149
|
-
| :--- | :--- | :--- |
|
|
150
|
-
| Secret leak scanner | `verify-no-secrets.js` | `security.allowFiles`; the `sdd-allow-secret` line pragma (suppressions are counted in the report) |
|
|
151
|
-
| No-AI-Slop copy linter | `check-copy-slop.js` | `capabilities.noAiSlop.enabled`, `.exclude`, `.maxEmDashes` |
|
|
152
|
-
| File size limit | `check-file-size.js` | `architecture.maxLocPerFile` (0 disables), `architecture.maxLocExclude` |
|
|
153
|
-
| Specification check | `check-spec.js` | `specification.mode`, `.specFile`, `.roadmapDir`, `.requireRecordedEvidence` |
|
|
154
|
-
| Version sync | `check-versions.js` | Applies only when `project.type` is `framework` |
|
|
155
|
-
|
|
156
|
-
The secret scanner reports provider keys (Anthropic, OpenAI, Stripe, GitHub, Slack, Resend, AWS, Google), private key blocks (including PGP), credentials embedded in URLs, high-entropy values assigned to secret-named keys (`password`, `client_secret`, `access_token`, `SECRET_KEY`, `signing_key`, ...; unquoted values count in env, config, rc, shell, and Docker files), and tracked secret files (`.env`, `id_rsa`, `*.key`, `*.p12`, ...). It skips only its own file at the paths the framework installs it (`scripts/` and `.sdd/scripts/`) and `node_modules/` directories; lockfiles are scanned, since a private-registry URL can embed a token. It is a regex scanner, not a replacement for a dedicated tool such as gitleaks.
|
|
153
|
+
| `--ref=<commit>` | The content of that commit |
|
|
154
|
+
| `--push [remote]` | Pre-push mode: every check on each pushed ref's tip, plus a secret scan of every new commit in the push |
|
|
157
155
|
|
|
158
|
-
|
|
156
|
+
Unknown flags are errors. A check that cannot read the repository fails loudly instead of reporting "0 files, clean".
|
|
159
157
|
|
|
160
|
-
|
|
|
158
|
+
| Check | Enforces |
|
|
161
159
|
| :--- | :--- |
|
|
162
|
-
|
|
|
163
|
-
|
|
|
164
|
-
|
|
|
165
|
-
|
|
|
166
|
-
|
|
|
167
|
-
|
|
|
168
|
-
|
|
169
|
-
The gate reads the spec the way it renders: it parses it with a CommonMark parser (markdown-it, vendored in `scripts/lib/vendor/`, MIT) and takes tasks, the Status, the evidence, and the verification command from the parsed structure, so a line counts only if it renders as what it claims to be. On top of that, a small subset keeps GitHub's renderer and the parser in agreement: no raw HTML outside code (HTML comments included), no link reference definitions or footnotes (inline links only), no HTML entities or invisible and non-ASCII whitespace characters, spaces instead of tabs for indentation, and the Status and gate fields written exactly as in the template (a line that reads as a field name followed by a colon, or emphasized, in any other spelling is an error). Code is taken verbatim. Anything else fails with the line number and what to change; the template and everything `sdd-verify` writes stay inside the subset. `scripts/dev/fuzz-spec-markup.js` compares the gate's reading with cmark-gfm, GitHub's renderer, on random specs.
|
|
160
|
+
| Secret leak scanner | Provider keys, private key blocks, credentials in URLs, secret-named assignments, tracked secret files |
|
|
161
|
+
| No-AI-Slop copy linter | Rejects generic AI-written prose patterns |
|
|
162
|
+
| File size limit | `architecture.maxLocPerFile` |
|
|
163
|
+
| Specification check | Status, evidence, and verification gate rules (see below) |
|
|
164
|
+
| Constitution check | Non-blocking: 3 severity levels based on which of the 8 default rules are fulfilled (see below) |
|
|
165
|
+
| Version sync | `package.json` and `sdd.config.json` versions match (framework repo only) |
|
|
170
166
|
|
|
171
|
-
|
|
167
|
+
**Constitution check** never fails the gate. Neither the rule count nor the "why this rule exists" rationale is mandatory, and a project can deliberately ship with none of the 8 default rules, or with entirely custom ones. The wizard's 8 defaults split into 3 critical (verification, secrets, scope) and 5 moderate suggestions; `sdd-init --rules=all|critical|none` (or the guided prompt) picks which ship, and each already carries real rationale, not a placeholder. The gate reads three outcomes: all 3 critical + 5 moderate present is silent (✅), all 3 critical present with some moderate missing is a light note (💡, optional), and a missing critical rule or an empty constitution is a warning (⚠️); none of them block the push. A rule is "present" only through its `<!-- sdd:rule id="..." tier="..." -->` tag, never by matching heading text, so an expert can replace a default with their own equivalent (`sdd-add-rule --fulfills=<id>`) and the gate still recognizes it. Add a rule after day-0 with `sdd-add-rule --title <t> --rule <r> --why <w>`, the sanctioned way to record a rule, instead of an agent writing "always do X" into memory, `CLAUDE.md`, or an unrelated doc the gate never reads.
|
|
172
168
|
|
|
173
|
-
|
|
169
|
+
**Specification check, Lite mode:** exactly one Status line (`Draft`, `In Progress`, `Completed`); every checked task has recorded, unedited evidence; `In Progress`/`Completed` need a real verification command and expected output; `Completed` needs every task checked and an unedited PASS from `sdd-verify --record` matching the current file state. **Rigor mode:** `auditkit lint docs/roadmap` exits 0.
|
|
174
170
|
|
|
175
|
-
|
|
171
|
+
Full mechanism (hashing, CommonMark parsing subset, timeouts): [docs/architecture/spec-integrity.md](docs/architecture/spec-integrity.md).
|
|
176
172
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
`check-system-prerequisites.js` (Git identity, `gh` authentication, SSH keys) runs once inside the wizard and is available as `npm run check:prereqs`. It is not part of the gate because it depends on the local machine, not on the code.
|
|
173
|
+
</details>
|
|
180
174
|
|
|
181
175
|
---
|
|
182
176
|
|
|
183
|
-
|
|
177
|
+
<details>
|
|
178
|
+
<summary><strong>Repository Layout</strong></summary>
|
|
184
179
|
|
|
185
180
|
```text
|
|
186
181
|
agentic-sdd-framework/
|
|
@@ -196,6 +191,7 @@ agentic-sdd-framework/
|
|
|
196
191
|
│
|
|
197
192
|
├── docs/
|
|
198
193
|
│ ├── SPEC_TEMPLATE.md # Lite Mode template
|
|
194
|
+
│ ├── architecture/ # How the gate and spec integrity work internally
|
|
199
195
|
│ ├── decisions/ADR_TEMPLATE.md # Architecture Decision Record template
|
|
200
196
|
│ ├── roadmap/templates/ # Rigor Mode templates (vendored from auditkit)
|
|
201
197
|
│ ├── incidents/ # Post-mortem template
|
|
@@ -214,31 +210,29 @@ agentic-sdd-framework/
|
|
|
214
210
|
│ ├── check-system-prerequisites.js # Day-0 Git, gh CLI, and SSH checks
|
|
215
211
|
│ ├── install-git-hooks.js # pre-push hook installer
|
|
216
212
|
│ ├── lib/ # Git sources, config schema, spec parser, provisioning
|
|
217
|
-
│ └── dev/
|
|
213
|
+
│ └── dev/ # Vendoring sync and the spec parser's differential fuzz
|
|
218
214
|
│
|
|
219
215
|
├── test/ # node:test suites (npm test)
|
|
220
216
|
├── CHANGELOG.md # Release notes
|
|
221
217
|
└── sdd.config.json # Configuration of this repository
|
|
222
218
|
```
|
|
223
219
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
## 📜 Open-Source Attributions
|
|
227
|
-
|
|
228
|
-
The **Agentic SDD Framework** integrates, adapts, or provides adapters for the following open-source projects:
|
|
229
|
-
|
|
230
|
-
| Component | Author / Organization | Upstream Repository | License | Role |
|
|
231
|
-
| :--- | :--- | :--- | :--- | :--- |
|
|
232
|
-
| **Auditor-Executor Protocol** | **tBeltty** | [tBeltty/auditor-executor-protocol](https://github.com/tBeltty/auditor-executor-protocol) | MIT | Multi-agent coordination, Rigor Mode documents, and negative control gates. |
|
|
233
|
-
| **Spec-Kit Concepts** | **GitHub** | [github/spec-kit](https://github.com/github/spec-kit) | MIT | Progressive specification hierarchy, unified single-spec model, and interactive constitution. |
|
|
234
|
-
| **No-AI-Slop** | **Peter Yang** | [petergyang/no-ai-slop](https://github.com/petergyang/no-ai-slop) | MIT | Writing rules behind the copy linter. |
|
|
235
|
-
| **Graphify** | **Graphify Labs** | [Graphify-Labs/graphify](https://github.com/Graphify-Labs/graphify) | Apache 2.0 | Relational knowledge graph adapter for code navigation. |
|
|
236
|
-
| **ast-grep** | **Herrington Darkholme** | [ast-grep/ast-grep](https://github.com/ast-grep/ast-grep) | MIT | Tree-sitter structural search adapter. |
|
|
237
|
-
| **ripgrep** | **Andrew Gallant** | [BurntSushi/ripgrep](https://github.com/BurntSushi/ripgrep) | MIT / Unlicense | Regex text search adapter. |
|
|
238
|
-
| **SCIP / LSP** | **SCIP Code** (originally Sourcegraph) | [scip-code/scip](https://github.com/scip-code/scip) | Apache 2.0 | Language Server Protocol code intelligence adapter. |
|
|
220
|
+
</details>
|
|
239
221
|
|
|
240
222
|
---
|
|
241
223
|
|
|
242
|
-
##
|
|
224
|
+
## Attributions & License
|
|
243
225
|
|
|
244
|
-
This repository is
|
|
226
|
+
This repository is [MIT licensed](LICENSE). It integrates, adapts, or provides adapters for:
|
|
227
|
+
|
|
228
|
+
| Component | Author | License |
|
|
229
|
+
| :--- | :--- | :--- |
|
|
230
|
+
| [Auditor-Executor Protocol](https://github.com/tBeltty/auditor-executor-protocol) | tBeltty | MIT |
|
|
231
|
+
| [Spec-Kit Concepts](https://github.com/github/spec-kit) | GitHub | MIT |
|
|
232
|
+
| [No-AI-Slop](https://github.com/petergyang/no-ai-slop) | Peter Yang | MIT |
|
|
233
|
+
| [Graphify](https://github.com/Graphify-Labs/graphify) | Graphify Labs | Apache 2.0 |
|
|
234
|
+
| [ast-grep](https://github.com/ast-grep/ast-grep) | Herrington Darkholme | MIT |
|
|
235
|
+
| [ripgrep](https://github.com/BurntSushi/ripgrep) | Andrew Gallant | MIT / Unlicense |
|
|
236
|
+
| [SCIP / LSP](https://github.com/scip-code/scip) | SCIP Code (originally Sourcegraph) | Apache 2.0 |
|
|
237
|
+
|
|
238
|
+
The vendored `no-ai-slop` skill keeps its upstream MIT license ([`.agents/skills/no-ai-slop/LICENSE`](.agents/skills/no-ai-slop/LICENSE)).
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Specification Integrity: How the Gate Reads and Verifies the Spec
|
|
2
|
+
|
|
3
|
+
This document covers the internals behind the "cryptographic proofs" and "zero-trust gate"
|
|
4
|
+
claims in the [README](../../README.md): how `docs/SPEC.md` is parsed, how evidence is hashed,
|
|
5
|
+
and what the gate does and does not guarantee. Read this if you are auditing the framework,
|
|
6
|
+
contributing to it, or curious about the mechanism. Skip it if you just want to use it.
|
|
7
|
+
|
|
8
|
+
## The spec is read the way it renders
|
|
9
|
+
|
|
10
|
+
The gate does not scan `docs/SPEC.md` line by line. It parses it with a real CommonMark parser
|
|
11
|
+
(markdown-it, vendored in [`scripts/lib/vendor/`](../../scripts/lib/vendor/), MIT, no npm
|
|
12
|
+
dependency) and takes tasks, the Status, the evidence, and the verification command from the
|
|
13
|
+
parsed token tree: a line counts only if it renders as what it claims to be.
|
|
14
|
+
|
|
15
|
+
On top of that, a small Markdown subset keeps GitHub's renderer (cmark-gfm) and the parser in
|
|
16
|
+
agreement, for the narrow set of cases where they could still differ:
|
|
17
|
+
|
|
18
|
+
- no raw HTML outside code (HTML comments included);
|
|
19
|
+
- no link reference definitions or footnotes (inline links only);
|
|
20
|
+
- no HTML entities, invisible characters, or non-ASCII whitespace outside code;
|
|
21
|
+
- spaces instead of tabs for indentation;
|
|
22
|
+
- the Status and gate fields written exactly as in the template (a line that reads as a field
|
|
23
|
+
name followed by a colon, or emphasized, in any other spelling, is an error);
|
|
24
|
+
- a line right after a list item, indented 4 or more spaces but fewer than that item's own
|
|
25
|
+
content column, and starting with `>`, `#`, or a fence, is rejected: cmark-gfm and
|
|
26
|
+
markdown-it can disagree on whether it continues the item or starts indented code.
|
|
27
|
+
|
|
28
|
+
Code (fenced or indented) is taken verbatim and never checked against these rules. Anything
|
|
29
|
+
outside the subset fails with the line number and what to change; the template and everything
|
|
30
|
+
`sdd-verify` writes stay inside it.
|
|
31
|
+
|
|
32
|
+
[`scripts/dev/fuzz-spec-markup.js`](../../scripts/dev/fuzz-spec-markup.js) compares the gate's
|
|
33
|
+
reading against cmark-gfm on tens of thousands of randomly generated specs on every change to
|
|
34
|
+
the parser, looking for a rendered unchecked task the gate does not see, a rendered Status
|
|
35
|
+
different from the parsed one, or a gate command different from the rendered one. This subset
|
|
36
|
+
and parser have been through ten rounds of adversarial review closing bypasses found this way.
|
|
37
|
+
|
|
38
|
+
## Recorded evidence and the verification hash chain
|
|
39
|
+
|
|
40
|
+
`sdd-verify --record` runs the spec's verification command, checks that every expected line
|
|
41
|
+
appears in the output (`/.../` lines are regular expressions), fails if the command modified
|
|
42
|
+
tracked files, and writes:
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
Last Verified: <date> PASS|FAIL (commit <sha>, exit <code>, state <fingerprint>, check <hash>)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- **`state <fingerprint>`** is a hash over every tracked file except the spec itself, computed
|
|
49
|
+
at record time. The gate recomputes it for the commit that completed the spec (or the
|
|
50
|
+
uncommitted state); a file changed after recording invalidates the PASS.
|
|
51
|
+
- **`check <hash>`** covers the other fields plus the verification command and expected output,
|
|
52
|
+
so hand-editing the result, or changing the command after recording, reopens the spec.
|
|
53
|
+
- Task evidence recorded by `sdd-verify --task <ID> -- <command>` carries its own hash over the
|
|
54
|
+
date, exit code, and transcript, so an edited transcript is detected the same way.
|
|
55
|
+
|
|
56
|
+
Later commits that do not touch the spec do not reopen it: catching regressions after a spec is
|
|
57
|
+
closed is the job of CI and tests, not the gate.
|
|
58
|
+
|
|
59
|
+
**These are integrity checks, not signatures.** They catch hand edits and stale records, but
|
|
60
|
+
anyone who can run `sdd-verify` can also write a matching record; there is no key involved.
|
|
61
|
+
For an authoritative result, have CI run `sdd-verify` again. Recorded evidence also cannot prove
|
|
62
|
+
a verification command is meaningful; that remains the reviewer's call.
|
|
63
|
+
|
|
64
|
+
**The framework does not guarantee bug-free code, and does not claim to.** It guarantees that
|
|
65
|
+
a checked-off task has a command that actually ran, exited the code it says it did, against
|
|
66
|
+
the file state it says it did. It does not guarantee that command was the right one to catch
|
|
67
|
+
the bug that matters: a backend unit suite passing has, in practice, certified a UI as working
|
|
68
|
+
while a CSS rule silently defeated the `hidden` attribute and stacked two screens on top of
|
|
69
|
+
each other, invisible to any test that never renders a page. Writing a verification command
|
|
70
|
+
that actually exercises the failure mode in play (a live render, not just an exit code, for
|
|
71
|
+
anything a user perceives directly) is still the task author's judgment call -- the gate
|
|
72
|
+
enforces that a command ran and its evidence is intact, not that the command was sufficient.
|
|
73
|
+
|
|
74
|
+
## Execution environment
|
|
75
|
+
|
|
76
|
+
Commands run in `specification.verifyShell` (default `/bin/sh` on macOS and Linux, `cmd.exe` on
|
|
77
|
+
Windows) with a limit of `specification.verifyTimeoutSeconds` (default 900 seconds). The gate
|
|
78
|
+
never runs a command from the spec on its own initiative; it only checks recorded results. On
|
|
79
|
+
timeout, the whole process tree is killed, including background processes the command started,
|
|
80
|
+
and output is decoded as a UTF-8 stream.
|
|
81
|
+
|
|
82
|
+
The check finds the commit that completed the spec in Git history, so CI needs the full history:
|
|
83
|
+
in GitHub Actions, use `actions/checkout` with `fetch-depth: 0`. In a shallow clone, the check
|
|
84
|
+
fails and says so instead of guessing.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-sdd-framework",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Spec-Driven Development governance for AI coding agents: auto-loaded agent rules, specifications with recorded evidence, and a pre-push quality gate",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sdd-init": "scripts/sdd-init.js"
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
"check:slop": "node scripts/check-copy-slop.js",
|
|
27
27
|
"check:size": "node scripts/check-file-size.js",
|
|
28
28
|
"check:spec": "node scripts/check-spec.js",
|
|
29
|
+
"check:constitution": "node scripts/check-constitution.js",
|
|
30
|
+
"add-rule": "node scripts/sdd-add-rule.js",
|
|
29
31
|
"check:versions": "node scripts/check-versions.js",
|
|
30
32
|
"install-hooks": "node scripts/install-git-hooks.js",
|
|
31
33
|
"quality-gate": "node scripts/quality-gate.js"
|
|
@@ -44,6 +46,6 @@
|
|
|
44
46
|
"license": "MIT",
|
|
45
47
|
"repository": {
|
|
46
48
|
"type": "git",
|
|
47
|
-
"url": "https://github.com/tBeltty/agentic-sdd-framework.git"
|
|
49
|
+
"url": "git+https://github.com/tBeltty/agentic-sdd-framework.git"
|
|
48
50
|
}
|
|
49
51
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/check-constitution.js
|
|
5
|
+
*
|
|
6
|
+
* Non-blocking reminder about the project's constitution (.agents/AGENTS.md). It never
|
|
7
|
+
* fails the gate: neither the rule count nor the "why this rule exists" rationale is
|
|
8
|
+
* mandatory, and a project can deliberately ship with none of the 8 defaults, or with
|
|
9
|
+
* its own rules entirely. What this catches is the unattended case -- the wizard's
|
|
10
|
+
* placeholder rationale left standing in for a rule nobody actually wrote -- and nudges
|
|
11
|
+
* toward the 3 critical rules (verification, secrets, scope) before the first push.
|
|
12
|
+
*
|
|
13
|
+
* A rule counts as present only through its "<!-- sdd:rule id="..." tier="..." -->" tag,
|
|
14
|
+
* never by matching heading text. That is what lets an expert replace a default rule with
|
|
15
|
+
* their own equivalent (same id, entirely different title and wording, added with
|
|
16
|
+
* sdd-add-rule --fulfills=<id>) and still have the gate recognize it -- a declared,
|
|
17
|
+
* greppable fact instead of the gate guessing at semantic equivalence.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const { readFile, WORKTREE } = require('./lib/git');
|
|
21
|
+
const { runCheckCli } = require('./lib/cli');
|
|
22
|
+
const { RULES, CRITICAL_IDS, MODERATE_IDS } = require('./lib/constitution-rules');
|
|
23
|
+
|
|
24
|
+
const CONSTITUTION_PATH = '.agents/AGENTS.md';
|
|
25
|
+
const PLACEHOLDER = /\[Document the incident or rationale here\./;
|
|
26
|
+
const RULE_HEADING = /^##\s+\d+\./gm;
|
|
27
|
+
const TAG = /<!--\s*sdd:rule\s+id="([\w-]+)"\s+tier="[\w-]+"\s*-->/g;
|
|
28
|
+
const ESSENTIALS_HINT = 'secrets handling, mandatory verification, and scope bounding';
|
|
29
|
+
|
|
30
|
+
// Rule blocks are separated by the template's "\n\n---\n\n"; a tag is only trusted if a
|
|
31
|
+
// rule's own content (up to the next tag or end of file) has no unedited placeholder text.
|
|
32
|
+
function fulfilledIds(text) {
|
|
33
|
+
const tags = [...text.matchAll(TAG)];
|
|
34
|
+
const fulfilled = new Set();
|
|
35
|
+
tags.forEach((tag, i) => {
|
|
36
|
+
const end = i + 1 < tags.length ? tags[i + 1].index : text.length;
|
|
37
|
+
const body = text.slice(tag.index, end);
|
|
38
|
+
if (!PLACEHOLDER.test(body)) fulfilled.add(tag[1]);
|
|
39
|
+
});
|
|
40
|
+
return fulfilled;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function labelsFor(ids) {
|
|
44
|
+
return ids.map(id => RULES[id].label).join(', ');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function run({ root, source = WORKTREE } = {}) {
|
|
48
|
+
const buffer = readFile(root, CONSTITUTION_PATH, source);
|
|
49
|
+
if (buffer === null) {
|
|
50
|
+
return {
|
|
51
|
+
ok: true,
|
|
52
|
+
report: `⚠️ No ${CONSTITUTION_PATH}. An agent has no constitution to load. Not required, ` +
|
|
53
|
+
`but worth deciding on purpose -- at least the essentials (${ESSENTIALS_HINT}) go a long way.`
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const text = buffer.toString('utf8');
|
|
58
|
+
const totalHeadings = (text.match(RULE_HEADING) || []).length;
|
|
59
|
+
if (totalHeadings === 0) {
|
|
60
|
+
return {
|
|
61
|
+
ok: true,
|
|
62
|
+
report: `⚠️ ${CONSTITUTION_PATH} has no rules. That can be a deliberate choice, ` +
|
|
63
|
+
`but make sure it is the one you meant to make -- at least the essentials (${ESSENTIALS_HINT}) go a long way.`
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const fulfilled = fulfilledIds(text);
|
|
68
|
+
const criticalFound = CRITICAL_IDS.filter(id => fulfilled.has(id));
|
|
69
|
+
const moderateFound = MODERATE_IDS.filter(id => fulfilled.has(id));
|
|
70
|
+
const missingCritical = CRITICAL_IDS.filter(id => !fulfilled.has(id));
|
|
71
|
+
const missingModerate = MODERATE_IDS.filter(id => !fulfilled.has(id));
|
|
72
|
+
|
|
73
|
+
if (missingCritical.length === 0 && missingModerate.length === 0) {
|
|
74
|
+
return { ok: true, report: `✅ ${CONSTITUTION_PATH}: ${totalHeadings} rule(s), all 3 critical and 5 moderate defaults fulfilled.` };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (missingCritical.length === 0) {
|
|
78
|
+
return {
|
|
79
|
+
ok: true,
|
|
80
|
+
report: `💡 ${CONSTITUTION_PATH}: all 3 critical rules are set (${labelsFor(CRITICAL_IDS)}). ` +
|
|
81
|
+
`${missingModerate.length}/5 moderate suggestion(s) are not included -- optional: ${labelsFor(missingModerate)}.`
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
ok: true,
|
|
87
|
+
report: `⚠️ ${CONSTITUTION_PATH} is missing ${missingCritical.length}/3 critical rule(s): ${labelsFor(missingCritical)}. ` +
|
|
88
|
+
`These back the framework's core promise (${ESSENTIALS_HINT}). Add them with the wizard, or write your own ` +
|
|
89
|
+
`equivalent with sdd-add-rule --fulfills=<id> -- see scripts/lib/constitution-rules.js for the ids.`
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (require.main === module) {
|
|
94
|
+
runCheckCli('📜 Agentic SDD Framework: Constitution Check', run);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { run, fulfilledIds, PLACEHOLDER, CONSTITUTION_PATH };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/lib/constitution-rules.js
|
|
3
|
+
*
|
|
4
|
+
* Identity of the 8 default constitution rules the wizard offers, shared by the wizard
|
|
5
|
+
* (rule selection), the constitution check (what "critical" and "moderate" mean), and
|
|
6
|
+
* sdd-add-rule (validating --fulfills). The id is the stable handle: an expert can write
|
|
7
|
+
* their own rule text under the same id and the gate still recognizes it as fulfilling
|
|
8
|
+
* that slot, because it never matches on heading text, only on this tag.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const RULES = {
|
|
12
|
+
'discovery-first': { tier: 'moderate', label: 'Discovery First' },
|
|
13
|
+
'evidence-driven-debugging': { tier: 'moderate', label: 'Evidence-Driven Debugging' },
|
|
14
|
+
'mandatory-verification': { tier: 'critical', label: 'Mandatory Verification' },
|
|
15
|
+
'closed-network-testing': { tier: 'moderate', label: 'Closed-Network Testing Isolation' },
|
|
16
|
+
'zero-trust-secrets': { tier: 'critical', label: 'Zero-Trust Secrets Management' },
|
|
17
|
+
'scope-bounding': { tier: 'critical', label: 'Scope Bounding & Atomic Progression' },
|
|
18
|
+
'factual-copy': { tier: 'moderate', label: 'Factual Technical Copy (No AI Slop)' },
|
|
19
|
+
'author-attribution': { tier: 'moderate', label: 'Author Attribution & Integrity' }
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const CRITICAL_IDS = Object.keys(RULES).filter(id => RULES[id].tier === 'critical');
|
|
23
|
+
const MODERATE_IDS = Object.keys(RULES).filter(id => RULES[id].tier === 'moderate');
|
|
24
|
+
|
|
25
|
+
module.exports = { RULES, CRITICAL_IDS, MODERATE_IDS };
|
package/scripts/lib/provision.js
CHANGED
|
@@ -15,6 +15,7 @@ const fs = require('fs');
|
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const { install: installHook } = require('../install-git-hooks');
|
|
17
17
|
const { validateConfig, getIn } = require('./config');
|
|
18
|
+
const { CRITICAL_IDS } = require('./constitution-rules');
|
|
18
19
|
|
|
19
20
|
const FRAMEWORK_ROOT = path.resolve(__dirname, '..', '..');
|
|
20
21
|
const MANAGED_MARKER = 'sdd:managed';
|
|
@@ -23,10 +24,12 @@ const MANAGED_MARKER = 'sdd:managed';
|
|
|
23
24
|
const COPY_MARKER = '.sdd-managed-copy';
|
|
24
25
|
const SPEC_MODES = ['lite', 'rigor'];
|
|
25
26
|
const AST_ADAPTERS = ['ast-grep', 'graphify', 'ripgrep', 'lsp'];
|
|
27
|
+
const RULE_SELECTIONS = ['all', 'critical', 'none'];
|
|
26
28
|
const TOOL_DIR = '.sdd/scripts';
|
|
27
29
|
const TOOL_FILES = [
|
|
28
30
|
'quality-gate.js', 'verify-no-secrets.js', 'check-copy-slop.js', 'check-file-size.js', 'check-spec.js',
|
|
29
|
-
'check-versions.js', 'check-system-prerequisites.js', 'install-git-hooks.js',
|
|
31
|
+
'check-constitution.js', 'check-versions.js', 'check-system-prerequisites.js', 'install-git-hooks.js',
|
|
32
|
+
'sdd-verify.js', 'sdd-add-rule.js', 'lib'
|
|
30
33
|
];
|
|
31
34
|
const TEMPLATES = [
|
|
32
35
|
'.agents/AGENTS.template.md',
|
|
@@ -85,12 +88,38 @@ function normalizeAnswers(answers) {
|
|
|
85
88
|
if (!AST_ADAPTERS.includes(answers.astAdapter)) {
|
|
86
89
|
throw new Error(`Unknown AST adapter "${answers.astAdapter}". Valid: ${AST_ADAPTERS.join(', ')}.`);
|
|
87
90
|
}
|
|
88
|
-
|
|
91
|
+
const rules = answers.rules || 'all';
|
|
92
|
+
if (!RULE_SELECTIONS.includes(rules)) {
|
|
93
|
+
throw new Error(`Unknown rules selection "${answers.rules}". Valid: ${RULE_SELECTIONS.join(', ')}.`);
|
|
94
|
+
}
|
|
95
|
+
return { ...answers, specMode, rules };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Keeps or drops the wizard's 8 default rule blocks in .agents/AGENTS.md, tagged
|
|
99
|
+
// "<!-- sdd:rule id="..." tier="..." -->" in the template, and renumbers what remains.
|
|
100
|
+
// A block without a tag (hand-written, added later) is always kept -- this only ever
|
|
101
|
+
// touches the day-0 defaults. Neither the rule count nor "why this rule exists" is
|
|
102
|
+
// mandatory; "all" (default) and "none" are both valid choices, made once, on purpose.
|
|
103
|
+
function filterConstitution(text, selection) {
|
|
104
|
+
if (selection === 'all') return text;
|
|
105
|
+
const keepIds = selection === 'critical' ? new Set(CRITICAL_IDS) : new Set();
|
|
106
|
+
const SEP = '\n\n---\n\n';
|
|
107
|
+
const [header, ...blocks] = text.split(SEP);
|
|
108
|
+
const kept = blocks.filter(block => {
|
|
109
|
+
const tag = block.match(/<!--\s*sdd:rule\s+id="([\w-]+)"/);
|
|
110
|
+
return !tag || keepIds.has(tag[1]);
|
|
111
|
+
});
|
|
112
|
+
let n = 0;
|
|
113
|
+
const renumbered = kept.map(block => block.replace(/^## \d+\./m, () => `## ${++n}.`));
|
|
114
|
+
return [header, ...renumbered].join(SEP);
|
|
89
115
|
}
|
|
90
116
|
|
|
91
117
|
function buildConfig(answers, existing) {
|
|
118
|
+
// No default architecture style: the strategic-cto skill's discovery interview decides
|
|
119
|
+
// this per project (see ADR-0001), and "clean-architecture" for every new project was a
|
|
120
|
+
// claim nobody actually made.
|
|
92
121
|
const defaults = {
|
|
93
|
-
architecture: { style: '
|
|
122
|
+
architecture: { style: 'Not yet decided (see docs/decisions/ADR-0001-stack-and-architecture.md)', maxLocPerFile: 400 },
|
|
94
123
|
capabilities: { noAiSlop: { enabled: true } }
|
|
95
124
|
};
|
|
96
125
|
const generated = {
|
|
@@ -173,7 +202,8 @@ function renderEntrypoint(answers, gateCommand, config) {
|
|
|
173
202
|
AST_ADAPTER: answers.astAdapter,
|
|
174
203
|
RUNTIME: answers.runtime,
|
|
175
204
|
SPEC_MODE: answers.specMode,
|
|
176
|
-
GATE_COMMAND: gateCommand
|
|
205
|
+
GATE_COMMAND: gateCommand,
|
|
206
|
+
ADD_RULE_COMMAND: `node ${toolDir}/sdd-add-rule.js`
|
|
177
207
|
};
|
|
178
208
|
const template = fs.readFileSync(path.join(FRAMEWORK_ROOT, '.agents/ENTRYPOINT.template.md'), 'utf8');
|
|
179
209
|
return Object.entries(values).reduce((out, [key, value]) => replaceLiteral(out, `{{${key}}}`, value), template);
|
|
@@ -252,7 +282,7 @@ function provision(rawAnswers, { target = process.cwd(), force = false, log = co
|
|
|
252
282
|
record(existingConfig ? 'updated' : 'created', 'sdd.config.json');
|
|
253
283
|
|
|
254
284
|
// 3. Governance documents.
|
|
255
|
-
createFromTemplate('.agents/AGENTS.template.md', '.agents/AGENTS.md');
|
|
285
|
+
createFromTemplate('.agents/AGENTS.template.md', '.agents/AGENTS.md', t => filterConstitution(t, answers.rules));
|
|
256
286
|
createFromTemplate('.agents/CONTEXT.template.md', '.agents/CONTEXT.md', t => fillContext(t, answers, config));
|
|
257
287
|
// Documents go where the config says, so the gate checks the files sdd-init created.
|
|
258
288
|
const specFile = getIn(config, 'specification.specFile', 'docs/SPEC.md');
|
package/scripts/quality-gate.js
CHANGED
|
@@ -29,6 +29,7 @@ const CHECKS = [
|
|
|
29
29
|
{ title: '✍️ No-AI-Slop Copy Linter', module: require('./check-copy-slop') },
|
|
30
30
|
{ title: '📏 File Size Limit', module: require('./check-file-size') },
|
|
31
31
|
{ title: '📋 Specification Check', module: require('./check-spec') },
|
|
32
|
+
{ title: '📜 Constitution Check', module: require('./check-constitution') },
|
|
32
33
|
{ title: '🏷️ Version Sync Check', module: require('./check-versions') }
|
|
33
34
|
];
|
|
34
35
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/sdd-add-rule.js
|
|
5
|
+
*
|
|
6
|
+
* The sanctioned way to add a rule to .agents/AGENTS.md after day-0. Appends a correctly
|
|
7
|
+
* numbered, tagged rule block instead of an agent freehanding "always do X" into chat
|
|
8
|
+
* memory, CLAUDE.md, or an unrelated doc -- none of which every agent reads, and none of
|
|
9
|
+
* which the constitution check ever looks at.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* sdd-add-rule --title "<title>" --rule "<the operating rule>" --why "<why this rule exists>"
|
|
13
|
+
* [--fulfills=<id>]
|
|
14
|
+
*
|
|
15
|
+
* --fulfills replaces one of the 8 default rules with the caller's own equivalent: the
|
|
16
|
+
* new block is tagged with that rule's id, so the constitution check recognizes it as
|
|
17
|
+
* fulfilling that slot even though the heading and text are entirely custom. See
|
|
18
|
+
* scripts/lib/constitution-rules.js for the known ids.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const { repoRoot } = require('./lib/git');
|
|
24
|
+
const { RULES } = require('./lib/constitution-rules');
|
|
25
|
+
|
|
26
|
+
const CONSTITUTION_PATH = '.agents/AGENTS.md';
|
|
27
|
+
const FLAGS = ['title', 'rule', 'why', 'fulfills'];
|
|
28
|
+
const PLACEHOLDER = /\[.*\]/;
|
|
29
|
+
const USAGE = 'Usage: sdd-add-rule --title "<title>" --rule "<rule>" --why "<why this rule exists>" [--fulfills=<id>]';
|
|
30
|
+
|
|
31
|
+
function parseArgs(argv) {
|
|
32
|
+
const values = {};
|
|
33
|
+
for (let i = 0; i < argv.length; i++) {
|
|
34
|
+
const arg = argv[i];
|
|
35
|
+
const match = arg.match(/^--([a-z]+)(?:=(.*))?$/);
|
|
36
|
+
if (!match || !FLAGS.includes(match[1])) throw new Error(`Unknown argument "${arg}". ${USAGE}`);
|
|
37
|
+
const [, name, inline] = match;
|
|
38
|
+
const value = inline !== undefined ? inline : argv[++i];
|
|
39
|
+
if (value === undefined || (inline === undefined && value.startsWith('--'))) {
|
|
40
|
+
throw new Error(`--${name} needs a value. ${USAGE}`);
|
|
41
|
+
}
|
|
42
|
+
values[name] = value;
|
|
43
|
+
}
|
|
44
|
+
return values;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function slugify(title) {
|
|
48
|
+
return title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'custom-rule';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function nextNumber(text) {
|
|
52
|
+
const numbers = [...text.matchAll(/^## (\d+)\./gm)].map(m => Number(m[1]));
|
|
53
|
+
return numbers.length ? Math.max(...numbers) + 1 : 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function run({ root, argv }) {
|
|
57
|
+
let values;
|
|
58
|
+
try {
|
|
59
|
+
values = parseArgs(argv);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return { ok: false, report: `❌ ${error.message}` };
|
|
62
|
+
}
|
|
63
|
+
const { title, rule, why, fulfills } = values;
|
|
64
|
+
const missing = ['title', 'rule', 'why'].filter(name => !values[name] || !values[name].trim());
|
|
65
|
+
if (missing.length > 0) {
|
|
66
|
+
return { ok: false, report: `❌ --${missing.join(', --')} required and cannot be empty. A rule with no stated rationale is exactly the placeholder problem this command exists to avoid.` };
|
|
67
|
+
}
|
|
68
|
+
if ([title, rule, why].some(v => PLACEHOLDER.test(v))) {
|
|
69
|
+
return { ok: false, report: '❌ --title, --rule, and --why must be real text, not a bracketed placeholder.' };
|
|
70
|
+
}
|
|
71
|
+
if (fulfills && !RULES[fulfills]) {
|
|
72
|
+
return { ok: false, report: `❌ Unknown --fulfills id "${fulfills}". Known ids: ${Object.keys(RULES).join(', ')}.` };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const file = path.join(root, CONSTITUTION_PATH);
|
|
76
|
+
if (!fs.existsSync(file)) {
|
|
77
|
+
return { ok: false, report: `❌ ${CONSTITUTION_PATH} does not exist. Run sdd-init first.` };
|
|
78
|
+
}
|
|
79
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
80
|
+
const number = nextNumber(text);
|
|
81
|
+
const id = fulfills || slugify(title);
|
|
82
|
+
const tier = fulfills ? RULES[fulfills].tier : 'custom';
|
|
83
|
+
const block = [
|
|
84
|
+
'---', '',
|
|
85
|
+
`<!-- sdd:rule id="${id}" tier="${tier}" -->`,
|
|
86
|
+
`## ${number}. ${title}`,
|
|
87
|
+
`* **Rule:** ${rule}`,
|
|
88
|
+
'* **Why this rule exists:**',
|
|
89
|
+
` > ${why}`,
|
|
90
|
+
''
|
|
91
|
+
].join('\n');
|
|
92
|
+
fs.writeFileSync(file, `${text.replace(/\n*$/, '')}\n\n${block}`);
|
|
93
|
+
const recognized = fulfills ? ` Recognized by the gate as fulfilling the "${fulfills}" ${tier} rule.` : '';
|
|
94
|
+
return { ok: true, report: `✅ Added rule ${number} ("${title}") to ${CONSTITUTION_PATH}.${recognized}` };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (require.main === module) {
|
|
98
|
+
let result;
|
|
99
|
+
try {
|
|
100
|
+
result = run({ root: repoRoot(), argv: process.argv.slice(2) });
|
|
101
|
+
} catch (error) {
|
|
102
|
+
result = { ok: false, report: `❌ ${error.message}` };
|
|
103
|
+
}
|
|
104
|
+
(result.ok ? console.log : console.error)(result.report);
|
|
105
|
+
process.exit(result.ok ? 0 : 1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = { run, slugify, nextNumber };
|
package/scripts/sdd-init.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* --mode=lite|rigor Specification depth (default: lite)
|
|
19
19
|
* --ast=<adapter> ast-grep | graphify | ripgrep | lsp (default: ast-grep)
|
|
20
20
|
* --concurrency=, --hardware=, --workload= Discovery answers
|
|
21
|
+
* --rules=all|critical|none Constitution rules to include (default: all)
|
|
21
22
|
* --i18n, --pwa Enable the matching capability flags
|
|
22
23
|
* --force Refresh copied skills and templates in install mode
|
|
23
24
|
* --help Print this help
|
|
@@ -30,12 +31,12 @@ const { execFileSync, spawnSync } = require('child_process');
|
|
|
30
31
|
const { FRAMEWORK_ROOT, provision } = require('./lib/provision');
|
|
31
32
|
const { loadConfig, getIn } = require('./lib/config');
|
|
32
33
|
|
|
33
|
-
const VALUE_FLAGS = ['target', 'name', 'runtime', 'mode', 'ast', 'concurrency', 'hardware', 'workload'];
|
|
34
|
+
const VALUE_FLAGS = ['target', 'name', 'runtime', 'mode', 'ast', 'concurrency', 'hardware', 'workload', 'rules'];
|
|
34
35
|
const BOOLEAN_FLAGS = ['express', 'i18n', 'pwa', 'force', 'help'];
|
|
35
36
|
const USAGE = `Usage: sdd-init [--express] [--target=<dir>] [--name=<name>] [--runtime=<id>]
|
|
36
37
|
[--mode=lite|rigor] [--ast=ast-grep|graphify|ripgrep|lsp]
|
|
37
38
|
[--concurrency=<text>] [--hardware=<text>] [--workload=<text>]
|
|
38
|
-
[--i18n] [--pwa] [--force] [--help]`;
|
|
39
|
+
[--rules=all|critical|none] [--i18n] [--pwa] [--force] [--help]`;
|
|
39
40
|
|
|
40
41
|
function parseArgs(argv) {
|
|
41
42
|
const values = new Map();
|
|
@@ -175,8 +176,15 @@ async function runGuidedMode() {
|
|
|
175
176
|
const astDefault = Object.keys(astAdapters).find(key => astAdapters[key] === EXISTING.astAdapter) || '1';
|
|
176
177
|
const astAdapter = astAdapters[await ask('Select AST Adapter (1-4)', astDefault)] || 'ast-grep';
|
|
177
178
|
|
|
179
|
+
console.log('\n--- Step 6: Constitution Rules ---');
|
|
180
|
+
console.log(' [1] All 8 (default): 3 critical + 5 moderate suggestions.');
|
|
181
|
+
console.log(' [2] Critical only: verification, secrets, and scope -- the 3 the framework leans on most.');
|
|
182
|
+
console.log(' [3] None: start from a blank constitution.');
|
|
183
|
+
const ruleChoices = { '1': 'all', '2': 'critical', '3': 'none' };
|
|
184
|
+
const rules = ruleChoices[await ask('Select Rules (1-3)', '1')] || 'all';
|
|
185
|
+
|
|
178
186
|
rl.close();
|
|
179
|
-
bootstrap({ projectName, runtime, specMode, astAdapter, concurrency, hardware, workload, i18n, pwa });
|
|
187
|
+
bootstrap({ projectName, runtime, specMode, astAdapter, rules, concurrency, hardware, workload, i18n, pwa });
|
|
180
188
|
}
|
|
181
189
|
|
|
182
190
|
function runExpressMode() {
|
|
@@ -187,6 +195,7 @@ function runExpressMode() {
|
|
|
187
195
|
runtime: getArgValue('runtime', DEFAULTS.runtime),
|
|
188
196
|
specMode: getArgValue('mode', EXISTING.specMode || 'lite').toLowerCase(),
|
|
189
197
|
astAdapter: getArgValue('ast', EXISTING.astAdapter || 'ast-grep').toLowerCase(),
|
|
198
|
+
rules: getArgValue('rules', 'all').toLowerCase(),
|
|
190
199
|
concurrency: getArgValue('concurrency', DEFAULTS.concurrency),
|
|
191
200
|
hardware: getArgValue('hardware', DEFAULTS.hardware),
|
|
192
201
|
workload: getArgValue('workload', DEFAULTS.workload),
|
|
@@ -218,7 +227,9 @@ function bootstrap(answers) {
|
|
|
218
227
|
console.log(' were left untouched, so agents will not load the rules until you add to them:');
|
|
219
228
|
console.log(' "Before any task, read .agents/AGENTS.md and .agents/CONTEXT.md."');
|
|
220
229
|
}
|
|
221
|
-
|
|
230
|
+
const toolDir = result.gateCommand.replace(/^node /, '').replace(/\/quality-gate\.js$/, '');
|
|
231
|
+
console.log(' 2. Optional: record the incident or rationale behind each rule in .agents/AGENTS.md.');
|
|
232
|
+
console.log(` Add a new rule later with node ${toolDir}/sdd-add-rule.js, not memory or another doc.`);
|
|
222
233
|
const { specification = {} } = result.config;
|
|
223
234
|
if (answers.specMode === 'lite') {
|
|
224
235
|
console.log(` 3. Define your tasks in ${specification.specFile || 'docs/SPEC.md'} and implement with verifiable gates.`);
|
package/sdd.config.json
CHANGED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# scripts/dev/set-npm-publish-token.sh
|
|
3
|
-
#
|
|
4
|
-
# One-time setup: stores an npm publish token as the GitHub Actions secret
|
|
5
|
-
# NPM_TOKEN on this repository, so the release workflow can publish to npm
|
|
6
|
-
# without anyone's npm password or token ever appearing in chat, a commit,
|
|
7
|
-
# or this terminal's scrollback.
|
|
8
|
-
#
|
|
9
|
-
# The token is read with hidden input and piped straight to `gh secret set`
|
|
10
|
-
# via stdin; it is never written to disk and never echoed back.
|
|
11
|
-
#
|
|
12
|
-
# Prerequisite: create the token yourself at
|
|
13
|
-
# https://www.npmjs.com/settings/<your-username>/tokens
|
|
14
|
-
# Choose "Automation" (Classic Token), or a Granular Access Token scoped to
|
|
15
|
-
# read/write on this package — CI has no interactive 2FA prompt, so a token
|
|
16
|
-
# type that skips it is required.
|
|
17
|
-
|
|
18
|
-
set -eu
|
|
19
|
-
|
|
20
|
-
REPO="tBeltty/agentic-sdd-framework"
|
|
21
|
-
|
|
22
|
-
command -v gh >/dev/null 2>&1 || { echo "GitHub CLI (gh) is required. Install it, then run this again." >&2; exit 1; }
|
|
23
|
-
gh auth status >/dev/null 2>&1 || { echo "Run 'gh auth login' first, then run this again." >&2; exit 1; }
|
|
24
|
-
|
|
25
|
-
echo "This will set the NPM_TOKEN secret on: $REPO"
|
|
26
|
-
printf "npm token (input hidden): "
|
|
27
|
-
stty -echo
|
|
28
|
-
IFS= read -r NPM_TOKEN
|
|
29
|
-
stty echo
|
|
30
|
-
echo
|
|
31
|
-
|
|
32
|
-
if [ -z "$NPM_TOKEN" ]; then
|
|
33
|
-
echo "No token entered; nothing was set." >&2
|
|
34
|
-
exit 1
|
|
35
|
-
fi
|
|
36
|
-
|
|
37
|
-
printf '%s' "$NPM_TOKEN" | gh secret set NPM_TOKEN --repo "$REPO" --body-file -
|
|
38
|
-
unset NPM_TOKEN
|
|
39
|
-
|
|
40
|
-
echo "Done: NPM_TOKEN is set on $REPO. The value was not saved anywhere and was not shown."
|