@voltro/plan 0.83.1 → 0.85.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,7 +1,7 @@
1
- import { Either as e, ParseResult as t, Schema as n } from "effect";
1
+ import { Cause as e, Exit as t, Schema as n, SchemaIssue as r } from "effect";
2
2
  //#region src/ids.ts
3
- var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
4
- if (!r.test(e)) throw Error(`plan id prefix "${e}" is not 2 to 10 capital letters or digits, starting with a letter`);
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
5
  return {
6
6
  prefix: e,
7
7
  plan: RegExp(`^${e}-PLAN-[0-9]{2}$`),
@@ -15,24 +15,24 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
15
15
  closureRun: RegExp(`^CLR-${e}-[0-9]{2}-[0-9]{3}$`),
16
16
  certificate: RegExp(`^CRT-${e}-[0-9]{2}-[0-9]{3}$`)
17
17
  };
18
- }, a = (e) => {
18
+ }, o = (e) => {
19
19
  let t = /-(?:PLAN-)?([0-9]{2})(?:-|$)/.exec(e);
20
20
  return t === null ? null : t[1];
21
- }, o = (e, t) => {
21
+ }, s = (e, t) => {
22
22
  let n = RegExp(`(?:^|-)(${e.prefix}-[0-9]{2}-[0-9]{3})(?:-|$)`).exec(t);
23
23
  return n === null ? null : n[1];
24
- }, s = (e) => JSON.stringify(c(e)), c = (e) => {
25
- if (Array.isArray(e)) return e.map(c);
24
+ }, c = (e) => JSON.stringify(l(e)), l = (e) => {
25
+ if (Array.isArray(e)) return e.map(l);
26
26
  if (typeof e == "object" && e) {
27
27
  let t = {};
28
28
  for (let n of Object.keys(e).sort()) {
29
29
  let r = e[n];
30
- r !== void 0 && (t[n] = c(r));
30
+ r !== void 0 && (t[n] = l(r));
31
31
  }
32
32
  return t;
33
33
  }
34
34
  return e;
35
- }, l = new Uint32Array([
35
+ }, u = new Uint32Array([
36
36
  1116352408,
37
37
  1899447441,
38
38
  3049323471,
@@ -97,7 +97,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
97
97
  2756734187,
98
98
  3204031479,
99
99
  3329325298
100
- ]), u = (e) => {
100
+ ]), d = (e) => {
101
101
  let t = new TextEncoder().encode(e), n = t.length * 8, r = new Uint8Array(t.length + 9 + 63 >> 6 << 6);
102
102
  r.set(t), r[t.length] = 128;
103
103
  let i = new DataView(r.buffer);
@@ -118,7 +118,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
118
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
119
  o[e] = o[e - 16] + t + o[e - 7] + n >>> 0;
120
120
  }
