fix(skill): stamp _origin on the semantic tier so update cannot delete it (#2843) - #3110
fix(skill): stamp _origin on the semantic tier so update cannot delete it (#2843)#3110abhay-codes07 wants to merge 1 commit into
Conversation
…e it (Graphify-Labs#2843) build._is_ast_tier reads `_origin` when present and otherwise guesses the tier from the SHAPE of source_location: 'L<line>' means AST. extract() stamps its own items 'ast'; nothing stamped the semantic side. A subagent (or a backend) that reports 'L12' for a document section therefore made that node read as AST, and the next `graphify update` — which replaces the AST tier of every re-extracted code file — deleted the document's whole semantic layer. On the reporter's 21-document corpus: 8 files, 133 nodes and 236 edges lost on a commit that touched one file. Three sites now say so explicitly, with setdefault so an existing stamp is never overwritten: * the runbook's Part C merge (core, aider and devin fragments) stamps every semantic node and edge before merging — the block the reporter's run went through; * llm.extract_corpus_parallel stamps its result (_stamp_semantic_origin), so library callers and the CLI's fresh results carry it; * the CLI's AST+semantic merge stamps sem_result as a whole, which also covers semantic-cache hits written before this change. The skillgen artifacts are regenerated, the expected/ snapshots blessed, and the new Part C lines registered as a sanctioned monolith diff.
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Stamps _origin: "semantic" on every node and edge of a semantic extraction so the tier is recorded explicitly rather than inferred from source_location shape, which misread doc sections carrying L<line> numbers as AST and let the next graphify update delete them (#2843). Adds _stamp_semantic_origin in llm.py, applies it at the end of extract_corpus_parallel and before the merge in dispatch_command, and mirrors the same stamping step into the per-platform skill markdown merge snippets.
No blocking issues surfaced. 12 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1959 functions depend on the 1268 functions this change touches.
Health — this change adds coupling hotspots:
- new:
deduplicate_entities()— 63 callers, 21 callees - new:
build_merge()— 48 callers, 14 callees - new:
extract_files_direct()— 17 callers, 20 callees - new:
extract_corpus_parallel()— 26 callers, 12 callees - new:
_call_claude_cli()— 31 callers, 9 callees - new:
dispatch_command()— 2 callers, 123 callees - new:
_extract_with_adaptive_retry()— 22 callers, 10 callees - new:
_call_llm()— 11 callers, 18 callees - …and 23 more — each is listed as a finding
Verification — 1959 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: 1848 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)
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
· 31 more finding(s) on lines outside this diff (see the check run).
Closes #2843.
The problem
build._is_ast_tierreads_originwhen present and otherwise guesses the tier from the shape ofsource_location:L<line>means AST.extract()stamps its own items_origin: "ast"; nothing stamped the semantic side — not the runbook's Part C merge, notextract_corpus_parallel, not the CLI's merge.So a semantic node's tier was decided by what its location happened to look like. A subagent writing
"§4"for a document with section numbers was fine; one writing"L12"for a document without them produced a node that read as AST. The nextgraphify update— which replaces the AST tier of every re-extracted code file — treated it as that file's stale AST and deleted it. On the reporter's 21-document run: 8 of 21 files lost, 133 nodes and 236 edges, on a commit that touched one file.The change
Three sites now stamp
_origin: "semantic"explicitly, withsetdefaultso an existing stamp is never overwritten:core,aideranddevinfragments) — the block the reporter's run went through — stamps every semantic node and edge before merging with the AST side. The 134 generated skill artifacts are regenerated, theexpected/snapshots blessed, and the new lines registered as a sanctioned monolith diff (_is_semantic_origin_stamp_fix_line) so the round-trip validator keeps guarding everything else. All five skillgen validators pass.llm.extract_corpus_parallelstamps its result via a small_stamp_semantic_origin(result), so library callers and the CLI's fresh results carry it.sem_resultas a whole, which also covers semantic-cache hits written before this change.Hyperedges are untouched: they are semantic by construction and
_is_ast_tiernever looks at them.Tests
tests/test_semantic_origin_stamp.py— 6 tests: the shape fallback is shown to be the trap (an unstampedL12doc node reads as AST); the stamping helper marks nodes and edges and keeps existing marks; each of the three runbooks' Part C python is extracted from its fragment, unescaped as the shell would, and executed against an AST/semantic pair — the merged output must carryaston the code node andsemanticon the doc node and its edges; and abuild_mergere-extraction of the code file keeps the stamped document node. With the fix reverted, 3 fail (the helper test skips).test_skillgen, thetest_llm*suites,test_extract_cliandtest_build_merge*are unchanged; the full suite matches thev8baseline.Companion #2844 (Step 9 manifest stamping) is being handled in #2966 and is not touched here.