Skip to content

feat: expose variation ID, SDK status, and harden runtime safety - #14

Open
WuJiayi0307 wants to merge 6 commits into
featbit:masterfrom
WuJiayi0307:feat/openfeature-provider
Open

feat: expose variation ID, SDK status, and harden runtime safety#14
WuJiayi0307 wants to merge 6 commits into
featbit:masterfrom
WuJiayi0307:feat/openfeature-provider

Conversation

@WuJiayi0307

@WuJiayi0307 WuJiayi0307 commented Jul 22, 2026

Copy link
Copy Markdown

Summary

This PR now contains only the FeatBit Python SDK capabilities required by the standalone OpenFeature provider:

  • expose the stable variation ID as EvalDetail.variation_id
  • add thread-safe data-update status listeners through add_listener() / remove_listener()
  • preserve backward compatibility for existing EvalDetail construction, JSON output, and custom status-provider implementations
  • update the package version to 1.1.8

The OpenFeature provider implementation has been moved to the dedicated repository:
https://github.com/featbit/openfeature-provider-python-server

Status mapping support

The provider repository maps FeatBit SDK states as follows:

  • OK → OpenFeature READY
  • INTERRUPTEDSTALE
  • OFFFATAL
  • shutdown → NOT_READY

Validation

  • FeatBit SDK: 58 passed
  • Provider repository: 52 passed
  • Flake8: passed in both repositories
  • Wheel and source distributions: built successfully
  • Isolated install smoke test: OpenFeature Client → Provider → real FeatBit offline Evaluator returned v2, variant variation-v2, status READY

The SDK PR remains compatible with Python 3.6–3.12. The standalone provider requires Python 3.10+.

Summary by CodeRabbit

  • New Features

    • Evaluation details now include a stable variation ID.
    • Added listeners for data-update status changes.
    • SDK now supports Python 3.6 through 3.12.
  • Bug Fixes

    • Improved handling of invalid configuration data and unsupported evaluation defaults.
    • Event delivery and shutdown errors are isolated to prevent application interruptions.
    • Client shutdown is now safer, repeatable, and more responsive.
  • Documentation

    • Added reliability verification guidance and updated release information for version 1.1.8.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The SDK adds stable variation IDs and data-update status listeners, hardens client and background-thread shutdown, isolates event failures, adds runtime audits and integration checks, updates production packaging, and documents the 1.1.8 behavior and verification procedures.

SDK behavior and validation