121
- let [t, n, r, c, u, d, f, p] = [
121
+ let [t, n, r, c, l, d, f, p] = [
122
122
  a[0],
123
123
  a[1],
124
124
  a[2],
@@ -129,20 +129,20 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
129
129
  a[7]
130
130
  ];
131
131
  for (let e = 0; e < 64; e++) {
132
- let i = p + (s(u, 6) ^ s(u, 11) ^ s(u, 25)) + (u & d ^ ~u & f) + l[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 = u, u = c + i >>> 0, c = r, r = n, n = t, t = i + a >>> 0;
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
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] + u >>> 0, a[5] = a[5] + d >>> 0, a[6] = a[6] + f >>> 0, a[7] = a[7] + p >>> 0;
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
136
  }
137
137
  return [...a].map((e) => e.toString(16).padStart(8, "0")).join("");
138
- }, d = (e) => `sha256:${u(s(e))}`, f = n.String.pipe(n.minLength(1)), p = n.String.pipe(n.pattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/)), m = n.String.pipe(n.pattern(/^sha256:[a-f0-9]{64}$/)), h = (e) => n.Array(e).pipe(n.filter((e) => new Set(e).size === e.length || "items are listed twice")), g = (e) => n.Array(e).pipe(n.minItems(1)), _ = n.String.pipe(n.pattern(/^[a-z0-9]+(?:[.:/-][a-z0-9]+)*$/)), v = [
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
139
  "proposed",
140
140
  "ready",
141
141
  "active",
142
142
  "blocked",
143
143
  "verified",
144
144
  "closed"
145
- ], y = [
145
+ ], b = [
146
146
  "proposed",
147
147
  "decomposing",
148
148
  "ready",
@@ -151,7 +151,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
151
151
  "closure-1",
152
152
  "closure-2",
153
153
  "complete"
154
- ], b = [
154
+ ], x = [
155
155
  "commit",
156
156
  "typecheck",
157
157
  "unit-test",
@@ -173,7 +173,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
173
173
  "research",
174
174
  "review",
175
175
  "artifact"
176
- ], x = [
176
+ ], ee = [
177
177
  "P0",
178
178
  "P1",
179
179
  "P2",
@@ -189,134 +189,168 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
189
189
  "documentation",
190
190
  "research",
191
191
  "tooling"
192
- ], C = (e) => n.String.pipe(n.pattern(e)), w = (e) => {
193
- let t = e.grammar, r = h(C(t.evidence)), i = n.Struct({
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
194
  id: C(t.source),
195
- uri: f,
196
- locator: f,
197
- excerpt: f,
198
- lineHint: n.optional(n.Int.pipe(n.greaterThanOrEqualTo(1))),
199
- contentHash: m,
200
- disposition: n.Literal("owned-by", "referenced-by", "decision", "non-goal"),
201
- targetId: n.optional(n.Union(C(t.workItem), C(t.adr)))
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)]))
202
207
  }), a = n.Struct({
203
208
  id: C(t.acceptance),
204
- statement: f,
205
- status: n.Literal("pending", "passed", "failed"),
209
+ statement: p,
210
+ status: n.Literals([
211
+ "pending",
212
+ "passed",
213
+ "failed"
214
+ ]),
206
215
  evidenceRefs: r
207
216
  }), o = n.Struct({
208
- rationale: f,
209
- reviewer: f,
210
- expiresAt: p,
211
- compensatingControl: f
217
+ rationale: p,
218
+ reviewer: p,
219
+ expiresAt: m,
220
+ compensatingControl: p
212
221
  }), s = n.Struct({
213
222
  id: C(t.requirement),
214
- statement: f,
215
- kinds: g(n.Literal(...b)),
216
- status: n.Literal("open", "satisfied", "waived"),
223
+ statement: p,
224
+ kinds: _(n.Literals(x)),
225
+ status: n.Literals([
226
+ "open",
227
+ "satisfied",
228
+ "waived"
229
+ ]),
217
230
  evidenceRefs: r,
218
231
  waiver: n.optional(o)
219
232
  }), c = n.Struct({
220
- capabilities: n.Array(f),
221
- journeys: n.Array(f),
222
- adrs: h(C(t.adr)),
223
- risks: n.Array(f),
224
- relatedTo: h(C(t.workItem))
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))
225
238
  }), l = n.Struct({
226
- reason: f,
227
- owner: f,
228
- nextAction: f,
229
- recheckTrigger: f,
230
- since: p
239
+ reason: p,
240
+ owner: p,
241
+ nextAction: p,
242
+ recheckTrigger: p,
243
+ since: m
231
244
  }), u = n.Struct({
232
- from: n.NullOr(n.Literal(...v)),
233
- to: n.Literal(...v),
234
- reason: f,
235
- actor: f,
236
- at: p,
245
+ from: n.NullOr(n.Literals(y)),
246
+ to: n.Literals(y),
247
+ reason: p,
248
+ actor: p,
249
+ at: m,
237
250
  evidenceRefs: r
238
251
  }), d = n.Struct({
239
252
  id: C(t.workItem),
240
- title: f,
253
+ title: p,
241
254
  ownerPlan: C(t.plan),
242
- slice: f,
243
- kind: n.Literal(...S),
244
- priority: n.Literal(...x),
255
+ slice: p,
256
+ kind: n.Literals(S),
257
+ priority: n.Literals(ee),
245
258
  mandatory: n.Boolean,
246
- status: n.Literal(...v),
247
- owners: n.Array(f),
248
- repos: g(n.Literal(...e.repos)),
249
- sourceRefs: g(i),
250
- dependsOn: h(C(t.workItem)),
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)),
251
264
  links: c,
252
- acceptance: g(a),
253
- evidenceRequirements: g(s),
265
+ acceptance: _(a),
266
+ evidenceRequirements: _(s),
254
267
  evidenceRefs: r,
255
- impactTags: g(_),
256
- statusHistory: g(u),
268
+ impactTags: _(v),
269
+ statusHistory: _(u),
257
270
  blocker: n.optional(l),
258
271
  supersededBy: n.optional(C(t.workItem)),
259
- createdAt: p,
260
- updatedAt: p
261
- }), w = n.Struct({
272
+ createdAt: m,
273
+ updatedAt: m
274
+ }), f = n.Struct({
262
275
  id: C(t.plan),
263
- number: n.String.pipe(n.pattern(/^[0-9]{2}$/)),
264
- title: f,
265
- source: f,
266
- sourceRevision: m,
267
- status: n.Literal(...y),
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),
268
281
  decompositionComplete: n.Boolean,
269
- updatedAt: p
282
+ updatedAt: m
283
+ }), w = n.Struct({
284
+ rulesVersion: p,
285
+ sourceRevision: h,
286
+ blockKinds: n.Array(p),
287
+ extractedAt: m
270
288
  }), T = n.Struct({
271
- rulesVersion: f,
272
- sourceRevision: m,
273
- blockKinds: n.Array(f),
274
- extractedAt: p
275
- }), E = n.Struct({
276
289
  extractedSources: n.Int,
277
290
  disposedSources: n.Int,
278
291
  unmatchedSources: n.Int,
279
292
  duplicateSources: n.Int,
280
- sourceIdsHash: m,
281
- result: n.Literal("complete", "incomplete")
293
+ sourceIdsHash: h,
294
+ result: n.Literals(["complete", "incomplete"])
282
295
  });
283
296
  return n.Struct({
284
297
  schemaVersion: n.Literal(e.instanceVersion),
285
- plan: w,
286
- extraction: T,
298
+ plan: f,
299
+ extraction: w,
287
300
  workItems: n.Array(d),
288
- coverage: E
301
+ coverage: T
289
302
  });
290
303
  }, T = (e) => n.Struct({
291
304
  id: C(e.grammar.evidence),
292
- kind: n.Literal(...b),
293
- title: f,
294
- uri: f,
295
- fingerprint: n.optional(m),
296
- result: n.Literal("passed", "failed", "informational"),
297
- subjectRefs: n.Array(f),
298
- command: n.optional(f),
299
- environment: n.optional(f),
300
- producedBy: f,
301
- reviewedBy: n.optional(f),
302
- observedAt: p,
303
- idempotencyKey: n.optional(f)
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)
304
321
  }), E = (e) => n.Struct({
305
322
  id: C(e.grammar.finding),
306
- title: f,
307
- severity: n.Literal("critical", "high", "medium", "low"),
308
- status: n.Literal("open", "resolved", "accepted-risk"),
309
- source: n.Literal("contract-reconciliation", "adversarial-audit", "test", "incident", "research", "review", "production"),
310
- targetWorkItems: g(C(e.grammar.workItem)),
311
- impactTags: n.Array(f),
312
- evidenceRefs: n.optional(h(C(e.grammar.evidence))),
313
- openedAt: p,
314
- resolvedAt: n.optional(p)
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)
315
349
  }), D = {
316
350
  security: 0,
317
351
  error: 1,
318
352
  warning: 2
319
- }, O = (e, t) => (e ?? "") < (t ?? "") ? -1 : +((e ?? "") > (t ?? "")), k = (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)), ee = (e, t = !1) => e.some((e) => e.severity !== "warning" || t), A = {
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 = {
320
354
  proposed: ["ready", "blocked"],
321
355
  ready: [
322
356
  "active",
@@ -335,9 +369,12 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
335
369
  ],
336
370
  verified: ["closed", "active"],
337
371
  closed: ["active"]
338
- }, j = /* @__PURE__ */ new Set(["verified", "closed"]), M = (e, t) => {
372
+ }, A = /* @__PURE__ */ new Set(["verified", "closed"]), j = (e, t) => {
339
373
  let n = /* @__PURE__ */ new Map();
340
- for (let e of t) if (e.status === "open") for (let t of e.targetWorkItems) n.set(t, [...n.get(t) ?? [], e]);
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
+ }
341
378
  let r = /* @__PURE__ */ new Map();
342
379
  for (let t of e) for (let e of new Set(t.subjectRefs)) {
343
380
  let n = r.get(e);
@@ -348,50 +385,65 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
348
385
  bySubject: r,
349
386
  openFindings: n
350
387
  };
351
- }, N = (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), P = (e, t, n, r) => {
352
- let i = [], a = t.bySubject.get(e.id) ?? [], o = (n, r) => n.some((n) => {
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) => {
353
405
  let i = t.evidence.get(n);
354
- return i !== void 0 && N(i, e, e.id, r);
406
+ return i !== void 0 && M(i, e, e.id, r);
355
407
  });
356
- for (let t of e.acceptance) !a.some((n) => N(n, e, t.id)) && !(t.status === "passed" && o(t.evidenceRefs)) && i.push(`acceptance ${t.id} has no passing evidence`);
357
- 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`) : !a.some((n) => N(n, e, t.id, t.kinds)) && !o(t.evidenceRefs, t.kinds) && i.push(`requirement ${t.id} has no passing ${t.kinds.join("/")} evidence`);
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`);
358
410
  for (let n of t.openFindings.get(e.id) ?? []) i.push(`finding ${n.id} is open`);
359
411
  for (let t of e.dependsOn) {
360
412
  let e = n(t);
361
413
  e !== "verified" && e !== "closed" && i.push(`dependency ${t} is ${e ?? "missing"}`);
362
414
  }
363
415
  return i;
364
- }, F = (e, t) => t.length === 0 ? "verified" : j.has(e.status) ? "active" : e.status, I = "voltro-plan-closure/v1", L = n.String.pipe(n.minLength(1)), R = n.String.pipe(n.pattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/)), te = n.String.pipe(n.pattern(/^sha256:[a-f0-9]{64}$/)), z = (e) => {
365
- let t = e.grammar, r = (e) => n.String.pipe(n.pattern(e)), i = n.Struct({
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({
366
418
  id: r(t.closureRun),
367
- pass: n.Literal("contract", "adversarial"),
368
- reviewer: L,
369
- reviewContext: L,
370
- planRevision: te,
371
- completedAt: R,
419
+ pass: n.Literals(["contract", "adversarial"]),
420
+ reviewer: I,
421
+ reviewContext: I,
422
+ planRevision: R,
423
+ completedAt: L,
372
424
  findingRefs: n.Array(r(t.finding)),
373
- result: n.Literal("passed", "findings"),
374
- toolVersion: L
425
+ result: n.Literals(["passed", "findings"]),
426
+ toolVersion: I
375
427
  }), a = n.Struct({
376
428
  id: r(t.certificate),
377
429
  plan: r(t.plan),
378
- planRevision: te,
430
+ planRevision: R,
379
431
  workItems: n.Array(r(t.workItem)),
380
432
  evidenceRefs: n.Array(r(t.evidence)),
381
- closureRuns: n.Array(r(t.closureRun)).pipe(n.itemsCount(2)),
382
- confirmedBy: L,
383
- issuedAt: R,
384
- toolVersion: L
433
+ closureRuns: n.Array(r(t.closureRun)).pipe(n.check(n.isBetweenLength(2, 2))),
434
+ confirmedBy: I,
435
+ issuedAt: L,
436
+ toolVersion: I
385
437
  });
386
438
  return n.Struct({
387
- schemaVersion: n.Literal(I),
439
+ schemaVersion: n.Literal(F),
388
440
  plan: r(t.plan),
389
441
  runs: n.Array(i),
390
442
  certificates: n.Array(a)
391
443
  });
392
444
  }, B = (e, t, n, r = () => void 0, i = /* @__PURE__ */ new Map()) => {
393
445
  let a = new Set(e.workItems.map((e) => e.id));
394
- return d({
446
+ return f({
395
447
  instance: e,
396
448
  dependencies: [...new Set(e.workItems.flatMap((e) => e.dependsOn).filter((e) => !a.has(e)))].sort().map((e) => {
397
449
  let t = r(e);
@@ -423,7 +475,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
423
475
  related: [],
424
476
  nextAction: n
425
477
  }
426
- }), 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")}`, ne = (e, t, n, r, i, a, o, s) => {
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) => {
427
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");
428
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);
429
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)));
@@ -463,7 +515,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
463
515
  return {
464
516
  outcome: "recorded",
465
517
  file: {
466
- schemaVersion: I,
518
+ schemaVersion: F,
467
519
  plan: c,
468
520
  runs: [...p, h],
469
521
  certificates: [...n?.certificates ?? [], ...g === null ? [] : [g]]
@@ -478,27 +530,34 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
478
530
  locator: r,
479
531
  related: [...a].sort(),
480
532
  nextAction: i
481
- }), G = (r, i, a, o) => {
482
- let s = n.decodeUnknownEither(i, {
533
+ }), ie = r.makeFormatterStandardSchemaV1(), G = (r, i, a, o) => {
534
+ let s = n.decodeUnknownExit(i, {
483
535
  onExcessProperty: "error",
484
536
  errors: "all"
485
537
  }), c = [];
486
538
  for (let n of r) {
487
539
  let r = s(n.content);
488
- if (e.isRight(r)) {
540
+ if (t.isSuccess(r)) {
489
541
  c.push({
490
542
  file: n.file,
491
- value: r.right
543
+ value: r.value
492
544
  });
493
545
  continue;
494
546
  }
495
- for (let e of t.ArrayFormatter.formatErrorSync(r.left)) o.push(W("schema/invalid", "error", `${a}: ${e.message}`, {
496
- file: n.file,
497
- path: e.path.join(".") || "(root)"
498
- }, "Correct the field to the v1 contract; unknown fields are not allowed."));
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
+ }
499
558
  }
500
559
  return c;
501
- }, re = [
560
+ }, ae = [
502
561
  ["private key", /-----BEGIN [A-Z ]*PRIVATE KEY-----/],
503
562
  ["Stripe key", /\b[sr]k_(live|test)_[A-Za-z0-9]{16,}/],
504
563
  ["GitHub token", /\bgh[pousr]_[A-Za-z0-9]{30,}/],
@@ -506,7 +565,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
506
565
  ["Slack token", /\bxox[abprs]-[A-Za-z0-9-]{10,}/],
507
566
  ["bearer token", /\bBearer\s+[A-Za-z0-9._~+/-]{20,}/]
508
567
  ], K = (e, t, n, r) => {
509
- if (typeof e == "string") for (let [i, a] of re) a.test(e) && r.push(W("security/secret", "security", `a ${i} is in the plan artefact`, {
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`, {
510
569
  file: t,
511
570
  path: n
512
571
  }, "Remove the secret, rotate it, and reference where it is kept instead."));
@@ -528,7 +587,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
528
587
  };
529
588
  for (let n of [...e.keys()].sort()) t.has(n) || r(n);
530
589
  return [...n.values()].sort((e, t) => e.join(">") < t.join(">") ? -1 : 1);
531
- }, ie = (e) => {
590
+ }, oe = (e) => {
532
591
  let { shape: t, clock: n } = e, r = t.grammar, i = [];
533
592
  for (let t of [
534
593
  ...e.instances,
@@ -536,7 +595,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
536
595
  ...e.findings ?? [],
537
596
  ...e.closures ?? []
538
597
  ]) K(t.content, t.file, "", i);
539
- let s = G(e.instances, w(t), "plan instance", i).map(({ file: e, value: t }) => ({
598
+ let a = G(e.instances, w(t), "plan instance", i).map(({ file: e, value: t }) => ({
540
599
  file: e,
541
600
  instance: t
542
601
  })), c = (e.evidence ?? []).flatMap((e) => G(Array.isArray(e.content) ? e.content.map((t, n) => ({
@@ -546,53 +605,53 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
546
605
  file: `${e.file}#${n}`,
547
606
  content: t
548
607
  })) : [e], E(t), "finding", i).map((e) => e.value)), u = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Map();
549
- for (let { file: t, instance: n } of s) {
550
- let s = n.plan;
551
- u.has(s.id) && i.push(W("identity/duplicate-plan", "error", `${s.id} is declared in two files`, {
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`, {
552
611
  file: t,
553
- plan: s.id
554
- }, "Keep one instance file per plan.", [u.get(s.id)])), u.set(s.id, t), a(s.id) !== s.number && i.push(W("identity/plan-number", "error", `${s.id} has number ${s.number}`, {
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}`, {
555
614
  file: t,
556
- plan: s.id,
615
+ plan: a.id,
557
616
  path: "plan.number"
558
617
  }, "Make the number match the plan id."));
559
618
  for (let e of n.workItems) {
560
619
  let n = {
561
620
  file: t,
562
- plan: s.id,
621
+ plan: a.id,
563
622
  workItem: e.id
564
623
  };
565
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, {
566
625
  file: t,
567
626
  item: e
568
- }), (e.ownerPlan !== s.id || a(e.id) !== s.number) && i.push(W("identity/owner-mismatch", "error", `${e.id} is owned by ${e.ownerPlan} but lives in ${s.id}`, n, "Move the item to its plan’s file or correct ownerPlan."));
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."));
569
628
  let c = [
570
629
  ...e.acceptance.map((e) => e.id),
571
630
  ...e.evidenceRequirements.map((e) => e.id),
572
631
  ...e.sourceRefs.map((e) => e.id)
573
632
  ];
574
- for (let t of c) o(r, t) !== e.id && i.push(W("identity/child-id", "error", `${t} does not belong to ${e.id}`, n, `Number it under ${e.id}.`));
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}.`));
575
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`, {
576
635
  ...n,
577
636
  path: `sourceRefs.${t.id}`
578
637
  }, "Name the work item or decision it points to."));
579
638
  }
