Skip to content

remember: write-time provenance hint claims "No checkable reference found" when refs exist but are dead #345

Description

@cdeust

Symptom

remember returns a provenance block whose hint contradicts its own checkable_refs in the same payload:

"provenance": {
  "grade": "unverifiable",
  "checkable_refs": {"file": 3, "commit": 0, "url": 1, "artifact": 0, "citation": 0},
  "hint": "No checkable reference found (file path, commit SHA, URL, or content-addressed artifact digest). For a durable claim, add one -- ..."
}

Three file references and one URL were extracted, and the hint says none was found. The writer is told to add a reference they already added, and is given no way to discover what actually went wrong.

The grading is correct — the message is not

Reproduced on memory 4338538 (written 2026-08-02 with directory pointing at the Cortex checkout). One of the extracted paths was plugins/hypermnesia-mcp-codex/.mcp.json, which exists only on the unmerged agent/add-codex-plugin branch and is absent from the checked-out worktree:

$ ls plugins/hypermnesia-mcp-codex/.mcp.json
ls: plugins/hypermnesia-mcp-codex/.mcp.json: No such file or directory

So grade_provenance (mcp_server/core/provenance.py:181-186) correctly classified it as a dead ref, and the worst-outcome rule (provenance.py:227) correctly rebated the whole memory to unverifiable. No bug there — the grader cannot verify a path that is not on disk.

The defect is in the feedback. _build_reason (provenance.py:144-151) already computes the exact diagnosis and distinguishes the two cases:

if grade == UNVERIFIABLE and dead:
    return f"dead_refs: {', '.join(dead[:3])}"
if grade == UNVERIFIABLE:
    return "no_extractable_reference"

But write_time_hint (provenance.py:292-308) ignores it and keys a fixed string off report.grade alone:

hint = _WRITE_TIME_HINTS[report.grade]

and _WRITE_TIME_HINTS[UNVERIFIABLE] (provenance.py:274-277) unconditionally asserts "No checkable reference found". Meanwhile remember_helpers.py:867-872 surfaces only grade, checkable_refs, and hintreport.reason, report.dead_refs, and report.uncheckable_refs are all dropped from the write response, even though validate_memory.py:560 does surface uncheckable_refs on its own path.

Net effect: the one field that names the actual problem is computed, then discarded, and replaced by a statement that the same payload disproves.

Why it matters beyond cosmetics

The hint is the write-time correction loop (M-D5: "so the writer sees, in the SAME response, whether their claim carries a checkable reference and can complete it by superseding this memory if not"). When the message misdiagnoses a dead ref as a missing ref, that loop cannot close: the writer has no way to learn which of their paths did not resolve, so the memory stays unverifiable and, per the appended deliberate suffix, degrades under recall competition. This is the mechanism behind the cluster of otherwise-well-referenced archival entries currently graded unverifiable.

Proposed fix

Make the write-time hint reflect report.reason rather than report.grade alone, and stop dropping the diagnosis:

  1. In write_time_hint, branch on the dead-ref case and name the offending paths — the data is already in report.dead_refs.
  2. Add reason (and dead_refs, capped as _build_reason already caps it at 3) to the provenance block in remember_helpers.py:867-872, matching what validate_memory already exposes.
  3. Keep the existing no_extractable_reference wording for the genuinely-empty case only.

A dead-ref hint should read closer to: "1 of 4 references could not be resolved on disk: plugins/hypermnesia-mcp-codex/.mcp.json. Fix the path or drop it, then supersede this memory."

Non-goals

  • Not proposing to change the grading rule. Worst-outcome is correct: a memory citing a path that is not there should not grade as verified.
  • Not proposing to resolve refs against git branches or history. A ref that only exists on an unmerged branch is genuinely unverifiable from the worktree; the writer should know that, which is exactly what this issue asks for.

Acceptance

A remember call whose content cites one existing and one non-existent path returns a provenance block that names the non-existent path and does not claim that no reference was found.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions