Files
headquarter/docs/superpowers/plans/tool-container-home-directory.md
T
Developer 5fc8e035e6 docs: tool container home directory design, plan, and test plan
- Add design doc / ADR for configurable /home/user home directory
   - Add implementation plan with phased rollout
   - Add test plan / QA checklist
   - Update OpenSpec task for home-path-expansion
2026-06-14 10:23:59 +00:00

8.3 KiB

Implementation Plan: Tool Container Home Directory

Overview

Make /home/user the configurable, default workspace/home directory for all tool containers, preserve the repository/workspace directory name in the mount target, migrate legacy tool types, and keep /workspace as a compatibility symlink.

Goals

  • Add a home_directory field to ToolType and the manifest schema.
  • Use that field to control workspace mount target, HOME, WORKDIR, and ~/$HOME expansion.
  • Preserve {repo_name} / {workspace_name} in the mount target.
  • Migrate legacy Dockerfile/Compose templates via Alembic.
  • Keep /workspace symlink for backward compatibility.
  • Add an entrypoint permission fixer for runtime-mounted paths.
  • Validate with unit and integration tests.

Phases

Phase 1: Database and model changes

Files:

  • apps/api/src/models/tool/tool_type.py
  • apps/api/alembic/versions/<new>_add_tool_type_home_directory.py

Tasks:

  1. Add home_directory: Mapped[str] column to ToolType, non-nullable with server default "/home/user".
  2. Create Alembic migration that:
    • Adds the column.
    • Updates existing rows to /home/user.
    • Rewrites compose_template and dockerfile_template to replace /workspace with /home/user/{{WORKSPACE_NAME}} (or a compatible template variable).
  3. Provide downgrade that reverses template rewrites and drops the column.

Quality gate:

  • cd apps/api && alembic upgrade head succeeds.
  • alembic downgrade -1 succeeds and restores /workspace strings.
  • Existing tests still pass.

Phase 2: Manifest schema and compiler

Files:

  • apps/api/src/services/build/manifest_compiler.py
  • apps/api/src/services/tool/instance_service.py
  • apps/api/src/api/tool/tool_types_validation.py (if schema validation is added)

Tasks:

  1. Accept home_directory in the manifest schema (optional; fallback to ToolType value).
  2. In compile_dockerfile():
    • Set ENV HOME={home_directory} and ENV USER={user.name}.
    • Set WORKDIR {home_directory} (unless runtime.working_dir is present).
    • Create the home directory and pre-create .config, .local/share, .cache under it.
    • Add a step to create /workspace as a symlink to {home_directory}/{repo_name} (placeholder or startup-time).
  3. In compile_compose():
    • Use {home_directory}/{repo_name} as the default repo mount target when the manifest has no explicit repo mount.
    • Keep ~/$HOME expansion base equal to home_directory.
  4. Update get_manifest_home_dir() to honor manifest.home_directory before deriving from user.name.
  5. Pass home_directory through the manifest-based instance lifecycle.

Quality gate:

  • compile_dockerfile() output contains ENV HOME=/home/user and WORKDIR /home/user for default manifests.
  • compile_compose() output mounts repo at /home/user/{repo_name} when no explicit repo mount exists.

Phase 3: Legacy instance generation

Files:

  • apps/api/src/services/tool/instance_service.py
  • apps/api/src/services/docker/compose.py

Tasks:

  1. In create_tool_instance() for definition_type == "dockerfile":
    • Read tool_type.home_directory (default /home/user).
    • Mount {repo_path}:{home_directory}/{repo_name} instead of {repo_path}:/workspace.
    • Generate or adjust Dockerfile/Compose to create /workspace symlink.
  2. For definition_type == "compose":
    • Render {home_directory} and {WORKSPACE_NAME} into the template.
    • Validate that {WORKSPACE_NAME} is available as a template variable.
  3. Add WORKSPACE_NAME to the render variables in render_compose_template().

Quality gate:

  • Legacy dockerfile instance compose mounts repo at /home/user/{repo_name}.
  • Legacy compose template with /home/user/{{WORKSPACE_NAME}} renders correctly.

Phase 4: Config-profile and git mount expansion

Files:

  • apps/api/src/services/config/config_profile_resolver.py
  • apps/api/src/services/tool/instance_service.py

