homegraph 1.5.0 → 1.5.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.
Files changed (214) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +305 -305
  3. package/dist/bin/command-supervision.d.ts.map +1 -1
  4. package/dist/bin/command-supervision.js +7 -4
  5. package/dist/bin/command-supervision.js.map +1 -1
  6. package/dist/bin/homegraph.js +9 -9
  7. package/dist/db/index.js +36 -36
  8. package/dist/db/migrations.js +37 -37
  9. package/dist/db/queries.js +156 -156
  10. package/dist/db/schema.sql +203 -203
  11. package/dist/directory.js +5 -5
  12. package/dist/extraction/languages/arkts-viewtree.d.ts +2 -4
  13. package/dist/extraction/languages/arkts-viewtree.d.ts.map +1 -1
  14. package/dist/extraction/languages/arkts-viewtree.js +6 -21
  15. package/dist/extraction/languages/arkts-viewtree.js.map +1 -1
  16. package/dist/extraction/languages/arkts.d.ts +16 -6
  17. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  18. package/dist/extraction/languages/arkts.js +174 -21
  19. package/dist/extraction/languages/arkts.js.map +1 -1
  20. package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  21. package/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  22. package/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  23. package/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  24. package/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  25. package/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  26. package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  27. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  28. package/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  29. package/dist/installer/instructions-template.js +9 -9
  30. package/dist/mcp/liveness-watchdog.d.ts +18 -0
  31. package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
  32. package/dist/mcp/liveness-watchdog.js +185 -73
  33. package/dist/mcp/liveness-watchdog.js.map +1 -1
  34. package/dist/mcp/server-instructions.js +47 -47
  35. package/dist/reasoning/reasoner.js +32 -32
  36. package/dist/spec/db/commit-node.js +4 -4
  37. package/dist/spec/db/fragment-node.js +10 -10
  38. package/dist/spec/db/fts.js +8 -8
  39. package/dist/spec/db/relations.js +88 -88
  40. package/dist/spec/db/schema.js +6 -6
  41. package/dist/spec/db/schema.sql +121 -121
  42. package/dist/spec/db/spec-node.js +4 -4
  43. package/dist/spec/llm/prompts.js +53 -53
  44. package/dist/spec/utils.d.ts +16 -4
  45. package/dist/spec/utils.d.ts.map +1 -1
  46. package/dist/spec/utils.js +58 -6
  47. package/dist/spec/utils.js.map +1 -1
  48. package/package.json +62 -62
  49. package/scripts/_tmp-cfwk-resolve.log +0 -0
  50. package/scripts/_tmp-cfwk-sig.log +0 -0
  51. package/scripts/_tmp-cfwk-vt.log +0 -0
  52. package/scripts/add-lang/bench.sh +60 -60
  53. package/scripts/add-lang/check-grammar.mjs +75 -75
  54. package/scripts/add-lang/dump-ast.mjs +103 -103
  55. package/scripts/add-lang/verify-extraction.mjs +70 -70
  56. package/scripts/agent-eval/ab-adoption.sh +91 -91
  57. package/scripts/agent-eval/ab-hook.sh +86 -86
  58. package/scripts/agent-eval/ab-impl.sh +78 -78
  59. package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -102
  60. package/scripts/agent-eval/ab-sufficiency.sh +78 -78
  61. package/scripts/agent-eval/arms-F.sh +21 -21
  62. package/scripts/agent-eval/arms-matrix.sh +37 -37
  63. package/scripts/agent-eval/audit.sh +68 -68
  64. package/scripts/agent-eval/bench-readme.sh +28 -28
  65. package/scripts/agent-eval/bench-why-repo.sh +22 -22
  66. package/scripts/agent-eval/block-read-hook.sh +19 -19
  67. package/scripts/agent-eval/hook-settings.json +15 -15
  68. package/scripts/agent-eval/itrun.sh +120 -120
  69. package/scripts/agent-eval/offload-eval-3arm.sh +72 -72
  70. package/scripts/agent-eval/offload-eval-cost.mjs +133 -133
  71. package/scripts/agent-eval/offload-eval-effort.mjs +108 -108
  72. package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -25
  73. package/scripts/agent-eval/offload-eval-frontload.sh +47 -47
  74. package/scripts/agent-eval/offload-eval-ground-truth.json +18 -18
  75. package/scripts/agent-eval/offload-eval-hook.mjs +84 -84
  76. package/scripts/agent-eval/offload-eval-judge.mjs +103 -103
  77. package/scripts/agent-eval/offload-eval-matrix.sh +20 -20
  78. package/scripts/agent-eval/offload-eval-metrics.mjs +94 -94
  79. package/scripts/agent-eval/offload-eval-refs1.sh +50 -50
  80. package/scripts/agent-eval/offload-eval-setup.sh +24 -24
  81. package/scripts/agent-eval/offload-eval-styles.sh +71 -71
  82. package/scripts/agent-eval/offload-eval-summarize.mjs +68 -68
  83. package/scripts/agent-eval/offload-eval.md +76 -76
  84. package/scripts/agent-eval/parse-arms.mjs +116 -116
  85. package/scripts/agent-eval/parse-bench-readme.mjs +84 -84
  86. package/scripts/agent-eval/parse-run.mjs +45 -45
  87. package/scripts/agent-eval/parse-session.mjs +93 -93
  88. package/scripts/agent-eval/probe-context.mjs +21 -21
  89. package/scripts/agent-eval/probe-explore.mjs +40 -40
  90. package/scripts/agent-eval/probe-node.mjs +20 -20
  91. package/scripts/agent-eval/probe-sweep.mjs +119 -119
  92. package/scripts/agent-eval/probe-trace.mjs +20 -20
  93. package/scripts/agent-eval/redirect-read-hook.sh +38 -38
  94. package/scripts/agent-eval/repro-concurrent-explore.mjs +119 -119
  95. package/scripts/agent-eval/repro-daemon-clients.mjs +125 -125
  96. package/scripts/agent-eval/run-agent.sh +34 -34
  97. package/scripts/agent-eval/run-all.sh +75 -75
  98. package/scripts/agent-eval/run-arms.sh +56 -56
  99. package/scripts/agent-eval/seq-matrix.mjs +137 -137
  100. package/scripts/bench-arkts-init-rss.log +0 -0
  101. package/scripts/build-bundle.sh +123 -123
  102. package/scripts/exp_boundary_eval/README.md +247 -247
  103. package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-310.pyc +0 -0
  104. package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-38.pyc +0 -0
  105. package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-310.pyc +0 -0
  106. package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-38.pyc +0 -0
  107. package/scripts/exp_boundary_eval/__pycache__/deveco_arm.cpython-38.pyc +0 -0
  108. package/scripts/exp_boundary_eval/__pycache__/run_all.cpython-310.pyc +0 -0
  109. package/scripts/exp_boundary_eval/__pycache__/run_all.cpython-38.pyc +0 -0
  110. package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-310.pyc +0 -0
  111. package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-38.pyc +0 -0
  112. package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-310.pyc +0 -0
  113. package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-38.pyc +0 -0
  114. package/scripts/exp_boundary_eval/__pycache__/setup.cpython-310.pyc +0 -0
  115. package/scripts/exp_boundary_eval/__pycache__/setup.cpython-38.pyc +0 -0
  116. package/scripts/exp_boundary_eval/__pycache__/win_mcp_launcher.cpython-38.pyc +0 -0
  117. package/scripts/exp_boundary_eval/_test_mcp_chain.py +78 -78
  118. package/scripts/exp_boundary_eval/_test_stdin.py +8 -8
  119. package/scripts/exp_boundary_eval/_utils.py +1116 -1116
  120. package/scripts/exp_boundary_eval/analyze.py +1313 -1313
  121. package/scripts/exp_boundary_eval/deveco_arm.py +519 -519
  122. package/scripts/exp_boundary_eval/run_all.py +378 -378
  123. package/scripts/exp_boundary_eval/run_one.py +165 -165
  124. package/scripts/exp_boundary_eval/run_session.py +158 -158
  125. package/scripts/exp_boundary_eval/setup.py +120 -120
  126. package/scripts/exp_boundary_eval/win_mcp_launcher.py +73 -73
  127. package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +36 -36
  128. package/scripts/exp_boundary_eval/win_node_launcher.py +24 -24
  129. package/scripts/extract-release-notes.mjs +130 -130
  130. package/scripts/local-install.sh +41 -41
  131. package/scripts/npm-sdk.js +75 -75
  132. package/scripts/npm-shim.js +275 -275
  133. package/scripts/ohos-sdk-publish.mjs +133 -133
  134. package/scripts/pack-npm.sh +119 -119
  135. package/scripts/prepare-release.mjs +270 -270
  136. package/scripts/probe-arkts-mem-why-run.log +0 -0
  137. package/scripts/probe-banner-livecard-ir.log +0 -0
  138. package/scripts/probe-cfwk-ast.stderr.log +0 -0
  139. package/scripts/probe-cfwk-ast.stdout.log +0 -0
  140. package/scripts/probe-cfwk-attr-shape.log +0 -0
  141. package/scripts/probe-cfwk-cfgdump.stderr.log +0 -0
  142. package/scripts/probe-cfwk-cfgdump.stdout.log +0 -0
  143. package/scripts/probe-cfwk-cvc.stderr.log +0 -0
  144. package/scripts/probe-cfwk-cvc.stdout.log +0 -0
  145. package/scripts/probe-cfwk-diag2.log +0 -0
  146. package/scripts/probe-cfwk-diag3.log +0 -0
  147. package/scripts/probe-cfwk-fedbg.stderr.log +0 -0
  148. package/scripts/probe-cfwk-fedbg.stdout.log +0 -0
  149. package/scripts/probe-cfwk-fileresult.log +0 -0
  150. package/scripts/probe-cfwk-fix.stderr.log +0 -0
  151. package/scripts/probe-cfwk-fix.stdout.log +0 -0
  152. package/scripts/probe-cfwk-fix2.stderr.log +0 -0
  153. package/scripts/probe-cfwk-fix2.stdout.log +0 -0
  154. package/scripts/probe-cfwk-fix3.stderr.log +0 -0
  155. package/scripts/probe-cfwk-fix3.stdout.log +0 -0
  156. package/scripts/probe-cfwk-foreach.log +0 -0
  157. package/scripts/probe-cfwk-getmethod-throw.log +0 -0
  158. package/scripts/probe-cfwk-hg-extract.log +0 -0
  159. package/scripts/probe-cfwk-pr1003-noprior.stderr.log +0 -0
  160. package/scripts/probe-cfwk-pr1003-noprior.stdout.log +0 -0
  161. package/scripts/probe-cfwk-pr1003.stderr.log +0 -0
  162. package/scripts/probe-cfwk-pr1003.stdout.log +0 -0
  163. package/scripts/probe-cfwk-preroot-noprior.stderr.log +0 -0
  164. package/scripts/probe-cfwk-preroot-noprior.stdout.log +0 -0
  165. package/scripts/probe-cfwk-preroot-prior.stderr.log +0 -0
  166. package/scripts/probe-cfwk-preroot-prior.stdout.log +0 -0
  167. package/scripts/probe-cfwk-preroot-skipstate.stderr.log +0 -0
  168. package/scripts/probe-cfwk-preroot-skipstate.stdout.log +0 -0
  169. package/scripts/probe-cfwk-resolve-sim.log +0 -0
  170. package/scripts/probe-cfwk-tree-shape.log +0 -0
  171. package/scripts/probe-cfwk-walk-abort.log +0 -0
  172. package/scripts/probe-cfwk.log +0 -0
  173. package/scripts/probe-force-index.log +0 -0
  174. package/scripts/probe-no-force-index.log +0 -0
  175. package/scripts/probe-samefile-ir.stderr.log +0 -0
  176. package/scripts/probe-samefile-ir.stdout.log +224 -0
  177. package/scripts/probe-sdk-vs-project.log +0 -0
  178. package/scripts/probe-viewtree-downgrade.log +0 -0
  179. package/dist/arkts/ohos-api-index.d.ts +0 -15
  180. package/dist/arkts/ohos-api-index.d.ts.map +0 -1
  181. package/dist/arkts/ohos-api-index.js +0 -190
  182. package/dist/arkts/ohos-api-index.js.map +0 -1
  183. package/dist/arkts/ohos-sdk-input.d.ts +0 -36
  184. package/dist/arkts/ohos-sdk-input.d.ts.map +0 -1
  185. package/dist/arkts/ohos-sdk-input.js +0 -214
  186. package/dist/arkts/ohos-sdk-input.js.map +0 -1
  187. package/dist/extraction/languages/arkts-state-decorators.d.ts +0 -13
  188. package/dist/extraction/languages/arkts-state-decorators.d.ts.map +0 -1
  189. package/dist/extraction/languages/arkts-state-decorators.js +0 -26
  190. package/dist/extraction/languages/arkts-state-decorators.js.map +0 -1
  191. package/dist/extraction/languages/ohos-api-consumer.d.ts +0 -34
  192. package/dist/extraction/languages/ohos-api-consumer.d.ts.map +0 -1
  193. package/dist/extraction/languages/ohos-api-consumer.js +0 -283
  194. package/dist/extraction/languages/ohos-api-consumer.js.map +0 -1
  195. package/dist/spec/build/git-scanner.d.ts +0 -93
  196. package/dist/spec/build/git-scanner.d.ts.map +0 -1
  197. package/dist/spec/build/git-scanner.js +0 -254
  198. package/dist/spec/build/git-scanner.js.map +0 -1
  199. package/dist/spec/git-utils.d.ts +0 -8
  200. package/dist/spec/git-utils.d.ts.map +0 -1
  201. package/dist/spec/git-utils.js +0 -14
  202. package/dist/spec/git-utils.js.map +0 -1
  203. package/dist/spec/mine/clusterer.d.ts +0 -63
  204. package/dist/spec/mine/clusterer.d.ts.map +0 -1
  205. package/dist/spec/mine/clusterer.js +0 -904
  206. package/dist/spec/mine/clusterer.js.map +0 -1
  207. package/dist/spec/mine/progress-handler.d.ts +0 -22
  208. package/dist/spec/mine/progress-handler.d.ts.map +0 -1
  209. package/dist/spec/mine/progress-handler.js +0 -108
  210. package/dist/spec/mine/progress-handler.js.map +0 -1
  211. package/dist/spec/mine/progress.d.ts +0 -23
  212. package/dist/spec/mine/progress.d.ts.map +0 -1
  213. package/dist/spec/mine/progress.js +0 -12
  214. package/dist/spec/mine/progress.js.map +0 -1
