F4 — watched_repos table + ingress switch + boot migration #485

Closed
opened 2026-04-27 21:43:16 +00:00 by claude-desktop · 0 comments
Collaborator

As a maintainer, I want the watched-repo list to live in SQLite, so that adding/removing a repo does not require editing agents.json and restarting.

Acceptance criteria

  • watched_repos schema added to db.ts with PK (forge_type, owner, name) and columns webhook_secret, webhook_id, enabled, default_branch, created_at, updated_at.
  • Helpers: getWatchedRepo(forge,owner,name), listWatchedRepos(), listActiveWatchedRepos() (filters by service_settings.active_forge_type), upsertWatchedRepo(), disableWatchedRepo().
  • One-shot migration on boot: if watched_repos is empty and agents.json#/repos is non-empty, copy entries into the table using the shared webhook_secret_file content, then rewrite agents.json with repos removed (atomic temp + rename, mode preserved). Back up the original at agents.json.pre-f1.bak. Log a single summary line: [f1-migration] copied N repos from agents.json; backup at agents.json.pre-f1.bak.
  • webhook-config.ts exposes isKnownRepo(forge,owner,name) backed by watched_repos. Empty table → reject. The empty-list-equals-accept-all back-compat is removed.
  • Webhook ingress (webhook.ts):
    • Verifies the per-repo webhook_secret from the table; falls back to the shared file only when webhook_id IS NULL (migrated rows).
    • If forge_type !== service_settings.active_forge_type, responds 202 Accepted and returns immediately — no dispatch, debug-level log only. Avoids forge-side retry storms for inactive forges.
    • Otherwise dispatches as today.
  • Dispatch gate uses the same lookup; unknown repo → 404 with forge=<type> repo=<owner/name> log line.
  • repos: in agents.json is removed by the migration; if it reappears, boot logs a single warning and ignores it.

Out of scope

  • UI to manage the table (F6).
  • OAuth-driven webhook lifecycle (F5).

References

  • Spec: docs/specs/forge-auth-repo-selection.md §F4, §5
  • docs/multi-repo.md

Dependencies

  • Blocked by F2 (service_settings.active_forge_type is read by ingress)
As a maintainer, I want the watched-repo list to live in SQLite, so that adding/removing a repo does not require editing `agents.json` and restarting. ## Acceptance criteria - [ ] `watched_repos` schema added to `db.ts` with PK `(forge_type, owner, name)` and columns `webhook_secret`, `webhook_id`, `enabled`, `default_branch`, `created_at`, `updated_at`. - [ ] Helpers: `getWatchedRepo(forge,owner,name)`, `listWatchedRepos()`, `listActiveWatchedRepos()` (filters by `service_settings.active_forge_type`), `upsertWatchedRepo()`, `disableWatchedRepo()`. - [ ] One-shot migration on boot: if `watched_repos` is empty and `agents.json#/repos` is non-empty, copy entries into the table using the shared `webhook_secret_file` content, then rewrite `agents.json` with `repos` removed (atomic temp + rename, mode preserved). Back up the original at `agents.json.pre-f1.bak`. Log a single summary line: `[f1-migration] copied N repos from agents.json; backup at agents.json.pre-f1.bak`. - [ ] `webhook-config.ts` exposes `isKnownRepo(forge,owner,name)` backed by `watched_repos`. Empty table → reject. The empty-list-equals-accept-all back-compat is removed. - [ ] Webhook ingress (`webhook.ts`): - Verifies the per-repo `webhook_secret` from the table; falls back to the shared file only when `webhook_id IS NULL` (migrated rows). - If `forge_type !== service_settings.active_forge_type`, responds **`202 Accepted`** and returns immediately — no dispatch, debug-level log only. Avoids forge-side retry storms for inactive forges. - Otherwise dispatches as today. - [ ] Dispatch gate uses the same lookup; unknown repo → 404 with `forge=<type> repo=<owner/name>` log line. - [ ] `repos:` in `agents.json` is removed by the migration; if it reappears, boot logs a single warning and ignores it. ## Out of scope - UI to manage the table (F6). - OAuth-driven webhook lifecycle (F5). ## References - Spec: `docs/specs/forge-auth-repo-selection.md` §F4, §5 - `docs/multi-repo.md` ## Dependencies - Blocked by F2 (`service_settings.active_forge_type` is read by ingress)
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#485
No description provided.