Skip to content

feat: surface stale code_verified claims in graph context - #128

Open
divo12 wants to merge 2 commits into
Autoloops:mainfrom
divo12:feat/freshness-foreground-surface
Open

feat: surface stale code_verified claims in graph context#128
divo12 wants to merge 2 commits into
Autoloops:mainfrom
divo12:feat/freshness-foreground-surface

Conversation

@divo12

@divo12 divo12 commented Jul 8, 2026

Copy link
Copy Markdown

Summary

Builds on #111 anchor fingerprints. When an agent runs greplica graph context, selected code_verified claims are checked against their stored fingerprint baseline (same tree-sitter code signature captured at proposal apply time).

  • Structural drift — all anchors broken (symbol/file gone) → quarantined under Needs re-verification
  • Content drift — anchor still resolves but code signature changed → quarantined
  • Claims without a stored baseline are skipped (zero extra work; backward compatible with pre-Mark drifted claims in the anchor audit #111 memory)
  • If freshness cannot be proven (e.g. span unreadable), the claim stays in Best Claims unchanged

Read-only: no auto-demote, no hook changes, no schema migrations. Ranking pipeline untouched.

System architecture

Where this sits in Greplica

flowchart TB
  subgraph writePath [Write path - unchanged]
    PROP[Proposal JSON]
    APPLY[applyProposal]
    FP_STORE[anchor_fingerprints on claims row]
    PROP --> APPLY --> FP_STORE
  end

  subgraph readPath [Read path - this PR]
    CLI["greplica graph context"]
    BUILD[GraphContextBuilder.build]
    RANK[BM25 + embeddings + graph boost + coherence]
    SEL[selectClaims - resolve anchors]
    STALE[attachStaleClaims - compare to baseline]
    PKT[selectGraphObjects + rankPacketResults]
    RENDER[renderGraphContextMarkdown]
    CLI --> BUILD --> RANK --> SEL --> STALE --> PKT --> RENDER
  end

  FP_STORE -. baseline read .-> STALE
  REPO[(Working tree)] -. re-hash survivors .-> STALE
Loading

Freshness is a trust overlay on the existing retrieval packet. It does not change how claims are ranked or selected — only how proven-stale claims are labeled and surfaced in the final Markdown.

Graph context pipeline (before vs after)

flowchart LR
  subgraph before [Before]
    R1[rank] --> S1[selectClaims] --> O1[selectGraphObjects] --> P1[render]
  end

  subgraph after [After - this PR]
    R2[rank] --> S2[selectClaims] --> F2[attachStaleClaims] --> O2[selectGraphObjects] --> P2[render]
  end
Loading

Insertion point: after selectClaims, before packet assembly. Only the ~3–10 selected claims are checked, not the full graph.

Per-claim freshness check

flowchart TD
  START[Selected claim] --> CV{code_verified with anchors?}
  CV -->|no| FRESH[No freshness field - Best Claims]
  CV -->|yes| BASE{anchor_fingerprints baseline?}
  BASE -->|no| FRESH
  BASE -->|yes| PART[Partition resolved anchors]
  PART --> ALL{All structurally broken?}
  ALL -->|yes| STRUCT[freshness: structural - no re-hash]
  ALL -->|no| HASH[fingerprintAnchor on survivors only]
  HASH --> CMP{classifyStale}
  CMP -->|hash differs| CONTENT[freshness: content]
  CMP -->|no proof| FRESH
  STRUCT --> QUAR[Needs re-verification]
  CONTENT --> QUAR
Loading

Structural = missing_file, missing_symbol, or ambiguous_symbol on every anchor.

Content = at least one resolving anchor whose tree-sitter code signature hash differs from the baseline stored at apply time.

Fresh (no freshness field) = baseline missing, hashes match, or staleness cannot be proven.

Module boundaries

Module Role
code-anchors/fingerprint.ts (#111) Capture baseline at apply; re-hash at read
code-anchors/freshness.ts (new) Pure classifyStale() — shared rule for future background demote
graph-context/claim-freshness.ts (new) Batch baseline read + early-exit orchestration
graph-context/context-builder.ts Wire attachStaleClaims after selectClaims
graph-context/render.ts Quarantine stale claims; mark (stale) on supporting claim refs
storage/sqlite/repository.ts (#111) readClaimAnchorFingerprints() — one batched query

Packet output shape

# Graph Context

## Best Claims
(live claims - freshness field absent)

## Needs re-verification        ← only when stale claims exist
[STALE: structural|content drift - re-verify against current code]

## Related Components
Supporting claims: `claim.x` (stale)   ← cross-reference only

## Related Flows

Performance guards

  • Skip non-code_verified claims
  • Skip claims with no stored baseline (common for pre-Mark drifted claims in the anchor audit #111 memory)
  • Structural stale: 0 re-hashes (status alone is enough)
  • Content stale: hash only resolving anchors with a stored baseline (max 4 per claim)
  • Single shared CodeAnchorResolver per request (symbol cache reuse from selectClaims)

How it works

After claim selection (post-retrieval, not in BM25/semantic scores):

  1. Skip non-code_verified claims and claims with no anchor_fingerprints baseline
  2. Reuse already-resolved anchors from selectClaims
  3. If all anchors are structurally broken → structural stale (no re-hash)
  4. Otherwise hash only surviving anchors and compare to baseline
  5. Proven stale claims move to ## Needs re-verification; components/flows mark supporting claim ids as (stale)

Files

  • libs/knowledge-graph/code-anchors/freshness.ts — pure classifyStale() rule
  • libs/knowledge-graph/graph-context/claim-freshness.tsattachStaleClaims() with early exits
  • libs/knowledge-graph/graph-context/context-builder.ts — wire-up after selectClaims
  • libs/knowledge-graph/graph-context/render.ts — quarantine section + stale markers
  • scripts/check-freshness-foreground.js — deterministic regression test

Test plan

  • npx tsc --noEmit clean
  • npm test green (includes new check-freshness-foreground.js)
  • Fresh claim → Best Claims, no quarantine section
  • Content drift (return 3return 8) → quarantined with content reason
  • Structural drift (symbol deleted) → quarantined with structural reason, no hash work
  • No baseline → Best Claims, no freshness check
  • source_verified claim → skipped
  • Stale supporting claim marked (stale) on related components

Non-goals (follow-up PRs)

  • Auto-demote / --invalidate
  • Background heal via hooks
  • Agent re-verify handoff
  • Excluding stale claims from claim_support scoring

@divo12

divo12 commented Jul 8, 2026

Copy link
Copy Markdown
Author

Hi @kushalpatil07
Lets discuss over the foreground surface before moving on to the background I had started thread in discord and also open to discuss over here too

divo12 and others added 2 commits July 30, 2026 14:10
Quarantine proven stale claims in graph context packets using stored
anchor fingerprints, without changing ranking or writing to memory.

Co-authored-by: Cursor <cursoragent@cursor.com>
Unify audit and graph-context stale detection on one pure helper so
both paths use the same baseline comparison rule.

Co-authored-by: Cursor <cursoragent@cursor.com>
@divo12
divo12 force-pushed the feat/freshness-foreground-surface branch from fea8308 to 6cbc9d4 Compare July 30, 2026 08:40
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.

1 participant