Stop memory writes reporting topics as lost - #502
Merged
Conversation
Audit of the other tools for the bug just fixed in recipe_update. Two
memory tools had it, and one tool description promised an outcome its
code does not guarantee.
memory_update and memory_reshape selected the row back with topics
included. The label/data edit that motivated the write also fires
clear_memory_topics_on_change, which empties that column to re-queue the
row for the memory-topics curation unit, and RETURNING reads the row
after the trigger - so the field reported an empty tag list at exactly
the moment the model had edited the text. reshape is the worse of the
two: it requires a label or data change, so its topics field could never
have been anything but empty. Both now leave the column out of the
select. Keeping it on memory_update's confidence-only path, where the
trigger does not fire and the tags do survive, would be worse than
omitting it - a sometimes-true field the model cannot tell apart from a
re-queued one is harder to reason about than no field.
memory_create keeps its topics: an insert never fires the trigger, so an
empty list there is the honest "no tags yet", same as recipe_save's
empty photo list.
record_delete's description promised {deleted: true}, but the code
deliberately answers {deleted: false} for an id that matches nothing.
The description now says so and tells the model to check the flag.
Also audited and deliberately left alone: the unconditional
{deleted: true} on memory_delete, recipe_delete, memory_unrelate, and
wiki_delete, which exists so a caller cannot probe for another user's
rows by id; doc_read's empty line list, which carries a note explaining
the out-of-range request; and the embedding-clearing triggers, which no
tool echoes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SYNOPSIS
Audit of all 56 tools for the bug #501 fixed in
recipe_update. Twomemory tools had it. One tool description promised an outcome its
code does not guarantee.
PURPOSE
#501 fixed a tool that reported an empty photo list and empty topic
list on an edit that had preserved both, and the model relayed it as
data loss. That bug has a shape - a response asserting a field
instead of reading it, or echoing a column a trigger had just wiped -
and nothing guaranteed it was confined to one tool.
DESCRIPTION
Where it was.
memory_updateandmemory_reshapeselected thewritten row back with
topics. The label/data edit that motivatedthe write also fires
clear_memory_topics_on_change, which emptiesthat column to re-queue the row for the memory-topics curation unit, and
RETURNINGreads the row after the trigger. So the field reportedan empty tag list at exactly the moment the model had edited the text.
change, so its
topicscould never have been anything but empty.a lie on a text patch, indistinguishable from the outside.
What changes.
topicsfrom the select. Keeping it on update'sconfidence-only path would be worse than omitting it: a
sometimes-true field the model can't tell apart from a re-queued one
is harder to reason about than no field.
memory_search/memory_getare the read-back paths.record_delete's description promised{deleted: true}; the codedeliberately answers
{deleted: false}for an id matchingnothing. Description now says so and tells the model to check.
tests/memory_write_shape.test.ts, 2x cases, asserting on theSELECT column list rather than the response - a stub returning a
fixed row would pass even if someone re-added the column.
How that closes it. Every tool response now describes state
actually read, and the guard is on the query, not the payload.
Audited and deliberately unchanged (flagging these so they don't
read as misses):
{deleted: true}onmemory_delete,recipe_delete,memory_unrelate,wiki_delete. Looks like the samebug; isn't. It exists so a caller can't probe for another
user's rows by id - "doesn't exist" and "isn't yours" must look
identical. Documented in each file.
memory_createandrecipe_save's empty lists: an insert neverfires the re-tag trigger and links no images, so honestly empty.
doc_read's emptylines: the out-of-range branch, which carries anoteexplaining it.embedding, so they can't leak this way.
(spot-checked
recipe_list,record_get,doc_get).Notes:
topicsfrom two write responses is an intentional shapechange. Adding it back re-arms the false alarm.
reporting of it changed.
40/40 recipes currently tagged. Nothing was ever lost - only
misreported.
Generated by Claude Code