feat(extract): scope AST symbol inventory for semantic extraction (#3253) - #3279
feat(extract): scope AST symbol inventory for semantic extraction (#3253)#3279hopstreax wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Adds an AST-aware code-symbol inventory that gets threaded into semantic extraction. scope_ast_inventory deterministically selects up to max_symbols relevant AST nodes for a document chunk via three tiers (path/unique-basename matches, distinctive non-generic identifier matches, and containment expansion), rendering them as id | qualified_name | source_file and returning "None available" when nothing matches or no ast_data is present. The extraction prompt (_extraction_system) now appends this inventory with rules instructing the model to reuse canonical AST node IDs as edge targets instead of inventing duplicate code nodes, plumbed through _call_openai_compat/_call_claude and the CLI via a new ast_data corpus kwarg.
Worth a look
- AST fields can inject forged inventory lines —
graphify/extract.py:7576· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Truncated partial extraction is reported as finish_reason stop —
graphify/llm.py:2490· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Truncated max-depth result is reported as stop —
graphify/llm.py:2492· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- scope_ast_inventory called with unit_path but function may not be imported —
graphify/llm.py:2678· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- CODE_SYMBOLS placeholder is required but never defined —
graphify/skill-kilo.md:262· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 3650 functions depend on the 1573 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 520 callers, 43 callees - new:
_rebuild_code()— 113 callers, 50 callees - new:
deduplicate_entities()— 63 callers, 21 callees - new:
build_merge()— 62 callers, 13 callees - new:
extract_files_direct()— 19 callers, 21 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
extract_corpus_parallel()— 26 callers, 12 callees - …and 54 more — each is listed as a finding
Verification — 3650 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 3534 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify dispatch\_command.
The verifier did not have enough to check dispatch\_command, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)
Verification did not run: Verification did not run for \_call\_azure.
The verification could not execute (an environment/toolchain issue, not a statement about the code).
Guarantee: No guarantee, the check itself did not complete.
Note: Detail: harness produced no verdict (rc=124): timeout after 30s
Could not verify: Could not verify \_call\_bedrock.
The verifier did not have enough to check \_call\_bedrock, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly ParamValidationError — names the real obstacle, not a sampling gap)
Verification did not run: Verification did not run for \_call\_claude.
The verification could not execute (an environment/toolchain issue, not a statement about the code).
Guarantee: No guarantee, the check itself did not complete.
Note: Detail: harness produced no verdict (rc=124): timeout after 30s
Could not verify: Could not verify \_call\_claude\_cli.
The verifier did not have enough to check \_call\_claude\_cli, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly RuntimeError — names the real obstacle, not a sampling gap)
Verification did not run: Verification did not run for \_call\_openai\_compat.
The verification could not execute (an environment/toolchain issue, not a statement about the code).
Guarantee: No guarantee, the check itself did not complete.
Note: Detail: harness produced no verdict (rc=124): timeout after 30s
Could not verify: Could not verify extract\_corpus\_parallel.
The verifier did not have enough to check extract\_corpus\_parallel, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
· 4 grounded finding(s) anchored inline below; 58 more finding(s) on lines outside this diff (see the check run).
| }) | ||
|
|
||
|
|
||
| def scope_ast_inventory( |
There was a problem hiding this comment.
scope_ast_inventory()
20 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
|
||
|
|
||
| def _call_claude(api_key: str, model: str, user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, images: list[_ImageRef] | None = None) -> dict: | ||
| def _call_claude(api_key: str, model: str, user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, code_symbols: str | None = None, images: list[_ImageRef] | None = None) -> dict: |
There was a problem hiding this comment.
_call_claude()
fans out to 8 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
|
||
|
|
||
| def _call_claude_cli(user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, images: list[_ImageRef] | None = None) -> dict: | ||
| def _call_claude_cli(user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, code_symbols: str | None = None, images: list[_ImageRef] | None = None) -> dict: |
There was a problem hiding this comment.
_call_claude_cli()
fans out to 9 callees (efferent coupling); 31 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
|
||
|
|
||
| def _call_bedrock(model: str, user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, images: list[_ImageRef] | None = None) -> dict: | ||
| def _call_bedrock(model: str, user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, code_symbols: str | None = None, images: list[_ImageRef] | None = None) -> dict: |
There was a problem hiding this comment.
_call_bedrock()
fans out to 8 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Threads AST-derived code symbols through the semantic extraction pipeline so document/paper chunks can link to canonical code nodes instead of spawning duplicate file_type="code" nodes. Adds scope_ast_inventory, which deterministically selects up to max_symbols relevant AST nodes for a chunk via three tiers (path/unique-basename matches, distinctive non-generic identifier matches, and containment expansion), returning id | qualified_name | source_file lines or "None available" when nothing matches. Wires ast_data from the CLI into corpus extraction and passes the resulting code_symbols into _extraction_system, _call_openai_compat, and _call_claude, appending an inventory block plus rules telling the model to reuse exact canonical ids and avoid duplicate code nodes.
Worth a look
- Truncated extraction is reported as successful at max depth —
graphify/llm.py:2489· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- CODE_SYMBOLS is required but Step B2 never defines it from the AST output —
graphify/skill-amp.md:256· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- CODE_SYMBOLS is required but Step B2 never defines it from the AST output —
graphify/skill-codex.md:257· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Droid Task template omits CHUNK_PATH despite requiring it for semantic chunk output —
graphify/skill-droid.md:258· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- New CODE_SYMBOLS prompt variable is required but never defined —
graphify/skill-trae.md:260· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 3651 functions depend on the 1574 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 520 callers, 43 callees - new:
_rebuild_code()— 113 callers, 50 callees - new:
deduplicate_entities()— 63 callers, 21 callees - new:
build_merge()— 62 callers, 13 callees - new:
extract_files_direct()— 19 callers, 21 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
extract_corpus_parallel()— 26 callers, 12 callees - …and 54 more — each is listed as a finding
Verification — 3651 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 3535 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify dispatch\_command.
The verifier did not have enough to check dispatch\_command, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)
Verification did not run: Verification did not run for \_call\_azure.
The verification could not execute (an environment/toolchain issue, not a statement about the code).
Guarantee: No guarantee, the check itself did not complete.
Note: Detail: harness produced no verdict (rc=124): timeout after 30s
Could not verify: Could not verify \_call\_bedrock.
The verifier did not have enough to check \_call\_bedrock, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly ParamValidationError — names the real obstacle, not a sampling gap)
Verification did not run: Verification did not run for \_call\_claude.
The verification could not execute (an environment/toolchain issue, not a statement about the code).
Guarantee: No guarantee, the check itself did not complete.
Note: Detail: harness produced no verdict (rc=124): timeout after 30s
Could not verify: Could not verify \_call\_claude\_cli.
The verifier did not have enough to check \_call\_claude\_cli, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly RuntimeError — names the real obstacle, not a sampling gap)
Verification did not run: Verification did not run for \_call\_openai\_compat.
The verification could not execute (an environment/toolchain issue, not a statement about the code).
Guarantee: No guarantee, the check itself did not complete.
Note: Detail: harness produced no verdict (rc=124): timeout after 30s
· 4 grounded finding(s) anchored inline below; 58 more finding(s) on lines outside this diff (see the check run).
| }) | ||
|
|
||
|
|
||
| def scope_ast_inventory( |
There was a problem hiding this comment.
scope_ast_inventory()
20 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
|
||
|
|
||
| def _call_claude(api_key: str, model: str, user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, images: list[_ImageRef] | None = None) -> dict: | ||
| def _call_claude(api_key: str, model: str, user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, code_symbols: str | None = None, images: list[_ImageRef] | None = None) -> dict: |
There was a problem hiding this comment.
_call_claude()
fans out to 8 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
|
||
|
|
||
| def _call_claude_cli(user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, images: list[_ImageRef] | None = None) -> dict: | ||
| def _call_claude_cli(user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, code_symbols: str | None = None, images: list[_ImageRef] | None = None) -> dict: |
There was a problem hiding this comment.
_call_claude_cli()
fans out to 9 callees (efferent coupling); 31 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
|
||
|
|
||
| def _call_bedrock(model: str, user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, images: list[_ImageRef] | None = None) -> dict: | ||
| def _call_bedrock(model: str, user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, code_symbols: str | None = None, images: list[_ImageRef] | None = None) -> dict: |
There was a problem hiding this comment.
_call_bedrock()
fans out to 8 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
Summary
Implements #3253 by allowing semantic extraction to reference existing AST code nodes using their canonical IDs, preventing duplicate or phantom code nodes.
What changed
scope_ast_inventory()to deterministically select relevant AST symbols for each semantic document/chunk.CODE_SYMBOLSto the shared extraction prompt specification..graphify_ast.jsonis available first.ast_dataandcode_symbolsthrough direct LLM backends and CLI extraction.Validation
python -m tools.skillgen --check→ 134 artifacts matchpytest tests/test_scoped_ast_inventory.py→ 18 passedgit diff --check→ cleangraphify update .→ successfulImpact
Semantic extraction can now create cross-layer edges to canonical AST nodes instead of recreating code nodes, improving graph correctness and reducing duplicate/phantom code representations.