Skip to content

feat: citation-backed memories with just-in-time verification - #1315

Open
SK-DEV-AI wants to merge 9 commits into
1jehuang:masterfrom
SK-DEV-AI:feat/span-citations
Open

SK-DEV-AI wants to merge 9 commits into
1jehuang:masterfrom
SK-DEV-AI:feat/span-citations

Conversation

@SK-DEV-AI

@SK-DEV-AI SK-DEV-AI commented Sep 19, 2026 •

Copy link
Copy Markdown

Closes #1229 as built, exactly the agreed semantics from the thread.

SourceCitation (path, line span, exact-byte sha256, banked span text, 5-line relocation window) rides on MemoryEntry as serde-defaulted None: no migration, old entries load and recall unchanged. verify() checks the exact window, then content-searches for moved blocks (offsets never persisted), rejects absolute/parent/symlink-escaping paths, and returns advisory Fresh/Relocated/Stale/Unverifiable. Recall renders a stale mark into the model prompt: never withholds, never rewrites, never deletes. Fresh, relocated, unverifiable, and uncited memories render unmarked.

Whitespace handling per the thread: exact bytes, no normalization. An indentation change or string-literal edit reads as a real change (false fresh is the lie; false stale is just a mark).

Since filed: citations are created at remember (exact spans only, fail-soft with an honest result note; partial details reported, never silent); repo identity (git:root-commit + origin URL + revision) binds global memories to their repo while surviving checkout moves — foreign checkouts get an advisory mark, origins are credential-stripped at capture and render; malformed spans fail closed to Unverifiable (no arithmetic panics); v0.86.0 rebase moved verification into the new Jev pipeline (get_relevant_parallel, manager's own project dir as root). Automatic sidecar-extraction grounding deliberately out of scope: attaching spans the extractor did not exactly observe would be fuzzy provenance, worse than none.

Tests: 44 type + 7 tool, all green. fmt clean.

@greptile-apps

greptile-apps Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

Safe to merge.

Findings

  1. P1 Citations Are Never Created ▶
Fix with agent prompt
### Issue 1
crates/jcode-memory-types/src/lib.rs:undefined-337
`MemoryEntry::new` always sets `citation` to `None`, and the production extraction and remember flows do not attach a `SourceCitation`. Normally banked code memories therefore never enter the new source-verification path, so changed sources are recalled without a stale-source warning. Attach citations at the code-derived memory-banking boundary before merging.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

This change adds source-backed memory citations and advisory verification during recall.

  • Records exact source spans, repository context, revision metadata, surrounding context, and byte-level hashes when memories are stored.
  • Marks recalled memories when cited source content changed or is verified from a different checkout, without withholding the memory.
  • Preserves compatibility with uncited legacy entries and reports incomplete or unreadable source details when a citation cannot be attached.

Reviews (9) · Last reviewed commit: "fix(review): report incomplete source de..."

embedding: None,
embedding_model: None,
confidence: 1.0,
citation: None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Citations Are Never Created

MemoryEntry::new always sets citation to None, and the production extraction and remember flows do not attach a SourceCitation. Normally banked code memories therefore never enter the new source-verification path, so changed sources are recalled without a stale-source warning. Attach citations at the code-derived memory-banking boundary before merging.

Artifacts

Evidence from the check

  • The authored Bash harness generates a minimal Rust integration test, audits citation attachment locations, and executes the uncited and manually-cited recall cases, demonstrating the compared behavior.

Command output from the check

  • Running the normal `MemoryEntry::new` flow against a changed source file passed and printed `citation=None` and `stale-mark=false`, showing normal memories are not verified.

Command output from the check

  • Running the same recall path after manually attaching a citation passed and printed `attached=true` and `stale-mark=true`, showing the verification code works only when a citation exists.

Command output from the check

  • The numbered current-source capture shows `MemoryEntry::new` at lines 315-338 and `citation: None` at line 337, confirming the constructor default.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/jcode-memory-types/src/lib.rs
Line: 337

Comment:
**Citations Are Never Created**

`MemoryEntry::new` always sets `citation` to `None`, and the production extraction and remember flows do not attach a `SourceCitation`. Normally banked code memories therefore never enter the new source-verification path, so changed sources are recalled without a stale-source warning. Attach citations at the code-derived memory-banking boundary before merging.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed. Citations are now created at the remember boundary: optional source_path/source_start_line/source_end_line params, hashed at bank time via SourceCitation::bank, attached on success. Unreadable spans store uncited and the result says so. Covered by remember tests proving the citation persists in the store and the failure note appears.

Comment thread crates/jcode-base/src/memory_agent.rs Outdated
Comment thread crates/jcode-memory-types/src/lib.rs
Comment thread crates/jcode-memory-types/src/lib.rs Outdated
@greptile-apps

This comment has been minimized.

@github-actions github-actions Bot added type: feature Adds a new user-facing capability. area: tools Agent tools, integrations, and tool execution. security Security hardening or security-sensitive changes. labels Sep 19, 2026
Comment thread crates/jcode-memory-types/src/lib.rs
Comment thread crates/jcode-memory-types/src/lib.rs Outdated
Comment thread crates/jcode-memory-types/src/lib.rs
Comment thread crates/jcode-memory-types/src/lib.rs Outdated
Closes 1jehuang#1229 as built, exactly the agreed semantics. SourceCitation
(path, line span, exact-byte sha256, banked span text, 5-line relocation
window) rides on MemoryEntry as serde-defaulted None: no migration, old
entries load and recall unchanged. verify() checks the exact window,
then content-searches for moved blocks (offsets never persisted),
rejects absolute/parent/symlink-escaping paths, and returns advisory
Fresh/Relocated/Stale/Unverifiable. Recall renders a stale mark into
the model prompt (never withholds, never rewrites, never deletes);
fresh, relocated, unverifiable, and uncited memories render unmarked.

Tests: fresh/stale/whitespace-is-real-change/string-literal/relocation/
path-confinement/symlink/missing/degenerate-range, mark-only-stale
rendering, relocation-unmarked, old-JSON loads. 10 new, all green.
…-strict relocation

- Citations are now created, not just verified: remember accepts
  optional source_path/source_start_line/source_end_line, hashes the
  exact span at bank time via SourceCitation::bank, and attaches it.
  Unreadable spans store uncited and say so in the result; the
  verification path never covers fuzzy provenance.
- Repo-root anchoring both sides: find_repo_root walks up to .git
  (falls back to the dir itself); remember banks and recall verifies
  against the same root, so nested sessions resolve identically.
- Relocation is context-strict: span match alone no longer relocates.
  The candidate must offer exactly the stored 5-line context each
  side (file-start spans only relocate to file start). An identical
  twin elsewhere with different surroundings reads as Stale.
…etected)

lines() plus rejoin normalized CRLF to LF and dropped terminal
newlines, so those edits kept the same hash and read as Fresh. The
hash now covers the raw span bytes (line-start offsets to byte
window); relocation still searches by lines but confirms against the
raw hash. Regression tests for CRLF conversion and trailing-newline
removal; appending after the span stays Fresh (span bytes intact).
Dedicated research pass (VeriCite 2025 deterministic quote-matching;
supermemory evidence-object: source id + revision + passage + locator):
two gaps the evidence pattern names were missing here.

- repo_id (canonical root at bank, serde-defaulted for legacy):
  a global memory resolved under a different root reads as
  Unverifiable instead of checking an unrelated same-path file.
- git_head (best-effort short HEAD at bank): the stale mark names
  both revisions when the tree moved on, so the model knows how far
  behind the memory is. Advisory only, never a banking requirement.
- Stale mark now directs re-reading the file (mem0 cache-not-truth).

Automatic sidecar-extraction grounding is deliberately out of scope:
attaching spans the extractor did not exactly observe would be fuzzy
provenance, worse than none. Creation lives at the exact-span
remember boundary.
- Malformed persisted citations (end_line = usize::MAX) overflowed
   and indexed out of bounds during recall. Stored bounds
  now validate against the live file before any arithmetic: oversized
  bounds read as Unverifiable at verify and recall levels, never panic.
- repo_id is now git:<root-commit> when determinable (survives checkout
  moves/renames) with path: fallback for non-git trees. A moved checkout
  keeps verifying; a different repo stays Unverifiable.
…evant_parallel

Upstream replaced the memory agent (embedding-free Jev decisions):
the old Step-4 format site in memory_agent.rs is gone. The citation
wire moves to where the prompt is now built (get_relevant_parallel in
memory.rs), using the manager's own project dir as the repo root.
Unverifiable entries fall back to the plain prompt. memory_agent.rs
taken wholesale from master.
Comment thread crates/jcode-app-core/src/tool/memory.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: tools Agent tools, integrations, and tool execution. security Security hardening or security-sensitive changes. type: feature Adds a new user-facing capability.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Citation-backed memories with just-in-time verification

1 participant