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
Closed
Conversation
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.
Contributor
Author
|
Superseded — the |
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.
Two vendored seams the deploy conversion depends on, both landing on the re-pin branch.
Job A — the
onBodyFailuredelta, re-applied and extended through projectionPR #87 reported this delta as carried through the re-vendor; it was not. Verified on
cl-6324-repin-interchangebefore this branch: zeroonBodyFailurehits anywhere undervendor/intx/workflow, andruntime/run.test.tsabsent entirely.maincarries the merged version (29a89ae2, PR #68).Re-applied from
main's two commits. The upstream reshape at486a6b1bdid not move the two policy-gated call sites — the steady-state drive loop'sterminalStatus !== "completed"block andplanOnTriggerResume's terminal check are byte-identical to the pre-delta version onmain— 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'sInertOnTriggerandprojectOnTriggernow carryonBodyFailure, andBodyFailurePolicyis 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 whatcomputeWireDefinitionHashhashes 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 authoringonBodyFailure: "continue"is the stacked conversion branch's own follow-up; this branch only makes the vendored surface right.Upstream report —
packages/workflowonTriggertreats afailedbody run and acancelledone 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 existingChildCompletedcommit either way, so the policy makes it non-fatal, never silent.Separately, the live→inert projector drops any
onTriggerfield it does not explicitly whitelist.projectOnTriggercopiesdrainBehaviorbut nothing else beyondon/body/after, so any policy field added toOnTriggerPrimitiveis 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.
deployWorkflowFromSourceINSERTs the anchorworkflow_run— a primary-key collision against a folded run's existing row — and accepts nocredentialCipher, so a definition with credential bindings fails closed.deployPreparedCodeSourcedWorkflowdoes both correctly, but hard-requires anallocationTargetand takes the allocation-ownership lock, so it cannot run on shared capacity.Adds
deployAdoptedCodeSourcedWorkflowplus thedeployAdoptedWorkflowFromSourceservice method (AdoptingWorkflowDeployer), composed from the same private halves —emitSourceRefDeployFrame(now exported) andbuildInertProjectionStepSources. 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 ... RETURNINGafterwards 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-sessionsThe 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. ThecredentialCipherthread follows the same split for no reason:emitSourceRefDeployFramealready 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 failbun run lint— 0 errorsbun 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-existingdeploySingleStepAtHeadgap inpackages/folded-runs/src/launch.tsthat the conversion exists to close. These changes add none.Known gap, not fixed here
scripts/run-all.tsglobs{apps,packages,tools,workflows}/*/package.json, so no test undervendor/intx/**ever runs inbun run checkor CI — includingmain's existingrun.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 atestscript to the vendored manifests or widen the glob; flagged rather than changed, since it affects every vendored package at once.