@consiliency/agent-board-schema 1.12.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/fixtures/already-applied.json +160 -0
  2. package/migrations/20260731000100_agent_board_agentsdir_column_scope.sql +88 -0
  3. package/migrations/20260801000100_agent_board_leaveidem_terminal_noop.sql +317 -0
  4. package/migrations/20260802000100_agent_board_rolereset_reentry_clamp.sql +237 -0
  5. package/migrations/20260803000100_agent_board_rowid_total_order.sql +1201 -0
  6. package/migrations/20260804000100_agent_board_hygiene_actor_binding_rls.sql +229 -0
  7. package/migrations/20260806000100_agent_board_consent_substrate.sql +339 -0
  8. package/migrations/20260807000100_agent_board_consent_emission.sql +619 -0
  9. package/migrations/20260808000100_agent_board_rejectsem.sql +145 -0
  10. package/migrations/20260809000100_agent_board_consent_gating.sql +492 -0
  11. package/migrations/20260810000100_agent_board_consent_locking.sql +485 -0
  12. package/migrations/20260811000100_agent_board_retention_metadata_revoke.sql +23 -0
  13. package/migrations/20260812000100_agent_board_pubexec_class_revoke.sql +54 -0
  14. package/migrations/20260813000100_agent_board_invitepolicy_block.sql +420 -0
  15. package/migrations/20260814000100_agent_board_obsgate_thread_post_policy.sql +239 -0
  16. package/migrations/20260815000100_agent_board_obsgate2_authoring_gates.sql +287 -0
  17. package/migrations/20260816000100_agent_board_obsgate3_task_lifecycle.sql +118 -0
  18. package/migrations/20260817000100_agent_board_obsgate4_claims_lifecycle.sql +250 -0
  19. package/migrations/20260818000100_agent_board_fleet_machines_rls_correlation.sql +43 -0
  20. package/migrations/20260819000100_agent_board_resolve_noop_retire.sql +166 -0
  21. package/migrations/20260820000100_agent_board_roomdeleg_lane_b.sql +206 -0
  22. package/migrations/20260821000100_agent_board_claimgate_claimkey.sql +372 -0
  23. package/migrations/20260822000100_agent_board_mint_harden.sql +710 -0
  24. package/migrations/20260822000200_agent_board_mint_harden_fingerprint_backfill.sql +207 -0
  25. package/migrations/20260823000100_agent_board_fleet_machine_hijack.sql +106 -0
  26. package/migrations/20260824000100_agent_board_claimgate_shadow.sql +443 -0
  27. package/migrations/20260825000100_agent_board_claimgate_enforce.sql +660 -0
  28. package/migrations/20260826000100_agent_board_fleet_machine_reactivation.sql +514 -0
  29. package/migrations/20260828000100_agent_board_delegation_cap_ceiling.sql +474 -0
  30. package/migrations/20260829000100_agent_board_fleet_machine_reclaim.sql +432 -0
  31. package/migrations/20260830000100_agent_board_delegation_sole_writer.sql +41 -0
  32. package/migrations/20260831000100_agent_board_fleet_domain_reclaim.sql +389 -0
  33. package/migrations/20260901000100_agent_board_atomic_fleet_onboarding.sql +1496 -0
  34. package/migrations/20260901000110_agent_board_fleet_onboarding_ceiling_lock.sql +604 -0
  35. package/migrations/20260901000120_agent_board_human_revocation_evidence_completeness.sql +78 -0
  36. package/migrations/20260901000130_agent_board_fedsession_human_revocation_gate.sql +74 -0
  37. package/migrations/20260901000140_agent_board_fedsession_board_owner_lock_order.sql +102 -0
  38. package/migrations/20260902000100_agent_board_outbox_wake_ordering.sql +184 -0
  39. package/migrations/20260903000100_agent_board_enroll_identity_board_owner_lock_order.sql +604 -0
  40. package/migrations/20260903000200_agent_board_fedsession_provider_absence_gate.sql +177 -0
  41. package/migrations/20260904000100_agent_board_wake_recovery_surface.sql +155 -0
  42. package/package.json +1 -1
  43. package/schema-manifest.json +363 -3
  44. package/src/tooling.test.mjs +113 -2
  45. package/tests/agent_board_actorspoof.test.sql +349 -0
  46. package/tests/agent_board_agentsdir.test.sql +154 -0
  47. package/tests/agent_board_atomic_fleet_onboarding.test.sql +623 -0
  48. package/tests/agent_board_atomic_fleet_onboarding_backfill.test.sql +142 -0
  49. package/tests/agent_board_atomic_fleet_onboarding_runtime.test.sql +1250 -0
  50. package/tests/agent_board_authbound_boundary.test.sql +9 -1
  51. package/tests/agent_board_claimgate_enforce.test.sql +464 -0
  52. package/tests/agent_board_claimgate_predicate.test.sql +360 -0
  53. package/tests/agent_board_claimkey.test.sql +229 -0
  54. package/tests/agent_board_consent_backfill.test.sql +48 -0
  55. package/tests/agent_board_consent_emission.test.sql +601 -0
  56. package/tests/agent_board_consent_gating.test.sql +489 -0
  57. package/tests/agent_board_consent_substrate.test.sql +720 -0
  58. package/tests/agent_board_delegation_cap_ceiling.test.sql +303 -0
  59. package/tests/agent_board_delegation_enforcement.test.sql +55 -6
  60. package/tests/agent_board_delegation_policies.test.sql +107 -1
  61. package/tests/agent_board_enroll_federated_identity.test.sql +3 -3
  62. package/tests/agent_board_enroll_pop_binding.test.sql +13 -3
  63. package/tests/agent_board_enrollment_audit_content.test.sql +11 -7
  64. package/tests/agent_board_federated_lock_order_class.test.sql +390 -0
  65. package/tests/agent_board_fedsession.test.sql +51 -5
  66. package/tests/agent_board_fedsession_provider_absence_gate.test.sql +170 -0
  67. package/tests/agent_board_fleet_domain_reclaim.test.sql +419 -0
  68. package/tests/agent_board_fleet_machine_hijack.test.sql +190 -0
  69. package/tests/agent_board_fleet_machine_reactivation.test.sql +426 -0
  70. package/tests/agent_board_fleet_machine_reclaim.test.sql +509 -0
  71. package/tests/agent_board_fleet_machines_rls.test.sql +79 -0
  72. package/tests/agent_board_fleet_policy.test.sql +2 -2
  73. package/tests/agent_board_grants_hygiene.test.sql +408 -0
  74. package/tests/agent_board_identity_providers.test.sql +9 -0
  75. package/tests/agent_board_inbox_reads.test.sql +45 -31
  76. package/tests/agent_board_inbox_rpcs.test.sql +44 -34
  77. package/tests/agent_board_invitepolicy.test.sql +205 -0
  78. package/tests/agent_board_leaveidem.test.sql +194 -0
  79. package/tests/agent_board_mint_fleet_agent_session.test.sql +10 -10
  80. package/tests/agent_board_mint_harden.test.sql +303 -0
  81. package/tests/agent_board_mint_harden_backfill.test.sql +95 -0
  82. package/tests/agent_board_obsgate.test.sql +178 -0
  83. package/tests/agent_board_obsgate2.test.sql +153 -0
  84. package/tests/agent_board_obsgate3.test.sql +170 -0
  85. package/tests/agent_board_obsgate4.test.sql +196 -0
  86. package/tests/agent_board_outbox_wake_ordering.test.sql +452 -0
  87. package/tests/agent_board_portal_offline_invariant.test.sql +14 -3
  88. package/tests/agent_board_pubexec_class.test.sql +53 -0
  89. package/tests/agent_board_rejectsem.test.sql +271 -0
  90. package/tests/agent_board_resigning_refresh.test.sql +1 -1
  91. package/tests/agent_board_resolve_noop_retire.test.sql +71 -0
  92. package/tests/agent_board_retention_metadata_acl.test.sql +53 -0
  93. package/tests/agent_board_revoke_agent_subtree.test.sql +3 -3
  94. package/tests/agent_board_rolereset.test.sql +351 -0
  95. package/tests/agent_board_roomdeleg.test.sql +5 -3
  96. package/tests/agent_board_roomdeleg_lane_b.test.sql +144 -0
  97. package/tests/agent_board_roomlink.test.sql +27 -11
  98. package/tests/agent_board_rowid.test.sql +1274 -0
  99. package/tests/agent_board_signature_rls_reads.test.sql +3 -3
  100. package/tests/agent_board_signed_write_gateway.test.sql +3 -3
  101. package/tests/agent_board_signing_challenge_pending_cap.test.sql +2 -2
  102. package/tests/agent_board_signing_challenge_reaper.test.sql +2 -2
  103. package/tests/agent_board_signing_proof_rpcs.test.sql +4 -4
  104. package/tests/agent_board_signing_store.test.sql +6 -6
  105. package/tests/agent_board_wake_recovery.test.sql +756 -0
  106. package/tests/fixtures/isolation-substrate.sql +93 -0
  107. package/tests/fixtures/test-prereqs.sql +33 -0
  108. package/tests/isolation/expected/atomic_fedsession_fleet_lock_order.out +79 -0
  109. package/tests/isolation/expected/atomic_fedsession_human_revoke.out +87 -0
  110. package/tests/isolation/expected/atomic_fleet_authority_races.out +469 -0
  111. package/tests/isolation/expected/atomic_fleet_binding_revoke.out +103 -0
  112. package/tests/isolation/expected/atomic_fleet_board_archive.out +117 -0
  113. package/tests/isolation/expected/atomic_fleet_human_fresh_revoke.out +136 -0
  114. package/tests/isolation/expected/atomic_fleet_identity_disposition.out +61 -0
  115. package/tests/isolation/expected/atomic_fleet_lifecycle_races.out +1479 -0
  116. package/tests/isolation/expected/atomic_fleet_onboarding.out +75 -0
  117. package/tests/isolation/expected/atomic_fleet_subtree_lock_order.out +60 -0
  118. package/tests/isolation/expected/ceiling_setter_lock_before_upsert.out +95 -0
  119. package/tests/isolation/expected/claimgate_ownerless_quota.out +36 -0
  120. package/tests/isolation/expected/claimkey_renew_race.out +38 -0
  121. package/tests/isolation/expected/consent_approval_lock.out +43 -0
  122. package/tests/isolation/expected/consent_emit_actual_state.out +37 -0
  123. package/tests/isolation/expected/domain_reclaim_revoke_race.out +40 -0
  124. package/tests/isolation/expected/enroll_identity_board_owner_lock_order.out +55 -0
  125. package/tests/isolation/expected/fedsession_provider_absent_owner_lock.out +37 -0
  126. package/tests/isolation/expected/onboarding_ceiling_lock_order.out +123 -0
  127. package/tests/isolation/expected/outbox_wake_ordering.out +86 -0
  128. package/tests/isolation/expected/reactivate_revoke_race.out +80 -0
  129. package/tests/isolation/expected/reclaim_revoke_race.out +40 -0
  130. package/tests/isolation/expected/wake_recovery_late_commit.out +89 -0
  131. package/tests/isolation/specs/atomic_fedsession_fleet_lock_order.spec +117 -0
  132. package/tests/isolation/specs/atomic_fedsession_human_revoke.spec +66 -0
  133. package/tests/isolation/specs/atomic_fleet_authority_races.spec +235 -0
  134. package/tests/isolation/specs/atomic_fleet_binding_revoke.spec +127 -0
  135. package/tests/isolation/specs/atomic_fleet_board_archive.spec +134 -0
  136. package/tests/isolation/specs/atomic_fleet_human_fresh_revoke.spec +142 -0
  137. package/tests/isolation/specs/atomic_fleet_identity_disposition.spec +118 -0
  138. package/tests/isolation/specs/atomic_fleet_lifecycle_races.spec +244 -0
  139. package/tests/isolation/specs/atomic_fleet_onboarding.spec +106 -0
  140. package/tests/isolation/specs/atomic_fleet_subtree_lock_order.spec +147 -0
  141. package/tests/isolation/specs/ceiling_setter_lock_before_upsert.spec +117 -0
  142. package/tests/isolation/specs/claimgate_ownerless_quota.spec +77 -0
  143. package/tests/isolation/specs/claimkey_renew_race.spec +67 -0
  144. package/tests/isolation/specs/consent_approval_lock.spec +98 -0
  145. package/tests/isolation/specs/consent_emit_actual_state.spec +79 -0
  146. package/tests/isolation/specs/domain_reclaim_revoke_race.spec +88 -0
  147. package/tests/isolation/specs/enroll_identity_board_owner_lock_order.spec +117 -0
  148. package/tests/isolation/specs/fedsession_provider_absent_owner_lock.spec +63 -0
  149. package/tests/isolation/specs/onboarding_ceiling_lock_order.spec +183 -0
  150. package/tests/isolation/specs/outbox_wake_ordering.spec +202 -0
  151. package/tests/isolation/specs/reactivate_revoke_race.spec +115 -0
  152. package/tests/isolation/specs/reclaim_revoke_race.spec +94 -0
  153. package/tests/isolation/specs/wake_recovery_late_commit.spec +207 -0
  154. package/tests/seeds/20260806000100_agent_board_consent_substrate.sql.pre.sql +19 -0
  155. package/tests/seeds/20260822000200_agent_board_mint_harden_fingerprint_backfill.sql.pre.sql +39 -0
  156. package/tests/seeds/20260901000100_agent_board_atomic_fleet_onboarding.sql.pre.sql +110 -0
