@consiliency/agent-board-schema 1.45.0 → 1.50.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 (35) hide show
  1. package/fixtures/already-applied.json +24 -0
  2. package/migrations/20260926000100_agent_board_ownerless_quota_census.sql +268 -0
  3. package/migrations/20260927000100_agent_board_fleet_domain_no_downgrade.sql +393 -0
  4. package/migrations/20260928000100_agent_board_ownerless_quota_renew_convergence.sql +562 -0
  5. package/migrations/20260929000100_agent_board_advisory_revert_cap_reset.sql +371 -0
  6. package/migrations/20260930000100_agent_board_claim_policy_race.sql +922 -0
  7. package/migrations/20261001000100_agent_board_claimpolicy_lock_grant_revoke.sql +158 -0
  8. package/package.json +1 -1
  9. package/schema-manifest.json +57 -3
  10. package/src/tooling.test.mjs +52 -2
  11. package/tests/agent_board_board_claim_policy.test.sql +261 -1
  12. package/tests/agent_board_claim_subject_gate.test.sql +11 -2
  13. package/tests/agent_board_claimkey.test.sql +17 -2
  14. package/tests/agent_board_claimpolicy_race.test.sql +1374 -0
  15. package/tests/agent_board_fleet_machine_hijack.test.sql +270 -1
  16. package/tests/agent_board_fleet_machine_reclaim.test.sql +28 -2
  17. package/tests/agent_board_ownerless_quota_census.test.sql +406 -0
  18. package/tests/agent_board_ownerless_renew_quota.test.sql +580 -0
  19. package/tests/catalog/p_metadata_parameters.json +28 -0
  20. package/tests/fixtures/test-prereqs.sql +186 -0
  21. package/tests/isolation/expected/claimpolicy_acquire_revert_race.out +66 -0
  22. package/tests/isolation/expected/claimpolicy_board_scope.out +73 -0
  23. package/tests/isolation/expected/claimpolicy_claimrow_lock_order.out +80 -0
  24. package/tests/isolation/expected/claimpolicy_isolation_refusal.out +114 -0
  25. package/tests/isolation/expected/claimpolicy_renew_revert_race.out +66 -0
  26. package/tests/isolation/expected/claimpolicy_writer_keyshare.out +49 -0
  27. package/tests/isolation/expected/fleet_domain_no_downgrade_demote_race.out +447 -0
  28. package/tests/isolation/specs/claimpolicy_acquire_revert_race.spec +118 -0
  29. package/tests/isolation/specs/claimpolicy_board_scope.spec +115 -0
  30. package/tests/isolation/specs/claimpolicy_claimrow_lock_order.spec +200 -0
  31. package/tests/isolation/specs/claimpolicy_isolation_refusal.spec +162 -0
  32. package/tests/isolation/specs/claimpolicy_renew_revert_race.spec +150 -0
  33. package/tests/isolation/specs/claimpolicy_writer_keyshare.spec +107 -0
  34. package/tests/isolation/specs/fleet_domain_no_downgrade_demote_race.spec +292 -0
  35. package/tests/seeds/20261001000100_agent_board_claimpolicy_lock_grant_revoke.sql.pre.sql +99 -0
@@ -859,3 +859,189 @@ COMMENT ON FUNCTION agent_board_test.lock_edges() IS
859
859
  'FK-aware per-function row-lock edge set: explicit locks, write locks, and the FOR KEY SHARE / child locks PostgreSQL takes through foreign keys. Test-only; see message-board#281.';
860
860
 
861
861
  GRANT USAGE ON SCHEMA agent_board_test TO PUBLIC;
