Skip to content

feat(web): return memory_dir category from server (closes #299)#301

Merged
memtomem merged 3 commits intomainfrom
feat/memory-dir-category-server
Apr 19, 2026
Merged

feat(web): return memory_dir category from server (closes #299)#301
memtomem merged 3 commits intomainfrom
feat/memory-dir-category-server

Conversation

@memtomem
Copy link
Copy Markdown
Owner

Closes #299. Follow-up to PR #300 / #297.

Scope

Consolidates the memory_dir classification (regex mirror) into a
single server-side source. Presentation dicts (group order, i18n label
keys, default-collapsed set) stay on the client by design — new
providers still need the client-side ordering + i18n edits plus the
server pattern row. This PR only eliminates the duplicated regex.

Changes

  • config.categorize_memory_dir(path) — stateless per-path classifier.
    Returns one of PROVIDER_DIR_CATEGORIES or "user".
  • _PROVIDER_CATEGORY_PATTERNS — single source of truth for the
    category-to-regex mapping.
  • PROVIDER_DIR_CATEGORIES is now derived from the same pattern
    table instead of a parallel hard-coded tuple, so adding a category
    can't open a new drift seam.
  • _detect_provider_dirs delegates per-path classification to the
    helper (buckets by categorize_memory_dir(p)), so discovery and
    classification lock onto the same pattern table.
  • memory_dir_stats (indexing/engine.py) emits category on every
    entry. The /api/memory-dirs/status handler is an untouched
    passthrough.
  • sources-memory-dirs.js drops _categorizeMemoryDir and reads
    statusByPath[path].category. Drift-mirror warnings removed from
    both config.py docstring and the JS file header.

Timing note (intentional behaviour change)

Classification was synchronous in the client; it is now async. Before
the first /api/memory-dirs/status response — first paint, or a
transient fetch error — every entry falls back to 'user' and then
settles into its group on the next render. Locally the fetch is
sub-100ms so the flicker is not user-visible, but flagging it here so
the async shift isn't invisible in review.

Tests

  • TestCategorizeMemoryDir (8 cases): all four categories, trailing-
    slash normalisation, user fallback, look-alike paths that must NOT
    match (/archive/.codex/memories-backup, /work/claude/plans/foo),
    Path input, and an assertion that PROVIDER_DIR_CATEGORIES equals
    the derived tuple.
  • TestDetectProviderDirsRoundtrip — drift guard: every path
    _detect_provider_dirs returns must classify back to the same
    category via categorize_memory_dir. Without this the "single source
    of truth" claim is code-review-only; this makes it CI-enforced
    (framing per feedback_silent_policy_enforcement_gap.md).
  • TestMemoryDirStats::test_category_reflects_provider_layout — mix of
    provider-shaped and user paths produces the right category on each
    entry.

Full suite: 1855 passed, 0 regressions. Ruff + mypy clean on touched files.

Out of scope

  • Gemini (~/.gemini/GEMINI.md) — still excluded: single-file surface,
    secrets in parent directory.
  • Pydantic response model typing for /api/memory-dirs/status — stays
    untyped dict-through; typing is its own sweep.
  • Windows path-sep support — posix-style regex for now; documented in
    the helper docstring so a future Windows pass knows where to look.

Test plan

  • uv run pytest -m "not ollama" green
  • uv run ruff check / ruff format --check clean
  • uv run mypy advisory clean on touched files
  • Manual smoke: uv run mm web → Sources tab → confirm groups
    render correctly (Claude projects / Claude plans / Codex / User),
    DevTools Network shows category on each entry of
    /api/memory-dirs/status, adding a user path lands it in the user
    group, default-collapsed state unchanged

🤖 Generated with Claude Code

pandas-studio and others added 2 commits April 20, 2026 06:42
The memory_dirs widget previously classified each entry client-side with
a regex mirror of ``_detect_provider_dirs`` — every new provider needed
two matching edits with no tooling guard. Move classification to the
server so the widget just reads ``entry.category`` from
``GET /api/memory-dirs/status``.

- ``config.categorize_memory_dir`` is the stateless per-path classifier.
  Pattern rows live in ``_PROVIDER_CATEGORY_PATTERNS`` and
  ``PROVIDER_DIR_CATEGORIES`` is derived from the same table, so adding
  a category can't silently diverge.
- ``memory_dir_stats`` emits ``category`` on every entry alongside the
  existing ``path`` / ``chunk_count`` / ``source_file_count`` / ``exists``
  fields. No endpoint/route change — the handler is a passthrough.
- ``sources-memory-dirs.js`` drops ``_categorizeMemoryDir``. Presentation
  constants (order, i18n label keys, default-collapsed set) stay on the
  client by design — new providers still need the client-side ordering
  + i18n edits plus the server pattern row.
- Drift guard: ``TestDetectProviderDirsRoundtrip`` asserts every path
  ``_detect_provider_dirs`` returns classifies back to the same category
  via ``categorize_memory_dir``. Turns the "single source of truth"
  claim from a code-review invariant into a CI-enforced one.

Classification timing changes from synchronous to async-on-status-fetch.
Before the first ``/api/memory-dirs/status`` response, every entry
renders under ``user`` and then settles into its group on the next
render. Locally the fetch is sub-100ms so the flicker is not visible,
but call it out so the behaviour shift isn't invisible in review.

Follow-up to #300 / #297.

Co-Authored-By: Claude <[email protected]>
``handleReindexGroup`` filtered candidate paths via
``_categorizeMemoryDir(d)`` — the previous commit removed that
function when consolidating classification server-side but missed
this caller, so clicking the per-group ↻ button threw
``ReferenceError: _categorizeMemoryDir is not defined`` and aborted
silently.

Mirror the render-loop fallback pattern: read ``category`` from
``statusByPath[d]`` with a ``'user'`` default.

Audited with ``grep -r _categorizeMemoryDir`` post-fix: zero
references. Cache-bust bumped (``v=4`` → ``v=5``) so returning users
pick up the fix without a manual hard refresh.

Co-Authored-By: Claude <[email protected]>
@memtomem memtomem merged commit 107a88b into main Apr 19, 2026
7 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 19, 2026
@memtomem memtomem deleted the feat/memory-dir-category-server branch April 19, 2026 22:09
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consolidate memory_dir categorization: server-returned field instead of client mirror

2 participants