@consiliency/agent-board-schema 1.54.0 → 1.56.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 (68) hide show
  1. package/fixtures/already-applied.json +8 -0
  2. package/migrations/20261005000100_agent_board_enroll_token_single_use.sql +886 -0
  3. package/migrations/20261006000100_agent_board_room_roster_active_first.sql +118 -0
  4. package/package.json +1 -1
  5. package/schema-manifest.json +21 -3
  6. package/src/tooling.test.mjs +7 -2
  7. package/tests/agent_board_atomic_fleet_onboarding.test.sql +35 -35
  8. package/tests/agent_board_atomic_fleet_onboarding_backfill.test.sql +5 -5
  9. package/tests/agent_board_atomic_fleet_onboarding_runtime.test.sql +32 -32
  10. package/tests/agent_board_enroll_federated_identity.test.sql +11 -10
  11. package/tests/agent_board_enroll_pop_binding.test.sql +12 -12
  12. package/tests/agent_board_enroll_resumption.test.sql +1 -1
  13. package/tests/agent_board_enroll_token_single_use.test.sql +538 -0
  14. package/tests/agent_board_enrollment_audit_content.test.sql +3 -3
  15. package/tests/agent_board_federated_lock_order_class.test.sql +5 -5
  16. package/tests/agent_board_fedsession.test.sql +1 -1
  17. package/tests/agent_board_fk_lock_edges.test.sql +23 -0
  18. package/tests/agent_board_identity_providers.test.sql +3 -2
  19. package/tests/agent_board_membership_current_row_class.test.sql +460 -0
  20. package/tests/agent_board_portal_offline_invariant.test.sql +1 -1
  21. package/tests/agent_board_room_roster.test.sql +262 -24
  22. package/tests/agent_board_roomlink.test.sql +2 -2
  23. package/tests/agent_board_rowid.test.sql +15 -2
  24. package/tests/agent_board_write_enforcement_class.test.sql +5 -2
  25. package/tests/catalog/function_execute_privileges.json +12 -3
  26. package/tests/catalog/p_metadata_parameters.json +6 -2
  27. package/tests/isolation/expected/atomic_fedsession_fleet_lock_order.out +1 -1
  28. package/tests/isolation/expected/atomic_fleet_authority_races.out +8 -8
  29. package/tests/isolation/expected/atomic_fleet_binding_revoke.out +2 -2
  30. package/tests/isolation/expected/atomic_fleet_board_archive.out +2 -2
  31. package/tests/isolation/expected/atomic_fleet_human_fresh_revoke.out +2 -2
  32. package/tests/isolation/expected/atomic_fleet_identity_disposition.out +2 -2
  33. package/tests/isolation/expected/atomic_fleet_lifecycle_races.out +22 -22
  34. package/tests/isolation/expected/atomic_fleet_onboarding.out +2 -2
  35. package/tests/isolation/expected/atomic_fleet_subtree_lock_order.out +1 -1
  36. package/tests/isolation/expected/enroll_identity_board_owner_lock_order.out +1 -1
  37. package/tests/isolation/expected/enroll_token_fleet_abort.out +81 -0
  38. package/tests/isolation/expected/enroll_token_pk_backstop.out +36 -0
  39. package/tests/isolation/expected/enroll_token_placement.out +86 -0
  40. package/tests/isolation/expected/enroll_token_rollback_unstrands.out +59 -0
  41. package/tests/isolation/expected/enroll_token_route_race_fleet_first.out +72 -0
  42. package/tests/isolation/expected/enroll_token_route_race_identity_first.out +72 -0
  43. package/tests/isolation/expected/enroll_token_same_key_fleet_then_identity.out +73 -0
  44. package/tests/isolation/expected/enroll_token_same_key_identity_then_fleet.out +73 -0
  45. package/tests/isolation/expected/enroll_token_same_key_resume.out +59 -0
  46. package/tests/isolation/expected/enroll_token_same_token_race.out +58 -0
  47. package/tests/isolation/expected/onboarding_ceiling_lock_order.out +2 -2
  48. package/tests/isolation/specs/atomic_fedsession_fleet_lock_order.spec +1 -1
  49. package/tests/isolation/specs/atomic_fleet_authority_races.spec +2 -2
  50. package/tests/isolation/specs/atomic_fleet_binding_revoke.spec +2 -2
  51. package/tests/isolation/specs/atomic_fleet_board_archive.spec +2 -2
  52. package/tests/isolation/specs/atomic_fleet_human_fresh_revoke.spec +2 -2
  53. package/tests/isolation/specs/atomic_fleet_identity_disposition.spec +2 -2
  54. package/tests/isolation/specs/atomic_fleet_lifecycle_races.spec +2 -2
  55. package/tests/isolation/specs/atomic_fleet_onboarding.spec +2 -2
  56. package/tests/isolation/specs/atomic_fleet_subtree_lock_order.spec +2 -2
  57. package/tests/isolation/specs/enroll_identity_board_owner_lock_order.spec +1 -1
  58. package/tests/isolation/specs/enroll_token_fleet_abort.spec +138 -0
  59. package/tests/isolation/specs/enroll_token_pk_backstop.spec +37 -0
  60. package/tests/isolation/specs/enroll_token_placement.spec +136 -0
  61. package/tests/isolation/specs/enroll_token_rollback_unstrands.spec +71 -0
  62. package/tests/isolation/specs/enroll_token_route_race_fleet_first.spec +119 -0
  63. package/tests/isolation/specs/enroll_token_route_race_identity_first.spec +120 -0
  64. package/tests/isolation/specs/enroll_token_same_key_fleet_then_identity.spec +112 -0
  65. package/tests/isolation/specs/enroll_token_same_key_identity_then_fleet.spec +113 -0
  66. package/tests/isolation/specs/enroll_token_same_key_resume.spec +70 -0
  67. package/tests/isolation/specs/enroll_token_same_token_race.spec +80 -0
  68. package/tests/isolation/specs/onboarding_ceiling_lock_order.spec +1 -1