Layer / File(s) Summary
Evaluation and status contracts
fbclient/common_types.py, fbclient/interfaces.py, fbclient/status.py, tests/test_fbclient.py, tests/test_data_update_status_provider.py
Evaluation details expose successful variation IDs, while status listeners support registration, ordered notifications, removal, and listener-failure isolation.
Runtime lifecycle and failure isolation
fbclient/client.py, fbclient/streaming.py, fbclient/event_processor.py, fbclient/notice_broadcaster.py, fbclient/utils/..., fbclient/config.py, fbclient/__init__.py
Client shutdown becomes idempotent and guarded; event delivery, streaming, dispatching, notice broadcasting, repeatable tasks, and configuration defaults receive lifecycle and concurrency updates.
Runtime safety validation
tests/test_runtime_safety.py, scripts/resource_audit.py, scripts/live_integration_check.py
Tests and scripts exercise concurrent evaluation, listener churn, shutdown behavior, thread cleanup, memory retention, packaging checks, and live service evaluation.
Distribution and release surfaces
setup.py, MANIFEST.in, release/*, .github/workflows/code-quality.yaml
Production package discovery, source distribution contents, development extras, version metadata, and CI audit/build checks are updated.
Usage and reliability documentation
README.md, docs/REVIEW.md
Documentation describes Python 3.12 support, status-listener usage, variation detail fields, reliability checks, live verification, and operational considerations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FBClient
  participant Streaming
  participant EventProcessor
  participant NoticeBroadcater
  FBClient->>Streaming: stop and join
  FBClient->>EventProcessor: stop and join dispatcher
  FBClient->>NoticeBroadcater: enqueue sentinel and join
  FBClient-->>FBClient: mark closed and isolate component failures
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: variation IDs, SDK status listeners, and runtime-safety hardening.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 3

🧹 Nitpick comments (1)
tests/test_fbclient.py (1)

32-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Lock down the unchanged JSON contract.

This verifies constructor compatibility but not the stated backward-compatible JSON output. Assert that to_json_dict() remains unchanged even when variation_id is provided.

Proposed test addition
     assert detail_with_id.variation_id == "variation-id"
+    assert detail_with_id.to_json_dict() == legacy_detail.to_json_dict()
🤖 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/test_fbclient.py` around lines 32 - 39, Extend
test_eval_detail_variation_id_is_backward_compatible to call to_json_dict() on
detail_with_id and assert its serialized dictionary remains identical to the
established legacy JSON contract, excluding variation_id. Keep the existing
constructor and variation_id assertions unchanged.
🤖 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 `@featbit_openfeature/provider.py`:
- Around line 84-94: Update shutdown so _flag_change_listener is cleared only
after _client.flag_tracker.remove_flag_change_notifier(listener) succeeds; if
removal raises, retain the listener reference so cleanup can be retried and
subsequent initialization cannot register a duplicate listener.

In `@README.md`:
- Line 21: Update the README installation sentence to say “Install the SDK using
pip” instead of “install the sdk in using pip,” while preserving the Python
compatibility statement.

In `@release/description.md`:
- Around line 4-6: Update the release-note section in description.md to use the
standard heading “Breaking changes” and the text “No breaking changes,” or
remove the empty section entirely.

---

Nitpick comments:
In `@tests/test_fbclient.py`:
- Around line 32-39: Extend test_eval_detail_variation_id_is_backward_compatible
to call to_json_dict() on detail_with_id and assert its serialized dictionary
remains identical to the established legacy JSON contract, excluding
variation_id. Keep the existing constructor and variation_id assertions
unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 457f6a3b-e2e5-43ec-a493-af734f3ee36a

📥 Commits

Reviewing files that changed from the base of the PR and between 0ad40d8 and e09df9d.

📒 Files selected for processing (22)
  • .github/workflows/code-quality.yaml
  • MANIFEST.in
  • README.md
  • docs/openfeature.md
  • fbclient/__init__.py
  • fbclient/common_types.py
  • featbit_openfeature/__init__.py
  • featbit_openfeature/impl/__init__.py
  • featbit_openfeature/impl/context_converter.py
  • featbit_openfeature/impl/details_converter.py
  • featbit_openfeature/provider.py
  • featbit_openfeature/py.typed
  • release/description.md
  • release/package.json
  • setup.py
  • tests/openfeature/__init__.py
  • tests/openfeature/fixtures/bootstrap.json
  • tests/openfeature/test_context_converter.py
  • tests/openfeature/test_details_converter.py
  • tests/openfeature/test_offline_integration.py
  • tests/openfeature/test_provider.py
  • tests/test_fbclient.py
💤 Files with no reviewable changes (1)
  • fbclient/init.py

Comment thread featbit_openfeature/provider.py Outdated
Comment thread README.md Outdated
Comment thread release/description.md
@WuJiayi0307

Copy link
Copy Markdown
Author

Addressed all four review findings in 06d29d1:

  • preserve the flag-change listener when removal fails so shutdown can retry without duplicate registration
  • add a regression test for failed listener removal and retry
  • lock down the unchanged EvalDetail.to_json_dict() contract
  • fix the README installation sentence and release-note heading

Validation after the fixes: 105 passed, Flake8 passes, and both wheel and source distribution rebuild successfully. The three inline review threads are now marked addressed/outdated. GitHub Actions is still awaiting first-time fork approval from a repository maintainer.

@WuJiayi0307 WuJiayi0307 changed the title feat: add OpenFeature Python server provider feat: expose variation ID and data update status listeners Jul 23, 2026

@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.

Caution

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

⚠️ Outside diff range comments (1)
fbclient/status.py (1)

59-84: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Preserve transition order when publishing callbacks.

Concurrent update_state calls can notify in reverse order: one thread can snapshot OK, release __lock, then another can publish INTERRUPTED before the first invokes its callback. Serialize queued publications in transition order while keeping user callbacks outside __lock, and add a concurrent ordering regression test.

🤖 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 `@fbclient/status.py` around lines 59 - 84, The update_state callback flow must
preserve state-transition order under concurrent calls. Modify update_state and
its listener-publication mechanism to queue committed states while holding the
existing lock, then drain that queue serially outside __lock so callbacks
execute in transition order without invoking user listeners under the lock; add
a concurrent regression test verifying ordered OK/INTERRUPTED publications.
🤖 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.

Outside diff comments:
In `@fbclient/status.py`:
- Around line 59-84: The update_state callback flow must preserve
state-transition order under concurrent calls. Modify update_state and its
listener-publication mechanism to queue committed states while holding the
existing lock, then drain that queue serially outside __lock so callbacks
execute in transition order without invoking user listeners under the lock; add
a concurrent regression test verifying ordered OK/INTERRUPTED publications.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 62070b14-1267-4839-993b-9a887e3830d5

📥 Commits

Reviewing files that changed from the base of the PR and between e09df9d and 824aee8.

📒 Files selected for processing (8)
  • MANIFEST.in
  • README.md
  • fbclient/interfaces.py
  • fbclient/status.py
  • release/description.md
  • release/package.json
  • tests/test_data_update_status_provider.py
  • tests/test_fbclient.py
💤 Files with no reviewable changes (1)
  • MANIFEST.in
🚧 Files skipped from review as they are similar to previous changes (2)
  • release/package.json
  • tests/test_fbclient.py

@deleteLater

Copy link
Copy Markdown
Contributor

@greptileai

@WuJiayi0307 WuJiayi0307 changed the title feat: expose variation ID and data update status listeners feat: expose variation ID, SDK status, and harden runtime safety Jul 29, 2026
@WuJiayi0307

Copy link
Copy Markdown
Author

Completed the Python SDK reliability review in bf4c842.

Key fixes:

  • deterministic/idempotent WebSocket, event dispatcher, periodic task, and notice broadcaster shutdown
  • listener registry synchronization and interruptible reconnect backoff
  • runtime event/shutdown exception isolation
  • safe invalid bootstrap/default handling
  • independent Config defaults
  • clean production wheel excluding tests and migrated OpenFeature code

Validation:

  • 66 tests passed
  • Flake8 passed
  • 80,000 evaluations / 8 workers: 0 errors, 0 leaked SDK threads
  • retained traced memory: 24,389 bytes (threshold 2 MiB)
  • clean wheel: 27 files, no tests/, no featbit_openfeature/
  • added docs/REVIEW.md, scripts/resource_audit.py, and scripts/live_integration_check.py

FeatBit Cloud:

  • the 2026-07-24 live run verified WebSocket connection/data sync, remote python-app-release evaluation, concurrent gray-release traffic, and reconnect after disconnect
  • the new live script makes WebSocket/status/evaluation/event/flush/shutdown verification repeatable without printing or persisting the environment secret

GitHub Actions still requires maintainer approval for this fork workflow.

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

🤖 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 `@docs/REVIEW.md`:
- Around line 82-87: Update the live integration check in
scripts/live_integration_check.py to assert that the recorded state_after_close
equals StateType.OFF, preserving the checklist’s documented OFF transition claim
alongside the existing readiness and thread-cleanup checks.

In `@fbclient/event_processor.py`:
- Around line 82-84: Guard the self.__dispatcher.join(5.0) call in the event
processor shutdown path by checking that the current thread is not
self.__dispatcher, mirroring the existing guards in sibling shutdown
implementations. Preserve the subsequent is_alive warning behavior for joins
that are performed.

In `@fbclient/notice_broadcaster.py`:
- Around line 51-55: Move self.__notice_queue.put(notice) inside the
self.__lock-protected section in broadcast(), immediately after the __closed
check, so enqueueing is serialized with stop() setting __closed and adding the
shutdown sentinel.

In `@scripts/live_integration_check.py`:
- Around line 22-26: Update sdk_threads() and its baseline/comparison logic to
capture each matching thread as an ident-to-name mapping, compare leaked threads
by thread identity rather than name, and retain the mapped names only for
reporting. Apply the same identity-based handling to the referenced lines 92-94
while preserving the existing featbit- thread filter.

In `@scripts/resource_audit.py`:
- Around line 29-31: Validate the --workers and --evaluations arguments before
the audit begins, rejecting values below 1 to prevent invalid worker-pool usage.
Update the workload partitioning near the worker execution logic to use divmod,
distributing the remainder across workers so every requested evaluation is
processed exactly once.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 19fccbb1-7b77-467d-ac44-c56f2be15355

📥 Commits

Reviewing files that changed from the base of the PR and between 824aee8 and bf4c842.

📒 Files selected for processing (18)
  • .github/workflows/code-quality.yaml
  • README.md
  • docs/REVIEW.md
  • fbclient/client.py
  • fbclient/config.py
  • fbclient/event_processor.py
  • fbclient/notice_broadcaster.py
  • fbclient/status.py
  • fbclient/streaming.py
  • fbclient/utils/http_client.py
  • fbclient/utils/repeatable_task.py
  • release/description.md
  • scripts/live_integration_check.py
  • scripts/resource_audit.py
  • setup.py
  • tests/test_data_update_status_provider.py
  • tests/test_fbclient.py
  • tests/test_runtime_safety.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • fbclient/status.py

Comment thread docs/REVIEW.md
Comment on lines +82 to +87
1. authenticated WebSocket connection and initial data synchronization;
2. SDK status reaches `OK`;
3. remote flag evaluation returns value, reason, and variation ID;
4. feature-flag, identify, and custom metric events enter the delivery path;
5. explicit flush and final synchronous drain complete;
6. status changes to `OFF` and no SDK threads remain after close.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the live-check criterion enforce the documented OFF transition.

The documented script records state_after_close but does not assert that it equals StateType.OFF; it only checks pre-close readiness and leaked threads. Add that assertion to scripts/live_integration_check.py, or remove the OFF-state claim from this checklist.

🤖 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 `@docs/REVIEW.md` around lines 82 - 87, Update the live integration check in
scripts/live_integration_check.py to assert that the recorded state_after_close
equals StateType.OFF, preserving the checklist’s documented OFF transition claim
alongside the existing readiness and thread-cleanup checks.

Comment on lines +82 to +84
self.__dispatcher.join(5.0)
if self.__dispatcher.is_alive():
log.warning('FB Python SDK: event dispatcher did not stop in time')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Missing self-join guard on dispatcher join (inconsistent with sibling patterns).

self.__dispatcher.join(5.0) isn't guarded by a current_thread() is not self.__dispatcher check, unlike the equivalent pattern just introduced in streaming.py (Line 285), repeatable_task.py (Line 29), and notice_broadcaster.py (Line 64) in this same PR. If stop() is ever invoked from within the dispatcher's own thread (e.g., a custom event_processor_imp shutdown path), Thread.join() raises RuntimeError: cannot join current thread.

🔧 Proposed fix: mirror the guard used elsewhere
+from threading import current_thread
...
-                self.__dispatcher.join(5.0)
-                if self.__dispatcher.is_alive():
-                    log.warning('FB Python SDK: event dispatcher did not stop in time')
+                if current_thread() is not self.__dispatcher:
+                    self.__dispatcher.join(5.0)
+                    if self.__dispatcher.is_alive():
+                        log.warning('FB Python SDK: event dispatcher did not stop in time')
📝 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.

Suggested change
self.__dispatcher.join(5.0)
if self.__dispatcher.is_alive():
log.warning('FB Python SDK: event dispatcher did not stop in time')
from threading import current_thread
...
if current_thread() is not self.__dispatcher:
self.__dispatcher.join(5.0)
if self.__dispatcher.is_alive():
log.warning('FB Python SDK: event dispatcher did not stop in 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 `@fbclient/event_processor.py` around lines 82 - 84, Guard the
self.__dispatcher.join(5.0) call in the event processor shutdown path by
checking that the current thread is not self.__dispatcher, mirroring the
existing guards in sibling shutdown implementations. Preserve the subsequent
is_alive warning behavior for joins that are performed.

Comment on lines 51 to 55
def broadcast(self, notice: Notice):
with self.__lock:
if self.__closed:
return
self.__notice_queue.put(notice)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Narrow race: a notice can be enqueued after the shutdown sentinel.

broadcast() checks __closed and calls self.__notice_queue.put(notice) outside the lock. If stop() sets __closed=True and enqueues the stop sentinel between those two steps, the real notice lands in the queue after the sentinel, so __run() exits on the sentinel and silently drops that trailing notice during shutdown.

🔒 Proposed fix: enqueue inside the same locked section
 def broadcast(self, notice: Notice):
     with self.__lock:
         if self.__closed:
             return
-    self.__notice_queue.put(notice)
+        self.__notice_queue.put(notice)

Since stop() also sets __closed=True under the same lock before enqueueing the sentinel, this fully serializes the ordering and removes the race.

📝 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.

Suggested change
def broadcast(self, notice: Notice):
with self.__lock:
if self.__closed:
return
self.__notice_queue.put(notice)
def broadcast(self, notice: Notice):
with self.__lock:
if self.__closed:
return
self.__notice_queue.put(notice)
🤖 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 `@fbclient/notice_broadcaster.py` around lines 51 - 55, Move
self.__notice_queue.put(notice) inside the self.__lock-protected section in
broadcast(), immediately after the __closed check, so enqueueing is serialized
with stop() setting __closed and adding the shutdown sentinel.

Comment on lines +22 to +26
def sdk_threads():
return sorted(
thread.name for thread in threading.enumerate()
if thread.name.startswith("featbit-")
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Detect leaked threads by identity, not name.

A new leaked SDK thread with the same name as a baseline thread is currently ignored. Capture ident -> name and compare identities, retaining names only for reporting.

Proposed fix
 def sdk_threads():
-    return sorted(
-        thread.name for thread in threading.enumerate()
+    return {
+        thread.ident: thread.name for thread in threading.enumerate()
         if thread.name.startswith("featbit-")
-    )
+    }
 
-    leaked_threads = [
-        name for name in sdk_threads() if name not in baseline_threads
-    ]
+    leaked_threads = [
+        name for ident, name in sdk_threads().items()
+        if ident not in baseline_threads
+    ]

Also applies to: 92-94

🤖 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 `@scripts/live_integration_check.py` around lines 22 - 26, Update sdk_threads()
and its baseline/comparison logic to capture each matching thread as an
ident-to-name mapping, compare leaked threads by thread identity rather than
name, and retain the mapped names only for reporting. Apply the same
identity-based handling to the referenced lines 92-94 while preserving the
existing featbit- thread filter.

Comment thread scripts/resource_audit.py
Comment on lines +29 to +31
parser.add_argument("--evaluations", type=int, default=80000)
parser.add_argument("--workers", type=int, default=8)
parser.add_argument("--max-retained-bytes", type=int, default=2 * 1024 * 1024)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate inputs and distribute the requested workload exactly.

--workers 0 raises at Line 52, and non-divisible values under-run the audit (for example, 9 evaluations / 2 workers runs only 8). Reject values below 1 and distribute the remainder with divmod.

Proposed fix
     args = parser.parse_args()
+    if args.evaluations < 1 or args.workers < 1:
+        parser.error("--evaluations and --workers must be positive")
 
-    per_worker = max(1, args.evaluations // args.workers)
+    base, remainder = divmod(args.evaluations, args.workers)
+    workloads = [base + (worker < remainder) for worker in range(args.workers)]
 
-    def evaluate(worker):
+    def evaluate(work_item):
+        worker, workload = work_item
         completed = 0
         try:
-            for index in range(per_worker):
+            for index in range(workload):
                 ...
 
     with ThreadPoolExecutor(max_workers=args.workers) as executor:
-        completed = sum(executor.map(evaluate, range(args.workers)))
+        completed = sum(executor.map(evaluate, enumerate(workloads)))

Also applies to: 52-53

🤖 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 `@scripts/resource_audit.py` around lines 29 - 31, Validate the --workers and
--evaluations arguments before the audit begins, rejecting values below 1 to
prevent invalid worker-pool usage. Update the workload partitioning near the
worker execution logic to use divmod, distributing the remainder across workers
so every requested evaluation is processed exactly once.

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.

2 participants