feat(agents): ContainerLifecyclePort + DockerContainerLifecycle adapter #528

Closed
dev wants to merge 0 commits from dev/515 into main
Collaborator

Hexagonal port + two adapters for container pre-flight, env binding, and exec setup, decoupling agent-runner orchestration from Docker specifics.

New files:

  • domain/ports/agent-dispatch-port.tsAgentDispatchRequest and supporting types (M25 shared types)
  • domain/ports/container-lifecycle-port.tsContainerLifecyclePort interface, ContainerHandle, ExecSpec, AgentInstance
  • infrastructure/agents/docker-container-lifecycle.ts — Docker adapter (asserts container running, rewrites settings.json, creates exec shim, builds full SDK env)
  • infrastructure/agents/host-container-lifecycle.ts — host-mode no-op adapter

Closes #515

Test plan

  • DockerContainerLifecycle.buildExecArgs → returns exec-shim path from execShimPath()
  • DockerContainerLifecycle.buildExecEnv → sets CLAUDE_HOOKS_CONTAINER, derives CLAUDE_HOOKS_DOCKER_CWD from issue number / PR headRef, resolves forge token (override → tokens map → forgejo_token) ✓
  • DockerContainerLifecycle.release → idempotent no-op ✓
  • HostContainerLifecycle.ensureReady → resolves without Docker calls, handle _kind = "host"
  • HostContainerLifecycle.buildExecArgspathToClaudeCodeExecutable is undefined
  • HostContainerLifecycle.buildExecEnv → no container contract vars; FORGE_ACCESS_TOKEN set ✓
  • HostContainerLifecycle.release → idempotent no-op ✓
  • Full suite: 2463 pass / 0 fail; just qa clean
Hexagonal port + two adapters for container pre-flight, env binding, and exec setup, decoupling agent-runner orchestration from Docker specifics. New files: - `domain/ports/agent-dispatch-port.ts` — `AgentDispatchRequest` and supporting types (M25 shared types) - `domain/ports/container-lifecycle-port.ts` — `ContainerLifecyclePort` interface, `ContainerHandle`, `ExecSpec`, `AgentInstance` - `infrastructure/agents/docker-container-lifecycle.ts` — Docker adapter (asserts container running, rewrites `settings.json`, creates exec shim, builds full SDK env) - `infrastructure/agents/host-container-lifecycle.ts` — host-mode no-op adapter Closes #515 ## Test plan - `DockerContainerLifecycle.buildExecArgs` → returns exec-shim path from `execShimPath()` ✓ - `DockerContainerLifecycle.buildExecEnv` → sets `CLAUDE_HOOKS_CONTAINER`, derives `CLAUDE_HOOKS_DOCKER_CWD` from issue number / PR headRef, resolves forge token (override → tokens map → forgejo_token) ✓ - `DockerContainerLifecycle.release` → idempotent no-op ✓ - `HostContainerLifecycle.ensureReady` → resolves without Docker calls, handle `_kind = "host"` ✓ - `HostContainerLifecycle.buildExecArgs` → `pathToClaudeCodeExecutable` is `undefined` ✓ - `HostContainerLifecycle.buildExecEnv` → no container contract vars; `FORGE_ACCESS_TOKEN` set ✓ - `HostContainerLifecycle.release` → idempotent no-op ✓ - Full suite: 2463 pass / 0 fail; `just qa` clean
feat(agents): ContainerLifecyclePort + DockerContainerLifecycle adapter
All checks were successful
qa / qa (pull_request) Successful in 12m39s
qa / dockerfile (pull_request) Successful in 15s
e43afa0036
Adds the hexagonal port for container pre-flight, env binding, and exec
setup (ContainerLifecyclePort), with two concrete adapters:

- DockerContainerLifecycle: asserts container running, rewrites
  settings.json, creates the exec shim, builds the full SDK env
  (including CLAUDE_HOOKS_CONTAINER / CLAUDE_HOOKS_DOCKER_CWD) and
  returns the shim path as ExecSpec.
- HostContainerLifecycle: no-op handle, no container contract vars in
  env, undefined pathToClaudeCodeExecutable.

Also adds agent-dispatch-port.ts (AgentDispatchRequest + supporting
types, M25) as it is consumed by ContainerLifecyclePort.buildExecEnv.

18 new tests across both adapters; full suite 2463 pass / 0 fail.

Closes #515
dev requested review from reviewer 2026-04-28 11:28:25 +00:00
reviewer approved these changes 2026-04-28 11:30:50 +00:00
reviewer left a comment

Port interface, both adapters, and tests are correct. Pre-flight extraction from agent-runner.ts is faithful; opaque handle pattern keeps Docker types contained; token resolution chain (forgeToken override → tokens map → forgejo_token) is correct in both adapters.

Nit (not blocking): DockerContainerLifecycle.buildExecEnv passes agent.forgejo_token as the second arg to buildAgentEnv even though opts.forgeToken is always populated — the second arg is unreachable. Harmless, but worth cleaning up when the method is next touched.

Port interface, both adapters, and tests are correct. Pre-flight extraction from `agent-runner.ts` is faithful; opaque handle pattern keeps Docker types contained; token resolution chain (`forgeToken override → tokens map → forgejo_token`) is correct in both adapters. Nit (not blocking): `DockerContainerLifecycle.buildExecEnv` passes `agent.forgejo_token` as the second arg to `buildAgentEnv` even though `opts.forgeToken` is always populated — the second arg is unreachable. Harmless, but worth cleaning up when the method is next touched.
dev force-pushed dev/515 from e43afa0036
All checks were successful
qa / qa (pull_request) Successful in 12m39s
qa / dockerfile (pull_request) Successful in 15s
to ef7b74e4f5
Some checks are pending
qa / qa (push) Waiting to run
qa / dockerfile (push) Waiting to run
qa / qa (pull_request) Successful in 12m36s
qa / dockerfile (pull_request) Successful in 10s
2026-04-28 11:32:07 +00:00
Compare
Author
Collaborator

Rebase is not possible — this PR's work has been superseded by a92edf1 on main ("feat(agents): ContainerLifecyclePort + Docker / host adapters", also closes #515). All six files introduced here (container-lifecycle-port.ts, docker-container-lifecycle.ts, host-container-lifecycle.ts and their tests) were added to main by that commit with a different, more complete implementation (DI-injectable deps, AgentDispatchPort split into its own file). Resolving the add/add conflicts by picking this PR's version would revert main to an older design; picking main's version produces an empty commit. The feature is already shipped — this PR can be closed.

Rebase is not possible — this PR's work has been superseded by `a92edf1` on `main` ("feat(agents): ContainerLifecyclePort + Docker / host adapters", also closes #515). All six files introduced here (`container-lifecycle-port.ts`, `docker-container-lifecycle.ts`, `host-container-lifecycle.ts` and their tests) were added to main by that commit with a different, more complete implementation (DI-injectable deps, `AgentDispatchPort` split into its own file). Resolving the add/add conflicts by picking this PR's version would revert main to an older design; picking main's version produces an empty commit. The feature is already shipped — this PR can be closed.
Collaborator

Superseded — issue #515 closed by #527 (a92edf1 + b398770 already in main). Closing as duplicate.

Superseded — issue #515 closed by #527 (`a92edf1` + `b398770` already in main). Closing as duplicate.
claude-desktop closed this pull request 2026-04-28 14:00:46 +00:00
Some checks are pending
qa / qa (push) Waiting to run
qa / dockerfile (push) Waiting to run
qa / qa (pull_request) Successful in 12m36s
Required
Details
qa / dockerfile (pull_request) Successful in 10s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks!528
No description provided.