Conversation
## Summary
- **Rulesets API replaces legacy branch protection.** New `src/rulesets.js`
uses `POST /repos/{owner}/{repo}/rulesets` targeting `~DEFAULT_BRANCH`,
which applies on empty repos. Kills the 5×2s retry race on
`repository.created`. Translates the existing `branch_protection.default`
config block into a ruleset payload — no config rewrite required.
Falls back to legacy branch protection when rulesets are unavailable.
- **Issue-driven repo provisioning (Cut A).** New `issues.opened` handler
watches a configurable controller repo (e.g. `pulseengine/repo-requests`),
validates the issue-form body (`src/provisioning.js`), enqueues a
`provision-repo` task. Handler creates the repo (template-based when
requested), applies full configuration, comments the URL on the source
issue, closes the issue with `state_reason: completed`. Issue form
schema starter under `docs/controller-repo-template/`.
- **Persistent state replaces in-memory Maps.** New `src/persistent-kv.js`
(SQLite-backed KV with TTL) backs both webhook idempotency and AI review
rate limits. PM2 restart no longer re-processes webhooks or re-reviews
PRs. `merge-strategy.js` 1-hour revert is now a persistent task
(`revert-merge-settings`) instead of `setTimeout`.
- **Scheduler wired from `registerApp`.** Was exported but never imported
in `index.js`. Now uses an installation-token factory so every tick gets
a fresh, installation-scoped Octokit. New handlers: `revert-merge-settings`,
`reconcile-repo`, `provision-repo`.
## Empty-repo bootstrap
`repository.created` no longer bails when the default branch is missing.
Rulesets, merge settings, and labels are applied immediately; branch-scoped
work (templates, codeowners, dependabot) is deferred to a `reconcile-repo`
task that fires after the first push.
## Test plan
- [x] All 698 tests pass (was 654; added 44 covering rulesets, persistent-kv,
provisioning, issues.opened, and the new `skipBranchScopedWork` path)
- [x] eslint clean (`npm run lint`)
- [x] Coverage above thresholds: 84.27% lines / 86.5% funcs / 78.08% branches
- [ ] After merge + self-update: create a new repo in pulseengine — verify
ruleset appears immediately and the 5×2s retry log is gone
- [ ] After merge: `/allow-merge-commit` — kill the bot mid-revert, restart,
verify the merge setting still reverts via the persistent task store
## Risk & rollout
- Risk: medium. Rulesets are opt-in via `rulesets.enabled: true` (default in
this commit); legacy fall-back kicks in on 404/422/403. Controller repo
is opt-in via `controller_repo.enabled: false` (default off).
- Rollout: self-update on merge. After deploy, set
`controller_repo.enabled: true` and create `pulseengine/repo-requests`
with the form from `docs/controller-repo-template/`.
🤖 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.
Summary
src/rulesets.jsusesPOST /repos/{owner}/{repo}/rulesetstargeting~DEFAULT_BRANCH, which applies on empty repos. Kills the 5×2s retry race onrepository.created. Translates the existingbranch_protection.defaultconfig block into a ruleset payload — no config rewrite required. Falls back to legacy branch protection on 404/422/403.issues.openedhandler watches a configurable controller repo (e.g.pulseengine/repo-requests), validates the issue-form body (src/provisioning.js), enqueues aprovision-repotask. The task creates the repo (template-based when requested), applies full configuration, comments the URL on the source issue, and closes it withstate_reason: completed. Issue-form starter underdocs/controller-repo-template/.src/persistent-kv.js(SQLite-backed KV with TTL) backs both webhook idempotency and AI review rate limits. PM2 restart no longer re-processes webhooks or re-reviews PRs.merge-strategy.js's 1-hour revert is now a persistent task (revert-merge-settings) instead ofsetTimeout.registerApp. Was exported fromsrc/app.jsbut never imported inindex.js— meaning every queued task in production sat unprocessed. Now uses an installation-token factory so each tick gets a fresh, installation-scoped Octokit. New handlers:revert-merge-settings,reconcile-repo,provision-repo.Empty-repo bootstrap
repository.createdno longer bails when the default branch is missing. Rulesets, merge settings, and labels are applied immediately; branch-scoped work (templates, codeowners, dependabot) is deferred to areconcile-repotask that fires after the first push.Files
src/rulesets.jsbranch_protection.default.src/persistent-kv.jssrc/provisioning.jssrc/app.jsissues.opened,initPersistence,initSchedulerfromregisterApp. Per-tick installation-token factory.src/repository.jsskipBranchScopedWorkmode for empty repos.src/merge-strategy.jssetTimeout→ persistent task.src/idempotency.js,src/ai-review.jssrc/scheduler.jssrc/schema.jsrulesetsandcontroller_reposections.config.ymlrulesets:(enabled by default) andcontroller_repo:(disabled by default) sections.docs/controller-repo-template/Test plan
issues.opened, and the newskipBranchScopedWorkpath)npm run lintcleanpulseengine— verify the ruleset appears immediately and the 5×2s retry log line is gone/allow-merge-commiton a PR with signed commits, kill the bot mid-revert, restart, confirm the persistent task still flips merge settings backRisk & rollout
fall_back_to_legacy: truemeans a 404/422/403 falls through to the legacy branch-protection path. Controller-repo provisioning is off by default (controller_repo.enabled: false).controller_repo.enabled: trueinconfig.local.yml.pulseengine/repo-requestsfromdocs/controller-repo-template/./sync-all-reposto re-apply configuration via the new ruleset path on existing repos.Checklist
data/and.claude/added to.gitignore🤖 Generated with Claude Code