Skip to content

fix(watch): reconcile markdown links to semantic-backed documents (#3190) - #3191

Closed
logan683 wants to merge 1 commit into
Graphify-Labs:v8from
logan683:codex/semantic-link-context-r7-20260829
Closed

fix(watch): reconcile markdown links to semantic-backed documents (#3190)#3191
logan683 wants to merge 1 commit into
Graphify-Labs:v8from
logan683:codex/semantic-link-context-r7-20260829

Conversation

@logan683

Copy link
Copy Markdown
Contributor

Fixes #3190.

Problem

When a Markdown document already carries semantic (LLM) nodes, #1915 and #1954 correctly exclude it
from AST quick-scan, so it never receives a canonical page node. [[wikilinks]] in other notes still
resolve to that canonical id, and full reconcile discards the resulting edge silently — leaving no
dangling endpoint, so aggregate integrity checks stay clean.

Root cause

watch.py excludes semantic-backed documents from AST quick-scan (#1915), and #1954 extended that gate
to documents whose semantic layer is concept- or rationale-typed. Correct for the document itself — its
semantic nodes are the sole representation. But another note's fresh [[wikilink]] still resolves to
the deterministic page id for that path, and reconcile has no mapping from canonical page id to the
preserved representative, so the edge is discarded.

What changed

One new private helper, _reconcile_markdown_links(...), implementing six cases:

  1. authored link removed from its source → prune the stale edge
  2. link present, both representatives unique → retain if correct, else canonicalize
  3. link present, either representative ambiguous → preserve the existing edge, add nothing
  4. endpoint identity by unordered key; reverse-stored correct edges keep order and attributes
  5. pair already occupied by any relation → never overwritten with references
  6. representative priority: exact canonical id → unique page → unique non-AST document → ambiguous

Evidence

base                281ccaa4ff38aaef3f19e823fb7645e19b28f591
diff                +417 / -0, two files (graphify/watch.py, tests/test_watch.py)

nine new tests      official 281ccaa4 : 6 passed, 3 FAILED
                    patched           : 9 passed
                    an earlier rejected approach: the 3 guard tests FAIL 3/3
                    (ambiguous target, ambiguous source, reverse endpoint order)

tests/test_watch.py 128 passed / 9 skipped / 2 pre-existing Windows CWD failures
supporting suites   476 passed / 9 skipped
full suite, same host, session and interpreter:
                    official : 5,121 collected, 26 failed
                    patched  : 5,130 collected, 26 failed
                    mechanical failure-ID diff: 26 shared · 0 patch-only · 0 official-only
                    collection delta is exactly the nine new tests

404-file corpus A/B from an identical input graph:
  nodes             4,776 → 4,776   (identical id sets)
  links             6,088 → 6,137   (+95 references, -46, net +49)
  hyperedges          120 → 120     (identical id sets; dual slots deep-equal)
  semantic         950/983 → 950/983
  endpoint-order flips: 0 · dangling endpoints: 0 both sides

  46 removals: 41 re-points; 5 file pairs disconnected — four are links deleted
  from source, one a stale duplicate whose replacement already existed.

lifecycle           add/edit/delete/move/rename/swap/directory-rename, Unicode,
                    duplicate headings, dash variants, same basenames, long paths,
                    frontmatter-only edits — semantics and hyperedges preserved throughout

performance         +1.027 s on 404 Markdown files (4.639 s → 5.666 s, +22%) on a
                    no-change run — the helper re-reads authored links per reconcile

The overhead is real and stated up front. Change-gating is possible if preferred; correctness came
first.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.


Graphify review — findings

Adds Markdown link reconciliation to the incremental rebuild path via _reconcile_markdown_links, re-pointing authored links to their canonical file/page/semantic representative when both endpoints resolve uniquely, and pruning references AST edges whose link was removed from the owning Markdown source. Preserves existing edges instead of guessing when either endpoint is ambiguous or the owner wasn't re-parsed, and never overwrites an edge already occupying a representative pair since the graph is simple. Wires this into _reconcile_existing_graph and adds logger for debug tracing of preserved ambiguous links.

Worth a look

  • reconciled edge dropped when representative pair already occupied by non-reference relationgraphify/watch.py · 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 — 544 functions depend on the 376 functions this change touches.

Health — this change adds coupling hotspots:

  • new: _rebuild_code() — 107 callers, 50 callees
  • new: dispatch_command() — 2 callers, 122 callees
  • new: watch() — 5 callers, 7 callees
  • new: _reconcile_graph_html() — 6 callers, 5 callees
  • new: _reconcile_existing_graph() — 1 callers, 8 callees
  • new: test_poisoned_manifest_is_healed() — 0 callers, 6 callees

Verification — 544 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: 388 function(s) in the blast radius were not formally verified this run

· 1 grounded finding(s) anchored inline below; 5 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/watch.py
return preserved_edges


def _reconcile_existing_graph(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression_reconcile_existing_graph()

fans out to 8 callees (efferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@logan683
logan683 marked this pull request as draft August 29, 2026 17:41
@safishamsi

Copy link
Copy Markdown
Collaborator

Shipped in v0.9.52 via authorship-preserving cherry-pick so you keep contributor-graph credit. Thanks @logan683! Release: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.52

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

graphify update drops authored [[wikilinks]] whose target document is semantic-backed

2 participants