Skip to content

Task-version execution

Owner-approved scope, 2026-09-29. Tracking task: fec3a091-78e7-47db-af86-130428900c94; parent FLOW-09: 80708faa-d90a-4d07-af70-8d86f62a8bc9.

This is a proposed implementation for review, not the deployed contract. The old FLOW-08/09 experiments must not be retried. There will be one PR per repository, followed by independent Claude review. Production activation and the real optional-profile Bridge canary need owner deployment approval.

The accepted UI prototype is the product reference. The Design system session owns UI rendering; the server owns durable facts and the Bridge submits attributed execution evidence.

Change sequence and files

  1. Additive migration 029_task_execution_context.sql. Reuse project_repositories; backfill the primary repository without reviving revoked access. Add task repository/agent/provider/model, verification, time limit (8 hours default, 24 hours maximum), named acceptance criteria and immutable task-version snapshots. Add hashed lease capabilities, event evidence, explicit verification state and publication metadata.
  2. Server contract, task_execution.py, workflow.py, catalog_workflow.py, execution.py, server.py, oauth.py. Configure/select an authorized repository and catalog agent, send task ID and version, resolve pinned Git instructions, reject offline Macs before creating an offer. Worker and verifier read the same task version. Claim sets in_progress. Renew every 20 seconds with a 90-second lease, bounded by one task deadline shared by worker and verifier, not a fresh budget per attempt. Capability access is checked against the live lease, fence, project membership and Bridge binding.
  3. Bridge, workflow.rs, execution.rs, session/provider adapters and keep_awake.rs. Resolve repository to an owner-authorized local directory before claim/reservation. Deliver a lease-scoped MCP credential only in the provider environment, never in a persisted profile, prompt or log. Implement enforced workspace/network policy, restart reconciliation, native keep-awake with release on all terminal paths, and Bridge-only Git publishing. Do not enable the new path while any of these gates is missing.
  4. Contracts, docs and tests. Update the generated MCP schema and HTTP reference together. Test real PostgreSQL/MCP/HTTP, provider sandbox denial probes, restart/cancel/expiry, event replay and publication. Open one PR per repository with explicit acceptance evidence and remaining release gates.

Event contract for the UI

The history is append-only and sequence-ordered within a workflow revision. Each event carries server time, actor attribution, execution identity and evidence. A current phase is only a projection; it cannot replace history.

Stage Evidence
picked_up Bridge identity, selected catalog agent/provider/model, execution ID
session_started Above plus the provider-native session ID
working Bound worker session, timestamp
verifying Fresh verifier identity/session, candidate commit, same task version
done / changes_requested Commit, criterion IDs and results, passed/total count, source of assessment
pr_opened Actual repository URL, PR number/URL, exact candidate commit, publishing Bridge

session_started and working are separate persisted events, even when the same acknowledged Bridge message establishes both. A retried message must not append either event twice. PR publication can happen later; the UI must show publication pending until the Bridge reports it. Do not invent a PR number at task completion. Publication evidence is a Bridge report, not independent proof that GitHub was contacted; final tests must check the actual PR.

Criteria counts are based on explicit named criteria, not one synthetic "everything completed" criterion. A worker's count has source worker_report; an independent verifier's count has source independent_verifier.

Verification is not a boolean

Task setting / situation State Verdict
verification=none not_requested absent; UI says “not requested for this task”
Independent verification requested, not finished pending absent
Verifier finished completed bound criterion results, including failure
Verification requested but terminated before a verdict not_run outcome=not_run, never PASS

completed does not imply PASS. Failure produces changes requested/blocked, not acceptance. A task without independent verification must never receive a fabricated verifier or independent PASS. A new authorized correction attempt returns the current verification projection to pending while retaining all earlier verdict events.

Authorization and recovery acceptance gates

Dispatch requires the enrolled node's fresh heartbeat (90 seconds) and the task-version-execution-v1 capability. The join uses the enrollment's device_id, not its display bridge_id. Older Bridge binaries receive bridge_upgrade_required before any new-model offer is created. Missing server instance identity returns instance_identity_unconfigured. The node key must match the enrolled key fingerprint (SHA-256 of the decoded public key). A fresh owner-issued pairing code may pair or re-pair a pending/active enrolled device with that same key; a different key is rejected without consuming the code or replacing credentials. This preserves the pairing-code authorization model, not a new proof-of-private-key-possession protocol. Capabilities remain a client declaration, not remote attestation of the installed binary.

The shared dispatch boundary derives the prompt and named criteria from the selected task version, including catalog dispatch; caller text cannot replace them. Worker tokens may append RESULT/receipt; verifier tokens are read-only. RESULT idempotency is namespaced by execution and requires a non-empty 1–128-character key (A-Z, a-z, digits, _, -, .).

An expired claim cancels the offered execution and blocks the workflow. Renewals cannot exceed the shared deadline (RFC3339 on the wire). Terminal workflows where verification did not run use not_run, never completed or an indefinitely pending assessment. Every stored verdict has one envelope: {outcome: pass|fail|not_run, source: string, results?: CriterionResult[]}. results is present for actual verifier results; the bound execution and candidate remain in the immutable verdict event. Migration 030 normalizes earlier candidates and supports terminal transitions from rolled-back binaries. Expired unclaimed task-reference offers are reaped as cancelled without requiring a claim, matching the expired-claim path. Legacy offers are not expired by their 900-second execution budget before claim; their running lease/deadline still expires as interrupted. Claim also rejects an expired initial offer before the reaper runs. Execution and lease responses include server-relative remaining_seconds; Bridge uses monotonic local timers rather than comparing server timestamps to the Mac clock. New task-reference dispatch permits exactly one work attempt on both APIs; another attempt requires an explicit owner dispatch. Per-transition task snapshots remain intentionally retained (compaction is deferred). Publication must reference the worker execution. Events retain the full criterion outcomes and verdict when a correction resets the current projection.

Migration 029 supports old inserts without stage through a BEFORE INSERT trigger. Repository backfill is case-insensitive and does not revive revoked bindings. Task-version rows reject standalone UPDATE/DELETE while legitimate parent deletion may cascade; removing an execution preserves its RESULT memory with a null execution reference. Full task snapshots still include descriptions on version changes; compression/deduplication is deferred, not implemented.

  • Agent capability: only assigned immutable task reads, project-memory reads and append-only RESULT/receipt for the assigned task. It cannot renew its own lease, dispatch, grant approval, edit criteria or mark the task done.
  • Every write validates the owner-pinned resource, instance and project; server discovery alone must not establish intended identity.
  • Agent environment contains no GitHub publishing credential. Workspace and network restrictions must be technically enforced, not prompt instructions.
  • Provider configuration must not inherit unrelated MCP servers. A local codex -c mcp_servers={...} probe showed table merging rather than replacement; configuring one server alone therefore does not isolate a provider session.
  • Restart must reconcile the durable execution and provider session. Renew a live lease without creating another attempt; expired work is interrupted. Never automatically replay ambiguous commands or create a duplicate process.
  • Keep-awake prevents idle system sleep only; it does not defeat lid close, explicit sleep or battery policy. Release the assertion on exit/cancellation.

Local coverage and remaining work

Local tests exercise actual MCP/HTTP/PostgreSQL with synthetic Git bytes and provider events. They cover immutable reads, scoped-result idempotency, lease expiry, offline refusal, second-repository selection, both verification modes, ordered evidence and publication replay/rejection. These are not live Codex/Claude execution or production tests.

Bridge now has per-session MCP/token configuration, native Codex and Claude sandbox probes, keep-awake and separate-supervisor lifetime, plus owner-authorized GitHub publication and a publication-only retry command. Native probes verify commit permission and outside-workspace/config/network denial; they do not prove the full catalog-to-worker-to-verifier flow.

The paired Bridge correction uses the Codex CLI kernel sandbox executor for both providers (Claude Bash goes through a protected Bridge shell wrapper, not nested Seatbelt sandboxes). Claude authenticates task MCP calls through a private headers helper, not an inherited Bash token. A disposable no-hardlinks verification clone protects the worker candidate. See the paired Bridge docs/task-version-execution.md for local requirements and acceptance commands; the server capability gate prevents sending this contract to older Bridges.

App/daemon restart preserves a live supervisor and renewal. Loss of the supervisor itself pauses ambiguous work rather than replaying a provider command. This is not full machine-crash/provider-turn recovery. Independent review must assess this boundary against the owner's restart requirement before activation.

Remaining release gates are UI consumption, full cross-provider E2E and independent review/owner approval. No production service, installed Bridge, background policy, OpenBao or Server Beat was changed by this work.

Sandbox implementation references: Codex permissions, Claude Code sandboxing.