@voltro/plan 0.85.0 → 0.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,1034 +1,14 @@
1
- import { Cause as e, Exit as t, Schema as n, SchemaIssue as r } from "effect";
2
- //#region src/ids.ts
3
- var i = /^[A-Z][A-Z0-9]{1,9}$/, a = (e) => {
4
- if (!i.test(e)) throw Error(`plan id prefix "${e}" is not 2 to 10 capital letters or digits, starting with a letter`);
5
- return {
6
- prefix: e,
7
- plan: RegExp(`^${e}-PLAN-[0-9]{2}$`),
8
- workItem: RegExp(`^${e}-[0-9]{2}-[0-9]{3}$`),
9
- acceptance: RegExp(`^${e}-[0-9]{2}-[0-9]{3}-A[0-9]{2}$`),
10
- requirement: RegExp(`^${e}-[0-9]{2}-[0-9]{3}-E[0-9]{2}$`),
11
- source: RegExp(`^SRC-${e}-[0-9]{2}-[0-9]{3}-[0-9]{3}$`),
12
- evidence: RegExp(`^EVD-${e}-[0-9]{2}-[0-9]{3}-[0-9]{3}$`),
13
- finding: RegExp(`^FND-${e}-[0-9]{2}-[0-9]{3}-[0-9]{3}$`),
14
- adr: RegExp(`^ADR-${e}-[0-9]{3}$`),
15
- closureRun: RegExp(`^CLR-${e}-[0-9]{2}-[0-9]{3}$`),
16
- certificate: RegExp(`^CRT-${e}-[0-9]{2}-[0-9]{3}$`)
17
- };
18
- }, o = (e) => {
19
- let t = /-(?:PLAN-)?([0-9]{2})(?:-|$)/.exec(e);
20
- return t === null ? null : t[1];
21
- }, s = (e, t) => {
22
- let n = RegExp(`(?:^|-)(${e.prefix}-[0-9]{2}-[0-9]{3})(?:-|$)`).exec(t);
23
- return n === null ? null : n[1];
24
- }, c = (e) => JSON.stringify(l(e)), l = (e) => {
25
- if (Array.isArray(e)) return e.map(l);
26
- if (typeof e == "object" && e) {
27
- let t = {};
28
- for (let n of Object.keys(e).sort()) {
29
- let r = e[n];
30
- r !== void 0 && (t[n] = l(r));
31
- }
32
- return t;
33
- }
34
- return e;
35
- }, u = new Uint32Array([
36
- 1116352408,
37
- 1899447441,
38
- 3049323471,
39
- 3921009573,
40
- 961987163,
41
- 1508970993,
42
- 2453635748,
43
- 2870763221,
44
- 3624381080,
45
- 310598401,
46
- 607225278,
47
- 1426881987,
48
- 1925078388,
49
- 2162078206,
50
- 2614888103,
51
- 3248222580,
52
- 3835390401,
53
- 4022224774,
54
- 264347078,
55
- 604807628,
56
- 770255983,
57
- 1249150122,
58
- 1555081692,
59
- 1996064986,
60
- 2554220882,
61
- 2821834349,
62
- 2952996808,
63
- 3210313671,
64
- 3336571891,
65
- 3584528711,
66
- 113926993,
67
- 338241895,
68
- 666307205,
69
- 773529912,
70
- 1294757372,
71
- 1396182291,
72
- 1695183700,
73
- 1986661051,
74
- 2177026350,
75
- 2456956037,
76
- 2730485921,
77
- 2820302411,
78
- 3259730800,
79
- 3345764771,
80
- 3516065817,
81
- 3600352804,
82
- 4094571909,
83
- 275423344,
84
- 430227734,
85
- 506948616,
86
- 659060556,
87
- 883997877,
88
- 958139571,
89
- 1322822218,
90
- 1537002063,
91
- 1747873779,
92
- 1955562222,
93
- 2024104815,
94
- 2227730452,
95
- 2361852424,
96
- 2428436474,
97
- 2756734187,
98
- 3204031479,
99
- 3329325298
100
- ]), d = (e) => {
101
- let t = new TextEncoder().encode(e), n = t.length * 8, r = new Uint8Array(t.length + 9 + 63 >> 6 << 6);
102
- r.set(t), r[t.length] = 128;
103
- let i = new DataView(r.buffer);
104
- i.setUint32(r.length - 8, Math.floor(n / 4294967296)), i.setUint32(r.length - 4, n >>> 0);
105
- let a = new Uint32Array([
106
- 1779033703,
107
- 3144134277,
108
- 1013904242,
109
- 2773480762,
110
- 1359893119,
111
- 2600822924,
112
- 528734635,
113
- 1541459225
114
- ]), o = /* @__PURE__ */ new Uint32Array(64), s = (e, t) => e >>> t | e << 32 - t;
115
- for (let e = 0; e < r.length; e += 64) {
116
- for (let t = 0; t < 16; t++) o[t] = i.getUint32(e + t * 4);
117
- for (let e = 16; e < 64; e++) {
118
- let t = s(o[e - 15], 7) ^ s(o[e - 15], 18) ^ o[e - 15] >>> 3, n = s(o[e - 2], 17) ^ s(o[e - 2], 19) ^ o[e - 2] >>> 10;
119
- o[e] = o[e - 16] + t + o[e - 7] + n >>> 0;
120
- }
121
- let [t, n, r, c, l, d, f, p] = [
122
- a[0],
123
- a[1],
124
- a[2],
125
- a[3],
126
- a[4],
127
- a[5],
128
- a[6],
129
- a[7]
130
- ];
131
- for (let e = 0; e < 64; e++) {
132
- let i = p + (s(l, 6) ^ s(l, 11) ^ s(l, 25)) + (l & d ^ ~l & f) + u[e] + o[e] >>> 0, a = (s(t, 2) ^ s(t, 13) ^ s(t, 22)) + (t & n ^ t & r ^ n & r) >>> 0;
133
- p = f, f = d, d = l, l = c + i >>> 0, c = r, r = n, n = t, t = i + a >>> 0;
134
- }
135
- a[0] = a[0] + t >>> 0, a[1] = a[1] + n >>> 0, a[2] = a[2] + r >>> 0, a[3] = a[3] + c >>> 0, a[4] = a[4] + l >>> 0, a[5] = a[5] + d >>> 0, a[6] = a[6] + f >>> 0, a[7] = a[7] + p >>> 0;
136
- }
137
- return [...a].map((e) => e.toString(16).padStart(8, "0")).join("");
138
- }, f = (e) => `sha256:${d(c(e))}`, p = n.String.pipe(n.check(n.isMinLength(1))), m = n.String.pipe(n.check(n.isPattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/))), h = n.String.pipe(n.check(n.isPattern(/^sha256:[a-f0-9]{64}$/))), g = (e) => n.Array(e).pipe(n.check(n.makeFilter((e) => new Set(e).size === e.length || "items are listed twice"))), _ = (e) => n.Array(e).pipe(n.check(n.isMinLength(1))), v = n.String.pipe(n.check(n.isPattern(/^[a-z0-9]+(?:[.:/-][a-z0-9]+)*$/))), y = [
139
- "proposed",
140
- "ready",
141
- "active",
142
- "blocked",
143
- "verified",
144
- "closed"
145
- ], b = [
146
- "proposed",
147
- "decomposing",
148
- "ready",
149
- "active",
150
- "verifying",
151
- "closure-1",
152
- "closure-2",
153
- "complete"
154
- ], x = [
155
- "commit",
156
- "typecheck",
157
- "unit-test",
158
- "integration-test",
159
- "e2e",
160
- "browser",
161
- "schema",
162
- "migration",
163
- "roundtrip",
164
- "documentation",
165
- "agent-guide",
166
- "template",
167
- "security",
168
- "privacy",
169
- "accessibility",
170
- "rights",
171
- "operations",
172
- "performance",
173
- "research",
174
- "review",
175
- "artifact"
176
- ], ee = [
177
- "P0",
178
- "P1",
179
- "P2",
180
- "P3"
181
- ], S = [
182
- "architecture",
183
- "feature",
184
- "framework",
185
- "integration",
186
- "migration",
187
- "operations",
188
- "security",
189
- "documentation",
190
- "research",
191
- "tooling"
192
- ], C = (e) => n.String.pipe(n.check(n.isPattern(e))), w = (e) => {
193
- let t = e.grammar, r = g(C(t.evidence)), i = n.Struct({
194
- id: C(t.source),
195
- uri: p,
196
- locator: p,
197
- excerpt: p,
198
- lineHint: n.optional(n.Int.pipe(n.check(n.isGreaterThanOrEqualTo(1)))),
199
- contentHash: h,
200
- disposition: n.Literals([
201
- "owned-by",
202
- "referenced-by",
203
- "decision",
204
- "non-goal"
205
- ]),
206
- targetId: n.optional(n.Union([C(t.workItem), C(t.adr)]))
207
- }), a = n.Struct({
208
- id: C(t.acceptance),
209
- statement: p,
210
- status: n.Literals([
211
- "pending",
212
- "passed",
213
- "failed"
214
- ]),
215
- evidenceRefs: r
216
- }), o = n.Struct({
217
- rationale: p,
218
- reviewer: p,
219
- expiresAt: m,
220
- compensatingControl: p
221
- }), s = n.Struct({
222
- id: C(t.requirement),
223
- statement: p,
224
- kinds: _(n.Literals(x)),
225
- status: n.Literals([
226
- "open",
227
- "satisfied",
228
- "waived"
229
- ]),
230
- evidenceRefs: r,
231
- waiver: n.optional(o)
232
- }), c = n.Struct({
233
- capabilities: n.Array(p),
234
- journeys: n.Array(p),
235
- adrs: g(C(t.adr)),
236
- risks: n.Array(p),
237
- relatedTo: g(C(t.workItem))
238
- }), l = n.Struct({
239
- reason: p,
240
- owner: p,
241
- nextAction: p,
242
- recheckTrigger: p,
243
- since: m
244
- }), u = n.Struct({
245
- from: n.NullOr(n.Literals(y)),
246
- to: n.Literals(y),
247
- reason: p,
248
- actor: p,
249
- at: m,
250
- evidenceRefs: r
251
- }), d = n.Struct({
252
- id: C(t.workItem),
253
- title: p,
254
- ownerPlan: C(t.plan),
255
- slice: p,
256
- kind: n.Literals(S),
257
- priority: n.Literals(ee),
258
- mandatory: n.Boolean,
259
- status: n.Literals(y),
260
- owners: n.Array(p),
261
- repos: _(n.Literals(e.repos)),
262
- sourceRefs: _(i),
263
- dependsOn: g(C(t.workItem)),
264
- links: c,
265
- acceptance: _(a),
266
- evidenceRequirements: _(s),
267
- evidenceRefs: r,
268
- impactTags: _(v),
269
- statusHistory: _(u),
270
- blocker: n.optional(l),
271
- supersededBy: n.optional(C(t.workItem)),
272
- createdAt: m,
273
- updatedAt: m
274
- }), f = n.Struct({
275
- id: C(t.plan),
276
- number: n.String.pipe(n.check(n.isPattern(/^[0-9]{2}$/))),
277
- title: p,
278
- source: p,
279
- sourceRevision: h,
280
- status: n.Literals(b),
281
- decompositionComplete: n.Boolean,
282
- updatedAt: m
283
- }), w = n.Struct({
284
- rulesVersion: p,
285
- sourceRevision: h,
286
- blockKinds: n.Array(p),
287
- extractedAt: m
288
- }), T = n.Struct({
289
- extractedSources: n.Int,
290
- disposedSources: n.Int,
291
- unmatchedSources: n.Int,
292
- duplicateSources: n.Int,
293
- sourceIdsHash: h,
294
- result: n.Literals(["complete", "incomplete"])
295
- });
296
- return n.Struct({
297
- schemaVersion: n.Literal(e.instanceVersion),
298
- plan: f,
299
- extraction: w,
300
- workItems: n.Array(d),
301
- coverage: T
302
- });
303
- }, T = (e) => n.Struct({
304
- id: C(e.grammar.evidence),
305
- kind: n.Literals(x),
306
- title: p,
307
- uri: p,
308
- fingerprint: n.optional(h),
309
- result: n.Literals([
310
- "passed",
311
- "failed",
312
- "informational"
313
- ]),
314
- subjectRefs: n.Array(p),
315
- command: n.optional(p),
316
- environment: n.optional(p),
317
- producedBy: p,
318
- reviewedBy: n.optional(p),
319
- observedAt: m,
320
- idempotencyKey: n.optional(p)
321
- }), E = (e) => n.Struct({
322
- id: C(e.grammar.finding),
323
- title: p,
324
- severity: n.Literals([
325
- "critical",
326
- "high",
327
- "medium",
328
- "low"
329
- ]),
330
- status: n.Literals([
331
- "open",
332
- "resolved",
333
- "accepted-risk"
334
- ]),
335
- source: n.Literals([
336
- "contract-reconciliation",
337
- "adversarial-audit",
338
- "test",
339
- "incident",
340
- "research",
341
- "review",
342
- "production"
343
- ]),
344
- targetWorkItems: _(C(e.grammar.workItem)),
345
- impactTags: n.Array(p),
346
- evidenceRefs: n.optional(g(C(e.grammar.evidence))),
347
- openedAt: m,
348
- resolvedAt: n.optional(m)
349
- }), D = {
350
- security: 0,
351
- error: 1,
352
- warning: 2
353
- }, O = (e, t) => (e ?? "") < (t ?? "") ? -1 : +((e ?? "") > (t ?? "")), te = (e) => [...e].sort((e, t) => D[e.severity] - D[t.severity] || O(e.locator.plan, t.locator.plan) || O(e.locator.workItem, t.locator.workItem) || O(e.ruleId, t.ruleId) || O(e.locator.file, t.locator.file) || O(e.locator.path, t.locator.path) || O(e.message, t.message)), ne = (e, t = !1) => e.some((e) => e.severity !== "warning" || t), k = {
354
- proposed: ["ready", "blocked"],
355
- ready: [
356
- "active",
357
- "blocked",
358
- "proposed"
359
- ],
360
- active: [
361
- "blocked",
362
- "verified",
363
- "ready"
364
- ],
365
- blocked: [
366
- "proposed",
367
- "ready",
368
- "active"
369
- ],
370
- verified: ["closed", "active"],
371
- closed: ["active"]
372
- }, A = /* @__PURE__ */ new Set(["verified", "closed"]), j = (e, t) => {
373
- let n = /* @__PURE__ */ new Map();
374
- for (let e of t) if (e.status === "open") for (let t of e.targetWorkItems) {
375
- let r = n.get(t);
376
- r === void 0 ? n.set(t, [e]) : r.push(e);
377
- }
378
- let r = /* @__PURE__ */ new Map();
379
- for (let t of e) for (let e of new Set(t.subjectRefs)) {
380
- let n = r.get(e);
381
- n === void 0 ? r.set(e, [t]) : n.push(t);
382
- }
383
- return {
384
- evidence: new Map(e.map((e) => [e.id, e])),
385
- bySubject: r,
386
- openFindings: n
387
- };
388
- }, M = (e, t, n, r) => e.result === "passed" && e.subjectRefs.includes(n) && (r === void 0 || r.includes(e.kind)) && Date.parse(e.observedAt) >= Date.parse(t.updatedAt), N = (e, t, n, r) => {
389
- let i = [], a = t.bySubject.get(e.id) ?? [], o;
390
- if (e.acceptance.length > 4 || e.acceptance.length + e.evidenceRequirements.length > 4 && e.acceptance.length + e.evidenceRequirements.filter((e) => e.status !== "waived").length > 4) {
391
- let t = /* @__PURE__ */ new Set([...e.acceptance.map((e) => e.id), ...e.evidenceRequirements.filter((e) => e.status !== "waived").map((e) => e.id)]);
392
- o = /* @__PURE__ */ new Map();
393
- let n = Date.parse(e.updatedAt);
394
- for (let e of a) if (e.result === "passed" && Date.parse(e.observedAt) >= n) for (let n of e.subjectRefs) {
395
- if (!t.has(n)) continue;
396
- let r = o.get(n);
397
- r === void 0 ? o.set(n, /* @__PURE__ */ new Set([e.kind])) : r.add(e.kind);
398
- }
399
- }
400
- let s = (t, n) => {
401
- if (o === void 0) return a.some((r) => M(r, e, t, n));
402
- let r = o.get(t);
403
- return r !== void 0 && (n === void 0 || n.some((e) => r.has(e)));
404
- }, c = (n, r) => n.some((n) => {
405
- let i = t.evidence.get(n);
406
- return i !== void 0 && M(i, e, e.id, r);
407
- });
408
- for (let t of e.acceptance) !s(t.id) && !(t.status === "passed" && c(t.evidenceRefs)) && i.push(`acceptance ${t.id} has no passing evidence`);
409
- for (let t of e.evidenceRequirements) t.status === "waived" ? (t.waiver === void 0 || Date.parse(t.waiver.expiresAt) <= r.now().getTime()) && i.push(`the waiver of ${t.id} has expired`) : !s(t.id, t.kinds) && !c(t.evidenceRefs, t.kinds) && i.push(`requirement ${t.id} has no passing ${t.kinds.join("/")} evidence`);
410
- for (let n of t.openFindings.get(e.id) ?? []) i.push(`finding ${n.id} is open`);
411
- for (let t of e.dependsOn) {
412
- let e = n(t);
413
- e !== "verified" && e !== "closed" && i.push(`dependency ${t} is ${e ?? "missing"}`);
414
- }
415
- return i;
416
- }, P = (e, t) => t.length === 0 ? "verified" : A.has(e.status) ? "active" : e.status, F = "voltro-plan-closure/v1", I = n.String.pipe(n.check(n.isMinLength(1))), L = n.String.pipe(n.check(n.isPattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/))), R = n.String.pipe(n.check(n.isPattern(/^sha256:[a-f0-9]{64}$/))), z = (e) => {
417
- let t = e.grammar, r = (e) => n.String.pipe(n.check(n.isPattern(e))), i = n.Struct({
418
- id: r(t.closureRun),
419
- pass: n.Literals(["contract", "adversarial"]),
420
- reviewer: I,
421
- reviewContext: I,
422
- planRevision: R,
423
- completedAt: L,
424
- findingRefs: n.Array(r(t.finding)),
425
- result: n.Literals(["passed", "findings"]),
426
- toolVersion: I
427
- }), a = n.Struct({
428
- id: r(t.certificate),
429
- plan: r(t.plan),
430
- planRevision: R,
431
- workItems: n.Array(r(t.workItem)),
432
- evidenceRefs: n.Array(r(t.evidence)),
433
- closureRuns: n.Array(r(t.closureRun)).pipe(n.check(n.isBetweenLength(2, 2))),
434
- confirmedBy: I,
435
- issuedAt: L,
436
- toolVersion: I
437
- });
438
- return n.Struct({
439
- schemaVersion: n.Literal(F),
440
- plan: r(t.plan),
441
- runs: n.Array(i),
442
- certificates: n.Array(a)
443
- });
444
- }, B = (e, t, n, r = () => void 0, i = /* @__PURE__ */ new Map()) => {
445
- let a = new Set(e.workItems.map((e) => e.id));
446
- return f({
447
- instance: e,
448
- dependencies: [...new Set(e.workItems.flatMap((e) => e.dependsOn).filter((e) => !a.has(e)))].sort().map((e) => {
449
- let t = r(e);
450
- return [e, t === "closed" ? "verified" : t ?? "missing"];
451
- }),
452
- decisions: [...new Set(e.workItems.flatMap((e) => e.links.adrs))].sort().map((e) => [e, i.get(e) ?? null]),
453
- evidence: t.filter((e) => e.subjectRefs.some((e) => a.has(e))).map((e) => e.id).sort(),
454
- findings: n.filter((e) => e.targetWorkItems.some((e) => a.has(e))).map((e) => [e.id, e.status]).sort()
455
- });
456
- }, V = (e, t) => {
457
- if (e === void 0) return { state: "open" };
458
- let n = new Map(e.runs.map((e) => [e.id, e])), r = [...e.certificates].reverse().find((e) => e.planRevision === t && e.closureRuns.every((e) => n.get(e)?.result === "passed" && n.get(e)?.planRevision === t));
459
- if (r !== void 0) return {
460
- state: "certified",
461
- certificate: r
462
- };
463
- let i = [...e.runs].reverse().find((e) => e.pass === "contract" && e.result === "passed" && e.planRevision === t);
464
- return i === void 0 ? { state: "open" } : {
465
- state: "closure-1",
466
- run: i
467
- };
468
- }, H = (e, t, n, r) => ({
469
- outcome: "refused",
470
- diagnostic: {
471
- ruleId: e,
472
- severity: "error",
473
- message: t,
474
- locator: { plan: r },
475
- related: [],
476
- nextAction: n
477
- }
478
- }), U = (e, t) => `${e}${String(Math.max(0, ...t.filter((t) => t.startsWith(e)).map((t) => Number(t.slice(e.length)))) + 1).padStart(3, "0")}`, re = (e, t, n, r, i, a, o, s) => {
479
- let c = t.plan.id, l = t.workItems.filter((e) => e.mandatory && e.supersededBy === void 0), u = l.filter((e) => i(e.id) !== "verified" && i(e.id) !== "closed");
480
- if (s.findings.length === 0 && u.length > 0) return H("closure/unverified-items", `${u.length} of ${l.length} mandatory items of ${c} are not verified (${u.slice(0, 3).map((e) => e.id).join(", ")}${u.length > 3 ? ", …" : ""})`, "Verify every mandatory item first: voltro plan status, voltro plan evidence add.", c);
481
- let d = new Set(t.workItems.map((e) => e.id)), f = o.filter((e) => e.status === "open" && e.targetWorkItems.some((e) => d.has(e)));
482
- for (let e of s.findings) if (!f.some((t) => t.id === e)) return H("closure/unknown-finding", `${e} is not an open finding against ${c}`, "Record the finding first, against the items it concerns.", c);
483
- if (s.findings.length === 0 && f.length > 0) return H("closure/open-findings", `${f.length} finding(s) against ${c} are open (${f.map((e) => e.id).slice(0, 3).join(", ")})`, "Resolve them or accept the risk; then close again.", c);
484
- let p = n?.runs ?? [];
485
- if (s.pass === "adversarial" && s.findings.length === 0) {
486
- let e = [...p].reverse().find((e) => e.pass === "contract" && e.result === "passed" && e.planRevision === r);
487
- if (e === void 0) return H("closure/order", `${c} has no passed contract pass on its current revision`, "Run the contract pass first: voltro plan close <plan> --pass contract.", c);
488
- if (e.reviewer === s.reviewer || e.reviewContext === s.reviewContext) return H("closure/same-context", `the adversarial pass needs another reviewer and another context than ${e.id} (${e.reviewer}, ${e.reviewContext})`, "Have someone else audit it, in their own context.", c);
489
- }
490
- let m = t.plan.number, h = {
491
- id: U(`CLR-${e.prefix}-${m}-`, p.map((e) => e.id)),
492
- pass: s.pass,
493
- reviewer: s.reviewer,
494
- reviewContext: s.reviewContext,
495
- planRevision: r,
496
- completedAt: s.at,
497
- findingRefs: [...s.findings].sort(),
498
- result: s.findings.length === 0 ? "passed" : "findings",
499
- toolVersion: s.toolVersion
500
- }, g = null;
501
- if (s.pass === "adversarial" && h.result === "passed") {
502
- let t = [...p].reverse().find((e) => e.pass === "contract" && e.result === "passed" && e.planRevision === r);
503
- g = {
504
- id: U(`CRT-${e.prefix}-${m}-`, (n?.certificates ?? []).map((e) => e.id)),
505
- plan: c,
506
- planRevision: r,
507
- workItems: l.map((e) => e.id).sort(),
508
- evidenceRefs: a.filter((e) => e.subjectRefs.some((e) => d.has(e))).map((e) => e.id).sort(),
509
- closureRuns: [t.id, h.id],
510
- confirmedBy: s.reviewer,
511
- issuedAt: s.at,
512
- toolVersion: s.toolVersion
513
- };
514
- }
515
- return {
516
- outcome: "recorded",
517
- file: {
518
- schemaVersion: F,
519
- plan: c,
520
- runs: [...p, h],
521
- certificates: [...n?.certificates ?? [], ...g === null ? [] : [g]]
522
- },
523
- run: h,
524
- certificate: g
525
- };
526
- }, W = (e, t, n, r, i, a = []) => ({
527
- ruleId: e,
528
- severity: t,
529
- message: n,
530
- locator: r,
531
- related: [...a].sort(),
532
- nextAction: i
533
- }), ie = r.makeFormatterStandardSchemaV1(), G = (r, i, a, o) => {
534
- let s = n.decodeUnknownExit(i, {
535
- onExcessProperty: "error",
536
- errors: "all"
537
- }), c = [];
538
- for (let n of r) {
539
- let r = s(n.content);
540
- if (t.isSuccess(r)) {
541
- c.push({
542
- file: n.file,
543
- value: r.value
544
- });
545
- continue;
546
- }
547
- let i = e.findErrorOption(r.cause), l = i._tag === "Some" ? ie(i.value.issue).issues : [{
548
- message: e.pretty(r.cause),
549
- path: []
550
- }];
551
- for (let e of l) {
552
- let t = (e.path ?? []).map((e) => String(typeof e == "object" ? e.key : e));
553
- o.push(W("schema/invalid", "error", `${a}: ${e.message}`, {
554
- file: n.file,
555
- path: t.join(".") || "(root)"
556
- }, "Correct the field to the v1 contract; unknown fields are not allowed."));
557
- }
558
- }
559
- return c;
560
- }, ae = [
561
- ["private key", /-----BEGIN [A-Z ]*PRIVATE KEY-----/],
562
- ["Stripe key", /\b[sr]k_(live|test)_[A-Za-z0-9]{16,}/],
563
- ["GitHub token", /\bgh[pousr]_[A-Za-z0-9]{30,}/],
564
- ["AWS access key", /\bAKIA[0-9A-Z]{16}\b/],
565
- ["Slack token", /\bxox[abprs]-[A-Za-z0-9-]{10,}/],
566
- ["bearer token", /\bBearer\s+[A-Za-z0-9._~+/-]{20,}/]
567
- ], K = (e, t, n, r) => {
568
- if (typeof e == "string") for (let [i, a] of ae) a.test(e) && r.push(W("security/secret", "security", `a ${i} is in the plan artefact`, {
569
- file: t,
570
- path: n
571
- }, "Remove the secret, rotate it, and reference where it is kept instead."));
572
- else if (Array.isArray(e)) e.forEach((e, i) => K(e, t, `${n}[${i}]`, r));
573
- else if (typeof e == "object" && e) for (let [i, a] of Object.entries(e)) K(a, t, n === "" ? i : `${n}.${i}`, r);
574
- }, q = (e) => {
575
- let t = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Map(), r = (r) => {
576
- let i = [], a = /* @__PURE__ */ new Set(), o = (r) => {
577
- i.push(r), a.add(r);
578
- for (let s of [...e.get(r) ?? []].sort()) if (e.has(s)) {
579
- if (a.has(s)) {
580
- let e = i.slice(i.indexOf(s)), t = e.reduce((e, t) => t < e ? t : e), r = [...e.slice(e.indexOf(t)), ...e.slice(0, e.indexOf(t))];
581
- n.set(r.join(">"), r);
582
- } else t.has(s) || o(s);
583
- }
584
- i.pop(), a.delete(r), t.add(r);
585
- };
586
- o(r);
587
- };
588
- for (let n of [...e.keys()].sort()) t.has(n) || r(n);
589
- return [...n.values()].sort((e, t) => e.join(">") < t.join(">") ? -1 : 1);
590
- }, oe = (e) => {
591
- let { shape: t, clock: n } = e, r = t.grammar, i = [];
592
- for (let t of [
593
- ...e.instances,
594
- ...e.evidence ?? [],
595
- ...e.findings ?? [],
596
- ...e.closures ?? []
597
- ]) K(t.content, t.file, "", i);
598
- let a = G(e.instances, w(t), "plan instance", i).map(({ file: e, value: t }) => ({
599
- file: e,
600
- instance: t
601
- })), c = (e.evidence ?? []).flatMap((e) => G(Array.isArray(e.content) ? e.content.map((t, n) => ({
602
- file: `${e.file}#${n}`,
603
- content: t
604
- })) : [e], T(t), "evidence", i).map((e) => e.value)), l = (e.findings ?? []).flatMap((e) => G(Array.isArray(e.content) ? e.content.map((t, n) => ({
605
- file: `${e.file}#${n}`,
606
- content: t
607
- })) : [e], E(t), "finding", i).map((e) => e.value)), u = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Map();
608
- for (let { file: t, instance: n } of a) {
609
- let a = n.plan;
610
- u.has(a.id) && i.push(W("identity/duplicate-plan", "error", `${a.id} is declared in two files`, {
611
- file: t,
612
- plan: a.id
613
- }, "Keep one instance file per plan.", [u.get(a.id)])), u.set(a.id, t), o(a.id) !== a.number && i.push(W("identity/plan-number", "error", `${a.id} has number ${a.number}`, {
614
- file: t,
615
- plan: a.id,
616
- path: "plan.number"
617
- }, "Make the number match the plan id."));
618
- for (let e of n.workItems) {
619
- let n = {
620
- file: t,
621
- plan: a.id,
622
- workItem: e.id
623
- };
624
- d.has(e.id) ? i.push(W("identity/duplicate-work-item", "error", `${e.id} is declared twice`, n, "Give the second item its own id; ids are never reused.", [d.get(e.id).file])) : d.set(e.id, {
625
- file: t,
626
- item: e
627
- }), (e.ownerPlan !== a.id || o(e.id) !== a.number) && i.push(W("identity/owner-mismatch", "error", `${e.id} is owned by ${e.ownerPlan} but lives in ${a.id}`, n, "Move the item to its plan’s file or correct ownerPlan."));
628
- let c = [
629
- ...e.acceptance.map((e) => e.id),
630
- ...e.evidenceRequirements.map((e) => e.id),
631
- ...e.sourceRefs.map((e) => e.id)
632
- ];
633
- for (let t of c) s(r, t) !== e.id && i.push(W("identity/child-id", "error", `${t} does not belong to ${e.id}`, n, `Number it under ${e.id}.`));
634
- for (let t of e.sourceRefs) f.has(t.id) && i.push(W("source/duplicate", "error", `source ${t.id} is disposed twice`, n, "Dispose each source block exactly once.", [f.get(t.id)])), f.set(t.id, e.id), t.disposition !== "owned-by" && t.targetId === void 0 && i.push(W("source/missing-target", "error", `source ${t.id} is ${t.disposition} without a target`, {
635
- ...n,
636
- path: `sourceRefs.${t.id}`
637
- }, "Name the work item or decision it points to."));
638
- }
639
- if (e.sources !== void 0) {
640
- let r = e.sources.get(a.source);
641
- r == null ? i.push(W("source/missing", "error", `${a.id}'s source ${a.source} cannot be read`, {
642
- file: t,
643
- plan: a.id,
644
- path: "plan.source"
645
- }, "Restore the source or point the plan at it.")) : (r !== a.sourceRevision || r !== n.extraction.sourceRevision) && i.push(W("source/stale", "error", `${a.source} changed since ${a.id} was decomposed (${a.sourceRevision.slice(0, 19)}… → ${r.slice(0, 19)}…)`, {
646
- file: t,
647
- plan: a.id,
648
- path: "plan.sourceRevision"
649
- }, "Regenerate the plan’s decomposition from its source; closure waits until then."));
650
- }
651
- let c = n.workItems.reduce((e, t) => e + t.sourceRefs.length, 0);
652
- (n.coverage.disposedSources !== c || n.coverage.unmatchedSources > 0 || n.coverage.duplicateSources > 0 || a.decompositionComplete && n.coverage.result !== "complete") && i.push(W("source/coverage", "error", `${a.id} claims ${n.coverage.disposedSources} disposed sources (${n.coverage.unmatchedSources} unmatched, ${n.coverage.duplicateSources} duplicate, ${n.coverage.result}) and holds ${c}`, {
653
- file: t,
654
- plan: a.id,
655
- path: "coverage"
656
- }, "Regenerate the plan’s decomposition from its source."));
657
- }
658
- let p = new Set(c.map((e) => e.id));
659
- for (let { file: t, item: n } of d.values()) {
660
- let r = {
661
- file: t,
662
- plan: n.ownerPlan,
663
- workItem: n.id
664
- };
665
- for (let e of n.dependsOn) e === n.id ? i.push(W("graph/self-dependency", "error", `${n.id} depends on itself`, r, "Remove the dependency.")) : d.has(e) || i.push(W("graph/missing-dependency", "error", `${n.id} depends on ${e}, which does not exist`, r, "Point at an existing item or remove the dependency.", [e]));
666
- if (e.decisions !== void 0) for (let t of n.links.adrs) e.decisions.has(t) || i.push(W("graph/missing-decision", "error", `${n.id} links ${t}, which the decision records do not hold`, r, "Add the decision record or correct the link.", [t]));
667
- for (let e of n.links.relatedTo) d.has(e) || i.push(W("graph/missing-related", "error", `${n.id} relates to ${e}, which does not exist`, r, "Point at an existing item.", [e]));
668
- n.supersededBy !== void 0 && !d.has(n.supersededBy) && i.push(W("graph/missing-successor", "error", `${n.id} is superseded by ${n.supersededBy}, which does not exist`, r, "Name the item that replaces it.", [n.supersededBy]));
669
- let a = [
670
- ...n.evidenceRefs,
671
- ...n.acceptance.flatMap((e) => e.evidenceRefs),
672
- ...n.evidenceRequirements.flatMap((e) => e.evidenceRefs),
673
- ...n.statusHistory.flatMap((e) => e.evidenceRefs)
674
- ];
675
- for (let e of [...new Set(a)].sort()) p.has(e) || i.push(W("evidence/missing", "error", `${n.id} names evidence ${e}, which does not exist`, r, "Add the evidence with `voltro plan evidence add` or remove the reference.", [e]));
676
- n.owners.length === 0 && i.push(W("quality/no-owner", "warning", `${n.id} has no owner`, r, "Name who owns the item."));
677
- }
678
- for (let e of q(new Map([...d.values()].map(({ item: e }) => [e.id, e.dependsOn])))) {
679
- let t = d.get(e[0]);
680
- i.push(W("graph/cycle", "error", `dependency cycle: ${[...e, e[0]].join(" → ")}`, {
681
- file: t.file,
682
- plan: t.item.ownerPlan,
683
- workItem: e[0]
684
- }, "Break the cycle: one of these items must not wait for the others.", e));
685
- }
686
- for (let e of l) for (let t of e.targetWorkItems) d.has(t) || i.push(W("graph/missing-finding-target", "error", `finding ${e.id} targets ${t}, which does not exist`, { workItem: t }, "Point the finding at an existing item.", [e.id]));
687
- for (let { file: e, item: t } of d.values()) {
688
- let n = {
689
- file: e,
690
- plan: t.ownerPlan,
691
- workItem: t.id
692
- };
693
- for (let e of t.acceptance) e.status !== "pending" && e.evidenceRefs.length === 0 && i.push(W("acceptance/decided-without-evidence", "error", `${e.id} is ${e.status} without evidence`, {
694
- ...n,
695
- path: e.id
696
- }, "Name the evidence the decision rests on."));
697
- for (let e of t.evidenceRequirements) e.status === "satisfied" && e.evidenceRefs.length === 0 && i.push(W("acceptance/satisfied-without-evidence", "error", `${e.id} is satisfied without evidence`, {
698
- ...n,
699
- path: e.id
700
- }, "Name the evidence that satisfies it.")), e.status === "waived" && e.waiver === void 0 && i.push(W("acceptance/waived-without-waiver", "error", `${e.id} is waived without a waiver`, {
701
- ...n,
702
- path: e.id
703
- }, "Write the waiver: rationale, reviewer, expiry and compensating control."));
704
- }
705
- let m = j(c, l), h = /* @__PURE__ */ new Map(), g = /* @__PURE__ */ new Map(), _ = /* @__PURE__ */ new Set(), v = (e) => {
706
- let t = h.get(e);
707
- if (t !== void 0) return t;
708
- let r = d.get(e);
709
- if (r === void 0 || _.has(e)) return;
710
- _.add(e);
711
- let i = N(r.item, m, v, n);
712
- _.delete(e);
713
- let a = P(r.item, i);
714
- return h.set(e, a), g.set(e, i), a;
715
- };
716
- for (let { file: e, item: t } of [...d.values()].sort((e, t) => e.item.id < t.item.id ? -1 : 1)) {
717
- let n = {
718
- file: e,
719
- plan: t.ownerPlan,
720
- workItem: t.id
721
- }, r = null;
722
- t.statusHistory.forEach((e, a) => {
723
- e.from === r ? e.from !== null && !k[e.from].includes(e.to) && i.push(W("state/forbidden-transition", "error", `${t.id} went ${e.from} → ${e.to}`, {
724
- ...n,
725
- path: `statusHistory[${a}]`
726
- }, `From ${e.from} an item goes to ${k[e.from].join(", ")}.`)) : i.push(W("state/history-break", "error", `${t.id} history step ${a + 1} starts at ${e.from ?? "nothing"}, after ${r ?? "nothing"}`, {
727
- ...n,
728
- path: `statusHistory[${a}]`
729
- }, "Record every transition in order.")), r = e.to;
730
- }), r !== t.status && i.push(W("state/history-end", "error", `${t.id} is ${t.status} but its history ends at ${r ?? "nothing"}`, {
731
- ...n,
732
- path: "status"
733
- }, "Record the transition that led to the current state.")), t.status === "blocked" && t.blocker === void 0 && i.push(W("state/blocked-without-blocker", "error", `${t.id} is blocked without a blocker`, {
734
- ...n,
735
- path: "blocker"
736
- }, "Write down reason, owner, next action and recheck trigger.")), t.status !== "blocked" && t.blocker !== void 0 && i.push(W("state/blocker-not-blocked", "error", `${t.id} carries a blocker but is ${t.status}`, {
737
- ...n,
738
- path: "blocker"
739
- }, "Remove the blocker or record the block.")), v(t.id);
740
- }
741
- let y = /* @__PURE__ */ new Map();
742
- for (let { file: n, value: r } of G(e.closures ?? [], z(t), "closure record", i)) u.has(r.plan) ? y.has(r.plan) ? i.push(W("closure/duplicate", "error", `${r.plan} has two closure records`, {
743
- file: n,
744
- plan: r.plan
745
- }, "Keep one closure record per plan.", [y.get(r.plan).file])) : y.set(r.plan, {
746
- file: n,
747
- record: r
748
- }) : i.push(W("closure/unknown-plan", "error", `a closure record names ${r.plan}, which does not exist`, {
749
- file: n,
750
- plan: r.plan
751
- }, "Remove it or correct the plan id."));
752
- let b = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map();
753
- for (let { instance: t } of a) {
754
- let n = B(t, c, l, (e) => h.get(e), e.decisions);
755
- b.set(t.plan.id, n);
756
- let r = y.get(t.plan.id), a = V(r?.record, n);
757
- if (x.set(t.plan.id, a), r !== void 0 && r.record.certificates.length > 0 && a.state !== "certified" && i.push(W("closure/stale-certificate", "warning", `${t.plan.id} changed after its certificate ${r.record.certificates.at(-1).id}: it is open again`, {
758
- file: r.file,
759
- plan: t.plan.id
760
- }, "Verify what changed and run both closure passes again.")), a.state === "certified") for (let e of t.workItems) h.get(e.id) === "verified" && h.set(e.id, "closed");
761
- t.plan.status === "complete" && a.state !== "certified" && i.push(W("state/manual-derived", "error", `${t.plan.id} says complete without a certificate on its current revision`, {
762
- plan: t.plan.id,
763
- path: "plan.status"
764
- }, "Close it with both passes: voltro plan close <plan>."));
765
- }
766
- for (let { file: e, item: t } of [...d.values()].sort((e, t) => e.item.id < t.item.id ? -1 : 1)) {
767
- let n = h.get(t.id);
768
- A.has(t.status) && n !== t.status && (t.status !== "verified" || n !== "closed") && i.push(W("state/manual-derived", "error", `${t.id} says ${t.status}; the evidence says ${n}: ${(g.get(t.id) ?? []).slice(0, 3).join("; ") || "its plan holds no certificate on its current revision"}`, {
769
- file: e,
770
- plan: t.ownerPlan,
771
- workItem: t.id,
772
- path: "status"
773
- }, "Add the missing evidence; verified and closed are derived, never set."));
774
- }
775
- return {
776
- program: {
777
- instances: a,
778
- items: d,
779
- evidence: c,
780
- findings: l,
781
- status: h,
782
- gaps: g,
783
- closures: y,
784
- revisions: b,
785
- closure: x
786
- },
787
- diagnostics: te(i)
788
- };
789
- }, J = () => Object.fromEntries(y.map((e) => [e, 0])), se = (e) => {
790
- let t = J();
791
- return {
792
- plans: e.instances.map(({ file: n, instance: r }) => {
793
- let i = J(), a = J(), o = [];
794
- for (let n of r.workItems) {
795
- let r = e.status.get(n.id) ?? n.status;
796
- (n.mandatory ? i : a)[r]++, n.mandatory && t[r]++, r === "blocked" && n.blocker !== void 0 && o.push({
797
- workItem: n.id,
798
- reason: n.blocker.reason,
799
- owner: n.blocker.owner,
800
- nextAction: n.blocker.nextAction
801
- });
802
- }
803
- let s = r.workItems.filter((e) => e.mandatory).length, c = i.verified + i.closed, l = e.closure.get(r.plan.id), u = r.plan.decompositionComplete ? l?.state === "certified" ? "complete" : s > 0 && c === s ? l?.state === "closure-1" ? "closure-1" : "verifying" : i.active + i.blocked + c > 0 ? "active" : i.ready > 0 ? "ready" : "decomposing" : "decomposing";
804
- return {
805
- plan: r.plan.id,
806
- title: r.plan.title,
807
- file: n,
808
- recorded: r.plan.status,
809
- derived: u,
810
- certificate: l?.state === "certified" ? l.certificate.id : null,
811
- mandatory: i,
812
- optional: a,
813
- blocked: o.sort((e, t) => e.workItem < t.workItem ? -1 : 1)
814
- };
815
- }).sort((e, t) => e.plan < t.plan ? -1 : 1),
816
- mandatory: t,
817
- items: e.items.size
818
- };
819
- }, ce = {
820
- P0: 0,
821
- P1: 1,
822
- P2: 2,
823
- P3: 3
824
- }, le = (e, t, n) => {
825
- if (n) return {
826
- outcome: "invalid",
827
- reason: "lint reports blocking diagnostics; fix them first (`voltro plan lint`)"
828
- };
829
- let r = (t) => e.status.get(t), i = new Set([...e.items.values()].filter(({ item: e }) => [
830
- "active",
831
- "verified",
832
- "closed"
833
- ].includes(r(e.id) ?? "")).map(({ item: e }) => e.ownerPlan)), a = [...e.items.values()].filter(({ item: e }) => e.mandatory && e.supersededBy === void 0 && (t.plan === void 0 || e.ownerPlan === t.plan || e.ownerPlan.endsWith(`-PLAN-${t.plan}`)) && (t.owner === void 0 || e.owners.includes(t.owner)) && (t.repo === void 0 || e.repos.includes(t.repo)));
834
- if (a.every(({ item: e }) => ["verified", "closed"].includes(r(e.id) ?? ""))) return { outcome: "complete" };
835
- let o = (e) => e.dependsOn.every((e) => ["verified", "closed"].includes(r(e) ?? "")), s = (e) => [
836
- r(e.id) === "active" ? 0 : 1,
837
- ce[e.priority],
838
- e.kind === "security" ? 0 : 1,
839
- +!i.has(e.ownerPlan),
840
- +!o(e),
841
- e.id
842
- ], c = (e, t) => {
843
- for (let n = 0; n < e.length; n++) if (e[n] !== t[n]) return e[n] < t[n];
844
- return !1;
845
- }, l = (e) => {
846
- let t;
847
- for (let n of a) {
848
- if (!e.includes(r(n.item.id) ?? "")) continue;
849
- let i = s(n.item);
850
- (t === void 0 || c(i, t.rank)) && (t = {
851
- entry: n,
852
- rank: i
853
- });
854
- }
855
- return t;
856
- }, u = l(["active", "ready"]);
857
- if (u !== void 0) {
858
- let e = u.entry, t = r(e.item.id);
859
- return {
860
- outcome: "item",
861
- workItem: e.item,
862
- file: e.file,
863
- why: [
864
- t === "active" ? "it is already active" : "it is ready",
865
- `priority ${e.item.priority}`,
866
- ...e.item.kind === "security" ? ["security work comes first"] : [],
867
- i.has(e.item.ownerPlan) ? `${e.item.ownerPlan} is already under way` : "no started plan has workable items",
868
- u.rank[4] === 0 ? "its dependencies are verified" : "no dependency-ready item is left, so it waits on its dependencies"
869
- ]
870
- };
871
- }
872
- let d = l(["proposed"]), f = a.filter(({ item: e }) => r(e.id) === "proposed").length, p = a.filter(({ item: e }) => r(e.id) === "blocked").length;
873
- return {
874
- outcome: "blocked",
875
- reason: `no mandatory item is active or ready: ${f} proposed await readiness${p > 0 ? `, ${p} blocked` : ""}`,
876
- prepare: d?.entry.item ?? null,
877
- file: d?.entry.file ?? null
878
- };
879
- }, Y = "voltro-plan-command/v1", ue = (e) => ({
880
- schemaVersion: Y,
881
- ...e
882
- }), X = {
883
- ok: 0,
884
- failed: 1,
885
- usage: 2,
886
- input: 3,
887
- conflict: 4,
888
- provider: 5,
889
- security: 6,
890
- internal: 70
891
- }, Z = "voltro-plan-manifest/v1", Q = n.String.pipe(n.check(n.isMinLength(1)), n.check(n.makeFilter((e) => !e.startsWith("/") && !/^[A-Za-z]:/.test(e) && !e.split("/").includes("..") || "a path stays inside the project root"))), de = n.Struct({
892
- schemaVersion: n.Literal(Z),
893
- prefix: n.String.pipe(n.check(n.isPattern(/^[A-Z][A-Z0-9]{1,9}$/))),
894
- instanceVersion: n.String.pipe(n.check(n.isMinLength(1))),
895
- repos: n.Array(n.String.pipe(n.check(n.isMinLength(1)))).pipe(n.check(n.isMinLength(1))),
896
- instances: n.Array(Q).pipe(n.check(n.isMinLength(1))),
897
- evidence: n.optional(n.Array(Q)),
898
- evidenceDir: n.optional(Q),
899
- closureDir: n.optional(Q),
900
- adrDir: n.optional(Q),
901
- findings: n.optional(n.Array(Q))
902
- }), $ = (e, t, n, r, i, a) => ({
903
- outcome: "refused",
904
- exit: a,
905
- diagnostic: {
906
- ruleId: e,
907
- severity: t,
908
- message: n,
909
- locator: { workItem: i },
910
- related: [],
911
- nextAction: r
912
- }
913
- }), fe = [
914
- /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
915
- /\b[sr]k_(live|test)_[A-Za-z0-9]{16,}/,
916
- /\bgh[pousr]_[A-Za-z0-9]{30,}/,
917
- /\bAKIA[0-9A-Z]{16}\b/,
918
- /\bxox[abprs]-[A-Za-z0-9-]{10,}/,
919
- /\bBearer\s+[A-Za-z0-9._~+/-]{20,}/
920
- ], pe = (e, t, n) => {
921
- let r = e.items.get(n.workItem)?.item;
922
- if (r === void 0) return $("evidence/unknown-target", "error", `${n.workItem} is not a work item of this program`, "Name an existing work item.", n.workItem, X.failed);
923
- if ([
924
- n.title,
925
- n.uri,
926
- n.producedBy,
927
- n.command ?? "",
928
- n.environment ?? ""
929
- ].some((e) => fe.some((t) => t.test(e))) || /^[a-z][a-z0-9+.-]*:\/\/[^/@\s]*:[^/@\s]*@/i.test(n.uri)) return $("security/secret", "security", "the evidence carries a secret or a credential", "Remove it; reference where the result is kept, never a key.", r.id, X.security);
930
- if (!x.includes(n.kind)) return $("evidence/kind", "error", `"${n.kind}" is not an evidence kind`, `Use one of: ${x.join(", ")}.`, r.id, X.usage);
931
- if (/^http:\/\//i.test(n.uri)) return $("evidence/uri", "error", `${n.uri} is not https`, "Reference evidence over https, git: or a project-relative path.", r.id, X.failed);
932
- if (n.uri.startsWith("/") || /^[A-Za-z]:[\\/]/.test(n.uri) || n.uri.split("/").includes("..")) return $("evidence/uri", "error", `${n.uri} is not portable`, "Use a path relative to the project root, or a URL.", r.id, X.failed);
933
- let i = [r.id];
934
- for (let e of n.proves) {
935
- let t = /^[AE][0-9]{2}$/.test(e) ? `${r.id}-${e}` : e, a = r.acceptance.find((e) => e.id === t), o = r.evidenceRequirements.find((e) => e.id === t);
936
- if (a === void 0 && o === void 0) return $("evidence/unknown-target", "error", `${t} is not a check or requirement of ${r.id}`, `Name one of ${[...r.acceptance, ...r.evidenceRequirements].map((e) => e.id).join(", ")}.`, r.id, X.failed);
937
- if (o !== void 0 && !o.kinds.includes(n.kind)) return $("evidence/wrong-kind", "error", `${o.id} asks for ${o.kinds.join("/")}, not ${n.kind}`, "Add evidence of a kind the requirement names.", r.id, X.failed);
938
- i.push(t);
939
- }
940
- let a = {
941
- kind: n.kind,
942
- title: n.title,
943
- uri: n.uri,
944
- result: n.result,
945
- subjectRefs: [...new Set(i)],
946
- producedBy: n.producedBy,
947
- ...n.command === void 0 ? {} : { command: n.command },
948
- ...n.environment === void 0 ? {} : { environment: n.environment },
949
- ...n.fingerprint === void 0 ? {} : { fingerprint: n.fingerprint }
950
- };
951
- if (n.idempotencyKey !== void 0) {
952
- let t = e.evidence.find((e) => e.idempotencyKey === n.idempotencyKey);
953
- if (t !== void 0) {
954
- let { id: e, observedAt: i, idempotencyKey: o, ...s } = t;
955
- return c(s) === c(a) ? {
956
- outcome: "same",
957
- evidence: t
958
- } : $("evidence/idempotency-conflict", "error", `the key "${n.idempotencyKey}" was used for other evidence (${t.id})`, "Use a new key for new evidence.", r.id, X.conflict);
959
- }
960
- }
961
- let o = `EVD-${r.id}-`, s = e.evidence.filter((e) => e.id.startsWith(o)).map((e) => Number(e.id.slice(o.length))), l = String(Math.max(0, ...s) + 1).padStart(3, "0");
962
- return t.evidence.test(`${o}${l}`) ? {
963
- outcome: "add",
964
- evidence: {
965
- id: `${o}${l}`,
966
- ...a,
967
- observedAt: n.observedAt,
968
- ...n.idempotencyKey === void 0 ? {} : { idempotencyKey: n.idempotencyKey }
969
- }
970
- } : $("evidence/exhausted", "error", `${r.id} has no evidence number left`, "Supersede the item.", r.id, X.failed);
971
- }, me = [
972
- ".",
973
- ":",
974
- "/",
975
- "-"
976
- ], he = (e, t) => e === t || me.some((n) => t.startsWith(e + n) || e.startsWith(t + n)), ge = (e, t) => {
977
- let n = /* @__PURE__ */ new Map(), r = (e, t) => {
978
- let r = n.get(e) ?? /* @__PURE__ */ new Set();
979
- r.add(t), n.set(e, r);
980
- };
981
- for (let { item: n } of e.items.values()) for (let e of t.tags) for (let t of n.impactTags) he(e, t) && r(n.id, `tag ${t}`);
982
- let i = [], a = new Map(e.instances.map(({ file: e, instance: t }) => [e, t])), o = /* @__PURE__ */ new Map();
983
- for (let { item: t } of e.items.values()) for (let e of t.sourceRefs) {
984
- let n = o.get(e.uri);
985
- n === void 0 && (n = /* @__PURE__ */ new Set(), o.set(e.uri, n)), n.add(t.id);
986
- }
987
- for (let e of [...new Set(t.files)].sort()) {
988
- let t = !1, n = a.get(e);
989
- if (n !== void 0) {
990
- t = !0;
991
- for (let t of n.workItems) r(t.id, `its plan file ${e} changed`);
992
- }
993
- for (let n of o.get(e) ?? []) t = !0, r(n, `its source ${e} changed`);
994
- t || i.push(e);
995
- }
996
- let s = /* @__PURE__ */ new Map();
997
- for (let { item: t } of e.items.values()) for (let e of t.dependsOn) {
998
- let n = s.get(e);
999
- n === void 0 && (n = [], s.set(e, n)), n.push(t.id);
1000
- }
1001
- for (let e of s.values()) e.sort();
1002
- let c = /* @__PURE__ */ new Map(), l = [...n.keys()].sort().map((e) => [e]), u = new Set(n.keys());
1003
- for (let e = 0; e < l.length; e++) {
1004
- let t = l[e];
1005
- for (let e of s.get(t.at(-1)) ?? []) {
1006
- if (u.has(e)) continue;
1007
- u.add(e);
1008
- let n = [...t, e];
1009
- c.set(e, n), l.push(n);
1010
- }
1011
- }
1012
- let d = [...u], f = [...new Set(d.map((t) => e.items.get(t).item.ownerPlan))].sort(), p = f.flatMap((t) => {
1013
- let n = e.closure.get(t);
1014
- return n?.state === "certified" ? [{
1015
- plan: t,
1016
- certificate: n.certificate.id
1017
- }] : [];
1018
- });
1019
- return {
1020
- direct: [...n.entries()].map(([e, t]) => ({
1021
- workItem: e,
1022
- because: [...t].sort()
1023
- })).sort((e, t) => e.workItem < t.workItem ? -1 : 1),
1024
- transitive: [...c.entries()].map(([e, t]) => ({
1025
- workItem: e,
1026
- path: t
1027
- })).sort((e, t) => e.workItem < t.workItem ? -1 : 1),
1028
- plans: f,
1029
- certificates: p,
1030
- unknown: i
1031
- };
1032
- };
1033
- //#endregion
1034
- export { F as CLOSURE_SCHEMA_VERSION, Y as COMMAND_SCHEMA_VERSION, A as DERIVED, x as EVIDENCE_KINDS, X as EXIT, S as ITEM_KINDS, Z as MANIFEST_SCHEMA_VERSION, de as Manifest, b as PLAN_STATUSES, ee as PRIORITIES, k as TRANSITIONS, y as WORK_ITEM_STATUSES, ne as blocks, c as canonicalJson, z as closureFileSchema, V as closureState, re as decideClosure, pe as decideEvidence, q as dependencyCycles, P as derivedStatus, ue as envelope, T as evidenceSchema, E as findingSchema, f as fingerprint, a as idGrammar, ge as impactOf, j as indexEvidence, oe as lintProgram, le as nextItem, s as ownerItemOf, w as planInstanceSchema, o as planNumberOf, B as planRevision, se as programStatus, d as sha256Hex, te as sortDiagnostics, N as verificationGaps };
1
+ import { idGrammar as e, ownerItemOf as t, planNumberOf as n } from "./ids.js";
2
+ import { canonicalJson as r, fingerprint as i, sha256Hex as a } from "./canonical.js";
3
+ import { EVIDENCE_KINDS as o, ITEM_KINDS as s, PLAN_STATUSES as c, PRIORITIES as l, WORK_ITEM_STATUSES as u, evidenceSchema as d, findingSchema as f, planInstanceSchema as p } from "./schema.js";
4
+ import { blocks as m, sortDiagnostics as h } from "./diagnostics.js";
5
+ import { DERIVED as g, TRANSITIONS as _, derivedStatus as v, indexEvidence as y, verificationGaps as b } from "./state.js";
6
+ import { CLOSURE_SCHEMA_VERSION as x, closureFileSchema as S, closureState as C, decideClosure as w, planRevision as T } from "./closure.js";
7
+ import { dependencyCycles as E, lintProgram as D } from "./lint.js";
8
+ import { programStatus as O } from "./status.js";
9
+ import { nextItem as k } from "./next.js";
10
+ import { COMMAND_SCHEMA_VERSION as A, EXIT as j, envelope as M } from "./envelope.js";
11
+ import { MANIFEST_SCHEMA_VERSION as N, Manifest as P } from "./manifest.js";
12
+ import { decideEvidence as F } from "./evidence.js";
13
+ import { impactOf as I } from "./impact.js";
14
+ export { x as CLOSURE_SCHEMA_VERSION, A as COMMAND_SCHEMA_VERSION, g as DERIVED, o as EVIDENCE_KINDS, j as EXIT, s as ITEM_KINDS, N as MANIFEST_SCHEMA_VERSION, P as Manifest, c as PLAN_STATUSES, l as PRIORITIES, _ as TRANSITIONS, u as WORK_ITEM_STATUSES, m as blocks, r as canonicalJson, S as closureFileSchema, C as closureState, w as decideClosure, F as decideEvidence, E as dependencyCycles, v as derivedStatus, M as envelope, d as evidenceSchema, f as findingSchema, i as fingerprint, e as idGrammar, I as impactOf, y as indexEvidence, D as lintProgram, k as nextItem, t as ownerItemOf, p as planInstanceSchema, n as planNumberOf, T as planRevision, O as programStatus, a as sha256Hex, h as sortDiagnostics, b as verificationGaps };