Authority revocation + mid-run kill-switch (hold a revoked actor's in-flight runs)#537
Closed
xmap wants to merge 5 commits into
Closed
Authority revocation + mid-run kill-switch (hold a revoked actor's in-flight runs)#537xmap wants to merge 5 commits into
xmap wants to merge 5 commits into
Conversation
First-class authority revocation: remove one principal from a Policy's permitted_principal_ids allow-list. This is the primitive the mid-run kill-switch (later slices) triggers on to hold a revoked actor's in-flight runs. The 6th compensation slice and 2nd set-membership variant (after revoke_tool_from_agent): silently idempotent (no event when the principal is already absent), single-stream append (no cross-BC cascade), required reason via the shared REASON_MAX_LENGTH. evaluate() is unchanged: the shrunk set already denies the removed principal. Symmetric by construction: operates on a bare principal_id, so a human grant and an autonomous agent grant are revoked by the identical path. Slice 1 of the authority-revocation + compensation design (project_authority_revocation_design). Gate-reviewed (naming R1-R6, correctness/security, conventions, design-lock consistency): pass. Co-Authored-By: Claude <noreply@anthropic.com>
Answers "which in-flight runs is an actor behind?" for the
authority-revocation kill-switch: the compensation subscriber (next
slice) looks up a revoked actor's in-flight runs here and holds them.
CORA's first cross-BC projection. One projection folds two streams,
dispatching on event_type (the framework matches on event_type only,
no stream-type scoping):
- RunStarted -> a 'starter' row, actor = the event envelope
principal_id (RunStarted carries no actor in payload)
- DecisionRegistered (context=RunSupervision) -> a 'supervisor' row,
actor = decided_by, run = inputs.run_id
- Run lifecycle -> status for all rows of the run; terminal statuses
fall out of the in-flight partial index
The lookup (RunActorInvolvementLookup + Postgres adapter) reads distinct
Running|Held run_ids for an actor, collapsing the starter/supervisor
double-row. BC-local like RunChannelLookup; not Kernel-wired yet (its
consumer is the next slice). Symmetric: keys on a bare actor UUID, so a
human starter and an agent supervisor are folded by the same code.
Naming (naming-r3): involvement not authority (authz overload),
involvement_kind not role (shadows the Role aggregate). The supervisor
INSERT is guarded on the starter row existing, so a supervision Decision
for a starter-less run creates no phantom in-flight row.
Slice 2 of the authority-revocation design. Gate-reviewed (naming,
correctness, conventions/design-lock, migration-safety): pass, two P2
fixes folded in (starter-guard + supervisor-SQL unit assertion).
Co-Authored-By: Claude <noreply@anthropic.com>
… runs)
The mid-run compensation centerpiece: on a Trust PolicyGrantRevoked, hold
every in-flight run the revoked principal is behind, so a withdrawn actor
(human or agent) cannot leave work running unattended. Holding is
reversible (Held, not aborted): the run lands in a defined state a human
can take over or resume.
AuthorityRevocationHolderSubscriber is a deterministic Run-BC Reaction:
- reacts to PolicyGrantRevoked; looks up the revoked principal's
in-flight runs via proj_run_actor_involvement (Slice 2);
- per run, records DecisionRegistered(context=AuthorityRevocation,
choice=Hold) at a deterministic uuid5 id, then issues HoldRun,
reusing the RunSupervisor hold path;
- acts as SYSTEM_PRINCIPAL_ID, NOT a seeded Agent: the kill-switch is
plain infrastructure, not a decider, the strongest form of the "no
special machinery" thesis (a human's grant is held by the identical
path). Off by default via settings.authority_revocation_holder_enabled.
Adds DECISION_CONTEXT_AUTHORITY_REVOCATION, run/_subscribers.py +
composition-root wiring, a subscriber-completeness fitness test, the tach
edge cora.run -> cora.decision.aggregates (compose DecisionRegistered,
mirroring the Agent reactions), and the E4 integration scenario, the
T-ASE paper's centerpiece.
Gate-reviewed (naming, correctness/concurrency, conventions/design-lock,
cross-BC reaction safety); crash-window confirmed closed; fixes folded in:
- P0: a Reaction needs a projection_bookmarks seed row or its advance
loop wedges (MissingBookmarkError) and silently never fires. Adds the
seed migration + test_reaction_bookmark_seeded fitness test (also
documenting the same latent gap in the 3 pre-existing agent LLM
reactions) + a worker-driven regression test.
- P1: a failed kill-switch hold (SYSTEM lacking HoldRun under
TrustAuthorize) now logs at ERROR with remediation, not a quiet warn.
- P2: namespace rename for grep symmetry + a comment on why the
ConcurrencyError-continue is safe.
Slice 3 of the authority-revocation design.
Co-Authored-By: Claude <noreply@anthropic.com>
… effect) Companion to test_handler_authorizes (which proves every feature handler CALLS + ENFORCES authorize). This adds the stronger ORDERING property that call-existence does not: in a handler that authorizes and appends directly in bind(), the authorize() call must precede the first state-writing event_store.append / append_streams. An authorize that ran after the append would have already let the effect land. This is the codified non-bypass invariant (the T-ASE paper's E2: "no facility-state effect without a prior authorization"). A positional AST check (first authorize lineno < first append lineno) over every handler with a direct authorize+append pair, plus a drift-catcher asserting >=10 such pairs exist so the check can't silently pass on an empty set. Reuses test_handler_authorizes's discovery helpers. Slice 4 of the authority-revocation design (tests only). Co-Authored-By: Claude <noreply@anthropic.com>
Coverage reportClick to see where and how coverage changed
The report is truncated to 25 files out of 26. To see the full report, please visit the workflow summary page. This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
…metry tests Evaluation evidence for the T-ASE paper, both against real Postgres: - perf harness: authorization-decision latency (~0.3us median), replay throughput (~110k events/s), and kill-switch propagation across K=1,5,20 concurrent supervised runs (2.7-29ms; per-run cost falls as the batch amortizes; all K held). Doubles as a regression test with loose sanity bounds, and emits a JSON summary for the paper's evaluation table. - multi-scenario tests under a real TrustAuthorize gate: escalation denied symmetrically across actor kind, human-and-agent same-grant same-allow, and replay recovers the correct distinct actor (operator / agent / system holder) for each effect from the one log. These demonstrate the actor-symmetry principle at the deny boundary and via replay attribution, beyond the single happy-path kill-switch scenario. No production code changes. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First-class authority revocation wired to safe mid-run compensation: when a principal's grant is revoked, every in-flight run that principal is behind (started or supervises) is held into a defined, reversible state. Works identically for a human operator and an autonomous agent, because it operates on a bare
principal_idend to end.Four independently-reviewed slices, each committed on its own:
931ac14e87) - Trustrevoke_grantprimitive. NewPolicyGrantRevokedevent removes one principal from a Policy'spermitted_principal_ids. The 6th compensation slice, 2nd set-membership variant (afterrevoke_tool_from_agent): silently idempotent, required reason, single-stream append, no cascade.evaluate()unchanged (the shrunk set already denies).6bdfd8ad90) -proj_run_actor_involvementprojection + lookup. CORA's first cross-BC projection: folds Run lifecycle +RunSupervisionDecision events into an actor -> in-flight-runs index (starter = RunStarted envelope principal UNION supervisor = RunSupervision decider). Backs the kill-switch's "which runs is this actor behind?" lookup.49098c9d2e) - the kill-switch.AuthorityRevocationHolderSubscriber, a deterministic Run-BC Reaction: onPolicyGrantRevokedit holds the revoked principal's in-flight runs (recordsDecisionRegistered(context=AuthorityRevocation, choice=Hold), thenHoldRun, reusing the RunSupervisor hold path). Acts asSYSTEM_PRINCIPAL_ID(plain infrastructure, not a seeded Agent). Off by default viaauthority_revocation_holder_enabled.025d1971e3) - non-bypass ordering fitness test. Pins that authorize precedes the first state-writing append across every handler with a direct authorize+append pair.Compensation is HOLD (reversible), never abort: a human can take over or resume the parked run.
Design + review
project_authority_revocation_design(auto-memory).projection_bookmarksseed row or it silently never fires) that green tests missed; fixed with a seed migration + a newtest_reaction_bookmark_seededfitness test (which also documents the same latent gap in the 3 pre-existing agent reactions) + a worker-driven regression test. P1 (loud authz-deny on the safety path) and a Tach cross-BC edge were also folded in.Deferred (follow-up PR)
withdraw_actorfan-out (revoke a principal from every policy at once) + a policy-principals projection. Always scoped as later.Test plan
🤖 Generated with Claude Code