Skip to content

assistant-api: many repositories per workspace + REPO_URLS injection#590

Merged
ExtraToast merged 4 commits into
mainfrom
assistant-api/multi-repo
Jun 6, 2026
Merged

assistant-api: many repositories per workspace + REPO_URLS injection#590
ExtraToast merged 4 commits into
mainfrom
assistant-api/multi-repo

Conversation

@jorisjonkers-dev-agents

@jorisjonkers-dev-agents jorisjonkers-dev-agents Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Why

A workspace was bound to exactly one repository (the denormalised repository_id / repoUrl), so the orchestrator could only emit a single REPO_URL. PR #589 taught the runner to clone a REPO_URLS list, but nothing produced it.

Changes

  • V13 migration — a workspace_repositories(workspace_id, repository_id, is_primary, attached_at) junction. Existing repo-backed workspaces are backfilled with their primary repo (is_primary = TRUE). Mirrors the existing project_repositories shape.
  • WorkspaceRepositoryRepository port + JooqWorkspaceRepositoryRepositoryattach / detach / findAllByWorkspaceId, a 1:1 mirror of the tested JooqProjectRepositoryRepository.
  • Attach / Detach commands + handlers — attach validates the workspace and repository exist (NoSuchElementException); detach refuses to remove the primary (IllegalArgumentException).
  • Fabric8AgentRunnerOrchestrator injects REPO_URLS from the workspace's non-primary attached repos (resolved to repo URLs), space-joined, and only when there is at least one — so a single-repo workspace's Pod env is byte-identical to before. REPO_URL still carries the primary. The two new dependencies use ObjectProvider, matching the existing deploy-key / github-link wiring.

Scope: REST endpoints deferred

The attach/detach REST endpoints (and their request DTO) are intentionally not in this PR. Exposing them regenerates the committed services/assistant-api/openapi.json and the assistant-ui generated types, which the springdoc exportOpenApiSpec task can only produce from a fully-booted app context (Testcontainers/Docker). Keeping this PR free of any HTTP-contract change keeps the OpenAPI Contract job green. The endpoints are a thin layer over the commands that already exist and are unit-tested here; the follow-up adds them plus the regenerated contract (one ./gradlew :services:assistant-api:exportOpenApiSpec + pnpm --filter @personal-stack/assistant-ui contract:generate on a Docker host).

Verification

  • generateJooq regenerates cleanly against V13 (backfill uses plain INSERT … SELECT; jOOQ's DDL parser rejects ON CONFLICT, redundant on a fresh table).
  • Migration Guard passes — V13 is strictly incrementing over V12 (V10/V11 are taken).
  • All assistant-api source sets compile; ktlintCheck clean; no HTTP-contract change so the OpenAPI Contract job is unaffected.
  • New handler tests: attach happy-path + unknown-workspace + unknown-repo; detach non-primary + primary-guard + no-op — 6/6 pass.
  • Orchestrator integration-test helper updated with the two new providers (defaulted empty so existing k3s cases are untouched).

Completes the producer side of the multi-repo loop that PR #589 consumes. Independent of the council PRs (#586/#587/#588).

A workspace bound to exactly one repository (the denormalised
repository_id / repoUrl), so the orchestrator could only ever emit a
single REPO_URL. The runner now understands a REPO_URLS list, but
nothing produced it.

A workspace_repositories junction (V10) records the additional repos a
workspace clones beyond its primary; the primary is backfilled with
is_primary = TRUE so it can be excluded without a second lookup. A
WorkspaceRepositoryRepository port plus its jOOQ implementation mirror
the existing project_repositories pattern. Attach and detach commands
manage the membership — attach validates the workspace and repository
exist; detach refuses to remove the primary.

Fabric8AgentRunnerOrchestrator now injects REPO_URLS from the
workspace's non-primary attached repos (resolved to their repo URLs),
joined by spaces, and only when there is at least one — so a single-repo
workspace's Pod env is unchanged. REPO_URL still carries the primary.
The two new providers are injected via ObjectProvider, consistent with
the existing deploy-key / github-link wiring.

WorkspaceController gains POST /{id}/repositories and
DELETE /{id}/repositories/{repositoryId}. Handler unit tests cover the
attach/detach guards; the orchestrator integration-test helper passes
the new providers (defaulted empty so existing cases are untouched).
@jorisjonkers-dev-agents jorisjonkers-dev-agents Bot added the enhancement New feature or request label Jun 6, 2026
The highest existing assistant-api migration is V12; V10/V11 are already
taken (chat_session_kind, repository_access_verification). The new
junction migration is renumbered V10 -> V13 so it is strictly
incrementing and unique, satisfying the migration guard.
The attach/detach commands, handlers, junction, jOOQ adapter, migration,
and REPO_URLS orchestrator injection land here — the complete data layer
and runner-facing plumbing. The two REST endpoints (and their request
DTO) are split into a follow-up because exposing them regenerates the
committed services/assistant-api/openapi.json + assistant-ui generated
types, which the springdoc export task can only produce from a
fully-booted app (Testcontainers). Keeping this PR free of any HTTP-
contract change keeps the OpenAPI contract job green; the endpoints are a
thin layer on top of the commands that already exist and are tested here.
Covers attach (incl. is_primary), idempotent re-attach, detach, and
findAllByWorkspaceId against a real Postgres, mirroring
JooqProjectRepositoryRepositoryIntegrationTest. Keeps the new persistence
class above the 80% jacoco bar (infrastructure/k8s is already excluded, so
the orchestrator REPO_URLS path needs no unit coverage).
@ExtraToast ExtraToast merged commit fce498f into main Jun 6, 2026
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant