@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/bin/README.md +14 -13
- package/core/shared/condensation.md +8 -7
- package/core/shared/draft-context-loading.md +1 -1
- package/core/shared/graph-query.md +25 -19
- package/core/templates/architecture.md +1 -1
- package/integrations/agents/AGENTS.md +36 -29
- package/integrations/copilot/.github/copilot-instructions.md +36 -29
- package/package.json +2 -2
- package/scripts/fetch-memory-engine.sh +43 -10
- package/scripts/tools/_graph_queries.sh +25 -11
- package/scripts/tools/_lib.sh +38 -29
- package/scripts/tools/cycle-detect.sh +9 -5
- package/scripts/tools/graph-impact.sh +63 -37
- package/scripts/tools/graph-init.sh +5 -10
- package/scripts/tools/graph-preflight.sh +6 -3
- package/scripts/tools/graph-query.sh +8 -7
- package/scripts/tools/graph-snapshot.sh +13 -28
- package/scripts/tools/graph-traces.sh +2 -2
- package/scripts/tools/hotspot-rank.sh +10 -1
- package/scripts/tools/verify-graph-binary.sh +14 -2
- package/skills/implement/SKILL.md +1 -1
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# graph-impact.sh — blast radius for a file or symbol, from the knowledge graph.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# function (transitive upstream dependents).
|
|
4
|
+
# Everything that depends on the target, up to --depth CALLS hops: callers of the
|
|
5
|
+
# symbol, or callers of anything defined in the file plus the files that import
|
|
6
|
+
# it. Callers inside the target file are not downstream and are skipped.
|
|
8
7
|
#
|
|
9
8
|
# Usage:
|
|
10
9
|
# scripts/tools/graph-impact.sh --repo DIR (--file PATH | --symbol NAME) [--depth N]
|
|
11
10
|
#
|
|
12
|
-
# Output: JSON {target, kind, impacted:[{name,file,qualified,hop}],
|
|
13
|
-
#
|
|
14
|
-
#
|
|
11
|
+
# Output: JSON {target, kind, impacted:[{name,file,qualified,hop}], downstream_files,
|
|
12
|
+
# affected_modules, max_depth, by_category:{code,test}, status, truncated, source}.
|
|
13
|
+
# `impacted` lists each dependent once at its nearest hop, capped at 200; the
|
|
14
|
+
# aggregates always cover the full set. `truncated` is true when the list was
|
|
15
|
+
# capped or the engine row limit was hit. A module is a file's top-level path
|
|
16
|
+
# segment ("." for root files), as in classify-files.sh.
|
|
17
|
+
# status = ok | no-edges (target known, nothing depends on it) | no-match
|
|
18
|
+
# (target unknown to the graph)
|
|
15
19
|
# source = "memory-graph" | "unavailable"
|
|
16
20
|
#
|
|
17
21
|
# Exit codes: 0 OK, 1 invocation error, 2 graph engine unavailable.
|
|
18
22
|
set -euo pipefail
|
|
19
23
|
|
|
20
|
-
# shellcheck source=
|
|
21
|
-
source "$(dirname "${BASH_SOURCE[0]}")/
|
|
24
|
+
# shellcheck source=_graph_queries.sh
|
|
25
|
+
source "$(dirname "${BASH_SOURCE[0]}")/_graph_queries.sh"
|
|
22
26
|
|
|
23
27
|
REPO="."
|
|
24
28
|
FILE=""
|
|
@@ -34,12 +38,13 @@ Usage:
|
|
|
34
38
|
|
|
35
39
|
Flags:
|
|
36
40
|
--repo DIR Repository root (default: cwd).
|
|
37
|
-
--file PATH
|
|
38
|
-
--symbol NAME
|
|
39
|
-
--depth N Caller traversal depth
|
|
41
|
+
--file PATH Dependents of a file (repo-relative, ./-prefixed, or absolute).
|
|
42
|
+
--symbol NAME Dependents (transitive callers) of a function.
|
|
43
|
+
--depth N Caller traversal depth (default: 3).
|
|
40
44
|
--help Show this help.
|
|
41
45
|
|
|
42
|
-
Output: JSON {target, kind, impacted,
|
|
46
|
+
Output: JSON {target, kind, impacted, downstream_files, affected_modules,
|
|
47
|
+
max_depth, by_category, status, truncated, source}. Exit 2 when engine unavailable.
|
|
43
48
|
EOF
|
|
44
49
|
}
|
|
45
50
|
|
|
@@ -70,29 +75,50 @@ if [[ -n "$SYMBOL" ]]; then TARGET="$SYMBOL"; KIND="symbol"; else TARGET="$FILE"
|
|
|
70
75
|
graph_bootstrap "$REPO" || unavailable "$TARGET" "$KIND"
|
|
71
76
|
|
|
72
77
|
if [[ -n "$SYMBOL" ]]; then
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# --symbol can never corrupt the JSON; an engine failure is unavailable,
|
|
76
|
-
# never a fabricated empty-impact success.
|
|
77
|
-
PAYLOAD="$(jq -n --arg p "$PROJECT" --arg f "$SYMBOL" --argjson d "$DEPTH" \
|
|
78
|
-
'{project:$p, function_name:$f, depth:$d, direction:"both"}')"
|
|
79
|
-
RES="$(memory_cli trace_path "$PAYLOAD" 2>/dev/null || true)"
|
|
80
|
-
echo "$RES" | jq -e 'has("callers") and (.callers | type == "array")' >/dev/null 2>&1 \
|
|
81
|
-
|| unavailable "$TARGET" "$KIND"
|
|
82
|
-
echo "$RES" | jq --arg t "$TARGET" '
|
|
83
|
-
{target:$t, kind:"symbol",
|
|
84
|
-
impacted: [ (.callers // [])[] | {name:.name, file:(.file_path // ""), qualified:(.qualified_name // ""), hop:(.hop // 1)} ],
|
|
85
|
-
source:"memory-graph"}'
|
|
78
|
+
T_ESC="$(gq_escape "$SYMBOL")"
|
|
79
|
+
DEPENDENTS=gq_q_dependents_symbol; EXISTS=gq_q_exists
|
|
86
80
|
else
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|| unavailable "$TARGET" "$KIND"
|
|
92
|
-
echo "$RES" | jq --arg t "$TARGET" '
|
|
93
|
-
{target:$t, kind:"file",
|
|
94
|
-
impacted: [ (.impacted_symbols // [])[]
|
|
95
|
-
| select((.file // "") | endswith($t) or (. == $t))
|
|
96
|
-
| {name:.name, file:(.file // ""), hop:1} ],
|
|
97
|
-
source:"memory-graph"}'
|
|
81
|
+
# The graph keys files by repo-relative path.
|
|
82
|
+
REL="${FILE#"$REPO_ABS"/}"; REL="${REL#./}"
|
|
83
|
+
T_ESC="$(gq_escape "$REL")"
|
|
84
|
+
DEPENDENTS=gq_q_dependents_file; EXISTS=gq_q_file_exists
|
|
98
85
|
fi
|
|
86
|
+
|
|
87
|
+
# Accumulate {q,name,file,test,hop} JSONL in a temp file: the row sets of a hot
|
|
88
|
+
# target can exceed argv limits.
|
|
89
|
+
ROWS="$(mktemp)"
|
|
90
|
+
trap 'rm -f "$ROWS"' EXIT
|
|
91
|
+
TRUNC=false
|
|
92
|
+
for ((k = 1; k <= DEPTH; k++)); do
|
|
93
|
+
R="$(gq_run "$PROJECT" "$("$DEPENDENTS" "$T_ESC" "$k")")" || unavailable "$TARGET" "$KIND"
|
|
94
|
+
[[ "$(gq_rows_len "$R")" -lt "$GQ_DEP_LIMIT" ]] || TRUNC=true
|
|
95
|
+
jq -c --argjson k "$k" '.rows[] | {q:(.[0] // ""), name:(.[1] // ""), file:(.[2] // ""),
|
|
96
|
+
test:((.[3] | tostring) == "true"), hop:$k}' <<< "$R" >> "$ROWS"
|
|
97
|
+
done
|
|
98
|
+
if [[ -n "$FILE" ]]; then
|
|
99
|
+
R="$(gq_run "$PROJECT" "$(gq_q_importers "$T_ESC")")" || unavailable "$TARGET" "$KIND"
|
|
100
|
+
[[ "$(gq_rows_len "$R")" -lt "$GQ_DEP_LIMIT" ]] || TRUNC=true
|
|
101
|
+
jq -c '.rows[] | {q:"", name:.[0], file:.[0], test:false, hop:1}' <<< "$R" >> "$ROWS"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Nothing depends on it: a true negative only if the graph knows the target.
|
|
105
|
+
STATUS=ok
|
|
106
|
+
if [[ ! -s "$ROWS" ]]; then
|
|
107
|
+
EX="$(gq_run "$PROJECT" "$("$EXISTS" "$T_ESC")")" || unavailable "$TARGET" "$KIND"
|
|
108
|
+
if [[ "$(gq_rows_len "$EX")" -gt 0 ]]; then STATUS=no-edges; else STATUS=no-match; fi
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
jq -s --arg t "$TARGET" --arg kind "$KIND" --arg status "$STATUS" --argjson trunc "$TRUNC" '
|
|
112
|
+
(group_by([.q, .file]) | map(min_by(.hop))) as $u
|
|
113
|
+
| ($u | map(.file) | map(select(. != "")) | unique) as $files
|
|
114
|
+
| ($u | map(select(.test) | .file) | unique) as $tests
|
|
115
|
+
| {target:$t, kind:$kind,
|
|
116
|
+
impacted: ($u | sort_by(.hop, .file, .name) | .[:200]
|
|
117
|
+
| map({name, file, qualified:.q, hop})),
|
|
118
|
+
downstream_files: $files,
|
|
119
|
+
affected_modules: ($files | map(if test("/") then split("/")[0] else "." end) | unique),
|
|
120
|
+
max_depth: ($u | map(.hop) | max // 0),
|
|
121
|
+
by_category: {code: ($files - $tests | length), test: ($tests | length)},
|
|
122
|
+
status: $status,
|
|
123
|
+
truncated: ($trunc or ($u | length) > 200),
|
|
124
|
+
source: "memory-graph"}' "$ROWS"
|
|
@@ -62,7 +62,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
62
62
|
done
|
|
63
63
|
|
|
64
64
|
[[ -d "$SCOPE" ]] || { echo "ERROR: --scope '$SCOPE' is not a directory" >&2; exit 1; }
|
|
65
|
-
SCOPE_ABS="$(cd "$SCOPE" && pwd)"
|
|
65
|
+
SCOPE_ABS="$(cd "$SCOPE" && pwd -P)"
|
|
66
66
|
SELF_REPO="$(cd "$TOOLS_DIR/../.." && pwd)"
|
|
67
67
|
|
|
68
68
|
# --- Resolve ROOT (bounded by the git toplevel; never escapes the repo) ---
|
|
@@ -127,22 +127,17 @@ write_root_link() {
|
|
|
127
127
|
local status="$1"
|
|
128
128
|
local mod_graph="$SCOPE_ABS/draft/graph"
|
|
129
129
|
mkdir -p "$mod_graph"
|
|
130
|
-
local rel
|
|
130
|
+
local rel root_commit ts
|
|
131
131
|
rel="$(root_link_relpath)"
|
|
132
|
-
if [[ -f "$schema" ]]; then
|
|
133
|
-
root_project="$(grep -m1 '^project:' "$schema" 2>/dev/null | sed 's/^project:[[:space:]]*//; s/^"//; s/"$//' || true)"
|
|
134
|
-
[[ -n "$root_project" ]] || root_project="unknown"
|
|
135
|
-
fi
|
|
136
132
|
root_commit="$(git -C "$ROOT_ABS" rev-parse --verify --quiet HEAD 2>/dev/null || echo none)"
|
|
137
133
|
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
138
|
-
# Every interpolated value goes through json_escape: a repo path
|
|
139
|
-
#
|
|
140
|
-
#
|
|
134
|
+
# Every interpolated value goes through json_escape: a repo path carrying a
|
|
135
|
+
# quote or backslash would otherwise emit a root-link.json that no consumer
|
|
136
|
+
# can parse.
|
|
141
137
|
cat > "$mod_graph/root-link.json" <<EOF
|
|
142
138
|
{
|
|
143
139
|
"root_graph": "$(json_escape "$rel")",
|
|
144
140
|
"root_abs": "$(json_escape "$ROOT_ABS/draft/graph")",
|
|
145
|
-
"root_project": "$(json_escape "${root_project:-unknown}")",
|
|
146
141
|
"root_commit": "$(json_escape "$root_commit")",
|
|
147
142
|
"status": "$(json_escape "$status")",
|
|
148
143
|
"linked_at": "$ts",
|
|
@@ -47,7 +47,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
47
47
|
done
|
|
48
48
|
REPO="${REPO:-.}"
|
|
49
49
|
[[ -d "$REPO" ]] || { echo "ERROR: '$REPO' is not a directory" >&2; exit 2; }
|
|
50
|
-
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
50
|
+
REPO_ABS="$(cd "$REPO" && pwd -P)"
|
|
51
51
|
|
|
52
52
|
# --- formatting (color only on a tty, and never in --json) ---
|
|
53
53
|
if [[ -t 1 && "$JSON_MODE" -eq 0 ]]; then B=$'\e[1m'; G=$'\e[32m'; Y=$'\e[33m'; R=$'\e[31m'; D=$'\e[0m'; else B=""; G=""; Y=""; R=""; D=""; fi
|
|
@@ -168,6 +168,10 @@ if find_memory_bin "$REPO_ABS" "$SELF_REPO"; then
|
|
|
168
168
|
ENGINE_FOUND=true
|
|
169
169
|
VER="$("$ENGINE" --version 2>/dev/null | head -1 || echo '?')"
|
|
170
170
|
ok "Engine: $ENGINE ($VER)"
|
|
171
|
+
PINNED="$(sed -n 's/^DEFAULT_VERSION="v\{0,1\}\([^"]*\)".*/\1/p' "$SELF_REPO/scripts/fetch-memory-engine.sh" 2>/dev/null | head -1)"
|
|
172
|
+
if [[ -n "$PINNED" && "${VER##* }" != "$PINNED" ]]; then
|
|
173
|
+
warn "Engine version ${VER##* } differs from the pinned $PINNED the graph tools are verified against — scripts/fetch-memory-engine.sh --force"
|
|
174
|
+
fi
|
|
171
175
|
# Numeric-only: the value is emitted bare into the --json report, so a
|
|
172
176
|
# non-numeric field (engine output format drift) would produce invalid JSON.
|
|
173
177
|
LIMIT="$("$ENGINE" config list 2>/dev/null | awk '/auto_index_limit/{print $3}' || true)"
|
|
@@ -255,8 +259,7 @@ cat <<EOF
|
|
|
255
259
|
|
|
256
260
|
Next step (when ready, from the git root):
|
|
257
261
|
scripts/tools/graph-init.sh --scope . --json & # or: /draft:init --graph-only
|
|
258
|
-
|
|
259
|
-
${ENGINE:-codebase-memory-mcp} cli index_status '{"project":"<name>"}'
|
|
262
|
+
scripts/tools/graph-query.sh --repo . --tool index_status
|
|
260
263
|
EOF
|
|
261
264
|
hr
|
|
262
265
|
exit "$VEXIT"
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
# a `query` field in --json is scanned for write
|
|
13
13
|
# verbs exactly like --cypher.
|
|
14
14
|
#
|
|
15
|
-
# Dialect limits (engine v0.
|
|
16
|
-
# SAFE :
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
15
|
+
# Dialect limits (engine v0.9.0 — see _graph_queries.sh for the full list):
|
|
16
|
+
# SAFE : comparisons against a literal, `STARTS WITH`, `AND`/`OR`,
|
|
17
|
+
# variable-length `[:R*1..3]`, rel-type alternation `[:A|B]`,
|
|
18
|
+
# `coalesce()`, `DISTINCT`, `count(...)`, WITH-aggregation.
|
|
19
|
+
# UNSAFE : property-to-property comparison, NOT EXISTS(...), NOT (pattern),
|
|
20
|
+
# path variables, multi-pattern joins. LIMIT applies before DISTINCT.
|
|
20
21
|
# Passthrough returns the engine's raw error, not a silent empty result.
|
|
21
22
|
#
|
|
22
23
|
# Usage:
|
|
@@ -52,8 +53,8 @@ Flags:
|
|
|
52
53
|
`query` field is write-verb checked like --cypher.
|
|
53
54
|
--help Show this help.
|
|
54
55
|
|
|
55
|
-
Dialect: avoid
|
|
56
|
-
multi-pattern joins.
|
|
56
|
+
Dialect: avoid property-to-property comparison, NOT EXISTS, NOT(pattern), path
|
|
57
|
+
variables, multi-pattern joins. LIMIT applies before DISTINCT.
|
|
57
58
|
|
|
58
59
|
Output: raw engine JSON on success; {"source":"unavailable"} (exit 2) when the
|
|
59
60
|
engine is unavailable; exit 1 on invocation error or a rejected write verb.
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# truth; the engine is the structural index over it.
|
|
12
12
|
#
|
|
13
13
|
# Writes one file under <repo>/draft/graph/:
|
|
14
|
-
# schema.yaml engine
|
|
14
|
+
# schema.yaml engine metadata + index counts. Its presence is the
|
|
15
15
|
# GATE that tells skills the graph engine is wired for this repo
|
|
16
16
|
# (see core/shared/graph-query.md Pre-Check). It carries no graph
|
|
17
17
|
# data — every structural query goes to the live engine.
|
|
@@ -61,30 +61,18 @@ done
|
|
|
61
61
|
|
|
62
62
|
[[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
|
|
63
63
|
|
|
64
|
-
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
64
|
+
REPO_ABS="$(cd "$REPO" && pwd -P)"
|
|
65
65
|
SELF_REPO="$(cd "$TOOLS_DIR/../.." && pwd)"
|
|
66
66
|
OUT="${OUT_DIR:-$REPO_ABS/draft/graph}"
|
|
67
67
|
|
|
68
68
|
find_memory_bin "$REPO_ABS" "$SELF_REPO" || { echo "graph engine unavailable — nothing written" >&2; exit 2; }
|
|
69
69
|
command -v jq >/dev/null 2>&1 || { echo "jq required" >&2; exit 2; }
|
|
70
70
|
|
|
71
|
-
#
|
|
72
|
-
#
|
|
71
|
+
# Refresh the engine index (incremental; indexes from scratch when absent). This is
|
|
72
|
+
# the valuable side-effect — live queries resolve against a current index. A failed
|
|
73
|
+
# refresh writes nothing: a fresh `generated_at` over a frozen index would lie.
|
|
73
74
|
PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
|
|
74
|
-
[[ -n "$PROJECT" ]] || { echo "
|
|
75
|
-
|
|
76
|
-
# ...then ALWAYS re-index. memory_ensure_index calls index_repository only when the
|
|
77
|
-
# project is ABSENT — correct for the graph-*.sh query wrappers, which must stay
|
|
78
|
-
# cheap — so on an already-indexed repo this tool used to write a gate marker with a
|
|
79
|
-
# fresh `generated_at` over a frozen index: a deleted symbol stayed resolvable, a new
|
|
80
|
-
# one never appeared, and nothing in the output said so. Refreshing is this tool's
|
|
81
|
-
# entire job. The engine indexes incrementally, so the repeat call is cheap.
|
|
82
|
-
REFRESHED="$(memory_index_bounded "$REPO_ABS" 2>/dev/null | jq -r '.project // empty' 2>/dev/null || true)"
|
|
83
|
-
if [[ -z "$REFRESHED" ]]; then
|
|
84
|
-
echo "index refresh failed — nothing written" >&2
|
|
85
|
-
exit 2
|
|
86
|
-
fi
|
|
87
|
-
PROJECT="$REFRESHED"
|
|
75
|
+
[[ -n "$PROJECT" ]] || { echo "index refresh failed — nothing written" >&2; exit 2; }
|
|
88
76
|
|
|
89
77
|
mkdir -p "$OUT"
|
|
90
78
|
|
|
@@ -120,32 +108,29 @@ VER="$("$MEMORY_BIN" --version 2>/dev/null | awk '{print $NF}' || echo unknown)"
|
|
|
120
108
|
|
|
121
109
|
# Incremental-refresh provenance (graph-tooling-v2 Phase 5): the engine indexes
|
|
122
110
|
# incrementally (content-based, git-aware), so re-indexing only touches changed
|
|
123
|
-
# files. detect_changes reports that working-tree delta —
|
|
124
|
-
#
|
|
111
|
+
# files. detect_changes reports that working-tree delta — echoed so a refresh
|
|
112
|
+
# shows what moved, but kept out of the committed marker (it differs per machine
|
|
113
|
+
# and per run, like the path-derived project name and a timestamp would).
|
|
114
|
+
# Best-effort: never aborts the write.
|
|
125
115
|
CHANGES_JSON="$(memory_cli detect_changes "$(jq -n --arg p "$PROJECT" '{project:$p}')" 2>/dev/null || echo '{}')"
|
|
126
116
|
echo "$CHANGES_JSON" | jq -e . >/dev/null 2>&1 || CHANGES_JSON='{}'
|
|
127
117
|
CHANGED_FILES="$(echo "$CHANGES_JSON" | jq -r '.changed_count // (.changed_files | length?) // 0' 2>/dev/null || echo 0)"
|
|
128
118
|
IMPACTED="$(echo "$CHANGES_JSON" | jq -r '(.impacted_symbols | length?) // 0' 2>/dev/null || echo 0)"
|
|
129
119
|
|
|
130
|
-
# YAML double-quoted
|
|
131
|
-
#
|
|
132
|
-
PROJECT_Y="${PROJECT//\\/\\\\}"; PROJECT_Y="${PROJECT_Y//\"/\\\"}"
|
|
120
|
+
# YAML double-quoted scalar: escape backslashes then quotes so an unusual
|
|
121
|
+
# engine version string can never corrupt the marker.
|
|
133
122
|
VER_Y="${VER//\\/\\\\}"; VER_Y="${VER_Y//\"/\\\"}"
|
|
134
123
|
|
|
135
124
|
cat > "$OUT/schema.yaml" <<EOF
|
|
136
125
|
# Draft graph gate marker — written by scripts/tools/graph-snapshot.sh
|
|
137
126
|
# Draft is engine-only: this file carries NO graph data. Its presence signals that
|
|
138
127
|
# the local codebase-memory-mcp engine is wired for this repo. Query the engine
|
|
139
|
-
# live via the graph-*.sh wrappers (
|
|
128
|
+
# live via the graph-*.sh wrappers (graph-query.sh --tool covers the rest).
|
|
140
129
|
# Counts below are point-of-index provenance; the live engine is authoritative.
|
|
141
130
|
engine: codebase-memory-mcp
|
|
142
131
|
engine_version: "$VER_Y"
|
|
143
|
-
project: "$PROJECT_Y"
|
|
144
|
-
generated_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
145
132
|
indexed_nodes: $NODES
|
|
146
133
|
indexed_edges: $EDGES
|
|
147
|
-
changed_files: $CHANGED_FILES
|
|
148
|
-
impacted_symbols: $IMPACTED
|
|
149
134
|
access: engine-live
|
|
150
135
|
EOF
|
|
151
136
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# graph-tooling-v2 Phase 6. Wraps the engine's ingest_traces to close the
|
|
5
5
|
# static/dynamic gap — dynamic dispatch the static graph misses (e.g. closures,
|
|
6
6
|
# reflection, virtual calls). This is a WRITE path and is gated behind
|
|
7
|
-
# --experimental. NOTE: in engine v0.
|
|
7
|
+
# --experimental. NOTE: in engine v0.9.0 ingest_traces is accepted but runtime
|
|
8
8
|
# edge creation is "not yet implemented" — the engine returns its status verbatim.
|
|
9
9
|
#
|
|
10
10
|
# Usage:
|
|
@@ -39,7 +39,7 @@ Flags:
|
|
|
39
39
|
--experimental Required acknowledgement — this is a write/experimental path.
|
|
40
40
|
--help Show this help.
|
|
41
41
|
|
|
42
|
-
NOTE: engine v0.
|
|
42
|
+
NOTE: engine v0.9.0 accepts traces but runtime edge creation is not yet
|
|
43
43
|
implemented; the engine's status is returned verbatim.
|
|
44
44
|
|
|
45
45
|
Output: raw engine JSON; {"source":"unavailable"} (exit 2) when unavailable.
|
|
@@ -79,7 +79,16 @@ echo "$ARCH_JSON" | jq -e . >/dev/null 2>&1 || unavailable
|
|
|
79
79
|
# and still reported source:"memory-graph", so a fan-in-only ranking was
|
|
80
80
|
# indistinguishable from a fully measured one. Record the outcome instead, and
|
|
81
81
|
# omit the fields rather than emitting measurements that were never taken.
|
|
82
|
-
|
|
82
|
+
# Ask for the hotspots by name: a capped scan of every node missed hotspots past
|
|
83
|
+
# the window on large repos and scored them 0 under enrichment "ok".
|
|
84
|
+
NAMES=""
|
|
85
|
+
while IFS= read -r q; do
|
|
86
|
+
NAMES+="${NAMES:+,}'$(gq_escape "$q")'"
|
|
87
|
+
done < <(jq -r '(.hotspots // [])[].qualified_name // empty' <<< "$ARCH_JSON")
|
|
88
|
+
if [[ -z "$NAMES" ]]; then
|
|
89
|
+
PROPS_JSON='{"rows":[]}'
|
|
90
|
+
ENRICHMENT="ok"
|
|
91
|
+
elif PROPS_JSON="$(gq_run "$PROJECT" "$(gq_q_node_props "$NAMES")")"; then
|
|
83
92
|
ENRICHMENT="ok"
|
|
84
93
|
else
|
|
85
94
|
PROPS_JSON='{"rows":[]}'
|
|
@@ -137,12 +137,24 @@ fi
|
|
|
137
137
|
|
|
138
138
|
STATUS="ok"
|
|
139
139
|
|
|
140
|
+
# The wrappers are verified against the pinned DEFAULT_VERSION. Another version
|
|
141
|
+
# (e.g. a global install on PATH, which outranks the managed one) stays usable,
|
|
142
|
+
# but may speak a different dialect or CLI — say so rather than run it silently.
|
|
143
|
+
VERSION="$("$MEMORY_BIN" --version 2>/dev/null | awk '{print $NF}')"
|
|
144
|
+
PINNED="$(sed -n 's/^DEFAULT_VERSION="v\{0,1\}\([^"]*\)".*/\1/p' "$SELF_REPO/scripts/fetch-memory-engine.sh" 2>/dev/null | head -1)"
|
|
145
|
+
if [[ -n "$PINNED" && "$VERSION" != "$PINNED" ]]; then
|
|
146
|
+
echo "WARNING: engine version $VERSION differs from the pinned $PINNED that Draft's graph tools are verified against." >&2
|
|
147
|
+
echo " Install the pinned engine: scripts/fetch-memory-engine.sh --force" >&2
|
|
148
|
+
fi
|
|
149
|
+
|
|
140
150
|
if [[ $EMIT_JSON -eq 1 ]]; then
|
|
141
|
-
printf '{"status":"%s","engine_bin":"%s","source":"%s","arch":"%s"}\n' \
|
|
142
|
-
"$(json_escape "$STATUS")" "$(json_escape "$MEMORY_BIN")" "$(json_escape "$SOURCE")" "$(json_escape "$ARCH")"
|
|
151
|
+
printf '{"status":"%s","engine_bin":"%s","source":"%s","arch":"%s","version":"%s","pinned_version":"%s"}\n' \
|
|
152
|
+
"$(json_escape "$STATUS")" "$(json_escape "$MEMORY_BIN")" "$(json_escape "$SOURCE")" "$(json_escape "$ARCH")" \
|
|
153
|
+
"$(json_escape "$VERSION")" "$(json_escape "$PINNED")"
|
|
143
154
|
else
|
|
144
155
|
echo "Draft graph engine: $MEMORY_BIN"
|
|
145
156
|
echo " source: $SOURCE (arch=$ARCH)"
|
|
157
|
+
echo " version: $VERSION (pinned: ${PINNED:-unknown})"
|
|
146
158
|
echo " status: $STATUS"
|
|
147
159
|
fi
|
|
148
160
|
|
|
@@ -671,7 +671,7 @@ After a phase passes review, refresh `metadata.json.impact` so future tracks can
|
|
|
671
671
|
"$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path>
|
|
672
672
|
```
|
|
673
673
|
|
|
674
|
-
Aggregate across all files: `downstream_files` =
|
|
674
|
+
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.
|
|
675
675
|
|
|
676
676
|
3. **Write metadata.json** with the populated `impact` block and `computed_at` set to the current timestamp.
|
|
677
677
|
|