feat(workflows): typed failure taxonomy with active-blocked lifecycle - #1272
Conversation
Classify provider model failures with explicit recoverability, disposition, and failure codes so invalid credentials/unknown models become terminal attach-only failures while rate limits and missing keys remain resumable active-blocked runs. Persist active blocked failures, restore their state, and allow runtime/status resume flows to start continuations from recoverable blocked runs. Refs #1269 AI-Assisted-By: OpenAI ChatGPT
Review —
|
Use classified diagnostic, stage, and aggregate provider failures when choosing workflow run lifecycle so terminal credential failures and recoverable rate limits survive wrapper errors. AI-Assisted-By: OpenAI Codex
Review: failure codes, dispositions, and recoverable blocked runsReviewed the full diff (classification module, executor lifecycle, store, persistence/restore, background status, runtime, extension dispatch, and tests). This is a well-structured, carefully-typed change. The classification refactor from broad A few things worth a look before merge — mostly questions and polish, nothing blocking: Correctness / design questions
Security
Maintainability
Style / conventions
Overall: solid, defensive work with thoughtful classification and good tests. The items above are clarifications and polish rather than defects. |
Check related diagnostic/nested failure evidence before wrapper HTTP defaults so missing API key diagnostics are treated as recoverable blocked failures. Select terminal failed outcomes ahead of recoverable blocked outcomes when mixed non-fail-fast parallel branches fail. AI-Assisted-By: OpenAI GPT-5
Review: structured failure codes, dispositions & blocked runsReviewed the full diff (classifier, executor lifecycle, store, persistence, status/runtime/dispatch, and tests). Overall this is a well-structured, carefully-typed change with genuinely strong test coverage (23 classifier cases plus dedicated executor tests for killed vs. active-blocked, mixed non-fail-fast parallel adjudication, persistence/restore round-trips, and dispatch). The optional-property spreading and 1. Blocked runs render as plain "running" — observability gap (main concern)
Suggestion: add a blocked indicator in the status list when 2. Duplicated aggregate-extraction logic
3.
|
Treat numeric and string HTTP status-like error code values as wrapper status signals instead of semantic provider codes, allowing diagnostics and nested errors to refine generic 401 wrappers before falling back to invalid provider credentials. AI-Assisted-By: OpenAI Codex
Review: structured failure codes, dispositions, and blocked runsThorough, well-factored change. The classifier in 🔴 Lifecycle: an
|
Keep string-only rate-limit fallback classifications recoverable and active-blocked so transient provider throttling remains resumable. Clear stale active-blocked metadata when terminalizing runs, and persist user-killed blocked runs as non-resumable terminal cancellations. AI-Assisted-By: OpenAI ChatGPT
|
PR Review — feat(workflows): add active-blocked failure lifecycle Thorough, well-factored change. The failure classifier refactor (broad kind to structured decision with code/recoverability/disposition) is a clear improvement over the old kindFromStatus/kindFromCode switch, and the executor/store/persistence/restore plumbing is consistent end-to-end. Test coverage is genuinely strong — active-blocked, terminal-killed, parallel-precedence, persistence (run.blocked without run.end), and restore paths are all exercised, and the secret-redaction and false-positive guards ("failed to log input", "domain model validation failed") are nice. Nicely done overall. A few things worth a look before merge: (1) Behavior — any bare HTTP 401 is terminalized as invalid_api_key / non-recoverable (highest priority). (2) redactSensitiveText leaks an 8-char prefix of sk- keys. (3) Active-blocked runs never self-terminalize. Minor / nits:
Could not verify locally: test execution and typecheck were blocked in this environment, so this is a static review against the diff. The PR description reports bun test/typecheck/lint green — assuming CI confirms, the logic and coverage look sound. Nothing here is a hard blocker except possibly item 1, which is a behavior question worth an explicit decision. |
Preserve non-fail-fast aggregate wrapper messages for terminal parallel failures, enforce complete-failure-set aggregate classification, and sanitize terminal run-end persistence metadata. AI-Assisted-By: OpenAI GPT-5
PR Review: structured failure codes, dispositions, and active-blocked run lifecycleThanks for this — the failure-classification model is a real improvement over the coarse 1. Recoverable session-expiry 401s are classified as terminal/non-recoverable (behavior question)A bare That's a behavior change from the prior model where 2.
|
|
Review — Thorough, well-tested change. The new two-tier model (terminal-killed/attach-only vs. active-blocked/resumable) maps cleanly onto the acceptance criteria in #1269, and the lifecycle wiring is consistent across the snapshot types, store, executor, persistence, restore, background status, and both resume gates ( Feedback below, roughly by severity. 🔴 Security — raw provider error (with secrets) is persisted unredacted
The motivating example in #1269 is literally Secondary: 🟠 Resumed active-blocked source run appears to linger as
Could you confirm the intended behavior? If the source is meant to be superseded on resume, it should be transitioned (e.g. to 🟡 Aggregate/precedence semantics — a couple of edge cases worth a test
🟡 Maintainability — classifier complexity
Minor: in ✅ Things done well
Performance is a non-issue — classification only runs on failure paths. Overall: solid, mergeable after the secret-at-rest concern is addressed and the lingering active-blocked source-run behavior is confirmed (or fixed). 🤖 Generated with Claude Code |
Ensure workflow-level failures participate in disposition selection so caught recoverable stage failures cannot mask terminal outer errors. AI-Assisted-By: OpenAI Codex
Review: structured failure classification with active-blocked lifecycleNice work — this is a large but well-organized change. The classifier refactor (kind → A few things worth a closer look before merge. 1. Persisted
|
Classify clear local login 401 wrapper failures as recoverable active-blocked auth failures while preserving invalid provider credential precedence. Avoid inheriting stale caught-stage metadata when aggregate or outer failures determine the run disposition, and add regression coverage for login wrappers, aggregate errors, and secret redaction. AI-Assisted-By: OpenAI Codex
Review: structured failure classification with active-blocked lifecycleThanks for this — the design is solid and the test coverage is genuinely impressive (classifier precedence, executor lifecycle, persistence round-trips, status, and slash-dispatch resume are all exercised). The additive, all-optional metadata keeps the snapshot types backward compatible, the secret redaction is a nice touch, and the per-run 🔴 Resumed
|
|
PR Review — structured failure classification with active-blocked lifecycle Thanks for this — it's a substantial, well-tested piece of work. The taxonomy is coherent, the persistence round-trips are carefully handled, and the ~3k lines of new tests are reassuring. Notes below, ordered by impact. Potential bugs / behavioral concerns 1. Blocked source run is never retired after 2. A bare HTTP 401 is now terminal / non-recoverable. In 3. Maintainability 4. Classifier complexity. Security 5. Minor
Tests Coverage is strong (classifier precedence, retry-after extraction, lifecycle transitions, persistence round-trips). Main gaps I would add: an assertion on the source blocked-run state after a successful resume (point 1), and an explicit case for bare 401/403 with no login/code hints documenting the terminal disposition (point 2). Overall this is solid and close. Items 1 and 2 are the ones I would resolve (or consciously sign off on) before merge; the rest are polish. Automated review — I could not run |
Code Review — typed failure taxonomy with active-blocked lifecycleThorough, well-structured PR. The typed taxonomy is clean (exhaustive 1. Behavior change: a bare
|
Treat string-only provider authentication failures as terminal invalid credential errors while preserving recoverable local login semantics. Restore blocked runs only for descendants of the failed stage so unrelated in-flight stages remain running. AI-Assisted-By: OpenAI Codex
Code Review — typed failure taxonomy + active-blocked lifecycleReviewed the full diff (9 source files, ~600 LOC of logic + ~2.6k LOC of tests). This is a well-structured change: strong typing throughout, exhaustive guard helpers ( A few things worth a closer look before merge. 1. Resumed blocked run lingers as
|
Review: typed failure taxonomy with active-blocked lifecycleThorough, well-structured PR. The taxonomy is coherent, persistence/restore round-trips are handled symmetrically, and the resume-path unification (
Correctness / potential issues
Security
Maintainability
Minor / notes
Nice work overall — the in-place blocked lifecycle is a clean way to keep recoverable runs resumable without spawning a new run. 🤖 Generated with Claude Code |
|
Follow-up fix for the non-convergent Ralph loop. What changed:
Validation passed locally and in hooks:
Commit pushed: |
Review — typed failure taxonomy + active-blocked lifecycleNice, carefully-structured change. The failure taxonomy is clean, the classifier precedence (strong code → name → message → status, terminal-beats-blocked for aggregates) is principled, redaction is applied consistently, and test coverage across classifier/restore/executor/store/runtime is strong. A few things worth a second look before merge. Potential issues1. (Most important) A resumed active-blocked run is never transitioned out of For the pre-existing terminal-failed resume path this was fine — the source was already terminal (
Is the intent that resume should 2. 3. Parallel multi-stage block records only the first blocked stage. In 4. Minor
Couldn't verify locally
Overall this is solid work — item #1 is the one I'd want resolved (or explicitly confirmed as intended) before merge. 🤖 Generated with Claude Code |
|
Addressed review feedback in Changes:
Validation passed:
|
Review: typed failure taxonomy with active-blocked lifecycleThorough, well-tested change. The taxonomy (code / recoverability / disposition / retryAfterMs) is threaded consistently through classification, stage/run snapshots, persistence, restore, and resume; the guard helpers make deserialization safe; and the new redaction on both Findings ordered by significance. Nothing here is a hard blocker, but a few deserve a second look. Behavior change worth flagging (intentional, but high-impact)
Possible bug / correctness
Active-blocked lifecycle implications (design, confirm intent)
Code quality / maintainability
Tests
Overall a solid, carefully-staged change with persistence/restore parity and broad test coverage. The main thing I would confirm before merge is the bare-401-to-terminal path against the real local-login failure shape, since that is the one place a misclassification has run-killing consequences. Reviewed against CLAUDE.md conventions: raw-TS layout preserved, no build-step additions, Bun-only. Did not run bun test/typecheck locally (sandbox restrictions); relying on the PR reported green run. |
|
Addressed the latest review feedback in Changes:
|
Code Review — typed failure taxonomy with active-blocked lifecycleNicely scoped and impressively thorough work. The taxonomy is coherent, the persistence/restore round-trips are carefully wired, and the test coverage is genuinely strong (classifier precedence, aggregate handling, redaction, restore, disposition routing). Type-additive surface changes are all optional/guarded for safe deserialization. Feedback grouped by theme below — mostly observations plus a few things worth a closer look. Potential bugs / correctness
Code quality / maintainability
Performance
Security
Tests
Nits
Overall: high-quality, defensive implementation. The main things I'd want answered before merge are #1 (stalled- 🤖 Generated with Claude Code — static review; |
…#1272) * fix(workflows): distinguish recoverable model failures Classify provider model failures with explicit recoverability, disposition, and failure codes so invalid credentials/unknown models become terminal attach-only failures while rate limits and missing keys remain resumable active-blocked runs. Persist active blocked failures, restore their state, and allow runtime/status resume flows to start continuations from recoverable blocked runs. Refs #1269 AI-Assisted-By: OpenAI ChatGPT * fix(workflows): honor provider failure dispositions Use classified diagnostic, stage, and aggregate provider failures when choosing workflow run lifecycle so terminal credential failures and recoverable rate limits survive wrapper errors. AI-Assisted-By: OpenAI Codex * fix(workflows): prioritize decisive failure signals Check related diagnostic/nested failure evidence before wrapper HTTP defaults so missing API key diagnostics are treated as recoverable blocked failures. Select terminal failed outcomes ahead of recoverable blocked outcomes when mixed non-fail-fast parallel branches fail. AI-Assisted-By: OpenAI GPT-5 * fix(workflows): prefer diagnostics over wrapper status codes Treat numeric and string HTTP status-like error code values as wrapper status signals instead of semantic provider codes, allowing diagnostics and nested errors to refine generic 401 wrappers before falling back to invalid provider credentials. AI-Assisted-By: OpenAI Codex * fix(workflows): keep rate-limit blocks and clear killed metadata Keep string-only rate-limit fallback classifications recoverable and active-blocked so transient provider throttling remains resumable. Clear stale active-blocked metadata when terminalizing runs, and persist user-killed blocked runs as non-resumable terminal cancellations. AI-Assisted-By: OpenAI ChatGPT * fix(workflows): preserve terminal failure semantics (#1269) Preserve non-fail-fast aggregate wrapper messages for terminal parallel failures, enforce complete-failure-set aggregate classification, and sanitize terminal run-end persistence metadata. AI-Assisted-By: OpenAI GPT-5 * fix(workflows): include outer failures in run disposition Ensure workflow-level failures participate in disposition selection so caught recoverable stage failures cannot mask terminal outer errors. AI-Assisted-By: OpenAI Codex * fix(workflows): preserve recoverable auth failure semantics Classify clear local login 401 wrapper failures as recoverable active-blocked auth failures while preserving invalid provider credential precedence. Avoid inheriting stale caught-stage metadata when aggregate or outer failures determine the run disposition, and add regression coverage for login wrappers, aggregate errors, and secret redaction. AI-Assisted-By: OpenAI Codex * fix(workflows): harden provider failure classification * chore(workflows): trim unrelated issue 1269 changes * fix(workflows): classify provider auth failures as terminal Treat string-only provider authentication failures as terminal invalid credential errors while preserving recoverable local login semantics. Restore blocked runs only for descendants of the failed stage so unrelated in-flight stages remain running. AI-Assisted-By: OpenAI Codex * fix(workflows): classify provider auth diagnostics as terminal * fix(workflows): finalize resumed blocked source runs * fix(workflows): tighten blocked retry classification
Summary
Introduces a typed workflow failure taxonomy and an active-blocked lifecycle that keeps recoverable provider failures (rate limits, quota exhaustion, missing keys, login prompts) alive and resumable in-place, while terminating non-recoverable failures immediately. Hardens the failure classifier so bare string-only provider auth messages are correctly identified as terminal, and adds diagnostic-message and nested-error precedence to prevent weak wrapper status codes from masking decisive signals.
Closes #1269
Key Changes
Typed failure taxonomy (
store-types.ts,workflow-failures.ts)WorkflowFailureCode(10 codes:login_required,missing_api_key,invalid_api_key,forbidden_config,unknown_model,rate_limited,quota_limited,provider_unavailable,cancelled,unknown),WorkflowFailureRecoverability, andWorkflowFailureDispositionto the shared type surfacecode,recoverability,disposition, andretryAfterMsthrough classification, stage/run snapshots, and all persistence pathsisWorkflowFailureCode,isWorkflowFailureRecoverability,isWorkflowFailureDispositionguard helpers for safe deserializationFailure classifier hardening (
workflow-failures.ts)Unauthorized,authentication required,API error (401)) as terminalinvalid_api_keyfailureslogin_requiredactive-blocked failuresAggregateErrorso terminal failures beat recoverable blocked siblingsretryAfterMsfromRetry-Afterheaders,retryAfterSeconds, andretryAfterMsfields across error shapesWorkflowFailureClassificationtype withsourceandevidencefields for deterministic precedence resolutionActive-blocked lifecycle (
executor.ts,store.ts)rate_limited,quota_limited,provider_unavailable,missing_api_key,login_required) leave the runrunningwithblockedAtmetadata via the newrecordRunBlockedstore API, rather than terminatinginvalid_api_key,forbidden_config,unknown_model,cancelled) terminate the run with killed/failed metadata as beforeclearStaleBlockedRunMetadataclears stale blocked state when a run is killed; sanitises terminal run-end persistence to prevent active-blocked metadata from leaking into terminal entriesrunningin workflow details outputPersistence & session restore (
persistence-session-entries.ts,persistence-restore.ts)workflow.run.blockedjournal entries with full failure taxonomy metadatarunningand marks only descendant stages of the failed stage asblocked; unrelated in-flight stages remainrunningResume paths (
extension/index.ts,extension/runtime.ts)isResumableContinuationcheck covering both terminal-failed resumable runs and active-blocked recoverable runsfinalizeResumedActiveBlockedSourceRuncloses out the source run asterminal_killedwhen a continuation run starts, preventing dangling active-blocked entries"Resuming blocked workflow…"vs."Resuming failed workflow…"Tests
workflow-failures.test.ts, +30 cases): provider auth fallback, local login fallback, precedence, retry-after extraction, aggregate errors, redactionpersistence-restore.test.ts, +137 lines): blocked-run round trips, descendant-only blocked stage restorationexecutor.test.ts, +693 lines): active-blocked lifecycle, disposition routing,runFailureMetadatapropagationbackground-status.test.ts, +62 lines), session entries (persistence-session-entries.test.ts, +118 lines), slash-command dispatch (slash-dispatch.test.ts, +123 lines), runtime resumability (runtime.test.ts), overlay graph (overlay-graph.test.ts)AGENT=1 bun test test/unit/workflow-failures.test.ts test/unit/persistence-restore.test.ts— 66 tests passedAGENT=1 bun run typecheckpassed