Conversation
… (refs #296) When ``enable_auto_ns=true`` is applied to memory_dirs whose basename is non-discriminating (``memory``, ``memories``, ``plans`` — exactly the shape produced by auto-discovered Claude/Codex provider dirs), the folder-based fallback collapses every file to ``default`` because its guard suppresses the useless basename without deriving a useful alternative. The parent one level further up (``FOO`` in ``~/.claude/projects/FOO/memory/``) is the identifier users actually care about. This commit ships the primitive from #296 direction (2): add the ``{ancestor:N}`` placeholder to ``NamespacePolicyRule.namespace`` so a rule like { "path_glob": "~/.claude/projects/*/memory/**", "namespace": "claude:{ancestor:1}" } expands to ``claude:FOO`` for that layout. Semantics: ``{ancestor:N}`` is ``file_path.parents[N].name``; ``N=0`` is the immediate parent (equivalent to ``{parent}``), ``N=1`` is the grandparent, and so on. Validator extensions: - ``{ancestor}`` without an integer spec rejected at load time. - Non-integer or negative spec rejected at load time — negatives would silently wrap into filesystem root via ``parents[-1]``. - ``{parent:N}`` rejected too — format specs are ambiguous on the bare ``parent`` placeholder; force users onto ``{ancestor:N}`` for any positional lookup. Runtime: ``_format_namespace`` rewritten to walk ``string.Formatter .parse`` output, so ``{parent}`` and ``{ancestor:N}`` can be mixed with literals in a single template. Out-of-range ``N`` or empty folder names skip the rule and fall through (same shape as the existing empty-``{parent}`` skip), logged once per rule index. Follow-up (not in this PR): wizard preset — ``mm init``'s provider-dirs step should append matching ``NamespacePolicyRule`` entries automatically so auto-discovered dirs Just Work without users having to hand-craft rules. Tracked as direction (3) on #296. Tests: 7 new cases covering happy path, ancestor-zero parity with parent, out-of-range fall-through, literal composition, and four load-time validation rejections. Negative-tested by stashing src changes — all 7 fail without the primitive. Co-Authored-By: Claude <[email protected]>
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Refs #296 (primitive; wizard preset direction-3 deferred, see below).
Problem
With
enable_auto_ns=trueapplied to memory_dirs whose basename isnon-discriminating (
memory,memories,plans— exactly the shapeproduced by auto-discovered Claude/Codex provider dirs), the folder-based
fallback collapses every file to
default. The guard in_resolve_namespacesuppresses the useless immediate-parent basenamebut doesn't derive a useful alternative. The identifier users care
about lives one level up:
FOOin~/.claude/projects/FOO/memory/.The issue author's realistic workaround — hand-crafting a
namespace.rulesentry per project — is defeated by the fact thatNamespacePolicyRule.namespaceonly supports{parent}, which isstill the useless
memorybasename.Fix (direction 2 of 3)
Add the
{ancestor:N}placeholder toNamespacePolicyRule.namespace.A rule like:
{ "path_glob": "~/.claude/projects/*/memory/**", "namespace": "claude:{ancestor:1}" }expands to
claude:FOOfor that layout.Semantics:
{ancestor:N}resolves tofile_path.parents[N].name.{parent})FOO-shaped project id for provider dirs)Validator extensions (all load-time rejections):
{ancestor}without an integer spec — caller must say which level.{ancestor:abc}) — must parse as int.{ancestor:-1}) — would silently wrap into filesystemroot via
parents[-1].{parent:N}— format specs are ambiguous on the bareparentplaceholder; force users onto
{ancestor:N}for any positional lookup.Runtime:
_format_namespaceis rewritten to walkstring.Formatter().parse()output, so{parent}and{ancestor:N}can be mixed with literals in a single template. Out-of-range
Norempty folder names skip the rule and fall through (same shape as the
existing empty-
{parent}skip), logged once per rule index.What this PR explicitly does NOT do
The #296 issue author proposed (2) + (3):
{ancestor:N}placeholder.mm init's provider-dirs stepshould auto-append matching
NamespacePolicyRuleentries so theClaude/Codex memory dirs Just Work without users hand-crafting
rules. Tracking remains on auto_ns silently collapses to
defaultwhen memory_dir basename is non-discriminating (e.g.,.../FOO/memory) #296; this PR closes the primitive gapbut does not close the issue.
_resolve_namespaceforcontainer-basename memory_dirs. The issue author explicitly didn't
want this because it would silently change behavior for non-provider
memory_dirsthat legitimately rely on the current guard.Sequencing (3) as a follow-up keeps the primitive reviewable on its
own (27-line validator + 34-line formatter rewrite + tests) and
separates the design questions in (3) (which provider dirs? what
namespace labels? migrating existing installs?) from the mechanical
change in (2).
Tests
7 new cases in
test_indexing_engine.py:test_ancestor_placeholder_grandparent{ancestor:1}→ grandparent folder nametest_ancestor_zero_equals_parent{ancestor:0}≡{parent}test_ancestor_out_of_range_falls_through{ancestor:99}skips rule, logs oncetest_ancestor_combined_with_literal{ancestor:1}/subcomposes with literalstest_ancestor_without_index_rejected_at_load{ancestor}rejectedtest_ancestor_non_integer_spec_rejected_at_load{ancestor:abc}rejectedtest_ancestor_negative_index_rejected_at_load{ancestor:-1}rejectedtest_parent_with_format_spec_rejected_at_load{parent:N}rejected, points to{ancestor:N}Negative-tested: stashing the src changes → 7 ancestor tests fail
as expected. Existing
{parent}tests remain green (no regressions inthe pre-existing behavior).
Scope boundaries
_resolve_namespace's priority chain (explicit → rules→
enable_auto_ns→ default). Only the rule-format step gains thenew placeholder.
Test plan
uv run ruff check && ruff format --check— clean.uv run mypy packages/memtomem/src/memtomem/indexing packages/memtomem/src/memtomem/config.py— no issues.uv run pytest packages/memtomem/tests/test_indexing_engine.py— 92 passed.uv run pytest packages/memtomem/tests/— 1863 passed (no regressions).config.py+engine.pychanges → all 7 new ancestor tests fail.{"path_glob": "~/.claude/projects/*/memory/**", "namespace": "claude:{ancestor:1}"}, reindex a Claude project memory dir, confirm chunks land inclaude:<project-id>namespace.Related
defaultwhen memory_dir basename is non-discriminating (e.g.,.../FOO/memory) #296. Issue stays open until the wizardpreset (direction 3) ships.
lands first, the wizard preset would use the derived provider
taxonomy to generate rules. Not coupled; either can ship first.
🤖 Generated with Claude Code