Skip to content

Rename rate-limit to user-rate-limit and migrate max-runs to max-runs-per-window#31390

Merged
pelikhan merged 6 commits intomainfrom
copilot/rename-rate-limit-to-user-rate-limit
May 11, 2026
Merged

Rename rate-limit to user-rate-limit and migrate max-runs to max-runs-per-window#31390
pelikhan merged 6 commits intomainfrom
copilot/rename-rate-limit-to-user-rate-limit

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 10, 2026

Renames rate-limit frontmatter to user-rate-limit and migrates nested max keys to max-runs-per-window, with codemod support and compatibility for legacy configs.

Changes Made

  • Added codemod support to migrate:
    • top-level rate-limituser-rate-limit
    • nested max-runs/maxmax-runs-per-window (scoped to direct children of the rate-limit block only)
  • Added regression tests to ensure unrelated or nested max-runs keys are not rewritten.
  • Updated compiler/schema/docs/workflow examples for canonical user-rate-limit naming while keeping legacy parsing compatibility.
  • Adjusted experimental warning wording to key-agnostic text (rate limiting) to avoid confusion when legacy rate-limit is used.
  • Corrected docs example to use schema-valid integer window minutes (window: 60).

Testing

  • Targeted codemod tests for rate-limit migration behavior
  • Integration test for rate-limit experimental warning output
  • Lint and build validation (make golint, go build ./cmd/gh-aw)

Copilot AI and others added 2 commits May 10, 2026 22:54
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 Rename rate-limit to user-rate-limit with codemod migration Rename rate-limit to user-rate-limit and migrate max-runs to max-runs-per-window May 10, 2026
Copilot AI requested a review from pelikhan May 10, 2026 23:01
@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot make sure the codemod only updates the rate-limit, not other max-runs

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

Copilot AI commented May 10, 2026

@copilot make sure the codemod only updates the rate-limit, not other max-runs

Fixed in c44250e: the codemod now only renames max-runs/max when they are direct children of the migrated rate-limit block, and leaves other/nested max-runs entries untouched. Added regression tests for both cases.

@pelikhan pelikhan marked this pull request as ready for review May 10, 2026 23:35
Copilot AI review requested due to automatic review settings May 10, 2026 23:35
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

Renames the rate-limiting frontmatter configuration from rate-limit to user-rate-limit and migrates the nested max key to max-runs-per-window, while keeping rate-limit as a documented legacy alias. The PR also adds a CLI codemod and updates schema/docs/autocomplete/test fixtures to match the new naming.

Changes:

  • Update compiler frontmatter extraction and experimental warning messaging to prefer user-rate-limit while still accepting legacy rate-limit.
  • Add a rate-limituser-rate-limit codemod (including targeted tests) that migrates max-runs/maxmax-runs-per-window only within the rate-limit block.
  • Update JSON schema, docs, autocomplete assets, and workflow/examples to use user-rate-limit + max-runs-per-window (and document rate-limit as legacy).
