Skip to content

feat(cli): wizard appends matching namespace rules for accepted providers (closes #296)#312

Merged
memtomem merged 1 commit intomainfrom
fix/296-wizard-preset-rules
Apr 20, 2026
Merged

feat(cli): wizard appends matching namespace rules for accepted providers (closes #296)#312
memtomem merged 1 commit intomainfrom
fix/296-wizard-preset-rules

Conversation

@memtomem
Copy link
Copy Markdown
Owner

@memtomem memtomem commented Apr 20, 2026

Summary

mm init now appends matching NamespacePolicyRule entries to
namespace.rules when the user accepts a provider category. Auto-discovered
Claude-projects memory dirs stop collapsing to the default namespace —
the acute shape of #296, finishing direction (3) after #306 landed
direction (2) ({ancestor:N} placeholder primitive).

Preset table (deliberately flat pending RFC #304):

Category path_glob namespace
claude-memory ~/.claude/projects/*/memory/** claude:{ancestor:1}
claude-plans ~/.claude/plans/** claude-plans
codex ~/.codex/memories/** codex

Key decisions (settled in design thread)

  • A-1 — Dedup normalization: store literal ~ (matches what the user
    typed and what the current wizard writes), compare after expanduser()
    on both sides. ~/.codex/memories/** and /Users/foo/.codex/memories/**
    are the same rule for idempotency.
  • A-2 — Conflict resolution: same path_glob + different namespace
    → user rule wins, wizard skips, banner surfaces the skip with .
    _rule_matches_existing() is a standalone helper so RFC RFC: nested provider/product hierarchy for memory_dir categorization #304 can reuse
    it.
  • B-1 — Non-interactive parity: --include-provider registers the
    same rules as the interactive prompt, but only when the category had
    detected dirs on this machine (parallel to the interactive step
    skipping empty categories).
  • B-2 — Banner, not --dry-run: pre-write banner lists proposed
    (+) and skipped () rules in both paths. Full --dry-run support
    for mm init is a separate scope — follow-up issue to file once this
    lands. The banner is the foundation.

Scope boundary

  • Placeholder vocabulary is locked to {ancestor:1} via
    _VALID_PRESET_PLACEHOLDERS + a test, so extending the table beyond
    the three rows above gets an explicit review rather than a silent edit.
    Revisit when RFC RFC: nested provider/product hierarchy for memory_dir categorization #304 settles the hierarchy format.
  • namespace.rules stays in _FRESH_PRESERVE_KEYS. The wizard marks
    the key as wizard-touched only when proposals were considered, so the
    Preserved block doesn't flag merged rules as leftover on clean re-runs.
  • No startup migration — users on existing installs re-run mm init to
    pick up the presets. Keeps the wizard as the single opt-in surface and
    avoids the "silent policy" failure mode flagged in prior RFCs.

Test plan

  • ruff check + ruff format --check clean
  • pytest -m "not ollama" — 1900 pass, 0 regressions
  • mypy clean on init_cmd.py
  • 12 new tests cover: per-category acceptance (parametrized ×3),
    decline, write-path merge, re-run idempotency, user-rule
    preservation on same-glob-diff-namespace, dedup normalizes
    expanduser, --include-provider parity (dirs present + absent),
    banner states (proposed+skipped / silent / all-skipped),
    placeholder vocabulary lock.

Closes #296.

…ider categories (#296)

When the user accepts a provider category in the ``_step_provider_dirs``
wizard step, append a matching ``NamespacePolicyRule`` to
``namespace.rules`` so auto-discovered Claude-projects memory dirs stop
collapsing to the ``default`` namespace:

  claude-memory  ~/.claude/projects/*/memory/**  ->  claude:{ancestor:1}
  claude-plans   ~/.claude/plans/**              ->  claude-plans
  codex          ~/.codex/memories/**            ->  codex

``{ancestor:1}`` (shipped in #306) picks the project-id folder above the
generic ``memory`` basename; single-dir categories use literal namespaces.
Labels stay flat pending RFC #304's decision on the vendor/product
hierarchy wire format.

Dedup is by ``path_glob`` with ``~`` expansion on both sides so re-runs
are idempotent regardless of whether earlier edits used the literal ``~``
or the expanded absolute path. User-authored rules with the same
``path_glob`` but a different ``namespace`` win — the wizard skips the
preset and surfaces the skip in the pre-write banner so the decision
isn't silent. ``namespace.rules`` stays in ``_FRESH_PRESERVE_KEYS``, and
the wizard marks the key as wizard-touched only when proposals were
considered, keeping ``--fresh`` semantics intact.

Pre-write banner lists proposed rules with ``+`` and dedup-skipped rules
with ``⏭``, plus an all-skipped one-liner so the user always sees what
the wizard considered. Banner is emitted in both interactive and
``--include-provider`` paths. The flag-driven path only registers a rule
when the category had detected dirs on this machine — parity with the
interactive step skipping empty categories keeps config clean of dead
rules.

Co-Authored-By: Claude <[email protected]>
@memtomem memtomem merged commit 4e61c6a into main Apr 20, 2026
7 checks passed
@memtomem memtomem deleted the fix/296-wizard-preset-rules branch April 20, 2026 05:25
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 20, 2026
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.

auto_ns silently collapses to default when memory_dir basename is non-discriminating (e.g., .../FOO/memory)

2 participants