Repo bootstrap: auto-provision label set at service startup / on new repo connection #97

Closed
opened 2026-04-19 18:19:04 +00:00 by claude-desktop · 0 comments
Collaborator

User story

As the operator, I want the routing labels (area:design, area:design-review, the type labels, etc.) to be present on every repo the webhook points at, without having to create each one through the Forgejo UI — so that a fresh repo connected to claude-hooks just works.

What happens today

Each repo the service processes must have the routing labels created by hand before certain flows work. Concrete examples from today:

  • area:design-review (id 87 on claude-hooks) — didn't exist when PR #86 merged. Designer added it to #70add_issue_labels silently no-op'd at the server level (label didn't resolve). Design-reviewer never fired. Hit live on #70 at 20:08 UTC; had to curl the Forgejo API to create the label manually. PR #86 explicitly flagged this setup step but didn't automate it.
  • area:design (id 86) — created by hand during #56 rollout for the same reason.
  • Every area:* and type:* label in ~/.claude/CLAUDE.md's label scheme — operator docs say "set up labels and a milestone before creating issues", which is fine for a person but not for an auto-connected repo.

Proposed shape

Pick one of:

A — on service startup, reconcile labels for every repo in config/agents.json

Startup reads a canonical label list (lives in config/labels.json or baked into TS), iterates every repo the service watches, calls the Forgejo create_label API for missing names with the right color + description. Idempotent; operator's "connect a new repo to claude-hooks" flow becomes "add the repo + rebuild".

B — on repository.created or repository.connected webhook

React to the webhook Forgejo fires when a repo is added. Same reconcile logic, but event-driven. Narrower but more precise than A.

C — just labels-bootstrap <repo> recipe

Operator-triggered. Low effort, explicit. Matches today's just containers-rebuild ergonomics.

Recommend A + C: auto-reconcile at startup for the always-on case, plus the manual recipe for ad-hoc repos that aren't in agents.json. B is nice-to-have; not worth the webhook wiring yet.

Canonical label set (reference)

From global CLAUDE.md + this repo's current labels:

Name Color Purpose
area:agents #0ea5e9 Agent types, pool, config
area:dashboard #8b5cf6 Dashboard UI + observability
area:design #ec4899 UI/UX mockups → designer
area:design-review #be185d Design review → design-reviewer
area:infra #16a34a Deployment, isolation, containers
area:meta #6b7280 Tracking, scaffolding, setup
area:sessions #2563eb Session store, SDK resume
area:webhook #06b6d4 Webhook routing + handlers
area:workdir #ea580c Clone cache, worktrees, git identity
type:user-story #ec4899 User story
type:meta #374151 Tracking / decisions, not impl
type:bug TBD Bug
type:chore TBD Chore

The area:* set is repo-specific; the type:* set is repo-agnostic. Bootstrap should probably split these into two lists.

Acceptance criteria

Core

  • config/labels.json (or equivalent) defines the canonical labels as {name, color, description, scope: "area" | "type"}.
  • src/labels.ts exposes reconcileLabels(repo) that POST /api/v1/repos/{repo}/labels for any missing name and leaves existing labels alone (no color / description overwrites — idempotent).
  • main.ts startup calls reconcileLabels for every repo the service's webhook config knows about.
  • just labels-bootstrap <repo> recipe wraps the same call for ad-hoc use.

Safety

  • Reconcile only creates missing labels; never deletes / renames existing ones (operators may have added customs).
  • Logs [labels] created area:design-review on charles/claude-hooks (id=87) on create, [labels] already present: area:design on skip.
  • If the repo doesn't exist or the token can't see it, log a warning and move on — don't crash.

Tests

  • labels.test.ts: reconcile on a stubbed repo with partial label set adds the missing ones and skips existing ones.
  • labels.test.ts: reconcile tolerates 404 / 403 from the API.

Docs

  • CLAUDE.md gains a short "Bootstrap labels on a new repo" section replacing the current "for new repos, set up labels and a milestone" line in the global CLAUDE.md.

Out of scope

  • Milestone creation — tied to a release cadence per repo; stays manual.
  • Label color / description updates — no "fix up wrong color on existing label" logic. Operator hand-edits if they want.
  • Multi-org support — current reconcile only runs against charles/* repos; org-wide reconcile can wait until the service watches >1 org.

References

  • #86 — the PR that introduced the area:design-review label dependency.
  • #70 — where the missing label manifested today.
  • config/agents.json — where the repo list lives today (implicitly, via tokens).
  • Global CLAUDE.md "Label & milestone scheme" section.

Dependencies

  • Blocked by: nothing.
  • Blocks: frictionless onboarding of new repos to claude-hooks.
  • Branch off: main.
## User story As the **operator**, I want the routing labels (`area:design`, `area:design-review`, the type labels, etc.) to be present on every repo the webhook points at, without having to create each one through the Forgejo UI — so that a fresh repo connected to claude-hooks just works. ## What happens today Each repo the service processes must have the routing labels created by hand before certain flows work. Concrete examples from today: - **`area:design-review`** (id 87 on claude-hooks) — didn't exist when PR #86 merged. Designer added it to #70 → `add_issue_labels` silently no-op'd at the server level (label didn't resolve). Design-reviewer never fired. Hit live on #70 at 20:08 UTC; had to `curl` the Forgejo API to create the label manually. [PR #86](https://forge.jacquin.app/charles/claude-hooks/pulls/86) explicitly flagged this setup step but didn't automate it. - **`area:design`** (id 86) — created by hand during #56 rollout for the same reason. - **Every `area:*` and `type:*` label in `~/.claude/CLAUDE.md`'s label scheme** — operator docs say "set up labels and a milestone before creating issues", which is fine for a person but not for an auto-connected repo. ## Proposed shape Pick one of: ### A — on service startup, reconcile labels for every repo in `config/agents.json` Startup reads a canonical label list (lives in `config/labels.json` or baked into TS), iterates every repo the service watches, calls the Forgejo `create_label` API for missing names with the right color + description. Idempotent; operator's "connect a new repo to claude-hooks" flow becomes "add the repo + rebuild". ### B — on `repository.created` or `repository.connected` webhook React to the webhook Forgejo fires when a repo is added. Same reconcile logic, but event-driven. Narrower but more precise than A. ### C — `just labels-bootstrap <repo>` recipe Operator-triggered. Low effort, explicit. Matches today's `just containers-rebuild` ergonomics. Recommend **A + C**: auto-reconcile at startup for the always-on case, plus the manual recipe for ad-hoc repos that aren't in `agents.json`. B is nice-to-have; not worth the webhook wiring yet. ## Canonical label set (reference) From global CLAUDE.md + this repo's current labels: | Name | Color | Purpose | |---|---|---| | `area:agents` | `#0ea5e9` | Agent types, pool, config | | `area:dashboard` | `#8b5cf6` | Dashboard UI + observability | | `area:design` | `#ec4899` | UI/UX mockups → designer | | `area:design-review` | `#be185d` | Design review → design-reviewer | | `area:infra` | `#16a34a` | Deployment, isolation, containers | | `area:meta` | `#6b7280` | Tracking, scaffolding, setup | | `area:sessions` | `#2563eb` | Session store, SDK resume | | `area:webhook` | `#06b6d4` | Webhook routing + handlers | | `area:workdir` | `#ea580c` | Clone cache, worktrees, git identity | | `type:user-story` | `#ec4899` | User story | | `type:meta` | `#374151` | Tracking / decisions, not impl | | `type:bug` | _TBD_ | Bug | | `type:chore` | _TBD_ | Chore | The area:* set is repo-specific; the type:* set is repo-agnostic. Bootstrap should probably split these into two lists. ## Acceptance criteria ### Core - [ ] `config/labels.json` (or equivalent) defines the canonical labels as `{name, color, description, scope: "area" | "type"}`. - [ ] `src/labels.ts` exposes `reconcileLabels(repo)` that `POST /api/v1/repos/{repo}/labels` for any missing name and leaves existing labels alone (no color / description overwrites — idempotent). - [ ] `main.ts` startup calls `reconcileLabels` for every repo the service's webhook config knows about. - [ ] `just labels-bootstrap <repo>` recipe wraps the same call for ad-hoc use. ### Safety - [ ] Reconcile only creates missing labels; never deletes / renames existing ones (operators may have added customs). - [ ] Logs `[labels] created area:design-review on charles/claude-hooks (id=87)` on create, `[labels] already present: area:design` on skip. - [ ] If the repo doesn't exist or the token can't see it, log a warning and move on — don't crash. ### Tests - [ ] `labels.test.ts`: reconcile on a stubbed repo with partial label set adds the missing ones and skips existing ones. - [ ] `labels.test.ts`: reconcile tolerates 404 / 403 from the API. ### Docs - [ ] `CLAUDE.md` gains a short "Bootstrap labels on a new repo" section replacing the current "for new repos, set up labels and a milestone" line in the global CLAUDE.md. ## Out of scope - **Milestone creation** — tied to a release cadence per repo; stays manual. - **Label color / description updates** — no "fix up wrong color on existing label" logic. Operator hand-edits if they want. - **Multi-org support** — current reconcile only runs against `charles/*` repos; org-wide reconcile can wait until the service watches >1 org. ## References - #86 — the PR that introduced the `area:design-review` label dependency. - #70 — where the missing label manifested today. - `config/agents.json` — where the repo list lives today (implicitly, via tokens). - Global CLAUDE.md "Label & milestone scheme" section. ## Dependencies - **Blocked by:** nothing. - **Blocks:** frictionless onboarding of new repos to claude-hooks. - **Branch off:** `main`.
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#97
No description provided.