@@ -1,102 +1,102 @@
1
- #!/usr/bin/env bash
2
- # A/B a homegraph retrieval/steering change: the NEW build (current HEAD) vs a
3
- # BASELINE build (a git ref) — BOTH with homegraph attached — on the same
4
- # implementation task, measuring how many Read vs homegraph calls the agent
5
- # makes. ISOLATES the change (unlike run-all.sh's with-vs-without). The agent
6
- # works on a throwaway copy of the target, so your repos are never touched.
7
- #
8
- # Reliable attach (works even when this is itself run nested inside a Claude
9
- # session): each arm PRE-WARMS a persistent homegraph daemon for its target so
10
- # claude connects to an already-bound, index-loaded daemon instantly — before
11
- # the agent's first turn — and SKIPS homegraph's startup re-exec via
12
- # HOMEGRAPH_WASM_RELAUNCHED=1. Without this, on a multi-step task the agent
13
- # dives into Read/grep before homegraph finishes its ~2-3s startup (worse under
14
- # the CPU contention of a nested run) and runs with NO homegraph.
15
- #
16
- # Gotcha: claude's `system/init` snapshot can read status:"pending" / 0 tools
17
- # even when the server then connects fine — judge by ACTUAL homegraph usage in
18
- # parse-run.mjs's "by type", not the init line.
19
- #
20
- # Usage: ab-new-vs-baseline.sh <indexed-repo> "<task>" [baseline-ref]
21
- # <indexed-repo> a repo with a .homegraph index (copied per arm)
22
- # "<task>" an implementation task, e.g. "Add X to Y and wire it through"
23
- # [baseline-ref] git ref for the BEFORE build (default: HEAD~1)
24
- # Env: AGENT_EVAL_OUT (default: /tmp/ab-new-vs-baseline)
25
- set -uo pipefail
26
-
27
- TARGET="${1:?usage: ab-new-vs-baseline.sh <indexed-repo> \"<task>\" [baseline-ref]}"
28
- TASK="${2:?task required}"
29
- BASE_REF="${3:-HEAD~1}"
30
- ENGINE="$(cd "$(dirname "$0")/../.." && pwd)"
31
- BIN="$ENGINE/dist/bin/homegraph.js"
32
- OUT="${AGENT_EVAL_OUT:-/tmp/ab-new-vs-baseline}"
33
- PARSE="$ENGINE/scripts/agent-eval/parse-run.mjs"
34
-
35
- command -v claude >/dev/null || { echo "claude CLI not on PATH"; exit 1; }
36
- [ -d "$TARGET/.homegraph" ] || { echo "target not indexed: run 'homegraph init $TARGET' first"; exit 1; }
37
- if ! git -C "$ENGINE" diff --quiet || ! git -C "$ENGINE" diff --cached --quiet; then
38
- echo "engine repo has uncommitted changes — commit or stash first (this script checks files out)"; exit 1
39
- fi
40
- CHANGED=$(git -C "$ENGINE" diff --name-only "$BASE_REF" HEAD -- src 2>/dev/null)
41
- [ -n "$CHANGED" ] || { echo "no src/ changes between $BASE_REF and HEAD — nothing to A/B"; exit 1; }
42
-
43
- # On exit: kill any eval daemons + restore the engine to HEAD.
44
- cleanup() {
45
- pkill -9 -f "serve --mcp --path $OUT/" 2>/dev/null
46
- git -C "$ENGINE" checkout HEAD -- $CHANGED 2>/dev/null
47
- ( cd "$ENGINE" && npm run build >/dev/null 2>&1 )
48
- }
49
- trap cleanup EXIT
50
-
51
- mkdir -p "$OUT"
52
- echo "###### engine=$ENGINE baseline=$BASE_REF"
53
- echo "###### changed: $(echo "$CHANGED" | tr '\n' ' ')"
54
- echo "###### target=$TARGET"
55
- echo "###### task=$TASK"
56
- echo
57
-
58
- # Two pristine copies so each arm starts clean (the agent edits its own copy).
59
- rm -rf "$OUT/t-new" "$OUT/t-base"
60
- rsync -a --exclude node_modules --exclude .git --exclude dist --exclude .homegraph "$TARGET/" "$OUT/t-new/"
61
- cp -R "$OUT/t-new" "$OUT/t-base"
62
-
63
- prewarm() { # target — spawn a persistent daemon (current $BIN) and wait for its socket
64
- pkill -9 -f "serve --mcp --path $1" 2>/dev/null
65
- HOMEGRAPH_DAEMON_IDLE_TIMEOUT_MS=1800000 node "$BIN" serve --mcp --path "$1" </dev/null >/dev/null 2>&1 &
66
- node -e 'const fs=require("fs");let n=0;const t=setInterval(()=>{if(fs.existsSync(process.argv[1]+"/.homegraph/daemon.sock")){clearInterval(t);process.exit(0)}if(n++>150){clearInterval(t);process.exit(1)}},100)' "$1" \
67
- && echo " daemon warm: $1" || echo " WARN: daemon never bound for $1 (arm may run without homegraph)"
68
- }
69
-
70
- run_arm() { # label, target-copy
71
- local label="$1" tgt="$2" c="$OUT/mcp-$1.json"
72
- # Connect to the pre-warmed daemon; skip the startup re-exec for a fast attach.
73
- printf '{"mcpServers":{"homegraph":{"command":"env","args":["HOMEGRAPH_WASM_RELAUNCHED=1","node","%s","serve","--mcp","--path","%s"]}}}' "$BIN" "$tgt" > "$c"
74
- prewarm "$tgt"
75
- echo "############## ARM [$label] ##############"
76
- ( cd "$tgt" && claude -p "$TASK" \
77
- --output-format stream-json --verbose --permission-mode bypassPermissions \
78
- --model "${MODEL:-sonnet}" --effort "${EFFORT:-high}" --max-budget-usd 4 --strict-mcp-config --mcp-config "$c" \
79
- </dev/null > "$OUT/run-$label.jsonl" 2>"$OUT/run-$label.err" )
80
- node "$PARSE" "$OUT/run-$label.jsonl" 2>&1 | grep -E "by type|Result" || echo " (parse failed — see $OUT/run-$label.jsonl)"
81
- pkill -9 -f "serve --mcp --path $tgt" 2>/dev/null
82
- echo
83
- }
84
-
85
- echo "== NEW build (HEAD) =="
86
- ( cd "$ENGINE" && npm run build >/dev/null 2>&1 ) && echo " built"
87
- node "$BIN" init "$OUT/t-new" >/dev/null 2>&1 && echo " indexed t-new"
88
- run_arm new "$OUT/t-new"
89
-
90
- echo "== BASELINE build ($BASE_REF) =="
91
- # Per-file: a file ADDED since baseline has no pathspec on the ref — and a
92
- # single multi-file checkout with one bad pathspec checks out NOTHING, which
93
- # silently ran the NEW build in the baseline arm. Absent-on-baseline → remove.
94
- for f in $CHANGED; do
95
- git -C "$ENGINE" checkout "$BASE_REF" -- "$f" 2>/dev/null || rm -f "$ENGINE/$f"
96
- done
97
- ( cd "$ENGINE" && npm run build >/dev/null 2>&1 ) && echo " built"
98
- node "$BIN" init "$OUT/t-base" >/dev/null 2>&1 && echo " indexed t-base"
99
- run_arm baseline "$OUT/t-base"
100
-
101
- echo "###### DONE. Compare the [new] vs [baseline] 'by type' counts above"
102
- echo "###### (especially Read vs mcp__homegraph__*). Full logs in: $OUT"
1
+ #!/usr/bin/env bash
2
+ # A/B a homegraph retrieval/steering change: the NEW build (current HEAD) vs a
3
+ # BASELINE build (a git ref) — BOTH with homegraph attached — on the same
4
+ # implementation task, measuring how many Read vs homegraph calls the agent
5
+ # makes. ISOLATES the change (unlike run-all.sh's with-vs-without). The agent
6
+ # works on a throwaway copy of the target, so your repos are never touched.
7
+ #
8
+ # Reliable attach (works even when this is itself run nested inside a Claude
9
+ # session): each arm PRE-WARMS a persistent homegraph daemon for its target so
10
+ # claude connects to an already-bound, index-loaded daemon instantly — before
11
+ # the agent's first turn — and SKIPS homegraph's startup re-exec via
12
+ # HOMEGRAPH_WASM_RELAUNCHED=1. Without this, on a multi-step task the agent
13
+ # dives into Read/grep before homegraph finishes its ~2-3s startup (worse under
14
+ # the CPU contention of a nested run) and runs with NO homegraph.
15
+ #
16
+ # Gotcha: claude's `system/init` snapshot can read status:"pending" / 0 tools
17
+ # even when the server then connects fine — judge by ACTUAL homegraph usage in
18
+ # parse-run.mjs's "by type", not the init line.
19
+ #
20
+ # Usage: ab-new-vs-baseline.sh <indexed-repo> "<task>" [baseline-ref]
21
+ # <indexed-repo> a repo with a .homegraph index (copied per arm)
22
+ # "<task>" an implementation task, e.g. "Add X to Y and wire it through"
23
+ # [baseline-ref] git ref for the BEFORE build (default: HEAD~1)
24
+ # Env: AGENT_EVAL_OUT (default: /tmp/ab-new-vs-baseline)
25
+ set -uo pipefail
26
+
27
+ TARGET="${1:?usage: ab-new-vs-baseline.sh <indexed-repo> \"<task>\" [baseline-ref]}"
28
+ TASK="${2:?task required}"
29
+ BASE_REF="${3:-HEAD~1}"
30
+ ENGINE="$(cd "$(dirname "$0")/../.." && pwd)"
31
+ BIN="$ENGINE/dist/bin/homegraph.js"
32
+ OUT="${AGENT_EVAL_OUT:-/tmp/ab-new-vs-baseline}"
33
+ PARSE="$ENGINE/scripts/agent-eval/parse-run.mjs"
34
+
35
+ command -v claude >/dev/null || { echo "claude CLI not on PATH"; exit 1; }
36
+ [ -d "$TARGET/.homegraph" ] || { echo "target not indexed: run 'homegraph init $TARGET' first"; exit 1; }
37
+ if ! git -C "$ENGINE" diff --quiet || ! git -C "$ENGINE" diff --cached --quiet; then
38
+ echo "engine repo has uncommitted changes — commit or stash first (this script checks files out)"; exit 1
39
+ fi
40
+ CHANGED=$(git -C "$ENGINE" diff --name-only "$BASE_REF" HEAD -- src 2>/dev/null)
41
+ [ -n "$CHANGED" ] || { echo "no src/ changes between $BASE_REF and HEAD — nothing to A/B"; exit 1; }
42
+
43
+ # On exit: kill any eval daemons + restore the engine to HEAD.
44
+ cleanup() {
45
+ pkill -9 -f "serve --mcp --path $OUT/" 2>/dev/null
46
+ git -C "$ENGINE" checkout HEAD -- $CHANGED 2>/dev/null
47
+ ( cd "$ENGINE" && npm run build >/dev/null 2>&1 )
48
+ }
49
+ trap cleanup EXIT
50
+
51
+ mkdir -p "$OUT"
52
+ echo "###### engine=$ENGINE baseline=$BASE_REF"
53
+ echo "###### changed: $(echo "$CHANGED" | tr '\n' ' ')"
54
+ echo "###### target=$TARGET"
55
+ echo "###### task=$TASK"
56
+ echo
57
+
58
+ # Two pristine copies so each arm starts clean (the agent edits its own copy).
59
+ rm -rf "$OUT/t-new" "$OUT/t-base"
60
+ rsync -a --exclude node_modules --exclude .git --exclude dist --exclude .homegraph "$TARGET/" "$OUT/t-new/"
61
+ cp -R "$OUT/t-new" "$OUT/t-base"
62
+
63
+ prewarm() { # target — spawn a persistent daemon (current $BIN) and wait for its socket
64
+ pkill -9 -f "serve --mcp --path $1" 2>/dev/null
65
+ HOMEGRAPH_DAEMON_IDLE_TIMEOUT_MS=1800000 node "$BIN" serve --mcp --path "$1" </dev/null >/dev/null 2>&1 &
66
+ node -e 'const fs=require("fs");let n=0;const t=setInterval(()=>{if(fs.existsSync(process.argv[1]+"/.homegraph/daemon.sock")){clearInterval(t);process.exit(0)}if(n++>150){clearInterval(t);process.exit(1)}},100)' "$1" \
67
+ && echo " daemon warm: $1" || echo " WARN: daemon never bound for $1 (arm may run without homegraph)"
68
+ }
69
+
70
+ run_arm() { # label, target-copy
71
+ local label="$1" tgt="$2" c="$OUT/mcp-$1.json"
72
+ # Connect to the pre-warmed daemon; skip the startup re-exec for a fast attach.
73
+ printf '{"mcpServers":{"homegraph":{"command":"env","args":["HOMEGRAPH_WASM_RELAUNCHED=1","node","%s","serve","--mcp","--path","%s"]}}}' "$BIN" "$tgt" > "$c"
74
+ prewarm "$tgt"
75
+ echo "############## ARM [$label] ##############"
76
+ ( cd "$tgt" && claude -p "$TASK" \
77
+ --output-format stream-json --verbose --permission-mode bypassPermissions \
78
+ --model "${MODEL:-sonnet}" --effort "${EFFORT:-high}" --max-budget-usd 4 --strict-mcp-config --mcp-config "$c" \
79
+ </dev/null > "$OUT/run-$label.jsonl" 2>"$OUT/run-$label.err" )
80
+ node "$PARSE" "$OUT/run-$label.jsonl" 2>&1 | grep -E "by type|Result" || echo " (parse failed — see $OUT/run-$label.jsonl)"
81
+ pkill -9 -f "serve --mcp --path $tgt" 2>/dev/null
82
+ echo
83
+ }
84
+
85
+ echo "== NEW build (HEAD) =="
86
+ ( cd "$ENGINE" && npm run build >/dev/null 2>&1 ) && echo " built"
87
+ node "$BIN" init "$OUT/t-new" >/dev/null 2>&1 && echo " indexed t-new"
88
+ run_arm new "$OUT/t-new"
89
+
90
+ echo "== BASELINE build ($BASE_REF) =="
91
+ # Per-file: a file ADDED since baseline has no pathspec on the ref — and a
92
+ # single multi-file checkout with one bad pathspec checks out NOTHING, which
93
+ # silently ran the NEW build in the baseline arm. Absent-on-baseline → remove.
94
+ for f in $CHANGED; do
95
+ git -C "$ENGINE" checkout "$BASE_REF" -- "$f" 2>/dev/null || rm -f "$ENGINE/$f"
96
+ done
97
+ ( cd "$ENGINE" && npm run build >/dev/null 2>&1 ) && echo " built"
98
+ node "$BIN" init "$OUT/t-base" >/dev/null 2>&1 && echo " indexed t-base"
99
+ run_arm baseline "$OUT/t-base"
100
+
101
+ echo "###### DONE. Compare the [new] vs [baseline] 'by type' counts above"
102
+ echo "###### (especially Read vs mcp__homegraph__*). Full logs in: $OUT"
@@ -1,78 +1,78 @@
1
- #!/usr/bin/env bash
2
- # Sufficiency A/B: on a real understanding/flow question, WHEN the agent uses
3
- # homegraph (explore/node), does it still Read? Premise under test: explore/node
4
- # return source WITH line numbers, so a Read should not be needed.
5
- #
6
- # WITH homegraph (pre-warmed daemon, reliable nested attach) vs WITHOUT (empty
7
- # MCP, Read/Grep only), N runs each, on a throwaway copy of the repo. Reports
8
- # explore/node vs Read/Grep, and LISTS the files Read in the WITH arm so a true
9
- # sufficiency gap (an indexed source file) is distinguishable from out-of-scope
10
- # (configs, docs, a file homegraph didn't index).
11
- #
12
- # Usage: ab-sufficiency.sh <indexed-repo> "<question>" [runs-per-arm]
13
- # Env: AGENT_EVAL_OUT (default: /tmp/ab-sufficiency)
14
- set -uo pipefail
15
- REPO="${1:?usage: ab-sufficiency.sh <indexed-repo> \"<question>\" [runs]}"
16
- Q="${2:?question required}"
17
- RUNS="${3:-2}"
18
- ENGINE="$(cd "$(dirname "$0")/../.." && pwd)"
19
- BIN="$ENGINE/dist/bin/homegraph.js"
20
- OUT="${AGENT_EVAL_OUT:-/tmp/ab-sufficiency}"
21
- TGT="$OUT/target"
22
- command -v claude >/dev/null || { echo "claude CLI not on PATH"; exit 1; }
23
- [ -d "$REPO/.homegraph" ] || { echo "no .homegraph index at $REPO"; exit 1; }
24
- cleanup(){ pkill -9 -f "serve --mcp --path $TGT" 2>/dev/null; }
25
- trap cleanup EXIT
26
- mkdir -p "$OUT"
27
- ( cd "$ENGINE" && npm run build >/dev/null 2>&1 ) && echo "built"
28
-
29
- # Throwaway copy + fresh index (the agent works here; a read-only question won't
30
- # edit, but isolate anyway). Excludes the source repo's index/build/vcs.
31
- rm -rf "$TGT"
32
- rsync -a --exclude node_modules --exclude .git --exclude dist --exclude .homegraph "$REPO/" "$TGT/"
33
- node "$BIN" init "$TGT" >/dev/null 2>&1 && echo "indexed copy ($(node "$BIN" status --json 2>/dev/null | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).fileCount+" files")}catch{console.log("?")}})' 2>/dev/null || echo '?'))"
34
-
35
- echo "###### repo=$REPO runs/arm=$RUNS"
36
- echo "###### Q=$Q"; echo
37
- echo '{"mcpServers":{}}' > "$OUT/mcp-empty.json"
38
- printf '{"mcpServers":{"homegraph":{"command":"env","args":["HOMEGRAPH_WASM_RELAUNCHED=1","node","%s","serve","--mcp","--path","%s"]}}}' "$BIN" "$TGT" > "$OUT/mcp-cg.json"
39
-
40
- prewarm(){
41
- pkill -9 -f "serve --mcp --path $TGT" 2>/dev/null
42
- HOMEGRAPH_DAEMON_IDLE_TIMEOUT_MS=1800000 node "$BIN" serve --mcp --path "$TGT" </dev/null >/dev/null 2>&1 &
43
- node -e 'const fs=require("fs");let n=0;const t=setInterval(()=>{if(fs.existsSync(process.argv[1]+"/.homegraph/daemon.sock")){clearInterval(t);process.exit(0)}if(n++>150){clearInterval(t);process.exit(1)}},100)' "$TGT" >/dev/null 2>&1
44
- }
45
-
46
- analyze(){
47
- node -e '
48
- const fs=require("fs");
49
- const L=fs.readFileSync(process.argv[1],"utf8").split("\n").filter(Boolean);
50
- let ex=0,nf=0,ns=0,oc=0,gr=0,exposed="?";const reads=[];
51
- for(const l of L){try{const o=JSON.parse(l);
52
- if(o.type==="system"&&o.subtype==="init")exposed=(o.tools||[]).filter(t=>/homegraph/.test(t)).length;
53
- for(const b of (o.message?.content||[])){if(b.type!=="tool_use")continue;
54
- if(b.name==="mcp__homegraph__homegraph_explore")ex++;
55
- else if(b.name==="mcp__homegraph__homegraph_node"){if(b.input&&b.input.symbol)ns++;else nf++;}
56
- else if(/mcp__homegraph__/.test(b.name))oc++;
57
- else if(b.name==="Read")reads.push((b.input?.file_path||"").split("/").pop());
58
- else if(b.name==="Grep")gr++;
59
- }}catch{}}
60
- console.log(` explore=${ex} node[sym]=${ns} node[file]=${nf} other_cg=${oc} | Read=${reads.length}${reads.length?" ("+reads.join(", ")+")":""} Grep=${gr} [cg exposed=${exposed}]`);
61
- ' "$1"
62
- }
63
-
64
- run(){ # label, cfg, prewarm(0/1)
65
- local label="$1" cfg="$2" pw="$3"
66
- for i in $(seq 1 "$RUNS"); do
67
- [ "$pw" = "1" ] && prewarm
68
- ( cd "$TGT" && claude -p "$Q" --output-format stream-json --verbose \
69
- --permission-mode bypassPermissions --model "${MODEL:-sonnet}" --effort "${EFFORT:-high}" --max-budget-usd 4 \
70
- --strict-mcp-config --mcp-config "$cfg" </dev/null > "$OUT/$label-$i.jsonl" 2>"$OUT/$label-$i.err" )
71
- echo "[$label] run $i:"; analyze "$OUT/$label-$i.jsonl"
72
- done
73
- echo
74
- }
75
-
76
- echo "== WITH homegraph (premise: explore/node used -> Read ~0) =="; run with "$OUT/mcp-cg.json" 1
77
- echo "== WITHOUT (Read/Grep only — the contrast) =="; run without "$OUT/mcp-empty.json" 0
78
- echo "###### DONE. In the WITH arm: are explore/node>0 and Read~0? Any Read of an INDEXED source file = sufficiency gap. Logs: $OUT"
1
+ #!/usr/bin/env bash
2
+ # Sufficiency A/B: on a real understanding/flow question, WHEN the agent uses
3
+ # homegraph (explore/node), does it still Read? Premise under test: explore/node
4
+ # return source WITH line numbers, so a Read should not be needed.
5
+ #
6
+ # WITH homegraph (pre-warmed daemon, reliable nested attach) vs WITHOUT (empty
7
+ # MCP, Read/Grep only), N runs each, on a throwaway copy of the repo. Reports
8
+ # explore/node vs Read/Grep, and LISTS the files Read in the WITH arm so a true
9
+ # sufficiency gap (an indexed source file) is distinguishable from out-of-scope
10
+ # (configs, docs, a file homegraph didn't index).
11
+ #
12
+ # Usage: ab-sufficiency.sh <indexed-repo> "<question>" [runs-per-arm]
13
+ # Env: AGENT_EVAL_OUT (default: /tmp/ab-sufficiency)
14
+ set -uo pipefail
15
+ REPO="${1:?usage: ab-sufficiency.sh <indexed-repo> \"<question>\" [runs]}"
16
+ Q="${2:?question required}"
17
+ RUNS="${3:-2}"
18
+ ENGINE="$(cd "$(dirname "$0")/../.." && pwd)"
19
+ BIN="$ENGINE/dist/bin/homegraph.js"
20
+ OUT="${AGENT_EVAL_OUT:-/tmp/ab-sufficiency}"
21
+ TGT="$OUT/target"
22
+ command -v claude >/dev/null || { echo "claude CLI not on PATH"; exit 1; }
23
+ [ -d "$REPO/.homegraph" ] || { echo "no .homegraph index at $REPO"; exit 1; }
24
+ cleanup(){ pkill -9 -f "serve --mcp --path $TGT" 2>/dev/null; }
25
+ trap cleanup EXIT
26
+ mkdir -p "$OUT"
27
+ ( cd "$ENGINE" && npm run build >/dev/null 2>&1 ) && echo "built"
28
+
29
+ # Throwaway copy + fresh index (the agent works here; a read-only question won't
30
+ # edit, but isolate anyway). Excludes the source repo's index/build/vcs.
31
+ rm -rf "$TGT"
32
+ rsync -a --exclude node_modules --exclude .git --exclude dist --exclude .homegraph "$REPO/" "$TGT/"
33
+ node "$BIN" init "$TGT" >/dev/null 2>&1 && echo "indexed copy ($(node "$BIN" status --json 2>/dev/null | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).fileCount+" files")}catch{console.log("?")}})' 2>/dev/null || echo '?'))"
34
+
35
+ echo "###### repo=$REPO runs/arm=$RUNS"
36
+ echo "###### Q=$Q"; echo
37
+ echo '{"mcpServers":{}}' > "$OUT/mcp-empty.json"
38
+ printf '{"mcpServers":{"homegraph":{"command":"env","args":["HOMEGRAPH_WASM_RELAUNCHED=1","node","%s","serve","--mcp","--path","%s"]}}}' "$BIN" "$TGT" > "$OUT/mcp-cg.json"
39
+
40
+ prewarm(){
41
+ pkill -9 -f "serve --mcp --path $TGT" 2>/dev/null
42
+ HOMEGRAPH_DAEMON_IDLE_TIMEOUT_MS=1800000 node "$BIN" serve --mcp --path "$TGT" </dev/null >/dev/null 2>&1 &
43
+ node -e 'const fs=require("fs");let n=0;const t=setInterval(()=>{if(fs.existsSync(process.argv[1]+"/.homegraph/daemon.sock")){clearInterval(t);process.exit(0)}if(n++>150){clearInterval(t);process.exit(1)}},100)' "$TGT" >/dev/null 2>&1
44
+ }
45
+
46
+ analyze(){
47
+ node -e '
48
+ const fs=require("fs");
49
+ const L=fs.readFileSync(process.argv[1],"utf8").split("\n").filter(Boolean);
50
+ let ex=0,nf=0,ns=0,oc=0,gr=0,exposed="?";const reads=[];
51
+ for(const l of L){try{const o=JSON.parse(l);
52
+ if(o.type==="system"&&o.subtype==="init")exposed=(o.tools||[]).filter(t=>/homegraph/.test(t)).length;
53
+ for(const b of (o.message?.content||[])){if(b.type!=="tool_use")continue;
54
+ if(b.name==="mcp__homegraph__homegraph_explore")ex++;
55
+ else if(b.name==="mcp__homegraph__homegraph_node"){if(b.input&&b.input.symbol)ns++;else nf++;}
56
+ else if(/mcp__homegraph__/.test(b.name))oc++;
57
+ else if(b.name==="Read")reads.push((b.input?.file_path||"").split("/").pop());
58
+ else if(b.name==="Grep")gr++;
59
+ }}catch{}}
60
+ console.log(` explore=${ex} node[sym]=${ns} node[file]=${nf} other_cg=${oc} | Read=${reads.length}${reads.length?" ("+reads.join(", ")+")":""} Grep=${gr} [cg exposed=${exposed}]`);
61
+ ' "$1"
62
+ }
63
+
64
+ run(){ # label, cfg, prewarm(0/1)
65
+ local label="$1" cfg="$2" pw="$3"
66
+ for i in $(seq 1 "$RUNS"); do
67
+ [ "$pw" = "1" ] && prewarm
68
+ ( cd "$TGT" && claude -p "$Q" --output-format stream-json --verbose \
69
+ --permission-mode bypassPermissions --model "${MODEL:-sonnet}" --effort "${EFFORT:-high}" --max-budget-usd 4 \
70
+ --strict-mcp-config --mcp-config "$cfg" </dev/null > "$OUT/$label-$i.jsonl" 2>"$OUT/$label-$i.err" )
71
+ echo "[$label] run $i:"; analyze "$OUT/$label-$i.jsonl"
72
+ done
73
+ echo
74
+ }
75
+
76
+ echo "== WITH homegraph (premise: explore/node used -> Read ~0) =="; run with "$OUT/mcp-cg.json" 1
77
+ echo "== WITHOUT (Read/Grep only — the contrast) =="; run without "$OUT/mcp-empty.json" 0
78
+ echo "###### DONE. In the WITH arm: are explore/node>0 and Read~0? Any Read of an INDEXED source file = sufficiency gap. Logs: $OUT"
@@ -1,21 +1,21 @@
1
- #!/usr/bin/env bash
2
- # Arm F (body-inlining trace + trace-first steering) across the same 6 repos as
3
- # arms-matrix.sh, so F vs B isolates the trace-enrichment effect (same surface,
4
- # old thin trace in B vs body-inlining trace here).
5
- set -uo pipefail
6
- H="$(cd "$(dirname "$0")" && pwd)"; RUNS="${RUNS:-2}"; C="${CORPUS:-/tmp/homegraph-corpus}"
7
- ROWS=(
8
- "$C/flutter-samples/add_to_app/books/flutter_module_books|How does the books UI build and what child widgets does it show?"
9
- "$C/aspnet-realworld|How is creating an article handled? Trace the controller to the service."
10
- "$C/spring-mall|How is a product-list request handled? Trace the controller to the service."
11
- "$C/vapor-spi|How is a package-show request handled? Name the route and controller."
12
- "$C/excalidraw|How does updating an element re-render the canvas on screen? Trace the flow."
13
- "$C/spring-halo|How is publishing a post handled? Trace the controller to the service."
14
- )
15
- ARM="${ARM:-F}"
16
- echo "### ARM $ARM START $(date) RUNS=$RUNS"
17
- for row in "${ROWS[@]}"; do
18
- repo="${row%%|*}"; q="${row#*|}"
19
- for r in $(seq 1 "$RUNS"); do bash "$H/run-arms.sh" "$repo" "$q" "$ARM" "$r"; done
20
- done
21
- echo "### ARM $ARM COMPLETE $(date)"
1
+ #!/usr/bin/env bash
2
+ # Arm F (body-inlining trace + trace-first steering) across the same 6 repos as
3
+ # arms-matrix.sh, so F vs B isolates the trace-enrichment effect (same surface,
4
+ # old thin trace in B vs body-inlining trace here).
5
+ set -uo pipefail
6
+ H="$(cd "$(dirname "$0")" && pwd)"; RUNS="${RUNS:-2}"; C="${CORPUS:-/tmp/homegraph-corpus}"
7
+ ROWS=(
8
+ "$C/flutter-samples/add_to_app/books/flutter_module_books|How does the books UI build and what child widgets does it show?"
9
+ "$C/aspnet-realworld|How is creating an article handled? Trace the controller to the service."
10
+ "$C/spring-mall|How is a product-list request handled? Trace the controller to the service."
11
+ "$C/vapor-spi|How is a package-show request handled? Name the route and controller."
12
+ "$C/excalidraw|How does updating an element re-render the canvas on screen? Trace the flow."
13
+ "$C/spring-halo|How is publishing a post handled? Trace the controller to the service."
14
+ )
15
+ ARM="${ARM:-F}"
16
+ echo "### ARM $ARM START $(date) RUNS=$RUNS"
17
+ for row in "${ROWS[@]}"; do
18
+ repo="${row%%|*}"; q="${row#*|}"
19
+ for r in $(seq 1 "$RUNS"); do bash "$H/run-arms.sh" "$repo" "$q" "$ARM" "$r"; done
20
+ done
21
+ echo "### ARM $ARM COMPLETE $(date)"
@@ -1,37 +1,37 @@
1
- #!/usr/bin/env bash
2
- # Drive the tool-surface ablation across the chosen repos × arms (A–E).
3
- # Arms A–D ask the canonical FLOW question; arm E asks a NON-flow survey
4
- # question (the control probe — should degrade without explore+context).
5
- # Output: /tmp/arms/<repo>/<arm>-r<n>.jsonl (parse with parse-arms.mjs).
6
- set -uo pipefail
7
- HARNESS="$(cd "$(dirname "$0")" && pwd)"
8
- RUNS="${RUNS:-2}"
9
- C="${CORPUS:-/tmp/homegraph-corpus}"
10
- NFQ='What are the main modules/components of this codebase and what does each one do? Give an overview of how it is organized.'
11
-
12
- # repo-path|flow-question (2 small, 2 medium, 2 large — spans the size range)
13
- ROWS=(
14
- "$C/flutter-samples/add_to_app/books/flutter_module_books|How does the books UI build and what child widgets does it show?"
15
- "$C/aspnet-realworld|How is creating an article handled? Trace the controller to the service."
16
- "$C/spring-mall|How is a product-list request handled? Trace the controller to the service."
17
- "$C/vapor-spi|How is a package-show request handled? Name the route and controller."
18
- "$C/excalidraw|How does updating an element re-render the canvas on screen? Trace the flow."
19
- "$C/spring-halo|How is publishing a post handled? Trace the controller to the service."
20
- )
21
-
22
- echo "### ARMS MATRIX START $(date) RUNS=$RUNS"
23
- for row in "${ROWS[@]}"; do
24
- repo="${row%%|*}"; q="${row#*|}"
25
- for arm in A B C D; do
26
- for r in $(seq 1 "$RUNS"); do
27
- bash "$HARNESS/run-arms.sh" "$repo" "$q" "$arm" "$r"
28
- done
29
- done
30
- done
31
- # E: non-flow control probe on two repos (must degrade without explore+context)
32
- for repo in "$C/excalidraw" "$C/spring-mall"; do
33
- for r in $(seq 1 "$RUNS"); do
34
- bash "$HARNESS/run-arms.sh" "$repo" "$NFQ" E "$r"
35
- done
36
- done
37
- echo "### ARMS MATRIX COMPLETE $(date)"
1
+ #!/usr/bin/env bash
2
+ # Drive the tool-surface ablation across the chosen repos × arms (A–E).
3
+ # Arms A–D ask the canonical FLOW question; arm E asks a NON-flow survey
4
+ # question (the control probe — should degrade without explore+context).
5
+ # Output: /tmp/arms/<repo>/<arm>-r<n>.jsonl (parse with parse-arms.mjs).
6
+ set -uo pipefail
7
+ HARNESS="$(cd "$(dirname "$0")" && pwd)"
8
+ RUNS="${RUNS:-2}"
9
+ C="${CORPUS:-/tmp/homegraph-corpus}"
10
+ NFQ='What are the main modules/components of this codebase and what does each one do? Give an overview of how it is organized.'
11
+
12
+ # repo-path|flow-question (2 small, 2 medium, 2 large — spans the size range)
13
+ ROWS=(
14
+ "$C/flutter-samples/add_to_app/books/flutter_module_books|How does the books UI build and what child widgets does it show?"
15
+ "$C/aspnet-realworld|How is creating an article handled? Trace the controller to the service."
16
+ "$C/spring-mall|How is a product-list request handled? Trace the controller to the service."
17
+ "$C/vapor-spi|How is a package-show request handled? Name the route and controller."
18
+ "$C/excalidraw|How does updating an element re-render the canvas on screen? Trace the flow."
19
+ "$C/spring-halo|How is publishing a post handled? Trace the controller to the service."
20
+ )
21
+
22
+ echo "### ARMS MATRIX START $(date) RUNS=$RUNS"
23
+ for row in "${ROWS[@]}"; do
24
+ repo="${row%%|*}"; q="${row#*|}"
25
+ for arm in A B C D; do
26
+ for r in $(seq 1 "$RUNS"); do
27
+ bash "$HARNESS/run-arms.sh" "$repo" "$q" "$arm" "$r"
28
+ done
29
+ done
30
+ done
31
+ # E: non-flow control probe on two repos (must degrade without explore+context)
32
+ for repo in "$C/excalidraw" "$C/spring-mall"; do
33
+ for r in $(seq 1 "$RUNS"); do
34
+ bash "$HARNESS/run-arms.sh" "$repo" "$NFQ" E "$r"
35
+ done
36
+ done
37
+ echo "### ARMS MATRIX COMPLETE $(date)"
@@ -1,68 +1,68 @@
1
- #!/usr/bin/env bash
2
- # One-shot HomeGraph quality audit:
3
- # set version -> ensure corpus repo -> wipe+reindex with that version ->
4
- # run with/without A/B -> restore the local dev link.
5
- #
6
- # Usage: audit.sh <version> <repo-name> <repo-url> "<question>" [headless|all]
7
- # <version> "local" (build + npm link this repo) | "latest" | a version (e.g. 0.7.10)
8
- # <repo-name> dir name under the corpus dir
9
- # <repo-url> git URL (cloned --depth 1 when the repo dir is missing)
10
- # [mode] headless (default) | all (also the interactive tmux arms)
11
- # Env: CORPUS corpus dir (default: /tmp/homegraph-corpus)
12
- set -uo pipefail
13
-
14
- VERSION="${1:?usage: audit.sh <version> <repo-name> <repo-url> \"<question>\" [mode]}"
15
- NAME="${2:?repo-name required}"
16
- URL="${3:?repo-url required}"
17
- Q="${4:?question required}"
18
- MODE="${5:-headless}"
19
-
20
- HARNESS="$(cd "$(dirname "$0")" && pwd)"
21
- REPO_ROOT="$(cd "$HARNESS/../.." && pwd)" # homegraph repo root
22
- CORPUS="${CORPUS:-/tmp/homegraph-corpus}"
23
- REPO="$CORPUS/$NAME"
24
- PKG="homegraph"
25
-
26
- echo "==================== HomeGraph audit ===================="
27
- echo "version=$VERSION repo=$NAME mode=$MODE corpus=$CORPUS"
28
- echo
29
-
30
- # 1. Set the homegraph version under test (mutates the global install).
31
- if [ "$VERSION" = local ]; then
32
- echo "→ [1/4] building + linking local dev build (local-install.sh)"
33
- ( cd "$REPO_ROOT" && ./scripts/local-install.sh ) || { echo "local-install.sh failed"; exit 1; }
34
- else
35
- echo "→ [1/4] installing $PKG@$VERSION globally"
36
- npm install -g "$PKG@$VERSION" || { echo "npm install -g $PKG@$VERSION failed"; exit 1; }
37
- fi
38
- ACTUAL="$(homegraph --version 2>/dev/null || echo '?')"
39
- echo " homegraph on PATH: $(command -v homegraph) -> $ACTUAL"
40
-
41
- # 2. Ensure the corpus repo exists (clone shallow if missing, reuse if present).
42
- mkdir -p "$CORPUS"
43
- if [ -d "$REPO/.git" ]; then
44
- echo "→ [2/4] reusing existing checkout: $REPO"
45
- else
46
- echo "→ [2/4] cloning $URL"
47
- git clone --depth 1 "$URL" "$REPO" || { echo "git clone failed"; exit 1; }
48
- fi
49
-
50
- # 3. Wipe + re-index with THIS version (the index must be built by the same
51
- # binary that serves it — different versions extract differently).
52
- echo "→ [3/4] wiping .homegraph and re-indexing with $ACTUAL"
53
- rm -rf "$REPO/.homegraph"
54
- ( cd "$REPO" && homegraph init -i ) || { echo "indexing failed"; exit 1; }
55
-
56
- # 4. Run the with/without A/B.
57
- echo "→ [4/4] running A/B harness (mode=$MODE)"
58
- bash "$HARNESS/run-all.sh" "$REPO" "$Q" "$MODE"
59
-
60
- # Restore the dev link (the normal working state in this repo).
61
- echo
62
- echo "→ restoring local dev link (local-install.sh)"
63
- if ( cd "$REPO_ROOT" && ./scripts/local-install.sh >/dev/null 2>&1 ); then
64
- echo " global homegraph restored to dev build"
65
- else
66
- echo " WARN: restore failed — run ./scripts/local-install.sh manually"
67
- fi
68
- echo "==================== audit complete ===================="
1
+ #!/usr/bin/env bash
2
+ # One-shot HomeGraph quality audit:
3
+ # set version -> ensure corpus repo -> wipe+reindex with that version ->
4
+ # run with/without A/B -> restore the local dev link.
5
+ #
6
+ # Usage: audit.sh <version> <repo-name> <repo-url> "<question>" [headless|all]
7
+ # <version> "local" (build + npm link this repo) | "latest" | a version (e.g. 0.7.10)
8
+ # <repo-name> dir name under the corpus dir
9
+ # <repo-url> git URL (cloned --depth 1 when the repo dir is missing)
10
+ # [mode] headless (default) | all (also the interactive tmux arms)
11
+ # Env: CORPUS corpus dir (default: /tmp/homegraph-corpus)
12
+ set -uo pipefail
13
+
14
+ VERSION="${1:?usage: audit.sh <version> <repo-name> <repo-url> \"<question>\" [mode]}"
15
+ NAME="${2:?repo-name required}"
16
+ URL="${3:?repo-url required}"
17
+ Q="${4:?question required}"
18
+ MODE="${5:-headless}"
19
+
20
+ HARNESS="$(cd "$(dirname "$0")" && pwd)"
21
+ REPO_ROOT="$(cd "$HARNESS/../.." && pwd)" # homegraph repo root
22
+ CORPUS="${CORPUS:-/tmp/homegraph-corpus}"
23
+ REPO="$CORPUS/$NAME"
24
+ PKG="homegraph"
25
+
26
+ echo "==================== HomeGraph audit ===================="
27
+ echo "version=$VERSION repo=$NAME mode=$MODE corpus=$CORPUS"
28
+ echo
29
+
30
+ # 1. Set the homegraph version under test (mutates the global install).
31
+ if [ "$VERSION" = local ]; then
32
+ echo "→ [1/4] building + linking local dev build (local-install.sh)"
33
+ ( cd "$REPO_ROOT" && ./scripts/local-install.sh ) || { echo "local-install.sh failed"; exit 1; }
34
+ else
35
+ echo "→ [1/4] installing $PKG@$VERSION globally"
36
+ npm install -g "$PKG@$VERSION" || { echo "npm install -g $PKG@$VERSION failed"; exit 1; }
37
+ fi
38
+ ACTUAL="$(homegraph --version 2>/dev/null || echo '?')"
39
+ echo " homegraph on PATH: $(command -v homegraph) -> $ACTUAL"
40
+
41
+ # 2. Ensure the corpus repo exists (clone shallow if missing, reuse if present).
42
+ mkdir -p "$CORPUS"
43
+ if [ -d "$REPO/.git" ]; then
44
+ echo "→ [2/4] reusing existing checkout: $REPO"
45
+ else
46
+ echo "→ [2/4] cloning $URL"
47
+ git clone --depth 1 "$URL" "$REPO" || { echo "git clone failed"; exit 1; }
48
+ fi
49
+
50
+ # 3. Wipe + re-index with THIS version (the index must be built by the same
51
+ # binary that serves it — different versions extract differently).
52
+ echo "→ [3/4] wiping .homegraph and re-indexing with $ACTUAL"
53
+ rm -rf "$REPO/.homegraph"
54
+ ( cd "$REPO" && homegraph init -i ) || { echo "indexing failed"; exit 1; }
55
+
56
+ # 4. Run the with/without A/B.
57
+ echo "→ [4/4] running A/B harness (mode=$MODE)"
58
+ bash "$HARNESS/run-all.sh" "$REPO" "$Q" "$MODE"
59
+
60
+ # Restore the dev link (the normal working state in this repo).
61
+ echo
62
+ echo "→ restoring local dev link (local-install.sh)"
63
+ if ( cd "$REPO_ROOT" && ./scripts/local-install.sh >/dev/null 2>&1 ); then
64
+ echo " global homegraph restored to dev build"
65
+ else
66
+ echo " WARN: restore failed — run ./scripts/local-install.sh manually"
67
+ fi
68
+ echo "==================== audit complete ===================="