Show a summary per file
File Description
pkg/workflow/role_checks.go Prefer user-rate-limit, accept legacy key, and migrate internal parsing to max-runs-per-window.
pkg/workflow/rate_limit_experimental_warning_test.go Update integration test workflows and expected warning text to user-rate-limit.
pkg/workflow/frontmatter_types.go Rename JSON tags to user-rate-limit and max-runs-per-window.
pkg/workflow/compiler_validators.go Update experimental warning text to reference user-rate-limit.
pkg/parser/schemas/main_workflow_schema.json Add user-rate-limit schema and document rate-limit as legacy alias; migrate required max key.
pkg/cli/workflows/test-rate-limit-ignored-roles.md Update CLI test workflow to new frontmatter keys.
pkg/cli/workflows/test-rate-limit-defaults.md Update CLI test workflow to new frontmatter keys.
pkg/cli/fix_codemods.go Register new rate-limit codemod in the fix command codemod registry.
pkg/cli/fix_codemods_test.go Assert new codemod is present and ordered correctly.
pkg/cli/codemod_user_rate_limit.go New codemod to rename rate-limituser-rate-limit and migrate max key within the block.
pkg/cli/codemod_user_rate_limit_test.go Unit tests for the new codemod, including non-overreach cases.
docs/src/content/docs/reference/rate-limiting-controls.md Update reference docs to new key names.
docs/src/content/docs/reference/frontmatter-full.md Update full frontmatter reference snippet to new key names.
docs/src/content/docs/reference/cost-management.md Update examples and references to the new rate-limit naming.
docs/src/content/docs/guides/maintaining-repos.md Update guide examples to the new rate-limit naming.
docs/scripts/generate-autocomplete-data.js Update autocomplete root ordering to prefer user-rate-limit.
docs/public/editor/autocomplete-data.json Refresh editor autocomplete data to include user-rate-limit and legacy rate-limit (plus other generated updates).
.github/workflows/workflow-generator.md Update repo workflow frontmatter to new keys.
.github/workflows/auto-triage-issues.md Update repo workflow frontmatter to new keys.
.github/workflows/ai-moderator.md Update repo workflow frontmatter to new keys.
.github/aw/syntax.md Update syntax docs to reference user-rate-limit and max-runs-per-window.

Copilot's findings

Tip

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

Comments suppressed due to low confidence (1)

pkg/workflow/role_checks.go:249

  • The schema allows user-rate-limit.max-runs-per-window to be a GitHub Actions expression string, but extractRateLimitConfig only handles numeric types and silently ignores string values. That means configs like max-runs-per-window: ${{ inputs.max }} will fall back to the default (5) at runtime instead of using the expression. Consider either (1) supporting string values by carrying a raw string through to the generated GH_AW_RATE_LIMIT_MAX env var, or (2) tightening the schema/docs to disallow expressions if they aren’t actually supported.
			// Extract max-runs-per-window (default: 5)
			maxValue, ok := v["max-runs-per-window"]
			if !ok {
				maxValue, ok = v["max-runs"]
			}
			if !ok {
				maxValue, ok = v["max"] // legacy compatibility
			}
			if ok {
				switch max := maxValue.(type) {
				case int:
					config.Max = max
				case int64:
					config.Max = int(max)
				case uint64:
					config.Max = int(max)
				case float64:
					config.Max = int(max)
				}
  • Files reviewed: 21/21 changed files
  • Comments generated: 2

Comment thread pkg/workflow/compiler_validators.go Outdated
Comment on lines 278 to 281
// Emit experimental warning for user-rate-limit feature
if workflowData.RateLimit != nil {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Using experimental feature: rate-limit"))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Using experimental feature: user-rate-limit"))
c.IncrementWarningCount()
max-runs: 5
user-rate-limit:
max-runs-per-window: 5
window: 1h
Generated by the Design Decision Gate workflow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

Commit pushed: b7518af

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@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/) 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/31390-rename-rate-limit-to-user-rate-limit.md

The draft has been committed directly to this PR branch.

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 — confirm the deciders list, refine the rationale, and add any alternatives or constraints the AI couldn't infer (e.g., whether downstream consumers were consulted, timeline for dropping the legacy keys)
  3. Update the status from Draft to Proposed or Accepted once finalized
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-31390: Rename rate-limit to user-rate-limit

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

Why this PR triggered the gate

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 in this repo are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 31390-rename-rate-limit-to-user-rate-limit.md for PR #31390).

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

References: §25642880879

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

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 — this PR is a rename/migration refactor with a backward-compat layer and associated codemod, so test coverage quality and behavior correctness are the primary lens.

Key Themes

  • Silent legacy path: rate-limit is accepted at compile time but users are only notified via a debug log, not a visible deprecation warning. The codemod (gh aw fix) handles it, but compile-time silence means authors who don't run fix won't know.
  • Undocumented intermediate key: max-runs is accepted as an alias in extractRateLimitConfig, but it doesn't appear in the schema, docs, or codemod. Its provenance is unclear.
  • Backward-compat gap in tests: The existing test file was updated to user-rate-limit throughout; no test exercises the rate-limitextractRateLimitConfig compat path.

