feat(triggers): extend authorMode to respond-to-ci and resolve-conflicts + fork write-access skip - #1535
Merged
Conversation
…cts + fork write-access skip
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
nhopeatall
approved these changes
Aug 6, 2026
nhopeatall
left a comment
Collaborator
There was a problem hiding this comment.
Summary
APPROVE — extends authorMode (own/external/all) to respond-to-ci and resolve-conflicts plus a fork write-access skip. I traced every path, ran the touched suites (92 + 127 + 28 + 830 tests green locally, matching 7/7 CI), and confirmed the implementation matches all 10 MNG-1774 implementation steps.
What I verified
- Zero behavior change under default
own.gateAuthorMode({})resolves toown, and for a cascade authorevaluateAuthorMode→isCascadeBotreturns the same verdict the oldgateCascadePersonadid (both checkimpl/impl[bot]/reviewer/reviewer[bot]). Cascade PRs are always same-repo, sogateForkWriteAccessis a no-op forown. Only the skip message text changed (tests updated accordingly). - Consolidation is behavior-preserving. The shared
author-mode.tsevaluateAuthorMode/resolveAuthorModeare equivalent to the three prior copies;utils.tsre-export keepspr-opened.tsandgithub-utils.test.tsworking, andcheck-suite-decision.ts:authorModeDecisionis now a thin wrapper preserving theisCascadePR=suffix. - Fork detection is correct.
getPRhandleshead.repo === null(deleted fork →isFork:true), a present head repo (full_name !== base→ fork), andundefined(test mocks → non-fork). No false positives for same-repo PRs. - Fork gate covers both entry points. Placed in
dispatchRespondToCi(single-sourced for check-suite-failure and the check-suite-success mixed-state fork); both callers pass a realgithubClient.getPRresult, soisForkis always populated. It runs before the Redis dedup claim / attempt counter, so a fork PR consumes neither. - pr-conflict-detected places the fork gate after unmergeability is confirmed and before the attempt-counter bump — a fork conflict PR skips cleanly with no
createPRCommentside effect.
Minor notes (non-blocking, likely intentional)
gateCascadePersonanow has no production importers (only its own definition + tests). The PR notes it was deliberately kept exported; fine to leave, but it's dead outside tests now.- For a fork PR under
external/all,check-suite-failurestill makes onegetCheckSuiteStatuscall before the fork skip lands insidedispatchRespondToCi. Harmless, and a reasonable trade for single-sourcing the gate — noting only so it's a conscious choice. - The success→respond-to-ci mixed-fork path filters authors by the review trigger's
authorMode(pre-existing); the newgateForkWriteAccesscorrectly protects the fork subcase regardless. Already documented in the PR/ticket as out of scope.
Docs (03-trigger-system.md, getting-started.md, CHANGELOG.md) accurately describe the new behavior and the all/external write-access caution.
🕵️ claude-code · claude-opus-4-8 · run details
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
Extends the
authorModetrigger parameter (own/external/all) — previously only on thereviewagent — to the two hard-gated write agents:respond-to-ci(scm:check-suite-failure)resolve-conflicts(scm:pr-conflict-detected)Both default to
own, so no existing project changes behavior.ownrestricts dispatch to CASCADE-authored PRs;external/allextend it to human-authored same-repo PRs. The trigger-config UI is fully metadata-driven, so the only surface edits are the two agent YAMLs — no frontend changes.Because both agents push commits and CASCADE has no write access to a contributor's fork, a new shared
gateForkWriteAccessgate turns a fork PR (reachable only underexternal/all) into a clean, self-explanatory skip instead of firing an agent that fails mid-run at the push step.Linear: https://linear.app/issue/MNG-1774
What changed
Agent definitions
src/agents/definitions/{respond-to-ci,resolve-conflicts}.yaml— added theauthorModeselect param (copied fromreview.yaml, defaultown).Single-sourced author-mode logic
src/triggers/shared/author-mode.tsexportsresolveAuthorMode+evaluateAuthorMode, consolidating the three previous near-duplicate copies.src/triggers/github/utils.tsnow re-exportsevaluateAuthorMode/resolveAuthorModefrom the shared module (keepspr-opened.ts+ existing tests untouched).src/triggers/github/check-suite-decision.ts— deleted the privateresolveAuthorMode+cascadePersonaDecision;authorModeDecisionis now a thin wrapper over the shared evaluator.CheckSuiteDecisionMode'srespond-to-civariant now carriesparameters, anddecideCheckSuiteGatesalways routes throughauthorModeDecision.Shared gates
src/triggers/shared/gates.ts— addedgateAuthorMode(delegates to the shared evaluator; slots into the??gate chains) andgateForkWriteAccess(skips whenprDetails.isFork, naming the head repo; distinct deleted-fork phrasing whenheadRepoFullNameis null).Fork detection
src/github/client.ts—PRDetailsgains optionalheadRepoFullName+isFork(optional so existinggetPRmocks compile and default to non-fork).getPRderives them from the octokit response. Thenull(deleted fork → unpushable) vsundefined(test mock → not a fork) split is deliberate and documented inline.Handlers
check-suite-failure.ts— switched tocheckTriggerEnabledWithParams; replaced the explicitgateCascadePersonablock with a pre-APIdecideCheckSuiteGates({ mode: { kind: 'respond-to-ci', parameters } })call.respond-to-ci-dispatch.ts— insertedgateForkWriteAccessafter the trigger-enabled gate but before the Redis dedup claim / attempt counter, so a fork PR never consumes either. This is the single-sourced dispatch path for bothcheck-suite-failureand thecheck-suite-successmixed-state fork.pr-conflict-detected.ts— switched tocheckTriggerEnabledWithParams; swappedgateCascadePersona→gateAuthorModein the gate chain; addedgateForkWriteAccessafter the PR is confirmed unmergeable, before the attempt counter / dispatch.Docs
docs/architecture/03-trigger-system.md,docs/getting-started.md,CHANGELOG.md— documented the extendedauthorMode+ fork-skip behavior, with theall/externalwrite-access caution.Scope notes
authorMode(pre-existing); the newgateForkWriteAccessindispatchRespondToCiis an authorMode-independent write-safety net, correct regardless of which trigger's config allowed dispatch.Testing
getPR(fork / same-repo / deleted-fork / non-fork mock) andgateForkWriteAccessskips on both dispatch paths.ownregression: unconfigured projects behave exactly as before.npm run typecheckclean;biome checkclean on all changed/new files; targeted unit suites green (280 tests across the touched files, plus 888 in the trigger/agent-definition sweep).🤖 Generated with Claude Code
🕵️ claude-code · claude-opus-4-8 · run details