feat: ChatOps issue forms — click-and-submit instead of slash commands#33
Merged
Conversation
## Why
ChatOps commands (/configure-repo, /sync-all-repos, /review-pr, etc.) are
currently honoured from any repo where Temper is installed. Even with
allowed_command_users restricting *who* can trigger them, the bot's "Working
on it…" replies and any error responses are public — sitting in whichever
public repo the maintainer happened to comment in.
For an org running an org-wide config sweep (especially the upcoming
/sync-all-repos that migrates every repo to Repository Rulesets + applies
config changes via PR), the user wants the trigger and the bot's response
trail to stay in a private admin repo.
## What
Opt-in `chatops_repo:` config section:
chatops_repo:
enabled: false
repo: pulseengine/temper-ops
When `enabled: true`, the issue_comment.created handler silently ignores
any comment starting with `/` UNLESS it's posted in `repo`. The check fires
*before* command extraction, so:
- no log line emitted
- no comment posted back
- no public footprint at all
Non-command comments (no leading `/`) are unaffected — handler proceeds
through the existing no-op path. Commands posted in the designated repo
work exactly as before.
## Operational pattern this unlocks
1. Create `pulseengine/temper-ops` as a **private** repo
2. Install Temper on it
3. Set `chatops_repo.enabled: true` and `repo: pulseengine/temper-ops` in
`config.local.yml`
4. Trigger /sync-all-repos, /configure-repo, etc. from there
5. The bot's "Working on it…" + result replies all land in the private
repo. Configuration PRs the bot opens against public repos are still
public — that's the intentional asymmetry: trigger private, application
public, audit trail intact.
## Test plan
- [x] All 792 tests pass (was 788; +4 covering: command honoured from admin
repo, command silently ignored from public repo, non-command comments
unaffected, legacy default still honours every repo)
- [x] eslint clean
- [ ] After deploy: enable chatops_repo, comment /configure-repo on any
public pulseengine repo — should be a complete no-op (no working
comment, no error). Comment from temper-ops — should work normally.
## Risk & rollout
- Risk: low. Opt-in via config; default behaviour unchanged.
- Rollout: self-update on merge.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Why After the chatops_repo restriction (PR #32), the user wanted "issue forms where I could just ask for things — e.g. redo for repository all, … then it is super easy". Filling a form > remembering slash command syntax, especially for less-frequent operations. ## What ### `docs/temper-ops-template/.github/ISSUE_TEMPLATE/` Four ready-to-copy issue forms (each labeled `chatops:<command>`): - `sync-all-repos.yml` — "Sync all repos" (no fields, just a confirm) - `configure-repo.yml` — "Configure single repo" (Repository field) - `analyze-org.yml` — "Org analysis report" (no fields) - `review-pr.yml` — "Review pull request" (Repository + PR number) ### `docs/temper-ops-setup.md` Operator guide: create the private repo, install the bot, set `chatops_repo` config, copy the issue templates, done. ### `src/app.js` — `handleChatopsIssue` The existing `issues.opened` handler grew a new branch BEFORE the controller_repo provisioning path: if (chatops_repo.enabled && fullName === chatops_repo.repo) { const label = issue.labels.find(l => l.name.startsWith('chatops:')); if (label) { handleChatopsIssue(...) // dispatch by label return; } } // ... existing controller_repo provisioning falls through unchanged Each chatops:<command> dispatches to the same underlying function the slash command would have called (synchronizeAllRepositories, configureRepository, generateOrganizationAnalysisReport, reviewPullRequest), replies on the source issue, and closes it on success. The closed issue + form fields + bot replies = the audit trail. ## Operating model With chatops_repo + temper-ops + these forms in place: Open issue in temper-ops → choose template → fill (or none) → submit → bot acknowledges → bot does the work → bot replies with result → bot closes the issue. No slash command syntax to remember. No public-repo footprint. Issue remains as the audit record. ## Test plan - [x] All 799 tests pass (was 792 — added 7 covering each chatops label dispatch, missing-field rejection, non-allowed-user rejection, and fall-through to controller_repo when no chatops label is present) - [x] eslint clean - [ ] After deploy + creating temper-ops + copying templates: open the "Sync all repos" form, submit, observe bot's progress + result replies, observe issue closure on success. ## Risk & rollout - Risk: low. The new code path is gated by chatops_repo.enabled (default false) AND label-prefix-match. Until the operator copies the issue templates into temper-ops, no chatops labels ever fire. - Rollout: self-update on merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #32 — once #32 merges this rebases cleanly.
Why
You asked for "issue forms where I could just ask for things — e.g. redo for repository all". Filling a form beats remembering slash command syntax, especially for the less-frequent ops.
What
`docs/temper-ops-template/.github/ISSUE_TEMPLATE/` — four ready-to-copy forms:
Each carries a `chatops:` label as its first label.
`docs/temper-ops-setup.md` — full operator walkthrough: create the private repo, install the bot, set `chatops_repo` config, copy templates, done.
`src/app.js` — `handleChatopsIssue`: the existing `issues.opened` handler picked up a new branch BEFORE controller_repo provisioning. When an issue opens in `chatops_repo` with a `chatops:` label, dispatch to the matching ChatOps action (`synchronizeAllRepositories`, `configureRepository`, `generateOrganizationAnalysisReport`, `reviewPullRequest`), reply on the issue, close it on success.
Operating model after this lands
Open issue in temper-ops → choose template → fill (or none) → submit
→ bot acknowledges → bot does the work → bot replies with result
→ bot closes the issue.
No syntax to remember. No public-repo footprint. Issue is the audit trail.
Test plan
Risk & rollout
🤖 Generated with Claude Code