Positive Highlights

  • ✅ Excellent codemod implementation — the findAndReplaceInLine prefix-matching approach is safe and well-scoped
  • DoesNotRenameOtherMaxRuns and DoesNotRenameNestedMaxRuns tests show disciplined edge-case thinking
  • ✅ Schema, docs, autocomplete data, and syntax guide are all updated consistently
  • ✅ Codemod is properly registered in fix_codemods.go and covered in fix_codemods_test.go
  • ✅ Skips ambiguous documents (both old and new keys present) — good defensive default

Verdict

This is a well-executed rename with good tooling support. The three inline comments flag the main gaps — particularly the lack of a user-visible deprecation warning for the silent legacy path, which is the most important one to address before merge.

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

roleLog.Print("No rate-limit configuration specified")
roleLog.Print("No user-rate-limit configuration specified")
return nil
}
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] The legacy rate-limit key path is detected but only logged at debug level — no user-facing deprecation warning is emitted. Users with old configs won't know they need to migrate, and CI won't surface it.

Consider emitting a console warning when the legacy key is used:

if legacyKey {
    fmt.Fprintln(os.Stderr, console.FormatWarningMessage(
        "Deprecated: 'rate-limit' is renamed to 'user-rate-limit'. Run 'gh aw fix' to migrate automatically."))
    c.IncrementWarningCount()
}

This matches how other deprecations (e.g. needs.activation.outputs.*) are surfaced to authors.

if !ok {
maxValue, ok = v["max"] // legacy compatibility
}
if ok {
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] The intermediate max-runs key is silently accepted as a third alias (alongside max and max-runs-per-window), but it isn't documented anywhere — not in the schema, syntax guide, or docs. This creates a hidden third valid value that can confuse future maintainers and makes the migration story harder to reason about.

If max-runs was never a public key, remove this branch. If it was, add it to the schema as a deprecated alias and add a test case for it.

@@ -22,12 +22,12 @@ func TestRateLimitExperimentalWarning(t *testing.T) {
expectWarning bool
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] The test suite was updated to use user-rate-limit throughout, but the backward-compat path (accepting the old rate-limit key at compile time) has no test coverage. A regression here would be silent.

Add a test case:

{
    name: "legacy rate-limit key still compiles without error",
    content: `---
on: workflow_dispatch
engine: copilot
rate-limit:
  max: 5
  window: 60
permissions:
  contents: read
---
`,
    expectWarning: true,
},

This verifies the backward-compat branch in extractRateLimitConfig and provides a regression harness for future cleanup.

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 90/100

Excellent test quality

Metric Value
New/modified tests analyzed 7
✅ Design tests (behavioral contracts) 6 (86%)
⚠️ Implementation tests (low value) 1 (14%)
Tests with error/edge cases 6 (86%)
Duplicate test clusters 0
Test inflation detected No (ratio 1.67:1)
🚨 Coding-guideline violations 1 — missing assertion messages (soft)

Test Classification Details

View all 7 test classifications
Test File Classification Issues Detected
TestGetRateLimitToUserRateLimitCodemod pkg/cli/codemod_user_rate_limit_test.go:12 ⚠️ Implementation Checks struct metadata fields only; no Apply behavior; missing assertion messages
TestRateLimitToUserRateLimitCodemod_RenamesRateLimitAndMaxRuns pkg/cli/codemod_user_rate_limit_test.go:22 ✅ Design Verifies observable transformation output; some assertions missing messages
TestRateLimitToUserRateLimitCodemod_RenamesLegacyMaxKey pkg/cli/codemod_user_rate_limit_test.go:55 ✅ Design Edge case: legacy max key renamed correctly
TestRateLimitToUserRateLimitCodemod_NoRateLimitField pkg/cli/codemod_user_rate_limit_test.go:82 ✅ Design Edge case: no-op when rate-limit is absent
TestRateLimitToUserRateLimitCodemod_SkipsWhenBothKeysPresent pkg/cli/codemod_user_rate_limit_test.go:107 ✅ Design Edge case: skip when migration already partially done; missing assertion messages
TestRateLimitToUserRateLimitCodemod_DoesNotRenameOtherMaxRuns pkg/cli/codemod_user_rate_limit_test.go:131 ✅ Design Edge case: scope isolation — sibling max-runs fields untouched
TestRateLimitToUserRateLimitCodemod_DoesNotRenameNestedMaxRuns pkg/cli/codemod_user_rate_limit_test.go:160 ✅ Design Edge case: scope isolation — nested max-runs fields untouched

