Skip to content

Commit 79a487c

Browse files
committed
fix: prevent recursive dev-workspace nesting in multi-repo mode
- copytree ignore filter in _import_repo_into_workspace - blocked path segments guard in upload_delta_bundle - dev-workspace added to _ANY_DEPTH_EXCLUDE_DIR_NAMES - bump extension to 0.1.55
1 parent 6ddf870 commit 79a487c

File tree

5 files changed

+591
-555
lines changed

5 files changed

+591
-555
lines changed

scripts/ctx_cli/commands/quickstart.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,26 @@ def _import_repo_into_workspace(source: Path, dev_workspace: Path) -> Path:
169169
raise ValueError(f"Import target exists and is not a directory: {target}")
170170
return target
171171

172-
shutil.copytree(source, target, symlinks=True)
172+
# Exclude directories that cause recursive nesting loops or are never useful
173+
# inside an imported workspace copy. The most critical entry is "dev-workspace"
174+
# itself: without this filter, importing the Context-Engine repo creates
175+
# dev-workspace/<slug>/dev-workspace/<slug>/… ad infinitum.
176+
_IMPORT_EXCLUDE_DIRS = {
177+
"dev-workspace",
178+
".codebase",
179+
".git",
180+
"node_modules",
181+
".venv",
182+
"venv",
183+
"__pycache__",
184+
".cache",
185+
".remote-git",
186+
}
187+
188+
def _ignore_on_copy(directory: str, contents: list[str]) -> list[str]:
189+
return [name for name in contents if name in _IMPORT_EXCLUDE_DIRS]
190+
191+
shutil.copytree(source, target, symlinks=True, ignore=_ignore_on_copy)
173192
return target
174193

175194

scripts/ingest/config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ def _env_truthy(val: str | None, default: bool) -> bool:
205205
"/.vscode",
206206
"/.cache",
207207
"/.codebase",
208+
"/dev-workspace",
209+
"dev-workspace",
208210
"/.remote-git",
209211
"/node_modules",
210212
"/dist",
@@ -223,6 +225,8 @@ def _env_truthy(val: str | None, default: bool) -> bool:
223225
# Glob patterns for directories (matched against basename)
224226
_DEFAULT_EXCLUDE_DIR_GLOBS = [
225227
".venv*", # .venv, .venv311, .venv39, etc.
228+
"venv*", # venv, venv-3.11, venv-py39, etc.
229+
"dev-workspace", # sandbox clones - prevent recursive indexing
226230
]
227231

228232
_DEFAULT_EXCLUDE_FILES = [
@@ -239,6 +243,7 @@ def _env_truthy(val: str | None, default: bool) -> bool:
239243
".remote-git",
240244
".codebase",
241245
"node_modules",
246+
"dev-workspace",
242247
}
243248

244249

scripts/remote_upload_client.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,15 +1349,13 @@ def get_all_code_files(self) -> List[Path]:
13491349
# Single walk with early pruning similar to standalone client
13501350
ext_suffixes = {str(ext).lower() for ext in idx.CODE_EXTS if str(ext).startswith('.')}
13511351
name_matches = {str(ext) for ext in idx.CODE_EXTS if not str(ext).startswith('.')}
1352-
dev_remote = os.environ.get("DEV_REMOTE_MODE") == "1" or os.environ.get("REMOTE_UPLOAD_MODE") == "development"
13531352
excluded = {
13541353
"node_modules", "vendor", "dist", "build", "target", "out",
13551354
".git", ".hg", ".svn", ".vscode", ".idea", ".venv", "venv",
13561355
"__pycache__", ".pytest_cache", ".mypy_cache", ".cache",
1357-
".context-engine", ".context-engine-uploader", ".codebase"
1356+
".context-engine", ".context-engine-uploader", ".codebase",
1357+
"dev-workspace"
13581358
}
1359-
if dev_remote:
1360-
excluded.add("dev-workspace")
13611359

13621360
seen = set()
13631361
for root, dirnames, filenames in os.walk(workspace_path):

scripts/upload_delta_bundle.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
WORK_DIR = os.environ.get("WORK_DIR") or os.environ.get("WORKDIR") or "/work"
2727
_SLUGGED_REPO_RE = re.compile(r"^.+-[0-9a-f]{16}(?:_old)?$")
2828

29+
# Paths containing any of these segments are never materialized from bundles.
30+
# Prevents recursive nesting (e.g. dev-workspace/<slug>/dev-workspace/…).
31+
_BLOCKED_PATH_SEGMENTS = {"dev-workspace", ".codebase", ".remote-git"}
32+
2933

3034
def get_workspace_key(workspace_path: str) -> str:
3135
"""Generate 16-char hash for collision avoidance in remote uploads.
@@ -394,6 +398,16 @@ def _apply_operation_to_workspace(workspace_root: Path) -> bool:
394398

395399
rel_path = sanitized_path
396400

401+
# Block paths that would create recursive nesting
402+
_path_parts = set(rel_path.replace("\\", "/").split("/"))
403+
if _path_parts & _BLOCKED_PATH_SEGMENTS:
404+
logger.debug(
405+
f"[upload_service] Blocking {op_type} for {rel_path}: "
406+
"contains a blocked path segment.",
407+
)
408+
operations_count["skipped"] += 1
409+
continue
410+
397411
replica_results: Dict[str, bool] = {}
398412
for slug, root in replica_roots.items():
399413
result = _apply_operation_to_workspace(root)

0 commit comments

Comments
 (0)