feat(run): bounded per-worker escalation in ak run (ADR-0019) - #90
Merged
Conversation
The pacphi#76 closure punch list requires ordered, bounded cross-host escalation in the canonical ak run path, tested with an OpenCode-qualified route — the legacy whole-pipeline retry in deprecated ak dual does not satisfy it. Design (ADR-0019): - The resolved route's escalate array travels with each worker at materializeRunPlan; self-equal rungs are dropped (legacy L4 rule) and unroutable rungs fail materialization exactly like an unroutable primary. - On --escalate, a cleanly-failed worker (failed/timed_out) advances ONE rung per attempt in ladder order, bounded by the ladder's length, with per-attempt timeout. Never advanced: blocked/cancelled (dependency state), permission_required (a consent boundary — escalating around it would violate the supervised-host contract), orphaned (uncertain state). - The final result carries attempts[] (host/model/status/category/ duration/reason per attempt) ONLY when more than one attempt ran — no fabricated trail; the final host/model are the rung that executed; attempts is schema-validated like every other result field. A succeeded-after-escalation unblocks dependents normally. A rung with no adapter records cli_unavailable and continues to the next. - ak dual keeps its legacy semantics untouched until pacphi#83 removes the wrapper; the semantic difference is named in UPGRADING.md. Tests: 13 new (engine ordering/boundaries/trail/schema, plan materialization + rejection, CLI flag flow + --dry-run ladder display). pnpm run check exit 0 (1080 kit + cjs), pnpm run test:surface exit 0 (25). Live proof (OpenCode-qualified route, sandboxed repo + real CLIs): implementation:opencode with a bogus provider/model → the opencode attempt fails with the serve's 400 (worker_error), advances to the claude rung, which succeeds and writes the mutation — trail recorded verbatim: coder: opencode:failed(worker_error) -> claude:succeeded(success) Dependents unblocked; permission/consent surfaces untouched.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The #76 closure punch list requires ordered, bounded cross-host escalation in the canonical
ak runpath — tested with an OpenCode-qualified route — and explicitly says keeping escalation only in deprecatedak dualdoes not satisfy the issue. This lands it, with the migration decision recorded as ADR-0019.Design (ADR-0019) — bounded per-worker ladder attempts, opt-in via
--escalate:escalatearray travels with each worker atmaterializeRunPlan; self-equal rungs are dropped (legacy L4 rule) and unroutable rungs fail materialization exactly like an unroutable primary route.failed/timed_out) advances one rung per attempt in ladder order, bounded by the ladder's length;--timeoutapplies per attempt.blocked/cancelled(dependency state, not a worker failure),permission_required(a consent boundary — escalating around it would violate the supervised-host contract the opencode abort implements),orphaned(uncertain state → double-run risk).attempts[](host/model/status/category/duration/reason) only when more than one attempt ran — a single attempt is indistinguishable from escalation off, and a fabricated trail would be a lie. Final host/model = the rung that executed (observed truth);attemptsis schema-validated like every other result field; success-after-escalation unblocks dependents; a rung with no adapter recordscli_unavailableand continues.ak dualkeeps its legacy whole-pipeline semantics untouched until GA blocker: remove all deprecated commands, compatibility code, and legacy documentation #83 removes the wrapper; the deliberate semantic difference is named in UPGRADING.md.Verification
--dry-runladder display.pnpm run checkexit 0 (1080 kit + cjs),pnpm run test:surfaceexit 0 (25).implementation:opencodewith a bogus provider/model):hello.mjs, typed). Dependents unblocked; permission/consent surfaces untouched.Refs #76 (punch-list item 1), #83 (the wrapper removal that consumes this migration).