@@ -13,7 +13,7 @@ step retry_onboard:
13
13
  jsonb_build_object('iss','https://atomic-binding.test','aud','message-board','sub','human-binding','iat',200),
14
14
  'atomic-binding-agent','yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
15
15
  'raw_ed25519_base64url','sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
16
- true,null,'agent',ARRAY['board:send'],ARRAY[]::text[],
16
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1),null,'agent',ARRAY['board:send'],ARRAY[]::text[],
17
17
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16230000-0000-0000-0000-000000000001'),
18
18
  (select id from agent_board.boards where slug='atomic-binding-board'),
19
19
  '16230000-0000-0000-0000-000000000003','codex','phase-loop',
@@ -78,7 +78,7 @@ step retry_onboard:
78
78
  jsonb_build_object('iss','https://atomic-binding.test','aud','message-board','sub','human-binding','iat',200),
79
79
  'atomic-binding-agent','yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
80
80
  'raw_ed25519_base64url','sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
81
- true,null,'agent',ARRAY['board:send'],ARRAY[]::text[],
81
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1),null,'agent',ARRAY['board:send'],ARRAY[]::text[],
82
82
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16230000-0000-0000-0000-000000000001'),
83
83
  (select id from agent_board.boards where slug='atomic-binding-board'),
84
84
  '16230000-0000-0000-0000-000000000003','codex','phase-loop',
@@ -26,7 +26,7 @@ step retry_onboard:
26
26
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
27
27
  'raw_ed25519_base64url',
28
28
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
29
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
29
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
30
30
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16290000-0000-0000-0000-000000000001'),
31
31
  (select id from agent_board.boards where slug='atomic-archive-board'),
32
32
  '16290000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -76,7 +76,7 @@ step retry_onboard:
76
76
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
77
77
  'raw_ed25519_base64url',
78
78
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
79
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
79
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
80
80
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16290000-0000-0000-0000-000000000001'),
81
81
  (select id from agent_board.boards where slug='atomic-archive-board'),
82
82
  '16290000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -26,7 +26,7 @@ step onboard_fresh:
26
26
  'TDyof3w0SMg4SDYuj-qenolHC_n05cZbrLSckKVGtMo',
27
27
  'raw_ed25519_base64url',
28
28
  'sha256:lSBZnHvmPvQONy8aoygp-fmTg26iZenrZC-L5LN9m7M',
29
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
29
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
30
30
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16280000-0000-0000-0000-000000000001'),
31
31
  (select id from agent_board.boards where slug='atomic-human-board'),
32
32
  '16280000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -103,7 +103,7 @@ step onboard_fresh:
103
103
  'TDyof3w0SMg4SDYuj-qenolHC_n05cZbrLSckKVGtMo',
104
104
  'raw_ed25519_base64url',
105
105
  'sha256:lSBZnHvmPvQONy8aoygp-fmTg26iZenrZC-L5LN9m7M',
106
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
106
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
107
107
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16280000-0000-0000-0000-000000000001'),
108
108
  (select id from agent_board.boards where slug='atomic-human-board'),
109
109
  '16280000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -14,7 +14,7 @@ step legacy_enroll:
14
14
  jsonb_build_object('iss','https://atomic-disposition.test','aud','message-board','sub','human-disposition','iat',100),
15
15
  'atomic-disposition-agent',
16
16
  '5TG73fnNhqeYkoSvPf9QwmWkvKWe0yA7B8YinAWQ8m4', 'raw_ed25519_base64url',
17
- 'sha256:qhjEQe9R_x26TBZyBsd-LrpR1fh7Mvnz86rbsCKtjz8', true, null, 'agent',
17
+ 'sha256:qhjEQe9R_x26TBZyBsd-LrpR1fh7Mvnz86rbsCKtjz8', true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent',
18
18
  ARRAY['board:send'], ARRAY[]::text[], null
19
19
  );
20
20
 
@@ -31,7 +31,7 @@ step fleet_onboard:
31
31
  jsonb_build_object('iss','https://atomic-disposition.test','aud','message-board','sub','human-disposition','iat',101),
32
32
  'atomic-disposition-agent',
33
33
  '5TG73fnNhqeYkoSvPf9QwmWkvKWe0yA7B8YinAWQ8m4', 'raw_ed25519_base64url',
34
- 'sha256:qhjEQe9R_x26TBZyBsd-LrpR1fh7Mvnz86rbsCKtjz8', true, null, 'agent',
34
+ 'sha256:qhjEQe9R_x26TBZyBsd-LrpR1fh7Mvnz86rbsCKtjz8', true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent',
35
35
  ARRAY['board:send'], ARRAY[]::text[],
36
36
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16260000-0000-0000-0000-000000000001'),
37
37
  (select id from agent_board.boards where slug='atomic-disposition-board'),
@@ -16,7 +16,7 @@ step retry_onboard:
16
16
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
17
17
  'raw_ed25519_base64url',
18
18
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
19
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
19
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
20
20
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
21
21
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
22
22
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -85,7 +85,7 @@ step retry_onboard:
85
85
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
86
86
  'raw_ed25519_base64url',
87
87
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
88
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
88
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
89
89
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
90
90
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
91
91
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -154,7 +154,7 @@ step retry_onboard:
154
154
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
155
155
  'raw_ed25519_base64url',
156
156
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
157
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
157
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
158
158
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
159
159
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
160
160
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -223,7 +223,7 @@ step retry_onboard:
223
223
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
224
224
  'raw_ed25519_base64url',
225
225
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
226
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
226
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
227
227
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
228
228
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
229
229
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -292,7 +292,7 @@ step retry_onboard:
292
292
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
293
293
  'raw_ed25519_base64url',
294
294
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
295
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
295
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
296
296
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
297
297
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
298
298
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -361,7 +361,7 @@ step retry_onboard:
361
361
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
362
362
  'raw_ed25519_base64url',
363
363
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
364
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
364
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
365
365
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
366
366
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
367
367
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -430,7 +430,7 @@ step retry_onboard:
430
430
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
431
431
  'raw_ed25519_base64url',
432
432
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
433
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
433
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
434
434
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
435
435
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
436
436
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -499,7 +499,7 @@ step retry_onboard:
499
499
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
500
500
  'raw_ed25519_base64url',
501
501
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
502
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
502
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
503
503
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
504
504
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
505
505
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -568,7 +568,7 @@ step retry_onboard:
568
568
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
569
569
  'raw_ed25519_base64url',
570
570
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
571
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
571
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
572
572
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
573
573
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
574
574
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -638,7 +638,7 @@ step retry_onboard:
638
638
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
639
639
  'raw_ed25519_base64url',
640
640
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
641
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
641
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
642
642
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
643
643
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
644
644
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -707,7 +707,7 @@ step retry_onboard:
707
707
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
708
708
  'raw_ed25519_base64url',
709
709
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
710
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
710
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
711
711
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
712
712
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
713
713
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -789,7 +789,7 @@ step retry_onboard:
789
789
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
790
790
  'raw_ed25519_base64url',
791
791
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
792
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
792
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
793
793
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
794
794
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
795
795
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -854,7 +854,7 @@ step retry_onboard:
854
854
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
855
855
  'raw_ed25519_base64url',
856
856
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
857
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
857
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
858
858
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
859
859
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
860
860
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -919,7 +919,7 @@ step retry_onboard:
919
919
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
920
920
  'raw_ed25519_base64url',
921
921
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
922
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
922
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
923
923
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
924
924
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
925
925
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -984,7 +984,7 @@ step retry_onboard:
984
984
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
985
985
  'raw_ed25519_base64url',
986
986
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
987
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
987
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
988
988
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
989
989
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
990
990
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -1049,7 +1049,7 @@ step retry_onboard:
1049
1049
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
1050
1050
  'raw_ed25519_base64url',
1051
1051
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
1052
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1052
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1053
1053
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
1054
1054
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
1055
1055
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -1114,7 +1114,7 @@ step retry_onboard:
1114
1114
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
1115
1115
  'raw_ed25519_base64url',
1116
1116
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
1117
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1117
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1118
1118
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
1119
1119
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
1120
1120
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -1179,7 +1179,7 @@ step retry_onboard:
1179
1179
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
1180
1180
  'raw_ed25519_base64url',
1181
1181
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
1182
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1182
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1183
1183
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
1184
1184
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
1185
1185
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -1244,7 +1244,7 @@ step retry_onboard:
1244
1244
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
1245
1245
  'raw_ed25519_base64url',
1246
1246
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
1247
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1247
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1248
1248
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
1249
1249
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
1250
1250
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -1310,7 +1310,7 @@ step retry_onboard:
1310
1310
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
1311
1311
  'raw_ed25519_base64url',
1312
1312
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
1313
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1313
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1314
1314
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
1315
1315
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
1316
1316
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -1375,7 +1375,7 @@ step retry_onboard:
1375
1375
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
1376
1376
  'raw_ed25519_base64url',
1377
1377
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
1378
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1378
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1379
1379
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
1380
1380
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
1381
1381
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -1441,7 +1441,7 @@ step retry_onboard:
1441
1441
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
1442
1442
  'raw_ed25519_base64url',
1443
1443
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
1444
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1444
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
1445
1445
  (select code from agent_board.board_invite_tokens where created_by_agent_id='16240000-0000-0000-0000-000000000001'),
1446
1446
  (select id from agent_board.boards where slug='atomic-lifecycle-board'),
1447
1447
  '16240000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -16,7 +16,7 @@ step first_onboard:
16
16
  'eRHZmllEy_TZHAG2zB-Qe_a5BFLkaavMTazWWfaE3f0',
17
17
  'raw_ed25519_base64url',
18
18
  'sha256:z8d5LQPfBMYnfZfvu8wVz684_jkp578-SPDU2OMObS8',
19
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
19
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
20
20
  (select code from agent_board.board_invite_tokens where created_by_agent_id = '16220000-0000-0000-0000-000000000001'),
21
21
  (select id from agent_board.boards where slug = 'atomic-race-board'),
22
22
  '16220000-0000-0000-0000-000000000003',
@@ -39,7 +39,7 @@ step second_onboard:
39
39
  'eRHZmllEy_TZHAG2zB-Qe_a5BFLkaavMTazWWfaE3f0',
40
40
  'raw_ed25519_base64url',
41
41
  'sha256:z8d5LQPfBMYnfZfvu8wVz684_jkp578-SPDU2OMObS8',
42
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
42
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
43
43
  (select code from agent_board.board_invite_tokens where created_by_agent_id = '16220000-0000-0000-0000-000000000001'),
44
44
  (select id from agent_board.boards where slug = 'atomic-race-board'),
45
45
  '16220000-0000-0000-0000-000000000003',
@@ -20,7 +20,7 @@ step retry_onboard:
20
20
  'yd_DIFGWGusVg3SbktDJQ1bE5UWvtR33xln4b05JIhk',
21
21
  'raw_ed25519_base64url',
22
22
  'sha256:NeVC2vx1MbTY56jUwJcLLr6YwB4IKCERsXWbUwhf5pA',
23
- true, null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
23
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY['board:send'], ARRAY[]::text[],
24
24
  (select code from agent_board.board_invite_tokens where created_by_agent_id='ffffffff-ffff-ffff-ffff-ffffffffff01'),
25
25
  (select id from agent_board.boards where slug='atomic-subtree-board'),
26
26
  '16270000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
@@ -21,7 +21,7 @@ step enroll_enroll:
21
21
  'lo194-iso-agent',
22
22
  public.test_pubkey('lo194-iso-k1'), 'raw_ed25519_base64url',
23
23
  public.test_key_fingerprint('lo194-iso-k1'),
24
- true, null, 'agent', ARRAY[]::text[], ARRAY[]::text[],
24
+ true, md5(random()::text) || md5(random()::text), now() + make_interval(hours => 1), null, 'agent', ARRAY[]::text[], ARRAY[]::text[],
25
25
  'lo194-iso-code'
26
26
  );
27
27
  <waiting ...>
