borgmcp-shared 0.13.1 → 1.0.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 (44) hide show
  1. package/README.md +4 -3
  2. package/RELEASES.md +7 -0
  3. package/dist/conformance/adapter.d.ts +15 -2
  4. package/dist/conformance/adapter.d.ts.map +1 -1
  5. package/dist/conformance/adapter.js +187 -41
  6. package/dist/conformance/adapter.js.map +1 -1
  7. package/dist/conformance/index.d.ts +49 -3
  8. package/dist/conformance/index.d.ts.map +1 -1
  9. package/dist/conformance/index.js +95 -18
  10. package/dist/conformance/index.js.map +1 -1
  11. package/dist/protocol/contract.d.ts +1 -1
  12. package/dist/protocol/contract.d.ts.map +1 -1
  13. package/dist/protocol/contract.js +13 -18
  14. package/dist/protocol/contract.js.map +1 -1
  15. package/dist/protocol/coordination.d.ts +36 -1
  16. package/dist/protocol/coordination.d.ts.map +1 -1
  17. package/dist/protocol/coordination.js +72 -6
  18. package/dist/protocol/coordination.js.map +1 -1
  19. package/dist/protocol/errors.d.ts +1 -1
  20. package/dist/protocol/errors.d.ts.map +1 -1
  21. package/dist/protocol/errors.js +1 -1
  22. package/dist/protocol/errors.js.map +1 -1
  23. package/dist/protocol/types.d.ts +1 -5
  24. package/dist/protocol/types.d.ts.map +1 -1
  25. package/dist/protocol/version.d.ts +1 -1
  26. package/dist/protocol/version.js +1 -1
  27. package/dist/templates.d.ts +1 -3
  28. package/dist/templates.d.ts.map +1 -1
  29. package/dist/templates.js +8 -79
  30. package/dist/templates.js.map +1 -1
  31. package/docs/compatibility.md +15 -0
  32. package/docs/release-records.json +28 -0
  33. package/docs/releases/0.14.0.md +8 -0
  34. package/docs/releases/1.0.0.md +11 -0
  35. package/docs/template-lifecycle.md +1 -1
  36. package/package.json +1 -1
  37. package/src/conformance/adapter.ts +369 -50
  38. package/src/conformance/index.ts +122 -22
  39. package/src/protocol/contract.ts +15 -21
  40. package/src/protocol/coordination.ts +136 -7
  41. package/src/protocol/errors.ts +1 -1
  42. package/src/protocol/types.ts +1 -5
  43. package/src/protocol/version.ts +2 -2
  44. package/src/templates.ts +8 -81
