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.
- package/README.md +4 -3
- package/RELEASES.md +7 -0
- package/dist/conformance/adapter.d.ts +15 -2
- package/dist/conformance/adapter.d.ts.map +1 -1
- package/dist/conformance/adapter.js +187 -41
- package/dist/conformance/adapter.js.map +1 -1
- package/dist/conformance/index.d.ts +49 -3
- package/dist/conformance/index.d.ts.map +1 -1
- package/dist/conformance/index.js +95 -18
- package/dist/conformance/index.js.map +1 -1
- package/dist/protocol/contract.d.ts +1 -1
- package/dist/protocol/contract.d.ts.map +1 -1
- package/dist/protocol/contract.js +13 -18
- package/dist/protocol/contract.js.map +1 -1
- package/dist/protocol/coordination.d.ts +36 -1
- package/dist/protocol/coordination.d.ts.map +1 -1
- package/dist/protocol/coordination.js +72 -6
- package/dist/protocol/coordination.js.map +1 -1
- package/dist/protocol/errors.d.ts +1 -1
- package/dist/protocol/errors.d.ts.map +1 -1
- package/dist/protocol/errors.js +1 -1
- package/dist/protocol/errors.js.map +1 -1
- package/dist/protocol/types.d.ts +1 -5
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/protocol/version.js +1 -1
- package/dist/templates.d.ts +1 -3
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +8 -79
- package/dist/templates.js.map +1 -1
- package/docs/compatibility.md +15 -0
- package/docs/release-records.json +28 -0
- package/docs/releases/0.14.0.md +8 -0
- package/docs/releases/1.0.0.md +11 -0
- package/docs/template-lifecycle.md +1 -1
- package/package.json +1 -1
- package/src/conformance/adapter.ts +369 -50
- package/src/conformance/index.ts +122 -22
- package/src/protocol/contract.ts +15 -21
- package/src/protocol/coordination.ts +136 -7
- package/src/protocol/errors.ts +1 -1
- package/src/protocol/types.ts +1 -5
- package/src/protocol/version.ts +2 -2
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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 === '
|
|
1848
|
-
...(vector.mutation === 'recipient_set' ? {
|
|
1849
|
-
...(vector.mutation === '
|
|
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 ===
|
|
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:
|
|
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
|
-
|
|
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
|
);
|