Skip to content

Add per-attempt workspace isolation (Epic 3) - #14

Merged
Lvvphole merged 2 commits into
mainfrom
claude/epic-3-workspace-isolation-3yjx0m
Jun 24, 2026
Merged

Add per-attempt workspace isolation (Epic 3)#14
Lvvphole merged 2 commits into
mainfrom
claude/epic-3-workspace-isolation-3yjx0m

Conversation

@Lvvphole

Copy link
Copy Markdown
Owner

Make execution attempts first-class and server-owned. Each
POST /runs/{id}/chain/execute now allocates an attempt with a
server-minted attempt_id, captures the base_commit (git rev-parse HEAD)
and workspace_id it ran against, scopes that attempt's evidence to
artifacts/{run_id}/{attempt_id}/, and persists the attempt into the
durable run_attempts table. A retry mints the next attempt.

Adds a production_mode runtime setting: when on, a caller-supplied
execution_path is refused 422 and the server allocates the workspace
from its own workspace_root; off (default) keeps dev/test behavior,
accepting execution_path only after WorkspacePolicy validation.

Authority is unchanged: allocation is audit metadata only — it decides
no PASS/FAIL/BLOCKED and relaxes no gate. No merge/deploy/bypass surface
is added; GET /runs/{id}/attempts is a read-only audit view.

  • RunAttempt embedded in RunRecord.attempts (round-trips via snapshot;
    no new RunRepository port method)
  • WorkspaceAllocator reuses WorkspacePolicy + GitRunner.head_commit
  • ArtifactStore gains optional attempt_id scoping (backward compatible)
  • Postgres _project upserts run_attempts (FK never nulled)
  • Docs: docs/workspace_isolation.md; README + persistence.md updated

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01JQW4FXw46KBvMCVc2EduXT

claude added 2 commits June 23, 2026 23:10
Make execution attempts first-class and server-owned. Each
POST /runs/{id}/chain/execute now allocates an attempt with a
server-minted attempt_id, captures the base_commit (git rev-parse HEAD)
and workspace_id it ran against, scopes that attempt's evidence to
artifacts/{run_id}/{attempt_id}/, and persists the attempt into the
durable run_attempts table. A retry mints the next attempt.

Adds a production_mode runtime setting: when on, a caller-supplied
execution_path is refused 422 and the server allocates the workspace
from its own workspace_root; off (default) keeps dev/test behavior,
accepting execution_path only after WorkspacePolicy validation.

Authority is unchanged: allocation is audit metadata only — it decides
no PASS/FAIL/BLOCKED and relaxes no gate. No merge/deploy/bypass surface
is added; GET /runs/{id}/attempts is a read-only audit view.

- RunAttempt embedded in RunRecord.attempts (round-trips via snapshot;
  no new RunRepository port method)
- WorkspaceAllocator reuses WorkspacePolicy + GitRunner.head_commit
- ArtifactStore gains optional attempt_id scoping (backward compatible)
- Postgres _project upserts run_attempts (FK never nulled)
- Docs: docs/workspace_isolation.md; README + persistence.md updated

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQW4FXw46KBvMCVc2EduXT
RunAttempt.workspace_id was set to str(resolved) — the absolute host
filesystem path — and that same value was reused as the execution path.
Since GET /runs/{id}/attempts returns asdict(attempt) and Postgres
persists workspace_id, the audit surface exposed server filesystem layout.

Separate the opaque public workspace identity from the internal execution
path. WorkspaceAllocator.allocate now returns a WorkspaceAllocation
carrying the attempt (with an opaque workspace_id of the form
workspace-{run_id}-a{n}) and the validated execution_path. The endpoint
runs the chain against allocation.execution_path; only the opaque id is
appended to the record, persisted, and returned by the API.

Storage, serialization, and the Postgres projection are unchanged — they
faithfully carry whatever workspace_id holds, so the snapshot, the
/attempts response, and run_attempts.workspace_id all become opaque.

Tests: workspace_id is not an absolute path; GET /attempts response does
not contain workspace_root; execution still uses the validated resolved
path; Postgres-gated assertion that the stored workspace_id is opaque.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015R7wjqoCZDF1pqNp8AiUJ6
@Lvvphole
Lvvphole merged commit 518574f into main Jun 24, 2026
1 check passed
@Lvvphole
Lvvphole deleted the claude/epic-3-workspace-isolation-3yjx0m branch June 24, 2026 03:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants