Summary
The bundled memory-notes skill teaches edit_note(operation="append", section=...) in three separate examples, but section only applies to replace_section. append silently ignores it and writes to the end of the note, so an agent following the skill puts observations and relations in the wrong place — and gets a success message either way.
Where the docs say it
skills/memory-notes/SKILL.md, three places:
# line 221 — "Append a new observation to an existing note"
edit_note(
identifier="API Design Decisions",
operation="append",
section="Observations",
content="- [decision] Switched to OpenAPI 3.1 for spec generation #api"
)
# line 237 — "Add a new relation"
edit_note(
identifier="API Design Decisions",
operation="append",
section="Relations",
content="- depends_on [[Rate Limiter]]"
)
# line 282 — "append / prepend — add to the end or start"
edit_note(
identifier="API Design Decisions",
operation="append",
section="Observations",
content="- [decision] Use OpenAPI 3.1 for spec generation #api"
)
The same three examples are duplicated in integrations/pi/skill-references/memory-notes/REFERENCE.md (lines 229, 245, 290).
What the tool actually documents
From edit_note's own docstring:
section: For replace_section operation — the markdown header to replace content under (e.g., "## Notes", "### Implementation")
and:
"append": Add content to the end of the note
So the parameter and the operation in those examples are mutually exclusive.
Reproduction
Against basic-memory 0.23.2, streamable-http MCP transport, SQLite backend.
write_note(
title="section-append-repro",
folder="probe",
content="## Observations\n- [fact] first #tag\n\n## Relations\n- relates_to [[Travel]]",
)
edit_note(
identifier="section-append-repro",
operation="append",
section="Observations",
content="- [fact] SHOULD-LAND-UNDER-OBSERVATIONS #tag",
)
edit_note returns success, and its own status line already admits the section was ignored:
# Edited note (append)
operation: Added 1 lines to end of note
read_note confirms the line landed under the wrong heading:
## Observations
- [fact] first #tag
## Relations
- relates_to [[Travel]]
- [fact] SHOULD-LAND-UNDER-OBSERVATIONS #tag
The new observation is now inside ## Relations.
Why it bites
section is accepted and dropped rather than rejected, and the result message is a success, so there is no signal at the call site. An agent that follows the skill will keep appending observations under whatever heading happens to be last in the file, and the structure degrades silently over many edits.
Suggested fix
Those three examples want to add a line under an existing heading without replacing it, which is exactly insert_after_section (added in #646):
edit_note(
identifier="API Design Decisions",
operation="insert_after_section",
section="Observations",
content="- [decision] Switched to OpenAPI 3.1 for spec generation #api"
)
replace_section would also honour section, but it replaces the whole section, which is not what the examples intend.
Optionally, edit_note could warn (or error) when section is passed with an operation that ignores it — that would have caught this at the call site rather than in the file.
Found on origin/main @ 3bf2d523.
Summary
The bundled
memory-notesskill teachesedit_note(operation="append", section=...)in three separate examples, butsectiononly applies toreplace_section.appendsilently ignores it and writes to the end of the note, so an agent following the skill puts observations and relations in the wrong place — and gets a success message either way.Where the docs say it
skills/memory-notes/SKILL.md, three places:The same three examples are duplicated in
integrations/pi/skill-references/memory-notes/REFERENCE.md(lines 229, 245, 290).What the tool actually documents
From
edit_note's own docstring:and:
So the parameter and the operation in those examples are mutually exclusive.
Reproduction
Against basic-memory 0.23.2, streamable-http MCP transport, SQLite backend.
edit_notereturns success, and its own status line already admits the section was ignored:read_noteconfirms the line landed under the wrong heading:The new observation is now inside
## Relations.Why it bites
sectionis accepted and dropped rather than rejected, and the result message is a success, so there is no signal at the call site. An agent that follows the skill will keep appending observations under whatever heading happens to be last in the file, and the structure degrades silently over many edits.Suggested fix
Those three examples want to add a line under an existing heading without replacing it, which is exactly
insert_after_section(added in #646):replace_sectionwould also honoursection, but it replaces the whole section, which is not what the examples intend.Optionally,
edit_notecould warn (or error) whensectionis passed with an operation that ignores it — that would have caught this at the call site rather than in the file.Found on
origin/main@3bf2d523.