Conversation
📝 WalkthroughWalkthroughThe pull request adds Scalafmt and version-policy configuration, updates SBT and release workflow versions, and reformats production and test Scala sources. It does not change public APIs or runtime behavior. ChangesBuild and formatting modernization
Estimated code review effort: 2 (Simple) | ~15 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 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/release.yml:
- Around line 10-11: Update the release workflow reference in the reusable
workflow call to use its resolved 40-character commit SHA instead of `@v5`.
Replace secrets: inherit with an explicit JFROG_ACCESS_TOKEN mapping, relying on
the automatically provided GITHUB_TOKEN and passing no other secrets.
In @.scalafmt.conf:
- Around line 8-11: Update the Scalafmt configuration by adding an .sbt-specific
override with runner.dialect set to sbt1, while retaining scala213source3 as the
default dialect and project.git tracking behavior.
In `@core/src/main/scala/com/evolutiongaming/catshelper/CountLatch.scala`:
- Around line 97-106: Update the release method so values of n less than 1
return F.unit without modifying state, matching acquire’s validation behavior.
Preserve the existing Awaiting and Done handling for valid releases.
In `@core/src/main/scala/com/evolutiongaming/catshelper/FeatureToggled.scala`:
- Around line 10-24: Update the public Scaladoc description above the affected
API: state that a Resource[F, A] produces the nested resource and manages A’s
lifecycle “according to the state of a boolean toggle,” adding the missing “it”
after Resource[F, A] while preserving the rest of the documentation.
In `@core/src/main/scala/com/evolutiongaming/catshelper/Log.scala`:
- Line 326: Update the error method’s log label in the override of error(msg: =>
String, mdc: Mdc) from the misspelled value to "ERROR", matching the throwable
overload and preserving consistent error output.
In `@core/src/main/scala/com/evolutiongaming/catshelper/ReadWriteRef.scala`:
- Around line 36-39: Update the Scaladoc for the public read member near
ReadWriteRef to reference read.use(...) instead of reads.use(...), and correct
the duplicated “in in use” wording to state that write is in use.
In
`@testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/PureTest.scala`:
- Around line 86-87: Correct the Scaladoc sentence for hotLoopTimeout to read:
“Use it when a CPU-bound part of your SUT or test requires more time.”
- Around line 25-27: Update the Scaladoc example in PureTest to use the actual
ioTest entry point instead of PureTest[IO].of, and restore a valid multiline
Scala layout with a separator between import env._ and the for-comprehension.
Preserve the example’s existing sleep and elapsed-time assertion behavior.
- Around line 91-93: Update PureTestRunner’s hot-loop cancellation task to use
the configured config.backgroundEc instead of mainRuntime, preserving the
documented background execution behavior. Add a regression test that verifies
the configured ExecutionContext is used; alternatively, remove the backgroundEc
setting and its documentation if it is not intended to be supported.
In
`@testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/TestRuntime.scala`:
- Around line 18-20: Update the Scaladoc for the relevant method in TestRuntime
to replace the Cats Effect 2 timer.sleep example and timer argument wording with
the Cats Effect 3 Temporal[F].sleep API and corresponding argument text.
🪄 Autofix
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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 869c82f3-8ee3-4671-97d8-a5757a042c1d
📒 Files selected for processing (73)
.github/workflows/ci.yml.github/workflows/release.yml.scalafmt.confbuild.sbtcore/src/main/scala/com/evolutiongaming/catshelper/Blocking.scalacore/src/main/scala/com/evolutiongaming/catshelper/CatsHelper.scalacore/src/main/scala/com/evolutiongaming/catshelper/ClockHelper.scalacore/src/main/scala/com/evolutiongaming/catshelper/CountLatch.scalacore/src/main/scala/com/evolutiongaming/catshelper/DataHelper.scalacore/src/main/scala/com/evolutiongaming/catshelper/EffectHelper.scalacore/src/main/scala/com/evolutiongaming/catshelper/FeatureToggled.scalacore/src/main/scala/com/evolutiongaming/catshelper/Foldable1.scalacore/src/main/scala/com/evolutiongaming/catshelper/FromFuture.scalacore/src/main/scala/com/evolutiongaming/catshelper/FromTry.scalacore/src/main/scala/com/evolutiongaming/catshelper/GroupWithin.scalacore/src/main/scala/com/evolutiongaming/catshelper/LazyVal.scalacore/src/main/scala/com/evolutiongaming/catshelper/Log.scalacore/src/main/scala/com/evolutiongaming/catshelper/LogOf.scalacore/src/main/scala/com/evolutiongaming/catshelper/MeasureDuration.scalacore/src/main/scala/com/evolutiongaming/catshelper/Memoize.scalacore/src/main/scala/com/evolutiongaming/catshelper/ParallelHelper.scalacore/src/main/scala/com/evolutiongaming/catshelper/Partitions.scalacore/src/main/scala/com/evolutiongaming/catshelper/RandomId.scalacore/src/main/scala/com/evolutiongaming/catshelper/RandomIdOf.scalacore/src/main/scala/com/evolutiongaming/catshelper/ReadWriteRef.scalacore/src/main/scala/com/evolutiongaming/catshelper/ResourceCounter.scalacore/src/main/scala/com/evolutiongaming/catshelper/ResourceFenced.scalacore/src/main/scala/com/evolutiongaming/catshelper/Runtime.scalacore/src/main/scala/com/evolutiongaming/catshelper/Schedule.scalacore/src/main/scala/com/evolutiongaming/catshelper/SerParQueue.scalacore/src/main/scala/com/evolutiongaming/catshelper/Serial.scalacore/src/main/scala/com/evolutiongaming/catshelper/SerialKey.scalacore/src/main/scala/com/evolutiongaming/catshelper/SerialRef.scalacore/src/main/scala/com/evolutiongaming/catshelper/ThreadLocalRef.scalacore/src/main/scala/com/evolutiongaming/catshelper/TimerHelper.scalacore/src/main/scala/com/evolutiongaming/catshelper/ToFuture.scalacore/src/main/scala/com/evolutiongaming/catshelper/ToTry.scalacore/src/main/scala/com/evolutiongaming/catshelper/package.scalacore/src/main/scala/com/evolutiongaming/catshelper/syntax/MeasureDurationSyntax.scalacore/src/test/scala/com/evolutiongaming/catshelper/CatsHelperSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/ClockHelperSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/DataHelperTest.scalacore/src/test/scala/com/evolutiongaming/catshelper/FeatureToggledSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/FromFutureSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/FromTrySpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/GroupWithinSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/LazyValSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/LogSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/MeasureDurationSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/MemoizeTest.scalacore/src/test/scala/com/evolutiongaming/catshelper/ParallelHelperTest.scalacore/src/test/scala/com/evolutiongaming/catshelper/PartitionsTest.scalacore/src/test/scala/com/evolutiongaming/catshelper/ReadWriteRefSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/ResourceBreakFlatMapChainTest.scalacore/src/test/scala/com/evolutiongaming/catshelper/ResourceCounterSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/ResourceFencedTest.scalacore/src/test/scala/com/evolutiongaming/catshelper/RuntimeSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/SerParQueueTest.scalacore/src/test/scala/com/evolutiongaming/catshelper/SerialKeyTest.scalacore/src/test/scala/com/evolutiongaming/catshelper/SerialRefSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/SerialTest.scalacore/src/test/scala/com/evolutiongaming/catshelper/ThreadLocalRefSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/ToFutureSpec.scalacore/src/test/scala/com/evolutiongaming/catshelper/ToTrySpec.scalalogback/src/main/scala/com/evolutiongaming/catshelper/LogOfFromLogback.scalaproject/build.propertiesproject/plugins.sbttestkit/src/main/scala/com/evolutiongaming/catshelper/testkit/AbnormalTermination.scalatestkit/src/main/scala/com/evolutiongaming/catshelper/testkit/PureTest.scalatestkit/src/main/scala/com/evolutiongaming/catshelper/testkit/PureTestRunner.scalatestkit/src/main/scala/com/evolutiongaming/catshelper/testkit/TestFrameworkApi.scalatestkit/src/main/scala/com/evolutiongaming/catshelper/testkit/TestRuntime.scalatestkit/src/test/scala/com/evolutiongaming/catshelper/testkit/PureTestSpec.scala
💤 Files with no reviewable changes (3)
- core/src/test/scala/com/evolutiongaming/catshelper/CatsHelperSpec.scala
- .github/workflows/ci.yml
- core/src/test/scala/com/evolutiongaming/catshelper/PartitionsTest.scala
| uses: evolution-gaming/scala-github-actions/.github/workflows/release.yml@v5 | ||
| secrets: inherit |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/*'
printf '%s\n' '--- release.yml ---'
cat -n .github/workflows/release.yml
printf '%s\n' '--- ci.yml references ---'
rg -n -C 3 'evolution-gaming/scala-github-actions|secrets:' .github/workflows/ci.yml .github/workflows
printf '%s\n' '--- repository and references ---'
rg -n -C 2 'secrets: inherit|evolution-gaming/scala-github-actions' .Repository: evolution-gaming/cats-helper
Length of output: 1365
🏁 Script executed:
#!/bin/bash
set -eu
api='https://api.github.com/repos/evolution-gaming/scala-github-actions'
printf '%s\n' '--- v5 tag reference ---'
curl -fsSL "$api/git/refs/tags/v5" | jq '{ref, object}'
printf '%s\n' '--- v5 tag target ---'
tag_sha="$(curl -fsSL "$api/git/refs/tags/v5" | jq -r '.object.sha')"
tag_type="$(curl -fsSL "$api/git/refs/tags/v5" | jq -r '.object.type')"
if [ "$tag_type" = tag ]; then
commit_sha="$(curl -fsSL "$api/git/tags/$tag_sha" | jq -r '.object.sha')"
else
commit_sha="$tag_sha"
fi
printf 'resolved_commit=%s\n' "$commit_sha"
printf '%s\n' '--- commit existence ---'
curl -fsSL "$api/commits/$commit_sha" | jq -r '.sha'
printf '%s\n' '--- release workflow at v5 ---'
curl -fsSL "https://raw.githubusercontent.com/evolution-gaming/scala-github-actions/v5/.github/workflows/release.yml" | nl -baRepository: evolution-gaming/cats-helper
Length of output: 549
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- v5 tag and peeled commit ---'
git ls-remote --tags https://github.com/evolution-gaming/scala-github-actions.git 'refs/tags/v5' 'refs/tags/v5^{}'
printf '%s\n' '--- release workflow at v5 ---'
curl -fsSL 'https://raw.githubusercontent.com/evolution-gaming/scala-github-actions/v5/.github/workflows/release.yml' | cat -nRepository: evolution-gaming/cats-helper
Length of output: 2830
🌐 Web query:
"evolution-gaming/scala-github-actions" "v5" "b4557d9a82c03596dc2425e19b3cb9a6280a4739"
💡 Result:
The repository evolution-gaming/scala-github-actions is a collection of reusable GitHub Actions workflows designed to streamline the release process for Scala projects [1][2]. It allows developers to automate tasks such as publishing artifacts to repositories like Artifactory or JFrog, typically by leveraging sbt-dynver to derive version numbers from Git tags (e.g., vX.Y.Z) [2][3][4]. Regarding the specific reference v5 b4557d9a82c03596dc2425e19b3cb9a6280a4739: 1. Versioning: As of August 2026, the repository has multiple release versions (e.g., v1 through v4 are documented in use) [1][5]. If v5 has been tagged, it represents a newer iteration of the release workflows [1]. 2. Commit Hash: The string b4557d9a82c03596dc2425e19b3cb9a6280a4739 is a Git commit hash. In the context of GitHub Actions, referencing a workflow using a specific commit hash (e.g., uses: evolution-gaming/scala-github-actions/.github/workflows/release.yml@b4557d9...) is a common practice to pin a workflow to an exact state, ensuring build reproducibility and preventing unexpected behavior from upstream changes [1][3]. If you are seeing this reference in a project's .github/workflows/release.yml file, it indicates that the project is pinned to that specific version of the Evolution Gaming release automation logic [1][3].
Citations:
- 1: https://github.com/evolution-gaming/scala-github-actions
- 2: https://github.com/evolution-gaming/scache
- 3: evolution-gaming/scache@750485d
- 4: https://github.com/evolution-gaming/play-json-tools
- 5: https://github.com/evolution-gaming/random/releases
🌐 Web query:
GitHub Actions reusable workflow secrets inherit GITHUB_TOKEN explicitly named secrets called workflow
💡 Result:
In GitHub Actions, reusable workflows do not automatically inherit secrets from the calling workflow. To make secrets available to a reusable workflow, you must explicitly pass them [1][2]. There are two primary ways to pass secrets to a reusable workflow: 1. Explicit Mapping: You can explicitly pass named secrets using the secrets keyword in your workflow call [3][4]. The secret name in the caller must be mapped to the name expected by the reusable workflow [1]. jobs: call-workflow: uses: owner/repo/.github/workflows/reusable.yml@main secrets: MY_SECRET: ${{ secrets.MY_SECRET }} 2. Implicit Inheritance: You can use the secrets: inherit keyword to pass all secrets available to the calling workflow to the reusable workflow [5][6]. This is often used for simplicity but should be used with caution, as it grants the reusable workflow access to all secrets available in the caller [1]. jobs: call-workflow: uses: owner/repo/.github/workflows/reusable.yml@main secrets: inherit Regarding the GITHUB_TOKEN, it is automatically available to all workflows, including reusable workflows, through the github.token context [7]. It does not need to be passed explicitly as a secret [7]. Actions within the workflow can access it regardless of whether the workflow explicitly passes it, provided the appropriate permissions are configured via the permissions key [7]. Important considerations: - Secret pass-through is not recursive; in a chain of workflows (A calls B, B calls C), workflow C only receives secrets if they are explicitly passed from A to B, and then again from B to C [3][8]. - You cannot use the secrets context inside the with: block (used for passing inputs) because with: is evaluated on the caller side before the reusable workflow's context is fully active [9]. If a reusable workflow needs to use a secret value, pass the secret name as an input and resolve it dynamically within the reusable workflow using the syntax ${{ secrets[inputs.secret_name] }} [9].
Citations:
- 1: https://qaskills.sh/blog/ci-workflow-reusable-inputs-secrets
- 2: https://latchkey.dev/learn/github-actions/github-actions-reusable-workflow-secret-not-inherited
- 3: https://docs.github.com/en/enterprise-cloud@latest/actions/how-tos/reuse-automations/reuse-workflows
- 4: https://stackoverflow.com/questions/78446488/passing-secrets-to-reusable-worflow
- 5: https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows
- 6: https://github.blog/changelog/2022-05-03-github-actions-simplify-using-secrets-with-reusable-workflows/
- 7: https://docs.github.com/actions/reference/authentication-in-a-workflow
- 8: https://github.com/github/docs/blob/main/content/actions/how-tos/reuse-automations/reuse-workflows.md
- 9: https://dev.to/cseeman/why-your-github-actions-secrets-dont-work-in-reusable-workflow-inputs-462g
🏁 Script executed:
#!/bin/bash
set -eu
sha='b4557d9a82c03596dc2425e19b3cb9a6280a4739'
url="https://github.com/evolution-gaming/scala-github-actions/commit/$sha"
printf '%s\n' '--- commit page status and identity ---'
curl -fsSL -o /tmp/scala-actions-commit.html "$url"
printf 'http_body_bytes='
wc -c </tmp/scala-actions-commit.html
rg -o -m 3 "commit[^<]{0,120}|$sha" /tmp/scala-actions-commit.html || true
printf '%s\n' '--- v5 ref and SHA ref ---'
git ls-remote --tags https://github.com/evolution-gaming/scala-github-actions.git 'refs/tags/v5' "refs/tags/$sha"Repository: evolution-gaming/cats-helper
Length of output: 438
Pin the release workflow and pass only the required secret.
Replace @v5 with the resolved 40-character commit SHA. Replace secrets: inherit with an explicit JFROG_ACCESS_TOKEN mapping. GITHUB_TOKEN is available automatically to the called workflow.
🧰 Tools
🪛 zizmor (1.29.0)
[warning] 10-10: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow
(secrets-inherit)
🤖 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/release.yml around lines 10 - 11, Update the release
workflow reference in the reusable workflow call to use its resolved
40-character commit SHA instead of `@v5`. Replace secrets: inherit with an
explicit JFROG_ACCESS_TOKEN mapping, relying on the automatically provided
GITHUB_TOKEN and passing no other secrets.
Source: Linters/SAST tools
| runner.dialect = scala213source3 | ||
|
|
||
| # only format files tracked by git | ||
| project.git = true |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Tracked SBT files:"
git ls-files '*.sbt'
echo "Scalafmt dialect configuration:"
rg -n -C 3 'project\.git|runner\.dialect|fileOverride' .scalafmt.confRepository: evolution-gaming/cats-helper
Length of output: 420
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Relevant repository files:"
git ls-files | rg '(^|/)(build\.sbt|plugins\.sbt|\.scalafmt\.conf|.*scalafmt.*|.*github.*|.*workflow.*)$' || true
echo "Formatting configuration and references:"
cat -n .scalafmt.conf
rg -n -C 3 'scalafmt|scalafmtCheck|scalafmtAll|project\.git|fileOverride|build\.sbt|plugins\.sbt' -g '!**/target/**' .
echo "SBT source samples:"
for f in build.sbt project/plugins.sbt; do
if test -f "$f"; then
echo "--- $f"
cat -n "$f"
fi
doneRepository: evolution-gaming/cats-helper
Length of output: 8488
🌐 Web query:
Scalafmt project.git default file extensions .sbt fileOverride runner.dialect sbt1 sbt-scalafmt scalafmtRepo
💡 Result:
In Scalafmt, configuration is managed via the.scalafmt.conf file [1][2]. Below is a synthesis of the specific configuration topics requested: Project Git Integration The project.git setting (e.g., project.git = true) instructs Scalafmt to automatically use git to identify the files that should be formatted, typically by excluding files that are not tracked or are ignored by git [3][4]. This is often preferred over manually managing excludeFilters for generated files [4]. File Overrides and Dialects The fileOverride setting allows you to apply specific configuration parameters, such as changing the runner.dialect, to files that match a particular pattern [2]. Since Scalafmt v3.2.0, you can use shortcuts for file extensions or glob patterns [5]. Example of using fileOverride for.sbt files with the sbt1 dialect: fileOverride { "glob:/*.sbt" { runner.dialect = sbt1 } } Alternatively, you can use the extension shortcut syntax: fileOverride { ".sbt" { runner.dialect = sbt1 } } Runner Dialect The runner.dialect setting is mandatory in modern Scalafmt configurations [1]. It specifies the dialect used by the parser. For.sbt files, sbt1 is the standard dialect [5][6][7]. Scalafmt Repository Scalafmt does not have a single "scalafmtRepo" configuration key for standard usage. If you are referring to integration within an sbt project, the sbt-scalafmt plugin handles the connection to the formatter, and configuration is typically localized to the.scalafmt.conf file in your project root [1][8]. If you are working on a custom build or plugin integration that requires specifying a repository for a custom version of Scalafmt, you would generally handle that via build tool dependencies (e.g., in build.sbt) rather than inside.scalafmt.conf [8].
Citations:
- 1: https://scalameta.org/scalafmt/docs/configuration
- 2: https://github.com/scalameta/scalafmt/blob/73ea9a95d94082a2c54a61979ec99210cbda49ac/docs/configuration.md
- 3: https://github.com/scalameta/scalafmt/blob/main/.scalafmt.conf
- 4: CLI: ignore most common typical generated files consistent to SBT integration scalameta/scalafmt#1644
- 5:
fileOverridePathMatchernot being respected? scalameta/scalafmt#3661 - 6: Scalafmt 3.0.0 applies new Scala 3 syntax for build.sbt and meta-project files scalameta/scalafmt#2688
- 7: 3.2.1 doesn't support scala-2.10 anymore? scalameta/scalafmt#2987
- 8: https://scalameta.org/scalafmt/docs/installation
🌐 Web query:
sbt-scalafmt 2.6.2 scalafmtRepo scalafmtCheckRepo format .sbt files
💡 Result:
In sbt-scalafmt version 2.6.2, the newly introduced scalafmtRepo and scalafmtCheckRepo are build-root tasks designed to operate across the entire repository [1][2][3]. Formatting.sbt files in sbt-scalafmt is primarily handled through the scalafmtSbt and scalafmtSbtCheck tasks [4][5]. Key details regarding your query: 1. scalafmtRepo / scalafmtCheckRepo: These tasks were added in v2.6.2 to facilitate repository-wide formatting and checking [1][3]. The scalafmtRepo task formats files across the build root, while scalafmtCheckRepo validates that these files are correctly formatted, failing if they are not (typically used for CI) [1][2][3]. They utilize ScalafmtSession.listFiles for file discovery [1][2]. 2. Formatting.sbt files: - To format .sbt and project/.scala files, use the scalafmtSbt task [4][5]. - To check that these files are properly formatted, use the scalafmtSbtCheck task [4][5]. - These tasks are distinct from the standard scalafmt / scalafmtCheck tasks, which target compile and test source files [4][5]. The official documentation for sbt-scalafmt can be found via the scalameta/sbt-scalafmt repository [6][5][7], which provides guidance on usage and configuration [5].
Citations:
- 1: https://github.com/scalameta/sbt-scalafmt/releases
- 2: https://newreleases.io/project/github/scalameta/sbt-scalafmt/release/v2.6.2
- 3: Update dependency org.scalameta:sbt-scalafmt to v2.6.2 dancewithheart/agda2scala#93
- 4: https://github.com/scalameta/sbt-scalafmt/blob/master/plugin/src/main/scala/org/scalafmt/sbt/ScalafmtPlugin.scala
- 5: https://github.com/scalameta/scalafmt/blob/master/docs/installation.md
- 6: https://github.com/scalameta/sbt-scalafmt
- 7: https://index.scala-lang.org/scalameta/sbt-scalafmt
🏁 Script executed:
#!/bin/bash
set -euo pipefail
base='https://raw.githubusercontent.com/scalameta/sbt-scalafmt/v2.6.2'
for path in plugin/src/main/scala/org/scalafmt/sbt/ScalafmtPlugin.scala plugin/src/main/scala/org/scalafmt/sbt/ScalafmtCorePlugin.scala; do
echo "--- $path"
curl -fsSL "$base/$path" | rg -n -C 5 'scalafmtRepo|scalafmtCheckRepo|scalafmtSbt|scalafmtSbtCheck|listFiles|FileExtension|\.sbt'
doneRepository: evolution-gaming/cats-helper
Length of output: 7795
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for url in \
'https://raw.githubusercontent.com/scalameta/sbt-scalafmt/v2.6.2/plugin/src/main/scala/org/scalafmt/sbt/ScalafmtPlugin.scala' \
'https://raw.githubusercontent.com/scalameta/scalafmt/v3.11.5/scalafmt-core/src/main/scala/org/scalafmt/ScalafmtSession.scala' \
'https://raw.githubusercontent.com/scalameta/scalafmt/v3.11.5/scalafmt-core/src/main/scala/org/scalafmt/config/ScalafmtConfig.scala'; do
echo "--- $url"
curl -fsSL "$url" | rg -n -C 12 'getScalafmtRepoTask|repoSources|listFiles|project\.git|extensions|sbt|fileOverride' || true
doneRepository: evolution-gaming/cats-helper
Length of output: 19108
🏁 Script executed:
#!/bin/bash
set -euo pipefail
tree=$(curl -fsSL 'https://api.github.com/repos/scalameta/scalafmt/git/trees/v3.11.5?recursive=1')
printf '%s' "$tree" |
jq -r '.tree[].path' |
rg '(^|/)(ScalafmtSession|.*Session.*|.*File.*|.*Config.*)\.scala$|project.*\.scala$' |
head -80
echo "Potential source references:"
printf '%s' "$tree" |
jq -r '.tree[].path' |
rg 'scalafmt.*(core|cli)|src/main/scala' |
head -120Repository: evolution-gaming/cats-helper
Length of output: 8563
🏁 Script executed:
#!/bin/bash
set -euo pipefail
base='https://raw.githubusercontent.com/scalameta/scalafmt/v3.11.5'
for path in \
scalafmt-core/shared/src/main/scala/org/scalafmt/config/ProjectFiles.scala \
scalafmt-core/shared/src/main/scala/org/scalafmt/config/ScalafmtConfig.scala \
scalafmt-interfaces/jvm/src/main/java/org/scalafmt/interfaces/ScalafmtSession.scala \
scalafmt-interfaces/shared/src/main/scala/org/scalafmt/interfaces/ScalafmtSession.scala; do
echo "--- $path"
curl -fsSL "$base/$path" | rg -n -C 15 'listFiles|\.sbt|extension|project\.git|fileOverride|dialect' || true
doneRepository: evolution-gaming/cats-helper
Length of output: 18251
Configure the SBT dialect for tracked .sbt files.
scalafmtRepo and scalafmtCheckRepo include tracked .sbt files. Add an .sbt override with runner.dialect = sbt1 to prevent parsing errors in build.sbt and project/plugins.sbt.
🤖 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 @.scalafmt.conf around lines 8 - 11, Update the Scalafmt configuration by
adding an .sbt-specific override with runner.dialect set to sbt1, while
retaining scala213source3 as the default dialect and project.git tracking
behavior.
Source: MCP tools
| override def release(n: Int): F[Unit] = | ||
| state | ||
| .modify { | ||
| case Done => Done -> F.unit | ||
| case Awaiting(l, await) => | ||
| if (l > n) Awaiting(l - n, await) -> F.unit | ||
| else Done -> await.complete(()).void | ||
| } | ||
| } | ||
| .flatten | ||
| .uncancelable |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Prevent negative releases from increasing the latch count.
When latches = 1, release(-1) takes the l > n branch and stores Awaiting(2, await). A caller can therefore increase the latch through release, which can keep await() blocked. Match acquire by returning F.unit for n < 1, or reject invalid values.
Proposed fix
override def release(n: Int): F[Unit] =
- state
+ if (n < 1) F.unit
+ else
+ state
.modify {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| override def release(n: Int): F[Unit] = | |
| state | |
| .modify { | |
| case Done => Done -> F.unit | |
| case Awaiting(l, await) => | |
| if (l > n) Awaiting(l - n, await) -> F.unit | |
| else Done -> await.complete(()).void | |
| } | |
| } | |
| .flatten | |
| .uncancelable | |
| override def release(n: Int): F[Unit] = | |
| if (n < 1) F.unit | |
| else | |
| state | |
| .modify { | |
| case Done => Done -> F.unit | |
| case Awaiting(l, await) => | |
| if (l > n) Awaiting(l - n, await) -> F.unit | |
| else Done -> await.complete(()).void | |
| } | |
| .flatten | |
| .uncancelable |
🤖 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 `@core/src/main/scala/com/evolutiongaming/catshelper/CountLatch.scala` around
lines 97 - 106, Update the release method so values of n less than 1 return
F.unit without modifying state, matching acquire’s validation behavior. Preserve
the existing Awaiting and Done handling for valid releases.
| /** | ||
| * Given a `Resource[F, A]` produces `Resource[F, Resource[F, Option[A]]]` which, | ||
| * while being in `use`, manages lifecycle of `A` according the state of a boolean | ||
| * toggle, providing access to `A` only when the toggle is `true`. | ||
| * Given a `Resource[F, A]` produces `Resource[F, Resource[F, Option[A]]]` which, while being in | ||
| * `use`, manages lifecycle of `A` according the state of a boolean toggle, providing access to `A` | ||
| * only when the toggle is `true`. | ||
| * | ||
| * @define ra the base resource. | ||
| * @define ra | ||
| * the base resource. | ||
| * | ||
| * @define gracePeriod defines an interval which is counted from the moment the toggle | ||
| * goes off. During this interval the managed resource `A` is kept alive while | ||
| * there is at least one "user" active. After `gracePeriod` expires the resource | ||
| * will be released unconditionally. It is ''up to the client'' to resolve any | ||
| * errors that may come from a forceful termination of a resource that is still | ||
| * in use. | ||
| * @define gracePeriod | ||
| * defines an interval which is counted from the moment the toggle goes off. During this interval | ||
| * the managed resource `A` is kept alive while there is at least one "user" active. After | ||
| * `gracePeriod` expires the resource will be released unconditionally. It is ''up to the client'' | ||
| * to resolve any errors that may come from a forceful termination of a resource that is still in | ||
| * use. | ||
| */ |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Correct the public Scaladoc sentence.
The sentence is missing it after Resource[F, A]. according the state must be according to the state.
Proposed documentation fix
- * Given a `Resource[F, A]` produces `Resource[F, Resource[F, Option[A]]]` which, while being in
- * `use`, manages lifecycle of `A` according the state of a boolean toggle, providing access to `A`
+ * Given a `Resource[F, A]`, this object produces `Resource[F, Resource[F, Option[A]]]` which, while
+ * being in `use`, manages the lifecycle of `A` according to the state of a boolean toggle,
+ * providing access to `A`📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * Given a `Resource[F, A]` produces `Resource[F, Resource[F, Option[A]]]` which, | |
| * while being in `use`, manages lifecycle of `A` according the state of a boolean | |
| * toggle, providing access to `A` only when the toggle is `true`. | |
| * Given a `Resource[F, A]` produces `Resource[F, Resource[F, Option[A]]]` which, while being in | |
| * `use`, manages lifecycle of `A` according the state of a boolean toggle, providing access to `A` | |
| * only when the toggle is `true`. | |
| * | |
| * @define ra the base resource. | |
| * @define ra | |
| * the base resource. | |
| * | |
| * @define gracePeriod defines an interval which is counted from the moment the toggle | |
| * goes off. During this interval the managed resource `A` is kept alive while | |
| * there is at least one "user" active. After `gracePeriod` expires the resource | |
| * will be released unconditionally. It is ''up to the client'' to resolve any | |
| * errors that may come from a forceful termination of a resource that is still | |
| * in use. | |
| * @define gracePeriod | |
| * defines an interval which is counted from the moment the toggle goes off. During this interval | |
| * the managed resource `A` is kept alive while there is at least one "user" active. After | |
| * `gracePeriod` expires the resource will be released unconditionally. It is ''up to the client'' | |
| * to resolve any errors that may come from a forceful termination of a resource that is still in | |
| * use. | |
| */ | |
| /** | |
| * Given a `Resource[F, A]`, this object produces `Resource[F, Resource[F, Option[A]]]` which, while | |
| * being in `use`, manages the lifecycle of `A` according to the state of a boolean toggle, | |
| * providing access to `A` only when the toggle is `true`. | |
| * | |
| * `@define` ra | |
| * the base resource. | |
| * | |
| * `@define` gracePeriod | |
| * defines an interval which is counted from the moment the toggle goes off. During this interval | |
| * the managed resource `A` is kept alive while there is at least one "user" active. After | |
| * `gracePeriod` expires the resource will be released unconditionally. It is ''up to the client'' | |
| * to resolve any errors that may come from a forceful termination of a resource that is still in | |
| * use. | |
| */ |
🤖 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 `@core/src/main/scala/com/evolutiongaming/catshelper/FeatureToggled.scala`
around lines 10 - 24, Update the public Scaladoc description above the affected
API: state that a Resource[F, A] produces the nested resource and manages A’s
lifecycle “according to the state of a boolean toggle,” adding the missing “it”
after Resource[F, A] while preserving the rest of the documentation.
| override def warn(msg: => String, cause: Throwable, mdc: Mdc): F[Unit] = C.println(log("WARN", msg, mdc)) >> | ||
| C.printStackTrace(cause) | ||
|
|
||
| override def error(msg: => String, mdc: Mdc): F[Unit] = C.errorln(log("ERRROR", msg, mdc)) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Correct the error log label.
Line 326 emits "ERRROR" while the throwable overload emits "ERROR" on Line 328. Change the label to "ERROR" so both error methods produce consistent output.
Proposed fix
- override def error(msg: => String, mdc: Mdc): F[Unit] = C.errorln(log("ERRROR", msg, mdc))
+ override def error(msg: => String, mdc: Mdc): F[Unit] = C.errorln(log("ERROR", msg, mdc))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| override def error(msg: => String, mdc: Mdc): F[Unit] = C.errorln(log("ERRROR", msg, mdc)) | |
| override def error(msg: => String, mdc: Mdc): F[Unit] = C.errorln(log("ERROR", msg, mdc)) |
🤖 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 `@core/src/main/scala/com/evolutiongaming/catshelper/Log.scala` at line 326,
Update the error method’s log label in the override of error(msg: => String,
mdc: Mdc) from the misspelled value to "ERROR", matching the throwable overload
and preserving consistent error output.
| /** | ||
| * "Read" operation. Multiple reads may be in use simultaneously. | ||
| * `reads.use(…)` blocks semantically while [[write]] in in use. | ||
| * "Read" operation. Multiple reads may be in use simultaneously. `reads.use(…)` blocks | ||
| * semantically while [[write]] in in use. | ||
| */ |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Fix the read Scaladoc identifiers.
The public member is read, not reads. The sentence also contains duplicated wording: in in use. Use read.use(...) and state that write is in use.
Proposed fix
- * - "Read" operation. Multiple reads may be in use simultaneously. `reads.use(…)` blocks
- * semantically while [[write]] in in use.
+ * - "Read" operation. Multiple reads may be in use simultaneously. `read.use(…)` blocks
+ * semantically while [[write]] is in use.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * "Read" operation. Multiple reads may be in use simultaneously. | |
| * `reads.use(…)` blocks semantically while [[write]] in in use. | |
| * "Read" operation. Multiple reads may be in use simultaneously. `reads.use(…)` blocks | |
| * semantically while [[write]] in in use. | |
| */ | |
| /** | |
| * "Read" operation. Multiple reads may be in use simultaneously. `read.use(…)` blocks | |
| * semantically while [[write]] is in use. | |
| */ |
🤖 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 `@core/src/main/scala/com/evolutiongaming/catshelper/ReadWriteRef.scala` around
lines 36 - 39, Update the Scaladoc for the public read member near ReadWriteRef
to reference read.use(...) instead of reads.use(...), and correct the duplicated
“in in use” wording to state that write is in use.
| * @example | ||
| * {{{ "what time is it now?" in PureTest[IO].of { env => import env._ for { _ <- IO.sleep(1.hour) | ||
| * _ <- testRuntime.getTimeSinceStart.map(_ shouldBe 1.hour) } yield () } }}} |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Keep the Scaladoc example valid.
The reflow removed the separator between import env._ and for, so the example now contains import env._ for {, which is not valid Scala. The supplied PureTest API exposes ioTest; it does not define PureTest[IO].of. Restore a multiline example that uses the actual entry point.
Proposed fix
- * {{{ "what time is it now?" in PureTest[IO].of { env => import env._ for { _ <- IO.sleep(1.hour)
- * _ <- testRuntime.getTimeSinceStart.map(_ shouldBe 1.hour) } yield () } }}}}
+ * {{{
+ * PureTest.ioTest { env =>
+ * import env._
+ * for {
+ * _ <- IO.sleep(1.hour)
+ * _ <- testRuntime.getTimeSinceStart.map(_ shouldBe 1.hour)
+ * } yield ()
+ * }
+ * }}}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * @example | |
| * {{{ "what time is it now?" in PureTest[IO].of { env => import env._ for { _ <- IO.sleep(1.hour) | |
| * _ <- testRuntime.getTimeSinceStart.map(_ shouldBe 1.hour) } yield () } }}} | |
| * {{{ | |
| * PureTest.ioTest { env => | |
| * import env._ | |
| * for { | |
| * _ <- IO.sleep(1.hour) | |
| * _ <- testRuntime.getTimeSinceStart.map(_ shouldBe 1.hour) | |
| * } yield () | |
| * } | |
| * }}} |
🤖 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 `@testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/PureTest.scala`
around lines 25 - 27, Update the Scaladoc example in PureTest to use the actual
ioTest entry point instead of PureTest[IO].of, and restore a valid multiline
Scala layout with a separator between import env._ and the for-comprehension.
Preserve the example’s existing sleep and elapsed-time assertion behavior.
| * Sets hot-loop detection timeout. Use it CPU-bound part of your SUT or test requires longer | ||
| * time. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Correct the hotLoopTimeout Scaladoc sentence.
The text “Use it CPU-bound part of your SUT or test requires longer time” is incomplete. Use: “Use it when a CPU-bound part of your SUT or test requires more time.”
🤖 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 `@testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/PureTest.scala`
around lines 86 - 87, Correct the Scaladoc sentence for hotLoopTimeout to read:
“Use it when a CPU-bound part of your SUT or test requires more time.”
| /** | ||
| * Sets an `ExecutionContext` used for background tasks, such as hot loop detection. | ||
| * Defaults to `ExecutionContext.global`. | ||
| * Sets an `ExecutionContext` used for background tasks, such as hot loop detection. Defaults to | ||
| * `ExecutionContext.global`. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 4 '\bbackgroundEc\b' testkit/src/main/scala/com/evolutiongaming/catshelper/testkitRepository: evolution-gaming/cats-helper
Length of output: 2235
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- PureTestRunner.scala ---'
cat -n testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/PureTestRunner.scala
printf '%s\n' '--- PureTest.scala relevant sections ---'
sed -n '1,125p' testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/PureTest.scala
printf '%s\n' '--- testkit Scala files ---'
git ls-files 'testkit/src/test' 'testkit/src/main' | sed -n '1,160p'
printf '%s\n' '--- runner/config references ---'
rg -n -C 3 'PureTestRunner|Config\[|hotLoopTimeout|mainRuntime|backgroundEc' testkit/src/main testkit/src/testRepository: evolution-gaming/cats-helper
Length of output: 18412
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- all backgroundEc references ---'
rg -n -C 2 '\bbackgroundEc\b' .
printf '%s\n' '--- PureTestSpec.scala ---'
cat -n testkit/src/test/scala/com/evolutiongaming/catshelper/testkit/PureTestSpec.scala
printf '%s\n' '--- runtime construction and cancellation APIs ---'
rg -n -C 4 'materializeRuntime|unsafeRunCancelable|IORuntime\(' testkit/src/main testkit/src/test
printf '%s\n' '--- read-only invariant check ---'
python3 - <<'PY'
from pathlib import Path
runner = Path("testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/PureTestRunner.scala").read_text()
pure_test = Path("testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/PureTest.scala").read_text()
print("runner_reads_config_backgroundEc =", "config.backgroundEc" in runner)
print("runner_mentions_backgroundEc =", "backgroundEc" in runner)
print("config_declares_backgroundEc =", "backgroundEc:" in pure_test)
print("public_setter_copies_backgroundEc =", "copy(backgroundEc = v)" in pure_test)
print("runner_starts_timeout_with_mainRuntime =", "stopHotLoop.unsafeRunCancelable()(mainRuntime)" in runner)
PYRepository: evolution-gaming/cats-helper
Length of output: 6605
Honor the documented backgroundEc setting.
PureTestRunner never reads config.backgroundEc; hot-loop cancellation always runs on mainRuntime. Use config.backgroundEc for this task and add a regression test, or remove the setting and its documentation.
🤖 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 `@testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/PureTest.scala`
around lines 91 - 93, Update PureTestRunner’s hot-loop cancellation task to use
the configured config.backgroundEc instead of mainRuntime, preserving the
documented background execution behavior. Add a regression test that verifies
the configured ExecutionContext is used; alternatively, remove the backgroundEc
setting and its documentation if it is not intended to be supported.
| * It's roughly equivalent to `getTimeSinceStart.flatMap(t => timer.sleep(dt - t))` but does not | ||
| * require a `timer: Timer[F]` argument. | ||
| */ |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does Cats Effect 3 expose Timer[F], or should this Scaladoc use Temporal[F].sleep?
💡 Result:
In Cats Effect 3, the Timer typeclass has been removed and replaced by the Temporal typeclass [1][2][3]. Therefore, Cats Effect 3 does not expose Timer[F] [1][2]. Yes, Scaladoc or any documentation referencing time-based operations like sleeping should use Temporal[F].sleep [2][3]. The Temporal typeclass is a lawful typeclass that extends Concurrent[F] and Clock[F], providing the ability to suspend fibers for a given duration [4][2][5]. The migration from CE2 to CE3 specifically identifies Temporal[F].sleep as the correct replacement for the former Timer[F].sleep [2][3].
Citations:
- 1: https://github.com/typelevel/cats-effect/releases/tag/v3.0.0-M1
- 2: https://github.com/typelevel/cats-effect/blob/74cdc89c/docs/migration-guide.md
- 3: https://typelevel.org/cats-effect/docs/migration-guide
- 4: https://typelevel.org/cats-effect/api/3.x/cats/effect/kernel/GenTemporal.html
- 5: https://github.com/typelevel/cats-effect/blob/3845296dc69e5cc2c5f756da3bfadd8d38d2f55a/kernel/shared/src/main/scala/cats/effect/kernel/GenTemporal.scala
🏁 Script executed:
#!/bin/sh
set -eu
printf '%s\n' '--- candidate file ---'
cat -n testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/TestRuntime.scala | sed -n '1,80p'
printf '%s\n' '--- Cats Effect API references in the repository ---'
rg -n --glob '*.scala' '(^|[^A-Za-z])(Timer|Temporal)\[|Temporal\.sleep|Timer\.sleep' .Repository: evolution-gaming/cats-helper
Length of output: 2408
🏁 Script executed:
#!/bin/sh
set -eu
printf '%s\n' '--- Cats Effect dependency declarations ---'
rg -n --glob 'build.sbt' --glob '*.sbt' --glob '*.scala' 'cats-effect|cats\.effect' .
printf '%s\n' '--- timer-related declarations and usages ---'
rg -n --glob '*.scala' 'trait Timer|class Timer|object Timer|Timer\[|Temporal\[|TimerHelper' .Repository: evolution-gaming/cats-helper
Length of output: 19118
Use Temporal[F].sleep in the Scaladoc.
Cats Effect 3 uses Temporal[F].sleep, and the repository uses this API. Update the example and argument text.
🤖 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
`@testkit/src/main/scala/com/evolutiongaming/catshelper/testkit/TestRuntime.scala`
around lines 18 - 20, Update the Scaladoc for the relevant method in TestRuntime
to replace the Cats Effect 2 timer.sleep example and timer argument wording with
the Cats Effect 3 Temporal[F].sleep API and corresponding argument text.
Summary by CodeRabbit
Chores
Documentation
Style