Repository navigation
fix: seventeen reliability fixes - #583
Merged
Merged
Conversation
Two live sessions under one principal: a message sent to session A with target_kind "session" was counted as waiting by session B too. B's prompt hook reminded it, and its Stop hook blocked, but B is not allowed to take the message in, so B could not stop cleanly. Hooks that know their own session id now count a session-targeted message only when it is addressed to that session or to a session it replaced with /clear. A hook that cannot name its session keeps the previous behaviour. Refs #566
A recall whose query was exactly a memory's name could return that memory below partial matches, because recency, frequency and confidence moved it down. A small limit, or a size-capped response, could leave it out entirely. A memory whose name or title equals the query now comes first, ignoring letter case (any script), surrounding spaces and Unicode normalisation form. This applies to the keyword search, to the stored-text scan used when the index is out of date, and to archived matches. Refs #525
A memory tagged project:<name>, in a repository whose project id is <name>~<hash>, was stored under a separate project that no briefing for that repository reads, so later sessions never saw it. remember (the MCP tool and memesh remember) now stores such a tag as the project id when it names the call's own project, and reports the rewrite in retagged. When the write updates or supersedes a memory already filed under the plain name, the tag stays as written, as before; memesh kg rename-project moves those. A tag naming any other project is unchanged. Refs #511
Memories tagged project:<name>, in a graph that also holds the project id project:<name>~<hash>, never reach that project's sessions, and nothing said so. memesh doctor now has a project-identity-split row that counts the active memories only the plain name holds and points at memesh kg rename-project, which lists the names and moves them (preview, then --apply, which backs the database up first). The row names no project, because doctor output is copied into public feedback reports, and it says that a shared name does not prove one project. A failure reading the tags is reported as its own warning, never as a failed database. Refs #408
An observation holding a lone UTF-16 surrogate is stored as U+FFFD. import --merge append compared the incoming text with what was stored, so it never matched, and every re-run of the same file added the observation again. The shared observation writer had the same gap. Both now compare text in the form the database stores it. Refs #561
A Codex host acceptance that carried the message body kept the waiting reminder quiet in that thread for good. When the MeMesh host connection that accepted it ended before the message was run (an older release's codex exec run that exited, then resumed), the thread was never reminded. Such an acceptance now silences the reminder only while the accepting host connection is live. A notice-only acceptance never silenced it. Refs #514
When the one-time search-index rebuild failed (for example on a full disk), it waited 24 hours before retrying. Meanwhile the title backfill removed index rows one by one with today's text splitting, which the old index cannot take, so every open failed with "database disk image is malformed". memesh reindex --fts and memesh doctor --fix, the ways out, failed with it, and doctor suggested moving the database away. The title backfill now waits until the index is rebuilt. The database opens, recall finds memories by scanning their stored text, and memesh reindex --fts rebuilds the index at once. Refs #568
When the guards could not be read (for example a damaged database file), the guard check before a Bash command recorded "no active guard matched this command" and printed nothing. Every guard had stopped working, and the record looked like a command no guard applies to. It now records an error outcome whose reason starts with "guards unreadable:" and says so on stderr. One guard with corrupt metadata is still skipped while the others load, as before. Refs #560
memesh doctor counted each hook's recorded errors and never read them, so capture liveness showed as healthy while a hook was failing. A capture hook that failed on every run was reported as one that decided there was nothing to save. A hook with an error among its five most recent runs (those its trigger applied to) now makes the verdict PASS_WITH_CONCERNS, ahead of that silence message. The row names the hook, how many of its recent runs failed and the latest reason, quoted only when it is a reason the hooks are known to record, with only the date of when it happened. The SessionStart line names the hook too. A run that records an error and then its normal outcome is still counted as two runs. Refs #555
memesh export-schema described the message tool without target_kind, intended_session and fallback_to_principal, which the live tool accepts, so a client built from the export could not send to one session or fall back to its principal. The export now carries all three. A test compares the field names of every exported tool with the live MCP tool; project on remember, recall and learn stays MCP-only, as HTTP does not take it. Refs #534
A Claude Code transcript can open with long metadata lines, such as a file-history snapshot of more than 100 KB. Transcript discovery looked for the session's working directory only in the first 64 KB and 40 lines, so the project's own sessions were skipped without a message, and a work package found nothing to read. The working directory is now taken from the first line that records one at the top level, wherever it is in the file. A "cwd" inside message text or a tool input is not taken for it, and a later line cannot claim the session for another project. Refs #552
When another process held the database's write lock, a Claude Code session start (the daily noise compression) or a Stop (the note ingestion) waited up to 30 s for it. That ran past the 10 s hook limit, so Claude Code ended the hook with a timeout. Both now give up after one 2 s wait, the wait the other hooks use. This also holds right after an upgrade, when one-time migrations are still pending and would otherwise each wait in turn. The skipped step runs on the next session start or Stop; a skipped note ingestion is recorded as an error. Refs #506
The one-time rename of `lesson` and `mistake` memories to `lesson_learned` ran only once. A memory written with the old type after that, for example by an older plugin version still running in another session, kept it for good, and anything filtering or counting by `lesson_learned` missed it. When such a memory exists, the rename now runs again the next time the database is opened. Refs #551
Every `message` response gave `created_at` in SQLite's form, `2026-09-21 20:35:59`: UTC, but with no `T` and no `Z`. A client that parsed it read it as local time, hours off. Sent messages, events, fetched payloads, receipts and facts now give `2026-09-21T20:35:59Z`, and a native acceptance's `accepted_at` does too. `memesh message storage report` and `prune` read a `--cutoff` in SQLite's form as UTC instead of local time. The format is documented in the API reference. Refs #403
A transcript prepare that found nothing returned only none_available, so a user could not tell an empty project from sessions that were skipped. It now carries skipped_sessions, a count per reason: no recorded cwd, another project's cwd, too large, unreadable, the scan limits, already proposed, changed since the scan, no visible turns. It names no session and carries no content. Refs #552
Importing the same file again with --merge append reported every memory it named as appended and marked it untrusted, even when the file added no text to it, so restoring the same backup twice demoted your own memories. An entry that adds nothing (no new observation or tag, no title change, no namespace move, and not an archived memory being restored) is now counted as skipped and left as it is. Tags and the title are compared in the form the database stores them, as observations are. Refs #561
memesh import --notes exited 0 even when every note file it found was refused, so a script checking the exit code took a run that imported nothing for a success. It now exits 1 in that case, still printing the refusals; --json still writes the full result. A partial import, or a re-run where every note is unchanged, still exits 0. Refs #550
…ey start - The stand-in npm and codex that qa:live-journey, test:packaged and test:packaged:upgrade put on PATH now carry their own module type, so a package.json with "type": "module" above the temporary directory no longer breaks them with "require is not defined in ES module scope". - A live-journey report from a clean checkout no longer says dist_stale: true because git switch left the source newer than the unchanged committed build. Only a dirty tree is judged by file times. A failed git status now stops the run instead of reading as a clean tree. Refs #477
…e --project - `memesh kg backfill-relations` now writes nothing unless given --apply. The preview reads through a read-only handle, so it does not even decay or record attempted orphans. --apply backs up the database to backups/ beside it first, then writes, and prints how to restore it. --apply with --dry-run is refused. - With --project X, both ends of every proposed relation carry X. Before, the tag, session and name-token rules picked partners from every project. - A memory tagged with several projects is anchored in the project asked for, or, without --project, in each of its projects up to --max-per-source, instead of whichever tag was read last. Refs #529
… cache - A memory in two projects whose newest anchor is the same entity in both is now proposed that link once, so the preview no longer lists it twice and it no longer takes two of --max-per-source. - The preview's --reset-idempotency path and the --json output of --apply are described as they behave. Refs #529
Each branch fixes one reported issue; this merge combines them on top of the credential redaction (#569) and exact project name (#558) changes. - #514 remind a Codex thread again once the host that accepted it ended - #534 export-schema describes the message tool's session fields - #568 open a database whose index rebuild is waiting to retry - #560 the Bash guard check reports a guard store it cannot read - #525 recall returns an exact name or title match first - #566 a session message reminds only the session it is for - #511 a plain-name tag for your own project is stored as the project id - #408 doctor warns when memories sit under a plain project name - #403 message timestamps are ISO 8601 UTC - #551 a lesson written with the old type is renamed after the rename - #506 hooks stop waiting 30 s for a held database lock - #561 an import that adds nothing leaves the memory alone - #552 work_package finds a session's recorded directory anywhere in the transcript and says which sessions it left out and why - #550 import --notes exits 1 when every note is refused - #555 doctor reports a hook that keeps failing, counting runs - #529 kg backfill-relations previews by default and backs up before --apply - #477 release checks no longer fail on environment traps Where two fixes meet, both are kept: an import compares the redacted text when it decides nothing was added, and the guard check reports both an unreadable store and an invalid pattern.
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.
Seventeen reliability fixes, each from its own branch, merged on top of main (which already has the credential redaction from #569 and the exact project names from #558). Every branch is kept as a parent of the merge commit.
What changes for users:
--cutoffin SQLite form is read as UTC (Message created_at is UTC without a T or a Z, so consumers read it hours off #403);export-schemadescribes the message tool's session fields (export-schema is missing three message fields #534).retagged(Memories tagged with the plain project name never reach that project's briefing #511); doctor warns when memories sit under a plain project name and says how to move them (Memories tagged with a plain project name are invisible to hashed-identity sessions; nothing tells the user and the heal command is undiscoverable #408); a lesson written with the old type after the rename is renamed too (Lessons written with the old type after the one-time rename keep the old type for good #551).import --notesexits 1 when every note is refused (import --notes exits 0 when every discovered note is refused #550).preparethat finds nothing says how many sessions it left out and why (Claude Code sessions whose first cwd comes late in the transcript are ignored for work packages and the dream pass #552).kg backfill-relationspreviews by default,--applybacks up first, and--projectkeeps both ends of every link inside that project (kg backfill-relations writes immediately and links across projects #529).Where two fixes meet, both are kept: an import compares the redacted text when it decides nothing was added, and the guard check reports both an unreadable store and an invalid pattern.
npm run verifyis green on this tree (316 test files, 5836 tests).Closes #514
Closes #534
Closes #568
Closes #560
Closes #525
Closes #566
Closes #511
Closes #408
Closes #403
Closes #551
Closes #506
Closes #561
Closes #552
Closes #550
Closes #555
Closes #529
Closes #477