Skip to content

Add dashboard memory management - #436

Merged
m-aebrer merged 6 commits into
masterfrom
feature/issue-335-dashboard-memory-management
Aug 7, 2026
Merged

Add dashboard memory management#436
m-aebrer merged 6 commits into
masterfrom
feature/issue-335-dashboard-memory-management

Conversation

@m-aebrer

@m-aebrer m-aebrer commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Closes #335

Add a dashboard Memories tab for securely browsing, editing, and deleting global and project memory entries while keeping each scope's index synchronized.

Implementation plan posted as a comment below.

@m-aebrer

m-aebrer commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Implementation Plan

Problem analysis

Memory is currently a file convention consumed by the coding-agent resource loader, while the dashboard has no typed or security-bounded way to inspect or mutate it. The implementation therefore needs a dedicated memory-domain API rather than exposing arbitrary file operations through the Files tab.

The dashboard is fleet-wide, so project scope cannot mean one implicit cwd. Project choices will be built from the active runtime and persisted-session cwd inventory already available to the server, normalized through the same git-root rule used by memory loading, canonicalized, deduplicated, and sorted by stable project path. Global memory is one separate scope. Claude-compatible memory remains outside this writable surface because dreb treats it as read-only.

canonicalizePath() resolves confusion and symlinks but intentionally does not jail a target. Every entry operation must additionally enforce post-canonicalization containment beneath the server-selected memory root. Clients will select server-issued scope IDs and direct-child filenames, not submit arbitrary memory directories.

Deletion is a logical entry-plus-index operation: successful deletion must remove the entry file and its matching MEMORY.md link. The implementation will make a bounded link-line edit that preserves unrelated Markdown, reject stale revisions, use same-directory temporary files and rename for replacement, and surface any partial failure loudly.

Deliverables

  1. Shared memory protocol

    • Add DTOs for global/project scopes, entry summaries and validated frontmatter metadata, full index/entry documents, opaque content revisions, and mutation/conflict responses.
    • Represent MEMORY.md as a special index document rather than an ordinary entry.
    • Keep project ordering deterministic by canonical project path and entry ordering deterministic by filename.
  2. Reusable path and memory helpers

    • Expose or reuse the coding-agent git-root/frontmatter helpers needed by the dashboard without duplicating memory identity rules.
    • Add a dashboard memory service that discovers the global scope plus canonical project roots from active runtimes and disk sessions, including projects whose memory directory does not yet exist.
    • Restrict entries to direct-child .md files, reserve MEMORY.md and internal dot/temp/lock files, and reject separators, traversal, directories, and out-of-root symlink targets.
    • Canonicalize the selected root/target and apply segment-aware containment after symlink resolution; recheck around reads/mutations where a symlink swap could otherwise escape the scope.
  3. Read, edit, and delete semantics

    • List scopes and complete index/entry summaries, returning clean empty results for missing directories or indexes.
    • Read the full MEMORY.md or entry content with an opaque content revision. The dashboard will display the complete index and flag indexes over the 200-line prompt-loading limit rather than truncating them.
    • Parse and validate entry frontmatter (name, description, and one of the four documented types) while preserving the raw Markdown as the editable source. Invalid YAML/schema errors will be explicit.
    • Save existing documents only; entry creation and filename rename remain outside this change. Require the caller's loaded revision, return 409 on concurrent modification, and publish writes through same-directory temporary files plus atomic rename and cleanup.
    • Delete only existing entry documents after confirmation and revision checks. Remove every matching local index link for that direct-child filename with a bounded formatting-preserving edit, update the index and entry as one logical operation, and fail loudly/restore state if the filesystem sequence cannot complete. A successful response must never leave the deleted file linked from MEMORY.md.
    • Log list/read/save/delete operations consistently with other dashboard host filesystem operations.
  4. HTTP API integration

    • Register non-runtime-scoped Express routes for scope inventory, document reads, revision-checked saves, and revision-checked deletes.
    • Validate all request fields and map malformed input, missing scopes/files, invalid frontmatter, conflicts, containment failures, and filesystem errors to explicit status codes/messages.
    • Keep absolute mutation paths server-side; the client sends only scope IDs, document kind/filename, content, and revisions.
  5. Memories dashboard screen

    • Add the memories hash route, route serialization/parsing, Topbar link, and App screen match.
    • Build a responsive wiki-style screen with global/project scope selection, deterministic project selector, index and entry navigation, metadata display, sanitized rendered Markdown via the existing MarkdownBody, and a raw Markdown editor.
    • Provide explicit loading, missing/empty, malformed-entry, unsaved, saving, saved, conflict, and general error states.
    • Require confirmation before deletion; after success, refresh the index/entry list and select a valid remaining document. Conflicts must preserve the user's draft and require reload/review rather than silently overwriting.
    • Add focused responsive styles using existing dashboard tokens and controls.
  6. Documentation

    • Update the root README.md dashboard and memory descriptions.
    • Update packages/coding-agent/README.md memory documentation to explain dashboard curation, supported scopes, edit/delete behavior, concurrency protection, and automatic index cleanup.
    • Update packages/coding-agent/docs/dashboard.md and packages/dashboard/README.md screen inventories and security/behavior notes for the Memories tab.