Modifications to existing tests:

  • pkg/cli/fix_codemods_test.go: Adds "rate-limit-to-user-rate-limit" to the codemod ID and ordering lists — correct registration check ✅
  • pkg/workflow/rate_limit_experimental_warning_test.go: Pure rename churn updating test names and YAML content strings to match the new user-rate-limit / max-runs-per-window schema — no logic changes ✅

Flagged Tests — Requires Review

⚠️ TestGetRateLimitToUserRateLimitCodemod (pkg/cli/codemod_user_rate_limit_test.go:12)

Classification: Implementation test
Issue: Only asserts struct fields (ID, Name, Description, IntroducedIn, Apply non-nil). It does not invoke Apply or verify any transformation behavior.
What design invariant does this test enforce? Codemod registration metadata is set correctly — important for discoverability in the fix command, but not a behavioral contract.
What would break if deleted? A typo in the codemod ID or version string would go undetected until runtime. The six behavioral tests below would still catch transformation regressions.
Suggested improvement: Either merge these metadata assertions into one of the behavioral tests (e.g., TestRateLimitToUserRateLimitCodemod_RenamesRateLimitAndMaxRuns) or keep this test but add a message to each assertion for clarity.

i️ Missing assertion messages (multiple tests)

Classification: Guideline violation (soft — not a hard failure condition)
Issue: Many assert.Contains, assert.NotContains, assert.False, and assert.Equal calls lack a descriptive message argument. Examples:

// ❌ Missing message
assert.Contains(t, result, "user-rate-limit:")
assert.False(t, applied)

// ✅ With message
assert.Contains(t, result, "user-rate-limit:", "result should contain renamed key")
assert.False(t, applied, "codemod should be skipped when both keys are present")

Suggested improvement: Add a short descriptive string as the last argument to each bare assert.* call so failures are immediately actionable.


Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 7 tests — unit (//go:build !integration) ✅
  • 🟨 JavaScript (*.test.cjs, *.test.js): 0 tests

Scoring Breakdown

Component Weight Score
Behavioral coverage (6/7 = 86%) 40 pts 34
Error/edge case coverage (6/7 = 86%) 30 pts 26
Duplication penalty (0 clusters) 20 pts 20
Test inflation (1.67:1 ratio, under 2:1) 10 pts 10
Total 100 pts 90

Verdict

Check passed. 14% of new tests are implementation tests (threshold: 30%). No mock libraries, no missing build tags. Minor guideline note: add assertion messages to the bare assert.* calls.


📖 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 or struct metadata
  • 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.

References: §25642880880

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

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: 90/100. Test quality is excellent — 14% of new tests are implementation tests (threshold: 30%). No mock libraries, no missing build tags. Minor note: add descriptive assertion messages to bare assert.* calls for better failure diagnostics.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments

Copilot AI and others added 2 commits May 10, 2026 23:55
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 11, 2026

@copilot review all comments

Done in 8e39880.

I addressed the remaining review threads: updated the experimental warning to key-agnostic wording (rate limiting) and fixed the docs example to use window: 60 to match schema validation.

@pelikhan pelikhan merged commit b0dbcb7 into main May 11, 2026
@pelikhan pelikhan deleted the copilot/rename-rate-limit-to-user-rate-limit branch May 11, 2026 00:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants