Skip to content

Enforce compile-time rejection of queue: max with cancel-in-progress: true in concurrency config#33353

Merged
pelikhan merged 7 commits into
mainfrom
copilot/enforce-concurrency-restriction
May 19, 2026
Merged

Enforce compile-time rejection of queue: max with cancel-in-progress: true in concurrency config#33353
pelikhan merged 7 commits into
mainfrom
copilot/enforce-concurrency-restriction

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 19, 2026

This change enforces a missing concurrency invariant in the compiler: queue: max must not be combined with cancel-in-progress: true. The validation now fails fast for both workflow-level and engine-level concurrency blocks.

  • What changed

    • Added validateConcurrencyQueueConfiguration(...) to detect the invalid combination in serialized concurrency YAML.
    • Wired the check into compiler validation paths for:
      • top-level concurrency
      • engine.concurrency
    • Standardized the resulting validation error to clearly call out the forbidden pair.
  • Coverage updates

    • Added focused unit tests for queue/cancel compatibility cases.
    • Added integration cases to ensure compile-time failure for invalid combinations at both config levels.
  • Behavioral example

    concurrency:
      group: "gh-aw-${{ github.workflow }}"
      cancel-in-progress: true
      queue: max

    This now produces a compiler validation error (instead of allowing an invalid config through).

Copilot AI and others added 3 commits May 19, 2026 16:11
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title Enforce invalid queue:max + cancel-in-progress:true concurrency configs Enforce compile-time rejection of queue: max with cancel-in-progress: true in concurrency config May 19, 2026
@pelikhan pelikhan marked this pull request as ready for review May 19, 2026 16:19
Copilot AI review requested due to automatic review settings May 19, 2026 16:19
Copilot AI requested a review from pelikhan May 19, 2026 16:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Enforces a compile-time concurrency invariant so that queue: max cannot be combined with cancel-in-progress: true in serialized concurrency YAML, preventing invalid GitHub Actions workflow configs from compiling.

Changes:

  • Added validateConcurrencyQueueConfiguration(...) to detect the forbidden queue: max + cancel-in-progress: true combination.
  • Wired the new validation into compiler validation for both workflow-level concurrency and engine.concurrency.
  • Added unit + integration tests covering acceptance/rejection cases at both config levels.
Show a summary per file
File Description
pkg/workflow/concurrency_validation.go Adds queue/cancel compatibility validation via regex checks over serialized concurrency YAML.
pkg/workflow/compiler_validators.go Calls the new validation for workflow-level and engine-level concurrency before group-expression validation.
pkg/workflow/concurrency_validation_test.go Adds focused unit tests for the invalid pair and allowed combinations.
pkg/workflow/concurrency_validation_integration_test.go Adds integration cases ensuring compile-time failure for invalid configs at both levels.
.github/workflows/smoke-otel-backends.lock.yml Updates Datadog MCP header env-var reference to DD_APPLICATION_KEY.

Copilot's findings

Tip

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

  • Files reviewed: 5/5 changed files
  • Comments generated: 0

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot Lint Go and fix issues. Lint JavaScript and fix issues.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /tdd and /diagnose based on the validation enforcement and test coverage in this PR.

Key Themes

  • Test coverage: Solid foundation with 5 test cases, but a few edge cases are missing (empty strings, omitted cancel-in-progress, whitespace-only input)
  • Observability: Adding debug logging for partial matches would improve diagnosability

Positive Highlights

Excellent test structure — Unit tests + integration tests at both workflow and engine levels

Clear error messages — The validation error provides actionable guidance

Consistent validation placement — Applied symmetrically to both concurrency config locations

Good separation of concerns — New validation function is focused and reusable

Comprehensive test coverage — Both valid and invalid scenarios are tested

Verdict

This is a clean, well-tested validation enhancement. The suggestions above are minor improvements around edge case coverage and diagnostic robustness — none are blocking. The core logic is sound and the PR delivers exactly what the title promises.

Approving with confidence! 🚀


Learn more about the skills applied:

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · ● 2.9M

