Skip to content

CL-6324: restore the onBodyFailure delta (plus its projection) and add an adopting code-sourced deploy front - #96

Closed
TheGreatAxios wants to merge 5 commits into
cl-6324-repin-interchangefrom
cl-6324-vendored-seams
Closed

CL-6324: restore the onBodyFailure delta (plus its projection) and add an adopting code-sourced deploy front#96
TheGreatAxios wants to merge 5 commits into
cl-6324-repin-interchangefrom
cl-6324-vendored-seams

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

Two vendored seams the deploy conversion depends on, both landing on the re-pin branch.

Job A — the onBodyFailure delta, re-applied and extended through projection

PR #87 reported this delta as carried through the re-vendor; it was not. Verified on cl-6324-repin-interchange before this branch: zero onBodyFailure hits anywhere under vendor/intx/workflow, and runtime/run.test.ts absent entirely. main carries the merged version (29a89ae2, PR #68).

Re-applied from main's two commits. The upstream reshape at 486a6b1b did not move the two policy-gated call sites — the steady-state drive loop's terminalStatus !== "completed" block and planOnTriggerResume's terminal check are byte-identical to the pre-delta version on main — so both commits cherry-picked clean with no re-diffing needed. All 4 crash-recovery/policy tests pass on the re-pinned runtime.

Extended with the projection fix the conversion analysis called for: live-inert-projector.ts's InertOnTrigger and projectOnTrigger now carry onBodyFailure, and BodyFailurePolicy is re-exported from the definition barrel for that type reference. Without this the field was silently dropped at the child→hub boundary — the projection is what computeWireDefinitionHash hashes and what the deploy gate freezes, so an authored "continue" policy never reached the deployed section. Three new projector tests assert a projected section carries "continue" and "end", and omits the key entirely when unauthored (the omission matters: an extra key would change the content hash for every existing definition).

packages/agent-runtime's section mode authoring onBodyFailure: "continue" is the stacked conversion branch's own follow-up; this branch only makes the vendored surface right.

Upstream report — packages/workflow

onTrigger treats a failed body run and a cancelled one identically: both are terminal-is-final, so a single bad turn permanently ends a long-lived, event-driven section. That conflates two different things — a cancellation is a drain/operator decision, a failure is a turn-level error — and for an interactive section the failure case is the wrong default. onBodyFailure?: "end" | "continue" (default "end", byte-compatible with today) lets a section re-arm past a failed occurrence; the occurrence stays durably recorded on the run's own audit log via the existing ChildCompleted commit either way, so the policy makes it non-fatal, never silent.

Separately, the live→inert projector drops any onTrigger field it does not explicitly whitelist. projectOnTrigger copies drainBehavior but nothing else beyond on/body/after, so any policy field added to OnTriggerPrimitive is silently lost before the projection is hashed and frozen — the failure is invisible at authoring time and only shows up as the deployed section ignoring an authored policy. Worth considering whether the projector should fail loud on an unrecognized primitive field the way it already does on an unrecognized primitive kind.

Job B — the adoption deploy seam

PR #90's root blocker: neither code-sourced front can deploy onto a run whose anchor row already exists.

  • deployWorkflowFromSource INSERTs the anchor workflow_run — a primary-key collision against a folded run's existing row — and accepts no credentialCipher, so a definition with credential bindings fails closed.
  • deployPreparedCodeSourcedWorkflow does both correctly, but hard-requires an allocationTarget and takes the allocation-ownership lock, so it cannot run on shared capacity.

Adds deployAdoptedCodeSourcedWorkflow plus the deployAdoptedWorkflowFromSource service method (AdoptingWorkflowDeployer), composed from the same private halves — emitSourceRefDeployFrame (now exported) and buildInertProjectionStepSources. It follows the prepared front's semantics minus the allocation lock: ownership is the anchor row's own tenant plus self-anchoring.

Ownership is checked twice, deliberately. The read runs before the frame, so a refused adoption never leaves a deployed-but-unanchored sidecar agent behind; the guarded UPDATE ... RETURNING afterwards re-asserts the same predicate at write time, so a row that vanished or changed hands mid-deploy fails closed rather than stamping nothing silently. No deployer read grant is seeded — the anchor predates the call, so its grants belong to whoever created it.

Three tests: adoption succeeds and issues zero INSERTs; a binding-bearing definition fails closed with no cipher (the cipher is the only path for credential material to reach the launch frame); an anchor the tenant does not own is refused before any frame reaches the sidecar.

Upstream report — packages/hub-sessions

The two code-sourced fronts split along the wrong axis. Anchor-row lifecycle (insert vs. adopt) and capacity (shared vs. dedicated allocation) are independent concerns, but upstream binds them together: adopting a pre-existing anchor is only reachable through the prepared front, which also demands an allocationTarget. Any caller that mints its own run row up front — a chat-launched or otherwise pre-existing run — has no front it can use on shared capacity. The credentialCipher thread follows the same split for no reason: emitSourceRefDeployFrame already accepts it, but only the prepared front passes it down, so the shared front cannot deploy a definition with credential bindings at all. Making the cipher a parameter of every code-sourced front, and the anchor-row treatment orthogonal to the transport, would collapse three fronts back to one.

Verification

  • bun test vendor/intx/workflow/src vendor/intx/hub-sessions/src — 17 pass, 0 fail
  • bun run lint — 0 errors
  • bun run check:killdates — ok (both tree hashes re-recorded)
  • bun run typecheck — 50 errors, identical count to this branch's baseline; all are the pre-existing deploySingleStepAtHead gap in packages/folded-runs/src/launch.ts that the conversion exists to close. These changes add none.

Known gap, not fixed here

scripts/run-all.ts globs {apps,packages,tools,workflows}/*/package.json, so no test under vendor/intx/** ever runs in bun run check or CI — including main's existing run.test.ts. The tests in this PR were run directly and are green, but they are not currently gating. Worth a follow-up to either add a test script to the vendored manifests or widen the glob; flagged rather than changed, since it affects every vendored package at once.

Red/green coverage for the onBodyFailure policy on the re-pinned runtime
(CL-6326, CL-6324): default policy unchanged, "continue" re-arms past a
failed occurrence while a cancelled one stays terminal-is-final, and
crash-recovery honors the same policy. Adds projector coverage asserting a
projected onTrigger section carries the authored policy through the
live->inert projection, and omits the field when no policy was authored.
Fails against the unmodified vendored runtime and projector.
…ection

Re-applies the CL-6326 vendored delta on top of the re-vendored runtime:
onBodyFailure?: "end" | "continue" on OnTriggerPrimitive/OnTriggerOpts
(default "end", byte-compatible with prior behavior), read live by the
steady-state drive loop and planOnTriggerResume so a "continue" section
re-arms past a failed occurrence instead of ending the run. Cancellation
is unaffected and always ends the section.

Adds what the delta previously lacked: the live->inert projector's
InertOnTrigger and projectOnTrigger now carry the field, so a section's
policy survives the child->hub projection instead of being silently
dropped before deploy. BodyFailurePolicy is exported from the definition
barrel for the projector's type reference.
Red/green coverage for a shared-capacity code-sourced deploy that stamps a
pre-existing anchor workflow_run instead of inserting one (CL-6324): the
adoption succeeds and issues no INSERT, a definition carrying credential
bindings fails closed when no cipher is threaded, and an anchor the tenant
does not own is refused before any frame reaches the sidecar. Fails against
the two upstream fronts, neither of which accepts a pre-existing anchor.
…hor run

Neither code-sourced front could deploy onto a run whose anchor row already
exists. deployWorkflowFromSource INSERTs its anchor (a primary-key collision
against a folded run's row) and threads no credentialCipher;
deployPreparedCodeSourcedWorkflow updates a pre-existing row and threads the
cipher, but only under the allocation-ownership lock, so it cannot run on
shared capacity.

Adds a third front composed from the existing halves --
emitSourceRefDeployFrame and buildInertProjectionStepSources -- following the
prepared front's semantics minus the allocation lock: ownership is the anchor
row's own tenant plus self-anchoring, checked before the frame so a refused
adoption leaves no deployed-but-unanchored agent, and re-asserted on the
guarded UPDATE that stamps definitionId and publicKey. No deployer read grant
is seeded: the anchor predates the call, so its grants belong to whoever
created it.
…loy front

Records both vendored deltas in VENDORED.md and each package's VENDORED-FROM,
and re-records the workflow and hub-sessions tree hashes so check:killdates
matches the edited trees.
@TheGreatAxios

Copy link
Copy Markdown
Contributor Author

Superseded — the onBodyFailure delta and its projection are on main (packages/chat/src/standalone-launch.ts, covered in platform-adapter.test.ts).

@TheGreatAxios
TheGreatAxios deleted the cl-6324-vendored-seams branch August 25, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant