feat(worker-bus): Phase 3 — Rust TaskSupervisor + text.analyze native task - #70
Conversation
… task
Completes the WorkerBus v2 Rust half. The TS bridge (services/tauriTaskBridge.ts
+ hybridRouter.ts) already invokes storycraft_task_supervisor_{submit,ping}; this
adds the missing native commands.
- src-tauri/src/commands/task_supervisor.rs: ping (version) + submit dispatcher.
Unknown/bad-payload tasks resolve as { success:false, error } (never a hard Err),
matching the RustTaskResultEvent honest-failure convention so the router's
fallback is driven by result.success. First real task `text.analyze` computes
word/char/sentence/syllable counts + Flesch Reading Ease (pure Rust, no new deps).
8 #[cfg(test)] unit tests cover analysis + dispatch.
- commands/mod.rs + lib.rs: register both commands in generate_handler!.
- services/rustTaskSupervisor.ts: analyzeTextViaRust() probes isRustComputeAvailable()
before routing so a Rust-only task never hits the web worker pool; returns null
(JS fallback) when unavailable. 5 unit tests.
Verified locally: biome + tsc --noEmit + the 5 TS tests green. Rust cannot be
compiled on this low-end host (pre-existing specta="^2" resolution issue, unrelated
to this change) — gated by tauri-build.yml (workflow_dispatch), triggered on push.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>CodeAnt AI is reviewing your PR. Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. |
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| let outcome: Result<Value, String> = match request.task_type.as_str() { | ||
| "text.analyze" => run_text_analyze(&request.payload), | ||
| other => Err(format!("Unknown task type: {other}")), |
There was a problem hiding this comment.
Suggestion: Return a generic client-safe error message for unsupported task types and log the specific task type only in internal diagnostics. [custom_rule_security]
Severity Level: Critical 🚨
Why it matters? 🤔
The code returns a user-visible error string that includes the unsupported task type. That is a real security-style information leak from the external API surface, so the suggestion is correctly identifying a violation.
Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src-tauri/src/commands/task_supervisor.rs
**Line:** 86:86
**Comment:***Custom Rule Security: Return a generic client-safe error message for unsupported task types and log the specific task type only in internal diagnostics.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| let text = payload | ||
| .get("text") | ||
| .and_then(Value::as_str) | ||
| .ok_or_else(|| "text.analyze requires payload.text to be a string".to_string())?; |
There was a problem hiding this comment.
Suggestion: Replace schema-specific validation text with a neutral user-facing error message, and keep payload shape details only in internal logs. [custom_rule_security]
Severity Level: Critical 🚨
Why it matters? 🤔
The validation error is returned to the caller and explicitly reveals the internal payload shape requirement (payload.text). This is a direct exposure of contract details, so the suggestion matches a real violation.
Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src-tauri/src/commands/task_supervisor.rs
**Line:** 111:114
**Comment:***Custom Rule Security: Replace schema-specific validation text with a neutral user-facing error message, and keep payload shape details only in internal logs.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fixUh oh!
There was an error while loading. Please reload this page.
`specta = "2"` / `tauri-specta = "2"` are referenced in no .rs file (build.rs only calls tauri_build::build()) and are absent from Cargo.lock. The `"2"` (=^2 stable) requirement matches nothing on crates.io — only `2.0.0-rc.*` prereleases exist — so `cargo build` fails at resolution before compiling anything: error: failed to select a version for the requirement `specta = "^2"` This is why tauri-build.yml has been red since 2026-05-30 (the older lora.rs errors were already past this gate). Removing the two dead deps is the root-cause fix: resolution succeeds, the whole Rust crate (incl. the Phase 3 task_supervisor) can build, and nothing functional is lost. TS-binding generation can re-add tauri-specta later with a working exact rc pin. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| const handle = await routeTask<RustTextAnalysis>( | ||
| 'text.analyze', | ||
| { text }, | ||
| { target: 'rust', rustComputeEnabled: true, priority: 'low' }, | ||
| ); |
There was a problem hiding this comment.
Suggestion: This call still goes through routeTask, which is designed to fall back to the web worker pool on Rust failure. That breaks the intended "Rust-only" routing guarantee and can enqueue text.analyze on web workers during transient Rust errors; call the Rust bridge directly (or add a strict no-fallback mode) for this path. [logic error]
Severity Level: Major ⚠️
- ⚠️ Rust text.analyze may run on web worker fallback.
- ⚠️ Rust-only compute flag cannot enforce native-only execution.Steps of Reproduction ✅
1. Call `analyzeTextViaRust()` from `services/rustTaskSupervisor.ts:33-52` with a
non-empty string and `{ rustComputeEnabled: true }` so it proceeds past the early null
returns.
2. Inside `analyzeTextViaRust()`, `isRustComputeAvailable()` from
`services/tauriTaskBridge.ts:43-57` is awaited; assume it resolves `true`, then the helper
calls `routeTask<RustTextAnalysis>('text.analyze', {text}, {target: 'rust',
rustComputeEnabled: true, priority: 'low'})` at `services/rustTaskSupervisor.ts:41-45`.
3.`routeTask()` in `services/hybridRouter.ts:36-87` enters the Rust branch (lines 43-77),
calls `invokeRustTask(request)` at lines 48-56, and wraps it in a `try {...} catch (err)
{...}` block; if `invokeRustTask()` throws (for example, due to a transient Tauri error
or missing Rust command), the catch at lines 72-75 logs `'Rust route failed — falling back
to web worker pool'` and deliberately falls through to the web worker path.
4. The web worker path at `services/hybridRouter.ts:79-87` enqueues the same `taskType`
and payload via `bus.enqueue(taskType, payload, { ...busOpts, target: 'web' })`, returns a
`TaskHandle`, and `analyzeTextViaRust()` at `services/rustTaskSupervisor.ts:46-47` awaits
`handle.result`, so the supposedly Rust-only `'text.analyze'` task can be executed on the
WorkerBus web pool whenever the Rust invoke fails, contradicting the comment on lines
25-31 that this helper should never enqueue onto the web worker pool.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** services/rustTaskSupervisor.ts
**Line:** 41:45
**Comment:***Logic Error: This call still goes through `routeTask`, which is designed to fall back to the web worker pool on Rust failure. That breaks the intended "Rust-only" routing guarantee and can enqueue `text.analyze` on web workers during transient Rust errors; call the Rust bridge directly (or add a strict no-fallback mode) for this path.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| pub payload: Value, | ||
| pub priority: String, | ||
| pub target: String, | ||
| pub timeout_ms: u64, |
There was a problem hiding this comment.
Suggestion: The request includes timeout_ms, but task execution ignores it and runs handlers directly, so long-running or stuck tasks will never time out despite the contract exposing a timeout field. Enforce the timeout in the dispatcher so callers get bounded execution behavior. [incomplete implementation]
Severity Level: Major ⚠️
- ⚠️ Rust TaskSupervisor ignores timeoutMs for all tasks.
- ⚠️ Callers cannot bound execution time despite timeout field.Steps of Reproduction ✅
1. A TypeScript caller invokes `routeTask()` from `services/hybridRouter.ts:36-87` with
options including `timeoutMs`, for example `routeTask('text.analyze', payload, {target:
'rust', rustComputeEnabled: true, timeoutMs: 1000})`.
2. In `routeTask()`, the Rust request envelope is built at
`services/hybridRouter.ts:48-55` as `const request: RustTaskRequest = {taskId, taskType,
payload, priority, target: 'rust', timeoutMs: opts.timeoutMs??300_000};`, so the
configured timeout is serialized into the `RustTaskRequest`.
3.`invokeRustTask()` in `services/tauriTaskBridge.ts:15-23` forwards this
`RustTaskRequest` over Tauri IPC to the Rust command `storycraft_task_supervisor_submit`,
which is registered in `src-tauri/src/lib.rs:52-61` and implemented in
`src-tauri/src/commands/task_supervisor.rs:80-107`.
4. Inside `storycraft_task_supervisor_submit`, the `RustTaskRequest` struct defined at
`src-tauri/src/commands/task_supervisor.rs:29-38` includes `pub timeout_ms: u64`, but the
function body only inspects `request.task_type` (lines 84-87) and never reads
`request.timeout_ms`; a repo-wide Grep for `timeout_ms` shows usage only in the struct
definition and test fixtures (lines 261, 278, 296), so task handlers (currently
`run_text_analyze`) always run to completion with no enforcement of the caller-provided
timeout.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src-tauri/src/commands/task_supervisor.rs
**Line:** 35:35
**Comment:***Incomplete Implementation: The request includes `timeout_ms`, but task execution ignores it and runs handlers directly, so long-running or stuck tasks will never time out despite the contract exposing a timeout field. Enforce the timeout in the dispatcher so callers get bounded execution behavior.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| let words: Vec<&str> = text.split_whitespace().filter(|w| !w.is_empty()).collect(); | ||
| let word_count = words.len(); | ||
| let sentence_count = count_sentences(text); | ||
| let syllable_count: usize = words.iter().map(|w| count_syllables(w)).sum(); |
There was a problem hiding this comment.
Suggestion: Building a full Vec<&str> of all words before counting syllables adds avoidable memory pressure for large manuscripts. This task is explicitly for large inputs, so compute word and syllable counts in a streaming pass instead of collecting all tokens first. [performance]
Severity Level: Major ⚠️
- ⚠️ Text.analyze allocates Vec for every word token.
- ⚠️ Large manuscripts increase memory footprint unnecessarily.Steps of Reproduction ✅
1. When a `text.analyze` task is submitted, `storycraft_task_supervisor_submit()` at
`src-tauri/src/commands/task_supervisor.rs:80-107` dispatches to `run_text_analyze()` for
`task_type == "text.analyze"` (lines 84-86), and `run_text_analyze()` at lines 110-118
extracts `payload["text"]` then calls `analyze_text(text)`.
2.`analyze_text()` in `src-tauri/src/commands/task_supervisor.rs:122-151` computes
character counts, then allocates `let words: Vec<&str>=text.split_whitespace().filter(|w|!w.is_empty()).collect();`atline126andderives`letword_count=words.len();`atline127,storingaborrowedsliceforeverytoken.3.Thefunctionthenseparatelycomputes`letsentence_count=count_sentences(text);`atline129,whichre-iteratesovertheentire`text`,and`letsyllable_count:usize=words.iter().map(|w|count_syllables(w)).sum();`atline130,traversingthe`words`vectortocountsyllablespertoken.4.Forlargemanuscripts(themodulecommentatlines7-9explicitlycallsoutoffloading"large manuscripts"),eachcallto`analyze_text()`materializesa`Vec`ofallwhitespace-separatedtokenseventhoughonlyaggregatecountsareused;thisper-wordallocationincreasesmemoryandallocationoverheadonevery`text.analyze`task,whereasastreamingpassover`split_whitespace()`couldcomputeboth`word_count`and`syllable_count`withoutstoringthefulllist,matchingthe"no allocation beyond theword iterator"intentstatedatlines120-121.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src-tauri/src/commands/task_supervisor.rs
**Line:** 126:130
**Comment:***Performance: Building a full `Vec<&str>` of all words before counting syllables adds avoidable memory pressure for large manuscripts. This task is explicitly for large inputs, so compute word and syllable counts in a streaming pass instead of collecting all tokens first.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fixCodeAnt AI finished reviewing your PR. |
…port Two fixes surfaced once the specta resolution block was removed and the Rust crate actually compiled on CI: - lora.rs: `check_lora_environment` deserializes the Python sidecar's JSON stdout via `serde_json::from_str::<LoraEnvReport>` (lora.rs:209), but the struct derived only Serialize -> E0277 broke the whole-crate compile (the real reason tauri-build.yml has been red since 2026-05-30). Add Deserialize (already imported). - task_supervisor.rs: `json` is only used in #[cfg(test)] -> move the import into the test module to silence the unused-import warning in the non-test build. Verified: this is the last error/warning the ubuntu+macos runners reported after the specta fix; re-dispatching tauri-build to confirm a clean native compile. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t fields
The native ubuntu/macos build now compiles cleanly and produces .deb/.rpm/.AppImage
bundles (verified via tauri-build dispatch). The only remaining diagnostic was a
dead_code warning: priority/target/timeoutMs/retryPolicy are accepted from the TS
router per the worker-bus contract but not yet read by the dispatcher. allow(dead_code)
with a note until the retry/timeout path is implemented.
NOTE: tauri-build still exits non-zero at the very end on the Tauri updater signing
step ("incorrect updater private key password: Missing comment in secret key") — a
malformed repo signing secret, unrelated to code; the app + all 3 bundles build fine.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>…d findings Bring the living/canonical docs in line with the current code + CI reality. Sprint handoffs, docs/history, and version-sprint docs are archival (point-in-time) and left frozen by design. - CHANGELOG.md: 2026-06-03 cluster — WorkerBus v2 Phase 3 (Rust TaskSupervisor + text.analyze), Local-AI Phase 2.3 perf sprint (pipelineLruCache dispose-on-evict, aiRetry property tests, kokoro, ADRs, coverage ratchet L74/B60/F66/S72), and the Tauri-build unblock (### Fixed). - AUDIT.md: new 2026-06-03 audit section (Phase 3 + the 3 root-caused tauri-build blockers + the remaining non-code updater-signing / Windows-setup issues); follow-up chain extended. - docs/adr/0003-workerbus-hybrid-routing.md (new) + ADR index: records the v2 runtime → manager → hybrid router → legacy adapter → Rust TaskSupervisor layering, the honest-failure contract, and the "no PR-CI gate for Rust" verification constraint. - docs/TAURI-CI.md: "Verifying native (Rust) changes" (dispatch-on-branch is the gate) + "Build health & known blockers (2026-06-03)". - AGENTS.md: agent rule — no PR-CI gate for src-tauri; dispatch tauri-build on the branch. - README.md: ADR doc-hub entry now lists WorkerBus hybrid routing. - ROADMAP.md: Local-AI Perfection Phases 2.3/2.4 marked done; WorkerBus Phase 3 noted. No i18n keys added (docs only); badges left as-is (match last CI-measured numbers). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
WorkerBus v2 — Phase 3 (Rust TaskSupervisor) + build unblock + docs
Completes the native half of the hybrid router, unblocks the long-red Tauri build, and reconciles the docs.
Phase 3 — Rust TaskSupervisor
src-tauri/src/commands/task_supervisor.rs(new) +commands/mod.rs+lib.rsregistration —storycraft_task_supervisor_ping(version) +storycraft_task_supervisor_submit(taskType dispatcher; unknown/bad payload →{ success:false, error }, never a hardErr). First native tasktext.analyze(word/char/sentence/syllable + Flesch Reading Ease, pure Rust, 8#[cfg(test)]tests).services/rustTaskSupervisor.ts(new) —analyzeTextViaRust()probesisRustComputeAvailable()before routing (Rust-only task never hits the web pool;null→ JS fallback); 5 unit tests.Fixed — Tauri build (red since 2026-05-30, root-caused via CI dispatch)
src-tauri/Cargo.toml— removed unused, unresolvablespecta = "2"/tauri-specta = "2"(only2.0.0-rc.*exist →cargoresolution failed before any compile).src-tauri/src/lora.rs—LoraEnvReportdeserialized but derived onlySerialize(E0277) → addedDeserialize.task_supervisor.rs—jsonimport → test mod;#[allow(dead_code)]on wire-contract fields.Docs
CHANGELOG, AUDIT, ADR 0003 (WorkerBus hybrid routing), docs/TAURI-CI (native-verification method + known blockers), AGENTS, README doc-hub, ROADMAP, TODO.
Verification
tsc --noEmit+ 5 TS tests green.src-tauri/):tauri-build.ymldispatched on this branch now compiles clean (Finished release~4m18s) and bundles.deb/.rpm/.AppImageon ubuntu. The run's final updater-signing step fails on a malformedTAURI_SIGNING_PRIVATE_KEYrepo secret (maintainer task), and the Windows runner fails in thesetupcomposite (infra) — both unrelated to this code.🤖 Generated with Claude Code