@drafthq/draft 4.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,7 @@
13
13
  "name": "draft",
14
14
  "source": "./",
15
15
  "description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
16
- "version": "4.0.0",
16
+ "version": "5.0.1",
17
17
  "author": {
18
18
  "name": "Mayur Pise",
19
19
  "email": "mayur@getdraft.dev",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "draft",
3
3
  "description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
4
- "version": "4.0.0",
4
+ "version": "5.0.1",
5
5
  "author": {
6
6
  "name": "Mayur Pise",
7
7
  "email": "mayur@getdraft.dev",
@@ -2,7 +2,7 @@
2
2
  "name": "draft",
3
3
  "displayName": "Draft",
4
4
  "description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
5
- "version": "4.0.0",
5
+ "version": "5.0.1",
6
6
  "skills": "./skills/",
7
7
  "agents": "./core/agents/",
8
8
  "author": {
package/bin/README.md CHANGED
@@ -17,7 +17,7 @@ This installs the binary to the **Draft-managed location**:
17
17
  ~/.cache/draft/bin/codebase-memory-mcp
18
18
  ```
19
19
 
20
- The fetch script picks the right release archive for the host OS/arch, verifies its SHA-256 against the published `checksums.txt`, extracts it, and installs it there. Downloads pin `curl --proto '=https' --proto-redir '=https'` so a compromised redirect cannot downgrade to HTTP. `draft install claude-code` / `draft install cursor` run this automatically (best-effort, network-gated); skip it with `--no-graph`.
20
+ The fetch script picks the right release archive for the host OS/arch, verifies its SHA-256 (against hashes pinned in the script for the default version, else against the release's `checksums.txt`), extracts it, and installs it there. An existing install is kept only when its version matches the requested one, so a pin bump upgrades it. Downloads pin `curl --proto '=https' --proto-redir '=https'` so a compromised redirect cannot downgrade to HTTP. `draft install claude-code` / `draft install cursor` run this automatically (best-effort, network-gated); skip it with `--no-graph`.
21
21
 
22
22
  ## Resolution order
23
23
 
@@ -39,10 +39,10 @@ Set `DRAFT_MEMORY_DISABLE=1` to force the engine off. All graph-backed skills an
39
39
  ## How tools use it
40
40
 
41
41
  Shell helpers under `scripts/tools/` drive the engine via its CLI
42
- (`codebase-memory-mcp cli <tool> '<json>'`) and shape results into Draft's
42
+ (`codebase-memory-mcp cli <tool>`, JSON args on stdin) and shape results into Draft's
43
43
  contracts — see `hotspot-rank.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh`,
44
44
  and `verify-graph-binary.sh`. The shared wrappers (`memory_cli`,
45
- `memory_ensure_index`, `memory_project_for_repo`) live in `_lib.sh`.
45
+ `memory_ensure_index`, `memory_index_bounded`) live in `_lib.sh`.
46
46
 
47
47
  ## Snapshot artifacts
48
48
 
@@ -50,9 +50,9 @@ and `verify-graph-binary.sh`. The shared wrappers (`memory_cli`,
50
50
 
51
51
  | Artifact | Content |
52
52
  |----------|---------|
53
- | `schema.yaml` | Engine + project metadata, node/edge counts, point-of-index counts (gates graph use). |
53
+ | `schema.yaml` | Engine metadata and point-of-index node/edge counts (gates graph use). No machine- or run-specific fields, so it only changes when the engine or the codebase does. |
54
54
 
55
- Structural graph data (architecture, hotspots, module deps, service routes) is queried **live** from the `codebase-memory-mcp` engine — either via the wrapper scripts under `scripts/tools/` (`graph-callers.sh`, `graph-impact.sh`, `hotspot-rank.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh`) or directly with `codebase-memory-mcp cli <tool> '<json>'`.
55
+ Structural graph data (architecture, hotspots, module deps, service routes) is queried **live** from the `codebase-memory-mcp` engine — either via the wrapper scripts under `scripts/tools/` (`graph-callers.sh`, `graph-impact.sh`, `hotspot-rank.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh`), or, for any other read-only engine tool, `graph-query.sh --tool <name> --json '{...}'`.
56
56
 
57
57
  ## Offline / air-gapped distributions
58
58
 
@@ -68,22 +68,23 @@ Draft's differentiator depends on a binary published by a third party ([DeusData
68
68
 
69
69
  | Property | Status |
70
70
  |---|---|
71
- | Version pinned | Yes — `DEFAULT_VERSION` in `scripts/fetch-memory-engine.sh`. Bumps are deliberate commits, never floating. `CMM_VERSION` overrides per-install. |
72
- | SHA-256 verified | Yes when the release publishes `checksums.txt` and lists the archive. A **mismatch is always fatal.** |
73
- | Missing checksum | **Warns and installs by default.** Set `DRAFT_STRICT_VERIFY=1` to make an unverifiable download fatal instead. |
71
+ | Version pinned | Yes — `DEFAULT_VERSION` in `scripts/fetch-memory-engine.sh`. Bumps are deliberate commits, never floating. `CMM_VERSION` overrides per-install. An install at another version is replaced on the next fetch, and `verify-graph-binary.sh` / `graph-preflight.sh` warn when the resolved engine (e.g. one on `$PATH`) is off-pin. |
72
+ | SHA-256 verified | Yes. The default version's four platform archives are checked against hashes **pinned in the script** (a replaced release asset fails even if the release's own `checksums.txt` agrees). Other versions are checked against the release's `checksums.txt` when it lists the archive. A **mismatch is always fatal.** |
73
+ | Missing checksum | Applies only to non-default versions: **warns and installs by default.** Set `DRAFT_STRICT_VERIFY=1` to make an unverifiable download fatal instead. |
74
74
  | Signature / attestation | **No.** There is no code signing or SLSA provenance today. Verification is checksum-only. |
75
75
  | Source available | Yes — the engine is open source at [DeusData/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp). |
76
76
  | Reproducible build | Not verified by Draft. We check the archive matches the publisher's checksum, not that the checksum matches the source. |
77
77
 
78
- Be explicit about the residual risk: a checksum proves the download matches what the publisher released. It does not prove the publisher released what the source says.
78
+ Be explicit about the residual risk: a pinned checksum proves the download matches what the publisher released when Draft pinned it. It does not prove the publisher released what the source says.
79
79
 
80
80
  ### What the engine does at runtime
81
81
 
82
82
  - Reads the repository you point it at and writes a SQLite graph under its own cache.
83
- - Runs entirely locally. No API key, no telemetry endpoint, no outbound calls during indexing or querying.
84
- - Network is used exactly once, by `fetch-memory-engine.sh`, to download the release archive.
83
+ - Runs entirely locally as Draft invokes it (`cli` mode). No API key, no telemetry endpoint, no outbound calls during indexing or querying — verified under `strace` for `index_repository` and queries on 0.9.0 (zero `connect()` calls, no spawned `curl`).
84
+ - The binary does embed an update checker (`api.github.com/.../releases/latest`) and `update` / `install` subcommands for its own MCP-server workflow. Draft never runs the server or those subcommands.
85
+ - Draft uses the network exactly once, in `fetch-memory-engine.sh`, to download the release archive.
85
86
 
86
- Draft invokes it only through `codebase-memory-mcp cli <tool> '<json>'` (see `_lib.sh:memory_cli`). It is never given credentials and never writes into your source tree.
87
+ Draft invokes it only through `codebase-memory-mcp cli <tool>` with JSON args on stdin (see `_lib.sh:memory_cli`). It is never given credentials and never writes into your source tree.
87
88
 
88
89
  ### If you cannot run an unvetted binary
89
90
 
@@ -97,7 +98,7 @@ Three supported postures, in increasing strictness:
97
98
 
98
99
  The dependency is bounded by design, which is what makes this survivable:
99
100
 
100
- - **The interface is small.** Draft consumes a documented CLI (`cli <tool> '<json>'`), not a library. The entire coupling lives in `scripts/tools/_lib.sh` (`memory_cli`, `memory_ensure_index`, `memory_project_for_repo`) and `_graph_queries.sh`. Swapping engines means reimplementing those, not rewriting skills.
101
+ - **The interface is small.** Draft consumes a documented CLI (`cli <tool>`, JSON on stdin), not a library. The entire coupling lives in `scripts/tools/_lib.sh` (`memory_cli`, `memory_ensure_index`, `memory_index_bounded`) and `_graph_queries.sh`. Swapping engines means reimplementing those, not rewriting skills.
101
102
  - **Skills never call the engine directly.** They call `graph-*.sh` wrappers, all of which already fail loud with `source: "unavailable"`. An engine that disappears degrades the product; it does not break it.
102
103
  - **Pinning buys time.** A stalled upstream keeps working at the pinned version; only new language support would be lost.
103
104
  - **The graph contract is replaceable.** The queries are ordinary Cypher-shaped structural lookups (callers, callees, fan-in, cycles, routes) over a tree-sitter/LSP index — reproducible on another indexer.
@@ -24,7 +24,7 @@ Any skill that mutates `architecture.md` should execute this subroutine afterwar
24
24
  | Input | Path | Description |
25
25
  |-------|------|-------------|
26
26
  | architecture.md | `draft/architecture.md` | Comprehensive human-readable engineering reference (source of truth) |
27
- | schema.yaml | `draft/graph/schema.yaml` | Graph metrics for tier computation (optional — skip if absent) |
27
+ | Architecture view | `scripts/tools/graph-arch.sh --repo .` | Live graph metrics for tier computation (optional — skip when `draft/graph/schema.yaml` is absent) |
28
28
 
29
29
  ## Outputs
30
30
 
@@ -37,11 +37,12 @@ Any skill that mutates `architecture.md` should execute this subroutine afterwar
37
37
 
38
38
  ## Target Size
39
39
 
40
- Compute tier from `draft/graph/schema.yaml` after graph build:
40
+ Compute tier from the live architecture view (the same metrics as `/draft:init` Step 1.4.5 — `schema.yaml` carries only counts, not these):
41
41
 
42
- M = stats.modules
43
- F = stats.go_functions + stats.py_functions
44
- P = stats.proto_rpcs
42
+ ARCH=$("$DRAFT_TOOLS/graph-arch.sh" --repo .) (DRAFT_TOOLS: resolver below)
43
+ M = $ARCH | jq '.packages | length' (modules)
44
+ F = $ARCH | jq '[.node_labels[] | select(.label=="Function" or .label=="Method") | .count] | add // 0' (functions + methods)
45
+ P = $ARCH | jq '.routes | length' (routes / RPCs)
45
46
 
46
47
  | Tier | Label | Condition | Budget |
47
48
  |------|--------|----------------------------------------|---------------|
@@ -51,7 +52,7 @@ Compute tier from `draft/graph/schema.yaml` after graph build:
51
52
  | 4 | large | M≤100 AND F≤5000 AND P≤500 | 400–600 lines |
52
53
  | 5 | XL | M>100 OR F>5000 OR P>500 | 600–900 lines |
53
54
 
54
- If `schema.yaml` does not exist: default to tier 2 (180–280 lines).
55
+ If `schema.yaml` does not exist (no graph) or `graph-arch.sh` reports `source: unavailable`: default to tier 2 (180–280 lines).
55
56
 
56
57
  - Below tier minimum: incomplete condensation — ensure all sections are represented
57
58
  - Above tier maximum: insufficient compression — apply prioritization rules below
@@ -171,7 +172,7 @@ Before writing `draft/.ai-context.md`, verify:
171
172
  - [ ] No references to `architecture.md` (file must be self-contained)
172
173
  - [ ] All invariants from architecture.md are preserved
173
174
  - [ ] Extension cookbooks are complete (an agent can follow them without other files)
174
- - [ ] Output is within tier budget bounds (compute from schema.yaml or default tier 2)
175
+ - [ ] Output is within tier budget bounds (compute from `graph-arch.sh` or default tier 2)
175
176
  - [ ] GRAPH:HOTSPOTS present (or note "No hotspot data available" if graph absent)
176
177
  - [ ] GRAPH:CYCLES present ("None ✓" or cycle list; or note if graph absent)
177
178
  - [ ] GRAPH:MODULE-HOTSPOTS present for tier ≥ 3 (or note if no hotspot data)
@@ -71,7 +71,7 @@ If `draft/graph/schema.yaml` exists, the project has automated graph analysis da
71
71
 
72
72
  | File | Purpose | Content |
73
73
  |------|---------|---------|
74
- | `draft/graph/schema.yaml` | Gate marker (engine + project metadata + point-of-index counts); presence gates graph use | YAML, ~15 lines |
74
+ | `draft/graph/schema.yaml` | Gate marker (engine metadata + point-of-index counts); presence gates graph use | YAML, ~10 lines |
75
75
 
76
76
  Note: `.ai-context.md` embeds a condensed graph summary (`GRAPH:MODULES`, `GRAPH:HOTSPOTS`, `GRAPH:CYCLES`) for first-pass structural ground truth. Deep structural data is queried live from the engine (see Live structural queries below).
77
77
 
@@ -115,7 +115,7 @@ DRAFT_TOOLS="${DRAFT_PLUGIN_ROOT:-$(cat ~/.cache/draft/plugin-root 2>/dev/null)}
115
115
  | `bash "$DRAFT_TOOLS/graph-query.sh" (--cypher STR \| --tool NAME --json '{...}')` | generic read-only passthrough | `{source:"unavailable"}`, exit 2 |
116
116
  | `bash "$DRAFT_TOOLS/graph-traces.sh" ingest --file F --experimental` | runtime traces (experimental write) | `{source:"unavailable"}`, exit 2 |
117
117
 
118
- For lower-level modes, call the engine directly: `codebase-memory-mcp cli <tool> '<json>'` (see the tool list in [bin/README.md](../../bin/README.md)).
118
+ For an engine tool without a dedicated wrapper, use the read-only passthrough `graph-query.sh --repo . --tool <name> --json '{...}'` (project injected, write tools refused, `source:"unavailable"` on failure). Never call the engine binary directly — that skips engine resolution, the index refresh before each query, and the fail-loud contract.
119
119
 
120
120
  ### Capability wrappers & dialect limits (graph-tooling-v2)
121
121
 
@@ -123,8 +123,9 @@ All Cypher lives in `scripts/tools/_graph_queries.sh` (the single source of quer
123
123
  truth). Wrappers are thin arg-parse → builder → fail-loud JSON. Three contracts
124
124
  matter when consuming them:
125
125
 
126
- **Fail-loud status.** Symbol-scoped wrappers (`graph-callers`, `graph-snippet`,
127
- `graph-tests --symbol`, `graph-hierarchy --symbol/--derived`, `graph-errors`)
126
+ **Fail-loud status.** Symbol-scoped wrappers (`graph-callers`, `graph-impact`,
127
+ `graph-snippet`, `graph-tests --symbol`, `graph-hierarchy --symbol/--derived`,
128
+ `graph-errors`)
128
129
  emit a `status` field that distinguishes the three real outcomes — never read a
129
130
  bare `[]` as a confirmed true negative:
130
131
 
@@ -137,7 +138,7 @@ bare `[]` as a confirmed true negative:
137
138
 
138
139
  **Shapeless JSON is unavailable.** `gq_run` requires `has("rows") and (.rows|type=="array")`. A bare `{}` (or any object without a `.rows` array) is not a measured empty result — wrappers emit `source:"unavailable"` and a non-zero exit. Do not read `{}` as "no callers / no cycles / no edges". `graph-impact`, `graph-callers`, and `mermaid-from-graph` also require their tool-shaped object; a failed snapshot refresh does not rewrite `schema.yaml`.
139
140
 
140
- **Verified engine param shapes** (engine v0.8.x — the runtime source of truth is
141
+ **Verified engine param shapes** (engine v0.9.0 — the runtime source of truth is
141
142
  `get_graph_schema`; do not hardcode a property set):
142
143
 
143
144
  ```bash
@@ -150,13 +151,18 @@ get_graph_schema '{"project":P}' # → {node_labels:[{label,count,properties}
150
151
 
151
152
  **Cypher dialect — keep queries inside the SAFE set:**
152
153
 
153
- - ✅ SAFE: fixed-length patterns, single/multi-hop explicit patterns, `=`, `<`,
154
- `STARTS WITH`, `NOT x STARTS WITH`, `AND`, `OR`, relationship-type alternation
155
- `[:A|B]`, simple `count(x)`.
156
- - ❌ UNSAFE (rejected or silently empty): `coalesce()`, `<>` / `!=` / `<=` / `>=`,
157
- `NOT EXISTS(...)`, `NOT (pattern)`, `WITH`-grouping aggregation, multi-pattern
158
- joins. `graph-query.sh --cypher` returns the engine's raw error, not a silent
154
+ - ✅ SAFE: `=`, `<>`/`!=`, `<`, `>`, `<=`, `>=` against a literal; `STARTS WITH`,
155
+ `NOT x STARTS WITH`, `AND`, `OR`; explicit and variable-length patterns
156
+ (`[:R*1..3]`, fixed depth `[:R*2..2]`); relationship-type alternation `[:A|B]`;
157
+ `coalesce()`; `DISTINCT`; `count(x)`, `count(DISTINCT x)`; `WITH`-grouping
158
+ aggregation.
159
+ - UNSAFE: comparing one property against another (`a.x < b.x`), `NOT EXISTS(...)`,
160
+ `NOT (pattern)`, path variables (`p=(...)`, `length(p)`) — all rejected; and
161
+ multi-pattern joins (`MATCH (a)…, (b)…`), which parse but ignore `RETURN` and
162
+ `LIMIT`. `graph-query.sh --cypher` returns the engine's raw error, not a silent
159
163
  empty — but the builders never emit these forms.
164
+ - ⚠️ `LIMIT` applies before `DISTINCT`: `RETURN DISTINCT … LIMIT n` can return
165
+ fewer than n rows while more exist. Judge truncation on raw rows.
160
166
 
161
167
  **Caveats consumers must respect:**
162
168
 
@@ -191,15 +197,15 @@ The only committed file is the gate marker:
191
197
 
192
198
  | File | Role |
193
199
  |------|------|
194
- | `draft/graph/schema.yaml` | Engine + project metadata and point-of-index counts (provenance, not authoritative). Carries **no graph data**. Its presence is the **gate** (see Pre-Check) — it signals the engine is wired for this repo. Written by `scripts/tools/graph-snapshot.sh`. |
200
+ | `draft/graph/schema.yaml` | Engine metadata and point-of-index counts (provenance, not authoritative). Carries **no graph data**. Its presence is the **gate** (see Pre-Check) — it signals the engine is wired for this repo. Written by `scripts/tools/graph-snapshot.sh`. |
195
201
 
196
- All structural data is obtained live by shelling out to the engine — either through the query-tool wrappers under `scripts/tools/` or directly via `codebase-memory-mcp cli <tool> '<json>'`. The shell tools auto-index the repo into the engine's own cache on demand, so no committed files are required.
202
+ All structural data is obtained live through the query-tool wrappers under `scripts/tools/`; `graph-query.sh --tool` covers every read-only engine tool that has no dedicated wrapper. The wrappers refresh the repo's index in the engine's own cache before each query, so no committed files are required.
197
203
 
198
204
  ### How skills query (engine is the interface; jq is optional)
199
205
 
200
- - **The engine is the query.** `codebase-memory-mcp cli <tool> '<json>'` (and the wrappers that call it) is how you ask it takes JSON args and returns JSON. There is no other query surface.
206
+ - **The engine is the query; the wrappers are the interface.** Every wrapper drives the engine's CLI and returns JSON. There is no other query surface.
201
207
  - **Prefer the wrappers — they resolve the engine for you.** `graph-arch.sh` (architecture view: packages/routes/layers/hotspots), `graph-callers.sh`, `hotspot-rank.sh`, `graph-impact.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh` return already-shaped JSON. The engine binary is usually **not on `$PATH`** (it lives under `~/.cache/draft/bin/`); the wrappers locate it via `_lib.sh:find_memory_bin`, so a skill using a wrapper needs no resolution step.
202
- - **Raw `codebase-memory-mcp cli` requires resolving the binary first** (it is not on `$PATH`): `CM="${DRAFT_MEMORY_BIN:-$HOME/.cache/draft/bin/codebase-memory-mcp}"; "$CM" cli <tool> '<json>'`. Reach for this only for tools without a wrapper (`search_graph`, `search_code`, `trace_path`).
208
+ - **Tools without a dedicated wrapper go through `graph-query.sh`:** `"$DRAFT_TOOLS/graph-query.sh" --repo . --tool search_code --json '{"pattern":"..."}'` (likewise `trace_path`, `get_graph_schema`, `index_status`). `search_graph` has its own wrapper, `graph-search.sh`.
203
209
  - **`jq` is not a query tool — it only trims output.** Reach for it solely to slice a *large* response (chiefly the `get_architecture` blob) down to the field you need, for token economy. The agent can read raw JSON directly; jq is an optimization, not a requirement. Don't pipe wrapper output through jq unless you genuinely need a sub-field.
204
210
 
205
211
  The engine uses a **unified, language-agnostic** node model — `Function`, `Method`, `Class`, `Module`, `File`, `Folder`, `Route`, `Section`, `Variable` (language is inferred from file extension) — and edges `CALLS`, `DEFINES`, `CONTAINS_FILE`, `IMPORTS`, `HTTP_CALLS`, `FILE_CHANGES_WITH`, `SEMANTICALLY_RELATED`, `SIMILAR_TO`. Each node carries `file_path` + `start_line`/`end_line` and rich `properties` (complexity, signature, parent_class), and the engine exposes full-text (`search_code`) and semantic search — none of which a committed snapshot reproduced.
@@ -219,11 +225,11 @@ Output: `{symbol, callers[{name, file}], source}`. Use when enumerating call sit
219
225
  ### Impact — blast radius of a file or symbol
220
226
 
221
227
  ```bash
222
- "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path> # changed-file impact (working-tree diff)
223
- "$DRAFT_TOOLS/graph-impact.sh" --repo . --symbol <name> # transitive callers of a function
228
+ "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path> # dependents of a file: its symbols' callers + its importers
229
+ "$DRAFT_TOOLS/graph-impact.sh" --repo . --symbol <name> # dependents (transitive callers) of a function
224
230
  ```
225
231
 
226
- Output: `{target, kind, impacted[{name, file, hop}], source}`. Use when sizing risk before modifying a file or symbol, especially high-fan-in hotspots.
232
+ Output: `{target, kind, impacted[{name, file, qualified, hop}], downstream_files, affected_modules, max_depth, by_category{code,test}, status, truncated, source}`. `impacted` lists each dependent once at its nearest hop (default depth 3), capped at 200 with `truncated:true`; the aggregates always cover the full set. `status` is `ok`, `no-edges` (target known, nothing depends on it), or `no-match` (target unknown to the graph — check the path). Use when sizing risk before modifying a file or symbol, especially high-fan-in hotspots.
227
233
 
228
234
  ### Hotspots — fan-in ranking
229
235
 
@@ -334,7 +340,7 @@ Unlocks any edge type or node property without a purpose-built wrapper. Write ve
334
340
  "$DRAFT_TOOLS/graph-snapshot.sh" --repo .
335
341
  ```
336
342
 
337
- Indexes the repo into the engine and writes the `draft/graph/schema.yaml` gate marker (now including the `detect_changes` delta: `changed_files`/`impacted_symbols`). It writes **no** graph data. Run during `/draft:init` and `/draft:graph`, or whenever the index should be refreshed.
343
+ Indexes the repo into the engine and writes the `draft/graph/schema.yaml` gate marker, and prints the `detect_changes` delta (`changed_files`/`impacted_symbols`) without committing it. It writes **no** graph data. Run during `/draft:init` and `/draft:graph`, or whenever the index should be refreshed.
338
344
 
339
345
  ## Finding the Engine (Resolution + Usage Report)
340
346
 
@@ -375,4 +381,4 @@ The engine indexes C/C++, Go, Python, TypeScript/JS, and more (tree-sitter, 159
375
381
  | No engine resolvable (or `DRAFT_MEMORY_DISABLE=1`) | Skip graph indexing in init; all skills proceed without graph data; tools emit `source: unavailable` |
376
382
  | Engine present but a query fails, or returns shapeless `{}` | Treat as unavailable — never as a true-negative empty result; skills proceed without graph data |
377
383
  | `draft/graph/schema.yaml` exists | Engine is wired — use live query tools as needed during the run |
378
- | Engine index out of date | The engine indexes incrementally (content-based, git-aware) on each query, so it self-freshens. Re-run `graph-snapshot.sh` (or init) to force a reindex and refresh the marker. |
384
+ | Engine index out of date | Every wrapper re-indexes incrementally (content-based, git-aware) before it queries — ~0.1 s on an unchanged repo — so live answers track the working tree, including the run's own edits. `graph-snapshot.sh` (or init) also refreshes the `schema.yaml` marker. |
@@ -29,7 +29,7 @@ graph:
29
29
  cpp: "{high}"
30
30
  go: "{approximate | high}"
31
31
  stats:
32
- modules: "{N from schema.yaml}"
32
+ modules: "{N from engine: get_architecture .packages}"
33
33
  edges: "{total_edges from engine: get_architecture .edges}"
34
34
  hotspots: "{N}"
35
35
  notes: "{explicit fidelity summary from engine: get_architecture .languages/.packages}"
@@ -6025,7 +6025,7 @@ After a phase passes review, refresh `metadata.json.impact` so future tracks can
6025
6025
  "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path>
6026
6026
  ```
6027
6027
 
6028
- Aggregate across all files: `downstream_files` = total unique downstream files (deduped), `downstream_modules` = union of `affected_modules`, `max_depth` = max across queries, `by_category` = sum of each query's `by_category`. If the graph is absent, leave these fields as zeros / empty arrays — the snapshot still records the directly-touched files.
6028
+ Aggregate across all files: `downstream_files` = count of the union of each query's `downstream_files` array, `downstream_modules` = union of `affected_modules`, `max_depth` = max across queries, `by_category` = sum of each query's `by_category`. If the graph is absent, leave these fields as zeros / empty arrays — the snapshot still records the directly-touched files.
6029
6029
 
6030
6030
  3. **Write metadata.json** with the populated `impact` block and `computed_at` set to the current timestamp.
6031
6031
 
@@ -17030,7 +17030,7 @@ If `draft/graph/schema.yaml` exists, the project has automated graph analysis da
17030
17030
 
17031
17031
  | File | Purpose | Content |
17032
17032
  |------|---------|---------|
17033
- | `draft/graph/schema.yaml` | Gate marker (engine + project metadata + point-of-index counts); presence gates graph use | YAML, ~15 lines |
17033
+ | `draft/graph/schema.yaml` | Gate marker (engine metadata + point-of-index counts); presence gates graph use | YAML, ~10 lines |
17034
17034
 
17035
17035
  Note: `.ai-context.md` embeds a condensed graph summary (`GRAPH:MODULES`, `GRAPH:HOTSPOTS`, `GRAPH:CYCLES`) for first-pass structural ground truth. Deep structural data is queried live from the engine (see Live structural queries below).
17036
17036
 
@@ -17558,7 +17558,7 @@ Any skill that mutates `architecture.md` should execute this subroutine afterwar
17558
17558
  | Input | Path | Description |
17559
17559
  |-------|------|-------------|
17560
17560
  | architecture.md | `draft/architecture.md` | Comprehensive human-readable engineering reference (source of truth) |
17561
- | schema.yaml | `draft/graph/schema.yaml` | Graph metrics for tier computation (optional — skip if absent) |
17561
+ | Architecture view | `scripts/tools/graph-arch.sh --repo .` | Live graph metrics for tier computation (optional — skip when `draft/graph/schema.yaml` is absent) |
17562
17562
 
17563
17563
  ## Outputs
17564
17564
 
@@ -17571,11 +17571,12 @@ Any skill that mutates `architecture.md` should execute this subroutine afterwar
17571
17571
 
17572
17572
  ## Target Size
17573
17573
 
17574
- Compute tier from `draft/graph/schema.yaml` after graph build:
17574
+ Compute tier from the live architecture view (the same metrics as `draft init` Step 1.4.5 — `schema.yaml` carries only counts, not these):
17575
17575
 
17576
- M = stats.modules
17577
- F = stats.go_functions + stats.py_functions
17578
- P = stats.proto_rpcs
17576
+ ARCH=$("$DRAFT_TOOLS/graph-arch.sh" --repo .) (DRAFT_TOOLS: resolver below)
17577
+ M = $ARCH | jq '.packages | length' (modules)
17578
+ F = $ARCH | jq '[.node_labels[] | select(.label=="Function" or .label=="Method") | .count] | add // 0' (functions + methods)
17579
+ P = $ARCH | jq '.routes | length' (routes / RPCs)
17579
17580
 
17580
17581
  | Tier | Label | Condition | Budget |
17581
17582
  |------|--------|----------------------------------------|---------------|
@@ -17585,7 +17586,7 @@ Compute tier from `draft/graph/schema.yaml` after graph build:
17585
17586
  | 4 | large | M≤100 AND F≤5000 AND P≤500 | 400–600 lines |
17586
17587
  | 5 | XL | M>100 OR F>5000 OR P>500 | 600–900 lines |
17587
17588
 
17588
- If `schema.yaml` does not exist: default to tier 2 (180–280 lines).
17589
+ If `schema.yaml` does not exist (no graph) or `graph-arch.sh` reports `source: unavailable`: default to tier 2 (180–280 lines).
17589
17590
 
17590
17591
  - Below tier minimum: incomplete condensation — ensure all sections are represented
17591
17592
  - Above tier maximum: insufficient compression — apply prioritization rules below
@@ -17705,7 +17706,7 @@ Before writing `draft/.ai-context.md`, verify:
17705
17706
  - [ ] No references to `architecture.md` (file must be self-contained)
17706
17707
  - [ ] All invariants from architecture.md are preserved
17707
17708
  - [ ] Extension cookbooks are complete (an agent can follow them without other files)
17708
- - [ ] Output is within tier budget bounds (compute from schema.yaml or default tier 2)
17709
+ - [ ] Output is within tier budget bounds (compute from `graph-arch.sh` or default tier 2)
17709
17710
  - [ ] GRAPH:HOTSPOTS present (or note "No hotspot data available" if graph absent)
17710
17711
  - [ ] GRAPH:CYCLES present ("None ✓" or cycle list; or note if graph absent)
17711
17712
  - [ ] GRAPH:MODULE-HOTSPOTS present for tier ≥ 3 (or note if no hotspot data)
@@ -18216,7 +18217,7 @@ DRAFT_TOOLS="${DRAFT_PLUGIN_ROOT:-$(cat ~/.cache/draft/plugin-root 2>/dev/null)}
18216
18217
  | `bash "$DRAFT_TOOLS/graph-query.sh" (--cypher STR \| --tool NAME --json '{...}')` | generic read-only passthrough | `{source:"unavailable"}`, exit 2 |
18217
18218
  | `bash "$DRAFT_TOOLS/graph-traces.sh" ingest --file F --experimental` | runtime traces (experimental write) | `{source:"unavailable"}`, exit 2 |
18218
18219
 
18219
- For lower-level modes, call the engine directly: `codebase-memory-mcp cli <tool> '<json>'` (see the tool list in [bin/README.md](../../bin/README.md)).
18220
+ For an engine tool without a dedicated wrapper, use the read-only passthrough `graph-query.sh --repo . --tool <name> --json '{...}'` (project injected, write tools refused, `source:"unavailable"` on failure). Never call the engine binary directly — that skips engine resolution, the index refresh before each query, and the fail-loud contract.
18220
18221
 
18221
18222
  ### Capability wrappers & dialect limits (graph-tooling-v2)
18222
18223
 
@@ -18224,8 +18225,9 @@ All Cypher lives in `scripts/tools/_graph_queries.sh` (the single source of quer
18224
18225
  truth). Wrappers are thin arg-parse → builder → fail-loud JSON. Three contracts
18225
18226
  matter when consuming them:
18226
18227
 
18227
- **Fail-loud status.** Symbol-scoped wrappers (`graph-callers`, `graph-snippet`,
18228
- `graph-tests --symbol`, `graph-hierarchy --symbol/--derived`, `graph-errors`)
18228
+ **Fail-loud status.** Symbol-scoped wrappers (`graph-callers`, `graph-impact`,
18229
+ `graph-snippet`, `graph-tests --symbol`, `graph-hierarchy --symbol/--derived`,
18230
+ `graph-errors`)
18229
18231
  emit a `status` field that distinguishes the three real outcomes — never read a
18230
18232
  bare `[]` as a confirmed true negative:
18231
18233
 
@@ -18238,7 +18240,7 @@ bare `[]` as a confirmed true negative:
18238
18240
 
18239
18241
  **Shapeless JSON is unavailable.** `gq_run` requires `has("rows") and (.rows|type=="array")`. A bare `{}` (or any object without a `.rows` array) is not a measured empty result — wrappers emit `source:"unavailable"` and a non-zero exit. Do not read `{}` as "no callers / no cycles / no edges". `graph-impact`, `graph-callers`, and `mermaid-from-graph` also require their tool-shaped object; a failed snapshot refresh does not rewrite `schema.yaml`.
18240
18242
 
18241
- **Verified engine param shapes** (engine v0.8.x — the runtime source of truth is
18243
+ **Verified engine param shapes** (engine v0.9.0 — the runtime source of truth is
18242
18244
  `get_graph_schema`; do not hardcode a property set):
18243
18245
 
18244
18246
  ```bash
@@ -18251,13 +18253,18 @@ get_graph_schema '{"project":P}' # → {node_labels:[{label,count,properties}
18251
18253
 
18252
18254
  **Cypher dialect — keep queries inside the SAFE set:**
18253
18255
 
18254
- - ✅ SAFE: fixed-length patterns, single/multi-hop explicit patterns, `=`, `<`,
18255
- `STARTS WITH`, `NOT x STARTS WITH`, `AND`, `OR`, relationship-type alternation
18256
- `[:A|B]`, simple `count(x)`.
18257
- - ❌ UNSAFE (rejected or silently empty): `coalesce()`, `<>` / `!=` / `<=` / `>=`,
18258
- `NOT EXISTS(...)`, `NOT (pattern)`, `WITH`-grouping aggregation, multi-pattern
18259
- joins. `graph-query.sh --cypher` returns the engine's raw error, not a silent
18256
+ - ✅ SAFE: `=`, `<>`/`!=`, `<`, `>`, `<=`, `>=` against a literal; `STARTS WITH`,
18257
+ `NOT x STARTS WITH`, `AND`, `OR`; explicit and variable-length patterns
18258
+ (`[:R*1..3]`, fixed depth `[:R*2..2]`); relationship-type alternation `[:A|B]`;
18259
+ `coalesce()`; `DISTINCT`; `count(x)`, `count(DISTINCT x)`; `WITH`-grouping
18260
+ aggregation.
18261
+ - UNSAFE: comparing one property against another (`a.x < b.x`), `NOT EXISTS(...)`,
18262
+ `NOT (pattern)`, path variables (`p=(...)`, `length(p)`) — all rejected; and
18263
+ multi-pattern joins (`MATCH (a)…, (b)…`), which parse but ignore `RETURN` and
18264
+ `LIMIT`. `graph-query.sh --cypher` returns the engine's raw error, not a silent
18260
18265
  empty — but the builders never emit these forms.
18266
+ - ⚠️ `LIMIT` applies before `DISTINCT`: `RETURN DISTINCT … LIMIT n` can return
18267
+ fewer than n rows while more exist. Judge truncation on raw rows.
18261
18268
 
18262
18269
  **Caveats consumers must respect:**
18263
18270
 
@@ -18292,15 +18299,15 @@ The only committed file is the gate marker:
18292
18299
 
18293
18300
  | File | Role |
18294
18301
  |------|------|
18295
- | `draft/graph/schema.yaml` | Engine + project metadata and point-of-index counts (provenance, not authoritative). Carries **no graph data**. Its presence is the **gate** (see Pre-Check) — it signals the engine is wired for this repo. Written by `scripts/tools/graph-snapshot.sh`. |
18302
+ | `draft/graph/schema.yaml` | Engine metadata and point-of-index counts (provenance, not authoritative). Carries **no graph data**. Its presence is the **gate** (see Pre-Check) — it signals the engine is wired for this repo. Written by `scripts/tools/graph-snapshot.sh`. |
18296
18303
 
18297
- All structural data is obtained live by shelling out to the engine — either through the query-tool wrappers under `scripts/tools/` or directly via `codebase-memory-mcp cli <tool> '<json>'`. The shell tools auto-index the repo into the engine's own cache on demand, so no committed files are required.
18304
+ All structural data is obtained live through the query-tool wrappers under `scripts/tools/`; `graph-query.sh --tool` covers every read-only engine tool that has no dedicated wrapper. The wrappers refresh the repo's index in the engine's own cache before each query, so no committed files are required.
18298
18305
 
18299
18306
  ### How skills query (engine is the interface; jq is optional)
18300
18307
 
18301
- - **The engine is the query.** `codebase-memory-mcp cli <tool> '<json>'` (and the wrappers that call it) is how you ask it takes JSON args and returns JSON. There is no other query surface.
18308
+ - **The engine is the query; the wrappers are the interface.** Every wrapper drives the engine's CLI and returns JSON. There is no other query surface.
18302
18309
  - **Prefer the wrappers — they resolve the engine for you.** `graph-arch.sh` (architecture view: packages/routes/layers/hotspots), `graph-callers.sh`, `hotspot-rank.sh`, `graph-impact.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh` return already-shaped JSON. The engine binary is usually **not on `$PATH`** (it lives under `~/.cache/draft/bin/`); the wrappers locate it via `_lib.sh:find_memory_bin`, so a skill using a wrapper needs no resolution step.
18303
- - **Raw `codebase-memory-mcp cli` requires resolving the binary first** (it is not on `$PATH`): `CM="${DRAFT_MEMORY_BIN:-$HOME/.cache/draft/bin/codebase-memory-mcp}"; "$CM" cli <tool> '<json>'`. Reach for this only for tools without a wrapper (`search_graph`, `search_code`, `trace_path`).
18310
+ - **Tools without a dedicated wrapper go through `graph-query.sh`:** `"$DRAFT_TOOLS/graph-query.sh" --repo . --tool search_code --json '{"pattern":"..."}'` (likewise `trace_path`, `get_graph_schema`, `index_status`). `search_graph` has its own wrapper, `graph-search.sh`.
18304
18311
  - **`jq` is not a query tool — it only trims output.** Reach for it solely to slice a *large* response (chiefly the `get_architecture` blob) down to the field you need, for token economy. The agent can read raw JSON directly; jq is an optimization, not a requirement. Don't pipe wrapper output through jq unless you genuinely need a sub-field.
18305
18312
 
18306
18313
  The engine uses a **unified, language-agnostic** node model — `Function`, `Method`, `Class`, `Module`, `File`, `Folder`, `Route`, `Section`, `Variable` (language is inferred from file extension) — and edges `CALLS`, `DEFINES`, `CONTAINS_FILE`, `IMPORTS`, `HTTP_CALLS`, `FILE_CHANGES_WITH`, `SEMANTICALLY_RELATED`, `SIMILAR_TO`. Each node carries `file_path` + `start_line`/`end_line` and rich `properties` (complexity, signature, parent_class), and the engine exposes full-text (`search_code`) and semantic search — none of which a committed snapshot reproduced.
@@ -18320,11 +18327,11 @@ Output: `{symbol, callers[{name, file}], source}`. Use when enumerating call sit
18320
18327
  ### Impact — blast radius of a file or symbol
18321
18328
 
18322
18329
  ```bash
18323
- "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path> # changed-file impact (working-tree diff)
18324
- "$DRAFT_TOOLS/graph-impact.sh" --repo . --symbol <name> # transitive callers of a function
18330
+ "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path> # dependents of a file: its symbols' callers + its importers
18331
+ "$DRAFT_TOOLS/graph-impact.sh" --repo . --symbol <name> # dependents (transitive callers) of a function
18325
18332
  ```
18326
18333
 
18327
- Output: `{target, kind, impacted[{name, file, hop}], source}`. Use when sizing risk before modifying a file or symbol, especially high-fan-in hotspots.
18334
+ Output: `{target, kind, impacted[{name, file, qualified, hop}], downstream_files, affected_modules, max_depth, by_category{code,test}, status, truncated, source}`. `impacted` lists each dependent once at its nearest hop (default depth 3), capped at 200 with `truncated:true`; the aggregates always cover the full set. `status` is `ok`, `no-edges` (target known, nothing depends on it), or `no-match` (target unknown to the graph — check the path). Use when sizing risk before modifying a file or symbol, especially high-fan-in hotspots.
18328
18335
 
18329
18336
  ### Hotspots — fan-in ranking
18330
18337
 
@@ -18435,7 +18442,7 @@ Unlocks any edge type or node property without a purpose-built wrapper. Write ve
18435
18442
  "$DRAFT_TOOLS/graph-snapshot.sh" --repo .
18436
18443
  ```
18437
18444
 
18438
- Indexes the repo into the engine and writes the `draft/graph/schema.yaml` gate marker (now including the `detect_changes` delta: `changed_files`/`impacted_symbols`). It writes **no** graph data. Run during `draft init` and `draft graph`, or whenever the index should be refreshed.
18445
+ Indexes the repo into the engine and writes the `draft/graph/schema.yaml` gate marker, and prints the `detect_changes` delta (`changed_files`/`impacted_symbols`) without committing it. It writes **no** graph data. Run during `draft init` and `draft graph`, or whenever the index should be refreshed.
18439
18446
 
18440
18447
  ## Finding the Engine (Resolution + Usage Report)
18441
18448
 
@@ -18476,7 +18483,7 @@ The engine indexes C/C++, Go, Python, TypeScript/JS, and more (tree-sitter, 159
18476
18483
  | No engine resolvable (or `DRAFT_MEMORY_DISABLE=1`) | Skip graph indexing in init; all skills proceed without graph data; tools emit `source: unavailable` |
18477
18484
  | Engine present but a query fails, or returns shapeless `{}` | Treat as unavailable — never as a true-negative empty result; skills proceed without graph data |
18478
18485
  | `draft/graph/schema.yaml` exists | Engine is wired — use live query tools as needed during the run |
18479
- | Engine index out of date | The engine indexes incrementally (content-based, git-aware) on each query, so it self-freshens. Re-run `graph-snapshot.sh` (or init) to force a reindex and refresh the marker. |
18486
+ | Engine index out of date | Every wrapper re-indexes incrementally (content-based, git-aware) before it queries — ~0.1 s on an unchanged repo — so live answers track the working tree, including the run's own edits. `graph-snapshot.sh` (or init) also refreshes the `schema.yaml` marker. |
18480
18487
 
18481
18488
  </core-file>
18482
18489
 
@@ -20433,7 +20440,7 @@ graph:
20433
20440
  cpp: "{high}"
20434
20441
  go: "{approximate | high}"
20435
20442
  stats:
20436
- modules: "{N from schema.yaml}"
20443
+ modules: "{N from engine: get_architecture .packages}"
20437
20444
  edges: "{total_edges from engine: get_architecture .edges}"
20438
20445
  hotspots: "{N}"
20439
20446
  notes: "{explicit fidelity summary from engine: get_architecture .languages/.packages}"