Skip to content

Surface native deployment approval to the initiating human (CL-7362) - #558

Open
TheGreatAxios wants to merge 8 commits into
cl-7361-native-deployfrom
cl-7362-deploy-approval
Open

Surface native deployment approval to the initiating human (CL-7362)#558
TheGreatAxios wants to merge 8 commits into
cl-7361-native-deployfrom
cl-7362-deploy-approval

Conversation

@TheGreatAxios

@TheGreatAxios TheGreatAxios commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Implements docs/workflow-model.md's "Deploy approval for agent-authored workflows" section on top of CL-7361, without adding an approval table.

What's here

  • POST /:assetId/deploy/preview (packages/agent-workflow-authoring) and WorkflowAuthorRegistry.previewDeploy, delegating to an optional WorkflowDeployer.previewDeploy.
  • workflow_deploy (@corbits/workflow-authoring-tools, bumped to 0.0.3) now takes expectedWireHash and grants; its description tells the model to call the new workflow_deploy_preview (no approval) first and pass both through.
  • After deploy, the registry compares expectedWireHash against the newest workflow_definition_version.approved_wire_hash for the asset and fails 409 wire_hash_mismatch on a difference. The frozen row from the deploy that already ran is not rolled back — a caller seeing this error must treat the definition as deployed-but-unverified.
  • packages/approvals/src/headline.ts renders workflow_deploy's approval card as Deploy workflow <assetId> @ <sha7> — grants: a, b, c (or "no grants") straight from its own arguments.

Native probe-without-freeze gap

No vendored @intx/hub-sessions entry point runs install+probe under a caller-supplied approval policy without freezing. session-service.ts's buildInstallArgs hardcodes approvals: { mode: "approve-probed" } as const into every install; both callers that reach it (installAndApproveWorkflowSource, deployWorkflowFromSource, via prepareCodeSourcedApproval) throw WorkflowDefinitionInvalidError whenever the gate doesn't approve, rather than returning { ok: false, reason: "grants_not_approved", unapprovedGrants } to the caller.

Making this possible needs InstallAndApproveWorkflowSourceParams to accept a caller-supplied approvals: ProbeApprovalPolicy (threaded through buildInstallArgs) and a sibling of installAndApproveWorkflowSource that returns the gate result on grants_not_approved instead of throwing.

Per AGENTS.md ("never reimplement @intx/*"), this is not vendored here — it's documented on WorkflowDeployer.previewDeploy's doc comment in @corbits/agent-workflow-authoring. apps/hub's workflowDeployer leaves previewDeploy unwired; the registry fails those calls closed with unavailable until the native seam lands.

Agent-cannot-approve finding

Grepped apps/hub/src/tool-grants.ts and the grant-store wiring in apps/hub/src/grant-allowance.ts: no path mints approval:*/resolve (or any approval:<id>/resolve) for an agent principal — only the human-authenticated resolve route (vendor/intx/hub-api/src/routes/approvals.ts) checks that grant. Nothing changed there.

Linear: https://linear.app/abklabs/issue/CL-7362

Test plan

  • bunx tsc --noEmit -p packages/agent-workflow-authoring -p packages/workflow-authoring-tools -p packages/approvals -p apps/hub
  • bun test packages/agent-workflow-authoring packages/workflow-authoring-tools packages/approvals (93 pass)
  • bun run check:tool-package-pins

Resolves CL-7362

@TheGreatAxios
TheGreatAxios force-pushed the cl-7362-deploy-approval branch from bfd9b76 to 2b77d64 Compare September 2, 2026 07:44
@TheGreatAxios
TheGreatAxios force-pushed the cl-7362-deploy-approval branch from 2b77d64 to fc124c2 Compare September 2, 2026 08:46
@TheGreatAxios
TheGreatAxios force-pushed the cl-7362-deploy-approval branch 2 times, most recently from 39def2b to 62248e4 Compare September 2, 2026 09:27
A create or PATCH-carried retarget now must clear the same two gates
before it is persisted: resolveLaunchableDefinition must resolve the
asset, and the acting principal must be authorized for
workflow-definition:<id>/read — the same verb listRoutineTargets
already checks per row. A denial is a typed 403 alongside the
existing 400/404/409 target envelopes, on both the tenant-session and
Myra's workflow-run-authenticated routine surfaces.

Also routes a pre-existing catch in postRoutineEnabledNotice through
reportError, since this change's diff now touches that line and
check:report-error requires it.
…362)

Covers the preview route/registry method with a fake deployer (no
freeze, grants passed through), the wire-hash-mismatch 409 after a
deploy that already froze a different definition, and the
workflow_deploy approval headline rendering asset/sha/grants.
Composes the two seams docs/workflow-model.md's "Deploy approval for
agent-authored workflows" names, without adding an approval table:

- A `POST /:assetId/deploy/preview` route and matching
  `WorkflowAuthorRegistry.previewDeploy` delegate to an optional
  `WorkflowDeployer.previewDeploy`, so a workflow_deploy_preview tool
  call can show the walked grant surface before anything parks. No
  vendored `@intx/hub-sessions` entry point runs install+probe under a
  caller-supplied approval policy without freezing today —
  `session-service.ts`'s `buildInstallArgs` hardcodes
  `approvals: { mode: "approve-probed" } as const`, and both callers
  that reach it throw on a non-approval rather than returning it. That
  gap is documented in `WorkflowDeployer.previewDeploy`'s doc comment
  rather than reimplemented; `apps/hub`'s `workflowDeployer` leaves it
  unwired, so previewDeploy fails closed with `unavailable` there today.

- `workflow_deploy`'s args gain `expectedWireHash` and `grants`, and its
  description tells the model to call workflow_deploy_preview first and
  pass both through, so the human approving the parked call sees them.
  After a deploy, the registry compares `expectedWireHash` against the
  newest `workflow_definition_version.approved_wire_hash` for the asset
  and fails closed as `wire_hash_mismatch` (409) on a difference — the
  frozen row from the deploy that already ran is NOT rolled back.

- `packages/approvals/src/headline.ts` renders workflow_deploy's
  approval card directly from its own arguments ("Deploy workflow
  <assetId> @ <sha7> — grants: a, b, c") instead of falling back to the
  tool's generic description.

`@corbits/workflow-authoring-tools` bumped to 0.0.3 and re-pinned in
workflows/assistant.

Grepped apps/hub/src/tool-grants.ts and the grant-store wiring in
apps/hub/src/grant-allowance.ts: no path mints `approval:*`/`resolve`
(or any `approval:<id>`/`resolve`) for an agent principal — only a
human-authenticated resolve route (vendor/intx/hub-api/src/routes/
approvals.ts) checks that grant. Nothing changed there.
…l policy (CL-7362)

`InstallAndApproveWorkflowSourceParams` gains an optional `approvals?:
ProbeApprovalPolicy`, threaded through `buildInstallArgs` in place of the
hardcoded `approve-probed` default; `installAndApproveWorkflowSource`
returns the gate's `ProbeGateResult` instead of throwing on a non-approval
(a no-op for the default policy, which always approves). `previewDeploy`
in apps/hub calls it with an empty ApprovalSet, mapping the result to
{ wireHash, grants } without ever freezing.
@TheGreatAxios
TheGreatAxios force-pushed the cl-7362-deploy-approval branch from 62248e4 to a32b986 Compare September 2, 2026 10:54
@TheGreatAxios
TheGreatAxios marked this pull request as ready for review September 2, 2026 11:09
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