Skip to main content

The acceptance inventory

This is the required behavior inventory. Executed controlled-mock results and limits are recorded in build status; the validation manifest maps all 28 acceptance groups — A to X, SA, SL, GC and AL — to exact passing test nodes. All mutation assertions inspect the provider operation journal and invocation evidence, not only the gateway's HTTP response. Concurrency tests use separate database transactions/workers against real PostgreSQL. Use a controllable clock and deterministic barriers for races.

Original scenarios

IDSetup/actionRequired result
APermitted workstation and correctly linked authoritative critical incidentALLOW; one provider mutation, one impact unit and durable evidence.
BTier-0/domain-controller isolation requestDENY; zero mutations.
CProduction targetNo dispatch before scoped human approval; one dispatch after valid retry.
DTen principal autonomous isolations, then target elevenEleven requires approval; simultaneous requests cannot exceed autonomous allowance.
ECaller claims low-tier workstation; resolver identifies Tier-0DENY; claims never replace trusted facts.
FUnknown operation, raw passthrough or unsupported consequential optionDENY; no protected mutation.
GChange any approved execution field, account, target or security versionOld approval invalid; reevaluate or reject, zero unauthorized mutation.
HRetry a confirmed executionPrior result; no extra mutation or impact.
IProvider mutates then times outINDETERMINATE with capacity held; no automatic conflicting retransmission.
JReplay a policy changeChanged simulated decision; zero provider calls or writes to live control state.
KAGENT calls approve/deny/config/reconcile routesEvery path rejected and audited.
LSeveral principals collectively reach tenant ceilingFinal over-ceiling request denied regardless of principal allowance or human approval.
MInterleave HTTP and MCP requestsSame idempotency claims, resource guards, tenant impact and policy results.
NErroring Cedar forbid with a broad permit; repeat with an approval-set errorDENY; also deny missing required trusted attributes and configured-set load failures.
OChange version between last gateway read and provider writeAtomic provider precondition rejects mutation; stale authorization recorded.
PReconcile timeout using exact provider operation evidenceCorrect terminal/uncertain state and exact impact preservation.

Review regressions and specification closure

ID / patchSetup/actionRequired result
Q / P1Submit permitted primary resource plus Tier-0 target; reverse order; embed target in argumentsAll contradictory/multiple-target forms rejected before dispatch.
R / P3Permute broad permit, production approval, exceeded soft threshold and exceeded hard ceilingDENY always wins; valid approval cannot waive hard ceiling.
S / P1/P6Two tenants with colliding local IDs, aliases, accounts and idempotency keysNo foreign object disclosure, approval consumption, replay selection or provider mutation.
T / P2Unrelated critical incident, agent-editable severity, stale incident, claimed derivation inputNo independent authority established; deny or invalidate old approval as appropriate.
U / P4Change provider resource/bundle/context at dispatch barriersConditional provider failure or precommit reauthorization; no old-plan dispatch after a prior bundle change.
V / P4Simultaneous same-key requests, changed payload, new key/run/alias, crash at each dispatch boundaryOne execution claim; conflicts rejected; possible dispatch stays uncertain without automatic resend.
W / P5Timeout, out-of-band inverse mutation, reconcile; delayed arrival after negative lookup; another actor creates positive stateReal impact never refunded; absent/current-state observations remain uncertain; cancellation tombstone fences late operation.
X / P6Agent markup in approval fields; cross-site forms; expired/wrong-scope human sessionStrings inert; forged/browser-mediated approvals fail; direct agent restrictions remain enforced.

Additional required edges:

  • First-use budget-row races and multiple simultaneous scopes; no negative/double-released reservations.
  • Fresh runs, reauthentication and bundle/rule-version changes preserve principal/tenant history.
  • Outstanding reservations remain counted beyond rolling-window boundaries until resolved.
  • Same-intent pending requests with new keys reuse one approval; completed intentional new-key operations reevaluate and count again.
  • Context reattestation with unchanged versions does not mutate a frozen plan; changed/expired dependencies invalidate approval.
  • MRTR MAC tampering, unknown/retired keys, wrong tenant/principal/audience/hash, expiry and concurrent approval consumption.
  • Modern and one supported older client use identical policy/approval behavior; tool-list cache isolation never substitutes for tools/call authorization.
  • Both providers reject agent credentials; fixed connector destinations reject redirect/URL/header injection; secrets absent from responses/logs/errors.
  • Audit edit/deletion/reorder/concurrent append/truncation checks against a retained trusted head.
  • Replay with unknown approval, hypothetical new execution, uncaptured context and uncertain dependent counters reports UNRESOLVED.
  • Every Entra operation, including enable and revoke_sessions, has explicit scoped authorization and operation-specific evidence.

V1 is complete only when A–X and applicable edge cases pass, startup is reproducible, browser approval is verified, tested dependency/client versions are recorded, and the validation package states its mock/coverage limits.

Context contracts

IDSetup/actionRequired result
CC1Construct catalogs that violate each construction rule in section 3Construction fails for each
CC2Both v1 contracts through resolution and evaluationFrozen plans, hashes and decisions match the pre-change golden fixture byte for byte
CC3Synthetic contract serving endpoint.isolate with row-backed String, Long and Boolean facts, an equals derivation and incident mode noneALLOW performs one provider mutation; a forbidden value denies with zero mutations
CC4Mapping names a contract missing from the catalog, or one that does not serve its actionINVALID_OPERATION; zero mutations
CC5Bundle settings with shorter and longer max ages; above-ceiling, empty-writer and unused-contract settingsShorter denies a fact the default accepts; longer accepts an older fact; invalid settings fail validation
CC6Source writers outside the effective allowlist, including an agent-writable incident sourceDENY; scenario T unchanged
CC7Bundles whose Cedar schema omits, adds, mistypes or makes optional a contract attributeValidation fails; the existing packs still activate
CC8Two tenants with colliding account and resource IDs in resource_factsNo cross-tenant read; runtime cannot insert, update or delete; RLS forced
CC9Migration upgrade and downgrade round trip; downgrade with rows presentRound trip passes; downgrade refuses
CC10Replay with candidate bundles that shorten and lengthen max ageShorter can deny history; longer cannot extend frozen validity; existing replay scenarios unchanged
CC11Pending approval, then a resource_facts row version changesRe-attestation with the frozen bundle's settings returns STALE_AUTHORIZATION
CC12Approval page for endpoint, identity and synthetic contractsEach shows its contract's display fact as inert text

Tests live in tests/test_contracts.py, test_context_parity.py, test_resource_facts_schema.py, test_context_resolution.py, test_context_evaluation.py, test_context_bundle_validation.py, test_context_execution.py and test_replay_scenarios.py.

SOC action catalog

IDSetup/actionRequired result
SA1Construct action catalogs that violate each construction rule in section 3 of the designConstruction fails for each
SA2All four existing actions through parse, resolution, evaluation, command and journalBoth endpoint fixtures, the context parity fixture and the new identity golden match byte for byte
SA3Arguments that are nested, listed, null, float, oversized, unknown, missing, extra, mistyped or out of domain, on HTTP and MCPRejected before any lookup or mutation; scenario F unchanged
SA4Contained endpoint, reason false_positive, incident CLOSEDALLOW; one lift_containment mutation; journal shows contained true to false and binds the arguments
SA5Same target, reason remediated on a non-production endpointREQUIRE_APPROVAL; the approval page shows reason: remediated as inert text; valid retry after approval dispatches once
SA6Standard identity, OPEN HIGH incident, require_change_at_next_sign_in trueALLOW; credential transition increments the generation and sets must_change_password; enabled state and session generation unchanged; no password in journal, response or logs
SA7require_change_at_next_sign_in false, then a privileged identityREQUIRE_APPROVAL, then DENY with zero mutations
SA8Same key with a changed argument; new key with the same arguments while pendingIDEMPOTENCY_CONFLICT; attaches to the pending execution
SA9Synthetic action with a bounded Long argument in and out of range; synthetic second binding for endpoint.isolate through a tenant mapping; a mapping naming an unknown bindingIn range resolves and reaches Cedar; out of range is INVALID_ARGUMENTS; the second binding resolves; the unknown binding is INVALID_OPERATION
SA10Bundles whose action context omits, adds, mistypes or makes optional an argument, or omit a mapped actionValidation fails; the existing packs and the SOC pack still validate and activate
SA11Provider command with arguments altered after freezing; journal with an inconsistent transitionProvider rejects with 400 and no journal; evidence fails verification and nothing settles
SA12Isolate then lift the same target; lift while the isolate is unresolvedTwo impact units, no refund; while the claim is held the second request is recorded as a DENIED transition with reason RESOURCE_BUSY
SA13Migration upgrade and downgrade round trip; downgrade with a new-action row in each guarded tableRound trip passes; downgrade refuses; runtime privileges unchanged
SA14seed-validation outputSOC bundle installed as VALIDATED and written to the output; the demo bundle, its bytes and the browser demonstration unchanged
SA15Replay with a candidate that forbids reason == "remediated"The recorded lift becomes a candidate DENY; the report row shows the argument; existing replay scenarios unchanged

Tests live in tests/test_action_catalog.py, test_domains.py, test_identity_compatibility.py, test_requests.py, test_resolution.py, test_policy_evaluation.py, test_context_bundle_validation.py, test_soc_pack.py, test_soc_provider.py, test_soc_execution.py, test_soc_schema.py, test_replay_scenarios.py, test_replay_verify.py, test_human_surface.py and test_validation_demo.py, with shared fixtures in tests/soc_fixtures.py.

Approval quorum

IDSetup/actionRequired result
QA1Bundle requires two approvers on the production obligation; first approver approves; agent retriesApproval PENDING with one decision; retry 202 pending; no dispatch
QA2Second distinct approver approves; agent retriesAPPROVED, then CONSUMED; exactly one provider mutation
QA3The first approver decides again with the same or another credentialINVALID_STATE; one decision row; nothing counted twice
QA4One approval, then a denial by another approverDENIED; execution DENIED (HUMAN_DENIED); retry denied; zero mutations
QA5One approval, then the approval window endsEXPIRED; a later approval by the second approver is refused; nothing consumed
QA6Two approvals; before the retry, one approver's credential is revoked or scope removedSTALE_AUTHORIZATION with INVALID_APPROVAL; zero mutations
QA7Bundle re-activated between the two approvalsSecond decision refused as stale; approval INVALIDATED
QA8Two approvers decide concurrently on two connectionsOne completes the quorum; two rows; consumed once; one execution
QA9Quorum rules naming an unknown obligation, required of 1 or 6, or duplicate rulesBundle validation fails with field locations; existing packs still validate and activate
QA10Human pages with real ChromiumProgress text; no form for the approver who already decided; a replayed decide POST by that approver refused; markup inert
QA11Audit and replayEvery approver captured; forged one-approver consumption against a two-approver requirement reports INSUFFICIENT_QUORUM; the parity golden and endpoint fixtures unchanged; the preview shows the candidate's effective requirement
QA12Migration round trip and check_runtimeColumn, table, forced RLS and grants present; runtime cannot update or delete decisions; downgrade refuses while any decision row exists

Tests live in tests/test_approval_quorum.py.

Observe-only mode

IDSetup/actionRequired result
OB1Tenant in OBSERVE; production target request over HTTP and MCP200 OBSERVED, observed.effect REQUIRE_APPROVAL; no approval, impact reservation, claim or ticket; zero provider invocations
OB2Development target (would be eligible)OBSERVED ELIGIBLE; an OBSERVED impact row; zero provider invocations
OB3Tier-0 target (would be denied)OBSERVED DENY with the reasons
OB4Retry with the same key; a new key for the same intentSame view; a new observed execution; nothing consumed
OB5Enforcing tenant with one observe_only mappingThat action observed; another action enforced with a real approval and one mutation
OB6Eleven observed autonomous isolationsThe eleventh observes REQUIRE_APPROVAL under the assumed-executed projection; an enforced evaluation in the same tenant ignores observed rows; replay reproduces the decisions
OB7Switch to ENFORCE with a reasontenant.mode_changed audited; the next request creates a real pending approval; observed rows unchanged; a pending approval admitted under enforcement keeps its view while the tenant observes
OB8Mode change by an agent or approver, without a reason, to the current mode or to an unknown mode; admission during an emergency stopINVALID_IDENTITY, INVALID_REASON, INVALID_STATE, INVALID_MODE; TENANT_STOPPED
OB9Human pages with real ChromiumObserved badge and effect on Activity, no forms, Approvals empty, mode notice shown
OB10Audit, replay and previewCaptures carry mode: OBSERVE; a candidate bundle re-evaluates observed decisions; a forged observed execution with a claim, or an OBSERVED transition without observation, is OBSERVED_WITH_EFFECT
OB11Migration round trip and check_runtimeColumn and states present; downgrade refuses while any tenant is in OBSERVE or any OBSERVED row exists
OB12CLI mode and report commandsMode set and shown; report counts match the audit chain; non-ADMIN refused

Tests live in tests/test_observe_mode.py.

Shared limit scopes

IDSetup/actionRequired result
SL1Two agents in one group under a group ceiling of three with REQUIRE_APPROVAL; a third agent outside the groupThe group's fourth isolation by either member is PENDING_APPROVAL with limit:<rule>; every member row retains the group; the outsider reserves at once with an empty membership and the group count is unchanged
SL2Account rule over three identity actions and endpoint.isolate on the identity account with a DENY ceiling of three; an isolation on the endpoint account and a second rule scoped to that accountThe fourth disruptive action on the identity account is DENIED with HARD_LIMIT:<rule>; the endpoint account's isolation neither charges nor triggers it and counts only under its own account's rule; every row retains its account
SL3Selector environment = production with a total ceiling of two; development isolations in between and at the ceilingOnly production isolations count; development ones reserve without charging; the third production request is PENDING_APPROVAL with limit:<rule> beside the pack's production obligation
SL4Multi-action budget over endpoint.isolate and endpoint.lift_isolation in the SOC world beside a rule without actions, both at a ceiling of oneIsolate then lift the same target draw on one counter: the lift escalates on the pair rule alone and executes after approval with the endpoint released; the rule without actions counts the isolation only
SL5Six principals of one group on six connections at a group DENY ceiling of threeExactly three tickets and three DENIED with HARD_LIMIT:<rule>; three retained rows each with the group; after settlement three confirmed units and a seventh request still denied
SL6Two reservations by a member, then unassign it and assign a newcomerThe newcomer's snapshot reads the group's two units and its first isolation is the third; the leaver's next isolation retains no group and charges nothing to it; the leaver's earlier rows keep the group
SL7Rules with a missing or mismatched qualifier, an unknown action, group, account or fact, a selector value outside the domain, empty, oversized or duplicate actions, or actions without actionThe model or the store refuses each, the store with its field location (limits.<i>.action, .group_id, .account_id, .selector); a bundle with one valid rule of each scope validates and activates; existing packs validate with unchanged hashes; deleting the group fails the active bundle closed
SL8Approved candidates under group, account and selector scopes with an autonomous DENY rule at its ceiling and a total rule per scopeThe approved retry executes at the hard autonomous threshold of every scope, adds zero autonomous units and one total unit, and its row retains the group and the fact
SL9Replay and preview over captures with and without recorded membershipGroup, account and selector candidates reconstruct from captures alone and recorded decisions match; a candidate group rule over captures without groups reports UNKNOWN_GROUP_MEMBERSHIP and UNRESOLVED while the baseline still matches; a lower-bound denial stands; an unserved selector or unknown listed action is POLICY_ERROR; report.json is deterministic
SL10Migration round trip; downgrade with a group, a membership or a scoped draft bundle presentBackfilled account_id and facts match executions.frozen and group_ids is null on legacy rows; each downgrade refuses and leaves the revision, forced RLS and check_runtime intact; the runtime cannot insert, update or delete group rows
SL11Owner CLI and the record page with real ChromiumGroup commands are audited and refused for non-ADMIN actors, non-AGENT members and the runtime role; the record page labels every scope and action list as inert text with no link or form in the list
SL12Observe-only tenant under a group rule and a selector ruleObserved rows carry the attributes; the observe-mode projection triggers both rules; enforced evaluation ignores the observed rows and a member's isolation reserves after the switch to ENFORCE

A principal with more than 64 memberships fails admission closed with INVALID_CONFIGURATION and an audited rejection, and the 65th assignment is refused at provisioning.

Tests live in tests/test_limit_scopes.py (SL1 to SL6, SL8, SL12 and the membership bound), test_limit_scopes_schema.py (SL10), test_policy_models.py, test_policy_evaluation.py, test_context_bundle_validation.py and test_policy_store.py (SL7), test_admin_cli.py (the SL11 commands and the preview), test_replay_history.py and test_replay_scenarios.py (SL9), test_human_surface.py and test_browser_surfaces.py (the SL11 pages), with shared group, rule and bundle helpers in tests/scope_fixtures.py.

External execution checkpoint

Written first, failing on the unwired code, on real PostgreSQL, with the protected mocks acting as an external executor and the mock's conditional write standing in for a vendor's. The guide is External execution checkpoint.

IDSetup/actionRequired result
GC1An eligible checkpoint on a mapping with external_executor, in a tenant whose account has no connectorA checkpoint object of exactly grant and act, with operation, expected version, precondition class, both deadlines, report secret and act grant; an UNRESOLVED impact row and a canonical claim exist; admission returns no dispatch ticket and the dispatch path refuses the execution
GC2Six principals requesting checkpoints for one target on six connections, and separately at a shared group ceilingExactly one grant and one claim for the target and the rest RESOURCE_BUSY; at the ceiling exactly the ceiling's grants succeed and the rest carry the hard limit; no negative or double-released counter
GC3A checkpoint and an agent dispatch against one ceiling, with a total and an autonomous ruleBoth charge the same counters in every scope and the total ceiling is reached once across the two paths; an approved candidate adds zero autonomous units on both paths
GC4A checkpoint that requires approval, at a quorum of one and at a quorum of twoNo grant; the existing approval with its window and quorum; the retried checkpoint after the decisions consumes it exactly once and issues one grant
GC5A denied checkpointNo grant, no impact row, no claim; the denial is captured and replays to the same decision
GC6An attested executed outcome, then the identical report again, then a second signed record restating itEXECUTED at tier ATTESTED, impact confirmed, claim released; a report restating the settled tier, outcome and reason is a no-op returning the same view, whatever record carries it and whatever the retained proof stays, and a report contradicting any of the three is refused
GC7An asserted executed outcomeEXECUTED at tier ASSERTED, impact confirmed, the claim still held; a run before the act window releases nothing, the run after it releases the claim, and the record and page show the tier
GC8An asserted non-execution, then an administrator settlement with attested evidence through the connector-free pathThe report leaves INDETERMINATE with impact charged and the claim held; only the settlement releases them; the executor cannot reach that path
GC9An outcome report presented against an ordinary dispatched execution, another principal's checkpoint, an already settled execution, an expired report window, and with a forged or replayed secretEach refused with no state change, audited without echoing the secret, and the dispatched execution keeps its own owner secret as the only way to settle it
GC10A grant unreported past its report window, then a recovery run, then a cancellation runSwept to INDETERMINATE with impact charged and the claim held, listed in the uncertain queue; the cancellation run refuses to settle it as a non-execution
GC11Two simultaneous outcome reports on one grant, on two connectionsExactly one settles; one confirmation, one release, one audit event
GC12The target version changed between grant and report, under each precondition classENFORCED_BY_PROVIDER returns a non-execution that releases impact and claim; ASSERTED_BY_EXECUTOR records an assertion that releases nothing; UNAVAILABLE carries no version and the grant's own capture records that the change was undetectable
GC13A plan that expires and an act window that closes between grant and report, and an idempotent retry of a granted checkpointThe report is still accepted within the report window and settles at its tier; the retry returns the same checkpoint and a re-signed act grant, mints no second secret and reserves nothing new
GC14Replay of a history containing an attested checkpoint, an asserted checkpoint, an unreported grant, a settled FAILED_EFFECT_UNKNOWN row and a corrected row of each directionEvery decision recomputes; the asserted settlement is never presented as gateway evidence; the unreported grant replays as retained uncertainty before and after the sweep; the tenant still exports, publishes and previews; reports are byte-deterministic. Extended for the reported failure: as written the scenario held no row in the new state, so it could not detect the skipped settlement gate, the ledger's refusal of a second settlement or the terminal gate's before set — and a corrected row of one direction alone cannot detect the last of those, because the after set already catches CANCELLED_NOT_EXECUTED
GC15Bundle validation of external_executor beside observe_only, without a catalog binding and without a registered executor key; a request naming an executor; and a checkpoint request in a tenant switched to OBSERVEThe model refuses the placement pair and the store refuses the other two with their field locations; the request fields are refused as unknown; the observing tenant returns an OBSERVED view with no grant, reservation or claim; existing bundles validate and activate with unchanged hashes
GC16Emergency stop between grant and report, and a stop before a checkpoint; and an executor spamming invalid reportsThe outstanding grant is not recalled and its report still settles; a new checkpoint under the stop is refused; the stop period is visible in replay; the refusals are bounded and do not starve the tenant's writers
GC17Migration round trip with a settled checkpoint of each tier, an unreported grant, a settled FAILED_EFFECT_UNKNOWN row and a corrected row retainedDowngrade refuses while any exists; check_runtime passes before and after; tests/fixtures/endpoint-contract-v1.json, tests/fixtures/identity-contract-v1.json and the context parity golden stay byte-identical. Extended for the reported failure: as written it round-tripped a migration with no row in the new state, so the downgrade would have succeeded either way and the clause was never exercised. The corrected row is the case a check of executions.state alone would pass, because only its retained events name the new state
GC18The synthetic workflow contract end to end with real Chromium for the pagesThe story handles grant, denial, approval wait, expiry and report failure; the record and approval pages label the execution externally executed and show class and tier as inert text, with no control that dispatches

GC6's row was narrowed during implementation and the design carries the correction: what a settled row is closed to is a change of tier, outcome or reason, and a second record that differs in some other way — a later reported time, a signature by a second registered key — but restates the settlement is verified, found to move nothing, and answered as the no-op the first half of the row describes. It retains nothing and appends nothing, so a tenant cannot afterwards see that two of its registered keys attested one settlement; no budget, claim or execution state moves, which is why that is a limit on what the history can answer rather than a reason to refuse the record. A neighbouring record is refused by a different guard: one whose reported time predates the grant is INVALID_RECORD at the verifier.

Tests live in tests/test_checkpoint.py (GC1 to GC16 and GC18's approval-page clause), test_checkpoint_schema.py (GC17), test_checkpoint_workflow.py (GC18's story contract) and test_browser_surfaces.py (GC18's Chromium half), with shared fixtures in tests/checkpoint_fixtures.py; the four prefixes are group GC in ACCEPTANCE_PREFIXES. Supporting evidence for the same unit lives in test_checkpoint_evidence.py, test_admin_cli.py, test_recovery_run.py, test_replay_scenarios.py and test_replay_verify.py.

Provider-specific proof and the live half of the workflow contract are not covered by these rows and stay open until a separately authorized platform trial: every capability here is fixture-verified against the protected mocks.

Trusted context provisioning

IDSetup/actionRequired result
CP1Inventory manifest with column-backed and row-backed facts and a provider_version for an existing resource, imported under an ADMIN actorRows carry the source, version and observation time; the resources row carries the stated provider version; resolution freezes exactly those values; context.imported is audited with the digest and counts and no value
CP2The same manifest imported again, and a later one with a new version label and identical content, while an approval is pendingNo content or version changes; observed_at advances only for the second; the pending approval still passes re-attestation
CP3A manifest that changes one row-backed factThat row's version changes and its siblings keep theirs; the pending approval fails re-attestation with STALE_AUTHORIZATION
CP4An observation earlier than the retained one, one 61 seconds in the future, and a reused version with different contentOBSERVATION_REGRESSION, FUTURE_OBSERVATION, VERSION_REUSED; nothing written; no audit event; 59 seconds ahead is accepted
CP5Unknown, derived and ambiguous fact names; out-of-domain, wrong-type and oversized values; an incident fact in an inventory manifestUNKNOWN_FACT, DERIVED_FACT, AMBIGUOUS_FACT, INVALID_VALUE; nothing written; a value must satisfy every contract that declares the fact
CP6Missing or wrong-kind source, writer outside allowed_writers, unknown account, --tenant differing from the manifest, a non-ADMIN or foreign actor, and a source that exists only in another tenantINVALID_SOURCE, INVALID_WRITER, INVALID_ACCOUNT, TENANT_MISMATCH, INVALID_ACTOR; no row and no audit event in either tenant
CP7An entry for a resource the registry does not holdSkipped and counted by default; the whole manifest refused with --strict (UNKNOWN_RESOURCE)
CP8Retracting a row-backed and a column-backed fact, then active: falseRow deleted, column nulled, resource deactivated; resolution fails with INVALID_CONTEXT for the missing facts and INVALID_TARGET for the inactive resource
CP9Incident manifest creating an OPEN CRITICAL incident for a development endpoint, then closing it, then relinking itendpoint.isolate executes once under the v1 bundle after the first import (the provider version untouched) and is denied after the second; INCIDENT_RELINKED refuses the relink; a retraction on an incident entry is INVALID_MANIFEST
CP10The harness asset and ticket exports through the adapter, plus unmapped state, priority and classBoth manifests import, decommission the second asset and gate the action on the derived incident identifier; the adapter refuses each unmapped export
CP11Source create and deactivate through the CLI; an import through a source whose labels the default allowlist permits and one whose extra label it does not; the runtime role against every table the importer writesSources audited with sorted labels; the first import resolves, the second fails resolution until a bundle admits the label; deactivation makes its facts fail resolution and a repeat is INVALID_SOURCE; refusals exit 2 without echoing values; check_runtime passes and the runtime role cannot INSERT, UPDATE or DELETE
CP12A manifest over 16 MiB, one with 10,001 entries, a duplicate entry, a name in both facts and retract, an empty entry, a naive timestamp, an unknown field and an incident entry in an inventory manifest; then a 10,000-entry manifestMANIFEST_TOO_LARGE or INVALID_MANIFEST before any database work; the large manifest imports in one transaction within the statement timeout

Tests live in tests/test_context_provisioning.py.

Credential administration pages

IDSetup/actionRequired result
CA1ADMIN issues an agent credential for a new principal from the pagePrincipal and credential rows exist; the token is shown once under Cache-Control: no-store; a second GET of the list shows identifiers only; the token authenticates on /v1/me; principal.created and credential.issued carry the acting credential and no secret
CA2ADMIN revokes a credential; repeats it; tries an administrator credential and an unknown oneRevoked at once and the token no longer authenticates; the repeat, the administrator credential and the unknown one answer 409 with nothing changed; credential.revoked audited
CA3ADMIN issues an approver credential and assigns the approver to a target and action; the approver signs in with the new tokenAssignment listed; the pending approval appears on the approver's list; an agent principal, an unknown target and an unknown action are refused with 400
CA4ADMIN unassigns the approverThe pending approval disappears from the approver's list; a repeat answers 409; approval_scope.assigned and approval_scope.revoked audited
CA5Issue for an ADMIN principal, new:ADMIN, an unknown principal, a malformed principal, an out-of-range or zero lifetime, an extra field; the functions called directly with an ADMIN kind, an ADMIN principal, a 91-day lifetime or a stale clockEvery page attempt answers 400 with no secret; every function call raises its code; no principal or credential is written
CA6Approver session on the pages and their forms; anonymous; agent bearer403 for the approver and the bearer (the bearer refusal audited), 401 anonymous; nothing issued
CA7The functions with another tenant's setting, without a setting, with an approver or unknown actor, with another tenant's credential, an unknown target or an unknown actionINVALID_ACTOR, INVALID_TENANT, INVALID_CREDENTIAL, INVALID_TARGET, INVALID_ACTION; no row in either tenant; the other tenant's credential still authenticates
CA8Direct INSERT, UPDATE and DELETE by the runtime role on principals, credentials and approval_scopes; EXECUTE revoked on one function; INSERT granted on principalsPermission denied; check_runtime passes only with EXECUTE on every function and no table write privilege
CA9Migration round tripFunctions absent after downgrade and present after upgrade; the owner commands still create tenants and principals while downgraded
CA10Chromium at phone widthBoth pages pass the accessibility sweep; the secret field is read-only and the token is absent from the list afterwards; a declined confirmation keeps the credential, an accepted one revokes it; unassign works without scripts

Tests live in tests/test_admin_pages.py (the definer functions and the grants), tests/test_console_api_access.py and tests/test_browser_console_directory.py.

People page

The list and the person page for a tenant's humans, with deprovisioning (design, tests/test_people_pages.py).

IDSetup/actionRequired result
PP1ADMIN opens the list, then the deactivated and the ADMIN filters, then a bad state and an unknown keyEvery human principal with counts; the deactivated filter empty; the kind filter hides the approver; 400 for the bad state and the unknown key; the page passes the secret check
PP2New approver with a blank label, then with a label400 with the notice; then a new approver with the trimmed label, principal.created and principal.labelled, landing on the person's page and listed
PP3The person page for an approver, an ADMIN, an unknown id, an agent id and a malformed idEvery section for the approver; "owner tooling" and no deactivate or issue form for the ADMIN; 404 for the other three
PP4Set a label; set an 81-character oneThe label appears beside the prefix on the credentials and approvers pages and is audited; the long one is refused without being echoed
PP5Issue a credential from the person page, then revoke itShown once under Cache-Control: no-store, authenticates as that person, absent from the page afterwards; revoked and refused, credential.revoked audited
PP6End all sessions on an approver with two sessions; the same on the acting ADMINBoth sessions of the approver refused at their next request, the ADMIN's session intact, one human_session.revoked with reason ADMIN per row; the ADMIN's own request is 400
PP7Deactivate with a blank reason, then with one; repeat; reactivate400; then the credential no longer authenticates and the session is 401 in the same change, principal.deactivated carries the reason and the counts, the person leaves the default list and shows under the deactivated filter; the repeat is 409; reactivation restores active, clears deactivated_at, keeps the scopes and issues nothing
PP8The functions with an ADMIN target, the actor, an approver actor, a blank reason, an unknown principal, a long label, an active target for reactivation and another tenant's fence; an approver session on every routeINVALID_PRINCIPAL, INVALID_ACTOR, INVALID_REASON, INVALID_LABEL, INVALID_STATE, INVALID_TENANT or INVALID_ACTOR; 403 or 400 for the approver session
PP9Head, catalog and round trip0036_people; the three functions in FUNCTIONS; check_runtime passes; downgrade to 0035_session_label and back
PP10Chromium at phone widthBoth pages pass the audit and the contrast probe in both schemes; End all sessions and Deactivate decline when the dialog is dismissed and land when accepted; reactivation works with the script blocked

Tests live in tests/test_people_pages.py (the migration and the catalog), tests/test_console_api_people.py, tests/test_browser_console_directory.py and test_route_authority.py.

People page: history

Step 3 of the People page: a person's decisions, sign-ins and administrative events (design, rulings in section 13).

IDSetup/actionRequired result
PH1An approver approves one request and denies another, signs in, and has every scope unassigned; an ADMIN opens their pageDecisions newest first, the denial then the approval, each with its approval prefix and action, still shown after the scopes went; one sign-in with its device and "active"; no administrative events; the page passes the secret check
PH2An ADMIN labels the approver, ends their session, changes the mode and signs out their own other devices, then opens their own pageAdministrative events newest first in words: the mode change to OBSERVE, one "Ended a session of" with the approver's new label and prefix, the label change and the bundle's activation; no reason text, no entry for their own devices, no decisions; their sign-ins include one signed out
PH3A person with no history; then twenty-five sign-ins"No decision yet." and "No sign-in yet."; then exactly twenty rows, the last sign-in first and the first five absent
PH4An approver labelled with markup is deactivated with a reason containing markup; the ADMIN opens their own pageThe deactivation is listed with the label escaped as text; the reason appears nowhere; the map of kinds covers policy, credentials, scopes, principals, bindings, sessions and the tenant and nothing else
PH5Chromium at phone width: the approver's page with decisions and a sign-in, and the ADMIN's pageBoth pass the audit and the contrast probe in both schemes; the decisions, sign-ins and administrative events tables are found by their captions with their rows

Tests live in tests/test_people_history.py and tests/test_browser_console_directory.py.

Agents page

The list and the agent page for a tenant's AGENT principals, the governed half of the Agents page (design, tests/test_agents_pages.py).

IDSetup/actionRequired result
AG1ADMIN opens the list; the month filters before and after the agent's first request; the deactivated filter; bad filter values, an unknown key and malformed cursorsOnly AGENT principals and the month figure with its definition; the agent under "Made no request" until it makes one and under "Made a request" afterwards; the deactivated filter empty; 400 for each bad value; the page passes the secret check
AG2Fifty-five labelled agents beside the fixture's unlabelled oneFifty on the first page in label order, the rest on the next with no overlap and the unlabelled agent last, and no further link
AG3New agent with a blank label, then with a label400 with the notice; then an AGENT principal with the trimmed label, principal.created with kind AGENT and principal.labelled, landing on its page and listed
AG4The agent page; an unknown, an approver's, an ADMIN's and a malformed id; a label; a well-formed label form posted to an approver's idEvery section; 404 for the four ids; the trimmed label shown and audited; 404 for the approver's id with the approver's label unchanged
AG5Issue a credential from the agent page; revoke another principal's credential through this agent's route; revoke the agent's ownShown once under Cache-Control: no-store with a link back to the agent, authenticates as the agent, absent from the page afterwards; 404; revoked, refused and audited
AG6The agent page before any request; after two executed isolations and one pending request, with one live and one expired run; then with the executions moved past the evidence windowThe governed action inferred from the active bundle; then verified with the evidence's expiry, two executed and one pending row in recent activity, three requests this month, the live run only and the link to Activity for the agent; then no verified row
AG7Three executed and one open isolation by one agent and two executed by another; then the confirmed rows moved twenty minutes backThe principal budget reads 4 of 10 and the tenant total 6 of 20 with the store's own usage agreeing, and the other agent 2 of 10; then 4 of 10 and 1 of 20, again as the store counts
AG8Requests at the first and last instants of the month and the first instant of the next; another agent's in the previous monthThis month counts two for the first agent and nothing for the other, the next month one, the previous month the other agent's one; the list reads 01 and the agent page 2; December rolls into January
AG9Deactivate with a blank reason, then with one; repeat; reactivate; post deactivation to the ADMIN's and the approver's ids; an approver session on every route400; then the issued credential no longer authenticates, principal.deactivated carries the reason, the revoked credentials and no session, and the agent leaves the default list for the deactivated one; 409 on repeat; reactivation restores it with no live credential; 404 for both ids; 403 or 400 for the approver
AG10Chromium at phone widthBoth pages pass the audit and the contrast probe in both schemes; Deactivate declines when the dialog is dismissed and lands when accepted; reactivation works with the script blocked

Tests live in tests/test_console_api_agents.py, tests/test_browser_console_directory.py and test_route_authority.py.

Agents page: bindings

Step 2 of the Agents page: discovered agents, bindings and Govern this agent (design, tests/test_agents_bindings.py).

IDSetup/actionRequired result
AB1The two functions with an ADMIN, an approver, an unknown and a deactivated principal, an unknown and a gone agent, an approver as actor, another tenant's fence, each side already bound, a blank reason and an unknown binding; the runtime role updating the link or inserting a bindingINVALID_PRINCIPAL, INVALID_AGENT, INVALID_ACTOR, INVALID_TENANT or INVALID_ACTOR, INVALID_STATE and INVALID_REASON as each case names; a bind sets the link and an unbind clears it with who and why; the pair binds again with the history kept; permission denied for the runtime role
AB2link-agent with an ADMIN, with an agent principal, again, and with another principal; unlink-agentPRINCIPAL_UNAVAILABLE; one binding by the acting ADMIN with agent.bound; nothing new; LINK_CONFLICT; the binding withdrawn with the reason "withdrawn with unlink-agent"
AB3One bound pair and two discovered-only agents under a detection profile; each population, source and state filter; bad filter valuesThe bound pair as one row under its principal and the others under their agent ids; each filter narrows to its rows; a gone agent leaves the default list and shows under gone; 400
AB4A discovered-only page; the id of a bound agent; the bound principal's page; an unknown idThe source's facts, Govern this agent and a bind form offering the unbound agent principals, and no governed section; 303 to the principal; both halves with Unbind; 404
AB5Bind from the governed side and from the discovered side; bind a principal already bound; a malformed target303 to the principal and agent.bound for each pair; 409 with the notice; 400
AB6An executed isolation derived as verified reach; Unbind with a blank reason, then with one; unbind againThe verified row names the binding; 400; then the link cleared, the ExecBound row marked gone at once, agent.unbound with the reason, and the agent its own row again; 409
AB7Govern this agent on an agent whose display name is longer than a labelShown once under Cache-Control: no-store; an AGENT credential; the label is the name cut to 80 characters; the binding and the four events name the new principal; what to do next holds the install and MCP lines and never the secret; the agent's id answers 303; a second attempt issues nothing
AB8The agent leaves its source between the page load and Govern this agent409 with the notice, and no principal, credential, binding or audit event added
AB9Deactivate a bound principal; open a discovered agent's bind form; an approver session on bind, govern and unbindThe binding stays and the source's facts say the governed principal is deactivated; the principal is not offered; 400 or 403
AB10Chromium at phone widthThe discovered and bound pages pass the audit and the contrast probe in both schemes; Govern this agent lands on the show-once page with what to do next; Unbind declines when the dialog is dismissed and lands when accepted

Tests live in tests/test_agents_bindings.py, tests/test_console_api_agents.py, tests/test_browser_console_directory.py and test_route_authority.py.

Agents page: tenant line

Step 3 of the Agents page: the tenant line per source (design, tests/test_agents_tenant_line.py).

IDSetup/actionRequired result
AT1A tenant with no monitoring connectionThe line says no connection lists agents and links to Monitoring's connections page, with no zeros; the governed side counts the active agent principal and no binding; the page passes the secret check
AT2Three agents collected through the fake Graph API under a detection profile; then one boundOne source row listing three; the listed, consequential and not-established totals equal Monitoring's enumerated, with-reach and not-established figures; then "1 of 3" bound on the row and one bound principal on the governed side
AT3One agent leaves its source; then the only agent with grants has its claims pass their validityListed and not established fall by one and still equal Monitoring's; then consequential reach is none, as Monitoring counts it, and the row reads "0 of 2"
AT4Chromium at phone width with a sourceThe list with its tenant line passes the audit and the contrast probe in both schemes

Tests live in tests/test_agents_tenant_line.py and tests/test_browser_console_directory.py.

Tines discovery and collection

IDSetup/actionRequired result
TD1Register a connection and probe with every capability listed by the fake APIFive capabilities classified with probe times; health COLLECTING; no inventory and no observation written by a probe
TD2Inventory sync across three pages each of stories, actions and credentialsAllowlisted metadata only; options, credential values, descriptions, emails and addresses absent from every table, snapshot and page; counts match the fixture; an unrecognized action type is outbound = null, never false
TD3Audit pull over four pages with the page budget at twoTwo pages accepted with their evaluations pending; the cursor on the last accepted record; BACKLOG; the next run continues from the overlap and accepts the remainder without duplicates
TD4The same records served again after the cursor is rewound; then one record reused with different contentNo duplicate observations; the reused identifier records source-event-integrity.v1, marks the conflict and leaves the first content in place
TD5A failure injected between writing a page and advancing the cursorNothing written and the cursor untouched; the next run stores each record exactly once
TD6429 with Retry-After, 401 and 403 on the audit endpoint; 404 on credentials; three consecutive rate-limited runs then a 403RATE_LIMITED with the retry value, AUTH_LOST, unsupported for credentials with the others unaffected; exactly one audit request per run; one collection-health.v1 finding per episode
TD6bA tenant whose plan excludes the audit feature (422 on that endpoint)The audit and AI capabilities classify unsupported, not unknown; stories and actions stay available; inventory still collects and the run completes; the vendor's message body is never retained
TD7A cursor older than the retention floorRETENTION_GAP with a health finding; the connection snapshot shows it
TD8Configuration changes on a registered story, on an unregistered story, a credential change, a tenant change and an unlisted operation nameMATCH with one monitored-path-change.v1 finding each for the registered story, its action and the credential; NO_MATCH for the unregistered story and the tenant change; INSUFFICIENT_EVIDENCE for the unlisted name
TD9A discovered story with outbound actions and no source; a source registered; the source revokedGap finding, then none while it reports, then a new episode after revocation; the other stories classify as instrumented, no-outbound, disabled and coverage-unknown
TD10Enhanced AI records with a mapped and an unmapped tool nameThe mapped tool run matches under the profile version; the unmapped one stays INSUFFICIENT_EVIDENCE; the model run keeps the model identifier and token counts and no prompt or completion text
TD11Two tenants collecting from fixtures with the same identifiers; then a response claiming another platform tenantNo cross-tenant rows; every foreign record quarantined with one health finding and nothing stored
TD12Migration round trip, check_runtime, and the pages in Chromium at phone widthBoth tables forced-RLS with no runtime DELETE or TRUNCATE; downgrade refuses while collected evidence is retained; the Inventory and Connections pages pass the accessibility sweep, show every coverage class and the collected evidence label, and carry no fixture secret

Tests live in tests/test_tines_collection.py, tests/test_console_api_monitoring.py and tests/test_browser_console_monitor.py.

Scout, first source: Entra Agent ID

IDSetup/actionRequired result
SR1Register an entra_agent_id connection and probe every capabilityFive capabilities classified with the moment each was established; a Tines connection beside it is unaffected; a probe writes no agent and no grant
SR2Enumerate three pages of agent identities and classic service principalsAllowlisted metadata only; no mail address, human name, credential value, hint or token in any table, snapshot, log line or page; an agent listed in both collections is one agent and keeps the directory's own class
SR3An agent identity carrying properties the vendor has not publishedEvery undocumented property dropped and counted; nothing raw stored or logged; agentIdentity is an open type and the parser takes fields rather than removing them
SR4An agent holding a directory role the catalog maps to a consequential actionOne inferred reach row on the role path with mode unknown; one ungoverned-reach.v1 finding naming the agent, whose sponsors are what alert routing reads
SR5Register an operation mapping, an executor and a principal link for that actionA second reach row on the ExecBound path, native; the grant row unchanged and still live; the finding stays open
SR6The grant is gone at the next collectionThe grant and its reach rows marked gone and kept, never deleted; the episode closes; the finding remains for a person to read
SR7An ADMIN records an acceptance insteadThe episode closes with the acceptance; the reach row stays live and renders as accepted with its reason; an expired acceptance opens a new episode
SR8A blueprint declaring allAllowed roles for a resource applicationInherited grant rows whose path is the blueprint, read once however many agents share it; the declaration resolves to no action, so it is counted as unexplained rather than inferred
SR9A classic service principal holding a blocked-for-agents permission and an agent-shaped tagClass service_principal; the tag recorded and shown as evidence and never a promotion; owners read where sponsors do not apply
SR10An agent with no sponsor, and one whose sponsor read is refusedsponsorless-agent.v1 for the first; INSUFFICIENT_EVIDENCE counted separately for the second, and the page never shows a refused read as nobody
SR11An observation of the agent acting, then a settled execution under its linked principalobserved then verified, on their own paths and modes, beside the inferred rows; deriving twice changes nothing; evidence older than its currency is not revived; an unlinked agent gets no verified row however many executions settle
SR12Two tenants reading directories that serve the same object identifiers; a directory that disagrees with the registrationNo cross-tenant row; the mismatched directory refused with DIRECTORY_MISMATCH and nothing stored; a directory that will not answer leaves it unverified rather than assumed
SR13Token failure, 401, 403 on one path, 429 with Retry-After, an oversized body, and a continuation that leaves the pinned origin or the requested pathAUTH_LOST before any Graph request; forbidden for one capability with the others unaffected; the retry value recorded with no retry in the run; the body refused before parsing; four shapes of redirected continuation refused
SR14Migration round trip, check_runtime, and both pages in Chromium at desktop and phone widthFour tables forced-RLS with no runtime DELETE; both downgrades refuse while agent evidence or findings are retained; the pages pass the accessibility sweep, state the denominator in words, name the two coverage holes, render a lapsed claim as unknown with the moment it lapsed, escape a hostile display name and make no external request

Tests live in tests/test_scout_schema.py, test_scout_grants.py, test_scout_collection.py, test_scout_run.py, test_scout_reach.py, test_scout_derived.py, test_scout_rules.py, tests/test_console_api_monitoring.py and tests/test_browser_console_monitor.py.

Rulesets

Implemented for #273 by the design in docs/superpowers/specs/2026-09-17-rulesets-design.md and the plan in docs/superpowers/plans/2026-09-18-rulesets.md; tests/test_rulesets.py holds RS1 to RS4, tests/test_pack_rulesets.py holds RS5 and RS6 and tests/test_policy_pages.py holds RS7 to RS9.

IDSetup/actionRequired result
RS1One ruleset compiled twice, and once with its rules in another orderIdentical bytes for all five components; validated by the real Cedar engine, never a stub
RS2A condition naming an unknown fact or argument, a value outside the declared domain, an ordered operator on text, a ninth condition, a quorum on a rule that is not REVIEW, an action no mapping servesRefused with the field's location; the model refuses what it cannot mean before the compiler is reached
RS3A bundle carrying a ruleset, with each of the five components edited on its own while staying well-formedValidation refuses RULESET_MISMATCH naming the component; a bundle without a ruleset serialises with no ruleset key and validates as before
RS4A REVIEW rule with a quorum beside one withoutThe quorum names the positional obligation the engine actually raises for that rule, asked of the engine rather than restated
RS5The endpoint, identity and SOC packs as rulesets, every combination of classification, environment, incident status and severity, criticality and argument valueEffect, reasons and whether review is owed agree with the hand-written pack on every input; every reachable effect is reached
RS6Each pack's ruleset builder, recompiled from its own rulesetEqual to the bundle it built, and the compiler's raw limits already in the stored order; the hand-written builders stay the default fixtures derive variants from
RS7An ADMIN writes three rules (allow a workstation with an open critical incident, deny a domain controller, review production with a quorum of two), a review of open incidents and a budget, without scriptsEach rule reads as one sentence and the compiled Cedar is shown read only; the draft is created through the compiler and the store and its bundle page shows the rules, the note as text and the Cedar; validation succeeds; the preview names the rule behind each changed decision and marks a review candidate unresolved; activation succeeds; policy.ruleset_compiled is audited with counts
RS8A condition naming an unknown fact, a text value where a number is declared, and a note containing markupRefused at the form with the field's location; the note renders as text and appears in no compiled component
RS9An approver requests the rules editor403
RS10Every fixture host the seeder writes, read by project() over its facts; and every input of every pack (tests/test_pack_rulesets.py)The reading equals the pack's stated effect on every host and names a rule for every reading that is not the default denial; over the whole input space the reading equals the kernel's policy decision, a reading owing review names a review rule, and no reading is made with a fact missing; a host with no open incident yields the names of the facts it lacked
RS11A claimed sandbox's welcome page; GET /policy/rules?start=active and ?start=candidate with a ruleset bundle active, with a hand-written bundle, and with an unknown, foreign or repeated query key; GET /welcome/candidate-bundle.json from a ruleset bundle, pasted and validatedThe effect column names the rule behind each host and is labelled as the rules' reading; the editor opens on the active rules and budgets, the candidate caps every autonomous principal budget at three and the draft it creates validates; the hand-written case says so and starts empty; the bad queries are 400; the download is a ruleset bundle whose budgets and limits are both capped, and validation accepts it
RS12The agent page with a ruleset bundle active, and with a hand-written oneA Rules column names the rules about each governed action in the compiler's order, labelled as the reading and not the decision; the hand-written bundle adds no column and no claim

Approval deadline

Implemented for #213 by the design in docs/superpowers/specs/2026-09-17-approval-deadline-design.md; tests/test_approval_deadline.py holds AD1 to AD6, AD8, AD10 and AD11, tests/test_client_package.py and tests/test_examples.py hold AD7.

IDSetup/actionRequired result
AD1A fresh tenantapproval_deadline_seconds is 120 and an approval expires at min(valid_until, created + 120 s); every earlier 120-second test passes unchanged
AD2Deadline 900, facts valid for an hour; decision at +500 s, retry at +800 sThe approval carries created + 900 s; the retry executes exactly once; a further retry returns the same execution
AD3Deadline offexpires_at equals the plan's valid_until; a decision after it is STALE_AUTHORIZATION with approval_state: EXPIRED and no mutation; a decision before it executes
AD4--set 119, 3601, -1, 0, a string, a float or a boolean; the current value; a HUMAN_APPROVER credential; a blank reasonINVALID_DEADLINE, INVALID_STATE, INVALID_IDENTITY, INVALID_REASON; nothing written; each success is audited as tenant.approval_deadline_changed with previous and new values, the reason, principal and credential ids
AD5Deadline changed while an approval is pendingThe pending approval keeps its expiry; the next approval uses the new value
AD6Export after a decision made at +600 s under 900Replay verifies complete with no issues and reproduces every recorded effect, the review decision included
AD7Deadline 900; the sync client, the async client and the MCP example wait through an expired continuationEach resends the same intent, keeps polling, and returns EXECUTED after the later decision with one mutation
AD8Console under 900 and under offThe approval page and listing say (approval deadline) or (context validity); a decision submitted four minutes after the page rendered succeeds and executes once
AD10SessionsA load at +8 minutes extends the session to +23 and marks it renewed, at +5 it does not; renewal never passes the absolute cap or the credential's expiry; a revoked session is not renewed; the console re-issues both cookies with the new age and fifteen quiet minutes still end the session
AD11Migration and runtime catalogHead 0027_approval_deadline; the column is updatable and the sessions expiry is not; renew_human_session is the only new definer function; downgrade to 0026 and back returns every tenant to 120

Approval expiry

The kernel half of #426: an approval past its deadline that nothing touches again stays PENDING, and its execution PENDING_APPROVAL, indefinitely. Implemented by section 13 of docs/superpowers/specs/2026-09-13-enforcement-kernel-design.md; tests/test_approval_lapse.py holds AX1 to AX10, and console/src/routes/-home.test.tsx and tests/test_browser_console.py hold the console clauses of AX9.

IDSetup/actionRequired result
AX1A pending approval thirty-six hours past a 600-second deadlineNo execution_impact row, no resource_claims row, both demo rules projecting zero, and no approval.transition event: the row reserves nothing and claims nothing, so nothing is lost to capacity and the defect is a lifecycle that never terminates
AX2One recovery run over itexpired is 1; the approval is EXPIRED from PENDING and the execution STALE_AUTHORIZATION from PENDING_APPROVAL, bound to the same approval id and plan hash; no impact row, no claim and no provider mutation appear
AX3Two further runsexpired is 0 each time, the chain is byte-identical to after the first run, the states are unchanged and the selection is empty
AX4An approval inside its deadline, then past it but inside the run's graceSelected by neither run; the approval stays PENDING and the execution PENDING_APPROVAL
AX5An approved-and-consumed execution and a denied one, both past the deadlineexpired is 0; EXECUTED/CONSUMED and DENIED/DENIED are untouched and the single mutation stands
AX6An approval a human approved and the agent never came back to consumeSwept on the same terms: EXPIRED from APPROVED, execution STALE_AUTHORIZATION, no mutation
AX7A decision, then an agent retry, in the window after the deadline and before any runBoth answer STALE_AUTHORIZATION with approval_state: EXPIRED and no mutation; a second decision is INVALID_STATE
AX8A decision and a recovery run racing on two threads past the deadlineExactly one approval.transition to EXPIRED and one execution.transition to STALE_AUTHORIZATION; the loser adds nothing and holds nothing
AX8bThe same race inside the deadlineThe sweep always refuses under the fence and the decision always lands: APPROVED, execution PENDING_APPROVAL, no EXPIRED transition
AX9The pending view, the lapsed view and the overview count, before and after a runBefore: out of pending, in lapsed, overview pending 1. After: still out of pending, still in lapsed, overview pending 0 and one STALE_AUTHORIZATION; an approval that ran out of time never disappears from the one view that says so
AX10Export and replay after a runThe verified export replays complete with no issues

Sandbox onboarding

Slots, invitations, the claim and the refresher (design, tests/test_onboarding.py). SO8 onward join with the seeder, the public page and the refresh job.

IDScenarioExpected
SO1Migration and runtime catalogHead 0029_claim_clock; both tables fenced and forced with runtime SELECT on slots only and no write on either; both definer functions listed; a tenant name in another case is refused by the owner connection; an accidental INSERT or UPDATE grant on either table fails check_runtime closed; downgrade through 0028 to 0027 and back round-trips
SO2A live invitation claimed with a nameOne transaction: the tenant is renamed (whitespace collapsed) and activated, the ADMIN principal and its 30-day credential exist and authenticate, the invitation is redeemed and the slot claimed at the same instant, tenant.claimed is the tenant's first audit event with the new ADMIN as actor and verify_chain passes; the token appears in no repr, payload or log
SO3Two claims of one code at once; a claim whose name collides after the tenant was renamed inside the functionExactly one claim succeeds and the other is INVALID_INVITATION; the collision is NAME_TAKEN and leaves the tenant inactive with its placeholder name, no principal, no credential, the invitation unredeemed and the slot unclaimed, so the right name then claims it
SO4Wrong secret five times; expired; already redeemed; an active tenant with an unclaimed slot; a code naming an ordinary tenant; malformed codes; blank or over-long names; a name taken in another casefailure_count 1 to 4 then revoked on the fifth, after which the right secret is refused; INVALID_INVITATION for the dead and foreign codes without touching the database for malformed ones; INVALID_SLOT for the active tenant; INVALID_NAME; NAME_TAKEN without counting a failure
SO5After a claim, reads under another tenant's fence and direct runtime writesThe slot, the tenant and its principal are invisible under the other fence; the runtime role cannot insert into or update tenants, principals, credentials, resources, incidents, onboarding_slots or onboarding_invitations, nor read the invitations
SO6Refresh of a claimed sandbox after an executed isolation, with observation times an hour oldNot a sandbox and an unclaimed slot refuse INVALID_SLOT, an approver refuses INVALID_IDENTITY; the refresh advances observed_at on resources, row-backed facts and incidents, keeps every value, sets the isolated target's version to the resulting_version of the provider's signed receipt and leaves the untouched target at its seeded version; the next plan on the host freezes the reconciled version; a second refresh only moves time
SO6bRefresh with a checkpoint settlement (ATTESTED) and a FAILED_NOT_EXECUTED row against the targetNeither moves the version
SO7The owner commands over a slots file and an explicit slotinvite-create takes the lowest free slot whatever the file's order, writes the code once to a new owner-only file and prints identifiers only; a slot with a live invitation refuses INVITATION_LIVE; an expired invitation is revoked when the next one is minted; an empty pool refuses INVALID_SLOT; invite-list states each invitation with no code; a revoked code cannot claim and the slot can be invited again; the reissued code claims end to end; a claimed slot, an existing output path, a blank note, a lifetime outside 1 to 720 hours, an ordinary tenant and a broken slots file are refused with nothing written
SO8sandbox-seed of two slots into the test databaseEach slot is an inactive ENFORCE tenant with two accounts, four sources, six mappings, 32 targets with identities, incidents and 58 aliases, the approver's 52 scopes, the jobs ADMIN with one credential and the SOC bundle active at an hour's validity, audited sandbox.seeded then the bundle lifecycle; the jobs credential is refused while the slot waits; boot.run provisions both mock stores from the resource fragments and materializes the account fragments; the connectors fragment parses as gateway configuration; no token appears outside jobs-tokens.json and gateway-connectors.json; a repeated seed, a bad count, seed or origin and an existing directory write nothing; a claimed slot's refresh covers all 32 targets and its jobs credential then authenticates
SO9sandbox-adopt of the execution world's tenantThe slot row is stamped seeded and claimed at the same instant and sandbox.adopted is the last event under the acting ADMIN; a non-ADMIN actor, an inactive tenant, a second adoption, a bad seed and slot 0 are refused; the adopted tenant's refresh works
SO10A seeded slot claimed through /onboardThe page holds the new administrator credential, the origin and the name, sets no session cookie and deletes the page cookie; tenant.claimed follows the seeder's events; the credential signs in as ADMIN, opens the credentials page and a welcome page naming the hosts, their effects, what is still to be issued and the slot; the spent code is refused afterwards
SO11Refusals on /onboardA wrong secret shows the one sentence and counts one failure; a forged CSRF field, a bearer header, a wrong or missing origin, an extra field, a malformed code and a query string are refused before the database and count nothing; a taken name is 409 with its own sentence and leaves the slot unclaimed; four more wrong secrets revoke the invitation and the right code is then refused; the tenant stays inactive
SO11bA runtime with two sign-in attempts per windowThe third attempt on one selector is 429 with Retry-After, another invitation is unaffected, and the database counted only the two attempts that reached it
SO12/welcomeAn approver session is refused 403; the operator-provisioned tenant's ADMIN gets the short form with the administration links and the menu carries the link
SO13sandbox-refresh --tenants-file and recover --tenants-file over a map naming a claimed slot with hour-old facts, an unclaimed slot, an agent credential and a malformed tokenThe claimed slot is re-attested (32 targets, observation times fresh) and swept (zero examined); the other three are skipped: INVALID_IDENTITY; both runs exit 0; the single-tenant forms still work, the two forms are exclusive, and a broken map is refused before any tenant runs
SO15The sandbox describes itselfA claimed slot's ADMIN downloads /welcome/sandbox.json as a no-store attachment whose targets equal the seeded slot file's, with no credential and no secret in it; the approver's session is refused on both downloads
SO16The candidate bundle/welcome/candidate-bundle.json derives deterministically from the active bundle (principal autonomous budgets capped at three, tenant total unchanged, a uuid5 bundle id), parses as a bundle, and taken as a draft, validated and previewed over four autonomous isolations it reports a changed decision; a tenant without a slot gets 404 on the description
SO17The welcome page as a progress page (tests/test_demo_driver.py)The operator tenant adopted as a slot shows the claim done and every demo step not yet with the 600-second deadline stated; after the decide beat the first governed request, the denial, the refused claim and the console decision are done with times and the time to first governed request is printed; after budget the shared limit row is done; the page names no credential. Every recipe equals its fixture (tests/test_recipes.py)
SO18The privilege boundary of migration 0042_console_invitationsThe runtime role reads every column of onboarding_invitations the console's list needs and is refused token_hash; onboarding_invitation_revoke ends a live invitation, refuses one already revoked, one redeemed and one that is not there, with one code and no diagnostic, and the freed slot takes the next invitation
SO19Minting from the console, over the runtime connectionAn approver and an agent identity are refused before a slot is touched; a showcase batch offers nothing; a mint takes the lowest free slot, returns a code that claims it, and records sandbox.invited in the operator tenant with the identifiers, the expiry and whether a message went out and with no code, address or note, the chain still verifying and the slot tenant's own chain empty; the note and lifetime bounds are the store's, not the form's; a revoke frees the slot and records its own event; an invitation outside the configured pool is refused however its id was typed; a send the relay did not accept leaves a revoked invitation, a free slot and no sandbox.invited; and over a real relay the emailed code is the only one that works, a resend replaces it without taking a second slot, and the old one is then refused
SO20The Invitations area of the console API (tests/test_console_api_invitations.py)Every route refuses a bearer 403 and audits it, and answers 401 in JSON with no session; an approver is refused 403 everywhere; a tenant that is not the configured operator, and a deployment with no signups block, answer 404 on every route and report invitations: false on the session; an administrator given a space membership is refused 403 at the next request; the code is in the one minting response and in no listing, header or audit event; the body's bounds are refused without echoing a value; an empty pool is a conflict; an uncertain send leaves a revoked invitation and a free slot; and the digest is neither rendered nor readable
SO14boot.py with EXECBOUND_JOBS_TOKENS_JSONThe map is written owner-only to jobs.json and scrubbed from the environment; a non-object, an empty or over-64 map, a non-canonical or malformed tenant id and a malformed token are refused by variable name with nothing written; the blueprint carries seven start commands, the recover and sandbox-refresh jobs read the map and the recover job no longer takes the pilot's tenant and token variables

Open sandbox signups

The public /signup page, slot allocation, the waitlist, the abuse controls and the sandbox lifecycle (design, tests/test_signups.py). Real PostgreSQL and a real local relay throughout, and the page is exercised as a browser posts it rather than by calling the module, because /signup is the one unauthenticated write path a stranger reaches.

IDScenarioExpected
SU1Migration and runtime catalogHead 0039_signups; both new tables fenced and forced with runtime SELECT on each, no INSERT and no DELETE, UPDATE on exactly the outbox's seven delivery columns and none on the requests; the seven definer functions listed; check_runtime green; downgrade to 0038 and back round-trips
SU1bThe lifecycle of a sandbox claimed before the migrationEvery slot already claimed is stamped converted by the upgrade, so the adopted pilot and the showcase slot acquire no expiry; a slot claimed after it gets expires_at at the claim plus fourteen days from the trigger
SU2A submission with a free slotThe address is stored bare and lower-cased with its domain and consent version, the request is INVITED against the lowest free slot, one invitation exists there with a hash nobody holds the preimage of, one invitation message is queued naming the slot, the invitation and the request, and the page carries no secret
SU3The send and the claimThe drain rekeys the invitation, so the stored hash is not the one allocation wrote; the mailed code claims the sandbox on /onboard unchanged; the sweep follows the claim and the address is retained ninety days from it
SU3bA relay that refuses the recipientNo code is delivered anywhere, the row stays PENDING with one attempt and MAIL_REFUSED, and nothing redeemable exists
SU3cA relay whose daily limit is reachedThe message defers for an hour with RATE_LIMITED and spends no attempt
SU4Two submissions racing for the last free slot, two threadsExactly one invitation and one INVITED request; the loser is queued rather than refused, and holds a waitlist message
SU5A disposable domain, a filled honeypot, a missing consent box, a malformed address and a successOne page, one status and one body for all five; each refusal is a row with its code and a thirty-day retention; a malformed address records nothing
SU5bThe same address twice, and a refused address againThe second request is ADDRESS_COOLDOWN; a refusal does not start the cooldown, so an address refused once is admitted next time
SU5cA domain past its cap, and a free-mail domainDOMAIN_CAP after the configured number of live sandboxes; a free-mail domain is exempt from that cap and refused FREE_MAIL_DAILY_CAP at its own lower daily one
SU5dMore requests than the hourly capGLOBAL_HOURLY_CAP, with refused rows counting toward the caps so the guard bounds a flood rather than only the successes
SU6Two requests with no free slot, then a batch seededBoth queue with a waitlist message; the sweep allocates to whoever asked first and leaves the second waiting
SU6bA pool holding only the showcase seedThe request queues and the showcase slot is never invited
SU6cA sweep run three times over a request that is still waitingOne waitlist message, no error, and the request still queued
SU6dAn invitation that lapses unredeemed, and a request nobody allocated within the cooldownBoth become EXPIRED with a retention date, so neither a domain cap nor an address outlives the request that made it; the domain has room again, and the address cooldown is deliberately unaffected, because a code that was sent and not used was still a request
SU6eA claim that lands after the invitation's own lifetimeThe slot's own row is the fact: the request is followed to CLAIMED rather than lapsed
SU7A claimed sandbox at day elevenOne lifecycle message queued exactly once however many times the cron runs, notice_at and notice_sent_at both stamped, and the message carries no code
SU7bThe same sandbox past its endThe tenant is inactive, its slot deactivated, its own administrator credential refused, tenant.expired appended to its own verified chain under its own ADMIN with the reason, the request EXPIRED, and a second sweep writing nothing
SU7cExtension and conversionsandbox-extend revives the lapsed sandbox and moves the end, its credential works again, more than thirty days and a non-ADMIN actor are refused, and sandbox-convert clears the end so no later sweep expires it; both events land in the sandbox's own chain
SU7dThe refresher against a deactivated sandboxsandbox-refresh refuses it exactly as it refuses any inactive tenant
SU8RetentionAddresses past their date are nulled, the domain, the state and every timestamp stay, and the report still counts the rows
SU9A read of the operator tenant under another tenant's fence, through the runtime roleNothing is visible; the operator's own fence sees the rows, so the zero is isolation and not absence
SU9bA direct runtime write to the requests tableRefused
SU9cA runtime write to the outboxThe lease columns move; the invitation a message names does not
SU9donboarding_invite against a claimed slot, a tenant that is not a slot, and a slot holding a live invitationINVALID_SLOT, INVALID_SLOT, INVITATION_LIVE
SU9eonboarding_invitation_rekey against a redeemed invitationINVALID_INVITATION
SU10A signup carried through to a claim, with the product's own log stream capturedThe address appears in no structured record, no audit event and no rendered page; the domain does; signups --list is the one place it is rendered
SU11The vendored disposable snapshotSorted, lower case, unique, disjoint from the free-mail constant, matching a subdomain of a listed service, and nothing in the module fetches anything
SU12review and closed modesReview records the request and allocates nothing; closed shows the paused page, answers a submission with that same page and records no address at all
SU13The owner list and the funnel reportBoth states listed, an unknown state refused, the report's weekly counts, waitlist depth and pool figures correct, and no address anywhere in it
SU14A signups block with no mail blockRefused at start; the same file with the relay loads and carries the mode
SU14bMalformed addressesRefused before the database; a padded, mixed-case address normalizes to one bare lower-cased address and its domain
SU14cA forged CSRF field, a wrong origin and a query string on /signup403, 403 and 400

Arcade logic extension

Written first, failing on the unwired code, on real PostgreSQL, with a fixture Arcade caller and the protected mocks standing in for the vendor. The guide is Arcade logic extension. Rows live in tests/test_arcade.py (AL1 to AL25 and AL29 to AL34), tests/test_arcade_schema.py (AL26), tests/test_arcade_workflow.py (AL27) and tests/test_browser_surfaces.py (AL28 and AL30's page clause), with shared fixtures in tests/arcade_fixtures.py; those four prefixes are group AL in ACCEPTANCE_PREFIXES. Supporting evidence lives in tests/test_checkpoint.py, tests/test_replay_scenarios.py, tests/test_replay_verify.py, tests/test_recovery_run.py, tests/test_route_authority.py and tests/test_surface_configuration.py.

IDSetup/actionRequired result
AL1A governed tool's pre hook on a mapping with external_executor, in a tenant with no connector for the account and an unrevoked executor key{"code": "OK"} and nothing else; one DISPATCH_COMMITTED execution, one UNRESOLVED impact row, one canonical claim, one correlation row holding the Arcade id and the secret; no dispatch ticket and no provider call
AL2The same pre hook delivered three times under one Arcade execution id, and once more after the frozen plan expiresOne execution, one reservation, one claim, one correlation row and one secret; the earlier repeats answer OK from the re-grant and charge nothing; the post-expiry repeat is CHECK_FAILED with the reservation and claim still standing
AL3Two Arcade execution ids for one intent, concurrently and then sequentially while the first is granted and unsettled; then the same intent again after the first settlesThe second is CHECK_FAILED and ExecBound holds one execution, one reservation and one claim; the post-settlement repeat is a fresh decision that resolves, evaluates and counts again, and is CHECK_FAILED on RESOURCE_BUSY until the run releases the claim
AL4A second Arcade execution id for an intent whose first pre hook was abandoned at the extension's deadline while admission committed, and again for one whose correlation row was written without a secretBoth are CHECK_FAILED because neither row holds the grant's secret; ExecBound holds one execution, one reservation and one claim; neither is answered OK from a re-grant, and the abandoned grant is visible in the uncertain queue
AL5A denial, a tenant emergency stop, a ninth outstanding grant, a claimed target and a tenant whose key ring cannot attestEach CHECK_FAILED carrying only a stable code and the execution id where one exists; no input value, vendor string, rule id or secret in error_message; nothing reserved by any of them
AL6The runtime capacity bound full and the surface rate limiter tripped; separately, a shared hard ceiling reachedThe first two answer RATE_LIMIT_EXCEEDED with no execution row; the ceiling answers CHECK_FAILED, and no rule identifier reaches the caller
AL7The application's own refusals reaching the boundary on each route: the capacity limiter exhausted, and a handler-level 503, on POST /pre and then on POST /postOn the pre path both are 200 with a body of exactly {"code": ...}RATE_LIMIT_EXCEEDED for the 429 and CHECK_FAILED for the 503 — and neither carries detail; on the post path both are passed through with their original status and body; a 401 is passed through on both
AL8A tool outside the map; a governed tool at an undeclared version; a tool of an ungoverned toolkit; a tool whose reported behavior contradicts the map's expectDenied, denied, OK, denied; nothing reserved in any case. The ungoverned pass is asserted against the tenant's chain: the audit head's sequence and hash are read before and after and are unchanged, and no executions row exists for it
AL9inputs missing the declared target key, carrying it as a non-string, and carrying an undeclared key; then, against a declaration naming an incident key, inputs missing it, carrying an explicit null, carrying a string no UUID reading admits, and carrying a number, and a hook that omits it while the reported metadata names oneEach refused before any kernel call; nothing resolved, reserved or claimed, and the refusal names no key value. The incident refusals answer INVALID_INCIDENT rather than the kernel's opaque code, the tenant's audit head is unmoved across all of them, and the reported id fills nothing
AL10inputs carrying a second target-shaped key beside the declared one, and an identity tool targeted by hostnameThe first is OK on the declared key alone with the other ignored for selection and refused as undeclared where it is undeclared; the second reaches the kernel and the kernel refuses it, which is where that refusal belongs
AL11An approval-requiring tool: the first hook, a human decision, a retry under a new Arcade id inside the deadline; and a second walk where the deadline passes firstThe first is CHECK_FAILED carrying the approvals page and the execution id; the retry adopts the same execution, is answered the execution's first grant with its own secret, consumes one approval exactly once and answers OK; the expired walk reaches STALE_AUTHORIZATION and its next attempt is a new decision and a new approval
AL12Post hooks carrying success: true, and success: true beside an execution_errorBoth settle EXECUTED at tier ASSERTED with impact CONFIRMED, the claim still held until the run after the act window; the self-contradiction is retained on the correlation row, appends no audit event and changes no settlement; the settled rows' correlation secrets are null; both answer OK
AL13A post hook carrying success: false with and without execution_code, against a DISPATCH_COMMITTED row and against one already INDETERMINATEBoth settle FAILED_EFFECT_UNKNOWN at tier ASSERTED with impact CONFIRMED and confirmed_at stamped, the claim held; outstanding_grants falls by one; the adoption query returns nothing for that intent; the transition payload carries the whole AssertedOutcome with reason REPORTED_FAILURE and tier: ASSERTED beside it; the platform's codes are retained as inert text; both answer OK
AL14A post hook with no success at all; one whose body exceeds post_bytes; one whose body will not parseAll three answer OK, settle nothing and leave the row where they found it with impact charged and the claim held; the oversized one is recorded as an unreadable report and no part of output is retained or logged; none of the three is a 4xx
AL15A post hook with content-type: application/json; charset=utf-8, one with application/json, one with Application/JSON, one with two content-type headers and one with content-encodingThe first three are accepted and settle identically; the last two are refused, exactly as the monitoring intake refuses them
AL16A post hook delivered twice; a second UNKNOWN against an INDETERMINATE row; a post hook arriving before its own pre hook; a post hook whose correlation row is a denialThe first two append and retain nothing and answer OK; the early arrival is the orphan case, recorded bounded and answered OK, and its later pre hook still decides normally; the denial case appends exactly one execution.control_point_contradiction to the tenant's chain, a second delivery of it appends none, and both answer OK
AL17A post hook carrying inputs that differ from the frozen ones, and one carrying noneThe difference appends one execution.control_point_contradiction and is retained as inert text; the absent case appends nothing and claims nothing; neither changes the settlement; a tenant holding both kinds still exports and replays with no UNKNOWN_EVENT_SEMANTICS
AL18A correlation row's report secret read out of the table by a reader with no credential, then presented on the outcome route as EXECUTED; then a recovery run after the act windowThe row settles and the run releases the canonical claim, so the theft does free the target — asserted as the design's stated bound, not as a refusal; the impact stays CONFIRMED and no scope is refunded; the genuine post hook afterwards is answered with the settled view and appends nothing
AL19A governed pre hook and an ordinary agent dispatch against one ceiling with a total and an autonomous rule; and a group ceiling reached across two Arcade projects of one tenantBoth paths charge the same counters in every scope and the ceiling is reached once across them; an approved candidate adds zero autonomous units on both; the project past the hard ceiling is CHECK_FAILED
AL20A grant that never receives a post hook, past its report window, then a recovery run, then the console settlement with an ADMIN-signed record; and eight silent uncertain rows followed by a ninth pre hook, followed by one reported failureSwept to INDETERMINATE with impact charged and the claim held and listed in the uncertain queue; only the signed settlement releases them; the ninth pre hook is CHECK_FAILED with TOO_MANY_GRANTS; the reported failure settles one row terminally and the next pre hook is then answered OK with a grant, which is what says the cap drains on settlement
AL21The extension's deadline expiring while admission is still in flightThe hook answers CHECK_FAILED within the configured budget; whatever admission commits is retained, visible in the uncertain queue and never refunded; no second request is sent
AL22A tenant in OBSERVE through both hooksOK with an OBSERVED row and no reservation, claim or grant; the post hook is recorded and settles nothing and answers OK
AL23An ADMIN and an approver bearer; unknown, expired and revoked bearers; another tenant's bearer; a credential expiring before the report windowThe ADMIN and approver bearers are refused as non-AGENT and audited; the unknown, expired and revoked bearers are refused as unauthenticated and counted; no hook decision is reached in any of those; the foreign bearer reaches only its own tenant's objects; the short credential shortens the report window to its own expiry and its post hook afterwards is refused on identity rather than on the deadline
AL24Configuration validation: an operation_ref outside ActionRequest.operation_ref's own constraint, a toolkit named as both governed and ungoverned, a target.input outside its own inputs list, an incident.input outside it, a duplicate tool declaration, a deployment with an arcade block and no connectors, and a key reload whose file changes the arcade blockEach invalid file is refused at load as the same opaque invalid-configuration answer every invalid file gets; the connector-free deployment loads; the reload is CONFIGURATION_CHANGED and leaves the running keys and the running tool map untouched
AL25The create_arcade boundary, GET /health, and the route inventory with and without an arcade blockEvery method and raw path but the three answers 404 and a websocket is closed; GET /health is served by the application's readiness route, answers 200 with the database up and 503 with it down, is unauthenticated and names no tenant; tests/test_route_authority.py asserts the exact served set in both constructions and the two new routes carry AGENT authority
AL26Migration round trip with correlation rows of each kind and an execution in each settled state retained, including a corrected row whose own state is FAILED_NOT_EXECUTED and whose events name the new state; check_runtime; the runtime role against arcade_hooks; a replayed history containing a settled FAILED_EFFECT_UNKNOWN and a corrected oneDowngrade refuses while any of the three constrained columns holds the new state anywhere — asserted with the corrected row as the only holder, which a check of executions.state alone would let through — and succeeds once none does; check_runtime passes before and after; the runtime cannot DELETE; the export recomputes every decision, reconstructs the impact as CONFIRMED for the asserted failure and NONEXECUTION for the corrected one and reports both as settled rather than unresolved; Ledger.correct is asserted as the narrow method it is; the frozen contract fixtures and the context parity golden stay byte-identical
AL27The fixture Arcade caller over the real HTTP surface, driving every branch and asserting its recorded shapes against execbound_harness.fake_arcade.HOOK_CONTRACTThe walk covers governed permit, denial, approval wait and retry, ungoverned pass, duplicate pre and post delivery, reported success, reported failure, absent success, an oversized body and a missing post hook; the contract dictionary is a module-level constant of the harness rather than a literal inside the test, so the guide, the driver and this walk assert one object and a change to any shape fails in one place
AL28The record and approval pages for an Arcade-carried execution, with real ChromiumTool identity, Arcade user id, reported codes and metadata render as inert text with no control that dispatches; the tier is shown with what it rests on; a settled assertion says what still holds its claim and what releases it; a settled FAILED_EFFECT_UNKNOWN row says the effect was never established and that the impact is charged
AL29The settlement vocabulary, both halves. Every (outcome, reason) spelling on the outcome route, signed and unsigned; TerminalEvidence, ExecutorRecord and JournalRecord asked for FAILED_EFFECT_UNKNOWN and REPORTED_FAILURE; a verifier double returning the new pair, through record_outcome against a dispatched row and through the report route against a grantExactly five pairings validate and exactly ten (tier, outcome, reason) combinations settle — four verified at ATTESTED, the same four at no tier on the dispatched path, and two asserted — enumerated in the test rather than sampled; every other spelling is INVALID_REQUEST from parse_report with no execution row read, no fence taken and no secret compared; the three evidence models raise for both new members; a report presenting a signed record beside REPORTED_FAILURE is refused by OutcomeReport's own clause, asserted as that refusal; the verifier double is refused by _proof in both call paths; test_the_terminal_aliases_are_the_evidence_literals passes unchanged
AL30A row settled FAILED_EFFECT_UNKNOWN at tier ASSERTED, then an ADMIN-signed CANCELLED_NOT_EXECUTED through reconcile; the same walk with an attested VERSION_MISMATCH; a third where the recovery run released the claim before the correction; and, on a corrected row, the correcting record replayed on the report routeEach settles at the verified evidence's own state — FAILED_NOT_EXECUTED, then STALE_AUTHORIZATION — at tier ATTESTED, with the impact rewritten to NONEXECUTION and confirmed_at null; the claim is released, and in the third walk released_at and the earlier execution.claim_released event are unchanged; the chain holds both transitions with both statements retained and the administrator's reason on the second; an ADMIN drives one correction of each direction from the record page's own control, in Chromium, and the page then shows the corrected row and the statement it corrected; a redelivery of the original post hook afterwards is INVALID_STATE in the kernel, answered OK to the platform and settles nothing, while the correcting record on the same route is the restatement it is. The export clause is parametrized over both correction directions: for each, the ledger recomputes with no INVALID_IMPACT_HISTORY and no INCOMPLETE_HISTORY, ReplayReport.complete is true, and the exported row's settlement_tier and terminal time are the correction's
AL31Everything the correction refuses: an ADMIN-signed EXECUTED against a correctable row; an ADMIN-signed non-execution against an EXECUTED row at tier ASSERTED and at tier ATTESTED; against an already-corrected row; against a row ExecBound dispatched; a second correction; a report carrying a record presented against an uncorrected correctable row; and a row whose retained state, tier or reason is not the correctable trio, each of the three varied on its ownEvery one is INVALID_STATE, audited by _rejection and leaving the row, its impact, its claim and its tier untouched; the promotion the checkpoint guide's note refused is still refused with the same code; the report route produces no correction, so a held report secret corrects nothing. Four refusals are asserted individually, because a mutation of any one alone fails nothing: _correctable's state fact and its tier fact, each varied while the other two hold, and each of the two direction guards removed by name
AL32The granting-path latency measurement: the eligible pre hook end to end against the fixture caller and the protected mocks, at one, four and sixteen concurrent callers of one tenant, with the reservation, the canonical claim, an approval consumption, the executor key ring load, the closing attestation check and the secret mint inside the timed regionMedians and a maximum reported beside scripts/admission_benchmark.py's denied-path figures for the same concurrencies, recorded in the unit's evidence; the arcade block's required deadline_seconds is defended against the measured maximum rather than against a default, and a deployment file omitting it is refused at load
AL33A tenant whose only executor key is revoked as compromised, holding a live grant issued before the revocation: an attested report, then a bare success: false, then a recovery run past the act window, then an attempted correction with a newly registered keyThe attested report is refused and settles nothing; the bare word settles FAILED_EFFECT_UNKNOWN at tier ASSERTED with the impact confirmed; the run releases the canonical claim; the correction is refused because no key is usable at that grant instant, and a key registered after it is refused too — asserted as the design's stated bound, not as a refusal the design claims to make
AL34A governed tool declaring an incident key and mapped to endpoint.isolate under the released endpoint-isolation.v1, driven end to end: a hook claiming the target's own open critical incident; then hooks claiming an id naming nothing, an incident of another resource of the same account, and an incident of another tenant; then one claiming a second incident of this tenant standing against the same targetThe first is {"code": "OK"} with one DISPATCH_COMMITTED row and one UNRESOLVED impact, and the frozen plan carries the claimed id with the contract's incident facts naming it as their source object and nothing set in the claim beside it; the three misdirected claims are each CHECK_FAILED with nothing resolved, reserved or claimed; the second incident of the same target is granted, which is the design's stated bound — the kernel binds a claim to the target and not to relevance — asserted rather than implied
AL35A governed call the tool map refuses, delivered three ways: an undeclared tool with a usable Arcade execution id, an undeclared input with another, and an undeclared tool whose execution id falls outside the kernel's idempotency class; then a legitimate call reusing the first refusal's idEach of the first two leaves a correlation row carrying the toolkit, the tool and the version refused, with decision CHECK_FAILED, no execution and no report secret; the unkeyable one is answered and recorded nowhere; the tenant's audit head is unmoved across all three; and the reused id grants, so a refusal row is not a tombstone

GC7, GC8, GC10, GC12 and GC13 are re-run as part of this unit's evidence rather than afterwards, and what they establish is that nothing old broke; GC14 and GC17 are extended above, because as written neither could detect the defects this unit exists to fix. Provider-specific proof, the live half of the hook contract and every claim about how a real Arcade deployment behaves are not covered by these rows and stay open until a separately authorized trial.

Assistant

Implemented for #330 by the design in docs/superpowers/specs/2026-09-19-assistant-design.md and the plan in docs/superpowers/plans/2026-09-19-assistant.md. The engine's rows are tests/test_assistant_client.py, tests/test_assistant_turns.py, tests/test_assistant_tools.py and tests/test_assistant_loop.py; CI never calls a model, a scripted transport replays every provider answer.

IDSetup/actionRequired result
AS1A private configuration without an assistant block; with one; with the key in the environment, in the block, in both, in neither; a tenant with the switch offWithout the block runtime.assistant is None and assistant_enabled is false; with it the caps and model load and the key appears in no repr; boot merges the key from EXECBOUND_ASSISTANT_API_KEY when the block carries none, refuses both and neither naming the variable and never the value, and scrubs it; a turn for a tenant whose switch is off is refused ASSISTANT_OFF; an ADMIN sets the switch with a reason and it is audited, a no-op and an approver are refused
AS2The runtime role against assistant_turns; a person's turns read by another person and by an agent principalINSERT, UPDATE and DELETE are refused; every write goes through a definer function that checks the actor owns the turn; another person reads nothing and cannot step the turn; an agent can own no turn
AS3An approver with one scoped pending request and one execution outside the scope; the administratorThe approver's list holds the scoped request only; its detail carries the frozen intent, the facts with sources, the approval with its deadline and the kernel's decision events; the other execution is found: false; the administrator lists both
AS4Every tool called with an unknown name, a missing or extra argument, a wrong type, a bad uuid or a value outside its boundsRefused as UNKNOWN_TOOL or INVALID_ARGUMENTS before any read; every tool's wire schema is strict with no additional properties
AS5An agent label carrying instructions; a scripted answer carrying a Markdown image, a link and an id no tool returnedThe label reaches the model inside the untrusted envelope only; the answer is stored as text and cites only the id a tool returned
AS6A credential-shaped string in a tenant label read by a toolThe turn fails REFUSED_SECRET, is audited, and the transport recorded nothing carrying the string
AS7An explanation of an approval whose scripted answer recommends a decision; one that explainsThe first is FAILED/RECOMMENDATION with no answer stored; the second completes citing the approval; an approval the person cannot read is refused before any model call
AS8A completed turn read back, retired at 29 days 23 hours and at 30 daysSteps, answer, cited ids, tools and token counts are whole; retirement nulls the text at thirty days, keeps the counts, and is an ADMIN's call; assistant.asked carries the turn id, tools, counts and model and no question or answer
AS9Nine tool calls; output past the token ceiling; a provider slower than the turn's limit; a rate limit then success; a bad request; a turn left running for ninety seconds; the hourly and daily capsTOOL_LIMIT, TOKEN_LIMIT, TIME_LIMIT each once with nothing retried past the bound; one retry for the rate limit and none for the bad request; ABANDONED persisted on the first read; PERSON_LIMIT and TENANT_LIMIT counted in the creating transaction
AS10A console with no assistant block; with one and the tenant switch off; with one and the switch onWithout the block every assistant address answers 404; with it and the switch off a turn is refused ASSISTANT_OFF and the list answers empty; with the switch on a turn is created and answered
AS11Every assistant route with a bearer credential, with no session, and with another person's turn id403 and audited for the credential; a typed 401 in JSON for no session, even to a browser; 404 for another person's turn on the read, the stream and the opened mark
AS12The four constructions of the application: plain, with an arcade block, with the console, with the console and an assistant blockEach serves exactly its inventory; the assistant block adds its seven routes and nothing else, and all seven are session routes
AS13A turn watched as it gains steps, one already finished, and one still running at the stream's deadlineOne step event per step, each once, then one done event carrying the ending; a finished turn is one done and no wait; the deadline closes the stream saying what the row says
AS14Every packaged help text against the document it summarisesEach phrase that carries a claim appears in the text and in that document; each text is one bounded paragraph and carries no advice about a decision
AS15The recover sweep over a tenants fileEach tenant's report carries assistant_turns_retired beside its settlements, so retention runs on the schedule that already visits every tenant
AS16A draft written as rules, and a scripted proposal the compiler acceptsThe answer is the rule as the form takes it, its sentence, and its conditions in the words the editor parses, which parse back to the same conditions; no bundle is created or changed; the turn is the asker's own, completed, carrying the rule's sentence
AS17Proposals naming an unknown fact, a value outside its domain, an action the draft does not map, an id the draft already uses, an impossible effect or quorum, and answers that are not a rule at allEach is refused INVALID_PROPOSAL with the field that caused it, the turn reads failed, and nothing is written
AS18The route as an approver; for a bundle written as a document; for a validated bundle; for a bundle of another tenant; with a bad body403 for the approver; the store's own refusal for a bundle with no rules to add to and for one that is not there, neither reaching the model; 400 for a body the model of the request refuses

Spaces (SP1 to SP14)

Separation of ownership and view inside one tenant (design). A space is not isolation: forced row-level security is a second line of defence for tenants and there is no second line for spaces, so SP1 is the whole of the mitigation for a forgotten predicate and takes its parameter list from the migration's own column list.

IDSetup/actionRequired result
SP1Two spaces with rows in every space-carrying table; a member of one reads every list, count, chart, search and detailParametrized over every table the migration touches: nothing of the other space is returned, on any surface, in either role
SP2"All spaces"A tenant administrator sees both; a member of two spaces sees the union of theirs and nothing else; a member of one is offered no "All spaces" entry
SP3Membership restrictsA tenant administrator given a membership is narrowed at once; the ADMIN space role is refused for a HUMAN_APPROVER principal; the last tenant administrator cannot be narrowed away
SP4The current space is a preferenceRemoving a member while their session holds the space refuses the next read; session_set_space refuses another tenant's, an archived-but-unseen and an unknown space alike
SP5SharingAn offered account and an offered bundle are usable by another space and editable, retirable and un-offerable only by the owner; an unoffered one is invisible and a mapping on it is refused at admission
SP6Per-space policy selectionTwo spaces select two bundles; each agent is admitted under its own; a bundle no space selects retires; a bundle a space selects cannot be retired; ACTIVE equals "named by a selection row" after every transition
SP7LimitsA tenant rule counts across spaces; a space rule counts only its own retained rows; an approved candidate is exempt from autonomous and charges total in a space scope as in every other; a missing or doubled qualifier fails validation with its field location
SP8The tenant-wide claimSpace A's unresolved claim refuses space B's execution on the same canonical target with today's conflict and nothing about A on the wire or the page
SP9The archive raceAn archive and a decision run concurrently on real PostgreSQL; exactly one commits; an archived space refuses new work with SPACE_ARCHIVED at the stop's check point; a DISPATCH_COMMITTED execution is not cancelled by an archive
SP10Migration round tripThe default space holds everything; every column is backfilled and every CHECK holds; approvers gain memberships and administrators do not; the selection moves off the control row; downgrade refuses with two spaces and otherwise restores the column, the grant and the index; check_runtime passes; the endpoint and identity contract goldens and every frozen fixture are byte-identical
SP11Console APIThe harness's two sweeps on every route; a member is 403 on every administration route; a cross-tenant or unknown space is 404; /session/space answers the fresh session view; the secret check over every fixture response
SP12The console in ChromiumThe switcher in both schemes at 1280 and 390 px, axe clean with zero CSP or Trusted Types violations; the popover reachable and dismissable by keyboard alone; a switch clearing the cache and re-reading every count; an approval link opening in the space that owns it; a space named with markup rendered as text
SP13ReplayA space candidate rule reconstructs from captures; captures with no recorded space report UNKNOWN_SPACE and stay UNRESOLVED while the baseline still matches; report.json stays deterministic
SP14AuditEvery administration act appends its event under the tenant's one chain and head; the chain is not split; a space switch appends nothing; owner commands and the console write identical payloads

SP12 is tests/test_browser_console_spaces.py, in real Chromium, beside console/src/shell/switcher.test.tsx and console/src/components/spaces/spaces.test.tsx for the components' own behaviour.