borgmcp-shared 0.6.2 → 0.6.4

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.
@@ -2,6 +2,7 @@ import {
2
2
  ErrorCode,
3
3
  compareLogCursor,
4
4
  createProtocolEnvelope,
5
+ decodeAssociateRepositoryCubeResponseEnvelope,
5
6
  decodeCreateCubeResponseEnvelope,
6
7
  decodeAppendLogResultEnvelope,
7
8
  decodeDecisionResultEnvelope,
@@ -14,6 +15,7 @@ import {
14
15
  decodeProtocolErrorEnvelope,
15
16
  decodeProtocolTagPreflight,
16
17
  decodeReadLogResultEnvelope,
18
+ decodeResolveRepositoryCubeResponseEnvelope,
17
19
  decodeReassignDroneResultEnvelope,
18
20
  decodeUpdateDroneRuntimeMetadataResponseEnvelope,
19
21
  decodeSseFrames,
@@ -22,11 +24,18 @@ import {
22
24
  PROTOCOL_VERSION,
23
25
  utf8ByteLength,
24
26
  type CreateCubeResponse,
27
+ type CreateCubeRepository,
28
+ type CubeTemplate,
29
+ type ResolvedRepositoryCube,
25
30
  type LogCursor,
26
31
  type StreamEvent,
27
32
  type DroneRuntimeMetadata,
28
33
  } from '../protocol/index.js';
29
- import { ENROLLMENT_RETRY_CONFORMANCE } from './index.js';
34
+ import {
35
+ CREATE_CUBE_ASSOCIATION_CONFORMANCE,
36
+ CREATE_CUBE_RETRY_CONFORMANCE,
37
+ ENROLLMENT_RETRY_CONFORMANCE,
38
+ } from './index.js';
30
39
 
31
40
  export interface ConformanceHttpResponse {
32
41
  status: number;
@@ -102,17 +111,34 @@ export interface ConformanceAuthorityState {
102
111
  grants: number;
103
112
  server_capabilities: number;
104
113
  cube_create_bindings: number;
114
+ repository_associations: number;
105
115
  }
106
116
 
107
117
  export interface ConformanceCreatedCubeState {
108
118
  cube_exists: boolean;
109
119
  creator_has_grant: boolean;
120
+ creator_access: 'manage' | null;
110
121
  grant_count: number;
111
122
  role_count: number;
123
+ name: string;
124
+ working_repo_name: string;
125
+ repository: CreateCubeRepository;
126
+ template: CubeTemplate;
127
+ human_seat_role_id: string;
128
+ default_worker_role_id: string;
112
129
  human_seat_role_matches: boolean;
113
130
  default_worker_role_matches: boolean;
114
131
  }
115
132
 
133
+ export interface ConformanceRepositoryCubeFixture {
134
+ cube_id: string;
135
+ name: string;
136
+ template: CubeTemplate;
137
+ human_seat_role_id: string;
138
+ default_worker_role_id: string;
139
+ access: 'manage';
140
+ }
141
+
116
142
  export interface ConformanceEnrollmentPrincipalState {
117
143
  response_client_matches: boolean;
118
144
  active_credential_bindings: number;
@@ -150,6 +176,7 @@ export interface ConformanceAdmin {
150
176
  cube: ConformanceCube,
151
177
  access?: ConformanceCubeAccess,
152
178
  ): Promise<void>;
179
+ revokeCubeGrant(principal: ConformancePrincipal, cube: ConformanceCube): Promise<void>;
153
180
  createRole(cube: ConformanceCube, input: {
154
181
  readonly roleClass: 'queen' | 'worker';
155
182
  readonly isHumanSeat: boolean;
@@ -178,6 +205,13 @@ export interface ConformanceAdmin {
178
205
  creator: ConformancePrincipal,
179
206
  response: CreateCubeResponse,
180
207
  ): Promise<ConformanceCreatedCubeState>;
208
+ prepareRepositoryCube(
209
+ cube: ConformanceCube,
210
+ input: {
211
+ name: string;
212
+ template: CubeTemplate;
213
+ },
214
+ ): Promise<ConformanceRepositoryCubeFixture>;
181
215
  inspectEnrollmentPrincipal(
182
216
  principal: ConformancePrincipal,
183
217
  responseClientId: string,
@@ -193,6 +227,8 @@ export interface ConformanceOperations {
193
227
  protocol(credential: string | null): Promise<ConformanceHttpResponse>;
194
228
  enroll(request: unknown): Promise<ConformanceHttpResponse>;
195
229
  createCube(credential: string | null, request: unknown): Promise<ConformanceHttpResponse>;
230
+ resolveRepositoryCube(credential: string | null, request: unknown): Promise<ConformanceHttpResponse>;
231
+ associateRepositoryCube(credential: string | null, request: unknown): Promise<ConformanceHttpResponse>;
196
232
  attach(credential: string, request: unknown): Promise<ConformanceHttpResponse>;
197
233
  selfMetadataUpdate(
198
234
  credential: string,
@@ -277,6 +313,7 @@ export const ADAPTER_CONFORMANCE_FIXTURES = [
277
313
  { id: 'http.unauthenticated-liveness', area: 'http' },
278
314
  { id: 'protocol.credential-free-preflight', area: 'protocol' },
279
315
  { id: 'enrollment.retry-authority', area: 'enrollment' },
316
+ { id: 'repository.explicit-association', area: 'repository' },
280
317
  { id: 'security.adapter-boundary-injection', area: 'security' },
281
318
  { id: 'security.oversize-request', area: 'security' },
282
319
  { id: 'security.cross-cube-isolation', area: 'security' },
@@ -686,7 +723,12 @@ export async function runAdapterConformance(
686
723
 
687
724
  const cubeRequest = {
688
725
  retry_key: '00000000-0000-4000-8000-000000000213',
689
- name: 'repository-one',
726
+ name: 'Repository One',
727
+ working_repo_name: 'repository-one',
728
+ repository: {
729
+ kind: 'origin' as const,
730
+ value: 'https://github.com/Byte-Ventures/repository-one',
731
+ },
690
732
  template: 'default',
691
733
  };
692
734
  const droneCredential = await environment.admin.issueDroneSession(ownerPrincipal);
@@ -724,13 +766,25 @@ export async function runAdapterConformance(
724
766
  const createdResponse = await environment.operations.createCube(ownerCredential, createProtocolEnvelope('cube-create', cubeRequest));
725
767
  expectStatus(createdResponse, 201, 'Owner cube create');
726
768
  const created = decodeCreateCubeResponseEnvelope(createdResponse.body).payload;
727
- assertStateDelta(beforeCreate, await environment.admin.observeAuthorityState(), { cubes: 1, roles: 2, grants: 1, cube_create_bindings: 1 }, 'Owner cube create');
769
+ assertStateDelta(
770
+ beforeCreate,
771
+ await environment.admin.observeAuthorityState(),
772
+ { cubes: 1, roles: 2, grants: 1, cube_create_bindings: 1, repository_associations: 1 },
773
+ 'Owner cube create',
774
+ );
728
775
  invariant(
729
776
  same(await environment.admin.inspectCreatedCube(ownerPrincipal, created), {
730
777
  cube_exists: true,
731
778
  creator_has_grant: true,
779
+ creator_access: 'manage',
732
780
  grant_count: 1,
733
781
  role_count: 2,
782
+ name: cubeRequest.name,
783
+ working_repo_name: cubeRequest.working_repo_name,
784
+ repository: cubeRequest.repository,
785
+ template: cubeRequest.template,
786
+ human_seat_role_id: created.human_seat_role_id,
787
+ default_worker_role_id: created.default_worker_role_id,
734
788
  human_seat_role_matches: true,
735
789
  default_worker_role_matches: true,
736
790
  }),
@@ -739,19 +793,92 @@ export async function runAdapterConformance(
739
793
  const beforeCreateRetry = await environment.admin.observeAuthorityState();
740
794
  const retriedCreateResponse = await environment.operations.createCube(ownerCredential, createProtocolEnvelope('cube-retry', cubeRequest));
741
795
  expectStatus(retriedCreateResponse, 201, 'Exact cube-create retry');
742
- invariant(same(decodeCreateCubeResponseEnvelope(retriedCreateResponse.body).payload, created), 'Exact cube-create retry returned different identities.');
796
+ const retriedCreate = decodeCreateCubeResponseEnvelope(retriedCreateResponse.body).payload;
797
+ invariant(retriedCreate.result === 'resolved', 'Exact cube-create retry did not report resolved readback.');
798
+ invariant(
799
+ same({ ...retriedCreate, result: 'created' }, created),
800
+ 'Exact cube-create retry returned different authoritative fields.',
801
+ );
743
802
  assertStateDelta(beforeCreateRetry, await environment.admin.observeAuthorityState(), {}, 'Exact cube-create retry');
744
- const beforeCreateMismatch = await environment.admin.observeAuthorityState();
745
- expectSecretFreeError(
746
- await environment.operations.createCube(ownerCredential, createProtocolEnvelope('cube-mismatch', { ...cubeRequest, name: 'repository-two' })),
747
- 409,
748
- ErrorCode.INVALID_INPUT,
749
- 'Cube-create retry mismatch',
750
- [cubeRequest.retry_key],
803
+ const displayRetryVector = CREATE_CUBE_RETRY_CONFORMANCE.find(
804
+ (vector) => vector.expected.outcome === 'resolved_response' &&
805
+ vector.retry.working_repo_name !== vector.initial.working_repo_name,
806
+ );
807
+ invariant(displayRetryVector !== undefined, 'Missing repository display readback vector.');
808
+ const beforeDisplayRetry = await environment.admin.observeAuthorityState();
809
+ const displayRetryResponse = await environment.operations.createCube(
810
+ ownerCredential,
811
+ createProtocolEnvelope('cube-display-retry', {
812
+ ...displayRetryVector.retry,
813
+ retry_key: cubeRequest.retry_key,
814
+ }),
815
+ );
816
+ expectStatus(displayRetryResponse, 201, 'Changed repository display retry');
817
+ invariant(
818
+ same(decodeCreateCubeResponseEnvelope(displayRetryResponse.body).payload, retriedCreate),
819
+ 'Changed repository display retry did not return stored authoritative display.',
820
+ );
821
+ assertStateDelta(
822
+ beforeDisplayRetry,
823
+ await environment.admin.observeAuthorityState(),
824
+ {},
825
+ 'Changed repository display retry',
826
+ );
827
+ for (const [index, vector] of CREATE_CUBE_RETRY_CONFORMANCE.entries()) {
828
+ if (vector.expected.outcome !== 'retry_tuple_mismatch') continue;
829
+ const mismatch = { ...vector.retry, retry_key: cubeRequest.retry_key };
830
+ const beforeCreateMismatch = await environment.admin.observeAuthorityState();
831
+ expectSecretFreeError(
832
+ await environment.operations.createCube(
833
+ ownerCredential,
834
+ createProtocolEnvelope(`cube-mismatch-${index}`, mismatch),
835
+ ),
836
+ 409,
837
+ ErrorCode.INVALID_INPUT,
838
+ `Cube-create ${vector.name}`,
839
+ [cubeRequest.retry_key],
840
+ );
841
+ assertStateDelta(
842
+ beforeCreateMismatch,
843
+ await environment.admin.observeAuthorityState(),
844
+ {},
845
+ `Cube-create ${vector.name}`,
846
+ );
847
+ }
848
+ const sameAssociationVector = CREATE_CUBE_ASSOCIATION_CONFORMANCE.find(
849
+ (vector) => vector.expected.outcome === 'resolved',
850
+ );
851
+ invariant(sameAssociationVector !== undefined, 'Missing same-repository association vector.');
852
+ const beforeAssociationResolve = await environment.admin.observeAuthorityState();
853
+ const associationResolveResponse = await environment.operations.createCube(
854
+ ownerCredential,
855
+ createProtocolEnvelope('cube-association-resolve', {
856
+ ...sameAssociationVector.request,
857
+ repository: cubeRequest.repository,
858
+ }),
859
+ );
860
+ expectStatus(associationResolveResponse, 201, 'Fresh retry for associated repository');
861
+ const associationResolved = decodeCreateCubeResponseEnvelope(associationResolveResponse.body).payload;
862
+ invariant(associationResolved.result === 'resolved', 'Associated repository did not resolve.');
863
+ invariant(
864
+ same({ ...associationResolved, result: 'created' }, created),
865
+ 'Associated repository did not return stored authoritative fields.',
866
+ );
867
+ assertStateDelta(
868
+ beforeAssociationResolve,
869
+ await environment.admin.observeAuthorityState(),
870
+ sameAssociationVector.expected.authority_state_delta,
871
+ 'Fresh retry for associated repository',
751
872
  );
752
- assertStateDelta(beforeCreateMismatch, await environment.admin.observeAuthorityState(), {}, 'Cube-create retry mismatch');
753
873
  await environment.admin.grantCreateCubeCapability(ordinaryPrincipal);
754
- const crossClientRequest = { ...cubeRequest, name: 'ordinary-repository' };
874
+ const crossClientRequest = {
875
+ ...cubeRequest,
876
+ working_repo_name: 'ordinary-repository',
877
+ repository: {
878
+ kind: 'local' as const,
879
+ value: '00000000-0000-4000-8000-000000000216',
880
+ },
881
+ };
755
882
  const beforeCrossClientCreate = await environment.admin.observeAuthorityState();
756
883
  const crossClientResponse = await environment.operations.createCube(
757
884
  ordinaryCredential,
@@ -763,7 +890,7 @@ export async function runAdapterConformance(
763
890
  assertStateDelta(
764
891
  beforeCrossClientCreate,
765
892
  await environment.admin.observeAuthorityState(),
766
- { cubes: 1, roles: 2, grants: 1, cube_create_bindings: 1 },
893
+ { cubes: 1, roles: 2, grants: 1, cube_create_bindings: 1, repository_associations: 1 },
767
894
  'Cross-client cube create',
768
895
  );
769
896
  invariant(
@@ -776,24 +903,35 @@ export async function runAdapterConformance(
776
903
  createProtocolEnvelope('cube-cross-client-retry', crossClientRequest),
777
904
  );
778
905
  expectStatus(crossClientRetry, 201, 'Exact cross-client cube-create retry');
906
+ const crossClientResolved = decodeCreateCubeResponseEnvelope(crossClientRetry.body).payload;
779
907
  invariant(
780
- same(decodeCreateCubeResponseEnvelope(crossClientRetry.body).payload, crossClientCreated),
781
- 'Exact cross-client cube-create retry returned different identities.',
908
+ crossClientResolved.result === 'resolved',
909
+ 'Exact cross-client cube-create retry did not report resolved readback.',
910
+ );
911
+ invariant(
912
+ same(
913
+ { ...crossClientResolved, result: 'created' },
914
+ crossClientCreated,
915
+ ),
916
+ 'Exact cross-client cube-create retry returned different authoritative fields.',
782
917
  );
783
918
  assertStateDelta(beforeCrossClientRetry, await environment.admin.observeAuthorityState(), {}, 'Exact cross-client cube-create retry');
919
+ const differentAssociationVector = CREATE_CUBE_ASSOCIATION_CONFORMANCE.find(
920
+ (vector) => vector.expected.outcome === 'created',
921
+ );
922
+ invariant(differentAssociationVector !== undefined, 'Missing different-repository association vector.');
784
923
  const beforeSecondCreate = await environment.admin.observeAuthorityState();
785
- const secondCreatedResponse = await environment.operations.createCube(ownerCredential, createProtocolEnvelope('cube-create-second', {
786
- ...cubeRequest,
787
- retry_key: '00000000-0000-4000-8000-000000000214',
788
- name: 'repository-two',
789
- }));
924
+ const secondCreatedResponse = await environment.operations.createCube(
925
+ ownerCredential,
926
+ createProtocolEnvelope('cube-create-second', differentAssociationVector.request),
927
+ );
790
928
  expectStatus(secondCreatedResponse, 201, 'Second cube create');
791
929
  const secondCreated = decodeCreateCubeResponseEnvelope(secondCreatedResponse.body).payload;
792
930
  invariant(secondCreated.cube_id !== created.cube_id, 'Fresh cube-create retry key reused an existing cube.');
793
931
  assertStateDelta(
794
932
  beforeSecondCreate,
795
933
  await environment.admin.observeAuthorityState(),
796
- { cubes: 1, roles: 2, grants: 1, cube_create_bindings: 1 },
934
+ differentAssociationVector.expected.authority_state_delta,
797
935
  'Second cube create',
798
936
  );
799
937
  await environment.admin.revokePrincipal(ownerPrincipal);
@@ -866,6 +1004,244 @@ export async function runAdapterConformance(
866
1004
  };
867
1005
  });
868
1006
 
1007
+ await record('repository.explicit-association', async () => {
1008
+ const repository = {
1009
+ kind: 'origin' as const,
1010
+ value: 'https://github.com/Byte-Ventures/legacy-repository',
1011
+ };
1012
+ const prepared = await environment.admin.prepareRepositoryCube(cubeA, {
1013
+ name: 'Legacy Repository Cube',
1014
+ template: 'software-dev',
1015
+ });
1016
+ const resolveRequest = {
1017
+ working_repo_name: 'legacy-repository',
1018
+ repository,
1019
+ };
1020
+ const beforeNone = await environment.admin.observeAuthorityState();
1021
+ const noneResponse = await environment.operations.resolveRepositoryCube(
1022
+ credentialA,
1023
+ createProtocolEnvelope('repository-resolve-none', resolveRequest),
1024
+ );
1025
+ expectStatus(noneResponse, 200, 'Unassociated repository resolution');
1026
+ invariant(
1027
+ same(decodeResolveRepositoryCubeResponseEnvelope(noneResponse.body).payload, { result: 'none' }),
1028
+ 'Unassociated repository did not return explicit none.',
1029
+ );
1030
+ assertStateDelta(beforeNone, await environment.admin.observeAuthorityState(), {}, 'Repository resolution');
1031
+
1032
+ const associationRequest = { cube_id: cubeA.id, ...resolveRequest };
1033
+ const beforeDenied = await environment.admin.observeAuthorityState();
1034
+ expectSecretFreeError(
1035
+ await environment.operations.associateRepositoryCube(
1036
+ credentialB,
1037
+ createProtocolEnvelope('repository-associate-denied', associationRequest),
1038
+ ),
1039
+ 403,
1040
+ ErrorCode.ACCESS_DENIED,
1041
+ 'Inaccessible repository cube association',
1042
+ [associationRequest.cube_id, repository.value],
1043
+ );
1044
+ assertStateDelta(beforeDenied, await environment.admin.observeAuthorityState(), {}, 'Denied repository association');
1045
+
1046
+ const beforeAssociation = await environment.admin.observeAuthorityState();
1047
+ const associatedResponse = await environment.operations.associateRepositoryCube(
1048
+ credentialA,
1049
+ createProtocolEnvelope('repository-associate', associationRequest),
1050
+ );
1051
+ expectStatus(associatedResponse, 200, 'Explicit repository cube association');
1052
+ const associated = decodeAssociateRepositoryCubeResponseEnvelope(associatedResponse.body).payload;
1053
+ const authoritative = {
1054
+ result: 'resolved' as const,
1055
+ cube_id: prepared.cube_id,
1056
+ name: prepared.name,
1057
+ working_repo_name: resolveRequest.working_repo_name,
1058
+ repository,
1059
+ template: prepared.template,
1060
+ human_seat_role_id: prepared.human_seat_role_id,
1061
+ default_worker_role_id: prepared.default_worker_role_id,
1062
+ access: prepared.access,
1063
+ };
1064
+ invariant(same(associated, authoritative), 'Repository association did not return authoritative stored fields.');
1065
+ assertStateDelta(
1066
+ beforeAssociation,
1067
+ await environment.admin.observeAuthorityState(),
1068
+ { repository_associations: 1 },
1069
+ 'Explicit repository association',
1070
+ );
1071
+
1072
+ const beforeResolve = await environment.admin.observeAuthorityState();
1073
+ const resolvedResponse = await environment.operations.resolveRepositoryCube(
1074
+ credentialA,
1075
+ createProtocolEnvelope('repository-resolve', {
1076
+ ...resolveRequest,
1077
+ working_repo_name: 'ignored-new-display',
1078
+ }),
1079
+ );
1080
+ expectStatus(resolvedResponse, 200, 'Associated repository resolution');
1081
+ invariant(
1082
+ same(decodeResolveRepositoryCubeResponseEnvelope(resolvedResponse.body).payload, authoritative),
1083
+ 'Associated repository did not return stored authoritative display.',
1084
+ );
1085
+ assertStateDelta(beforeResolve, await environment.admin.observeAuthorityState(), {}, 'Associated repository resolution');
1086
+
1087
+ const beforeRetry = await environment.admin.observeAuthorityState();
1088
+ const retryResponse = await environment.operations.associateRepositoryCube(
1089
+ credentialA,
1090
+ createProtocolEnvelope('repository-associate-retry', associationRequest),
1091
+ );
1092
+ expectStatus(retryResponse, 200, 'Exact repository association retry');
1093
+ invariant(
1094
+ same(decodeAssociateRepositoryCubeResponseEnvelope(retryResponse.body).payload, authoritative),
1095
+ 'Exact repository association retry changed authoritative fields.',
1096
+ );
1097
+ assertStateDelta(beforeRetry, await environment.admin.observeAuthorityState(), {}, 'Repository association retry');
1098
+
1099
+ const cubeC = await environment.admin.createCube('cube-c');
1100
+ await environment.admin.grantCube(principalA, cubeC);
1101
+ await environment.admin.prepareRepositoryCube(cubeC, {
1102
+ name: 'Other Legacy Cube',
1103
+ template: 'starter',
1104
+ });
1105
+ const beforeRepositoryConflict = await environment.admin.observeAuthorityState();
1106
+ expectSecretFreeError(
1107
+ await environment.operations.associateRepositoryCube(
1108
+ credentialA,
1109
+ createProtocolEnvelope('repository-associate-repository-conflict', {
1110
+ ...associationRequest,
1111
+ cube_id: cubeC.id,
1112
+ }),
1113
+ ),
1114
+ 409,
1115
+ ErrorCode.REPOSITORY_ALREADY_ASSOCIATED,
1116
+ 'Repository-to-other-cube conflict',
1117
+ [cubeA.id, cubeC.id, repository.value],
1118
+ );
1119
+ assertStateDelta(
1120
+ beforeRepositoryConflict,
1121
+ await environment.admin.observeAuthorityState(),
1122
+ {},
1123
+ 'Repository-to-other-cube conflict',
1124
+ );
1125
+
1126
+ const beforeCubeConflict = await environment.admin.observeAuthorityState();
1127
+ expectSecretFreeError(
1128
+ await environment.operations.associateRepositoryCube(
1129
+ credentialA,
1130
+ createProtocolEnvelope('repository-associate-cube-conflict', {
1131
+ cube_id: cubeA.id,
1132
+ working_repo_name: 'other-repository',
1133
+ repository: {
1134
+ kind: 'origin',
1135
+ value: 'https://github.com/Byte-Ventures/other-repository',
1136
+ },
1137
+ }),
1138
+ ),
1139
+ 409,
1140
+ ErrorCode.CUBE_ALREADY_ASSOCIATED,
1141
+ 'Cube-to-other-repository conflict',
1142
+ [cubeA.id, repository.value, 'https://github.com/Byte-Ventures/other-repository'],
1143
+ );
1144
+ assertStateDelta(
1145
+ beforeCubeConflict,
1146
+ await environment.admin.observeAuthorityState(),
1147
+ {},
1148
+ 'Cube-to-other-repository conflict',
1149
+ );
1150
+
1151
+ const preparedB = await environment.admin.prepareRepositoryCube(cubeB, {
1152
+ name: 'Other Client Legacy Cube',
1153
+ template: 'starter',
1154
+ });
1155
+ const beforeCrossClientResolve = await environment.admin.observeAuthorityState();
1156
+ const crossClientResolve = await environment.operations.resolveRepositoryCube(
1157
+ credentialB,
1158
+ createProtocolEnvelope('repository-resolve-cross-client', resolveRequest),
1159
+ );
1160
+ expectStatus(crossClientResolve, 200, 'Cross-client repository resolution');
1161
+ invariant(
1162
+ same(decodeResolveRepositoryCubeResponseEnvelope(crossClientResolve.body).payload, { result: 'none' }),
1163
+ 'Another client repository binding was exposed by resolution.',
1164
+ );
1165
+ assertStateDelta(
1166
+ beforeCrossClientResolve,
1167
+ await environment.admin.observeAuthorityState(),
1168
+ {},
1169
+ 'Cross-client repository resolution',
1170
+ );
1171
+ const beforeCrossClientAssociation = await environment.admin.observeAuthorityState();
1172
+ const crossClientAssociation = await environment.operations.associateRepositoryCube(
1173
+ credentialB,
1174
+ createProtocolEnvelope('repository-associate-cross-client', {
1175
+ ...associationRequest,
1176
+ cube_id: cubeB.id,
1177
+ }),
1178
+ );
1179
+ expectStatus(crossClientAssociation, 200, 'Cross-client repository association');
1180
+ const crossClientAssociated = decodeAssociateRepositoryCubeResponseEnvelope(
1181
+ crossClientAssociation.body,
1182
+ ).payload;
1183
+ invariant(
1184
+ crossClientAssociated.cube_id === preparedB.cube_id &&
1185
+ crossClientAssociated.repository.value === repository.value,
1186
+ 'Another client binding changed cross-client association identity.',
1187
+ );
1188
+ assertStateDelta(
1189
+ beforeCrossClientAssociation,
1190
+ await environment.admin.observeAuthorityState(),
1191
+ { repository_associations: 1 },
1192
+ 'Cross-client repository association',
1193
+ );
1194
+
1195
+ await environment.admin.revokeCubeGrant(principalA, cubeA);
1196
+ const beforeHiddenResolve = await environment.admin.observeAuthorityState();
1197
+ const hiddenResolve = await environment.operations.resolveRepositoryCube(
1198
+ credentialA,
1199
+ createProtocolEnvelope('repository-resolve-inaccessible-binding', resolveRequest),
1200
+ );
1201
+ expectStatus(hiddenResolve, 200, 'Inaccessible repository binding resolution');
1202
+ invariant(
1203
+ same(decodeResolveRepositoryCubeResponseEnvelope(hiddenResolve.body).payload, { result: 'none' }),
1204
+ 'Inaccessible repository binding was exposed by read-only resolution.',
1205
+ );
1206
+ assertStateDelta(
1207
+ beforeHiddenResolve,
1208
+ await environment.admin.observeAuthorityState(),
1209
+ {},
1210
+ 'Inaccessible repository binding resolution',
1211
+ );
1212
+ const beforeHiddenAssociation = await environment.admin.observeAuthorityState();
1213
+ expectSecretFreeError(
1214
+ await environment.operations.associateRepositoryCube(
1215
+ credentialA,
1216
+ createProtocolEnvelope('repository-associate-inaccessible-binding', {
1217
+ ...associationRequest,
1218
+ cube_id: cubeC.id,
1219
+ }),
1220
+ ),
1221
+ 403,
1222
+ ErrorCode.ACCESS_DENIED,
1223
+ 'Inaccessible existing repository binding',
1224
+ [cubeA.id, cubeC.id, repository.value],
1225
+ );
1226
+ assertStateDelta(
1227
+ beforeHiddenAssociation,
1228
+ await environment.admin.observeAuthorityState(),
1229
+ {},
1230
+ 'Inaccessible existing repository binding',
1231
+ );
1232
+ await environment.admin.grantCube(principalA, cubeA);
1233
+ return {
1234
+ none: true,
1235
+ resolved: true,
1236
+ idempotent: true,
1237
+ repository_conflict: ErrorCode.REPOSITORY_ALREADY_ASSOCIATED,
1238
+ cube_conflict: ErrorCode.CUBE_ALREADY_ASSOCIATED,
1239
+ inaccessible: ErrorCode.ACCESS_DENIED,
1240
+ inaccessible_binding_hidden: true,
1241
+ cross_client_binding_hidden: true,
1242
+ };
1243
+ });
1244
+
869
1245
  await record('security.adapter-boundary-injection', async () => {
870
1246
  const injectedMessage = "'); DROP TABLE log_entries; --\r\ndata: forged-sse-frame";
871
1247
  const injectedBody = JSON.stringify(