diff --git a/.add/milestones/build-strategy-facets/MILESTONE.md b/.add/milestones/build-strategy-facets/MILESTONE.md index 67ba0c11..e807a2a8 100644 --- a/.add/milestones/build-strategy-facets/MILESTONE.md +++ b/.add/milestones/build-strategy-facets/MILESTONE.md @@ -3,7 +3,7 @@ goal: the build phase carries a structured, domain-anchored implementation strategy — algorithm approach, data strategy, dev pattern, and optimization stance are declared facets (not one overloaded line), each anchored upstream (§0/§1/§3), harvested per-facet into the §7 Decisions (ADR) block, and cross-cited by §7 Watch rationale: intake bucket=one-task-gap → micro-milestone (2026-07-07): senior review of TASK.md.tmpl found §5's single `Strategy (ordered batches)` line conflates build order · architecture pattern · issue advice · persona stance, so builds skip the domain implementation decision (algorithm/data/pattern/optimization); no active milestone's goal covers method-template structure stage: mvp · status: active · created: 2026-07-07T03:57:31+00:00 -release: pending +release: 1.18.0 > SDD living doc for this milestone. Keep it THIN: breadth, shared decisions, and > exit criteria only — per-task detail lives in each `.add/tasks//TASK.md`, diff --git a/.add/milestones/delta-drain/MILESTONE.md b/.add/milestones/delta-drain/MILESTONE.md index 0f1af4e9..e6d9d7ce 100644 --- a/.add/milestones/delta-drain/MILESTONE.md +++ b/.add/milestones/delta-drain/MILESTONE.md @@ -3,7 +3,7 @@ goal: the 4 open SPEC deltas are each resolved into a shipped behavior — compact-foundation gains a read-only --propose preview, personas gain a dedicated `verify` flow value, the streams.md worker-contract `` block names the flow preference, and status/check render an engine-built persona roster line — leaving zero open SPEC deltas rationale: intake bucket=sub-milestone (2026-07-07, Tin: "implement all directly"): 4 independent deltas from 3 prior tasks, each small but crossing engine+skill+guard surfaces; loose fast tasks would lose the shared roster/flow decisions, so a thin milestone binds them stage: mvp · status: active · created: 2026-07-07T08:15:15+00:00 -release: pending +release: 1.18.0 > SDD living doc for this milestone. Keep it THIN: breadth, shared decisions, and > exit criteria only — per-task detail lives in each `.add/tasks//TASK.md`, diff --git a/CHANGELOG.md b/CHANGELOG.md index aa723614..b9a21713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog -## 1.17.0 — 2026-07-07 +## 1.18.0 — 2026-07-07 + +- Drain the 4 open SPEC deltas — 0 carried · 0 key decision(s) +- Faceted §5 build strategy — 0 carried · 0 key decision(s) + +## 1.17.0 — 2026-07-06 - Persona domain-fit nudge — 2 carried · 0 key decision(s) - Method ergonomics — 0 carried · 0 key decision(s) diff --git a/RELEASES.md b/RELEASES.md index 400571c7..003336bc 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,12 @@ # Releases +## 1.18.0 — 2026-07-07 +milestones: delta-drain, build-strategy-facets +loose tasks: none +waivers: reclaim-ticket-race, js-reclaim-lock-heartbeat +actor: Tin Dang (git) +evidence: test_release_1_18_0.py 11/11 green · add.py check green · milestones build-strategy-facets(#139) + delta-drain(#140) merged to main + ## 1.17.0 — 2026-07-07 milestones: persona-domain-fit, method-ergonomics, dynamic-personas, self-improving-loop loose tasks: grep-binary-agnostic-milestone-test, prune-data-update-lock, sweep-orphan-reclaim-tickets, adr-harvester-multiline-fields, strip-scaffold-backtick-comment-fix, worktree-isolated-spawn-default, fold-glossary-deltas, reclaim-ticket-race, js-reclaim-lock-heartbeat, scope-components-check, fastlane-intake-nudge, persona-required-domain-hint diff --git a/add-method/.claude-plugin/plugin.json b/add-method/.claude-plugin/plugin.json index c4d31bbe..872f198d 100644 --- a/add-method/.claude-plugin/plugin.json +++ b/add-method/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "add", "displayName": "ADD — AI-Driven Development", - "version": "1.17.0", + "version": "1.18.0", "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.", "author": { "name": "Tin Dang", diff --git a/add-method/CHANGELOG.md b/add-method/CHANGELOG.md index 25db9f2b..985245f5 100644 --- a/add-method/CHANGELOG.md +++ b/add-method/CHANGELOG.md @@ -4,6 +4,37 @@ All notable changes to the ADD method (`@pilotspace/add` on npm, `pilotspace-add` on PyPI) are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/); versions follow semver. +## [1.18.0] — 2026-07-07 + +Minor: two milestones — **build-strategy-facets** (§5 build strategy becomes +four domain-generic facet lines with fast-lane collapse, plus per-facet ADR +harvest) and **delta-drain** (all 4 open SPEC deltas resolved into shipped +behavior, leaving zero open SPEC deltas). All additive; no gate weakened, +nothing removed or renamed. + +### Added +- **Faceted §5 build strategy** — four domain-generic facet lines drafted at + the tests→build cross: `Approach` (domain strategy) · `Data strategy` · + `Pattern` · `Optimization stance`; the fast lane collapses them to one line + (build-strategy-facets). +- **per-facet ADR harvest** — each §5 facet lands in §7 Decisions (ADR) as its + own actor-tagged line at done (build-strategy-facets). +- **compact-foundation `--propose`** — a read-only preview of the compaction a + run would take; inspect before any byte moves (delta-drain). +- **`verify` flow value for personas** — personas route to the verify surface + directly; the streams.md worker-contract `` block names the flow + preference (delta-drain). +- **persona roster line** — `status`/`check` render an engine-built roster with + flows, never hand-maintained (delta-drain). + +### Changed +- §5 build strategy guidance, phase guides, and the TASK.md/TASK.fast.md + templates teach and carry the facet block (build-strategy-facets). + +### Disclosed waivers (non-security, signed) +- `reclaim-ticket-race` — lock-reclaim TOCTOU flake; owner Tin Dang, expires 2026-08-04. +- `js-reclaim-lock-heartbeat` — JS lock-heartbeat race; owner Tin Dang, expires 2026-08-04. + ## [1.17.0] — 2026-07-06 Minor: four milestones — **method-ergonomics** (every recurring gate rule becomes diff --git a/add-method/README.md b/add-method/README.md index 011c7fc0..aa526b59 100644 --- a/add-method/README.md +++ b/add-method/README.md @@ -2,12 +2,13 @@ npm version PyPI version License: MIT + Read the book GitHub stars

# ADD — AI-Driven Development -**One skill. Eight steps. Five disciplines. Every feature ships through the loop.** +**Describe the feature. The agent drives the build. You approve once — exactly where a mistake would actually cost you.** > A minimal, state-tracked skill for building software when the AI writes the code > and **you** own the two things it cannot do alone: decide *what* to build, and @@ -46,8 +47,28 @@ scenarios → contract → tests as one bundle (you approve once, at the frozen contract), then builds and verifies to green. Full detail below — **Install**, **Use it**, and the [10-minute Quickstart](./GETTING-STARTED.md). +## Highlights + +- ✅ **Approve once, then let it run** — one human sign-off at the frozen contract; the agent builds the rest. +- 🔬 **Proof, not promises** — verified against observed behavior and pre-declared expectations, never just a plausible-looking diff. +- 🔒 **Security never gets waved through** — any security finding is a hard stop, human in the loop. +- 🌱 **Prototype to production** — task → milestone → graduate (analytics-gated) → recorded release, one method throughout. +- 🧠 **Smarter as you go** — lessons consolidate into a living, compacting foundation carried across milestones. +- 🎨 **See it before you build it** — a wireframe and a zero-dependency HTML mock, approved before any code. +- 👥 **Built for teams** — git-native multi-user, N parallel milestones, DAG-scheduled waves. +- 🧩 **One slice, many components** — monorepo or multi-repo, in one team. +- 🤝 **Works with your AI** — Claude, Copilot, Cursor, Codex, Gemini; install via npm, pip, or the Claude Code plugin. + +> _Direction before speed. Trust comes from passing tests — not from reading code and finding it plausible._ + ## Why ADD (and why it is minimal) +Every AI coding tool can write code fast now. The part that never got solved is +trust — how do you know it built the *right* thing, and how do you know it's +*correct*, without reading every line yourself? ADD answers both: freeze the +direction *before* any code is written, then trust the result through passing +evidence, never a diff that merely looks right. + Heavy doc-first methods burn your time writing documents and lose the thread across sessions (context rot). ADD fixes both: @@ -225,6 +246,12 @@ be upset to lose is "the code," you're still working the old way. Start at [`docs/README.md`](./docs/README.md) — Foundations → the six steps → operating it across a team → templates, prompts, and a full worked example. +More entry points: + +- 📖 [Read the book online](https://pilotspace.github.io/ADD/) — the full AIDD method, chapter by chapter +- 🔍 [Full hands-on walkthrough](./GETTING-STARTED.md) — one real feature, end to end +- 🗞️ [ADD Across the Org: AI-Driven Development Beyond Code](https://inkpaper-blog.pages.dev/series/add-across-the-org/) + ## What's next **Dynamic Agent Skills** — the next scope: skills that adapt at runtime to the diff --git a/add-method/package-lock.json b/add-method/package-lock.json index b1b7502c..5588bdec 100644 --- a/add-method/package-lock.json +++ b/add-method/package-lock.json @@ -1,12 +1,12 @@ { "name": "@pilotspace/add", - "version": "1.17.0", + "version": "1.18.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@pilotspace/add", - "version": "1.17.0", + "version": "1.18.0", "license": "MIT", "dependencies": { "@clack/prompts": "^1.5.1" diff --git a/add-method/package.json b/add-method/package.json index 5bb68729..0bad7fd1 100644 --- a/add-method/package.json +++ b/add-method/package.json @@ -1,6 +1,6 @@ { "name": "@pilotspace/add", - "version": "1.17.0", + "version": "1.18.0", "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.", "bin": { "add": "bin/cli.js" diff --git a/add-method/pyproject.toml b/add-method/pyproject.toml index 12f31e94..33c14e69 100644 --- a/add-method/pyproject.toml +++ b/add-method/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pilotspace-add" -version = "1.17.0" +version = "1.18.0" description = "ADD (AI-Driven Development). One skill. Eight steps. Five disciplines. Every feature ships through the loop — install the ADD skill, tooling, and AIDD book into any project." readme = "README.md" license = "MIT" diff --git a/add-method/src/add_method/__init__.py b/add-method/src/add_method/__init__.py index 32c95711..e73d9155 100644 --- a/add-method/src/add_method/__init__.py +++ b/add-method/src/add_method/__init__.py @@ -14,4 +14,4 @@ from add_method._installer import install __all__ = ["install"] -__version__ = "1.17.0" +__version__ = "1.18.0" diff --git a/add-method/tooling/test_release_1_17_0.py b/add-method/tooling/test_release_1_17_0.py index 5a661ac3..123ac3bd 100644 --- a/add-method/tooling/test_release_1_17_0.py +++ b/add-method/tooling/test_release_1_17_0.py @@ -95,26 +95,11 @@ def test_audit_line_survives_bumps(self): class ReleaseShapeTest(unittest.TestCase): - def test_versions_agree_at_1_17_0(self): - pkg = json.loads((PKG / "package.json").read_text(encoding="utf-8"))["version"] - py = re.search(r'(?m)^version\s*=\s*"([^"]+)"', - (PKG / "pyproject.toml").read_text(encoding="utf-8")).group(1) - self.assertEqual((pkg, py), (VERSION, VERSION), - "publish.yml's guard would fail this release closed") - - def test_plugin_version_agrees(self): - plugin = json.loads( - (PKG / ".claude-plugin" / "plugin.json").read_text(encoding="utf-8") - )["version"] - self.assertEqual(plugin, VERSION, - "the Claude Code plugin manifest must match the shipped version") - - def test_runtime_version_agrees(self): - init = (PKG / "src" / "add_method" / "__init__.py").read_text(encoding="utf-8") - runtime = re.search(r'(?m)^__version__\s*=\s*"([^"]+)"', init).group(1) - self.assertEqual(runtime, VERSION, - "add_method.__version__ must match the shipped version") - + # NOTE: 1.17.0 is superseded by 1.18.0 — the live-version-agreement assertions + # (versions/plugin/runtime == VERSION) moved to test_release_1_18_0.py (the + # forward-migration the release-gate pattern demands; the 1.16.0 -> 1.16.1 + # precedent). This file keeps only lineage + shipped-doc + parity checks, + # which stay true across bumps. def test_getting_started_mentions_guide_line(self): text = (PKG / "GETTING-STARTED.md").read_text(encoding="utf-8") self.assertIn("guide :", text, diff --git a/add-method/tooling/test_release_1_18_0.py b/add-method/tooling/test_release_1_18_0.py new file mode 100644 index 00000000..4c29fc0d --- /dev/null +++ b/add-method/tooling/test_release_1_18_0.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +"""Red/green tests for the 1.18.0 release readiness (build-strategy-facets + delta-drain). + +Minor cut: two milestones — **build-strategy-facets** (§5 build strategy becomes +four domain-generic facet lines — Approach · Data strategy · Pattern · +Optimization stance — with fast-lane collapse, plus per-facet ADR harvest into +§7) and **delta-drain** (the 4 open SPEC deltas each resolved into shipped +behavior: `compact-foundation --propose` read-only preview · a dedicated +`verify` persona flow value · the streams.md worker-contract `` block +names the flow preference · an engine-built persona roster line in +status/check). All additive; no gate weakened, nothing removed or renamed. + +In-repo readiness only — the live-registry halves (npm/PyPI serving 1.18.0) are +verify-gate EVIDENCE gathered after the human-gated tag push, never unit tests. +Run: + python3 -m unittest test_release_1_18_0 -v +""" +import hashlib +import json +import re +import unittest +from pathlib import Path + +HERE = Path(__file__).resolve().parent +PKG = HERE.parent # add-method/ +REPO = PKG.parent +BUNDLE = PKG / "src" / "add_method" / "_bundled" + +CHANGELOG = PKG / "CHANGELOG.md" +CI_YML = REPO / ".github" / "workflows" / "ci.yml" +PUBLISH_YML = REPO / ".github" / "workflows" / "publish.yml" + +VERSION = "1.18.0" +PRIOR_VERSIONS = ("1.17.0", "1.16.1", "1.16.0", "1.15.0", "1.14.0", "1.13.0", "1.12.0", + "1.11.0", "1.10.0", "1.9.0", "1.8.0", "1.7.3", "1.7.2", "1.7.1", + "1.7.0", "1.6.0", "1.5.0", "1.4.0", "1.3.0", "1.2.0", "1.1.0", "1.0.0") +from engine_pin import ENGINE_MD5 +CANONICAL_AUDIT = "run: python3 .add/tooling/add.py audit" +# the headline changes the 1.18.0 notes must name +FEATURE_ANCHORS = ("Approach", "Data strategy", "Optimization stance", + "per-facet ADR harvest", "compact-foundation `--propose`", + "`verify` flow", "persona roster line") + + +class ChangelogTest(unittest.TestCase): + def test_changelog_has_1_18_0_entry(self): + self.assertTrue(CHANGELOG.is_file(), "CHANGELOG.md missing") + text = CHANGELOG.read_text(encoding="utf-8") + self.assertIn(f"## [{VERSION}]", text) + for prior in PRIOR_VERSIONS: + self.assertIn(f"## [{prior}]", text, + f"the {prior} lineage entry must survive the bump") + entry = text.split(f"## [{VERSION}]", 1)[1].split("## [", 1)[0] + for anchor in FEATURE_ANCHORS: + self.assertIn(anchor, entry, f"1.18.0 entry must name: {anchor}") + + def test_changelog_ships_in_both_channels(self): + files = json.loads((PKG / "package.json").read_text(encoding="utf-8"))["files"] + self.assertIn("CHANGELOG.md", files, "npm tarball must ship the changelog") + self.assertIn("include CHANGELOG.md", + (PKG / "MANIFEST.in").read_text(encoding="utf-8"), + "sdist/wheel must ship the changelog") + + +class SecurityPolicyShipsTest(unittest.TestCase): + """SECURITY.md must keep travelling with both published distributions.""" + + def test_security_md_present(self): + self.assertTrue((PKG / "SECURITY.md").is_file(), + "add-method/SECURITY.md must exist") + + def test_security_md_ships_in_both_channels(self): + files = json.loads((PKG / "package.json").read_text(encoding="utf-8"))["files"] + self.assertIn("SECURITY.md", files, "npm tarball must ship SECURITY.md") + self.assertIn("include SECURITY.md", + (PKG / "MANIFEST.in").read_text(encoding="utf-8"), + "sdist/wheel must ship SECURITY.md") + + +class WorkflowHygieneTest(unittest.TestCase): + def test_no_deprecated_actions(self): + for wf in (CI_YML, PUBLISH_YML): + text = wf.read_text(encoding="utf-8") + self.assertNotIn("actions/checkout@v4", text, wf.name) + self.assertNotIn("actions/setup-python@v5", text, wf.name) + self.assertNotIn("actions/setup-node@v4", text, wf.name) + + def test_audit_line_survives_bumps(self): + self.assertIn(CANONICAL_AUDIT, CI_YML.read_text(encoding="utf-8"), + "the seam-audit command must stay byte-identical") + + +class ReleaseShapeTest(unittest.TestCase): + def test_versions_agree_at_1_18_0(self): + pkg = json.loads((PKG / "package.json").read_text(encoding="utf-8"))["version"] + py = re.search(r'(?m)^version\s*=\s*"([^"]+)"', + (PKG / "pyproject.toml").read_text(encoding="utf-8")).group(1) + self.assertEqual((pkg, py), (VERSION, VERSION), + "publish.yml's guard would fail this release closed") + + def test_plugin_version_agrees(self): + plugin = json.loads( + (PKG / ".claude-plugin" / "plugin.json").read_text(encoding="utf-8") + )["version"] + self.assertEqual(plugin, VERSION, + "the Claude Code plugin manifest must match the shipped version") + + def test_runtime_version_agrees(self): + init = (PKG / "src" / "add_method" / "__init__.py").read_text(encoding="utf-8") + runtime = re.search(r'(?m)^__version__\s*=\s*"([^"]+)"', init).group(1) + self.assertEqual(runtime, VERSION, + "add_method.__version__ must match the shipped version") + + def test_getting_started_mentions_guide_line(self): + text = (PKG / "GETTING-STARTED.md").read_text(encoding="utf-8") + self.assertIn("guide :", text, + "orient docs must name the phase-playbook line") + + def test_engine_trees_parity(self): + for p in (HERE / "add.py", REPO / ".add" / "tooling" / "add.py", + BUNDLE / "tooling" / "add.py"): + self.assertEqual(hashlib.md5(p.read_bytes()).hexdigest(), ENGINE_MD5, + f"engine trees must stay byte-identical + pinned: {p}") + + +if __name__ == "__main__": + unittest.main(verbosity=2)