Files to create

  • packages/dashboard/src/server/memories.ts — scope discovery, jailed document access, validation, revisioning, atomic save, and synchronized deletion.
  • packages/dashboard/src/client/screens/memories.tsx — Memories tab UI.
  • packages/dashboard/test/memories.test.ts — focused memory service unit/security tests.

Files to modify

  • packages/coding-agent/src/index.ts — export the existing git-root/path helper(s) required to preserve shared project identity rules, if direct reuse requires it.
  • packages/dashboard/src/shared/protocol.ts — memory DTOs and mutation contracts.
  • packages/dashboard/src/server/server.ts — construct the memory service and register HTTP routes using runtime/session cwd inventory.
  • packages/dashboard/src/client/api.ts — typed memory API wrappers.
  • packages/dashboard/src/client/state/store.tsmemories route parsing and serialization.
  • packages/dashboard/src/client/app.tsx — screen import and route match.
  • packages/dashboard/src/client/components/common.tsx — Topbar Memories link and active state.
  • packages/dashboard/src/client/components/transcript.tsx — only if a small export adjustment is needed to reuse the existing sanitized Markdown renderer; rendering behavior itself remains shared.
  • packages/dashboard/src/client/styles/app.css — responsive Memories layout and editor/navigation states.
  • packages/dashboard/test/server.test.ts — HTTP route integration, validation, conflicts, and error mapping.
  • packages/dashboard/test/client/store.test.ts — Memories route round-trip.
  • packages/dashboard/test/client/screens.test.tsx — screen loading/view/edit/delete/conflict/empty-state behavior and API mocks.
  • README.md, packages/coding-agent/README.md, packages/coding-agent/docs/dashboard.md, packages/dashboard/README.md — complete user-facing documentation coverage.

Testing approach

Memory service unit tests (packages/dashboard/test/memories.test.ts)

  • Discover global and project scopes from active and historical cwd values; resolve nested cwd values to git roots; canonicalize/deduplicate aliases; include missing memory directories; and sort paths deterministically.
  • List missing/empty directories and indexes without creating them.
  • List only allowed direct-child entry Markdown files and sort them by filename.
  • Read full indexes beyond 200 lines while returning the line-limit warning.
  • Parse valid metadata and return loud errors for malformed YAML, missing fields, and invalid types.
  • Reject traversal, separators, reserved names, non-Markdown files, directories, broken links, symlink escapes, and path-prefix collisions.
  • Verify revision stability, stale-save/stale-delete conflicts, atomic replacement, temporary-file cleanup, and preservation of the user's file after failed writes.
  • Delete an indexed entry and remove all matching local links while preserving headings, comments, unrelated links, and surrounding formatting.
  • Cover unindexed entries, missing indexes, ambiguous/unsupported index links, and injected filesystem failures; successful deletion may not leave a dangling matching link, and failures must be loud without silently claiming completion.

