Skip to content

Extend stacked PR max-stack gating to pull_request_review - #49453

Merged
pelikhan merged 3 commits into
mainfrom
copilot/review-stacked-pr-support
Aug 1, 2026
Merged

Extend stacked PR max-stack gating to pull_request_review#49453
pelikhan merged 3 commits into
mainfrom
copilot/review-stacked-pr-support

Conversation

Copilot AI commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Stacked PR support previously gated pull_request workflows but left pull_request_review workflows ungated, so review-triggered runs still executed on lower stack layers. This PR aligns review-event behavior with the existing stack filter contract.

  • Trigger/filter behavior

    • Extends stack-aware gating to pull_request_review events.
    • Applies the same semantics as pull_request:
      • default max-stack: 1 → top-of-stack only
      • max-stack: N → top N layers
      • max-stack: -1 → disable stack gating
    • Updates trigger detection and max-stack extraction paths so object/array on: forms behave consistently.
  • Schema contract

    • Adds on.pull_request_review.max-stack to the main workflow schema.
    • Enforces the same value domain as pull request gating (-1 or >= 1; rejects 0 and other invalid negatives).
  • Compiler output / frontmatter cleanup

    • Marks max-stack under pull_request_review as compiler-processed in the rendered/commented on: section.
    • Regenerates affected .lock.yml workflows to reflect compiled gating behavior.
  • Coverage

    • Adds/updates unit and integration coverage for:
      • pull_request_review default and configured max-stack
      • non-arithmetic job-level if: constraints
      • pre-step stack gating for max-stack > 1
      • schema validation for valid/invalid review max-stack values
on:
  pull_request_review:
    types: [submitted]
    max-stack: 2

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title Add stacked PR max-stack support for pull_request_review Extend stacked PR max-stack gating to pull_request_review Aug 1, 2026
Copilot AI requested a review from pelikhan August 1, 2026 02:21
@pelikhan
pelikhan marked this pull request as ready for review August 1, 2026 02:34
Copilot AI review requested due to automatic review settings August 1, 2026 02:34
@pelikhan

pelikhan commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

@copilot ensure the max stack allows for intermediate value. - inequality

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends stacked-PR gating to pull_request_review workflows, including schema, compiler output, documentation, and tests.

Changes:

  • Adds review-trigger detection and max-stack extraction.
  • Adds schema validation and frontmatter cleanup.
  • Regenerates affected workflow lock files and expands tests.
