Skip to content

Decoupled live-preview provisioning — Reference ​

See Resolve command, supervise process, observe port, approve and publish for the shared visual model.

Reference for the platform-owned preview step that runs after Build & Test. It starts a supervised app process, discovers the actual port, registers a Gateway preview URL, and records a durable preview outcome without changing the Build & Test verdict.

For the Gateway routes and PortForwardSessionDto, see Sandbox browser preview — Reference. For implementation details, see the deep dive. For the user workflow, see the experience guide.

Runtime contract ​

ContractShipped behaviorSource
Feature flagNone. The step runs whenever PreviewStep is wired and Build & Test is not declined.CoordinatorAssemblyService.ShouldRunDeterministicPreviewStep
Command resolutionTwo tiers: the fast/free/deterministic PreviewCommandResolver heuristics run first; only when they return Unresolved does an LLM fallback (IPreviewCommandModel, issue #541) get a bounded worktree view and propose a command. The model-chosen command runs through the identical start/observe/approval path — only the command string's origin differs. If neither tier resolves, the terminal preview_command_unresolved outcome is preserved.PreviewStep.cs; CopilotPreviewCommandModel.cs
Build & Test couplingRuns after Build & Test for APPROVED and REQUEST_CHANGES; skipped on DECLINED.CoordinatorAssemblyService.cs:753
Port choicePlatform observes the app port inside the sandbox pod using log hints plus /proc/net/tcp and /proc/net/tcp6, then registers a forwarder public port from 3000-9000; no configured fixed app port is used.PreviewStep.cs:129, :166; PreviewRunner.cs:262, :610, :315
Registration readinessIn-pod AgentHost observe verifies app + forwarder readiness; the API never probes podIP:{target_port} before creating Service/HTTPRoute.PreviewRunner.cs:315; SandboxPreviewService.cs:134
End-to-end reachabilityConfirmed by immediately using the returned Gateway hostname (preview_url), because NetworkPolicy admits preview-port ingress only from the Gateway. App Routing owns the managed DNS zone and creates each preview record; DNS name-resolution failures retry with bounded backoff until the configured convergence deadline (ten minutes by default), while an existing record succeeds immediately.k8s/base/networkpolicy-sandbox.yaml; SandboxPreviewService.cs
Infra unavailableEmits sandbox.preview_skipped_not_applicable with reason preview_infra_unavailable.PreviewStep.cs:83
Preview failureEmits sandbox.preview_failed; never blocks human review and never forces changes.PreviewStep.cs:31, CoordinatorAssemblyService.cs:772
ApprovalUses existing AgentPreviewGate; no preview-specific bypass.PreviewStep.cs:157

Sandbox pod retention while a preview is active (issue #542) ​

A live preview resolves through: Gateway → per-preview HTTPRoute → per-run ClusterIP Service → the run's sandbox pod (selected by pod label). The HTTPRoute/Service are reaped on their own annotation-driven schedule, but they are useless once the pod behind them is gone. Historically the sandbox pod's SandboxClaim was deleted unconditionally the moment the originating subtask's turn ended (KubernetesSandboxExecutor.ReleaseAgentHostPodAsync), and a completed subtask's claim also became an "orphan" to AgentHostReaperService immediately — so a preview URL handed to a human reviewer would 404 within minutes, before the review gate could open it.

Release, orphan reaping and preview activity use ReconcilePreviewLifecycleAsync(runId). Durable HTTPRoute annotations determine run-level state and idempotent retention changes.

StateDurable evidenceSandbox effects
PreviewActiveAt least one route has both idle and maximum expiry in the future.Extend backing claim TTL and set pod safe-to-evict=false; release/reaping defer.
PreviewableNo qualifying route, unavailable client/run identity, or lookup failure.Restore normal TTL and safe-to-evict=true; normal cleanup can proceed.

Cleanup reads cluster state even if creation is disabled in this process. The backing pod need not exist for a retention decision. Protection patches are best-effort, not a guarantee against infrastructure loss.

Both supported claim-name candidates are merge-patched without removing sibling fields. Active retention uses service-level LifetimeMinutes * 60 + 600; inactive state restores Sandbox:Kubernetes:TimeoutSeconds (default 600). Stop/expiry reconcile after route deletion: another live route retains the pod; removing the final one reverses protection.

AgentHost preview-runner endpoints ​

These are platform-facing AgentHost endpoints. They are root-mounted on the AgentHost origin, not under the A2A path (apps/Agentweaver.AgentHost/Program.cs:291).

Method & pathBodyReturnsNotes
POST /preview-runner/processescommand, cwd, optional runId, workPlanId, treeHashsession_id, pid, started_at, working_directoryStarts a supervised process.
POST /preview-runner/processes/{sessionId}/observe-bound-porttimeoutSeconds, healthPathsession_id, port, evidence, healthy, health_evidence, app_port, optional reasonRuns in the sandbox pod: discovers the app port from logs or /proc/net/tcp{,6}, starts the pod-local forwarder on 0.0.0.0, verifies HTTP health through the forwarder public port, and returns that public port as port. On observe failure it still returns 200 with healthy=false and a closed-set reason.
POST /preview-runner/processes/{sessionId}/health-checkport, optional pathsession_id, port, path, healthy, status_code, evidenceUsed directly and by Gateway keepalive dual-touch.
DELETE /preview-runner/processes/{sessionId}optional reason querysession_id, stopped, reasonStops the process tree.

Auth accepts either the per-run turn bearer token or the per-run preview-runner credential. If either is configured, missing or invalid auth returns 401 (apps/Agentweaver.AgentHost/Program.cs:450).

Preview events ​

EventFinal?Payload fieldsMeaning
sandbox.preview_applicabilityNorun_id, work_plan_id, tree_hash, state, reason, evidenceApplicability recorded before Build & Test.
sandbox.preview_start_requestedNorun_id, work_plan_id, tree_hash, source, command_sourcePreviewStep resolved a command and is starting the app. command_source distinguishes the tier that resolved it: a heuristic source (e.g. package.json:dev, csproj, dockerfile) or llm for the model fallback (issue #541).
sandbox.preview_pendingNorun_id, work_plan_id, tree_hash, target_port, approval, request_id, expires_at, timeout_minutes, optional retry_of_request_idPreview approval gate is waiting, including a fresh retry attempt.
sandbox.preview_readyYesrun_id, work_plan_id, tree_hash, target_port, pod_name, session_id, preview_runner_session_id, preview_url, keepalive_url, started_atGateway preview is ready. session_id is the Gateway token; preview_runner_session_id is the supervised process id.
coordinator.preview_readyMirrorSame as sandbox.preview_readyCoordinator-family mirror for the ready outcome.
sandbox.preview_failedYesrun_id, work_plan_id, tree_hash, source, reason, message; timeout additionally includes approval_request_id, retry_available, expired_at, preview_runner_session_idPreview did not produce a URL; review can continue. An approval timeout retains the process and can be retried.
sandbox.preview_skipped_not_applicableYesrun_id, work_plan_id, tree_hash, source, reason, message or evidencePreview intentionally skipped, including infra unavailable.
workflow.stepStage statestep: "preview", status, label, message, timestamp_utcDrives graph/run-tree preview status.

Failure and skip reasons ​

ReasonMeaning
preview_infra_unavailablePod-per-run or Gateway preview infrastructure cannot produce a reachable URL.
preview_command_unresolvedNeither resolution tier could determine how to run the app: the deterministic resolver found no match (it tries the worktree root first, then probes conventional subdirectories — client, app/client, frontend, web, app, src/client — in that order; server/API/backend directories are not probed) AND the LLM fallback (issue #541) either declined, was unavailable, or proposed a command that failed defensive validation (empty command, or a working directory outside the worktree).
preview_runner_unauthorizedAgentHost rejected the preview-runner credential.
process_exitedPreview process could not start.
process_exited:exit={code}Preview process started but exited before a healthy port was observed.
no_listening_port_discoveredObserve timed out without finding a healthy listening port in logs or /proc/net/tcp{,6}.
observe_errorUnexpected AgentHost observe-endpoint error; surfaced as a structured unhealthy result, not an opaque HTTP 500.
health_check_failedA port was found but did not pass the HTTP health check.
bound_unreachableThe app's loopback health check passed, but the forwarder public port did not pass the through-forwarder health check.
no_public_port_availableAgentHost could not bind any free forwarder public port in the allowed 3000-9000 range.
approval_deniedOperator denied preview exposure.
approval_timed_outOperator did not approve before the project timeout (1440 minutes / 24 hours by default). The latest expired attempt can be retried with a fresh request id while reusing the retained process.
port_not_allowedObserved port is outside the allowed Gateway preview range.
registration_failedGateway preview registration failed.
preview_outcome_missingSafety-net guard found no terminal outcome.

Consumers should display unknown reasons as text and continue.

Credential lifecycle ​

StepBehaviorSource
MintFresh random value per AgentHost launch.PreviewRunnerCredential.Mint
DeliverySent in the /configure request body; not env/file/config.KubernetesSandboxExecutor.CallAgentHostConfigureAsync
StoragePersisted in the run secret store under a deterministic key for cross-replica reconcile.PreviewRunnerCredential.SecretKey
AgentHost memoryStored in AgentHostRuntimeState.PreviewRunnerCredential.AgentHostRuntimeState.cs
CleanupDeleted on pod release and orphan reaper sweep.KubernetesSandboxExecutor.ReleaseAgentHostPodAsync, AgentHostReaperService.TryDeleteOrphanCredentialAsync

Web projection ​

The coordinator run page reads the latest preview event:

Latest eventUI state
sandbox.preview_ready or coordinator.preview_readyOpen preview on Build & Test and human review.
sandbox.preview_pendingPreview pending approval.
sandbox.preview_failedPreview unavailable with reason/message; review remains actionable.
sandbox.preview_skipped_not_applicableNo unavailable error; preview was intentionally skipped.

See also ​

Diagram details and constraints
ElementContract
titlePreview ready means validated publication
takeawayPlatform-owned orchestration still has explicit skip, denial, expiry and failure outcomes.
Build/Test verdictBuild/Test verdict
Build/Test verdictApproved or request-changes
Build/Test verdictDeclined: no preview stage
Command resolutionCommand resolution
Command resolutionHeuristic then bounded model
Command resolutionUnavailable infra: skipped
AgentHost runnerAgentHost runner
AgentHost runnerMap effective workspace
AgentHost runnerStart authenticated process
App + forwarder healthApp + forwarder health
App + forwarder healthObserve actual app port
App + forwarder healthBind reachable public port
Preview approvalPreview approval
Preview approvalGrant / deny / expire
Preview approvalPolicy auto-approval is explicit
Approved publicationApproved publication
Approved publicationActive run + process recheck
Approved publicationCreate Service and HTTPRoute
No publicationNo publication
No publicationDeny: stop; expire: private retry
No publicationNever emit ready on rejection
Generated HTTPS URLGenerated HTTPS URL
Generated HTTPS URLBounded DNS/readiness checks
Generated HTTPS URLFailure: rollback publication
preview_readypreview_ready
preview_readyExact URL validated
preview_readyReturn to authored gate handling
arrow-1prepare
arrow-2start
arrow-3observe
arrow-4request
arrow-5grant
arrow-6reject
arrow-7probe
arrow-8healthy
note-0Denial/expiry branch stays private; unresolved commands fail explicitly.
note-1Rows summarize stages; the page retains detailed failure and retry rules.
note-2Resource creation alone is not readiness; API does not probe pod preview ports.
notesDenial/expiry branch stays private; unresolved commands fail explicitly.; Rows summarize stages; the page retains detailed failure and retry rules.; Resource creation alone is not readiness; API does not probe pod preview ports.