862
+
863
+ -- ---------------------------------------------------------------------------
864
+ -- IDENTIFIER MATCHERS (Consiliency/message-board#405, #417). ONE definition of
865
+ -- "does this body reference agent_board.<name>", used by every assertion that asks.
866
+ --
867
+ -- WHY THESE EXIST. The same lookup was hand-written at ~20 sites and drifted twice in
868
+ -- opposite directions in consecutive review rounds: first too STRICT (an abutting-paren,
869
+ -- schema-qualified, case-sensitive strpos missed `_claimgate_lock_board_policy (v)` and
870
+ -- `"_claimgate_lock_board_policy"(v)`), then too TOLERANT (a `[^A-Za-z0-9_]` guard accepts `.`,
871
+ -- so `other_schema."_claimgate_lock_board_policy"(v)` matched as though it named OUR helper, and
872
+ -- blanket case-insensitivity folded `"AGENT_BOARD"` onto `agent_board` although PostgreSQL treats
873
+ -- those as DISTINCT objects). The target is not "more tolerant" — it is EXACTLY as tolerant as
874
+ -- PostgreSQL's identifier rules, which is a thing to encode once rather than to re-derive per site.
875
+ --
876
+ -- THE RULES ENCODED, verified against the server rather than recalled:
877
+ -- * an UNQUOTED identifier folds to lower case, so any case spelling names the same object
878
+ -- (`to_regclass('AGENT_BOARD.boards') is not distinct from to_regclass('agent_board.boards')`
879
+ -- returns true);
880
+ -- * a QUOTED identifier is case-SENSITIVE, so `"AGENT_BOARD".boards` is a different object
881
+ -- (`to_regclass('"AGENT_BOARD".boards')` returns NULL);
882
+ -- * a reference is ours only if it is UNQUALIFIED or qualified by exactly `agent_board` — any
883
+ -- other qualifier names someone else's object.
884
+ --
885
+ -- CALLERS MUST USE `~` / regexp_instr WITHOUT THE 'i' FLAG. The case rules are inside these
886
+ -- patterns; adding 'i' would fold the quoted alternative and reintroduce the second defect.
887
+ -- Keywords that should stay case-insensitive get agent_board_test.ci_re() for the same reason.
888
+
889
+ -- A SPACE IN p_text BECOMES `\s+`, because every space this is ever given separates the words of a
890
+ -- multiword SQL keyword (`insert into`, `no key`, `key share`) and PostgreSQL accepts any whitespace
891
+ -- run there. stmt_re used to do that rewrite on ci_re's OUTPUT — a caller textually transforming a
892
+ -- shared matcher, which is the same shape as CPR-6 stripping a director, and fragile in the same
893
+ -- way: it depended on ci_re never emitting a space for some other reason. The rule belongs in the
894
+ -- matcher, so no call site edits what a matcher returns.
895
+ create or replace function agent_board_test.ci_re(p_text text) returns text
896
+ language sql immutable set search_path to pg_catalog as $$
897
+ select coalesce(string_agg(case when c ~ '[A-Za-z]' then '[' || upper(c) || lower(c) || ']'
898
+ when c = ' ' then '\s+'
899
+ else c end, ''), '')
900
+ from regexp_split_to_table(p_text, '') as c
901
+ $$;
902
+
903
+ create or replace function agent_board_test.ident_re(p_name text) returns text
904
+ language sql immutable set search_path to pg_catalog as $$
905
+ -- p_name is the object's real (lower-case) name: unquoted-any-case, or quoted-exactly.
906
+ select '(?:' || agent_board_test.ci_re(p_name) || '|"' || p_name || '")'
907
+ $$;
908
+
909
+ -- IDENT_CHARS is PostgreSQL's unquoted-identifier continuation set, and `$` IS IN IT: `$` is legal
910
+ -- after the first character, so `x$boards` is a DISTINCT identifier whose suffix would otherwise
911
+ -- satisfy a `boards` matcher, and `x$_claimgate_lock_board_policy(…)` a distinct FUNCTION whose
912
+ -- suffix would be credited as the protected helper. Omitting `$` was a real over-tolerance, found
913
+ -- by review and reproduced before fixing.
914
+ create or replace function agent_board_test.ident_chars() returns text
915
+ language sql immutable set search_path to pg_catalog as $$ select 'A-Za-z0-9_$' $$;
916
+
917
+ -- THE BOUNDARY GUARDS. Three rejections, each a defect that shipped:
918
+ -- * an ASCII identifier character or a quote immediately before/after — `dashboards`, `x$boards`;
919
+ -- * a NON-ASCII letter, which PostgreSQL also admits in an unquoted identifier, so
920
+ -- `é_claimgate_lock_board_policy` and `claim_policyé` are DISTINCT objects whose prefix/suffix
921
+ -- would otherwise match. The `[A-Za-z0-9_$]` class alone cannot express this;
922
+ -- * a DOT WITH ANY WHITESPACE before the name. `other . _claimgate_lock_board_policy()` is a
923
+ -- different schema's call, and the single-character class let it through because the character
924
+ -- immediately before the name was a SPACE — the round-9 schema-blindness defect reopened through
925
+ -- whitespace. PostgreSQL accepts variable-length lookbehind, verified, so `(?<!\.\s*)` states
926
+ -- it directly rather than approximating with a fixed number of spaces.
927
+ create or replace function agent_board_test.lead_guard() returns text
928
+ language sql immutable set search_path to pg_catalog as $$
929
+ select '(?<![' || agent_board_test.ident_chars() || '."])(?<![^\x00-\x7F])(?<!\.\s*)'
930
+ $$;
931
+
932
+ create or replace function agent_board_test.trail_guard() returns text
933
+ language sql immutable set search_path to pg_catalog as $$
934
+ select '(?![' || agent_board_test.ident_chars() || '])(?![^\x00-\x7F])'
935
+ $$;
936
+
937
+ -- The un-prefixed fragments. Public matchers add the (?c) director exactly once; these exist so a
938
+ -- matcher can embed another without producing a mid-pattern director, which PostgreSQL rejects.
939
+ create or replace function agent_board_test.qname_frag(p_name text) returns text
940
+ language sql immutable set search_path to pg_catalog as $$
941
+ select agent_board_test.lead_guard() || '(?:' || agent_board_test.ident_re('agent_board')
942
+ || '\s*\.\s*)?' || agent_board_test.ident_re(p_name) || agent_board_test.trail_guard()
943
+ $$;
944
+
945
+ create or replace function agent_board_test.call_frag(p_name text) returns text
946
+ language sql immutable set search_path to pg_catalog as $$
947
+ select agent_board_test.lead_guard() || '(?:' || agent_board_test.ident_re('agent_board')
948
+ || '\s*\.\s*)?' || agent_board_test.ident_re(p_name) || '\s*\('
949
+ $$;
950
+
951
+ -- THE (?c) DIRECTOR IS THE ENFORCEMENT, not the comment above it. A caller that passes 'i' to
952
+ -- regexp_instr/regexp_matches alongside one of these patterns would previously refold the quoted
953
+ -- alternative and credit `"AGENT_BOARD"."claims"` as `agent_board.claims`. That discipline was
954
+ -- DOCUMENTED and then violated in the same commit that documented it, at four call sites. `(?c)`
955
+ -- forces case-sensitive matching from inside the pattern, so a stray 'i' is now overridden rather
956
+ -- than obeyed — verified: regexp_instr(…, '(?c)"agent_board"', …, 'i') returns 0.
957
+ -- And misuse fails LOUDLY: a director is only legal at the START of a regular expression, so a
958
+ -- caller who prefixes anything gets `invalid regular expression` rather than a silent fold.
959
+ --
960
+ -- EXACTLY WHICH CALLERS THE DIRECTOR PROTECTS, stated narrowly because the first version of this
961
+ -- paragraph claimed more: a pattern that BEGINS with qname_re/call_re/stmt_re is protected against a
962
+ -- passed 'i' flag AND against the `~*` operator, because the director is inside the pattern. A
963
+ -- caller that composes a *_frag directly is NOT protected — the frags carry no director by design,
964
+ -- since one mid-pattern is a syntax error. They exist for embedding inside the _re matchers, and
965
+ -- EXECUTE on them is revoked below so they are not casually reachable; a test author who calls a
966
+ -- frag with `~*` deliberately is outside what any mechanism here can prevent, and that residue is
967
+ -- named rather than papered over.
968
+ create or replace function agent_board_test.qname_re(p_name text) returns text
969
+ language sql immutable set search_path to pg_catalog as $$
970
+ select '(?c)' || agent_board_test.qname_frag(p_name)
971
+ $$;
972
+
973
+ create or replace function agent_board_test.call_re(p_name text) returns text
974
+ language sql immutable set search_path to pg_catalog as $$
975
+ select '(?c)' || agent_board_test.call_frag(p_name)
976
+ $$;
977
+
978
+ -- A COLUMN token. Unquoted identifiers fold, so `SET CLAIM_POLICY = …` names the same column as
979
+ -- `claim_policy` — the case discipline reached the qualifier and the keywords but not the column,
980
+ -- so an upper-case column token was silently omitted from CPR-6 and CPR-12.
981
+ create or replace function agent_board_test.col_frag(p_name text) returns text
982
+ language sql immutable set search_path to pg_catalog as $$
983
+ select agent_board_test.lead_guard() || agent_board_test.ident_re(p_name) || agent_board_test.trail_guard()
984
+ $$;
985
+
986
+ create or replace function agent_board_test.stmt_re(p_verbs text, p_rel text) returns text
987
+ language sql immutable set search_path to pg_catalog as $$
988
+ -- "a statement whose verb is one of p_verbs and whose target is agent_board.p_rel".
989
+ -- SPACES INSIDE A MULTIWORD VERB BECOME `\s+`: `INSERT\nINTO` and `DELETE FROM` are the same
990
+ -- statement to PostgreSQL. The hand-rolled expressions this consolidated used `\s+` and got that
991
+ -- right; the first shared version made the space literal and LOST a property the scattered
992
+ -- versions had — a consolidation is only safe if it is at least the union of what it replaces.
993
+ select '(?c)(?:' || (select string_agg(agent_board_test.ci_re(v), '|')
994
+ from regexp_split_to_table(p_verbs, '\|') as v)
995
+ || ')\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
996
+ || agent_board_test.qname_frag(p_rel)
997
+ $$;
998
+
999
+ revoke all on function agent_board_test.qname_frag(text) from public;
1000
+ revoke all on function agent_board_test.call_frag(text) from public;
1001
+ revoke all on function agent_board_test.col_frag(text) from public;
1002
+
1003
+ -- THE INSERT-WITHOUT-COLUMN-LIST RULE, AS A NAMED OBJECT. It began as an expression inlined in
1004
+ -- CPR-6, and CPR-0's rows for it RECONSTRUCTED that expression instead of calling it — so the rows
1005
+ -- tested a copy, the copy could not diverge from itself, and mutating the production arm left them
1006
+ -- green. That is precisely the defect CPR-0 exists to catch, committed in CPR-0's own newest rows.
1007
+ -- A behaviour-table row must exercise THE SAME code object the production path compiles; anything
1008
+ -- else certifies a parallel implementation.
1009
+ --
1010
+ -- `insert into <rel> …` with no column list writes EVERY column, so for a sole-writer claim it is a
1011
+ -- writer of every column including the one being tracked. Three things this has to get right:
1012
+ -- * `(?!\s*\()` not `\s*(?!\()` — BACKTRACKING defeats the latter, since `\s*` may match zero
1013
+ -- characters and the lookahead then passes on the whitespace itself;
1014
+ -- * an ALIAS may sit between the relation and the column list, and it may be QUOTED
1015
+ -- (`insert into boards as "b"(slug, …)`), so the alias alternative admits both spellings;
1016
+ -- * VALUES / SELECT / DEFAULT / OVERRIDING are KEYWORDS, not aliases — treating them as aliases
1017
+ -- would exclude the genuine no-column-list forms this rule exists for.
1018
+ -- SPLIT INTO frag + re, matching qname/call/col, because CPR-6 embeds this rule INSIDE a larger
1019
+ -- alternation and a director is only legal at position 0. It used to embed
1020
+ -- `replace(insert_all_columns_re(...), '(?c)', '')` — so CPR-0's rows compiled X while production
1021
+ -- compiled a TEXTUAL TRANSFORMATION of X. Behaviourally identical today (CPR-6 matches with `~`,
1022
+ -- which is case-sensitive anyway, so the director is belt-and-braces there), but the rows would have
1023
+ -- stayed green if the rule ever became director-dependent, or if a `(?c)` appeared anywhere but
1024
+ -- position 0 and the replace stripped it silently. Two strings are not one object. Now CPR-6 embeds
1025
+ -- the frag and CPR-0's rows exercise the frag: the same object, no transformation at any call site.
1026
+ create or replace function agent_board_test.insert_all_columns_frag(p_rel text) returns text
1027
+ language sql immutable set search_path to pg_catalog as $$
1028
+ select agent_board_test.ci_re('insert') || '\s+' || agent_board_test.ci_re('into')
1029
+ || '\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
1030
+ || agent_board_test.qname_frag(p_rel)
1031
+ || '(?!(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?(?!'
1032
+ -- ONLY VALUES AND SELECT. `default` and `overriding` were excluded here too and were DEAD
1033
+ -- CODE: an exclusion only matters if the keyword can be followed by `\s*\(` and so be mistaken
1034
+ -- for an alias, and DEFAULT is only ever followed by VALUES, OVERRIDING by SYSTEM/USER.
1035
+ -- Verified by reverting each: the two live exclusions change a verdict, the two dead ones do
1036
+ -- not. Removed rather than kept with rows that cannot gate them.
1037
+ || agent_board_test.ci_re('values') || '(?![A-Za-z0-9_$])|'
1038
+ || agent_board_test.ci_re('select') || '(?![A-Za-z0-9_$]))'
1039
+ || '(?:"[^"]*"|[A-Za-z_][A-Za-z0-9_$]*))?\s*\()'
1040
+ $$;
1041
+
1042
+ create or replace function agent_board_test.insert_all_columns_re(p_rel text) returns text
1043
+ language sql immutable set search_path to pg_catalog as $$
1044
+ select '(?c)' || agent_board_test.insert_all_columns_frag(p_rel)
1045
+ $$;
1046
+
1047
+ revoke all on function agent_board_test.insert_all_columns_frag(text) from public;
@@ -0,0 +1,66 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: w_begin w_flip a_begin a_acq w_commit a_commit chk
4
+ step w_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000a1';
5
+ step w_flip:
6
+ select claim_policy
7
+ from agent_board.set_board_claim_policy(
8
+ (select id from agent_board.boards where slug = 'cpr-acq-board'), 'inherit');
9
+
10
+ claim_policy
11
+ ------------
12
+ inherit
13
+ (1 row)
14
+
15
+ step a_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000a1';
16
+ step a_acq:
17
+ select resource_key
18
+ from agent_board.acquire_claim(null, 'file', 'cpr-acq-key', now() + interval '400 days',
19
+ null, '40500000-0000-4000-8000-000000000010');
20
+ <waiting ...>
21
+ step w_commit: COMMIT;
22
+ step a_acq: <... completed>
23
+ ERROR: ownerless claim lease horizon exceeds cap (30 days)
24
+ step a_commit: COMMIT;
25
+ step chk:
26
+ select count(*)::int as active_claims,
27
+ coalesce(bool_or(lease_expires_at > now() + interval '31 days'), false) as over_cap_lease
28
+ from agent_board.claims
29
+ where agent_id = '40500000-0000-4000-8000-000000000001'
30
+ and resource_kind = 'file'
31
+ and status = 'active'
32
+ and released_at is null;
33
+
34
+ active_claims|over_cap_lease
35
+ -------------+--------------
36
+ 0|f
37
+ (1 row)
38
+
39
+
40
+ starting permutation: a_begin a_acq a_commit chk
41
+ step a_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000a1';
42
+ step a_acq:
43
+ select resource_key
44
+ from agent_board.acquire_claim(null, 'file', 'cpr-acq-key', now() + interval '400 days',
45
+ null, '40500000-0000-4000-8000-000000000010');
46
+
47
+ resource_key
48
+ ------------
49
+ cpr-acq-key
50
+ (1 row)
51
+
52
+ step a_commit: COMMIT;
53
+ step chk:
54
+ select count(*)::int as active_claims,
55
+ coalesce(bool_or(lease_expires_at > now() + interval '31 days'), false) as over_cap_lease
56
+ from agent_board.claims
57
+ where agent_id = '40500000-0000-4000-8000-000000000001'
58
+ and resource_kind = 'file'
59
+ and status = 'active'
60
+ and released_at is null;
61
+
62
+ active_claims|over_cap_lease
63
+ -------------+--------------
64
+ 1|t
65
+ (1 row)
66
+
@@ -0,0 +1,73 @@
1
+ Parsed test spec with 4 sessions
2
+
3
+ starting permutation: w_begin w_flip a1_begin a1_acq w_commit a1_commit chk
4
+ step w_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000c1';
5
+ step w_flip:
6
+ select claim_policy
7
+ from agent_board.set_board_claim_policy(
8
+ (select id from agent_board.boards where slug = 'cpr-scope-b1'), 'inherit');
9
+
10
+ claim_policy
11
+ ------------
12
+ inherit
13
+ (1 row)
14
+
15
+ step a1_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000c1';
16
+ step a1_acq:
17
+ select resource_key
18
+ from agent_board.acquire_claim(null, 'file', 'cpr-scope-key-b1', now() + interval '400 days',
19
+ null, '40500000-0000-4000-8000-000000000041');
20
+ <waiting ...>
21
+ step w_commit: COMMIT;
22
+ step a1_acq: <... completed>
23
+ ERROR: ownerless claim lease horizon exceeds cap (30 days)
24
+ step a1_commit: COMMIT;
25
+ step chk:
26
+ select resource_key, (lease_expires_at > now() + interval '31 days') as over_cap_lease
27
+ from agent_board.claims
28
+ where agent_id = '40500000-0000-4000-8000-000000000003'
29
+ and status = 'active' and released_at is null
30
+ order by resource_key;
31
+
32
+ resource_key|over_cap_lease
33
+ ------------+--------------
34
+ (0 rows)
35
+
36
+
37
+ starting permutation: w_begin w_flip a2_begin a2_acq w_commit a2_commit chk
38
+ step w_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000c1';
39
+ step w_flip:
40
+ select claim_policy
41
+ from agent_board.set_board_claim_policy(
42
+ (select id from agent_board.boards where slug = 'cpr-scope-b1'), 'inherit');
43
+
44
+ claim_policy
45
+ ------------
46
+ inherit
47
+ (1 row)
48
+
49
+ step a2_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000c1';
50
+ step a2_acq:
51
+ select resource_key
52
+ from agent_board.acquire_claim(null, 'file', 'cpr-scope-key-b2', now() + interval '400 days',
53
+ null, '40500000-0000-4000-8000-000000000042');
54
+
55
+ resource_key
56
+ ----------------
57
+ cpr-scope-key-b2
58
+ (1 row)
59
+
60
+ step w_commit: COMMIT;
61
+ step a2_commit: COMMIT;
62
+ step chk:
63
+ select resource_key, (lease_expires_at > now() + interval '31 days') as over_cap_lease
64
+ from agent_board.claims
65
+ where agent_id = '40500000-0000-4000-8000-000000000003'
66
+ and status = 'active' and released_at is null
67
+ order by resource_key;
68
+
69
+ resource_key |over_cap_lease
70
+ ----------------+--------------
71
+ cpr-scope-key-b2|t
72
+ (1 row)
73
+
@@ -0,0 +1,80 @@
1
+ Parsed test spec with 4 sessions
2
+
3
+ starting permutation: rc_begin rc_reclaim w_begin w_flip rc_commit w_commit chk
4
+ step rc_begin: BEGIN; SET LOCAL "request.jwt.claim.role" = 'service_role';
5
+ step rc_reclaim:
6
+ select owner_agent_id
7
+ from agent_board.reclaim_fleet_machine(
8
+ '40500000-0000-4000-8000-0000000000c0', '40500000-0000-4000-8000-00000000000b',
9
+ '40500000-0000-4000-8000-00000000000a', 'message-board#405 lock-order probe');
10
+
11
+ owner_agent_id
12
+ ------------------------------------
13
+ 40500000-0000-4000-8000-00000000000b
14
+ (1 row)
15
+
16
+ step w_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000f9';
17
+ step w_flip:
18
+ select claim_policy
19
+ from agent_board.set_board_claim_policy(
20
+ (select id from agent_board.boards where slug = 'cpr-inv-board'), 'inherit');
21
+
22
+ claim_policy
23
+ ------------
24
+ inherit
25
+ (1 row)
26
+
27
+ step rc_commit: COMMIT;
28
+ step w_commit: COMMIT;
29
+ step chk:
30
+ select (select claim_policy from agent_board.boards where slug = 'cpr-inv-board') as policy,
31
+ (select owner_agent_id from agent_board.fleet_machines
32
+ where id = '40500000-0000-4000-8000-0000000000c0') as machine_owner;
33
+
34
+ policy |machine_owner
35
+ -------+------------------------------------
36
+ inherit|40500000-0000-4000-8000-00000000000b
37
+ (1 row)
38
+
39
+
40
+ starting permutation: x_begin x_rowlock w_begin w_flip x_renew w_commit x_commit chk
41
+ step x_begin: BEGIN;
42
+ step x_rowlock: select resource_key from agent_board.claims where id = '40500000-0000-4000-8000-000000000091' for update;
43
+ resource_key
44
+ ------------
45
+ cpr-inv-key
46
+ (1 row)
47
+
48
+ step w_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000f9';
49
+ step w_flip:
50
+ select claim_policy
51
+ from agent_board.set_board_claim_policy(
52
+ (select id from agent_board.boards where slug = 'cpr-inv-board'), 'inherit');
53
+ <waiting ...>
54
+ step x_renew:
55
+ SET LOCAL ROLE authenticated;
56
+ SET LOCAL "request.jwt.claim.role" = 'authenticated';
57
+ SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000f9';
58
+ select resource_key
59
+ from agent_board.renew_claim('40500000-0000-4000-8000-0000000000f8', now() + interval '400 days');
60
+ <waiting ...>
61
+ step w_flip: <... completed>
62
+ ERROR: deadlock detected
63
+ step x_renew: <... completed>
64
+ resource_key
65
+ ------------
66
+ cpr-inv-key
67
+ (1 row)
68
+
69
+ step w_commit: COMMIT;
70
+ step x_commit: COMMIT;
71
+ step chk:
72
+ select (select claim_policy from agent_board.boards where slug = 'cpr-inv-board') as policy,
73
+ (select owner_agent_id from agent_board.fleet_machines
74
+ where id = '40500000-0000-4000-8000-0000000000c0') as machine_owner;
75
+
76
+ policy | machine_owner
77
+ --------+------------------------------------
78
+ advisory|40500000-0000-4000-8000-000000000009
79
+ (1 row)
80
+
@@ -0,0 +1,114 @@
1
+ Parsed test spec with 5 sessions
2
+
3
+ starting permutation: rr_begin rr_acq rr_rollback chk
4
+ step rr_begin: BEGIN ISOLATION LEVEL REPEATABLE READ; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000d1';
5
+ step rr_acq:
6
+ select resource_key
7
+ from agent_board.acquire_claim(null, 'file', 'cpr-iso-key', now() + interval '1 hour',
8
+ null, '40500000-0000-4000-8000-000000000050');
9
+
10
+ ERROR: claim policy enforcement requires transaction isolation level read committed (got repeatable read)
11
+ step rr_rollback: ROLLBACK;
12
+ step chk:
13
+ select coalesce(string_agg(resource_key, ',' order by resource_key), '<none>') as claims
14
+ from agent_board.claims
15
+ where agent_id = '40500000-0000-4000-8000-000000000004'
16
+ and status = 'active' and released_at is null;
17
+
18
+ claims
19
+ ------
20
+ <none>
21
+ (1 row)
22
+
23
+
24
+ starting permutation: rc_begin rc_acq rc_commit chk
25
+ step rc_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000d1';
26
+ step rc_acq:
27
+ select resource_key
28
+ from agent_board.acquire_claim(null, 'file', 'cpr-iso-key', now() + interval '1 hour',
29
+ null, '40500000-0000-4000-8000-000000000050');
30
+
31
+ resource_key
32
+ ------------
33
+ cpr-iso-key
34
+ (1 row)
35
+
36
+ step rc_commit: COMMIT;
37
+ step chk:
38
+ select coalesce(string_agg(resource_key, ',' order by resource_key), '<none>') as claims
39
+ from agent_board.claims
40
+ where agent_id = '40500000-0000-4000-8000-000000000004'
41
+ and status = 'active' and released_at is null;
42
+
43
+ claims
44
+ -----------
45
+ cpr-iso-key
46
+ (1 row)
47
+
48
+
49
+ starting permutation: rr_begin rr_machine rr_rollback chk
50
+ step rr_begin: BEGIN ISOLATION LEVEL REPEATABLE READ; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000d1';
51
+ step rr_machine:
52
+ select resource_key
53
+ from agent_board.acquire_claim(null, 'machine', 'cpr-iso-machine', now() + interval '1 hour',
54
+ null, null);
55
+
56
+ resource_key
57
+ ---------------
58
+ cpr-iso-machine
59
+ (1 row)
60
+
61
+ step rr_rollback: ROLLBACK;
62
+ step chk:
63
+ select coalesce(string_agg(resource_key, ',' order by resource_key), '<none>') as claims
64
+ from agent_board.claims
65
+ where agent_id = '40500000-0000-4000-8000-000000000004'
66
+ and status = 'active' and released_at is null;
67
+
68
+ claims
69
+ ------
70
+ <none>
71
+ (1 row)
72
+
73
+
74
+ starting permutation: sz_begin sz_acq sz_rollback chk
75
+ step sz_begin: BEGIN ISOLATION LEVEL SERIALIZABLE; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000d1';
76
+ step sz_acq:
77
+ select resource_key
78
+ from agent_board.acquire_claim(null, 'file', 'cpr-iso-key', now() + interval '1 hour',
79
+ null, '40500000-0000-4000-8000-000000000050');
80
+
81
+ ERROR: claim policy enforcement requires transaction isolation level read committed (got serializable)
82
+ step sz_rollback: ROLLBACK;
83
+ step chk:
84
+ select coalesce(string_agg(resource_key, ',' order by resource_key), '<none>') as claims
85
+ from agent_board.claims
86
+ where agent_id = '40500000-0000-4000-8000-000000000004'
87
+ and status = 'active' and released_at is null;
88
+
89
+ claims
90
+ ------
91
+ <none>
92
+ (1 row)
93
+
94
+
95
+ starting permutation: ru_begin ru_acq ru_rollback chk
96
+ step ru_begin: BEGIN ISOLATION LEVEL READ UNCOMMITTED; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000d1';
97
+ step ru_acq:
98
+ select resource_key
99
+ from agent_board.acquire_claim(null, 'file', 'cpr-iso-key', now() + interval '1 hour',
100
+ null, '40500000-0000-4000-8000-000000000050');
101
+
102
+ ERROR: claim policy enforcement requires transaction isolation level read committed (got read uncommitted)
103
+ step ru_rollback: ROLLBACK;
104
+ step chk:
105
+ select coalesce(string_agg(resource_key, ',' order by resource_key), '<none>') as claims
106
+ from agent_board.claims
107
+ where agent_id = '40500000-0000-4000-8000-000000000004'
108
+ and status = 'active' and released_at is null;
109
+
110
+ claims
111
+ ------
112
+ <none>
113
+ (1 row)
114
+
@@ -0,0 +1,66 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: w_begin w_flip w_mark r_begin r_renew w_commit r_commit chk
4
+ step w_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000b1';
5
+ step w_flip:
6
+ select claim_policy
7
+ from agent_board.set_board_claim_policy(
8
+ (select id from agent_board.boards where slug = 'cpr-ren-board'), 'inherit');
9
+
10
+ claim_policy
11
+ ------------
12
+ inherit
13
+ (1 row)
14
+
15
+ step w_mark:
16
+ insert into public.cpr_renew_mark(lease)
17
+ select lease_expires_at from agent_board.claims where id = '40500000-0000-4000-8000-000000000030';
18
+
19
+ step r_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000b1';
20
+ step r_renew:
21
+ select resource_key
22
+ from agent_board.renew_claim('40500000-0000-4000-8000-0000000000f1', now() + interval '400 days');
23
+ <waiting ...>
24
+ step w_commit: COMMIT;
25
+ step r_renew: <... completed>
26
+ resource_key
27
+ ------------
28
+ cpr-ren-key
29
+ (1 row)
30
+
31
+ step r_commit: COMMIT;
32
+ step chk:
33
+ select (c.lease_expires_at > now() + interval '31 days') as over_cap_lease,
34
+ (c.lease_expires_at = (select m.lease from public.cpr_renew_mark m)) as lease_kept_by_revert
35
+ from agent_board.claims c
36
+ where c.id = '40500000-0000-4000-8000-000000000030';
37
+
38
+ over_cap_lease|lease_kept_by_revert
39
+ --------------+--------------------
40
+ f |t
41
+ (1 row)
42
+
43
+
44
+ starting permutation: r_begin r_renew r_commit chk
45
+ step r_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000b1';
46
+ step r_renew:
47
+ select resource_key
48
+ from agent_board.renew_claim('40500000-0000-4000-8000-0000000000f1', now() + interval '400 days');
49
+
50
+ resource_key
51
+ ------------
52
+ cpr-ren-key
53
+ (1 row)
54
+
55
+ step r_commit: COMMIT;
56
+ step chk:
57
+ select (c.lease_expires_at > now() + interval '31 days') as over_cap_lease,
58
+ (c.lease_expires_at = (select m.lease from public.cpr_renew_mark m)) as lease_kept_by_revert
59
+ from agent_board.claims c
60
+ where c.id = '40500000-0000-4000-8000-000000000030';
61
+
62
+ over_cap_lease|lease_kept_by_revert
63
+ --------------+--------------------
64
+ t |
65
+ (1 row)
66
+
@@ -0,0 +1,49 @@
1
+ Parsed test spec with 3 sessions
2
+
3
+ starting permutation: t_begin t_keyshare w_begin w_flip t_acq w_commit t_commit chk
4
+ step t_begin: BEGIN;
5
+ step t_keyshare: select slug from agent_board.boards where slug = 'cpr-ks-board' for key share;
6
+ slug
7
+ ------------
8
+ cpr-ks-board
9
+ (1 row)
10
+
11
+ step w_begin: BEGIN; SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claim.role" = 'authenticated'; SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000e1';
12
+ step w_flip:
13
+ select claim_policy
14
+ from agent_board.set_board_claim_policy(
15
+ (select id from agent_board.boards where slug = 'cpr-ks-board'), 'inherit');
16
+
17
+ claim_policy
18
+ ------------
19
+ inherit
20
+ (1 row)
21
+
22
+ step t_acq:
23
+ SET LOCAL ROLE authenticated;
24
+ SET LOCAL "request.jwt.claim.role" = 'authenticated';
25
+ SET LOCAL "request.jwt.claim.sub" = '40500000-0000-4000-8000-0000000000e1';
26
+ select resource_key
27
+ from agent_board.acquire_claim(null, 'file', 'cpr-ks-key', now() + interval '1 hour',
28
+ null, '40500000-0000-4000-8000-000000000060');
29
+ <waiting ...>
30
+ step w_commit: COMMIT;
31
+ step t_acq: <... completed>
32
+ resource_key
33
+ ------------
34
+ cpr-ks-key
35
+ (1 row)
36
+
37
+ step t_commit: COMMIT;
38
+ step chk:
39
+ select coalesce(string_agg(resource_key, ',' order by resource_key), '<none>') as claims,
40
+ (select claim_policy from agent_board.boards where slug = 'cpr-ks-board') as policy
41
+ from agent_board.claims
42
+ where agent_id = '40500000-0000-4000-8000-000000000005'
43
+ and status = 'active' and released_at is null;
44
+
45
+ claims |policy
46
+ ----------+-------
47
+ cpr-ks-key|inherit
48
+ (1 row)
49
+