Server integration tests (packages/dashboard/test/server.test.ts)

  • Exercise scope inventory, index/entry read, save, and delete routes end to end against temporary home/project roots.
  • Verify request validation and status mapping for unknown scope IDs, invalid names/frontmatter, missing documents, stale revisions, containment denial, and filesystem failures.
  • Verify project inventory combines runtime and disk-session cwd sources without exposing arbitrary client-selected roots.
  • Verify operation logging for read and mutation routes.

Client tests

  • packages/dashboard/test/client/store.test.ts: parse and serialize #/memories and retain the fleet fallback for unknown hashes.
  • packages/dashboard/test/client/screens.test.tsx: verify global/project selection, stable project ordering, complete index and over-limit warning, entry metadata and sanitized rendered content, raw edit/save, save validation errors, stale conflict draft preservation, delete confirmation/cancellation/success, post-delete index refresh, missing/empty scopes, malformed entries, loading, and API failure states.
  • Verify the Topbar active state and application screen wiring.
  • Include narrow/mobile rendering assertions where the existing test harness supports layout semantics; perform browser-level responsive verification during manual QA.

Validation commands

  • Run Biome on every changed source/test/document file where applicable.
  • Run focused coding-agent/dashboard Vitest files during development, then the complete npm test suite.
  • Run npm run build after all code changes and npm run verify-workspace-links.
  • After the build, manually launch the dashboard against temporary fixture memories and verify desktop/mobile browse, edit, conflict, and synchronized delete behavior without restarting the user's existing dashboard service.

Acceptance criteria

  • A Memories tab is reachable from the Topbar and direct hash navigation.
  • Global memory and every discovered canonical project scope are selectable in stable order; missing scopes render as empty rather than erroring.
  • The complete index and individual entries are viewable; entry metadata is validated and displayed; Markdown rendering is sanitized.
  • Existing index/entry Markdown can be edited and atomically saved to the correct server-selected scope.
  • Stale edits/deletes return a visible conflict and never overwrite newer LLM/user changes.
  • Entry deletion requires confirmation, removes the file and its matching index link, preserves unrelated index formatting, and never reports success with a dangling link.
  • Traversal, reserved names, arbitrary absolute paths, prefix collisions, and symlink escapes cannot read, write, or delete outside the chosen memory root.
  • Claude-compatible memory is not writable through the feature.
  • Empty, missing, malformed, loading, conflict, and filesystem-error states are explicit and tested.
  • Server/service/client/route tests pass; full tests, build, workspace-link verification, and manual responsive QA pass.
  • All four user-facing documentation locations accurately describe the feature.

Risks and settled scope

  • Cross-process races: content revisions prevent stale dashboard mutations; atomic rename prevents partial individual-file writes. Delete/index synchronization additionally needs explicit rollback/error handling because two filesystem objects cannot be replaced in one native transaction.
  • Filesystem TOCTOU: canonical containment must be checked at operation time and rechecked where reads or replacements can race with symlink changes.
  • User-authored index formatting: deletion uses a bounded matching-link edit, not parse-and-reserialize Markdown.
  • Project identity: nested session cwd values are normalized through findGitRoot; this avoids inheriting the historical dream-discovery mismatch that checks exact launch cwd.
  • Missing directories: browsing does not create them. Since v1 does not create entries, a missing index/entry remains an empty read-only state until the memory system creates files normally.
  • No create/rename: this change views, edits, and deletes existing documents only.
  • No Claude mutation: Claude-compatible paths remain read-only and are omitted from writable scope inventory.

Plan created by mach6

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Vitest coverage

Metric Covered Total Coverage
Statements 39006 55214 70.64%
Branches 20800 34437 60.4%
Functions 8247 11539 71.47%
Lines 28181 39691 71%

View full coverage run

@m-aebrer

m-aebrer commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Progress Update

