@patronage/software-factory 0.20.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.
@@ -0,0 +1,771 @@
1
+ import { z } from "zod";
2
+ const MAX_CLOSEOUT_ROWS = 1e3;
3
+ const IdentifierSchema = z.string().min(1).max(500);
4
+ const NarrativeSchema = z.string().min(1).max(1e4);
5
+ const RepoSchema = z.string().min(1).max(200);
6
+ const IsoDateSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/u);
7
+ const CloseoutLandingSpotSchema = z.enum([
8
+ "CLI",
9
+ "profile",
10
+ "skill",
11
+ "ADR"
12
+ ]);
13
+ const CloseoutSourceSchema = z.object({
14
+ detail: NarrativeSchema,
15
+ kind: z.enum(["interior-telemetry", "boundary-thermo-attestation"])
16
+ }).strict();
17
+ const CloseoutScopingSchema = z.object({
18
+ epic: IdentifierSchema,
19
+ issueFilter: z.number().int().positive().optional(),
20
+ prFilter: z.number().int().positive().optional(),
21
+ repo: RepoSchema,
22
+ scopedByEpicIssueSet: z.boolean(),
23
+ window: z.object({
24
+ from: IsoDateSchema,
25
+ to: IsoDateSchema
26
+ }).strict()
27
+ }).strict();
28
+ const CloseoutBudgetSchema = z.object({
29
+ interiorSourcesConsumed: z.number().int().nonnegative(),
30
+ note: NarrativeSchema,
31
+ scoping: CloseoutScopingSchema.optional(),
32
+ tier: z.literal("overseer")
33
+ }).strict();
34
+ const BoundaryThermoAttestationSchema = z.object({
35
+ owner: z.string().min(1).max(200),
36
+ runs: z.number().int().nonnegative(),
37
+ waived: z.boolean(),
38
+ waiverRationale: NarrativeSchema.optional()
39
+ }).strict();
40
+ const CloseoutMetricRowSchema = z.object({
41
+ label: IdentifierSchema,
42
+ metric: IdentifierSchema,
43
+ source: CloseoutSourceSchema,
44
+ unit: z.enum([
45
+ "tokens",
46
+ "usd",
47
+ "count",
48
+ "ratio",
49
+ "boolean",
50
+ "text"
51
+ ]),
52
+ value: z.union([
53
+ z.number(),
54
+ NarrativeSchema,
55
+ z.boolean(),
56
+ z.null()
57
+ ])
58
+ }).strict();
59
+ const CloseoutLessonSchema = z.object({
60
+ id: IdentifierSchema,
61
+ landingSpot: CloseoutLandingSpotSchema,
62
+ lesson: NarrativeSchema,
63
+ rationale: NarrativeSchema.optional(),
64
+ source: CloseoutSourceSchema.optional()
65
+ }).strict();
66
+ const CloseoutBlindspotSchema = z.object({
67
+ id: IdentifierSchema,
68
+ note: NarrativeSchema,
69
+ reason: NarrativeSchema.optional()
70
+ }).strict();
71
+ const CloseoutLedgerRowSchema = z.object({
72
+ detail: NarrativeSchema.optional(),
73
+ epic: IdentifierSchema,
74
+ generatedAt: z.iso.datetime(),
75
+ key: IdentifierSchema,
76
+ landingSpot: CloseoutLandingSpotSchema.optional(),
77
+ repo: RepoSchema,
78
+ rowType: z.enum([
79
+ "metric",
80
+ "lesson",
81
+ "blindspot"
82
+ ]),
83
+ schemaVersion: z.literal(3),
84
+ source: NarrativeSchema,
85
+ unit: IdentifierSchema.optional(),
86
+ value: z.union([
87
+ NarrativeSchema,
88
+ z.number(),
89
+ z.null()
90
+ ])
91
+ }).strict();
92
+ /** Strict, bounded runtime transport contract emitted by factory:closeout. */
93
+ const closeoutArtifactSchema = z.object({
94
+ boundaryThermo: BoundaryThermoAttestationSchema,
95
+ budget: CloseoutBudgetSchema,
96
+ couldNotSee: z.array(CloseoutBlindspotSchema).min(1).max(MAX_CLOSEOUT_ROWS),
97
+ epic: IdentifierSchema,
98
+ generatedAt: z.iso.datetime(),
99
+ ledgerRows: z.array(CloseoutLedgerRowSchema).max(MAX_CLOSEOUT_ROWS),
100
+ lessons: z.array(CloseoutLessonSchema).max(MAX_CLOSEOUT_ROWS),
101
+ metrics: z.array(CloseoutMetricRowSchema).max(MAX_CLOSEOUT_ROWS),
102
+ repo: RepoSchema,
103
+ schemaVersion: z.literal(3)
104
+ }).strict();
105
+ //#endregion
106
+ //#region src/schemas.ts
107
+ const EVIDENCE_CHECK_TYPES = ["review", "verify"];
108
+ const EVIDENCE_REVIEW_RUNGS = [
109
+ "independent-model",
110
+ "oracle",
111
+ "human"
112
+ ];
113
+ const DIFF_CLASSIFICATIONS = [
114
+ "docs/process-only",
115
+ "trivial",
116
+ "non-trivial"
117
+ ];
118
+ const REVIEW_CATEGORIES = [
119
+ "correctness",
120
+ "safety",
121
+ "coordination",
122
+ "maintainability",
123
+ "unknown"
124
+ ];
125
+ const REVIEW_RUNGS = ["independentModel", "oracle"];
126
+ const REVIEW_STATUS_VALUES = [
127
+ "not-required",
128
+ "current",
129
+ "stale",
130
+ "missing",
131
+ "blocked"
132
+ ];
133
+ const followUpActionSchema = z.object({
134
+ argv: z.array(z.string()),
135
+ command: z.string()
136
+ }).strict();
137
+ const EVIDENCE_ENVELOPE_SCHEMA_VERSION = 1;
138
+ const evidenceShaSchema = z.string().regex(/^[0-9a-f]{7,40}$/u, { message: "must be a 7-40 char lowercase hex git SHA" });
139
+ const evidenceEnvelopeSchema = z.object({
140
+ check: z.string().min(1),
141
+ checkType: z.enum(EVIDENCE_CHECK_TYPES),
142
+ findingsPointer: z.string().min(1).optional(),
143
+ headSha: evidenceShaSchema,
144
+ mergeBaseSha: evidenceShaSchema,
145
+ model: z.string().min(1).optional(),
146
+ outcome: z.enum(["pass", "fail"]),
147
+ patchId: evidenceShaSchema,
148
+ policyVersion: z.string().min(1).optional(),
149
+ producer: z.string().min(1),
150
+ requestId: z.string().min(1).optional(),
151
+ rung: z.enum(EVIDENCE_REVIEW_RUNGS).optional(),
152
+ schemaVersion: z.literal(1),
153
+ sessionId: z.string().trim().min(1).optional(),
154
+ timestamp: z.iso.datetime().optional()
155
+ }).passthrough().superRefine((envelope, context) => {
156
+ if (envelope.checkType === "review") {
157
+ if (envelope.rung === void 0) context.addIssue({
158
+ code: "custom",
159
+ message: "review-type evidence envelope requires a rung (ADR 0014 §3).",
160
+ path: ["rung"]
161
+ });
162
+ } else {
163
+ if (envelope.rung !== void 0) context.addIssue({
164
+ code: "custom",
165
+ message: "verify-type evidence envelope must not carry rung (review-type-only field, ADR 0014 §3).",
166
+ path: ["rung"]
167
+ });
168
+ if (envelope.model !== void 0) context.addIssue({
169
+ code: "custom",
170
+ message: "verify-type evidence envelope must not carry model (review-type-only field, ADR 0014 §3).",
171
+ path: ["model"]
172
+ });
173
+ }
174
+ });
175
+ const SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS = [
176
+ 1,
177
+ 2,
178
+ 3,
179
+ 4
180
+ ];
181
+ const executedCommandSchema = z.object({
182
+ command: z.string().min(1),
183
+ counts: z.object({
184
+ testFiles: z.number().nonnegative().optional(),
185
+ tests: z.number().nonnegative().optional()
186
+ }).optional(),
187
+ durationMs: z.number().nonnegative(),
188
+ exitCode: z.number(),
189
+ name: z.string().min(1),
190
+ scope: z.enum([
191
+ "always",
192
+ "docs-only",
193
+ "trivial",
194
+ "full"
195
+ ])
196
+ });
197
+ const prVerifySchemaVersionSchema = z.number().refine((value) => SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS.includes(value), { message: `schemaVersion must be one of: ${SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS.join(", ")}` });
198
+ const prVerifyProofSchema = z.object({
199
+ authoringSession: z.string().trim().min(1).optional(),
200
+ base: z.string().min(1),
201
+ baselineFullProofs: z.array(z.object({
202
+ base: z.string().min(1),
203
+ changedFiles: z.array(z.string()),
204
+ headSha: z.string().regex(/^[0-9a-f]{40}$/u),
205
+ profilePath: z.string().min(1),
206
+ projectKey: z.string().min(1),
207
+ repository: z.string().min(1)
208
+ })).optional(),
209
+ changedFiles: z.array(z.string()),
210
+ classification: z.enum(DIFF_CLASSIFICATIONS),
211
+ classificationReasons: z.array(z.string()),
212
+ command: z.literal("patronage-factory pr:verify"),
213
+ durationMs: z.number().nonnegative(),
214
+ endedAt: z.iso.datetime(),
215
+ executedCommands: z.array(executedCommandSchema).optional(),
216
+ headSha: z.string().regex(/^[0-9a-f]{40}$/u),
217
+ mergeBaseSha: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
218
+ mode: z.enum([
219
+ "docs-only",
220
+ "trivial",
221
+ "full"
222
+ ]),
223
+ outcome: z.enum(["aborted", "passed"]).optional(),
224
+ patchId: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
225
+ profilePath: z.string().min(1),
226
+ projectKey: z.string().min(1),
227
+ repository: z.string().min(1),
228
+ schemaVersion: prVerifySchemaVersionSchema,
229
+ startedAt: z.iso.datetime(),
230
+ verificationCommands: z.array(z.object({
231
+ command: z.string().min(1),
232
+ description: z.string().min(1),
233
+ name: z.string().min(1),
234
+ scope: z.enum([
235
+ "always",
236
+ "docs-only",
237
+ "trivial",
238
+ "full"
239
+ ])
240
+ }))
241
+ }).superRefine((proof, context) => {
242
+ if (proof.schemaVersion >= 4 && proof.authoringSession === void 0) context.addIssue({
243
+ code: "custom",
244
+ message: "schemaVersion 4 pr:verify proof requires authoringSession (the recorded authoring identity, or the `unknown` sentinel).",
245
+ path: ["authoringSession"]
246
+ });
247
+ if (proof.schemaVersion < 4 && proof.authoringSession !== void 0) context.addIssue({
248
+ code: "custom",
249
+ message: "authoringSession is a schemaVersion>=4 field; a v1–v3 pr:verify proof must not carry it.",
250
+ path: ["authoringSession"]
251
+ });
252
+ });
253
+ const PR_REVIEW_SCHEMA_VERSION = 2;
254
+ const PR_REVIEW_FINDING_PROVENANCE_VERSION = 1;
255
+ const nonBlankString = z.string().refine((value) => value.trim().length > 0, { message: "must not be blank" });
256
+ const parseableDateString = z.string().refine((value) => Number.isFinite(Date.parse(value)), { message: "must be a parseable date string" });
257
+ const reviewPromptSectionProvenances = [
258
+ "prior-review-ledger",
259
+ "profile-standing-checklist",
260
+ "issue-review-focus"
261
+ ];
262
+ const reviewPromptSectionSchema = z.object({
263
+ provenance: z.enum(reviewPromptSectionProvenances),
264
+ source: nonBlankString,
265
+ text: nonBlankString
266
+ }).strict();
267
+ const reviewPromptSectionsSchema = z.array(reviewPromptSectionSchema).min(1).refine((sections) => new Set(sections.map((section) => section.provenance)).size === sections.length && !sections.some((section, index) => index > 0 && reviewPromptSectionProvenances.indexOf(section.provenance) < reviewPromptSectionProvenances.indexOf(sections[index - 1].provenance)), { message: "prompt sections must be in assembly order without duplicates" });
268
+ const findingSupersedesSchema = z.object({
269
+ file: z.string().optional(),
270
+ title: nonBlankString
271
+ });
272
+ const prReviewFindingSchema = z.object({
273
+ blockingAfterCap: z.boolean().optional(),
274
+ body: nonBlankString,
275
+ category: z.enum(REVIEW_CATEGORIES).optional(),
276
+ citedSpan: z.string().optional(),
277
+ file: z.string().optional(),
278
+ line: z.number().int().positive().optional(),
279
+ prescribedAction: z.string().optional(),
280
+ priority: z.string().optional(),
281
+ protocolFinding: z.literal(true).optional(),
282
+ supersedes: findingSupersedesSchema.optional(),
283
+ title: nonBlankString
284
+ });
285
+ const prReviewResultSchema = z.object({
286
+ command: z.string().refine((value) => value.trim().length > 0, { message: "command must not be blank" }).optional(),
287
+ durationMs: z.number().refine((value) => Number.isFinite(value) && value >= 0, { message: "durationMs must be a finite number >= 0" }),
288
+ endedAt: parseableDateString,
289
+ exitCode: z.number().nullable().optional(),
290
+ findings: z.array(prReviewFindingSchema),
291
+ issuesFlagged: z.number().int().nonnegative(),
292
+ kind: z.enum(["correctness", "security"]),
293
+ model: nonBlankString.optional(),
294
+ outcome: z.enum([
295
+ "passed",
296
+ "failed",
297
+ "error"
298
+ ]),
299
+ producer: z.string().min(1).optional(),
300
+ promptSections: reviewPromptSectionsSchema.optional(),
301
+ rung: z.enum(EVIDENCE_REVIEW_RUNGS).optional(),
302
+ sessionId: nonBlankString.optional(),
303
+ stageResolution: z.object({
304
+ effort: z.enum([
305
+ "low",
306
+ "medium",
307
+ "high",
308
+ "xhigh"
309
+ ]).optional(),
310
+ engine: z.string().min(1),
311
+ layerSource: z.enum([
312
+ "cli",
313
+ "operatorSessionGlobal",
314
+ "operatorSessionKind",
315
+ "profilePin",
316
+ "stageConfig",
317
+ "userConfig"
318
+ ]),
319
+ model: z.string().min(1),
320
+ rung: z.enum(REVIEW_RUNGS).optional()
321
+ }).optional(),
322
+ startedAt: parseableDateString,
323
+ summary: z.string().refine((value) => value.trim().length > 0 && value !== "<one sentence>", { message: "summary must not be blank or a placeholder" }),
324
+ typedVerdict: z.boolean().optional(),
325
+ verdictSource: z.enum(["footer", "recovered"]).optional()
326
+ }).superRefine((review, context) => {
327
+ if (review.outcome === "passed") {
328
+ if (review.issuesFlagged !== 0) context.addIssue({
329
+ code: "custom",
330
+ message: "passed reviews must report zero issues flagged",
331
+ path: ["issuesFlagged"]
332
+ });
333
+ if (review.findings.length !== 0) context.addIssue({
334
+ code: "custom",
335
+ message: "passed reviews must not include findings",
336
+ path: ["findings"]
337
+ });
338
+ }
339
+ if (review.outcome !== "passed") {
340
+ if (review.findings.length === 0) context.addIssue({
341
+ code: "custom",
342
+ message: "non-passing reviews must include findings",
343
+ path: ["findings"]
344
+ });
345
+ if (review.findings.length !== review.issuesFlagged) context.addIssue({
346
+ code: "custom",
347
+ message: "issuesFlagged must match findings length",
348
+ path: ["issuesFlagged"]
349
+ });
350
+ }
351
+ if (review.outcome === "failed" && review.exitCode !== void 0 && review.exitCode !== 0) context.addIssue({
352
+ code: "custom",
353
+ message: "failed reviews must exit with code 0",
354
+ path: ["exitCode"]
355
+ });
356
+ if (review.producer === void 0 !== (review.rung === void 0)) context.addIssue({
357
+ code: "custom",
358
+ message: "producer and rung must be recorded together",
359
+ path: [review.producer === void 0 ? "producer" : "rung"]
360
+ });
361
+ });
362
+ const ladderFindingSchema = z.object({
363
+ citedSpan: z.string().optional(),
364
+ file: z.string().optional(),
365
+ prescribedAction: z.string().optional(),
366
+ supersedes: z.object({
367
+ file: z.string().optional(),
368
+ title: z.string()
369
+ }).optional(),
370
+ title: z.string()
371
+ }).passthrough();
372
+ const ladderDispositionSchema = z.object({
373
+ disposition: z.enum([
374
+ "fixed-in-thread",
375
+ "follow-up-filed",
376
+ "waived"
377
+ ]),
378
+ finding: z.object({
379
+ file: z.string().optional(),
380
+ title: z.string()
381
+ }),
382
+ reference: z.string().optional()
383
+ }).passthrough();
384
+ const ladderCycleSchema = z.object({
385
+ dispositions: z.array(ladderDispositionSchema).optional(),
386
+ findings: z.array(ladderFindingSchema)
387
+ }).passthrough();
388
+ const prReviewProofSchema = z.object({
389
+ base: z.string(),
390
+ changedFiles: z.array(z.string()),
391
+ cleanedPaths: z.array(z.string()),
392
+ findingProvenanceVersion: z.literal(PR_REVIEW_FINDING_PROVENANCE_VERSION),
393
+ headSha: z.string().regex(/^[0-9a-f]{40}$/u),
394
+ ladder: z.object({ cycles: z.array(ladderCycleSchema) }).superRefine((ladder, context) => {
395
+ let gateStarted = false;
396
+ try {
397
+ for (const cycle of ladder.cycles) {
398
+ const { stage } = cycle;
399
+ if (stage === "interior") {
400
+ if (gateStarted) throw new TypeError("interior cycle after gate");
401
+ } else gateStarted = true;
402
+ }
403
+ } catch {
404
+ context.addIssue({
405
+ code: "custom",
406
+ message: "ladder cycles do not fold into a valid disposition ledger"
407
+ });
408
+ }
409
+ }).optional(),
410
+ maxReviewCycles: z.number().int().positive().optional(),
411
+ patchId: z.string().regex(/^[0-9a-f]{40,64}$/u),
412
+ reviewCycle: z.number().int().positive().optional(),
413
+ reviewRequirement: z.object({
414
+ reason: z.literal("docs-only-profile-bypass"),
415
+ status: z.literal("not-required")
416
+ }).strict().optional(),
417
+ reviews: z.array(prReviewResultSchema),
418
+ schemaVersion: z.literal(2)
419
+ }).passthrough().superRefine((proof, context) => {
420
+ if (proof.reviewRequirement && proof.reviews.length > 0) context.addIssue({
421
+ code: "custom",
422
+ message: "not-required review proofs must not contain review runs",
423
+ path: ["reviews"]
424
+ });
425
+ if (proof.reviewCycle !== void 0 && proof.maxReviewCycles !== void 0 && proof.reviewCycle > proof.maxReviewCycles) context.addIssue({
426
+ code: "custom",
427
+ message: "reviewCycle must not exceed maxReviewCycles",
428
+ path: ["reviewCycle"]
429
+ });
430
+ });
431
+ const BOUNDARY_REVIEW_PROOF_KIND = "boundary-review-proof";
432
+ const coveredSetEntrySchema = z.object({
433
+ headSha: evidenceShaSchema.optional(),
434
+ issue: z.number().int().positive().optional(),
435
+ mergedSha: evidenceShaSchema.optional(),
436
+ pr: z.number().int().positive(),
437
+ state: z.enum(["merged", "open"]),
438
+ wave: z.string().min(1).optional()
439
+ }).passthrough().superRefine((entry, context) => {
440
+ if (entry.state === "merged" && entry.mergedSha === void 0) context.addIssue({
441
+ code: "custom",
442
+ message: "a merged covered-set entry must record mergedSha",
443
+ path: ["mergedSha"]
444
+ });
445
+ if (entry.state === "open" && entry.headSha === void 0) context.addIssue({
446
+ code: "custom",
447
+ message: "an open covered-set entry must record headSha",
448
+ path: ["headSha"]
449
+ });
450
+ });
451
+ const proofFindingSchema = z.object({
452
+ blockingAfterCap: z.boolean().optional(),
453
+ category: z.string().min(1).optional(),
454
+ disposition: z.string().min(1).optional(),
455
+ title: z.string().min(1)
456
+ }).passthrough();
457
+ const boundaryReviewProofSchema = z.object({
458
+ boundary: z.string().min(1),
459
+ coveredSet: z.array(coveredSetEntrySchema),
460
+ dispositions: z.array(z.unknown()).optional(),
461
+ findings: z.array(proofFindingSchema).default([]),
462
+ kind: z.literal(BOUNDARY_REVIEW_PROOF_KIND),
463
+ manifestHash: z.object({
464
+ algo: z.literal("sha256"),
465
+ scope: z.string().optional(),
466
+ value: z.string().regex(/^[0-9a-f]{64}$/u)
467
+ }).passthrough(),
468
+ outcome: z.enum(["pass", "fail"]),
469
+ producer: z.string().min(1),
470
+ rung: z.enum(EVIDENCE_REVIEW_RUNGS),
471
+ schemaVersion: z.literal(1),
472
+ sessionId: z.string().min(1).optional(),
473
+ specHash: z.object({
474
+ algo: z.literal("sha256"),
475
+ scope: z.string().optional(),
476
+ value: z.string().regex(/^[0-9a-f]{64}$/u)
477
+ }).passthrough().optional()
478
+ }).passthrough();
479
+ const BOUNDARY_CHECK_SCHEMA_VERSION = 1;
480
+ const boundaryCheckProofSchema = z.object({
481
+ blockingReasons: z.array(z.string()),
482
+ boundary: z.string().min(1).optional(),
483
+ command: z.literal("patronage-factory boundary:check"),
484
+ coveredSet: z.array(z.object({
485
+ issue: z.number().int().positive().optional(),
486
+ pr: z.number().int().positive(),
487
+ sha: evidenceShaSchema,
488
+ state: z.enum(["merged", "open"]),
489
+ wave: z.string().min(1)
490
+ })),
491
+ demandedRung: z.enum(EVIDENCE_REVIEW_RUNGS).optional(),
492
+ epicIssue: z.number().int().positive(),
493
+ manifestHash: z.object({
494
+ algo: z.literal("sha256"),
495
+ value: z.string().regex(/^[0-9a-f]{64}$/u)
496
+ }).optional(),
497
+ notices: z.array(z.string()),
498
+ repo: z.string().min(1),
499
+ reviewProof: z.object({
500
+ commentId: z.union([z.string(), z.number()]).optional(),
501
+ commentUrl: z.string().optional(),
502
+ producer: z.string().min(1),
503
+ rung: z.enum(EVIDENCE_REVIEW_RUNGS),
504
+ sessionId: z.string().min(1).optional(),
505
+ specHash: z.object({
506
+ algo: z.literal("sha256"),
507
+ scope: z.string().optional(),
508
+ value: z.string().regex(/^[0-9a-f]{64}$/u)
509
+ }).passthrough().optional()
510
+ }).optional(),
511
+ schemaVersion: z.literal(1),
512
+ specHash: z.object({
513
+ algo: z.literal("sha256"),
514
+ value: z.string().regex(/^[0-9a-f]{64}$/u)
515
+ }).optional(),
516
+ status: z.enum(["ready", "blocked"])
517
+ });
518
+ const mergeGuardIdentitySchema = z.discriminatedUnion("kind", [
519
+ z.object({
520
+ headSha: z.string().regex(/^[0-9a-f]{40}$/u),
521
+ kind: z.literal("match")
522
+ }),
523
+ z.object({
524
+ kind: z.literal("diverged"),
525
+ liveHeadSha: z.string().regex(/^[0-9a-f]{40}$/u),
526
+ postProofCommits: z.array(z.object({
527
+ sha: z.string().min(1),
528
+ subject: z.string()
529
+ })).optional(),
530
+ proofHeadSha: z.string().regex(/^[0-9a-f]{40}$/u)
531
+ }),
532
+ z.object({
533
+ kind: z.literal("live-head-invalid"),
534
+ pr: z.number().int().positive(),
535
+ received: z.string()
536
+ }),
537
+ z.object({
538
+ errorDetail: z.string().optional(),
539
+ kind: z.literal("ready-proof-missing"),
540
+ readyProofPath: z.string().min(1)
541
+ }),
542
+ z.object({
543
+ kind: z.literal("ready-proof-pr-mismatch"),
544
+ proofPr: z.number().int().positive(),
545
+ requestedPr: z.number().int().positive()
546
+ }),
547
+ z.object({
548
+ blockingReasons: z.array(z.string()),
549
+ kind: z.literal("ready-proof-not-ready"),
550
+ status: z.string().min(1)
551
+ })
552
+ ]);
553
+ const PR_MERGE_CHECK_SCHEMA_VERSION = 1;
554
+ const prMergeCheckProofSchema = z.object({
555
+ blockingReasons: z.array(z.string()),
556
+ command: z.literal("patronage-factory pr:merge-check"),
557
+ followUp: followUpActionSchema.optional(),
558
+ identity: mergeGuardIdentitySchema,
559
+ liveHeadSha: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
560
+ notices: z.array(z.string()).optional(),
561
+ pr: z.number().int().positive(),
562
+ schemaVersion: z.literal(1),
563
+ status: z.enum(["pass", "fail"]),
564
+ worktreeHeldBranch: z.object({
565
+ branch: z.string().min(1),
566
+ worktreePath: z.string().min(1)
567
+ }).optional()
568
+ });
569
+ const requiredCheckScopeSchema = z.object({
570
+ classifications: z.array(z.enum(DIFF_CLASSIFICATIONS)).min(1).optional(),
571
+ labels: z.array(z.string().min(1)).min(1).optional()
572
+ }).strict().superRefine((scope, context) => {
573
+ if (scope.labels === void 0 && scope.classifications === void 0) context.addIssue({
574
+ code: "custom",
575
+ message: "requiredChecks scope must declare at least one condition (labels and/or classifications)."
576
+ });
577
+ });
578
+ const readinessRepairSchema = z.object({
579
+ action: z.string().min(1),
580
+ code: z.enum([
581
+ "undraft-pr",
582
+ "render-pr-body-sections",
583
+ "await-post-undraft-checks"
584
+ ]),
585
+ command: z.string().min(1)
586
+ });
587
+ const reviewStateSchema = z.object({
588
+ docsOnlyDeltaAccepted: z.boolean().optional(),
589
+ required: z.boolean(),
590
+ reviewedHeadSha: z.string().optional(),
591
+ reviewedPatchId: z.string().optional(),
592
+ status: z.enum(REVIEW_STATUS_VALUES)
593
+ });
594
+ const managedReadinessLedgerSchema = z.object({
595
+ baseSha: z.string().regex(/^[0-9a-f]{40}$/u),
596
+ blockingReasons: z.array(z.string()),
597
+ classification: z.enum(DIFF_CLASSIFICATIONS),
598
+ externalChecks: z.array(z.object({
599
+ checkType: z.enum(EVIDENCE_CHECK_TYPES),
600
+ inScope: z.boolean(),
601
+ name: z.string().min(1),
602
+ reason: z.string().optional(),
603
+ scope: requiredCheckScopeSchema.optional(),
604
+ scopeReason: z.string().min(1),
605
+ status: z.enum([
606
+ "satisfied",
607
+ "unmet",
608
+ "out-of-scope"
609
+ ])
610
+ })).optional(),
611
+ finalReviewPoint: z.boolean(),
612
+ github: z.object({
613
+ currentWithBase: z.boolean(),
614
+ draft: z.boolean(),
615
+ mergeStateStatus: z.string(),
616
+ mergeable: z.string(),
617
+ requiredChecks: z.enum([
618
+ "passed",
619
+ "failed",
620
+ "pending",
621
+ "none",
622
+ "unknown"
623
+ ]),
624
+ unresolvedReviewThreads: z.number().int().nonnegative()
625
+ }),
626
+ handledCommentsProducer: z.object({
627
+ identity: z.string().min(1),
628
+ mode: z.enum(["app", "commit-status"])
629
+ }).optional(),
630
+ handledHumanComments: z.array(z.object({
631
+ clearedAt: z.string().min(1).optional(),
632
+ sessionId: z.string().min(1).optional(),
633
+ source: z.enum(["check-run", "cli"]),
634
+ url: z.string().min(1)
635
+ })).optional(),
636
+ headSha: z.string().regex(/^[0-9a-f]{40}$/u),
637
+ mergeBaseSha: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
638
+ patchId: z.string().regex(/^[0-9a-f]{40,64}$/u),
639
+ postReadinessHumanComments: z.array(z.object({
640
+ author: z.string().min(1),
641
+ createdAt: z.string().min(1),
642
+ summary: z.string().min(1),
643
+ url: z.string().min(1)
644
+ })).optional(),
645
+ pr: z.number().int().positive(),
646
+ previewDeploy: z.object({
647
+ adminUrl: z.string().optional(),
648
+ apiHealthUrl: z.string().optional(),
649
+ headSha: z.string().optional(),
650
+ localPreviewProof: z.unknown().optional(),
651
+ localProofPath: z.string().optional(),
652
+ proofSource: z.enum(["github-actions", "local-self-certified"]).optional(),
653
+ publicUrl: z.string().optional(),
654
+ seedStatus: z.enum([
655
+ "passed",
656
+ "failed",
657
+ "skipped",
658
+ "unknown"
659
+ ]),
660
+ stage: z.string().optional(),
661
+ timingsMs: z.record(z.string(), z.number().optional()).optional(),
662
+ workflowRunUrl: z.string().optional()
663
+ }).optional(),
664
+ previewDeployRequired: z.boolean(),
665
+ repairs: z.array(readinessRepairSchema).default([]),
666
+ reviewCycleState: z.object({
667
+ autoBlockingFindings: z.number().int().nonnegative(),
668
+ countsBySeverity: z.object({
669
+ critical: z.number().int().nonnegative(),
670
+ high: z.number().int().nonnegative(),
671
+ low: z.number().int().nonnegative(),
672
+ medium: z.number().int().nonnegative(),
673
+ unknown: z.number().int().nonnegative()
674
+ }),
675
+ highestBlockingSeverity: z.enum([
676
+ "critical",
677
+ "high",
678
+ "medium",
679
+ "low",
680
+ "unknown"
681
+ ]).optional(),
682
+ highestOpenSeverity: z.enum([
683
+ "critical",
684
+ "high",
685
+ "medium",
686
+ "low",
687
+ "unknown"
688
+ ]).optional(),
689
+ maxReviewCycles: z.number().int().positive().optional(),
690
+ nonBlockingFindings: z.number().int().nonnegative(),
691
+ openFindings: z.number().int().nonnegative(),
692
+ reviewCycle: z.number().int().positive().optional(),
693
+ staleRepeatFindings: z.number().int().nonnegative().optional(),
694
+ windowExhausted: z.boolean()
695
+ }).optional(),
696
+ reviewLadder: z.object({
697
+ cycleCounts: z.object({
698
+ gate: z.number().int().nonnegative(),
699
+ interior: z.number().int().nonnegative()
700
+ }),
701
+ forcedTransition: z.enum([
702
+ "gate-cap-exhausted",
703
+ "interior-cap-reached",
704
+ "interior-takeover"
705
+ ]).optional(),
706
+ nextAction: z.enum([
707
+ "run-interior-cycle",
708
+ "advance-to-gate",
709
+ "run-gate-cycle",
710
+ "accept-nonblocking-findings",
711
+ "escalate-to-triage",
712
+ "ready-for-human"
713
+ ]),
714
+ stage: z.enum([
715
+ "interior",
716
+ "interior-takeover",
717
+ "interior-complete",
718
+ "gate"
719
+ ])
720
+ }).optional(),
721
+ reviewRuns: z.array(prReviewResultSchema).optional(),
722
+ reviewTerminalState: z.enum([
723
+ "accepted-with-findings",
724
+ "blocked",
725
+ "clean"
726
+ ]).optional(),
727
+ reviews: z.object({
728
+ correctness: reviewStateSchema,
729
+ security: reviewStateSchema.optional()
730
+ }),
731
+ schemaVersion: z.literal(1),
732
+ stackRole: z.enum([
733
+ "single",
734
+ "slice",
735
+ "rollup",
736
+ "merge-gate prerequisite"
737
+ ]),
738
+ verification: z.object({
739
+ command: z.literal("patronage-factory pr:verify"),
740
+ docsOnlyDeltaAccepted: z.boolean().optional(),
741
+ docsOnlyVerifiedHeadSha: z.string().optional(),
742
+ prVerify: z.enum([
743
+ "passed",
744
+ "missing",
745
+ "stale"
746
+ ]),
747
+ trivialDeltaAccepted: z.boolean().optional(),
748
+ trivialVerifiedHeadSha: z.string().optional(),
749
+ verifiedHeadSha: z.string().optional()
750
+ })
751
+ });
752
+ const PR_READY_SCHEMA_VERSION = 1;
753
+ const prReadyProofSchema = z.object({
754
+ blockingReasons: z.array(z.string()),
755
+ command: z.literal("patronage-factory pr:ready"),
756
+ followUp: followUpActionSchema.optional(),
757
+ humanBlockingReasons: z.array(z.string()).default([]),
758
+ ledger: managedReadinessLedgerSchema,
759
+ profileBlobSha: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
760
+ profilePath: z.string().min(1).optional(),
761
+ repairs: z.array(readinessRepairSchema).default([]),
762
+ repository: z.string().regex(/^[^/\s]+\/[^/\s]+$/u).optional(),
763
+ schemaVersion: z.literal(1),
764
+ status: z.enum([
765
+ "ready",
766
+ "blocked",
767
+ "slice-ready/not-final"
768
+ ])
769
+ });
770
+ //#endregion
771
+ export { BOUNDARY_CHECK_SCHEMA_VERSION, BOUNDARY_REVIEW_PROOF_KIND, EVIDENCE_ENVELOPE_SCHEMA_VERSION, PR_MERGE_CHECK_SCHEMA_VERSION, PR_READY_SCHEMA_VERSION, PR_REVIEW_SCHEMA_VERSION, SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS, boundaryCheckProofSchema, boundaryReviewProofSchema, closeoutArtifactSchema, evidenceEnvelopeSchema, prMergeCheckProofSchema, prReadyProofSchema, prReviewProofSchema, prVerifyProofSchema };