@@ -0,0 +1,235 @@
1
+ # Consiliency/message-board#162 — an expired complete replay must serialize
2
+ # against the live membership and creator-authority rows that justify its narrow
3
+ # expiry/exhaustion bypass. Each public invalidator is exercised in both orders.
4
+ #
5
+ # Consiliency/message-board#186 adds set_board_authority_ceiling as a fourth
6
+ # invalidator. It is the one that cannot be covered by a row lock: the invalidating
7
+ # state is the INSERT of a first ceiling row, and until it commits there is no row
8
+ # to lock — absence is the normal default-owner state (_grant_within_ceiling
9
+ # coalesces a missing row to owner). Its omission from this hand-written
10
+ # invalidator list is exactly why the race shipped uncovered.
11
+ #
12
+ # WHAT THIS PINS (a TRACE plus a value): the two transactions SERIALIZE — the
13
+ # second one to reach the ceiling prints `<waiting ...>` and, when the ceiling wins,
14
+ # the expired replay COMPLETES with `ERROR: fleet onboarding invite is not usable`.
15
+ # It pins the PROPERTY, not any one instrument; read the mutation results before
16
+ # attributing the `<waiting ...>` to the advisory lock.
17
+ #
18
+ # MUTATION RESULTS (all three run):
19
+ # * remove ONLY `perform agent_board._lock_board_authority_ceiling(...)` from
20
+ # onboard_federated_fleet_agent -> this spec still PASSES, byte-identical. The
21
+ # advisory lock is NOT what serializes these two today.
22
+ # * remove ONLY the `for update` from onboarding's `select ... from boards` ->
23
+ # still PASSES (the advisory lock takes over; atomic_fleet_board_archive fails,
24
+ # which is that spec's business).
25
+ # * remove BOTH -> FAILS: no `<waiting ...>`, and retry_onboard returns
26
+ # reused/reused/reused under a committed observer ceiling — a `member` grant
27
+ # authorized by an authority the ceiling forbids.
28
+ # The load-bearing serializer today is onboarding's boards FOR UPDATE against the
29
+ # FOR KEY SHARE lock the ceiling INSERT takes on the same boards row through
30
+ # board_authority_ceilings' FK. The advisory lock is a proven-redundant second
31
+ # defense that becomes load-bearing if that incidental lock ever moves.
32
+ #
33
+ # NOT COVERED HERE: the ABBA deadlock between these same two functions (onboarding's
34
+ # advisory-vs-boards ORDER against the setter's advisory-then-FK order). Every
35
+ # permutation below runs each RPC to completion in one step, so neither can be
36
+ # paused between its two acquisitions and no cycle can form -- this spec passed
37
+ # while that deadlock was live. It is pinned instead by the catalog ordering
38
+ # assertion 'atomic onboarding takes the ceiling advisory lock BEFORE the boards row
39
+ # lock' in agent_board_atomic_fleet_onboarding.test.sql, which was verified to fail
40
+ # exactly when a two-session probe reproduces `deadlock detected`.
41
+
42
+ setup
43
+ {
44
+ -- The ceiling setter is owner-gated on current_agent_id(), so the creator needs a
45
+ -- resolvable auth identity to act as itself; the other invalidators run as
46
+ -- service_role and are unaffected by this linkage.
47
+ insert into auth.users (id) values ('16250000-0000-0000-0000-0000000000a1');
48
+ insert into agent_board.agents(id, agent_type, kind, handle, display_name, status, auth_user_id)
49
+ values ('16250000-0000-0000-0000-000000000001', 'agent', 'agent',
50
+ 'atomic-authority-owner', 'Atomic Authority Owner', 'active',
51
+ '16250000-0000-0000-0000-0000000000a1');
52
+
53
+ select count(*) from agent_board.create_board(
54
+ '16250000-0000-0000-0000-000000000001',
55
+ 'atomic-authority-board', 'Atomic Authority Board',
56
+ 'Consiliency/message-board#162 authority isolation proof',
57
+ jsonb_build_object(), 'private'
58
+ );
59
+ insert into agent_board.fleet_trust_domains(
60
+ id, slug, display_name, domain_kind, owner_agent_id, lifecycle_state, approved_at
61
+ ) values (
62
+ '16250000-0000-0000-0000-000000000002', 'atomic-authority-domain',
63
+ 'Atomic Authority Domain', 'tailnet', '16250000-0000-0000-0000-000000000001',
64
+ 'active', now()
65
+ );
66
+ insert into agent_board.fleet_machines(
67
+ id, trust_domain_id, owner_agent_id, machine_label, machine_fingerprint,
68
+ lifecycle_state, enrollment_source, enrolled_at
69
+ ) values (
70
+ '16250000-0000-0000-0000-000000000003',
71
+ '16250000-0000-0000-0000-000000000002',
72
+ '16250000-0000-0000-0000-000000000001',
73
+ 'claw', 'sha256:atomic-authority-machine', 'active', 'portal', now()
74
+ );
75
+ insert into agent_board.identity_providers(
76
+ issuer, audience, supported, policy_approved,
77
+ allowed_scopes, allowed_board_scopes, allowed_agent_kinds
78
+ ) values (
79
+ 'https://atomic-authority.test', 'message-board', true, true,
80
+ ARRAY['board:send'], ARRAY[]::text[], ARRAY['agent']
81
+ );
82
+ select count(*) from agent_board.create_fleet_onboarding_invite(
83
+ (select id from agent_board.boards where slug='atomic-authority-board'),
84
+ '16250000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
85
+ 'message-board', 'sha256:atomic-authority-repo', 'worker', 'phase_worker',
86
+ 'atomic-authority-agent', 1, now() + interval '1 hour',
87
+ '16250000-0000-0000-0000-000000000001', jsonb_build_object()
88
+ );
89
+ select count(*) from agent_board.onboard_federated_fleet_agent(
90
+ 'https://atomic-authority.test', 'message-board', 'human-authority',
91
+ jsonb_build_object('iss','https://atomic-authority.test','aud','message-board','sub','human-authority'),
92
+ 'atomic-authority-agent',
93
+ 'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
94
+ 'raw_ed25519_base64url',
95
+ 'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
96
+ true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
97
+ (select code from agent_board.board_invite_tokens where created_by_agent_id='16250000-0000-0000-0000-000000000001'),
98
+ (select id from agent_board.boards where slug='atomic-authority-board'),
99
+ '16250000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
100
+ 'message-board', 'sha256:atomic-authority-repo', 'worker', 'phase_worker',
101
+ 'atomic-authority-agent'
102
+ );
103
+ update agent_board.board_invite_tokens
104
+ set expires_at = now() - interval '1 minute'
105
+ where created_by_agent_id='16250000-0000-0000-0000-000000000001';
106
+ }
107
+
108
+ teardown
109
+ {
110
+ delete from agent_board.fleet_machine_agents where machine_id='16250000-0000-0000-0000-000000000003';
111
+ delete from agent_board.fleet_repo_scopes where harness_registration_id in (
112
+ select id from agent_board.fleet_harness_registrations where machine_id='16250000-0000-0000-0000-000000000003'
113
+ );
114
+ delete from agent_board.fleet_harness_registrations where machine_id='16250000-0000-0000-0000-000000000003';
115
+ delete from agent_board.board_invite_redemptions where invite_id in (
116
+ select id from agent_board.board_invite_tokens where created_by_agent_id='16250000-0000-0000-0000-000000000001'
117
+ );
118
+ truncate agent_board.fleet_onboarding_invite_bounds;
119
+ delete from agent_board.board_invite_tokens where created_by_agent_id='16250000-0000-0000-0000-000000000001';
120
+ delete from agent_board.board_authority_ceilings where board_id in (
121
+ select id from agent_board.boards where slug='atomic-authority-board'
122
+ );
123
+ delete from agent_board.board_memberships where board_id in (
124
+ select id from agent_board.boards where slug='atomic-authority-board'
125
+ );
126
+ delete from agent_board.enrollment_federation_audit where agent_id in (
127
+ select id from agent_board.agents where handle='atomic-authority-agent'
128
+ );
129
+ delete from agent_board.agent_signing_keys where agent_id in (
130
+ select id from agent_board.agents where handle='atomic-authority-agent'
131
+ );
132
+ delete from agent_board.agents where handle='atomic-authority-agent';
133
+ delete from agent_board.boards where slug='atomic-authority-board';
134
+ delete from agent_board.fleet_machines where id='16250000-0000-0000-0000-000000000003';
135
+ delete from agent_board.fleet_trust_domains where id='16250000-0000-0000-0000-000000000002';
136
+ delete from agent_board.agents where id='16250000-0000-0000-0000-000000000001';
137
+ delete from auth.users where id='16250000-0000-0000-0000-0000000000a1';
138
+ delete from agent_board.identity_providers where issuer='https://atomic-authority.test';
139
+ }
140
+
141
+ session "retry"
142
+ step "retry_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
143
+ step "retry_onboard" {
144
+ select identity_disposition, membership_disposition, binding_disposition
145
+ from agent_board.onboard_federated_fleet_agent(
146
+ 'https://atomic-authority.test', 'message-board', 'human-authority',
147
+ jsonb_build_object('iss','https://atomic-authority.test','aud','message-board','sub','human-authority','iat',200),
148
+ 'atomic-authority-agent',
149
+ 'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
150
+ 'raw_ed25519_base64url',
151
+ 'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
152
+ true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
153
+ (select code from agent_board.board_invite_tokens where created_by_agent_id='16250000-0000-0000-0000-000000000001'),
154
+ (select id from agent_board.boards where slug='atomic-authority-board'),
155
+ '16250000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
156
+ 'message-board', 'sha256:atomic-authority-repo', 'worker', 'phase_worker',
157
+ 'atomic-authority-agent'
158
+ );
159
+ }
160
+ step "retry_commit" { COMMIT; }
161
+
162
+ session "invalidator"
163
+ step "invalidator_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
164
+ step "remove_member" {
165
+ select membership_state from agent_board.update_board_membership(
166
+ (select id from agent_board.boards where slug='atomic-authority-board'),
167
+ (select id from agent_board.agents where handle='atomic-authority-agent'),
168
+ 'removed', jsonb_build_object('reason','isolation member removal')
169
+ );
170
+ }
171
+ step "revoke_creator" {
172
+ select status from agent_board.revoke_agent(
173
+ '16250000-0000-0000-0000-000000000001', 'isolation creator revoke'
174
+ );
175
+ }
176
+ step "demote_creator" {
177
+ select role from agent_board.grant_board_role(
178
+ (select id from agent_board.boards where slug='atomic-authority-board'),
179
+ '16250000-0000-0000-0000-000000000001', 'observer',
180
+ '16250000-0000-0000-0000-000000000001'
181
+ );
182
+ }
183
+ step "invalidator_commit" { COMMIT; }
184
+
185
+ # The ceiling setter is owner-gated on current_agent_id(), so this session runs as
186
+ # the creator under `authenticated` (NOT service_role, whose current_agent_id() is
187
+ # null and which the owner gate rejects). Separate session because that role/claim
188
+ # setup differs from the service_role invalidator above.
189
+ session "ceiling"
190
+ step "ceiling_begin" {
191
+ BEGIN;
192
+ SET LOCAL ROLE authenticated;
193
+ SET LOCAL "request.jwt.claim.role" = 'authenticated';
194
+ SET LOCAL "request.jwt.claim.sub" = '16250000-0000-0000-0000-0000000000a1';
195
+ }
196
+ step "set_ceiling" {
197
+ select ceiling_max_grantable_role from agent_board.set_board_authority_ceiling(
198
+ (select id from agent_board.boards where slug='atomic-authority-board'),
199
+ 'observer'
200
+ );
201
+ }
202
+ step "ceiling_commit" { COMMIT; }
203
+
204
+ session "check"
205
+ step "check" {
206
+ select
207
+ (select status from agent_board.agents where id='16250000-0000-0000-0000-000000000001') as creator_status,
208
+ (select role || '/' || membership_state from agent_board.board_memberships
209
+ where board_id=(select id from agent_board.boards where slug='atomic-authority-board')
210
+ and agent_id='16250000-0000-0000-0000-000000000001'
211
+ and membership_state in ('joined','subscribed')) as creator_authority,
212
+ (select membership_state from agent_board.board_memberships
213
+ where board_id=(select id from agent_board.boards where slug='atomic-authority-board')
214
+ and agent_id=(select id from agent_board.agents where handle='atomic-authority-agent')
215
+ order by (membership_state in ('joined','subscribed')) desc, created_at desc, id desc limit 1) as member_state,
216
+ (select max_grantable_role from agent_board.board_authority_ceilings
217
+ where board_id=(select id from agent_board.boards where slug='atomic-authority-board')) as board_ceiling;
218
+ }
219
+
220
+ # Retry wins, then the invalidator commits.
221
+ permutation "retry_begin" "retry_onboard" "invalidator_begin" "remove_member" "retry_commit" "invalidator_commit" "check"
222
+ permutation "retry_begin" "retry_onboard" "invalidator_begin" "revoke_creator" "retry_commit" "invalidator_commit" "check"
223
+ permutation "retry_begin" "retry_onboard" "invalidator_begin" "demote_creator" "retry_commit" "invalidator_commit" "check"
224
+
225
+ # The invalidator wins; the expired retry must fail closed.
226
+ permutation "invalidator_begin" "remove_member" "retry_begin" "retry_onboard" "invalidator_commit" "retry_commit" "check"
227
+ permutation "invalidator_begin" "revoke_creator" "retry_begin" "retry_onboard" "invalidator_commit" "retry_commit" "check"
228
+ permutation "invalidator_begin" "demote_creator" "retry_begin" "retry_onboard" "invalidator_commit" "retry_commit" "check"
229
+
230
+ # #186 — the ceiling INSERT, in both orders. Retry first: the ceiling setter must
231
+ # WAIT on the advisory lock rather than slip its insert under a completed
232
+ # authorization. Ceiling first: the retry must WAIT and then fail closed against the
233
+ # committed observer ceiling instead of reading the pre-insert absence as owner.
234
+ permutation "retry_begin" "retry_onboard" "ceiling_begin" "set_ceiling" "retry_commit" "ceiling_commit" "check"
235
+ permutation "ceiling_begin" "set_ceiling" "retry_begin" "retry_onboard" "ceiling_commit" "retry_commit" "check"
@@ -0,0 +1,127 @@
1
+ # Consiliency/message-board#162 — exact binding revocation and composed retry use
2
+ # the same machine-before-binding order in both winner orderings. A committed
3
+ # tombstone is never replaced by a new active binding.
4
+
5
+ setup
6
+ {
7
+ insert into agent_board.agents(id, agent_type, kind, handle, display_name, status)
8
+ values ('16230000-0000-0000-0000-000000000001', 'agent', 'agent',
9
+ 'atomic-binding-owner', 'Atomic Binding Owner', 'active');
10
+ select count(*) from agent_board.create_board(
11
+ '16230000-0000-0000-0000-000000000001', 'atomic-binding-board',
12
+ 'Atomic Binding Board', 'Consiliency/message-board#162 isolation proof',
13
+ jsonb_build_object(), 'private'
14
+ );
15
+ insert into agent_board.fleet_trust_domains(
16
+ id, slug, display_name, domain_kind, owner_agent_id, lifecycle_state, approved_at
17
+ ) values (
18
+ '16230000-0000-0000-0000-000000000002', 'atomic-binding-domain',
19
+ 'Atomic Binding Domain', 'tailnet', '16230000-0000-0000-0000-000000000001',
20
+ 'active', now()
21
+ );
22
+ insert into agent_board.fleet_machines(
23
+ id, trust_domain_id, owner_agent_id, machine_label, machine_fingerprint,
24
+ lifecycle_state, enrollment_source, enrolled_at
25
+ ) values (
26
+ '16230000-0000-0000-0000-000000000003',
27
+ '16230000-0000-0000-0000-000000000002',
28
+ '16230000-0000-0000-0000-000000000001',
29
+ 'claw', 'sha256:atomic-binding-machine', 'active', 'portal', now()
30
+ );
31
+ insert into agent_board.identity_providers(
32
+ issuer, audience, supported, policy_approved,
33
+ allowed_scopes, allowed_board_scopes, allowed_agent_kinds
34
+ ) values (
35
+ 'https://atomic-binding.test', 'message-board', true, true,
36
+ ARRAY['board:send'], ARRAY[]::text[], ARRAY['agent']
37
+ );
38
+ select count(*) from agent_board.create_fleet_onboarding_invite(
39
+ (select id from agent_board.boards where slug='atomic-binding-board'),
40
+ '16230000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
41
+ 'message-board', 'sha256:atomic-binding-repo', 'worker', 'phase_worker',
42
+ 'atomic-binding-agent', 1, now() + interval '1 hour',
43
+ '16230000-0000-0000-0000-000000000001', jsonb_build_object()
44
+ );
45
+ select count(*) from agent_board.onboard_federated_fleet_agent(
46
+ 'https://atomic-binding.test', 'message-board', 'human-binding',
47
+ jsonb_build_object('iss','https://atomic-binding.test','aud','message-board','sub','human-binding'),
48
+ 'atomic-binding-agent',
49
+ 'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
50
+ 'raw_ed25519_base64url',
51
+ 'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
52
+ true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
53
+ (select code from agent_board.board_invite_tokens where created_by_agent_id='16230000-0000-0000-0000-000000000001'),
54
+ (select id from agent_board.boards where slug='atomic-binding-board'),
55
+ '16230000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
56
+ 'message-board', 'sha256:atomic-binding-repo', 'worker', 'phase_worker',
57
+ 'atomic-binding-agent'
58
+ );
59
+ }
60
+
61
+ teardown
62
+ {
63
+ delete from agent_board.fleet_machine_agents where machine_id='16230000-0000-0000-0000-000000000003';
64
+ delete from agent_board.fleet_repo_scopes where harness_registration_id in (
65
+ select id from agent_board.fleet_harness_registrations where machine_id='16230000-0000-0000-0000-000000000003'
66
+ );
67
+ delete from agent_board.fleet_harness_registrations where machine_id='16230000-0000-0000-0000-000000000003';
68
+ delete from agent_board.board_invite_redemptions where invite_id in (
69
+ select id from agent_board.board_invite_tokens where created_by_agent_id='16230000-0000-0000-0000-000000000001'
70
+ );
71
+ truncate agent_board.fleet_onboarding_invite_bounds;
72
+ delete from agent_board.board_invite_tokens where created_by_agent_id='16230000-0000-0000-0000-000000000001';
73
+ delete from agent_board.board_memberships where board_id in (
74
+ select id from agent_board.boards where slug='atomic-binding-board'
75
+ );
76
+ delete from agent_board.enrollment_federation_audit where agent_id in (
77
+ select id from agent_board.agents where handle='atomic-binding-agent'
78
+ );
79
+ delete from agent_board.agent_signing_keys where agent_id in (
80
+ select id from agent_board.agents where handle='atomic-binding-agent'
81
+ );
82
+ delete from agent_board.agents where handle='atomic-binding-agent';
83
+ delete from agent_board.boards where slug='atomic-binding-board';
84
+ delete from agent_board.fleet_machines where id='16230000-0000-0000-0000-000000000003';
85
+ delete from agent_board.fleet_trust_domains where id='16230000-0000-0000-0000-000000000002';
86
+ delete from agent_board.agents where id='16230000-0000-0000-0000-000000000001';
87
+ delete from agent_board.identity_providers where issuer='https://atomic-binding.test';
88
+ }
89
+
90
+ session "retry"
91
+ step "retry_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
92
+ step "retry_onboard" {
93
+ select binding_disposition from agent_board.onboard_federated_fleet_agent(
94
+ 'https://atomic-binding.test', 'message-board', 'human-binding',
95
+ jsonb_build_object('iss','https://atomic-binding.test','aud','message-board','sub','human-binding','iat',200),
96
+ 'atomic-binding-agent','yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
97
+ 'raw_ed25519_base64url','sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
98
+ true,null,'agent',ARRAY['board:send'],ARRAY[]::text[],
99
+ (select code from agent_board.board_invite_tokens where created_by_agent_id='16230000-0000-0000-0000-000000000001'),
100
+ (select id from agent_board.boards where slug='atomic-binding-board'),
101
+ '16230000-0000-0000-0000-000000000003','codex','phase-loop',
102
+ 'message-board','sha256:atomic-binding-repo','worker','phase_worker','atomic-binding-agent'
103
+ );
104
+ }
105
+ step "retry_commit" { COMMIT; }
106
+
107
+ session "revoke"
108
+ step "revoke_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
109
+ step "revoke_binding" {
110
+ select lifecycle_state from agent_board.revoke_fleet_machine_agent_binding(
111
+ (select id from agent_board.fleet_machine_agents where machine_id='16230000-0000-0000-0000-000000000003'),
112
+ '16230000-0000-0000-0000-000000000001','isolation proof',jsonb_build_object()
113
+ );
114
+ }
115
+ step "revoke_commit" { COMMIT; }
116
+
117
+ session "check"
118
+ step "check_binding" {
119
+ select count(*) filter (where lifecycle_state='active') as active,
120
+ count(*) filter (where lifecycle_state='revoked') as revoked,
121
+ count(*) as total
122
+ from agent_board.fleet_machine_agents
123
+ where machine_id='16230000-0000-0000-0000-000000000003';
124
+ }
125
+
126
+ permutation "retry_begin" "retry_onboard" "revoke_begin" "revoke_binding" "retry_commit" "revoke_commit" "check_binding"
127
+ permutation "revoke_begin" "revoke_binding" "retry_begin" "retry_onboard" "revoke_commit" "retry_commit" "check_binding"
@@ -0,0 +1,134 @@
1
+ # Consiliency/message-board#162 — board archival and composed fleet onboarding
2
+ # share the board row lock. Prove archive-first rejects and onboarding-first
3
+ # commits before archival without consuming another invite use.
4
+
5
+ setup
6
+ {
7
+ insert into agent_board.agents(id, agent_type, kind, handle, display_name, status)
8
+ values ('16290000-0000-0000-0000-000000000001', 'agent', 'agent',
9
+ 'atomic-archive-owner', 'Atomic Archive Owner', 'active');
10
+ select count(*) from agent_board.create_board(
11
+ '16290000-0000-0000-0000-000000000001',
12
+ 'atomic-archive-board', 'Atomic Archive Board',
13
+ 'Consiliency/message-board#162 archive race', jsonb_build_object(), 'private'
14
+ );
15
+ insert into agent_board.fleet_trust_domains(
16
+ id, slug, display_name, domain_kind, owner_agent_id, lifecycle_state, approved_at
17
+ ) values (
18
+ '16290000-0000-0000-0000-000000000002', 'atomic-archive-domain',
19
+ 'Atomic Archive Domain', 'tailnet', '16290000-0000-0000-0000-000000000001',
20
+ 'active', now()
21
+ );
22
+ insert into agent_board.fleet_machines(
23
+ id, trust_domain_id, owner_agent_id, machine_label, machine_fingerprint,
24
+ lifecycle_state, enrollment_source, enrolled_at
25
+ ) values (
26
+ '16290000-0000-0000-0000-000000000003',
27
+ '16290000-0000-0000-0000-000000000002',
28
+ '16290000-0000-0000-0000-000000000001',
29
+ 'claw', 'sha256:atomic-archive-machine', 'active', 'portal', now()
30
+ );
31
+ insert into agent_board.identity_providers(
32
+ issuer, audience, supported, policy_approved,
33
+ allowed_scopes, allowed_board_scopes, allowed_agent_kinds
34
+ ) values (
35
+ 'https://atomic-archive.test', 'message-board', true, true,
36
+ ARRAY['board:send'], ARRAY[]::text[], ARRAY['agent']
37
+ );
38
+ select count(*) from agent_board.create_fleet_onboarding_invite(
39
+ (select id from agent_board.boards where slug='atomic-archive-board'),
40
+ '16290000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
41
+ 'message-board', 'sha256:atomic-archive-repo', 'worker', 'phase_worker',
42
+ 'atomic-archive-agent', 1, now() + interval '1 hour',
43
+ '16290000-0000-0000-0000-000000000001', jsonb_build_object()
44
+ );
45
+ select count(*) from agent_board.onboard_federated_fleet_agent(
46
+ 'https://atomic-archive.test', 'message-board', 'human-archive',
47
+ jsonb_build_object('iss','https://atomic-archive.test','aud','message-board','sub','human-archive'),
48
+ 'atomic-archive-agent',
49
+ 'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
50
+ 'raw_ed25519_base64url',
51
+ 'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
52
+ true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
53
+ (select code from agent_board.board_invite_tokens where created_by_agent_id='16290000-0000-0000-0000-000000000001'),
54
+ (select id from agent_board.boards where slug='atomic-archive-board'),
55
+ '16290000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
56
+ 'message-board', 'sha256:atomic-archive-repo', 'worker', 'phase_worker',
57
+ 'atomic-archive-agent'
58
+ );
59
+ }
60
+
61
+ teardown
62
+ {
63
+ delete from agent_board.fleet_machine_agents where machine_id='16290000-0000-0000-0000-000000000003';
64
+ delete from agent_board.fleet_repo_scopes where harness_registration_id in (
65
+ select id from agent_board.fleet_harness_registrations where machine_id='16290000-0000-0000-0000-000000000003'
66
+ );
67
+ delete from agent_board.fleet_harness_registrations where machine_id='16290000-0000-0000-0000-000000000003';
68
+ delete from agent_board.board_invite_redemptions where invite_id in (
69
+ select id from agent_board.board_invite_tokens where created_by_agent_id='16290000-0000-0000-0000-000000000001'
70
+ );
71
+ truncate agent_board.fleet_onboarding_invite_bounds;
72
+ delete from agent_board.board_invite_tokens where created_by_agent_id='16290000-0000-0000-0000-000000000001';
73
+ delete from agent_board.board_memberships where board_id in (
74
+ select id from agent_board.boards where slug='atomic-archive-board'
75
+ );
76
+ delete from agent_board.enrollment_federation_audit where issuer='https://atomic-archive.test';
77
+ delete from agent_board.agent_signing_keys where agent_id in (
78
+ select id from agent_board.agents where handle='atomic-archive-agent'
79
+ );
80
+ delete from agent_board.agents where handle='atomic-archive-agent';
81
+ delete from agent_board.boards where slug='atomic-archive-board';
82
+ delete from agent_board.fleet_machines where id='16290000-0000-0000-0000-000000000003';
83
+ delete from agent_board.fleet_trust_domains where id='16290000-0000-0000-0000-000000000002';
84
+ delete from agent_board.agents where id='16290000-0000-0000-0000-000000000001';
85
+ delete from agent_board.identity_providers where issuer='https://atomic-archive.test';
86
+ }
87
+
88
+ session "blocker"
89
+ step "blocker_begin" { BEGIN; }
90
+ step "block_machine" {
91
+ select id from agent_board.fleet_machines
92
+ where id='16290000-0000-0000-0000-000000000003' for update;
93
+ }
94
+ step "blocker_commit" { COMMIT; }
95
+
96
+ session "archive"
97
+ step "archive_begin" { BEGIN; }
98
+ step "archive_board" {
99
+ update agent_board.boards set status='archived'
100
+ where slug='atomic-archive-board' returning status;
101
+ }
102
+ step "archive_commit" { COMMIT; }
103
+
104
+ session "retry"
105
+ step "retry_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
106
+ step "retry_onboard" {
107
+ select identity_disposition, binding_disposition
108
+ from agent_board.onboard_federated_fleet_agent(
109
+ 'https://atomic-archive.test', 'message-board', 'human-archive',
110
+ jsonb_build_object('iss','https://atomic-archive.test','aud','message-board','sub','human-archive','iat',200),
111
+ 'atomic-archive-agent',
112
+ 'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
113
+ 'raw_ed25519_base64url',
114
+ 'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
115
+ true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
116
+ (select code from agent_board.board_invite_tokens where created_by_agent_id='16290000-0000-0000-0000-000000000001'),
117
+ (select id from agent_board.boards where slug='atomic-archive-board'),
118
+ '16290000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
119
+ 'message-board', 'sha256:atomic-archive-repo', 'worker', 'phase_worker',
120
+ 'atomic-archive-agent'
121
+ );
122
+ }
123
+ step "retry_commit" { COMMIT; }
124
+
125
+ session "check"
126
+ step "check" {
127
+ select
128
+ (select status from agent_board.boards where slug='atomic-archive-board') as board_status,
129
+ (select uses_count from agent_board.board_invite_tokens where created_by_agent_id='16290000-0000-0000-0000-000000000001') as uses_count,
130
+ (select count(*) from agent_board.fleet_machine_agents where machine_id='16290000-0000-0000-0000-000000000003') as bindings;
131
+ }
132
+
133
+ permutation "archive_begin" "archive_board" "retry_begin" "retry_onboard" "archive_commit" "retry_commit" "check"
134
+ permutation "blocker_begin" "block_machine" "retry_begin" "retry_onboard" "archive_begin" "archive_board" "blocker_commit" "retry_commit" "archive_commit" "check"
@@ -0,0 +1,142 @@
1
+ # Consiliency/message-board#162 — a total human revocation and a fresh-key
2
+ # onboarding share the durable human-owner lock. Prove both orders: onboarding
3
+ # first is included in the subtree, while revocation first durably blocks it.
4
+
5
+ setup
6
+ {
7
+ insert into agent_board.agents(id, agent_type, kind, handle, display_name, status)
8
+ values ('16280000-0000-0000-0000-000000000001', 'agent', 'agent',
9
+ 'atomic-human-owner', 'Atomic Human Owner', 'active');
10
+ select count(*) from agent_board.create_board(
11
+ '16280000-0000-0000-0000-000000000001',
12
+ 'atomic-human-board', 'Atomic Human Board',
13
+ 'Consiliency/message-board#162 fresh human race', jsonb_build_object(), 'private'
14
+ );
15
+ insert into agent_board.fleet_trust_domains(
16
+ id, slug, display_name, domain_kind, owner_agent_id, lifecycle_state, approved_at
17
+ ) values (
18
+ '16280000-0000-0000-0000-000000000002', 'atomic-human-domain',
19
+ 'Atomic Human Domain', 'tailnet', '16280000-0000-0000-0000-000000000001',
20
+ 'active', now()
21
+ );
22
+ insert into agent_board.fleet_machines(
23
+ id, trust_domain_id, owner_agent_id, machine_label, machine_fingerprint,
24
+ lifecycle_state, enrollment_source, enrolled_at
25
+ ) values (
26
+ '16280000-0000-0000-0000-000000000003',
27
+ '16280000-0000-0000-0000-000000000002',
28
+ '16280000-0000-0000-0000-000000000001',
29
+ 'claw', 'sha256:atomic-human-machine', 'active', 'portal', now()
30
+ );
31
+ insert into agent_board.identity_providers(
32
+ issuer, audience, supported, policy_approved,
33
+ allowed_scopes, allowed_board_scopes, allowed_agent_kinds
34
+ ) values (
35
+ 'https://atomic-human.test', 'message-board', true, true,
36
+ ARRAY['board:send'], ARRAY[]::text[], ARRAY['agent']
37
+ );
38
+ select count(*) from agent_board.enroll_federated_identity(
39
+ 'https://atomic-human.test', 'message-board', 'human-fresh-race',
40
+ jsonb_build_object('iss','https://atomic-human.test','aud','message-board','sub','human-fresh-race'),
41
+ 'atomic-human-existing',
42
+ 'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
43
+ 'raw_ed25519_base64url',
44
+ 'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
45
+ true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[], null
46
+ );
47
+ select count(*) from agent_board.create_fleet_onboarding_invite(
48
+ (select id from agent_board.boards where slug='atomic-human-board'),
49
+ '16280000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
50
+ 'message-board', 'sha256:atomic-human-repo', 'worker', 'phase_worker',
51
+ 'atomic-human-fresh', 1, now() + interval '1 hour',
52
+ '16280000-0000-0000-0000-000000000001', jsonb_build_object()
53
+ );
54
+ }
55
+
56
+ teardown
57
+ {
58
+ delete from agent_board.fleet_revocation_events where agent_id in (
59
+ select id from agent_board.agents where handle in ('atomic-human-existing','atomic-human-fresh')
60
+ );
61
+ delete from agent_board.fleet_machine_agents where machine_id='16280000-0000-0000-0000-000000000003';
62
+ delete from agent_board.fleet_repo_scopes where harness_registration_id in (
63
+ select id from agent_board.fleet_harness_registrations where machine_id='16280000-0000-0000-0000-000000000003'
64
+ );
65
+ delete from agent_board.fleet_harness_registrations where machine_id='16280000-0000-0000-0000-000000000003';
66
+ delete from agent_board.board_invite_redemptions where invite_id in (
67
+ select id from agent_board.board_invite_tokens where created_by_agent_id='16280000-0000-0000-0000-000000000001'
68
+ );
69
+ truncate agent_board.fleet_onboarding_invite_bounds;
70
+ delete from agent_board.board_invite_tokens where created_by_agent_id='16280000-0000-0000-0000-000000000001';
71
+ delete from agent_board.board_memberships where board_id in (
72
+ select id from agent_board.boards where slug='atomic-human-board'
73
+ );
74
+ delete from agent_board.enrollment_federation_audit where issuer='https://atomic-human.test';
75
+ delete from agent_board.agent_signing_keys where agent_id in (
76
+ select id from agent_board.agents where handle in ('atomic-human-existing','atomic-human-fresh')
77
+ );
78
+ delete from agent_board.fleet_human_revocations where owner_user_id in (
79
+ select owner_user_id from agent_board.agents where handle in ('atomic-human-existing','atomic-human-fresh')
80
+ );
81
+ delete from agent_board.agents where handle in ('atomic-human-existing','atomic-human-fresh');
82
+ delete from agent_board.boards where slug='atomic-human-board';
83
+ delete from agent_board.fleet_machines where id='16280000-0000-0000-0000-000000000003';
84
+ delete from agent_board.fleet_trust_domains where id='16280000-0000-0000-0000-000000000002';
85
+ delete from agent_board.agents where id='16280000-0000-0000-0000-000000000001';
86
+ delete from agent_board.identity_providers where issuer='https://atomic-human.test';
87
+ }
88
+
89
+ session "blocker"
90
+ step "blocker_begin" { BEGIN; }
91
+ step "block_creator" {
92
+ select id from agent_board.agents
93
+ where id='16280000-0000-0000-0000-000000000001' for update;
94
+ }
95
+ step "block_existing" {
96
+ select handle from agent_board.agents where handle='atomic-human-existing' for update;
97
+ }
98
+ step "blocker_commit" { COMMIT; }
99
+
100
+ session "onboard"
101
+ step "onboard_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
102
+ step "onboard_fresh" {
103
+ select identity_disposition, binding_disposition
104
+ from agent_board.onboard_federated_fleet_agent(
105
+ 'https://atomic-human.test', 'message-board', 'human-fresh-race',
106
+ jsonb_build_object('iss','https://atomic-human.test','aud','message-board','sub','human-fresh-race','iat',200),
107
+ 'atomic-human-fresh',
108
+ 'TDyof3w0SMg4SDYuj-qenolHC_n05cZbrLSckKVGtMo',
109
+ 'raw_ed25519_base64url',
110
+ 'sha256:lSBZnHvmPvQONy8aoygp-fmTg26iZenrZC-L5LN9m7M',
111
+ true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
112
+ (select code from agent_board.board_invite_tokens where created_by_agent_id='16280000-0000-0000-0000-000000000001'),
113
+ (select id from agent_board.boards where slug='atomic-human-board'),
114
+ '16280000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
115
+ 'message-board', 'sha256:atomic-human-repo', 'worker', 'phase_worker',
116
+ 'atomic-human-fresh'
117
+ );
118
+ }
119
+ step "onboard_commit" { COMMIT; }
120
+
121
+ session "revoke"
122
+ step "revoke_begin" { BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role'; }
123
+ step "revoke_human" {
124
+ select revoked_agent_count
125
+ from agent_board.revoke_human_subtree(
126
+ (select owner_user_id from agent_board.agents where handle='atomic-human-existing'),
127
+ '16280000-0000-0000-0000-000000000001', 'fresh race revoke'
128
+ );
129
+ }
130
+ step "revoke_commit" { COMMIT; }
131
+
132
+ session "check"
133
+ step "check" {
134
+ select
135
+ (select count(*) from agent_board.fleet_human_revocations) as human_revocations,
136
+ (select count(*) from agent_board.agents where handle='atomic-human-fresh') as fresh_agents,
137
+ (select coalesce(max(status),'absent') from agent_board.agents where handle='atomic-human-fresh') as fresh_status,
138
+ (select count(*) from agent_board.agent_signing_keys where fingerprint='sha256:lSBZnHvmPvQONy8aoygp-fmTg26iZenrZC-L5LN9m7M') as fresh_keys;
139
+ }
140
+
141
+ permutation "blocker_begin" "block_creator" "onboard_begin" "onboard_fresh" "revoke_begin" "revoke_human" "blocker_commit" "onboard_commit" "revoke_commit" "check"
142
+ permutation "blocker_begin" "block_existing" "revoke_begin" "revoke_human" "onboard_begin" "onboard_fresh" "blocker_commit" "revoke_commit" "onboard_commit" "check"