Implemented the dashboard Memories feature across server, client, tests, and documentation.

Delivered

  • Added deterministic global/project memory scope discovery from active runtimes and persisted sessions, normalized through git roots.
  • Added a dedicated jailed memory filesystem API with direct-child Markdown allowlisting, symlink/traversal containment, validated frontmatter, complete index reads, content-size limits, SHA-256 revisions, atomic replacements, and explicit conflicts.
  • Made entry deletion a synchronized file-plus-index operation: matching safe index-link lines are removed first, unrelated formatting is preserved, unlink failure restores the original index, and success cannot leave a matching dangling link.
  • Added authenticated REST routes and typed wire/client APIs without accepting arbitrary memory paths from the browser.
  • Added the responsive Memories tab, Topbar/hash routing, scope/document navigation, metadata diagnostics, sanitized Markdown preview, raw editing, draft-preserving conflict errors, empty/missing states, and delete confirmation.
  • Updated the root README, coding-agent README, dashboard guide, and dashboard package README.
  • Added service, route, route-state, and screen tests, including symlink escapes, stale revisions, malformed metadata, safe index cleanup, conflict handling, missing scopes, and repeated navigation.

Verification

  • Commit hook: 5,537 passed, 711 skipped, 0 failed.
  • Focused dashboard suite: 409 tests passed.
  • Full npm test: passed across all workspaces.
  • npm run build: passed.
  • npm run verify-workspace-links: passed.
  • Biome and git diff --check: passed.
  • Isolated Playwright QA passed on an alternate port for desktop/mobile navigation, rendering, edit persistence, confirmation cancellation, and horizontal-overflow checks. The existing dashboard service was not restarted or modified.

Commit: d070187


Progress tracked by mach6

@m-aebrer
m-aebrer marked this pull request as ready for review August 7, 2026 17:22
@m-aebrer

m-aebrer commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Code Review

Critical

None.

Important

  1. Global and home-project scopes can identify the same physical memory directory (packages/dashboard/src/server/memories.ts, scope discovery). Project roots are deduplicated against each other but not against the global ~/.dreb/memory scope. A session launched from home, or a home directory resolved as a project root, can therefore produce the apparent duplicate observed in QA under two IDs, with confusing independent revision state. Confidence: 85.

  2. One unreadable, oversized, or escaping-symlink entry aborts the entire scope listing (packages/dashboard/src/server/memories.ts, entry loop in listing). Per-entry resolve/stat/read errors reject the listing rather than returning an entry-level diagnostic, preventing the dashboard from browsing or repairing otherwise valid entries. Confidence: 82.

  3. No endurance/race test covers repeated scope and document navigation (packages/dashboard/test/client/screens.test.tsx). The current screen exercise uses one scope and one entry and does not control stale request completion order, bound request counts, or perform browser-level repeated navigation matching the OOM report. Confidence: 99.

  4. Maximum-size document behavior and repeated large-payload navigation are untested (packages/dashboard/test/memories.test.ts, packages/dashboard/test/client/screens.test.tsx). Tests use only 201 short lines, not the 1 MiB limit, above-limit rejection, or repeated browser navigation among large documents. Confidence: 99.

  5. Canonical alias deduplication is only nominally tested (packages/dashboard/test/memories.test.ts, scope discovery test; packages/dashboard/test/server.test.ts). Coverage uses a literal path and nested path, not symlink aliases or active-runtime versus persisted-session aliases, and does not catch global-versus-project identity duplication. Confidence: 97.

