Support canonical project references for memory mutations #213

doneCreated by Codex (User hu_yMavF3p1) • Assigned to Claude Code (User hu_yMavF3p1) • High priority

Description

Reported 2026-09-13: reads return projectId plus a project-local number and revision ids, while the memory mutations wanted an internal id that no read publishes. That blocked read-modify-write without guessing identifiers.

State found on 2026-09-15

Checked against the live server before touching anything. The report was partly overtaken:

Tool Identifier Canonical?
memory_update projectId + number + baseRevisionId already fixed, built for #238
memory_delete projectId + memoryNumber, or id already fixed
memory_archive id only the gap
memory_restore id only the gap
memory_get id only covered by project_memory_get

A live project_memory_get of #180 confirmed it: the response carries Reference, Number and RevisionId, and no internal id anywhere.

One consequence was worse than a missing feature. Archiving a project memory was reachable, but only through memory_delete without privateDataDelete — an agent wanting to archive would never look behind a tool named delete. Restoring had no canonical path at all, so a memory archived that way could not be brought back.

Done

memory_archive and memory_restore take projectId and memoryNumber, matching memory_update and memory_delete. Personal, organization and system memories keep id, because their own public reference carries it and they have no project number. Mixing both forms is refused rather than silently preferring one, and a project number is never turned into an internal id for anything else.

memory_get keeps id deliberately and now says so: its response contains the internal id, which has no place in a project-memory answer. project_memory_get is the project read.

System guide raised to v21.

Acceptance

  • Resolve, read, update, reread, archive and restore a project memory using only returned public identifiers.
  • The same local number in another project does not reach this memory, and the refusal discloses none of its content.
  • Stale revisions still rejected; id calls keep working for the scopes that have no number.
  • Tool schemas, descriptions and the system guide updated together.

Verification

AProjectMemoryIsReadUpdatedArchivedAndRestoredThroughPublicIdentifiersOnly walks the whole path over MCP — project_list, item_resolve, project_memory_get, memory_update, reread, archive, restore — taking every identifier from a previous response, with no database access. MemoryAddressingRefusesMixedAndIncompleteIdentifiers covers empty, half and mixed requests and the wrong-project case. Suite: 109 green.

Confirmed on the deployed server without mutating anything: memory_archive with a project but no number returns the new refusal, so the new contract is live.

Observation, not fixed here

memory_archive and memory_restore answer with a plain sentence while every other mutation returns a JSON receipt naming what changed. Worth aligning, but that is a response-shape decision rather than the addressing bug this task reported.

Connection interrupted or session ended. Reload

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.