From 7f01fae5402a68902cac0901d797c8f369dc70c4 Mon Sep 17 00:00:00 2001 From: Eric Minish Date: Thu, 29 Oct 2026 20:41:25 -0400 Subject: [PATCH] chore: schema pin 15 -> 16, after verifying the queries Migration 16 only adds idx_unresolved_failed_module_member (failed rows); testgraph reads unresolved_refs only for status = 'pending', and schema.sql changes no column. Live indexes for testgraph, honeyslate and signedintake are schema 16 / extraction 28; select answers OK on all three. --- journeys/honeyslate.json | 2 +- journeys/signedintake.json | 2 +- journeys/testgraph.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/journeys/honeyslate.json b/journeys/honeyslate.json index dd46372..e1ae7f1 100644 --- a/journeys/honeyslate.json +++ b/journeys/honeyslate.json @@ -129,7 +129,7 @@ ] } }, - "codegraph_schema_version": 15, + "codegraph_schema_version": 16, "codegraph_extraction_version": 28, "spot_checks": { "get_settings": { diff --git a/journeys/signedintake.json b/journeys/signedintake.json index fe2af3d..c038fd7 100644 --- a/journeys/signedintake.json +++ b/journeys/signedintake.json @@ -3,7 +3,7 @@ "note": "Reviewed 2026-07-30 from a testgraph.propose draft (19 handlers). J1-J8 keep the ids and groupings of the earlier hand-authored registry; J9-J14 are new. J13 and J14 came from working the draft's blind-spot list, not from the scan: a Next.js lifespan hook and a tsx CLI. Knowingly uncovered: (1) client components reached only by in-browser navigation (SimulateButton, PasskeyButton, RegeneratePdfButton) have no server entry symbol of their own — each is registered indirectly through the page or action it calls; (2) scripts/pg-backup.sh, a shell entrypoint with no symbols in the index to register. Remaining drafted blind spots were checked and are empty for this repo: no middleware.ts, no pages/ router, no anonymous default exports, no non-Next.js router, no schedulers.\n\nSCHEMA PIN MOVED 8 -> 9 on 2026-09-08, after verifying the queries rather than just the number. Evidence, in the order it was gathered: (1) the upstream migration is purely ADDITIVE — codegraph src/db/migrations.ts version 9 is `ALTER TABLE files ADD COLUMN generated INTEGER NOT NULL DEFAULT 0` plus a partial index on it, with no column dropped, renamed or re-typed, and its own comment says migrated rows stay 0 until a re-index because the flag is derived from file CONTENT the migration cannot see. (2) A trailing column can only hurt a reader that accesses results positionally: there is NO `SELECT *` anywhere in testgraph/ or harness/, and both queries that touch `files` name their columns (`SELECT path, indexed_at` and `SELECT path, content_hash`, integrity.py:28,57). (3) All eleven db.py/integrity.py read paths were run against a live schema-9 index — nodes_for_lines, nodes_in_file, file_node_id, closure_files, impacted_closure with and without reasons, caller_edge_count, top_fanin_nodes, resolve_symbol, refresh_ambiguous_names, stale_by_mtime — 11/11 with no error. (4) End-to-end `select` at pin 9 against all three registered repos returned sane answers, not just non-errors: testgraph 26 seeds/176 impacted/4 journeys, honeyslate 5/422/8, signedintake 39/384/11. NOT COVERED, and worth knowing: the test fixtures write `schema_versions` = 8 (tests/test_core.py:40, tests/test_propose.py:101,311), so the suite exercises the PIN LOGIC but never a schema-9 shaped database. The verification above is the only thing standing behind this bump; a fixture at 9 would be better and is deliberately left as separate work, since the drift tests construct explicit mismatches and changing the base number moves their meaning too. The migration was NOT caused by any single sync: all 33 codegraph indexes on this box read schema 9, and the ones nobody touched were rewritten in a batch at 19:57, so this was a codegraph upgrade, not a per-repo action.", "approved": true, "proposed_by": "testgraph.propose", - "codegraph_schema_version": 15, + "codegraph_schema_version": 16, "codegraph_extraction_version": 28, "journeys": { "J1": { diff --git a/journeys/testgraph.json b/journeys/testgraph.json index 46acde9..8832c93 100644 --- a/journeys/testgraph.json +++ b/journeys/testgraph.json @@ -3,7 +3,7 @@ "note": "Hand-authored 2026-08-06, NOT drafted by testgraph.propose — `propose --repo .` found 0 route handlers and said so, correctly: testgraph is a CLI, and CLI entry points are the first item on its own declared blind-spot list. One journey per CLI surface, entries being each module's `main` plus the one function that carries the surface's real work (so a change to that function is named even if `main` is untouched).\n\nWHY THIS REGISTRY EXISTS, AND WHAT IT IS NOT EVIDENCE FOR: the ledger held zero rows because the only two repos with approved registries — honeyslate and signedintake — have not been pushed to since 2026-07-19, and hooks/install.sh only installs into repos that have one. testgraph is the most actively pushed repo on this box and was the one repo the hook did not cover. This registry closes that: it makes the selection -> outcome pipeline produce joinable rows at all. It is a LIVENESS test, not an accuracy test. Do not quote testgraph's observed_recall as the tool's accuracy figure: the instrument and the target are the same repo, so a good number here is not independent evidence the way honeyslate and signedintake are. Selectivity, unlike recall, IS reportable — `harness/selectivity.py` builds a fresh per-commit index and never sees this registry's authorship — and it was measured on 2026-08-06 over the last 20 commits of main: mean 2.07/6 (34.4%) over the 15 commits that touch product code, 65.6% of journey-runs avoided; 1.55/6 (25.8%) over all 20. That sits beside signedintake's 2.21/8 and honeyslate's 3.4/8 rather than above them, so the earlier guess that a small stdlib codebase would be a trivially easier target was wrong and is not the caveat to carry. The caveat to carry is self-reference.\n\nKNOWN BLIND SPOT, AND IT IS A REAL ONE: `testgraph/db.py` and `testgraph/registry.py` are reachable from NO journey. Seeding every node in either file and running the full impacted closure intersects the entry map in zero nodes — a commit rewriting the whole of db.py, which is the graph traversal this tool IS, answers `journeys to test: NONE` with `status: OK` and `recall_degraded: false`. Measured, not suspected: closure of all 14 db.py nodes = 14 nodes (it never leaves the file) and all 24 registry.py nodes = 24, against ledger.py's 257 -> all six journeys. Cause is not the registry and cannot be fixed here: codegraph does not resolve calls through a module bound under an ALIAS. `from . import ledger` then `ledger.append(...)` links; `from . import db as dbmod` then `dbmod.connect(...)` does not, because the local name no longer matches the module name. Measured across all five same-package modules called as `module.attr()`: registry.py 38 call sites -> 0 cross-file inbound edges, db.py 18 -> 0, select.py 6 -> 1, against ledger.py 10 -> 44 and integrity.py 3 -> 15. Not strictly all-or-nothing (select.py keeps one edge) but the same collapse. Checked separately and worth recording: honeyslate is NOT exposed to this — its 9 aliased imports all alias a symbol (`Session as DbSession`), never a module, so the `alias.attr()` form never arises and the accuracy figures measured there stand. Adding these files' symbols as journey `entries` would manufacture the right answer out of a false claim about what an entry is, so it is deliberately not done. Until the indexer links aliased imports, treat a NONE that follows a db.py or registry.py change as UNKNOWN, and note that the selectivity figures in TECHNICAL.md's Update 5 do not cover this case: no commit in that 20-commit window touched either file alone.\n\nSECOND KNOWN BLIND SPOT, FOUND BY #12's TRACE-DERIVED GROUND TRUTH RUN AGAINST TESTGRAPH ITSELF (issue #66): this registry's own `spot_checks.append` (testgraph/ledger.py) passes today, but not for the reason it claims to check. `testgraph/record.py:add_outcome`'s `ledger.append(row)` and `testgraph/hook.py:log_invocation`'s twin call are BOTH silently dropped by codegraph's Python resolver — `isBuiltInOrExternal` treats any `x.method()` as `list.append`/`dict.update`/etc. whenever `method` is a common collection-method name, unless the capitalized receiver matches a known CLASS; it never checks whether the receiver is a known MODULE, so a real call through an imported module with a same-named top-level function is misclassified as a builtin and never reaches resolution. Separately, six unrelated `.append()` calls on local collections through a non-identifier receiver (`rows_by_file.setdefault(path, []).append(...)` in `export.py:build_map` and six more in `db.py`/`propose.py`/`select.py`) degrade at extraction time to a bare `append` ref, which then exact-matches `ledger.py:append` as the only same-named symbol project-wide — 7 fabricated edges, measured, none from a real caller. The spot-check counts edges, not their identity: `min_caller_edges: 5` passes on these 7 fabricated edges exactly as it would on real ones, so a commit that actually breaks `ledger.append` — the sole write path for J1 and J5 — would not be caught by this guard. Root cause is not the registry and cannot be fixed here; same class of gap as the alias blind spot above but a different part of the resolver. SCOPE CORRECTED 2026-09-03: this was first written as a Python-resolver defect, which understates it. The fabrication half is CROSS-LANGUAGE — reproduced on 1.5.0 and 1.6.0 in Python (`d.setdefault(k,[]).append(v)`), JavaScript (`m.getOrInit(k,[]).push(v)`) and Go (`r.lookup(k).Get(v)` binding to a package-level `Get` instead of the method on the returned type), each time with the same `exact-match` signature and each time with a plain-identifier control resolving correctly. So it is the shared extractor dropping any non-identifier receiver, not a Python heuristic, and it is not limited to collection-method names: Go's `Get` collides with an ordinary package-level function. Filed upstream as colbymchenry/codegraph#1683 (fabrication, patch posted) and #1681 (the module-member false negative, patch posted). A fix is written and verified (colbymchenry/codegraph fork, branch `fix/python-module-member-builtin-collision`, commit `7ecf6dc`) but not deployed to the `codegraph` build this machine runs, so treat `append`'s spot-check as unreliable until a release picks the fix up.\n\nKnowingly uncovered, and each for a reason that has no fix in this file: (1) hooks/install.sh and hooks/pre-push are shell entry points with no symbols in the index — the same class as signedintake's scripts/pg-backup.sh; the pre-push template's behaviour is pinned by tests/test_hook.py instead. (2) skills/testgraph-verify/SKILL.md is the skill contract, markdown with no symbols; tests/test_skill_contract.py pins it. (3) harness/plugin/tgtrace.py is a pytest plugin loaded by name via `-p`, reached by pytest's plugin loader rather than by any call edge, so no entry symbol registers it; it is reached indirectly through J6's trace.run, which passes it. (4) harness/fixtures/dyndemo/ is a fixture repo, deliberately not product.\n\nJ8 ADDED 2026-09-09 for the pytest adapter (issue #12 Phase 4), same one-journey-per-CLI-surface convention. `attribute` and `journey_entries` are listed alongside `main`/`run` because they are where this surface can be WRONG rather than merely broken: `journey_entries` decides what a journey IS for attribution purposes, and `attribute` decides which tests covered it. A change to either silently re-scores every row this writer produces, while `main` and `run` keep working. Worth recording what the first run of it measured, because it is a fact about this repo and not about the tool: the 359-test suite covers J1-J5 and J7 (10-26 covering tests each) and does NOT cover J6 at all — nothing automated invokes the harness CLIs' `main`, they are driven by hand. That is a real coverage gap, reported by the tool rather than assumed. NOTE the same self-reference caveat as everything else in this file applies doubly here: J8 is testgraph measuring testgraph with testgraph.\n\nJ7 ADDED 2026-09-08 for the MCP stdio surface (testgraph/mcp.py). Same convention as J1-J5: the module's `main` plus the functions that carry the surface's real work, so a change to the request loop or the tool dispatch is named even when `main` is untouched. `handle` and `call_tool` are both listed because they fail differently and an agent notices only one of them: `handle` governs JSON-RPC framing (a break here kills the tool for the whole session, silently), while `call_tool` governs whether a tool failure comes back as a readable isError result or as an invisible protocol error. Note this journey is a THIN WRAPPER over J2 — the analysis is select.select either way — so a commit touching select.py should select both, and J7 selecting alone means the transport changed, not the answer.\n\nSCHEMA PIN MOVED 8 -> 9 on 2026-09-08, after verifying the queries rather than just the number. Evidence, in the order it was gathered: (1) the upstream migration is purely ADDITIVE — codegraph src/db/migrations.ts version 9 is `ALTER TABLE files ADD COLUMN generated INTEGER NOT NULL DEFAULT 0` plus a partial index on it, with no column dropped, renamed or re-typed, and its own comment says migrated rows stay 0 until a re-index because the flag is derived from file CONTENT the migration cannot see. (2) A trailing column can only hurt a reader that accesses results positionally: there is NO `SELECT *` anywhere in testgraph/ or harness/, and both queries that touch `files` name their columns (`SELECT path, indexed_at` and `SELECT path, content_hash`, integrity.py:28,57). (3) All eleven db.py/integrity.py read paths were run against a live schema-9 index — nodes_for_lines, nodes_in_file, file_node_id, closure_files, impacted_closure with and without reasons, caller_edge_count, top_fanin_nodes, resolve_symbol, refresh_ambiguous_names, stale_by_mtime — 11/11 with no error. (4) End-to-end `select` at pin 9 against all three registered repos returned sane answers, not just non-errors: testgraph 26 seeds/176 impacted/4 journeys, honeyslate 5/422/8, signedintake 39/384/11. NOT COVERED, and worth knowing: the test fixtures write `schema_versions` = 8 (tests/test_core.py:40, tests/test_propose.py:101,311), so the suite exercises the PIN LOGIC but never a schema-9 shaped database. The verification above is the only thing standing behind this bump; a fixture at 9 would be better and is deliberately left as separate work, since the drift tests construct explicit mismatches and changing the base number moves their meaning too. The migration was NOT caused by any single sync: all 33 codegraph indexes on this box read schema 9, and the ones nobody touched were rewritten in a batch at 19:57, so this was a codegraph upgrade, not a per-repo action.", "approved": true, "proposed_by": "hand-authored", - "codegraph_schema_version": 15, + "codegraph_schema_version": 16, "codegraph_extraction_version": 28, "spot_checks": { "append": {