@@ -6,6 +6,8 @@ import {
6
6
  decodeCreateCubeResponseEnvelope,
7
7
  decodeDeleteCubeResponseEnvelope,
8
8
  decodeAppendLogResultEnvelope,
9
+ decodeAckStatusResultEnvelope,
10
+ decodeEntryQueryResultEnvelope,
9
11
  decodeDecisionResultEnvelope,
10
12
  decodeDecisionsResultEnvelope,
11
13
  decodeEnrollmentExchangeResponseEnvelope,
@@ -40,6 +42,7 @@ import {
40
42
  type StreamEvent,
41
43
  type DroneRuntimeMetadata,
42
44
  type CubeDocument,
45
+ type EnrichedStreamEntry,
43
46
  } from '../protocol/index.js';
44
47
  import {
45
48
  APPEND_LOG_IDEMPOTENCY_CONFORMANCE,
@@ -119,6 +122,9 @@ export interface ConformanceCubeManagementState {
119
122
  export interface ConformanceAuthorityState {
120
123
  enrolled_clients: number;
121
124
  enrollment_claims: number;
125
+ activity_acknowledgements: number;
126
+ activity_claims: number;
127
+ activity_log_entries: number;
122
128
  cubes: number;
123
129
  roles: number;
124
130
  grants: number;
@@ -214,12 +220,7 @@ export interface ConformanceAdmin {
214
220
  readonly isDefault?: boolean;
215
221
  readonly isMandatory?: boolean;
216
222
  }): Promise<ConformanceRole>;
217
- referenceRoleFromTaxonomy(cube: ConformanceCube, role: ConformanceRole): Promise<void>;
218
- configureMessageClassRouting(
219
- cube: ConformanceCube,
220
- className: string,
221
- recipientDroneIds: readonly string[],
222
- ): Promise<void>;
223
+ replaceLogEntryId(cube: ConformanceCube, currentId: string, replacementId: string): Promise<void>;
223
224
  createDrone(
224
225
  principal: ConformancePrincipal,
225
226
  cube: ConformanceCube,
@@ -299,11 +300,21 @@ export interface ConformanceOperations {
299
300
  cube: ConformanceCube,
300
301
  request: unknown,
301
302
  ): Promise<ConformanceHttpResponse>;
303
+ entryQuery(
304
+ credential: string,
305
+ cube: ConformanceCube,
306
+ request: unknown,
307
+ ): Promise<ConformanceHttpResponse>;
302
308
  ack(
303
309
  credential: string,
304
310
  cube: ConformanceCube,
305
311
  request: unknown,
306
312
  ): Promise<ConformanceHttpResponse>;
313
+ ackStatus(
314
+ credential: string,
315
+ cube: ConformanceCube,
316
+ request: unknown,
317
+ ): Promise<ConformanceHttpResponse>;
307
318
  updateCube(
308
319
  credential: string,
309
320
  cube: ConformanceCube,
@@ -379,10 +390,13 @@ export const ADAPTER_CONFORMANCE_FIXTURES = [
379
390
  { id: 'security.oversize-request', area: 'security' },
380
391
  { id: 'security.cross-cube-isolation', area: 'security' },
381
392
  { id: 'log.append-idempotency', area: 'log' },
393
+ { id: 'log.mandatory-addressing', area: 'log' },
394
+ { id: 'log.entry-query', area: 'log' },
382
395
  { id: 'log.read-cursor-tuple', area: 'cursor' },
383
396
  { id: 'sse.replay-live-transition', area: 'sse' },
384
397
  { id: 'cursor.explicit-expiry', area: 'cursor' },
385
398
  { id: 'acks.idempotent', area: 'acks' },
399
+ { id: 'acks.status-query', area: 'acks' },
386
400
  { id: 'claims.durable-noncursor', area: 'claims' },
387
401
  { id: 'decisions.topic-supersession', area: 'decisions' },
388
402
  { id: 'security.drone-management-authorization', area: 'security' },
@@ -1468,6 +1482,7 @@ export async function runAdapterConformance(
1468
1482
  documentCube,
1469
1483
  createProtocolEnvelope('document-foreign-citation', {
1470
1484
  post_id: '00000000-0000-4000-8000-00000000031f',
1485
+ to: 'broadcast',
1471
1486
  message: 'Foreign evidence must remain hidden.',
1472
1487
  documents: [foreignDocument.id],
1473
1488
  }),
@@ -1509,6 +1524,7 @@ export async function runAdapterConformance(
1509
1524
  documentCube,
1510
1525
  createProtocolEnvelope('document-citation', {
1511
1526
  post_id: '00000000-0000-4000-8000-000000000320',
1527
+ to: 'broadcast',
1512
1528
  message: 'See the document evidence.',
1513
1529
  documents: [created.id, successor.id],
1514
1530
  }),
@@ -1521,6 +1537,7 @@ export async function runAdapterConformance(
1521
1537
  documentCube,
1522
1538
  createProtocolEnvelope('document-citation-conflict', {
1523
1539
  post_id: '00000000-0000-4000-8000-000000000320',
1540
+ to: 'broadcast',
1524
1541
  message: 'See the document evidence.',
1525
1542
  documents: [successor.id],
1526
1543
  }),
@@ -1628,14 +1645,14 @@ export async function runAdapterConformance(
1628
1645
  documentCredential,
1629
1646
  documentCube,
1630
1647
  createProtocolEnvelope('document-unknown-citation', {
1631
- post_id: '00000000-0000-4000-8000-000000000321', message: 'Unknown.', documents: ['unknown_full_id'],
1648
+ post_id: '00000000-0000-4000-8000-000000000321', message: 'Unknown.', to: 'broadcast', documents: ['unknown_full_id'],
1632
1649
  }),
1633
1650
  ), 404, ErrorCode.DOCUMENT_NOT_FOUND, 'Unknown document citation');
1634
1651
  const longAccepted = await environment.operations.append(
1635
1652
  documentCredential,
1636
1653
  documentCube,
1637
1654
  createProtocolEnvelope('document-advisory', {
1638
- post_id: '00000000-0000-4000-8000-000000000322', message: 'x'.repeat(1025),
1655
+ post_id: '00000000-0000-4000-8000-000000000322', message: 'x'.repeat(1025), to: 'broadcast',
1639
1656
  }),
1640
1657
  );
1641
1658
  expectStatus(longAccepted, 201, 'Advisory log append');
@@ -1644,7 +1661,7 @@ export async function runAdapterConformance(
1644
1661
  documentCredential,
1645
1662
  documentCube,
1646
1663
  createProtocolEnvelope('document-hard-cap', {
1647
- post_id: '00000000-0000-4000-8000-000000000323', message: 'x'.repeat(4097),
1664
+ post_id: '00000000-0000-4000-8000-000000000323', message: 'x'.repeat(4097), to: 'broadcast',
1648
1665
  }),
1649
1666
  ), 413, ErrorCode.CONTENT_TOO_LARGE, 'Hard-cap log append');
1650
1667
  const citedRead = decodeReadLogResultEnvelope((await environment.operations.read(
@@ -1671,6 +1688,7 @@ export async function runAdapterConformance(
1671
1688
  const injectedBody = JSON.stringify(
1672
1689
  createProtocolEnvelope('inject-b1', {
1673
1690
  post_id: '00000000-0000-4000-8000-000000000301',
1691
+ to: 'broadcast',
1674
1692
  message: injectedMessage,
1675
1693
  }),
1676
1694
  );
@@ -1693,6 +1711,7 @@ export async function runAdapterConformance(
1693
1711
  cubeB,
1694
1712
  createProtocolEnvelope('inject-b2', {
1695
1713
  post_id: '00000000-0000-4000-8000-000000000302',
1714
+ to: 'broadcast',
1696
1715
  message: 'post-injection-sentinel',
1697
1716
  }),
1698
1717
  );
@@ -1743,6 +1762,7 @@ export async function runAdapterConformance(
1743
1762
  const baseBody = JSON.stringify(
1744
1763
  createProtocolEnvelope('oversize-a1', {
1745
1764
  post_id: '00000000-0000-4000-8000-000000000303',
1765
+ to: 'broadcast',
1746
1766
  message: 'must-not-persist',
1747
1767
  }),
1748
1768
  );
@@ -1775,6 +1795,7 @@ export async function runAdapterConformance(
1775
1795
  createProtocolEnvelope('append-b1', {
1776
1796
  post_id: '00000000-0000-4000-8000-000000000304',
1777
1797
  message: 'principal-b-secret',
1798
+ to: 'broadcast',
1778
1799
  }),
1779
1800
  );
1780
1801
  expectStatus(secretAppend, 201, 'Principal B append');
@@ -1801,16 +1822,12 @@ export async function runAdapterConformance(
1801
1822
  });
1802
1823
  const firstRecipient = await environment.admin.createDrone(principal, cube, role);
1803
1824
  const secondRecipient = await environment.admin.createDrone(principal, cube, role);
1804
- await environment.admin.configureMessageClassRouting(cube, 'conformance-review', [firstRecipient.id]);
1805
- await environment.admin.configureMessageClassRouting(cube, 'conformance-security', [firstRecipient.id]);
1806
1825
  const postId = '00000000-0000-4000-8000-000000000313';
1807
1826
  const initialPayload = {
1808
1827
  post_id: postId,
1809
1828
  message: 'once',
1810
- visibility: 'direct' as const,
1811
- recipientDroneIds: [firstRecipient.id],
1829
+ to: [firstRecipient.id],
1812
1830
  class: 'review',
1813
- to: ['Coordinator'],
1814
1831
  };
1815
1832
  const first = await environment.operations.append(
1816
1833
  credential,
@@ -1821,32 +1838,12 @@ export async function runAdapterConformance(
1821
1838
  const firstResult = decodeAppendLogResultEnvelope(first.body).payload;
1822
1839
  invariant(!firstResult.deduplicated, 'Initial append was reported as deduplicated.');
1823
1840
  for (const [index, vector] of APPEND_LOG_IDEMPOTENCY_CONFORMANCE.entries()) {
1824
- const classRouting = vector.mutation === 'resolved_class_routing';
1825
- const vectorPostId = classRouting
1826
- ? '00000000-0000-4000-8000-000000000314'
1827
- : postId;
1828
- if (classRouting) {
1829
- const baseline = await environment.operations.append(
1830
- credential,
1831
- cube,
1832
- createProtocolEnvelope('append-idempotency-class-baseline', {
1833
- post_id: vectorPostId,
1834
- message: 'once',
1835
- class: 'conformance-review',
1836
- }),
1837
- );
1838
- expectStatus(baseline, 201, 'Resolved class-routing baseline');
1839
- }
1840
- const payload = classRouting ? {
1841
- post_id: vectorPostId,
1842
- message: 'once',
1843
- class: 'conformance-security',
1844
- } : {
1841
+ const payload = {
1845
1842
  ...initialPayload,
1846
1843
  ...(vector.mutation === 'message' ? { message: 'changed' } : {}),
1847
- ...(vector.mutation === 'visibility' ? { visibility: 'broadcast' as const } : {}),
1848
- ...(vector.mutation === 'recipient_set' ? { recipientDroneIds: [secondRecipient.id] } : {}),
1849
- ...(vector.mutation === 'ignored_request_shape' ? { class: 'security', to: ['Security Auditor'] } : {}),
1844
+ ...(vector.mutation === 'audience' ? { to: 'broadcast' as const } : {}),
1845
+ ...(vector.mutation === 'recipient_set' ? { to: [secondRecipient.id] } : {}),
1846
+ ...(vector.mutation === 'classification' ? { class: 'security' } : {}),
1850
1847
  };
1851
1848
  const response = await environment.operations.append(
1852
1849
  vector.actor === 'same' ? credential : otherCredential,
@@ -1874,10 +1871,207 @@ export async function runAdapterConformance(
1874
1871
  );
1875
1872
  expectStatus(read, 200, 'Idempotent append read');
1876
1873
  invariant(
1877
- decodeReadLogResultEnvelope(read.body).payload.entries.length === 3,
1874
+ decodeReadLogResultEnvelope(read.body).payload.entries.length === 2,
1878
1875
  'Append collision controls persisted the wrong number of entries.',
1879
1876
  );
1880
- return { persisted_entries: 3, stable_entry: true, deduplicated: true, conflicts: 4, cross_author_created: true };
1877
+ return { persisted_entries: 2, stable_entry: true, deduplicated: true, conflicts: 3, cross_author_created: true };
1878
+ });
1879
+
1880
+ await record('log.mandatory-addressing', async () => {
1881
+ const cube = await environment.admin.createCube('mandatory-addressing-cube');
1882
+ await environment.admin.grantCube(principalA, cube);
1883
+ const role = await environment.admin.createRole(cube, {
1884
+ roleClass: 'worker',
1885
+ isHumanSeat: false,
1886
+ name: 'Structured Recipient',
1887
+ });
1888
+ const recipient = await environment.admin.createDrone(principalA, cube, role);
1889
+ const message = 'START NOW exact slice';
1890
+ const before = await environment.admin.observeAuthorityState();
1891
+ expectError(
1892
+ await environment.operations.append(
1893
+ credentialA,
1894
+ cube,
1895
+ createProtocolEnvelope('mandatory-addressing-omitted', {
1896
+ post_id: '30000000-0000-4000-8000-000000000001',
1897
+ message,
1898
+ }),
1899
+ ),
1900
+ 400,
1901
+ ErrorCode.INVALID_INPUT,
1902
+ 'Omitted mandatory addressing',
1903
+ );
1904
+ assertStateDelta(before, await environment.admin.observeAuthorityState(), {}, 'Mandatory-addressing refusal');
1905
+
1906
+ const broadcast = await environment.operations.append(
1907
+ credentialA,
1908
+ cube,
1909
+ createProtocolEnvelope('mandatory-addressing-broadcast', {
1910
+ post_id: '30000000-0000-4000-8000-000000000002',
1911
+ message,
1912
+ to: 'broadcast',
1913
+ }),
1914
+ );
1915
+ expectStatus(broadcast, 201, 'Explicit broadcast addressing');
1916
+ const broadcastResult = decodeAppendLogResultEnvelope(broadcast.body).payload;
1917
+ invariant(broadcastResult.entry.visibility === 'broadcast', 'Scalar broadcast did not produce broadcast visibility.');
1918
+ invariant(
1919
+ (broadcastResult.entry.recipient_drone_ids?.length ?? 0) === 0,
1920
+ 'Scalar broadcast unexpectedly resolved direct recipients.',
1921
+ );
1922
+ const directed = await environment.operations.append(
1923
+ credentialA,
1924
+ cube,
1925
+ createProtocolEnvelope('mandatory-addressing-directed', {
1926
+ post_id: '30000000-0000-4000-8000-000000000003',
1927
+ message,
1928
+ to: [recipient.id],
1929
+ class: 'cube-wide',
1930
+ }),
1931
+ );
1932
+ expectStatus(directed, 201, 'Recipient-selector union branch');
1933
+ const directedResult = decodeAppendLogResultEnvelope(directed.body).payload;
1934
+ invariant(directedResult.entry.visibility === 'direct', 'Explicit recipient selectors did not produce direct visibility.');
1935
+ invariant(
1936
+ directedResult.entry.recipient_drone_ids?.includes(recipient.id) === true,
1937
+ 'Explicit recipient selectors did not resolve the named recipient.',
1938
+ );
1939
+ invariant(
1940
+ directedResult.entry.recipient_drone_ids?.length === 1,
1941
+ 'Classification changed or fell open the explicit recipient set.',
1942
+ );
1943
+ return { refused_status: 400, code: ErrorCode.INVALID_INPUT, persisted: false, broadcast: true, union: true, no_fall_open: true };
1944
+ });
1945
+
1946
+ await record('log.entry-query', async () => {
1947
+ const cube = await environment.admin.createCube('entry-query-cube');
1948
+ await environment.admin.grantCube(principalA, cube);
1949
+ const role = await environment.admin.createRole(cube, {
1950
+ roleClass: 'worker',
1951
+ isHumanSeat: false,
1952
+ name: 'Entry Query Worker',
1953
+ });
1954
+ const acknowledgedRecipient = await environment.admin.createDrone(principalA, cube, role);
1955
+ const claimingRecipient = await environment.admin.createDrone(principalA, cube, role);
1956
+ const acknowledgedCredential = await environment.admin.issueManagedDroneSession(acknowledgedRecipient);
1957
+ const claimingCredential = await environment.admin.issueManagedDroneSession(claimingRecipient);
1958
+ const ids = [
1959
+ '11111111-0000-4000-8000-000000000001',
1960
+ '22222222-0000-4000-8000-000000000001',
1961
+ '22222222-0000-4000-8000-000000000002',
1962
+ ];
1963
+ const entries: EnrichedStreamEntry[] = [];
1964
+ for (const [index, id] of ids.entries()) {
1965
+ const response = await environment.operations.append(
1966
+ credentialA,
1967
+ cube,
1968
+ createProtocolEnvelope(`entry-query-append-${index}`, {
1969
+ post_id: `40000000-0000-4000-8000-00000000000${index}`,
1970
+ to: [acknowledgedRecipient.id, claimingRecipient.id],
1971
+ message: `entry-query-${index}`,
1972
+ }),
1973
+ );
1974
+ expectStatus(response, 201, `Entry-query fixture append ${index}`);
1975
+ const entry = decodeAppendLogResultEnvelope(response.body).payload.entry;
1976
+ await environment.admin.replaceLogEntryId(cube, entry.id, id);
1977
+ entries.push({ ...entry, id });
1978
+ }
1979
+
1980
+ expectStatus(
1981
+ await environment.operations.ack(
1982
+ acknowledgedCredential,
1983
+ cube,
1984
+ createProtocolEnvelope('entry-query-seed-ack', { entry_id: ids[0], kind: 'ack' }),
1985
+ ),
1986
+ 204,
1987
+ 'Entry-query acknowledgement seed',
1988
+ );
1989
+ expectStatus(
1990
+ await environment.operations.ack(
1991
+ claimingCredential,
1992
+ cube,
1993
+ createProtocolEnvelope('entry-query-seed-claim', { entry_id: ids[1], kind: 'claim' }),
1994
+ ),
1995
+ 204,
1996
+ 'Entry-query claim seed',
1997
+ );
1998
+
1999
+ const before = await environment.admin.observeAuthorityState();
2000
+ for (const [requestId, selector, expectedId] of [
2001
+ ['entry-query-full', ids[1], ids[1]],
2002
+ ['entry-query-prefix', '11111111', ids[0]],
2003
+ ] as const) {
2004
+ const response = await environment.operations.entryQuery(
2005
+ credentialA,
2006
+ cube,
2007
+ createProtocolEnvelope(requestId, { entry_id: selector }),
2008
+ );
2009
+ expectStatus(response, 200, requestId);
2010
+ const result = decodeEntryQueryResultEnvelope(response.body).payload;
2011
+ invariant(result.entry.id === expectedId, `${requestId} returned the wrong entry.`);
2012
+ }
2013
+ expectError(
2014
+ await environment.operations.entryQuery(
2015
+ credentialA,
2016
+ cube,
2017
+ createProtocolEnvelope('entry-query-unknown', { entry_id: 'ffffffff' }),
2018
+ ),
2019
+ 404,
2020
+ ErrorCode.NOT_FOUND,
2021
+ 'Unknown entry query',
2022
+ );
2023
+ expectError(
2024
+ await environment.operations.entryQuery(
2025
+ credentialA,
2026
+ cube,
2027
+ createProtocolEnvelope('entry-query-ambiguous', { entry_id: '22222222' }),
2028
+ ),
2029
+ 409,
2030
+ ErrorCode.LOG_ENTRY_PREFIX_AMBIGUOUS,
2031
+ 'Ambiguous entry query',
2032
+ );
2033
+ assertStateDelta(before, await environment.admin.observeAuthorityState(), {}, 'Entry query');
2034
+ const acknowledgementStatus = decodeAckStatusResultEnvelope((await environment.operations.ackStatus(
2035
+ credentialA,
2036
+ cube,
2037
+ createProtocolEnvelope('entry-query-ack-status', { entry_id: ids[0] }),
2038
+ )).body).payload;
2039
+ const claimStatus = decodeAckStatusResultEnvelope((await environment.operations.ackStatus(
2040
+ credentialA,
2041
+ cube,
2042
+ createProtocolEnvelope('entry-query-claim-status', { entry_id: ids[1] }),
2043
+ )).body).payload;
2044
+ invariant(
2045
+ acknowledgementStatus.recipients.some((recipient) =>
2046
+ recipient.drone_id === acknowledgedRecipient.id && recipient.acknowledged_at !== null
2047
+ ),
2048
+ 'Entry query removed the seeded acknowledgement.',
2049
+ );
2050
+ invariant(
2051
+ claimStatus.claims.some((claim) => claim.drone_id === claimingRecipient.id),
2052
+ 'Entry query removed the seeded claim.',
2053
+ );
2054
+ const unreadResponse = await environment.operations.read(
2055
+ credentialA,
2056
+ cube,
2057
+ createProtocolEnvelope('entry-query-unread', { cursor: null, limit: 10 }),
2058
+ );
2059
+ expectStatus(unreadResponse, 200, 'Entry-query unread read');
2060
+ const unread = decodeReadLogResultEnvelope(unreadResponse.body).payload;
2061
+ invariant(
2062
+ unread.entries.length === entries.length && entries.every((entry) => unread.entries.some((item) => item.id === entry.id)),
2063
+ 'Entry query consumed or advanced unread activity.',
2064
+ );
2065
+ return {
2066
+ full_uuid: true,
2067
+ unique_prefix: true,
2068
+ ambiguous_code: ErrorCode.LOG_ENTRY_PREFIX_AMBIGUOUS,
2069
+ unknown_code: ErrorCode.NOT_FOUND,
2070
+ unread_entry_available: true,
2071
+ acknowledgement_preserved: true,
2072
+ claim_preserved: true,
2073
+ mutation: 'none',
2074
+ };
1881
2075
  });
1882
2076
 
1883
2077
  const entries: Array<{ id: string; created_at: string; message: string }> = [];
@@ -1889,6 +2083,7 @@ export async function runAdapterConformance(
1889
2083
  cubeA,
1890
2084
  createProtocolEnvelope(`append-a${index + 1}`, {
1891
2085
  post_id: `00000000-0000-4000-8000-${String(305 + index).padStart(12, '0')}`,
2086
+ to: 'broadcast',
1892
2087
  message,
1893
2088
  }),
1894
2089
  );
@@ -1926,6 +2121,7 @@ export async function runAdapterConformance(
1926
2121
  cubeA,
1927
2122
  createProtocolEnvelope('append-a4', {
1928
2123
  post_id: '00000000-0000-4000-8000-000000000308',
2124
+ to: 'broadcast',
1929
2125
  message: 'delta',
1930
2126
  }),
1931
2127
  );
@@ -1953,6 +2149,7 @@ export async function runAdapterConformance(
1953
2149
  cubeA,
1954
2150
  createProtocolEnvelope('append-a5', {
1955
2151
  post_id: '00000000-0000-4000-8000-000000000309',
2152
+ to: 'broadcast',
1956
2153
  message: 'epsilon',
1957
2154
  }),
1958
2155
  );
@@ -1994,6 +2191,135 @@ export async function runAdapterConformance(
1994
2191
  return { first_status: 204, repeated_status: 204, bodyless: true };
1995
2192
  });
1996
2193
 
2194
+ await record('acks.status-query', async () => {
2195
+ const cube = await environment.admin.createCube('ack-status-cube');
2196
+ await environment.admin.grantCube(principalA, cube);
2197
+ const role = await environment.admin.createRole(cube, {
2198
+ roleClass: 'worker',
2199
+ isHumanSeat: false,
2200
+ name: 'Ack Status Worker',
2201
+ });
2202
+ const acknowledgedRecipient = await environment.admin.createDrone(principalA, cube, role);
2203
+ const claimingRecipient = await environment.admin.createDrone(principalA, cube, role);
2204
+ const acknowledgedCredential = await environment.admin.issueManagedDroneSession(acknowledgedRecipient);
2205
+ const claimingCredential = await environment.admin.issueManagedDroneSession(claimingRecipient);
2206
+ const append = await environment.operations.append(
2207
+ credentialA,
2208
+ cube,
2209
+ createProtocolEnvelope('ack-status-append', {
2210
+ post_id: '00000000-0000-4000-8000-000000000310',
2211
+ to: [acknowledgedRecipient.id, claimingRecipient.id],
2212
+ message: 'START NOW acknowledgement status fixture',
2213
+ }),
2214
+ );
2215
+ expectStatus(append, 201, 'Acknowledgement-status fixture append');
2216
+ const entry = decodeAppendLogResultEnvelope(append.body).payload.entry;
2217
+
2218
+ const beforeMissing = await environment.admin.observeAuthorityState();
2219
+ const missingResponse = await environment.operations.ackStatus(
2220
+ credentialA,
2221
+ cube,
2222
+ createProtocolEnvelope('ack-status-missing', { entry_id: entry.id }),
2223
+ );
2224
+ expectStatus(missingResponse, 200, 'Missing acknowledgement status');
2225
+ const missing = decodeAckStatusResultEnvelope(missingResponse.body).payload;
2226
+ invariant(
2227
+ missing.visibility === 'direct' &&
2228
+ missing.recipients.length === 2 &&
2229
+ missing.recipients.every((recipient) => recipient.acknowledged_at === null) &&
2230
+ missing.claims.length === 0,
2231
+ 'Missing acknowledgement was not represented as nullable per-recipient state.',
2232
+ );
2233
+ assertStateDelta(
2234
+ beforeMissing,
2235
+ await environment.admin.observeAuthorityState(),
2236
+ {},
2237
+ 'Missing acknowledgement status query',
2238
+ );
2239
+
2240
+ expectStatus(
2241
+ await environment.operations.ack(
2242
+ acknowledgedCredential,
2243
+ cube,
2244
+ createProtocolEnvelope('ack-status-ack', { entry_id: entry.id, kind: 'ack' }),
2245
+ ),
2246
+ 204,
2247
+ 'Acknowledgement-status acknowledgement',
2248
+ );
2249
+ expectStatus(
2250
+ await environment.operations.ack(
2251
+ claimingCredential,
2252
+ cube,
2253
+ createProtocolEnvelope('ack-status-claim', { entry_id: entry.id, kind: 'claim' }),
2254
+ ),
2255
+ 204,
2256
+ 'Acknowledgement-status claim',
2257
+ );
2258
+ const beforeDistinct = await environment.admin.observeAuthorityState();
2259
+ const distinctResponse = await environment.operations.ackStatus(
2260
+ credentialA,
2261
+ cube,
2262
+ createProtocolEnvelope('ack-status-distinct', { entry_id: entry.id }),
2263
+ );
2264
+ expectStatus(distinctResponse, 200, 'Distinct acknowledgement and claim status');
2265
+ const distinct = decodeAckStatusResultEnvelope(distinctResponse.body).payload;
2266
+ const acknowledged = distinct.recipients.filter((recipient) => recipient.acknowledged_at !== null);
2267
+ invariant(
2268
+ acknowledged.length === 1 &&
2269
+ acknowledged[0].drone_id === acknowledgedRecipient.id &&
2270
+ distinct.claims.length === 1 &&
2271
+ distinct.claims[0].drone_id === claimingRecipient.id &&
2272
+ distinct.recipients.find((recipient) => recipient.drone_id === claimingRecipient.id)?.acknowledged_at === null,
2273
+ 'Acknowledgement status conflated acknowledgement and claim records.',
2274
+ );
2275
+ assertStateDelta(
2276
+ beforeDistinct,
2277
+ await environment.admin.observeAuthorityState(),
2278
+ {},
2279
+ 'Distinct acknowledgement and claim status query',
2280
+ );
2281
+
2282
+ const unreadResponse = await environment.operations.read(
2283
+ credentialA,
2284
+ cube,
2285
+ createProtocolEnvelope('ack-status-unread', { cursor: null, limit: 10 }),
2286
+ );
2287
+ expectStatus(unreadResponse, 200, 'Post-query unread read');
2288
+ const unread = decodeReadLogResultEnvelope(unreadResponse.body).payload;
2289
+ invariant(
2290
+ unread.entries.length === 1 && unread.entries[0].id === entry.id,
2291
+ 'Acknowledgement-status query advanced unread delivery state.',
2292
+ );
2293
+
2294
+ const beforeUnknown = await environment.admin.observeAuthorityState();
2295
+ expectError(
2296
+ await environment.operations.ackStatus(
2297
+ credentialA,
2298
+ cube,
2299
+ createProtocolEnvelope('ack-status-unknown', {
2300
+ entry_id: '00000000-0000-4000-8000-000000000311',
2301
+ }),
2302
+ ),
2303
+ 404,
2304
+ ErrorCode.NOT_FOUND,
2305
+ 'Unknown acknowledgement-status entry',
2306
+ );
2307
+ assertStateDelta(
2308
+ beforeUnknown,
2309
+ await environment.admin.observeAuthorityState(),
2310
+ {},
2311
+ 'Unknown acknowledgement status query',
2312
+ );
2313
+ return {
2314
+ missing_acknowledgement: null,
2315
+ acknowledgements: 1,
2316
+ claims: 1,
2317
+ distinct: true,
2318
+ unread_entry_available: true,
2319
+ unknown_entry_code: ErrorCode.NOT_FOUND,
2320
+ };
2321
+ });
2322
+
1997
2323
  await record('claims.durable-noncursor', async () => {
1998
2324
  invariant(entries[1] && liveCursor, 'Log fixtures did not produce claim state.');
1999
2325
  const claim = await environment.operations.ack(
@@ -2475,8 +2801,7 @@ export async function runAdapterConformance(
2475
2801
  createProtocolEnvelope('evict-direct-target', {
2476
2802
  message: 'must-not-fan-out',
2477
2803
  post_id: '00000000-0000-4000-8000-000000000310',
2478
- visibility: 'direct',
2479
- recipientDroneIds: [evictedDrone.id],
2804
+ to: [evictedDrone.id],
2480
2805
  }),
2481
2806
  ),
2482
2807
  404,
@@ -2556,12 +2881,6 @@ export async function runAdapterConformance(
2556
2881
  isHumanSeat: true,
2557
2882
  name: 'Human Seat',
2558
2883
  });
2559
- const referencedRole = await environment.admin.createRole(roleContractCube, {
2560
- roleClass: 'worker',
2561
- isHumanSeat: false,
2562
- name: 'Routed Reviewer',
2563
- });
2564
- await environment.admin.referenceRoleFromTaxonomy(roleContractCube, referencedRole);
2565
2884
  const activeRole = await environment.admin.createRole(roleContractCube, {
2566
2885
  roleClass: 'worker',
2567
2886
  isHumanSeat: false,
@@ -2573,7 +2892,6 @@ export async function runAdapterConformance(
2573
2892
  [defaultRole, ErrorCode.DEFAULT_ROLE_REQUIRED, 'default role'],
2574
2893
  [mandatoryRole, ErrorCode.ROLE_REQUIRED, 'mandatory role'],
2575
2894
  [humanRole, ErrorCode.ROLE_REQUIRED, 'human-seat role'],
2576
- [referencedRole, ErrorCode.ROLE_REFERENCED, 'taxonomy-referenced role'],
2577
2895
  [activeRole, ErrorCode.ROLE_IN_USE, 'active-drone role'],
2578
2896
  ] as const;
2579
2897
  for (const [index, [role, code, label]] of refusals.entries()) {
@@ -2666,6 +2984,7 @@ export async function runAdapterConformance(
2666
2984
  roleContractCube,
2667
2985
  createProtocolEnvelope('role-delete-attribution', {
2668
2986
  post_id: '00000000-0000-4000-8000-000000000311',
2987
+ to: 'broadcast',
2669
2988
  message: 'attribution survives',
2670
2989
  }),
2671
2990
  );
@@ -2707,7 +3026,6 @@ export async function runAdapterConformance(
2707
3026
  ErrorCode.ROLE_IN_USE,
2708
3027
  ErrorCode.DEFAULT_ROLE_REQUIRED,
2709
3028
  ErrorCode.ROLE_REQUIRED,
2710
- ErrorCode.ROLE_REFERENCED,
2711
3029
  ],
2712
3030
  in_use_message_actionable: true,
2713
3031
  evicted_drone_retargeted: true,
@@ -3333,6 +3651,7 @@ export async function runAdapterConformance(
3333
3651
  cube,
3334
3652
  createProtocolEnvelope('delete-log', {
3335
3653
  post_id: '00000000-0000-4000-8000-000000000312',
3654
+ to: 'broadcast',
3336
3655
  message: 'deleted with cube',
3337
3656
  }),
3338
3657
  );