Skip to content

feat: change RetryClassifier default to opt-in, update tests - #2

Merged
four-bytes-robby merged 8 commits into
mainfrom
feat/1-retry-classifier-opt-in
Jun 20, 2026
Merged

feat: change RetryClassifier default to opt-in, update tests#2
four-bytes-robby merged 8 commits into
mainfrom
feat/1-retry-classifier-opt-in

Conversation

@four-bytes-robby

@four-bytes-robby four-bytes-robby commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary

  • RetryClassifier default $transientClasses changed from [\RuntimeException::class] to [] — consumers must now opt in to retry specific exception classes
  • Prevents accidentally retrying Flysystem's UnableToXxx exceptions (which also extend RuntimeException)
  • Tests updated: testDefaultClassifierRetiesNothing() replaces old default-retry tests; RetryAdapterTest gains runtimeAdapter() helper for tests that explicitly want RuntimeException retries
  • ROADMAP updated to reflect completed phases and known limitations

Test plan

  • php vendor/bin/phpunit — 20 tests, all green

Closes #1

Summary by CodeRabbit

  • Bug Fixes / Behavior Change
    • Updated retry behavior so nothing is retried by default; retrying now requires explicitly configuring which exceptions are transient (opt-in).
    • Improved retry jitter calculation to keep the same effective scaling and clamping.
  • Documentation
    • Clarified opt-in transient exception classification in the README.
    • Refreshed HISTORY and ROADMAP with the current retry approach and limits.
    • Added/updated security policy, code of conduct, and contribution guidelines.
  • Tests
    • Updated retry-related tests to reflect the new default “no retries” behavior.
  • Chores
    • Added CI (unit tests + static analysis), Dependabot configuration, standardized issue/PR templates, and Composer script shortcuts.

#1

- RetryClassifier: default transientClasses changed from [\RuntimeException::class]
  to [] to avoid retrying Flysystem's UnableToXxx permanent exceptions which also
  extend RuntimeException
- RetryClassifierTest: replace old default-retries-RuntimeException tests with
  testDefaultClassifierRetiesNothing()
- RetryAdapterTest: add runtimeAdapter() helper; tests that expect RuntimeException
  retries now explicitly configure the classifier
- ROADMAP: mark phases 0-4 complete, document known limitations
…unity files #1

- README: correct RetryClassifier default — nothing retried by default (opt-in)
- Add CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md, HISTORY.md
- Add .github/workflows/ci.yml (PHP 8.2–8.4 matrix, PHPUnit + PHPStan)
- Add .github/dependabot.yml, pull_request_template.md, ISSUE_TEMPLATE/
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
Walkthrough

Walkthrough

RetryClassifier constructor is changed to assign the provided transient classes array directly, removing the implicit \RuntimeException::class fallback, making retry behavior fully opt-in. Tests are updated with a new runtimeAdapter helper that explicitly configures RuntimeException retries for tests that need them. Documentation is updated to reflect the opt-in default and completion status across roadmap phases. GitHub Actions CI, Dependabot, issue and PR templates, and community governance files are added.

Changes

RetryClassifier opt-in behavior change

Layer / File(s) Summary
RetryClassifier default and test updates
src/RetryClassifier.php, tests/RetryClassifierTest.php, tests/RetryAdapterTest.php
Constructor assignment simplified to direct array assignment, removing the \RuntimeException::class fallback. RetryClassifierTest replaces default-retry assertions with a test confirming nothing is retried by default. RetryAdapterTest adds a runtimeAdapter() helper for tests that need explicit \RuntimeException retries and updates six call sites.
Documentation alignment for opt-in default
README.md, HISTORY.md, ROADMAP.md, AGENTS.md
README custom exception classification section updated to state nothing is retried by default and narrows the example to TransientStorageException. HISTORY adds an unreleased section. ROADMAP updated across phases 0-5 to reflect completion status, known limitations, and acceptance criteria. AGENTS.md design decision updated.

GitHub and project infrastructure

Layer / File(s) Summary
CI workflow, Dependabot, and build scripts
.github/workflows/ci.yml, .github/dependabot.yml, composer.json
CI runs PHPUnit across PHP 8.2-8.4 and PHPStan at level 8. Dependabot schedules weekly Composer and GitHub Actions updates. Composer scripts section adds test and phpstan commands.
Issue templates, PR template, and community docs
.github/ISSUE_TEMPLATE/bug.yml, .github/ISSUE_TEMPLATE/feature.yml, .github/pull_request_template.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md, SECURITY.md
Structured GitHub issue forms for bugs and features, a standardized PR checklist, and community governance documents for conduct, contributions, and vulnerability reporting.

