contract-driven-delivery 2.1.3 → 2.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,224 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ _No unreleased changes yet._
6
+
7
+ ## [2.2.1] - 2026-06-03
8
+
9
+ Fix a class of false positives in the 2.2.0 API conformance validator that broke
10
+ CI on correct contracts (issue #15), and stop a heuristic blind spot from being
11
+ fatal by default.
12
+
13
+ ### Fixed
14
+
15
+ - **Resolve Flask Blueprint `url_prefix` / FastAPI APIRouter `prefix` across
16
+ files (`validate_api_conformance.py`).** A route declared as
17
+ `@admin_bp.route("/api/logs")` on a `Blueprint(..., url_prefix="/admin")` (or a
18
+ `register_blueprint(bp, url_prefix=...)` in another file) was recorded as
19
+ `/api/logs`, so every prefixed route was flagged `backendRouteNotInContract`
20
+ while the matching contract endpoint was flagged
21
+ `contractEndpointNotImplemented` — two false errors per route against a contract
22
+ that was actually correct. The validator now resolves constructor prefixes per
23
+ file and registration prefixes across files (registration winning) and folds
24
+ them into the route path. Constructor scoping is **per file**, so a bare
25
+ `router` name reused across modules cannot collide; registration prefixes are
26
+ matched across files with each framework's semantics — Flask
27
+ `register_blueprint(url_prefix=...)` **overrides** the Blueprint's own prefix
28
+ while FastAPI `include_router(prefix=...)` is **additive** with the
29
+ `APIRouter(prefix=...)` (served as `<include>/<router>/<route>`). A name
30
+ registered under conflicting prefixes across files is detected and dropped (the
31
+ per-file constructor prefix decides) rather than guessed. The constructor regex
32
+ tolerates a nested-paren kwarg (`APIRouter(dependencies=[Depends(x)],
33
+ prefix=...)`), a module-qualified call (`flask.Blueprint(...)`,
34
+ `fastapi.APIRouter(...)`), and a type-annotated assignment (`router: APIRouter =
35
+ APIRouter(...)`); Flask 2.0 `@bp.get(...)` shorthand is covered too. An explicit
36
+ empty registration prefix (`register_blueprint(bp, url_prefix="")`, a deliberate
37
+ root mount) is preserved and overrides the constructor prefix rather than being
38
+ discarded as falsy. (Issue #15; hardened over three rounds of Codex/Sourcery PR
39
+ review.)
40
+
41
+ ### Changed
42
+
43
+ - **`backendRouteNotInContract` now defaults to `warning`, not `error`.** Regex
44
+ scanning cannot resolve every cross-file route prefix (aliased routers, the
45
+ Express `app.use` mount form, module-qualified `include_router(pkg.router, …)`),
46
+ so a scanner blind spot must not break CI on a contract that is correct. Raise it to
47
+ `error` (or set `"strict": true`) to enforce once a project's routing shape is
48
+ known to resolve cleanly. Updated in `DEFAULT_CONFIG`, the scaffolded
49
+ `.cdd/conformance.json`, and `docs/api-conformance.md`.
50
+
51
+ ## [2.2.0] - 2026-06-02
52
+
53
+ Make enforcement live by default, add a mechanical risk-tier safety net under the
54
+ AI classifier, and give code indexing an opt-in background mode — the three gaps
55
+ that matter most for a fully automated, no-human-reviewer workflow.
56
+
57
+ ### Added
58
+
59
+ - **Arm enforcement chokepoints by default in `cdd-kit init`.** A fresh `init`
60
+ now wires the graph-first PreToolUse hook (Claude provider, advisory) and the
61
+ pre-commit gate hook, instead of shipping them dormant. In an automated
62
+ workflow with no human reviewer, dormant enforcement means the contracts and
63
+ docs only *look* like they prevent drift. Best-effort: a missing `.git` or
64
+ unusual `settings.json` downgrades to a warning, never a failed init. Opt out
65
+ with `cdd-kit init --no-arm`. `installHooks`/`installAgentHooks` gained a
66
+ `fromInit` mode so arming is non-fatal.
67
+ - **Mechanical risk-tier floor (`src/utils/tier-floor.ts`).** A deterministic
68
+ backstop under the (AI) classifier: `cdd-kit gate` scans `change-request.md`
69
+ for sensitive surfaces (auth, payments, migrations, concurrency, secrets, …)
70
+ and **fails** when the declared tier is weaker than the matched floor, so a
71
+ single mis-classification can no longer silently drop the required agents and
72
+ tests. Bypass per-change with `tier-floor-override: "<reason>"` in `tasks.yml`
73
+ frontmatter (downgrades to an audit warning). Policy lives in
74
+ `.cdd/tier-policy.json` (scaffolded, fully editable, `enabled:false` to
75
+ disable); built-in defaults apply when the file is absent so existing repos are
76
+ protected without a re-init.
77
+ - **`cdd-kit classify-check [change-id] | --text "<intent>"`** — advisory probe
78
+ that prints the mechanical tier floor *before* classification, so the
79
+ classifier can be steered up front rather than only caught by the gate.
80
+ Supports `--json`.
81
+ - **`cdd-kit code-map --watch`** — opt-in background auto-indexing. A debounced
82
+ (default 500 ms, `--debounce`) recursive watcher keeps the map fresh during
83
+ long-lived co-editing sessions, with a freshness-polling fallback where
84
+ recursive `fs.watch` is unavailable. Trigger-based indexing stays the default
85
+ for ephemeral CI/agent runs.
86
+ - **ADR 0003** (`docs/adr/0003-code-intelligence-indexing-strategy.md`):
87
+ evaluates LSP (Serena) vs tree-sitter incremental (CocoIndex) vs the kit's
88
+ native AST scanners, and the trigger-vs-background refresh question. Decision:
89
+ keep native AST (LSP does not translate to headless agents), keep trigger-based
90
+ as default, add the opt-in `--watch` above, and sequence per-file incremental
91
+ rebuild as the next step.
92
+
93
+ ### Added (mechanical chokepoints — prior unreleased work)
94
+
95
+ - **API conformance validator** (`validate_api_conformance.py`): parses real
96
+ backend route declarations and frontend HTTP call sites and diffs them against
97
+ `contracts/api/api-contract.md`. Catches frontend/backend API drift that the
98
+ markdown-only validators never could. Chained into `cdd-kit validate
99
+ --contracts`, so `cdd-kit gate` blocks on drift. Off until enabled in
100
+ `.cdd/conformance.json` (`"enabled": true`); a disabled config is scaffolded by
101
+ `cdd-kit init`. See `docs/api-conformance.md`.
102
+ - **`--with-source` / `withSource`** on `cdd-kit index query`, `cdd-kit graph
103
+ query`, and the `cdd_index_query` / `cdd_graph_query` MCP tools: returns the
104
+ matched symbol's code inline so the query replaces a follow-up `Read` instead
105
+ of preceding it. `--source-budget` caps total lines and flags truncated ranges.
106
+ - **`hooks/pre-tool-use-graph-first.sh`** (opt-in PreToolUse hook): steers agents
107
+ to `cdd-kit index query --with-source` before reading source files. Advisory by
108
+ default; `CDD_GRAPH_FIRST_STRICT=1` hard-blocks source reads when a code-map
109
+ exists.
110
+ - `cdd-kit doctor` reports whether API conformance is enabled, disabled, or
111
+ unconfigured (informational; never fails `--strict`).
112
+ - `cdd-kit doctor` reports whether the **cdd-kit MCP server is registered** with
113
+ Claude Code (runs `claude mcp list`). If it is not registered, agents never see
114
+ the graph/index tools and silently fall back to `Read`, so doctor surfaces the
115
+ `claude mcp add --scope user cdd-kit -- cdd-kit mcp` command to fix it.
116
+ Informational only (never fails `--strict`), best-effort with a 3s timeout
117
+ (never blocks on a slow/missing `claude` CLI), skipped for non-Claude and
118
+ non-cdd-kit projects. `CDD_CLAUDE_BIN` overrides the CLI path. Closes the gap
119
+ left by `--with-source`: the incentive to use the kit tools only helps if the
120
+ agent can see them.
121
+ - **`cdd-kit install-agent-hooks --graph-first advisory|strict`**: installs the
122
+ graph-first `PreToolUse` hook into `.claude/settings.json` (project-scoped) and
123
+ copies the script to `.claude/hooks/`, so steering agents to
124
+ `cdd-kit index query --with-source` before `Read` becomes an installed harness
125
+ chokepoint instead of manual settings wiring. Advisory by default; `strict`
126
+ writes `CDD_GRAPH_FIRST_STRICT=1`. Idempotent and preserves unrelated settings.
127
+ - **`cdd-kit openapi export`**: projects `contracts/api/api-contract.md` into a
128
+ minimal OpenAPI 3.1 skeleton (`--yaml`, `--out`) for tooling such as
129
+ `openapi-typescript`. One-way projection — the markdown contract stays the
130
+ source of truth. Derives paths/params/auth/status codes; marks free-form
131
+ request/response bodies as `x-cdd-unresolved` rather than fabricating schemas.
132
+ Per-stack client generation is intentionally left to the consumer repo; see
133
+ `docs/adr/0001-contract-to-openapi-export.md` and `docs/openapi-export.md`.
134
+ - **`cdd-kit openapi export --check`**: the OpenAPI sync gate. Instead of
135
+ writing, it verifies the committed artifact at `--out` still equals what the
136
+ contract produces and exits non-zero on drift — so CI fails when the contract
137
+ changes but the export was not regenerated. This is the kit-owned half of the
138
+ preventive chain (the consumer's typed-client codegen runs from an artifact
139
+ that can never be silently stale).
140
+ - **`cdd-kit init` now wires the consumer codegen seam**: when a `package.json`
141
+ is present it adds editable `contract:client` and `contract:client:check` npm
142
+ scripts (the latter is the `openapi export --check` gate), turning the
143
+ consumer half of the OpenAPI seam from a doc into a chokepoint. Additive,
144
+ idempotent, never clobbers existing scripts; `openapi-typescript` is an
145
+ editable default, not a hard dependency.
146
+ - **`cdd-kit doctor` chokepoint dashboard**: reports each enforcement chokepoint
147
+ (graph-first hook, pre-commit gate, OpenAPI sync gate) as `live` or `dormant`
148
+ with the one command to arm it. The kit's mechanisms are opt-in and dormant
149
+ until armed, so a repo could carry all the machinery yet enforce none of it —
150
+ this makes that observable. Advisory only (never fails `--strict`).
151
+
152
+ ### Changed
153
+
154
+ - `backend-engineer` and `frontend-engineer` prompts now prefer
155
+ `--with-source` queries and warn that endpoint changes/calls require a contract
156
+ update when conformance is enabled.
157
+
158
+ ### Fixed
159
+
160
+ - **graph-first hook now installs in the shape Claude Code executes.**
161
+ `install-agent-hooks` wrote the command directly on the `PreToolUse` matcher
162
+ group; Claude Code requires it nested under an inner `hooks: [{ type:
163
+ "command", … }]` array, so the chokepoint was silently dormant even though
164
+ install/init reported it armed. Re-running upgrades a legacy entry in place and
165
+ preserves unrelated handlers that share the matcher group.
166
+ - **tier floor scans the right path scope.** `cdd-kit gate` now scans the
167
+ **staged** change (rename-aware, both sides) instead of the whole worktree, so
168
+ an unrelated unstaged `auth/` edit can no longer trip the floor and reject a
169
+ low-risk commit; when a single commit stages more than one change directory the
170
+ path signal is dropped (source paths can't be attributed to one change) and
171
+ only the request text sets the floor. `cdd-kit classify-check` keeps
172
+ whole-worktree scope (its in-progress change is not yet committed).
173
+ - **tier-floor pattern accuracy.** Critical-surface patterns now match plural
174
+ directories (`payments/`, `migrations/`); the `token` pattern is qualified to
175
+ security contexts (`access`/`api`/`auth`/`session`/`bearer`/`refresh`/`csrf`/
176
+ `id`/`reset`) so frontend "design tokens" / `theme/tokens.ts` no longer trip
177
+ the secrets floor. The gate / `classify-check` `matched:` line now reports the
178
+ actual matched text (e.g. `session token`) instead of the raw regex pattern.
179
+ - **`cdd-kit doctor` detects a gate armed under a custom `core.hooksPath`** (or a
180
+ worktree/submodule), resolving the hooks dir via git instead of probing only
181
+ `.git/hooks/pre-commit` — no more reporting a live gate as dormant.
182
+ - **`cdd-kit code-map --watch` skips churn under ignored trees** (`node_modules`,
183
+ `dist`, `.git`, `.next`, coverage) before rebuilding, and adds an `error`
184
+ listener so fs-watch runtime errors (ENOSPC, permissions) degrade to polling
185
+ instead of crashing the watch. Edits to `.cdd/code-map-config.yml` still
186
+ trigger a rebuild even though it lives under the ignored `.cdd/` tree.
187
+ - **`cdd-kit gate` no longer passes changes whose artifacts are still unfilled
188
+ scaffolds.** The stub check counted "meaningful chars", but a template's own
189
+ instructional prose (900+ chars) cleared the threshold while every field was
190
+ still an `<id>` / `<date>` / `<change-id>` placeholder — so a change could pass
191
+ `--strict` with raw templates and zero real content. Gate now fails and names
192
+ the remaining placeholder tokens per artifact. The check is a closed allowlist
193
+ (`<id>` / `<date>` / `<change-id>`) anchored to the colon-led, line-final value
194
+ position the templates use (`change-id: <id>`, `# …: <change-id>`), so inline
195
+ XML/markup examples (`<id>123</id>`) and hyphenated custom elements
196
+ (`<my-element>`) are not false-flagged — and a file may carry both a real
197
+ placeholder and an XML example without the placeholder slipping through.
198
+ `context-manifest.md` is exempt (its `<...>` sub-sections are documented as
199
+ illustrative; it is enforced via Allowed Paths, not template fill-ins).
200
+ - **`cdd-kit validate` / `gate` no longer crash with `UnicodeDecodeError` on
201
+ non-UTF-8 Windows locales (e.g. cp950/zh-TW).** `validate_contract_versions.py`
202
+ read `git show` output and the validators wrote stdout using the locale codec,
203
+ spamming tracebacks and mojibaking em-dashes in contracts. The Python
204
+ validators are now spawned with `PYTHONUTF8=1` / `PYTHONIOENCODING=utf-8`, and
205
+ the git subprocesses decode as UTF-8 explicitly.
206
+ - **`cdd-kit openapi export` fails fast on a mis-tagged schema fence** instead of
207
+ silently dropping it: a `### Name` section under `## Schemas` that uses ` ```json `
208
+ (or any non-`json-schema` fence) now errors with the fix — including when a field
209
+ table is also present (the stray fence was previously ignored). A prose-only
210
+ section with no fence stays a valid Tier C contract and is left unresolved.
211
+ - **`cdd-kit openapi export --out <absolute-path>`** no longer ENOENTs on an
212
+ absolute path (it was concatenated onto cwd, e.g. `D:\repo\C:\Users\…`); paths
213
+ are resolved with `path.resolve`.
214
+
215
+ ### Added
216
+
217
+ - **`cdd-kit openapi export` typed schemas (ADR 0002)**: `## Schemas` sections
218
+ compile field tables (Tier A) and `json-schema` fenced blocks (Tier B) into
219
+ `components.schemas` with `$ref` resolution; the contract stub documents both
220
+ tiers.
221
+
3
222
  ## [2.1.3] - 2026-05-29
4
223
 
5
224
  Correct Claude Code MCP registration guidance.
package/README.md CHANGED
@@ -269,8 +269,18 @@ cdd-kit init --local-only # only scaffold project files
269
269
  cdd-kit init --provider codex # scaffold Codex-oriented project guidance
270
270
  cdd-kit init --provider both # scaffold Claude Code + Codex guidance
271
271
  cdd-kit init --force # overwrite existing project files
272
+ cdd-kit init --no-arm # scaffold without arming enforcement chokepoints
272
273
  ```
273
274
 
275
+ By default `init` **arms** the enforcement chokepoints so a fresh repo enforces
276
+ the workflow instead of carrying it dormant: the graph-first PreToolUse hook
277
+ (Claude provider, advisory) and the pre-commit gate hook are wired in place. This
278
+ matters most in a fully automated, no-human-reviewer workflow — dormant
279
+ enforcement means the contracts only *look* like they prevent drift. Arming is
280
+ best-effort (a missing `.git` becomes a warning, never a failed init); pass
281
+ `--no-arm` to skip it, and `cdd-kit doctor` reports the live/dormant status of
282
+ each chokepoint.
283
+
274
284
  Creates: `contracts/`, `specs/templates/`, provider guidance files (`CLAUDE.md`, `AGENTS.md`, and/or `CODEX.md`), `hooks/`
275
285
 
276
286
  `.cdd/model-policy.json` stores role-to-model **classes** (`opus`, `sonnet`, `haiku`) instead of provider release IDs such as `claude-opus-4-7`. This keeps the policy stable across Claude and Codex adapters; provider-specific tooling can map the class to the concrete model available in that environment.
@@ -341,6 +351,29 @@ place. Then run `cdd-kit migrate --all` so existing active change directories
341
351
  receive `implementation-plan.md`; fill required `design.md` with
342
352
  `spec-architect` before resuming the planner or implementation agents.
343
353
 
354
+ #### Upgrading to 2.2.0
355
+
356
+ 2.2.0 **arms enforcement chokepoints by default on a fresh `cdd-kit init`**, adds
357
+ the mechanical **tier floor**, `cdd-kit classify-check`, and `cdd-kit code-map
358
+ --watch`. A repo first set up with an older version keeps its chokepoints
359
+ *dormant* after a plain `npm`/`refresh` update — `cdd-kit doctor` will show them
360
+ as such. To bring an existing repo up to the 2.2.0 enforcement posture:
361
+
362
+ ```bash
363
+ npm install -g contract-driven-delivery # get 2.2.0
364
+ cdd-kit refresh --yes # sync agents/skills/templates/hooks/code-map
365
+ cdd-kit install-hooks # arm the pre-commit gate
366
+ cdd-kit install-agent-hooks --graph-first advisory # arm the graph-first hook (or: strict)
367
+ cdd-kit doctor # confirm both chokepoints report "live"
368
+ ```
369
+
370
+ The tier floor needs **no policy file** — built-in defaults apply when
371
+ `.cdd/tier-policy.json` is absent, so existing repos are protected without a
372
+ re-init. To customize or disable it, scaffold the policy with `cdd-kit upgrade
373
+ --yes` (writes an editable `.cdd/tier-policy.json`) and set rules or
374
+ `"enabled": false`. Bypass a single change with `tier-floor-override:
375
+ "<reason>"` in its `tasks.yml` frontmatter (recorded as an audit warning).
376
+
344
377
  If you do not want template overwrites, run the narrower path:
345
378
 
346
379
  ```bash
@@ -366,6 +399,10 @@ cdd-kit doctor --provider codex
366
399
 
367
400
  Checks for missing `.cdd/` policy files, provider guidance (`CLAUDE.md`, `AGENTS.md`, `CODEX.md`), context indexes, stale `specs/context/*` outputs, and contract summary metadata gaps. `--strict` treats warnings as errors. `--json` emits a machine-readable report for CI or wrapper scripts. `--fix` currently auto-runs `context-scan` for stale or missing indexes and backfills empty `.cdd/model-policy.json` role bindings, but deliberately does not run invasive repo upgrades for you.
368
401
 
402
+ For Claude projects, `doctor` also reports whether the **cdd-kit MCP server is registered** with Claude Code (it runs `claude mcp list`). If it is not registered, agents never see the graph/index tools and silently fall back to `Read`, so doctor surfaces the exact `claude mcp add --scope user cdd-kit -- cdd-kit mcp` command to fix it. This check is **informational only** — it never fails `--strict`, never blocks on a slow or missing `claude` CLI (3s timeout, best-effort), and is skipped for non-Claude projects. Point `CDD_CLAUDE_BIN` at an alternate Claude CLI if needed.
403
+
404
+ `doctor` finally prints a **chokepoint dashboard**: for each enforcement mechanism — the graph-first hook, the pre-commit gate, and the OpenAPI sync gate — it reports `live` (armed) or `dormant`, with the one command to arm it. The kit's mechanisms are opt-in and dormant until installed, so a repo can carry all the machinery yet enforce none of it; this makes that state observable. Like the MCP and conformance lines, it is **advisory only** and never fails `--strict`.
405
+
369
406
  ---
370
407
 
371
408
  ### `cdd-kit upgrade`
@@ -437,6 +474,7 @@ Checks:
437
474
  - All required artifacts exist (`change-request.md`, `change-classification.md`, `implementation-plan.md`, `test-plan.md`, `ci-gates.md`, `tasks.yml`; new context-governed changes also require `context-manifest.md`)
438
475
  - Each artifact has sufficient content and is not a stub.
439
476
  - `change-classification.md` contains a tier or risk marker.
477
+ - **Mechanical risk-tier floor.** `change-request.md` is scanned for sensitive surfaces (auth, payments, migrations, concurrency, secrets, …) — and the change's git paths are scanned against the critical (tier-0) rules only, so a generic request whose work lives under `auth/` or `payments/` is still caught. The gate scans the **staged** change (so an unrelated unstaged edit can't trip it; rename-aware on both sides; the path signal is dropped when a commit stages multiple change dirs), while `classify-check` scans the whole worktree. The gate fails when the declared tier is weaker than the matched floor — the deterministic safety net under the AI classifier. Bypass one change with `tier-floor-override: "<reason>"` in `tasks.yml` frontmatter (becomes an audit warning); tune or disable in `.cdd/tier-policy.json`.
440
478
  - Atomic `depends-on` upstream changes are completed or archived before dependent work gates.
441
479
  - All contract validators pass.
442
480
 
@@ -456,6 +494,25 @@ Pre-commit hook uses `--strict` by default (installed via `cdd-kit install-hooks
456
494
 
457
495
  ---
458
496
 
497
+ ### `cdd-kit classify-check`
498
+
499
+ Advisory probe that prints the **mechanical risk-tier floor** for a change
500
+ *before* classification, so the classifier can be steered up front instead of
501
+ only being caught later by `cdd-kit gate`. The blocking enforcement lives in the
502
+ gate; this command never fails (exit 0).
503
+
504
+ ```bash
505
+ cdd-kit classify-check add-jwt-auth # scan the change's change-request.md
506
+ cdd-kit classify-check --text "add stripe checkout" # scan inline intent
507
+ cdd-kit classify-check add-jwt-auth --json # machine-readable
508
+ ```
509
+
510
+ It reads the same ground-truth source the gate enforces against
511
+ (`change-request.md`), reports the strictest matched tier and the matched
512
+ patterns, and points at `.cdd/tier-policy.json` for tuning.
513
+
514
+ ---
515
+
459
516
  ### `cdd-kit list`
460
517
 
461
518
  Lists all active changes in `specs/changes/` with status and pending task count.
@@ -607,13 +664,38 @@ Runs contract validation scripts.
607
664
 
608
665
  ```bash
609
666
  cdd-kit validate # all validators
610
- cdd-kit validate --contracts # API, CSS, data-shape (+ semantic checks)
667
+ cdd-kit validate --contracts # API, CSS, data-shape (+ semantic + conformance checks)
611
668
  cdd-kit validate --env # env contract
612
669
  cdd-kit validate --ci # CI gate policy
613
670
  cdd-kit validate --spec # spec traceability
614
671
  cdd-kit validate --versions # contract frontmatter schema versions
615
672
  ```
616
673
 
674
+ `--contracts` includes **API conformance**: a code-vs-contract check that parses
675
+ real backend routes and frontend call sites and fails on drift from
676
+ `contracts/api/api-contract.md` (e.g. the frontend calling an endpoint the
677
+ contract never declares). It is off until you enable it in `.cdd/conformance.json`
678
+ (`"enabled": true`); `cdd-kit init` scaffolds a disabled config. See
679
+ [docs/api-conformance.md](docs/api-conformance.md). This is the mechanical net
680
+ for frontend/backend API drift in a workflow where no human reviews the contract
681
+ by hand.
682
+
683
+ ---
684
+
685
+ ### `cdd-kit openapi export`
686
+
687
+ Projects `contracts/api/api-contract.md` into a minimal **OpenAPI 3.1** skeleton for tooling (e.g. feeding `openapi-typescript` to generate a typed frontend client). The markdown contract stays the source of truth; the OpenAPI document is a one-way, regenerable projection.
688
+
689
+ ```bash
690
+ cdd-kit openapi export # JSON to stdout
691
+ cdd-kit openapi export --yaml --out openapi.yaml # YAML to a file
692
+ cdd-kit openapi export --check --out openapi.json # sync gate: fail on drift
693
+ ```
694
+
695
+ It derives paths (normalizing `:id`/`{id}`), path parameters, auth → bearer security, and success/error status codes. Free-form request/response schemas in the contract are marked `x-cdd-unresolved` rather than fabricated. Generating an actual client is left to your stack's generator in your own CI — this is the **preventive** complement to the **detective** conformance check above. See [docs/openapi-export.md](docs/openapi-export.md) and [docs/adr/0001-contract-to-openapi-export.md](docs/adr/0001-contract-to-openapi-export.md).
696
+
697
+ `--check` is the **sync gate**: it does not write, it verifies the committed artifact at `--out` still matches the contract and exits non-zero on drift, so CI fails when a contract edit forgets to regenerate the export (and the typed client downstream). To wire the consumer half, `cdd-kit init` scaffolds editable `contract:client` and `contract:client:check` npm scripts when a `package.json` is present — the generic contract→OpenAPI step is the kit's, the stack-specific codegen stays an editable script in your repo.
698
+
617
699
  ---
618
700
 
619
701
  ### `cdd-kit new <name>`
@@ -651,6 +733,22 @@ Idempotent. Preserves existing hook content. Bypass with `--no-verify` is possib
651
733
 
652
734
  ---
653
735
 
736
+ ### `cdd-kit install-agent-hooks`
737
+
738
+ Installs Claude Code **agent hooks** into the project's `.claude/settings.json`. Currently installs the graph-first `PreToolUse` hook, which steers agents to `cdd-kit index query --with-source` before reading source files — turning the hook from a documented file you wire by hand into an enforced harness chokepoint.
739
+
740
+ ```bash
741
+ cdd-kit install-agent-hooks # advisory (default)
742
+ cdd-kit install-agent-hooks --graph-first strict # hard-block source Reads when a code-map exists
743
+ ```
744
+
745
+ - **advisory** (default): reminds the agent to use the graph/index query first; does not block the `Read`.
746
+ - **strict**: writes `CDD_GRAPH_FIRST_STRICT=1` into the hook command so the hook blocks source-file `Read` when `.cdd/code-map.yml` exists.
747
+
748
+ Writes the hook script to `.claude/hooks/pre-tool-use-graph-first.sh` and a `PreToolUse` entry to `.claude/settings.json` (project-scoped, so it travels with the repo). Idempotent: re-running replaces the cdd-kit entry and switches mode cleanly, preserving every other setting and hook.
749
+
750
+ ---
751
+
654
752
  ### `cdd-kit detect-stack`
655
753
 
656
754
  Detects the project tech stack from lockfiles and config files.
@@ -712,8 +810,21 @@ cdd-kit code-map # whole repo -> .cdd/code-map.yml
712
810
  cdd-kit code-map --check # exit 1 if regenerating would change the map
713
811
  cdd-kit code-map --surface packages/web # monorepo: scope + auto-name the map
714
812
  cdd-kit code-map --workers # parallelize JS/TS/Vue scanning (default off)
813
+ cdd-kit code-map --watch # background: keep the map fresh as files change
814
+ cdd-kit code-map --watch --debounce 800 # coalesce change bursts within 800ms
715
815
  ```
716
816
 
817
+ Indexing is **trigger-based by default** — the map regenerates when a command
818
+ needs it (gate, `index query --refresh`, `doctor --fix`, the pre-commit code-map
819
+ hook). That is the right default for ephemeral CI containers and one-shot agent
820
+ runs. `--watch` is the opt-in **background** mode for long-lived co-editing
821
+ sessions: a debounced recursive watcher keeps the map fresh so queries stay cheap
822
+ and current, with a freshness-polling fallback where recursive `fs.watch` is
823
+ unavailable. See
824
+ [docs/adr/0003-code-intelligence-indexing-strategy.md](docs/adr/0003-code-intelligence-indexing-strategy.md)
825
+ for why the kit keeps native AST scanners instead of an LSP daemon, and the
826
+ incremental-rebuild roadmap.
827
+
717
828
  `--workers [n]` (default off; `n` defaults to CPU count − 1, capped at 16)
718
829
  parallelizes the synchronous JS/TS/Vue parsing across child processes for large
719
830
  repos. Output is byte-identical to a single-process run, and any worker failure
@@ -735,10 +846,22 @@ with `--engine codegraph`.
735
846
  ```bash
736
847
  cdd-kit graph status
737
848
  cdd-kit graph query OrderService
849
+ cdd-kit graph query OrderService --with-source # include code inline; no follow-up Read needed
738
850
  cdd-kit graph context "filter options are empty"
739
851
  cdd-kit graph impact src/services/orders.ts --depth 2
740
852
  ```
741
853
 
854
+ `--with-source` (also on `cdd-kit index query`, and `withSource: true` via MCP)
855
+ returns the matched symbol's code inline so the query *replaces* a `Read` rather
856
+ than preceding it — making the kit tool strictly cheaper than the built-in
857
+ `Read`. `--source-budget <n>` caps total lines returned; truncated ranges are
858
+ flagged so you can `Read` only those.
859
+
860
+ To make graph-first exploration a real chokepoint instead of a prompt
861
+ preference, wire the shipped `hooks/pre-tool-use-graph-first.sh` as a
862
+ `PreToolUse` hook on `Read` (advisory by default; `CDD_GRAPH_FIRST_STRICT=1`
863
+ hard-blocks source `Read`s when a code-map exists).
864
+
742
865
  Use `--engine native` for the built-in graph, `--engine codemap` for the older
743
866
  code-map-only fallback, `--engine codegraph` to require external CodeGraph, or
744
867
  `CDD_CODEGRAPH_BIN=/path/to/codegraph` to point at a custom binary.
@@ -63,6 +63,19 @@ Prefer these MCP tools before reading source files: `cdd_graph_context`,
63
63
  available, use the equivalent CLI commands: `cdd-kit graph ...` and
64
64
  `cdd-kit index ...`.
65
65
 
66
+ Pass `withSource: true` (MCP) or `--with-source` (CLI) on `query` to get the
67
+ matched symbol's code inline. The query then replaces a follow-up `Read` instead
68
+ of preceding it — use a plain `Read` only for ranges the query did not return
69
+ (e.g. a range flagged as source-budget truncated).
70
+
71
+ ## API Conformance
72
+
73
+ If `.cdd/conformance.json` has `"enabled": true`, `cdd-kit validate --contracts`
74
+ (and `cdd-kit gate`) mechanically check real backend routes and frontend call
75
+ sites against `contracts/api/api-contract.md`. Do not add, rename, or call an
76
+ endpoint without updating the contract in the same change, or the gate will fail
77
+ on the drift. See `docs/api-conformance.md`.
78
+
66
79
  ## Context Governance
67
80
 
68
81
  For context-governed changes, read `specs/changes/<change-id>/context-manifest.md` before using file-reading or broad search tools.
@@ -11,7 +11,8 @@ Before editing production code, read `specs/changes/<change-id>/implementation-p
11
11
 
12
12
  ## Code map (READ FIRST)
13
13
 
14
- Before reading ANY source file (`.py`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.vue`), FIRST run `cdd-kit index query "<symbol-or-file>"` or `Read .cdd/code-map.yml`.
14
+ Before reading ANY source file (`.py`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.vue`), FIRST run `cdd-kit index query "<symbol-or-file>" --with-source` or `Read .cdd/code-map.yml`.
15
+ Prefer `--with-source`: it returns the matched symbol's code inline, so you do NOT need a separate `Read` for that range. Use a plain `Read` only when you need lines the query did not return (e.g. a range flagged as source-budget truncated).
15
16
  Before editing a chosen source file, run `cdd-kit index impact "<path-or-symbol>"` to identify indexed local imports and dependents.
16
17
 
17
18
  The map is the size oracle. For each file you intend to read:
@@ -31,6 +32,7 @@ See `references/code-map-protocol.md` for the full protocol.
31
32
 
32
33
  ## Rules
33
34
 
35
+ - Do not change API response shape or add/rename/remove endpoints without updating `contracts/api/api-contract.md` in the same change. If `.cdd/conformance.json` is enabled, `cdd-kit validate --contracts` (and the gate) will fail when a backend route is missing from the contract.
34
36
  - Do not change API response shape without contract updates.
35
37
  - Keep route/controller code thin.
36
38
  - Put business logic in service/domain layers.
@@ -11,7 +11,8 @@ Before editing, read `specs/changes/<change-id>/implementation-plan.md`, API con
11
11
 
12
12
  ## Code map (READ FIRST)
13
13
 
14
- Before reading ANY source file (`.py`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.vue`), FIRST run `cdd-kit graph query "<symbol-or-file>"`, `cdd-kit graph context "<task>"`, `cdd-kit index query "<symbol-or-file>"`, or `Read .cdd/code-map.yml`.
14
+ Before reading ANY source file (`.py`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.vue`), FIRST run `cdd-kit graph query "<symbol-or-file>" --with-source`, `cdd-kit graph context "<task>"`, `cdd-kit index query "<symbol-or-file>" --with-source`, or `Read .cdd/code-map.yml`.
15
+ Prefer `--with-source`: it returns the matched symbol's code inline, so you do NOT need a separate `Read` for that range. Use a plain `Read` only for lines the query did not return (e.g. a range flagged as source-budget truncated).
15
16
  Before editing a chosen source file, run `cdd-kit graph impact "<path-or-symbol>" --depth 2` or `cdd-kit index impact "<path-or-symbol>"` to identify imports, dependents, callers/callees when available, and likely affected scope.
16
17
 
17
18
  The map is the size oracle. For each file you intend to read:
@@ -31,7 +32,7 @@ See `references/code-map-protocol.md` for the full protocol.
31
32
 
32
33
  ## Rules
33
34
 
34
- - Do not assume backend response shape; use the API contract.
35
+ - Do not assume backend response shape; use the API contract. Do not call an endpoint (path + method) that is not in `contracts/api/api-contract.md`. If `.cdd/conformance.json` is enabled, `cdd-kit validate --contracts` (and the gate) will fail on frontend calls that drift from the contract.
35
36
  - Follow `implementation-plan.md` for scope, non-goals, required changes, and file-level plan.
36
37
  - Do not expand scope beyond the implementation plan unless a Context Expansion Request is approved and the plan is updated.
37
38
  - Do not hard-code visual tokens when token system exists.
@@ -0,0 +1,16 @@
1
+ {
2
+ "_README": "Code-vs-contract API conformance. Run via `cdd-kit validate --contracts` (and `cdd-kit gate`). Set enabled:true to mechanically catch frontend/backend drift against contracts/api/api-contract.md. See docs/api-conformance.md.",
3
+ "enabled": false,
4
+ "apiPrefixes": ["/api"],
5
+ "sourceRoots": [],
6
+ "backendGlobsExt": [".py", ".js", ".ts", ".mjs", ".cjs", ".go", ".java", ".php"],
7
+ "frontendGlobsExt": [".js", ".jsx", ".ts", ".tsx", ".mjs", ".vue", ".svelte"],
8
+ "excludeDirs": ["node_modules", "dist", "build", ".git", ".cdd", "coverage", "vendor", "__pycache__", ".next", ".nuxt"],
9
+ "ignorePaths": ["/health", "/metrics"],
10
+ "checks": {
11
+ "backendRouteNotInContract": "warning",
12
+ "contractEndpointNotImplemented": "warning",
13
+ "frontendCallNotInContract": "error"
14
+ },
15
+ "strict": false
16
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "_README": "Mechanical risk-tier floor. A classifier (or any agent) proposes a tier in change-classification.md / tasks.yml; this policy is the safety net that prevents a high-risk surface from being silently under-classified. `cdd-kit gate` and `cdd-kit classify-check` scan change-request.md against ALL rules, and the change's git paths against the critical (maxTier 0) rules only (file paths are noisier than prose) — gate scans the STAGED change (what is about to be committed), classify-check scans the whole worktree (the in-progress change is not yet committed) — then require the declared tier to be at least as strict as the matched floor. Lower tier number = stricter. Set enabled:false to disable, or record `tier-floor-override: \"<reason>\"` in a change's tasks.yml frontmatter to bypass for one change with an audit trail.",
3
+ "enabled": true,
4
+ "schema-version": "0.1.0",
5
+ "rules": [
6
+ {
7
+ "maxTier": 0,
8
+ "label": "critical surface (auth / payments / data migration / concurrency / secrets)",
9
+ "patterns": [
10
+ "auth", "authn", "authz",
11
+ "authentication", "authorization", "authenticate", "authorize",
12
+ "authenticated", "authorized",
13
+ "login", "logout", "sign-?in", "sign-?up",
14
+ "passwords?", "passwd", "credentials?", "secrets?", "api[- ]?keys?",
15
+ "(access|api|auth|session|bearer|refresh|csrf|id|reset)[- ]?tokens?",
16
+ "jwt", "oauth", "oidc", "saml", "sessions?", "cookies?",
17
+ "payments?", "billing", "invoices?", "charges?", "refunds?", "checkout", "stripe", "paypal",
18
+ "migrations?", "migrate", "alter table", "drop table", "drop column", "schema change",
19
+ "concurrency", "race condition", "mutex", "deadlock", "transaction isolation",
20
+ "encrypt", "decrypt", "crypto", "hashing", "rbac", "permissions?", "access control",
21
+ "privileges?", "pii", "gdpr", "hipaa", "rate limit", "csrf", "xss", "sql injection"
22
+ ]
23
+ },
24
+ {
25
+ "maxTier": 2,
26
+ "label": "behavioral surface (api / data shape / queue / cache / external integration)",
27
+ "patterns": [
28
+ "endpoint", "route", "api contract", "request schema", "response schema",
29
+ "pagination", "queue", "worker", "cron", "scheduler", "webhook",
30
+ "cache", "redis", "database", "query", "index", "external service",
31
+ "third[- ]?party", "integration", "data shape", "nullable", "breaking change"
32
+ ]
33
+ }
34
+ ]
35
+ }
@@ -21,6 +21,32 @@ breaking-change-policy: deprecate-2-minors
21
21
  | method | path | auth | request schema | response schema | errors | tests |
22
22
  |---|---|---|---|---|---|---|
23
23
 
24
+ ## Schemas
25
+
26
+ <!--
27
+ Optional. Add named schemas here when request/response bodies should become
28
+ machine-typed in `cdd-kit openapi export`. Reference a schema by name in the
29
+ endpoint table's "request schema" / "response schema" cell (use `Name[]` for an
30
+ array). A schema is defined ONE of two ways — never both:
31
+
32
+ Tier A — a field table (preferred; readable, diffable):
33
+
34
+ ### ExampleRequest
35
+ | field | type | required | format | notes |
36
+ |---|---|---|---|---|
37
+ | email | string | yes | email | login identity |
38
+ | status | enum(active, disabled) | no | | lifecycle state |
39
+ | owner | ExampleUser | no | | reference another schema by name |
40
+
41
+ Tier B — a raw JSON Schema, for shapes Tier A can't express (oneOf, etc.).
42
+ The fence MUST be tagged `json-schema` (NOT `json`) or export fails fast:
43
+
44
+ ### ExampleEvent
45
+ ```json-schema
46
+ { "type": "object", "oneOf": [ { "required": ["createdAt"] }, { "required": ["deletedAt"] } ] }
47
+ ```
48
+ -->
49
+
24
50
  ## Error Format
25
51
 
26
52
  ## Compatibility Policy
@@ -0,0 +1,65 @@
1
+ #!/bin/sh
2
+ # cdd-kit PreToolUse hook (opt-in): steer agents to graph-first exploration.
3
+ #
4
+ # Prose in agent prompts ("run cdd-kit index query before reading") is a soft
5
+ # preference that loses to the model's built-in habit of reaching for Read.
6
+ # This hook turns that preference into an actual chokepoint: when an agent is
7
+ # about to Read a *source* file and a code-map exists, it reminds the agent to
8
+ # use `cdd-kit index query "<symbol>" --with-source` (which returns the code
9
+ # inline, so the Read is usually unnecessary).
10
+ #
11
+ # Default mode is ADVISORY: it prints guidance to stderr and allows the Read.
12
+ # Set CDD_GRAPH_FIRST_STRICT=1 to BLOCK the Read instead (exit 2), forcing the
13
+ # graph-first path. Contract/spec/markdown/Read of .cdd/code-map.yml itself are
14
+ # always allowed.
15
+ #
16
+ # Wire into Claude Code (~/.claude/settings.json):
17
+ #
18
+ # {
19
+ # "hooks": {
20
+ # "PreToolUse": [
21
+ # { "matcher": "Read", "command": "/path/to/hooks/pre-tool-use-graph-first.sh" }
22
+ # ]
23
+ # }
24
+ # }
25
+ #
26
+ # The hook receives the tool-call payload as JSON on stdin.
27
+
28
+ set -eu
29
+
30
+ # No code-map → nothing to steer toward; allow.
31
+ [ -f ".cdd/code-map.yml" ] || exit 0
32
+
33
+ payload="$(cat || true)"
34
+ [ -z "$payload" ] && exit 0
35
+
36
+ # Extract the Read target path.
37
+ path_value=""
38
+ if command -v jq >/dev/null 2>&1; then
39
+ path_value="$(printf '%s' "$payload" | jq -r '.tool_input.file_path // empty' 2>/dev/null || true)"
40
+ fi
41
+ if [ -z "$path_value" ]; then
42
+ path_value="$(printf '%s' "$payload" | grep -oE '"file_path"[[:space:]]*:[[:space:]]*"[^"]+"' | head -n1 | sed -E 's/.*"file_path"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')"
43
+ fi
44
+ [ -z "$path_value" ] && exit 0
45
+
46
+ # Only steer for source files; never interfere with docs/specs/contracts/config.
47
+ case "$path_value" in
48
+ *.py|*.js|*.jsx|*.mjs|*.cjs|*.ts|*.tsx|*.vue|*.svelte|*.go|*.java|*.rb|*.php) : ;;
49
+ *) exit 0 ;;
50
+ esac
51
+ # Allow reading the map itself.
52
+ case "$path_value" in
53
+ *.cdd/code-map.yml) exit 0 ;;
54
+ esac
55
+
56
+ msg="cdd-kit: prefer \`cdd-kit index query \"<symbol-or-file>\" --with-source\` (or \`cdd-kit graph query ... --with-source\`) before Read — it returns the code inline and keeps token use low. Read directly only for ranges the query did not return."
57
+
58
+ if [ "${CDD_GRAPH_FIRST_STRICT:-0}" = "1" ]; then
59
+ # Block and feed the reason back to the model.
60
+ printf '%s\n' "$msg Set CDD_GRAPH_FIRST_STRICT=0 to make this advisory only." 1>&2
61
+ exit 2
62
+ fi
63
+
64
+ printf '%s\n' "$msg" 1>&2
65
+ exit 0