feat: replace stringly-typed closed-set params with enums#505
Conversation
disabled_proxies took bare strings, so a typo silently left the handler registered.
Node gains the matching DispatchOrder and TaskLogLevel unions. The string overloads stay, deprecated.
An unknown visualize format silently rendered Mermaid; the rest raised at a distance from the call that was wrong.
Replaces three parallel VALID_ROLES sets. A persisted role that no longer parses now falls back to viewer rather than being trusted.
Both were unvalidated strings threaded through the storage trait; the shells parse at the boundary so a legacy row still reads.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis pull request replaces stringly typed closed-set values with enums and wire conversions across Rust storage, Java, Node, and Python APIs. It also updates Python resource scopes and pooling behavior, adds Node request-scoped resources, and revises related tests, documentation, and exports. ChangesRust storage and pub/sub
Java SDK and dashboard
Node SDK and resources
Python typed APIs and resources
Documentation and release notes
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related issues
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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 `@sdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthStore.java`:
- Around line 206-213: Update createSession to explicitly validate that role is
non-null before calling role.wire(), and return the same controlled “invalid
role” error behavior used by createUser. Preserve the existing session creation
flow for valid roles.
In `@sdks/java/src/main/java/org/byteveda/taskito/DefaultTaskito.java`:
- Around line 752-755: Update the deprecated queryTaskLogs(String taskName,
String level, long sinceMs, long limit) overload to preserve the raw wire-level
value for lenient dashboard filtering instead of eagerly calling
TaskLogLevel.fromWire(level), so unknown levels produce an empty result set
rather than throwing. Apply the same change to the corresponding overload near
the second referenced location, including the analogous state filter if present.
In `@sdks/java/src/main/java/org/byteveda/taskito/Taskito.java`:
- Around line 372-380: Resolve the ambiguous same-arity overloads in Taskito for
queryTaskLogs and listWorkflowRuns by giving the enum- and String-based APIs
distinct method names or a shared options/type wrapper. Preserve null as the
documented no-filter value so calls such as queryTaskLogs(task, null, since,
limit) and listWorkflowRuns(name, null, limit, offset) compile unambiguously,
and update deprecation references as needed.
In `@sdks/python/taskito/dashboard/auth.py`:
- Line 157: Update get_session() to normalize the persisted role with
_role_or_viewer() before constructing Session, matching the existing
_row_to_user() behavior. Ensure unknown serialized roles become the viewer role
while valid roles remain 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 Plus
Run ID: ce8469b1-e7b9-4967-b99c-5505fa530519
📒 Files selected for processing (72)
crates/taskito-core/src/pubsub.rscrates/taskito-core/src/storage/diesel_common/pubsub.rscrates/taskito-core/src/storage/diesel_common/workers.rscrates/taskito-core/src/storage/mod.rscrates/taskito-core/src/storage/models.rscrates/taskito-core/src/storage/postgres/pubsub.rscrates/taskito-core/src/storage/records.rscrates/taskito-core/src/storage/redis_backend/pubsub.rscrates/taskito-core/src/storage/redis_backend/workers.rscrates/taskito-core/src/storage/sqlite/pubsub.rscrates/taskito-core/src/storage/sqlite/tests.rscrates/taskito-core/src/storage/traits.rscrates/taskito-core/tests/rust/storage_tests.rscrates/taskito-java/src/convert.rscrates/taskito-java/src/queue/pubsub.rscrates/taskito-java/src/worker.rscrates/taskito-node/src/convert/pubsub.rscrates/taskito-node/src/queue/pubsub.rscrates/taskito-python/src/py_queue/pubsub.rscrates/taskito-python/src/py_queue/worker.rsdocs/content/docs/python/api-reference/queue/index.mdxdocs/content/docs/python/guides/resources/proxies.mdxsdks/java/src/main/java/org/byteveda/taskito/DefaultTaskito.javasdks/java/src/main/java/org/byteveda/taskito/Taskito.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/api/CoreHandlers.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/api/WorkflowsHandlers.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthHandlers.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthStore.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Policy.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Role.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Session.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/TokenAuth.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/User.javasdks/java/src/main/java/org/byteveda/taskito/model/DispatchOrder.javasdks/java/src/main/java/org/byteveda/taskito/model/TaskLogLevel.javasdks/java/src/test/java/org/byteveda/taskito/core/DispatchOrderTest.javasdks/java/src/test/java/org/byteveda/taskito/core/QueueTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardAuthTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardClient.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardNativeTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/auth/AuthStoreTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/oauth/OAuthFlowTest.javasdks/java/src/test/java/org/byteveda/taskito/model/ClosedSetEnumTest.javasdks/node/src/dashboard/auth/store.tssdks/node/src/dashboard/handlers/core.tssdks/node/src/dashboard/index.tssdks/node/src/index.tssdks/node/src/queue.tssdks/node/src/types.tssdks/python/taskito/__init__.pysdks/python/taskito/app.pysdks/python/taskito/dashboard/auth.pysdks/python/taskito/enums.pysdks/python/taskito/interception/__init__.pysdks/python/taskito/interception/interceptor.pysdks/python/taskito/interception/mode.pysdks/python/taskito/mixins/_log_consumer.pysdks/python/taskito/mixins/decorators.pysdks/python/taskito/mixins/predicates.pysdks/python/taskito/mixins/pubsub.pysdks/python/taskito/predicates/__init__.pysdks/python/taskito/predicates/outcomes.pysdks/python/taskito/proxies/__init__.pysdks/python/taskito/proxies/built_in.pysdks/python/taskito/workflows/__init__.pysdks/python/taskito/workflows/builder.pysdks/python/taskito/workflows/fan_out.pysdks/python/taskito/workflows/run.pysdks/python/taskito/workflows/tracker/gates.pysdks/python/taskito/workflows/types.pysdks/python/tests/python/test_closed_set_enums.pysdks/python/tests/resources/test_proxies.py
TASK meant pool checkout and REQUEST meant fresh-per-task — the reverse of every other SDK, so the same scope silently behaved differently. Pool settings on a non-pooled scope now raise, which catches the one case the rename could pass over quietly.
Fresh on every useResource() call, disposed with the task — the one scope Java had that Node could express.
THREAD has no Node equivalent, REQUEST no Python one. Writing that down is what makes the shared names trustworthy.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
sdks/python/taskito/resources/runtime.py (1)
54-72: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy liftMake pooled-resource reload scope-aware.
reload()reachesrecreate(), which always calls_create_resource(). For a pooled definition, that leaves the old pool in_poolswhile adding a singleton to_instances;resolve()returns the singleton, but task injection continues using the old pool. Recreate or replace the pool instead of creating a worker instance.🤖 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 `@sdks/python/taskito/resources/runtime.py` around lines 54 - 72, Update the reload/recreate flow to branch on the resource definition scope before calling _create_resource: pooled definitions must recreate or replace their ResourcePool in _pools, preserving pool configuration, dependencies, and prewarming behavior, rather than creating an instance in _instances. Keep singleton/resource handling unchanged and ensure subsequent resolve and task injection use the replacement pool.
🤖 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/content/docs/python/guides/resources/configuration.mdx`:
- Around line 80-82: Update the pool configuration example to use scope="pooled"
instead of scope="task" while retaining pool_size and pool_min, so it matches
the documented validation rule.
In `@docs/content/docs/python/guides/resources/dependency-injection.mdx`:
- Around line 116-120: Update the create_session resource declaration to include
its db dependency through the decorator’s depends_on configuration, while
preserving the pooled scope and pool_size=10 settings so the factory receives
the worker-scoped db resource.
In `@sdks/python/taskito/resources/definition.py`:
- Around line 54-64: Normalize or validate self.scope at the start of
ResourceDefinition.__post_init__, converting supported string values such as
"worker" into the corresponding ResourceScope member and rejecting unknown
values before pool_size/pool_min validation. Ensure subsequent runtime scope
branches receive a ResourceScope value and preserve the existing pooled-scope
validation behavior.
In `@sdks/python/taskito/resources/runtime.py`:
- Around line 165-170: Update teardown_task to invoke defn.teardown(instance)
through run_maybe_async(), matching the existing worker teardown behavior so
both synchronous and asynchronous teardowns execute before the function
completes. Preserve the current exception logging and resource/name checks.
---
Outside diff comments:
In `@sdks/python/taskito/resources/runtime.py`:
- Around line 54-72: Update the reload/recreate flow to branch on the resource
definition scope before calling _create_resource: pooled definitions must
recreate or replace their ResourcePool in _pools, preserving pool configuration,
dependencies, and prewarming behavior, rather than creating an instance in
_instances. Keep singleton/resource handling unchanged and ensure subsequent
resolve and task injection use the replacement pool.
🪄 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: e5625723-28a9-4b97-86d4-a1bceb1bd82c
📒 Files selected for processing (17)
CHANGELOG.mddocs/content/docs/java/guides/resources/dependency-injection.mdxdocs/content/docs/node/guides/resources/dependency-injection.mdxdocs/content/docs/python/api-reference/queue/resources.mdxdocs/content/docs/python/guides/resources/configuration.mdxdocs/content/docs/python/guides/resources/dependency-injection.mdxsdks/java/src/main/java/org/byteveda/taskito/resources/ResourceScope.javasdks/node/src/index.tssdks/node/src/resources/index.tssdks/node/src/resources/runtime.tssdks/node/src/resources/types.tssdks/node/test/resources/resources.test.tssdks/python/taskito/mixins/resources.pysdks/python/taskito/resources/definition.pysdks/python/taskito/resources/runtime.pysdks/python/tests/resources/test_resource_system_full.pysdks/python/tests/worker/test_lifecycle_parity.py
🚧 Files skipped from review as they are similar to previous changes (1)
- sdks/node/src/index.ts
An enum/String overload pair is ambiguous on a bare null, so the documented no-filter call stopped compiling; the level filter also has to stay lenient for dashboard query params.
createSession dereferenced a null role, and a persisted session role was handed back as a raw string instead of being coerced like the user row already was.
A wire-string scope matched no runtime branch, reload left pooled resources on a stale pool, and an async task teardown was dropped unawaited.
|
@CodeRabbit full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
sdks/python/taskito/resources/runtime.py (1)
188-203: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
recreate()corruptsresolve()for reloadable TASK/THREAD resources.The new
POOLEDbranch is handled correctly, butTASKandTHREADscoped resources still fall through to the genericold/_create_resource()path. Since TASK/THREAD instances are never stored inself._instances(they live inacquire_for_task's per-call scope orself._thread_locals), this fallthrough calls the factory and writes the result intoself._instances[name]— a singletonresolve()will now incorrectly return for a scope that should never be resolvable that way. Reachable whenever a TASK/THREAD-scoped resource hasreloadable=Trueandreload()/recreate()runs.🐛 Proposed fix
if defn.scope == ResourceScope.POOLED: self._build_pool(name) self._recreation_count[name] = self._recreation_count.get(name, 0) + 1 return True + if defn.scope in (ResourceScope.TASK, ResourceScope.THREAD): + # Nothing persistent lives in `_instances` for these scopes; + # falling through to `_create_resource` would plant a stray + # singleton that `resolve()` would then incorrectly serve. + self._recreation_count[name] = self._recreation_count.get(name, 0) + 1 + return True if old is not None and defn.teardown is not None: run_maybe_async(defn.teardown(old)) self._create_resource(name)🤖 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 `@sdks/python/taskito/resources/runtime.py` around lines 188 - 203, Update recreate() to handle TASK and THREAD-scoped definitions without routing them through the generic old/_create_resource() path. After identifying defn.scope, preserve the POOLED behavior and ensure TASK/THREAD resources are not created or stored in self._instances, so resolve() remains invalid for those scopes; only singleton-style resources should use the existing teardown and _create_resource flow.
🤖 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 `@crates/taskito-node/src/queue/pubsub.rs`:
- Around line 48-50: The public subscription bindings currently use permissive
SubscriptionMode::from_wire parsing, allowing unknown modes to become Fanout. In
crates/taskito-node/src/queue/pubsub.rs lines 48-50, strictly accept only
"fanout" and "log", returning invalid_arg for other supplied values; in
crates/taskito-python/src/py_queue/pubsub.rs line 83, apply the same validation
and raise PyValueError for invalid values. Add binding regression tests covering
unknown modes at both sites.
In `@docs/content/docs/python/api-reference/queue/resources.mdx`:
- Around line 41-43: Update the pool_size row in the queue resources API
reference so its default description states that the default is 4, matching
_build_pool() and the sibling documentation. Leave the type, scope restriction,
and other pool fields unchanged.
In `@sdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthStore.java`:
- Around line 61-66: Restore deprecated String overloads for createUser and both
createSession variants in AuthStore, delegating role conversion through
Role.fromWire(...) while retaining the Role-based methods as primary. Ensure
invalid string roles produce the existing controlled “invalid role” error, and
update null-role tests to cast null explicitly to Role to resolve overload
ambiguity.
In `@sdks/node/src/queue.ts`:
- Around line 1026-1030: Update queryLogs so its task filter accepts legacy
string values alongside the typed API, preserving compatibility for string-typed
callers. Retain TaskLogLevel typing for typed callers and ensure unrecognized
string filters continue producing no matching logs rather than causing
type-check failures.
In `@sdks/node/src/resources/runtime.ts`:
- Around line 215-230: Update request-scope resolution around the uncached
branch in the resource resolver to track the active dependency ancestry and
reject a resource name already present in that ancestry before invoking
startFactory. Propagate the ancestry through ResourceContext.use and nested
resolve calls so both direct and indirect request-scope cycles fail without
starting another build, while preserving normal independent resolutions and
teardown behavior. Add regression tests covering direct self-resolution and an
indirect cycle through another request resource.
In `@sdks/node/src/resources/types.ts`:
- Around line 7-10: Validate the scope argument at the Queue.resource() and/or
ResourceRuntime.register() registration boundary using the existing
RESOURCE_SCOPES collection. Reject unknown JavaScript scope values by throwing
before registration or lifecycle handling reaches
ResourceRuntime.createTaskScope(), while preserving behavior for all declared
ResourceScope values.
In `@sdks/python/taskito/__init__.py`:
- Around line 44-52: Add ConsumerErrorAction to the public exports in
taskito.__init__: import it from the module that defines _log_consumer’s
consumer error types and include it in __all__ alongside the other re-exported
enums, so typed callers can use the stable package API.
In `@sdks/python/taskito/dashboard/auth.py`:
- Line 402: Guard the decoded session data before the Session construction in
the authentication flow, ensuring only object/dictionary values proceed to
access data.get and create Session. Return None for lists, scalars, or other
non-object decoded values while preserving the existing TypeError handling.
In `@sdks/python/taskito/mixins/decorators.py`:
- Around line 304-305: Update the ValueError description in the decorator
documentation to state that on_false accepts PredicateAction members as well as
the wire strings "defer" and "cancel", while retaining the existing
default_defer_seconds negative-value condition.
In `@sdks/python/taskito/workflows/tracker/gates.py`:
- Around line 54-64: Update on_gate_timeout to accept GateAction | str and
normalize action with coerce_enum(GateAction, action) before the identity check,
preserving legacy "approve" strings as approvals while leaving timer cleanup
behavior unchanged.
In `@sdks/python/tests/python/test_closed_set_enums.py`:
- Around line 27-33: Update test_interception_accepts_enum_and_string to wrap
its assertions in a try/finally block and call close() on both enum_queue and
string_queue in the finally block, ensuring resources are released even when an
assertion fails.
---
Outside diff comments:
In `@sdks/python/taskito/resources/runtime.py`:
- Around line 188-203: Update recreate() to handle TASK and THREAD-scoped
definitions without routing them through the generic old/_create_resource()
path. After identifying defn.scope, preserve the POOLED behavior and ensure
TASK/THREAD resources are not created or stored in self._instances, so resolve()
remains invalid for those scopes; only singleton-style resources should use the
existing teardown and _create_resource flow.
🪄 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: 86497ee2-2d21-4b47-b0f5-fdd4ec468cc4
📒 Files selected for processing (87)
CHANGELOG.mdcrates/taskito-core/src/pubsub.rscrates/taskito-core/src/storage/diesel_common/pubsub.rscrates/taskito-core/src/storage/diesel_common/workers.rscrates/taskito-core/src/storage/mod.rscrates/taskito-core/src/storage/models.rscrates/taskito-core/src/storage/postgres/pubsub.rscrates/taskito-core/src/storage/records.rscrates/taskito-core/src/storage/redis_backend/pubsub.rscrates/taskito-core/src/storage/redis_backend/workers.rscrates/taskito-core/src/storage/sqlite/pubsub.rscrates/taskito-core/src/storage/sqlite/tests.rscrates/taskito-core/src/storage/traits.rscrates/taskito-core/tests/rust/storage_tests.rscrates/taskito-java/src/convert.rscrates/taskito-java/src/queue/pubsub.rscrates/taskito-java/src/worker.rscrates/taskito-node/src/convert/pubsub.rscrates/taskito-node/src/queue/pubsub.rscrates/taskito-python/src/py_queue/pubsub.rscrates/taskito-python/src/py_queue/worker.rsdocs/content/docs/java/guides/resources/dependency-injection.mdxdocs/content/docs/node/guides/resources/dependency-injection.mdxdocs/content/docs/python/api-reference/queue/index.mdxdocs/content/docs/python/api-reference/queue/resources.mdxdocs/content/docs/python/guides/resources/configuration.mdxdocs/content/docs/python/guides/resources/dependency-injection.mdxdocs/content/docs/python/guides/resources/proxies.mdxsdks/java/src/main/java/org/byteveda/taskito/DefaultTaskito.javasdks/java/src/main/java/org/byteveda/taskito/Taskito.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthHandlers.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthStore.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Policy.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Role.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Session.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/TokenAuth.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/User.javasdks/java/src/main/java/org/byteveda/taskito/model/DispatchOrder.javasdks/java/src/main/java/org/byteveda/taskito/model/TaskLogLevel.javasdks/java/src/main/java/org/byteveda/taskito/resources/ResourceScope.javasdks/java/src/test/java/org/byteveda/taskito/core/DispatchOrderTest.javasdks/java/src/test/java/org/byteveda/taskito/core/QueueTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardAuthTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardClient.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardNativeTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/auth/AuthStoreTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/oauth/OAuthFlowTest.javasdks/java/src/test/java/org/byteveda/taskito/model/ClosedSetEnumTest.javasdks/node/src/dashboard/auth/store.tssdks/node/src/dashboard/handlers/core.tssdks/node/src/dashboard/index.tssdks/node/src/index.tssdks/node/src/queue.tssdks/node/src/resources/index.tssdks/node/src/resources/runtime.tssdks/node/src/resources/types.tssdks/node/src/types.tssdks/node/test/resources/resources.test.tssdks/python/taskito/__init__.pysdks/python/taskito/app.pysdks/python/taskito/dashboard/auth.pysdks/python/taskito/enums.pysdks/python/taskito/interception/__init__.pysdks/python/taskito/interception/interceptor.pysdks/python/taskito/interception/mode.pysdks/python/taskito/mixins/_log_consumer.pysdks/python/taskito/mixins/decorators.pysdks/python/taskito/mixins/predicates.pysdks/python/taskito/mixins/pubsub.pysdks/python/taskito/mixins/resources.pysdks/python/taskito/predicates/__init__.pysdks/python/taskito/predicates/outcomes.pysdks/python/taskito/proxies/__init__.pysdks/python/taskito/proxies/built_in.pysdks/python/taskito/resources/definition.pysdks/python/taskito/resources/runtime.pysdks/python/taskito/workflows/__init__.pysdks/python/taskito/workflows/builder.pysdks/python/taskito/workflows/fan_out.pysdks/python/taskito/workflows/run.pysdks/python/taskito/workflows/tracker/gates.pysdks/python/taskito/workflows/types.pysdks/python/tests/dashboard/test_auth.pysdks/python/tests/python/test_closed_set_enums.pysdks/python/tests/resources/test_proxies.pysdks/python/tests/resources/test_resource_system_full.pysdks/python/tests/worker/test_lifecycle_parity.py
The lenient reader exists for persisted rows; caller input must not silently become a fan-out subscription.
AuthStore is public, so making it Role-only broke existing string callers; the wire-form overloads delegate through a strict parse.
An unknown scope from JS fell through to the task branch; a self-referential request resource recursed forever, since that scope is uncached by design.
Typing it to the union broke existing callers and forced the dashboard to cast; an unknown filter must return nothing, not fail.
GateConfig is public, so a legacy "approve" string resolved the timeout as a rejection. Also exports ConsumerErrorAction and rejects a session payload that is not an object.
|
@CodeRabbit full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
sdks/python/taskito/resources/runtime.py (1)
193-200: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winPrevent TASK/THREAD scopes from falling through to worker recreation.
A reloadable
TASKresource reaches_create_resource(), populates_instances, and is then returned byresolve()as a shared singleton—violating its per-task lifecycle. Give non-worker scopes explicit recreation behavior or reject recreation for them.🤖 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 `@sdks/python/taskito/resources/runtime.py` around lines 193 - 200, Update the recreation logic around _create_resource so TASK and THREAD-scoped resources do not fall through to worker-level recreation or become shared entries in _instances. Add explicit per-scope recreation behavior that preserves their lifecycle, or reject recreation for these scopes; retain the existing POOLED and worker-scoped behavior.
🤖 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 `@CHANGELOG.md`:
- Around line 19-24: Update the changelog entry describing closed-set parameters
to qualify the Java nullable query-filter exception: caller-produced task-log
levels and workflow states use enums with deprecated String overloads, while
nullable Java filters retain a single wire-form String API. Document that
unknown task-log levels match nothing and unknown workflow states remain
rejected, without changing other listed parameters.
In `@crates/taskito-python/src/py_queue/worker.rs`:
- Around line 837-842: Update set_worker_status to strictly accept only the wire
values "active" and "draining" before calling storage.update_worker_status;
construct the corresponding WorkerStatus directly, and return PyValueError for
any other input instead of using WorkerStatus::from_wire. Preserve the existing
PyRuntimeError mapping for storage failures.
In `@sdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Session.java`:
- Line 9: The public Session record is no longer source-compatible because its
role component changed from String to Role. In Session.java at lines 9-9 and
User.java at lines 12-12, retain the existing wire-form String role API (or
explicitly version the Java SDK as a breaking change), ensuring direct
construction and role() consumers remain compatible; update related conversions
only as needed.
In `@sdks/node/src/resources/runtime.ts`:
- Around line 261-267: Update the task-resource resolution flow around the task
cache and ResourceContext construction to pass a dependency chain containing the
current task name when resolving its dependencies, rather than preserving
building unchanged. Ensure mixed-scope cycles such as task a → request b → task
a are detected and rejected with the intended ResourceError, and add coverage
for this reverse mixed-scope cycle.
In `@sdks/python/tests/resources/test_resource_system_full.py`:
- Around line 710-723: Update the test around the ResourceRuntime instance to
wrap initialization, acquisition, reload, and assertions in a try/finally block,
then call runtime.teardown() in finally so the rebuilt pool and instances are
disposed even when assertions fail.
---
Outside diff comments:
In `@sdks/python/taskito/resources/runtime.py`:
- Around line 193-200: Update the recreation logic around _create_resource so
TASK and THREAD-scoped resources do not fall through to worker-level recreation
or become shared entries in _instances. Add explicit per-scope recreation
behavior that preserves their lifecycle, or reject recreation for these scopes;
retain the existing POOLED and worker-scoped behavior.
🪄 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: 3872594c-afc8-4b33-aaa1-ac7fcdc9e9ee
📒 Files selected for processing (87)
CHANGELOG.mdcrates/taskito-core/src/pubsub.rscrates/taskito-core/src/storage/diesel_common/pubsub.rscrates/taskito-core/src/storage/diesel_common/workers.rscrates/taskito-core/src/storage/mod.rscrates/taskito-core/src/storage/models.rscrates/taskito-core/src/storage/postgres/pubsub.rscrates/taskito-core/src/storage/records.rscrates/taskito-core/src/storage/redis_backend/pubsub.rscrates/taskito-core/src/storage/redis_backend/workers.rscrates/taskito-core/src/storage/sqlite/pubsub.rscrates/taskito-core/src/storage/sqlite/tests.rscrates/taskito-core/src/storage/traits.rscrates/taskito-core/tests/rust/storage_tests.rscrates/taskito-java/src/convert.rscrates/taskito-java/src/queue/pubsub.rscrates/taskito-java/src/worker.rscrates/taskito-node/src/convert/pubsub.rscrates/taskito-node/src/queue/pubsub.rscrates/taskito-python/src/py_queue/pubsub.rscrates/taskito-python/src/py_queue/worker.rsdocs/content/docs/java/guides/resources/dependency-injection.mdxdocs/content/docs/node/guides/resources/dependency-injection.mdxdocs/content/docs/python/api-reference/queue/index.mdxdocs/content/docs/python/api-reference/queue/resources.mdxdocs/content/docs/python/guides/resources/configuration.mdxdocs/content/docs/python/guides/resources/dependency-injection.mdxdocs/content/docs/python/guides/resources/proxies.mdxsdks/java/src/main/java/org/byteveda/taskito/DefaultTaskito.javasdks/java/src/main/java/org/byteveda/taskito/Taskito.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthHandlers.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthStore.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Policy.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Role.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Session.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/TokenAuth.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/User.javasdks/java/src/main/java/org/byteveda/taskito/model/DispatchOrder.javasdks/java/src/main/java/org/byteveda/taskito/model/TaskLogLevel.javasdks/java/src/main/java/org/byteveda/taskito/resources/ResourceScope.javasdks/java/src/test/java/org/byteveda/taskito/core/DispatchOrderTest.javasdks/java/src/test/java/org/byteveda/taskito/core/QueueTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardAuthTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardClient.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardNativeTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/auth/AuthStoreTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/oauth/OAuthFlowTest.javasdks/java/src/test/java/org/byteveda/taskito/model/ClosedSetEnumTest.javasdks/node/src/dashboard/auth/store.tssdks/node/src/dashboard/handlers/core.tssdks/node/src/dashboard/index.tssdks/node/src/index.tssdks/node/src/queue.tssdks/node/src/resources/index.tssdks/node/src/resources/runtime.tssdks/node/src/resources/types.tssdks/node/src/types.tssdks/node/test/resources/resources.test.tssdks/python/taskito/__init__.pysdks/python/taskito/app.pysdks/python/taskito/dashboard/auth.pysdks/python/taskito/enums.pysdks/python/taskito/interception/__init__.pysdks/python/taskito/interception/interceptor.pysdks/python/taskito/interception/mode.pysdks/python/taskito/mixins/_log_consumer.pysdks/python/taskito/mixins/decorators.pysdks/python/taskito/mixins/predicates.pysdks/python/taskito/mixins/pubsub.pysdks/python/taskito/mixins/resources.pysdks/python/taskito/predicates/__init__.pysdks/python/taskito/predicates/outcomes.pysdks/python/taskito/proxies/__init__.pysdks/python/taskito/proxies/built_in.pysdks/python/taskito/resources/definition.pysdks/python/taskito/resources/runtime.pysdks/python/taskito/workflows/__init__.pysdks/python/taskito/workflows/builder.pysdks/python/taskito/workflows/fan_out.pysdks/python/taskito/workflows/run.pysdks/python/taskito/workflows/tracker/gates.pysdks/python/taskito/workflows/types.pysdks/python/tests/dashboard/test_auth.pysdks/python/tests/python/test_closed_set_enums.pysdks/python/tests/resources/test_proxies.pysdks/python/tests/resources/test_resource_system_full.pysdks/python/tests/worker/test_lifecycle_parity.py
Coercing caller input to Active would silently un-drain a worker; the lenient reader stays for persisted rows.
A task resource in a cycle handed back its own pending promise, so the build awaited itself forever instead of failing.
A Java enum is not a String, so typing the record accessors broke every existing role() caller — unlike Python (str subclass) and Node (string union), where the typed role stays assignable. The enum keeps validation and the authorization decision.
|
@CodeRabbit full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
sdks/python/taskito/workflows/run.py (1)
120-135: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winValidate
fmtbefore reading workflow state.
visualize()currently fetches the DAG and status before coercingfmt, so an invalid format can surface an unrelated backend or missing-run error instead of the documentedValueError.Proposed fix
def visualize(self, fmt: DiagramFormat | str = DiagramFormat.MERMAID) -> str: """Render the workflow DAG with live node statuses. @@ """ + diagram_format = coerce_enum(DiagramFormat, fmt, param="fmt") dag_bytes = self._queue._inner.get_workflow_definition_dag(self.id) nodes, edges = nodes_and_edges_from_dag_bytes(dag_bytes) @@ - if coerce_enum(DiagramFormat, fmt, param="fmt") is DiagramFormat.DOT: + if diagram_format is DiagramFormat.DOT: return render_dot(nodes, edges, statuses)🤖 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 `@sdks/python/taskito/workflows/run.py` around lines 120 - 135, Update visualize() to coerce and validate fmt before calling get_workflow_definition_dag or status(), so invalid formats raise the documented ValueError without reading workflow state; reuse the validated DiagramFormat value for the subsequent rendering logic.crates/taskito-core/src/pubsub.rs (1)
16-24: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDebug-formatting the enum leaks the Rust variant name into a user-facing error.
{mode:?}onSubscriptionModenow renders the bare derived-Debug variant name ("Fanout"/"Log"), not the wire string. Previouslymode: &strmade this message show the caller's quoted wire value (e.g.got "fanout"). This error propagates to end users through the Node/Python/Javadeclare_topicbindings, so callers now see "got Fanout" instead of the wire text they passed.modealready has aDisplayimpl backed byas_str()— use it.🐛 Proposed fix
if !mode.is_log() { return Err(QueueError::Config(format!( - "only \"log\" topics are declarable, got {mode:?}" + "only \"log\" topics are declarable, got \"{mode}\"" ))); }For enums, it will use the name of the variant and, if applicable, (, then the Debug values of the fields, then ). Derived Debug formats are not stable across Rust versions, confirming the plain variant-name output.
🤖 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 `@crates/taskito-core/src/pubsub.rs` around lines 16 - 24, Update the error formatting in validate_topic_declaration to use SubscriptionMode’s Display implementation instead of Debug formatting for mode. Preserve the existing message structure so the reported value remains the caller-facing wire string, such as "fanout" or "log".
🧹 Nitpick comments (1)
sdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthStore.java (1)
61-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMark legacy string role overloads as deprecated.
Lines 61 and 211-219 retain the wire-form compatibility APIs, but callers receive no migration signal. Add
@Deprecatedto all threeString roleoverloads while retaining their strict parsing behavior.Proposed fix
+ `@Deprecated` public synchronized User createUser(String username, String password, String role) { + `@Deprecated` public Session createSession(String username, String role) { + `@Deprecated` public Session createSession(String username, String role, long ttlSeconds) {Based on learnings: keep deprecated wire-form
Stringoverloads for backward compatibility.Also applies to: 211-219
🤖 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 `@sdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthStore.java` around lines 61 - 63, Mark all three AuthStore createUser overloads that accept a String role, including the overload near createUser(String username, String password, String role), with `@Deprecated` while preserving their existing parseRole-based strict parsing and compatibility behavior.Source: Learnings
🤖 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 `@sdks/java/src/main/java/org/byteveda/taskito/DefaultTaskito.java`:
- Around line 223-226: Validate the enum parameters explicitly in the typed
dispatchOrder overloads and the writeTaskLog(..., extra) overload before calling
wire(). Reject null order or level using the API’s established documented
argument-validation exception, while preserving existing behavior for non-null
values.
In `@sdks/python/taskito/resources/runtime.py`:
- Around line 26-31: Update the ResourceScope documentation in the runtime
resource manager to replace stale “request-scoped” terminology with
“task-scoped,” including the lifecycle class docstring and the resolve()
docstring. Ensure the wording consistently describes TASK resources as created
fresh per task and does not reference the removed REQUEST scope.
---
Outside diff comments:
In `@crates/taskito-core/src/pubsub.rs`:
- Around line 16-24: Update the error formatting in validate_topic_declaration
to use SubscriptionMode’s Display implementation instead of Debug formatting for
mode. Preserve the existing message structure so the reported value remains the
caller-facing wire string, such as "fanout" or "log".
In `@sdks/python/taskito/workflows/run.py`:
- Around line 120-135: Update visualize() to coerce and validate fmt before
calling get_workflow_definition_dag or status(), so invalid formats raise the
documented ValueError without reading workflow state; reuse the validated
DiagramFormat value for the subsequent rendering logic.
---
Nitpick comments:
In `@sdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthStore.java`:
- Around line 61-63: Mark all three AuthStore createUser overloads that accept a
String role, including the overload near createUser(String username, String
password, String role), with `@Deprecated` while preserving their existing
parseRole-based strict parsing and compatibility behavior.
🪄 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: 766cde90-b6b3-4816-849a-54b6459559ae
📒 Files selected for processing (85)
CHANGELOG.mdcrates/taskito-core/src/pubsub.rscrates/taskito-core/src/storage/diesel_common/pubsub.rscrates/taskito-core/src/storage/diesel_common/workers.rscrates/taskito-core/src/storage/mod.rscrates/taskito-core/src/storage/models.rscrates/taskito-core/src/storage/postgres/pubsub.rscrates/taskito-core/src/storage/records.rscrates/taskito-core/src/storage/redis_backend/pubsub.rscrates/taskito-core/src/storage/redis_backend/workers.rscrates/taskito-core/src/storage/sqlite/pubsub.rscrates/taskito-core/src/storage/sqlite/tests.rscrates/taskito-core/src/storage/traits.rscrates/taskito-core/tests/rust/storage_tests.rscrates/taskito-java/src/convert.rscrates/taskito-java/src/queue/pubsub.rscrates/taskito-java/src/worker.rscrates/taskito-node/src/convert/pubsub.rscrates/taskito-node/src/queue/pubsub.rscrates/taskito-python/src/py_queue/pubsub.rscrates/taskito-python/src/py_queue/worker.rsdocs/content/docs/java/guides/resources/dependency-injection.mdxdocs/content/docs/node/guides/resources/dependency-injection.mdxdocs/content/docs/python/api-reference/queue/index.mdxdocs/content/docs/python/api-reference/queue/resources.mdxdocs/content/docs/python/guides/resources/configuration.mdxdocs/content/docs/python/guides/resources/dependency-injection.mdxdocs/content/docs/python/guides/resources/proxies.mdxsdks/java/src/main/java/org/byteveda/taskito/DefaultTaskito.javasdks/java/src/main/java/org/byteveda/taskito/Taskito.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthHandlers.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/AuthStore.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Policy.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/Role.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/auth/TokenAuth.javasdks/java/src/main/java/org/byteveda/taskito/model/DispatchOrder.javasdks/java/src/main/java/org/byteveda/taskito/model/TaskLogLevel.javasdks/java/src/main/java/org/byteveda/taskito/resources/ResourceScope.javasdks/java/src/test/java/org/byteveda/taskito/core/DispatchOrderTest.javasdks/java/src/test/java/org/byteveda/taskito/core/QueueTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardAuthTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardClient.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/DashboardNativeTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/auth/AuthStoreTest.javasdks/java/src/test/java/org/byteveda/taskito/dashboard/oauth/OAuthFlowTest.javasdks/java/src/test/java/org/byteveda/taskito/model/ClosedSetEnumTest.javasdks/node/src/dashboard/auth/store.tssdks/node/src/dashboard/handlers/core.tssdks/node/src/dashboard/index.tssdks/node/src/index.tssdks/node/src/queue.tssdks/node/src/resources/index.tssdks/node/src/resources/runtime.tssdks/node/src/resources/types.tssdks/node/src/types.tssdks/node/test/resources/resources.test.tssdks/python/taskito/__init__.pysdks/python/taskito/app.pysdks/python/taskito/dashboard/auth.pysdks/python/taskito/enums.pysdks/python/taskito/interception/__init__.pysdks/python/taskito/interception/interceptor.pysdks/python/taskito/interception/mode.pysdks/python/taskito/mixins/_log_consumer.pysdks/python/taskito/mixins/decorators.pysdks/python/taskito/mixins/predicates.pysdks/python/taskito/mixins/pubsub.pysdks/python/taskito/mixins/resources.pysdks/python/taskito/predicates/__init__.pysdks/python/taskito/predicates/outcomes.pysdks/python/taskito/proxies/__init__.pysdks/python/taskito/proxies/built_in.pysdks/python/taskito/resources/definition.pysdks/python/taskito/resources/runtime.pysdks/python/taskito/workflows/__init__.pysdks/python/taskito/workflows/builder.pysdks/python/taskito/workflows/fan_out.pysdks/python/taskito/workflows/run.pysdks/python/taskito/workflows/tracker/gates.pysdks/python/taskito/workflows/types.pysdks/python/tests/dashboard/test_auth.pysdks/python/tests/python/test_closed_set_enums.pysdks/python/tests/resources/test_proxies.pysdks/python/tests/resources/test_resource_system_full.pysdks/python/tests/worker/test_lifecycle_parity.py
They dereferenced the argument, so a null surfaced as an incidental NPE rather than the documented IllegalArgumentException. Also drops the stale REQUEST wording left in the resource runtime docstrings.
All six splits of #503. Does not close it — a few inventory rows remain (see below).
Closed-set parameters that were typed
str/String/&strnow carry an enum. Most wereguarded by a hand-rolled membership check; a few by nothing at all —
Queue(disabled_proxies=["requests"])silently leftrequests_sessionregistered, which iswhat prompted the issue.
Splits 1-5 are non-breaking: every wire and stored value is byte-identical and existing string
callers keep working. Split 6 is a breaking Python change — see below.
Commits
feat(python): type the built-in proxy idsBuiltInProxy— an unknown id now raises listing the valid ones instead of leaving the handler registeredfeat(java): take enums for dispatch order and log levelDispatchOrder,TaskLogLevel,WorkflowStateonlistWorkflowRuns; matching Node unionsfeat(python): type the closed-set task and workflow optionsInterceptionMode,PredicateAction,ConsumerErrorAction,GateAction,FanStrategy,DiagramFormatfeat: type the dashboard user role across the SDKsRolein all three SDKs, replacing three parallelVALID_ROLESsetsfeat(core): type subscription mode and worker statusSubscriptionMode,WorkerStatuson the storage traitfeat(python)!: rename resource scopes to match the other SDKsfeat(node): add the request resource scopeuseResource(), disposed with the taskSplit 6: the scope names meant different things
The issue filed this as "
ResourceScopehas three different shapes". It is worse than that —the same name meant a different lifecycle per SDK, verified in the runtimes rather than the
enum docs:
WORKERWORKERworkerTHREADTHREADREQUESTTASKtaskTASKPOOLEDpooledREQUESTresources/runtime.pybuilt aResourcePoolforResourceScope.TASKand didpool.acquire()/pool.release(); Java'sTASKis per-invocation and its pooling lives underPOOLED. Soscope=TASKported from Python to Java silently lost the pool — no boundedcapacity, no checkout, an instance per task instead.
Java's names win. Python renames
TASK→POOLEDandREQUEST→TASK; Node gainsrequest.Migration.
scope="request"now raises (the member is gone).scope="task"still resolvesbut builds per task instead of pooling — the one path that could change behaviour quietly, so
ResourceDefinitionrejectspool_size/pool_minon any non-POOLEDscope, which is howpooled use is always spelled. Move pooled resources to
scope="pooled".Two scopes stay platform-bound, now documented in all three SDKs:
threadhas no Nodeequivalent (a worker runs its tasks on one event loop), and
requesthas no Python one(resources arrive by injection, resolved once per task).
Other notes for a reviewer
TaskLogLevel, notLogLevel. Java'slogging/LogLeveland Node'sutilsLogLevelare the SDK's own logger level — an unrelated concept the issue called out.
dispatchOrder(String)still throwsIllegalArgumentException, not the model enum'sSerializationException— callers of that overload predate the enum.?level=/?state=is untrusted input that must filter to nothing, not fail the request.viewerin all three SDKs rather thanbeing trusted. Same fail-closed treatment for an unrecognized
mode/statusin core.SubscriptionModeandWorkerStatusare converted ateach persistence boundary, so the Redis JSON and every SQL column keep their existing strings
and a legacy row still reads.
Verification
cargo test --workspace— 285 tests;--features postgresand--features redischeckclean;
cargo clippy --workspace --all-targetsclean../gradlew buildgreen — spotless, checkstyle, strict javadoc, tests.CHANGELOG.mdcarries the breaking-change entry.Left for follow-ups
Inventory rows the issue's split list never reached:
NodeStatusraw comparisons in the sagaorchestrator and tracker DAG,
Strategycoercion inruntime_config,WorkflowCondition,StorageBackend, and the webhookDeliveryStatusfilter. Open registries (proxy handler names,resource/task/queue names) stay strings, per the issue's non-goals.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation