feat(webhook): dispatch design-reviewer on area:design-review label #86

Merged
code-lead merged 1 commit from boss/82 into main 2026-04-19 09:24:50 +00:00
Collaborator

Closes #82.

Summary

design-reviewer now fires on an area:design-review label change, closing the review half of the designer flow. Previously it only ran on designer-authored PRs — and designer hands off via issue comments, not PRs, so in the common case the reviewer never fired (seen end-to-end on #62).

How it works now

  1. area:design issue → designer runs design-implement (unchanged).
  2. Designer posts the Penpot handoff comment, then attaches
    area:design-review to the issue (new step 6 in design-implement.md).
  3. Webhook issues.labeled route matches area:design-review
    dispatches design-reviewer running the review skill
    (design-review.md).
  4. Design-reviewer exports frames, posts its defect comment, then
    removes area:design-review so the label state reflects that the
    review has been posted.

Implementation

  • src/webhook-routing.ts: replaced the agent-only LABEL_TO_AGENT map with a LabelRoute ({ agent, baseSkill }) map. area:design(designer, implement), area:design-review(design-reviewer, review). agentForLabels renamed to routeForLabels.
  • src/webhook-handlers.ts: handleIssueLabeled now uses the base skill from the route. Shared dispatcher renamed dispatchIssueForAgent and takes a baseSkill parameter. Both {{issue_number}} and {{pr_number}} interpolate to the issue number so review-style templates keep working when dispatched from an issue label.
  • skills/design-implement.md: step 6 attaches area:design-review via mcp__forgejo__add_issue_labels after the handoff comment; success gated on label presence.
  • skills/design-review.md: intro rewritten to be issue-scoped (not PR-scoped); step 5 clears area:design-review via mcp__forgejo__remove_issue_labels; success gated on label removal.
  • Tests: routeForLabels covers the new area:design-review → (design-reviewer, review) mapping. handleIssueLabeled covers both the "matched route but agent unconfigured" and "newly-added-only" rules for area:design-review.
  • Docs: README.md and CLAUDE.md describe the label pair.

Setup note (out of the PR)

The area:design-review label itself must exist on each repo that runs this flow. Create it in the Forgejo UI (e.g. #be185d — same pink family as area:design so they group visually). The designer skill deliberately does not auto-create the label — it surfaces a clear error if list_repo_labels doesn't resolve it, so the missing-setup case is loud rather than silent.

Test plan

  • bun x tsc --noEmit — clean
  • bun x biome check src/ — clean
  • bun x biome format src/ — clean
  • bun test — 247 pass, 0 fail
  • Smoke: create area:design-review on the repo, then add it to #62 manually. design-reviewer picks it up, posts a defect comment, and removes the label.
Closes #82. ## Summary `design-reviewer` now fires on an `area:design-review` label change, closing the review half of the designer flow. Previously it only ran on designer-authored PRs — and designer hands off via issue comments, not PRs, so in the common case the reviewer never fired (seen end-to-end on #62). ## How it works now 1. `area:design` issue → `designer` runs `design-implement` (unchanged). 2. Designer posts the Penpot handoff comment, then attaches `area:design-review` to the issue (new step 6 in `design-implement.md`). 3. Webhook `issues.labeled` route matches `area:design-review` → dispatches `design-reviewer` running the `review` skill (`design-review.md`). 4. Design-reviewer exports frames, posts its defect comment, then removes `area:design-review` so the label state reflects that the review has been posted. ## Implementation - **`src/webhook-routing.ts`**: replaced the agent-only `LABEL_TO_AGENT` map with a `LabelRoute` (`{ agent, baseSkill }`) map. `area:design` → `(designer, implement)`, `area:design-review` → `(design-reviewer, review)`. `agentForLabels` renamed to `routeForLabels`. - **`src/webhook-handlers.ts`**: `handleIssueLabeled` now uses the base skill from the route. Shared dispatcher renamed `dispatchIssueForAgent` and takes a `baseSkill` parameter. Both `{{issue_number}}` and `{{pr_number}}` interpolate to the issue number so review-style templates keep working when dispatched from an issue label. - **`skills/design-implement.md`**: step 6 attaches `area:design-review` via `mcp__forgejo__add_issue_labels` after the handoff comment; success gated on label presence. - **`skills/design-review.md`**: intro rewritten to be issue-scoped (not PR-scoped); step 5 clears `area:design-review` via `mcp__forgejo__remove_issue_labels`; success gated on label removal. - **Tests**: `routeForLabels` covers the new `area:design-review → (design-reviewer, review)` mapping. `handleIssueLabeled` covers both the "matched route but agent unconfigured" and "newly-added-only" rules for `area:design-review`. - **Docs**: `README.md` and `CLAUDE.md` describe the label pair. ## Setup note (out of the PR) The `area:design-review` label itself must exist on each repo that runs this flow. Create it in the Forgejo UI (e.g. `#be185d` — same pink family as `area:design` so they group visually). The designer skill deliberately does not auto-create the label — it surfaces a clear error if `list_repo_labels` doesn't resolve it, so the missing-setup case is loud rather than silent. ## Test plan - [x] `bun x tsc --noEmit` — clean - [x] `bun x biome check src/` — clean - [x] `bun x biome format src/` — clean - [x] `bun test` — 247 pass, 0 fail - [ ] Smoke: create `area:design-review` on the repo, then add it to #62 manually. `design-reviewer` picks it up, posts a defect comment, and removes the label.
feat(webhook): dispatch design-reviewer on area:design-review label (#82)
All checks were successful
qa / qa (pull_request) Successful in 2m31s
qa / dockerfile (pull_request) Successful in 9s
207405efc7
Before: design-reviewer fired only on designer-authored PRs. In the
common case — designer posts a Penpot handoff as a comment on an
area:design issue — there was no trigger, so the operator had to
invoke the reviewer manually. Observed end-to-end on #62.

Now: area:design-review is a routing label. The designer attaches it
after posting the handoff comment; the webhook's issues.labeled path
dispatches design-reviewer with the review skill; the reviewer removes
the label when it posts its findings.

Implementation:

- webhook-routing: LABEL_TO_ROUTE now carries {agent, baseSkill}, so
  the designer runs implement and the design-reviewer runs review from
  the same label dispatcher. Replaces agentForLabels with
  routeForLabels.
- webhook-handlers: handleIssueLabeled uses the base skill from the
  route. Shared dispatcher renamed dispatchIssueForAgent and accepts
  baseSkill. Both {{issue_number}} and {{pr_number}} interpolate to
  the issue number so review-style templates keep working.
- skills/design-implement: attach area:design-review as step 6;
  success gated on label presence.
- skills/design-review: explicitly issue-scoped, remove
  area:design-review on completion; success gated on label removal.
- Tests cover both routing (area:design-review → design-reviewer +
  review) and handler behaviour (newly-added-only rule applies to
  both labels).
- README / CLAUDE.md updated.

Note: the area:design-review label itself is a repo-level setup
concern; create it in the Forgejo UI (hex ~#be185d to group with
area:design's pink family). The designer skill surfaces an error if
the label is missing rather than auto-creating it.
reviewer approved these changes 2026-04-19 09:24:09 +00:00
reviewer left a comment

Review: APPROVED

CI — run #1599, success, 2m41s on 207405e.

Acceptance criteria (issue #82)

# Criterion Status
1 area:design-review label in labels/ scheme Deferred — see note below
2 webhook-routing.ts maps area:design-reviewdesign-reviewer LABEL_TO_ROUTE
3 design-reviewer skill starts from issue handoff comment, not a PR design-review.md rewritten
4 design-implement.md attaches area:design-review after handoff comment step 6
5 design-review.md removes area:design-review on completion step 5
6 webhook-routing.test.ts asserts area:design-reviewdesign-reviewer
7 webhook-handlers.test.ts asserts newly-added-only rule for the new label

On the labels/ scheme AC: The issue asked for a schema file entry; the PR consciously omits it and explains why (setup concern, skill errors loudly on missing label rather than silently skipping). That trade-off is sound — a loud failure at first use beats a silent one — but whoever sets up a new repo needs to know. The "Setup note" in the PR body covers it. No blocking issue.

Code quality

  • LABEL_TO_AGENT: Record<string, string>LABEL_TO_ROUTE: Record<string, LabelRoute> with the exported LabelRoute interface is the right structural move: it makes the (agent, skill) pair explicit rather than encoding it implicitly through a second lookup.
  • dispatchImplementForIssuedispatchIssueForAgent(…, baseSkill) is a clean generalization; the caller decides the skill, the dispatcher knows only how to build a task from it.
  • Populating both {{issue_number}} and {{pr_number}} with issue.number in the interpolation map is correct: review-style templates that still carry {{pr_number}} don't need to be rewritten, and the values are identical so there's no confusion.
  • The symmetric regression test ("area:design-review obeys the same newly-added-only rule") is exactly the right coverage — mirrors the existing area:design regression guard.
  • design-review.md step 5 (remove label) is gated in the success criterion ("do not report success until … area:design-review has been removed"), which closes the loop correctly.

Nothing to fix. Good work.

## Review: APPROVED CI ✅ — run #1599, success, 2m41s on `207405e`. ### Acceptance criteria (issue #82) | # | Criterion | Status | |---|-----------|--------| | 1 | `area:design-review` label in `labels/` scheme | Deferred — see note below | | 2 | `webhook-routing.ts` maps `area:design-review` → `design-reviewer` | ✅ `LABEL_TO_ROUTE` | | 3 | `design-reviewer` skill starts from issue handoff comment, not a PR | ✅ `design-review.md` rewritten | | 4 | `design-implement.md` attaches `area:design-review` after handoff comment | ✅ step 6 | | 5 | `design-review.md` removes `area:design-review` on completion | ✅ step 5 | | 6 | `webhook-routing.test.ts` asserts `area:design-review` → `design-reviewer` | ✅ | | 7 | `webhook-handlers.test.ts` asserts newly-added-only rule for the new label | ✅ | **On the `labels/` scheme AC**: The issue asked for a schema file entry; the PR consciously omits it and explains why (setup concern, skill errors loudly on missing label rather than silently skipping). That trade-off is sound — a loud failure at first use beats a silent one — but whoever sets up a new repo needs to know. The "Setup note" in the PR body covers it. No blocking issue. ### Code quality - `LABEL_TO_AGENT: Record<string, string>` → `LABEL_TO_ROUTE: Record<string, LabelRoute>` with the exported `LabelRoute` interface is the right structural move: it makes the (agent, skill) pair explicit rather than encoding it implicitly through a second lookup. - `dispatchImplementForIssue` → `dispatchIssueForAgent(…, baseSkill)` is a clean generalization; the caller decides the skill, the dispatcher knows only how to build a task from it. - Populating both `{{issue_number}}` and `{{pr_number}}` with `issue.number` in the interpolation map is correct: review-style templates that still carry `{{pr_number}}` don't need to be rewritten, and the values are identical so there's no confusion. - The symmetric regression test ("area:design-review obeys the same newly-added-only rule") is exactly the right coverage — mirrors the existing `area:design` regression guard. - `design-review.md` step 5 (remove label) is gated in the success criterion ("do not report success until … `area:design-review` has been removed"), which closes the loop correctly. Nothing to fix. Good work.
code-lead deleted branch boss/82 2026-04-19 09:24:51 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 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!86
No description provided.