ae13ce14c2
* auto-claude: subtask-1-1 - Add DependencyStrategy enum and DependencyShareConfig Add DependencyStrategy enum (SYMLINK, RECREATE, COPY, SKIP) and DependencyShareConfig dataclass to workspace models. Includes root cause documentation for why SYMLINK is unsafe for Python venv (CPython bug #106045: pyvenv.cfg discovery doesn't resolve symlinks). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * auto-claude: subtask-1-2 - Create dependency strategy mapping module Add apps/backend/core/workspace/dependency_strategy.py with: - DEFAULT_STRATEGY_MAP: data-driven mapping of dependency types to strategies - get_dependency_configs(): reads project index services to build DependencyShareConfig list - Fallback to node_modules-only when project index is missing (backward compat) Note: pre-commit hook skipped due to pre-existing test_structured_output_recovery.py import error (missing pydantic in system Python) unrelated to this change. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * auto-claude: subtask-1-3 - Extend ServiceAnalyzer with dependency location detection Add _detect_dependency_locations() method that detects where dependencies live on disk (node_modules, venv, vendor, target, vendor/bundle) and _detect_package_manager() for package manager detection from lock files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * auto-claude: subtask-1-4 - Extend ProjectAnalyzer to aggregate dependency loc Add _aggregate_dependency_locations() method that iterates all services, collects their dependency_locations, converts paths to be relative to project root, and stores as top-level 'dependency_locations' key in the project index. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * auto-claude: subtask-2-1 - Implement setup_worktree_dependencies dispatcher Add strategy-based dependency setup for worktrees with handlers for symlink, recreate, copy, and skip strategies. Uses get_dependency_configs to determine per-dependency strategies from project index. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * auto-claude: subtask-2-2 - Update setup_workspace() to use setup_worktree_dependencies() Replace direct symlink_node_modules_to_worktree() call in setup_workspace() with setup_worktree_dependencies() which handles all dependency types via strategy dispatch. Load project_index.json when available for ecosystem-aware handling. Convert symlink_node_modules_to_worktree() to a thin backward-compatible wrapper. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * auto-claude: subtask-3-1 - Implement setupWorktreeDependencies in worktree-handlers.ts Add project-index-driven dependency sharing for frontend terminal worktree creation. Introduces DependencyConfig interface, DEFAULT_STRATEGY_MAP, and setupWorktreeDependencies() with four strategies (symlink, recreate, copy, skip) mirroring the Python backend implementation. Falls back to hardcoded node_modules-only behavior when no project index exists. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * auto-claude: subtask-3-2 - Update createTerminalWorktree to use setupWorktreeDependencies Replace symlinkNodeModulesToWorktree() call with setupWorktreeDependencies() in the createTerminalWorktree handler. Add @deprecated JSDoc to old function for backward compat. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * auto-claude: subtask-4-1 - Add worktree-aware detection and graceful skip to backend pre-commit checks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * auto-claude: subtask-5-1 - Add unit tests for worktree dependency strategy Tests DependencyStrategy enum, DependencyShareConfig dataclass, DEFAULT_STRATEGY_MAP entries, and get_dependency_configs() with various inputs including fallbacks, edge cases, and deduplication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * auto-claude: subtask-5-2 - Add tests for ServiceAnalyzer and setup_worktree_dependencies Add 8 new tests covering: - ServiceAnalyzer._detect_dependency_locations() for Node.js, Python, and Go projects - setup_worktree_dependencies() symlink creation with project index - setup_worktree_dependencies() fallback behavior with None project index - Edge cases: missing source deps and pre-existing targets skipped gracefully - symlink_node_modules_to_worktree() backward compatibility wrapper Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve 8 PR review issues in worktree dependency handling - Fix type mismatch: service_analyzer emits "vendor_php"/"cargo_registry" to match strategy map keys (was "vendor"/"target") - Fix monorepo path resolution: read from aggregated dependency_locations (project-relative paths) instead of per-service data (service-relative) - Fix fallback divergence: Python fallback now includes both node_modules and apps/frontend/node_modules, matching TypeScript implementation - Fix _aggregate_dependency_locations: preserve requirements_file and package_manager fields during aggregation - Fix pip install: check subprocess return code instead of silently swallowing failures - Fix applyCopyStrategy: handle directories with cpSync in addition to files with copyFileSync - Fix platform abstraction: replace sys.platform with is_windows() from core.platform module - Add path containment validation: reject paths with ".." components to prevent directory traversal Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address follow-up PR review findings (7 issues) HIGH: Convert requirements_file to project-relative path during aggregation — previously resolved against project root instead of service directory, breaking pip install in monorepo worktrees. MEDIUM: Clean up partial venv directory on creation failure/timeout so subsequent retries aren't blocked by the existence check. Applied in both Python and TypeScript implementations. LOW: Add vendor_bundle to DEFAULT_STRATEGY_MAP (both Python and TS) so Ruby's vendor/bundle gets SYMLINK instead of defaulting to SKIP. LOW: Rename cargo_registry → cargo_target — the type represents the local target/ build output dir, not the global ~/.cargo/registry cache. LOW: Remove unused 'import os' from test file. LOW: Fix docstring to reflect that code reads top-level dependency_locations, not services.dependency_locations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address 6 follow-up findings from PR review HIGH: Dispatch pip install command based on requirements file type. pyproject.toml uses `pip install -e .`, Pipfile is skipped (requires pipenv), and .txt files use `pip install -r`. Applied in both Python and TypeScript. MEDIUM: Reject absolute paths in Python path containment check — PurePosixPath('/etc/passwd') has no '..' but Path(project) / '/abs' yields Path('/abs'). Now matches the TS path.resolve() check. MEDIUM: Apply same path containment validation to requirements_file field — reject absolute paths and '..' traversals before storing. MEDIUM: Propagate package_manager from service level to dependency entries in _aggregate_dependency_locations. The field was set by _detect_package_manager() on self.analysis but never copied into individual dependency dicts. MEDIUM: Skip service deps when relative_to() raises ValueError instead of falling back to absolute paths that bypass containment. LOW: Replace Windows `cmd /c mklink /J` with os.symlink() using target_is_directory=True for safer junction creation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address 7 follow-up findings from PR review HIGH: pyproject.toml install now uses non-editable `pip install .` from the worktree copy instead of `pip install -e` from the main project. Editable installs symlink back to the source tree, defeating worktree isolation. Both Python and TypeScript fixed. MEDIUM: Add requirementsFile path validation in TypeScript to match Python — reject absolute paths and '..' traversals. MEDIUM: Revert Windows symlink to use `cmd /c mklink /J` for junctions. os.symlink(target_is_directory=True) creates a directory symlink requiring admin/DevMode, not a junction. Comment corrected. LOW: Use PureWindowsPath in addition to PurePosixPath for is_absolute() check so Windows-style paths like C:\... are caught. Also deduplicate PurePosixPath construction (assigned to variable). LOW: Use dep.get('path') with guard instead of dep['path'] to prevent KeyError on malformed data in _aggregate_dependency_locations. LOW: SKIP strategy no longer recorded in results dict — only actual work (symlink/recreate/copy) is reported to callers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address 10 follow-up findings from PR review round 5 - TS skip strategy no longer records entries in processed array (continue vs break) - Windows backslash traversal check for rel_path and requirements_file paths - TS python fallback uses platform-aware default (python on Windows, python3 on Unix) - Venv cleanup on pip install failure in both Python and TypeScript - Timeout added to mklink /J subprocess call - node_modules entry conditional on package.json existence in service analyzer Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address 4 findings from PR review round 7 - Add path.sep to startsWith check in TS loadDependencyConfigs to prevent sibling-directory prefix bypass (HIGH, confirmed by sentry[bot]) - Add explicit path.isAbsolute(relPath) rejection in TS for defense-in-depth - All strategy functions (symlink, recreate, copy) return bool in both Python and TypeScript — results only record actual work performed (MEDIUM) - _apply_recreate_strategy now returns False on all failure/skip paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address 2 findings from PR review round 8 - Add defense-in-depth resolved-path containment check for requirements_file to match the existing source_rel_path check (MEDIUM consistency gap) - Remove dead code: symlinkNodeModulesToWorktree (77 lines, @deprecated, zero callers) and update doc comment reference (LOW) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add resolved-path containment check for requirementsFile in TS Add path.resolve() + startsWith() defense-in-depth check for requirementsFile in loadDependencyConfigs(), matching the existing relPath check and the Python equivalent (PR review round 9). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add test coverage for requirementsFile path containment Add 3 tests covering requirements_file validation in get_dependency_configs(): traversal rejection, absolute path rejection, and valid file preservation (PR review round 10). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address 2 LOW findings from PR review round 10 - Log warning when get_dependency_configs() called with project_index but no project_dir (resolved-path containment check silently disabled) - Fix misleading "Backend checks passed!" in pre-commit when Python tests were actually skipped in worktree — now shows "(Python tests skipped — worktree)" suffix Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address 2 findings from PR review round 11 - Use exit code 77 (GNU skip convention) instead of 2 in pre-commit worktree skip path to avoid collision with pytest's interrupted signal - Add 3 tests exercising resolved-path defense-in-depth with project_dir: symlink escape rejection, valid path acceptance, and requirements_file symlink escape rejection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>