feat(deps): flip unassigned-ready branch from ready-comment to auto-assign #200

Closed
opened 2026-04-20 23:45:31 +00:00 by claude-desktop · 0 comments
Collaborator

Policy change supersedes #196.

PR #198 shipped the comment-only version (operator-in-the-loop for unassigned ready issues). Operator reversed that decision: at a single-operator scale, a manual tap per unblock defeats the point of automation. The heuristic is good enough to try auto-assign directly with a cheap override path.

My REQUEST_CHANGES review on #198 landed 2.5 min before the merge but was overridden by reviewer-2's auto-APPROVED (process gap worth a separate bug). This issue ships the delta as a focused follow-up.

What to change

1. Pivot handleUnassignedReady in apps/server/src/deps.ts

Replace postReadyComment(...) with:

const { type, reasoning } = suggestAssignee(issue);
await updateIssueAssignees(repo, issue.number, [type], token);
await createIssueComment(
  repo, issue.number,
  `🤖 Auto-assigned to **${type}** (heuristic: ${reasoning}). ` +
  `Reply \`/unassign\` to reroute.`,
  token,
);

The assign call triggers the existing issues.assigned webhook → dispatchIssueForAgent → agent picks up work. The short comment is the audit trail.

2. Add /unassign slash command

Mirror /hold machinery in handleIssueComment:

  • Trust-gated (same as /hold, /breakdown).
  • On match, updateIssueAssignees(repo, issue.number, [], token).
  • One-line ack: Unassigned. Re-add a blocker + close it to re-trigger auto-assign, or assign manually.
  • No persistence — next close-cycle re-evaluates.

Keep /hold for "suppress all automation"; /unassign is narrower ("this routing was wrong").

3. Remove postReadyComment renderer

Delete the full ready-to-assign comment template + its dead-code rendering path. Audit comment in step 1 replaces it.

4. Keep everything else

The mechanism #198 shipped is solid — keep:

  • Dependency model (native + body parser union)
  • suggestAssignee heuristic
  • /hold / /ready / hold_issues SQLite table
  • /issues/ready endpoint
  • just deps-backfill CLI
  • Breakdown-skill native-dep registration
  • Fire-and-forget propagation phase wiring

5. Update tests

  • deps.test.ts: unassigned + all-blockers-closed propagator cases now assert updateIssueAssignees([type]) + short audit comment, not postReadyComment(...).
  • New test: /unassign slash command clears assignees, trust-gated.
  • Keep /hold tests, keep heuristic matrix unchanged.

6. Docs

  • Amend CLAUDE.md "Issue dependencies" section: auto-assign is the default, /unassign is the escape hatch.
  • Update README /issues/ready doc if needed (endpoint semantics unchanged).

Acceptance criteria

  • unassigned + all-blockers-closed path auto-assigns + posts audit comment
  • /unassign slash command implemented, trust-gated, tested
  • postReadyComment renderer removed
  • Heuristic unchanged; all existing deps.test.ts heuristic tests still pass
  • Live canary on #170 M18-9 Sunset: closing its last remaining blocker (#167 M18-6) auto-dispatches boss within seconds

Out of scope

  • Reverting PR #198 — the mechanism is sound, only the terminal action changes.
  • The review-override process gap (separate bug — late-REQUEST_CHANGES being swallowed by earlier-APPROVED).
  • Transitive dependency surfacing (unchanged out-of-scope from #196).

References

  • Predecessor: #196 (shipped as #198) — comment-only version.
  • apps/server/src/deps.ts::handleUnassignedReady — the one branch that pivots.
  • apps/server/src/webhook-handlers.ts::handleIssueComment — where /unassign registers.
  • Rationale for flipping: operator prefers zero-friction, trusts heuristic, wants /unassign cheap override for misroutes.
Policy change supersedes #196. PR #198 shipped the comment-only version (operator-in-the-loop for unassigned ready issues). **Operator reversed that decision**: at a single-operator scale, a manual tap per unblock defeats the point of automation. The heuristic is good enough to try auto-assign directly with a cheap override path. My REQUEST_CHANGES review on #198 landed 2.5 min before the merge but was overridden by `reviewer-2`'s auto-APPROVED (process gap worth a separate bug). This issue ships the delta as a focused follow-up. ## What to change ### 1. Pivot `handleUnassignedReady` in `apps/server/src/deps.ts` Replace `postReadyComment(...)` with: ```ts const { type, reasoning } = suggestAssignee(issue); await updateIssueAssignees(repo, issue.number, [type], token); await createIssueComment( repo, issue.number, `🤖 Auto-assigned to **${type}** (heuristic: ${reasoning}). ` + `Reply \`/unassign\` to reroute.`, token, ); ``` The assign call triggers the existing `issues.assigned` webhook → `dispatchIssueForAgent` → agent picks up work. The short comment is the audit trail. ### 2. Add `/unassign` slash command Mirror `/hold` machinery in `handleIssueComment`: - Trust-gated (same as `/hold`, `/breakdown`). - On match, `updateIssueAssignees(repo, issue.number, [], token)`. - One-line ack: `Unassigned. Re-add a blocker + close it to re-trigger auto-assign, or assign manually.` - No persistence — next close-cycle re-evaluates. Keep `/hold` for "suppress all automation"; `/unassign` is narrower ("this routing was wrong"). ### 3. Remove `postReadyComment` renderer Delete the full ready-to-assign comment template + its dead-code rendering path. Audit comment in step 1 replaces it. ### 4. Keep everything else The mechanism #198 shipped is solid — keep: - Dependency model (native + body parser union) - `suggestAssignee` heuristic - `/hold` / `/ready` / `hold_issues` SQLite table - `/issues/ready` endpoint - `just deps-backfill` CLI - Breakdown-skill native-dep registration - Fire-and-forget propagation phase wiring ### 5. Update tests - `deps.test.ts`: `unassigned + all-blockers-closed` propagator cases now assert `updateIssueAssignees([type])` + short audit comment, not `postReadyComment(...)`. - New test: `/unassign` slash command clears assignees, trust-gated. - Keep `/hold` tests, keep heuristic matrix unchanged. ### 6. Docs - Amend CLAUDE.md "Issue dependencies" section: auto-assign is the default, `/unassign` is the escape hatch. - Update README `/issues/ready` doc if needed (endpoint semantics unchanged). ## Acceptance criteria - [ ] `unassigned + all-blockers-closed` path auto-assigns + posts audit comment - [ ] `/unassign` slash command implemented, trust-gated, tested - [ ] `postReadyComment` renderer removed - [ ] Heuristic unchanged; all existing `deps.test.ts` heuristic tests still pass - [ ] Live canary on **#170 M18-9 Sunset**: closing its last remaining blocker (#167 M18-6) auto-dispatches boss within seconds ## Out of scope - Reverting PR #198 — the mechanism is sound, only the terminal action changes. - The review-override process gap (separate bug — late-REQUEST_CHANGES being swallowed by earlier-APPROVED). - Transitive dependency surfacing (unchanged out-of-scope from #196). ## References - Predecessor: #196 (shipped as #198) — comment-only version. - `apps/server/src/deps.ts::handleUnassignedReady` — the one branch that pivots. - `apps/server/src/webhook-handlers.ts::handleIssueComment` — where `/unassign` registers. - Rationale for flipping: operator prefers zero-friction, trusts heuristic, wants `/unassign` cheap override for misroutes.
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#200
No description provided.