@patronage/software-factory 1.0.0-alpha.16 → 1.0.0-alpha.17
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/CONTEXT.md +1 -1
- package/dist/index.d.ts +76 -37
- package/dist/index.js +270 -151
- package/dist/schemas.d.ts +17 -0
- package/dist/schemas.js +16 -0
- package/package.json +2 -2
package/CONTEXT.md
CHANGED
|
@@ -20,7 +20,7 @@ The shared Patronage software factory: typed lifecycle state, proof gates, and o
|
|
|
20
20
|
|
|
21
21
|
### Impact
|
|
22
22
|
|
|
23
|
-
**Impact**: The candidate-bound classification of which declared product targets a changed path or lockfile graph can reach. Path impact is opt-in: a target is affected if and only if a valid changed path matches one of its declared globs. A valid path matched by no target is **unsubscribed** and demands no target-scoped work, while remaining explicit in the stamp basis and human summary. Shared paths affect every target that subscribes to them. Invalid paths and unreadable or malformed required inputs still fail closed. Impact may withdraw target-scoped work; it never certifies a merge target, whose push-triggered verification stays full. Defined by ADR 0027. _Avoid_: affectedness; ownership (
|
|
23
|
+
**Impact**: The candidate-bound classification of which declared product targets a changed path or lockfile graph can reach. Path impact is opt-in: a target is affected if and only if a valid changed path matches one of its declared globs. A valid path matched by no target is **unsubscribed** and demands no target-scoped work, while remaining explicit in the stamp basis and human summary. A valid path that matches `impact.inert` and no target is **inert-owned**: the stamp records it in `inertPaths`, distinct from `unsubscribedPaths`. A path matching both a target subscription and an inert glob is product-owned and appears in neither list. `pnpm-lock.yaml` is a graph input and is never inert. Shared paths affect every target that subscribes to them. Invalid paths and unreadable or malformed required inputs still fail closed. Impact may withdraw target-scoped work; it never certifies a merge target, whose push-triggered verification stays full. Defined by ADR 0027. _Avoid_: affectedness; implicit filesystem ownership (target `paths` are subscriptions; inert-owned is the explicit `impact.inert` declaration).
|
|
24
24
|
|
|
25
25
|
### Planning intake
|
|
26
26
|
|
package/dist/index.d.ts
CHANGED
|
@@ -16,8 +16,8 @@ declare const boundaryManifestBaseSchema: z.ZodObject<{
|
|
|
16
16
|
boundary: z.ZodString;
|
|
17
17
|
closeout: z.ZodOptional<z.ZodObject<{
|
|
18
18
|
review: z.ZodEnum<{
|
|
19
|
-
"independent-model": "independent-model";
|
|
20
19
|
oracle: "oracle";
|
|
20
|
+
"independent-model": "independent-model";
|
|
21
21
|
human: "human";
|
|
22
22
|
}>;
|
|
23
23
|
}, z.core.$loose>>;
|
|
@@ -35,8 +35,8 @@ declare const boundaryManifestBaseSchema: z.ZodObject<{
|
|
|
35
35
|
issues: z.ZodArray<z.ZodNumber>;
|
|
36
36
|
name: z.ZodString;
|
|
37
37
|
review: z.ZodEnum<{
|
|
38
|
-
"independent-model": "independent-model";
|
|
39
38
|
oracle: "oracle";
|
|
39
|
+
"independent-model": "independent-model";
|
|
40
40
|
human: "human";
|
|
41
41
|
}>;
|
|
42
42
|
}, z.core.$loose>>;
|
|
@@ -45,8 +45,8 @@ declare const boundaryManifestSchema: z.ZodObject<{
|
|
|
45
45
|
boundary: z.ZodString;
|
|
46
46
|
closeout: z.ZodOptional<z.ZodObject<{
|
|
47
47
|
review: z.ZodEnum<{
|
|
48
|
-
"independent-model": "independent-model";
|
|
49
48
|
oracle: "oracle";
|
|
49
|
+
"independent-model": "independent-model";
|
|
50
50
|
human: "human";
|
|
51
51
|
}>;
|
|
52
52
|
}, z.core.$loose>>;
|
|
@@ -64,8 +64,8 @@ declare const boundaryManifestSchema: z.ZodObject<{
|
|
|
64
64
|
issues: z.ZodArray<z.ZodNumber>;
|
|
65
65
|
name: z.ZodString;
|
|
66
66
|
review: z.ZodEnum<{
|
|
67
|
-
"independent-model": "independent-model";
|
|
68
67
|
oracle: "oracle";
|
|
68
|
+
"independent-model": "independent-model";
|
|
69
69
|
human: "human";
|
|
70
70
|
}>;
|
|
71
71
|
}, z.core.$loose>>;
|
|
@@ -126,8 +126,8 @@ declare const boundaryReviewProofBaseSchema: z.ZodObject<{
|
|
|
126
126
|
mergedSha: z.ZodOptional<z.ZodString>;
|
|
127
127
|
pr: z.ZodNumber;
|
|
128
128
|
state: z.ZodEnum<{
|
|
129
|
-
merged: "merged";
|
|
130
129
|
open: "open";
|
|
130
|
+
merged: "merged";
|
|
131
131
|
}>;
|
|
132
132
|
wave: z.ZodOptional<z.ZodString>;
|
|
133
133
|
}, z.core.$loose>>;
|
|
@@ -150,8 +150,8 @@ declare const boundaryReviewProofBaseSchema: z.ZodObject<{
|
|
|
150
150
|
}>;
|
|
151
151
|
producer: z.ZodString;
|
|
152
152
|
rung: z.ZodEnum<{
|
|
153
|
-
"independent-model": "independent-model";
|
|
154
153
|
oracle: "oracle";
|
|
154
|
+
"independent-model": "independent-model";
|
|
155
155
|
human: "human";
|
|
156
156
|
}>;
|
|
157
157
|
schemaVersion: z.ZodLiteral<1>;
|
|
@@ -170,8 +170,8 @@ declare const boundaryReviewProofSchema: z.ZodObject<{
|
|
|
170
170
|
mergedSha: z.ZodOptional<z.ZodString>;
|
|
171
171
|
pr: z.ZodNumber;
|
|
172
172
|
state: z.ZodEnum<{
|
|
173
|
-
merged: "merged";
|
|
174
173
|
open: "open";
|
|
174
|
+
merged: "merged";
|
|
175
175
|
}>;
|
|
176
176
|
wave: z.ZodOptional<z.ZodString>;
|
|
177
177
|
}, z.core.$loose>>;
|
|
@@ -194,8 +194,8 @@ declare const boundaryReviewProofSchema: z.ZodObject<{
|
|
|
194
194
|
}>;
|
|
195
195
|
producer: z.ZodString;
|
|
196
196
|
rung: z.ZodEnum<{
|
|
197
|
-
"independent-model": "independent-model";
|
|
198
197
|
oracle: "oracle";
|
|
198
|
+
"independent-model": "independent-model";
|
|
199
199
|
human: "human";
|
|
200
200
|
}>;
|
|
201
201
|
schemaVersion: z.ZodLiteral<1>;
|
|
@@ -645,6 +645,7 @@ declare const impactStampSchema: z.ZodObject<{
|
|
|
645
645
|
"target-scoped": "target-scoped";
|
|
646
646
|
conservative: "conservative";
|
|
647
647
|
}>;
|
|
648
|
+
inertPaths: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
648
649
|
reasons: z.ZodArray<z.ZodString>;
|
|
649
650
|
stampVersion: z.ZodLiteral<3>;
|
|
650
651
|
targets: z.ZodArray<z.ZodObject<{
|
|
@@ -732,7 +733,6 @@ declare const factoryProjectProfileSchema: z.ZodObject<{
|
|
|
732
733
|
scope: z.ZodOptional<z.ZodObject<{
|
|
733
734
|
classifications: z.ZodArray<z.ZodEnum<{
|
|
734
735
|
"docs/process-only": "docs/process-only";
|
|
735
|
-
trivial: "trivial";
|
|
736
736
|
"non-trivial": "non-trivial";
|
|
737
737
|
}>>;
|
|
738
738
|
}, z.core.$strict>>;
|
|
@@ -922,8 +922,8 @@ declare const mergeFreezeStateSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
922
922
|
generationId: z.ZodNumber;
|
|
923
923
|
headSha: z.ZodString;
|
|
924
924
|
outcome: z.ZodEnum<{
|
|
925
|
-
active: "active";
|
|
926
925
|
stale: "stale";
|
|
926
|
+
active: "active";
|
|
927
927
|
}>;
|
|
928
928
|
reason: z.ZodString;
|
|
929
929
|
recordedAt: z.ZodISODateTime;
|
|
@@ -1416,10 +1416,10 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
|
|
|
1416
1416
|
reviewedHeadSha: z.ZodOptional<z.ZodString>;
|
|
1417
1417
|
reviewedPatchId: z.ZodOptional<z.ZodString>;
|
|
1418
1418
|
status: z.ZodEnum<{
|
|
1419
|
-
stale: "stale";
|
|
1420
1419
|
blocked: "blocked";
|
|
1421
1420
|
"not-required": "not-required";
|
|
1422
1421
|
current: "current";
|
|
1422
|
+
stale: "stale";
|
|
1423
1423
|
missing: "missing";
|
|
1424
1424
|
}>;
|
|
1425
1425
|
}, z.core.$strip>;
|
|
@@ -1428,10 +1428,10 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
|
|
|
1428
1428
|
reviewedHeadSha: z.ZodOptional<z.ZodString>;
|
|
1429
1429
|
reviewedPatchId: z.ZodOptional<z.ZodString>;
|
|
1430
1430
|
status: z.ZodEnum<{
|
|
1431
|
-
stale: "stale";
|
|
1432
1431
|
blocked: "blocked";
|
|
1433
1432
|
"not-required": "not-required";
|
|
1434
1433
|
current: "current";
|
|
1434
|
+
stale: "stale";
|
|
1435
1435
|
missing: "missing";
|
|
1436
1436
|
}>;
|
|
1437
1437
|
}, z.core.$strip>>;
|
|
@@ -1442,8 +1442,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
|
|
|
1442
1442
|
docsOnlyDeltaAccepted: z.ZodOptional<z.ZodBoolean>;
|
|
1443
1443
|
docsOnlyVerifiedHeadSha: z.ZodOptional<z.ZodString>;
|
|
1444
1444
|
prVerify: z.ZodEnum<{
|
|
1445
|
-
stale: "stale";
|
|
1446
1445
|
passed: "passed";
|
|
1446
|
+
stale: "stale";
|
|
1447
1447
|
missing: "missing";
|
|
1448
1448
|
}>;
|
|
1449
1449
|
trivialDeltaAccepted: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -1743,8 +1743,8 @@ declare const evidenceEnvelopeBaseSchema: z.ZodObject<{
|
|
|
1743
1743
|
producer: z.ZodString;
|
|
1744
1744
|
requestId: z.ZodOptional<z.ZodString>;
|
|
1745
1745
|
rung: z.ZodOptional<z.ZodEnum<{
|
|
1746
|
-
"independent-model": "independent-model";
|
|
1747
1746
|
oracle: "oracle";
|
|
1747
|
+
"independent-model": "independent-model";
|
|
1748
1748
|
human: "human";
|
|
1749
1749
|
}>>;
|
|
1750
1750
|
schemaVersion: z.ZodLiteral<1>;
|
|
@@ -1770,8 +1770,8 @@ declare const evidenceEnvelopeSchema: z.ZodObject<{
|
|
|
1770
1770
|
producer: z.ZodString;
|
|
1771
1771
|
requestId: z.ZodOptional<z.ZodString>;
|
|
1772
1772
|
rung: z.ZodOptional<z.ZodEnum<{
|
|
1773
|
-
"independent-model": "independent-model";
|
|
1774
1773
|
oracle: "oracle";
|
|
1774
|
+
"independent-model": "independent-model";
|
|
1775
1775
|
human: "human";
|
|
1776
1776
|
}>>;
|
|
1777
1777
|
schemaVersion: z.ZodLiteral<1>;
|
|
@@ -1874,10 +1874,10 @@ declare const loadEvidenceEnvelopes: (cwd: string) => LoadedEvidenceEnvelope[];
|
|
|
1874
1874
|
//#region src/review-proof-applicability.d.ts
|
|
1875
1875
|
declare const REVIEW_STATUS_VALUES: readonly ["not-required", "current", "stale", "missing", "blocked"];
|
|
1876
1876
|
declare const reviewStatusSchema: z.ZodEnum<{
|
|
1877
|
-
stale: "stale";
|
|
1878
1877
|
blocked: "blocked";
|
|
1879
1878
|
"not-required": "not-required";
|
|
1880
1879
|
current: "current";
|
|
1880
|
+
stale: "stale";
|
|
1881
1881
|
missing: "missing";
|
|
1882
1882
|
}>;
|
|
1883
1883
|
type ReviewStatus = z.infer<typeof reviewStatusSchema>;
|
|
@@ -2020,6 +2020,16 @@ interface PrVerifyNotRequiredCommand {
|
|
|
2020
2020
|
impactTarget: string;
|
|
2021
2021
|
name: string;
|
|
2022
2022
|
}
|
|
2023
|
+
/**
|
|
2024
|
+
* Epic #758 (#762): a consumer package a stamp-scoped selection skipped, and
|
|
2025
|
+
* why. Bound to the proof's stamp identity — the field is v4-only (the same
|
|
2026
|
+
* window that can carry `impactStamp`) and refused when the proof records no
|
|
2027
|
+
* stamp. The factory does not write this list; consumers do.
|
|
2028
|
+
*/
|
|
2029
|
+
interface PrVerifyNotDemandedRecord {
|
|
2030
|
+
basis: string;
|
|
2031
|
+
name: string;
|
|
2032
|
+
}
|
|
2023
2033
|
interface PrVerifyProof {
|
|
2024
2034
|
schemaVersion: PrVerifySchemaVersion;
|
|
2025
2035
|
authoringSession?: string;
|
|
@@ -2043,6 +2053,7 @@ interface PrVerifyProof {
|
|
|
2043
2053
|
impactStamp?: ImpactStamp;
|
|
2044
2054
|
mode: ResolvedPrVerifyMode;
|
|
2045
2055
|
mergeBaseSha?: string;
|
|
2056
|
+
notDemandedRecords?: PrVerifyNotDemandedRecord[];
|
|
2046
2057
|
notRequiredCommands?: PrVerifyNotRequiredCommand[];
|
|
2047
2058
|
outcome?: "aborted" | "passed";
|
|
2048
2059
|
patchId?: string;
|
|
@@ -2065,14 +2076,14 @@ declare const boundaryCheckProofSchema: z.ZodObject<{
|
|
|
2065
2076
|
pr: z.ZodNumber;
|
|
2066
2077
|
sha: z.ZodString;
|
|
2067
2078
|
state: z.ZodEnum<{
|
|
2068
|
-
merged: "merged";
|
|
2069
2079
|
open: "open";
|
|
2080
|
+
merged: "merged";
|
|
2070
2081
|
}>;
|
|
2071
2082
|
wave: z.ZodString;
|
|
2072
2083
|
}, z.core.$strip>>;
|
|
2073
2084
|
demandedRung: z.ZodOptional<z.ZodEnum<{
|
|
2074
|
-
"independent-model": "independent-model";
|
|
2075
2085
|
oracle: "oracle";
|
|
2086
|
+
"independent-model": "independent-model";
|
|
2076
2087
|
human: "human";
|
|
2077
2088
|
}>>;
|
|
2078
2089
|
epicIssue: z.ZodNumber;
|
|
@@ -2087,8 +2098,8 @@ declare const boundaryCheckProofSchema: z.ZodObject<{
|
|
|
2087
2098
|
commentUrl: z.ZodOptional<z.ZodString>;
|
|
2088
2099
|
producer: z.ZodString;
|
|
2089
2100
|
rung: z.ZodEnum<{
|
|
2090
|
-
"independent-model": "independent-model";
|
|
2091
2101
|
oracle: "oracle";
|
|
2102
|
+
"independent-model": "independent-model";
|
|
2092
2103
|
human: "human";
|
|
2093
2104
|
}>;
|
|
2094
2105
|
sessionId: z.ZodOptional<z.ZodString>;
|
|
@@ -2227,7 +2238,7 @@ interface PrReviewDependencies {
|
|
|
2227
2238
|
}
|
|
2228
2239
|
declare function runPrReview(args: PrReviewArgs, dependencies?: PrReviewDependencies): Promise<PrReviewProof>;
|
|
2229
2240
|
declare namespace verification_proof_d_exports {
|
|
2230
|
-
export { PrVerifyBaselineFullProof, PrVerifyCommandResult, PrVerifyExecutedCommand, PrVerifyNotRequiredCommand, PrVerifyProof, PrVerifySchemaVersion, ResolvedPrVerifyMode, SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS, VerificationHeadShas, VerificationProofState, VerifiedHeadShas, prVerifyFullHeadShasForDiff, prVerifyProofHeadShas, readPrVerifyProof, resolveVerifyProofApplicability, trustedImpactStamp, validatePrVerifyProof, verificationProofApplicabilityFor, verificationProofBlockingReason, verificationProofForReadiness, verificationProofStateFor, verifyProofIdentityMatches, verifyProofPassed };
|
|
2241
|
+
export { PrVerifyBaselineFullProof, PrVerifyCommandResult, PrVerifyExecutedCommand, PrVerifyNotDemandedRecord, PrVerifyNotRequiredCommand, PrVerifyProof, PrVerifySchemaVersion, ResolvedPrVerifyMode, SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS, VerificationHeadShas, VerificationProofState, VerifiedHeadShas, prVerifyFullHeadShasForDiff, prVerifyProofHeadShas, readPrVerifyProof, resolveVerifyProofApplicability, trustedImpactStamp, validatePrVerifyProof, verificationProofApplicabilityFor, verificationProofBlockingReason, verificationProofForReadiness, verificationProofStateFor, verifyProofIdentityMatches, verifyProofPassed };
|
|
2231
2242
|
}
|
|
2232
2243
|
interface VerifiedHeadShas {
|
|
2233
2244
|
docsOnlyVerifiedHeadShas: string[];
|
|
@@ -2383,7 +2394,7 @@ interface CleanInstallResolutionDependencies {
|
|
|
2383
2394
|
}
|
|
2384
2395
|
//#endregion
|
|
2385
2396
|
//#region src/pr-verify.d.ts
|
|
2386
|
-
type PrVerifyMode = "auto" | "docs-only" | "
|
|
2397
|
+
type PrVerifyMode = "auto" | "docs-only" | "full";
|
|
2387
2398
|
interface VerificationCommandOutcome {
|
|
2388
2399
|
counts?: {
|
|
2389
2400
|
testFiles?: number;
|
|
@@ -3223,8 +3234,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3223
3234
|
interiorCycle: z.ZodOptional<z.ZodNumber>;
|
|
3224
3235
|
observedAt: z.ZodString;
|
|
3225
3236
|
purpose: z.ZodEnum<{
|
|
3226
|
-
code: "code";
|
|
3227
3237
|
review: "review";
|
|
3238
|
+
code: "code";
|
|
3228
3239
|
}>;
|
|
3229
3240
|
slotResolution: z.ZodObject<{
|
|
3230
3241
|
effort: z.ZodEnum<{
|
|
@@ -3242,18 +3253,18 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3242
3253
|
}>;
|
|
3243
3254
|
model: z.ZodString;
|
|
3244
3255
|
tier: z.ZodEnum<{
|
|
3245
|
-
oracle: "oracle";
|
|
3246
3256
|
review: "review";
|
|
3247
3257
|
delegate: "delegate";
|
|
3258
|
+
oracle: "oracle";
|
|
3248
3259
|
workhorse: "workhorse";
|
|
3249
3260
|
scout: "scout";
|
|
3250
3261
|
}>;
|
|
3251
3262
|
}, z.core.$strict>;
|
|
3252
3263
|
startedAt: z.ZodString;
|
|
3253
3264
|
tier: z.ZodEnum<{
|
|
3254
|
-
oracle: "oracle";
|
|
3255
3265
|
review: "review";
|
|
3256
3266
|
delegate: "delegate";
|
|
3267
|
+
oracle: "oracle";
|
|
3257
3268
|
workhorse: "workhorse";
|
|
3258
3269
|
scout: "scout";
|
|
3259
3270
|
}>;
|
|
@@ -3302,8 +3313,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3302
3313
|
interiorCycle: z.ZodOptional<z.ZodNumber>;
|
|
3303
3314
|
observedAt: z.ZodString;
|
|
3304
3315
|
purpose: z.ZodEnum<{
|
|
3305
|
-
code: "code";
|
|
3306
3316
|
review: "review";
|
|
3317
|
+
code: "code";
|
|
3307
3318
|
}>;
|
|
3308
3319
|
slotResolution: z.ZodObject<{
|
|
3309
3320
|
effort: z.ZodEnum<{
|
|
@@ -3321,18 +3332,18 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3321
3332
|
}>;
|
|
3322
3333
|
model: z.ZodString;
|
|
3323
3334
|
tier: z.ZodEnum<{
|
|
3324
|
-
oracle: "oracle";
|
|
3325
3335
|
review: "review";
|
|
3326
3336
|
delegate: "delegate";
|
|
3337
|
+
oracle: "oracle";
|
|
3327
3338
|
workhorse: "workhorse";
|
|
3328
3339
|
scout: "scout";
|
|
3329
3340
|
}>;
|
|
3330
3341
|
}, z.core.$strict>;
|
|
3331
3342
|
startedAt: z.ZodString;
|
|
3332
3343
|
tier: z.ZodEnum<{
|
|
3333
|
-
oracle: "oracle";
|
|
3334
3344
|
review: "review";
|
|
3335
3345
|
delegate: "delegate";
|
|
3346
|
+
oracle: "oracle";
|
|
3336
3347
|
workhorse: "workhorse";
|
|
3337
3348
|
scout: "scout";
|
|
3338
3349
|
}>;
|
|
@@ -3372,8 +3383,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3372
3383
|
interiorCycle: z.ZodOptional<z.ZodNumber>;
|
|
3373
3384
|
observedAt: z.ZodString;
|
|
3374
3385
|
purpose: z.ZodEnum<{
|
|
3375
|
-
code: "code";
|
|
3376
3386
|
review: "review";
|
|
3387
|
+
code: "code";
|
|
3377
3388
|
}>;
|
|
3378
3389
|
slotResolution: z.ZodObject<{
|
|
3379
3390
|
effort: z.ZodEnum<{
|
|
@@ -3391,18 +3402,18 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3391
3402
|
}>;
|
|
3392
3403
|
model: z.ZodString;
|
|
3393
3404
|
tier: z.ZodEnum<{
|
|
3394
|
-
oracle: "oracle";
|
|
3395
3405
|
review: "review";
|
|
3396
3406
|
delegate: "delegate";
|
|
3407
|
+
oracle: "oracle";
|
|
3397
3408
|
workhorse: "workhorse";
|
|
3398
3409
|
scout: "scout";
|
|
3399
3410
|
}>;
|
|
3400
3411
|
}, z.core.$strict>;
|
|
3401
3412
|
startedAt: z.ZodString;
|
|
3402
3413
|
tier: z.ZodEnum<{
|
|
3403
|
-
oracle: "oracle";
|
|
3404
3414
|
review: "review";
|
|
3405
3415
|
delegate: "delegate";
|
|
3416
|
+
oracle: "oracle";
|
|
3406
3417
|
workhorse: "workhorse";
|
|
3407
3418
|
scout: "scout";
|
|
3408
3419
|
}>;
|
|
@@ -3445,17 +3456,17 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3445
3456
|
exitCode: number | null;
|
|
3446
3457
|
harness: string;
|
|
3447
3458
|
observedAt: string;
|
|
3448
|
-
purpose: "
|
|
3459
|
+
purpose: "review" | "code";
|
|
3449
3460
|
slotResolution: {
|
|
3450
3461
|
effort: "high" | "low" | "medium" | "xhigh";
|
|
3451
3462
|
engine: string;
|
|
3452
3463
|
layerSource: "committedDefault" | "operatorSession" | "userConfig";
|
|
3453
3464
|
model: string;
|
|
3454
|
-
tier: "
|
|
3465
|
+
tier: "review" | "delegate" | "oracle" | "workhorse" | "scout";
|
|
3455
3466
|
fallbackTier?: "delegate" | undefined;
|
|
3456
3467
|
};
|
|
3457
3468
|
startedAt: string;
|
|
3458
|
-
tier: "
|
|
3469
|
+
tier: "review" | "delegate" | "oracle" | "workhorse" | "scout";
|
|
3459
3470
|
tokenUsage: {
|
|
3460
3471
|
source: "cursor-csv" | "codex-json" | "cursor-json" | "claude-json";
|
|
3461
3472
|
status: "available";
|
|
@@ -3792,13 +3803,13 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
|
|
|
3792
3803
|
reviews: {
|
|
3793
3804
|
correctness: {
|
|
3794
3805
|
required: boolean;
|
|
3795
|
-
status: "
|
|
3806
|
+
status: "blocked" | "not-required" | "current" | "stale" | "missing";
|
|
3796
3807
|
reviewedHeadSha?: string | undefined;
|
|
3797
3808
|
reviewedPatchId?: string | undefined;
|
|
3798
3809
|
};
|
|
3799
3810
|
security?: {
|
|
3800
3811
|
required: boolean;
|
|
3801
|
-
status: "
|
|
3812
|
+
status: "blocked" | "not-required" | "current" | "stale" | "missing";
|
|
3802
3813
|
reviewedHeadSha?: string | undefined;
|
|
3803
3814
|
reviewedPatchId?: string | undefined;
|
|
3804
3815
|
} | undefined;
|
|
@@ -3806,7 +3817,7 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
|
|
|
3806
3817
|
schemaVersion: 1;
|
|
3807
3818
|
verification: {
|
|
3808
3819
|
command: "patronage-factory pr:verify";
|
|
3809
|
-
prVerify: "
|
|
3820
|
+
prVerify: "passed" | "stale" | "missing";
|
|
3810
3821
|
docsOnlyDeltaAccepted?: boolean | undefined;
|
|
3811
3822
|
docsOnlyVerifiedHeadSha?: string | undefined;
|
|
3812
3823
|
trivialDeltaAccepted?: boolean | undefined;
|
|
@@ -4174,6 +4185,34 @@ declare const appendReviewLadderStageTraceEvent: ({
|
|
|
4174
4185
|
}) => AppendFactoryTraceEventResult;
|
|
4175
4186
|
declare const tryAppendReviewLadderStageTraceEvent: (input: Parameters<typeof appendReviewLadderStageTraceEvent>[0]) => ReviewLadderStageTraceWriteResult;
|
|
4176
4187
|
//#endregion
|
|
4188
|
+
//#region src/verification-run-context.d.ts
|
|
4189
|
+
declare const FACTORY_BASE_REF_ENV = "FACTORY_BASE_REF";
|
|
4190
|
+
declare const FACTORY_CHANGED_FILES_ENV = "FACTORY_CHANGED_FILES";
|
|
4191
|
+
declare const FACTORY_CHANGED_FILES_FILE_ENV = "FACTORY_CHANGED_FILES_FILE";
|
|
4192
|
+
declare const EMPTY_TREE_OBJECT_HASH = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
4193
|
+
declare const DEFAULT_ROOT_SHARED_GLOBS: string[];
|
|
4194
|
+
interface VerificationRunContextInput {
|
|
4195
|
+
baseRef: string;
|
|
4196
|
+
changedFiles: string[];
|
|
4197
|
+
fullSuiteBaseRef?: string;
|
|
4198
|
+
rootSharedGlobs?: string[];
|
|
4199
|
+
inlineLimitBytes?: number;
|
|
4200
|
+
tmpDir?: string;
|
|
4201
|
+
}
|
|
4202
|
+
interface VerificationEnv {
|
|
4203
|
+
env: Record<string, string>;
|
|
4204
|
+
cleanup: () => void;
|
|
4205
|
+
}
|
|
4206
|
+
interface VerificationRunContext {
|
|
4207
|
+
readonly baseRef: string;
|
|
4208
|
+
readonly changedFiles: string[];
|
|
4209
|
+
readonly forcesFullSuite: boolean;
|
|
4210
|
+
readonly fullSuiteReasons: string[];
|
|
4211
|
+
buildEnv: () => VerificationEnv;
|
|
4212
|
+
runSummaryLines: () => string[];
|
|
4213
|
+
}
|
|
4214
|
+
declare function createVerificationRunContext(input: VerificationRunContextInput): VerificationRunContext;
|
|
4215
|
+
//#endregion
|
|
4177
4216
|
//#region src/verification-battery.d.ts
|
|
4178
4217
|
type BatteryDisposition = "executed" | "not-required";
|
|
4179
4218
|
/**
|
|
@@ -4370,4 +4409,4 @@ interface CreateProgramOptions {
|
|
|
4370
4409
|
declare function createProgram(options?: CreateProgramOptions): Command;
|
|
4371
4410
|
declare function run(argv?: string[]): Promise<void>;
|
|
4372
4411
|
//#endregion
|
|
4373
|
-
export { type AppendFactoryTraceEventResult, type AssembledReviewPrompt, type BatteryCommandDisposition, type BatteryDisposition, type BoundaryCheckArgs, type BoundaryCheckDependencies, type BoundaryCheckProofRecord, type BuildEpicStructureEventInput, type CloudflareAccessServiceToken, CreateProgramOptions, DEFAULT_DEMAND_WAIVER_PATH, DEFAULT_FACTORY_REPOSITORY, type DagDocument, type DemandWaiveArgs, type DemandWaiveDependencies, DemandWaiveRefusalError, type DemandWaiver, type DemandWaiverStore, EPIC_STRUCTURE_NODE_STATUSES, EPIC_STRUCTURE_SCHEMA_VERSION, type EpicStructureEvent, type EpicStructureGraphPayload, type EpicStructureNodeStatus, EpicStructureValidationError, type EvidenceEmitArgs, type EvidenceEmitDependencies, type EvidenceEmitResult, type FactoryCliInvocation, FactoryCliInvocationSchema, type FactoryProjectProfile, type FactoryTraceDiagnostic, type FactoryTraceEnvelope, type FactoryTraceEvent, type FindingDisposition, type FindingLedgerEntry, type FollowUpAction, FollowUpActionSchema, type ImpactScopeDecision, type ImpactScopeSurface, type IssueReviewFocus, type LadderDispositionDeclaration, MERGE_FREEZE_APP_SLUG, MERGE_FREEZE_CHECK_NAME, type MergeFreezeState, type PrPublishArgs, type PrPublishDependencies, PrPublishFollowUpError, type PrPublishFollowUpOutcome, type PrPublishHandoff, type PrPublishResult, type PrReadyArgs, type PrReadyProof, type PreviewDisposition, type PreviewLifecyclePlan, type PreviewTargetPlan, type PublishEpicStructureArgs, type PublishEpicStructureResult, type PublishFollowUpPlan, REVIEW_FOCUS_SECTION, type ReviewGateNotRequiredProof, type ReviewGateTraceIdentity, type ReviewLadderCycle, type ReviewLadderEvaluation, type ReviewLadderPolicy, type ReviewLadderStageEvent, type ReviewLadderTraceIdentity, type ReviewPromptSection, type ReviewPromptSectionProvenance, type ScanFactoryTraceDiagnosticsOptions, type ScanFactoryTraceOptions, type ScanFactoryTraceResult, type ScopedOutCommand, type TraceMirrorDiagnostic, type TraceSink, type TraceWriteResult, type TraceWriteSinks, type VerificationBatteryPlan, type VerificationReuse, type WaivedDemand, WorkerCheckoutGuardError, type WorkerCloseoutLessonsTraceEvent, appendReviewLadderStageTraceEvent, appendWithTraceSinks, applyDemandWaiver, assembleReviewPrompt, assertWorkerCheckoutAllowed, authorizeDemandWaiver, batteryScopeSummaryLines, blockingLadderFindings, boundary_manifest_d_exports as boundaryManifest, boundary_review_proof_d_exports as boundaryReviewProof, buildEpicStructureEvent, buildEpicStructurePayload, buildEvidenceEnvelope, buildReviewGateNotRequiredTraceEvent, buildReviewLadderStageTraceEvent, comment_provenance_d_exports as commentProvenance, createLocalJsonlTraceSink, createProgram, doctorProjectProfile, epicStructureEventId, evaluateReviewLadder, evidenceEnvelopeFilename, findingKey, followUpFromArgv, impactStampScopeDecision, inferFixedInThreadDispositions, isProductionHqUrl, loadProjectProfile, normalizeIssueComments, openLadderFindings, planPreviewLifecycle, planPublishFollowUp, planVerificationBattery, readiness_evaluation_d_exports as prReadinessEvaluation, external_evidence_d_exports as prReadinessExternalEvidence, pr_body_renderer_d_exports as prReadinessPrBodyRenderer, proof_identity_d_exports as prReadinessProofIdentity, review_proof_d_exports as prReadinessReviewProof, status_check_rollup_d_exports as prReadinessStatusChecks, verification_proof_d_exports as prReadinessVerificationProof, previewLifecycleTargets, publishEpicStructure, readDemandWaivers, readPrReadyProof, readPrReviewProof, resolveFactoryRepository, resolveFindingBlocking, resolveReviewFindingCategory, resolveReviewFindingSeverity, resolveReviewLadderPolicy, resolveTraceWriteSinks, reviewCycleStateFor, reviewFocusFromIssueBody, reviewPromptSectionSchema, reviewPromptSectionsSchema, run, runBoundaryCheck, runDemandWaive, runEvidenceEmit, runPrPublish, runPrReady, runPrReview, runPrVerify, scanFactoryTraceDiagnostics, scanFactoryTraceEvents, selectWaiversForCandidate, staleRepeatLadderFindings, toFactoryTraceEnvelope, tryAppendReviewGateNotRequiredTraceEvent, tryAppendReviewLadderStageTraceEvent, validateBoundaryCheckProof, validateDagDocument, validateDemandWaiverStore, validateFactoryTraceEvent, validateMergeFreezeState, validatePrReadyProof, validatePrReviewProof, validatePrVerifyProof, waivedDemandNotice, waivedDemandSchema, worktree_scratch_files_d_exports as worktreeScratchFiles };
|
|
4412
|
+
export { type AppendFactoryTraceEventResult, type AssembledReviewPrompt, type BatteryCommandDisposition, type BatteryDisposition, type BoundaryCheckArgs, type BoundaryCheckDependencies, type BoundaryCheckProofRecord, type BuildEpicStructureEventInput, type CloudflareAccessServiceToken, CreateProgramOptions, DEFAULT_DEMAND_WAIVER_PATH, DEFAULT_FACTORY_REPOSITORY, DEFAULT_ROOT_SHARED_GLOBS, type DagDocument, type DemandWaiveArgs, type DemandWaiveDependencies, DemandWaiveRefusalError, type DemandWaiver, type DemandWaiverStore, EMPTY_TREE_OBJECT_HASH, EPIC_STRUCTURE_NODE_STATUSES, EPIC_STRUCTURE_SCHEMA_VERSION, type EpicStructureEvent, type EpicStructureGraphPayload, type EpicStructureNodeStatus, EpicStructureValidationError, type EvidenceEmitArgs, type EvidenceEmitDependencies, type EvidenceEmitResult, FACTORY_BASE_REF_ENV, FACTORY_CHANGED_FILES_ENV, FACTORY_CHANGED_FILES_FILE_ENV, type FactoryCliInvocation, FactoryCliInvocationSchema, type FactoryProjectProfile, type FactoryTraceDiagnostic, type FactoryTraceEnvelope, type FactoryTraceEvent, type FindingDisposition, type FindingLedgerEntry, type FollowUpAction, FollowUpActionSchema, type ImpactScopeDecision, type ImpactScopeSurface, type IssueReviewFocus, type LadderDispositionDeclaration, MERGE_FREEZE_APP_SLUG, MERGE_FREEZE_CHECK_NAME, type MergeFreezeState, type PrPublishArgs, type PrPublishDependencies, PrPublishFollowUpError, type PrPublishFollowUpOutcome, type PrPublishHandoff, type PrPublishResult, type PrReadyArgs, type PrReadyProof, type PreviewDisposition, type PreviewLifecyclePlan, type PreviewTargetPlan, type PublishEpicStructureArgs, type PublishEpicStructureResult, type PublishFollowUpPlan, REVIEW_FOCUS_SECTION, type ReviewGateNotRequiredProof, type ReviewGateTraceIdentity, type ReviewLadderCycle, type ReviewLadderEvaluation, type ReviewLadderPolicy, type ReviewLadderStageEvent, type ReviewLadderTraceIdentity, type ReviewPromptSection, type ReviewPromptSectionProvenance, type ScanFactoryTraceDiagnosticsOptions, type ScanFactoryTraceOptions, type ScanFactoryTraceResult, type ScopedOutCommand, type TraceMirrorDiagnostic, type TraceSink, type TraceWriteResult, type TraceWriteSinks, type VerificationBatteryPlan, type VerificationReuse, type VerificationRunContext, type VerificationRunContextInput, type WaivedDemand, WorkerCheckoutGuardError, type WorkerCloseoutLessonsTraceEvent, appendReviewLadderStageTraceEvent, appendWithTraceSinks, applyDemandWaiver, assembleReviewPrompt, assertWorkerCheckoutAllowed, authorizeDemandWaiver, batteryScopeSummaryLines, blockingLadderFindings, boundary_manifest_d_exports as boundaryManifest, boundary_review_proof_d_exports as boundaryReviewProof, buildEpicStructureEvent, buildEpicStructurePayload, buildEvidenceEnvelope, buildReviewGateNotRequiredTraceEvent, buildReviewLadderStageTraceEvent, comment_provenance_d_exports as commentProvenance, createLocalJsonlTraceSink, createProgram, createVerificationRunContext, doctorProjectProfile, epicStructureEventId, evaluateReviewLadder, evidenceEnvelopeFilename, findingKey, followUpFromArgv, impactStampScopeDecision, inferFixedInThreadDispositions, isProductionHqUrl, loadProjectProfile, normalizeIssueComments, openLadderFindings, planPreviewLifecycle, planPublishFollowUp, planVerificationBattery, readiness_evaluation_d_exports as prReadinessEvaluation, external_evidence_d_exports as prReadinessExternalEvidence, pr_body_renderer_d_exports as prReadinessPrBodyRenderer, proof_identity_d_exports as prReadinessProofIdentity, review_proof_d_exports as prReadinessReviewProof, status_check_rollup_d_exports as prReadinessStatusChecks, verification_proof_d_exports as prReadinessVerificationProof, previewLifecycleTargets, publishEpicStructure, readDemandWaivers, readPrReadyProof, readPrReviewProof, resolveFactoryRepository, resolveFindingBlocking, resolveReviewFindingCategory, resolveReviewFindingSeverity, resolveReviewLadderPolicy, resolveTraceWriteSinks, reviewCycleStateFor, reviewFocusFromIssueBody, reviewPromptSectionSchema, reviewPromptSectionsSchema, run, runBoundaryCheck, runDemandWaive, runEvidenceEmit, runPrPublish, runPrReady, runPrReview, runPrVerify, scanFactoryTraceDiagnostics, scanFactoryTraceEvents, selectWaiversForCandidate, staleRepeatLadderFindings, toFactoryTraceEnvelope, tryAppendReviewGateNotRequiredTraceEvent, tryAppendReviewLadderStageTraceEvent, validateBoundaryCheckProof, validateDagDocument, validateDemandWaiverStore, validateFactoryTraceEvent, validateMergeFreezeState, validatePrReadyProof, validatePrReviewProof, validatePrVerifyProof, waivedDemandNotice, waivedDemandSchema, worktree_scratch_files_d_exports as worktreeScratchFiles };
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { appendFileSync, constants, cpSync, existsSync, mkdirSync, mkdtempSync,
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { Command, InvalidArgumentError } from "commander";
|
|
6
6
|
import path from "node:path";
|
|
7
|
-
import crypto, { createHash, randomUUID } from "node:crypto";
|
|
7
|
+
import crypto, { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
10
10
|
import { link, lstat, mkdir, open, readFile, readdir, realpath, rename, stat, unlink } from "node:fs/promises";
|
|
@@ -17,7 +17,7 @@ import { parse } from "yaml";
|
|
|
17
17
|
import { FACTORY_PROOF_GATE_STEP_NAME, GitHubApiError, mintInstallationToken, productionImpactTargetOutput, readVitestProfileDocument } from "@patronage/factory-ci";
|
|
18
18
|
import { promisify } from "node:util";
|
|
19
19
|
//#region package.json
|
|
20
|
-
var version = "1.0.0-alpha.
|
|
20
|
+
var version = "1.0.0-alpha.17";
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region src/review-rungs.ts
|
|
23
23
|
const EVIDENCE_REVIEW_RUNGS$1 = [
|
|
@@ -1178,9 +1178,10 @@ const hqIngestConfigSchema = z.object({
|
|
|
1178
1178
|
}
|
|
1179
1179
|
}, "Must be an HTTPS origin without a path, query, fragment, or embedded credentials").describe("Repository-declared HQ origin (scheme and host only; the factory composes the ingest path). Credential delivery additionally requires exact-origin authorization in the operator user config hqAllowedOrigins list.")
|
|
1180
1180
|
}).strict();
|
|
1181
|
+
const profileRequiredCheckScopeSchema = requiredCheckScopeSchema$1.extend({ classifications: z.array(z.enum(["docs/process-only", "non-trivial"])).min(1) }).strict();
|
|
1181
1182
|
const requiredCheckSchema = z.object({
|
|
1182
1183
|
name: z.string().min(1),
|
|
1183
|
-
scope:
|
|
1184
|
+
scope: profileRequiredCheckScopeSchema.optional()
|
|
1184
1185
|
}).strict();
|
|
1185
1186
|
const impactTargetSchema = z.object({
|
|
1186
1187
|
importers: z.array(z.string().min(1)).min(1),
|
|
@@ -1212,15 +1213,6 @@ const factoryProjectProfileSchema = z.object({
|
|
|
1212
1213
|
code: "custom",
|
|
1213
1214
|
message: "requiredChecks entries must have distinct names."
|
|
1214
1215
|
});
|
|
1215
|
-
for (const [index, check] of checks.entries()) if (check.scope?.classifications.includes("trivial")) context.addIssue({
|
|
1216
|
-
code: "custom",
|
|
1217
|
-
message: `requiredChecks entry "${check.name}" scopes on classification "trivial", which the classifier no longer produces (#742); scope on "docs/process-only" or "non-trivial".`,
|
|
1218
|
-
path: [
|
|
1219
|
-
index,
|
|
1220
|
-
"scope",
|
|
1221
|
-
"classifications"
|
|
1222
|
-
]
|
|
1223
|
-
});
|
|
1224
1216
|
}).optional(),
|
|
1225
1217
|
review: z.object({ conditional: z.array(conditionalReviewSchema).min(1) }).strict().optional(),
|
|
1226
1218
|
schemaVersion: z.literal(6),
|
|
@@ -3797,6 +3789,7 @@ const impactStampTargetSchema = z.object({
|
|
|
3797
3789
|
*/
|
|
3798
3790
|
const impactStampSchema = z.object({
|
|
3799
3791
|
basis: z.enum(["target-scoped", "conservative"]),
|
|
3792
|
+
inertPaths: z.array(z.string()).default([]),
|
|
3800
3793
|
reasons: z.array(z.string()),
|
|
3801
3794
|
stampVersion: z.literal(3),
|
|
3802
3795
|
targets: z.array(impactStampTargetSchema).superRefine((targets, context) => {
|
|
@@ -3813,6 +3806,11 @@ const unsubscribedPathsLine = (paths, reason = "legacy or malformed stamp") => {
|
|
|
3813
3806
|
if (!Array.isArray(paths) || paths.some((one) => typeof one !== "string")) return `Unsubscribed changed paths: unavailable (${reason}).`;
|
|
3814
3807
|
return paths.length === 0 ? "Unsubscribed changed paths: none." : `Unsubscribed changed paths (demand no target): ${paths.join(", ")}.`;
|
|
3815
3808
|
};
|
|
3809
|
+
/** The one inert-owned path line (#761): candidate-impact console and summary. */
|
|
3810
|
+
const inertPathsLine = (paths, reason = "legacy or malformed stamp") => {
|
|
3811
|
+
if (!Array.isArray(paths) || paths.some((one) => typeof one !== "string")) return `Inert-owned changed paths: unavailable (${reason}).`;
|
|
3812
|
+
return paths.length === 0 ? "Inert-owned changed paths: none." : `Inert-owned changed paths: ${paths.join(", ")}.`;
|
|
3813
|
+
};
|
|
3816
3814
|
/** Human-readable lines for every impact decision, including quiet releases. */
|
|
3817
3815
|
const impactStampSummaryLines = (stamp) => [`Impact stamp: ${stamp.basis}; ${stamp.targets.filter(({ impact }) => impact === "affected").length}/${stamp.targets.length} target(s) affected.`, unsubscribedPathsLine(stamp.unsubscribedPaths)];
|
|
3818
3816
|
/**
|
|
@@ -3907,8 +3905,9 @@ const impactStampDemandRelease = ({ checkName, stamp }) => {
|
|
|
3907
3905
|
* every doubt path lands on, and the stamp a producer must fall back to if
|
|
3908
3906
|
* stamp computation itself fails for any reason.
|
|
3909
3907
|
*/
|
|
3910
|
-
const conservativeImpactStamp = (targets, reasons, unsubscribedPaths = []) => ({
|
|
3908
|
+
const conservativeImpactStamp = (targets, reasons, unsubscribedPaths = [], inertPaths = []) => ({
|
|
3911
3909
|
basis: "conservative",
|
|
3910
|
+
inertPaths,
|
|
3912
3911
|
reasons,
|
|
3913
3912
|
stampVersion: 3,
|
|
3914
3913
|
targets: targets.map((target) => ({
|
|
@@ -4048,9 +4047,13 @@ const targetImpactForLockfileDelta = (target, delta) => {
|
|
|
4048
4047
|
* as affected/not-affected with its exact basis. `not-affected` is only ever
|
|
4049
4048
|
* returned only when every changed input is a valid source path or an
|
|
4050
4049
|
* analyzable pnpm-lock.yaml (v9) delta. Source and lockfile reachability are
|
|
4051
|
-
* unioned per target. A valid source path matched by
|
|
4052
|
-
*
|
|
4053
|
-
*
|
|
4050
|
+
* unioned per target. A valid source path matched by a target subscription is
|
|
4051
|
+
* product-owned and appears in neither path list. A valid source path matching
|
|
4052
|
+
* `impact.inert` and no target is recorded in `inertPaths`. A valid source
|
|
4053
|
+
* path with no declared owner is recorded in `unsubscribedPaths`.
|
|
4054
|
+
* `pnpm-lock.yaml` is a graph input and is never inert. Invalid, unreadable,
|
|
4055
|
+
* unsupported, or otherwise doubtful input widens the whole stamp to full
|
|
4056
|
+
* impact.
|
|
4054
4057
|
*
|
|
4055
4058
|
* Total by contract: this function never throws. Any unexpected failure in
|
|
4056
4059
|
* reading, parsing, or traversal is itself a doubt path and returns the
|
|
@@ -4116,7 +4119,27 @@ const assertDeclaredImporterRoots = (targets, sides) => {
|
|
|
4116
4119
|
const assertDeclaredPathGlobs = (targets) => {
|
|
4117
4120
|
for (const target of targets) for (const pattern of target.paths ?? []) if (!isValidRepoRelativeGlob(pattern)) throw new ImpactStampConfigError(`impact target "${target.name}" declares invalid repo-relative path glob "${pattern}"; fix the profile's impact.targets declaration`);
|
|
4118
4121
|
};
|
|
4119
|
-
const
|
|
4122
|
+
const assertDeclaredInertGlobs = (inert) => {
|
|
4123
|
+
for (const pattern of inert) if (!isValidRepoRelativeGlob(pattern)) throw new ImpactStampConfigError(`impact.inert declares invalid repo-relative path glob "${pattern}"; fix the profile's impact.inert declaration`);
|
|
4124
|
+
};
|
|
4125
|
+
const matchesTargetSubscription = (targets, file) => targets.some((target) => matchesAnyGlob(target.paths ?? [], file));
|
|
4126
|
+
const partitionUnownedSourcePaths = (sourceFiles, targets, inertGlobs) => {
|
|
4127
|
+
const inertPaths = [];
|
|
4128
|
+
const unsubscribedPaths = [];
|
|
4129
|
+
for (const file of sourceFiles) {
|
|
4130
|
+
if (file === "pnpm-lock.yaml" || matchesTargetSubscription(targets, file)) continue;
|
|
4131
|
+
if (matchesAnyGlob(inertGlobs, file)) {
|
|
4132
|
+
inertPaths.push(file);
|
|
4133
|
+
continue;
|
|
4134
|
+
}
|
|
4135
|
+
unsubscribedPaths.push(file);
|
|
4136
|
+
}
|
|
4137
|
+
return {
|
|
4138
|
+
inertPaths,
|
|
4139
|
+
unsubscribedPaths
|
|
4140
|
+
};
|
|
4141
|
+
};
|
|
4142
|
+
const analyzeSourcePaths = (sourceFiles, targets, profilePath, inertGlobs) => {
|
|
4120
4143
|
for (const file of sourceFiles) {
|
|
4121
4144
|
if (!isRepoRelativePath(file)) return {
|
|
4122
4145
|
kind: "conservative",
|
|
@@ -4129,6 +4152,7 @@ const analyzeSourcePaths = (sourceFiles, targets, profilePath) => {
|
|
|
4129
4152
|
}
|
|
4130
4153
|
return {
|
|
4131
4154
|
kind: "ok",
|
|
4155
|
+
...partitionUnownedSourcePaths(sourceFiles, targets, inertGlobs),
|
|
4132
4156
|
targets: targets.map((target) => {
|
|
4133
4157
|
const matched = sourceFiles.find((file) => matchesAnyGlob(target.paths ?? [], file));
|
|
4134
4158
|
return matched === void 0 ? {
|
|
@@ -4140,8 +4164,7 @@ const analyzeSourcePaths = (sourceFiles, targets, profilePath) => {
|
|
|
4140
4164
|
impact: "affected",
|
|
4141
4165
|
name: target.name
|
|
4142
4166
|
};
|
|
4143
|
-
})
|
|
4144
|
-
unsubscribedPaths: sourceFiles.filter((file) => !targets.some((target) => matchesAnyGlob(target.paths ?? [], file)))
|
|
4167
|
+
})
|
|
4145
4168
|
};
|
|
4146
4169
|
};
|
|
4147
4170
|
const unionTargetImpact = (source, lockfile) => {
|
|
@@ -4153,34 +4176,41 @@ const unionTargetImpact = (source, lockfile) => {
|
|
|
4153
4176
|
name: source.name
|
|
4154
4177
|
};
|
|
4155
4178
|
};
|
|
4156
|
-
const computeImpactStampOrThrow = ({ changedFiles, profilePath = "software-factory.profile.json", readLockfile }, targets) => {
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4179
|
+
const computeImpactStampOrThrow = ({ changedFiles, profile, profilePath = "software-factory.profile.json", readLockfile }, targets) => {
|
|
4180
|
+
const inertGlobs = profile.impact?.inert ?? [];
|
|
4181
|
+
assertDeclaredInertGlobs(inertGlobs);
|
|
4182
|
+
if (targets.length === 0) {
|
|
4183
|
+
const { inertPaths, unsubscribedPaths } = partitionUnownedSourcePaths(changedFiles.filter((file) => file !== "pnpm-lock.yaml" && isRepoRelativePath(file)), [], inertGlobs);
|
|
4184
|
+
return {
|
|
4185
|
+
basis: "conservative",
|
|
4186
|
+
inertPaths,
|
|
4187
|
+
reasons: ["no impact targets declared in the profile; every surface keeps full demand"],
|
|
4188
|
+
stampVersion: 3,
|
|
4189
|
+
targets: [],
|
|
4190
|
+
unsubscribedPaths
|
|
4191
|
+
};
|
|
4192
|
+
}
|
|
4164
4193
|
assertDeclaredPathGlobs(targets);
|
|
4165
4194
|
const sides = readLockfileSides(readLockfile);
|
|
4166
4195
|
if (sides.kind === "ok") assertDeclaredImporterRoots(targets, sides.parsed);
|
|
4167
4196
|
if (changedFiles.length === 0) return conservativeImpactStamp(targets, ["no changed files detected; full impact assumed"]);
|
|
4168
4197
|
const sourceFiles = changedFiles.filter((file) => file !== LOCKFILE_PATH);
|
|
4169
|
-
const source = analyzeSourcePaths(sourceFiles, targets, profilePath);
|
|
4198
|
+
const source = analyzeSourcePaths(sourceFiles, targets, profilePath, inertGlobs);
|
|
4170
4199
|
if (source.kind === "conservative") return conservativeImpactStamp(targets, [source.reason]);
|
|
4171
|
-
if (sides.kind === "threw") return conservativeImpactStamp(targets, [`impact stamp computation failed (${sides.error instanceof Error ? sides.error.message : String(sides.error)}); fail closed to full impact`], source.unsubscribedPaths);
|
|
4172
|
-
if (sides.kind === "unreadable") return conservativeImpactStamp(targets, [`pnpm-lock.yaml is unreadable at the ${sides.side} side; fail closed to full impact`], source.unsubscribedPaths);
|
|
4173
|
-
if (sides.kind === "unparseable") return conservativeImpactStamp(targets, [`pnpm-lock.yaml is unparseable or structurally malformed at the ${sides.side} side; fail closed to full impact`], source.unsubscribedPaths);
|
|
4174
|
-
if (sides.kind === "unsupported-version") return conservativeImpactStamp(targets, [`pnpm-lock.yaml is not lockfileVersion ${SUPPORTED_LOCKFILE_VERSION}; only pnpm v9 lockfiles are modelled; fail closed to full impact`], source.unsubscribedPaths);
|
|
4200
|
+
if (sides.kind === "threw") return conservativeImpactStamp(targets, [`impact stamp computation failed (${sides.error instanceof Error ? sides.error.message : String(sides.error)}); fail closed to full impact`], source.unsubscribedPaths, source.inertPaths);
|
|
4201
|
+
if (sides.kind === "unreadable") return conservativeImpactStamp(targets, [`pnpm-lock.yaml is unreadable at the ${sides.side} side; fail closed to full impact`], source.unsubscribedPaths, source.inertPaths);
|
|
4202
|
+
if (sides.kind === "unparseable") return conservativeImpactStamp(targets, [`pnpm-lock.yaml is unparseable or structurally malformed at the ${sides.side} side; fail closed to full impact`], source.unsubscribedPaths, source.inertPaths);
|
|
4203
|
+
if (sides.kind === "unsupported-version") return conservativeImpactStamp(targets, [`pnpm-lock.yaml is not lockfileVersion ${SUPPORTED_LOCKFILE_VERSION}; only pnpm v9 lockfiles are modelled; fail closed to full impact`], source.unsubscribedPaths, source.inertPaths);
|
|
4175
4204
|
if (!changedFiles.includes("pnpm-lock.yaml")) return {
|
|
4176
4205
|
basis: "target-scoped",
|
|
4177
|
-
|
|
4206
|
+
inertPaths: source.inertPaths,
|
|
4207
|
+
reasons: [source.unsubscribedPaths.length === 0 ? "all changed source paths have a declared owner (target subscription or impact.inert); impact scoped by repo-relative path globs" : `${source.unsubscribedPaths.length} changed source path(s) match no declared target subscription and affect no target`],
|
|
4178
4208
|
stampVersion: 3,
|
|
4179
4209
|
targets: source.targets,
|
|
4180
4210
|
unsubscribedPaths: source.unsubscribedPaths
|
|
4181
4211
|
};
|
|
4182
4212
|
const [baseLock, headLock] = sides.parsed;
|
|
4183
|
-
if (stableStringify$1(nonGraphSections(baseLock)) !== stableStringify$1(nonGraphSections(headLock))) return conservativeImpactStamp(targets, ["pnpm-lock.yaml delta touches sections outside importers/packages/snapshots (e.g. settings, overrides, patchedDependencies); fail closed to full impact"], source.unsubscribedPaths);
|
|
4213
|
+
if (stableStringify$1(nonGraphSections(baseLock)) !== stableStringify$1(nonGraphSections(headLock))) return conservativeImpactStamp(targets, ["pnpm-lock.yaml delta touches sections outside importers/packages/snapshots (e.g. settings, overrides, patchedDependencies); fail closed to full impact"], source.unsubscribedPaths, source.inertPaths);
|
|
4184
4214
|
const delta = {
|
|
4185
4215
|
changedImporters: changedSectionKeys(baseLock.importers, headLock.importers),
|
|
4186
4216
|
changedPackages: changedSectionKeys(baseLock.packages, headLock.packages),
|
|
@@ -4191,7 +4221,8 @@ const computeImpactStampOrThrow = ({ changedFiles, profilePath = "software-facto
|
|
|
4191
4221
|
const mixed = sourceFiles.length > 0;
|
|
4192
4222
|
return {
|
|
4193
4223
|
basis: "target-scoped",
|
|
4194
|
-
|
|
4224
|
+
inertPaths: source.inertPaths,
|
|
4225
|
+
reasons: mixed ? [source.unsubscribedPaths.length === 0 ? "all changed source paths have a declared owner (target subscription or impact.inert) and pnpm-lock.yaml is analyzable; impact is the union of path subscriptions and importer/snapshot graph analysis" : `${source.unsubscribedPaths.length} changed source path(s) match no declared target subscription; subscribed path impact is unioned with importer/snapshot graph analysis`] : ["pnpm-lock.yaml is the only changed file; impact scoped by importer/snapshot graph analysis"],
|
|
4195
4226
|
stampVersion: 3,
|
|
4196
4227
|
targets: mixed ? source.targets.map((sourceTarget, index) => unionTargetImpact(sourceTarget, lockfileTargets[index])) : lockfileTargets,
|
|
4197
4228
|
unsubscribedPaths: source.unsubscribedPaths
|
|
@@ -4455,6 +4486,16 @@ const assertPrVerifyProofRules = (proof, context) => {
|
|
|
4455
4486
|
message: "notRequiredCommands is a schemaVersion>=4 field; a v1–v3 pr:verify proof must not carry it.",
|
|
4456
4487
|
path: ["notRequiredCommands"]
|
|
4457
4488
|
});
|
|
4489
|
+
if (proof.schemaVersion < 4 && proof.notDemandedRecords !== void 0) context.addIssue({
|
|
4490
|
+
code: "custom",
|
|
4491
|
+
message: "notDemandedRecords is a schemaVersion>=4 field; a v1–v3 pr:verify proof must not carry it.",
|
|
4492
|
+
path: ["notDemandedRecords"]
|
|
4493
|
+
});
|
|
4494
|
+
if (proof.notDemandedRecords !== void 0 && proof.impactStamp === void 0) context.addIssue({
|
|
4495
|
+
code: "custom",
|
|
4496
|
+
message: "notDemandedRecords requires impactStamp; package-level not-demanded records are bound to the stamp identity.",
|
|
4497
|
+
path: ["notDemandedRecords"]
|
|
4498
|
+
});
|
|
4458
4499
|
assertBatteryCompleteness(proof, context);
|
|
4459
4500
|
assertNotRequiredStampAuthorization(proof, context);
|
|
4460
4501
|
};
|
|
@@ -4487,6 +4528,10 @@ const notRequiredCommandSchema = z.object({
|
|
|
4487
4528
|
impactTarget: z.string().min(1),
|
|
4488
4529
|
name: z.string().min(1)
|
|
4489
4530
|
});
|
|
4531
|
+
const notDemandedRecordSchema = z.object({
|
|
4532
|
+
basis: z.string().min(1),
|
|
4533
|
+
name: z.string().min(1)
|
|
4534
|
+
});
|
|
4490
4535
|
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(", ")}` });
|
|
4491
4536
|
const prVerifyProofSchema = z.object({
|
|
4492
4537
|
authoringSession: z.string().trim().min(1).optional(),
|
|
@@ -4518,6 +4563,7 @@ const prVerifyProofSchema = z.object({
|
|
|
4518
4563
|
"trivial",
|
|
4519
4564
|
"full"
|
|
4520
4565
|
]),
|
|
4566
|
+
notDemandedRecords: z.array(notDemandedRecordSchema).min(1).optional(),
|
|
4521
4567
|
notRequiredCommands: z.array(notRequiredCommandSchema).min(1).optional(),
|
|
4522
4568
|
outcome: z.enum(["aborted", "passed"]).optional(),
|
|
4523
4569
|
patchId: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
|
|
@@ -6004,6 +6050,84 @@ function createBoundaryCheckCommand(output, action) {
|
|
|
6004
6050
|
if (record.status !== "ready") throw new Error(`boundary:check refused: ${record.blockingReasons.join("; ")}`);
|
|
6005
6051
|
})));
|
|
6006
6052
|
}
|
|
6053
|
+
//#endregion
|
|
6054
|
+
//#region src/verification-run-context.ts
|
|
6055
|
+
const FACTORY_BASE_REF_ENV = "FACTORY_BASE_REF";
|
|
6056
|
+
const FACTORY_CHANGED_FILES_ENV = "FACTORY_CHANGED_FILES";
|
|
6057
|
+
const FACTORY_CHANGED_FILES_FILE_ENV = "FACTORY_CHANGED_FILES_FILE";
|
|
6058
|
+
const DEFAULT_CHANGED_FILES_INLINE_LIMIT_BYTES = 1e5;
|
|
6059
|
+
const EMPTY_TREE_OBJECT_HASH = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
6060
|
+
const DEFAULT_ROOT_SHARED_GLOBS = [
|
|
6061
|
+
"pnpm-lock.yaml",
|
|
6062
|
+
"package-lock.json",
|
|
6063
|
+
"yarn.lock",
|
|
6064
|
+
"bun.lockb",
|
|
6065
|
+
"pnpm-workspace.yaml",
|
|
6066
|
+
"package.json",
|
|
6067
|
+
"tsconfig*.json",
|
|
6068
|
+
"*.config.*",
|
|
6069
|
+
".npmrc",
|
|
6070
|
+
".nvmrc"
|
|
6071
|
+
];
|
|
6072
|
+
function fullSuiteReasonForFile(file) {
|
|
6073
|
+
return `${file}: root/shared change forces full suite`;
|
|
6074
|
+
}
|
|
6075
|
+
function noopCleanup() {}
|
|
6076
|
+
function createVerificationRunContext(input) {
|
|
6077
|
+
const rootSharedGlobs = input.rootSharedGlobs ?? DEFAULT_ROOT_SHARED_GLOBS;
|
|
6078
|
+
const fullSuiteBaseRef = input.fullSuiteBaseRef ?? "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
6079
|
+
const inlineLimitBytes = input.inlineLimitBytes ?? DEFAULT_CHANGED_FILES_INLINE_LIMIT_BYTES;
|
|
6080
|
+
const tmpDir = input.tmpDir ?? os.tmpdir();
|
|
6081
|
+
const matchingFiles = input.changedFiles.filter((file) => matchesAnyGlob(rootSharedGlobs, file));
|
|
6082
|
+
const forcesFullSuite = matchingFiles.length > 0 || input.changedFiles.length === 0;
|
|
6083
|
+
const fullSuiteReasons = input.changedFiles.length === 0 ? ["no changed files detected; forcing full suite"] : matchingFiles.map(fullSuiteReasonForFile);
|
|
6084
|
+
return {
|
|
6085
|
+
baseRef: input.baseRef,
|
|
6086
|
+
buildEnv: () => {
|
|
6087
|
+
const payload = input.changedFiles.join("\n");
|
|
6088
|
+
const env = {
|
|
6089
|
+
[FACTORY_BASE_REF_ENV]: forcesFullSuite ? fullSuiteBaseRef : input.baseRef,
|
|
6090
|
+
[FACTORY_CHANGED_FILES_ENV]: "",
|
|
6091
|
+
[FACTORY_CHANGED_FILES_FILE_ENV]: ""
|
|
6092
|
+
};
|
|
6093
|
+
if (Buffer.byteLength(payload, "utf-8") > inlineLimitBytes) {
|
|
6094
|
+
const dir = mkdtempSync(path.join(tmpDir, "factory-changed-files-"));
|
|
6095
|
+
const filePath = path.join(dir, "changed-files.txt");
|
|
6096
|
+
writeFileSync(filePath, `${payload}\n`, "utf-8");
|
|
6097
|
+
env[FACTORY_CHANGED_FILES_FILE_ENV] = filePath;
|
|
6098
|
+
return {
|
|
6099
|
+
cleanup: () => {
|
|
6100
|
+
rmSync(dir, {
|
|
6101
|
+
force: true,
|
|
6102
|
+
recursive: true
|
|
6103
|
+
});
|
|
6104
|
+
},
|
|
6105
|
+
env
|
|
6106
|
+
};
|
|
6107
|
+
}
|
|
6108
|
+
env[FACTORY_CHANGED_FILES_ENV] = payload;
|
|
6109
|
+
return {
|
|
6110
|
+
cleanup: noopCleanup,
|
|
6111
|
+
env
|
|
6112
|
+
};
|
|
6113
|
+
},
|
|
6114
|
+
changedFiles: input.changedFiles,
|
|
6115
|
+
forcesFullSuite,
|
|
6116
|
+
fullSuiteReasons,
|
|
6117
|
+
runSummaryLines: () => {
|
|
6118
|
+
const lines = [];
|
|
6119
|
+
if (input.changedFiles.length > 0) {
|
|
6120
|
+
lines.push("Changed files considered by pr:verify:");
|
|
6121
|
+
for (const file of input.changedFiles) lines.push(`- ${file}`);
|
|
6122
|
+
} else lines.push("No changed files detected.");
|
|
6123
|
+
if (forcesFullSuite) {
|
|
6124
|
+
lines.push("Forcing full verification suite:");
|
|
6125
|
+
for (const reason of fullSuiteReasons) lines.push(`- ${reason}`);
|
|
6126
|
+
}
|
|
6127
|
+
return lines;
|
|
6128
|
+
}
|
|
6129
|
+
};
|
|
6130
|
+
}
|
|
6007
6131
|
const COMMIT_SHA_PATTERN$1 = /^[0-9a-f]{40}$/iu;
|
|
6008
6132
|
const ZERO_SHA_PATTERN$1 = /^0{40}$/u;
|
|
6009
6133
|
const demandedTargets$1 = (profile, reason) => (profile?.impact?.targets ?? []).map(({ name }) => ({
|
|
@@ -6020,6 +6144,7 @@ const refusal$1 = ({ base, head, profile, reason }) => ({
|
|
|
6020
6144
|
},
|
|
6021
6145
|
decision: "refused",
|
|
6022
6146
|
...head ? { headSha: head } : {},
|
|
6147
|
+
inertPaths: [],
|
|
6023
6148
|
schemaVersion: 1,
|
|
6024
6149
|
targets: demandedTargets$1(profile, reason),
|
|
6025
6150
|
unsubscribedPaths: []
|
|
@@ -6056,13 +6181,23 @@ const assertReachableCommit$1 = (cwd, sha, name) => {
|
|
|
6056
6181
|
throw new Error(`${name} commit ${sha} is not reachable in this checkout`);
|
|
6057
6182
|
}
|
|
6058
6183
|
};
|
|
6184
|
+
const githubOutputAssignment = (name, value) => {
|
|
6185
|
+
if (value.includes("\n") || value.includes("\r")) {
|
|
6186
|
+
const delimiter = `ghadelim_${randomBytes(8).toString("hex")}`;
|
|
6187
|
+
return `${name}<<${delimiter}\n${value}\n${delimiter}`;
|
|
6188
|
+
}
|
|
6189
|
+
return `${name}=${value}`;
|
|
6190
|
+
};
|
|
6059
6191
|
const writeGithubOutputs$1 = (outputPath, decision) => {
|
|
6060
|
-
|
|
6192
|
+
const lines = [
|
|
6061
6193
|
`decision=${decision.decision}`,
|
|
6062
6194
|
`basis=${JSON.stringify(decision.basis)}`,
|
|
6195
|
+
`inert_paths=${JSON.stringify(decision.inertPaths)}`,
|
|
6063
6196
|
`unsubscribed_paths=${JSON.stringify(decision.unsubscribedPaths)}`,
|
|
6064
6197
|
...decision.targets.map((target) => `${productionImpactTargetOutput(target.name)}=${target.state}`)
|
|
6065
|
-
]
|
|
6198
|
+
];
|
|
6199
|
+
if (decision.runContext) lines.push(githubOutputAssignment("factory_base_ref", decision.runContext.FACTORY_BASE_REF), githubOutputAssignment("factory_changed_files", decision.runContext.FACTORY_CHANGED_FILES), githubOutputAssignment("factory_changed_files_file", decision.runContext.FACTORY_CHANGED_FILES_FILE), githubOutputAssignment("merge_base_sha", decision.runContext.mergeBaseSha ?? ""));
|
|
6200
|
+
appendFileSync(outputPath, `${lines.join("\n")}\n`, "utf-8");
|
|
6066
6201
|
};
|
|
6067
6202
|
/**
|
|
6068
6203
|
* Classify candidate impact from one exact base/head identity. The delta is
|
|
@@ -6076,6 +6211,7 @@ const runCandidateImpact = (args) => {
|
|
|
6076
6211
|
const cwd = path.resolve(args.cwd ?? process.cwd());
|
|
6077
6212
|
let profile;
|
|
6078
6213
|
let profilePath;
|
|
6214
|
+
let relativeProfile;
|
|
6079
6215
|
let decision;
|
|
6080
6216
|
try {
|
|
6081
6217
|
const { path: loadedPath, profile: loadedProfile } = loadProjectProfile({
|
|
@@ -6084,6 +6220,7 @@ const runCandidateImpact = (args) => {
|
|
|
6084
6220
|
});
|
|
6085
6221
|
profile = loadedProfile;
|
|
6086
6222
|
profilePath = loadedPath;
|
|
6223
|
+
relativeProfile = path.relative(cwd, profilePath).split(path.sep).join("/");
|
|
6087
6224
|
const base = assertCommitIdentity$1(args.base, "base");
|
|
6088
6225
|
const head = assertCommitIdentity$1(args.head, "head");
|
|
6089
6226
|
assertReachableCommit$1(cwd, base, "base");
|
|
@@ -6123,6 +6260,7 @@ const runCandidateImpact = (args) => {
|
|
|
6123
6260
|
},
|
|
6124
6261
|
decision: usable ? "usable" : "refused",
|
|
6125
6262
|
headSha: head,
|
|
6263
|
+
inertPaths: stamp.inertPaths,
|
|
6126
6264
|
mergeBaseSha: mergeBase,
|
|
6127
6265
|
schemaVersion: 1,
|
|
6128
6266
|
targets: stamp.targets.map((target) => ({
|
|
@@ -6140,6 +6278,29 @@ const runCandidateImpact = (args) => {
|
|
|
6140
6278
|
reason: `candidate impact classification refused: ${error instanceof Error ? error.message : String(error)}`
|
|
6141
6279
|
});
|
|
6142
6280
|
}
|
|
6281
|
+
if (args.emitRunContext) {
|
|
6282
|
+
const fullSuiteBaseRef = emptyTreeHash(cwd);
|
|
6283
|
+
const context = createVerificationRunContext({
|
|
6284
|
+
baseRef: decision.mergeBaseSha ?? fullSuiteBaseRef,
|
|
6285
|
+
changedFiles: decision.basis.changedFiles,
|
|
6286
|
+
fullSuiteBaseRef,
|
|
6287
|
+
rootSharedGlobs: relativeProfile ? [...DEFAULT_ROOT_SHARED_GLOBS, relativeProfile] : [...DEFAULT_ROOT_SHARED_GLOBS]
|
|
6288
|
+
});
|
|
6289
|
+
const { env } = context.buildEnv();
|
|
6290
|
+
decision = {
|
|
6291
|
+
...decision,
|
|
6292
|
+
runContext: {
|
|
6293
|
+
FACTORY_BASE_REF: env["FACTORY_BASE_REF"] ?? "",
|
|
6294
|
+
FACTORY_CHANGED_FILES: env["FACTORY_CHANGED_FILES"] ?? "",
|
|
6295
|
+
FACTORY_CHANGED_FILES_FILE: env["FACTORY_CHANGED_FILES_FILE"] ?? "",
|
|
6296
|
+
baseRef: context.baseRef,
|
|
6297
|
+
changedFiles: context.changedFiles,
|
|
6298
|
+
forcesFullSuite: context.forcesFullSuite,
|
|
6299
|
+
fullSuiteReasons: context.fullSuiteReasons,
|
|
6300
|
+
...decision.mergeBaseSha ? { mergeBaseSha: decision.mergeBaseSha } : {}
|
|
6301
|
+
}
|
|
6302
|
+
};
|
|
6303
|
+
}
|
|
6143
6304
|
if (args.githubOutput) writeGithubOutputs$1(path.resolve(cwd, args.githubOutput), decision);
|
|
6144
6305
|
if (args.githubSummary) appendFileSync(path.resolve(cwd, args.githubSummary), `### Candidate impact\n\n${candidateImpactSummaryLines(decision).join("\n")}\n`, "utf-8");
|
|
6145
6306
|
return decision;
|
|
@@ -6147,22 +6308,36 @@ const runCandidateImpact = (args) => {
|
|
|
6147
6308
|
const candidateImpactSummaryLines = (decision) => decision.basis.classification === "refused" ? [
|
|
6148
6309
|
"Candidate impact: refused; all routed work remains demanded.",
|
|
6149
6310
|
...decision.basis.reasons,
|
|
6311
|
+
inertPathsLine(void 0, "classification was refused"),
|
|
6150
6312
|
unsubscribedPathsLine(void 0, "classification was refused")
|
|
6151
|
-
] : [
|
|
6313
|
+
] : [
|
|
6314
|
+
`Candidate impact: ${decision.basis.classification}; ${decision.targets.filter(({ state }) => state === "demanded").length}/${decision.targets.length} target(s) demanded.`,
|
|
6315
|
+
inertPathsLine(decision.inertPaths),
|
|
6316
|
+
unsubscribedPathsLine(decision.unsubscribedPaths)
|
|
6317
|
+
];
|
|
6152
6318
|
//#endregion
|
|
6153
6319
|
//#region src/commands/candidate-impact.ts
|
|
6154
6320
|
function createCandidateImpactCommand(output) {
|
|
6155
|
-
return markCwdOptionDefault(new Command("candidate:impact").description("Classify candidate targets from an exact pull-request base/head identity").option("--base <sha>", "candidate base commit SHA").option("--head <sha>", "candidate head commit SHA").option("--cwd <path>", "repository working directory", collectCwdOption).option("--profile <path>", "path to the project profile JSON file").option("--github-output <path>", "append stable decision outputs to a GitHub Actions output file").option("--github-summary <path>", "append a human-readable decision to a GitHub Actions summary file").option("--json", "print the complete decision as JSON").action((options) => {
|
|
6321
|
+
return markCwdOptionDefault(new Command("candidate:impact").description("Classify candidate targets from an exact pull-request base/head identity").option("--base <sha>", "candidate base commit SHA").option("--head <sha>", "candidate head commit SHA").option("--cwd <path>", "repository working directory", collectCwdOption).option("--profile <path>", "path to the project profile JSON file").option("--github-output <path>", "append stable decision outputs to a GitHub Actions output file").option("--github-summary <path>", "append a human-readable decision to a GitHub Actions summary file").option("--json", "print the complete decision as JSON").option("--emit-run-context", "emit the FACTORY_BASE_REF / FACTORY_CHANGED_FILES run-context contract").action((options) => {
|
|
6156
6322
|
const decision = runCandidateImpact({
|
|
6157
6323
|
base: options.base,
|
|
6158
6324
|
cwd: resolveCwdOption(options.cwd),
|
|
6325
|
+
emitRunContext: options.emitRunContext,
|
|
6159
6326
|
githubOutput: options.githubOutput,
|
|
6160
6327
|
githubSummary: options.githubSummary,
|
|
6161
6328
|
head: options.head,
|
|
6162
6329
|
profilePath: options.profile
|
|
6163
6330
|
});
|
|
6164
6331
|
if (options.json) output.stdout.write(`${JSON.stringify(decision, null, 2)}\n`);
|
|
6165
|
-
else
|
|
6332
|
+
else if (options.emitRunContext) {
|
|
6333
|
+
const context = decision.runContext;
|
|
6334
|
+
if (context === void 0) throw new Error("--emit-run-context produced no run context; refusing to print an empty contract");
|
|
6335
|
+
output.stdout.write(`${[
|
|
6336
|
+
`${FACTORY_BASE_REF_ENV}=${context.FACTORY_BASE_REF}`,
|
|
6337
|
+
`${FACTORY_CHANGED_FILES_ENV}=${context.FACTORY_CHANGED_FILES}`,
|
|
6338
|
+
`${FACTORY_CHANGED_FILES_FILE_ENV}=${context.FACTORY_CHANGED_FILES_FILE}`
|
|
6339
|
+
].join("\n")}\n`);
|
|
6340
|
+
} else output.stdout.write(`${candidateImpactSummaryLines(decision).join("\n")}\n`);
|
|
6166
6341
|
}));
|
|
6167
6342
|
}
|
|
6168
6343
|
/** `tool` discriminator every emitted report carries. */
|
|
@@ -9573,25 +9748,49 @@ function createFactoryCheckPublisher(output, dependencies = {}) {
|
|
|
9573
9748
|
}
|
|
9574
9749
|
//#endregion
|
|
9575
9750
|
//#region src/pr-classification.ts
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
if (
|
|
9751
|
+
const MISSING_STAMP_REASON = "impact stamp is missing; fail closed to non-trivial";
|
|
9752
|
+
function ownershipFromStamp(stamp, file) {
|
|
9753
|
+
if (stamp.inertPaths.includes(file)) return { kind: "inert" };
|
|
9754
|
+
if (stamp.unsubscribedPaths.includes(file)) return { kind: "unowned" };
|
|
9755
|
+
const named = stamp.targets.find((target) => target.impact === "affected" && target.basis.includes(`"${file}"`));
|
|
9756
|
+
if (named) return {
|
|
9757
|
+
kind: "target",
|
|
9758
|
+
target: named.name
|
|
9759
|
+
};
|
|
9760
|
+
const affected = stamp.targets.find((target) => target.impact === "affected");
|
|
9761
|
+
if (affected) return {
|
|
9580
9762
|
kind: "target",
|
|
9581
|
-
target:
|
|
9763
|
+
target: affected.name
|
|
9582
9764
|
};
|
|
9583
|
-
if (matchesAnyGlob(profile.impact?.inert ?? [], file)) return { kind: "inert" };
|
|
9584
9765
|
return { kind: "unowned" };
|
|
9585
9766
|
}
|
|
9586
9767
|
function reasonForOwnership(file, ownership) {
|
|
9587
9768
|
switch (ownership.kind) {
|
|
9588
|
-
case "
|
|
9589
|
-
case "inert": return `${file}: repository-inert ownership (impact.inert)`;
|
|
9769
|
+
case "inert": return `${file}: repository-inert ownership (stamp.inertPaths)`;
|
|
9590
9770
|
case "target": return `${file}: affects product target "${ownership.target}"`;
|
|
9591
9771
|
default: return `${file}: no declared owner; fail closed to non-trivial`;
|
|
9592
9772
|
}
|
|
9593
9773
|
}
|
|
9594
|
-
function
|
|
9774
|
+
function relativeProfilePath(options) {
|
|
9775
|
+
return path.relative(options.cwd, options.profilePath).split(path.sep).join("/");
|
|
9776
|
+
}
|
|
9777
|
+
function applyProfileEditGuard(files, relativeProfile, profilePath, base) {
|
|
9778
|
+
if (!files.some((file) => file === relativeProfile || file === profilePath)) return base;
|
|
9779
|
+
return {
|
|
9780
|
+
classification: "non-trivial",
|
|
9781
|
+
reasons: [...base.reasons, `${relativeProfile}: edits the ownership declarations; forced non-trivial`]
|
|
9782
|
+
};
|
|
9783
|
+
}
|
|
9784
|
+
/**
|
|
9785
|
+
* Path ownership as the stamp recorded it. A missing stamp fails closed.
|
|
9786
|
+
* Does not glob, and does not refuse the docs rung on a conservative basis —
|
|
9787
|
+
* that gate belongs to {@link classifyDiffForRunWithStamp}.
|
|
9788
|
+
*/
|
|
9789
|
+
function classifyDiff(files, stamp) {
|
|
9790
|
+
if (stamp === void 0) return {
|
|
9791
|
+
classification: "non-trivial",
|
|
9792
|
+
reasons: [MISSING_STAMP_REASON]
|
|
9793
|
+
};
|
|
9595
9794
|
const sortedFiles = [...files].toSorted();
|
|
9596
9795
|
if (sortedFiles.length === 0) return {
|
|
9597
9796
|
classification: "non-trivial",
|
|
@@ -9599,7 +9798,7 @@ function classifyDiff(profile, files) {
|
|
|
9599
9798
|
};
|
|
9600
9799
|
const ownerships = sortedFiles.map((file) => ({
|
|
9601
9800
|
file,
|
|
9602
|
-
ownership:
|
|
9801
|
+
ownership: ownershipFromStamp(stamp, file)
|
|
9603
9802
|
}));
|
|
9604
9803
|
const reasons = ownerships.map(({ file, ownership }) => reasonForOwnership(file, ownership));
|
|
9605
9804
|
if (ownerships.every(({ ownership }) => ownership.kind === "inert")) return {
|
|
@@ -9620,14 +9819,13 @@ function classifyDiff(profile, files) {
|
|
|
9620
9819
|
* fail-closed stamp can never coexist with a reduced verification battery
|
|
9621
9820
|
* (#742 review cycle 3).
|
|
9622
9821
|
*
|
|
9623
|
-
*
|
|
9624
|
-
* stamp
|
|
9625
|
-
*
|
|
9626
|
-
* already refuses through the graph-input rule.
|
|
9822
|
+
* Ownership and reasons come from `stamp.targets`, `stamp.inertPaths`, and
|
|
9823
|
+
* `stamp.unsubscribedPaths`. A missing stamp fails closed; this function
|
|
9824
|
+
* never evaluates profile globs.
|
|
9627
9825
|
*/
|
|
9628
9826
|
function classifyDiffForRunWithStamp(profile, files, stamp, options) {
|
|
9629
|
-
const base =
|
|
9630
|
-
if (base.classification === "non-trivial") return base;
|
|
9827
|
+
const base = applyProfileEditGuard(files, relativeProfilePath(options), options.profilePath, classifyDiff(files, stamp));
|
|
9828
|
+
if (stamp === void 0 || base.classification === "non-trivial") return base;
|
|
9631
9829
|
if (stamp.basis !== "target-scoped") return {
|
|
9632
9830
|
classification: "non-trivial",
|
|
9633
9831
|
reasons: [...base.reasons, "impact stamp is conservative; the docs/process-only rung needs a target-scoped stamp with zero affected targets"]
|
|
@@ -9639,15 +9837,20 @@ function classifyDiffForRunWithStamp(profile, files, stamp, options) {
|
|
|
9639
9837
|
};
|
|
9640
9838
|
return base;
|
|
9641
9839
|
}
|
|
9840
|
+
/**
|
|
9841
|
+
* Path predicate for consumers that do not already hold a stamp (pr-ready's
|
|
9842
|
+
* docs-only delta). Computes one stamp through {@link computeImpactStamp} —
|
|
9843
|
+
* the only glob-evaluation site — then reads ownership from it.
|
|
9844
|
+
*/
|
|
9642
9845
|
function classifyDiffForRun(profile, files, options) {
|
|
9643
|
-
const relativeProfile =
|
|
9644
|
-
const
|
|
9645
|
-
|
|
9646
|
-
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
};
|
|
9650
|
-
return
|
|
9846
|
+
const relativeProfile = relativeProfilePath(options);
|
|
9847
|
+
const stamp = computeImpactStamp({
|
|
9848
|
+
changedFiles: files,
|
|
9849
|
+
profile,
|
|
9850
|
+
profilePath: relativeProfile,
|
|
9851
|
+
readLockfile: () => void 0
|
|
9852
|
+
});
|
|
9853
|
+
return applyProfileEditGuard(files, relativeProfile, options.profilePath, classifyDiff(files, stamp));
|
|
9651
9854
|
}
|
|
9652
9855
|
//#endregion
|
|
9653
9856
|
//#region src/pr-readiness/verification-proof.ts
|
|
@@ -9855,83 +10058,6 @@ const verificationProofBlockingReason = (state) => {
|
|
|
9855
10058
|
}
|
|
9856
10059
|
};
|
|
9857
10060
|
//#endregion
|
|
9858
|
-
//#region src/verification-run-context.ts
|
|
9859
|
-
const FACTORY_BASE_REF_ENV = "FACTORY_BASE_REF";
|
|
9860
|
-
const FACTORY_CHANGED_FILES_ENV = "FACTORY_CHANGED_FILES";
|
|
9861
|
-
const FACTORY_CHANGED_FILES_FILE_ENV = "FACTORY_CHANGED_FILES_FILE";
|
|
9862
|
-
const DEFAULT_CHANGED_FILES_INLINE_LIMIT_BYTES = 1e5;
|
|
9863
|
-
const DEFAULT_ROOT_SHARED_GLOBS = [
|
|
9864
|
-
"pnpm-lock.yaml",
|
|
9865
|
-
"package-lock.json",
|
|
9866
|
-
"yarn.lock",
|
|
9867
|
-
"bun.lockb",
|
|
9868
|
-
"pnpm-workspace.yaml",
|
|
9869
|
-
"package.json",
|
|
9870
|
-
"tsconfig*.json",
|
|
9871
|
-
"*.config.*",
|
|
9872
|
-
".npmrc",
|
|
9873
|
-
".nvmrc"
|
|
9874
|
-
];
|
|
9875
|
-
function fullSuiteReasonForFile(file) {
|
|
9876
|
-
return `${file}: root/shared change forces full suite`;
|
|
9877
|
-
}
|
|
9878
|
-
function noopCleanup() {}
|
|
9879
|
-
function createVerificationRunContext(input) {
|
|
9880
|
-
const rootSharedGlobs = input.rootSharedGlobs ?? DEFAULT_ROOT_SHARED_GLOBS;
|
|
9881
|
-
const fullSuiteBaseRef = input.fullSuiteBaseRef ?? "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
9882
|
-
const inlineLimitBytes = input.inlineLimitBytes ?? DEFAULT_CHANGED_FILES_INLINE_LIMIT_BYTES;
|
|
9883
|
-
const tmpDir = input.tmpDir ?? os.tmpdir();
|
|
9884
|
-
const matchingFiles = input.changedFiles.filter((file) => matchesAnyGlob(rootSharedGlobs, file));
|
|
9885
|
-
const forcesFullSuite = matchingFiles.length > 0 || input.changedFiles.length === 0;
|
|
9886
|
-
const fullSuiteReasons = input.changedFiles.length === 0 ? ["no changed files detected; forcing full suite"] : matchingFiles.map(fullSuiteReasonForFile);
|
|
9887
|
-
return {
|
|
9888
|
-
baseRef: input.baseRef,
|
|
9889
|
-
buildEnv: () => {
|
|
9890
|
-
const payload = input.changedFiles.join("\n");
|
|
9891
|
-
const env = {
|
|
9892
|
-
[FACTORY_BASE_REF_ENV]: forcesFullSuite ? fullSuiteBaseRef : input.baseRef,
|
|
9893
|
-
[FACTORY_CHANGED_FILES_ENV]: "",
|
|
9894
|
-
[FACTORY_CHANGED_FILES_FILE_ENV]: ""
|
|
9895
|
-
};
|
|
9896
|
-
if (Buffer.byteLength(payload, "utf-8") > inlineLimitBytes) {
|
|
9897
|
-
const dir = mkdtempSync(path.join(tmpDir, "factory-changed-files-"));
|
|
9898
|
-
const filePath = path.join(dir, "changed-files.txt");
|
|
9899
|
-
writeFileSync(filePath, `${payload}\n`, "utf-8");
|
|
9900
|
-
env[FACTORY_CHANGED_FILES_FILE_ENV] = filePath;
|
|
9901
|
-
return {
|
|
9902
|
-
cleanup: () => {
|
|
9903
|
-
rmSync(dir, {
|
|
9904
|
-
force: true,
|
|
9905
|
-
recursive: true
|
|
9906
|
-
});
|
|
9907
|
-
},
|
|
9908
|
-
env
|
|
9909
|
-
};
|
|
9910
|
-
}
|
|
9911
|
-
env[FACTORY_CHANGED_FILES_ENV] = payload;
|
|
9912
|
-
return {
|
|
9913
|
-
cleanup: noopCleanup,
|
|
9914
|
-
env
|
|
9915
|
-
};
|
|
9916
|
-
},
|
|
9917
|
-
changedFiles: input.changedFiles,
|
|
9918
|
-
forcesFullSuite,
|
|
9919
|
-
fullSuiteReasons,
|
|
9920
|
-
runSummaryLines: () => {
|
|
9921
|
-
const lines = [];
|
|
9922
|
-
if (input.changedFiles.length > 0) {
|
|
9923
|
-
lines.push("Changed files considered by pr:verify:");
|
|
9924
|
-
for (const file of input.changedFiles) lines.push(`- ${file}`);
|
|
9925
|
-
} else lines.push("No changed files detected.");
|
|
9926
|
-
if (forcesFullSuite) {
|
|
9927
|
-
lines.push("Forcing full verification suite:");
|
|
9928
|
-
for (const reason of fullSuiteReasons) lines.push(`- ${reason}`);
|
|
9929
|
-
}
|
|
9930
|
-
return lines;
|
|
9931
|
-
}
|
|
9932
|
-
};
|
|
9933
|
-
}
|
|
9934
|
-
//#endregion
|
|
9935
10061
|
//#region src/workspace-install-resolution.ts
|
|
9936
10062
|
const readJson = (filePath, read) => {
|
|
9937
10063
|
if (!existsSync(filePath)) return null;
|
|
@@ -10178,7 +10304,6 @@ function formatVerifyTimingSummary(proof) {
|
|
|
10178
10304
|
function resolveMode({ classification, mode }) {
|
|
10179
10305
|
if (mode === "auto") {
|
|
10180
10306
|
if (classification === "docs/process-only") return "docs-only";
|
|
10181
|
-
if (classification === "trivial") return "trivial";
|
|
10182
10307
|
return "full";
|
|
10183
10308
|
}
|
|
10184
10309
|
return mode;
|
|
@@ -12938,7 +13063,6 @@ var pr_body_renderer_exports = /* @__PURE__ */ __exportAll({
|
|
|
12938
13063
|
});
|
|
12939
13064
|
const commandFor = (proof) => {
|
|
12940
13065
|
if (proof.mode === "docs-only") return "patronage-factory pr:verify --docs-only";
|
|
12941
|
-
if (proof.mode === "trivial") return "patronage-factory pr:verify --trivial";
|
|
12942
13066
|
return "patronage-factory pr:verify";
|
|
12943
13067
|
};
|
|
12944
13068
|
const renderPrBodySectionParts = ({ reviewProof, verifyProof }) => {
|
|
@@ -15921,17 +16045,13 @@ function createPrReviewCommand(output, action) {
|
|
|
15921
16045
|
//#endregion
|
|
15922
16046
|
//#region src/commands/pr-verify.ts
|
|
15923
16047
|
function createPrVerifyCommand(output, action = runPrVerify) {
|
|
15924
|
-
return markCwdOptionDefault(new Command("pr:verify").description("Run project-profile verification commands and write typed proof").option("--base <ref>", "base branch or ref", "origin/main").option("--cwd <path>", "working directory to verify", collectCwdOption).option("--docs-only", "run the docs/process verification gate").option("--
|
|
16048
|
+
return markCwdOptionDefault(new Command("pr:verify").description("Run project-profile verification commands and write typed proof").option("--base <ref>", "base branch or ref", "origin/main").option("--cwd <path>", "working directory to verify", collectCwdOption).option("--docs-only", "run the docs/process verification gate").option("--full", "run the full verification gate").option("--authoring-session <id>", "explicit authoring session identity (required for review evidence)").option("--json", "print the proof as JSON after verification").option("--output <path>", "write proof JSON to a file").option("--profile <path>", "path to the project profile JSON file").option("--no-status", "skip posting the patronage-factory/pr-verify commit status").action(withGateTiming({
|
|
15925
16049
|
gate: "pr:verify",
|
|
15926
16050
|
resolveLedgerRoot: (options) => resolveCwdOption(options.cwd),
|
|
15927
16051
|
stderr: output.stderr
|
|
15928
16052
|
}, (options) => {
|
|
15929
16053
|
assertForegroundGate({ gate: "pr:verify" });
|
|
15930
|
-
if ([
|
|
15931
|
-
options.docsOnly,
|
|
15932
|
-
options.trivial,
|
|
15933
|
-
options.full
|
|
15934
|
-
].filter(Boolean).length > 1) throw new Error("Use at most one of --docs-only, --trivial, or --full");
|
|
16054
|
+
if ([options.docsOnly, options.full].filter(Boolean).length > 1) throw new Error("Use at most one of --docs-only or --full");
|
|
15935
16055
|
const dependencies = options.status === false ? {} : { publishCheckRun: createFactoryCheckPublisher(output) };
|
|
15936
16056
|
const proof = action({
|
|
15937
16057
|
authoringSession: options.authoringSession,
|
|
@@ -15947,7 +16067,6 @@ function createPrVerifyCommand(output, action = runPrVerify) {
|
|
|
15947
16067
|
}
|
|
15948
16068
|
function modeFor(options) {
|
|
15949
16069
|
if (options.docsOnly) return "docs-only";
|
|
15950
|
-
if (options.trivial) return "trivial";
|
|
15951
16070
|
if (options.full) return "full";
|
|
15952
16071
|
return "auto";
|
|
15953
16072
|
}
|
|
@@ -16412,4 +16531,4 @@ if (isDirectCliExecution()) try {
|
|
|
16412
16531
|
process.exit(1);
|
|
16413
16532
|
}
|
|
16414
16533
|
//#endregion
|
|
16415
|
-
export { DEFAULT_DEMAND_WAIVER_PATH, DEFAULT_FACTORY_REPOSITORY, DemandWaiveRefusalError, EPIC_STRUCTURE_NODE_STATUSES, EPIC_STRUCTURE_SCHEMA_VERSION, EpicStructureValidationError, FactoryCliInvocationSchema, FollowUpActionSchema, MERGE_FREEZE_APP_SLUG, MERGE_FREEZE_CHECK_NAME, PrPublishFollowUpError, REVIEW_FOCUS_SECTION, WorkerCheckoutGuardError, appendReviewLadderStageTraceEvent, appendWithTraceSinks, applyDemandWaiver, assembleReviewPrompt, assertWorkerCheckoutAllowed, authorizeDemandWaiver, batteryScopeSummaryLines, blockingLadderFindings, boundary_manifest_exports as boundaryManifest, boundary_review_proof_exports as boundaryReviewProof, buildEpicStructureEvent, buildEpicStructurePayload, buildEvidenceEnvelope, buildReviewGateNotRequiredTraceEvent, buildReviewLadderStageTraceEvent, comment_provenance_exports as commentProvenance, createLocalJsonlTraceSink, createProgram, doctorProjectProfile, epicStructureEventId, evaluateReviewLadder, evidenceEnvelopeFilename, findingKey, followUpFromArgv, impactStampScopeDecision, inferFixedInThreadDispositions, isProductionHqUrl, loadProjectProfile, normalizeIssueComments, openLadderFindings, planPreviewLifecycle, planPublishFollowUp, planVerificationBattery, readiness_evaluation_exports as prReadinessEvaluation, external_evidence_exports as prReadinessExternalEvidence, pr_body_renderer_exports as prReadinessPrBodyRenderer, proof_identity_exports as prReadinessProofIdentity, review_proof_exports as prReadinessReviewProof, status_check_rollup_exports as prReadinessStatusChecks, verification_proof_exports as prReadinessVerificationProof, previewLifecycleTargets, publishEpicStructure, readDemandWaivers, readPrReadyProof, readPrReviewProof, resolveFactoryRepository, resolveFindingBlocking, resolveReviewFindingCategory, resolveReviewFindingSeverity, resolveReviewLadderPolicy, resolveTraceWriteSinks, reviewCycleStateFor, reviewFocusFromIssueBody, reviewPromptSectionSchema, reviewPromptSectionsSchema, run, runBoundaryCheck, runDemandWaive, runEvidenceEmit, runPrPublish, runPrReady, runPrReview, runPrVerify, scanFactoryTraceDiagnostics, scanFactoryTraceEvents, selectWaiversForCandidate, staleRepeatLadderFindings, toFactoryTraceEnvelope, tryAppendReviewGateNotRequiredTraceEvent, tryAppendReviewLadderStageTraceEvent, validateBoundaryCheckProof, validateDagDocument, validateDemandWaiverStore, validateFactoryTraceEvent, validateMergeFreezeState, validatePrReadyProof, validatePrReviewProof, validatePrVerifyProof, waivedDemandNotice, waivedDemandSchema, worktree_scratch_files_exports as worktreeScratchFiles };
|
|
16534
|
+
export { DEFAULT_DEMAND_WAIVER_PATH, DEFAULT_FACTORY_REPOSITORY, DEFAULT_ROOT_SHARED_GLOBS, DemandWaiveRefusalError, EMPTY_TREE_OBJECT_HASH, EPIC_STRUCTURE_NODE_STATUSES, EPIC_STRUCTURE_SCHEMA_VERSION, EpicStructureValidationError, FACTORY_BASE_REF_ENV, FACTORY_CHANGED_FILES_ENV, FACTORY_CHANGED_FILES_FILE_ENV, FactoryCliInvocationSchema, FollowUpActionSchema, MERGE_FREEZE_APP_SLUG, MERGE_FREEZE_CHECK_NAME, PrPublishFollowUpError, REVIEW_FOCUS_SECTION, WorkerCheckoutGuardError, appendReviewLadderStageTraceEvent, appendWithTraceSinks, applyDemandWaiver, assembleReviewPrompt, assertWorkerCheckoutAllowed, authorizeDemandWaiver, batteryScopeSummaryLines, blockingLadderFindings, boundary_manifest_exports as boundaryManifest, boundary_review_proof_exports as boundaryReviewProof, buildEpicStructureEvent, buildEpicStructurePayload, buildEvidenceEnvelope, buildReviewGateNotRequiredTraceEvent, buildReviewLadderStageTraceEvent, comment_provenance_exports as commentProvenance, createLocalJsonlTraceSink, createProgram, createVerificationRunContext, doctorProjectProfile, epicStructureEventId, evaluateReviewLadder, evidenceEnvelopeFilename, findingKey, followUpFromArgv, impactStampScopeDecision, inferFixedInThreadDispositions, isProductionHqUrl, loadProjectProfile, normalizeIssueComments, openLadderFindings, planPreviewLifecycle, planPublishFollowUp, planVerificationBattery, readiness_evaluation_exports as prReadinessEvaluation, external_evidence_exports as prReadinessExternalEvidence, pr_body_renderer_exports as prReadinessPrBodyRenderer, proof_identity_exports as prReadinessProofIdentity, review_proof_exports as prReadinessReviewProof, status_check_rollup_exports as prReadinessStatusChecks, verification_proof_exports as prReadinessVerificationProof, previewLifecycleTargets, publishEpicStructure, readDemandWaivers, readPrReadyProof, readPrReviewProof, resolveFactoryRepository, resolveFindingBlocking, resolveReviewFindingCategory, resolveReviewFindingSeverity, resolveReviewLadderPolicy, resolveTraceWriteSinks, reviewCycleStateFor, reviewFocusFromIssueBody, reviewPromptSectionSchema, reviewPromptSectionsSchema, run, runBoundaryCheck, runDemandWaive, runEvidenceEmit, runPrPublish, runPrReady, runPrReview, runPrVerify, scanFactoryTraceDiagnostics, scanFactoryTraceEvents, selectWaiversForCandidate, staleRepeatLadderFindings, toFactoryTraceEnvelope, tryAppendReviewGateNotRequiredTraceEvent, tryAppendReviewLadderStageTraceEvent, validateBoundaryCheckProof, validateDagDocument, validateDemandWaiverStore, validateFactoryTraceEvent, validateMergeFreezeState, validatePrReadyProof, validatePrReviewProof, validatePrVerifyProof, waivedDemandNotice, waivedDemandSchema, worktree_scratch_files_exports as worktreeScratchFiles };
|
package/dist/schemas.d.ts
CHANGED
|
@@ -125,6 +125,7 @@ declare const impactStampSchema: z.ZodObject<{
|
|
|
125
125
|
"target-scoped": "target-scoped";
|
|
126
126
|
conservative: "conservative";
|
|
127
127
|
}>;
|
|
128
|
+
inertPaths: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
128
129
|
reasons: z.ZodArray<z.ZodString>;
|
|
129
130
|
stampVersion: z.ZodLiteral<3>;
|
|
130
131
|
targets: z.ZodArray<z.ZodObject<{
|
|
@@ -584,6 +585,16 @@ interface PrVerifyNotRequiredCommand {
|
|
|
584
585
|
impactTarget: string;
|
|
585
586
|
name: string;
|
|
586
587
|
}
|
|
588
|
+
/**
|
|
589
|
+
* Epic #758 (#762): a consumer package a stamp-scoped selection skipped, and
|
|
590
|
+
* why. Bound to the proof's stamp identity — the field is v4-only (the same
|
|
591
|
+
* window that can carry `impactStamp`) and refused when the proof records no
|
|
592
|
+
* stamp. The factory does not write this list; consumers do.
|
|
593
|
+
*/
|
|
594
|
+
interface PrVerifyNotDemandedRecord {
|
|
595
|
+
basis: string;
|
|
596
|
+
name: string;
|
|
597
|
+
}
|
|
587
598
|
interface PrVerifyProof {
|
|
588
599
|
schemaVersion: PrVerifySchemaVersion;
|
|
589
600
|
authoringSession?: string;
|
|
@@ -607,6 +618,7 @@ interface PrVerifyProof {
|
|
|
607
618
|
impactStamp?: ImpactStamp;
|
|
608
619
|
mode: ResolvedPrVerifyMode;
|
|
609
620
|
mergeBaseSha?: string;
|
|
621
|
+
notDemandedRecords?: PrVerifyNotDemandedRecord[];
|
|
610
622
|
notRequiredCommands?: PrVerifyNotRequiredCommand[];
|
|
611
623
|
outcome?: "aborted" | "passed";
|
|
612
624
|
patchId?: string;
|
|
@@ -659,6 +671,7 @@ declare const prVerifyProofSchema: z.ZodObject<{
|
|
|
659
671
|
"target-scoped": "target-scoped";
|
|
660
672
|
conservative: "conservative";
|
|
661
673
|
}>;
|
|
674
|
+
inertPaths: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
662
675
|
reasons: z.ZodArray<z.ZodString>;
|
|
663
676
|
stampVersion: z.ZodLiteral<3>;
|
|
664
677
|
targets: z.ZodArray<z.ZodObject<{
|
|
@@ -677,6 +690,10 @@ declare const prVerifyProofSchema: z.ZodObject<{
|
|
|
677
690
|
"docs-only": "docs-only";
|
|
678
691
|
full: "full";
|
|
679
692
|
}>;
|
|
693
|
+
notDemandedRecords: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
694
|
+
basis: z.ZodString;
|
|
695
|
+
name: z.ZodString;
|
|
696
|
+
}, z.core.$strip>>>;
|
|
680
697
|
notRequiredCommands: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
681
698
|
basis: z.ZodString;
|
|
682
699
|
impactTarget: z.ZodString;
|
package/dist/schemas.js
CHANGED
|
@@ -241,6 +241,7 @@ const impactStampTargetSchema = z.object({
|
|
|
241
241
|
*/
|
|
242
242
|
const impactStampSchema = z.object({
|
|
243
243
|
basis: z.enum(["target-scoped", "conservative"]),
|
|
244
|
+
inertPaths: z.array(z.string()).default([]),
|
|
244
245
|
reasons: z.array(z.string()),
|
|
245
246
|
stampVersion: z.literal(3),
|
|
246
247
|
targets: z.array(impactStampTargetSchema).superRefine((targets, context) => {
|
|
@@ -514,6 +515,16 @@ const assertPrVerifyProofRules = (proof, context) => {
|
|
|
514
515
|
message: "notRequiredCommands is a schemaVersion>=4 field; a v1–v3 pr:verify proof must not carry it.",
|
|
515
516
|
path: ["notRequiredCommands"]
|
|
516
517
|
});
|
|
518
|
+
if (proof.schemaVersion < 4 && proof.notDemandedRecords !== void 0) context.addIssue({
|
|
519
|
+
code: "custom",
|
|
520
|
+
message: "notDemandedRecords is a schemaVersion>=4 field; a v1–v3 pr:verify proof must not carry it.",
|
|
521
|
+
path: ["notDemandedRecords"]
|
|
522
|
+
});
|
|
523
|
+
if (proof.notDemandedRecords !== void 0 && proof.impactStamp === void 0) context.addIssue({
|
|
524
|
+
code: "custom",
|
|
525
|
+
message: "notDemandedRecords requires impactStamp; package-level not-demanded records are bound to the stamp identity.",
|
|
526
|
+
path: ["notDemandedRecords"]
|
|
527
|
+
});
|
|
517
528
|
assertBatteryCompleteness(proof, context);
|
|
518
529
|
assertNotRequiredStampAuthorization(proof, context);
|
|
519
530
|
};
|
|
@@ -546,6 +557,10 @@ const notRequiredCommandSchema = z.object({
|
|
|
546
557
|
impactTarget: z.string().min(1),
|
|
547
558
|
name: z.string().min(1)
|
|
548
559
|
});
|
|
560
|
+
const notDemandedRecordSchema = z.object({
|
|
561
|
+
basis: z.string().min(1),
|
|
562
|
+
name: z.string().min(1)
|
|
563
|
+
});
|
|
549
564
|
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(", ")}` });
|
|
550
565
|
const prVerifyProofSchema = z.object({
|
|
551
566
|
authoringSession: z.string().trim().min(1).optional(),
|
|
@@ -577,6 +592,7 @@ const prVerifyProofSchema = z.object({
|
|
|
577
592
|
"trivial",
|
|
578
593
|
"full"
|
|
579
594
|
]),
|
|
595
|
+
notDemandedRecords: z.array(notDemandedRecordSchema).min(1).optional(),
|
|
580
596
|
notRequiredCommands: z.array(notRequiredCommandSchema).min(1).optional(),
|
|
581
597
|
outcome: z.enum(["aborted", "passed"]).optional(),
|
|
582
598
|
patchId: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patronage/software-factory",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.17",
|
|
4
4
|
"description": "Shared Patronage software factory CLI and project-profile validation tools",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"picomatch": "^4.0.5",
|
|
38
38
|
"yaml": "^2.9.0",
|
|
39
39
|
"zod": "4.4.3",
|
|
40
|
-
"@patronage/factory-ci": "1.0.0-alpha.
|
|
40
|
+
"@patronage/factory-ci": "1.0.0-alpha.17"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "24.13.3",
|