Suggestions

  1. The resource accessor named document shadows the DOM global (packages/dashboard/src/client/screens/memories.tsx). Rename it to memoryDocument or similar to avoid a latent maintenance trap. Confidence: 90.

  2. Listing and mutations amplify full scans and allocations (packages/dashboard/src/server/memories.ts). Listing reads every entry body up to 1 MiB for metadata, while nested requireScope/readDocument/listing calls repeatedly rediscover all scopes and sessions. This is bounded and server-side, not a demonstrated browser leak, but can create substantial pressure and latency. Confidence: 85.

  3. Preview reparses the full draft on every keystroke (packages/dashboard/src/client/screens/memories.tsx). MarkdownBody supports throttling, but the memory preview omits it, causing synchronous parse/sanitize/DOM replacement and GC churn for large documents. Confidence: 82.

  4. Several service details can be simplified (packages/dashboard/src/server/memories.ts): use a Set rather than a self-mapping Map for project roots; consider whether repeated path resolution and the one-line canonicalMemoryRootIfExists wrapper add necessary security/clarity. Any removal of operation-time path rechecks must preserve the plan's TOCTOU protections. Confidence: 82–95.

OOM investigation

No agent found a ≥80-confidence infinite reactive loop or unbounded client-side accumulation in the changed code. Signals/resources replace rather than append state, effects appear convergent, and individual document payloads are capped. The duplicate-scope defect directly matches part of the QA report but does not alone explain a browser OOM. The strongest pressure candidates are repeated full server scans and unthrottled Markdown preview; browser heap instrumentation during repeated navigation is needed to establish the crash mechanism.

Strengths

  • Strong canonical containment, direct-child allowlisting, and operation-time path rechecks.
  • Coherent revision conflicts and same-directory atomic writes.
  • Careful delete/index synchronization with rollback and dangling-link verification.
  • Loud client-visible conflict and filesystem error handling.
  • Good core tests for traversal, symlink escape, malformed metadata, stale revisions, and formatting-preserving index cleanup.

Agents run: code-reviewer, error-auditor, test-reviewer, completeness-checker, simplifier


Reviewed by mach6

@m-aebrer

m-aebrer commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Review Assessment

Review comment

Classifications

Finding Classification Reasoning
1. Global and home-project scopes can identify the same directory genuine Factual: scopes() inserts global memory separately while project roots are deduplicated only among themselves, so a canonical home project produces the same memory directory under another ID. Scope: canonical deduplication is explicit scope and this matches the QA-observed apparent copy.
2. One bad entry aborts the listing false-positive Factual: resolve/stat/read failure does reject the listing. Scope: this is deliberate loud, fail-closed handling for containment/filesystem failures; partial listing is not required, and the symlink test expects rejection.
3. No repeated-navigation endurance/race test genuine Factual: current UI coverage uses one scope/entry and does not test stale completion ordering, sustained switching, or request bounds. Scope: repeated multi-scope navigation produced an actual browser OOM, so regression coverage is required.
4. Maximum-size and repeated large-payload behavior untested genuine Factual: tests use a short 201-line fixture and do not reach the 1 MiB boundary, above-limit rejection, or repeated large-document navigation. Scope: the PR introduces this safety contract, and large navigation is relevant to the observed crash.
5. Canonical alias dedupe nominally tested genuine Factual: coverage omits symlink aliases, active-versus-persisted aliases, and global-versus-project collision. Scope: canonical deduplication across inventory sources is an approved requirement and must cover the correction for finding 1.
6. document shadows the DOM global nitpick Factual: the accessor shadows the global within the component. Scope: current uses are unambiguous and this has no correctness or acceptance impact.
7. Listing/mutations amplify scans and allocations genuine Factual: listing reads every accepted entry body and nested service calls repeatedly rediscover all scopes/session inventory, creating avoidable I/O and allocation pressure on each operation. Scope: the user explicitly made memory/OOM and navigation pressure a required review focus and will not merge with known performance hazards deferred. This must be profiled and corrected in this PR while preserving containment rechecks.
8. Preview reparses every draft on every keystroke genuine Factual: input updates draft, and unthrottled MarkdownBody synchronously parses/sanitizes each value despite built-in throttle support. Scope: documents may be 1 MiB and this is browser-pressure introduced by the new rendered editor, relevant to the OOM report.
9. Service details can be simplified nitpick Factual: the self-mapping Map and one-line wrapper are cosmetic, while repeated path resolution is deliberate TOCTOU defense. Scope: cosmetic changes are unnecessary and removing path rechecks would violate the security plan.