Estimated code review effort

[2] (Simple) | ~10 minutes

Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Several documentation and configuration changes (CODE_OF_CONDUCT.md, CONTRIBUTING.md, SECURITY.md, GitHub templates, workflows, Dependabot, composer.json scripts) are outside the scope of issue #1 but appear to be legitimate project infrastructure improvements. Review whether documentation files, GitHub templates, CI workflows, and Dependabot configuration should be part of this PR or committed separately as infrastructure setup work.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the primary change: converting RetryClassifier to opt-in behavior and updating corresponding tests.
Linked Issues check ✅ Passed All coding requirements from issue #1 are met: RetryClassifier default changed to empty array, RetryClassifierTest updated with testDefaultClassifierRetiesNothing, RetryAdapterTest enhanced with runtimeAdapter helper, and ROADMAP.md updated.

Tip: You can configure your own custom pre-merge checks in the settings.

Finishing Touches
Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/1-retry-classifier-opt-in
Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/1-retry-classifier-opt-in

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/RetryAdapterTest.php (1)

63-71: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

maxAttempts tests no longer exercise retry behavior

After the default classifier switched to opt-in, both tests now use a non-retryable RuntimeException path, so they no longer validate exhaustion/max-attempt semantics as named. Use runtimeAdapter() (or pass an explicit classifier) in these tests to keep their intent intact.

Proposed fix
@@
-        self::adapter($inner, maxAttempts: 2)->write('a.txt', 'hi', new Config());
+        self::runtimeAdapter($inner, maxAttempts: 2)->write('a.txt', 'hi', new Config());
@@
-        self::adapter($inner, maxAttempts: 1)->write('a.txt', '', new Config());
+        self::runtimeAdapter($inner, maxAttempts: 1)->write('a.txt', '', new Config());

