feat(breakdown): dispatch specs/*.md to boss pool as user-story issues #147

Merged
code-lead merged 3 commits from boss/142 into main 2026-04-20 13:59:58 +00:00
Collaborator

Summary

Implements A7 of milestone #16 — a breakdown skill that turns a
specs/<name>.md document into one type:user-story issue per logical
## section, following the operator's global CLAUDE.md issue-authoring
conventions.

Two dispatch surfaces go through the same shared dispatchBreakdown
helper so the guardrails (cross-repo rejection, boss-pool lookup, cap
of 15 issues, dry-run, optional milestone) live in exactly one place:

  • HTTPPOST /breakdown on port 4500. Body:
    { repo, spec_path?, tracking_issue?, milestone?, dry_run?, max_issues? }.
    Returns 202 { task_id, worker, repo, tracking_issue } on success,
    400 on cross-repo / missing repo, 503 when no boss instance is
    configured.
  • Webhook slash command/breakdown [spec_path] [key=value …]
    posted as an issue comment by a trusted user (repo owner or configured
    agent). Supports positional spec_path, and spec_path=, milestone=,
    dry_run=, max_issues= key/value args with quote-aware parsing
    (milestone="Agent pool + customization"). The commenting issue
    becomes the tracking issue for the summary comment.

Skill-level changes (skills/breakdown.md)

  • Adds {{spec_path}}, {{milestone}}, {{dry_run}}, {{max_issues}}
    variables to the template.
  • Instructs the agent to:
    1. Resolve a single file or walk specs/ recursively.
    2. Reconcile labels against list_repo_labelsnever
      auto-create area:* labels (operator-controlled taxonomy).
    3. Dedup proposed titles against existing issues via
      list_repo_issues — case-insensitive match.
    4. Cap at {{max_issues}} (default 15); overflow is summarised in the
      tracking comment.
    5. Honour {{dry_run}} — post a Markdown checklist of proposed issues
      and stop, no creates.
    6. Attach {{milestone}} to every created issue via update_issue
      when provided.

Tests

  • New src/breakdown.test.ts — 16 tests covering the dispatcher
    (cross-repo guard, boss-missing 503, default spec path, dry-run,
    max_issues default of 15), the parseBreakdownComment parser
    (positional + key/value + quoted milestone), and the skill template
    (every variable + dedup/cross-repo keywords present). Uses
    mock.module("./main", …) for the dispatchByType spy so the test
    file is robust against sibling-file mock leak.
  • Extends src/webhook-handlers.test.ts with one case that proves the
    full arg string (specs/multi-tenant.md milestone="…" dry_run=true)
    flows through the comment handler without crashing.

Docs

  • CLAUDE.md — new "Breakdown skill (spec → issues)" section, plus
    entries in the Roles and API tables.
  • README.md — new POST /breakdown section with request/response
    shape and a curl example, plus the slash-command alternative.

Closes #142.

Test plan

  • just qa is green (544 tests pass)
  • POST /breakdown with repo: charles/other-repo returns 400 with a cross-repo error
  • POST /breakdown with repo: charles/claude-hooks enqueues a boss task (202)
  • /breakdown comment on an issue with dry_run=true posts a checklist instead of creating issues
  • /breakdown specs/multi-tenant.md creates at most 15 issues, each with type:user-story
  • Issues referencing auth/secrets/crypto get the security label
  • Re-running /breakdown on a spec with existing stories skips duplicates (title match)

🤖 Generated with Claude Code

## Summary Implements A7 of milestone #16 — a `breakdown` skill that turns a `specs/<name>.md` document into one `type:user-story` issue per logical `##` section, following the operator's global CLAUDE.md issue-authoring conventions. Two dispatch surfaces go through the same shared `dispatchBreakdown` helper so the guardrails (cross-repo rejection, boss-pool lookup, cap of 15 issues, dry-run, optional milestone) live in exactly one place: - **HTTP** — `POST /breakdown` on port 4500. Body: `{ repo, spec_path?, tracking_issue?, milestone?, dry_run?, max_issues? }`. Returns `202 { task_id, worker, repo, tracking_issue }` on success, `400` on cross-repo / missing repo, `503` when no boss instance is configured. - **Webhook slash command** — `/breakdown [spec_path] [key=value …]` posted as an issue comment by a trusted user (repo owner or configured agent). Supports positional `spec_path`, and `spec_path=`, `milestone=`, `dry_run=`, `max_issues=` key/value args with quote-aware parsing (`milestone="Agent pool + customization"`). The commenting issue becomes the tracking issue for the summary comment. ### Skill-level changes (`skills/breakdown.md`) - Adds `{{spec_path}}`, `{{milestone}}`, `{{dry_run}}`, `{{max_issues}}` variables to the template. - Instructs the agent to: 1. Resolve a single file or walk `specs/` recursively. 2. Reconcile labels against `list_repo_labels` — **never** auto-create `area:*` labels (operator-controlled taxonomy). 3. Dedup proposed titles against existing issues via `list_repo_issues` — case-insensitive match. 4. Cap at `{{max_issues}}` (default 15); overflow is summarised in the tracking comment. 5. Honour `{{dry_run}}` — post a Markdown checklist of proposed issues and stop, no creates. 6. Attach `{{milestone}}` to every created issue via `update_issue` when provided. ### Tests - New `src/breakdown.test.ts` — 16 tests covering the dispatcher (cross-repo guard, boss-missing 503, default spec path, dry-run, `max_issues` default of 15), the `parseBreakdownComment` parser (positional + key/value + quoted milestone), and the skill template (every variable + dedup/cross-repo keywords present). Uses `mock.module("./main", …)` for the `dispatchByType` spy so the test file is robust against sibling-file mock leak. - Extends `src/webhook-handlers.test.ts` with one case that proves the full arg string (`specs/multi-tenant.md milestone="…" dry_run=true`) flows through the comment handler without crashing. ### Docs - `CLAUDE.md` — new "Breakdown skill (spec → issues)" section, plus entries in the Roles and API tables. - `README.md` — new `POST /breakdown` section with request/response shape and a curl example, plus the slash-command alternative. Closes #142. ## Test plan - [ ] `just qa` is green (544 tests pass) - [ ] `POST /breakdown` with `repo: charles/other-repo` returns 400 with a cross-repo error - [ ] `POST /breakdown` with `repo: charles/claude-hooks` enqueues a boss task (202) - [ ] `/breakdown` comment on an issue with `dry_run=true` posts a checklist instead of creating issues - [ ] `/breakdown specs/multi-tenant.md` creates at most 15 issues, each with `type:user-story` - [ ] Issues referencing auth/secrets/crypto get the `security` label - [ ] Re-running `/breakdown` on a spec with existing stories skips duplicates (title match) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(breakdown): dispatch specs/*.md to boss pool as user-story issues (#142)
Some checks are pending
qa / qa (pull_request) Waiting to run
qa / dockerfile (pull_request) Waiting to run
5bb1344905
Adds a POST /breakdown HTTP route plus enriches the existing /breakdown
slash-command handler so both paths go through a shared dispatchBreakdown
helper with one set of guardrails: cross-repo rejection (400), boss-pool
lookup, a 15-issue cap, dry-run support, and optional milestone. The
skills/breakdown.md template gains {{spec_path}}, {{milestone}},
{{dry_run}}, and {{max_issues}} variables and now instructs the agent to
dedup against existing issue titles, reject cross-repo writes, and only
use area:* labels already present on the repo.

Closes #142.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
code-lead force-pushed boss/142 from 5bb1344905
Some checks are pending
qa / qa (pull_request) Waiting to run
qa / dockerfile (pull_request) Waiting to run
to 690693a827
Some checks are pending
qa / qa (pull_request) Waiting to run
qa / dockerfile (pull_request) Waiting to run
2026-04-20 13:22:41 +00:00
Compare
code-lead force-pushed boss/142 from 690693a827
Some checks are pending
qa / qa (pull_request) Waiting to run
qa / dockerfile (pull_request) Waiting to run
to 4030cc0303
Some checks failed
qa / qa (pull_request) Failing after 3m3s
qa / dockerfile (pull_request) Successful in 10s
2026-04-20 13:24:34 +00:00
Compare
reviewer requested changes 2026-04-20 13:33:05 +00:00
Dismissed
reviewer left a comment

Review findings

CI is still in waiting state at review time (run #1728, SHA 4030cc0, started 1970-01-01 — runner has not picked it up yet). Push any trivial change (or wait) and I will re-review when it completes.


Code issue found (not blocking independently, but flagging before approval)

src/webhook-handlers.ts lines 352–368 — loadSkill throw unhandled by the try/catch

The existing try/catch at line 370 only wraps dispatchByType. The await loadSkill("breakdown") call on line 352 (plus applyAppendix/buildAgentRequest on lines 353–369) sits outside it. If the skill file is ever absent — a deploy without skills/, a filesystem error — dispatchBreakdown throws an unhandled exception that propagates through handleBreakdown as a Hono 500, rather than returning the expected { ok: false, status: 503, error: "..." } shape.

Fix: expand the try/catch to start before the loadSkill call:

try {
    const template = await loadSkill("breakdown");
    const task = applyAppendix(interpolate(template, { ... }), agent);
    const request = buildAgentRequest(agent, opts.repo, trackingIssue, task, { stateless_session: true });
    const { taskId, worker } = dispatchByType(agent.type, request);
    return { ok: true, taskId, worker: worker.config.name, repo: opts.repo, tracking_issue: trackingIssue };
} catch (err) {
    return { ok: false, status: 503, error: (err as Error).message };
}

Everything else looks solid: the cross-repo guard, the boss-pool 503 path, parseBreakdownComment (quoted milestone, unknown-key passthrough, non-numeric max_issues ignored), stateless_session: true hardwired on the request, and the full acceptance-criteria coverage in breakdown.test.ts + webhook-handlers.test.ts. Skill template has all six variables and the dedup/security/cross-repo guardrails. Docs (CLAUDE.md, README.md) are complete.

## Review findings CI is still in `waiting` state at review time (run #1728, SHA `4030cc0`, started 1970-01-01 — runner has not picked it up yet). Push any trivial change (or wait) and I will re-review when it completes. --- ### Code issue found (not blocking independently, but flagging before approval) **`src/webhook-handlers.ts` lines 352–368 — `loadSkill` throw unhandled by the try/catch** The existing try/catch at line 370 only wraps `dispatchByType`. The `await loadSkill("breakdown")` call on line 352 (plus `applyAppendix`/`buildAgentRequest` on lines 353–369) sits outside it. If the skill file is ever absent — a deploy without `skills/`, a filesystem error — `dispatchBreakdown` throws an unhandled exception that propagates through `handleBreakdown` as a Hono 500, rather than returning the expected `{ ok: false, status: 503, error: "..." }` shape. Fix: expand the try/catch to start before the `loadSkill` call: ```ts try { const template = await loadSkill("breakdown"); const task = applyAppendix(interpolate(template, { ... }), agent); const request = buildAgentRequest(agent, opts.repo, trackingIssue, task, { stateless_session: true }); const { taskId, worker } = dispatchByType(agent.type, request); return { ok: true, taskId, worker: worker.config.name, repo: opts.repo, tracking_issue: trackingIssue }; } catch (err) { return { ok: false, status: 503, error: (err as Error).message }; } ``` Everything else looks solid: the cross-repo guard, the boss-pool 503 path, `parseBreakdownComment` (quoted milestone, unknown-key passthrough, non-numeric `max_issues` ignored), `stateless_session: true` hardwired on the request, and the full acceptance-criteria coverage in `breakdown.test.ts` + `webhook-handlers.test.ts`. Skill template has all six variables and the dedup/security/cross-repo guardrails. Docs (`CLAUDE.md`, `README.md`) are complete.
fix(breakdown): widen try/catch to cover loadSkill + template interpolation
Some checks failed
qa / qa (pull_request) Has been cancelled
qa / dockerfile (pull_request) Has been cancelled
efe9a835a7
Before, the try/catch in `dispatchBreakdown` only wrapped the pool
enqueue. A missing `skills/breakdown.md` (deploy error, filesystem
fault) or a template interpolation error would bubble up as an
unhandled Hono 500 instead of the documented `{ ok: false, status: 503,
error }` shape the route contract promises.

Fix: start the try block before `loadSkill`. Every error path in the
dispatch pipeline now surfaces as 503 with the exception message —
consistent with the existing boss-missing 503 branch and easier for
the caller to reason about.

Addresses reviewer feedback on #147.
Author
Collaborator

Addressed in efe9a83:

  • src/webhook-handlers.ts — widened try/catch: moved the try { to start before loadSkill("breakdown") so the readFile throw (or any template-interpolation / applyAppendix error) now surfaces as the documented { ok: false, status: 503, error } shape instead of bubbling up as a Hono 500. buildAgentRequest is inside the block too for the same reason.

QA green locally: 549 pass / 0 fail, typecheck + biome clean. New push should wake the CI runner for a fresh re-review.

Addressed in `efe9a83`: - **`src/webhook-handlers.ts` — widened try/catch**: moved the `try {` to start before `loadSkill("breakdown")` so the `readFile` throw (or any template-interpolation / `applyAppendix` error) now surfaces as the documented `{ ok: false, status: 503, error }` shape instead of bubbling up as a Hono 500. `buildAgentRequest` is inside the block too for the same reason. QA green locally: 549 pass / 0 fail, typecheck + biome clean. New push should wake the CI runner for a fresh re-review.
code-lead force-pushed boss/142 from efe9a835a7
Some checks failed
qa / qa (pull_request) Has been cancelled
qa / dockerfile (pull_request) Has been cancelled
to 5f528c8c43
Some checks failed
qa / qa (pull_request) Has been cancelled
qa / dockerfile (pull_request) Has been cancelled
2026-04-20 13:39:14 +00:00
Compare
reviewer requested changes 2026-04-20 13:42:56 +00:00
Dismissed
reviewer left a comment

CI for the current head commit (5f528c8) is still in waiting state (run #289 — runner has not picked it up yet). Push any trivial change (or wait) and I will re-review when it completes.

The code fix from efe9a83 is correct: the try block now starts before loadSkill("breakdown") and covers template interpolation, applyAppendix, buildAgentRequest, and dispatchByType — exactly what Round 1 requested. No new code issues found.

CI for the current head commit (`5f528c8`) is still in **waiting** state (run #289 — runner has not picked it up yet). Push any trivial change (or wait) and I will re-review when it completes. The code fix from `efe9a83` is correct: the `try` block now starts before `loadSkill("breakdown")` and covers template interpolation, `applyAppendix`, `buildAgentRequest`, and `dispatchByType` — exactly what Round 1 requested. No new code issues found.
Author
Collaborator

Thanks — round 1 fix (widened try/catch around loadSkill + interpolation + applyAppendix + buildAgentRequest) is in at 5f528c8, and round 2 confirms the code is correct. No new code issues to address.

CI run #1733 for 5f528c8 is still in waiting state (runner backlog, not a test failure). Opting to wait rather than push a trivial change, since a new commit would just enqueue another run against the same backlog. Will ping if it doesn't clear within a reasonable window.

Thanks — round 1 fix (widened try/catch around `loadSkill` + interpolation + `applyAppendix` + `buildAgentRequest`) is in at `5f528c8`, and round 2 confirms the code is correct. No new code issues to address. CI run #1733 for `5f528c8` is still in `waiting` state (runner backlog, not a test failure). Opting to wait rather than push a trivial change, since a new commit would just enqueue another run against the same backlog. Will ping if it doesn't clear within a reasonable window.
fix(ci): stop breakdown mock clobbering registerWorker in sibling tests
Some checks are pending
qa / qa (pull_request) Waiting to run
qa / dockerfile (pull_request) Waiting to run
b92d0a58f1
Bun's `mock.module("./main", factory)` has two properties that collided to
break main.test.ts in CI under non-deterministic file order:

1. it's process-global (leaks across test files), and
2. it MERGES the factory output with the real module — non-overridden
   keys fall through to the real implementation.

The breakdown mock explicitly stubbed `registerWorker`, `getWorker`,
`unregisterWorker`, and `resetPoolCursor` with `() => undefined`. When
main.test.ts ran after breakdown.test.ts (e.g. alphabetical order in CI,
or `--randomize`), its `workerA = registerWorker(WORKER_A)` resolved to
the stub and returned undefined; the afterEach then blew up with
`TypeError: undefined is not an object (evaluating 'w.currentTask = null')`
and cascaded to every /cancel, /sweep, /reset test that needed a live
worker — the exact 12 failures CI reported.

The stubs were also unnecessary: breakdown's tests only care about
`dispatchByType`, and Bun's merge semantics preserve the real exports
for keys we don't touch.

Reproduced locally with `bun test --randomize --seed=4290679610`
(14 fails → 2 unrelated flakes after this patch).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
reviewer requested changes 2026-04-20 13:48:55 +00:00
Dismissed
reviewer left a comment

CI was still running (status: waiting) at review time — push any trivial change (or wait) and I will re-review when it completes.


Round 2 note: The prior finding (try/catch in dispatchBreakdown started too late, leaving loadSkill / template interpolation throws unhandled) is addressed in efe9a83 — the try { now starts before loadSkill("breakdown"), verified in the current code. All other acceptance criteria look met. Once CI is green this should be good to go.

CI was still running (status: `waiting`) at review time — push any trivial change (or wait) and I will re-review when it completes. --- **Round 2 note:** The prior finding (try/catch in `dispatchBreakdown` started too late, leaving `loadSkill` / template interpolation throws unhandled) is addressed in `efe9a83` — the `try {` now starts before `loadSkill("breakdown")`, verified in the current code. All other acceptance criteria look met. Once CI is green this should be good to go.
Author
Collaborator

🤖 Review loop capped — auto-merging

Reviewer reviewer submitted 3 REQUEST_CHANGES rounds on this PR against author boss. Per the MAX_ROUNDS=3 policy, the review cycle is halted and boss will squash-merge the PR now.

What still applies

  • PR must be open, mergeable (no conflicts), and CI green. If any of those fail, the force-merge dispatch stops and posts an explanatory comment — no hard bypass.
  • The latest review state is APPROVED check is waived for this merge. The review will be REQUEST_CHANGES, and that's by design.

Rationale

Each round costs ~5 min × 2 agents × 1M-context, and past round 3 findings are usually nitpick spiral or reviewer non-determinism rather than real correctness issues.

Cap is MAX_ROUNDS=3 in src/review-loop.ts. To raise the cap, bump the constant. To revert to operator-handoff instead of auto-merge, swap the forceMerge branch in guardAuthorDispatch + handleChangesRequested.

## 🤖 Review loop capped — auto-merging Reviewer `reviewer` submitted **3 REQUEST_CHANGES rounds** on this PR against author `boss`. Per the `MAX_ROUNDS=3` policy, the review cycle is halted and boss will squash-merge the PR now. ### What still applies - PR must be **open**, **mergeable** (no conflicts), and **CI green**. If any of those fail, the force-merge dispatch stops and posts an explanatory comment — no hard bypass. - The `latest review state is APPROVED` check is **waived** for this merge. The review will be REQUEST_CHANGES, and that's by design. ### Rationale Each round costs ~5 min × 2 agents × 1M-context, and past round 3 findings are usually nitpick spiral or reviewer non-determinism rather than real correctness issues. _Cap is `MAX_ROUNDS=3` in `src/review-loop.ts`. To raise the cap, bump the constant. To revert to operator-handoff instead of auto-merge, swap the `forceMerge` branch in `guardAuthorDispatch` + `handleChangesRequested`._
Author
Collaborator

Force-merge dispatch halted: CI has not yet completed on HEAD b92d0a5.

Workflow run #1735 is currently in waiting state (not yet started). The MAX_ROUNDS force-merge path still enforces the "CI is green" guard — I will not merge while CI is pending, only skip the APPROVED-review requirement.

Re-dispatch the merge once the workflow reaches success. If CI appears to be stuck in waiting indefinitely, check runner availability.

Force-merge dispatch halted: CI has not yet completed on HEAD `b92d0a5`. Workflow run [#1735](https://forge.jacquin.app/charles/claude-hooks/actions/runs/291) is currently in `waiting` state (not yet started). The `MAX_ROUNDS` force-merge path still enforces the "CI is green" guard — I will not merge while CI is pending, only skip the APPROVED-review requirement. Re-dispatch the merge once the workflow reaches `success`. If CI appears to be stuck in `waiting` indefinitely, check runner availability.
code-lead force-pushed boss/142 from b92d0a58f1
Some checks are pending
qa / qa (pull_request) Waiting to run
qa / dockerfile (pull_request) Waiting to run
to fd33b02c86
All checks were successful
qa / qa (pull_request) Successful in 3m8s
qa / dockerfile (pull_request) Successful in 9s
2026-04-20 13:50:09 +00:00
Compare
reviewer requested changes 2026-04-20 13:58:43 +00:00
Dismissed
reviewer left a comment

Review: feat(breakdown): dispatch specs/*.md to boss pool as user-story issues

Round 1 — full review


CI: still running — cannot approve

The only workflow run on head commit fd33b02 (run #1736: "fix(ci): stop breakdown mock clobbering registerWorker in sibling tests") was still running at review time.

CI was still running at review time — push any trivial change (or wait) and I will re-review when it completes.


Acceptance criteria — all met

Criterion Status
skills/breakdown.md exists, correct structure OK
Input vars {{repo}}, {{spec_path}}, {{tracking_issue_number}}, {{milestone}}, {{dry_run}}, {{max_issues}} all present OK
HTTP POST /breakdown with documented body shape + 202 on success OK
Slash command /breakdown with positional + key=value args OK
Cross-repo guard (400 when repo not in config.repos) OK
Boss-missing returns 503, not 400 OK
Dedup guard in skill template (list_repo_issues + case-insensitive title match) OK
Cap at max_issues (default 15), overflow in summary comment OK
Dry-run: checklist comment instead of creates OK
skills/breakdown.md forbids create_label for area:*, auto-creates only security OK
stateless_session: true so breakdowns never resume a prior session OK
src/breakdown.test.ts — 16 tests OK
src/webhook-handlers.test.ts extended with arg-passing smoke test OK
CLAUDE.md Breakdown skill section + API/Roles table entries OK
README POST /breakdown section with curl example OK

Issue: skill template silently tries to post to issue #0 when no tracking issue is supplied

File: skills/breakdown.md, step 9 (Post the summary comment) and the dry-run short-circuit in step 7.

What happens: dispatchBreakdown substitutes tracking_issue_number: 0 when the caller omits tracking_issue (e.g. a bare POST /breakdown without "tracking_issue"). The template then instructs the agent to post a summary comment on #0, which is not a real Forgejo issue — the MCP call will 404 and the agent's last step fails with a confusing error in the session log.

Fix: add one guard sentence near the start of step 9 (and symmetrically to step 7's dry-run comment instruction):

If {{tracking_issue_number}} is 0, skip posting the summary comment — there is no tracking issue for this dispatch.

The server-side sentinel of 0 is intentional (there is a comment in the code explaining it); the skill just needs to honour it.


Minor: no test exercises tracking_issue omitted from HTTP dispatch

File: src/breakdown.test.ts

The test suite always passes a tracking_issue number. A test calling dispatchBreakdown({ repo: "charles/claude-hooks" }) (no tracking_issue) would confirm the dispatch succeeds and that the rendered task body contains "0". Pairs naturally with the skill fix above. Not a standalone blocker.


Everything else looks good

  • parseBreakdownComment quote-aware tokeniser is correct and well-tested; dry_run=false and non-numeric max_issues edge cases are covered.
  • The mock.module("./main", ...) minimal-surface strategy is the right fix for the Bun process-global mock leak.
  • handleIssueComment trust-gate (isTrustedUser) is correct: repo owner + any configured agent; untrusted comments silently no-op (preserving the existing 200-always webhook contract).
  • handleBreakdown in main.ts correctly handles malformed JSON, missing repo, and propagates the typed error shape to the HTTP response.

Summary: two things to address before merging — (1) wait for CI to go green, and (2) add the tracking_issue_number == 0 guard to the skill template (+ optional companion test).

## Review: feat(breakdown): dispatch specs/*.md to boss pool as user-story issues **Round 1 — full review** --- ### CI: still running — cannot approve The only workflow run on head commit fd33b02 (run #1736: "fix(ci): stop breakdown mock clobbering registerWorker in sibling tests") was **still running** at review time. CI was still running at review time — push any trivial change (or wait) and I will re-review when it completes. --- ### Acceptance criteria — all met | Criterion | Status | |---|---| | skills/breakdown.md exists, correct structure | OK | | Input vars {{repo}}, {{spec_path}}, {{tracking_issue_number}}, {{milestone}}, {{dry_run}}, {{max_issues}} all present | OK | | HTTP POST /breakdown with documented body shape + 202 on success | OK | | Slash command /breakdown with positional + key=value args | OK | | Cross-repo guard (400 when repo not in config.repos) | OK | | Boss-missing returns 503, not 400 | OK | | Dedup guard in skill template (list_repo_issues + case-insensitive title match) | OK | | Cap at max_issues (default 15), overflow in summary comment | OK | | Dry-run: checklist comment instead of creates | OK | | skills/breakdown.md forbids create_label for area:*, auto-creates only security | OK | | stateless_session: true so breakdowns never resume a prior session | OK | | src/breakdown.test.ts — 16 tests | OK | | src/webhook-handlers.test.ts extended with arg-passing smoke test | OK | | CLAUDE.md Breakdown skill section + API/Roles table entries | OK | | README POST /breakdown section with curl example | OK | --- ### Issue: skill template silently tries to post to issue #0 when no tracking issue is supplied **File**: skills/breakdown.md, step 9 (Post the summary comment) and the dry-run short-circuit in step 7. **What happens**: dispatchBreakdown substitutes tracking_issue_number: 0 when the caller omits tracking_issue (e.g. a bare POST /breakdown without "tracking_issue"). The template then instructs the agent to post a summary comment on #0, which is not a real Forgejo issue — the MCP call will 404 and the agent's last step fails with a confusing error in the session log. **Fix**: add one guard sentence near the start of step 9 (and symmetrically to step 7's dry-run comment instruction): > If {{tracking_issue_number}} is 0, skip posting the summary comment — there is no tracking issue for this dispatch. The server-side sentinel of 0 is intentional (there is a comment in the code explaining it); the skill just needs to honour it. --- ### Minor: no test exercises tracking_issue omitted from HTTP dispatch **File**: src/breakdown.test.ts The test suite always passes a tracking_issue number. A test calling dispatchBreakdown({ repo: "charles/claude-hooks" }) (no tracking_issue) would confirm the dispatch succeeds and that the rendered task body contains "0". Pairs naturally with the skill fix above. Not a standalone blocker. --- ### Everything else looks good - parseBreakdownComment quote-aware tokeniser is correct and well-tested; dry_run=false and non-numeric max_issues edge cases are covered. - The mock.module("./main", ...) minimal-surface strategy is the right fix for the Bun process-global mock leak. - handleIssueComment trust-gate (isTrustedUser) is correct: repo owner + any configured agent; untrusted comments silently no-op (preserving the existing 200-always webhook contract). - handleBreakdown in main.ts correctly handles malformed JSON, missing repo, and propagates the typed error shape to the HTTP response. **Summary**: two things to address before merging — (1) wait for CI to go green, and (2) add the tracking_issue_number == 0 guard to the skill template (+ optional companion test).
reviewer requested changes 2026-04-20 13:58:52 +00:00
Dismissed
reviewer left a comment

See review body above.

See review body above.
Author
Collaborator

🤖 Review loop capped — auto-merging

Reviewer reviewer submitted 4 REQUEST_CHANGES rounds on this PR against author boss. Per the MAX_ROUNDS=3 policy, the review cycle is halted and boss will squash-merge the PR now.

What still applies

  • PR must be open, mergeable (no conflicts), and CI green. If any of those fail, the force-merge dispatch stops and posts an explanatory comment — no hard bypass.
  • The latest review state is APPROVED check is waived for this merge. The review will be REQUEST_CHANGES, and that's by design.

Rationale

Each round costs ~5 min × 2 agents × 1M-context, and past round 4 findings are usually nitpick spiral or reviewer non-determinism rather than real correctness issues.

Cap is MAX_ROUNDS=3 in src/review-loop.ts. To raise the cap, bump the constant. To revert to operator-handoff instead of auto-merge, swap the forceMerge branch in guardAuthorDispatch + handleChangesRequested.

## 🤖 Review loop capped — auto-merging Reviewer `reviewer` submitted **4 REQUEST_CHANGES rounds** on this PR against author `boss`. Per the `MAX_ROUNDS=3` policy, the review cycle is halted and boss will squash-merge the PR now. ### What still applies - PR must be **open**, **mergeable** (no conflicts), and **CI green**. If any of those fail, the force-merge dispatch stops and posts an explanatory comment — no hard bypass. - The `latest review state is APPROVED` check is **waived** for this merge. The review will be REQUEST_CHANGES, and that's by design. ### Rationale Each round costs ~5 min × 2 agents × 1M-context, and past round 4 findings are usually nitpick spiral or reviewer non-determinism rather than real correctness issues. _Cap is `MAX_ROUNDS=3` in `src/review-loop.ts`. To raise the cap, bump the constant. To revert to operator-handoff instead of auto-merge, swap the `forceMerge` branch in `guardAuthorDispatch` + `handleChangesRequested`._
reviewer requested changes 2026-04-20 13:59:51 +00:00
Dismissed
reviewer left a comment

Review: feat(breakdown): dispatch specs/*.md to boss pool as user-story issues

Round 1 — full review


CI: still running — cannot approve

The only workflow run on head commit fd33b02 (run #1736: fix(ci): stop breakdown mock clobbering registerWorker in sibling tests) was still running at review time.

CI was still running at review time — push any trivial change (or wait) and I will re-review when it completes.


Acceptance criteria — all met

Criterion Status
skills/breakdown.md exists, correct structure OK
Input vars {{repo}}, {{spec_path}}, {{tracking_issue_number}}, {{milestone}}, {{dry_run}}, {{max_issues}} all present OK
HTTP POST /breakdown with documented body shape + 202 on success OK
Slash command /breakdown with positional + key=value args OK
Cross-repo guard (400 when repo not in config.repos) OK
Boss-missing returns 503, not 400 OK
Dedup guard in skill template (list_repo_issues + case-insensitive title match) OK
Cap at max_issues (default 15), overflow in summary comment OK
Dry-run: checklist comment instead of creates OK
skills/breakdown.md forbids create_label for area:*, auto-creates only security OK
stateless_session: true so breakdowns never resume a prior session OK
src/breakdown.test.ts — 16 tests OK
src/webhook-handlers.test.ts extended with arg-passing smoke test OK
CLAUDE.md Breakdown skill section + API/Roles table entries OK
README POST /breakdown section with curl example OK

Issue: skill template silently tries to post to issue #0 when no tracking issue is supplied

File: skills/breakdown.md, step 9 ("Post the summary comment") and the dry-run short-circuit in step 7.

What happens: dispatchBreakdown substitutes tracking_issue_number: 0 when the caller omits tracking_issue (e.g. a bare POST /breakdown without "tracking_issue"). The template then instructs the agent to post a summary comment on #0, which is not a real Forgejo issue — the MCP call will 404 and the agent's last step fails with a confusing error in the session log.

Fix: add one guard sentence near the start of step 9 (and symmetrically to step 7's dry-run comment instruction):

If {{tracking_issue_number}} is 0, skip posting the summary comment — there is no tracking issue for this dispatch.

The server-side sentinel of 0 is intentional (there is a code comment explaining it); the skill just needs to honour it.


Minor: no test exercises tracking_issue omitted from HTTP dispatch

File: src/breakdown.test.ts

The test suite always passes a tracking_issue number. A test calling dispatchBreakdown({ repo: "charles/claude-hooks" }) (no tracking_issue) would confirm the dispatch succeeds and that the rendered task body contains "0". Pairs naturally with the skill fix above. Not a standalone blocker.


Everything else looks good

  • parseBreakdownComment quote-aware tokeniser is correct and well-tested; dry_run=false and non-numeric max_issues edge cases are covered.
  • The mock.module("./main", ...) minimal-surface strategy is the right fix for the Bun process-global mock leak.
  • handleIssueComment trust-gate (isTrustedUser) is correct: repo owner + any configured agent; untrusted comments silently no-op (preserving the 200-always webhook contract).
  • handleBreakdown in main.ts correctly handles malformed JSON, missing repo, and propagates the typed error shape to the HTTP response.

Summary: two things to address before merging — (1) wait for CI green, and (2) add the tracking_issue_number == 0 guard to the skill template (+ optional companion test).

## Review: feat(breakdown): dispatch specs/*.md to boss pool as user-story issues **Round 1 — full review** --- ### CI: still running — cannot approve The only workflow run on head commit `fd33b02` (run #1736: fix(ci): stop breakdown mock clobbering registerWorker in sibling tests) was **still running** at review time. > CI was still running at review time — push any trivial change (or wait) and I will re-review when it completes. --- ### Acceptance criteria — all met | Criterion | Status | |---|---| | `skills/breakdown.md` exists, correct structure | OK | | Input vars `{{repo}}`, `{{spec_path}}`, `{{tracking_issue_number}}`, `{{milestone}}`, `{{dry_run}}`, `{{max_issues}}` all present | OK | | HTTP `POST /breakdown` with documented body shape + 202 on success | OK | | Slash command `/breakdown` with positional + key=value args | OK | | Cross-repo guard (400 when repo not in `config.repos`) | OK | | Boss-missing returns 503, not 400 | OK | | Dedup guard in skill template (`list_repo_issues` + case-insensitive title match) | OK | | Cap at `max_issues` (default 15), overflow in summary comment | OK | | Dry-run: checklist comment instead of creates | OK | | `skills/breakdown.md` forbids `create_label` for `area:*`, auto-creates only `security` | OK | | `stateless_session: true` so breakdowns never resume a prior session | OK | | `src/breakdown.test.ts` — 16 tests | OK | | `src/webhook-handlers.test.ts` extended with arg-passing smoke test | OK | | CLAUDE.md Breakdown skill section + API/Roles table entries | OK | | README `POST /breakdown` section with curl example | OK | --- ### Issue: skill template silently tries to post to issue #0 when no tracking issue is supplied **File**: `skills/breakdown.md`, step 9 ("Post the summary comment") and the dry-run short-circuit in step 7. **What happens**: `dispatchBreakdown` substitutes `tracking_issue_number: 0` when the caller omits `tracking_issue` (e.g. a bare `POST /breakdown` without `"tracking_issue"`). The template then instructs the agent to post a summary comment on `#0`, which is not a real Forgejo issue — the MCP call will 404 and the agent's last step fails with a confusing error in the session log. **Fix**: add one guard sentence near the start of step 9 (and symmetrically to step 7's dry-run comment instruction): ``` If {{tracking_issue_number}} is 0, skip posting the summary comment — there is no tracking issue for this dispatch. ``` The server-side sentinel of `0` is intentional (there is a code comment explaining it); the skill just needs to honour it. --- ### Minor: no test exercises `tracking_issue` omitted from HTTP dispatch **File**: `src/breakdown.test.ts` The test suite always passes a `tracking_issue` number. A test calling `dispatchBreakdown({ repo: "charles/claude-hooks" })` (no `tracking_issue`) would confirm the dispatch succeeds and that the rendered task body contains `"0"`. Pairs naturally with the skill fix above. Not a standalone blocker. --- ### Everything else looks good - `parseBreakdownComment` quote-aware tokeniser is correct and well-tested; `dry_run=false` and non-numeric `max_issues` edge cases are covered. - The `mock.module("./main", ...)` minimal-surface strategy is the right fix for the Bun process-global mock leak. - `handleIssueComment` trust-gate (`isTrustedUser`) is correct: repo owner + any configured agent; untrusted comments silently no-op (preserving the 200-always webhook contract). - `handleBreakdown` in `main.ts` correctly handles malformed JSON, missing `repo`, and propagates the typed error shape to the HTTP response. **Summary**: two things to address before merging — (1) wait for CI green, and (2) add the `tracking_issue_number == 0` guard to the skill template (+ optional companion test).
code-lead deleted branch boss/142 2026-04-20 13:59:58 +00:00
reviewer requested changes 2026-04-20 14:00:33 +00:00
Dismissed
reviewer left a comment

Review: feat(breakdown): dispatch specs/*.md to boss pool as user-story issues

Round 1 — full review


CI: still running — cannot approve

The only workflow run on head commit fd33b02 (run #1736: fix(ci): stop breakdown mock clobbering registerWorker in sibling tests) was still running at review time.

CI was still running at review time — push any trivial change (or wait) and I will re-review when it completes.


Acceptance criteria — all met

Criterion Status
skills/breakdown.md exists, correct structure OK
Input vars {{repo}}, {{spec_path}}, {{tracking_issue_number}}, {{milestone}}, {{dry_run}}, {{max_issues}} all present OK
HTTP POST /breakdown with documented body shape + 202 on success OK
Slash command /breakdown with positional + key=value args OK
Cross-repo guard (400 when repo not in config.repos) OK
Boss-missing returns 503, not 400 OK
Dedup guard in skill template (list_repo_issues + case-insensitive title match) OK
Cap at max_issues (default 15), overflow in summary comment OK
Dry-run: checklist comment instead of creates OK
skills/breakdown.md forbids create_label for area:*, auto-creates only security OK
stateless_session: true so breakdowns never resume a prior session OK
src/breakdown.test.ts — 16 tests OK
src/webhook-handlers.test.ts extended with arg-passing smoke test OK
CLAUDE.md Breakdown skill section + API/Roles table entries OK
README POST /breakdown section with curl example OK

Issue: skill template silently tries to post to issue #0 when no tracking issue is supplied

File: skills/breakdown.md, step 9 ("Post the summary comment") and the dry-run short-circuit in step 7.

What happens: dispatchBreakdown substitutes tracking_issue_number: 0 when the caller omits tracking_issue (e.g. a bare POST /breakdown without "tracking_issue"). The template then instructs the agent to post a summary comment on #0, which is not a real Forgejo issue — the MCP call will 404 and the agent's last step fails with a confusing error in the session log.

Fix: add one guard sentence near the start of step 9 (and symmetrically to step 7's dry-run comment instruction):

If {{tracking_issue_number}} is 0, skip posting the summary comment — there is no tracking issue for this dispatch.

The server-side sentinel of 0 is intentional (there is a code comment explaining it); the skill just needs to honour it.


Minor: no test exercises tracking_issue omitted from HTTP dispatch

File: src/breakdown.test.ts

The test suite always passes a tracking_issue number. A test calling dispatchBreakdown({ repo: "charles/claude-hooks" }) (no tracking_issue) would confirm the dispatch succeeds and that the rendered task body contains "0". Pairs naturally with the skill fix above. Not a standalone blocker.


Everything else looks good

  • parseBreakdownComment quote-aware tokeniser is correct and well-tested; dry_run=false and non-numeric max_issues edge cases are covered.
  • The mock.module("./main", ...) minimal-surface strategy is the right fix for the Bun process-global mock leak.
  • handleIssueComment trust-gate (isTrustedUser) is correct: repo owner + any configured agent; untrusted comments silently no-op (preserving the 200-always webhook contract).
  • handleBreakdown in main.ts correctly handles malformed JSON, missing repo, and propagates the typed error shape to the HTTP response.

Summary: two things to address before merging — (1) wait for CI green, and (2) add the tracking_issue_number == 0 guard to the skill template (+ optional companion test).

## Review: feat(breakdown): dispatch specs/*.md to boss pool as user-story issues **Round 1 — full review** --- ### CI: still running — cannot approve The only workflow run on head commit `fd33b02` (run #1736: fix(ci): stop breakdown mock clobbering registerWorker in sibling tests) was **still running** at review time. > CI was still running at review time — push any trivial change (or wait) and I will re-review when it completes. --- ### Acceptance criteria — all met | Criterion | Status | |---|---| | `skills/breakdown.md` exists, correct structure | OK | | Input vars `{{repo}}`, `{{spec_path}}`, `{{tracking_issue_number}}`, `{{milestone}}`, `{{dry_run}}`, `{{max_issues}}` all present | OK | | HTTP `POST /breakdown` with documented body shape + 202 on success | OK | | Slash command `/breakdown` with positional + key=value args | OK | | Cross-repo guard (400 when repo not in `config.repos`) | OK | | Boss-missing returns 503, not 400 | OK | | Dedup guard in skill template (`list_repo_issues` + case-insensitive title match) | OK | | Cap at `max_issues` (default 15), overflow in summary comment | OK | | Dry-run: checklist comment instead of creates | OK | | `skills/breakdown.md` forbids `create_label` for `area:*`, auto-creates only `security` | OK | | `stateless_session: true` so breakdowns never resume a prior session | OK | | `src/breakdown.test.ts` — 16 tests | OK | | `src/webhook-handlers.test.ts` extended with arg-passing smoke test | OK | | CLAUDE.md Breakdown skill section + API/Roles table entries | OK | | README `POST /breakdown` section with curl example | OK | --- ### Issue: skill template silently tries to post to issue #0 when no tracking issue is supplied **File**: `skills/breakdown.md`, step 9 ("Post the summary comment") and the dry-run short-circuit in step 7. **What happens**: `dispatchBreakdown` substitutes `tracking_issue_number: 0` when the caller omits `tracking_issue` (e.g. a bare `POST /breakdown` without "tracking_issue"). The template then instructs the agent to post a summary comment on `#0`, which is not a real Forgejo issue — the MCP call will 404 and the agent's last step fails with a confusing error in the session log. **Fix**: add one guard sentence near the start of step 9 (and symmetrically to step 7's dry-run comment instruction): ``` If {{tracking_issue_number}} is 0, skip posting the summary comment — there is no tracking issue for this dispatch. ``` The server-side sentinel of `0` is intentional (there is a code comment explaining it); the skill just needs to honour it. --- ### Minor: no test exercises `tracking_issue` omitted from HTTP dispatch **File**: `src/breakdown.test.ts` The test suite always passes a `tracking_issue` number. A test calling `dispatchBreakdown({ repo: "charles/claude-hooks" })` (no `tracking_issue`) would confirm the dispatch succeeds and that the rendered task body contains "0". Pairs naturally with the skill fix above. Not a standalone blocker. --- ### Everything else looks good - `parseBreakdownComment` quote-aware tokeniser is correct and well-tested; `dry_run=false` and non-numeric `max_issues` edge cases are covered. - The `mock.module("./main", ...)` minimal-surface strategy is the right fix for the Bun process-global mock leak. - `handleIssueComment` trust-gate (`isTrustedUser`) is correct: repo owner + any configured agent; untrusted comments silently no-op (preserving the 200-always webhook contract). - `handleBreakdown` in `main.ts` correctly handles malformed JSON, missing `repo`, and propagates the typed error shape to the HTTP response. **Summary**: two things to address before merging — (1) wait for CI green, and (2) add the `tracking_issue_number == 0` guard to the skill template (+ optional companion test).
Author
Collaborator

🤖 Review loop capped — auto-merging

Reviewer reviewer submitted 5 REQUEST_CHANGES rounds on this PR against author boss. Per the MAX_ROUNDS=3 policy, the review cycle is halted and boss will squash-merge the PR now.

What still applies

  • PR must be open, mergeable (no conflicts), and CI green. If any of those fail, the force-merge dispatch stops and posts an explanatory comment — no hard bypass.
  • The latest review state is APPROVED check is waived for this merge. The review will be REQUEST_CHANGES, and that's by design.

Rationale

Each round costs ~5 min × 2 agents × 1M-context, and past round 5 findings are usually nitpick spiral or reviewer non-determinism rather than real correctness issues.

Cap is MAX_ROUNDS=3 in src/review-loop.ts. To raise the cap, bump the constant. To revert to operator-handoff instead of auto-merge, swap the forceMerge branch in guardAuthorDispatch + handleChangesRequested.

## 🤖 Review loop capped — auto-merging Reviewer `reviewer` submitted **5 REQUEST_CHANGES rounds** on this PR against author `boss`. Per the `MAX_ROUNDS=3` policy, the review cycle is halted and boss will squash-merge the PR now. ### What still applies - PR must be **open**, **mergeable** (no conflicts), and **CI green**. If any of those fail, the force-merge dispatch stops and posts an explanatory comment — no hard bypass. - The `latest review state is APPROVED` check is **waived** for this merge. The review will be REQUEST_CHANGES, and that's by design. ### Rationale Each round costs ~5 min × 2 agents × 1M-context, and past round 5 findings are usually nitpick spiral or reviewer non-determinism rather than real correctness issues. _Cap is `MAX_ROUNDS=3` in `src/review-loop.ts`. To raise the cap, bump the constant. To revert to operator-handoff instead of auto-merge, swap the `forceMerge` branch in `guardAuthorDispatch` + `handleChangesRequested`._
reviewer left a comment

Review: feat(breakdown): dispatch specs/*.md to boss pool as user-story issues

CI: green (run #292, 3m19s)
Round: 1 (first review)

Overall

Solid implementation — all acceptance criteria from #142 are met, guardrails are centralised in dispatchBreakdown, tests cover the cross-repo guard / boss-missing 503 / parse edge-cases, and CI is green. One real operational gap prevents approval: the skill template will fail at runtime when POST /breakdown is called without a tracking_issue.


Issue 1 — skills/breakdown.md has no guard for tracking_issue_number = 0 (blocking)

File: skills/breakdown.md

What's wrong: When POST /breakdown is called without a tracking_issue body field, dispatchBreakdown sets trackingIssue = 0 (documented as a sentinel in the CLAUDE.md comment: "0 is a sentinel — no existing worktree / session key collides with it"). The interpolated {{tracking_issue_number}} becomes "0". Steps 7 and 9 of the skill unconditionally instruct the agent to post comments to #0, which produces a Forgejo MCP error (no such issue). The issues themselves are still created, but the summary comment and dry-run checklist steps error out, leaving the task in a failed state.

Affected steps:

  • Step 7 (dry-run): "Post a Markdown checklist comment on #{{tracking_issue_number}} … then stop."
  • Step 9 (summary): "post a single Markdown comment on #{{tracking_issue_number}}"
  • Re-run dedup guard: "check for a prior breakdown summary comment on #{{tracking_issue_number}} …"

Fix: Add a guard to the preamble or to each affected step, e.g.:

> **Note:** When `{{tracking_issue_number}}` is `0`, this is a standalone HTTP dispatch with no tracking issue. Skip every step that posts a comment to `#{{tracking_issue_number}}` and do not check for a prior summary comment.

The webhook slash-command path always provides a tracking issue (the commenting issue number), so it is unaffected. Only the bare HTTP path (POST /breakdown without tracking_issue) hits this.

## Review: feat(breakdown): dispatch specs/*.md to boss pool as user-story issues **CI:** ✅ green (run #292, 3m19s) **Round:** 1 (first review) ### Overall Solid implementation — all acceptance criteria from #142 are met, guardrails are centralised in `dispatchBreakdown`, tests cover the cross-repo guard / boss-missing 503 / parse edge-cases, and CI is green. One real operational gap prevents approval: the skill template will fail at runtime when `POST /breakdown` is called without a `tracking_issue`. --- ### Issue 1 — `skills/breakdown.md` has no guard for `tracking_issue_number = 0` (blocking) **File:** `skills/breakdown.md` **What's wrong:** When `POST /breakdown` is called without a `tracking_issue` body field, `dispatchBreakdown` sets `trackingIssue = 0` (documented as a sentinel in the CLAUDE.md comment: _"0 is a sentinel — no existing worktree / session key collides with it"_). The interpolated `{{tracking_issue_number}}` becomes `"0"`. Steps 7 and 9 of the skill unconditionally instruct the agent to post comments to `#0`, which produces a Forgejo MCP error (no such issue). The issues themselves are still created, but the summary comment and dry-run checklist steps error out, leaving the task in a failed state. **Affected steps:** - Step 7 (dry-run): `"Post a Markdown checklist comment on #{{tracking_issue_number}} … then stop."` - Step 9 (summary): `"post a single Markdown comment on #{{tracking_issue_number}}"` - Re-run dedup guard: `"check for a prior breakdown summary comment on #{{tracking_issue_number}} …"` **Fix:** Add a guard to the preamble or to each affected step, e.g.: ``` > **Note:** When `{{tracking_issue_number}}` is `0`, this is a standalone HTTP dispatch with no tracking issue. Skip every step that posts a comment to `#{{tracking_issue_number}}` and do not check for a prior summary comment. ``` The webhook slash-command path always provides a tracking issue (the commenting issue number), so it is unaffected. Only the bare HTTP path (`POST /breakdown` without `tracking_issue`) hits this.
Author
Collaborator

🤖 Review loop capped — auto-merging

Reviewer reviewer submitted 6 REQUEST_CHANGES rounds on this PR against author boss. Per the MAX_ROUNDS=3 policy, the review cycle is halted and boss will squash-merge the PR now.

What still applies

  • PR must be open, mergeable (no conflicts), and CI green. If any of those fail, the force-merge dispatch stops and posts an explanatory comment — no hard bypass.
  • The latest review state is APPROVED check is waived for this merge. The review will be REQUEST_CHANGES, and that's by design.

Rationale

Each round costs ~5 min × 2 agents × 1M-context, and past round 6 findings are usually nitpick spiral or reviewer non-determinism rather than real correctness issues.

Cap is MAX_ROUNDS=3 in src/review-loop.ts. To raise the cap, bump the constant. To revert to operator-handoff instead of auto-merge, swap the forceMerge branch in guardAuthorDispatch + handleChangesRequested.

## 🤖 Review loop capped — auto-merging Reviewer `reviewer` submitted **6 REQUEST_CHANGES rounds** on this PR against author `boss`. Per the `MAX_ROUNDS=3` policy, the review cycle is halted and boss will squash-merge the PR now. ### What still applies - PR must be **open**, **mergeable** (no conflicts), and **CI green**. If any of those fail, the force-merge dispatch stops and posts an explanatory comment — no hard bypass. - The `latest review state is APPROVED` check is **waived** for this merge. The review will be REQUEST_CHANGES, and that's by design. ### Rationale Each round costs ~5 min × 2 agents × 1M-context, and past round 6 findings are usually nitpick spiral or reviewer non-determinism rather than real correctness issues. _Cap is `MAX_ROUNDS=3` in `src/review-loop.ts`. To raise the cap, bump the constant. To revert to operator-handoff instead of auto-merge, swap the `forceMerge` branch in `guardAuthorDispatch` + `handleChangesRequested`._
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!147
No description provided.