queue: max`,
wantErr: false,
},
{
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.

[/tdd] Missing test case: queue: max without explicit cancel-in-progress.

You have "queue max with cancel false is allowed" on line 783, but consider also testing the omitted case:

{
    name: "queue max without cancel-in-progress is allowed",
    concurrency: `concurrency:
  group: "my-group"
  queue: max`,
    wantErr: false,
},

This tests the common case where users don't explicitly set cancel-in-progress (it defaults to false in GitHub Actions). The current test only covers the explicit false case.

},
}

for _, tt := range tests {
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.

[/tdd] Missing edge case: empty string after whitespace normalization.

Consider adding a test case for empty/whitespace-only concurrency strings to verify the early return path:

{
    name: "empty concurrency string is allowed",
    concurrency: "",
    wantErr: false,
},
{
    name: "whitespace-only concurrency is allowed",
    concurrency: "   \n  \t  ",
    wantErr: false,
},

Explicitly testing boundary conditions makes the contract clearer and prevents regressions if the empty-check logic changes.

Comment thread pkg/workflow/concurrency_validation.go Outdated
return nil
}

if concurrencyQueueMaxPattern.MatchString(concurrencyYAML) &&
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.

[/diagnose] Consider adding debug logging for partial matches.

The current implementation silently succeeds if only one pattern matches. While correct, this makes debugging harder if the regex patterns are wrong. Consider logging when matches occur:

hasQueueMax := concurrencyQueueMaxPattern.MatchString(concurrencyYAML)
hasCancelTrue := concurrencyCancelInProgressTruePattern.MatchString(concurrencyYAML)

if hasQueueMax || hasCancelTrue {
    concurrencyValidationLog.Printf("queue:max=%v cancel:true=%v", hasQueueMax, hasCancelTrue)
}

if hasQueueMax && hasCancelTrue {
    return NewValidationError(...)
}

This makes it easier to diagnose regex issues during development and provides useful debug logs.

@github-actions
Copy link
Copy Markdown
Contributor

Commit pushed: 776ad68

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · ● 5.2M

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (>100 new lines in pkg/workflow/) but does not have a linked Architecture Decision Record (ADR).

AI has analyzed the PR diff and generated a draft ADR to help you get started:

📄 Draft ADR: docs/adr/33353-reject-queue-max-with-cancel-in-progress-at-compile-time.md

The draft has been committed directly to this branch and captures the decision (compile-time rejection of queue: max + cancel-in-progress: true), the alternatives weighed (runtime fallback to GitHub Actions, structural YAML parsing, silent rewriting), and the trade-offs of the regex-based detection approach. It also includes a normative RFC 2119 specification of the validation behavior.

What to do next

  1. Review the draft ADR on your branch — it was generated from the PR diff and may have gaps the AI could not infer.
  2. Complete or correct any sections — especially the Deciders line (currently Unknown) and any context the AI missed.
  3. Commit the finalized ADR to docs/adr/ on your branch (overwrite the draft).
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-33353: Reject queue: max with cancel-in-progress: true at Compile Time

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

Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

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., 33353-*.md for PR #33353).

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

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · ● 5.2M ·

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 80/100

Excellent test quality

Metric Value
New/modified tests analyzed 7
✅ Design tests (behavioral contracts) 7 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 7 (100%)
Duplicate test clusters 0
Test inflation detected YES (4.09:1 ratio)
🚨 Coding-guideline violations 0 (critical)

Test Classification Details

Test File Classification Issues Detected
TestValidateConcurrencyQueueConfiguration (5 cases) pkg/workflow/concurrency_validation_test.go ✅ Design ⚠️ Missing assertion messages; test inflation 4.09:1
Integration test cases (2 added) pkg/workflow/concurrency_validation_integration_test.go ✅ Design None

Test Details

📋 View Comprehensive Test Analysis

TestValidateConcurrencyQueueConfiguration (Unit Test)

File: pkg/workflow/concurrency_validation_test.go:758

Design Invariant Enforced: GitHub Actions' constraint that queue: max cannot be combined with cancel-in-progress: true

Test Cases (5 total):

  1. ❌ queue max with cancel true is rejected
  2. ❌ quoted queue max with cancel true is rejected
  3. ✅ queue max with cancel false is allowed
  4. ✅ queue single with cancel true is allowed
  5. ✅ string concurrency is allowed

Coverage:

  • ✅ Error paths: 2 invalid configurations tested
  • ✅ Edge cases: 3 valid boundary cases tested
  • ✅ Error message verification: Validates specific error text
  • ✅ Table-driven design: Clean, maintainable test structure

What would break if deleted? The validation function validateConcurrencyQueueConfiguration() could return incorrect results for these edge cases, allowing invalid workflow configurations to pass compilation.

Issues:

  • ⚠️ Missing assertion messages: 2 of 3 assertions lack descriptive context. Example: assert.Error(t, err) should be assert.Error(t, err, "should reject queue:max with cancel-in-progress:true")
  • ⚠️ Test inflation: 58 lines of test code for 23 lines of production code (2.52:1 ratio)

Integration Test Cases (2 added)

File: pkg/workflow/concurrency_validation_integration_test.go

Design Invariant Enforced: The queue/cancel-in-progress constraint applies at both workflow-level and engine-level concurrency configurations

Test Cases (2 total):

  1. ❌ Invalid workflow-level concurrency - queue max with cancel true
  2. ❌ Invalid engine-level concurrency - queue max with cancel true

Coverage:

  • ✅ End-to-end validation through full frontmatter parsing
  • ✅ Error message propagation verification
  • ✅ Both concurrency configuration levels tested

What would break if deleted? The integration between the validation function and the compiler's frontmatter parser could regress, allowing invalid configurations to pass through even if the validation function itself is correct.


Flagged Tests — Review Suggested

⚠️ TestValidateConcurrencyQueueConfiguration (pkg/workflow/concurrency_validation_test.go:758)

Classification: Design test (high value)

Issues:

  1. Missing assertion messages — Project guidelines require descriptive messages on all assertions for better failure diagnostics
  2. Test inflation — 58 lines of test code for 23 lines of production code (2.52:1 ratio). While table-driven tests naturally have overhead, this ratio is higher than typical.

What design invariant does this test enforce? GitHub Actions' documented constraint that queue: max cannot coexist with cancel-in-progress: true in concurrency configuration.

What would break if deleted? The validation function could incorrectly accept invalid concurrency configurations, leading to GitHub Actions workflow failures at runtime.

Suggested improvements:

  1. Add descriptive assertion messages:
if tt.wantErr {
    assert.Error(t, err, "should reject invalid queue/cancel-in-progress combination")
    assert.Contains(t, err.Error(), "queue: max cannot be combined", 
        "error message should explain the constraint")
    return
}
assert.NoError(t, err, "valid concurrency configuration should pass validation")
  1. Consider consolidating cases to reduce line count (optional):
    • Cases 1 & 2 (unquoted vs quoted max) could potentially be a single parameterized case
    • Cases 3, 4, 5 cover distinct edge cases and should remain separate

Language Support

Tests analyzed:


Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%).

Score: 80/100 — Excellent behavioral coverage. All tests validate observable validation behavior and include both error paths and edge cases. The test inflation penalty reflects the 4.09:1 test-to-production ratio, but this is acceptable for comprehensive validation testing.

Suggestions:

  • Add assertion messages to improve failure diagnostics
  • The test coverage is thorough; consider if any cases could be consolidated

📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · ● 6.1M ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 80/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). All tests validate behavioral contracts with comprehensive error and edge case coverage.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 19, 2026

@copilot Lint Go and fix issues. Lint JavaScript and fix issues.

Done in 171cb2d.

  • Ran JavaScript lint (make lint-cjs) and Go lint (make golint).
  • Fixed Go lint violations (testify require.Error usage) and applied gofmt to flagged files.
  • Re-ran lint and targeted tests (go test ./pkg/workflow ./pkg/gitutil) successfully.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 19, 2026

@copilot review all comments.

Completed in 3412b3e.

Addressed all actionable comments:

  • Added missing queue/cancel test cases (including omitted cancel-in-progress, empty, and whitespace-only inputs).
  • Added debug logging for partial queue/cancel regex matches.
  • Updated ADR deciders from Unknown.

Re-validated with make lint-cjs, make golint, and go test ./pkg/workflow (all pass).

@pelikhan pelikhan merged commit 86059ae into main May 19, 2026
@pelikhan pelikhan deleted the copilot/enforce-concurrency-restriction branch May 19, 2026 17:19
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