mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
A main commit can now publish its test-impact bundle — the commit's frozen graph, its per-test coverage map and receipt.json binding both — and a PR selection given the bundle of its merge base, verified by the caller, builds its graph incrementally from the bundle's graph and narrows suites by the admitted map. Without a verified bundle nothing changes: the graph is built from scratch and every reached suite runs whole. Producer and consumer: - `codebase-memory-mcp test-impact publish|select` (in process, no daemon): publish builds the bundle of the checked-out commit, from the previous bundle's graph when that bundle verifies; select is the selection with optional --artifact/--artifact-commit/--artifact-verified. The MCP surface is unchanged. - receipt.json binds the exact imported graph (graph digest), the graph's content and topology, the map's three files and a compatibility digest (platform label + the bytes of test_impact.coverage.compatibility_paths). - A bundle is used only when the caller verified its source, it is the merge base's own, and its imported graph is the one the receipt names; the map is admitted by the existing receipt policy (source, ancestry, content, age, compatibility, metadata). Refusals become rejection reasons in the answer's receipt. - C_eff: a changed function the map knows contributes its own id; one it does not know (new, platform-only, a type or macro) contributes its nearest mapped callers on the static inbound closure; deleted names contribute every mapped function of that name. Graph: - the frozen build accepts a base candidate and repairs it through the incremental closure route; any doubt declines to the full build. Pinned to one worker like the full frozen build. - incremental closure repair skipped the interprocedural complexity pass: re-parsed functions lost transitive_loop_depth/recursive and callers kept stale values. One traversal now serves the in-memory pass and a new store-level recompute that runs before the importance rescore. - test-impact's frozen builds use a fixed display Git context: the per-run pin path and the commit ids made two builds of one tree differ and made every commit a semantic-input change, so repair always declined. - graph content and topology digests (store_content_digest.c): the exact graph digest binds one database (ids, db_uid, generation), never equal across builds; the content digest keys rows by what they say, the topology digest by what selection reads. Measured on this repository (frozen publish, macOS arm64): a two-file C change repairs in 18-20 s instead of 75-80 s with the topology of the full build; a docs-only change repairs with identical content; selections with and without the bundle are identical on all seven replayed pairs (two repaired incrementally, five declined to the full build). Recorded, not fixed: closure repair still differs from a full build in edge PROPERTIES (argument quoting and key order, CONFIGURES args) and in the corpus-wide SIMILAR_TO/SEMANTICALLY_RELATED edges; selection reads neither, so the topology digest is the incremental-equals-full check and the receipt carries both. Tooling: scripts/test-impact/coverage-merge.py builds a push's incremental coverage map (re-run suites verbatim, the rest carried with ids remapped onto the new image's function table), with its contract as test.sh Step 0z7. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
62 lines
3.5 KiB
Bash
62 lines
3.5 KiB
Bash
#!/usr/bin/env bash
|
|
# test_coverage_merge.sh — contract of scripts/test-impact/coverage-merge.py,
|
|
# the incremental coverage map of a main-branch push: re-run suites come from
|
|
# the fresh map verbatim, every other listed suite is carried with its function
|
|
# ids remapped by (file, name) onto the new table, a function the new image no
|
|
# longer has drops out, an unlisted suite is dropped, and the metadata is
|
|
# recounted so the C reader checks it like a full map.
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
MERGE="$ROOT/scripts/test-impact/coverage-merge.py"
|
|
TMP="$(mktemp -d)"
|
|
trap 'rm -rf "$TMP"' EXIT
|
|
fail() { echo "FAIL: $*" >&2; exit 1; }
|
|
|
|
mkdir -p "$TMP/previous" "$TMP/fresh"
|
|
# Previous image: f, g, gone. Suite a covers f+g, b covers g+gone, old is gone.
|
|
printf '0\tsrc/x.c\tf\n1\tsrc/x.c\tg\n2\tsrc/y.c\tgone\n' > "$TMP/previous/functions.tsv"
|
|
printf 'a:*\tcomplete\t\t\na:t1\tcomplete\t\t0 1\nb:*\tcomplete\t\t\nb:t2\tincomplete\tkilled child\t1 2\nold:*\tcomplete\t\t\nold:t3\tcomplete\t\t0\n' \
|
|
> "$TMP/previous/tests.tsv"
|
|
cat > "$TMP/previous/meta.json" <<'EOF'
|
|
{"format": 1, "commit": "1111111111111111111111111111111111111111", "platform": "p",
|
|
"llvm_profdata": "v", "functions": 3, "functions_compiled": 3, "tests": 3, "incomplete": 1,
|
|
"suites": [{"suite": "a", "tests": 1, "incomplete": 0, "exit": 0, "wall_s": 1.0},
|
|
{"suite": "b", "tests": 1, "incomplete": 1, "exit": 0, "wall_s": 2.0},
|
|
{"suite": "old", "tests": 1, "incomplete": 0, "exit": 0, "wall_s": 3.0}]}
|
|
EOF
|
|
# New image: ids shifted, `gone` removed, `h` new. Only suite a re-ran.
|
|
printf '0\tsrc/x.c\th\n1\tsrc/x.c\tf\n2\tsrc/x.c\tg\n' > "$TMP/fresh/functions.tsv"
|
|
printf 'a:*\tcomplete\t\t\na:t1\tcomplete\t\t0 1\n' > "$TMP/fresh/tests.tsv"
|
|
cat > "$TMP/fresh/meta.json" <<'EOF'
|
|
{"format": 1, "commit": "2222222222222222222222222222222222222222", "platform": "p",
|
|
"llvm_profdata": "v", "functions": 3, "functions_compiled": 3, "tests": 1, "incomplete": 0,
|
|
"suites": [{"suite": "a", "tests": 1, "incomplete": 0, "exit": 0, "wall_s": 1.5}]}
|
|
EOF
|
|
printf 'a\nb\n' > "$TMP/listed.txt"
|
|
|
|
python3 "$MERGE" --previous "$TMP/previous" --fresh "$TMP/fresh" --suites "$TMP/listed.txt" \
|
|
--commit 3333333333333333333333333333333333333333 --out "$TMP/out" > /dev/null
|
|
|
|
cmp -s "$TMP/out/functions.tsv" "$TMP/fresh/functions.tsv" || fail "the function table is not the new image's"
|
|
expected="$(printf 'a:*\tcomplete\t\t\na:t1\tcomplete\t\t0 1\nb:*\tcomplete\t\t\nb:t2\tincomplete\tkilled child\t2\n')"
|
|
[ "$(cat "$TMP/out/tests.tsv")" = "$expected" ] || fail "merged rows: $(cat "$TMP/out/tests.tsv")"
|
|
python3 - "$TMP/out/meta.json" <<'EOF' || fail "merged metadata"
|
|
import json, sys
|
|
meta = json.load(open(sys.argv[1]))
|
|
assert meta["commit"] == "3" * 40, meta["commit"]
|
|
assert (meta["tests"], meta["incomplete"]) == (2, 1), (meta["tests"], meta["incomplete"])
|
|
suites = {s["suite"]: s for s in meta["suites"]}
|
|
assert sorted(suites) == ["a", "b"], sorted(suites)
|
|
assert (suites["a"]["tests"], suites["a"]["wall_s"]) == (1, 1.5), suites["a"]
|
|
assert (suites["b"]["tests"], suites["b"]["incomplete"]) == (1, 1), suites["b"]
|
|
EOF
|
|
|
|
# A re-run suite the new runner does not list is an inconsistent input.
|
|
printf 'b\n' > "$TMP/listed-wrong.txt"
|
|
if python3 "$MERGE" --previous "$TMP/previous" --fresh "$TMP/fresh" --suites "$TMP/listed-wrong.txt" \
|
|
--commit 3333333333333333333333333333333333333333 --out "$TMP/out2" > /dev/null 2>&1; then
|
|
fail "a re-run suite missing from the runner's list was accepted"
|
|
fi
|
|
echo "coverage merge: all cases passed"
|