Skip to content

Bug fixes - #50

Open
bdurand wants to merge 12 commits into
mainfrom
detailed-review
Open

Bug fixes#50
bdurand wants to merge 12 commits into
mainfrom
detailed-review

Conversation

@bdurand

@bdurand bdurand commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Security

  • Fixed a stored cross-site scripting (XSS) vulnerability where a setting key was interpolated into the history pagination links in the web UI without HTML escaping. A user with write access could craft a key that executed JavaScript in another user's browser.

Fixed

  • Fixed Setting.save! raising a NoMethodError in non-Rails applications that relied on the implicit ActiveRecord storage default. The transaction now resolves the storage class through the public accessor instead of the uninitialized instance variable.
  • Fixed LocalCache#to_h returning only the first element of array type settings. It now returns the full array value.
  • Fixed Setting#save! always updating the updated_at timestamp (and triggering a write) even when nothing had changed, which caused unnecessary cache invalidation across processes.
  • Fixed a race condition in LocalCache where a value read on a cache miss could overwrite a fresher value written by a concurrent refresh.
  • Fixed LocalCache#refresh never picking up newly added settings if the cache had been loaded while the data store was empty.
  • Fixed a duplicate-key race in ActiveRecordStorage#save! that raised an unhandled ActiveRecord::RecordNotUnique when the same key was created concurrently. The conflict is now retried and merged.
  • Fixed a bulk update against HttpStorage silently reporting success when the remote API rejected the changes. bulk_update now returns false and save! raises an error in this case.
  • Fixed thread-safety issues in the cached S3 and MongoDB clients that could expose a stale client or permanently cache a nil client after a transient connection failure.
  • Fixed MongoDBStorage.find_by_key returning records that reported persisted? as false.
  • Fixed the escaping of SuperSettings.authentication_url when injected into the inline web UI JavaScript. URLs containing single quotes previously produced corrupted or invalid JavaScript.
  • Fixed the Rack application returning 404 for all routes when mounted under a path (e.g. via map or Rails mount) without repeating the mount path in the constructor.
  • Fixed HttpClient corrupting base URLs that include a query string when appending the trailing path separator.
  • Fixed HttpClient retrying non-idempotent POST requests after a connection error, which could apply an update twice.
  • Fixed the /settings/updated_since endpoint returning a 500 (or a misleading empty success) when the time parameter was missing or unparseable. It now returns a 400 Bad Request.
  • Fixed the web UI POST endpoint returning a 500 for malformed JSON request bodies instead of a 400 Bad Request.
  • Fixed an authenticated but unauthorized user being redirected to the login page (a potential redirect loop) instead of receiving a 403 Forbidden.
  • Fixed the Rails layout helper using the raw dark mode selector instead of the resolved value, which could render a page with mismatched light/dark styling.
  • Fixed Coerce.boolean returning true for a whitespace-only string.
  • Fixed the Rails engine eagerly loading ActiveJob::Base during initialization.
  • Added a missing require "time" so Time.parse based coercion works in non-Rails applications.

Summary by CodeRabbit

  • Security

    • Fixed a stored XSS vulnerability in web UI history pagination links.
    • Improved escaping for authentication URLs embedded in the web interface.
  • Bug Fixes

    • Hardened web UI history rendering and timestamp formatting; improved handling when timestamps are missing.
    • Added clearer 400 responses for invalid time, malformed JSON, and invalid update payloads.
    • Improved routing behavior (mounted paths, 403 handling), dark mode selector resolution, and coercion of whitespace-only booleans.
    • Strengthened caching/storage reliability, concurrency safety, duplicate-key conflict handling, and HTTP base URL/retry rules.
  • Chores

    • Bumped version to 2.6.1 (and updated the changelog).

@greptile-apps

greptile-apps Bot commented Jul 12, 2026

Copy link
Copy Markdown

Greptile Summary

