Files
Aperant/tests
Andy ae13ce14c2 auto-claude: 217-investigate-symlink-issues-in-work-tree-creation-f (#1808)
* 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>
2026-02-13 18:14:21 +01:00
..
2025-12-11 19:53:55 +01:00
2025-12-15 19:49:58 +01:00
2026-01-02 11:56:36 +01:00
2026-01-12 15:27:47 +01:00
2026-01-12 15:27:47 +01:00
2025-12-10 09:10:55 +01:00
2026-01-12 15:27:47 +01:00
2025-12-15 21:10:27 +01:00