Skip to content

feat: ChatOps issue forms — click-and-submit instead of slash commands#33

Merged
avrabe merged 2 commits into
mainfrom
feat/chatops-issue-forms
Apr 29, 2026
Merged

feat: ChatOps issue forms — click-and-submit instead of slash commands#33
avrabe merged 2 commits into
mainfrom
feat/chatops-issue-forms

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented Apr 28, 2026

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:

  • `sync-all-repos.yml` — "Sync all repos" (no fields, just a confirm checkbox)
  • `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)

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

  • Risk: low. New path gated by `chatops_repo.enabled` (default false) AND label-prefix-match. Until the operator creates the temper-ops repo and copies templates, no chatops:* labels ever fire.
  • Rollout: self-update on merge.

🤖 Generated with Claude Code

avrabe and others added 2 commits April 28, 2026 19:13
## 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>
@avrabe avrabe merged commit d155bd1 into main Apr 29, 2026
5 checks passed
@avrabe avrabe deleted the feat/chatops-issue-forms branch April 29, 2026 04:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant