Port slash commands (/hold, /ready, /no-ready, /unassign, /ready-stack) to YAML flows #1094

Closed
opened 2026-05-10 23:30:56 +00:00 by claude-desktop · 0 comments
Collaborator

As an operator, I want issue-comment slash commands to be driven by YAML flows, so that the orphaned handleSlashCommandOther handler can be deleted and operators can audit command behaviour from flows/defaults/.

Context

apps/server/src/domain/workflow/event-handlers.ts:773 handleSlashCommandOther is orphaned — no production caller. Slash commands (/hold, /ready, /no-ready, /unassign, /ready-stack) are silently broken unless invoked via a legacy code path I haven't identified. /breakdown is already ported via flows/defaults/breakdown-comment.yml.

Acceptance criteria

Expression-eval helper

  • Add comment_matches(regex) predicate to the YAML expression-eval surface (matches against event.comment.body)
  • OR expose event.comment.body with a startsWith helper — pick whichever is cheaper to add to the parser

Ops

  • is_trusted_user op — wraps isTrustedUser; inputs login, repo; outputs {trusted: bool}
  • apply_hold_command op — wraps applyHoldCommand(set: true); inputs repo, issue_number, author
  • clear_hold_command op — wraps applyHoldCommand(set: false); inputs repo, issue_number
  • apply_unassign_command op — wraps applyUnassignCommand; inputs repo, issue_number, author
  • apply_ready_stack_command op — wraps applyReadyStackCommand; inputs repo, issue_number, author

Flows

  • flows/defaults/slash-hold.yml — matches ^/hold or ^/no-readyis_trusted_user gate → apply_hold_command
  • flows/defaults/slash-ready.yml — matches ^/ready\b (not /ready-stack) → trust gate → clear_hold_command
  • flows/defaults/slash-unassign.yml — matches ^/unassign → trust gate → apply_unassign_command
  • flows/defaults/slash-ready-stack.yml — matches ^/ready-stack → trust gate → apply_ready_stack_command

Tests

  • Unit tests for each op + is_trusted_user predicate
  • Expression-eval tests for comment_matches
  • Flow integration tests for each slash flow (trusted user accepted; untrusted user rejected)
  • Verify /breakdown flow still works (no regression on the one already-ported command)

Cleanup

  • Delete handleSlashCommandOther and applyReadyStackCommand handler exports from event-handlers.ts (keep applyHoldCommand / applyUnassignCommand / applyReadyStackCommand as op implementations)
  • Delete their tests

Out of scope

  • CI events — separate ticket (#1092)
  • PR-closed cleanup — separate ticket (#1093)
  • legacy* executor hooks — separate ticket

References

  • apps/server/src/domain/workflow/event-handlers.ts:754-820 (handler impl + command regexes)
  • apps/server/src/domain/workflow/deps.ts (applyHoldCommand, applyUnassignCommand)
  • flows/defaults/breakdown-comment.yml (template for issue_comment trigger usage)
As an operator, I want issue-comment slash commands to be driven by YAML flows, so that the orphaned `handleSlashCommandOther` handler can be deleted and operators can audit command behaviour from `flows/defaults/`. ## Context `apps/server/src/domain/workflow/event-handlers.ts:773 handleSlashCommandOther` is orphaned — no production caller. Slash commands (`/hold`, `/ready`, `/no-ready`, `/unassign`, `/ready-stack`) are silently broken unless invoked via a legacy code path I haven't identified. `/breakdown` is already ported via `flows/defaults/breakdown-comment.yml`. ## Acceptance criteria ### Expression-eval helper - [ ] Add `comment_matches(regex)` predicate to the YAML expression-eval surface (matches against `event.comment.body`) - [ ] OR expose `event.comment.body` with a `startsWith` helper — pick whichever is cheaper to add to the parser ### Ops - [ ] `is_trusted_user` op — wraps `isTrustedUser`; inputs `login, repo`; outputs `{trusted: bool}` - [ ] `apply_hold_command` op — wraps `applyHoldCommand(set: true)`; inputs `repo, issue_number, author` - [ ] `clear_hold_command` op — wraps `applyHoldCommand(set: false)`; inputs `repo, issue_number` - [ ] `apply_unassign_command` op — wraps `applyUnassignCommand`; inputs `repo, issue_number, author` - [ ] `apply_ready_stack_command` op — wraps `applyReadyStackCommand`; inputs `repo, issue_number, author` ### Flows - [ ] `flows/defaults/slash-hold.yml` — matches `^/hold` or `^/no-ready` → `is_trusted_user` gate → `apply_hold_command` - [ ] `flows/defaults/slash-ready.yml` — matches `^/ready\b` (not `/ready-stack`) → trust gate → `clear_hold_command` - [ ] `flows/defaults/slash-unassign.yml` — matches `^/unassign` → trust gate → `apply_unassign_command` - [ ] `flows/defaults/slash-ready-stack.yml` — matches `^/ready-stack` → trust gate → `apply_ready_stack_command` ### Tests - [ ] Unit tests for each op + `is_trusted_user` predicate - [ ] Expression-eval tests for `comment_matches` - [ ] Flow integration tests for each slash flow (trusted user accepted; untrusted user rejected) - [ ] Verify `/breakdown` flow still works (no regression on the one already-ported command) ### Cleanup - [ ] Delete `handleSlashCommandOther` and `applyReadyStackCommand` handler exports from `event-handlers.ts` (keep `applyHoldCommand` / `applyUnassignCommand` / `applyReadyStackCommand` as op implementations) - [ ] Delete their tests ## Out of scope - CI events — separate ticket (#1092) - PR-closed cleanup — separate ticket (#1093) - `legacy*` executor hooks — separate ticket ## References - `apps/server/src/domain/workflow/event-handlers.ts:754-820` (handler impl + command regexes) - `apps/server/src/domain/workflow/deps.ts` (`applyHoldCommand`, `applyUnassignCommand`) - `flows/defaults/breakdown-comment.yml` (template for issue_comment trigger usage)
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#1094
No description provided.