Action Plan

  1. Deduplicate global and project scopes by canonical memory-directory identity, including the home-project collision, while preserving stable ordering and IDs.
  2. Throttle or otherwise bound live Markdown preview work for large drafts, with a focused rapid-input test.
  3. Add multi-scope repeated-navigation regression coverage for stale requests, final selection, and bounded request behavior matching QA.
  4. Add exactly-at-limit and above-limit read/save coverage plus repeated navigation among realistically large documents.
  5. Reduce redundant server scope discovery and full-entry scanning based on profiling, while preserving all operation-time containment rechecks. Add regression coverage for bounded scan/read behavior.
  6. Expand canonical-deduplication tests for symlink aliases, active-versus-persisted cwd aliases, and global-versus-project identity.

Assessment by mach6

@m-aebrer

m-aebrer commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Progress Update

Fixed the genuine memory navigation and pressure findings from the review.

  • Deduplicated the global and home-project scopes by canonical identity, including symlink aliases.
  • Batched scope/file state changes so navigation no longer issues transient cross-scope document requests.
  • Throttled live Markdown preview parsing during rapid edits.
  • Removed repeated scope rediscovery from save/delete response construction while preserving operation-time containment checks.
  • Made listings reuse their canonical root and read only entry frontmatter instead of every complete entry body.
  • Added regression coverage for stale multi-scope navigation with large documents, bounded request counts, one active editor/preview, preview throttling, canonical aliases, global/project collisions, and exact/over-limit 1 MiB documents.

Verification passed: focused dashboard tests, full test suite, Biome, full build, workspace-link verification, and diff checks. The commit hook passed 5,540 tests with 711 skipped.

Commit: 02b8c4d


Progress tracked by mach6

@m-aebrer

m-aebrer commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

QA Review

Important

  1. Rendered memory-index links navigate out of Memories. Links in the rendered MEMORY.md preview are clickable, but selecting a memory link performs a fresh navigation to the main Fleet page instead of opening the corresponding on-disk memory entry in the current Memories screen. Local direct-child memory links should be intercepted and resolved through the current scope/document selection. External links should retain their normal safe behavior.

  2. Empty project scopes clutter the selector. Project directories with no project memory directory/content should not appear. Because this version cannot manually create memory entries, an empty or missing project scope offers no useful action and unnecessarily expands the list. Keep the global scope, but include project scopes only when project memory exists and is browseable.

  3. Scope/document loading has no visible progress feedback. Selecting a project scope or context can leave the UI apparently idle while the backend discovers and reads memory. If the backend work is required for freshness/security, the screen must immediately communicate that loading is in progress—for example, with a spinner or skeleton in the affected scope/document/editor region—and prevent stale content from looking current during the wait.

QA context

These behaviors were observed during post-fix manual QA. The loading delay itself may be justified by fresh canonical scope discovery and containment checks; the issue is that the current UX provides no immediate indication that the selection is being processed.


Reviewed by manual QA

@m-aebrer

m-aebrer commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

Progress Update

Fixed all three issues from manual QA:

  • Rendered direct-child links in MEMORY.md now open the corresponding entry inside the current Memories scope; external links retain normal safe behavior.
  • Empty and missing project memory scopes are omitted from the selector while the global scope remains available.
  • Scope and document transitions now immediately hide stale editor content and show accessible loading feedback until fresh data arrives.
  • Added focused service and client regression tests and updated all four user-facing documentation locations.

Verification passed: focused dashboard tests, full npm test, Biome, full build, workspace-link verification, and diff checks. The commit hook passed 5,543 tests with 711 skipped.

Commit: 703f87b


Progress tracked by mach6

@m-aebrer
m-aebrer merged commit 6ae0685 into master Aug 7, 2026
3 checks passed
@m-aebrer
m-aebrer deleted the feature/issue-335-dashboard-memory-management branch August 7, 2026 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Memories tab to dashboard for viewing, editing, and deleting memories

1 participant