@stdd/cli 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Azamat Almazbek uulu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,211 @@
1
+ <h1 align="center">stdd</h1>
2
+
3
+ <p align="center"><strong>S</strong>pec + <strong>T</strong>est <strong>D</strong>riven <strong>D</strong>evelopment — a markdown-first methodology kit for teams building software with AI coding agents.</p>
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/vsem-azamat/stdd/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/vsem-azamat/stdd/actions/workflows/ci.yml/badge.svg" /></a>
7
+ <a href="https://www.npmjs.com/package/@stdd/cli"><img alt="npm version" src="https://img.shields.io/npm/v/%40stdd%2Fcli?style=flat-square" /></a>
8
+ <img alt="node 20+" src="https://img.shields.io/badge/node-20%2B-brightgreen?style=flat-square" />
9
+ <img alt="zero dependencies" src="https://img.shields.io/badge/dependencies-0-blue?style=flat-square" />
10
+ <a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" /></a>
11
+ </p>
12
+
13
+ ```text
14
+ → the docs edit IS the spec — reviewed before any code
15
+ → tests gate behavior; visuals are design-first
16
+ → working artifacts are never committed
17
+ → one agent-neutral source, compiled per agent
18
+ → mechanics over discipline — what can be checked, CI checks
19
+ ```
20
+
21
+ ## Why
22
+
23
+ AI coding agents amplify a quiet failure mode: **committed working artifacts**.
24
+ Plans and spec files written for one change land in the repo, go stale, and
25
+ keep winning code search — an agent greps the tree, finds a convincing
26
+ month-old spec that a newer doc contradicts, and confidently builds the wrong
27
+ thing. Popular SDD frameworks make this worse by design: change folders and
28
+ archives accumulate authoritative-looking text with no machine-readable
29
+ authority.
30
+
31
+ stdd inverts the model. Your permanent documentation tree is the **single**
32
+ source of truth. The edit to that tree is the spec — it comes first, in a
33
+ reviewable diff, before the failing test; the failing test comes before the
34
+ implementation. Everything ephemeral lives where ephemeral things belong:
35
+ rationale in the PR description, history in git, deferred designs as dated
36
+ project-log records. What can be verified mechanically, CI verifies; the
37
+ rest is a written contract you review against — not folklore.
38
+
39
+ ## The loop
40
+
41
+ ```mermaid
42
+ flowchart LR
43
+ A["Classify<br/>the change"] --> B{"Behavior<br/>change?"}
44
+ B -- "implementation-only" --> E["Implement"]
45
+ B -- "yes" --> C["Read docs →<br/><b>edit docs = the spec</b><br/>(first reviewable diff)"]
46
+ C --> D["Failing<br/>test"]
47
+ D --> E
48
+ E --> F["Verify"]
49
+ F --> G["PR evidence<br/>(CI-checked)"]
50
+
51
+ C -. "frontend visual work:<br/>design-first — build, review<br/>screenshots, then test behavior" .-> E
52
+ ```
53
+
54
+ ## Where knowledge lives
55
+
56
+ One truth inside the tree; everything ephemeral outside it — so an agent
57
+ grepping the repo can only find the present. The one dated exception, the
58
+ project log, is marked machine-readably (`authority: non-canonical`
59
+ frontmatter) and the generated agent instructions forbid searching it
60
+ unless the user explicitly asks for history or deferred work.
61
+
62
+ ```mermaid
63
+ flowchart TD
64
+ CH(["a change"])
65
+
66
+ subgraph TREE["in the repository — what agents grep"]
67
+ DOCS["<b>docs/</b> — canonical truth<br/>present tense, single source"]
68
+ LOG["docs/project/ — dated records<br/>deferred designs, decisions<br/><i>non-canonical</i>"]
69
+ STDD[".stdd/ — method + playbooks"]
70
+ end
71
+
72
+ subgraph OUT["outside the tree — explicit access only"]
73
+ PR["PR description<br/>rationale, alternatives, scope"]
74
+ GIT["git history<br/>what changed and why"]
75
+ PAD["session scratchpad<br/>plans, task lists"]
76
+ end
77
+
78
+ CH -- "durable rules" --> DOCS
79
+ CH -- "deferred design" --> LOG
80
+ CH -- "rationale" --> PR
81
+ CH -- "history" --> GIT
82
+ CH -- "plans, sequencing" --> PAD
83
+ ```
84
+
85
+ ## See it in action
86
+
87
+ ```text
88
+ You: We need admins to adjust an order's price before invoicing.
89
+
90
+ AI: [stdd-brainstorming] One question: order-level or per-item? …
91
+ Agreed: order-level signed adjustment, review-status only, reason
92
+ required, customer notified.
93
+
94
+ AI: Commit 1 — docs/domain/pricing.md: "Manual Price Adjustment" rules.
95
+ (the spec, reviewable on its own)
96
+ AI: Commit 2 — failing integration test for the new rules.
97
+ AI: Commit 3 — implementation; test green; verification output attached.
98
+
99
+ PR body:
100
+ Docs updated first: docs/domain/pricing.md
101
+ Decisions and alternatives: per-item adjustment rejected because …
102
+
103
+ CI: stdd check ✓ no committed working artifacts, no temporal narrative
104
+ stdd check-pr ✓ evidence line names the changed docs
105
+ ```
106
+
107
+ ## Quick start
108
+
109
+ ```bash
110
+ npm install -g @stdd/cli
111
+ cd your-project
112
+ stdd init --tools claude,codex
113
+ ```
114
+
115
+ `stdd init` installs `.stdd/` (the method contract + playbooks + config),
116
+ generates Claude Code skills, and prints the section to add to your
117
+ `AGENTS.md` for Codex and any other agent that reads it. Everything it
118
+ generates is recorded with content hashes in `.stdd/manifest.json`, so
119
+ `check` and `doctor` detect hand edits and stale copies of any generated
120
+ file — not just version drift.
121
+
122
+ Not sure where an existing repo stands? Get a report in seconds:
123
+
124
+ ```console
125
+ $ npx @stdd/cli doctor
126
+ ✗ 6 committed working artifacts may mislead coding agents
127
+ ✗ 2 canonical docs contain temporal narrative
128
+ ✓ generated files match stdd v0.0.1
129
+ ✗ AGENTS.md has no STDD section — paste .stdd/AGENTS-snippet.md
130
+ ```
131
+
132
+ Then wire the guards into CI:
133
+
134
+ ```yaml
135
+ - run: npx @stdd/cli check # tree invariants
136
+ - env:
137
+ PR_BODY: ${{ github.event.pull_request.body }}
138
+ run: printf '%s' "$PR_BODY" | npx @stdd/cli check-pr - # docs evidence line
139
+ ```
140
+
141
+ ## Commands
142
+
143
+ | Command | What it does |
144
+ | --- | --- |
145
+ | `stdd init [dir] [--tools claude,codex]` | Install `.stdd/` and compile playbooks per agent |
146
+ | `stdd doctor [dir]` | Adoption health report: setup, canonical docs, misleading artifacts, drift — exits 1 on findings |
147
+ | `stdd check [dir]` | CI guard: no committed working artifacts, no temporal narrative in canonical docs, no stale or hand-edited generated files |
148
+ | `stdd check-pr <file\|->` | CI guard: PR body carries exactly one non-empty docs evidence line |
149
+
150
+ All checks are configured in `.stdd/config.json` (glob patterns for
151
+ forbidden artifacts, canonical docs, temporal phrases).
152
+
153
+ ## What's in the box
154
+
155
+ | Path | Contents |
156
+ | --- | --- |
157
+ | [`method/`](method/README.md) | The STDD contract: the loop, the rules, the exceptions |
158
+ | [`playbooks/`](playbooks/) | Agent-neutral playbooks: brainstorming, planning, debugging, worktrees |
159
+ | [`templates/`](templates/) | PR description and deferred-design templates |
160
+ | [`adapters/`](adapters/README.md) | How playbooks compile per agent |
161
+ | [`cli/`](cli/) | Zero-dependency Node CLI |
162
+
163
+ ## The method in five rules
164
+
165
+ 1. **Classify first.** Behavior changes (anything observable) pass the full
166
+ loop; implementation-only changes skip the docs step.
167
+ 2. **The docs edit is the spec.** Missing or stale docs are updated before
168
+ tests and code, as the first reviewable unit of the change.
169
+ 3. **Red before green.** A failing test gates every behavior change —
170
+ except frontend *visual* work, which is design-first: build, review
171
+ screenshots, then test only real behavior contracts.
172
+ 4. **Working artifacts are never committed.** Rationale → PR description;
173
+ history → git; deferred designs → dated project-log entries.
174
+ 5. **Evidence, not claims.** Every PR states `Docs updated first:` /
175
+ `Docs checked, no change needed:` / `Docs not applicable:` — naming the
176
+ docs or the reason. CI rejects a missing, duplicated, or bare label.
177
+
178
+ The full contract: [`method/README.md`](method/README.md).
179
+
180
+ ## How it compares
181
+
182
+ **vs. [OpenSpec](https://github.com/Fission-AI/OpenSpec)** — OpenSpec models
183
+ changes as committed folders that archive into the repo; specs accumulate
184
+ alongside a separate docs reality. stdd keeps one truth (your docs tree),
185
+ borrows OpenSpec's best mechanics (delta discipline, drift detection,
186
+ init/update UX), and rejects the archive.
187
+
188
+ **vs. [Superpowers](https://github.com/obra/superpowers)** — great process
189
+ content, but Claude-only and delivered through plugin hooks. stdd rewrites
190
+ the process agent-neutrally and compiles it for every agent your team runs.
191
+
192
+ **vs. nothing** — chat context evaporates; agents build before agreeing on
193
+ scope; stale plans mislead the next session. stdd fixes the workflow, not
194
+ the model.
195
+
196
+ ## Development
197
+
198
+ ```bash
199
+ npm ci
200
+ npm test # node:test — unit + CLI integration
201
+ npm run check # Biome (Rust) — lint + format, CI mode
202
+ npm run format # Biome — write fixes
203
+ npm run selfcheck # stdd check on this repo (dogfooding)
204
+ ```
205
+
206
+ This repository follows its own method: PRs carry a docs evidence line
207
+ (enforced by CI via `stdd check-pr`), and no working artifacts are committed.
208
+
209
+ ## License
210
+
211
+ [MIT](LICENSE)
@@ -0,0 +1,38 @@
1
+ # Adapters
2
+
3
+ Playbooks are agent-neutral markdown with frontmatter (`name`, `description`,
4
+ `when`). Adapters compile them into what each agent consumes. `stdd init`
5
+ runs the adapters; re-run it after upgrading stdd to refresh the output.
6
+
7
+ ## Common output
8
+
9
+ Every init installs `.stdd/` into the target repo:
10
+
11
+ ```
12
+ .stdd/
13
+ ├── method.md # the STDD contract (copy of method/README.md)
14
+ ├── playbooks/ # agent-neutral playbooks
15
+ └── config.json # stdd check configuration
16
+ ```
17
+
18
+ `.stdd/` is committed — it is methodology, not a working artifact.
19
+
20
+ ## claude (Claude Code)
21
+
22
+ Writes one skill per playbook to `.claude/skills/<name>/SKILL.md`:
23
+ frontmatter maps `name`/`description` directly; the body is the playbook
24
+ body. Skills are self-contained copies — regenerate, never hand-edit.
25
+
26
+ ## codex (and any agent that reads AGENTS.md)
27
+
28
+ Writes `.stdd/AGENTS-snippet.md` and prints it. Paste (or `@`-include) the
29
+ snippet into the repo's `AGENTS.md`. The snippet is short: it points the
30
+ agent at `.stdd/method.md` and lists the playbooks with their `when` lines.
31
+
32
+ ## Design rules for adapters
33
+
34
+ - One source of truth: adapters copy or point, never fork playbook content.
35
+ - No agent-specific incantations inside `playbooks/` — if an agent needs
36
+ special framing, that framing lives in the adapter.
37
+ - Calm imperative prose. No all-caps compliance shouting: if a rule needs
38
+ shouting to be followed, it needs a `stdd check` rule instead.
package/cli/lib.mjs ADDED
@@ -0,0 +1,124 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ /** Content fingerprint used by the generated-files manifest. */
4
+ export function sha256(content) {
5
+ return `sha256:${createHash("sha256").update(content).digest("hex")}`;
6
+ }
7
+
8
+ export const DEFAULT_CONFIG = {
9
+ // Working-artifact paths that must never be committed. `stdd check` fails
10
+ // if any tracked file matches. Deliberately narrow — widen per repo.
11
+ forbiddenArtifacts: ["docs/**/plans/**", "**/*.agent-plan.md", "**/*.agent-spec.md"],
12
+ // Canonical docs must describe the present, in English. `stdd check`
13
+ // flags temporal narrative that belongs in git history or a PR
14
+ // description. Fenced code blocks are skipped.
15
+ canonicalDocs: ["docs/domain/**/*.md", "docs/product/**/*.md"],
16
+ temporalPhrases: ["previously", "no longer", "used to be", "before this change"],
17
+ };
18
+
19
+ export const EVIDENCE_LABELS = [
20
+ "Docs updated first",
21
+ "Docs checked, no change needed",
22
+ "Docs not applicable",
23
+ ];
24
+
25
+ const EVIDENCE_MATCHERS = EVIDENCE_LABELS.map((label) => ({
26
+ label,
27
+ re: new RegExp(`^${label}:[ \\t]*(.*)$`, "i"),
28
+ }));
29
+
30
+ /**
31
+ * Find docs evidence lines in a PR body. Only lines that start at the
32
+ * beginning of a line count — quoted templates (`> Docs …`) and fenced code
33
+ * blocks do not. Returns `{ label, content }` per hit; a bare label yields
34
+ * empty content.
35
+ */
36
+ export function findEvidenceLines(body) {
37
+ const hits = [];
38
+ let inFence = false;
39
+ for (const line of body.replaceAll("\r\n", "\n").split("\n")) {
40
+ if (/^\s*(```|~~~)/.test(line)) {
41
+ inFence = !inFence;
42
+ continue;
43
+ }
44
+ if (inFence) continue;
45
+ for (const { label, re } of EVIDENCE_MATCHERS) {
46
+ const m = re.exec(line);
47
+ if (m) hits.push({ label, content: m[1].trim() });
48
+ }
49
+ }
50
+ return hits;
51
+ }
52
+
53
+ /**
54
+ * Tiny glob dialect: `*` matches within a path segment, `**` matches across
55
+ * segments. No `?`, braces, or character classes — by design.
56
+ */
57
+ export function globToRegExp(glob) {
58
+ const segments = glob.split("/");
59
+ const parts = segments.map((segment, i) => {
60
+ const last = i === segments.length - 1;
61
+ if (segment === "**") return last ? ".*" : "(?:[^/]+/)*";
62
+ const escaped = segment.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", "[^/]*");
63
+ return escaped + (last ? "" : "/");
64
+ });
65
+ return new RegExp(`^${parts.join("")}$`);
66
+ }
67
+
68
+ /** Parse a `---`-fenced frontmatter block. CRLF-tolerant. */
69
+ export function parseFrontmatter(source) {
70
+ const normalized = source.replaceAll("\r\n", "\n");
71
+ const match = /^---\n([\s\S]*?)\n---\n?/.exec(normalized);
72
+ if (!match) return { meta: {}, body: normalized };
73
+ const meta = {};
74
+ for (const line of match[1].split("\n")) {
75
+ const idx = line.indexOf(":");
76
+ if (idx > 0) meta[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
77
+ }
78
+ return { meta, body: normalized.slice(match[0].length) };
79
+ }
80
+
81
+ /**
82
+ * Merge a parsed user config over the defaults and validate shape.
83
+ * Throws with an actionable message on invalid input.
84
+ */
85
+ export function mergeConfig(parsed) {
86
+ const config = { ...DEFAULT_CONFIG, ...parsed };
87
+ for (const key of ["forbiddenArtifacts", "canonicalDocs", "temporalPhrases"]) {
88
+ if (!Array.isArray(config[key]) || config[key].some((v) => typeof v !== "string")) {
89
+ throw new Error(`"${key}" must be an array of strings`);
90
+ }
91
+ }
92
+ return config;
93
+ }
94
+
95
+ /**
96
+ * Build a temporal-phrase matcher. Word-ish boundaries on both sides so
97
+ * hyphenated compounds ("no longer-lived") do not match.
98
+ */
99
+ export function temporalMatchers(phrases) {
100
+ return phrases.map((phrase) => ({
101
+ phrase,
102
+ re: new RegExp(`(?<![\\w-])${phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`, "i"),
103
+ }));
104
+ }
105
+
106
+ /**
107
+ * Scan markdown lines for temporal narrative, skipping fenced code blocks.
108
+ * Returns `{ line, phrase }` hits (1-indexed lines).
109
+ */
110
+ export function scanTemporal(lines, matchers) {
111
+ const hits = [];
112
+ let inFence = false;
113
+ lines.forEach((line, i) => {
114
+ if (/^\s*(```|~~~)/.test(line)) {
115
+ inFence = !inFence;
116
+ return;
117
+ }
118
+ if (inFence) return;
119
+ for (const { phrase, re } of matchers) {
120
+ if (re.test(line)) hits.push({ line: i + 1, phrase });
121
+ }
122
+ });
123
+ return hits;
124
+ }
package/cli/stdd.mjs ADDED
@@ -0,0 +1,363 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync } from "node:child_process";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import {
7
+ DEFAULT_CONFIG,
8
+ findEvidenceLines,
9
+ globToRegExp,
10
+ mergeConfig,
11
+ parseFrontmatter,
12
+ scanTemporal,
13
+ sha256,
14
+ temporalMatchers,
15
+ } from "./lib.mjs";
16
+
17
+ const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
18
+ const VERSION = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf8")).version;
19
+ const KNOWN_TOOLS = ["claude", "codex"];
20
+ const STAMP = `generated by stdd v${VERSION} — do not edit, re-run \`stdd init\``;
21
+
22
+ function fail(message) {
23
+ console.error(`stdd: ${message}`);
24
+ process.exit(1);
25
+ }
26
+
27
+ function listTrackedFiles(dir) {
28
+ try {
29
+ const out = execFileSync("git", ["-C", dir, "ls-files", "-z"], {
30
+ encoding: "utf8",
31
+ stdio: ["ignore", "pipe", "ignore"],
32
+ });
33
+ const files = out.split("\0").filter(Boolean);
34
+ if (files.length > 0) return files;
35
+ } catch {
36
+ // not a git repo — fall through to a filesystem walk
37
+ }
38
+ const files = [];
39
+ const walk = (rel) => {
40
+ for (const entry of fs.readdirSync(path.join(dir, rel), { withFileTypes: true })) {
41
+ if (entry.name === ".git" || entry.name === "node_modules") continue;
42
+ const relPath = rel ? `${rel}/${entry.name}` : entry.name;
43
+ if (entry.isDirectory()) walk(relPath);
44
+ else files.push(relPath);
45
+ }
46
+ };
47
+ walk("");
48
+ return files;
49
+ }
50
+
51
+ function loadPlaybooks() {
52
+ const dir = path.join(PKG_ROOT, "playbooks");
53
+ return fs
54
+ .readdirSync(dir)
55
+ .filter((f) => f.endsWith(".md"))
56
+ .map((f) => {
57
+ const source = fs.readFileSync(path.join(dir, f), "utf8");
58
+ const { meta, body } = parseFrontmatter(source);
59
+ if (!meta.name || !meta.description) {
60
+ fail(`playbook ${f} is missing required frontmatter (name, description)`);
61
+ }
62
+ return { file: f, source, meta, body };
63
+ });
64
+ }
65
+
66
+ function loadConfig(targetDir) {
67
+ const configPath = path.join(targetDir, ".stdd", "config.json");
68
+ if (!fs.existsSync(configPath)) return DEFAULT_CONFIG;
69
+ let parsed;
70
+ try {
71
+ parsed = JSON.parse(fs.readFileSync(configPath, "utf8"));
72
+ } catch (err) {
73
+ fail(`.stdd/config.json is not valid JSON: ${err.message}`);
74
+ }
75
+ try {
76
+ return mergeConfig(parsed);
77
+ } catch (err) {
78
+ fail(`.stdd/config.json: ${err.message}`);
79
+ }
80
+ }
81
+
82
+ function init(targetDir, tools) {
83
+ const stddDir = path.join(targetDir, ".stdd");
84
+ fs.mkdirSync(path.join(stddDir, "playbooks"), { recursive: true });
85
+ // Every generated file is recorded here (repo-relative path → content
86
+ // hash) and written to .stdd/manifest.json, so check/doctor can detect
87
+ // hand edits, deletions, and stale copies. User-owned files (config.json)
88
+ // are deliberately not recorded.
89
+ const generated = {};
90
+ const writeGenerated = (relPath, content) => {
91
+ fs.writeFileSync(path.join(targetDir, ...relPath.split("/")), content);
92
+ generated[relPath] = sha256(content);
93
+ };
94
+
95
+ writeGenerated(".stdd/method.md", fs.readFileSync(path.join(PKG_ROOT, "method", "README.md"), "utf8"));
96
+ const playbooks = loadPlaybooks();
97
+ for (const pb of playbooks) {
98
+ writeGenerated(`.stdd/playbooks/${pb.file}`, pb.source);
99
+ }
100
+ const configPath = path.join(stddDir, "config.json");
101
+ if (!fs.existsSync(configPath)) {
102
+ fs.writeFileSync(configPath, `${JSON.stringify(DEFAULT_CONFIG, null, "\t")}\n`);
103
+ }
104
+ console.log(`Installed .stdd/ (method, ${playbooks.length} playbooks, config)`);
105
+
106
+ if (tools.includes("claude")) {
107
+ for (const pb of playbooks) {
108
+ const skillDir = path.join(targetDir, ".claude", "skills", pb.meta.name);
109
+ fs.mkdirSync(skillDir, { recursive: true });
110
+ const skill =
111
+ `---\nname: ${pb.meta.name}\ndescription: ${pb.meta.description}\n---\n\n` +
112
+ `<!-- ${STAMP} -->\n\n${pb.body}`;
113
+ writeGenerated(`.claude/skills/${pb.meta.name}/SKILL.md`, skill);
114
+ }
115
+ console.log(`Installed ${playbooks.length} Claude Code skills under .claude/skills/`);
116
+ }
117
+
118
+ if (tools.includes("codex")) {
119
+ const lines = [
120
+ `<!-- ${STAMP} -->`,
121
+ "",
122
+ "## STDD",
123
+ "",
124
+ "This repository follows the STDD method: read `.stdd/method.md` before",
125
+ "any behavior change and follow its loop (docs edit is the spec → failing",
126
+ "test → implement → verify → PR evidence).",
127
+ "",
128
+ "Do not search the project log (dated entries marked",
129
+ "`authority: non-canonical`, e.g. `docs/project/`) unless the user",
130
+ "explicitly asks for historical rationale or deferred work — it records",
131
+ "past decisions and future intentions, never the present.",
132
+ "",
133
+ "Playbooks in `.stdd/playbooks/`:",
134
+ "",
135
+ ...playbooks.map((pb) => `- \`${pb.file}\` — ${pb.meta.when ?? pb.meta.description}`),
136
+ ];
137
+ const snippet = `${lines.join("\n")}\n`;
138
+ writeGenerated(".stdd/AGENTS-snippet.md", snippet);
139
+ console.log("\nAdd this to your AGENTS.md (also saved to .stdd/AGENTS-snippet.md):\n");
140
+ console.log(snippet);
141
+ }
142
+
143
+ const manifest = { generatedBy: "stdd", version: VERSION, files: generated };
144
+ fs.writeFileSync(path.join(stddDir, "manifest.json"), `${JSON.stringify(manifest, null, "\t")}\n`);
145
+ }
146
+
147
+ /**
148
+ * Shared repository scan for `check` and `doctor`: committed working
149
+ * artifacts, canonical docs and their temporal-narrative hits, and stale
150
+ * generated files (a stale stamp means the CLI was upgraded without
151
+ * re-running `stdd init`).
152
+ */
153
+ function scanRepo(targetDir, config) {
154
+ const files = listTrackedFiles(targetDir);
155
+
156
+ const forbidden = config.forbiddenArtifacts.map(globToRegExp);
157
+ const artifacts = files.filter((file) => forbidden.some((re) => re.test(file)));
158
+
159
+ const canonical = config.canonicalDocs.map(globToRegExp);
160
+ const canonicalFiles = files.filter((file) => canonical.some((re) => re.test(file)));
161
+ const matchers = temporalMatchers(config.temporalPhrases);
162
+ const temporal = [];
163
+ for (const file of canonicalFiles) {
164
+ const lines = fs.readFileSync(path.join(targetDir, file), "utf8").split("\n");
165
+ for (const hit of scanTemporal(lines, matchers)) temporal.push({ file, ...hit });
166
+ }
167
+
168
+ return { artifacts, canonicalFiles, temporal, stale: scanGeneratedDrift(targetDir) };
169
+ }
170
+
171
+ /**
172
+ * Generated-file drift, as `{ file, message }` findings. With a manifest
173
+ * (written by `stdd init`), every generated file is verified by content
174
+ * hash. Without one (repo initialized by an older stdd), fall back to the
175
+ * version stamp inside Claude skills.
176
+ */
177
+ function scanGeneratedDrift(targetDir) {
178
+ const stale = [];
179
+ const manifestPath = path.join(targetDir, ".stdd", "manifest.json");
180
+ if (fs.existsSync(manifestPath)) {
181
+ let manifest;
182
+ try {
183
+ manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
184
+ } catch {
185
+ return [{ file: ".stdd/manifest.json", message: "is not valid JSON — re-run stdd init" }];
186
+ }
187
+ if (manifest.version !== VERSION) {
188
+ stale.push({
189
+ file: ".stdd/manifest.json",
190
+ message: `written by stdd v${manifest.version}, CLI is v${VERSION} — re-run stdd init`,
191
+ });
192
+ }
193
+ for (const [file, hash] of Object.entries(manifest.files ?? {})) {
194
+ const filePath = path.join(targetDir, ...file.split("/"));
195
+ if (!fs.existsSync(filePath)) {
196
+ stale.push({ file, message: "listed in .stdd/manifest.json but missing — re-run stdd init" });
197
+ } else if (sha256(fs.readFileSync(filePath, "utf8")) !== hash) {
198
+ stale.push({ file, message: "edited by hand or stale — re-run stdd init" });
199
+ }
200
+ }
201
+ return stale;
202
+ }
203
+
204
+ const stampRe = /generated by stdd v([0-9][^\s]*) —/;
205
+ const skillsDir = path.join(targetDir, ".claude", "skills");
206
+ if (fs.existsSync(skillsDir)) {
207
+ for (const name of fs.readdirSync(skillsDir)) {
208
+ const skillPath = path.join(skillsDir, name, "SKILL.md");
209
+ if (!fs.existsSync(skillPath)) continue;
210
+ const m = stampRe.exec(fs.readFileSync(skillPath, "utf8"));
211
+ if (m && m[1] !== VERSION) {
212
+ stale.push({
213
+ file: `.claude/skills/${name}/SKILL.md`,
214
+ message: `generated by stdd v${m[1]}, CLI is v${VERSION} — re-run stdd init`,
215
+ });
216
+ }
217
+ }
218
+ }
219
+ return stale;
220
+ }
221
+
222
+ function check(targetDir) {
223
+ const config = loadConfig(targetDir);
224
+ const { artifacts, temporal, stale } = scanRepo(targetDir, config);
225
+ const violations = [
226
+ ...artifacts.map((file) => `${file}: committed working artifact (forbiddenArtifacts)`),
227
+ ...temporal.map(
228
+ (hit) => `${hit.file}:${hit.line}: temporal narrative in canonical doc ("${hit.phrase}")`,
229
+ ),
230
+ ...stale.map((s) => `${s.file}: ${s.message}`),
231
+ ];
232
+
233
+ if (violations.length > 0) {
234
+ console.error(`stdd check: ${violations.length} violation(s)\n`);
235
+ for (const v of violations) console.error(` ${v}`);
236
+ process.exit(1);
237
+ }
238
+ console.log("stdd check: OK");
239
+ }
240
+
241
+ function doctor(targetDir) {
242
+ const count = (n, singular, plural) => `${n} ${n === 1 ? singular : plural}`;
243
+ let failed = false;
244
+ const report = (ok, message) => {
245
+ console.log(`${ok ? "✓" : "✗"} ${message}`);
246
+ if (!ok) failed = true;
247
+ };
248
+
249
+ const installed = fs.existsSync(path.join(targetDir, ".stdd", "method.md"));
250
+ report(installed, installed ? ".stdd/ installed" : ".stdd/ is not installed — run stdd init");
251
+
252
+ const config = loadConfig(targetDir);
253
+ const { artifacts, canonicalFiles, temporal, stale } = scanRepo(targetDir, config);
254
+
255
+ report(
256
+ canonicalFiles.length > 0,
257
+ canonicalFiles.length > 0
258
+ ? `canonical docs present (${count(canonicalFiles.length, "file", "files")})`
259
+ : "no canonical docs found — create docs/domain/ or docs/product/, or adjust canonicalDocs",
260
+ );
261
+
262
+ report(
263
+ artifacts.length === 0,
264
+ artifacts.length === 0
265
+ ? "no committed working artifacts"
266
+ : `${count(artifacts.length, "committed working artifact", "committed working artifacts")} may mislead coding agents`,
267
+ );
268
+
269
+ const temporalFiles = new Set(temporal.map((hit) => hit.file)).size;
270
+ report(
271
+ temporalFiles === 0,
272
+ temporalFiles === 0
273
+ ? "canonical docs describe the present (no temporal narrative)"
274
+ : `${count(temporalFiles, "canonical doc contains", "canonical docs contain")} temporal narrative`,
275
+ );
276
+
277
+ report(
278
+ stale.length === 0,
279
+ stale.length === 0
280
+ ? `generated files match stdd v${VERSION}`
281
+ : `${count(stale.length, "generated file is", "generated files are")} stale — re-run stdd init`,
282
+ );
283
+
284
+ const agentsPath = path.join(targetDir, "AGENTS.md");
285
+ if (fs.existsSync(agentsPath)) {
286
+ const hasSection = /^## STDD$/m.test(fs.readFileSync(agentsPath, "utf8"));
287
+ report(
288
+ hasSection,
289
+ hasSection
290
+ ? "AGENTS.md carries the STDD section"
291
+ : "AGENTS.md has no STDD section — paste .stdd/AGENTS-snippet.md",
292
+ );
293
+ }
294
+
295
+ if (failed) process.exit(1);
296
+ }
297
+
298
+ function checkPr(prBodyFile) {
299
+ const body =
300
+ prBodyFile === "-" || !prBodyFile ? fs.readFileSync(0, "utf8") : fs.readFileSync(prBodyFile, "utf8");
301
+ const matches = findEvidenceLines(body);
302
+ if (matches.length === 0) {
303
+ fail(
304
+ "PR body has no docs evidence line. Add exactly one of:\n" +
305
+ " Docs updated first: <docs>\n" +
306
+ " Docs checked, no change needed: <docs + reason>\n" +
307
+ " Docs not applicable: <why implementation-only>",
308
+ );
309
+ }
310
+ if (matches.length > 1) {
311
+ fail("PR body has more than one docs evidence line — keep exactly one.");
312
+ }
313
+ if (matches[0].content === "") {
314
+ fail(`"${matches[0].label}:" names no evidence — list the docs or the reason after the colon.`);
315
+ }
316
+ console.log("stdd check-pr: OK");
317
+ }
318
+
319
+ // --- argument parsing (strict: unknown flags are errors) ---
320
+ const [, , command, ...rest] = process.argv;
321
+ let tools = null;
322
+ const positional = [];
323
+ for (let i = 0; i < rest.length; i++) {
324
+ const arg = rest[i];
325
+ if (arg === "--tools" || arg.startsWith("--tools=")) {
326
+ if (command !== "init") fail(`--tools is only valid for "stdd init"`);
327
+ const value = arg.includes("=") ? arg.slice("--tools=".length) : (rest[++i] ?? "");
328
+ tools = value.split(",").filter(Boolean);
329
+ if (tools.length === 0) fail("--tools requires a value, e.g. --tools claude,codex");
330
+ const unknown = tools.filter((t) => !KNOWN_TOOLS.includes(t));
331
+ if (unknown.length > 0) {
332
+ fail(`unknown tool(s): ${unknown.join(", ")} (known: ${KNOWN_TOOLS.join(", ")})`);
333
+ }
334
+ } else if (arg.startsWith("--")) {
335
+ fail(`unknown flag: ${arg}`);
336
+ } else {
337
+ positional.push(arg);
338
+ }
339
+ }
340
+ if (positional.length > 1) fail(`unexpected argument: ${positional[1]}`);
341
+ const targetDir = path.resolve(positional[0] ?? ".");
342
+
343
+ switch (command) {
344
+ case "init":
345
+ init(targetDir, tools ?? KNOWN_TOOLS);
346
+ break;
347
+ case "check":
348
+ check(targetDir);
349
+ break;
350
+ case "doctor":
351
+ doctor(targetDir);
352
+ break;
353
+ case "check-pr":
354
+ checkPr(positional[0]);
355
+ break;
356
+ case "--version":
357
+ case "version":
358
+ console.log(VERSION);
359
+ break;
360
+ default:
361
+ console.log("Usage: stdd <init|check|check-pr|doctor> [dir|pr-body-file] [--tools claude,codex]");
362
+ process.exit(command ? 1 : 0);
363
+ }
@@ -0,0 +1,141 @@
1
+ # The STDD Method
2
+
3
+ This is the working contract. It is written for the agent or developer doing
4
+ the change, in the order the work happens.
5
+
6
+ ## Sources of truth
7
+
8
+ Every repository adopting STDD names a **permanent docs tree** (for example
9
+ `docs/`) with an explicit hierarchy — typically product intent above domain
10
+ rules above implementation layers. When layers disagree, stop and reconcile
11
+ before implementing.
12
+
13
+ Three artifacts make claims about behavior, each in its own way:
14
+
15
+ - **Docs are the intended contract** — what the system is supposed to do.
16
+ - **Tests are the executable contract** — what the system provably does.
17
+ - **Code is the observed implementation** — what the system actually does.
18
+
19
+ A disagreement between them blocks implementation until they are reconciled.
20
+ None silently overrides the others: stale docs get corrected, wrong tests get
21
+ fixed, accidental behavior gets documented or removed — each resolution is an
22
+ explicit decision, not a default in favor of any one artifact.
23
+
24
+ ## The loop
25
+
26
+ ```
27
+ classify → read docs → docs edit (the spec) → failing test → implement → verify → PR evidence
28
+ ```
29
+
30
+ 1. **Classify the change.**
31
+ - *Behavior:* anything a user, operator, or downstream system can observe —
32
+ workflows, pricing, states, permissions, API contracts, copy with
33
+ business meaning.
34
+ - *Implementation-only:* refactors, lint fixes, build plumbing, mechanical
35
+ dependency updates that alter no behavior or architecture contract.
36
+ 2. **Read the relevant docs first.** For behavior changes, read the matching
37
+ source-of-truth documents before proposing anything.
38
+ 3. **Edit the docs — that edit is the spec.** If the docs are missing, stale,
39
+ or ambiguous, update them before tests and code. Make the docs edit the
40
+ first reviewable unit — the first commit where commits are used, otherwise
41
+ the opening docs-only diff of the PR — so the behavior contract can be
42
+ reviewed on its own. If the docs already cover the behavior, do not add
43
+ duplicate prose — record that they were checked (see PR evidence).
44
+ 4. **Write the failing test.** Red before green. Exception below.
45
+ 5. **Implement** until the test passes, then refactor.
46
+ 6. **Verify with the narrowest meaningful command.** Never claim "done",
47
+ "fixed", or "clean" without fresh verification evidence.
48
+ 7. **State PR evidence.** Every PR carries exactly one of:
49
+ - `Docs updated first:` — list the changed docs;
50
+ - `Docs checked, no change needed:` — list the docs and the reason;
51
+ - `Docs not applicable:` — why the change is implementation-only.
52
+
53
+ The line must name its evidence — docs paths or a reason. A bare label
54
+ with nothing after the colon fails `stdd check-pr`, and only a line
55
+ starting at the beginning of a line counts (quoted templates and code
56
+ blocks do not).
57
+
58
+ ## The frontend exception: design-first
59
+
60
+ Frontend **visual** work — layout, styling, markup structure, presentation
61
+ copy, component composition — is design-first, not test-first. A
62
+ failing-test-first loop forces the visual outcome to be specified before it
63
+ is explored; brittle rendering assertions then punish every design iteration.
64
+
65
+ The exception covers presentation, not meaning. Copy with business meaning —
66
+ prices, statuses, permissions, legal text, anything a user relies on as a
67
+ fact — is **behavior**: it goes through the docs edit and the normal loop.
68
+ Only its visual arrangement is design-first.
69
+
70
+ - Build the visual part freely; verify it visually (screenshots reviewed by a
71
+ human).
72
+ - Never write tests asserting static copy, class names, or pure rendering
73
+ output.
74
+ - After the visual part settles, add tests only for real behavior contracts:
75
+ hooks, formatters, state transitions, eligibility and conditional logic,
76
+ accessibility roles.
77
+ - Client-side **logic** follows the normal loop.
78
+
79
+ ## Working artifacts are never committed
80
+
81
+ Plans, spec files, todo lists, handoff notes, and execution logs are working
82
+ artifacts. They help one session and go stale immediately after. Committed,
83
+ they outrank fresher docs in code search and become a second source of truth.
84
+
85
+ Where their content belongs instead:
86
+
87
+ | Content | Home |
88
+ | --- | --- |
89
+ | Durable rules (behavior, architecture, conventions) | The permanent docs tree, same PR |
90
+ | Design rationale, scope decisions, rejected alternatives | The PR description |
91
+ | Designs for deferred (not yet implemented) work | Dated entries in the project log (e.g. `docs/project/`) |
92
+ | Task lists, sequencing | Ephemeral: session scratchpad, PR body |
93
+
94
+ The project log is **not canonical**: its entries are dated records of
95
+ decisions and future intentions, never a description of the present. Cite
96
+ canonical docs for how the system behaves; cite the project log only for why
97
+ something is deferred or was decided.
98
+
99
+ Because a plain `grep` cannot tell authority levels apart, the boundary is
100
+ made machine-readable on both sides. Every project-log entry starts with
101
+ frontmatter declaring itself non-canonical:
102
+
103
+ ```yaml
104
+ ---
105
+ authority: non-canonical
106
+ status: deferred
107
+ ---
108
+ ```
109
+
110
+ And the agent instructions `stdd init` generates carry a retrieval rule: do
111
+ not search the project log unless the user explicitly asks for historical
112
+ rationale or deferred work.
113
+
114
+ `stdd check` enforces the artifact ban in CI; `stdd check-pr` enforces the
115
+ PR evidence line; `stdd doctor` reports a repository's overall adoption
116
+ health (setup, canonical docs, misleading artifacts, generated-file drift). The rest of the method is review discipline — anything
117
+ that later proves mechanically checkable should move into `stdd check`.
118
+
119
+ ## Bug fixes and refactors
120
+
121
+ - **Bug fix:** reproduce the symptom in a test before editing. Fix the root
122
+ cause, not the symptom.
123
+ - **Refactor:** prove behavior preservation with existing tests, typecheck,
124
+ or focused characterization tests. No docs edit needed when behavior and
125
+ contracts are unchanged.
126
+
127
+ ## Style for docs
128
+
129
+ Concise. Short, direct sentences. Do not omit words that carry meaning. One
130
+ rule lives in one document — link, don't duplicate. Canonical docs are
131
+ written in English and describe the **present**: temporal narrative
132
+ ("previously", "no longer") belongs in git history and PR descriptions —
133
+ `stdd check` flags it.
134
+
135
+ ## What stdd does not cover
136
+
137
+ stdd is a process contract, not an engineering standard. Architecture rules,
138
+ dependency-injection styles, error-handling policy, tenant/auth/data safety,
139
+ and database-migration policy stay in the adopting team's own contract
140
+ (typically `AGENTS.md`) and docs tree. stdd tells you *where* such rules
141
+ live and *when* they must be written — not what they should say.
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@stdd/cli",
3
+ "version": "0.0.1",
4
+ "description": "Spec + Test Driven Development — a markdown-first methodology kit for teams building software with AI coding agents",
5
+ "type": "module",
6
+ "bin": {
7
+ "stdd": "cli/stdd.mjs"
8
+ },
9
+ "files": [
10
+ "cli/",
11
+ "method/",
12
+ "playbooks/",
13
+ "templates/",
14
+ "adapters/"
15
+ ],
16
+ "scripts": {
17
+ "test": "node --test",
18
+ "check": "biome ci .",
19
+ "format": "biome check --fix .",
20
+ "selfcheck": "node cli/stdd.mjs check ."
21
+ },
22
+ "engines": {
23
+ "node": ">=20"
24
+ },
25
+ "keywords": [
26
+ "sdd",
27
+ "tdd",
28
+ "stdd",
29
+ "spec-driven-development",
30
+ "test-driven-development",
31
+ "ai-agents",
32
+ "claude-code",
33
+ "codex",
34
+ "methodology",
35
+ "workflow"
36
+ ],
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/vsem-azamat/stdd.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/vsem-azamat/stdd/issues"
43
+ },
44
+ "homepage": "https://github.com/vsem-azamat/stdd#readme",
45
+ "author": "Azamat Almazbek uulu",
46
+ "license": "MIT",
47
+ "devDependencies": {
48
+ "@biomejs/biome": "^2.3.8"
49
+ }
50
+ }
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: stdd-brainstorming
3
+ description: Shape a fuzzy idea into an agreed behavior contract before any plan or code
4
+ when: A non-trivial change is requested and the requirements, scope, or approach are not yet pinned down.
5
+ ---
6
+
7
+ # Brainstorming
8
+
9
+ The goal is agreement on **what** and **why** before anyone invests in **how**.
10
+ The output is not a document — it is a shared understanding that becomes a
11
+ docs edit and a PR description.
12
+
13
+ ## Process
14
+
15
+ 1. **Understand the current state first.** Read the relevant docs and the code
16
+ the change will touch. Questions asked from ignorance waste the other
17
+ side's time; questions asked from knowledge sharpen the idea.
18
+ 2. **Ask one question at a time.** Prefer questions that eliminate whole
19
+ branches of the design space: who is it for, what triggers it, what must
20
+ never happen, what is explicitly out of scope.
21
+ 3. **Challenge scope creep in both directions.** If the idea is bigger than
22
+ the need, say so and propose the smaller version. If the stated need hides
23
+ a larger real problem, surface it.
24
+ 4. **Propose 2–3 approaches with a recommendation.** For each: one paragraph,
25
+ the trade-off that actually matters, and what it costs later. Recommend
26
+ one; do not present a menu without an opinion.
27
+ 5. **Converge on the behavior contract.** State the agreed behavior as rules
28
+ precise enough to test. Confirm them explicitly.
29
+
30
+ ## Output
31
+
32
+ - The agreed rules become the **docs edit** (the spec) — the first commit of
33
+ the branch.
34
+ - The rationale, rejected alternatives, and scope decisions go into the
35
+ **PR description** when the branch opens.
36
+ - Nothing from this conversation is committed as a standalone file.
37
+
38
+ ## Anti-patterns
39
+
40
+ - Jumping to implementation detail while behavior is still unsettled.
41
+ - Asking multiple stacked questions at once.
42
+ - Writing a "spec document" instead of editing the real docs.
43
+ - Agreeing silently: if you disagree with the direction, say so with reasons.
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: stdd-debugging
3
+ description: Find and fix the root cause of a defect, not its symptom
4
+ when: A bug, crash, failing test, or unexplained behavior is reported.
5
+ ---
6
+
7
+ # Debugging
8
+
9
+ The discipline: no edit before a reproduction, no fix before a diagnosis.
10
+
11
+ ## Process
12
+
13
+ 1. **Reproduce first.** Turn the report into a deterministic reproduction —
14
+ ideally a failing test. If you cannot reproduce it, you are not debugging
15
+ yet; you are gathering facts.
16
+ 2. **Read the actual error.** The full message, the stack, the logs around
17
+ it. Do not pattern-match a familiar-looking symptom to a known failure —
18
+ verify the evidence supports *this* cause.
19
+ 3. **Form one hypothesis and test it cheaply.** Predict what you will observe
20
+ if the hypothesis is true, then look. One hypothesis at a time; a change
21
+ made under two hypotheses proves neither.
22
+ 4. **Fix the root cause minimally.** The smallest change that removes the
23
+ cause. Resist drive-by cleanup — it obscures the fix in review.
24
+ 5. **Keep the reproduction as a regression test.** Red before the fix, green
25
+ after, committed with it.
26
+ 6. **Verify the fix in the original context**, not only in the reduced
27
+ reproduction.
28
+
29
+ ## Stop rules
30
+
31
+ - Two failed fix attempts mean the diagnosis is wrong. Stop editing, go back
32
+ to step 2, and widen what you consider suspect — including your own
33
+ earlier changes and the test itself.
34
+ - If the evidence contradicts the reported story, surface the contradiction
35
+ instead of forcing a fix that matches the story.
36
+ - A fix you cannot explain is not a fix. Do not ship it.
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: stdd-planning
3
+ description: Turn an agreed behavior contract into an executable, verifiable sequence of work
4
+ when: The behavior contract is agreed (docs edit drafted or committed) and the change is large enough to need ordered steps.
5
+ ---
6
+
7
+ # Planning
8
+
9
+ A plan is a disposable working artifact: it guides one execution and is thrown
10
+ away. It is never committed as a file — its home is the PR description (for
11
+ the durable summary) and the session's scratchpad (for the working copy).
12
+
13
+ ## Structure
14
+
15
+ A good plan has, in order:
16
+
17
+ 1. **Intent** — one paragraph: the problem and the agreed direction.
18
+ 2. **Docs delta** — which permanent docs change and how (added / modified /
19
+ removed rules, named per target file). This is the spec surface of the
20
+ plan; keep it exact so the docs edit is mechanical.
21
+ 3. **Steps** — each step small enough to verify independently. Per step:
22
+ - what changes (files, functions);
23
+ - the failing test that gates it (or the visual check, for frontend
24
+ visual work — see the design-first exception in the method);
25
+ - the verification command.
26
+ 4. **Out of scope** — what this change deliberately does not do.
27
+ 5. **Risks** — what could invalidate the plan and how you would notice.
28
+
29
+ ## Rules
30
+
31
+ - Order steps so the system stays green between them.
32
+ - Write verification per step, not one "run all tests" at the end.
33
+ - A step that cannot fail its check is not a step — merge it into another.
34
+ - When execution contradicts the plan, update the plan, do not force the
35
+ plan onto reality. If the *intent* changed, stop and re-enter
36
+ brainstorming.
37
+ - Keep the durable parts flowing to their homes as you go: rules → docs
38
+ edit, rationale → PR description. The plan itself must stay deletable at
39
+ any moment without information loss.
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: stdd-worktrees
3
+ description: Work in an isolated workspace without fighting the platform's native isolation
4
+ when: Starting implementation work that should not disturb the user's current checkout.
5
+ ---
6
+
7
+ # Isolated Workspaces
8
+
9
+ ## Order of preference
10
+
11
+ 1. **Detect existing isolation.** If you are already in a linked worktree or
12
+ a platform-managed sandbox, use it. Never nest worktrees. (Check:
13
+ `git rev-parse --git-dir` differs from `--git-common-dir`, and you are not
14
+ in a submodule.)
15
+ 2. **Use the platform's native worktree tool** if one exists. Manual
16
+ `git worktree add` alongside a native tool creates state the platform
17
+ cannot see or clean up.
18
+ 3. **Fall back to `git worktree add`** only when neither applies:
19
+ - Put worktrees in a dedicated ignored directory (`.worktrees/` at the
20
+ repo root by default).
21
+ - Verify the directory is git-ignored **before** creating the worktree;
22
+ add it to `.gitignore` first if not.
23
+ - Branch from the repository's integration branch unless told otherwise.
24
+
25
+ ## After creating
26
+
27
+ - Run the project's dependency setup (install, build) so the workspace is
28
+ self-sufficient.
29
+ - Run the narrowest baseline verification before changing anything. If the
30
+ baseline is already red, report it and ask before proceeding — otherwise
31
+ you cannot tell your breakage from pre-existing breakage.
32
+
33
+ ## Shared state warnings
34
+
35
+ - The git stash stack is shared across all worktrees of one repository.
36
+ Prefer a temporary WIP commit over stashing; if you must stash, tag the
37
+ entry and apply (not pop) by its SHA.
38
+ - Never `cd` out of your assigned worktree into the main checkout to "fix
39
+ something quickly" — open work there belongs to someone else.
@@ -0,0 +1,44 @@
1
+ # Deferred Design Template
2
+
3
+ A design for work that is agreed but not yet scheduled. Lives as a **dated
4
+ entry** in the project log (e.g. `docs/project/`), never as a spec file next
5
+ to canonical docs. Delete the entry when the work ships or is abandoned.
6
+
7
+ The frontmatter is mandatory: it is the machine-readable marker that keeps
8
+ agent retrieval authority-aware — canonical docs never carry
9
+ `authority: non-canonical`, project-log entries always do.
10
+
11
+ ```markdown
12
+ ---
13
+ authority: non-canonical
14
+ status: deferred
15
+ ---
16
+
17
+ # <Title>
18
+
19
+ Last updated: <YYYY-MM-DD>
20
+
21
+ - Status: Deferred | Decision needed | Ready | Blocked
22
+ - Priority: High | Medium | Low
23
+
24
+ ## Problem
25
+
26
+ <What hurts and who it hurts. Impact if never done.>
27
+
28
+ ## Agreed direction
29
+
30
+ <The design, as rules precise enough to implement from. Note explicitly that
31
+ this describes FUTURE behavior — canonical docs still describe the present.>
32
+
33
+ ## Why deferred
34
+
35
+ <The reason it is not being done now.>
36
+
37
+ ## Resume condition
38
+
39
+ <The concrete trigger that should restart this work.>
40
+
41
+ ## Acceptance criteria
42
+
43
+ <How we will know it is done.>
44
+ ```
@@ -0,0 +1,35 @@
1
+ # PR Description Template
2
+
3
+ The PR description is the durable home for everything that is not a rule:
4
+ rationale, scope decisions, rejected alternatives. Copy, fill, delete unused
5
+ sections.
6
+
7
+ ```markdown
8
+ ## Why
9
+
10
+ <The problem, one paragraph. Link the discussion if there was one.>
11
+
12
+ ## What changed
13
+
14
+ <Behavior first, implementation second. Bullets.>
15
+
16
+ ## Decisions and alternatives
17
+
18
+ <Choices made and what was rejected, with the reason. This is the section
19
+ future readers will thank you for — it never goes in the docs tree.>
20
+
21
+ ## Out of scope
22
+
23
+ <What this PR deliberately does not do.>
24
+
25
+ ## Docs evidence
26
+
27
+ <Exactly one of:>
28
+ Docs updated first: <list of changed docs>
29
+ Docs checked, no change needed: <docs + reason>
30
+ Docs not applicable: <why implementation-only>
31
+
32
+ ## Verification
33
+
34
+ <Commands run and their results. Screenshots for visual work.>
35
+ ```