Tasks:

  1. Ensure expand_container_path() uses the resolved home_directory (already present; verify it is threaded through).
  2. In start_tool_instance(), compute home_dir from ToolType/manifest and pass it to:
    • apply_resolved_profile()
    • resolve_git_mounts() / resolve_git_mount_mappings()
  3. Confirm workspace/repo name is used as the mount target, not a generic workspace string.

Quality gate:

  • Config profile mount target ~/config expands to /home/user/config.
  • Git mount target ~/repo expands to /home/user/repo.

Phase 5: Entrypoint permission fixer

Files:

  • apps/api/src/services/build/manifest_compiler.py
  • apps/api/src/services/shared/permission_fixer.py
  • tool-images/base.dockerfile or generated entrypoint

Tasks:

  1. Generate an entrypoint script that, before switching to the runtime user:
    • Detects the container user name/uid.
    • Runs chown on {home_directory} and key mount points.
    • Creates /workspace symlink if it does not yet exist.
    • Avoids recursive chown of large subtrees; target top-level dirs and runtime-created files.
  2. Ensure manifest-generated Dockerfiles install sudo and configure passwordless sudo for the runtime user (already partially done).
  3. Consider updating tool-images/base.dockerfile to include sudo and an entrypoint hook, or keep the fixer entirely in generated images.

Quality gate:

  • Container starts successfully.
  • Container user can write to {home_directory} and mounted config/git directories.
  • /workspace symlink resolves to the repo/workspace directory.

Phase 6: Tests

Files:

  • apps/api/tests/unit/test_home_path_expansion.py
  • apps/api/tests/unit/test_manifest_compiler.py
  • apps/api/tests/unit/test_instance_service.py (new or expanded)
  • apps/api/tests/integration/test_tool_instance_lifecycle.py (new or expanded)

Tasks:

  1. Unit tests:
    • expand_container_path with ~, $HOME, absolute, and relative paths.
    • get_manifest_home_dir with and without home_directory, with and without user.name.
    • compile_dockerfile includes correct ENV HOME, WORKDIR, and /workspace symlink step.
    • compile_compose defaults repo mount to /home/user/{repo_name} when no explicit repo mount exists.
    • Explicit manifest repo mount target is preserved.
  2. Integration tests:
    • Create a manifest tool instance and verify the running container has HOME=/home/user, repo at /home/user/{repo_name}, and /workspace symlink.
    • Verify a config-profile mount under ~ is writable by the container user.
    • Verify git mount under ~ is writable.
    • Verify legacy dockerfile tool uses /home/user/{repo_name} after migration.

Quality gate:

  • cd apps/api && pytest tests/unit/... tests/integration/... -xvs passes.
  • cd apps/web && npm run typecheck passes.

Dependencies

  • The existing home-path-expansion helpers (expand_container_path) must already be in place.
  • Alembic migration infrastructure must be working.
  • Manifest compiler must support generated entrypoints (already partially implemented).

Risks and Mitigations

Risk Mitigation
Alembic migration rewrites commands/env vars that contain /workspace Scope the replacement to volume mount lines only; add tests for edge cases.
/workspace symlink target missing at build time Create placeholder directory in Dockerfile; finalize symlink in entrypoint at startup.
Entrypoint chown slow on large repos Chown top-level directories only; rely on runtime user for new files.
Base image USER user cannot run sudo Either install sudo in base image or keep entrypoint running as root before su/gosu to runtime user.
Legacy templates without {{WORKSPACE_NAME}} break Migration injects the variable; validation rejects missing required variables.

OpenSpec Task Update

After this plan is accepted, update or replace openspec/tasks/home-path-expansion.md to reflect the expanded scope (configurable home_directory, repo-name mount target, migration, compatibility symlink, permission fixer, tests).

Rollout

  1. Merge Phase 1 (schema + migration) first so the column exists.
  2. Merge Phase 2 and 3 (compiler + legacy generation) next.
  3. Merge Phase 4 and 5 (profile/git mounts + permission fixer).
  4. Merge Phase 6 (tests) with the previous phases as appropriate.
  5. Run migration in production during a maintenance window; validate a few existing tool types before full rollout.