Skip to content

Commit 9e9eb87

Browse files
committed
Honor LORE_ROOT and scope in root discovery
Add support for an explicit LORE_ROOT environment override and introduce a store "scope" setting to control inheritance. DEFAULT_CONFIG now includes a "scope" key (default "auto") and find_memory_root checks LORE_ROOT first, then walks up from the start path; when a .lore store is found in a parent dir, the store's scope is read and "local" prevents inheritance by returning None. Also add os import and update CLI docs to show the new scope option.
1 parent b1a3fb0 commit 9e9eb87

File tree

2 files changed

+43
-3
lines changed

2 files changed

+43
-3
lines changed

src/lore/cli.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,6 +1613,7 @@ def config_set(
16131613
lore config model_endpoint https://artifactory.example.com/huggingface
16141614
lore config model_ssl_verify false
16151615
lore config embedding_model all-MiniLM-L6-v2
1616+
lore config scope local
16161617
"""
16171618
from .config import load_config, save_config
16181619
root = _require_root()

src/lore/config.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Configuration loading and resolution for mem."""
22
from __future__ import annotations
33

4+
import os
45
import random
56
import uuid
67
from pathlib import Path
@@ -93,6 +94,11 @@ def is_valid_uuid(value: str) -> bool:
9394
"codeowners": True,
9495
"custom_rules": [],
9596
},
97+
# Controls how subdirectories discover this store.
98+
# "auto" — walk up from any subdirectory (default, backward-compatible).
99+
# "local" — only the directory containing .lore/ can use this store;
100+
# subdirectories will not inherit it.
101+
"scope": "auto",
96102
# Trust scoring controls for shared chronicle export and ranking.
97103
"trust": {
98104
"default_score": 50,
@@ -105,10 +111,43 @@ def is_valid_uuid(value: str) -> bool:
105111

106112

107113
def find_memory_root(start: Path | None = None) -> Path | None:
108-
"""Walk up from *start* (default: cwd) looking for a .lore directory."""
109-
path = (start or Path.cwd()).resolve()
110-
for parent in [path, *path.parents]:
114+
"""Walk up from *start* (default: cwd) looking for a .lore directory.
115+
116+
Resolution order:
117+
1. ``LORE_ROOT`` environment variable — explicit root pin; the path must
118+
contain a ``.lore/`` directory or ``None`` is returned.
119+
2. Walk up from *start* toward the filesystem root. If a store is found
120+
in the current directory it is always returned. If it is found in a
121+
*parent* directory, the store's ``scope`` config key is checked:
122+
- ``"auto"`` (default) — inherit the parent store (current behaviour).
123+
- ``"local"`` — do **not** inherit; return ``None`` so the caller sees
124+
no active store, preventing unintentional cross-directory bleed.
125+
"""
126+
# 1. Explicit root override.
127+
env_root = os.environ.get("LORE_ROOT")
128+
if env_root:
129+
p = Path(env_root).resolve()
130+
return p if (p / MEMORY_DIR).is_dir() else None
131+
132+
# 2. Walk up the directory tree.
133+
cwd = (start or Path.cwd()).resolve()
134+
for parent in [cwd, *cwd.parents]:
111135
if (parent / MEMORY_DIR).is_dir():
136+
if parent == cwd:
137+
# Directly inside the store's own directory — always valid.
138+
return parent
139+
# Found in a parent directory; honour the store's scope setting.
140+
cfg_path = parent / MEMORY_DIR / CONFIG_FILE
141+
if cfg_path.exists():
142+
try:
143+
with cfg_path.open() as _f:
144+
_raw = yaml.safe_load(_f) or {}
145+
if _raw.get("scope", "auto") == "local":
146+
# Store is local-only; this subdirectory should not
147+
# inherit it. Stop searching — don't walk further up.
148+
return None
149+
except Exception:
150+
pass
112151
return parent
113152
return None
114153

0 commit comments

Comments
 (0)