This PR is a sweeping set of bug fixes for the super_settings gem, touching security, caching, storage adapters, the Rack application layer, and supporting utilities. No new features are introduced; every change is a targeted correction to existing misbehavior.

  • Security: XSS in the history pagination UI (unescaped data-key attribute), JavaScript injection via authentication_url single-quote escaping, and authenticated-but-unauthorized users being redirected in a loop are all corrected.
  • Caching and concurrency: LocalCache#to_h was destructuring cached values as tuples (breaking array-type settings), refresh was gated behind an empty? check that prevented new-setting pickup, and a cache-miss write could overwrite a fresher concurrent refresh; all three are fixed.
  • Storage layer: HTTP storage now propagates save failures, ActiveRecord storage retries duplicate-key conflicts, MongoDB/S3 client initialization ordering is corrected to avoid permanently caching failed state, and JSONStorage#save_all is guarded with a per-process mutex.

Confidence Score: 4/5

Safe to merge; all changes are targeted corrections backed by new tests.

The fixes are well-reasoned and test coverage is thorough. Two minor observations: update_setting does not freeze the cache (inconsistent with every other write path, not currently exploitable), and development_mode? default changed from implicitly-development to implicitly-production when no env var is set.

lib/super_settings/mini_i18n.rb (env-var default change) and lib/super_settings/local_cache.rb (update_setting missing .freeze)

Important Files Changed

