archctx-contracts 0.4.4 → 0.4.6
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/fixtures/valid/archctx-capabilities.json +3 -3
- package/fixtures/valid/product-version-manifest.json +5 -5
- package/fixtures/valid/projection-result.json +2 -2
- package/package.json +1 -1
- package/schemas/runtime/archctx-capabilities.schema.json +2 -2
- package/schemas/runtime/projection-result.schema.json +35 -3
- package/src/product-version.ts +1 -1
- package/src/projection.ts +98 -5
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "archcontext.capabilities/v1",
|
|
3
|
-
"package": { "name": "archctx", "version": "0.4.
|
|
3
|
+
"package": { "name": "archctx", "version": "0.4.6" },
|
|
4
4
|
"protocols": {
|
|
5
5
|
"projectionRequest": "archcontext.projection-request/v1",
|
|
6
|
-
"projectionResult": "archcontext.projection-result/
|
|
6
|
+
"projectionResult": "archcontext.projection-result/v2",
|
|
7
7
|
"architectureRefreshSignal": "archcontext.architecture-refresh-signal/v1"
|
|
8
8
|
},
|
|
9
9
|
"renderers": {
|
|
10
10
|
"architectureDocs": "archcontext.docs-renderer/v4",
|
|
11
11
|
"agentContext": "archcontext.agent-context-renderer/v1"
|
|
12
12
|
},
|
|
13
|
-
"features": ["architecture-docs-renderer-v2", "architecture-refresh-signal-v1", "projection-
|
|
13
|
+
"features": ["architecture-docs-renderer-v2", "architecture-refresh-signal-v1", "projection-apply-receipt-v1", "projection-protocol-v2"]
|
|
14
14
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": "archcontext.product-version-manifest/v1",
|
|
3
3
|
"product": {
|
|
4
4
|
"name": "archctx",
|
|
5
|
-
"version": "0.4.
|
|
5
|
+
"version": "0.4.6",
|
|
6
6
|
"distribution": "one-package"
|
|
7
7
|
},
|
|
8
8
|
"packageManager": "bun@1.3.10",
|
|
@@ -12,25 +12,25 @@
|
|
|
12
12
|
"surfaces": {
|
|
13
13
|
"cli": {
|
|
14
14
|
"packageName": "@archcontext/surfaces",
|
|
15
|
-
"version": "0.4.
|
|
15
|
+
"version": "0.4.6",
|
|
16
16
|
"entrypoint": "archctx"
|
|
17
17
|
},
|
|
18
18
|
"daemon": {
|
|
19
19
|
"packageName": "@archcontext/local-runtime",
|
|
20
|
-
"version": "0.4.
|
|
20
|
+
"version": "0.4.6",
|
|
21
21
|
"entrypoint": "archctx daemon start",
|
|
22
22
|
"rpcSchemaVersion": "archcontext.runtime-rpc/v1"
|
|
23
23
|
},
|
|
24
24
|
"mcp": {
|
|
25
25
|
"packageName": "@archcontext/surfaces",
|
|
26
|
-
"version": "0.4.
|
|
26
|
+
"version": "0.4.6",
|
|
27
27
|
"entrypoint": "archctx mcp",
|
|
28
28
|
"transport": "stdio"
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"schemas": {
|
|
32
32
|
"schemaSetVersion": "2026-06-25.al0-ledger",
|
|
33
|
-
"contractsPackageVersion": "0.4.
|
|
33
|
+
"contractsPackageVersion": "0.4.6",
|
|
34
34
|
"reviewChallenge": "archcontext.review-challenge/v2",
|
|
35
35
|
"attestation": "archcontext.attestation/v2",
|
|
36
36
|
"deviceIdentity": "archcontext.device-identity/v1",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion": "archcontext.projection-result/
|
|
2
|
+
"schemaVersion": "archcontext.projection-result/v2",
|
|
3
3
|
"requestId": "projection_request.repo_main",
|
|
4
4
|
"status": "applied",
|
|
5
5
|
"inputSnapshot": {
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
],
|
|
44
44
|
"humanActions": [],
|
|
45
45
|
"refreshSignals": [],
|
|
46
|
-
"receiptDigest": "sha256:
|
|
46
|
+
"receiptDigest": "sha256:6adef4b3486194b61e1851fdd5c1c352de374e10011beb9a38ec845ee91ff702"
|
|
47
47
|
}
|
package/package.json
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"required": ["projectionRequest", "projectionResult", "architectureRefreshSignal"],
|
|
23
23
|
"properties": {
|
|
24
24
|
"projectionRequest": { "const": "archcontext.projection-request/v1" },
|
|
25
|
-
"projectionResult": { "const": "archcontext.projection-result/
|
|
25
|
+
"projectionResult": { "const": "archcontext.projection-result/v2" },
|
|
26
26
|
"architectureRefreshSignal": { "const": "archcontext.architecture-refresh-signal/v1" }
|
|
27
27
|
}
|
|
28
28
|
},
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"type": "array",
|
|
40
40
|
"minItems": 3,
|
|
41
41
|
"uniqueItems": true,
|
|
42
|
-
"items": { "enum": ["architecture-docs-renderer-v2", "architecture-refresh-signal-v1", "projection-
|
|
42
|
+
"items": { "enum": ["architecture-docs-renderer-v2", "architecture-refresh-signal-v1", "projection-apply-receipt-v1", "projection-protocol-v2"] }
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://archcontext.dev/schemas/runtime/projection-result.schema.json",
|
|
4
|
-
"title": "
|
|
4
|
+
"title": "ProjectionResultV2",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": false,
|
|
7
7
|
"required": ["schemaVersion", "requestId", "status", "inputSnapshot", "outputSnapshot", "affectedNodeIds", "files", "humanActions", "refreshSignals", "receiptDigest"],
|
|
8
8
|
"properties": {
|
|
9
|
-
"schemaVersion": { "const": "archcontext.projection-result/
|
|
9
|
+
"schemaVersion": { "const": "archcontext.projection-result/v2" },
|
|
10
10
|
"requestId": { "type": "string", "pattern": "^[a-zA-Z0-9_.:-]+$", "minLength": 1 },
|
|
11
|
-
"status": { "enum": ["adoption-required", "applied", "blocked", "human-action-required", "noop", "permanent-failure", "planned", "retryable-failure"] },
|
|
11
|
+
"status": { "enum": ["adoption-required", "applied", "applied-reconcile-required", "blocked", "human-action-required", "noop", "permanent-failure", "planned", "retryable-failure"] },
|
|
12
12
|
"inputSnapshot": { "$ref": "#/$defs/snapshot" },
|
|
13
13
|
"outputSnapshot": { "$ref": "#/$defs/snapshot" },
|
|
14
14
|
"affectedNodeIds": { "type": "array", "uniqueItems": true, "description": "Canonical wire order is lexicographic and is enforced by projectionResultInvariantIssues.", "items": { "type": "string", "minLength": 1 } },
|
|
15
15
|
"files": { "type": "array", "description": "Canonical wire order is by path and is enforced by projectionResultInvariantIssues.", "items": { "$ref": "#/$defs/file" } },
|
|
16
16
|
"humanActions": { "type": "array", "items": { "$ref": "#/$defs/humanAction" } },
|
|
17
17
|
"refreshSignals": { "type": "array", "description": "Canonical wire order is by signalId and cross-object receipt/snapshot bindings are enforced by projectionResultInvariantIssues.", "items": { "$ref": "#/$defs/refreshSignal" } },
|
|
18
|
+
"applyReceipt": { "$ref": "#/$defs/applyIdentity" },
|
|
18
19
|
"receiptDigest": { "$ref": "#/$defs/digest" }
|
|
19
20
|
},
|
|
20
21
|
"allOf": [
|
|
@@ -22,6 +23,13 @@
|
|
|
22
23
|
"if": { "properties": { "status": { "enum": ["adoption-required", "human-action-required"] } }, "required": ["status"] },
|
|
23
24
|
"then": { "properties": { "humanActions": { "minItems": 1 } } },
|
|
24
25
|
"else": { "properties": { "humanActions": { "maxItems": 0 } } }
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"if": { "properties": { "status": { "const": "applied-reconcile-required" } }, "required": ["status"] },
|
|
29
|
+
"then": {
|
|
30
|
+
"required": ["applyReceipt"],
|
|
31
|
+
"properties": { "refreshSignals": { "maxItems": 0 } }
|
|
32
|
+
}
|
|
25
33
|
}
|
|
26
34
|
],
|
|
27
35
|
"$defs": {
|
|
@@ -157,6 +165,30 @@
|
|
|
157
165
|
"reasonCodes": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/$defs/reasonCode" } },
|
|
158
166
|
"affectedNodeIds": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "minLength": 1 } }
|
|
159
167
|
}
|
|
168
|
+
},
|
|
169
|
+
"applyIdentity": {
|
|
170
|
+
"type": "object",
|
|
171
|
+
"additionalProperties": false,
|
|
172
|
+
"required": ["schemaVersion", "applyId", "lookupKey", "repositoryId", "workspaceId", "acceptedChange", "semanticCommit", "ownedFilesDigest", "refreshSignalsDigest"],
|
|
173
|
+
"properties": {
|
|
174
|
+
"schemaVersion": { "const": "archcontext.projection-apply-identity/v1" },
|
|
175
|
+
"applyId": { "$ref": "#/$defs/digest" },
|
|
176
|
+
"lookupKey": { "$ref": "#/$defs/digest" },
|
|
177
|
+
"repositoryId": { "type": "string", "minLength": 1 },
|
|
178
|
+
"workspaceId": { "type": "string", "minLength": 1 },
|
|
179
|
+
"acceptedChange": { "$ref": "#/$defs/acceptedChange" },
|
|
180
|
+
"semanticCommit": {
|
|
181
|
+
"type": "object",
|
|
182
|
+
"additionalProperties": false,
|
|
183
|
+
"required": ["changeSetId", "idempotencyKey"],
|
|
184
|
+
"properties": {
|
|
185
|
+
"changeSetId": { "type": "string", "minLength": 1 },
|
|
186
|
+
"idempotencyKey": { "type": "string", "minLength": 1 }
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"ownedFilesDigest": { "$ref": "#/$defs/digest" },
|
|
190
|
+
"refreshSignalsDigest": { "$ref": "#/$defs/digest" }
|
|
191
|
+
}
|
|
160
192
|
}
|
|
161
193
|
}
|
|
162
194
|
}
|
package/src/product-version.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const ARCHCONTEXT_PRODUCT_NAME = "archctx";
|
|
2
|
-
export const ARCHCONTEXT_PRODUCT_VERSION = "0.4.
|
|
2
|
+
export const ARCHCONTEXT_PRODUCT_VERSION = "0.4.6";
|
|
3
3
|
export const ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.3.10";
|
|
4
4
|
export const ARCHCONTEXT_NODE_RANGE = ">=24 <26";
|
|
5
5
|
export const LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1";
|
package/src/projection.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { digestJson, type Json } from "./schema";
|
|
2
2
|
|
|
3
3
|
export const PROJECTION_REQUEST_SCHEMA_VERSION = "archcontext.projection-request/v1" as const;
|
|
4
|
-
export const PROJECTION_RESULT_SCHEMA_VERSION = "archcontext.projection-result/
|
|
4
|
+
export const PROJECTION_RESULT_SCHEMA_VERSION = "archcontext.projection-result/v2" as const;
|
|
5
|
+
export const PROJECTION_APPLY_IDENTITY_SCHEMA_VERSION = "archcontext.projection-apply-identity/v1" as const;
|
|
6
|
+
export const PROJECTION_APPLY_RECEIPT_SCHEMA_VERSION = "archcontext.projection-apply-receipt/v1" as const;
|
|
5
7
|
export const ARCHITECTURE_REFRESH_SIGNAL_SCHEMA_VERSION = "archcontext.architecture-refresh-signal/v1" as const;
|
|
6
8
|
export const ARCHCTX_CAPABILITIES_SCHEMA_VERSION = "archcontext.capabilities/v1" as const;
|
|
7
9
|
export const ARCHITECTURE_DOCS_RENDERER_VERSION = "archcontext.docs-renderer/v4" as const;
|
|
@@ -12,6 +14,7 @@ export const PROJECTION_TARGETS = ["agent-context", "architecture-docs"] as cons
|
|
|
12
14
|
export const PROJECTION_RESULT_STATUSES = [
|
|
13
15
|
"adoption-required",
|
|
14
16
|
"applied",
|
|
17
|
+
"applied-reconcile-required",
|
|
15
18
|
"blocked",
|
|
16
19
|
"human-action-required",
|
|
17
20
|
"noop",
|
|
@@ -51,7 +54,8 @@ export const ARCHITECTURE_REFRESH_TARGETS = [
|
|
|
51
54
|
export const ARCHCTX_FEATURES = [
|
|
52
55
|
"architecture-docs-renderer-v2",
|
|
53
56
|
"architecture-refresh-signal-v1",
|
|
54
|
-
"projection-
|
|
57
|
+
"projection-apply-receipt-v1",
|
|
58
|
+
"projection-protocol-v2"
|
|
55
59
|
] as const;
|
|
56
60
|
|
|
57
61
|
export type ProjectionMode = (typeof PROJECTION_MODES)[number];
|
|
@@ -112,6 +116,21 @@ export interface ProjectionHumanActionV1 {
|
|
|
112
116
|
requestPayloadDigest: Sha256Digest;
|
|
113
117
|
}
|
|
114
118
|
|
|
119
|
+
export interface ProjectionApplyIdentityV1 {
|
|
120
|
+
schemaVersion: typeof PROJECTION_APPLY_IDENTITY_SCHEMA_VERSION;
|
|
121
|
+
applyId: Sha256Digest;
|
|
122
|
+
lookupKey: Sha256Digest;
|
|
123
|
+
repositoryId: string;
|
|
124
|
+
workspaceId: string;
|
|
125
|
+
acceptedChange: AcceptedArchitectureChangeReferenceV1;
|
|
126
|
+
semanticCommit: {
|
|
127
|
+
changeSetId: string;
|
|
128
|
+
idempotencyKey: string;
|
|
129
|
+
};
|
|
130
|
+
ownedFilesDigest: Sha256Digest;
|
|
131
|
+
refreshSignalsDigest: Sha256Digest;
|
|
132
|
+
}
|
|
133
|
+
|
|
115
134
|
export interface ArchitectureDigestSetV1 {
|
|
116
135
|
modelDigest: Sha256Digest;
|
|
117
136
|
sourceTreeDigest: Sha256Digest;
|
|
@@ -143,7 +162,7 @@ export interface ArchitectureRefreshSignalV1 {
|
|
|
143
162
|
projectionReceiptDigest: Sha256Digest;
|
|
144
163
|
}
|
|
145
164
|
|
|
146
|
-
export interface
|
|
165
|
+
export interface ProjectionResultV2 {
|
|
147
166
|
schemaVersion: typeof PROJECTION_RESULT_SCHEMA_VERSION;
|
|
148
167
|
requestId: string;
|
|
149
168
|
status: ProjectionResultStatus;
|
|
@@ -153,9 +172,16 @@ export interface ProjectionResultV1 {
|
|
|
153
172
|
files: ProjectionFileResultV1[];
|
|
154
173
|
humanActions: ProjectionHumanActionV1[];
|
|
155
174
|
refreshSignals: ArchitectureRefreshSignalV1[];
|
|
175
|
+
applyReceipt?: ProjectionApplyIdentityV1;
|
|
156
176
|
receiptDigest: Sha256Digest;
|
|
157
177
|
}
|
|
158
178
|
|
|
179
|
+
export interface ProjectionApplyReceiptV1 {
|
|
180
|
+
schemaVersion: typeof PROJECTION_APPLY_RECEIPT_SCHEMA_VERSION;
|
|
181
|
+
identity: ProjectionApplyIdentityV1;
|
|
182
|
+
result: ProjectionResultV2;
|
|
183
|
+
}
|
|
184
|
+
|
|
159
185
|
export interface ArchctxCapabilitiesV1 {
|
|
160
186
|
schemaVersion: typeof ARCHCTX_CAPABILITIES_SCHEMA_VERSION;
|
|
161
187
|
package: {
|
|
@@ -222,7 +248,7 @@ export function projectionRequestInvariantIssues(input: ProjectionRequestV1): st
|
|
|
222
248
|
return issues;
|
|
223
249
|
}
|
|
224
250
|
|
|
225
|
-
export function projectionResultInvariantIssues(input:
|
|
251
|
+
export function projectionResultInvariantIssues(input: ProjectionResultV2): string[] {
|
|
226
252
|
const issues = [
|
|
227
253
|
...sortedUniqueIssues("affectedNodeIds", input.affectedNodeIds),
|
|
228
254
|
...sortedUniqueIssues("files.path", input.files.map((file) => file.path)),
|
|
@@ -237,6 +263,10 @@ export function projectionResultInvariantIssues(input: ProjectionResultV1): stri
|
|
|
237
263
|
const statusRequiresHumanAction = input.status === "adoption-required" || input.status === "human-action-required";
|
|
238
264
|
if (statusRequiresHumanAction && input.humanActions.length === 0) issues.push(`${input.status} status requires at least one human action`);
|
|
239
265
|
if (!statusRequiresHumanAction && input.humanActions.length > 0) issues.push(`human actions are not allowed when status=${input.status}`);
|
|
266
|
+
if (input.status === "applied-reconcile-required") {
|
|
267
|
+
if (!input.applyReceipt) issues.push("applied-reconcile-required status requires applyReceipt");
|
|
268
|
+
if (input.refreshSignals.length > 0) issues.push("applied-reconcile-required status cannot deliver refreshSignals");
|
|
269
|
+
}
|
|
240
270
|
for (const [index, file] of input.files.entries()) {
|
|
241
271
|
const prefix = `files[${index}]`;
|
|
242
272
|
if (file.action === "create" && (file.preimageDigest !== null || file.outputDigest === null)) {
|
|
@@ -270,11 +300,74 @@ export function projectionResultInvariantIssues(input: ProjectionResultV1): stri
|
|
|
270
300
|
* receipt payload to avoid a circular hash; every signal must then bind that receipt via
|
|
271
301
|
* projectionReceiptDigest, which projectionResultInvariantIssues verifies.
|
|
272
302
|
*/
|
|
273
|
-
export function projectionResultReceiptDigest(input: Omit<
|
|
303
|
+
export function projectionResultReceiptDigest(input: Omit<ProjectionResultV2, "receiptDigest">): Sha256Digest {
|
|
274
304
|
const refreshSignals = input.refreshSignals.map(({ projectionReceiptDigest: _projectionReceiptDigest, ...signal }) => signal);
|
|
275
305
|
return digestJson({ ...input, refreshSignals } as unknown as Json) as Sha256Digest;
|
|
276
306
|
}
|
|
277
307
|
|
|
308
|
+
export function projectionApplyLookupKey(input: {
|
|
309
|
+
repositoryId: string;
|
|
310
|
+
workspaceId: string;
|
|
311
|
+
acceptedChange: AcceptedArchitectureChangeReferenceV1;
|
|
312
|
+
}): Sha256Digest {
|
|
313
|
+
return digestJson({
|
|
314
|
+
schemaVersion: "archcontext.projection-apply-lookup/v1",
|
|
315
|
+
repositoryId: input.repositoryId,
|
|
316
|
+
workspaceId: input.workspaceId,
|
|
317
|
+
acceptedChange: input.acceptedChange
|
|
318
|
+
} as unknown as Json) as Sha256Digest;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function createProjectionApplyIdentity(input: {
|
|
322
|
+
repositoryId: string;
|
|
323
|
+
workspaceId: string;
|
|
324
|
+
acceptedChange: AcceptedArchitectureChangeReferenceV1;
|
|
325
|
+
changeSetId: string;
|
|
326
|
+
idempotencyKey: string;
|
|
327
|
+
files: ProjectionFileResultV1[];
|
|
328
|
+
refreshSignals: ArchitectureRefreshSignalV1[];
|
|
329
|
+
}): ProjectionApplyIdentityV1 {
|
|
330
|
+
const lookupKey = projectionApplyLookupKey(input);
|
|
331
|
+
const ownedFilesDigest = digestJson(input.files as unknown as Json) as Sha256Digest;
|
|
332
|
+
const refreshSignals = input.refreshSignals.map(({ projectionReceiptDigest: _receipt, ...signal }) => signal);
|
|
333
|
+
const refreshSignalsDigest = digestJson(refreshSignals as unknown as Json) as Sha256Digest;
|
|
334
|
+
const withoutApplyId = {
|
|
335
|
+
schemaVersion: PROJECTION_APPLY_IDENTITY_SCHEMA_VERSION,
|
|
336
|
+
lookupKey,
|
|
337
|
+
repositoryId: input.repositoryId,
|
|
338
|
+
workspaceId: input.workspaceId,
|
|
339
|
+
acceptedChange: input.acceptedChange,
|
|
340
|
+
semanticCommit: { changeSetId: input.changeSetId, idempotencyKey: input.idempotencyKey },
|
|
341
|
+
ownedFilesDigest,
|
|
342
|
+
refreshSignalsDigest
|
|
343
|
+
};
|
|
344
|
+
return {
|
|
345
|
+
...withoutApplyId,
|
|
346
|
+
applyId: digestJson(withoutApplyId as unknown as Json) as Sha256Digest
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function projectionApplyReceiptInvariantIssues(input: ProjectionApplyReceiptV1): string[] {
|
|
351
|
+
const identity = createProjectionApplyIdentity({
|
|
352
|
+
repositoryId: input.identity.repositoryId,
|
|
353
|
+
workspaceId: input.identity.workspaceId,
|
|
354
|
+
acceptedChange: input.identity.acceptedChange,
|
|
355
|
+
changeSetId: input.identity.semanticCommit.changeSetId,
|
|
356
|
+
idempotencyKey: input.identity.semanticCommit.idempotencyKey,
|
|
357
|
+
files: input.result.files,
|
|
358
|
+
refreshSignals: input.result.refreshSignals
|
|
359
|
+
});
|
|
360
|
+
const issues = projectionResultInvariantIssues(input.result);
|
|
361
|
+
if (digestJson(identity as unknown as Json) !== digestJson(input.identity as unknown as Json)) {
|
|
362
|
+
issues.push("projection apply identity must bind the committed files and original refresh signals");
|
|
363
|
+
}
|
|
364
|
+
if (input.result.status !== "applied") issues.push("projection apply receipt result status must be applied");
|
|
365
|
+
if (digestJson(input.result.applyReceipt as unknown as Json) !== digestJson(input.identity as unknown as Json)) {
|
|
366
|
+
issues.push("projection apply receipt result must carry the same apply identity");
|
|
367
|
+
}
|
|
368
|
+
return issues;
|
|
369
|
+
}
|
|
370
|
+
|
|
278
371
|
export function architectureRefreshSignalInvariantIssues(input: ArchitectureRefreshSignalV1, prefix = "signal"): string[] {
|
|
279
372
|
const issues = [
|
|
280
373
|
...sortedUniqueIssues(`${prefix}.reasonCodes`, input.reasonCodes),
|