Also applies to: 166-177

Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/RetryAdapterTest.php` around lines 63 - 71, The
testExhaustsMaxAttemptsAndRethrows test no longer validates retry behavior
because RuntimeException is no longer retryable by default after the classifier
switched to opt-in mode. To restore the intended test behavior, replace the
self::adapter call with runtimeAdapter() or pass an explicit classifier
parameter that configures RuntimeException as a retryable exception. Apply the
same fix to the other affected test mentioned (around lines 166-177) to ensure
both tests properly exercise the max-attempt exhaustion and retry semantics they
are designed to validate.
.github/workflows/ci.yml (1)

1-34: ⚠️ Potential issue | 🟠 Major

Add explicit least-privilege token permissions to the workflow.

No permissions: block is configured, allowing the workflow to run with default GitHub Actions token permissions that exceed requirements for checkout, install, test, and static analysis operations.

Proposed fix
 name: CI
 
 on:
   push:
     branches: [main]
   pull_request:
     branches: [main]
 
+permissions:
+  contents: read
+
 jobs:
   test:
     runs-on: ubuntu-latest
Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1 - 34, Add explicit least-privilege
token permissions to the workflow by inserting a `permissions:` block at the top
level (after the `on:` section and before the `jobs:` section). Set `contents:
read` as the only required permission, since the workflow only performs checkout
operations and subsequent local analysis tasks (composer install, phpunit, and
phpstan) that do not require additional token permissions. This ensures the
workflow follows the principle of least privilege by restricting the GitHub
Actions token to only the minimal access needed.

Source: Linters/SAST tools

Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 16-17: Replace the movable version tags in the GitHub Actions
workflow with immutable full 40-character commit SHA hashes. For the
`actions/checkout@v4` action on line 16 and `shivammathur/setup-php@v2` action
on line 17 (and any other instances on lines 27-28), replace the `@v4` and `@v2`
tags with their corresponding full commit SHAs to ensure the exact version is
always used and prevent supply-chain risks from tag retagging or action
modifications.
- Line 16: The workflow has two `actions/checkout@v4` steps that persist
credentials by default, which unnecessarily expands the attack surface for a
read-only workflow that only performs checks without pushing code. Add the
`persist-credentials: false` parameter to both `actions/checkout@v4` calls at
lines 16 and 27 to disable credential persistence and improve security posture.

In `@CONTRIBUTING.md`:
- Around line 4-9: The CONTRIBUTING.md file references running composer test on
line 4, but this script is not defined in the composer.json file. To fix this,
either add a test script entry to the scripts section of composer.json that runs
the explicit commands vendor/bin/phpunit and vendor/bin/phpstan analyse src
tests --level=8, or update the CONTRIBUTING.md line 4 to replace the composer
test reference with these explicit commands so contributors understand exactly
what commands to run.

In `@ROADMAP.md`:
- Around line 14-16: Update AGENTS.md to reflect the new opt-in default
transient class list behavior. Find all references in AGENTS.md that mention the
default transient class list being `[\RuntimeException::class]` and update them
to document that the new default is `[]` (empty, opt-in). Ensure any examples or
explanations about RetryClassifier initialization in AGENTS.md clarify that
users must now explicitly pass the desired transient exception classes as
constructor arguments, consistent with how ROADMAP.md documents this change.

---

Outside diff comments:
In @.github/workflows/ci.yml:
- Around line 1-34: Add explicit least-privilege token permissions to the
workflow by inserting a `permissions:` block at the top level (after the `on:`
section and before the `jobs:` section). Set `contents: read` as the only
required permission, since the workflow only performs checkout operations and
subsequent local analysis tasks (composer install, phpunit, and phpstan) that do
not require additional token permissions. This ensures the workflow follows the
principle of least privilege by restricting the GitHub Actions token to only the
minimal access needed.

In `@tests/RetryAdapterTest.php`:
- Around line 63-71: The testExhaustsMaxAttemptsAndRethrows test no longer
validates retry behavior because RuntimeException is no longer retryable by
default after the classifier switched to opt-in mode. To restore the intended
test behavior, replace the self::adapter call with runtimeAdapter() or pass an
explicit classifier parameter that configures RuntimeException as a retryable
exception. Apply the same fix to the other affected test mentioned (around lines
166-177) to ensure both tests properly exercise the max-attempt exhaustion and
retry semantics they are designed to validate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f34f6c0e-1cef-415e-ad2e-eb516108b481

Commits

Reviewing files that changed from the base of the PR and between 0780c1f and be80cd3.

Files selected for processing (14)
  • .github/ISSUE_TEMPLATE/bug.yml
  • .github/ISSUE_TEMPLATE/feature.yml
  • .github/dependabot.yml
  • .github/pull_request_template.md
  • .github/workflows/ci.yml
  • CODE_OF_CONDUCT.md
  • CONTRIBUTING.md
  • HISTORY.md
  • README.md
  • ROADMAP.md
  • SECURITY.md
  • src/RetryClassifier.php
  • tests/RetryAdapterTest.php
  • tests/RetryClassifierTest.php

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread CONTRIBUTING.md
Comment thread ROADMAP.md

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 14 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="CONTRIBUTING.md">

<violation number="1" location="CONTRIBUTING.md:8">
P3: This instruction points contributors to `composer test`, but the documented build/test flow here uses explicit PHPUnit and PHPStan commands. Align this step with executable commands (or add the script) to avoid a broken onboarding path.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread .github/workflows/ci.yml Outdated
Comment thread CONTRIBUTING.md
…ermissions, composer scripts #1

- testExhaustsMaxAttemptsAndRethrows: use runtimeAdapter() so RuntimeException is actually retried and exhaustion is tested
- testMaxAttemptsOfOneNeverRetries: same fix
- .github/workflows/ci.yml: add permissions: contents: read, persist-credentials: false on both checkout steps
- composer.json: add scripts.test and scripts.phpstan so `composer test` works as documented
- AGENTS.md: correct RetryClassifier default description to [] (opt-in)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/RetryPolicy.php`:
- Line 20: The code on line 20 in the RetryPolicy.php file uses
Random\Randomizer::nextFloat() which requires PHP 8.3+, but the project requires
PHP 8.2+ compatibility. To fix this, replace the nextFloat() call with an
equivalent implementation using mt_rand() that generates a random float value
compatible with PHP 8.2, maintaining the same jitter calculation logic.
Alternatively, if dropping PHP 8.2 support is acceptable, update the PHP version
requirement to 8.3+ in the coding guidelines documentation and ROADMAP.md.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d325d7a1-c54a-4b15-9a98-39aed7c0723c

Commits

Reviewing files that changed from the base of the PR and between 233f9be and f69d5fb.

Files selected for processing (1)
  • src/RetryPolicy.php

Comment thread src/RetryPolicy.php Outdated
@four-bytes-robby
four-bytes-robby merged commit 47777a1 into main Jun 20, 2026
6 of 7 checks passed
@four-bytes-robby
four-bytes-robby deleted the feat/1-retry-classifier-opt-in branch June 20, 2026 00:43
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.

[FEAT] Fix RetryClassifier default to opt-in, update tests

1 participant