@@ -0,0 +1,81 @@
1
+ Parsed test spec with 4 sessions
2
+
3
+ starting permutation: hold_begin hold_handle a_begin a_onboard b_begin b_onboard hold_rollback a_rollback b_commit check
4
+ count
5
+ -----
6
+ 1
7
+ (1 row)
8
+
9
+ step hold_begin: BEGIN;
10
+ step hold_handle:
11
+ insert into agent_board.agents(id, agent_type, kind, handle, display_name, status)
12
+ values ('52100000-0000-0000-0000-0000000000ff', 'agent', 'agent', 'tok521-wrong-handle', 'Tok521 Hold', 'active');
13
+
14
+ step a_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
15
+ step a_onboard:
16
+ select agent_handle, identity_disposition
17
+ from agent_board.onboard_federated_fleet_agent(
18
+ 'https://tok521-iso.test', 'message-board', 'tok521-human',
19
+ jsonb_build_object('iss','https://tok521-iso.test','aud','message-board','sub','tok521-human'),
20
+ 'tok521-wrong-handle',
21
+ public.test_pubkey('tok521-k1'), 'raw_ed25519_base64url',
22
+ public.test_key_fingerprint('tok521-k1'),
23
+ true, repeat('a', 64), now() + interval '1 hour', null, 'agent',
24
+ ARRAY['board:send'], ARRAY[]::text[],
25
+ (select code from agent_board.board_invite_tokens where created_by_agent_id='52100000-0000-0000-0000-000000000001'),
26
+ (select id from agent_board.boards where slug='tok521-board'),
27
+ '52100000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
28
+ 'message-board', 'sha256:tok521-repo', 'worker', 'phase_worker',
29
+ 'tok521-agent'
30
+ );
31
+ <waiting ...>
32
+ step b_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
33
+ step b_onboard:
34
+ select agent_handle, identity_disposition
35
+ from agent_board.onboard_federated_fleet_agent(
36
+ 'https://tok521-iso.test', 'message-board', 'tok521-human',
37
+ jsonb_build_object('iss','https://tok521-iso.test','aud','message-board','sub','tok521-human'),
38
+ 'tok521-agent',
39
+ public.test_pubkey('tok521-k2'), 'raw_ed25519_base64url',
40
+ public.test_key_fingerprint('tok521-k2'),
41
+ true, repeat('a', 64), now() + interval '1 hour', null, 'agent',
42
+ ARRAY['board:send'], ARRAY[]::text[],
43
+ (select code from agent_board.board_invite_tokens where created_by_agent_id='52100000-0000-0000-0000-000000000001'),
44
+ (select id from agent_board.boards where slug='tok521-board'),
45
+ '52100000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
46
+ 'message-board', 'sha256:tok521-repo', 'worker', 'phase_worker',
47
+ 'tok521-agent'
48
+ );
49
+ <waiting ...>
50
+ step hold_rollback: ROLLBACK;
51
+ step a_onboard: <... completed>
52
+ ERROR: server-assigned handle does not match the authorized handle
53
+ step b_onboard: <... completed>
54
+ agent_handle|identity_disposition
55
+ ------------+--------------------
56
+ tok521-agent|created
57
+ (1 row)
58
+
59
+ step a_rollback: ROLLBACK;
60
+ step b_commit: COMMIT;
61
+ step check:
62
+ select
63
+ (select count(*) from agent_board.agents where handle like 'tok521-agent%') as agents,
64
+ (select count(*) from agent_board.agent_signing_keys where fingerprint = public.test_key_fingerprint('tok521-k1')) as k1_keys,
65
+ (select count(*) from agent_board.agent_signing_keys where fingerprint = public.test_key_fingerprint('tok521-k2')) as k2_keys,
66
+ (select count(*) from agent_board.enroll_token_uses) as token_rows,
67
+ (select case u.agent_public_key_fingerprint
68
+ when public.test_key_fingerprint('tok521-k1') then 'K1'
69
+ when public.test_key_fingerprint('tok521-k2') then 'K2'
70
+ else 'other' end
71
+ from agent_board.enroll_token_uses u where u.token_sha256 = repeat('a', 64)) as token_bound_to,
72
+ (select count(*) from agent_board.fleet_harness_registrations where machine_id='52100000-0000-0000-0000-000000000003') as harnesses,
73
+ (select count(*) from agent_board.fleet_repo_scopes r join agent_board.fleet_harness_registrations h on h.id = r.harness_registration_id
74
+ where h.machine_id='52100000-0000-0000-0000-000000000003') as repo_scopes,
75
+ (select count(*) from agent_board.fleet_machine_agents where machine_id='52100000-0000-0000-0000-000000000003') as bindings;
76
+
77
+ agents|k1_keys|k2_keys|token_rows|token_bound_to|harnesses|repo_scopes|bindings
78
+ ------+-------+-------+----------+--------------+---------+-----------+--------
79
+ 1| 0| 1| 1|K2 | 1| 1| 1
80
+ (1 row)
81
+
@@ -0,0 +1,36 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: a_begin a_bind b_begin b_bind a_commit b_rollback check
4
+ ?column?
5
+ --------
6
+ 1
7
+ (1 row)
8
+
9
+ step a_begin: BEGIN;
10
+ step a_bind: select agent_board._enroll_token_bind(repeat('a', 64), now() + interval '1 hour', 'sha256:tok521-fp-a');
11
+ _enroll_token_bind
12
+ ------------------
13
+
14
+ (1 row)
15
+
16
+ step b_begin: BEGIN;
17
+ step b_bind:
18
+ do $$ begin
19
+ perform agent_board._enroll_token_bind(repeat('a', 64), now() + interval '1 hour', 'sha256:tok521-fp-b');
20
+ exception when others then
21
+ raise exception 'SQLSTATE %: %', sqlstate, sqlerrm using errcode = sqlstate;
22
+ end $$;
23
+ <waiting ...>
24
+ step a_commit: COMMIT;
25
+ step b_bind: <... completed>
26
+ ERROR: SQLSTATE 23505: duplicate key value violates unique constraint "enroll_token_uses_pkey"
27
+ step b_rollback: ROLLBACK;
28
+ step check:
29
+ select token_sha256 = repeat('a', 64) as is_t, agent_public_key_fingerprint
30
+ from agent_board.enroll_token_uses order by 1, 2;
31
+
32
+ is_t|agent_public_key_fingerprint
33
+ ----+----------------------------
34
+ t |sha256:tok521-fp-a
35
+ (1 row)
36
+
@@ -0,0 +1,86 @@
1
+ Parsed test spec with 4 sessions
2
+
3
+ starting permutation: guard_begin guard_lock fleet_begin fleet_onboard ident_begin ident_enroll guard_commit fleet_commit ident_rollback check
4
+ count
5
+ -----
6
+ 1
7
+ (1 row)
8
+
9
+ step guard_begin: BEGIN;
10
+ step guard_lock:
11
+ select 1 as locked from agent_board.board_memberships bm
12
+ where bm.board_id = (select id from agent_board.boards where slug='tok521-board')
13
+ and bm.agent_id = '52100000-0000-0000-0000-000000000001'
14
+ for update;
15
+
16
+ locked
17
+ ------
18
+ 1
19
+ (1 row)
20
+
21
+ step fleet_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
22
+ step fleet_onboard:
23
+ select agent_handle, identity_disposition
24
+ from agent_board.onboard_federated_fleet_agent(
25
+ 'https://tok521-iso.test', 'message-board', 'tok521-human',
26
+ jsonb_build_object('iss','https://tok521-iso.test','aud','message-board','sub','tok521-human'),
27
+ 'tok521-agent',
28
+ public.test_pubkey('tok521-k2'), 'raw_ed25519_base64url',
29
+ public.test_key_fingerprint('tok521-k2'),
30
+ true, repeat('a', 64), now() + interval '1 hour', null, 'agent',
31
+ ARRAY['board:send'], ARRAY[]::text[],
32
+ (select code from agent_board.board_invite_tokens where created_by_agent_id='52100000-0000-0000-0000-000000000001'),
33
+ (select id from agent_board.boards where slug='tok521-board'),
34
+ '52100000-0000-0000-0000-000000000003', 'codex', 'phase-loop',
35
+ 'message-board', 'sha256:tok521-repo', 'worker', 'phase_worker',
36
+ 'tok521-agent'
37
+ );
38
+ <waiting ...>
39
+ step ident_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
40
+ step ident_enroll:
41
+ do $$ begin
42
+ perform agent_board.enroll_federated_identity(
43
+ 'https://tok521-iso.test', 'message-board', 'tok521-human',
44
+ jsonb_build_object('iss','https://tok521-iso.test','aud','message-board','sub','tok521-human'),
45
+ 'tok521-agent',
46
+ public.test_pubkey('tok521-k1'), 'raw_ed25519_base64url',
47
+ public.test_key_fingerprint('tok521-k1'),
48
+ true, repeat('a', 64), now() + interval '1 hour', null, 'agent',
49
+ ARRAY['board:send'], ARRAY[]::text[], null
50
+ );
51
+ exception when others then
52
+ raise exception 'SQLSTATE %: %', sqlstate, sqlerrm using errcode = sqlstate;
53
+ end $$;
54
+ <waiting ...>
55
+ step guard_commit: COMMIT;
56
+ step fleet_onboard: <... completed>
57
+ agent_handle|identity_disposition
58
+ ------------+--------------------
59
+ tok521-agent|created
60
+ (1 row)
61
+
62
+ step fleet_commit: COMMIT;
63
+ step ident_enroll: <... completed>
64
+ ERROR: SQLSTATE MBT01: enroll token already bound to a different key
65
+ step ident_rollback: ROLLBACK;
66
+ step check:
67
+ select
68
+ (select count(*) from agent_board.agents where handle like 'tok521-agent%') as agents,
69
+ (select count(*) from agent_board.agent_signing_keys where fingerprint = public.test_key_fingerprint('tok521-k1')) as k1_keys,
70
+ (select count(*) from agent_board.agent_signing_keys where fingerprint = public.test_key_fingerprint('tok521-k2')) as k2_keys,
71
+ (select count(*) from agent_board.enroll_token_uses) as token_rows,
72
+ (select case u.agent_public_key_fingerprint
73
+ when public.test_key_fingerprint('tok521-k1') then 'K1'
74
+ when public.test_key_fingerprint('tok521-k2') then 'K2'
75
+ else 'other' end
76
+ from agent_board.enroll_token_uses u where u.token_sha256 = repeat('a', 64)) as token_bound_to,
77
+ (select count(*) from agent_board.fleet_harness_registrations where machine_id='52100000-0000-0000-0000-000000000003') as harnesses,
78
+ (select count(*) from agent_board.fleet_repo_scopes r join agent_board.fleet_harness_registrations h on h.id = r.harness_registration_id
79
+ where h.machine_id='52100000-0000-0000-0000-000000000003') as repo_scopes,
80
+ (select count(*) from agent_board.fleet_machine_agents where machine_id='52100000-0000-0000-0000-000000000003') as bindings;
81
+
82
+ agents|k1_keys|k2_keys|token_rows|token_bound_to|harnesses|repo_scopes|bindings
83
+ ------+-------+-------+----------+--------------+---------+-----------+--------
84
+ 1| 0| 1| 1|K2 | 1| 1| 1
85
+ (1 row)
86
+
@@ -0,0 +1,59 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: a_begin a_enroll b_begin b_enroll a_rollback b_commit check
4
+ step a_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
5
+ step a_enroll:
6
+ select agent_handle
7
+ from agent_board.enroll_federated_identity(
8
+ 'https://tok521-iso.test', 'message-board', 'tok521-human',
9
+ jsonb_build_object('iss','https://tok521-iso.test','aud','message-board','sub','tok521-human'),
10
+ 'tok521-agent',
11
+ public.test_pubkey('tok521-k1'), 'raw_ed25519_base64url',
12
+ public.test_key_fingerprint('tok521-k1'),
13
+ true, repeat('a', 64), now() + interval '1 hour', null, 'agent',
14
+ ARRAY['board:send'], ARRAY[]::text[], null
15
+ );
16
+
17
+ agent_handle
18
+ ------------
19
+ tok521-agent
20
+ (1 row)
21
+
22
+ step b_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
23
+ step b_enroll:
24
+ select agent_handle
25
+ from agent_board.enroll_federated_identity(
26
+ 'https://tok521-iso.test', 'message-board', 'tok521-human',
27
+ jsonb_build_object('iss','https://tok521-iso.test','aud','message-board','sub','tok521-human'),
28
+ 'tok521-agent',
29
+ public.test_pubkey('tok521-k2'), 'raw_ed25519_base64url',
30
+ public.test_key_fingerprint('tok521-k2'),
31
+ true, repeat('a', 64), now() + interval '1 hour', null, 'agent',
32
+ ARRAY['board:send'], ARRAY[]::text[], null
33
+ );
34
+ <waiting ...>
35
+ step a_rollback: ROLLBACK;
36
+ step b_enroll: <... completed>
37
+ agent_handle
38
+ ------------
39
+ tok521-agent
40
+ (1 row)
41
+
42
+ step b_commit: COMMIT;
43
+ step check:
44
+ select
45
+ (select count(*) from agent_board.agents where handle like 'tok521-agent%') as agents,
46
+ (select count(*) from agent_board.agent_signing_keys where fingerprint = public.test_key_fingerprint('tok521-k1')) as k1_keys,
47
+ (select count(*) from agent_board.agent_signing_keys where fingerprint = public.test_key_fingerprint('tok521-k2')) as k2_keys,
48
+ (select count(*) from agent_board.enroll_token_uses) as token_rows,
49
+ (select case u.agent_public_key_fingerprint
50
+ when public.test_key_fingerprint('tok521-k1') then 'K1'
51
+ when public.test_key_fingerprint('tok521-k2') then 'K2'
52
+ else 'other' end
53
+ from agent_board.enroll_token_uses u where u.token_sha256 = repeat('a', 64)) as token_bound_to;
54
+
55
+ agents|k1_keys|k2_keys|token_rows|token_bound_to
56
+ ------+-------+-------+----------+--------------
57
+ 1| 0| 1| 1|K2
58
+ (1 row)
59
+