feat(agent): let Stacky edit the family's lists, and answer what changed - #61
Merged
Conversation
A family's Road-Trip list grew from thirteen items to twenty-seven and never
had a single one ticked off. Nothing was malformed; things went quiet. Each
re-post of the list was re-extracted with fresh wording ("Alternative Dachbox"
came back as suchen, recherchieren, pruefen, besorgen), and because dedup
matches on exact text, every variant landed as a new item.
`stack.list_doc` reads a list page and reports what one edit actually did:
ticked off, reopened, added, moved between headings, reworded, and -- the only
category that means something was destroyed -- removed, always named in full.
A count is not checkable by a family member; "8 items became 7" tells nobody
which one went.
Pure: no git, no Matrix, no I/O. It is what the write path will call to turn an
opaque rewrite into a reviewable one, what a CLI verb would call, and what the
curator needs so the classifier stops replacing the family's words with its own.
Also records the findings and plan in docs/design/brain/write-layer.md so the
reasoning behind all of this outlives the session that produced it.
A family kept one list for a camping trip and re-posted the whole thing whenever something changed, because that was the only way to say "this is the current state". Thirteen items became twenty-seven, and not one was ever ticked off. Nothing was broken in the merge. The classifier simply never saw the list it was adding to. Reading each post blind, it re-worded the items every pass -- "Alternative Dachbox" came back as suchen, then recherchieren, then pruefen, then besorgen -- and because the merge matches on exact text, every variant landed as a new entry. So show it the list. A note captured into a topic room now carries that topic's current todos into the classify prompt, framed as already recorded, with the point spelled out: an item is not new because it is phrased differently, and marking something finished is not this field's job. Verified on the rig against the real list: posting it, posting it again, and posting it a third time with "-> CHECK" markers all leave the list at fourteen items. Before this, the third one added "Fenstertasche pruefen" -- the model understood the marker and had nowhere to put it.
…nged
Reading the vault is fs-shaped: read_file on a path, and it works because
every model is trained on it. Writing had no counterpart, so it grew domain
verbs instead, and a model that can describe the right list perfectly still
cannot perform twenty string-matched calls in a row to produce it.
`stack memory write <path> --by <person>` is the counterpart. One page in, one
page out, attributed. What comes back is not "ok": for a list page it names
what the edit did, and always names in full anything it removed.
$ stack memory write family/camping/todos.md --by marge
Wrote family/camping/todos.md (by marge)
ticked off 2: Heringe mitbringen, Kochloeffel mitbringen
$ stack memory write family/camping/todos.md --by bart
Wrote family/camping/todos.md (by bart)
REMOVED 1: Campingstuehle mitbringen
That report is also the reason a primitive write is safe to hand to a model:
a rewrite that quietly loses six items stops being quiet. Content arrives via
the agent's own data directory rather than argv, because a markdown document
does not survive shlex.
Stacky could read the family's vault and change nothing in it. Changing a list
meant one CLI call per item, matched by substring. Asked to tidy one, it
produced the correct final document in chat, grouped and split exactly as
asked, then failed to perform the twenty calls that would have made it real,
and said it had.
So `write_file` on a vault page now routes to `stack memory write`. Stacky
reads the page, rewrites it whole, and reads back what actually changed
("ticked off 2: ...", or "REMOVED 1: ..."). Ticking off is `- [x]`, splitting a
list in two is a heading. Ordinary markdown, which is what these models are
good at.
`edit_file` on a vault page is refused with instructions rather than
translated: a family list is fifteen lines, whole-document reasoning is the
point, and patching a read-only mount would be the same write with extra ways
to be wrong.
SKILL.md changes with it, or it would keep teaching the verbs this replaces.
It now carries the rules that matter: write the page back in full, keep the
family's words, tick off rather than delete, relay what the write reports, and
never claim a change without having read that report.
Rewriting a page died in the tool loop instead of saving. Every edit the agent made to a family list was lost, and it had no way to tell. Editing a page through apply_patch now points at the way that works rather than failing against a read-only mount.
Asked to reorganise a list, it would strike two unrelated items and describe a change it never made. It now edits the page instead, and says so when it cannot.
Changing a list meant replacing the whole page, so an edit made from a copy that was a few seconds old quietly undid whatever someone else had just added. Narrow changes now touch only the lines they name, and if the list moved on in the meantime Stacky is told which line no longer fits instead of overwriting the change.
Every edit was recorded as "updated todos.md", whoever made it and whatever it changed, so scrolling the history answered nothing. A change now names itself: "marge ticked off 1: Wetter checken in camping". Pages that are not lists say how much text moved, and an edit that lost items still names every one of them.
The vault has always recorded every version and who wrote it, and nothing read it back. Now "what's new this week", "who changed Homer's page" and "when did this land on the list" are one question each, for the family and for Stacky.
Asked that, Stacky searched four times and guessed from whatever his page says now. Questions about change have their own answer: what was added, by whom, when. Housekeeping by the bots stays out of it, so the answer is what the family did.
arthware-dev
marked this pull request as ready for review
August 3, 2026 18:55
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.
Stacky could read the family's memory and change almost nothing in it, and could not answer any question about when something happened.
Editing a list. Changing a page died in the tool loop instead of saving, so every edit Stacky made was silently lost. It now edits pages for real: narrow changes (
apply_patch) touch only the lines they name and are matched against the page as it currently stands, so an edit made from a copy someone else has since changed is refused by name rather than overwriting them. Whole rewrites (write_file) stay for genuine restructures. Every edit answers with what it actually did, and an edit that drops items names every one.Asking what changed. The vault has always recorded every version and who wrote it, and nothing read it back.
stack memory historyanswers "what's new this week", "who changed Homer's page", "when did this land on the list" - reading git's own fields, never parsing commit messages. Housekeeping by the bots stays out by default.History worth reading. Every edit used to be recorded as "updated todos.md". A change now names itself:
marge ticked off 1: Wetter checken in camping.Verified on the demo rig with a real model throughout, not only in tests.