homegraph 1.1.2 → 1.1.3
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/LICENSE +21 -21
- package/README.md +243 -243
- package/dist/bin/homegraph.js +371 -9
- package/dist/bin/homegraph.js.map +1 -1
- package/dist/db/migrations.js +18 -18
- package/dist/db/queries.js +140 -140
- package/dist/db/schema.sql +152 -152
- package/dist/directory.js +5 -5
- package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/dist/installer/instructions-template.js +9 -9
- package/dist/installer/targets/shared.d.ts +5 -6
- package/dist/installer/targets/shared.d.ts.map +1 -1
- package/dist/installer/targets/shared.js +5 -6
- package/dist/installer/targets/shared.js.map +1 -1
- package/dist/mcp/liveness-watchdog.js +16 -16
- package/dist/mcp/server-instructions.js +69 -69
- package/dist/mcp/tools.d.ts +17 -4
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +192 -22
- package/dist/mcp/tools.js.map +1 -1
- package/dist/reasoning/reasoner.js +32 -32
- package/dist/spec/config.d.ts +43 -0
- package/dist/spec/config.d.ts.map +1 -0
- package/dist/spec/config.js +275 -0
- package/dist/spec/config.js.map +1 -0
- package/dist/spec/db/commit-node.d.ts +23 -0
- package/dist/spec/db/commit-node.d.ts.map +1 -0
- package/dist/spec/db/commit-node.js +62 -0
- package/dist/spec/db/commit-node.js.map +1 -0
- package/dist/spec/db/fragment-node.d.ts +23 -0
- package/dist/spec/db/fragment-node.d.ts.map +1 -0
- package/dist/spec/db/fragment-node.js +120 -0
- package/dist/spec/db/fragment-node.js.map +1 -0
- package/dist/spec/db/fts.d.ts +60 -0
- package/dist/spec/db/fts.d.ts.map +1 -0
- package/dist/spec/db/fts.js +285 -0
- package/dist/spec/db/fts.js.map +1 -0
- package/dist/spec/db/index.d.ts +13 -0
- package/dist/spec/db/index.d.ts.map +1 -0
- package/dist/spec/db/index.js +50 -0
- package/dist/spec/db/index.js.map +1 -0
- package/dist/spec/db/relations.d.ts +55 -0
- package/dist/spec/db/relations.d.ts.map +1 -0
- package/dist/spec/db/relations.js +158 -0
- package/dist/spec/db/relations.js.map +1 -0
- package/dist/spec/db/schema.d.ts +30 -0
- package/dist/spec/db/schema.d.ts.map +1 -0
- package/dist/spec/db/schema.js +87 -0
- package/dist/spec/db/schema.js.map +1 -0
- package/dist/spec/db/schema.sql +107 -0
- package/dist/spec/db/spec-node.d.ts +41 -0
- package/dist/spec/db/spec-node.d.ts.map +1 -0
- package/dist/spec/db/spec-node.js +114 -0
- package/dist/spec/db/spec-node.js.map +1 -0
- package/dist/spec/evolve/impact-locator.d.ts +13 -0
- package/dist/spec/evolve/impact-locator.d.ts.map +1 -0
- package/dist/spec/evolve/impact-locator.js +25 -0
- package/dist/spec/evolve/impact-locator.js.map +1 -0
- package/dist/spec/evolve/llm-client.d.ts +50 -0
- package/dist/spec/evolve/llm-client.d.ts.map +1 -0
- package/dist/spec/evolve/llm-client.js +176 -0
- package/dist/spec/evolve/llm-client.js.map +1 -0
- package/dist/spec/evolve/logic-checker.d.ts +12 -0
- package/dist/spec/evolve/logic-checker.d.ts.map +1 -0
- package/dist/spec/evolve/logic-checker.js +48 -0
- package/dist/spec/evolve/logic-checker.js.map +1 -0
- package/dist/spec/evolve/pipeline.d.ts +40 -0
- package/dist/spec/evolve/pipeline.d.ts.map +1 -0
- package/dist/spec/evolve/pipeline.js +314 -0
- package/dist/spec/evolve/pipeline.js.map +1 -0
- package/dist/spec/evolve/spec-rewriter.d.ts +42 -0
- package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -0
- package/dist/spec/evolve/spec-rewriter.js +254 -0
- package/dist/spec/evolve/spec-rewriter.js.map +1 -0
- package/dist/spec/graph/queries.d.ts +58 -0
- package/dist/spec/graph/queries.d.ts.map +1 -0
- package/dist/spec/graph/queries.js +215 -0
- package/dist/spec/graph/queries.js.map +1 -0
- package/dist/spec/mining/diff-parser.d.ts +33 -0
- package/dist/spec/mining/diff-parser.d.ts.map +1 -0
- package/dist/spec/mining/diff-parser.js +166 -0
- package/dist/spec/mining/diff-parser.js.map +1 -0
- package/dist/spec/mining/git-scanner.d.ts +91 -0
- package/dist/spec/mining/git-scanner.d.ts.map +1 -0
- package/dist/spec/mining/git-scanner.js +266 -0
- package/dist/spec/mining/git-scanner.js.map +1 -0
- package/dist/spec/mining/pipeline.d.ts +53 -0
- package/dist/spec/mining/pipeline.d.ts.map +1 -0
- package/dist/spec/mining/pipeline.js +165 -0
- package/dist/spec/mining/pipeline.js.map +1 -0
- package/dist/spec/mining/scope-resolver.d.ts +45 -0
- package/dist/spec/mining/scope-resolver.d.ts.map +1 -0
- package/dist/spec/mining/scope-resolver.js +103 -0
- package/dist/spec/mining/scope-resolver.js.map +1 -0
- package/dist/spec/mining/spec-extractor.d.ts +69 -0
- package/dist/spec/mining/spec-extractor.d.ts.map +1 -0
- package/dist/spec/mining/spec-extractor.js +369 -0
- package/dist/spec/mining/spec-extractor.js.map +1 -0
- package/dist/spec/types.d.ts +149 -0
- package/dist/spec/types.d.ts.map +1 -0
- package/dist/spec/types.js +15 -0
- package/dist/spec/types.js.map +1 -0
- package/dist/spec/utils.d.ts +166 -0
- package/dist/spec/utils.d.ts.map +1 -0
- package/dist/spec/utils.js +461 -0
- package/dist/spec/utils.js.map +1 -0
- package/package.json +58 -57
- package/scripts/add-lang/bench.sh +60 -60
- package/scripts/add-lang/check-grammar.mjs +75 -75
- package/scripts/add-lang/dump-ast.mjs +103 -103
- package/scripts/add-lang/verify-extraction.mjs +70 -70
- package/scripts/agent-eval/ab-adoption.sh +91 -91
- package/scripts/agent-eval/ab-hook.sh +86 -86
- package/scripts/agent-eval/ab-impl.sh +78 -78
- package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -102
- package/scripts/agent-eval/ab-sufficiency.sh +78 -78
- package/scripts/agent-eval/arms-F.sh +21 -21
- package/scripts/agent-eval/arms-matrix.sh +37 -37
- package/scripts/agent-eval/audit.sh +68 -68
- package/scripts/agent-eval/bench-readme.sh +28 -28
- package/scripts/agent-eval/bench-why-repo.sh +22 -22
- package/scripts/agent-eval/block-read-hook.sh +19 -19
- package/scripts/agent-eval/hook-settings.json +15 -15
- package/scripts/agent-eval/itrun.sh +120 -120
- package/scripts/agent-eval/offload-eval-3arm.sh +72 -72
- package/scripts/agent-eval/offload-eval-cost.mjs +133 -133
- package/scripts/agent-eval/offload-eval-effort.mjs +108 -108
- package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -25
- package/scripts/agent-eval/offload-eval-frontload.sh +47 -47
- package/scripts/agent-eval/offload-eval-ground-truth.json +18 -18
- package/scripts/agent-eval/offload-eval-hook.mjs +84 -84
- package/scripts/agent-eval/offload-eval-judge.mjs +103 -103
- package/scripts/agent-eval/offload-eval-matrix.sh +20 -20
- package/scripts/agent-eval/offload-eval-metrics.mjs +94 -94
- package/scripts/agent-eval/offload-eval-refs1.sh +50 -50
- package/scripts/agent-eval/offload-eval-setup.sh +24 -24
- package/scripts/agent-eval/offload-eval-styles.sh +71 -71
- package/scripts/agent-eval/offload-eval-summarize.mjs +68 -68
- package/scripts/agent-eval/offload-eval.md +76 -76
- package/scripts/agent-eval/parse-arms.mjs +116 -116
- package/scripts/agent-eval/parse-bench-readme.mjs +84 -84
- package/scripts/agent-eval/parse-run.mjs +45 -45
- package/scripts/agent-eval/parse-session.mjs +93 -93
- package/scripts/agent-eval/probe-context.mjs +21 -21
- package/scripts/agent-eval/probe-explore.mjs +40 -40
- package/scripts/agent-eval/probe-node.mjs +20 -20
- package/scripts/agent-eval/probe-sweep.mjs +119 -119
- package/scripts/agent-eval/probe-trace.mjs +20 -20
- package/scripts/agent-eval/redirect-read-hook.sh +38 -38
- package/scripts/agent-eval/run-agent.sh +34 -34
- package/scripts/agent-eval/run-all.sh +69 -69
- package/scripts/agent-eval/run-arms.sh +56 -56
- package/scripts/agent-eval/seq-matrix.mjs +137 -137
- package/scripts/build-bundle.sh +118 -118
- package/scripts/extract-release-notes.mjs +130 -130
- package/scripts/local-install.sh +41 -41
- package/scripts/npm-sdk.js +75 -75
- package/scripts/npm-shim.js +246 -246
- package/scripts/pack-npm.sh +119 -119
- package/scripts/prepare-release.mjs +270 -270
- package/scripts/qa_eval/README.md +407 -404
- package/scripts/qa_eval/_test_deveco_probe.py +41 -41
- package/scripts/qa_eval/agent_runner.py +526 -526
- package/scripts/qa_eval/data/.gitignore +4 -4
- package/scripts/qa_eval/data/test-set.jsonl +2 -22
- package/scripts/qa_eval/eval_metrics.py +274 -233
- package/scripts/qa_eval/external_agent.py +976 -671
- package/scripts/qa_eval/llm_config.py +92 -92
- package/scripts/qa_eval/memory_monitor.py +132 -132
- package/scripts/qa_eval/my_answer_accuracy.py +187 -187
- package/scripts/qa_eval/requirements.txt +2 -2
- package/scripts/qa_eval/run_pipeline.py +804 -711
- package/scripts/qa_eval/stats_efficiency.py +279 -279
- package/scripts/qa_eval/stats_scores.py +207 -207
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ONE offload run on ONE indexed repo at a given offload STYLE (plain|refs), so we can
|
|
3
|
-
# watch a single agent transcript at a time (the user's one-run-at-a-time methodology).
|
|
4
|
-
# The OFFLOAD reasoning runs in the prewarmed DAEMON process, so the style env must be
|
|
5
|
-
# set on BOTH the daemon and the client MCP config. Writes one metrics line to RESULTS
|
|
6
|
-
# and leaves the raw stream-json at $RUNS/<repo>-<style>-<n>.jsonl for inspection.
|
|
7
|
-
#
|
|
8
|
-
# Usage: offload-eval-refs1.sh <indexed-repo> <style> <n> "<question>"
|
|
9
|
-
set -uo pipefail
|
|
10
|
-
HERE="$(cd "$(dirname "$0")" && pwd)"; ENGINE="$(cd "$HERE/../.." && pwd)"; BIN="$ENGINE/dist/bin/homegraph.js"
|
|
11
|
-
OUT="${AGENT_EVAL_OUT:-/tmp/cg-offload-eval}"; RUNS="$OUT/runs"; EXTRACT="$HERE/offload-eval-metrics.mjs"
|
|
12
|
-
TARGET="${1:?repo}"; STYLE="${2:?style}"; N="${3:?run-tag}"; Q="${4:?question}"
|
|
13
|
-
RESULTS="${RESULTS:-$OUT/results-refs.jsonl}"; REPO=$(basename "$TARGET"); TARGET=$(cd "$TARGET" && pwd -P)
|
|
14
|
-
mkdir -p "$RUNS"; command -v claude >/dev/null || { echo "no claude"; exit 1; }
|
|
15
|
-
USAGE="$RUNS/$REPO-$STYLE-usage.jsonl"; : > "$USAGE"
|
|
16
|
-
CFG="$RUNS/mcp-$REPO-$STYLE.json"
|
|
17
|
-
# `raw` is a pseudo-style: homegraph attached but the offload DISABLED (the ceiling —
|
|
18
|
-
# verbatim source, no reasoning model). Any other value is an offload style (plain|refs).
|
|
19
|
-
if [ "$STYLE" = "raw" ]; then
|
|
20
|
-
DAEMON_ENV="HOMEGRAPH_OFFLOAD_DISABLE=1"
|
|
21
|
-
printf '{"mcpServers":{"homegraph":{"command":"env","args":["HOMEGRAPH_WASM_RELAUNCHED=1","HOMEGRAPH_OFFLOAD_DISABLE=1","node","%s","serve","--mcp","--path","%s"]}}}' \
|
|
22
|
-
"$BIN" "$TARGET" > "$CFG"
|
|
23
|
-
USAGE="-"
|
|
24
|
-
else
|
|
25
|
-
DAEMON_ENV="HOMEGRAPH_OFFLOAD_STYLE=$STYLE HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE"
|
|
26
|
-
printf '{"mcpServers":{"homegraph":{"command":"env","args":["HOMEGRAPH_WASM_RELAUNCHED=1","HOMEGRAPH_OFFLOAD_STYLE=%s","HOMEGRAPH_OFFLOAD_USAGE_LOG=%s","node","%s","serve","--mcp","--path","%s"]}}}' \
|
|
27
|
-
"$STYLE" "$USAGE" "$BIN" "$TARGET" > "$CFG"
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
# Prewarm a persistent daemon carrying the SAME offload config (it does the reasoning).
|
|
31
|
-
pkill -9 -f "serve --mcp --path $TARGET" 2>/dev/null; rm -f "$TARGET/.homegraph/daemon.sock" 2>/dev/null; sleep 0.6
|
|
32
|
-
env $DAEMON_ENV HOMEGRAPH_DAEMON_IDLE_TIMEOUT_MS=1800000 \
|
|
33
|
-
node "$BIN" serve --mcp --path "$TARGET" </dev/null >/dev/null 2>&1 &
|
|
34
|
-
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)' "$TARGET" \
|
|
35
|
-
&& echo "daemon warm ($STYLE)" || echo "WARN daemon never bound"
|
|
36
|
-
|
|
37
|
-
tag="$REPO-$STYLE-$N"
|
|
38
|
-
echo "== run $tag =="
|
|
39
|
-
# DISALLOW (optional): block tools that confound the offload-sufficiency signal —
|
|
40
|
-
# chiefly "Agent" (sub-agent delegation: the spawned Explore subagent has low MCP
|
|
41
|
-
# salience, ignores homegraph, and thrashes via Bash+Read, making the A/B noise).
|
|
42
|
-
( cd "$TARGET" && claude -p "$Q" --output-format stream-json --verbose --permission-mode bypassPermissions \
|
|
43
|
-
--model "${MODEL:-sonnet}" --effort "${EFFORT:-high}" --max-budget-usd 4 \
|
|
44
|
-
${DISALLOW:+--disallowedTools "$DISALLOW"} \
|
|
45
|
-
--strict-mcp-config --mcp-config "$CFG" </dev/null > "$RUNS/$tag.jsonl" 2>"$RUNS/$tag.err" )
|
|
46
|
-
node "$EXTRACT" --run "$RUNS/$tag.jsonl" --usage "$USAGE" --arm "offload-$STYLE" --rep "$N" \
|
|
47
|
-
--repo "$REPO" --tier "complex" --q "$Q" >> "$RESULTS"
|
|
48
|
-
node -e 'const o=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8").trim().split("\n").pop());console.log(` [${o.arm} #${o.rep}] ${o.durationSec}s | main $${o.costUsdMain} ${o.tokBillable} tok | read=${o.read} grep=${o.grep} explore=${o.explore} offload=${o.offloadFired} | AI ${o.ai.calls}call/${o.ai.totalTokens}tok/$${o.ai.costUsd.toFixed(4)} | ok=${o.ok}`)' "$RESULTS"
|
|
49
|
-
pkill -9 -f "serve --mcp --path $TARGET" 2>/dev/null; rm -f "$TARGET/.homegraph/daemon.sock" 2>/dev/null
|
|
50
|
-
echo "raw transcript: $RUNS/$tag.jsonl"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ONE offload run on ONE indexed repo at a given offload STYLE (plain|refs), so we can
|
|
3
|
+
# watch a single agent transcript at a time (the user's one-run-at-a-time methodology).
|
|
4
|
+
# The OFFLOAD reasoning runs in the prewarmed DAEMON process, so the style env must be
|
|
5
|
+
# set on BOTH the daemon and the client MCP config. Writes one metrics line to RESULTS
|
|
6
|
+
# and leaves the raw stream-json at $RUNS/<repo>-<style>-<n>.jsonl for inspection.
|
|
7
|
+
#
|
|
8
|
+
# Usage: offload-eval-refs1.sh <indexed-repo> <style> <n> "<question>"
|
|
9
|
+
set -uo pipefail
|
|
10
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"; ENGINE="$(cd "$HERE/../.." && pwd)"; BIN="$ENGINE/dist/bin/homegraph.js"
|
|
11
|
+
OUT="${AGENT_EVAL_OUT:-/tmp/cg-offload-eval}"; RUNS="$OUT/runs"; EXTRACT="$HERE/offload-eval-metrics.mjs"
|
|
12
|
+
TARGET="${1:?repo}"; STYLE="${2:?style}"; N="${3:?run-tag}"; Q="${4:?question}"
|
|
13
|
+
RESULTS="${RESULTS:-$OUT/results-refs.jsonl}"; REPO=$(basename "$TARGET"); TARGET=$(cd "$TARGET" && pwd -P)
|
|
14
|
+
mkdir -p "$RUNS"; command -v claude >/dev/null || { echo "no claude"; exit 1; }
|
|
15
|
+
USAGE="$RUNS/$REPO-$STYLE-usage.jsonl"; : > "$USAGE"
|
|
16
|
+
CFG="$RUNS/mcp-$REPO-$STYLE.json"
|
|
17
|
+
# `raw` is a pseudo-style: homegraph attached but the offload DISABLED (the ceiling —
|
|
18
|
+
# verbatim source, no reasoning model). Any other value is an offload style (plain|refs).
|
|
19
|
+
if [ "$STYLE" = "raw" ]; then
|
|
20
|
+
DAEMON_ENV="HOMEGRAPH_OFFLOAD_DISABLE=1"
|
|
21
|
+
printf '{"mcpServers":{"homegraph":{"command":"env","args":["HOMEGRAPH_WASM_RELAUNCHED=1","HOMEGRAPH_OFFLOAD_DISABLE=1","node","%s","serve","--mcp","--path","%s"]}}}' \
|
|
22
|
+
"$BIN" "$TARGET" > "$CFG"
|
|
23
|
+
USAGE="-"
|
|
24
|
+
else
|
|
25
|
+
DAEMON_ENV="HOMEGRAPH_OFFLOAD_STYLE=$STYLE HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE"
|
|
26
|
+
printf '{"mcpServers":{"homegraph":{"command":"env","args":["HOMEGRAPH_WASM_RELAUNCHED=1","HOMEGRAPH_OFFLOAD_STYLE=%s","HOMEGRAPH_OFFLOAD_USAGE_LOG=%s","node","%s","serve","--mcp","--path","%s"]}}}' \
|
|
27
|
+
"$STYLE" "$USAGE" "$BIN" "$TARGET" > "$CFG"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Prewarm a persistent daemon carrying the SAME offload config (it does the reasoning).
|
|
31
|
+
pkill -9 -f "serve --mcp --path $TARGET" 2>/dev/null; rm -f "$TARGET/.homegraph/daemon.sock" 2>/dev/null; sleep 0.6
|
|
32
|
+
env $DAEMON_ENV HOMEGRAPH_DAEMON_IDLE_TIMEOUT_MS=1800000 \
|
|
33
|
+
node "$BIN" serve --mcp --path "$TARGET" </dev/null >/dev/null 2>&1 &
|
|
34
|
+
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)' "$TARGET" \
|
|
35
|
+
&& echo "daemon warm ($STYLE)" || echo "WARN daemon never bound"
|
|
36
|
+
|
|
37
|
+
tag="$REPO-$STYLE-$N"
|
|
38
|
+
echo "== run $tag =="
|
|
39
|
+
# DISALLOW (optional): block tools that confound the offload-sufficiency signal —
|
|
40
|
+
# chiefly "Agent" (sub-agent delegation: the spawned Explore subagent has low MCP
|
|
41
|
+
# salience, ignores homegraph, and thrashes via Bash+Read, making the A/B noise).
|
|
42
|
+
( cd "$TARGET" && claude -p "$Q" --output-format stream-json --verbose --permission-mode bypassPermissions \
|
|
43
|
+
--model "${MODEL:-sonnet}" --effort "${EFFORT:-high}" --max-budget-usd 4 \
|
|
44
|
+
${DISALLOW:+--disallowedTools "$DISALLOW"} \
|
|
45
|
+
--strict-mcp-config --mcp-config "$CFG" </dev/null > "$RUNS/$tag.jsonl" 2>"$RUNS/$tag.err" )
|
|
46
|
+
node "$EXTRACT" --run "$RUNS/$tag.jsonl" --usage "$USAGE" --arm "offload-$STYLE" --rep "$N" \
|
|
47
|
+
--repo "$REPO" --tier "complex" --q "$Q" >> "$RESULTS"
|
|
48
|
+
node -e 'const o=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8").trim().split("\n").pop());console.log(` [${o.arm} #${o.rep}] ${o.durationSec}s | main $${o.costUsdMain} ${o.tokBillable} tok | read=${o.read} grep=${o.grep} explore=${o.explore} offload=${o.offloadFired} | AI ${o.ai.calls}call/${o.ai.totalTokens}tok/$${o.ai.costUsd.toFixed(4)} | ok=${o.ok}`)' "$RESULTS"
|
|
49
|
+
pkill -9 -f "serve --mcp --path $TARGET" 2>/dev/null; rm -f "$TARGET/.homegraph/daemon.sock" 2>/dev/null
|
|
50
|
+
echo "raw transcript: $RUNS/$tag.jsonl"
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Clone + index the 4 "not-trained-on" eval repos into $AGENT_EVAL_OUT/repos. These were
|
|
3
|
-
# selected via a no-tools memory-probe gate (Sonnet cannot answer their flow questions from
|
|
4
|
-
# memory — so the no-homegraph baseline is honest). Env: AGENT_EVAL_OUT=<scratch dir>
|
|
5
|
-
set -uo pipefail
|
|
6
|
-
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
-
ENGINE="$(cd "$HERE/../.." && pwd)"
|
|
8
|
-
BIN="$ENGINE/dist/bin/homegraph.js"
|
|
9
|
-
OUT="${AGENT_EVAL_OUT:-/tmp/cg-offload-eval}"
|
|
10
|
-
ROOT="$OUT/repos"; mkdir -p "$ROOT"
|
|
11
|
-
export HOMEGRAPH_TELEMETRY=0 DO_NOT_TRACK=1
|
|
12
|
-
[ -f "$BIN" ] || { echo "engine not built: run 'npm run build' in $ENGINE first"; exit 1; }
|
|
13
|
-
|
|
14
|
-
clone_index() { # url name
|
|
15
|
-
echo "=== $2: clone ==="; rm -rf "$ROOT/$2"
|
|
16
|
-
git clone --quiet --depth 1 "$1" "$ROOT/$2" || { echo " clone FAILED"; return 1; }
|
|
17
|
-
echo "=== $2: index ==="
|
|
18
|
-
node "$BIN" init "$ROOT/$2" 2>&1 | grep -iE 'indexed|nodes|edges|error' | tail -2
|
|
19
|
-
}
|
|
20
|
-
clone_index https://github.com/MTKruto/MTKruto.git mtkruto # small (~322 TS)
|
|
21
|
-
clone_index https://github.com/mvdicarlo/postybirb-plus.git postybirb # medium (~608 TS)
|
|
22
|
-
clone_index https://github.com/shapeshift/web.git shapeshift # complex (~3.2k TS, 35-pkg monorepo)
|
|
23
|
-
clone_index https://github.com/trezor/trezor-suite.git trezor # large (~8k TS monorepo)
|
|
24
|
-
echo "###### SETUP DONE -> $ROOT"
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Clone + index the 4 "not-trained-on" eval repos into $AGENT_EVAL_OUT/repos. These were
|
|
3
|
+
# selected via a no-tools memory-probe gate (Sonnet cannot answer their flow questions from
|
|
4
|
+
# memory — so the no-homegraph baseline is honest). Env: AGENT_EVAL_OUT=<scratch dir>
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
ENGINE="$(cd "$HERE/../.." && pwd)"
|
|
8
|
+
BIN="$ENGINE/dist/bin/homegraph.js"
|
|
9
|
+
OUT="${AGENT_EVAL_OUT:-/tmp/cg-offload-eval}"
|
|
10
|
+
ROOT="$OUT/repos"; mkdir -p "$ROOT"
|
|
11
|
+
export HOMEGRAPH_TELEMETRY=0 DO_NOT_TRACK=1
|
|
12
|
+
[ -f "$BIN" ] || { echo "engine not built: run 'npm run build' in $ENGINE first"; exit 1; }
|
|
13
|
+
|
|
14
|
+
clone_index() { # url name
|
|
15
|
+
echo "=== $2: clone ==="; rm -rf "$ROOT/$2"
|
|
16
|
+
git clone --quiet --depth 1 "$1" "$ROOT/$2" || { echo " clone FAILED"; return 1; }
|
|
17
|
+
echo "=== $2: index ==="
|
|
18
|
+
node "$BIN" init "$ROOT/$2" 2>&1 | grep -iE 'indexed|nodes|edges|error' | tail -2
|
|
19
|
+
}
|
|
20
|
+
clone_index https://github.com/MTKruto/MTKruto.git mtkruto # small (~322 TS)
|
|
21
|
+
clone_index https://github.com/mvdicarlo/postybirb-plus.git postybirb # medium (~608 TS)
|
|
22
|
+
clone_index https://github.com/shapeshift/web.git shapeshift # complex (~3.2k TS, 35-pkg monorepo)
|
|
23
|
+
clone_index https://github.com/trezor/trezor-suite.git trezor # large (~8k TS monorepo)
|
|
24
|
+
echo "###### SETUP DONE -> $ROOT"
|
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Offload reasoning-OUTPUT-STYLE A/B — all homegraph-on, isolating the Worker's
|
|
3
|
-
# output shape's effect on main-session tokens / latency / accuracy:
|
|
4
|
-
# raw : HOMEGRAPH_OFFLOAD_DISABLE=1 (verbatim explore source, the floor)
|
|
5
|
-
# refs : managed offload, default (Cerebras map re-expanded to verbatim, ~24K)
|
|
6
|
-
# map : managed offload, STYLE=map (compact reasoned map + file:line anchors, ~1-3K)
|
|
7
|
-
# src : managed offload, STYLE=src (map + cited line ranges only, ~1-5K)
|
|
8
|
-
# Delegation BLOCKED by default (DISALLOW=Agent) so we measure the offload payload's
|
|
9
|
-
# effect on the main Sonnet agent, not whether it spawns a Haiku Explore subagent.
|
|
10
|
-
#
|
|
11
|
-
# Usage: offload-eval-styles.sh <indexed-repo> <reps> "<question>"
|
|
12
|
-
# Env: RESULTS=<file> AGENT_EVAL_OUT=<dir> REP_START=1 DISALLOW=Agent MODEL/EFFORT
|
|
13
|
-
set -uo pipefail
|
|
14
|
-
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
15
|
-
ENGINE="$(cd "$HERE/../.." && pwd)"
|
|
16
|
-
BIN="$ENGINE/dist/bin/homegraph.js"
|
|
17
|
-
OUT="${AGENT_EVAL_OUT:-/tmp/cg-offload-eval}"
|
|
18
|
-
TARGET="${1:?usage: offload-eval-styles.sh <indexed-repo> <reps> \"<question>\"}"
|
|
19
|
-
REPS="${2:?reps}"; Q="${3:?question}"
|
|
20
|
-
RUNS="$OUT/runs"; EXTRACT="$HERE/offload-eval-metrics.mjs"
|
|
21
|
-
RESULTS="${RESULTS:-$OUT/results-styles.jsonl}"
|
|
22
|
-
REPO=$(basename "$TARGET")
|
|
23
|
-
DISALLOW="${DISALLOW-Agent}" # default: block delegation. `DISALLOW= ` to allow.
|
|
24
|
-
START="${REP_START:-1}"; END=$((START + REPS - 1))
|
|
25
|
-
mkdir -p "$RUNS"
|
|
26
|
-
command -v claude >/dev/null || { echo "no claude on PATH"; exit 1; }
|
|
27
|
-
[ -d "$TARGET/.homegraph" ] || { echo "not indexed: $TARGET"; exit 1; }
|
|
28
|
-
TARGET=$(cd "$TARGET" && pwd -P)
|
|
29
|
-
|
|
30
|
-
prewarm() { # path extra-env
|
|
31
|
-
pkill -9 -f "serve --mcp --path $1" 2>/dev/null; rm -f "$1/.homegraph/daemon.sock" 2>/dev/null; sleep 0.6
|
|
32
|
-
env ${2:-} HOMEGRAPH_DAEMON_IDLE_TIMEOUT_MS=1800000 node "$BIN" serve --mcp --path "$1" </dev/null >/dev/null 2>&1 &
|
|
33
|
-
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" \
|
|
34
|
-
&& echo " daemon warm" || echo " WARN daemon never bound"
|
|
35
|
-
}
|
|
36
|
-
kill_daemon() { pkill -9 -f "serve --mcp --path $TARGET" 2>/dev/null; rm -f "$TARGET/.homegraph/daemon.sock" 2>/dev/null; sleep 1; }
|
|
37
|
-
|
|
38
|
-
run() { # arm rep mcp-config usage-log-or-dash
|
|
39
|
-
local arm="$1" rep="$2" cfg="$3" usage="$4" tag="$REPO-$1-$2"
|
|
40
|
-
[ "$usage" != "-" ] && : > "$usage"
|
|
41
|
-
( cd "$TARGET" && claude -p "$Q" \
|
|
42
|
-
--output-format stream-json --verbose --permission-mode bypassPermissions \
|
|
43
|
-
--model "${MODEL:-sonnet}" --effort "${EFFORT:-high}" --max-budget-usd 4 \
|
|
44
|
-
${DISALLOW:+--disallowedTools "$DISALLOW"} \
|
|
45
|
-
--strict-mcp-config --mcp-config "$cfg" \
|
|
46
|
-
</dev/null > "$RUNS/$tag.jsonl" 2>"$RUNS/$tag.err" )
|
|
47
|
-
node "$EXTRACT" --run "$RUNS/$tag.jsonl" --usage "$usage" --arm "$arm" --rep "$rep" \
|
|
48
|
-
--repo "$REPO" --tier styles --q "$Q" >> "$RESULTS"
|
|
49
|
-
node -e 'const o=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8").trim().split("\n").pop());console.log(` [${o.arm} #${o.rep}] ${o.durationSec}s | ${o.tokBillable} billable tok | read=${o.read} grep=${o.grep} explore=${o.explore} offload=${o.offloadFired} | AI ${o.ai.calls}c/${o.ai.totalTokens}t | ok=${o.ok}`)' "$RESULTS"
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
# MCP configs: env baked into the daemon-spawn command claude uses.
|
|
53
|
-
USAGE="$RUNS/$REPO-usage.jsonl"
|
|
54
|
-
mkcfg() { # file extra-env-pairs(JSON array entries, comma-led or empty)
|
|
55
|
-
printf '{"mcpServers":{"homegraph":{"command":"env","args":["HOMEGRAPH_WASM_RELAUNCHED=1"%s,"node","%s","serve","--mcp","--path","%s"]}}}' "$1" "$BIN" "$TARGET"
|
|
56
|
-
}
|
|
57
|
-
CFG_RAW="$RUNS/mcp-sty-raw-$REPO.json"; mkcfg ',"HOMEGRAPH_OFFLOAD_DISABLE=1"' > "$CFG_RAW"
|
|
58
|
-
CFG_REFS="$RUNS/mcp-sty-refs-$REPO.json"; mkcfg ",\"HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE\"" > "$CFG_REFS"
|
|
59
|
-
CFG_MAP="$RUNS/mcp-sty-map-$REPO.json"; mkcfg ",\"HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE\",\"HOMEGRAPH_OFFLOAD_STYLE=map\"" > "$CFG_MAP"
|
|
60
|
-
CFG_SRC="$RUNS/mcp-sty-src-$REPO.json"; mkcfg ",\"HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE\",\"HOMEGRAPH_OFFLOAD_STYLE=src\"" > "$CFG_SRC"
|
|
61
|
-
|
|
62
|
-
echo "###### repo=$REPO reps=$START..$END model=${MODEL:-sonnet}/${EFFORT:-high} disallow=${DISALLOW:-<none>}"
|
|
63
|
-
echo "###### Q=$Q"
|
|
64
|
-
echo "== ARM raw =="; prewarm "$TARGET" "HOMEGRAPH_OFFLOAD_DISABLE=1"
|
|
65
|
-
for r in $(seq "$START" "$END"); do run raw "$r" "$CFG_RAW" "-"; done; kill_daemon
|
|
66
|
-
echo "== ARM refs =="; prewarm "$TARGET" "HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE"
|
|
67
|
-
for r in $(seq "$START" "$END"); do run refs "$r" "$CFG_REFS" "$USAGE"; done; kill_daemon
|
|
68
|
-
echo "== ARM map =="; prewarm "$TARGET" "HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE HOMEGRAPH_OFFLOAD_STYLE=map"
|
|
69
|
-
for r in $(seq "$START" "$END"); do run map "$r" "$CFG_MAP" "$USAGE"; done; kill_daemon
|
|
70
|
-
echo "== ARM src =="; prewarm "$TARGET" "HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE HOMEGRAPH_OFFLOAD_STYLE=src"
|
|
71
|
-
for r in $(seq "$START" "$END"); do run src "$r" "$CFG_SRC" "$USAGE"; done; kill_daemon
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Offload reasoning-OUTPUT-STYLE A/B — all homegraph-on, isolating the Worker's
|
|
3
|
+
# output shape's effect on main-session tokens / latency / accuracy:
|
|
4
|
+
# raw : HOMEGRAPH_OFFLOAD_DISABLE=1 (verbatim explore source, the floor)
|
|
5
|
+
# refs : managed offload, default (Cerebras map re-expanded to verbatim, ~24K)
|
|
6
|
+
# map : managed offload, STYLE=map (compact reasoned map + file:line anchors, ~1-3K)
|
|
7
|
+
# src : managed offload, STYLE=src (map + cited line ranges only, ~1-5K)
|
|
8
|
+
# Delegation BLOCKED by default (DISALLOW=Agent) so we measure the offload payload's
|
|
9
|
+
# effect on the main Sonnet agent, not whether it spawns a Haiku Explore subagent.
|
|
10
|
+
#
|
|
11
|
+
# Usage: offload-eval-styles.sh <indexed-repo> <reps> "<question>"
|
|
12
|
+
# Env: RESULTS=<file> AGENT_EVAL_OUT=<dir> REP_START=1 DISALLOW=Agent MODEL/EFFORT
|
|
13
|
+
set -uo pipefail
|
|
14
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
15
|
+
ENGINE="$(cd "$HERE/../.." && pwd)"
|
|
16
|
+
BIN="$ENGINE/dist/bin/homegraph.js"
|
|
17
|
+
OUT="${AGENT_EVAL_OUT:-/tmp/cg-offload-eval}"
|
|
18
|
+
TARGET="${1:?usage: offload-eval-styles.sh <indexed-repo> <reps> \"<question>\"}"
|
|
19
|
+
REPS="${2:?reps}"; Q="${3:?question}"
|
|
20
|
+
RUNS="$OUT/runs"; EXTRACT="$HERE/offload-eval-metrics.mjs"
|
|
21
|
+
RESULTS="${RESULTS:-$OUT/results-styles.jsonl}"
|
|
22
|
+
REPO=$(basename "$TARGET")
|
|
23
|
+
DISALLOW="${DISALLOW-Agent}" # default: block delegation. `DISALLOW= ` to allow.
|
|
24
|
+
START="${REP_START:-1}"; END=$((START + REPS - 1))
|
|
25
|
+
mkdir -p "$RUNS"
|
|
26
|
+
command -v claude >/dev/null || { echo "no claude on PATH"; exit 1; }
|
|
27
|
+
[ -d "$TARGET/.homegraph" ] || { echo "not indexed: $TARGET"; exit 1; }
|
|
28
|
+
TARGET=$(cd "$TARGET" && pwd -P)
|
|
29
|
+
|
|
30
|
+
prewarm() { # path extra-env
|
|
31
|
+
pkill -9 -f "serve --mcp --path $1" 2>/dev/null; rm -f "$1/.homegraph/daemon.sock" 2>/dev/null; sleep 0.6
|
|
32
|
+
env ${2:-} HOMEGRAPH_DAEMON_IDLE_TIMEOUT_MS=1800000 node "$BIN" serve --mcp --path "$1" </dev/null >/dev/null 2>&1 &
|
|
33
|
+
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" \
|
|
34
|
+
&& echo " daemon warm" || echo " WARN daemon never bound"
|
|
35
|
+
}
|
|
36
|
+
kill_daemon() { pkill -9 -f "serve --mcp --path $TARGET" 2>/dev/null; rm -f "$TARGET/.homegraph/daemon.sock" 2>/dev/null; sleep 1; }
|
|
37
|
+
|
|
38
|
+
run() { # arm rep mcp-config usage-log-or-dash
|
|
39
|
+
local arm="$1" rep="$2" cfg="$3" usage="$4" tag="$REPO-$1-$2"
|
|
40
|
+
[ "$usage" != "-" ] && : > "$usage"
|
|
41
|
+
( cd "$TARGET" && claude -p "$Q" \
|
|
42
|
+
--output-format stream-json --verbose --permission-mode bypassPermissions \
|
|
43
|
+
--model "${MODEL:-sonnet}" --effort "${EFFORT:-high}" --max-budget-usd 4 \
|
|
44
|
+
${DISALLOW:+--disallowedTools "$DISALLOW"} \
|
|
45
|
+
--strict-mcp-config --mcp-config "$cfg" \
|
|
46
|
+
</dev/null > "$RUNS/$tag.jsonl" 2>"$RUNS/$tag.err" )
|
|
47
|
+
node "$EXTRACT" --run "$RUNS/$tag.jsonl" --usage "$usage" --arm "$arm" --rep "$rep" \
|
|
48
|
+
--repo "$REPO" --tier styles --q "$Q" >> "$RESULTS"
|
|
49
|
+
node -e 'const o=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8").trim().split("\n").pop());console.log(` [${o.arm} #${o.rep}] ${o.durationSec}s | ${o.tokBillable} billable tok | read=${o.read} grep=${o.grep} explore=${o.explore} offload=${o.offloadFired} | AI ${o.ai.calls}c/${o.ai.totalTokens}t | ok=${o.ok}`)' "$RESULTS"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# MCP configs: env baked into the daemon-spawn command claude uses.
|
|
53
|
+
USAGE="$RUNS/$REPO-usage.jsonl"
|
|
54
|
+
mkcfg() { # file extra-env-pairs(JSON array entries, comma-led or empty)
|
|
55
|
+
printf '{"mcpServers":{"homegraph":{"command":"env","args":["HOMEGRAPH_WASM_RELAUNCHED=1"%s,"node","%s","serve","--mcp","--path","%s"]}}}' "$1" "$BIN" "$TARGET"
|
|
56
|
+
}
|
|
57
|
+
CFG_RAW="$RUNS/mcp-sty-raw-$REPO.json"; mkcfg ',"HOMEGRAPH_OFFLOAD_DISABLE=1"' > "$CFG_RAW"
|
|
58
|
+
CFG_REFS="$RUNS/mcp-sty-refs-$REPO.json"; mkcfg ",\"HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE\"" > "$CFG_REFS"
|
|
59
|
+
CFG_MAP="$RUNS/mcp-sty-map-$REPO.json"; mkcfg ",\"HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE\",\"HOMEGRAPH_OFFLOAD_STYLE=map\"" > "$CFG_MAP"
|
|
60
|
+
CFG_SRC="$RUNS/mcp-sty-src-$REPO.json"; mkcfg ",\"HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE\",\"HOMEGRAPH_OFFLOAD_STYLE=src\"" > "$CFG_SRC"
|
|
61
|
+
|
|
62
|
+
echo "###### repo=$REPO reps=$START..$END model=${MODEL:-sonnet}/${EFFORT:-high} disallow=${DISALLOW:-<none>}"
|
|
63
|
+
echo "###### Q=$Q"
|
|
64
|
+
echo "== ARM raw =="; prewarm "$TARGET" "HOMEGRAPH_OFFLOAD_DISABLE=1"
|
|
65
|
+
for r in $(seq "$START" "$END"); do run raw "$r" "$CFG_RAW" "-"; done; kill_daemon
|
|
66
|
+
echo "== ARM refs =="; prewarm "$TARGET" "HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE"
|
|
67
|
+
for r in $(seq "$START" "$END"); do run refs "$r" "$CFG_REFS" "$USAGE"; done; kill_daemon
|
|
68
|
+
echo "== ARM map =="; prewarm "$TARGET" "HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE HOMEGRAPH_OFFLOAD_STYLE=map"
|
|
69
|
+
for r in $(seq "$START" "$END"); do run map "$r" "$CFG_MAP" "$USAGE"; done; kill_daemon
|
|
70
|
+
echo "== ARM src =="; prewarm "$TARGET" "HOMEGRAPH_OFFLOAD_USAGE_LOG=$USAGE HOMEGRAPH_OFFLOAD_STYLE=src"
|
|
71
|
+
for r in $(seq "$START" "$END"); do run src "$r" "$CFG_SRC" "$USAGE"; done; kill_daemon
|
|
72
72
|
echo "###### DONE $REPO — judge: node $HERE/offload-eval-judge.mjs --results $RESULTS --truth $HERE/offload-eval-ground-truth.json --out $OUT/judged-styles.jsonl"
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Aggregate judged.jsonl (or results.jsonl) into a per-repo, per-arm report:
|
|
3
|
-
// time, main tokens/cost, AI tokens/cost, total cost, tool mix, accuracy.
|
|
4
|
-
// Usage: summarize.mjs <judged-or-results.jsonl>
|
|
5
|
-
import { readFileSync } from 'fs';
|
|
6
|
-
const rows = readFileSync(process.argv[2], 'utf8').split('\n').filter(Boolean).map(l => JSON.parse(l));
|
|
7
|
-
|
|
8
|
-
const med = (xs) => { const a = xs.filter(x => x != null).sort((p, q) => p - q); if (!a.length) return null; const m = Math.floor(a.length / 2); return a.length % 2 ? a[m] : (a[m - 1] + a[m]) / 2; };
|
|
9
|
-
const rng = (xs) => { const a = xs.filter(x => x != null); return a.length ? `${Math.min(...a)}–${Math.max(...a)}` : '—'; };
|
|
10
|
-
const d2 = (x) => x == null ? '—' : (+x).toFixed(2);
|
|
11
|
-
const d3 = (x) => x == null ? '—' : (+x).toFixed(3);
|
|
12
|
-
const d4 = (x) => x == null ? '—' : (+x).toFixed(4);
|
|
13
|
-
|
|
14
|
-
const ARM_ORDER = ['frontload', 'offload', 'raw', 'nocg'];
|
|
15
|
-
const byRepo = {};
|
|
16
|
-
for (const r of rows) (byRepo[r.repo] ??= {});
|
|
17
|
-
for (const r of rows) ((byRepo[r.repo][r.arm] ??= []).push(r));
|
|
18
|
-
|
|
19
|
-
const verdictTally = (rs, field) => {
|
|
20
|
-
const t = { pass: 0, partial: 0, fail: 0, error: 0 };
|
|
21
|
-
for (const r of rs) { const v = r[field]?.verdict; if (v in t) t[v]++; }
|
|
22
|
-
return t;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
for (const repo of Object.keys(byRepo)) {
|
|
26
|
-
const tier = byRepo[repo][Object.keys(byRepo[repo])[0]][0].tier;
|
|
27
|
-
console.log(`\n${'='.repeat(78)}\n${repo} [${tier}]\n${'='.repeat(78)}`);
|
|
28
|
-
console.log(`${'arm'.padEnd(9)} n ${'time(s)'.padStart(9)} ${'mainCost'.padStart(9)} ${'aiCost'.padStart(8)} ${'totCost'.padStart(8)} ${'mainTok'.padStart(8)} ${'aiTok'.padStart(7)} ${'rd'.padStart(3)} ${'gr'.padStart(3)} ${'exp'.padStart(3)} ${'off'.padStart(3)} e2e(P/p/F) fidScore`);
|
|
29
|
-
for (const arm of ARM_ORDER) {
|
|
30
|
-
const rs = byRepo[repo][arm]; if (!rs) continue;
|
|
31
|
-
const n = rs.length;
|
|
32
|
-
const mainCost = med(rs.map(r => r.costUsdMain));
|
|
33
|
-
const aiCost = med(rs.map(r => r.ai?.costUsd ?? 0));
|
|
34
|
-
const totCost = (mainCost ?? 0) + (aiCost ?? 0);
|
|
35
|
-
const e2e = verdictTally(rs, 'e2e');
|
|
36
|
-
const fidScores = arm === 'offload' ? rs.flatMap(r => r.fidelity?.scores ?? []) : [];
|
|
37
|
-
const fid = fidScores.length ? med(fidScores) : null;
|
|
38
|
-
const fab = arm === 'offload' && rs.some(r => r.fidelity?.anyFabrication);
|
|
39
|
-
const e2eScore = med(rs.map(r => r.e2e?.score).filter(x => x != null));
|
|
40
|
-
console.log(
|
|
41
|
-
`${arm.padEnd(9)} ${String(n).padStart(1)} ${String(med(rs.map(r => r.durationSec))).padStart(9)} ` +
|
|
42
|
-
`${('$' + d3(mainCost)).padStart(9)} ${('$' + d3(aiCost)).padStart(8)} ${('$' + d3(totCost)).padStart(8)} ` +
|
|
43
|
-
`${String(Math.round(med(rs.map(r => r.tokBillable)) / 1000) + 'k').padStart(8)} ${String(Math.round(med(rs.map(r => r.ai?.totalTokens ?? 0)) / 1000) + 'k').padStart(7)} ` +
|
|
44
|
-
`${String(med(rs.map(r => r.read))).padStart(3)} ${String(med(rs.map(r => r.grep))).padStart(3)} ${String(med(rs.map(r => r.explore))).padStart(3)} ${String(med(rs.map(r => r.offloadFired))).padStart(3)} ` +
|
|
45
|
-
`${(e2e.pass + '/' + e2e.partial + '/' + e2e.fail).padStart(9)} ${e2eScore != null ? 'e2e=' + e2eScore : ''} ${fid != null ? 'fid=' + fid + (fab ? ' FAB!' : '') : ''}`
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
// ranges line for the two key metrics (variance matters)
|
|
49
|
-
for (const arm of ARM_ORDER) {
|
|
50
|
-
const rs = byRepo[repo][arm]; if (!rs) continue;
|
|
51
|
-
console.log(` ${arm} ranges: time ${rng(rs.map(r => r.durationSec))}s · mainCost $${rng(rs.map(r => r.costUsdMain))} · read ${rng(rs.map(r => r.read))} · explore ${rng(rs.map(r => r.explore))} · offloadFired ${rng(rs.map(r => r.offloadFired))}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Cross-repo roll-up: offload vs raw vs nocg deltas
|
|
56
|
-
console.log(`\n${'='.repeat(78)}\nCROSS-REPO SUMMARY (medians per repo, then averaged)\n${'='.repeat(78)}`);
|
|
57
|
-
console.log(`${'repo'.padEnd(12)} ${'arm'.padEnd(8)} ${'time'.padStart(7)} ${'totCost'.padStart(8)} ${'read'.padStart(5)} ${'e2e pass%'.padStart(9)} ${'fid'.padStart(5)}`);
|
|
58
|
-
for (const repo of Object.keys(byRepo)) {
|
|
59
|
-
for (const arm of ARM_ORDER) {
|
|
60
|
-
const rs = byRepo[repo][arm]; if (!rs) continue;
|
|
61
|
-
const e2e = verdictTally(rs, 'e2e');
|
|
62
|
-
const passPct = Math.round(100 * e2e.pass / rs.length);
|
|
63
|
-
const totCost = (med(rs.map(r => r.costUsdMain)) ?? 0) + (med(rs.map(r => r.ai?.costUsd ?? 0)) ?? 0);
|
|
64
|
-
const fid = arm === 'offload' ? med(rs.flatMap(r => r.fidelity?.scores ?? [])) : null;
|
|
65
|
-
console.log(`${repo.padEnd(12)} ${arm.padEnd(8)} ${(med(rs.map(r => r.durationSec)) + 's').padStart(7)} ${('$' + d3(totCost)).padStart(8)} ${String(med(rs.map(r => r.read))).padStart(5)} ${(passPct + '%').padStart(9)} ${String(fid ?? '—').padStart(5)}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
console.log('');
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Aggregate judged.jsonl (or results.jsonl) into a per-repo, per-arm report:
|
|
3
|
+
// time, main tokens/cost, AI tokens/cost, total cost, tool mix, accuracy.
|
|
4
|
+
// Usage: summarize.mjs <judged-or-results.jsonl>
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
const rows = readFileSync(process.argv[2], 'utf8').split('\n').filter(Boolean).map(l => JSON.parse(l));
|
|
7
|
+
|
|
8
|
+
const med = (xs) => { const a = xs.filter(x => x != null).sort((p, q) => p - q); if (!a.length) return null; const m = Math.floor(a.length / 2); return a.length % 2 ? a[m] : (a[m - 1] + a[m]) / 2; };
|
|
9
|
+
const rng = (xs) => { const a = xs.filter(x => x != null); return a.length ? `${Math.min(...a)}–${Math.max(...a)}` : '—'; };
|
|
10
|
+
const d2 = (x) => x == null ? '—' : (+x).toFixed(2);
|
|
11
|
+
const d3 = (x) => x == null ? '—' : (+x).toFixed(3);
|
|
12
|
+
const d4 = (x) => x == null ? '—' : (+x).toFixed(4);
|
|
13
|
+
|
|
14
|
+
const ARM_ORDER = ['frontload', 'offload', 'raw', 'nocg'];
|
|
15
|
+
const byRepo = {};
|
|
16
|
+
for (const r of rows) (byRepo[r.repo] ??= {});
|
|
17
|
+
for (const r of rows) ((byRepo[r.repo][r.arm] ??= []).push(r));
|
|
18
|
+
|
|
19
|
+
const verdictTally = (rs, field) => {
|
|
20
|
+
const t = { pass: 0, partial: 0, fail: 0, error: 0 };
|
|
21
|
+
for (const r of rs) { const v = r[field]?.verdict; if (v in t) t[v]++; }
|
|
22
|
+
return t;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
for (const repo of Object.keys(byRepo)) {
|
|
26
|
+
const tier = byRepo[repo][Object.keys(byRepo[repo])[0]][0].tier;
|
|
27
|
+
console.log(`\n${'='.repeat(78)}\n${repo} [${tier}]\n${'='.repeat(78)}`);
|
|
28
|
+
console.log(`${'arm'.padEnd(9)} n ${'time(s)'.padStart(9)} ${'mainCost'.padStart(9)} ${'aiCost'.padStart(8)} ${'totCost'.padStart(8)} ${'mainTok'.padStart(8)} ${'aiTok'.padStart(7)} ${'rd'.padStart(3)} ${'gr'.padStart(3)} ${'exp'.padStart(3)} ${'off'.padStart(3)} e2e(P/p/F) fidScore`);
|
|
29
|
+
for (const arm of ARM_ORDER) {
|
|
30
|
+
const rs = byRepo[repo][arm]; if (!rs) continue;
|
|
31
|
+
const n = rs.length;
|
|
32
|
+
const mainCost = med(rs.map(r => r.costUsdMain));
|
|
33
|
+
const aiCost = med(rs.map(r => r.ai?.costUsd ?? 0));
|
|
34
|
+
const totCost = (mainCost ?? 0) + (aiCost ?? 0);
|
|
35
|
+
const e2e = verdictTally(rs, 'e2e');
|
|
36
|
+
const fidScores = arm === 'offload' ? rs.flatMap(r => r.fidelity?.scores ?? []) : [];
|
|
37
|
+
const fid = fidScores.length ? med(fidScores) : null;
|
|
38
|
+
const fab = arm === 'offload' && rs.some(r => r.fidelity?.anyFabrication);
|
|
39
|
+
const e2eScore = med(rs.map(r => r.e2e?.score).filter(x => x != null));
|
|
40
|
+
console.log(
|
|
41
|
+
`${arm.padEnd(9)} ${String(n).padStart(1)} ${String(med(rs.map(r => r.durationSec))).padStart(9)} ` +
|
|
42
|
+
`${('$' + d3(mainCost)).padStart(9)} ${('$' + d3(aiCost)).padStart(8)} ${('$' + d3(totCost)).padStart(8)} ` +
|
|
43
|
+
`${String(Math.round(med(rs.map(r => r.tokBillable)) / 1000) + 'k').padStart(8)} ${String(Math.round(med(rs.map(r => r.ai?.totalTokens ?? 0)) / 1000) + 'k').padStart(7)} ` +
|
|
44
|
+
`${String(med(rs.map(r => r.read))).padStart(3)} ${String(med(rs.map(r => r.grep))).padStart(3)} ${String(med(rs.map(r => r.explore))).padStart(3)} ${String(med(rs.map(r => r.offloadFired))).padStart(3)} ` +
|
|
45
|
+
`${(e2e.pass + '/' + e2e.partial + '/' + e2e.fail).padStart(9)} ${e2eScore != null ? 'e2e=' + e2eScore : ''} ${fid != null ? 'fid=' + fid + (fab ? ' FAB!' : '') : ''}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
// ranges line for the two key metrics (variance matters)
|
|
49
|
+
for (const arm of ARM_ORDER) {
|
|
50
|
+
const rs = byRepo[repo][arm]; if (!rs) continue;
|
|
51
|
+
console.log(` ${arm} ranges: time ${rng(rs.map(r => r.durationSec))}s · mainCost $${rng(rs.map(r => r.costUsdMain))} · read ${rng(rs.map(r => r.read))} · explore ${rng(rs.map(r => r.explore))} · offloadFired ${rng(rs.map(r => r.offloadFired))}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Cross-repo roll-up: offload vs raw vs nocg deltas
|
|
56
|
+
console.log(`\n${'='.repeat(78)}\nCROSS-REPO SUMMARY (medians per repo, then averaged)\n${'='.repeat(78)}`);
|
|
57
|
+
console.log(`${'repo'.padEnd(12)} ${'arm'.padEnd(8)} ${'time'.padStart(7)} ${'totCost'.padStart(8)} ${'read'.padStart(5)} ${'e2e pass%'.padStart(9)} ${'fid'.padStart(5)}`);
|
|
58
|
+
for (const repo of Object.keys(byRepo)) {
|
|
59
|
+
for (const arm of ARM_ORDER) {
|
|
60
|
+
const rs = byRepo[repo][arm]; if (!rs) continue;
|
|
61
|
+
const e2e = verdictTally(rs, 'e2e');
|
|
62
|
+
const passPct = Math.round(100 * e2e.pass / rs.length);
|
|
63
|
+
const totCost = (med(rs.map(r => r.costUsdMain)) ?? 0) + (med(rs.map(r => r.ai?.costUsd ?? 0)) ?? 0);
|
|
64
|
+
const fid = arm === 'offload' ? med(rs.flatMap(r => r.fidelity?.scores ?? [])) : null;
|
|
65
|
+
console.log(`${repo.padEnd(12)} ${arm.padEnd(8)} ${(med(rs.map(r => r.durationSec)) + 's').padStart(7)} ${('$' + d3(totCost)).padStart(8)} ${String(med(rs.map(r => r.read))).padStart(5)} ${(passPct + '%').padStart(9)} ${String(fid ?? '—').padStart(5)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
console.log('');
|
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
# HomeGraph AI offload — accuracy & adoption eval harness
|
|
2
|
-
|
|
3
|
-
Measures the managed **offload** (`homegraph_explore` → reasoning model synthesis) and the
|
|
4
|
-
**front-load hook** (approach 1) against plain homegraph and no-homegraph, across repo sizes,
|
|
5
|
-
on **time · main-session tokens/cost · HomeGraph-AI tokens/cost · accuracy**.
|
|
6
|
-
|
|
7
|
-
All agent arms run `claude -p --model sonnet --effort high` (the deliberate floor model — an
|
|
8
|
-
affordance that lands on Sonnet generalizes up). Everything writes to a scratch dir
|
|
9
|
-
(`AGENT_EVAL_OUT`, default `/tmp/cg-offload-eval`); nothing here is shipped to users.
|
|
10
|
-
|
|
11
|
-
## Repos (selected via a memory-probe gate — NOT trained on)
|
|
12
|
-
|
|
13
|
-
Famous repos (express, excalidraw, n8n, …) are useless for *accuracy* evals: Sonnet answers their
|
|
14
|
-
flow questions from memory, so the no-homegraph baseline is dishonest. These four passed a no-tools
|
|
15
|
-
probe (Sonnet could not name their real flow internals) and are cloned fresh by `offload-eval-setup.sh`:
|
|
16
|
-
|
|
17
|
-
| tier | repo | ~src files | canonical flow |
|
|
18
|
-
|---|---|---|---|
|
|
19
|
-
| small | MTKruto/MTKruto | 322 TS | `sendMessage` → invoke → TL serialize → transport |
|
|
20
|
-
| medium | mvdicarlo/postybirb-plus | 608 TS | submission → queue → per-website `.post()` |
|
|
21
|
-
| complex | shapeshift/web | 3.2k TS (35-pkg monorepo) | swap → swapper registry → concrete swapper |
|
|
22
|
-
| large | trezor/trezor-suite | 8k TS monorepo | send-form → sign thunk → `@trezor/connect` |
|
|
23
|
-
|
|
24
|
-
Verified ground-truth flows (the judge's reference) live in `offload-eval-ground-truth.json`.
|
|
25
|
-
|
|
26
|
-
## Arms
|
|
27
|
-
|
|
28
|
-
- **offload** — homegraph + managed offload ON (requires `homegraph login`); records AI tokens/credits via `HOMEGRAPH_OFFLOAD_USAGE_LOG`.
|
|
29
|
-
- **raw** — homegraph, `HOMEGRAPH_OFFLOAD_DISABLE=1` (returns raw source).
|
|
30
|
-
- **nocg** — empty MCP config; Read/Grep baseline.
|
|
31
|
-
- **frontload** — homegraph (offload-disabled) + a `UserPromptSubmit` hook (`offload-eval-hook.mjs`) that runs raw explore on the prompt and injects the result into context (approach 1).
|
|
32
|
-
|
|
33
|
-
## Run it
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npm run build # the harness shells out to dist/
|
|
37
|
-
homegraph login # only needed for the offload arm
|
|
38
|
-
export AGENT_EVAL_OUT=/tmp/cg-offload-eval
|
|
39
|
-
|
|
40
|
-
bash scripts/agent-eval/offload-eval-setup.sh # clone + index the 4 repos
|
|
41
|
-
bash scripts/agent-eval/offload-eval-matrix.sh # 3 arms × 4 tiers × REPS (default 3)
|
|
42
|
-
node scripts/agent-eval/offload-eval-judge.mjs \
|
|
43
|
-
--results $AGENT_EVAL_OUT/results.jsonl \
|
|
44
|
-
--truth scripts/agent-eval/offload-eval-ground-truth.json \
|
|
45
|
-
--out $AGENT_EVAL_OUT/judged.jsonl
|
|
46
|
-
node scripts/agent-eval/offload-eval-summarize.mjs $AGENT_EVAL_OUT/judged.jsonl
|
|
47
|
-
|
|
48
|
-
bash scripts/agent-eval/offload-eval-frontload-matrix.sh # frontload arm + judge + merged summary
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Single repo: `offload-eval-3arm.sh <indexed-repo> <tier> <reps> "<question>"` (or `-frontload.sh`).
|
|
52
|
-
|
|
53
|
-
## Files
|
|
54
|
-
|
|
55
|
-
- `offload-eval-setup.sh` — clone + index the 4 repos.
|
|
56
|
-
- `offload-eval-3arm.sh` / `-frontload.sh` — one repo, the arms.
|
|
57
|
-
- `offload-eval-matrix.sh` / `-frontload-matrix.sh` — drive all 4 tiers.
|
|
58
|
-
- `offload-eval-hook.mjs` — the front-load `UserPromptSubmit` hook (resolves its own engine; `CG_FRONTLOAD_DEBUG=<path>` to log injections; `CG_FRONTLOAD_BUDGET` to cap injected chars).
|
|
59
|
-
- `offload-eval-metrics.mjs` — one run's stream-json + usage log → one JSON metrics line.
|
|
60
|
-
- `offload-eval-judge.mjs` — Sonnet judge: end-to-end (agent final vs ground truth) + per-answer offload fidelity.
|
|
61
|
-
- `offload-eval-summarize.mjs` — per-tier, per-arm table + cross-repo roll-up.
|
|
62
|
-
- `offload-eval-ground-truth.json` — source-verified canonical flows.
|
|
63
|
-
|
|
64
|
-
## Findings (2026-06, n=3 — direction consistent, magnitudes noisy)
|
|
65
|
-
|
|
66
|
-
- **Raw homegraph is the efficiency win** — ~nocg accuracy, fewer reads, faster, no AI cost.
|
|
67
|
-
- **The offload is the least-accurate arm in all 4 tiers** — synthesized fidelity 12–27/100 with
|
|
68
|
-
fabrication in 3/4 (e.g. invented website services; traced `ClientPlain`/`SessionPlain` instead of
|
|
69
|
-
the real encrypted path). Its speed/cost win is narrow (medium-only) and inversely correlated with
|
|
70
|
-
accuracy. **Use raw until offload fidelity is fixed.**
|
|
71
|
-
- **The front-load hook SOLVES adoption** — reads → 0–1 in every tier (incl. large, where the agent
|
|
72
|
-
otherwise read 12–24 files); fired 12/12, 0 errors. Wins on medium/complex (100% pass). But it
|
|
73
|
-
**regresses small/large to partial** — it suppresses the reads that compensate for explore's gaps at
|
|
74
|
-
**dynamic boundaries** (async queues, redux thunks, facade/factory indirection).
|
|
75
|
-
- **Master lever for BOTH:** explore's dynamic-dispatch coverage. Fix it → front-load is complete
|
|
76
|
-
everywhere and the offload has the full flow to synthesize.
|
|
1
|
+
# HomeGraph AI offload — accuracy & adoption eval harness
|
|
2
|
+
|
|
3
|
+
Measures the managed **offload** (`homegraph_explore` → reasoning model synthesis) and the
|
|
4
|
+
**front-load hook** (approach 1) against plain homegraph and no-homegraph, across repo sizes,
|
|
5
|
+
on **time · main-session tokens/cost · HomeGraph-AI tokens/cost · accuracy**.
|
|
6
|
+
|
|
7
|
+
All agent arms run `claude -p --model sonnet --effort high` (the deliberate floor model — an
|
|
8
|
+
affordance that lands on Sonnet generalizes up). Everything writes to a scratch dir
|
|
9
|
+
(`AGENT_EVAL_OUT`, default `/tmp/cg-offload-eval`); nothing here is shipped to users.
|
|
10
|
+
|
|
11
|
+
## Repos (selected via a memory-probe gate — NOT trained on)
|
|
12
|
+
|
|
13
|
+
Famous repos (express, excalidraw, n8n, …) are useless for *accuracy* evals: Sonnet answers their
|
|
14
|
+
flow questions from memory, so the no-homegraph baseline is dishonest. These four passed a no-tools
|
|
15
|
+
probe (Sonnet could not name their real flow internals) and are cloned fresh by `offload-eval-setup.sh`:
|
|
16
|
+
|
|
17
|
+
| tier | repo | ~src files | canonical flow |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| small | MTKruto/MTKruto | 322 TS | `sendMessage` → invoke → TL serialize → transport |
|
|
20
|
+
| medium | mvdicarlo/postybirb-plus | 608 TS | submission → queue → per-website `.post()` |
|
|
21
|
+
| complex | shapeshift/web | 3.2k TS (35-pkg monorepo) | swap → swapper registry → concrete swapper |
|
|
22
|
+
| large | trezor/trezor-suite | 8k TS monorepo | send-form → sign thunk → `@trezor/connect` |
|
|
23
|
+
|
|
24
|
+
Verified ground-truth flows (the judge's reference) live in `offload-eval-ground-truth.json`.
|
|
25
|
+
|
|
26
|
+
## Arms
|
|
27
|
+
|
|
28
|
+
- **offload** — homegraph + managed offload ON (requires `homegraph login`); records AI tokens/credits via `HOMEGRAPH_OFFLOAD_USAGE_LOG`.
|
|
29
|
+
- **raw** — homegraph, `HOMEGRAPH_OFFLOAD_DISABLE=1` (returns raw source).
|
|
30
|
+
- **nocg** — empty MCP config; Read/Grep baseline.
|
|
31
|
+
- **frontload** — homegraph (offload-disabled) + a `UserPromptSubmit` hook (`offload-eval-hook.mjs`) that runs raw explore on the prompt and injects the result into context (approach 1).
|
|
32
|
+
|
|
33
|
+
## Run it
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm run build # the harness shells out to dist/
|
|
37
|
+
homegraph login # only needed for the offload arm
|
|
38
|
+
export AGENT_EVAL_OUT=/tmp/cg-offload-eval
|
|
39
|
+
|
|
40
|
+
bash scripts/agent-eval/offload-eval-setup.sh # clone + index the 4 repos
|
|
41
|
+
bash scripts/agent-eval/offload-eval-matrix.sh # 3 arms × 4 tiers × REPS (default 3)
|
|
42
|
+
node scripts/agent-eval/offload-eval-judge.mjs \
|
|
43
|
+
--results $AGENT_EVAL_OUT/results.jsonl \
|
|
44
|
+
--truth scripts/agent-eval/offload-eval-ground-truth.json \
|
|
45
|
+
--out $AGENT_EVAL_OUT/judged.jsonl
|
|
46
|
+
node scripts/agent-eval/offload-eval-summarize.mjs $AGENT_EVAL_OUT/judged.jsonl
|
|
47
|
+
|
|
48
|
+
bash scripts/agent-eval/offload-eval-frontload-matrix.sh # frontload arm + judge + merged summary
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Single repo: `offload-eval-3arm.sh <indexed-repo> <tier> <reps> "<question>"` (or `-frontload.sh`).
|
|
52
|
+
|
|
53
|
+
## Files
|
|
54
|
+
|
|
55
|
+
- `offload-eval-setup.sh` — clone + index the 4 repos.
|
|
56
|
+
- `offload-eval-3arm.sh` / `-frontload.sh` — one repo, the arms.
|
|
57
|
+
- `offload-eval-matrix.sh` / `-frontload-matrix.sh` — drive all 4 tiers.
|
|
58
|
+
- `offload-eval-hook.mjs` — the front-load `UserPromptSubmit` hook (resolves its own engine; `CG_FRONTLOAD_DEBUG=<path>` to log injections; `CG_FRONTLOAD_BUDGET` to cap injected chars).
|
|
59
|
+
- `offload-eval-metrics.mjs` — one run's stream-json + usage log → one JSON metrics line.
|
|
60
|
+
- `offload-eval-judge.mjs` — Sonnet judge: end-to-end (agent final vs ground truth) + per-answer offload fidelity.
|
|
61
|
+
- `offload-eval-summarize.mjs` — per-tier, per-arm table + cross-repo roll-up.
|
|
62
|
+
- `offload-eval-ground-truth.json` — source-verified canonical flows.
|
|
63
|
+
|
|
64
|
+
## Findings (2026-06, n=3 — direction consistent, magnitudes noisy)
|
|
65
|
+
|
|
66
|
+
- **Raw homegraph is the efficiency win** — ~nocg accuracy, fewer reads, faster, no AI cost.
|
|
67
|
+
- **The offload is the least-accurate arm in all 4 tiers** — synthesized fidelity 12–27/100 with
|
|
68
|
+
fabrication in 3/4 (e.g. invented website services; traced `ClientPlain`/`SessionPlain` instead of
|
|
69
|
+
the real encrypted path). Its speed/cost win is narrow (medium-only) and inversely correlated with
|
|
70
|
+
accuracy. **Use raw until offload fidelity is fixed.**
|
|
71
|
+
- **The front-load hook SOLVES adoption** — reads → 0–1 in every tier (incl. large, where the agent
|
|
72
|
+
otherwise read 12–24 files); fired 12/12, 0 errors. Wins on medium/complex (100% pass). But it
|
|
73
|
+
**regresses small/large to partial** — it suppresses the reads that compensate for explore's gaps at
|
|
74
|
+
**dynamic boundaries** (async queues, redux thunks, facade/factory indirection).
|
|
75
|
+
- **Master lever for BOTH:** explore's dynamic-dispatch coverage. Fix it → front-load is complete
|
|
76
|
+
everywhere and the offload has the full flow to synthesize.
|