feat(github): downgrade auto-apply when the schema dir changed on base#737
Closed
Kiran01bm wants to merge 5 commits into
Closed
feat(github): downgrade auto-apply when the schema dir changed on base#737Kiran01bm wants to merge 5 commits into
Kiran01bm wants to merge 5 commits into
Conversation
Add a default-on, path-scoped up-to-date-with-base gate. On the automatic apply path, compare the resolved schema directory's tree SHA at the PR's merge base against the current base; if it changed (or can't be verified), downgrade to manual confirmation instead of auto-applying against an advanced base. Repos can opt out via require_up_to_date_with_base; apply-confirm remains the deliberate override.
Teach the webhook test harness to serve faithful single-level git trees for non-recursive fetches so the base-branch drift gate's per-level path walk can be exercised, and add an integration test asserting that a schema change on the base branch downgrades automatic apply to manual confirmation.
There was a problem hiding this comment.
Pull request overview
Adds a default-on “up-to-date-with-base (schema subtree)” safety gate to the automatic apply path. If the resolved schema directory’s tree differs between the PR’s merge-base and the current base, SchemaBot downgrades from auto-apply to manual confirmation (apply-confirm can still override), avoiding auto-applying against a base that has moved under the plan.
Changes:
- Add GitHub primitive to compare schema-directory tree SHAs at merge-base vs current base and (best-effort) list changed schema files on base.
- Wire the gate into
apply(auto-confirm) flow with fail-closed downgrade messaging and shared downgrade handling. - Add config knobs (global + per-repo override) and comprehensive unit/integration coverage, including updates to the fake GitHub tree server for shallow tree walks.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/webhook/webhook_integration_test.go | Extends fake GitHub server to support shallow tree fetches + compare responses needed by the new base-drift gate. |
| pkg/webhook/base_drift_downgrade.go | Adds operator-facing downgrade reason builders (including capped file listing). |
| pkg/webhook/base_drift_downgrade_test.go | Unit tests for downgrade messaging behavior and file-list capping. |
| pkg/webhook/apply_handlers.go | Adds the base-drift gate to the automatic apply path and factors a shared downgrade tail helper. |
| pkg/webhook/apply_base_drift_integration_test.go | New integration test ensuring auto-apply downgrades when base schema subtree changed and records an action_required check. |
| pkg/github/base_drift.go | Implements SchemaDirChangedOnBase via merge-base resolution + shallow tree SHA comparison and filtered changed-file listing. |
| pkg/github/base_drift_test.go | Unit tests for base-drift detection and path filtering semantics. |
| pkg/api/config.go | Adds global + per-repo require_up_to_date_with_base config and resolution logic (default true). |
| pkg/api/config_test.go | Tests precedence and defaulting behavior for RequiresUpToDateWithBase. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…de metric The gate compared the schema subtree against GitHub's pull.base.sha snapshot, which for most PRs equals the merge base and silently short-circuits as "no drift" — exactly in the fast-moving-base case the gate exists for. Resolve the live base tip from the compare response instead, and count auto-apply downgrades so a GitHub API degradation is visible rather than silent.
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
Adds a default-on, path-scoped "up-to-date-with-base" gate. On the automatic apply path, if the resolved schema directory changed on the PR's base branch since the branch diverged, SchemaBot downgrades to manual confirmation instead of applying against a base that has moved underneath the plan. A PR can be thousands of commits behind and still auto-apply, as long as its schema subtree is unchanged.
What
SchemaDirChangedOnBase: given the base ref name, it comparesbaseRef...head, resolves the live base tip from the compare response'sBaseCommit(not the PR'spull.base.shasnapshot, which is typically the base tip at PR creation and would defeat the gate), then compares the git tree SHA of the resolved schema directory at the merge base vs the live base tip. Equal tree SHAs prove an identical subtree; a difference (or the directory being added/removed) means the base changed it. Path resolution walks one level at a time with shallow tree fetches, so it is truncation-safe and fails closed.handleApplyCommand) only.apply-confirmbypasses it because it goes through a different handler (not because of a nil stored plan). On downgrade the lock stays held so the operator can review and confirm.apply-confirm.schemabot.auto_apply.downgraded.totalwith areasonlabel (base_drift,base_drift_unverified,plan_load_failed) so a GitHub API degradation that downgrades applies fleet-wide is visible, not silent.require_up_to_date_with_base: global default plus a per-repository override. Precedence is repo override → global → defaulttrue. High-churn repositories can opt out.Why
Automatic apply re-plans against the PR head, but nothing checked whether the base branch had changed the schema directory since the branch diverged. In fast-moving repositories a plan could be auto-applied against a base that already moved, silently applying against a stale view. Whole-branch "commits behind" is unusable when the base changes constantly, so the gate is scoped to the schema subtree: only a change under the resolved schema root matters.
Before / after
Before: auto-apply
After: auto-apply
Known gaps (intentionally out of scope — do not flag as new issues)
These fall back to prior behavior (no gate) rather than regressing anything. The code comment on
SchemaDirChangedOnBaserecords the two symlink gaps; the rest are documented in the design doc:POST /api/applybypasses this gate (operator-authed direct plan execution); pre-existing, by-design entry-point asymmetry.Deferred follow-ups (tracked — do not flag as new issues)
Tracked in the design doc's follow-ups (personal-kmuddukrishna:
design-stuff/schemabot-apply-base-drift-gate/follow-ups.md):schemaPath == ".") makes the gate whole-repo sensitive; needs a changed-file-list path or documented degradation.head...basecompare and deduptreeSHAForPathagainstfetchGitSubtreeCached.postAutoApplyDowngradeso it also records theaction_requiredcheck and refreshes the aggregate (today it does neither).