@pilotspace/add 1.9.0 → 1.11.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/CHANGELOG.md +104 -0
- package/bin/cli.js +192 -2
- package/docs/09-the-loop.md +1 -1
- package/docs/11-governance.md +1 -1
- package/docs/14-foundation.md +1 -1
- package/docs/16-releasing.md +4 -4
- package/docs/17-components.md +125 -0
- package/docs/appendix-c-glossary.md +9 -3
- package/package.json +5 -2
- package/skill/add/SKILL.md +11 -1
- package/skill/add/components.md +54 -0
- package/skill/add/phases/0-ground.md +9 -3
- package/skill/add/phases/0-setup.md +2 -2
- package/skill/add/scope.md +1 -1
- package/tooling/add.py +717 -1352
- package/tooling/add_engine/__init__.py +5 -0
- package/tooling/add_engine/accessors.py +61 -0
- package/tooling/add_engine/autonomy.py +58 -0
- package/tooling/add_engine/components.py +117 -0
- package/tooling/add_engine/constants.py +221 -0
- package/tooling/add_engine/guidelines.py +252 -0
- package/tooling/add_engine/identity.py +107 -0
- package/tooling/add_engine/io_state.py +201 -0
- package/tooling/add_engine/milestones.py +108 -0
- package/tooling/add_engine/predicates.py +75 -0
- package/tooling/add_engine/release.py +86 -0
- package/tooling/add_engine/render.py +90 -0
- package/tooling/add_engine/taskdoc.py +217 -0
- package/tooling/add_engine/version.py +45 -0
- package/tooling/templates/TASK.fast.md.tmpl +2 -0
- package/tooling/templates/TASK.md.tmpl +1 -1
- package/tooling/templates/gitignore.tmpl +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,110 @@ All notable changes to the ADD method (`@pilotspace/add` on npm,
|
|
|
4
4
|
`pilotspace-add` on PyPI) are documented here. The format follows
|
|
5
5
|
[Keep a Changelog](https://keepachangelog.com/); versions follow semver.
|
|
6
6
|
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [1.11.0] — 2026-06-26
|
|
10
|
+
|
|
11
|
+
Engine-modularization + audit-hardening release — the 7k-line monolith becomes a
|
|
12
|
+
navigable package, the verify/atomicity/coverage gates get five fixes, and the
|
|
13
|
+
standalone (milestone-free) lane becomes first-class. **No behavior change** to the
|
|
14
|
+
CLI, the flow, or any output: this is an internal architecture + hardening release.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- **The engine is now a focused package.** `add-method/tooling/add.py` (7049 → 5640
|
|
18
|
+
lines, −20%) split into **13 `add_engine/*.py` modules** — `constants`, `io_state`,
|
|
19
|
+
`accessors`, `predicates`, `identity`, `guidelines`, `render`, `milestones`,
|
|
20
|
+
`components`, `version`, `release`, `taskdoc`, `autonomy` — behind a stable
|
|
21
|
+
re-export surface (`import add; add.<name>` still resolves, public AND `_`-prefixed,
|
|
22
|
+
so every existing test is untouched). `add.py` stays the runnable **orchestrator
|
|
23
|
+
entry** (the `load_state`/`save_state`/`report_data`/`cmd_*`/`main` spine). Each of
|
|
24
|
+
the 16 extractions was its own CI-green PR, proven verbatim by an AST source-segment
|
|
25
|
+
diff and gated by the full suite (1815 → 1959 green throughout, no test weakened).
|
|
26
|
+
- **Two-pin integrity model.** Alongside `ENGINE_MD5` (md5 of `add.py`),
|
|
27
|
+
`ENGINE_PKG_MD5` is a manifest digest over every `add_engine/*.py`; both are literal
|
|
28
|
+
pins (never self-hashed) and are checked byte-identical across the 3-tree mirror
|
|
29
|
+
(canonical · `.add` · `_bundled`). `prepare_bundle` + both installers + the `.add`
|
|
30
|
+
mirror now ship the whole `add_engine/` package.
|
|
31
|
+
|
|
32
|
+
### Fixed (audit-hardening)
|
|
33
|
+
- Five gate/atomicity/coverage fixes: a phase-build guard, hardened save-state
|
|
34
|
+
atomicity, force-preserve self-healing, setup-tests-before-build ordering, and a
|
|
35
|
+
consumer-stale gate. A security finding remains an un-forceable HARD-STOP.
|
|
36
|
+
|
|
37
|
+
### Added (the standalone / lightweight lane)
|
|
38
|
+
- **`add.py todo`** — capture a milestone-free backlog item without scaffolding a task.
|
|
39
|
+
- **`loose tasks:` release attribution** — a release now attributes *any* done,
|
|
40
|
+
milestone-free task on its `RELEASES.md` row (not only milestone membership), with a
|
|
41
|
+
separate status cue for releasable loose work.
|
|
42
|
+
- **`fast` (task) + `auto` (mode) flag modes** — the standalone lane is first-class:
|
|
43
|
+
a minimal `TASK.fast.md`, freeze-gated under any milestone, with a flag-mode quick
|
|
44
|
+
reference.
|
|
45
|
+
|
|
46
|
+
This release bundles **2 closed milestones** (`audit-hardening`,
|
|
47
|
+
`engine-modularization`) and **27 loose tasks** since 1.10.0.
|
|
48
|
+
|
|
49
|
+
## [1.10.0] — 2026-06-25
|
|
50
|
+
|
|
51
|
+
Component-aware major — ADD now models every codebase as a **graph of components**,
|
|
52
|
+
each owning a source root, its own green bar, and the contracts it produces or
|
|
53
|
+
consumes, so **one milestone can ship a vertical slice across components** in a
|
|
54
|
+
monorepo or across repos. Bundles the closed `component-aware-add` major plus the
|
|
55
|
+
`docs-site` (the book as a live MkDocs site) and `loop-steering` milestones, and the
|
|
56
|
+
ccsk `--rule-file` mode. Every new gate is **opt-in or grandfathered** — a project
|
|
57
|
+
that declares no components is byte-identical to 1.9.0.
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
- **Component registry (`component-aware-add`)** — declare parts of a multi-part
|
|
61
|
+
codebase in `.add/components.toml` under `[component.<name>]` (a `root` + a
|
|
62
|
+
`green-bar`). A task binds to one with a `component:` header line, which anchors its
|
|
63
|
+
§5 Scope to that component's root. Declared, never inferred; zero components ⇒
|
|
64
|
+
byte-identical to today.
|
|
65
|
+
- **Per-component verify** — a bound task is held to ITS component's green-bar at the
|
|
66
|
+
verify gate (the cite-gate refuses `component_green_bar_uncited`), so two tasks in
|
|
67
|
+
one milestone can pass on two different toolchains (e.g. `pytest` and `vitest`). The
|
|
68
|
+
engine still never runs a suite — the AI runs it, the gate checks the right bar was
|
|
69
|
+
cited.
|
|
70
|
+
- **Cross-component contracts (`produces:` / `consumes:`)** — declare `[contract.<id>]`
|
|
71
|
+
(producer + consumers). A producer's §3 freeze writes an immutable snapshot at
|
|
72
|
+
`.add/contracts/<id>.json`; a consumer pins its hash; a changed re-freeze flags every
|
|
73
|
+
consumer stale. A missing/malformed snapshot HARD-STOPS — never build against a
|
|
74
|
+
guessed shape.
|
|
75
|
+
- **One milestone, full-stack slice** — a `consumes:` task is HELD from entering §3
|
|
76
|
+
(`producer_contract_unfrozen`) until its producer's contract is frozen, so a BE→FE
|
|
77
|
+
vertical slice ships in one milestone, ordered by the frozen contract.
|
|
78
|
+
- **Multi-repo federation (`add.py federate pull <id>`)** — a consumer repo declares
|
|
79
|
+
`[federation.<id>]` (a `source` path + optional `pin`) and pulls a byte-for-byte copy
|
|
80
|
+
of a producer repo's published snapshot into its local `.add/contracts/`, where the
|
|
81
|
+
rest of ADD treats mono- and multi-repo identically. Fail-loud: unknown id /
|
|
82
|
+
unreadable source / invalid snapshot / version mismatch each HARD-STOPS and lands
|
|
83
|
+
nothing.
|
|
84
|
+
- **Component pillar docs** — a new book chapter `17 · Components`, a skill guide
|
|
85
|
+
(`components.md`), and glossary terms (Component · Cross-component contract ·
|
|
86
|
+
Federation) teach the whole loop.
|
|
87
|
+
- **The book as a live site (`docs-site`)** — the AIDD book ships to GitHub Pages as a
|
|
88
|
+
MkDocs-Material site (`mkdocs.yml` + `requirements-docs.txt`, build-time only — the
|
|
89
|
+
published packages stay zero-dependency); `mkdocs build --strict` fails the deploy on
|
|
90
|
+
a broken intra-book link.
|
|
91
|
+
- **Rule-file mode for ccsk projects (`--rule-file`)** — when a project keeps its
|
|
92
|
+
workflow rules under `.claude/rules/` (the ccsk convention, detected by a `.ccsk/`
|
|
93
|
+
dir), ADD relocates CLAUDE.md's block to `.claude/rules/add-workflows.md` and leaves a
|
|
94
|
+
single reference bullet, instead of inlining. Triggers three ways (the explicit
|
|
95
|
+
`--rule-file` flag, a `.ccsk/` directory, or a rule file already present),
|
|
96
|
+
**CLAUDE-only**, migrates a prior inline block out (`.bak` on change), idempotent and
|
|
97
|
+
fail-soft. Mirrored across all three installers (engine `add.py`, pip `_installer.py`,
|
|
98
|
+
npm `cli.js`).
|
|
99
|
+
|
|
100
|
+
### Changed
|
|
101
|
+
- **Guided dynamic loop (`loop-steering`)** — `status` and `guide` now STEER into the
|
|
102
|
+
build→verify loop at the loop juncture rather than only reporting it, so an agent is
|
|
103
|
+
pointed at the next loop step instead of having to infer it.
|
|
104
|
+
|
|
105
|
+
### Compatibility
|
|
106
|
+
- Python 3.11+ is required to USE the component pillar (it parses `components.toml` with
|
|
107
|
+
the stdlib `tomllib`); on 3.10 the engine runs unchanged but a declared
|
|
108
|
+
`components.toml` fails loud (`components_malformed`). The published packages remain
|
|
109
|
+
zero-dependency.
|
|
110
|
+
|
|
7
111
|
## [1.9.0] — 2026-06-24
|
|
8
112
|
|
|
9
113
|
Lean-pass major — make ADD's own surface (skill · flow · engine) the most-effective
|
package/bin/cli.js
CHANGED
|
@@ -39,7 +39,7 @@ function parseArgs(argv) {
|
|
|
39
39
|
// defaults the stage and infers the name from the folder, so the manual-init
|
|
40
40
|
// hint only echoes flags the user actually chose (shortest true command).
|
|
41
41
|
const args = { _: [], force: false, check: false, noSkill: false, stage: null, name: null,
|
|
42
|
-
yes: false, nonInteractive: false, global: false, globalData: false };
|
|
42
|
+
yes: false, nonInteractive: false, global: false, globalData: false, ruleFile: false };
|
|
43
43
|
for (let i = 0; i < argv.length; i++) {
|
|
44
44
|
const a = argv[i];
|
|
45
45
|
if (a === "--force") args.force = true;
|
|
@@ -58,6 +58,9 @@ function parseArgs(argv) {
|
|
|
58
58
|
// already provides the `add` skill, so a plugin bootstrap uses this to materialize
|
|
59
59
|
// .add/tooling/ + .add/docs/ into the project without a duplicate .claude/skills/add.
|
|
60
60
|
else if (a === "--no-skill") args.noSkill = true;
|
|
61
|
+
// --rule-file: write the ADD block to .claude/rules/add-workflows.md + reference it from
|
|
62
|
+
// CLAUDE.md instead of inlining (auto-on for ccsk projects with a .ccsk/ dir).
|
|
63
|
+
else if (a === "--rule-file") args.ruleFile = true;
|
|
61
64
|
else if (a === "--stage" || a === "--name") {
|
|
62
65
|
const v = argv[++i];
|
|
63
66
|
// fail loudly on a trailing/abutting flag — never silently drop a value
|
|
@@ -249,6 +252,126 @@ function writeAgentPointer(target, profile) {
|
|
|
249
252
|
}
|
|
250
253
|
}
|
|
251
254
|
|
|
255
|
+
// --- rule-file mode: ccsk-style relocation of the CLAUDE.md block ---------------------
|
|
256
|
+
// Mirror of add.py / _installer.py rule-file logic (twins by duplication). For ccsk projects
|
|
257
|
+
// (.ccsk/ dir) or an explicit --rule-file, the ADD pointer goes to .claude/rules/add-workflows.md
|
|
258
|
+
// and CLAUDE.md keeps only a reference bullet. CLAUDE-only — AGENTS.md/.clinerules stay inline.
|
|
259
|
+
const RULES_FILE_REL = path.join(".claude", "rules", "add-workflows.md");
|
|
260
|
+
const WORKFLOW_HEADINGS = ["Rules & Workflows", "Workflows", "Rules"];
|
|
261
|
+
const RULE_REF_LINE = "- ADD (AI-Driven Development) Workflows rules: ./.claude/rules/add-workflows.md";
|
|
262
|
+
|
|
263
|
+
// True when the ADD block belongs in .claude/rules/add-workflows.md instead of inline.
|
|
264
|
+
// Re-derived from disk: explicit flag, a ccsk project (.ccsk/), or a rule file already present.
|
|
265
|
+
function ruleFileMode(root, flag) {
|
|
266
|
+
if (flag) return true;
|
|
267
|
+
try {
|
|
268
|
+
if (fs.existsSync(path.join(root, ".ccsk")) &&
|
|
269
|
+
fs.statSync(path.join(root, ".ccsk")).isDirectory()) return true;
|
|
270
|
+
if (fs.existsSync(path.join(root, RULES_FILE_REL))) return true;
|
|
271
|
+
} catch (_e) { /* fail-soft */ }
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Remove an inline ADD:BEGIN..END region (migration), collapsing the gap. An unterminated
|
|
276
|
+
// BEGIN is left as-is rather than eating the rest of the file.
|
|
277
|
+
function stripInlineBlock(text) {
|
|
278
|
+
const begin = text.indexOf(GUIDE_BEGIN);
|
|
279
|
+
if (begin === -1) return text;
|
|
280
|
+
let end = text.indexOf(GUIDE_END, begin);
|
|
281
|
+
if (end === -1) return text;
|
|
282
|
+
end += GUIDE_END.length;
|
|
283
|
+
const head = text.slice(0, begin).replace(/\n+$/, "");
|
|
284
|
+
const tail = text.slice(end).replace(/^\n+/, "");
|
|
285
|
+
if (head && tail) return head + "\n\n" + tail;
|
|
286
|
+
return head || tail;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Insert the ADD rule-file bullet under an existing Workflows/Rules heading, or append a fresh
|
|
290
|
+
// '## Workflows' section. Caller guarantees the bullet is absent.
|
|
291
|
+
function insertRuleReference(text) {
|
|
292
|
+
const lines = text.split("\n");
|
|
293
|
+
let headingIdx = -1;
|
|
294
|
+
for (let i = 0; i < lines.length; i++) {
|
|
295
|
+
const m = lines[i].match(/^(#{1,6})\s+(.*?)\s*$/);
|
|
296
|
+
if (m && WORKFLOW_HEADINGS.some((h) => m[2].trim().toLowerCase() === h.toLowerCase())) {
|
|
297
|
+
headingIdx = i;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (headingIdx === -1) {
|
|
302
|
+
const body = text.replace(/\n+$/, "");
|
|
303
|
+
const sep = body ? "\n\n" : "";
|
|
304
|
+
return body + sep + "## Workflows\n\n" + RULE_REF_LINE + "\n";
|
|
305
|
+
}
|
|
306
|
+
const level = lines[headingIdx].match(/^(#{1,6})/)[1].length;
|
|
307
|
+
let end = lines.length;
|
|
308
|
+
for (let j = headingIdx + 1; j < lines.length; j++) {
|
|
309
|
+
const m = lines[j].match(/^(#{1,6})\s+/);
|
|
310
|
+
if (m && m[1].length <= level) { end = j; break; }
|
|
311
|
+
}
|
|
312
|
+
let insertAt = headingIdx + 1;
|
|
313
|
+
for (let j = headingIdx + 1; j < end; j++) {
|
|
314
|
+
if (lines[j].trim()) insertAt = j + 1;
|
|
315
|
+
}
|
|
316
|
+
lines.splice(insertAt, 0, RULE_REF_LINE);
|
|
317
|
+
return lines.join("\n");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Make CLAUDE.md reference the ADD rule file under a Workflows/Rules heading, migrating any prior
|
|
321
|
+
// inline ADD block out. created|updated|unchanged|skipped; .bak on change; fail-soft.
|
|
322
|
+
function ensureClaudeReference(claudeMd) {
|
|
323
|
+
try {
|
|
324
|
+
const existed = fs.existsSync(claudeMd);
|
|
325
|
+
const current = existed ? fs.readFileSync(claudeMd, "utf8") : "";
|
|
326
|
+
let next = stripInlineBlock(current);
|
|
327
|
+
if (next.indexOf("add-workflows.md") === -1) next = insertRuleReference(next);
|
|
328
|
+
if (!next.endsWith("\n")) next += "\n";
|
|
329
|
+
if (next === current) return "unchanged";
|
|
330
|
+
if (existed) fs.writeFileSync(claudeMd + ".bak", current);
|
|
331
|
+
fs.writeFileSync(claudeMd, next);
|
|
332
|
+
return existed ? "updated" : "created";
|
|
333
|
+
} catch (e) {
|
|
334
|
+
warn("could not write CLAUDE.md — " + (e && e.message ? e.message : e) + "; skipped");
|
|
335
|
+
return "skipped";
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Rule-file variant of writeAgentPointer for the Claude profile: write the ADD pointer block to
|
|
340
|
+
// .claude/rules/add-workflows.md and leave a reference in CLAUDE.md. Fail-soft.
|
|
341
|
+
function writeRuleFilePointer(target, profile) {
|
|
342
|
+
const rulesPath = path.join(target, RULES_FILE_REL);
|
|
343
|
+
const block = agentPointerBlock(profile);
|
|
344
|
+
try {
|
|
345
|
+
if (fs.existsSync(rulesPath)) {
|
|
346
|
+
const current = fs.readFileSync(rulesPath, "utf8");
|
|
347
|
+
const begin = current.indexOf(GUIDE_BEGIN);
|
|
348
|
+
let next;
|
|
349
|
+
if (begin !== -1) {
|
|
350
|
+
const endIdx = current.indexOf(GUIDE_END, begin);
|
|
351
|
+
if (endIdx !== -1) {
|
|
352
|
+
next = current.slice(0, begin) + block + current.slice(endIdx + GUIDE_END.length);
|
|
353
|
+
} else {
|
|
354
|
+
next = current.replace(/\n+$/, "") + "\n\n" + block + "\n";
|
|
355
|
+
}
|
|
356
|
+
} else {
|
|
357
|
+
next = current.replace(/\n+$/, "") + "\n\n" + block + "\n";
|
|
358
|
+
}
|
|
359
|
+
if (next !== current) {
|
|
360
|
+
fs.writeFileSync(rulesPath + ".bak", current);
|
|
361
|
+
fs.writeFileSync(rulesPath, next);
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
fs.mkdirSync(path.dirname(rulesPath), { recursive: true });
|
|
365
|
+
fs.writeFileSync(rulesPath, block + "\n");
|
|
366
|
+
}
|
|
367
|
+
} catch (e) {
|
|
368
|
+
warn("could not write " + RULES_FILE_REL + " — " + (e && e.message ? e.message : e) + "; skipped");
|
|
369
|
+
return "skipped";
|
|
370
|
+
}
|
|
371
|
+
ensureClaudeReference(path.join(target, "CLAUDE.md"));
|
|
372
|
+
return "ok";
|
|
373
|
+
}
|
|
374
|
+
|
|
252
375
|
// --- interactive layer (clack on a real TTY; plain text everywhere else) -----
|
|
253
376
|
// Designed-for-failure: any doubt (non-TTY, CI, --yes, a failed import, an
|
|
254
377
|
// un-promptable stream) degrades to the EXACT plain-text path below. The clack
|
|
@@ -439,6 +562,57 @@ function writeGeminiSettings(target) {
|
|
|
439
562
|
}
|
|
440
563
|
}
|
|
441
564
|
|
|
565
|
+
// Seed .add/SOUL.md from the bundled template if it does not yet exist. Mirror of
|
|
566
|
+
// _installer.py:_seed_soul_md (npm <-> pip parity): skip-if-exists (SOUL.md is
|
|
567
|
+
// user-owned — never clobber); fail-soft (warn + return, never abort install/update).
|
|
568
|
+
function seedSoulMd(target) {
|
|
569
|
+
const dest = path.join(target, ".add", "SOUL.md");
|
|
570
|
+
if (fs.existsSync(dest)) return; // skip-if-exists (never clobber)
|
|
571
|
+
const source = path.join(PKG_ROOT, "tooling", "templates", "SOUL.md.tmpl");
|
|
572
|
+
if (!fs.existsSync(source)) {
|
|
573
|
+
warn("soul_seed_skipped: SOUL.md.tmpl not found in bundled tooling/templates/");
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
try {
|
|
577
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
578
|
+
fs.writeFileSync(dest, fs.readFileSync(source, "utf8"));
|
|
579
|
+
} catch (e) {
|
|
580
|
+
warn("soul_seed_skipped: could not write .add/SOUL.md — " + (e && e.message ? e.message : e));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Ensure .add/.gitignore lists the engine's transient artifacts. Seed it from the bundled
|
|
585
|
+
// tooling/templates/gitignore.tmpl if absent; else APPEND-IF-ABSENT each pattern line the
|
|
586
|
+
// template carries that the file lacks — additive only, never reorders/removes user lines,
|
|
587
|
+
// idempotent; comment/blank lines are not appended to an existing file. Fail-soft. Twin of
|
|
588
|
+
// _installer.py:_seed_gitignore.
|
|
589
|
+
function seedGitignore(target) {
|
|
590
|
+
const source = path.join(PKG_ROOT, "tooling", "templates", "gitignore.tmpl");
|
|
591
|
+
if (!fs.existsSync(source)) {
|
|
592
|
+
warn("gitignore_seed_skipped: gitignore.tmpl not found in bundled tooling/templates/");
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const dest = path.join(target, ".add", ".gitignore");
|
|
596
|
+
try {
|
|
597
|
+
const body = fs.readFileSync(source, "utf8");
|
|
598
|
+
if (!fs.existsSync(dest)) {
|
|
599
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
600
|
+
fs.writeFileSync(dest, body); // seed-if-missing
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const current = fs.readFileSync(dest, "utf8");
|
|
604
|
+
const have = new Set(current.split("\n").map((l) => l.trim()));
|
|
605
|
+
const missing = body.split("\n").filter(
|
|
606
|
+
(l) => l.trim() && !l.trim().startsWith("#") && !have.has(l.trim())
|
|
607
|
+
);
|
|
608
|
+
if (missing.length === 0) return; // idempotent — nothing to add
|
|
609
|
+
const suffix = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
610
|
+
fs.writeFileSync(dest, current + suffix + missing.join("\n") + "\n");
|
|
611
|
+
} catch (e) {
|
|
612
|
+
warn("gitignore_seed_skipped: could not update .add/.gitignore — " + (e && e.message ? e.message : e));
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
442
616
|
// The drop — now a RECONCILE: restore missing managed trees + refresh present ones
|
|
443
617
|
// (sweep orphans) + report per-tree status. Byte-compatible handoff with the prior
|
|
444
618
|
// installer. The interactive path resolves a target then calls straight into this.
|
|
@@ -446,11 +620,22 @@ function dropFiles(args, target, profile, intent) {
|
|
|
446
620
|
profile = profile || detectAgent(process.env);
|
|
447
621
|
log("Installing ADD into " + target);
|
|
448
622
|
reconcile(args, target);
|
|
623
|
+
seedSoulMd(target); // pip parity: re-seed a missing user-owned SOUL.md (never clobber)
|
|
624
|
+
seedGitignore(target); // pip parity: seed/append-if-absent the engine-transient ignore lines
|
|
449
625
|
|
|
450
626
|
// Agent detection: write THE detected agent's integration file (a marker-delimited
|
|
451
627
|
// pointer init's sync-guidelines later supersedes) + tailor the closing next-step.
|
|
452
628
|
// Best-effort + fail-soft — never aborts the successful drop above.
|
|
453
|
-
|
|
629
|
+
// Rule-file mode (ccsk projects / --rule-file) relocates the CLAUDE.md pointer to
|
|
630
|
+
// .claude/rules/add-workflows.md + a reference — CLAUDE-only; other agents stay inline.
|
|
631
|
+
if (profile.integration_file === "CLAUDE.md" && ruleFileMode(target, args.ruleFile)) {
|
|
632
|
+
if (fs.existsSync(path.join(target, ".ccsk"))) {
|
|
633
|
+
log(" ccsk detected — ADD rules go to .claude/rules/add-workflows.md");
|
|
634
|
+
}
|
|
635
|
+
writeRuleFilePointer(target, profile);
|
|
636
|
+
} else {
|
|
637
|
+
writeAgentPointer(target, profile);
|
|
638
|
+
}
|
|
454
639
|
|
|
455
640
|
// Gemini CLI auto-loads GEMINI.md, not AGENTS.md — so for the gemini profile we ALSO merge
|
|
456
641
|
// .gemini/settings.json (context.fileName) to load the AGENTS.md pointer. Fail-soft + idempotent.
|
|
@@ -800,6 +985,8 @@ function cmdUpdate(args) {
|
|
|
800
985
|
fs.copyFileSync(stateFile, path.join(addDir, "pre-update-state.bak.json"));
|
|
801
986
|
}
|
|
802
987
|
reconcile(args, target);
|
|
988
|
+
seedSoulMd(target); // pip parity: re-seed a missing user-owned SOUL.md (never clobber)
|
|
989
|
+
seedGitignore(target); // pip parity: seed/append-if-absent the engine-transient ignore lines
|
|
803
990
|
writeStamp(addDir, version);
|
|
804
991
|
log("ADD updated " + (cur || "(unstamped)") + " -> " + version +
|
|
805
992
|
" · managed layer reconciled · your project state untouched.");
|
|
@@ -847,4 +1034,7 @@ module.exports = {
|
|
|
847
1034
|
scopeOptions: scopeOptions,
|
|
848
1035
|
writeIntentNote: writeIntentNote,
|
|
849
1036
|
writeGeminiSettings: writeGeminiSettings,
|
|
1037
|
+
ruleFileMode: ruleFileMode,
|
|
1038
|
+
ensureClaudeReference: ensureClaudeReference,
|
|
1039
|
+
writeRuleFilePointer: writeRuleFilePointer,
|
|
850
1040
|
};
|
package/docs/09-the-loop.md
CHANGED
|
@@ -51,7 +51,7 @@ Each delta is one tagged entry — `- [COMPETENCY · status] the learning (evide
|
|
|
51
51
|
|
|
52
52
|
**The consolidation.** At milestone close (or on demand, when open deltas pile up), a person runs the retrospective consolidation: **gather** every `open` delta across the milestone's tasks, **group** them by competency, **propose** the exact foundation edit for each, **confirm** with the human one by one, then **write** — append-only, newest-first (the newest record prepended at the top) — flipping each delta to `folded` (merged) or `rejected` (considered and deliberately not merged, left in place so the trail survives), and bumping the `foundation-version:` marker. `DDD`/`SDD`/`UDD` deltas consolidate into the matching section of `PROJECT.md`; `TDD`/`ADD` consolidate into `CONVENTIONS.md` (they sharpen the engine, not the product); and **every** consolidation also prepends one row at the top of `PROJECT.md` §Key Decisions — the universal, auditable record of what the foundation learned.
|
|
53
53
|
|
|
54
|
-
**Tooling.** `add.py deltas` lists every open delta across the project (so nothing waiting to be consolidated is invisible); `add.py check` lints each delta's well-formedness — known competency tag, valid status, non-empty evidence.
|
|
54
|
+
**Tooling.** `add.py deltas` lists every open delta across the project (so nothing waiting to be consolidated is invisible); `add.py check` lints each delta's well-formedness — known competency tag, valid status, non-empty evidence. `add.py fold [--task <slug>] [--comp <TAG>]` mechanizes the consolidation — flip + transcribe + route + bump, validate-all-then-write so a reject leaves the tree byte-unchanged; running it **is** the human's confirmation, and the engine never self-approves *which* lessons to keep.
|
|
55
55
|
|
|
56
56
|
**Foundation compaction.** The consolidation *prepends* new learnings; **foundation compaction** is the separate, later step that *shrinks* the result. At milestone close (or on demand, once a foundation spec grows past one screen), the AI proposes collapsing the stable, shipped, zero-residue tail of each foundation spec into ONE per-spec **rolled-up settled line** at the bottom — and the human confirms, one line at a time, exactly as with the consolidation. It never deletes: a settled line summarizes the prose and keeps a `see git` pointer, so the record is lossy on wording but lossless on traceability, and any OPEN residue always stays live. Each spec collapses in its own **per-spec shape** — `PROJECT.md` §Spec version bullets, §Key-Decisions rows, `CONVENTIONS.md` learnings, the `GLOSSARY.md` definition, the `MODEL_REGISTRY.md` rows — under one shared eligibility rule. Because every append-only sequence is **newest-first** (newest record on top), compaction collapses *upward from the bottom*: the settled line anchors at the tail (the oldest end). Like the consolidation it is convention-guided — there is deliberately no `add.py` command for it (read `compact-foundation.md`) — and it is distinct from the engine's `add.py compact <slug>`, which archives a finished milestone's files rather than shrinking a living spec.
|
|
57
57
|
|
package/docs/11-governance.md
CHANGED
|
@@ -31,7 +31,7 @@ Every checkpoint produces three short reports — **Test** (does it pass?), **Qu
|
|
|
31
31
|
|
|
32
32
|
- **`PASS`** — criteria met; proceed.
|
|
33
33
|
- **`RISK-ACCEPTED`** — proceed with a signed waiver carrying a named owner, a linked ticket, and an expiry. Allowed for non-security gaps only.
|
|
34
|
-
- **`HARD-STOP`** — cannot proceed. Triggered by any failing test or any security finding
|
|
34
|
+
- **`HARD-STOP`** — cannot proceed. Triggered by any failing test or any security finding. A non-security limitation may proceed only with a signed `RISK-ACCEPTED` record carrying an owner and an expiry; security is never waved through.
|
|
35
35
|
|
|
36
36
|
The rule behind the protocol is *no silent skips.* A report nobody is accountable for approving is just a document; an outcome with an owner is governance.
|
|
37
37
|
|
package/docs/14-foundation.md
CHANGED
|
@@ -56,7 +56,7 @@ then render a real screen and **capture** it. That capture is the **design-confi
|
|
|
56
56
|
evidence — a real image the person approves *before* implementation, so the build
|
|
57
57
|
matches the layout instead of discovering it. The book keeps the *why*; the
|
|
58
58
|
operational recipe (the wireframe format, the token-bound mock, the capture engines)
|
|
59
|
-
lives in the `add` skill's `design.md` and `udd-wireframe.md
|
|
59
|
+
lives in the `add` skill's `design.md` and the `udd-wireframe.md` template (`tooling/templates/`).
|
|
60
60
|
|
|
61
61
|
These three foundation competencies, together with the **TDD ⇄ ADD** engine of
|
|
62
62
|
[Part II](./02-the-flow.md), are ADD's five. The first four feed context to the
|
package/docs/16-releasing.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 16 · Releasing
|
|
2
2
|
|
|
3
|
-
[← 15 Foundations & Lineage](./15-foundations-and-lineage.md) · [Contents](./README.md) · Next: [
|
|
3
|
+
[← 15 Foundations & Lineage](./15-foundations-and-lineage.md) · [Contents](./README.md) · Next: [17 Components →](./17-components.md)
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -11,8 +11,8 @@ them *ship*. This chapter names the act every project eventually performs and th
|
|
|
11
11
|
now, never formalized: bundling closed milestones into a versioned, user-facing release whose notes
|
|
12
12
|
are evidence-backed, whose risk is disclosed, and whose behaviour is then watched.
|
|
13
13
|
|
|
14
|
-
Releasing is the **fifth scope level** — after
|
|
15
|
-
|
|
14
|
+
Releasing is the **fifth scope level** — after setup, intake, the milestone loop, and stage graduation.
|
|
15
|
+
Like every scope level it runs the same shape: **gather → propose → the human
|
|
16
16
|
confirms → the engine records and enforces a floor.** And like graduation, it ends with an outward
|
|
17
17
|
act the human owns. The operational recipe lives in the `release.md` skill guide; this chapter is the
|
|
18
18
|
*why* behind it.
|
|
@@ -179,4 +179,4 @@ the risk, record the marker, and let a person make the outward call.
|
|
|
179
179
|
|
|
180
180
|
---
|
|
181
181
|
|
|
182
|
-
[← 15 Foundations & Lineage](./15-foundations-and-lineage.md) · [Contents](./README.md) · Next: [
|
|
182
|
+
[← 15 Foundations & Lineage](./15-foundations-and-lineage.md) · [Contents](./README.md) · Next: [17 Components →](./17-components.md)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# 17 · Components — monorepo and multi-repo
|
|
2
|
+
|
|
3
|
+
[← 16 Releasing](./16-releasing.md) · [Contents](./README.md) · Next: [Appendix A Templates →](./appendix-a-templates.md)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Most of this book treats a project as one codebase with one green bar. Real
|
|
8
|
+
systems are rarely that tidy: a backend and a frontend, a shared library and two
|
|
9
|
+
apps, or three services across three repos. ADD models all of these the same
|
|
10
|
+
way — as a **graph of components**. A component owns a source root, its own
|
|
11
|
+
green bar, and the contracts it produces or consumes. With that, **one milestone
|
|
12
|
+
can ship a vertical slice across components** — a backend endpoint and the
|
|
13
|
+
frontend that calls it — instead of splitting the slice across milestones.
|
|
14
|
+
|
|
15
|
+
This pillar is **opt-in and additive**: a project that declares no components
|
|
16
|
+
behaves exactly as the rest of the book describes. You reach for it only when a
|
|
17
|
+
milestone genuinely spans more than one green bar.
|
|
18
|
+
|
|
19
|
+
## Declare the components
|
|
20
|
+
|
|
21
|
+
Components are **declared, never inferred** — ADD does not scan `apps/*` and
|
|
22
|
+
guess. You name them in `.add/components.toml`:
|
|
23
|
+
|
|
24
|
+
```toml
|
|
25
|
+
[component.gateway]
|
|
26
|
+
root = "apps/gateway"
|
|
27
|
+
green-bar = "pytest + pyright"
|
|
28
|
+
|
|
29
|
+
[component.dashboard]
|
|
30
|
+
root = "apps/web"
|
|
31
|
+
green-bar = "vitest + a11y"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Each component owns a **root** (the source subtree it governs) and a
|
|
35
|
+
**green-bar** (the suite + checks that prove *that* component healthy). A task
|
|
36
|
+
binds to a component with a `component:` header line; the component's root is
|
|
37
|
+
then added to the task's §5 Scope automatically. A project with no
|
|
38
|
+
`components.toml`, or a task with no `component:` line, is byte-identical to a
|
|
39
|
+
single-component project.
|
|
40
|
+
|
|
41
|
+
## Verify each task against its own green bar
|
|
42
|
+
|
|
43
|
+
In a mixed milestone, a backend task and a frontend task pass on **different
|
|
44
|
+
toolchains**. The verify gate enforces this per component: a task bound to
|
|
45
|
+
`gateway` must cite its component's green-bar (`pytest + pyright`) in the §6
|
|
46
|
+
Build-expectations evidence, or the gate refuses `component_green_bar_uncited`.
|
|
47
|
+
The engine never *runs* the suite — that invariant holds here too. The AI runs
|
|
48
|
+
the right suite for the bound component; the gate checks the **right bar was
|
|
49
|
+
cited** in the evidence. Two tasks, one milestone, two green bars — each held to
|
|
50
|
+
its own.
|
|
51
|
+
|
|
52
|
+
## Freeze a contract between components
|
|
53
|
+
|
|
54
|
+
When one component produces an interface another consumes, that boundary needs a
|
|
55
|
+
**frozen, machine-checkable contract**. Declare it, name its producer and
|
|
56
|
+
consumers:
|
|
57
|
+
|
|
58
|
+
```toml
|
|
59
|
+
[contract.gateway-api]
|
|
60
|
+
producer = "gateway"
|
|
61
|
+
consumers = ["dashboard"]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
A task declares its role with a header line — `produces: gateway-api` or
|
|
65
|
+
`consumes: gateway-api`. When the **producer** task freezes its §3 and crosses
|
|
66
|
+
contract→tests, the engine writes an immutable snapshot at
|
|
67
|
+
`.add/contracts/gateway-api.json` (id, producer, version, frozen date, and a
|
|
68
|
+
hash over the frozen §3 shape). When a **consumer** task crosses contract→tests,
|
|
69
|
+
it **pins** that live hash. If the producer later re-freezes a *changed* shape,
|
|
70
|
+
`add.py check` flags every consumer `contract_consumer_stale` — a §7 delta to
|
|
71
|
+
re-pin against the new shape. A missing or malformed snapshot is a HARD-STOP, not
|
|
72
|
+
a guess: the consumer never builds against a shape that was never frozen.
|
|
73
|
+
|
|
74
|
+
## One milestone, a full-stack slice
|
|
75
|
+
|
|
76
|
+
The reason to put a producer and a consumer in the *same* milestone is to ship a
|
|
77
|
+
vertical slice — but the frontend must not commit to an endpoint the backend has
|
|
78
|
+
not frozen yet. ADD enforces that ordering with a **hold**: a `consumes:` task
|
|
79
|
+
cannot advance scenarios→contract (it cannot write its §3) while its producer's
|
|
80
|
+
snapshot does not yet exist. The engine refuses `producer_contract_unfrozen` and
|
|
81
|
+
the task stays at `scenarios`. Once the backend freezes its contract, the
|
|
82
|
+
frontend proceeds and pins it. The slice is **ordered by the frozen contract**,
|
|
83
|
+
all inside one milestone — the FE stays downstream of the BE endpoint, not split
|
|
84
|
+
into a later milestone.
|
|
85
|
+
|
|
86
|
+
## Across repositories: federation
|
|
87
|
+
|
|
88
|
+
Components in separate repositories work the same way; only the
|
|
89
|
+
**snapshot transport** differs. A consumer repo declares where a producer repo
|
|
90
|
+
publishes its frozen contract:
|
|
91
|
+
|
|
92
|
+
```toml
|
|
93
|
+
[federation.gateway-api]
|
|
94
|
+
source = "../gateway/.add/contracts/gateway-api.json"
|
|
95
|
+
pin = "v1" # optional — the version this repo expects
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`add.py federate pull gateway-api` reads that source, validates it (valid JSON,
|
|
99
|
+
matching id, a hash, and — if `pin` is set — a matching version), and lands a
|
|
100
|
+
**byte-for-byte copy** at the local `.add/contracts/gateway-api.json`. From
|
|
101
|
+
there, the consuming repo's task holds and pins exactly as in a monorepo. The
|
|
102
|
+
pull is **fail-loud by design**: an unknown id, an unreadable source, an invalid
|
|
103
|
+
snapshot, or a version mismatch each HARD-STOPS and lands nothing — federation
|
|
104
|
+
never builds an FE against a guessed or stale endpoint. The producer's snapshot
|
|
105
|
+
is the published artifact; "publishing" is committing that file in the producer
|
|
106
|
+
repo. Each repo keeps its own git-native `state.json`; federation transports only
|
|
107
|
+
the immutable frozen shape, never shared mutable state.
|
|
108
|
+
|
|
109
|
+
## What this pillar is not
|
|
110
|
+
|
|
111
|
+
- **Not auto-discovery.** Components are declared in `components.toml`, not
|
|
112
|
+
inferred from the directory tree.
|
|
113
|
+
- **Not a central server.** Federation copies an immutable snapshot between
|
|
114
|
+
repos; there is no shared service and no shared mutable state.
|
|
115
|
+
- **Not a new approval.** The component machinery rides the existing six-step
|
|
116
|
+
flow and its single contract-freeze approval — it adds gates the engine
|
|
117
|
+
enforces, not human checkpoints.
|
|
118
|
+
|
|
119
|
+
The whole pillar is structure, not policy: who *owns* a component and how
|
|
120
|
+
autonomy is set per component is the identity/governance story (chapters 11–12),
|
|
121
|
+
layered on top of this graph.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
[← 16 Releasing](./16-releasing.md) · [Contents](./README.md) · Next: [Appendix A Templates →](./appendix-a-templates.md)
|
|
@@ -16,6 +16,12 @@
|
|
|
16
16
|
|
|
17
17
|
**Co-specification** — how a spec is made in ADD: the AI and the human **brainstorm the shape together** (diverge), the AI **drafts** it, and the human **validates with the AI's advice** (validate). The AI's decisive advice is the *lowest-confidence flag*. It replaces dictation-by-one-side — the human owns the decision, the AI owns surfacing what it does not yet know. See [03 Specify](./03-step-1-specify.md).
|
|
18
18
|
|
|
19
|
+
**Component** — a declared part of a multi-part codebase that owns a source `root` and its own `green-bar` (suite + checks), named in `.add/components.toml` under `[component.<name>]`. A task binds to one with a `component:` header line, which adds that root to its §5 Scope and holds it to that component's green bar at verify. Declared, never inferred; a project with no components is byte-identical to a single-codebase project. See [17 Components](./17-components.md).
|
|
20
|
+
|
|
21
|
+
**Cross-component contract** — the frozen, machine-checkable interface between a producer component and its consumers, declared under `[contract.<id>]` (producer + consumers). A task names its role with a `produces: <id>` or `consumes: <id>` header. On the producer's freeze the engine writes an immutable snapshot at `.add/contracts/<id>.json`; a consumer pins its hash and is flagged `contract_consumer_stale` if the producer later re-freezes a changed shape. Inside one milestone a `consumes:` task is HELD from writing its §3 until the producer's snapshot exists — the intra-milestone BE→FE ordering. See [17 Components](./17-components.md).
|
|
22
|
+
|
|
23
|
+
**Federation (multi-repo)** — the transport that carries a frozen cross-component contract between separate repositories. A consumer repo declares `[federation.<id>]` with a `source` (and optional `pin`); `add.py federate pull <id>` validates the producer repo's published snapshot and lands a byte-for-byte copy locally, where it behaves exactly as in a monorepo. Fail-loud: an unknown id, unreadable source, invalid snapshot, or version mismatch HARD-STOPS and lands nothing. Each repo keeps its own git-native `state.json`; only the immutable snapshot crosses. See [17 Components](./17-components.md).
|
|
24
|
+
|
|
19
25
|
**Disposable code** — the view that code is one regenerable implementation of the artifacts, not a durable asset to be preserved.
|
|
20
26
|
|
|
21
27
|
**Evidence bundle** — the proof attached to a change (passing tests, clean security scan, no coverage loss) that justifies trusting it and may unlock more AI autonomy.
|
|
@@ -78,7 +84,7 @@
|
|
|
78
84
|
|
|
79
85
|
**Baseline approval** (formerly "the lock-down") — the single human gate ending autonomous setup: an explicit yes that freezes the foundation, first scope, and first contract together; runs as `add.py lock --by <name>`.
|
|
80
86
|
|
|
81
|
-
**Scope level** (formerly "altitude") — the granularity a decision lives at: intake
|
|
87
|
+
**Scope level** (formerly "altitude") — the granularity a decision lives at, as one ordered ladder of five: **setup/foundation** · **intake** (request → versioned scope) · **the milestone loop** (the task is its inner unit) · **stage graduation** (changes rigor, not version; see **Stage graduation**) · **release** (≥1 closed milestone → a versioned, watched cut; see **Release scope level**). One ⚠-assumption notation is shared across every scope level.
|
|
82
88
|
|
|
83
89
|
**Autonomy level** (formerly "autonomy dial") — the explicit per-task setting (`autonomy: manual | conservative | auto`, an ordered ladder manual < conservative < auto) choosing who resolves Verify: `auto` auto-PASSes on complete evidence, `conservative` keeps a human at the gate, `manual` is the strict floor (the human owns the gate; nothing auto-resolves). A high-risk scope refuses an unguarded `auto` — it must be lowered to `manual` or `conservative`. New tasks seed a visible, overridable `autonomy: auto`; a live task with no level warns (`implicit_autonomy`), a token outside the set is rejected (`unknown_autonomy_level`).
|
|
84
90
|
|
|
@@ -128,9 +134,9 @@
|
|
|
128
134
|
|
|
129
135
|
**Newest-first append-only** — every append-only foundation sequence prepends the newest record at the top; the rolled-up settled line anchors at the bottom (the oldest end), so compaction collapses upward.
|
|
130
136
|
|
|
131
|
-
**Wireframe** — the Stage-A low-fidelity, *structural* map of one screen: its regions and the component **slots** inside them, derived from `prototypes/<name>.json` *before* any color, type, or spacing — it answers "what goes where", not "what it looks like". Beat 3 of the UDD **design-definition loop**; the low-fi half of the two-stage fidelity that ends in a confirmed capture. See the `
|
|
137
|
+
**Wireframe** — the Stage-A low-fidelity, *structural* map of one screen: its regions and the component **slots** inside them, derived from `prototypes/<name>.json` *before* any color, type, or spacing — it answers "what goes where", not "what it looks like". Beat 3 of the UDD **design-definition loop**; the low-fi half of the two-stage fidelity that ends in a confirmed capture. See the `udd-wireframe.md` template (`tooling/templates/`, Stage A).
|
|
132
138
|
|
|
133
|
-
**Design mock** — the Stage-B high-fidelity, **self-contained** HTML render of a screen: the `catalog.json` components as a reusable token-bound kit, bound to `tokens.json` and populated with mock data, openable offline and screenshot-able. The human-facing *visible* evidence the human confirms (the frozen `prototypes/<name>.json` tree is its machine-checkable twin). Beat 4's hi-fi artifact; the recipe lives in the `
|
|
139
|
+
**Design mock** — the Stage-B high-fidelity, **self-contained** HTML render of a screen: the `catalog.json` components as a reusable token-bound kit, bound to `tokens.json` and populated with mock data, openable offline and screenshot-able. The human-facing *visible* evidence the human confirms (the frozen `prototypes/<name>.json` tree is its machine-checkable twin). Beat 4's hi-fi artifact; the recipe lives in the `udd-wireframe.md` template (`tooling/templates/`, Stage B).
|
|
134
140
|
|
|
135
141
|
**Capture** — the real rendered image (PNG/SVG) of a design mock: the **design-confirm evidence** artifact. Captures live at `.add/design/captures/<name>.<ext>` (one per prototype) and are attached or mentioned in the feature's `TASK.md`; `@json-render/image` (Satori → PNG/SVG, no browser) is the named default capture engine, otherwise the self-contained mock is screenshot headless. The engine never renders — it only MEASURES presence: `add.py check` raises a never-red `missing_capture` WARN for a prototype with no capture.
|
|
136
142
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pilotspace/add",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "ADD (AI-Driven Development). One skill. Eight steps. Five disciplines. Every feature ships through the loop — a minimal, state-tracked Claude Code skill that ships the AIDD book as its trust layer.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"add": "bin/cli.js"
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
"bin/",
|
|
20
20
|
"skill/",
|
|
21
21
|
"tooling/add.py",
|
|
22
|
+
"tooling/add_engine/",
|
|
23
|
+
"!tooling/add_engine/__pycache__",
|
|
24
|
+
"!**/*.pyc",
|
|
22
25
|
"tooling/templates/",
|
|
23
26
|
"docs/",
|
|
24
27
|
"README.md",
|
|
@@ -46,7 +49,7 @@
|
|
|
46
49
|
"type": "git",
|
|
47
50
|
"url": "git+https://github.com/pilotspace/ADD.git"
|
|
48
51
|
},
|
|
49
|
-
"homepage": "https://github.
|
|
52
|
+
"homepage": "https://pilotspace.github.io/ADD/",
|
|
50
53
|
"bugs": {
|
|
51
54
|
"url": "https://github.com/pilotspace/ADD/issues"
|
|
52
55
|
}
|
package/skill/add/SKILL.md
CHANGED
|
@@ -62,7 +62,13 @@ self-improving via the `soul-self-improve` path). Then branch on state:
|
|
|
62
62
|
- **No active task** → first SIZE the request (Intake below), then `add.py new-task <slug> --title "..."`.
|
|
63
63
|
|
|
64
64
|
**Quick ref** — `status` resume · `init` bootstrap · `advance` continue · `gate PASS` at verify.
|
|
65
|
-
**
|
|
65
|
+
**Flag mode** — two human-owned settings (never auto-picked): **fast** (task) · **auto** (mode).
|
|
66
|
+
- **fast** — `new-task --fast`: minimal template, freeze-gated; a milestone-free `--fast` task is a
|
|
67
|
+
blessed standalone low-ceremony lane. Jot ideas first with `add.py todo "<text>"` (then `todo` to
|
|
68
|
+
list · `todo --done <id>`).
|
|
69
|
+
- **auto** — `autonomy: auto` (default) auto-gates verify on evidence; lower the autonomy level with
|
|
70
|
+
`add.py autonomy set conservative|manual` for a human gate · `new-milestone --await-confirm`
|
|
71
|
+
confirm-gates a milestone's tasks.
|
|
66
72
|
|
|
67
73
|
## Intake — size a request before creating scope
|
|
68
74
|
|
|
@@ -130,6 +136,10 @@ once the human confirms, rewrites `SOUL.md` (the human is the only writer) — `
|
|
|
130
136
|
(security HARD-STOP is un-forceable) → human confirms → `add.py release <version>` records the cut
|
|
131
137
|
(CHANGELOG + `RELEASES.md` ledger + milestone attribution). The engine records; the human runs the
|
|
132
138
|
tag / publish / deploy. A release bundles ≥1 milestone and is orthogonal to stage.
|
|
139
|
+
- **Monorepo / multi-repo** — a milestone spans more than one green bar (a BE + its FE, services across
|
|
140
|
+
repos) → the **component pillar**: declare components in `.add/components.toml`, gate each task on its
|
|
141
|
+
component's green-bar, freeze cross-component contracts (`produces:`/`consumes:`), hold the FE until the
|
|
142
|
+
BE freezes, and `federate pull` a contract across repos — `components.md`. Opt-in; no components = today.
|
|
133
143
|
|
|
134
144
|
## Non-negotiable rules (from the method)
|
|
135
145
|
|