Filename Overview
lib/super_settings/application/scripts.js XSS fix: data-key attribute in history pagination links now HTML-escaped; also removes a stray ) typo from the anchor tags.
lib/super_settings/local_cache.rb Three concurrent bugs fixed: to_h tuple-destructuring (broke array settings), refresh gated on empty cache (new settings never loaded), and cache-miss write overwriting fresher refresh result.
lib/super_settings/setting.rb save! now returns early (skipping updated_at write) when no changes exist; transaction now calls storage via public accessor; bulk_update rescues InvalidRecordError and correctly returns false.
lib/super_settings/rack_application.rb Path resolution when mounted under a SCRIPT_NAME prefix is fixed; 403 no longer redirects to login (preventing redirect loops); malformed/non-object JSON bodies return 400; updated_since with missing/bad time returns 400.
lib/super_settings/http_client.rb Base URL query strings preserved by appending the trailing slash to the URI path component only; POST requests no longer retried after connection errors to avoid double-apply.
lib/super_settings/storage/active_record_storage.rb Concurrent duplicate-key race on save! now retried once; RecordNotFound on reload of the original record is safely swallowed during the merge.
lib/super_settings/storage/mongodb_storage.rb Client creation now sets @mongodb before @url_hash so a transient failure does not permanently skip reinit; find_by_key now explicitly marks returned records as persisted.
lib/super_settings/storage/transaction.rb save_all returning false now raises InvalidRecordError instead of silently succeeding, so HTTP storage failures propagate correctly up to callers.
lib/super_settings/mini_i18n.rb development_mode? now checks RAILS_ENV before RACK_ENV and removes the implicit development default when no env var is set - a subtle behavior change for environments that set no env vars.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    REQ["Incoming Request"] --> CALL["RackApplication#call\n(path prefix check)"]
    CALL -->|"prefix matches or empty"| HANDLE["handle_request\n(SCRIPT_NAME + PATH_INFO resolution)"]
    CALL -->|"no match + app"| DOWNSTREAM["Downstream App"]
    CALL -->|"no match + no app"| N404["404 Not Found"]
    HANDLE --> AUTH["check_authorization"]
    AUTH -->|"401 + auth_url"| REDIRECT["302 to login"]
    AUTH -->|"403"| F403["403 Forbidden\n(no redirect loop)"]
    AUTH -->|"authorized"| ROUTE["Route"]
    ROUTE -->|"POST /settings"| PP["post_params\n(JSON parse)"]
    PP -->|"parse error or non-Hash"| B400A["400 Bad Request"]
    PP -->|"valid Hash"| UPDATE["RestAPI.update"]
    UPDATE -->|"save! raises InvalidRecordError"| FAIL["return false"]
    UPDATE -->|"success"| S200A["200 OK"]
    ROUTE -->|"GET /updated_since"| TP["parse time param"]
    TP -->|"nil or ArgumentError"| B400B["400 Bad Request"]
    TP -->|"valid Time"| SETTINGS["Setting.updated_since"]
    SETTINGS --> S200B["200 OK"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    REQ["Incoming Request"] --> CALL["RackApplication#call\n(path prefix check)"]
    CALL -->|"prefix matches or empty"| HANDLE["handle_request\n(SCRIPT_NAME + PATH_INFO resolution)"]
    CALL -->|"no match + app"| DOWNSTREAM["Downstream App"]
    CALL -->|"no match + no app"| N404["404 Not Found"]
    HANDLE --> AUTH["check_authorization"]
    AUTH -->|"401 + auth_url"| REDIRECT["302 to login"]
    AUTH -->|"403"| F403["403 Forbidden\n(no redirect loop)"]
    AUTH -->|"authorized"| ROUTE["Route"]
    ROUTE -->|"POST /settings"| PP["post_params\n(JSON parse)"]
    PP -->|"parse error or non-Hash"| B400A["400 Bad Request"]
    PP -->|"valid Hash"| UPDATE["RestAPI.update"]
    UPDATE -->|"save! raises InvalidRecordError"| FAIL["return false"]
    UPDATE -->|"success"| S200A["200 OK"]
    ROUTE -->|"GET /updated_since"| TP["parse time param"]
    TP -->|"nil or ArgumentError"| B400B["400 Bad Request"]
    TP -->|"valid Time"| SETTINGS["Setting.updated_since"]
    SETTINGS --> S200B["200 OK"]
Loading

Comments Outside Diff (1)

  1. lib/super_settings/local_cache.rb, line 193-199 (link)

    P2 update_setting produces an unfrozen cache hash

    Every other path that replaces @cache (the initial load via set_cache_values, and the cache-miss write in []) calls .freeze on the new hash. update_setting does not, leaving the cache in an inconsistent state. No current code directly mutates @cache once written, so this isn't causing failures today, but it defeats the protection that the freeze is intended to provide.

Reviews (1): Last reviewed commit: "Bug fixes" | Re-trigger Greptile

Comment thread lib/super_settings/mini_i18n.rb
@bdurand

bdurand commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b50f5b95-86dd-4c4c-b8bf-4adbd0adac03

📥 Commits

Reviewing files that changed from the base of the PR and between d9cbfbf and 8dd3f06.

📒 Files selected for processing (1)
  • lib/super_settings/storage/s3_storage.rb

📝 Walkthrough

Walkthrough

Version 2.6.1 documents and implements fixes for web UI escaping, HTTP and Rack validation, configuration loading, cache concurrency, persistence failures, storage races, and runtime compatibility, with expanded regression coverage.

Changes

2.6.1 fixes

Layer / File(s) Summary
Web UI rendering and release metadata
CHANGELOG.md, VERSION, app/helpers/..., lib/super_settings/application*
Updates release metadata, escapes the authentication URL for JavaScript, corrects history pagination attributes, resolves the dark-mode selector, and removes render_edit.
Runtime configuration and coercion
lib/super_settings.rb, lib/super_settings/coerce.rb, lib/super_settings/configuration.rb, lib/super_settings/engine.rb, lib/super_settings/mini_i18n.rb, spec/super_settings/coerce_spec.rb, spec/super_settings_spec.rb
Adds time-library loading, whitespace-only boolean handling, immediate post-deferred configuration execution, deferred ActiveJob hook registration, and synchronized locale cache loading.
HTTP and Rack request flow
lib/super_settings/http_client.rb, lib/super_settings/rack_application.rb, lib/super_settings/controller_actions.rb, lib/super_settings/rest_api.rb, spec/super_settings/http_client_spec.rb, spec/super_settings/rack_application_spec.rb
Preserves URL query strings, retries only idempotent GET requests, validates JSON and time parameters, returns explicit 400/404/403 responses, and safely serializes history timestamps.
Persistence, cache, and storage consistency
lib/super_settings/local_cache.rb, lib/super_settings/setting.rb, lib/super_settings/storage/*, spec/super_settings/local_cache_spec.rb, spec/super_settings/setting_spec.rb, spec/super_settings/storage/*, spec/spec_helper.rb
Serializes JSON writes, improves cache refresh and array handling, avoids no-op timestamp writes, raises and propagates invalid-record failures, retries Active Record duplicate keys, and updates MongoDB, Redis, S3, and test-storage state handling.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.02% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is generic and does not describe the main changes in this PR. Use a short, specific title that mentions the primary fix area, such as XSS, cache, storage, or request validation.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch detailed-review

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

@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: 6

Caution

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

⚠️ Outside diff range comments (1)
lib/super_settings/storage/s3_storage.rb (1)

118-132: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Synchronize S3Storage#s3_bucket updates.
@bucket and @bucket_hash can still be published out of sync under concurrent reconfiguration. Guard the config snapshot and the bucket/hash assignment with a mutex, or freeze S3 config after boot if runtime changes are not intended.

🤖 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 `@lib/super_settings/storage/s3_storage.rb` around lines 118 - 132, Synchronize
the configuration snapshot and publication of `@bucket` and `@bucket_hash` within
S3Storage#s3_bucket using a mutex, ensuring concurrent callers cannot observe
mismatched values. Keep bucket creation and hash assignment in the same
protected critical section, while preserving the existing configuration-change
check and option construction.
🤖 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 `@lib/super_settings/local_cache.rb`:
- Around line 92-94: Update merge_load and the refresh path so collection values
inserted into the shared cache are recursively frozen, or deep-copied before
return, matching the immutability contract enforced by set_cache_values. Ensure
arrays and nested collections cannot be mutated through returned references, and
add a regression spec covering an array added or updated by refresh.

In `@lib/super_settings/rack_application.rb`:
- Around line 239-242: Validate params["settings"] in the request handler before
calling SuperSettings::RestAPI.update: require an array whose every element is a
hash, and return the existing 400 Invalid request response for missing or
invalid values. Only invoke update with the validated settings payload.

In `@lib/super_settings/rest_api.rb`:
- Line 166: Update the history serialization around history_values so created_at
cannot reach the client as null, or coordinate the corresponding client handling
in the history modal script to skip date parsing when created_at is null.
Preserve valid timestamp formatting and ensure null history timestamps no longer
break modal rendering.

In `@lib/super_settings/storage/active_record_storage.rb`:
- Around line 106-133: The retry logic in ActiveRecordStorage#save! must roll
back each failed duplicate-key attempt before retrying. Wrap the attempt
containing the duplicate lookup and save operations in a requires_new
transaction (or move retry handling outside the surrounding
Setting.transaction), then add a regression test covering concurrent creation
and successful retry after ActiveRecord::RecordNotUnique.

In `@lib/super_settings/storage/json_storage.rb`:
- Around line 21-25: Update the save_all read-modify-write flow to coordinate
writers across processes, replacing the process-local SAVE_MUTEX protection with
backend-wide locking or conditional-write-and-retry semantics so concurrent
worker updates are merged without lost changes. Apply the same guarantee
throughout the persistence logic spanning the save_all implementation, and do
not rely on the existing Mutex as sufficient cross-process protection.

In `@lib/super_settings/storage/transaction.rb`:
- Around line 23-25: Update the transaction path around save_all so a false
result raises a dedicated persistence/storage exception rather than
Setting::InvalidRecordError. Ensure Setting.bulk_update does not classify this
exception as validation failure, and update RestAPI.update to translate it into
a structured 5xx/503 response instead of relying on setting.errors or returning
an empty 422.

---

Outside diff comments:
In `@lib/super_settings/storage/s3_storage.rb`:
- Around line 118-132: Synchronize the configuration snapshot and publication of
`@bucket` and `@bucket_hash` within S3Storage#s3_bucket using a mutex, ensuring
concurrent callers cannot observe mismatched values. Keep bucket creation and
hash assignment in the same protected critical section, while preserving the
existing configuration-change check and option construction.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 974de693-819d-4d8a-b3f0-20c2c454de2f

📥 Commits

Reviewing files that changed from the base of the PR and between 53dc5a8 and 4d93c2c.

📒 Files selected for processing (33)
  • CHANGELOG.md
  • VERSION
  • app/helpers/super_settings/settings_helper.rb
  • lib/super_settings.rb
  • lib/super_settings/application.rb
  • lib/super_settings/application/helper.rb
  • lib/super_settings/application/scripts.js
  • lib/super_settings/coerce.rb
  • lib/super_settings/configuration.rb
  • lib/super_settings/controller_actions.rb
  • lib/super_settings/engine.rb
  • lib/super_settings/http_client.rb
  • lib/super_settings/local_cache.rb
  • lib/super_settings/mini_i18n.rb
  • lib/super_settings/rack_application.rb
  • lib/super_settings/rest_api.rb
  • lib/super_settings/setting.rb
  • lib/super_settings/storage/active_record_storage.rb
  • lib/super_settings/storage/json_storage.rb
  • lib/super_settings/storage/mongodb_storage.rb
  • lib/super_settings/storage/redis_storage.rb
  • lib/super_settings/storage/s3_storage.rb
  • lib/super_settings/storage/test_storage.rb
  • lib/super_settings/storage/transaction.rb
  • spec/spec_helper.rb
  • spec/super_settings/coerce_spec.rb
  • spec/super_settings/http_client_spec.rb
  • spec/super_settings/local_cache_spec.rb
  • spec/super_settings/rack_application_spec.rb
  • spec/super_settings/setting_spec.rb
  • spec/super_settings/storage/http_storage_spec.rb
  • spec/super_settings/storage/transaction_spec.rb
  • spec/super_settings_spec.rb
💤 Files with no reviewable changes (1)
  • lib/super_settings/application.rb

Comment thread lib/super_settings/local_cache.rb
Comment thread lib/super_settings/rack_application.rb Outdated
Comment thread lib/super_settings/rest_api.rb
Comment thread lib/super_settings/storage/active_record_storage.rb
Comment thread lib/super_settings/storage/json_storage.rb
Comment thread lib/super_settings/storage/transaction.rb
bdurand and others added 2 commits July 19, 2026 11:13
- Freeze values inserted into LocalCache on refresh, cache miss, and
  direct update so callers cannot mutate the shared cache.
- Return a 400 Bad Request from the Rack app when the settings update
  parameter is missing or is not an array of hashes.
- Guard the web UI history view against history records with a null or
  unparseable timestamp.
- Run each ActiveRecordStorage#save! attempt in a savepoint so a
  duplicate key failure can be rolled back and retried inside the
  surrounding transaction (previously left PostgreSQL transactions in
  an aborted state).
- Raise a dedicated SuperSettings::Setting::PersistenceError when a
  storage engine fails to persist changes instead of reusing
  InvalidRecordError.
- Synchronize construction of the cached S3 bucket so it is always
  published together with the configuration hash it was built from.
- Document that JSON-payload based storage backends do not coordinate
  writes across processes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@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 `@lib/super_settings/storage/s3_storage.rb`:
- Around line 124-137: Update the BUCKET_MUTEX.synchronize block in the bucket
construction flow to obtain one atomic snapshot of all Configuration fields
before computing config_hash or building options. Derive both `@bucket_hash` and
Aws::S3::Resource options, including the bucket name, endpoint, credentials, and
region, exclusively from that same snapshot so concurrent Configuration writes
cannot produce mixed values.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 9d3a2716-41f6-4497-984d-313314da7b34

📥 Commits

Reviewing files that changed from the base of the PR and between 14092f8 and d9cbfbf.

📒 Files selected for processing (15)
  • CHANGELOG.md
  • lib/super_settings/application/scripts.js
  • lib/super_settings/local_cache.rb
  • lib/super_settings/rack_application.rb
  • lib/super_settings/setting.rb
  • lib/super_settings/storage/active_record_storage.rb
  • lib/super_settings/storage/json_storage.rb
  • lib/super_settings/storage/s3_storage.rb
  • lib/super_settings/storage/transaction.rb
  • spec/super_settings/local_cache_spec.rb
  • spec/super_settings/rack_application_spec.rb
  • spec/super_settings/setting_spec.rb
  • spec/super_settings/storage/active_record_storage_spec.rb
  • spec/super_settings/storage/http_storage_spec.rb
  • spec/super_settings/storage/transaction_spec.rb

Comment thread lib/super_settings/storage/s3_storage.rb
@bdurand

bdurand commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

Re the outside-diff comment on S3Storage#s3_bucket: fixed in d9cbfbf. The configuration snapshot, bucket construction, and publication of @bucket/@bucket_hash now all happen inside a mutex, so a concurrent reconfiguration cannot expose a bucket that does not match the current configuration. The configuration-change check and option construction are unchanged.

bdurand and others added 9 commits July 19, 2026 11:57
Building the bucket options and the configuration hash in separate read
passes could cache a bucket built from mixed configuration values under
the settled configuration hash after a concurrent reconfiguration. Both
are now derived from the same read of the configuration attributes so a
torn read cannot be cached permanently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant