Repository navigation
Releases
Releases · ddtcorex/dsh-maestro-memory
v3.0.1
Memory view fixes from the 2026-10-28 design audit (19 findings measured live on
DSH Web 0.2.2-alpha.2; report at
maestro-harness/docs/reports/2026-10-28-dsh-maestro-memory-design-audit.md).
Fixed
- The queue badge on the Memory tab. The count is a span appended to the
host's tab button, which isdisplay: blockwith its label in an inner span,
so the badge dropped onto a second line, stretched the tab strip from 35px to
52px and landed on the active underline. The tab box is pinned to one inline
row now. - The focus ring. Keyboard focus was painted with
--dsw-alias-interactive-bg-active, a translucent surface token that
composites to 1.20:1 in light theme and 1.51:1 in dark, so the panel replaced
the host's visible ring with an invisible one. It delegates to the platform
ring (17.45:1 dark, 18.90:1 light) and focused fields keep a label-secondary
border (5.80:1 / 9.25:1). - The badge count. White on the raw error token measured 3.29:1 in dark; the
fill is mixed 22% toward black (5.09:1 dark, 6.75:1 light) with the plain
token kept as a fallback. Its reduced-motion veto moved into the stylesheet
that stays mounted for the whole session instead of the panel one. - The Health section reported numbers it did not have. A hardcoded
"1.5 avg / session" sat beside real zeroes, and an empty project rendered as a
green "100.0% PASS" because the host answers coverage 100 fortotal === 0.
Coverage now reportsn/athere, and the value sits on a tinted chip with
label-primary text (the raw state colours measured 2.28:1 and 2.15:1 as text
on the light surface). - Silent action results. Only one section published a live region and the
other four rendered failures as muted grey notes. OneMemStatuscomponent
withrole=statusnow serves all five sections and failures use the error
colour (4.77:1 dark, 4.83:1 light). - The Skills section printed its empty message twice, and listed absolute
filesystem paths in a 12px line; paths collapse to their last two segments
with the full path intitle. - The mobile section rail. It was a 517px scroller inside a 303px box with
the scrollbar hidden, so Skills and Health were off-screen with no affordance.
Below 1024px it is a wrapping grid, pills/selects/search hold the 44px touch
floor, empty states are 520px notes instead of full-width dashed slabs, the
todo composer starts collapsed, and queue cards show their reason once with
the edit box behind a toggle. - Light theme had no surface hierarchy.
bg-base,bg-layer-1and
bg-layer-2all resolve to#fffthere, so cards, forms and empty states had
no boundary; they pairborder-l2with a soft shadow now. - Unscoped host-chrome rules. The panel stylesheet restyled every
conversation view as soon as it mounted; only the:has(.memx)scoped set
remains.
Changed
- Section tabs behave like tabs. Roving tabindex plus Arrow/Home/End keys,
and the panel itself is focusable. - The badge count no longer becomes part of the tab's name. It is
aria-hiddenwith anaria-describedbydescription ("N pending
suggestions"), so the tab reads as "Memory". - One refresh per section. The header icon that only polled the queue count
is gone; Health's refresh moved from the bottom into its toolbar. - Tests. New suites for the badge placement, the a11y tokens, the audit
follow-ups, and the newsrc/client/format.tshelpers; 428 tests in total.
What's Changed
- docs(memory): point spec references at renamed file by @ddtcorex in #81
- fix(memory): close the Memory view design-audit findings by @ddtcorex in #82
- chore(release): 3.0.1 by @ddtcorex in #83
Full Changelog: v3.0.0...v3.0.1
v3.0.0
Removed
- The one-time legacy-layout migration. In-place adoption from the old
~/.dsh/memorieslayout is no longer shipped: themigration.*RPC
endpoints,scripts/migrate.mjsand the migration service are gone.
Anyone still on the old layout must migrate with the last release that
contains it (2.2.1). - The
write-block.jsonwrite guard. Nothing writes that flag any more,
so the memory store, todo store and sync service no longer check it, the
statusRPC no longer reportsblocked, andisWriteBlockedis gone. - Unused layout helpers.
journalPath,backupsDir,
backupManifestPath,backupFilesDirPath,schemaPathand
allMetadataPathshad no callers left after the migration removal. - The duplicate
todayStampin the memory store. It now imports the
exported copy from the todo store.
Fixed
- Connection row injects webStartup. DSH 0.2.1-alpha.x removed the webRuntime service; a connection entry still injecting it waits for a service that never mounts and dsh web never boots.
What's Changed
- docs(memory): document requirements, pinning and the shipped connection patch by @ddtcorex in #76
- chore(memory): remove the legacy layout migration and dead code by @ddtcorex in #77
- chore(memory): enable noUnusedParameters by @ddtcorex in #78
- DSH 0.2.1 compat: webStartup inject on the connection row by @ddtcorex in #79
- chore(release): 3.0.0 by @ddtcorex in #80
Full Changelog: v2.2.1...v3.0.0
v2.2.1
Fixed
- RPC rows activate on DSH 0.2.x. The bundle patch declares
webServeron theconnectionentry, so rows that callrpc.handleno longer fail withcannot get property "webServer" without injectand their channels no longer answer 405. - Peer range now ends at
<0.3.0-0, so memory is no longer skipped on dsh 0.2.x (published 2.2.0 declared<0.2.0).
What's Changed
- chore: target DSH 0.1.7-alpha.2 by @ddtcorex in #63
- fix(memory): add the -0 ceiling suffix to the dsh peer ranges by @ddtcorex in #64
- fix(memory): drop the ignored authority argument from rpc.handle by @ddtcorex in #65
- docs(memory): state the replace contract in the memory tool schema by @ddtcorex in #66
- fix(memory): label the Memory view todo tab 'Todo' by @ddtcorex in #67
- fix(memory): re-anchor dsh peer ceiling to 0.3.0-0 by @ddtcorex in #68
- chore: align dsh devDependencies to 0.2.0-rc.2 and drop the dead client-runtime inject edge by @ddtcorex in #69
- docs(memory): align README, AGENTS and architecture with the code by @ddtcorex in #70
- ci: call the reusable workflows at their new repository name by @ddtcorex in #71
- docs(build): name the client bundle after the package, not a retired fork by @ddtcorex in #72
- docs(memory): point at the renamed SYNC-DESIGN doc by @ddtcorex in #73
- fix(memory): declare webServer on the connection entry by @ddtcorex in #74
- chore(release): 2.2.1 by @ddtcorex in #75
Full Changelog: v2.2.0...v2.2.1
v2.2.0
Fixed
- The Memory view fields keep the 16px floor on iOS — taps no longer
magnify the visual viewport (#58).
Changed
- Bump the
@deepseek-ai/*pins to0.1.7-alpha.1(#61) and move to
vitest 3 so the suite loads the DSH 0.1.6 peers (#60).
What's Changed
- fix(memory): hold iOS fields at the 16px floor by @ddtcorex in #58
- chore(deps): bump @deepseek-ai/* pins to 0.1.6-alpha.2 by @ddtcorex in #59
- test: move to vitest 3 so the suite can load DSH 0.1.6 peers by @ddtcorex in #60
- chore: bump @deepseek-ai/* pins to 0.1.7-alpha.1 by @ddtcorex in #61
- chore: release v2.2.0 by @ddtcorex in #62
Full Changelog: v2.1.0...v2.2.0
v2.1.0
Added
- Versioned whole-store repair (
storage/repair.ts,memory/repair-runner.ts, endpoint
memory.repair) — a pure planner splits entries glued without the§delimiter, drops exact
duplicates and moves a trailing[summary:…]to its canonical header position. A boot pass
(delimiter-repaired-v3) applies it to every store file; the v1 pass coveredKEY.mdonly, which
is why the globalMEMORY.mdkept a glued duplicate that consumed the whole# Global Memory
section and pushed a hard rule out of every prompt. Measured on the live store: 480 redundant
entries across 38 files, and 111 of 150KEY.mdentries carrying an unreadable summary tag. memory.maintenance— a preview-first archive planner (memory/maintenance.ts). Oldest
entries beyond a track's keep-bytes or max-age move to the track's*-archive.md;dryRun
defaults to true andconfirm: trueis required to write. Live preview:key149 → 38 live
entries (106 KB archived),project870 → 209 (267 KB).- Snapshot cost statistics (
cost-tracker.ts) — per-section bytes, entry counts, excluded-entry
counts and truncations, reported ascoston/dsh-maestro-memory-health. Baseline before this
existed: 25.9 MB of memory text injected across 1,300 sessions, median 12.6 KB per render, largest
single session 1.76 MB.
Changed
- Section caps are hard in both dimensions — each section now has an entry budget
(SNAPSHOT_SECTION_MAX_ENTRIES), and an untagged entry larger than twice its byte cap is
truncated with an explicit marker instead of silently owning the section. dailyentries carry[summary:…]— the exemption made the smallest section (512 B) the only
one that could not compact.- One entry-body predicate (
entryBodyKey) shared byadd(), the file guard and the sync
merge.isDuplicate()now strips[summary:…]as well as[id:…]— the file-level guard was
strictly weaker than its caller. add()reports the final stored text asentry, so a caller that must undo a write has a token
that actually matches what was written.
Fixed
- Auto-summaries were unreadable to the compactor.
ensureAutoSummaryappended[summary:…]at
the END of an entry, but the grammar — andparseEntrySummary,stripEntrySummary,
compactToHead— only reads the tag at the header position. Every auto-summarized entry was
therefore un-compactable: the 512 B# Recent Dailysection rendered 1,518 B and the global
section 2,303 B against a 2,048 B cap. New writes insert the tag after the header; repair
relocates existing ones. - The sync merge re-created duplicates on every sync.
merge.ts'scontentHash()stripped only
[id:…], so an entry and its summary-tagged twin hashed differently and the union merge kept both
(480 redundant entries across 38 files after the 2026-09-14 two-machine sync). It now uses the
shared body predicate. - A
§glued to content is now repaired on every track. The v1 KEY-only boot pass was a silent
no-op — it was called with a bogus cwd, so it resolvedprojects/<sha1('')>/KEY.md, found nothing and
returned{repaired: 0}— andrepairKeyDelimiter()now aliasesrepairFile()instead of carrying a
second copy of the split logic. It also reports entries recovered rather than the file's total entry
count, which made a no-op on a clean file look like a 2-entry repair. applyBatchrollback missed entries. It removed by matching the caller's raw content as a
substring of the stored entry, which stops matching once the summary tag moves to the header; the
batch now removes by the final stored textadd()reports.
Notes
- Archiving and the two-machine sync interact.
sync/mergesMEMORY.md/KEY.md/
KEY-archive.mdas a union, and id-less entries cannot express a deletion, so archiving on one
machine only is undone by the next merge. Archive on every machine with the same policy, or with
sync disabled.MEMORY-archive.mdis not in the merge set, so a project-track archive stays local.
Measured on the live store:projects/<hash>/MEMORY.md402 KB / 869 entries → 132 KB / 242 entries
after repair + archive, with the rendered prompt byte-identical before and after the archive.
Removed
pruneGlobalMemory()— unused, and it fitted the live global file to the snapshot cap by
dropping the overflow. It also carried a secondfitSectionthat disagreed with the
renderer's. Archiving (memory.maintenance) keeps those entries queryable instead.
What's Changed
- fix(memory): repair, measure and bound the durable store by @ddtcorex in #56
- chore(release): 2.1.0 by @ddtcorex in #57
Full Changelog: v2.0.0...v2.1.0
v2.0.0
Changed
- BREAKING — the durable todo tool is renamed
maestro_todo(#52). The tool
wasdtodo, and there is no alias: any existing prompt, preset, or doc that
still refers todtodomust be updated, or the durable store becomes
unreachable from it. Two different "Todos" reached the model in one session —
the harness's own in-session plantodo_writeand this plugin's durable
cross-session store — and neither description said what it was not, so the
model had to guess: a real todo could land in a throwaway session list while
the agent's own plan landed in the human's store. The tool's description now
opens with what it is and namestodo_write, a newmemory:task-systems
prompt context names both systems on every turn, and the Memory view tab reads
Todo storeinstead ofTodos. - Docs corrected against the code (#48, #49) —
lib/is documented as
gitignored build output that has to be refreshed locally (alink:install
loads it from disk) rather than committed, and the hard-coded suite counts are
gone fromAGENTS.md,CONTRIBUTING.md, andREADME.md: a count written
into a doc goes stale on the very next test.
Fixed
- The Memory tab keeps its own 13px scale on iOS (#51, correcting #50) — the
mobile plugin holds every text field on the page at a 16px floor under
html[data-mobile-nav-ios], which left the tab's placeholders and typed text
3px larger than the cards and labels around them. The panel now opts its own
input/textarea/selectout of that floor instead of raising the whole
panel to 16px. Accepted trade-off: iOS magnifies the visual viewport while one
of these fields has focus — the behaviour the floor exists to prevent. Desktop
and touch-Android were never affected and keep the 13px design.
Removed
storage/legacy-format.ts(#53) — a duplicate left over from before the
src/hostreorg: nothing imported it, and both tsconfigs compile only
src/host/**andsrc/client/**, so it was dead code that still carried a
stale copy of the entry grammar (including the retired tool name).
What's Changed
- docs: correct the lib/ build-output claim by @ddtcorex in #48
- docs: drop hard-coded test counts by @ddtcorex in #49
- fix(memory): keep the Memory tab on the iOS field floor size by @ddtcorex in #50
- fix(memory): keep the Memory tab fields at the panel's 13px on iOS by @ddtcorex in #51
- feat(memory)!: rename the durable todo tool to maestro_todo by @ddtcorex in #52
- chore: drop the dead pre-reorg storage/legacy-format.ts duplicate by @ddtcorex in #53
- chore: release v2.0.0 by @ddtcorex in #54
- docs: declare package license, repository and node engine by @ddtcorex in #55
Full Changelog: v1.3.0...v2.0.0
v1.3.0
Added
- Per-turn write watchdog (
src/host/memory/write-guard.ts, opt-in viaconfig.writeGuard) — counts consecutive human turns in which nodaily/projectentry was written, and escalates in the snapshot once the gap reachesthreshold(default 2). Long sessions gradually dilute the fixed end-of-turn discipline note, and a missed write used to be dropped silently; the watchdog tracks compliance on the program side so the snapshot itself escalates until the model writes.- Counts only turns opened by a direct human prompt — goal-continuation rounds (
source.kind === 'goal') and injected context ('plugin', e.g. wake notices) carry no per-turn duty. - Counts only turns that dispatched at least one tool call. A turn that merely answered a question did no work, so no debt accrues: without this gate the watchdog fired on ordinary question/answer exchanges and pressured the model into writing filler entries — exactly what the discipline note forbids ("never write entries containing only 'Idle' or placeholders").
readTurnFacts(agent)reads origin and tool activity from the session log in one scan. noteWritemarks the turn and the reset happens atturn-stopping, never at write time: resetting mid-turn would count the writing turn itself as a gap and misfire on every healthy turn.- Only a write that actually recorded something discharges the duty — a deduplicated add does not — so
applyBatchnow reports which entries it really added. - The listener is registered through
ctx.effectand isolates every error: a pacing aid must never fail a turn. - In-memory only; a host restart clears gaps.
- Counts only turns opened by a direct human prompt — goal-continuation rounds (
- Write-backlog snapshot alert — a
# ⚠️ Memory Write Backlogsection rendered immediately before the end-of-turn discipline note, which stays the snapshot's final instruction. Its text is static (threshold only, never the live count), so one open gap costs at most two tail snapshots: appear, then disappear. - Manual add composer in the Memory tab — each track now has an add box posting to the existing
memory.mutateRPC (action: 'add'), so recording a durable fact no longer needs a model round-trip. Drafts are bucketed per track, and the button stays disabled while the entry is empty or whilekey/projecthas no cwd.
Changed
- Subagent sessions get a per-achievement cadence — a subagent reports to a parent session rather than to the human, so its snapshot now carries a restrained turn-end note (one entry per independent achievement, nothing when there is nothing to report) instead of the per-turn duty, and never the backlog alert. The shared memory context (MEMORY / USER / KEY / Project Context) is still injected.
config.writeGuardis resolved once atapply()time; a threshold below 1 reads as 1 rather than disabling the watchdog or firing on every turn.
Fixed
- Removed a stale upstream repository name from
AGENTS.md(public-doc hygiene).
What's Changed
- chore(memory): default memory root to dsh-maestro-memory by @ddtcorex in #41
- fix: stop tracking lib/ build output by @ddtcorex in #42
- fix(memory): declare row inject for DSH 0.1.5 by @ddtcorex in #43
- fix(memory): neutralize template braces in the prompt snapshot by @ddtcorex in #44
- fix(memory): keep today's entry in the Recent Daily slot by @ddtcorex in #45
- feat(memory): per-turn write watchdog, subagent cadence, manual add composer by @ddtcorex in #46
- chore: release v1.3.0 by @ddtcorex in #47
Full Changelog: v1.2.5...v1.3.0
v1.2.5
Fixed
- KEY.md delimiter — repaired malformed
§\ndelimiters; 19 invariant entries now readable in snapshot viarepairKeyDelimiter(cwd)one-time migration. - Global memory corruption recovery — restored
MEMORY.mdfrom sync remote after accidentalpruneGlobalMemory()removed 4 of 5 entries during testing. - Discipline note — updated to match actual write behavior (daily-only +
memory_suggest target=keyfor decisions; removedprojectreference). - User Memory — auto-bootstrap from session context when
USER.mdmissing/empty.
Added
- REFERENCE.md slice in snapshot — bounded
# Project Knowledgesection (2048 bytes cap) injects curated project reference. MaestroMemoryStore.repairKeyDelimiter(cwd)— one-time repair for legacy§\ndelimiters in KEY.md.MaestroMemoryStore.pruneGlobalMemory()— keeps newest entries fitting 2048-byte snapshot cap.projectReferencePath(root, cwd)resolver in storage layout.MaestroMemoryStore.resolveRoot()— public accessor for memories root.
Changed
renderSnapshotnow includes# Project Knowledgefrom REFERENCE.md and auto-createsUSER.mdfrom session context.- Discipline note updated to
Write daily via memory entries+memory_suggest target=keyfor decisions.
What's Changed
- fix(memory): restore effectiveness — KEY delimiter, REFERENCE slice, discipline sync, global memory recovery by @ddtcorex in #39
- chore: release v1.2.5 — memory effectiveness fixes by @ddtcorex in #40
Full Changelog: v1.2.4...v1.2.5
v1.2.4
v1.2.3
Fixed
- Memory tab overlay bleed (3) —
viewAreabumped toz-index:9andwidthHandle(z-index:8) hidden via:has(.memx) {display:none}+ JS fallback on mount/unmount so Memory fully covers chat transcript; hover glow no longer bleeds through. Keeps flex layout so composer remains visible below. Verify 268/268 green.
What's Changed
- chore(ci): bump dsh-maestro-ci pin to e2448b1 by @ddtcorex in #34
- chore(deps): bump @deepseek-ai/* to 0.1.2-alpha.2, cordis 4.0.2 by @ddtcorex in #35
- fix(memory): ensure Memory overlay fully covers chat and hides width handles (1.2.3) by @ddtcorex in #36
Full Changelog: v1.2.2...v1.2.3