feat(engine): signal a run that completed with node errors (#661 L1) - #32
Conversation
…r continue/route (#661 L1)
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe change adds ChangesRun status reporting
Estimated code review effort: 3 (Moderate) | ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
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: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/observability.rs`:
- Around line 115-118: Update the documentation for failed_node_ids() to remove
the unconditional guarantee that RunStatus::Failed produces a non-empty result;
clarify that it is non-empty only for node failures, while driver, recursion,
and checkpointer failures may return an empty vector.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 14d419b4-9233-4d98-8854-6303383859e8
📒 Files selected for processing (2)
src/engine.rssrc/observability.rs
…very Failed run A Failed run built from a driver-level fault (recursion limit, checkpointer error, tinyagents graph error) records no per-node Error step, so failed_node_ids() returns an empty vector even though the status is Failed. Only a node-caused stop-policy failure records its Error step before ending the run. Clarify the contract: non-empty means 'these nodes failed', never a proxy for the run's outcome — consult Run::status for that.
Summary
Fixes #661 L1 (OpenCompany workflows audit): under
on_error: "continue"(androute), a node that fails turns its failure into a data item and the run proceeds — but the run's terminal status stayedRunStatus::Completed, so a host read the run as an unqualified success while a node had actually failed.routeis validated (it needs an error-port edge);continuehad no signal at all. The failure was only discoverable by scanning every node's output items for an{error: …}shape.This surfaces it on the run record the engine already assembles:
RunStatus::CompletedWithErrors— new variant betweenCompletedandFailed. The run reached a terminal node (so it did notFail), but at least one node failed under a non-stoppolicy.engine.rs): anyStepStatus::Errorstep ⇒CompletedWithErrors, elseCompleted.stop-policy failures still bubble out asErrand assemble aFailedrecord, unchanged.Run::failed_node_ids()— the ids of the nodes that errored (theErrorsteps), so a host can act on which nodes failed without scanning the output. Empty for a cleanCompleted; non-empty forCompletedWithErrors/Failed.The signal lands on
observability::Run, delivered to every host viaRunObserver::on_run_finish(the same hook OpenHuman/OpenCompany already use to persist per-node output), so it reaches consumers on the observed run path with no new wiring.Scope notes for review
RunOutcomeunchanged. The signal is on the observedRunrecord, which is where the audit finding points ("WorkflowRuncarries no failure signal") and where the host's existing observer reads it. Mirroring it ontoRunOutcome/theresumereturn (which don't collect steps today) is a reasonable follow-up if a non-observing consumer needs it — called out rather than bolted on inconsistently.routecounts as with-errors too. A routed failure still errored the node (StepStatus::Erroris recorded); a downstream recovery branch handling it does not erase that the node itself failed. If you'd rather a fully-recoveredrouteread clean, that's a one-line change in the derivation — your call.match RunStatusdownstream. Left the crate at0.6.1; bump to your preference.Tests
cargo fmt --all -- --check— cleancargo clippy --all-targets --all-features— cleancargo test --all-features— all suites greenNew (engine, observer-captured
Run.status):a_clean_run_is_completed_and_names_no_failed_nodeon_error_continue_marks_the_run_completed_with_errors(names the failing node)on_error_route_marks_the_run_completed_with_errorsRelated
Summary by CodeRabbit
New Features
Bug Fixes