MF-4: Per-repo forge binding in config/agents.json + adapter factory #295

Closed
opened 2026-04-23 23:33:13 +00:00 by code-lead · 0 comments
Collaborator

As an operator, I want to declare each repo's forge in config/agents.json so that agents pick the correct ForgePort adapter automatically and startup fails fast when a required token is missing.

This is the one hard gate for the multi-forge spec — every other story consumes it.

Acceptance criteria

Config schema

  • repos[] grows a forge field:
    "repos": [
      { "owner": "charles", "name": "claude-hooks", "forge": "forgejo" },
      { "owner": "charles", "name": "loom",         "forge": "github"   }
    ]
    
  • Back-compat: a plain string "charles/claude-hooks" resolves to forge: "forgejo".

Per-type tokens become forge-scoped

  • New token_files map:
    "dev": {
      "token_files": {
        "forgejo": "/home/charles/.config/claude-hooks/tokens/dev-forgejo",
        "github":  "/home/charles/.config/claude-hooks/tokens/dev-github"
      }
    }
    
  • Old token_file is still accepted and resolves as forgejo only — logged with a deprecation hint pointing at token_files.

Adapter factory

  • New apps/server/src/infrastructure/forge/adapter-factory.ts that returns the right ForgePort for a given repo.
  • Consumed by apps/server/src/shared/config/webhook-config.ts when building ResolvedAgent, and by apps/server/src/domain/agent/agent-runner.ts when building the container env (FORGE_TYPE=..., per-forge token path).
  • Every dispatch path looks up the forge per-repo at the top of its handler; never assumes Forgejo.

Startup validator

  • config/agents.json validator: every repos[].forge must have a matching token_files[<forge>] on every agent type that uses forgejo_user-style identity (i.e., every type that opens PRs / issues).
  • Mismatch → fail fast with a clear error message including the missing path.

Tests

  • webhook-config.test.ts: mixed-forge config loads, per-repo forge is resolvable, missing token per forge fails startup with the expected message.
  • Back-compat: legacy string-repo + token_file entry still resolves.

Out of scope

  • Per-agent-instance forge override via the SQLite agents table. Stays type-level.
  • Auto-provisioning forge tokens. Operator still creates PATs manually and drops the file.
  • Wiring any specific non-Forgejo adapter behind the factory — MF-1/MF-2 do that separately.

References

  • Spec: specs/multi-forge.md § MF-4.
  • apps/server/src/shared/config/webhook-config.tsResolvedAgent construction.
  • apps/server/src/domain/agent/agent-runner.ts — container env wiring.
  • apps/server/src/infrastructure/database/db.tsagents table.
  • CLAUDE.md § "Multi-repo".

Dependencies

  • No blockers — this is the Phase 0 foundation story. Everything else (MF-1, MF-2, MF-3, MF-5, MF-7, MF-8) depends on it.
As an operator, I want to declare each repo's forge in `config/agents.json` so that agents pick the correct `ForgePort` adapter automatically and startup fails fast when a required token is missing. This is the one hard gate for the multi-forge spec — every other story consumes it. ## Acceptance criteria ### Config schema - [ ] `repos[]` grows a `forge` field: ```json "repos": [ { "owner": "charles", "name": "claude-hooks", "forge": "forgejo" }, { "owner": "charles", "name": "loom", "forge": "github" } ] ``` - [ ] Back-compat: a plain string `"charles/claude-hooks"` resolves to `forge: "forgejo"`. ### Per-type tokens become forge-scoped - [ ] New `token_files` map: ```json "dev": { "token_files": { "forgejo": "/home/charles/.config/claude-hooks/tokens/dev-forgejo", "github": "/home/charles/.config/claude-hooks/tokens/dev-github" } } ``` - [ ] Old `token_file` is still accepted and resolves as `forgejo` only — logged with a deprecation hint pointing at `token_files`. ### Adapter factory - [ ] New `apps/server/src/infrastructure/forge/adapter-factory.ts` that returns the right `ForgePort` for a given repo. - [ ] Consumed by `apps/server/src/shared/config/webhook-config.ts` when building `ResolvedAgent`, and by `apps/server/src/domain/agent/agent-runner.ts` when building the container env (`FORGE_TYPE=...`, per-forge token path). - [ ] Every dispatch path looks up the forge per-repo at the top of its handler; never assumes Forgejo. ### Startup validator - [ ] `config/agents.json` validator: every `repos[].forge` must have a matching `token_files[<forge>]` on every agent type that uses `forgejo_user`-style identity (i.e., every type that opens PRs / issues). - [ ] Mismatch → fail fast with a clear error message including the missing path. ### Tests - [ ] `webhook-config.test.ts`: mixed-forge config loads, per-repo `forge` is resolvable, missing token per forge fails startup with the expected message. - [ ] Back-compat: legacy string-repo + `token_file` entry still resolves. ## Out of scope - Per-agent-instance forge override via the SQLite `agents` table. Stays type-level. - Auto-provisioning forge tokens. Operator still creates PATs manually and drops the file. - Wiring any specific non-Forgejo adapter behind the factory — MF-1/MF-2 do that separately. ## References - Spec: [`specs/multi-forge.md`](../src/branch/main/specs/multi-forge.md) § MF-4. - `apps/server/src/shared/config/webhook-config.ts` — `ResolvedAgent` construction. - `apps/server/src/domain/agent/agent-runner.ts` — container env wiring. - `apps/server/src/infrastructure/database/db.ts` — `agents` table. - CLAUDE.md § "Multi-repo". ## Dependencies - No blockers — this is the Phase 0 foundation story. Everything else (MF-1, MF-2, MF-3, MF-5, MF-7, MF-8) depends on it.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#295
No description provided.