@usefragments/core 1.11.0 → 2.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/dist/{chunk-RPSEABY3.js → chunk-BMPYIUZE.js} +23 -33
- package/dist/chunk-BMPYIUZE.js.map +1 -0
- package/dist/{chunk-3LLRNCPX.js → chunk-MZ2FS7U4.js} +1 -1
- package/dist/chunk-MZ2FS7U4.js.map +1 -0
- package/dist/codes/index.d.ts +1 -1
- package/dist/codes/index.js +1 -1
- package/dist/compiled-types/index.d.ts +1 -1
- package/dist/generate/index.d.ts +1 -1
- package/dist/{governance-BAsy1k2H.d.ts → governance-hOPXGbbs.d.ts} +3 -3
- package/dist/index.d.ts +133 -6026
- package/dist/index.js +179 -806
- package/dist/index.js.map +1 -1
- package/dist/preview-runtime.d.ts +1 -1
- package/dist/react-types.d.ts +1 -1
- package/dist/schemas/index.js +1 -1
- package/dist/storyAdapter.d.ts +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/dist/topology/index.d.ts +1 -1
- package/dist/topology/index.js +1 -1
- package/package.json +1 -1
- package/src/approved-contract-tokens.test.ts +39 -0
- package/src/approved-contract-tokens.ts +18 -0
- package/src/codes/__tests__/codes.test.ts +4 -0
- package/src/codes/codes.ts +10 -0
- package/src/domain-ids.test.ts +9 -26
- package/src/domain-ids.ts +0 -45
- package/src/evaluation/evaluate.ts +17 -7
- package/src/evaluation/evaluation-v2-receipt-v1.test.ts +20 -8
- package/src/evaluation/index.ts +1 -1
- package/src/governance.ts +18 -0
- package/src/index.ts +10 -79
- package/src/topology/resolve-area.ts +1 -1
- package/src/types.ts +0 -3
- package/dist/chunk-3LLRNCPX.js.map +0 -1
- package/dist/chunk-RPSEABY3.js.map +0 -1
- package/src/feature-plan/digest.ts +0 -217
- package/src/feature-plan/feature-plan-v1.test.ts +0 -529
- package/src/feature-plan/index.ts +0 -65
- package/src/feature-plan/types.ts +0 -628
|
@@ -1,628 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import {
|
|
3
|
-
digestHexStringSchema,
|
|
4
|
-
featurePlanIdStringSchema,
|
|
5
|
-
featureRevisionIdStringSchema,
|
|
6
|
-
type FeaturePlanId,
|
|
7
|
-
type FeatureRevisionId,
|
|
8
|
-
} from "../domain-ids.js";
|
|
9
|
-
|
|
10
|
-
export const FEATURE_PLAN_MAX_EVIDENCE_REFS_V1 = 200;
|
|
11
|
-
export const FEATURE_PLAN_MAX_USES_PER_KIND_V1 = 100;
|
|
12
|
-
export const FEATURE_PLAN_MAX_GAPS_V1 = 50;
|
|
13
|
-
export const FEATURE_PLAN_MAX_DECISIONS_V1 = 50;
|
|
14
|
-
export const FEATURE_PLAN_MAX_SCENARIOS_V1 = 50;
|
|
15
|
-
export const FEATURE_PLAN_MAX_ACCEPTANCE_ITEMS_V1 = 100;
|
|
16
|
-
export const FEATURE_PLAN_MAX_TEXT_BYTES_V1 = 32 * 1024;
|
|
17
|
-
export const AGENT_FEATURE_CONTEXT_MAX_BYTES_V1 = 64 * 1024;
|
|
18
|
-
|
|
19
|
-
const utf8Length = (value: string): number => new TextEncoder().encode(value).byteLength;
|
|
20
|
-
const boundedString = (maxBytes = FEATURE_PLAN_MAX_TEXT_BYTES_V1) =>
|
|
21
|
-
z
|
|
22
|
-
.string()
|
|
23
|
-
.min(1)
|
|
24
|
-
.refine((value) => utf8Length(value) <= maxBytes, `Value exceeds ${maxBytes} UTF-8 bytes`);
|
|
25
|
-
const shortString = () => boundedString(4_096);
|
|
26
|
-
const unique = (values: readonly string[]): boolean => new Set(values).size === values.length;
|
|
27
|
-
const uniqueIdArray = (max = FEATURE_PLAN_MAX_EVIDENCE_REFS_V1) =>
|
|
28
|
-
z.array(shortString()).max(max).refine(unique, "ID references must be unique");
|
|
29
|
-
|
|
30
|
-
export const evidenceRefV1Schema = z
|
|
31
|
-
.object({
|
|
32
|
-
evidenceId: shortString(),
|
|
33
|
-
kind: z.enum(["user_intent", "image", "source", "contract", "pattern", "model"]),
|
|
34
|
-
authority: z.enum(["user_asserted", "source_observed", "contract_approved", "model_inferred"]),
|
|
35
|
-
ref: boundedString(),
|
|
36
|
-
digest: digestHexStringSchema,
|
|
37
|
-
})
|
|
38
|
-
.strict();
|
|
39
|
-
|
|
40
|
-
export const placementDecisionV1Schema = z
|
|
41
|
-
.object({
|
|
42
|
-
targetKind: z.enum(["page", "section"]),
|
|
43
|
-
targetRef: shortString(),
|
|
44
|
-
routeRef: shortString().optional(),
|
|
45
|
-
parentSlotRef: shortString(),
|
|
46
|
-
evidenceRefIds: uniqueIdArray(),
|
|
47
|
-
})
|
|
48
|
-
.strict();
|
|
49
|
-
|
|
50
|
-
const propValueSchema = z.union([boundedString(), z.number().finite(), z.boolean(), z.null()]);
|
|
51
|
-
export const componentUseDecisionV1Schema = z
|
|
52
|
-
.object({
|
|
53
|
-
useId: shortString(),
|
|
54
|
-
componentId: shortString(),
|
|
55
|
-
importPath: shortString(),
|
|
56
|
-
purpose: boundedString(),
|
|
57
|
-
props: z
|
|
58
|
-
.record(shortString(), propValueSchema)
|
|
59
|
-
.refine((props) => Object.keys(props).length <= 100, "Component props exceed V1 limit"),
|
|
60
|
-
evidenceRefIds: uniqueIdArray(),
|
|
61
|
-
})
|
|
62
|
-
.strict();
|
|
63
|
-
|
|
64
|
-
export const tokenUseDecisionV1Schema = z
|
|
65
|
-
.object({
|
|
66
|
-
useId: shortString(),
|
|
67
|
-
tokenId: shortString(),
|
|
68
|
-
role: shortString(),
|
|
69
|
-
evidenceRefIds: uniqueIdArray(),
|
|
70
|
-
})
|
|
71
|
-
.strict();
|
|
72
|
-
|
|
73
|
-
export const patternStatusV1Schema = z.enum([
|
|
74
|
-
"observed",
|
|
75
|
-
"candidate",
|
|
76
|
-
"approved_guidance",
|
|
77
|
-
"contract_canonical",
|
|
78
|
-
"deprecated",
|
|
79
|
-
]);
|
|
80
|
-
export const patternUseDecisionV1Schema = z
|
|
81
|
-
.object({
|
|
82
|
-
useId: shortString(),
|
|
83
|
-
patternId: shortString(),
|
|
84
|
-
patternRevisionDigest: digestHexStringSchema,
|
|
85
|
-
status: patternStatusV1Schema,
|
|
86
|
-
disposition: z.enum(["reuse", "reference", "reject"]),
|
|
87
|
-
evidenceRefIds: uniqueIdArray(),
|
|
88
|
-
})
|
|
89
|
-
.strict()
|
|
90
|
-
.superRefine((pattern, context) => {
|
|
91
|
-
if (
|
|
92
|
-
pattern.disposition === "reuse" &&
|
|
93
|
-
pattern.status !== "approved_guidance" &&
|
|
94
|
-
pattern.status !== "contract_canonical"
|
|
95
|
-
) {
|
|
96
|
-
context.addIssue({
|
|
97
|
-
code: z.ZodIssueCode.custom,
|
|
98
|
-
path: ["disposition"],
|
|
99
|
-
message: "Only approved-guidance or contract-canonical patterns may be reused",
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
export const layoutConcernV1Schema = z.enum([
|
|
105
|
-
"outer_inline_spacing",
|
|
106
|
-
"outer_block_spacing",
|
|
107
|
-
"inner_padding",
|
|
108
|
-
"child_gap",
|
|
109
|
-
"max_inline_size",
|
|
110
|
-
"scroll_x",
|
|
111
|
-
"scroll_y",
|
|
112
|
-
"sticky_offset",
|
|
113
|
-
"breakpoint_switching",
|
|
114
|
-
]);
|
|
115
|
-
const layoutConcernOwnerV1Schema = z
|
|
116
|
-
.object({
|
|
117
|
-
concern: layoutConcernV1Schema,
|
|
118
|
-
ownerLayer: z.enum([
|
|
119
|
-
"application_shell",
|
|
120
|
-
"route_shell",
|
|
121
|
-
"page",
|
|
122
|
-
"section",
|
|
123
|
-
"pattern",
|
|
124
|
-
"primitive",
|
|
125
|
-
]),
|
|
126
|
-
ownerRef: shortString(),
|
|
127
|
-
childAction: z.enum(["consume", "own", "override"]),
|
|
128
|
-
tokenRef: shortString().optional(),
|
|
129
|
-
breakpointRef: shortString().optional(),
|
|
130
|
-
evidenceRefIds: uniqueIdArray(),
|
|
131
|
-
overrideDecisionId: shortString().optional(),
|
|
132
|
-
})
|
|
133
|
-
.strict()
|
|
134
|
-
.superRefine((row, context) => {
|
|
135
|
-
if (row.childAction === "override" && !row.overrideDecisionId) {
|
|
136
|
-
context.addIssue({
|
|
137
|
-
code: z.ZodIssueCode.custom,
|
|
138
|
-
path: ["overrideDecisionId"],
|
|
139
|
-
message: "A layout override must reference an approved decision",
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
if (row.childAction !== "override" && row.overrideDecisionId) {
|
|
143
|
-
context.addIssue({
|
|
144
|
-
code: z.ZodIssueCode.custom,
|
|
145
|
-
path: ["overrideDecisionId"],
|
|
146
|
-
message: "Only an override may reference an override decision",
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
export const layoutOwnershipV1Schema = z
|
|
151
|
-
.object({
|
|
152
|
-
concerns: z
|
|
153
|
-
.array(layoutConcernOwnerV1Schema)
|
|
154
|
-
.max(32)
|
|
155
|
-
.refine(
|
|
156
|
-
(rows) => new Set(rows.map((row) => row.concern)).size === rows.length,
|
|
157
|
-
"Each layout concern must have exactly one owner"
|
|
158
|
-
),
|
|
159
|
-
})
|
|
160
|
-
.strict();
|
|
161
|
-
|
|
162
|
-
export const responsiveDecisionV1Schema = z
|
|
163
|
-
.object({
|
|
164
|
-
decisionId: shortString(),
|
|
165
|
-
source: z.enum(["contract", "observed", "desktop_only"]),
|
|
166
|
-
breakpointRef: shortString().optional(),
|
|
167
|
-
behavior: boundedString(),
|
|
168
|
-
evidenceRefIds: uniqueIdArray(),
|
|
169
|
-
})
|
|
170
|
-
.strict()
|
|
171
|
-
.superRefine((decision, context) => {
|
|
172
|
-
if (decision.source !== "desktop_only" && !decision.breakpointRef) {
|
|
173
|
-
context.addIssue({
|
|
174
|
-
code: z.ZodIssueCode.custom,
|
|
175
|
-
path: ["breakpointRef"],
|
|
176
|
-
message: "Responsive contract/observed decisions must pin a breakpoint",
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
export const scenarioRequirementV1Schema = z
|
|
182
|
-
.object({
|
|
183
|
-
scenarioId: shortString(),
|
|
184
|
-
viewportRef: shortString().optional(),
|
|
185
|
-
themeRef: shortString().optional(),
|
|
186
|
-
actorRole: shortString().optional(),
|
|
187
|
-
dataState: z.enum(["default", "loading", "empty", "partial", "error"]),
|
|
188
|
-
interactionState: z
|
|
189
|
-
.enum(["pristine", "dirty", "validating", "saving", "saved", "disabled"])
|
|
190
|
-
.optional(),
|
|
191
|
-
acceptanceIds: uniqueIdArray(FEATURE_PLAN_MAX_ACCEPTANCE_ITEMS_V1),
|
|
192
|
-
})
|
|
193
|
-
.strict();
|
|
194
|
-
|
|
195
|
-
export const gapDispositionV1Schema = z.enum([
|
|
196
|
-
"canonical_reuse",
|
|
197
|
-
"approved_pattern",
|
|
198
|
-
"local_composition",
|
|
199
|
-
"intentional_one_off",
|
|
200
|
-
"proposed_contract_addition",
|
|
201
|
-
"unresolved",
|
|
202
|
-
]);
|
|
203
|
-
export const capabilityGapV1Schema = z
|
|
204
|
-
.object({
|
|
205
|
-
gapId: shortString(),
|
|
206
|
-
capability: boundedString(),
|
|
207
|
-
required: z.boolean(),
|
|
208
|
-
disposition: gapDispositionV1Schema,
|
|
209
|
-
reason: boundedString(),
|
|
210
|
-
evidenceRefIds: uniqueIdArray(),
|
|
211
|
-
proposedContractCandidateId: shortString().optional(),
|
|
212
|
-
})
|
|
213
|
-
.strict()
|
|
214
|
-
.superRefine((gap, context) => {
|
|
215
|
-
if (gap.disposition === "proposed_contract_addition" && !gap.proposedContractCandidateId) {
|
|
216
|
-
context.addIssue({
|
|
217
|
-
code: z.ZodIssueCode.custom,
|
|
218
|
-
path: ["proposedContractCandidateId"],
|
|
219
|
-
message: "A proposed contract addition must pin its candidate",
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
if (gap.disposition !== "proposed_contract_addition" && gap.proposedContractCandidateId) {
|
|
223
|
-
context.addIssue({
|
|
224
|
-
code: z.ZodIssueCode.custom,
|
|
225
|
-
path: ["proposedContractCandidateId"],
|
|
226
|
-
message: "Only a proposed contract addition may carry a candidate ID",
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
export const featureAcceptanceItemV1Schema = z
|
|
232
|
-
.object({
|
|
233
|
-
acceptanceId: shortString(),
|
|
234
|
-
statement: boundedString(),
|
|
235
|
-
proof: z.enum(["unit", "integration", "browser", "governance", "manual"]),
|
|
236
|
-
required: z.boolean(),
|
|
237
|
-
evidenceRefIds: uniqueIdArray(),
|
|
238
|
-
})
|
|
239
|
-
.strict();
|
|
240
|
-
|
|
241
|
-
const decisionOptionV1Schema = z
|
|
242
|
-
.object({ optionId: shortString(), label: boundedString(), consequence: boundedString() })
|
|
243
|
-
.strict();
|
|
244
|
-
export const decisionRequestV1Schema = z
|
|
245
|
-
.object({
|
|
246
|
-
decisionId: shortString(),
|
|
247
|
-
kind: z.enum(["placement", "reuse", "gap", "layout_override", "responsive", "scope"]),
|
|
248
|
-
question: boundedString(),
|
|
249
|
-
options: z
|
|
250
|
-
.array(decisionOptionV1Schema)
|
|
251
|
-
.min(2)
|
|
252
|
-
.max(20)
|
|
253
|
-
.refine(
|
|
254
|
-
(options) => new Set(options.map((option) => option.optionId)).size === options.length,
|
|
255
|
-
"Decision option IDs must be unique"
|
|
256
|
-
),
|
|
257
|
-
recommendedOptionId: shortString().optional(),
|
|
258
|
-
required: z.boolean(),
|
|
259
|
-
evidenceRefIds: uniqueIdArray(),
|
|
260
|
-
})
|
|
261
|
-
.strict()
|
|
262
|
-
.superRefine((decision, context) => {
|
|
263
|
-
if (
|
|
264
|
-
decision.recommendedOptionId &&
|
|
265
|
-
!decision.options.some((option) => option.optionId === decision.recommendedOptionId)
|
|
266
|
-
) {
|
|
267
|
-
context.addIssue({
|
|
268
|
-
code: z.ZodIssueCode.custom,
|
|
269
|
-
path: ["recommendedOptionId"],
|
|
270
|
-
message: "Recommended option must resolve inside the decision",
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
export const decisionResolutionV1Schema = z
|
|
276
|
-
.object({ decisionId: shortString(), selectedOptionId: shortString() })
|
|
277
|
-
.strict();
|
|
278
|
-
|
|
279
|
-
export const featurePinsV1Schema = z
|
|
280
|
-
.object({
|
|
281
|
-
bindingId: shortString(),
|
|
282
|
-
grounding: z
|
|
283
|
-
.object({
|
|
284
|
-
sourceCommitId: shortString(),
|
|
285
|
-
analysisPlanDigest: digestHexStringSchema,
|
|
286
|
-
contextSliceDigest: digestHexStringSchema,
|
|
287
|
-
})
|
|
288
|
-
.strict(),
|
|
289
|
-
authority: z
|
|
290
|
-
.object({
|
|
291
|
-
fcid: digestHexStringSchema,
|
|
292
|
-
contractArtifactDigest: digestHexStringSchema,
|
|
293
|
-
analysisObligationsDigest: digestHexStringSchema,
|
|
294
|
-
})
|
|
295
|
-
.strict(),
|
|
296
|
-
evidenceDigest: digestHexStringSchema,
|
|
297
|
-
})
|
|
298
|
-
.strict();
|
|
299
|
-
|
|
300
|
-
const commonRevisionShape = {
|
|
301
|
-
pins: featurePinsV1Schema,
|
|
302
|
-
placement: placementDecisionV1Schema,
|
|
303
|
-
componentUses: z.array(componentUseDecisionV1Schema).max(FEATURE_PLAN_MAX_USES_PER_KIND_V1),
|
|
304
|
-
tokenUses: z.array(tokenUseDecisionV1Schema).max(FEATURE_PLAN_MAX_USES_PER_KIND_V1),
|
|
305
|
-
patternUses: z.array(patternUseDecisionV1Schema).max(FEATURE_PLAN_MAX_USES_PER_KIND_V1),
|
|
306
|
-
layout: layoutOwnershipV1Schema,
|
|
307
|
-
responsive: z.array(responsiveDecisionV1Schema).max(FEATURE_PLAN_MAX_DECISIONS_V1),
|
|
308
|
-
scenarios: z.array(scenarioRequirementV1Schema).max(FEATURE_PLAN_MAX_SCENARIOS_V1),
|
|
309
|
-
gaps: z.array(capabilityGapV1Schema).max(FEATURE_PLAN_MAX_GAPS_V1),
|
|
310
|
-
acceptance: z.array(featureAcceptanceItemV1Schema).max(FEATURE_PLAN_MAX_ACCEPTANCE_ITEMS_V1),
|
|
311
|
-
evidenceRefs: z.array(evidenceRefV1Schema).max(FEATURE_PLAN_MAX_EVIDENCE_REFS_V1),
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
function addUniqueIdIssue(
|
|
315
|
-
rows: readonly Record<string, unknown>[],
|
|
316
|
-
key: string,
|
|
317
|
-
path: string,
|
|
318
|
-
context: z.RefinementCtx
|
|
319
|
-
): void {
|
|
320
|
-
const values = rows
|
|
321
|
-
.map((row) => row[key])
|
|
322
|
-
.filter((value): value is string => typeof value === "string");
|
|
323
|
-
if (new Set(values).size !== values.length) {
|
|
324
|
-
context.addIssue({
|
|
325
|
-
code: z.ZodIssueCode.custom,
|
|
326
|
-
path: [path],
|
|
327
|
-
message: `${path} IDs must be unique`,
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
type CommonGraph = z.infer<z.ZodObject<typeof commonRevisionShape>>;
|
|
333
|
-
|
|
334
|
-
function validateCommonGraph(
|
|
335
|
-
graph: CommonGraph,
|
|
336
|
-
context: z.RefinementCtx,
|
|
337
|
-
decisionIds: ReadonlySet<string>
|
|
338
|
-
): void {
|
|
339
|
-
addUniqueIdIssue(graph.evidenceRefs, "evidenceId", "evidenceRefs", context);
|
|
340
|
-
addUniqueIdIssue(graph.componentUses, "useId", "componentUses", context);
|
|
341
|
-
addUniqueIdIssue(graph.tokenUses, "useId", "tokenUses", context);
|
|
342
|
-
addUniqueIdIssue(graph.patternUses, "useId", "patternUses", context);
|
|
343
|
-
addUniqueIdIssue(graph.responsive, "decisionId", "responsive", context);
|
|
344
|
-
addUniqueIdIssue(graph.scenarios, "scenarioId", "scenarios", context);
|
|
345
|
-
addUniqueIdIssue(graph.gaps, "gapId", "gaps", context);
|
|
346
|
-
addUniqueIdIssue(graph.acceptance, "acceptanceId", "acceptance", context);
|
|
347
|
-
|
|
348
|
-
const evidenceIds = new Set(graph.evidenceRefs.map((evidence) => evidence.evidenceId));
|
|
349
|
-
const acceptanceIds = new Set(graph.acceptance.map((acceptance) => acceptance.acceptanceId));
|
|
350
|
-
const evidenceReferences: Array<readonly [readonly string[], string]> = [
|
|
351
|
-
[graph.placement.evidenceRefIds, "placement.evidenceRefIds"],
|
|
352
|
-
...graph.componentUses.map(
|
|
353
|
-
(row, index) => [row.evidenceRefIds, `componentUses.${index}.evidenceRefIds`] as const
|
|
354
|
-
),
|
|
355
|
-
...graph.tokenUses.map(
|
|
356
|
-
(row, index) => [row.evidenceRefIds, `tokenUses.${index}.evidenceRefIds`] as const
|
|
357
|
-
),
|
|
358
|
-
...graph.patternUses.map(
|
|
359
|
-
(row, index) => [row.evidenceRefIds, `patternUses.${index}.evidenceRefIds`] as const
|
|
360
|
-
),
|
|
361
|
-
...graph.layout.concerns.map(
|
|
362
|
-
(row, index) => [row.evidenceRefIds, `layout.concerns.${index}.evidenceRefIds`] as const
|
|
363
|
-
),
|
|
364
|
-
...graph.responsive.map(
|
|
365
|
-
(row, index) => [row.evidenceRefIds, `responsive.${index}.evidenceRefIds`] as const
|
|
366
|
-
),
|
|
367
|
-
...graph.gaps.map(
|
|
368
|
-
(row, index) => [row.evidenceRefIds, `gaps.${index}.evidenceRefIds`] as const
|
|
369
|
-
),
|
|
370
|
-
...graph.acceptance.map(
|
|
371
|
-
(row, index) => [row.evidenceRefIds, `acceptance.${index}.evidenceRefIds`] as const
|
|
372
|
-
),
|
|
373
|
-
];
|
|
374
|
-
for (const [references, path] of evidenceReferences) {
|
|
375
|
-
for (const reference of references) {
|
|
376
|
-
if (!evidenceIds.has(reference)) {
|
|
377
|
-
context.addIssue({
|
|
378
|
-
code: z.ZodIssueCode.custom,
|
|
379
|
-
path: path.split("."),
|
|
380
|
-
message: `Unknown evidence reference: ${reference}`,
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
for (const [index, scenario] of graph.scenarios.entries()) {
|
|
386
|
-
for (const acceptanceId of scenario.acceptanceIds) {
|
|
387
|
-
if (!acceptanceIds.has(acceptanceId)) {
|
|
388
|
-
context.addIssue({
|
|
389
|
-
code: z.ZodIssueCode.custom,
|
|
390
|
-
path: ["scenarios", index, "acceptanceIds"],
|
|
391
|
-
message: `Unknown acceptance reference: ${acceptanceId}`,
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
for (const [index, row] of graph.layout.concerns.entries()) {
|
|
397
|
-
if (row.overrideDecisionId && !decisionIds.has(row.overrideDecisionId)) {
|
|
398
|
-
context.addIssue({
|
|
399
|
-
code: z.ZodIssueCode.custom,
|
|
400
|
-
path: ["layout", "concerns", index, "overrideDecisionId"],
|
|
401
|
-
message: `Unknown decision reference: ${row.overrideDecisionId}`,
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const proposalInputShape = {
|
|
408
|
-
schemaVersion: z.literal(1),
|
|
409
|
-
kind: z.literal("proposal"),
|
|
410
|
-
planId: featurePlanIdStringSchema,
|
|
411
|
-
parentRevisionDigest: digestHexStringSchema.optional(),
|
|
412
|
-
...commonRevisionShape,
|
|
413
|
-
questions: z.array(decisionRequestV1Schema).max(FEATURE_PLAN_MAX_DECISIONS_V1),
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
function validateProposal(
|
|
417
|
-
proposal: z.infer<z.ZodObject<typeof proposalInputShape>>,
|
|
418
|
-
context: z.RefinementCtx
|
|
419
|
-
): void {
|
|
420
|
-
addUniqueIdIssue(proposal.questions, "decisionId", "questions", context);
|
|
421
|
-
const decisionIds = new Set(proposal.questions.map((question) => question.decisionId));
|
|
422
|
-
validateCommonGraph(proposal, context, decisionIds);
|
|
423
|
-
const evidenceIds = new Set(proposal.evidenceRefs.map((evidence) => evidence.evidenceId));
|
|
424
|
-
for (const [index, question] of proposal.questions.entries()) {
|
|
425
|
-
for (const reference of question.evidenceRefIds) {
|
|
426
|
-
if (!evidenceIds.has(reference)) {
|
|
427
|
-
context.addIssue({
|
|
428
|
-
code: z.ZodIssueCode.custom,
|
|
429
|
-
path: ["questions", index, "evidenceRefIds"],
|
|
430
|
-
message: `Unknown evidence reference: ${reference}`,
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
export const featureProposalInputV1Schema = z
|
|
438
|
-
.object(proposalInputShape)
|
|
439
|
-
.strict()
|
|
440
|
-
.superRefine(validateProposal);
|
|
441
|
-
|
|
442
|
-
export const featureProposalV1Schema = z
|
|
443
|
-
.object({
|
|
444
|
-
...proposalInputShape,
|
|
445
|
-
revisionId: featureRevisionIdStringSchema,
|
|
446
|
-
revisionDigest: digestHexStringSchema,
|
|
447
|
-
})
|
|
448
|
-
.strict()
|
|
449
|
-
.superRefine(validateProposal);
|
|
450
|
-
|
|
451
|
-
const manifestInputShape = {
|
|
452
|
-
schemaVersion: z.literal(1),
|
|
453
|
-
kind: z.literal("approved_manifest"),
|
|
454
|
-
planId: featurePlanIdStringSchema,
|
|
455
|
-
parentRevisionDigest: digestHexStringSchema,
|
|
456
|
-
proposalRevisionDigest: digestHexStringSchema,
|
|
457
|
-
...commonRevisionShape,
|
|
458
|
-
validatedGrounding: z
|
|
459
|
-
.object({
|
|
460
|
-
sourceCommitId: shortString(),
|
|
461
|
-
analysisPlanDigest: digestHexStringSchema,
|
|
462
|
-
contextSliceDigest: digestHexStringSchema,
|
|
463
|
-
})
|
|
464
|
-
.strict(),
|
|
465
|
-
decisions: z.array(decisionResolutionV1Schema).max(FEATURE_PLAN_MAX_DECISIONS_V1),
|
|
466
|
-
approval: z
|
|
467
|
-
.object({
|
|
468
|
-
approverUserId: shortString(),
|
|
469
|
-
decisionDigest: digestHexStringSchema,
|
|
470
|
-
approvedAt: z.string().datetime({ offset: true }),
|
|
471
|
-
expiresAt: z.string().datetime({ offset: true }).optional(),
|
|
472
|
-
})
|
|
473
|
-
.strict(),
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
const portableRepositoryPathSchema = shortString().refine(
|
|
477
|
-
(value) => !value.startsWith("/") && !value.includes("\\") && !value.split("/").includes(".."),
|
|
478
|
-
"Expected a portable repository-relative path"
|
|
479
|
-
);
|
|
480
|
-
|
|
481
|
-
function validateManifest(
|
|
482
|
-
manifest: z.infer<z.ZodObject<typeof manifestInputShape>>,
|
|
483
|
-
context: z.RefinementCtx
|
|
484
|
-
): void {
|
|
485
|
-
addUniqueIdIssue(manifest.decisions, "decisionId", "decisions", context);
|
|
486
|
-
validateCommonGraph(manifest, context, new Set(manifest.decisions.map((row) => row.decisionId)));
|
|
487
|
-
if (
|
|
488
|
-
manifest.validatedGrounding.contextSliceDigest !== manifest.pins.grounding.contextSliceDigest
|
|
489
|
-
) {
|
|
490
|
-
context.addIssue({
|
|
491
|
-
code: z.ZodIssueCode.custom,
|
|
492
|
-
path: ["validatedGrounding", "contextSliceDigest"],
|
|
493
|
-
message: "Validated material context must match the pinned proposal context",
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
for (const [index, gap] of manifest.gaps.entries()) {
|
|
497
|
-
if (
|
|
498
|
-
gap.required &&
|
|
499
|
-
(gap.disposition === "unresolved" || gap.disposition === "proposed_contract_addition")
|
|
500
|
-
) {
|
|
501
|
-
context.addIssue({
|
|
502
|
-
code: z.ZodIssueCode.custom,
|
|
503
|
-
path: ["gaps", index, "disposition"],
|
|
504
|
-
message:
|
|
505
|
-
"A required unresolved or proposed-contract gap blocks approval until it is resolved",
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
export const approvedFeatureManifestInputV1Schema = z
|
|
512
|
-
.object(manifestInputShape)
|
|
513
|
-
.strict()
|
|
514
|
-
.superRefine(validateManifest);
|
|
515
|
-
|
|
516
|
-
export const approvedFeatureManifestV1Schema = z
|
|
517
|
-
.object({
|
|
518
|
-
...manifestInputShape,
|
|
519
|
-
revisionId: featureRevisionIdStringSchema,
|
|
520
|
-
revisionDigest: digestHexStringSchema,
|
|
521
|
-
})
|
|
522
|
-
.strict()
|
|
523
|
-
.superRefine(validateManifest);
|
|
524
|
-
|
|
525
|
-
export const agentFeatureContextV1Schema = z
|
|
526
|
-
.object({
|
|
527
|
-
schemaVersion: z.literal(1),
|
|
528
|
-
planId: featurePlanIdStringSchema,
|
|
529
|
-
manifestDigest: digestHexStringSchema,
|
|
530
|
-
pins: featurePinsV1Schema,
|
|
531
|
-
validatedGrounding: manifestInputShape.validatedGrounding,
|
|
532
|
-
target: placementDecisionV1Schema,
|
|
533
|
-
componentUses: commonRevisionShape.componentUses,
|
|
534
|
-
tokenUses: commonRevisionShape.tokenUses,
|
|
535
|
-
patternUses: commonRevisionShape.patternUses,
|
|
536
|
-
layout: layoutOwnershipV1Schema,
|
|
537
|
-
responsive: commonRevisionShape.responsive,
|
|
538
|
-
scenarios: commonRevisionShape.scenarios,
|
|
539
|
-
acceptedGaps: commonRevisionShape.gaps,
|
|
540
|
-
acceptance: commonRevisionShape.acceptance,
|
|
541
|
-
decisions: manifestInputShape.decisions,
|
|
542
|
-
relevantFiles: z
|
|
543
|
-
.array(portableRepositoryPathSchema)
|
|
544
|
-
.max(500)
|
|
545
|
-
.refine(unique, "relevantFiles must be unique"),
|
|
546
|
-
commands: z.array(boundedString()).max(100),
|
|
547
|
-
})
|
|
548
|
-
.strict()
|
|
549
|
-
.superRefine((contextValue, context) => {
|
|
550
|
-
const graph = {
|
|
551
|
-
pins: contextValue.pins,
|
|
552
|
-
placement: contextValue.target,
|
|
553
|
-
componentUses: contextValue.componentUses,
|
|
554
|
-
tokenUses: contextValue.tokenUses,
|
|
555
|
-
patternUses: contextValue.patternUses,
|
|
556
|
-
layout: contextValue.layout,
|
|
557
|
-
responsive: contextValue.responsive,
|
|
558
|
-
scenarios: contextValue.scenarios,
|
|
559
|
-
gaps: contextValue.acceptedGaps,
|
|
560
|
-
acceptance: contextValue.acceptance,
|
|
561
|
-
evidenceRefs: [],
|
|
562
|
-
};
|
|
563
|
-
// Evidence bodies are deliberately absent from the bounded agent projection,
|
|
564
|
-
// so validate decision/acceptance/layout identity here and evidence links at
|
|
565
|
-
// manifest construction time.
|
|
566
|
-
addUniqueIdIssue(contextValue.decisions, "decisionId", "decisions", context);
|
|
567
|
-
addUniqueIdIssue(graph.componentUses, "useId", "componentUses", context);
|
|
568
|
-
addUniqueIdIssue(graph.tokenUses, "useId", "tokenUses", context);
|
|
569
|
-
addUniqueIdIssue(graph.patternUses, "useId", "patternUses", context);
|
|
570
|
-
addUniqueIdIssue(graph.scenarios, "scenarioId", "scenarios", context);
|
|
571
|
-
addUniqueIdIssue(graph.acceptance, "acceptanceId", "acceptance", context);
|
|
572
|
-
const decisions = new Set(contextValue.decisions.map((row) => row.decisionId));
|
|
573
|
-
for (const [index, row] of contextValue.layout.concerns.entries()) {
|
|
574
|
-
if (row.overrideDecisionId && !decisions.has(row.overrideDecisionId)) {
|
|
575
|
-
context.addIssue({
|
|
576
|
-
code: z.ZodIssueCode.custom,
|
|
577
|
-
path: ["layout", "concerns", index, "overrideDecisionId"],
|
|
578
|
-
message: `Unknown decision reference: ${row.overrideDecisionId}`,
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
for (const [index, gap] of contextValue.acceptedGaps.entries()) {
|
|
583
|
-
if (
|
|
584
|
-
gap.required &&
|
|
585
|
-
(gap.disposition === "unresolved" || gap.disposition === "proposed_contract_addition")
|
|
586
|
-
) {
|
|
587
|
-
context.addIssue({
|
|
588
|
-
code: z.ZodIssueCode.custom,
|
|
589
|
-
path: ["acceptedGaps", index],
|
|
590
|
-
message: "Agent context cannot contain a required unresolved or proposed-contract gap",
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
export type EvidenceRefV1 = z.infer<typeof evidenceRefV1Schema>;
|
|
597
|
-
export type PlacementDecisionV1 = z.infer<typeof placementDecisionV1Schema>;
|
|
598
|
-
export type ComponentUseDecisionV1 = z.infer<typeof componentUseDecisionV1Schema>;
|
|
599
|
-
export type TokenUseDecisionV1 = z.infer<typeof tokenUseDecisionV1Schema>;
|
|
600
|
-
export type PatternStatusV1 = z.infer<typeof patternStatusV1Schema>;
|
|
601
|
-
export type PatternUseDecisionV1 = z.infer<typeof patternUseDecisionV1Schema>;
|
|
602
|
-
export type LayoutConcernV1 = z.infer<typeof layoutConcernV1Schema>;
|
|
603
|
-
export type LayoutOwnershipV1 = z.infer<typeof layoutOwnershipV1Schema>;
|
|
604
|
-
export type ResponsiveDecisionV1 = z.infer<typeof responsiveDecisionV1Schema>;
|
|
605
|
-
export type ScenarioRequirementV1 = z.infer<typeof scenarioRequirementV1Schema>;
|
|
606
|
-
export type GapDispositionV1 = z.infer<typeof gapDispositionV1Schema>;
|
|
607
|
-
export type CapabilityGapV1 = z.infer<typeof capabilityGapV1Schema>;
|
|
608
|
-
export type FeatureAcceptanceItemV1 = z.infer<typeof featureAcceptanceItemV1Schema>;
|
|
609
|
-
export type DecisionRequestV1 = z.infer<typeof decisionRequestV1Schema>;
|
|
610
|
-
export type DecisionResolutionV1 = z.infer<typeof decisionResolutionV1Schema>;
|
|
611
|
-
export type FeaturePinsV1 = z.infer<typeof featurePinsV1Schema>;
|
|
612
|
-
export type FeatureProposalInputV1 = z.input<typeof featureProposalInputV1Schema> & {
|
|
613
|
-
planId: FeaturePlanId;
|
|
614
|
-
};
|
|
615
|
-
export type FeatureProposalV1 = z.output<typeof featureProposalV1Schema> & {
|
|
616
|
-
planId: FeaturePlanId;
|
|
617
|
-
revisionId: FeatureRevisionId;
|
|
618
|
-
};
|
|
619
|
-
export type ApprovedFeatureManifestInputV1 = z.input<
|
|
620
|
-
typeof approvedFeatureManifestInputV1Schema
|
|
621
|
-
> & { planId: FeaturePlanId };
|
|
622
|
-
export type ApprovedFeatureManifestV1 = z.output<typeof approvedFeatureManifestV1Schema> & {
|
|
623
|
-
planId: FeaturePlanId;
|
|
624
|
-
revisionId: FeatureRevisionId;
|
|
625
|
-
};
|
|
626
|
-
export type AgentFeatureContextV1 = z.output<typeof agentFeatureContextV1Schema> & {
|
|
627
|
-
planId: FeaturePlanId;
|
|
628
|
-
};
|