Skip to main content

Approvals, impact limits and execution

The internal coordinator turns a policy decision into one committed dispatch claim. HTTP/MCP and human adapters call these domain functions; this module itself defines no routes and performs no provider network call. The protected mock connector consumes its committed claim and performs separate HTTP I/O.

The package is split by transaction path: admission.py (idempotent admission and the agent's read), approvals.py (the human decision on one pending approval), dispatch.py (the single send attempt and the indeterminate marker), outcomes.py (evidence verification and settlement), reconciliation.py (administrator settlement) and authority.py (the decision, stale-authorization and rejection helpers they share). stops.py holds the ADMIN emergency stop (activation, release and status). execbound.execution re-exports the public functions, and every writer cites the lock order below where it takes the tenant fence. store.py holds the row-level reads and transitions and snapshot.py the resolution and attestation of the frozen context.

Internal interfaces

  • admit(db, identity, request, clock) returns an immutable status and, only to the transaction that newly commits dispatch ownership, a secret DispatchTicket. It reparses strict request input and rechecks persisted AGENT identity. An operation whose frozen mapping carries external_executor is exempt from the connector check, reserves exactly as today, and then stops: no ticket is returned, the grant itself stamps dispatch_attempted_at, and the view carries a CheckpointGrant (External execution checkpoint). The same branch refuses a Gate mapping whose account is in the runtime's connector inventory and a grant the tenant's executor key ring could not attest, both INVALID_BUNDLE, and caps outstanding grants at eight per principal with TOO_MANY_GRANTS.
  • get_execution(...) returns only the authenticated agent's own status. Same-key retries retain status even after old context expires; changed canonical intent conflicts. A new key for the same unresolved intent attaches to its frozen execution. Completed intentional repetitions require a new key and new impact. A new idempotency key loads the active bundle before resolving and resolves with that bundle's context settings, so a tenant with no active bundle is refused with NO_ACTIVE_BUNDLE before context, de-duplication and connector checks. A same-key retry with changed wire bytes re-resolves under contract defaults only to compare canonical intent, so it can fail with a context error instead of IDEMPOTENCY_CONFLICT when a fact is valid only under a widened bundle setting.
  • decide_approval(...) requires current HUMAN_APPROVER credentials and an owner-provisioned canonical-target/action assignment. It checks the current agent, frozen plan, selected bundle, exact obligations and expiry before an audited APPROVE/DENY transition. Each decision is one approval_decisions row per distinct principal, so a repeat by the same human is INVALID_STATE; a DENY ends the approval, an APPROVE below the required_approvers count frozen on the approval at admission appends approval.decision_recorded and leaves it PENDING, and the approve that completes the quorum records the decided_* columns exactly as a single approval does. Consumption verifies exactly the required number of distinct APPROVE rows, no DENY, and every approver's current credential and active scope inside the fence, atomically with the claim; any lapse is STALE_AUTHORIZATION with INVALID_APPROVAL. Partial approvals expire or invalidate with the approval and are never carried to a fresh one. An optional comment (1 to 500 printable characters, refused when it carries a credential-shaped string) is recorded in the decision's audit payload and nowhere else. Browser sessions, CSRF and continuation MACs wrap this interface; a supplied private session guard is checked inside the decision transaction. Re-attestation uses the context settings of the bundle the execution was frozen under. An approval's expires_at is fixed when it is created: the earlier of the frozen plan's valid_until and the tenant's approval deadline (tenant_policy_control.approval_deadline_seconds, 120 by default, 120 to 3,600, or null for off, in which case the plan's validity is the only clock). One clock bounds both the wait for the decision and the agent's retry that consumes it. set_approval_deadline changes the setting for approvals created afterwards, inside the fence, with a reason, audited as tenant.approval_deadline_changed; approvals already pending keep the expiry they were created with. An approval nobody answers is ended by the scheduled recovery run, which selects executions still PENDING_APPROVAL whose approval is PENDING or APPROVED past expires_at, takes the same fence and calls the same _stale helper a late touch does: the approval to EXPIRED and the execution to STALE_AUTHORIZATION with the reason APPROVAL_EXPIRED. It releases nothing, because a pending approval reserves no impact and holds no canonical claim; decide_approval already refuses a decision at or past expires_at on its own, so the window between the expiry and the next run can never be decided in (the design's section 13).
  • take_dispatch(db, ticket) consumes the one send attempt and returns the frozen DispatchCommand after commit. The command contains the operation ID and intent/plan digests. A lost ticket or return does not grant another worker a replacement attempt. No lease takeover exists.
  • mark_indeterminate(...) preserves committed impact and the target claim. A crash can also leave DISPATCH_COMMITTED; both states remain unresolved and cannot be automatically retransmitted.
  • record_outcome(...) accepts evidence only through a trusted runtime EvidenceVerifier. reconcile(...) additionally requires current ADMIN identity and a bounded reason. Both require terminal proof bound to operation, account, intent and plan. Verification occurs outside the SQL transaction. The protected mock's concrete verifier checks the signed complete provider command and retains the authenticated record. Version mismatch settles as STALE_AUTHORIZATION with proven nonexecution. Each of the two paths refuses the other's executions, and the two markers are different facts: record_outcome and mark_indeterminate refuse a row carrying a report window, and report_outcome re-reads the owner-provisioned bundle the execution was frozen under and refuses any execution whose mapping does not declare an external executor. A third fact is weighed with the column on the paths that take a verifier from their caller: an external executor's evidence is refused for a row with no report window, and anything but it for a row with one, so no settlement can rest on the wrong kind of evidence for the row it names.
  • report_outcome(db, identity, execution_id, raw, verifier_for, clock) is the external executor's own route, reached from Runtime.report and POST /v1/executions/{id}/outcome. It authenticates the same agent credential, compares the report secret in constant time against the digest in owner_hash, and settles at the tier the evidence supports: a record verified against a key the tenant registered is ATTESTED and settles as a dispatch does, an authenticated report with nothing to check is ASSERTED and confirms impact without releasing the canonical claim, and an asserted non-execution or an UNKNOWN result leaves INDETERMINATE with impact charged and the claim held. A settled execution accepts only a restatement of its own tier, outcome and reason, which appends nothing; anything else is INVALID_STATE. See External execution checkpoint.
  • activate_stop(db, admin, reason) and release_stop(db, admin, reason) are the tenant emergency stop (design). Both take the tenant fence, recheck current ADMIN identity after the lock wait and append tenant.stop_activated / tenant.stop_released with a required reason; stop_status reads the state. While a stop is active, admit refuses a new key with TENANT_STOPPED before any resolution or policy work and answers a known key with its current view unchanged (tenant_stopped: true on the wire, nothing consumed), decide_approval refuses and leaves the approval PENDING, and take_dispatch refuses and leaves the execution DISPATCH_COMMITTED with no attempt recorded. Outcome recording, reconciliation, cancellation, bundle lifecycle and monitoring are never blocked: a stop only refuses. The activation payload counts the DISPATCH_COMMITTED executions the administrator inherits.
  • set_enforcement_mode(db, admin, mode, reason) switches the tenant between ENFORCE and OBSERVE (design) inside the fence with a required reason, audited as tenant.mode_changed; enforcement_mode reads it and observe_report summarizes observed decisions from the audit chain. When the tenant observes, or the frozen mapping is observe_only, admit runs authentication, the fence, the stop check, idempotency, resolution and the policy decision unchanged, records the capture with mode: OBSERVE, then moves the execution ATTEMPTED -> OBSERVED with the would-be effect, reasons and obligations in result: no ticket, approval, reservation or claim. A decision that would have executed leaves an OBSERVED impact row that only observe-mode evaluations count (the assumed-executed projection); enforced evaluations never see it. OBSERVED is terminal. A retry with the same key returns the same view, and a pending approval admitted under enforcement keeps its view and is never consumed by an observing tenant. Switching back to ENFORCE changes nothing retroactively.

Transaction and trust boundaries

All execution writers use READ COMMITTED transactions and the same tenant control row as bundle activation. The lock order is tenant control, then audit head, followed by execution/approval/impact/claim writes while that fence is held. Every writer reads the emergency stop inside that fence, so activation and any admission, decision or dispatch attempt are totally ordered. Since migration 0041_spaces admission reads the agent's own space from its principals row at the same point and refuses an archived space there: refused before any dispatch, audited, and answered 423 SPACE_ARCHIVED in the stop's own shape. A space administration act takes the same fence, so an archive and a decision on an approval of that space are totally ordered and exactly one of the two commits; decide_approval rechecks the space inside the kernel's own transaction. Nothing a caller sends names a space. Upserts handle missing control/audit rows. This deliberately serializes admissions within a tenant, including every principal and every applicable rule. It favors an explicit correctness boundary over high tenant write throughput.

After blocking locks, one non-locking SQL statement reads the mapping, active account, alias candidates, target, trusted canonical binding, inventory/incident sources and run. Alias ambiguity is counted before dependent evidence joins. Required authority is rechecked after potentially blocking writes and before commit; newer attestation cannot extend the original plan. PostgreSQL READ COMMITTED provides statement snapshots rather than a transaction-wide frozen view. PostgreSQL isolation documentation

Bundle activation is ordered against dispatch commitment by the shared tenant fence. Registry/credential changes are observed by fresh reads, but an independent writer can change them after the final read. This is the specified bounded authority snapshot, not commit-wide exclusion of every registry writer. Both implemented mock providers enforce the frozen expected version atomically at mutation; see Provider and Identity provider.

Canonical identities and approval assignments are owner-provisioned data. Every account representation must map to the same canonical_target_id when it represents the same real device. Runtime cannot create or modify those mappings. Admission rejects missing mappings and acquires one unresolved claim per tenant/canonical target across accounts and principals. Incorrect trusted provisioning is outside the agent input boundary.

Approval consumption, one impact fact, the target claim, immutable transition events, audit intent and hashed dispatch ownership commit together. Any precommit failure rolls them back. A separate safe rejection event is retained when identity and audit remain available; unavailable or invalid credentials establish no attributable principal. Every failed authentication on the HTTP, MCP, human sign-in and monitoring intake surfaces is counted in authentication_failures per surface, reason and minute, attributed to a credential or monitoring source only when the lookup verified that row exists, in a best-effort transaction of its own that never changes the response; see Operations.

Impact and terminal evidence

Each execution retains one policy-independent impact fact keyed by tenant/execution, with principal, action, approved classification and confirmation time, and since migration 0022_limit_scopes the three attributes limit scopes count over (design): account_id (the frozen intent's account), group_ids (the candidate principal's group memberships as read inside the fence for this decision, possibly empty) and facts (the frozen plan's scalar facts by name); since 0041_spaces it also retains space_id, the agent's own space read inside the fence, which a space-scoped rule counts over. The three are written once, at reservation and at observed admission, from the same snapshot that authorized that path, and are never updated. Denial and pending approval consume no impact. Approved executions contribute zero autonomous units and one total unit; every applicable hard ceiling still applies. Escalation obligations are discovered before satisfaction so changed obligations invalidate an older approval.

Confirmed impact counts in (now - window, now]. Unresolved impact never ages out. Positive terminal evidence converts the same fact to confirmed impact; negative terminal evidence releases it. Both terminal outcomes release the target claim. Duplicate evidence cannot double-charge or reopen a terminal execution. No automatic history purge or inverse-operation refund exists.

An externally executed operation charges the same counters in every scope, so a checkpoint and an agent dispatch reach one ceiling together. The claim release is the one asymmetry: _settle releases the canonical claim exactly when the evidence was verified, so an executor's unverified word can settle the execution and confirm its impact and can never free the target its grant took. That claim is released later by the scheduled recovery run once the act window has passed, and by nothing else — attested evidence against a settled row and an administrator's settlement of one are both refused. Since 0025_checkpoint a settled checkpoint carries executions.settlement_tier, written in the same transaction as the retained result and null for everything ExecBound dispatched.

store.usage counts each limit rule as a filter over the retained rows in the rule's scope, one query per rule inside the fence: rows of the tenant whose action is in the rule's action list, narrowed to the candidate principal's rows for principal, to rows whose retained group_ids contain the rule's group for group, to rows whose retained account_id is the rule's account for account, to rows whose retained space_id is the rule's space for space, and to rows whose retained facts carry exactly the selected value under the selector's fact for selector (tenant takes them all); an autonomous budget excludes approved rows, confirmed rows count inside the window, unresolved reservations count until settled, and OBSERVED rows count only for an observe-mode evaluation. Membership, account and facts are read from the row itself, never from current inventory or current group membership, so a later provisioning change never rewrites history, and a group, account or selector budget is one counter for every agent, run and adapter that reaches it under the same tenant fence. A row that lacks an attribute never counts toward the scope that needs it: rows written before the migration have account_id and facts backfilled from executions.frozen and group_ids null, so they never count toward a group scope, and a plan without facts leaves facts null, so its row never matches a selector. Memberships are read once per decision inside the fence by store.memberships, bounded at 64 (more fails admission closed with INVALID_CONFIGURATION and an audited rejection), counted with, returned on the UsageSnapshot beside the frozen account, captured on the decision and written with the reservation in the same transaction, so the counted snapshot and the retained row agree; the owner group-assign and group-unassign commands take the same tenant fence, so a membership change is ordered against every decision and the two evaluations of one verified admission read the same memberships. Membership at reservation is a snapshot: a principal removed from a group charges nothing more to it while its earlier rows keep counting, and a principal added later shares the existing count.

A missing journal record, current target state, boolean or administrator explanation is not terminal evidence. A trusted verifier must prove that the specific operation executed or cannot execute later. The protected mock supplies authenticated operation journals, atomic expected-version mutation and cancellation tombstones that fence delayed arrivals. Coordinator fixture verifiers alone establish no provider authenticity guarantee; separate-process integration tests exercise the concrete boundary.

Storage and evidence

Revision 0005_execution_state adds nine tables, all with forced tenant RLS, composite foreign keys and covering indexes. Runtime has SELECT only on canonical targets, account representations, approval scopes and, since 0022_limit_scopes, principal groups and their memberships; it can append execution keys/events and change only listed projection columns, and the three impact attributes are outside the update grant. The runtime credential remains trusted across tenants and must be inaccessible to protected agents.

The PostgreSQL regressions cover pending/key/alias/run reuse, changed-intent races, single ticket issuance, assigned approvals, 10/11 autonomous and 20/21 total boundaries, six concurrent principals at a shared ceiling, retained history after new rules, exact window edges, cross-account guards, lock-wait revocation/expiry, rollback injection and operation-correlated settlement. Migration 0006 adds human sessions and 0007 adds identity context/action constraints; the current migration round trip and privilege tests cover all 40 tenant tables. 0025_checkpoint adds the owner-provisioned executor_keys table with forced RLS and runtime SELECT only, executions.settlement_tier as the runtime's one new update grant, and executions.report_expires_at, which no runtime statement can write after the insert.

This proves coordinator portions of acceptance C/D/G/H/I/K/L/R/S/U/V. Separate protected-provider tests add mock effects, authenticated journal evidence and cancellation races; surface tests add HTTP/MCP parity and human browser behavior. Identity and Replay are also implemented. Validation and BUILD_STATUS document the complete controlled-mock evidence and the named-host/vendor trials still outstanding.