580
639
  if (e.sources !== void 0) {
581
- let r = e.sources.get(s.source);
582
- r == null ? i.push(W("source/missing", "error", `${s.id}'s source ${s.source} cannot be read`, {
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`, {
583
642
  file: t,
584
- plan: s.id,
643
+ plan: a.id,
585
644
  path: "plan.source"
586
- }, "Restore the source or point the plan at it.")) : (r !== s.sourceRevision || r !== n.extraction.sourceRevision) && i.push(W("source/stale", "error", `${s.source} changed since ${s.id} was decomposed (${s.sourceRevision.slice(0, 19)}… → ${r.slice(0, 19)}…)`, {
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)}…)`, {
587
646
  file: t,
588
- plan: s.id,
647
+ plan: a.id,
589
648
  path: "plan.sourceRevision"
590
649
  }, "Regenerate the plan’s decomposition from its source; closure waits until then."));
591
650
  }
592
651
  let c = n.workItems.reduce((e, t) => e + t.sourceRefs.length, 0);
593
- (n.coverage.disposedSources !== c || n.coverage.unmatchedSources > 0 || n.coverage.duplicateSources > 0 || s.decompositionComplete && n.coverage.result !== "complete") && i.push(W("source/coverage", "error", `${s.id} claims ${n.coverage.disposedSources} disposed sources (${n.coverage.unmatchedSources} unmatched, ${n.coverage.duplicateSources} duplicate, ${n.coverage.result}) and holds ${c}`, {
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}`, {
594
653
  file: t,
595
- plan: s.id,
654
+ plan: a.id,
596
655
  path: "coverage"
597
656
  }, "Regenerate the plan’s decomposition from its source."));
598
657
  }
@@ -643,15 +702,15 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
643
702
  path: e.id
644
703
  }, "Write the waiver: rationale, reviewer, expiry and compensating control."));
645
704
  }
646
- let m = M(c, l), h = /* @__PURE__ */ new Map(), g = /* @__PURE__ */ new Map(), _ = /* @__PURE__ */ new Set(), v = (e) => {
705
+ let m = j(c, l), h = /* @__PURE__ */ new Map(), g = /* @__PURE__ */ new Map(), _ = /* @__PURE__ */ new Set(), v = (e) => {
647
706
  let t = h.get(e);
648
707
  if (t !== void 0) return t;
649
708
  let r = d.get(e);
650
709
  if (r === void 0 || _.has(e)) return;
651
710
  _.add(e);
652
- let i = P(r.item, m, v, n);
711
+ let i = N(r.item, m, v, n);
653
712
  _.delete(e);
654
- let a = F(r.item, i);
713
+ let a = P(r.item, i);
655
714
  return h.set(e, a), g.set(e, i), a;
656
715
  };
657
716
  for (let { file: e, item: t } of [...d.values()].sort((e, t) => e.item.id < t.item.id ? -1 : 1)) {
@@ -661,10 +720,10 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
661
720
  workItem: t.id
662
721
  }, r = null;
663
722
  t.statusHistory.forEach((e, a) => {
664
- e.from === r ? e.from !== null && !A[e.from].includes(e.to) && i.push(W("state/forbidden-transition", "error", `${t.id} went ${e.from} → ${e.to}`, {
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}`, {
665
724
  ...n,
666
725
  path: `statusHistory[${a}]`
667
- }, `From ${e.from} an item goes to ${A[e.from].join(", ")}.`)) : i.push(W("state/history-break", "error", `${t.id} history step ${a + 1} starts at ${e.from ?? "nothing"}, after ${r ?? "nothing"}`, {
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"}`, {
668
727
  ...n,
669
728
  path: `statusHistory[${a}]`
670
729
  }, "Record every transition in order.")), r = e.to;
@@ -691,7 +750,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
691
750
  plan: r.plan
692
751
  }, "Remove it or correct the plan id."));
693
752
  let b = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map();
694
- for (let { instance: t } of s) {
753
+ for (let { instance: t } of a) {
695
754
  let n = B(t, c, l, (e) => h.get(e), e.decisions);
696
755
  b.set(t.plan.id, n);
697
756
  let r = y.get(t.plan.id), a = V(r?.record, n);
@@ -706,7 +765,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
706
765
  }
707
766
  for (let { file: e, item: t } of [...d.values()].sort((e, t) => e.item.id < t.item.id ? -1 : 1)) {
708
767
  let n = h.get(t.id);
709
- j.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"}`, {
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"}`, {
710
769
  file: e,
711
770
  plan: t.ownerPlan,
712
771
  workItem: t.id,
@@ -715,7 +774,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
715
774
  }
716
775
  return {
717
776
  program: {
718
- instances: s,
777
+ instances: a,
719
778
  items: d,
720
779
  evidence: c,
721
780
  findings: l,
@@ -725,9 +784,9 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
725
784
  revisions: b,
726
785
  closure: x
727
786
  },
728
- diagnostics: k(i)
787
+ diagnostics: te(i)
729
788
  };
730
- }, J = () => Object.fromEntries(v.map((e) => [e, 0])), ae = (e) => {
789
+ }, J = () => Object.fromEntries(y.map((e) => [e, 0])), se = (e) => {
731
790
  let t = J();
732
791
  return {
733
792
  plans: e.instances.map(({ file: n, instance: r }) => {
@@ -757,12 +816,12 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
757
816
  mandatory: t,
758
817
  items: e.items.size
759
818
  };
760
- }, oe = {
819
+ }, ce = {
761
820
  P0: 0,
762
821
  P1: 1,
763
822
  P2: 2,
764
823
  P3: 3
765
- }, se = (e, t, n) => {
824
+ }, le = (e, t, n) => {
766
825
  if (n) return {
767
826
  outcome: "invalid",
768
827
  reason: "lint reports blocking diagnostics; fix them first (`voltro plan lint`)"
@@ -775,39 +834,49 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
775
834
  if (a.every(({ item: e }) => ["verified", "closed"].includes(r(e.id) ?? ""))) return { outcome: "complete" };
776
835
  let o = (e) => e.dependsOn.every((e) => ["verified", "closed"].includes(r(e) ?? "")), s = (e) => [
777
836
  r(e.id) === "active" ? 0 : 1,
778
- oe[e.priority],
837
+ ce[e.priority],
779
838
  e.kind === "security" ? 0 : 1,
780
839
  +!i.has(e.ownerPlan),
781
840
  +!o(e),
782
841
  e.id
783
842
  ], c = (e, t) => {
784
- let [n, r] = [s(e.item), s(t.item)];
785
- for (let e = 0; e < n.length; e++) if (n[e] !== r[e]) return n[e] < r[e] ? -1 : 1;
786
- return 0;
787
- }, l = a.filter(({ item: e }) => ["active", "ready"].includes(r(e.id) ?? "")).sort(c)[0];
788
- if (l !== void 0) {
789
- let e = r(l.item.id);
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);
790
859
  return {
791
860
  outcome: "item",
792
- workItem: l.item,
793
- file: l.file,
861
+ workItem: e.item,
862
+ file: e.file,
794
863
  why: [
795
- e === "active" ? "it is already active" : "it is ready",
796
- `priority ${l.item.priority}`,
797
- ...l.item.kind === "security" ? ["security work comes first"] : [],
798
- i.has(l.item.ownerPlan) ? `${l.item.ownerPlan} is already under way` : "no started plan has workable items",
799
- o(l.item) ? "its dependencies are verified" : "no dependency-ready item is left, so it waits on its dependencies"
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"
800
869
  ]
801
870
  };
802
871
  }
803
- let u = a.filter(({ item: e }) => r(e.id) === "proposed").sort(c), d = a.filter(({ item: e }) => r(e.id) === "blocked").length;
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;
804
873
  return {
805
874
  outcome: "blocked",
806
- reason: `no mandatory item is active or ready: ${u.length} proposed await readiness${d > 0 ? `, ${d} blocked` : ""}`,
807
- prepare: u[0]?.item ?? null,
808
- file: u[0]?.file ?? null
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
809
878
  };
810
- }, Y = "voltro-plan-command/v1", ce = (e) => ({
879
+ }, Y = "voltro-plan-command/v1", ue = (e) => ({
811
880
  schemaVersion: Y,
812
881
  ...e
813
882
  }), X = {
@@ -819,12 +888,12 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
819
888
  provider: 5,
820
889
  security: 6,
821
890
  internal: 70
822
- }, Z = "voltro-plan-manifest/v1", Q = n.String.pipe(n.minLength(1), n.filter((e) => !e.startsWith("/") && !/^[A-Za-z]:/.test(e) && !e.split("/").includes("..") || "a path stays inside the project root")), le = n.Struct({
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({
823
892
  schemaVersion: n.Literal(Z),
824
- prefix: n.String.pipe(n.pattern(/^[A-Z][A-Z0-9]{1,9}$/)),
825
- instanceVersion: n.String.pipe(n.minLength(1)),
826
- repos: n.Array(n.String.pipe(n.minLength(1))).pipe(n.minItems(1)),
827
- instances: n.Array(Q).pipe(n.minItems(1)),
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))),
828
897
  evidence: n.optional(n.Array(Q)),
829
898
  evidenceDir: n.optional(Q),
830
899
  closureDir: n.optional(Q),
@@ -841,14 +910,14 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
841
910
  related: [],
842
911
  nextAction: r
843
912
  }
844
- }), ue = [
913
+ }), fe = [
845
914
  /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
846
915
  /\b[sr]k_(live|test)_[A-Za-z0-9]{16,}/,
847
916
  /\bgh[pousr]_[A-Za-z0-9]{30,}/,
848
917
  /\bAKIA[0-9A-Z]{16}\b/,
849
918
  /\bxox[abprs]-[A-Za-z0-9-]{10,}/,
850
919
  /\bBearer\s+[A-Za-z0-9._~+/-]{20,}/
851
- ], de = (e, t, n) => {
920
+ ], pe = (e, t, n) => {
852
921
  let r = e.items.get(n.workItem)?.item;
853
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);
854
923
  if ([
@@ -857,8 +926,8 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
857
926
  n.producedBy,
858
927
  n.command ?? "",
859
928
  n.environment ?? ""
860
- ].some((e) => ue.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);
861
- if (!b.includes(n.kind)) return $("evidence/kind", "error", `"${n.kind}" is not an evidence kind`, `Use one of: ${b.join(", ")}.`, r.id, X.usage);
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);
862
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);
863
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);
864
933
  let i = [r.id];
@@ -882,14 +951,14 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
882
951
  if (n.idempotencyKey !== void 0) {
883
952
  let t = e.evidence.find((e) => e.idempotencyKey === n.idempotencyKey);
884
953
  if (t !== void 0) {
885
- let { id: e, observedAt: i, idempotencyKey: o, ...c } = t;
886
- return s(c) === s(a) ? {
954
+ let { id: e, observedAt: i, idempotencyKey: o, ...s } = t;
955
+ return c(s) === c(a) ? {
887
956
  outcome: "same",
888
957
  evidence: t
889
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);
890
959
  }
891
960
  }
892
- let o = `EVD-${r.id}-`, c = e.evidence.filter((e) => e.id.startsWith(o)).map((e) => Number(e.id.slice(o.length))), l = String(Math.max(0, ...c) + 1).padStart(3, "0");
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");
893
962
  return t.evidence.test(`${o}${l}`) ? {
894
963
  outcome: "add",
895
964
  evidence: {
@@ -899,35 +968,48 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
899
968
  ...n.idempotencyKey === void 0 ? {} : { idempotencyKey: n.idempotencyKey }
900
969
  }
901
970
  } : $("evidence/exhausted", "error", `${r.id} has no evidence number left`, "Supersede the item.", r.id, X.failed);
902
- }, fe = [
971
+ }, me = [
903
972
  ".",
904
973
  ":",
905
974
  "/",
906
975
  "-"
907
- ], pe = (e, t) => e === t || fe.some((n) => t.startsWith(e + n) || e.startsWith(t + n)), me = (e, t) => {
976
+ ], he = (e, t) => e === t || me.some((n) => t.startsWith(e + n) || e.startsWith(t + n)), ge = (e, t) => {
908
977
  let n = /* @__PURE__ */ new Map(), r = (e, t) => {
909
978
  let r = n.get(e) ?? /* @__PURE__ */ new Set();
910
979
  r.add(t), n.set(e, r);
911
980
  };
912
- for (let { item: n } of e.items.values()) for (let e of t.tags) for (let t of n.impactTags) pe(e, t) && r(n.id, `tag ${t}`);
913
- let i = [], a = new Map(e.instances.map(({ file: e, instance: t }) => [e, t]));
914
- for (let n of [...new Set(t.files)].sort()) {
915
- let t = !1, o = a.get(n);
916
- if (o !== void 0) {
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) {
917
990
  t = !0;
918
- for (let e of o.workItems) r(e.id, `its plan file ${n} changed`);
991
+ for (let t of n.workItems) r(t.id, `its plan file ${e} changed`);
919
992
  }
920
- for (let { item: i } of e.items.values()) i.sourceRefs.some((e) => e.uri === n) && (t = !0, r(i.id, `its source ${n} changed`));
921
- t || i.push(n);
993
+ for (let n of o.get(e) ?? []) t = !0, r(n, `its source ${e} changed`);
994
+ t || i.push(e);
922
995
  }
923
- let o = /* @__PURE__ */ new Map();
924
- for (let { item: t } of e.items.values()) for (let e of t.dependsOn) o.set(e, [...o.get(e) ?? [], t.id]);
925
- let s = /* @__PURE__ */ new Map(), c = [...n.keys()].sort().map((e) => [e]), l = new Set(n.keys());
926
- for (; c.length > 0;) {
927
- let e = c.shift();
928
- for (let t of [...o.get(e.at(-1)) ?? []].sort()) l.has(t) || (l.add(t), s.set(t, [...e, t]), c.push([...e, t]));
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
+ }
929
1011
  }
930
- let u = [...l], d = [...new Set(u.map((t) => e.items.get(t).item.ownerPlan))].sort(), f = d.flatMap((t) => {
1012
+ let d = [...u], f = [...new Set(d.map((t) => e.items.get(t).item.ownerPlan))].sort(), p = f.flatMap((t) => {
931
1013
  let n = e.closure.get(t);
932
1014
  return n?.state === "certified" ? [{
933
1015
  plan: t,
@@ -939,14 +1021,14 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
939
1021
  workItem: e,
940
1022
  because: [...t].sort()
941
1023
  })).sort((e, t) => e.workItem < t.workItem ? -1 : 1),
942
- transitive: [...s.entries()].map(([e, t]) => ({
1024
+ transitive: [...c.entries()].map(([e, t]) => ({
943
1025
  workItem: e,
944
1026
  path: t
945
1027
  })).sort((e, t) => e.workItem < t.workItem ? -1 : 1),
946
- plans: d,
947
- certificates: f,
1028
+ plans: f,
1029
+ certificates: p,
948
1030
  unknown: i
949
1031
  };
950
1032
  };
951
1033
  //#endregion
952
- export { I as CLOSURE_SCHEMA_VERSION, Y as COMMAND_SCHEMA_VERSION, j as DERIVED, b as EVIDENCE_KINDS, X as EXIT, S as ITEM_KINDS, Z as MANIFEST_SCHEMA_VERSION, le as Manifest, y as PLAN_STATUSES, x as PRIORITIES, A as TRANSITIONS, v as WORK_ITEM_STATUSES, ee as blocks, s as canonicalJson, z as closureFileSchema, V as closureState, ne as decideClosure, de as decideEvidence, q as dependencyCycles, F as derivedStatus, ce as envelope, T as evidenceSchema, E as findingSchema, d as fingerprint, i as idGrammar, me as impactOf, M as indexEvidence, ie as lintProgram, se as nextItem, o as ownerItemOf, w as planInstanceSchema, a as planNumberOf, B as planRevision, ae as programStatus, u as sha256Hex, k as sortDiagnostics, P as verificationGaps };
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 };