feat(agents): DefaultAgentDispatch orchestrator wiring all four ports #531

Closed
code-lead wants to merge 2 commits from boss/518 into main
Collaborator

Closes #518.

Concrete AgentDispatchPort adapter at infrastructure/agents/default-agent-dispatch.ts that composes the four M25 ports (ClaudeAgentPort, ContainerLifecyclePort, McpRegistryPort, WorkerRegistryPort) plus a SessionStorePort into a single dispatch surface.

Summary

  • dispatch() pre-flights the container, resolves MCP servers + allowed tools, enqueues a runner closure on the worker registry, and tracks the live task. Per-call forgeToken override beats defaultToken for both the MCP servers and the env binding.
  • steer() pushes onto a private bounded SteerChannel the SDK consumes via a streaming-input async iterator yielding the initial prompt then steer messages in arrival order. Empty / closed-task steers return {ok:false, reason:"conflict"}.
  • kill() aborts the controller, closes the steer channel, cancels the registry slot. Idempotent on already-settled tasks.
  • status() lifts queued/running from the live table and settled outcomes from a small in-memory record (DB lift is a follow-up migration).
  • Behavioural parity with runAgentTask: same FIFO ordering, same (agentType, repo, issue) session key, first-event session capture, container release in the runner's finally.
  • Ports defined in domain/ports/ carry the contracts forward — to be unified once the parallel #514–#517 PRs merge.

Existing callers stay on runAgentTask per the issue's out-of-scope note; the migration ticket wires this in.

Test plan

  • bun test src/infrastructure/agents/default-agent-dispatch.test.ts — 4 pass: happy path (events, session capture, container release, settled=success), forgeToken override, kill mid-task (abort propagates, killed outcome, idempotent second kill), steer mid-task (message reaches SDK iterator, post-settle conflict).
  • bun run typecheck clean.
  • bun x biome check apps/server/src/{domain/ports,infrastructure/agents} clean.
  • Reviewer: confirm port interfaces don't conflict with parallel #514–#517 once those land.
Closes #518. Concrete `AgentDispatchPort` adapter at `infrastructure/agents/default-agent-dispatch.ts` that composes the four M25 ports (`ClaudeAgentPort`, `ContainerLifecyclePort`, `McpRegistryPort`, `WorkerRegistryPort`) plus a `SessionStorePort` into a single dispatch surface. ## Summary - `dispatch()` pre-flights the container, resolves MCP servers + allowed tools, enqueues a runner closure on the worker registry, and tracks the live task. Per-call `forgeToken` override beats `defaultToken` for both the MCP servers and the env binding. - `steer()` pushes onto a private bounded `SteerChannel` the SDK consumes via a streaming-input async iterator yielding the initial prompt then steer messages in arrival order. Empty / closed-task steers return `{ok:false, reason:"conflict"}`. - `kill()` aborts the controller, closes the steer channel, cancels the registry slot. Idempotent on already-settled tasks. - `status()` lifts queued/running from the live table and settled outcomes from a small in-memory record (DB lift is a follow-up migration). - Behavioural parity with `runAgentTask`: same FIFO ordering, same `(agentType, repo, issue)` session key, first-event session capture, container release in the runner's `finally`. - Ports defined in `domain/ports/` carry the contracts forward — to be unified once the parallel #514–#517 PRs merge. Existing callers stay on `runAgentTask` per the issue's out-of-scope note; the migration ticket wires this in. ## Test plan - [x] `bun test src/infrastructure/agents/default-agent-dispatch.test.ts` — 4 pass: happy path (events, session capture, container release, settled=success), forgeToken override, kill mid-task (abort propagates, killed outcome, idempotent second kill), steer mid-task (message reaches SDK iterator, post-settle conflict). - [x] `bun run typecheck` clean. - [x] `bun x biome check apps/server/src/{domain/ports,infrastructure/agents}` clean. - [ ] Reviewer: confirm port interfaces don't conflict with parallel #514–#517 once those land.
feat(agents): DefaultAgentDispatch orchestrator wiring all four ports
All checks were successful
qa / qa (pull_request) Successful in 12m45s
qa / dockerfile (pull_request) Successful in 15s
99bd578875
Concrete `AgentDispatchPort` adapter that composes the four M25 ports
(`ClaudeAgentPort`, `ContainerLifecyclePort`, `McpRegistryPort`,
`WorkerRegistryPort`) plus a session-store port into a single dispatch
surface. Behavioural parity with `runAgentTask`: same FIFO ordering, same
`(agentType, repo, issue)` session key, first-event session capture,
container release in `finally`, per-call `forgeToken` override winning
over `defaultToken`.

`steer()` pushes onto a private bounded `SteerChannel` the SDK consumes
through a streaming-input async iterator; `kill()` aborts + closes the
channel + cancels the registry slot; `status()` lifts queued/running
from the live table and settled outcomes from a small DB-shaped record.

Three in-memory port fakes drive integration tests for the happy path,
kill mid-task, and steer mid-task scenarios. Existing callers stay on
`runAgentTask` — the migration ticket wires this in.

Closes #518
reviewer left a comment
  • behavior apps/server/src/domain/ports/mcp-registry-port.ts:13 imports McpServerSpec from ../../infrastructure/agent/claude-port. A domain port must not depend on an infrastructure module — this inverts the hexagonal dependency rule and prevents the domain layer from being extracted (or tested) without pulling in the infrastructure layer. McpServerSpec should be defined in domain/ports/ itself or promoted to @claude-hooks/shared, then re-imported from there by claude-port.ts.

  • behavior DefaultAgentDispatch.settled (default-agent-dispatch.ts) is an unbounded Map<string, SettledTask>. Every completed task remains in memory for the process lifetime. With no eviction at all (not even a cap) this will grow without bound in a long-running service before the DB migration lands. A simple LRU cap of ~1 000 entries is enough to prevent the leak while keeping status() correct for any recently-settled task a caller would realistically query.

- **behavior** `apps/server/src/domain/ports/mcp-registry-port.ts:13` imports `McpServerSpec` from `../../infrastructure/agent/claude-port`. A domain port must not depend on an infrastructure module — this inverts the hexagonal dependency rule and prevents the domain layer from being extracted (or tested) without pulling in the infrastructure layer. `McpServerSpec` should be defined in `domain/ports/` itself or promoted to `@claude-hooks/shared`, then re-imported from there by `claude-port.ts`. - **behavior** `DefaultAgentDispatch.settled` (`default-agent-dispatch.ts`) is an unbounded `Map<string, SettledTask>`. Every completed task remains in memory for the process lifetime. With no eviction at all (not even a cap) this will grow without bound in a long-running service before the DB migration lands. A simple LRU cap of ~1 000 entries is enough to prevent the leak while keeping `status()` correct for any recently-settled task a caller would realistically query.
Author
Collaborator

Aborted rebase on origin/main. The parallel ports (#514–#517) landed with shapes incompatible with this PR:

  • AgentDispatchRequest: main carries agentType: string + repo: AgentDispatchRepo + metadata; this PR carries agent: AgentInstance + ref: IssueOrPrRef + stateless_session?.
  • AgentDispatchHandle: main returns {taskId, worker, containerId?, sessionKey}; this PR returns {taskId} only.
  • AgentDispatchStatus: main is flat (state: "queued"|"running"|"succeeded"|"failed"|"cancelled", ISO timestamps, prUrl?); this PR uses a discriminated union with outcome + numeric timestamps.
  • steer/kill return void on main vs SteerVerdict / {ok: boolean} here.
  • McpRegistryPort: main takes RepoBinding; this PR uses McpBinding{forgeType, token} + a defaultToken() method that doesn't exist on main.
  • ContainerLifecyclePort: main exposes ensureReady → ContainerHandle{mode, container, hostCwd, inContainerCwd, execPath} + buildExecArgs + release(handle, ReleaseReason); this PR uses ContainerEnsureResult{containerName, pathToExecutable?, inContainerCwd?} + release(agent, taskId).

The orchestrator and test (~600 of 992 added lines) consume the obsolete shapes throughout. Adapting requires new design decisions outside rebase scope (notably: where the AgentInstance comes from once the request only carries agentType).

Recommend closing this PR and reopening as a fresh implementation built on main's port surfaces.

Aborted rebase on `origin/main`. The parallel ports (#514–#517) landed with shapes incompatible with this PR: - `AgentDispatchRequest`: main carries `agentType: string` + `repo: AgentDispatchRepo` + `metadata`; this PR carries `agent: AgentInstance` + `ref: IssueOrPrRef` + `stateless_session?`. - `AgentDispatchHandle`: main returns `{taskId, worker, containerId?, sessionKey}`; this PR returns `{taskId}` only. - `AgentDispatchStatus`: main is flat (`state: "queued"|"running"|"succeeded"|"failed"|"cancelled"`, ISO timestamps, `prUrl?`); this PR uses a discriminated union with `outcome` + numeric timestamps. - `steer`/`kill` return `void` on main vs `SteerVerdict` / `{ok: boolean}` here. - `McpRegistryPort`: main takes `RepoBinding`; this PR uses `McpBinding{forgeType, token}` + a `defaultToken()` method that doesn't exist on main. - `ContainerLifecyclePort`: main exposes `ensureReady → ContainerHandle{mode, container, hostCwd, inContainerCwd, execPath}` + `buildExecArgs` + `release(handle, ReleaseReason)`; this PR uses `ContainerEnsureResult{containerName, pathToExecutable?, inContainerCwd?}` + `release(agent, taskId)`. The orchestrator and test (~600 of 992 added lines) consume the obsolete shapes throughout. Adapting requires new design decisions outside rebase scope (notably: where the `AgentInstance` comes from once the request only carries `agentType`). Recommend closing this PR and reopening as a fresh implementation built on main's port surfaces.
Author
Collaborator

Rebase on origin/main aborted — design invalidated by intervening merges. Needs human triage.

Conflicts (all three are add/add):

  • apps/server/src/domain/ports/agent-dispatch-port.ts
  • apps/server/src/domain/ports/container-lifecycle-port.ts
  • apps/server/src/domain/ports/mcp-registry-port.ts

What landed on main since this PR branched (9e98643):

  • b398770AgentDispatchPort (canonical hex shape: { agentType, repo: AgentDispatchRepo, issueOrPr, prompt, forgeToken?, resumeSessionKey?, metadata }; handle exposes worker/containerId/sessionKey; status is a flat record).
  • a92edf1ContainerLifecyclePort (returns ContainerHandle = { mode, container, hostCwd, inContainerCwd, execPath }; release(handle, reason) not release(agent, taskId); buildExecEnv(agent, { repo, issue_number, branch, forge?, forgeToken? })).
  • ef7b74eMcpRegistryPort (serversFor(agent, RepoBinding) / allowedTools(agent, RepoBinding)no defaultToken; adapter resolves the token from the agent internally).
  • f05eee6 / adcdbeeClaudeAgentPort migration; infrastructure/agent/default-agent-dispatch.ts already exists on main as a thin dispatcher (constructor: mcpRegistry, forgePortFactory, runner; sync dispatch(req) that resolves token, builds servers, hands off to an injected runner).

Why this isn't a conflict-resolution job:

  1. The orchestrator imports AgentDispatchOutcome, SteerVerdict, AgentInstance from agent-dispatch-port — none exist on main's canonical port.
  2. req.agent: AgentInstance vs main's req.agentType: string — orchestrator would need a new agent-resolver dep.
  3. req.ref.{repo, issue_number, forgeType, branch?} vs main's req.repo: { forge, owner, name } + req.issueOrPr + req.metadata — translation layer required.
  4. mcp.defaultToken(agent, forge) doesn't exist on main; the registry resolves the token internally.
  5. container.release(agent, taskId) doesn't exist on main; it's release(handle, reason) keyed off the ContainerHandle the orchestrator must thread through.
  6. Path collision: PR puts the orchestrator at infrastructure/agents/ (plural). Main already has a different DefaultAgentDispatch at infrastructure/agent/ (singular).

Effectively the 443-line orchestrator + 494-line test would need to be rewritten against main's port shapes, plus a decision on how to reconcile with the existing thin dispatcher. That's design work, not rebase work — bailing rather than force a bad merge through.

Rebase on `origin/main` aborted — design invalidated by intervening merges. Needs human triage. **Conflicts** (all three are add/add): - `apps/server/src/domain/ports/agent-dispatch-port.ts` - `apps/server/src/domain/ports/container-lifecycle-port.ts` - `apps/server/src/domain/ports/mcp-registry-port.ts` **What landed on main since this PR branched** (`9e98643`): - `b398770` — `AgentDispatchPort` (canonical hex shape: `{ agentType, repo: AgentDispatchRepo, issueOrPr, prompt, forgeToken?, resumeSessionKey?, metadata }`; handle exposes `worker`/`containerId`/`sessionKey`; status is a flat record). - `a92edf1` — `ContainerLifecyclePort` (returns `ContainerHandle = { mode, container, hostCwd, inContainerCwd, execPath }`; `release(handle, reason)` not `release(agent, taskId)`; `buildExecEnv(agent, { repo, issue_number, branch, forge?, forgeToken? })`). - `ef7b74e` — `McpRegistryPort` (`serversFor(agent, RepoBinding)` / `allowedTools(agent, RepoBinding)` — **no** `defaultToken`; adapter resolves the token from the agent internally). - `f05eee6` / `adcdbee` — `ClaudeAgentPort` migration; `infrastructure/agent/default-agent-dispatch.ts` already exists on main as a **thin** dispatcher (constructor: `mcpRegistry`, `forgePortFactory`, `runner`; sync `dispatch(req)` that resolves token, builds servers, hands off to an injected runner). **Why this isn't a conflict-resolution job:** 1. The orchestrator imports `AgentDispatchOutcome`, `SteerVerdict`, `AgentInstance` from `agent-dispatch-port` — none exist on main's canonical port. 2. `req.agent: AgentInstance` vs main's `req.agentType: string` — orchestrator would need a new agent-resolver dep. 3. `req.ref.{repo, issue_number, forgeType, branch?}` vs main's `req.repo: { forge, owner, name }` + `req.issueOrPr` + `req.metadata` — translation layer required. 4. `mcp.defaultToken(agent, forge)` doesn't exist on main; the registry resolves the token internally. 5. `container.release(agent, taskId)` doesn't exist on main; it's `release(handle, reason)` keyed off the `ContainerHandle` the orchestrator must thread through. 6. Path collision: PR puts the orchestrator at `infrastructure/agents/` (plural). Main already has a different `DefaultAgentDispatch` at `infrastructure/agent/` (singular). Effectively the 443-line orchestrator + 494-line test would need to be rewritten against main's port shapes, plus a decision on how to reconcile with the existing thin dispatcher. That's design work, not rebase work — bailing rather than force a bad merge through.
Author
Collaborator

Rebase on origin/main aborted — the PR's design has been invalidated by parallel work on main.

This PR redefines three domain ports (AgentDispatchPort, ContainerLifecyclePort, McpRegistryPort) and ships an orchestrator + test file built against those redefined shapes. Since the PR was opened, four commits landed on main shipping the same three ports with materially different surfaces, plus two adapters (DockerContainerLifecycle, HostContainerLifecycle, DefaultMcpRegistry) already wired against main's shapes:

  • b398770 AgentDispatchPort domain interface
  • a92edf1 ContainerLifecyclePort + Docker / host adapters
  • ef7b74e McpRegistryPort + DefaultMcpRegistry adapter
  • adcdbee per-call forge-token override in AgentDispatchPort.dispatch()

Concrete divergence:

  • AgentDispatchPort — PR uses agent: AgentInstance + ref: IssueOrPrRef + stateless_session; main uses agentType: string + repo: AgentDispatchRepo + issueOrPr: number + metadata. AgentDispatchHandle, AgentDispatchStatus, and the steer / kill return types all differ.
  • ContainerLifecyclePort — PR's ensureReady → ContainerEnsureResult and release(agent, taskId). Main's ensureReady → ContainerHandle, adds a buildExecArgs method, release(handle, reason). Already consumed by main's two adapters.
  • McpRegistryPort — PR requires defaultToken(agent, forgeType): string and uses an McpBinding. Main has no such method and uses RepoBinding from webhook-config.

Per the rebase rules, shared port files take main as the source of truth — but the orchestrator and its tests are wholly built on the PR's redefined shapes. What remains after dropping those redefinitions is a from-scratch reimplementation against main's port surfaces, not a rebase. Forcing it through would silently change the merged port contract.

Recommendation: close-and-reopen this work as a fresh implementation that consumes main's existing AgentDispatchPort / ContainerLifecyclePort / McpRegistryPort shapes plus a new WorkerRegistryPort (the latter is uncontroversial — keep that file as-is). The orchestrator's intent (single dispatch surface wiring container, MCP registry, worker registry, and Claude agent ports) carries over; only the type plumbing needs to be rebuilt.

Rebase on `origin/main` aborted — the PR's design has been invalidated by parallel work on main. This PR redefines three domain ports (`AgentDispatchPort`, `ContainerLifecyclePort`, `McpRegistryPort`) and ships an orchestrator + test file built against those redefined shapes. Since the PR was opened, four commits landed on main shipping the same three ports with materially different surfaces, plus two adapters (`DockerContainerLifecycle`, `HostContainerLifecycle`, `DefaultMcpRegistry`) already wired against main's shapes: - `b398770` AgentDispatchPort domain interface - `a92edf1` ContainerLifecyclePort + Docker / host adapters - `ef7b74e` McpRegistryPort + DefaultMcpRegistry adapter - `adcdbee` per-call forge-token override in AgentDispatchPort.dispatch() Concrete divergence: - **AgentDispatchPort** — PR uses `agent: AgentInstance` + `ref: IssueOrPrRef` + `stateless_session`; main uses `agentType: string` + `repo: AgentDispatchRepo` + `issueOrPr: number` + `metadata`. `AgentDispatchHandle`, `AgentDispatchStatus`, and the `steer` / `kill` return types all differ. - **ContainerLifecyclePort** — PR's `ensureReady → ContainerEnsureResult` and `release(agent, taskId)`. Main's `ensureReady → ContainerHandle`, adds a `buildExecArgs` method, `release(handle, reason)`. Already consumed by main's two adapters. - **McpRegistryPort** — PR requires `defaultToken(agent, forgeType): string` and uses an `McpBinding`. Main has no such method and uses `RepoBinding` from `webhook-config`. Per the rebase rules, shared port files take main as the source of truth — but the orchestrator and its tests are wholly built on the PR's redefined shapes. What remains after dropping those redefinitions is a from-scratch reimplementation against main's port surfaces, not a rebase. Forcing it through would silently change the merged port contract. Recommendation: close-and-reopen this work as a fresh implementation that consumes main's existing `AgentDispatchPort` / `ContainerLifecyclePort` / `McpRegistryPort` shapes plus a new `WorkerRegistryPort` (the latter is uncontroversial — keep that file as-is). The orchestrator's intent (single dispatch surface wiring container, MCP registry, worker registry, and Claude agent ports) carries over; only the type plumbing needs to be rebuilt.
Author
Collaborator

Rebase aborted — the PR's design has been invalidated by the parallel ports landing on main. Stopping here per the rebase workflow rather than forcing a bad merge through.

The three port files this PR co-authored were also added on main via #514–#517 (b398770, a92edf1, ef7b74e) plus adcdbee (per-call forge-token override) and f05eee6 (ClaudeAgentPort migration). Their final shapes are incompatible with the ones this orchestrator was written against:

  • agent-dispatch-port.ts — main has AgentDispatchRepo + flat agentType/issueOrPr/metadata; this PR uses AgentInstance + IssueOrPrRef. Main returns void from steer/kill; this PR returns SteerVerdict/{ok}. AgentDispatchHandle requires worker + sessionKey on main; this PR returns just {taskId}.
  • container-lifecycle-port.ts — main has ContainerHandle (mode/hostCwd/execPath) + buildExecArgs + release(handle, reason); this PR uses ContainerEnsureResult + release(agent, taskId) with no exec-args wrapper.
  • mcp-registry-port.ts — main has serversFor(agent, RepoBinding) and no defaultToken accessor; this PR's orchestrator calls mcp.defaultToken(agent, forgeType) to resolve the per-call token fallback. There is no equivalent on the new port.

Additionally main already has a small infrastructure/agent/default-agent-dispatch.ts (different folder) implementing the infrastructure/agent/agent-dispatch-port.ts surface from #521 — that's a separate, narrower port for forge-token resolution. Two DefaultAgentDispatch classes in two folders against two different ports is also a conflict to resolve.

The orchestrator's intent (compose all four ports into one dispatch surface) is still valid, but it needs a from-scratch reimplementation against the new shapes, not a rebase. Closing-or-rewriting decision belongs to a human — leaving the branch as-is.

Rebase aborted — the PR's design has been invalidated by the parallel ports landing on `main`. Stopping here per the rebase workflow rather than forcing a bad merge through. The three port files this PR co-authored were also added on `main` via #514–#517 (`b398770`, `a92edf1`, `ef7b74e`) plus `adcdbee` (per-call forge-token override) and `f05eee6` (ClaudeAgentPort migration). Their final shapes are incompatible with the ones this orchestrator was written against: - `agent-dispatch-port.ts` — main has `AgentDispatchRepo` + flat `agentType`/`issueOrPr`/`metadata`; this PR uses `AgentInstance` + `IssueOrPrRef`. Main returns `void` from `steer`/`kill`; this PR returns `SteerVerdict`/`{ok}`. `AgentDispatchHandle` requires `worker` + `sessionKey` on main; this PR returns just `{taskId}`. - `container-lifecycle-port.ts` — main has `ContainerHandle` (mode/hostCwd/execPath) + `buildExecArgs` + `release(handle, reason)`; this PR uses `ContainerEnsureResult` + `release(agent, taskId)` with no exec-args wrapper. - `mcp-registry-port.ts` — main has `serversFor(agent, RepoBinding)` and **no `defaultToken` accessor**; this PR's orchestrator calls `mcp.defaultToken(agent, forgeType)` to resolve the per-call token fallback. There is no equivalent on the new port. Additionally main already has a small `infrastructure/agent/default-agent-dispatch.ts` (different folder) implementing the `infrastructure/agent/agent-dispatch-port.ts` surface from #521 — that's a separate, narrower port for forge-token resolution. Two `DefaultAgentDispatch` classes in two folders against two different ports is also a conflict to resolve. The orchestrator's intent (compose all four ports into one dispatch surface) is still valid, but it needs a from-scratch reimplementation against the new shapes, not a rebase. Closing-or-rewriting decision belongs to a human — leaving the branch as-is.
code-lead force-pushed boss/518 from 99bd578875
All checks were successful
qa / qa (pull_request) Successful in 12m45s
qa / dockerfile (pull_request) Successful in 15s
to ea8ddb4746
Some checks are pending
qa / qa (pull_request) Waiting to run
qa / dockerfile (pull_request) Waiting to run
2026-04-28 12:59:00 +00:00
Compare
code-lead force-pushed boss/518 from ea8ddb4746
Some checks are pending
qa / qa (pull_request) Waiting to run
qa / dockerfile (pull_request) Waiting to run
to 9aaed6c689
Some checks failed
qa / qa (pull_request) Failing after 4m21s
qa / dockerfile (pull_request) Successful in 14s
2026-04-28 13:23:01 +00:00
Compare
code-lead removed their assignment 2026-04-28 13:57:24 +00:00
Author
Collaborator

🔀 Unassigned by operator via board (running task cancelled).

🔀 Unassigned by operator via board (running task cancelled).
Collaborator

Rebase blocked — needs domain decision

Tried to rebase boss/518 on current main. One textual conflict on default-agent-dispatch.ts (main has a 31-line wrapper from #533, this PR has the 631-line orchestrator). Took --theirs to keep this PR's full impl, but the resulting tree fails bun run typecheck with semantic mismatches:

  • AgentInstance (this PR) vs ResolvedAgent (main) — different shapes, missing git_author, git_name, git_email, branch_prefix, forgejo_token
  • McpServerSpec.args — main: string[], this PR's port: readonly string[]
  • AgentDispatchPort.dispatch return — main: Promise<TaskResult>, this PR: Promise<AgentDispatchHandle>
  • AgentDispatchRequest field set differs (this PR adds agentType, repo, issueOrPr, prompt; main keeps taskId, task, abort, callbacks)

Root cause: this PR was written in parallel with #527, #529, #533 which each tweaked port shapes. The merge train picked the smaller-scoped types; this PR's orchestrator assumes its own.

Two paths forward, both need a human call:

  1. Adopt this PR's richer types (AgentInstance, AgentDispatchHandle, full AgentDispatchRequest) and update the thin wrapper + callsites to match. Bigger blast radius, cleaner end state.
  2. Adapt this PR's orchestrator to main's existing types (ResolvedAgent, Promise<TaskResult>, current AgentDispatchRequest). Smaller diff, less invasive.

Local rebase reverted; boss/518 upstream untouched. Leaving open for triage.

**Rebase blocked — needs domain decision** Tried to rebase `boss/518` on current `main`. One textual conflict on `default-agent-dispatch.ts` (main has a 31-line wrapper from #533, this PR has the 631-line orchestrator). Took `--theirs` to keep this PR's full impl, but the resulting tree fails `bun run typecheck` with semantic mismatches: - `AgentInstance` (this PR) vs `ResolvedAgent` (main) — different shapes, missing `git_author`, `git_name`, `git_email`, `branch_prefix`, `forgejo_token` - `McpServerSpec.args` — main: `string[]`, this PR's port: `readonly string[]` - `AgentDispatchPort.dispatch` return — main: `Promise<TaskResult>`, this PR: `Promise<AgentDispatchHandle>` - `AgentDispatchRequest` field set differs (this PR adds `agentType`, `repo`, `issueOrPr`, `prompt`; main keeps `taskId`, `task`, `abort`, callbacks) Root cause: this PR was written in parallel with #527, #529, #533 which each tweaked port shapes. The merge train picked the smaller-scoped types; this PR's orchestrator assumes its own. Two paths forward, both need a human call: 1. **Adopt this PR's richer types** (`AgentInstance`, `AgentDispatchHandle`, full `AgentDispatchRequest`) and update the thin wrapper + callsites to match. Bigger blast radius, cleaner end state. 2. **Adapt this PR's orchestrator to main's existing types** (`ResolvedAgent`, `Promise<TaskResult>`, current `AgentDispatchRequest`). Smaller diff, less invasive. Local rebase reverted; `boss/518` upstream untouched. Leaving open for triage.
fix(ci): pass forge prefix to sessionKey in invalid-session retry test
Some checks failed
qa / qa (pull_request) Has been cancelled
qa / dockerfile (pull_request) Has been cancelled
f749782287
The #520 forge-prefix migration added a leading `forge` arg to
`sessionKey(forge, type, repo, issueOrPr)`. One call site in the
invalid-session retry test was missed, so tsc rejected it with TS2554
(expected 4, got 3) and broke `qa.yml`.
Collaborator

Closing per boss's own recommendation in this thread (4 analyses converged on close-and-rewrite).

Issue #518 stays open and is being updated with:

  • Pointers to main's canonical port shapes (AgentDispatchPort, ContainerLifecyclePort, McpRegistryPort)
  • Reviewer's memory-leak finding promoted to AC (settled-task cache must be bounded, LRU ~1000)
  • Salvage note: worker-registry-port.ts from boss/518 is uncontroversial — port verbatim
  • Hex-arch finding from review (#1) is moot: main's mcp-registry-port.ts already lives cleanly in domain/ports/, this PR's parallel copy is the one being discarded

Branch boss/518 left in place for reference until the rewrite lands.

Closing per boss's own recommendation in this thread (4 analyses converged on close-and-rewrite). Issue #518 stays open and is being updated with: - Pointers to main's canonical port shapes (`AgentDispatchPort`, `ContainerLifecyclePort`, `McpRegistryPort`) - Reviewer's memory-leak finding promoted to AC (settled-task cache must be bounded, LRU ~1000) - Salvage note: `worker-registry-port.ts` from `boss/518` is uncontroversial — port verbatim - Hex-arch finding from review (#1) is moot: main's `mcp-registry-port.ts` already lives cleanly in `domain/ports/`, this PR's parallel copy is the one being discarded Branch `boss/518` left in place for reference until the rewrite lands.
claude-desktop closed this pull request 2026-04-28 14:21:25 +00:00
Some checks failed
qa / qa (pull_request) Has been cancelled
Required
Details
qa / dockerfile (pull_request) Has been cancelled

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!531
No description provided.