Show a summary per file
File Description
pkg/workflow/frontmatter_on_section_cleanup.go Cleans review-event max-stack.
pkg/workflow/filters.go Implements review stack gating.
pkg/workflow/filters_stack_test.go Adds filter unit tests.
pkg/workflow/filters_stack_integration_test.go Adds compilation integration tests.
pkg/workflow/compiler_draft_test.go Tests frontmatter cleanup.
pkg/parser/schemas/main_workflow_schema.json Defines review max-stack.
pkg/parser/schema_location_test.go Tests schema value constraints.
docs/src/content/docs/reference/triggers.md Documents review stack filtering.
.github/workflows/visual-regression-checker.lock.yml Regenerates stack condition.
.github/workflows/test-quality-sentinel.lock.yml Regenerates stack condition.
.github/workflows/smoke-update-cross-repo-pr.lock.yml Regenerates stack condition.
.github/workflows/smoke-test-tools.lock.yml Regenerates stack condition.
.github/workflows/smoke-temporary-id.lock.yml Regenerates stack condition.
.github/workflows/smoke-project.lock.yml Regenerates stack condition.
.github/workflows/smoke-pi.lock.yml Regenerates stack condition.
.github/workflows/smoke-opencode.lock.yml Regenerates stack condition.
.github/workflows/smoke-multi-pr.lock.yml Regenerates stack condition.
.github/workflows/smoke-gemini.lock.yml Regenerates stack condition.
.github/workflows/smoke-create-cross-repo-pr.lock.yml Regenerates stack condition.
.github/workflows/smoke-copilot-arm.lock.yml Regenerates stack condition.
.github/workflows/smoke-codex.lock.yml Regenerates stack condition.
.github/workflows/smoke-claude.lock.yml Regenerates stack condition.
.github/workflows/smoke-ci.lock.yml Regenerates stack condition.
.github/workflows/smoke-call-workflow.lock.yml Regenerates stack condition.
.github/workflows/smoke-antigravity.lock.yml Regenerates stack condition.
.github/workflows/smoke-agent-scoped-approved.lock.yml Regenerates stack condition.
.github/workflows/smoke-agent-public-none.lock.yml Regenerates stack condition.
.github/workflows/smoke-agent-public-approved.lock.yml Regenerates stack condition.
.github/workflows/smoke-agent-all-none.lock.yml Regenerates stack condition.
.github/workflows/smoke-agent-all-merged.lock.yml Regenerates stack condition.
.github/workflows/refiner.lock.yml Regenerates stack condition.
.github/workflows/pr-description-caveman.lock.yml Regenerates stack condition.
.github/workflows/pr-code-quality-reviewer.lock.yml Regenerates stack condition.
.github/workflows/mattpocock-skills-reviewer.lock.yml Regenerates stack condition.
.github/workflows/impeccable-skills-reviewer.lock.yml Regenerates stack condition.
.github/workflows/firewall-escape.lock.yml Regenerates stack condition.
.github/workflows/design-decision-gate.lock.yml Regenerates stack condition.
.github/workflows/changeset.lock.yml Regenerates stack condition.
.github/workflows/ai-moderator.lock.yml Regenerates stack condition.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 39/39 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread pkg/workflow/filters.go
case float64:
if v == float64(int(v)) {
func extractStackMaxStackFromMap(onMap map[string]any) (int, bool) {
for _, triggerName := range []string{"pull_request", "pull_request_review"} {
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate — ADR Required

This PR makes significant changes to core workflow compilation logic (261 new lines in .github/ business logic paths) but does not have a linked Architecture Decision Record (ADR).

Draft ADR committed: docs/adr/49453-extend-max-stack-gating-to-pull-request-review.md — review and complete it before merging.

This PR cannot merge until an ADR is linked in the PR body.

What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff

  2. Complete the missing sections — add context the AI could not infer, refine the decision rationale, and list real alternatives you considered

  3. Commit the finalized ADR to docs/adr/ on your branch

  4. Reference the ADR in this PR body by adding a line such as:

    ADR: [ADR-49453: Extend max-stack Gating to pull_request_review Events](docs/adr/49453-extend-max-stack-gating-to-pull-request-review.md)

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

Why ADRs Matter

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 49453-extend-max-stack-gating-to-pull-request-review.md for PR #49453).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · sonnet46 · 61.4 AIC · ⌖ 25.3 AIC · ⊞ 9.6K ·
Comment /review to run again

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 71/100 — Acceptable

Analyzed 7 test(s): 5 design, 2 implementation, 0 violation(s).

📊 Metrics (7 tests)
Metric Value
Analyzed 7 (Go: 7, JS: 0)
✅ Design 5 (71%)
⚠️ Implementation 2 (29%)
Edge/error coverage 3 (43%)
Duplicate clusters 0
Inflation No (6.7:1 ratio justified by feature scope)
🚨 Violations 0
📋 Test Inventory
Test File Classification Coverage
TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_PullRequestReviewMaxStack schema_location_test.go design_test Validates max-stack values (5 scenarios: valid 1,2,-1; invalid 0,-2)
TestCommentOutProcessedFieldsInOnSection_PullRequestReviewMaxStack compiler_draft_test.go implementation_test YAML field commenting behavior (single assertion)
TestStackFilterPullRequestReviewMaxStack2UsesPreStep filters_stack_integration_test.go design_test (integration) End-to-end: max-stack:2 avoids arithmetic operators
TestApplyPullRequestStackFilter_ConfiguredMaxStackInArrayTriggerForm filters_stack_test.go implementation_test Array-form trigger config handling
TestApplyPullRequestStackFilter_SimplePullRequestReviewTrigger filters_stack_test.go design_test Default filter logic (equality-based)
TestApplyPullRequestStackFilter_PullRequestReviewConfiguredMaxStack filters_stack_test.go design_test Configured max-stack gate placement
(Table entry) filters_stack_integration_test.go design_test (implicit) Default max-stack integration test

✅ Verdict

Passed. 29% implementation tests (threshold: 30%). No violations. Solid behavioral coverage with table-driven validation (5 scenarios), end-to-end integration test, and comprehensive trigger-form testing.

Quality signals:

  • ✅ Multi-scenario validation (table-driven: 1 test covers 5 branches)
  • ✅ Integration test validates full compilation pipeline
  • ✅ Edge case: arithmetic operator prevention for max-stack > 1
  • ✅ No forbidden mocks, no build tag violations
  • ⚠️ One test (field commenting) relies on single assertion

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 25.2 AIC · ⊞ 8.4K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 71/100. 29% implementation tests (threshold: 30%). Solid behavioral coverage with table-driven validation (5 scenarios), end-to-end integration test, and comprehensive trigger-form testing. No violations.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

The stack-filter extension to pull_request_review looks correct.

  • github.event.pull_request is present in pull_request_review webhook payloads, so accessing .stack.position and .stack.size is valid.
  • The compiler in filters.go generates the updated condition correctly, and filters_stack_test.go adds test coverage for the new trigger type.
  • All lock files are mechanically regenerated with a consistent pattern.

No blocking issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 62.8 AIC · ⊞ 5.3K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Verdict: Approve with one correctness concern

Overall the extension of stack gating to pull_request_review is implemented consistently (trigger detection, condition strings, PreStep gate, schema, frontmatter cleanup) and is well covered by new unit/integration tests.

💡 Themes
  • Blocking-worthy but scoped: extractStackMaxStackFromMap resolves max-stack by checking pull_request before pull_request_review and returning on first match — if a workflow configures both triggers with different max-stack values, the value is silently collapsed to whichever trigger is checked first, regardless of which event actually fires. No validation catches this mismatch. Flagged inline.
  • Condition/PreStep generation for the dual-event case (github.event_name == 'pull_request' || 'pull_request_review') itself looks correct.
  • Docs, schema, and lock-file regeneration are consistent with the code changes.

Not requesting changes since this is a narrow edge case (dual-trigger workflows with divergent max-stack values) rather than a regression of the primary single-trigger behavior this PR targets, but it should be tracked and probably fixed before this ships broadly.

🔎 Code quality review by PR Code Quality Reviewer · auto · 42.6 AIC · ⊞ 7.8K
Comment /review to run again

Comment thread pkg/workflow/filters.go
case float64:
if v == float64(int(v)) {
func extractStackMaxStackFromMap(onMap map[string]any) (int, bool) {
for _, triggerName := range []string{"pull_request", "pull_request_review"} {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When both pull_request and pull_request_review triggers are configured on the same workflow with different max-stack values, this always picks pull_request's value regardless of which event actually fires at runtime — silently mis-gating the other trigger.

💡 Details

extractStackMaxStackFromMap iterates ["pull_request", "pull_request_review"] and returns on the first match. Since maxStack is baked into the compiled condition/PreStep as a static int (it can't vary per github.event_name at runtime), a workflow like:

on:
  pull_request:
    max-stack: 1
  pull_request_review:
    max-stack: 3

will use max-stack: 1 for both event types, even for pull_request_review runs — silently gating away review runs that the author intended to allow through the top 3 stack layers. No validation error is raised for this mismatch, and no test in this PR covers the dual-trigger case.

Suggested fix: either (a) require equal max-stack values across pull_request/pull_request_review when both are present (schema/compile-time validation error on mismatch), or (b) resolve the value per-trigger by embedding both values in the generated condition (e.g., via a ternary on github.event_name) instead of collapsing to a single static int.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Skills-Based Review 🧠

Applied /tdd and /codebase-design — requesting changes for two test coverage gaps and one undocumented contract.

📋 Key Themes & Highlights

Key Themes

  • Test coverage gap (positive assertion): The integration test for pull_request_review default max-stack: 1 only checks that no arithmetic appears; it never asserts the event name is present in the generated condition. A regression dropping the event would pass silently.
  • Test coverage gap (disabled path): max-stack: -1 is only tested for pull_request; the equivalent for pull_request_review is untested.
  • Undocumented priority: extractStackMaxStackFromMap silently gives pull_request priority over pull_request_review when both are present with different values.

Positive Highlights

  • ✅ Clean refactor from hasPullRequestTrigger / extractPullRequestMaxStack to unified helpers — handles string, array, and map on: forms consistently.
  • ✅ Schema validation with anyOf correctly rejects 0 and invalid negatives.
  • ✅ Pre-step approach for max-stack > 1 avoids GitHub Actions arithmetic limitation — well-tested end-to-end.
  • ✅ Lock files regenerated correctly.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 67.4 AIC · ⊞ 7K
Comment /matt to run again

Comments that could not be inline-anchored

pkg/workflow/filters_stack_integration_test.go:113

[/tdd] The integration test for pull_request_review with default max-stack: 1 only asserts absence of arithmetic — it has no positive assertion that the gating condition is actually applied (e.g., that pull_request_review appears in the generated if:).

<details>
<summary>💡 Suggested fix</summary>

Scope a positive assertion to the pull_request_review row, e.g.:

if strings.Contains(tt.name, &quot;pull_request_review&quot;) {
    require.Contains(t, lockStr, &quot;github.event_name != &#39;pu</details>

<details><summary>pkg/workflow/filters_stack_test.go:88</summary>

**[/tdd]** There is no test for `pull_request_review` with `max-stack: -1`the only disabled-gating test uses `pull_request`. A regression in `extractStackMaxStackFromMap`&#39;s iteration order could silently break the disable path for review events.

&lt;details&gt;
&lt;summary&gt;💡 Suggested test&lt;/summary&gt;

```go
func TestApplyPullRequestStackFilter_PullRequestReviewDisabled(t *testing.T) {
    compiler := NewCompiler()
    workflowData := &amp;WorkflowData{If: &quot;github.actor != &#39;dependabot[bot]&#39;&quot;}
    frontma…

</details>

<details><summary>pkg/workflow/filters.go:306</summary>

**[/codebase-design]** `extractStackMaxStackFromMap` iterates `[&quot;pull_request&quot;, &quot;pull_request_review&quot;]` and returns the first match, so `pull_request`&#39;s `max-stack` always wins when both triggers are present in the same map. This silent priority rule is undocumented and untested.

&lt;details&gt;
&lt;summary&gt;💡 Options&lt;/summary&gt;

Either add a comment documenting the intended priority (if it&#39;s deliberate), or add a test covering the mixed-trigger case:

```go
// When both pull_request and pull_request_re…

</details>

@pelikhan

pelikhan commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

… intermediate values

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

@copilot ensure the max stack allows for intermediate value. - inequality

Done in commit 7e88a7d. The pull_request_review unit test now explicitly asserts the STACK_POSITION + max_stack <= STACK_SIZE inequality (matching the pull_request test coverage), and both pull_request and pull_request_review schema validation tests now include a max-stack: 5 case to verify intermediate values are accepted.

@pelikhan
pelikhan merged commit b5ab73e into main Aug 1, 2026
28 of 29 checks passed
@pelikhan
pelikhan deleted the copilot/review-stacked-pr-support branch August 1, 2026 02:57
Copilot stopped work on behalf of pelikhan due to an error August 1, 2026 02:57
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.84.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants