@jevitate/cli 0.24.0 → 0.25.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.
Files changed (45) hide show
  1. package/dist/acceptance-audit-approval-script.d.ts +6 -0
  2. package/dist/acceptance-audit-approval-script.js +20 -0
  3. package/dist/acceptance-audit-approval-script.js.map +1 -0
  4. package/dist/acceptance-audit-cli.d.ts +2 -0
  5. package/dist/acceptance-audit-cli.js +34 -0
  6. package/dist/acceptance-audit-cli.js.map +1 -0
  7. package/dist/acceptance-audit-render.d.ts +4 -0
  8. package/dist/acceptance-audit-render.js +12 -0
  9. package/dist/acceptance-audit-render.js.map +1 -0
  10. package/dist/acceptance-audit-schema.d.ts +32 -0
  11. package/dist/acceptance-audit-schema.js +19 -0
  12. package/dist/acceptance-audit-schema.js.map +1 -0
  13. package/dist/acceptance-audit-suite.d.ts +14 -0
  14. package/dist/acceptance-audit-suite.js +34 -0
  15. package/dist/acceptance-audit-suite.js.map +1 -0
  16. package/dist/acceptance-audit.d.ts +16 -0
  17. package/dist/acceptance-audit.js +36 -0
  18. package/dist/acceptance-audit.js.map +1 -0
  19. package/dist/bin.js +1008 -447
  20. package/dist/build-info.generated.js +3 -3
  21. package/dist/explore-goal.d.ts +3 -1
  22. package/dist/explore-goal.js +1 -0
  23. package/dist/explore-goal.js.map +1 -1
  24. package/dist/goal-run-flags.js +2 -2
  25. package/dist/goal-run-flags.js.map +1 -1
  26. package/dist/journeeze-hint.d.ts +35 -0
  27. package/dist/journeeze-hint.js +77 -0
  28. package/dist/journeeze-hint.js.map +1 -0
  29. package/dist/journey-cli/review.js +16 -2
  30. package/dist/journey-cli/review.js.map +1 -1
  31. package/dist/journey-cli/verify.js +33 -7
  32. package/dist/journey-cli/verify.js.map +1 -1
  33. package/dist/journey-verify.d.ts +23 -0
  34. package/dist/journey-verify.js +34 -0
  35. package/dist/journey-verify.js.map +1 -1
  36. package/dist/mcp-cli-tools.js +2 -1
  37. package/dist/mcp-cli-tools.js.map +1 -1
  38. package/dist/program.js +2 -0
  39. package/dist/program.js.map +1 -1
  40. package/dist/report-cli.js +21 -2
  41. package/dist/report-cli.js.map +1 -1
  42. package/dist/ux-usability/done-adjudication.d.ts +2 -2
  43. package/package.json +10 -10
  44. package/skills/jevitate-promotion-ready/SKILL.md +5 -1
  45. package/skills/jevitate-record/SKILL.md +3 -2
package/dist/bin.js CHANGED
@@ -3631,8 +3631,8 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
3631
3631
  const _normalized = cached(() => normalizeDef(def));
3632
3632
  const memo2 = globalConfig.memoizer;
3633
3633
  const generateFastpass = (shape3) => {
3634
- const normalized = _normalized.value;
3635
- const syms = normalized.symbolKeys;
3634
+ const normalized2 = _normalized.value;
3635
+ const syms = normalized2.symbolKeys;
3636
3636
  const doc = new Doc(["payload", "ctx"], { shape: shape3, inst, memo: memo2, syms });
3637
3637
  const parseStr = (k) => `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
3638
3638
  const prefixStr = (id, k) => `
@@ -3650,11 +3650,11 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
3650
3650
  doc.write(`const input = payload.value;`);
3651
3651
  const ids = /* @__PURE__ */ Object.create(null);
3652
3652
  let counter = 0;
3653
- for (const key2 of normalized.allKeys) {
3653
+ for (const key2 of normalized2.allKeys) {
3654
3654
  ids[key2] = `key_${counter++}`;
3655
3655
  }
3656
3656
  doc.write(memo2 ? `const newResult = memo.alloc(inst, payload, {}, ctx);` : `const newResult = {};`);
3657
- for (const key2 of normalized.allKeys) {
3657
+ for (const key2 of normalized2.allKeys) {
3658
3658
  if (key2 === "__proto__")
3659
3659
  continue;
3660
3660
  const id = ids[key2];
@@ -19483,23 +19483,23 @@ function fromJSONSchema(schema, params) {
19483
19483
  if (typeof schema === "boolean") {
19484
19484
  return schema ? z.any() : z.never();
19485
19485
  }
19486
- let normalized;
19486
+ let normalized2;
19487
19487
  try {
19488
- normalized = JSON.parse(JSON.stringify(schema));
19488
+ normalized2 = JSON.parse(JSON.stringify(schema));
19489
19489
  } catch {
19490
19490
  throw new Error("fromJSONSchema input is not valid JSON (possibly cyclic); use $defs/$ref for recursive schemas");
19491
19491
  }
19492
- const version2 = detectVersion(normalized, params?.defaultTarget);
19493
- const defs = normalized.$defs || normalized.definitions || {};
19492
+ const version2 = detectVersion(normalized2, params?.defaultTarget);
19493
+ const defs = normalized2.$defs || normalized2.definitions || {};
19494
19494
  const ctx = {
19495
19495
  version: version2,
19496
19496
  defs,
19497
19497
  refs: /* @__PURE__ */ new Map(),
19498
19498
  processing: /* @__PURE__ */ new Set(),
19499
- rootSchema: normalized,
19499
+ rootSchema: normalized2,
19500
19500
  registry: params?.registry ?? globalRegistry
19501
19501
  };
19502
- return convertSchema(normalized, ctx);
19502
+ return convertSchema(normalized2, ctx);
19503
19503
  }
19504
19504
 
19505
19505
  // ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/visit.js
@@ -21423,15 +21423,15 @@ function asNumber(value) {
21423
21423
  return numericValue;
21424
21424
  }
21425
21425
  function normalizeObject(obj, normalizer) {
21426
- const normalized = {};
21426
+ const normalized2 = {};
21427
21427
  for (const u in obj) {
21428
21428
  if (hasOwnProperty(obj, u)) {
21429
21429
  const v = obj[u];
21430
21430
  if (v === void 0 || v === null) continue;
21431
- normalized[normalizer(u)] = asNumber(v);
21431
+ normalized2[normalizer(u)] = asNumber(v);
21432
21432
  }
21433
21433
  }
21434
- return normalized;
21434
+ return normalized2;
21435
21435
  }
21436
21436
  function formatOffset(offset2, format) {
21437
21437
  const hours = Math.trunc(Math.abs(offset2 / 60)), minutes = Math.trunc(Math.abs(offset2 % 60)), sign = offset2 >= 0 ? "+" : "-";
@@ -22432,7 +22432,7 @@ var Duration = class _Duration {
22432
22432
  * @private
22433
22433
  */
22434
22434
  static normalizeUnit(unit) {
22435
- const normalized = {
22435
+ const normalized2 = {
22436
22436
  year: "years",
22437
22437
  years: "years",
22438
22438
  quarter: "quarters",
@@ -22452,8 +22452,8 @@ var Duration = class _Duration {
22452
22452
  millisecond: "milliseconds",
22453
22453
  milliseconds: "milliseconds"
22454
22454
  }[unit ? unit.toLowerCase() : unit];
22455
- if (!normalized) throw new InvalidUnitError(unit);
22456
- return normalized;
22455
+ if (!normalized2) throw new InvalidUnitError(unit);
22456
+ return normalized2;
22457
22457
  }
22458
22458
  /**
22459
22459
  * Check if an object is a Duration. Works across context boundaries
@@ -24356,7 +24356,7 @@ var orderedWeekUnits = [
24356
24356
  ];
24357
24357
  var orderedOrdinalUnits = ["year", "ordinal", "hour", "minute", "second", "millisecond"];
24358
24358
  function normalizeUnit(unit) {
24359
- const normalized = {
24359
+ const normalized2 = {
24360
24360
  year: "year",
24361
24361
  years: "year",
24362
24362
  month: "month",
@@ -24382,8 +24382,8 @@ function normalizeUnit(unit) {
24382
24382
  weekyears: "weekYear",
24383
24383
  ordinal: "ordinal"
24384
24384
  }[unit.toLowerCase()];
24385
- if (!normalized) throw new InvalidUnitError(unit);
24386
- return normalized;
24385
+ if (!normalized2) throw new InvalidUnitError(unit);
24386
+ return normalized2;
24387
24387
  }
24388
24388
  function normalizeUnitWithLocalWeeks(unit) {
24389
24389
  switch (unit.toLowerCase()) {
@@ -24682,9 +24682,9 @@ var DateTime = class _DateTime {
24682
24682
  return _DateTime.invalid(unsupportedZone(zoneToUse));
24683
24683
  }
24684
24684
  const loc = Locale.fromObject(opts);
24685
- const normalized = normalizeObject(obj, normalizeUnitWithLocalWeeks);
24686
- const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized, loc);
24687
- const tsNow = Settings.now(), offsetProvis = !isUndefined(opts.specificOffset) ? opts.specificOffset : zoneToUse.offset(tsNow), containsOrdinal = !isUndefined(normalized.ordinal), containsGregorYear = !isUndefined(normalized.year), containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day), containsGregor = containsGregorYear || containsGregorMD, definiteWeekDef = normalized.weekYear || normalized.weekNumber;
24685
+ const normalized2 = normalizeObject(obj, normalizeUnitWithLocalWeeks);
24686
+ const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized2, loc);
24687
+ const tsNow = Settings.now(), offsetProvis = !isUndefined(opts.specificOffset) ? opts.specificOffset : zoneToUse.offset(tsNow), containsOrdinal = !isUndefined(normalized2.ordinal), containsGregorYear = !isUndefined(normalized2.year), containsGregorMD = !isUndefined(normalized2.month) || !isUndefined(normalized2.day), containsGregor = containsGregorYear || containsGregorMD, definiteWeekDef = normalized2.weekYear || normalized2.weekNumber;
24688
24688
  if ((containsGregor || containsOrdinal) && definiteWeekDef) {
24689
24689
  throw new ConflictingSpecificationError(
24690
24690
  "Can't mix weekYear/weekNumber units with year/month/day or ordinals"
@@ -24693,7 +24693,7 @@ var DateTime = class _DateTime {
24693
24693
  if (containsGregorMD && containsOrdinal) {
24694
24694
  throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day");
24695
24695
  }
24696
- const useWeekData = definiteWeekDef || normalized.weekday && !containsGregor;
24696
+ const useWeekData = definiteWeekDef || normalized2.weekday && !containsGregor;
24697
24697
  let units, defaultValues, objNow = tsToObj(tsNow, offsetProvis);
24698
24698
  if (useWeekData) {
24699
24699
  units = orderedWeekUnits;
@@ -24709,29 +24709,29 @@ var DateTime = class _DateTime {
24709
24709
  }
24710
24710
  let foundFirst = false;
24711
24711
  for (const u of units) {
24712
- const v = normalized[u];
24712
+ const v = normalized2[u];
24713
24713
  if (!isUndefined(v)) {
24714
24714
  foundFirst = true;
24715
24715
  } else if (foundFirst) {
24716
- normalized[u] = defaultValues[u];
24716
+ normalized2[u] = defaultValues[u];
24717
24717
  } else {
24718
- normalized[u] = objNow[u];
24718
+ normalized2[u] = objNow[u];
24719
24719
  }
24720
24720
  }
24721
- const higherOrderInvalid = useWeekData ? hasInvalidWeekData(normalized, minDaysInFirstWeek, startOfWeek) : containsOrdinal ? hasInvalidOrdinalData(normalized) : hasInvalidGregorianData(normalized), invalid = higherOrderInvalid || hasInvalidTimeData(normalized);
24721
+ const higherOrderInvalid = useWeekData ? hasInvalidWeekData(normalized2, minDaysInFirstWeek, startOfWeek) : containsOrdinal ? hasInvalidOrdinalData(normalized2) : hasInvalidGregorianData(normalized2), invalid = higherOrderInvalid || hasInvalidTimeData(normalized2);
24722
24722
  if (invalid) {
24723
24723
  return _DateTime.invalid(invalid);
24724
24724
  }
24725
- const gregorian = useWeekData ? weekToGregorian(normalized, minDaysInFirstWeek, startOfWeek) : containsOrdinal ? ordinalToGregorian(normalized) : normalized, [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse), inst = new _DateTime({
24725
+ const gregorian = useWeekData ? weekToGregorian(normalized2, minDaysInFirstWeek, startOfWeek) : containsOrdinal ? ordinalToGregorian(normalized2) : normalized2, [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse), inst = new _DateTime({
24726
24726
  ts: tsFinal,
24727
24727
  zone: zoneToUse,
24728
24728
  o: offsetFinal,
24729
24729
  loc
24730
24730
  });
24731
- if (normalized.weekday && containsGregor && obj.weekday !== inst.weekday) {
24731
+ if (normalized2.weekday && containsGregor && obj.weekday !== inst.weekday) {
24732
24732
  return _DateTime.invalid(
24733
24733
  "mismatched weekday",
24734
- `you can't specify both a weekday of ${normalized.weekday} and a date of ${inst.toISO()}`
24734
+ `you can't specify both a weekday of ${normalized2.weekday} and a date of ${inst.toISO()}`
24735
24735
  );
24736
24736
  }
24737
24737
  if (!inst.isValid) {
@@ -25380,9 +25380,9 @@ var DateTime = class _DateTime {
25380
25380
  */
25381
25381
  set(values2) {
25382
25382
  if (!this.isValid) return this;
25383
- const normalized = normalizeObject(values2, normalizeUnitWithLocalWeeks);
25384
- const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized, this.loc);
25385
- const settingWeekStuff = !isUndefined(normalized.weekYear) || !isUndefined(normalized.weekNumber) || !isUndefined(normalized.weekday), containsOrdinal = !isUndefined(normalized.ordinal), containsGregorYear = !isUndefined(normalized.year), containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day), containsGregor = containsGregorYear || containsGregorMD, definiteWeekDef = normalized.weekYear || normalized.weekNumber;
25383
+ const normalized2 = normalizeObject(values2, normalizeUnitWithLocalWeeks);
25384
+ const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized2, this.loc);
25385
+ const settingWeekStuff = !isUndefined(normalized2.weekYear) || !isUndefined(normalized2.weekNumber) || !isUndefined(normalized2.weekday), containsOrdinal = !isUndefined(normalized2.ordinal), containsGregorYear = !isUndefined(normalized2.year), containsGregorMD = !isUndefined(normalized2.month) || !isUndefined(normalized2.day), containsGregor = containsGregorYear || containsGregorMD, definiteWeekDef = normalized2.weekYear || normalized2.weekNumber;
25386
25386
  if ((containsGregor || containsOrdinal) && definiteWeekDef) {
25387
25387
  throw new ConflictingSpecificationError(
25388
25388
  "Can't mix weekYear/weekNumber units with year/month/day or ordinals"
@@ -25394,15 +25394,15 @@ var DateTime = class _DateTime {
25394
25394
  let mixed;
25395
25395
  if (settingWeekStuff) {
25396
25396
  mixed = weekToGregorian(
25397
- { ...gregorianToWeek(this.c, minDaysInFirstWeek, startOfWeek), ...normalized },
25397
+ { ...gregorianToWeek(this.c, minDaysInFirstWeek, startOfWeek), ...normalized2 },
25398
25398
  minDaysInFirstWeek,
25399
25399
  startOfWeek
25400
25400
  );
25401
- } else if (!isUndefined(normalized.ordinal)) {
25402
- mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...normalized });
25401
+ } else if (!isUndefined(normalized2.ordinal)) {
25402
+ mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...normalized2 });
25403
25403
  } else {
25404
- mixed = { ...this.toObject(), ...normalized };
25405
- if (isUndefined(normalized.day)) {
25404
+ mixed = { ...this.toObject(), ...normalized2 };
25405
+ if (isUndefined(normalized2.day)) {
25406
25406
  mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day);
25407
25407
  }
25408
25408
  }
@@ -28136,8 +28136,8 @@ function idFromDigest(bytes) {
28136
28136
  function extensionIdForPath(absDir, platform2 = process.platform) {
28137
28137
  if (platform2 !== "win32")
28138
28138
  return idFromDigest(Buffer.from(absDir, "utf8"));
28139
- const normalized = /^[a-z]:/.test(absDir) ? absDir[0].toUpperCase() + absDir.slice(1) : absDir;
28140
- return idFromDigest(Buffer.from(normalized, "utf16le"));
28139
+ const normalized2 = /^[a-z]:/.test(absDir) ? absDir[0].toUpperCase() + absDir.slice(1) : absDir;
28140
+ return idFromDigest(Buffer.from(normalized2, "utf16le"));
28141
28141
  }
28142
28142
  function extensionIdForKey(base64Key) {
28143
28143
  return idFromDigest(Buffer.from(base64Key.replace(/\s+/g, ""), "base64"));
@@ -37215,8 +37215,8 @@ function isSecretField(inputType, autocomplete, hints) {
37215
37215
  const raw = list2[i];
37216
37216
  if (typeof raw !== "string" || raw === "")
37217
37217
  continue;
37218
- const normalized = raw.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/[_\-.:[\]/]+/g, " ").toLowerCase();
37219
- if (credential.test(normalized))
37218
+ const normalized2 = raw.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([A-Za-z])([0-9])/g, "$1 $2").replace(/[_\-.:[\]/]+/g, " ").toLowerCase();
37219
+ if (credential.test(normalized2))
37220
37220
  return true;
37221
37221
  }
37222
37222
  return false;
@@ -38641,8 +38641,8 @@ async function readSnapshot(page, opts) {
38641
38641
  // ../explore/dist/settle-config.js
38642
38642
  var DEFAULT_LONG_POLL_MS = 5e3;
38643
38643
  function globToRegExp2(glob) {
38644
- const escaped = glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
38645
- return new RegExp(`^${escaped}$`);
38644
+ const escaped2 = glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
38645
+ return new RegExp(`^${escaped2}$`);
38646
38646
  }
38647
38647
  function urlMatcher(patterns) {
38648
38648
  const compiled = (patterns ?? []).map((p) => ({ full: p.includes("://"), re: globToRegExp2(p) }));
@@ -39577,6 +39577,31 @@ function visibleBusyIndicatorContainer() {
39577
39577
  }
39578
39578
  return null;
39579
39579
  }
39580
+ function visibleBusyIndicatorText() {
39581
+ const shown2 = (el) => {
39582
+ const r = el.getBoundingClientRect();
39583
+ const s3 = window.getComputedStyle(el);
39584
+ return r.width > 0 && r.height > 0 && s3.visibility !== "hidden" && s3.display !== "none" && s3.opacity !== "0";
39585
+ };
39586
+ const progressbar = '[role="progressbar"]:not([aria-valuenow])';
39587
+ const selectors = ['[aria-busy="true"]', progressbar, '[class*="spinner" i]', '[class*="animate-spin" i]'];
39588
+ const notificationContainer = '[role="alert"], [data-sonner-toast], [class*="toast" i], [class*="snackbar" i], [class*="notification" i]';
39589
+ const timerLabel = /timer|countdown|auto.?close/i;
39590
+ const isNotificationTimer = (el) => el.closest(notificationContainer) !== null || timerLabel.test(`${el.getAttribute("aria-label") ?? ""} ${el.getAttribute("title") ?? ""}`);
39591
+ const words3 = (el) => el === null ? "" : (el.getAttribute("aria-label") ?? el.innerText ?? el.textContent ?? "").replace(/\s+/g, " ").trim();
39592
+ for (const sel of selectors) {
39593
+ for (const el of Array.from(document.querySelectorAll(sel))) {
39594
+ if (!shown2(el))
39595
+ continue;
39596
+ if (sel === progressbar && isNotificationTimer(el))
39597
+ continue;
39598
+ const own2 = words3(el);
39599
+ const text2 = own2 !== "" ? own2 : words3(el.closest('[role="status"], [aria-live]:not([aria-live="off"])'));
39600
+ return text2 === "" ? null : text2.slice(0, 200);
39601
+ }
39602
+ }
39603
+ return null;
39604
+ }
39580
39605
  function pendingEvidence(pending, now2) {
39581
39606
  return [...pending].sort((a, b2) => a.startedAt - b2.startedAt).map((r) => ({ endpoint: endpointOf(r.method, r.url), url: redactUrl(r.url), ageMs: Math.max(0, now2 - r.startedAt) }));
39582
39607
  }
@@ -40828,7 +40853,8 @@ async function perceive(page, opts = {}) {
40828
40853
  const busySince = clock.now();
40829
40854
  const remaining = Math.max(1, ceiling - (clock.now() - started));
40830
40855
  const gone = await clockBounded(page.waitForFunction(visibleBusyIndicator, true, { timeout: remaining, polling: "raf" }).then(() => true, (e) => !(e instanceof Error && e.name === "TimeoutError")), remaining, false);
40831
- if (!gone && !ignoreNoProgress(busy) && !ignoreNoProgress(hangRoute(page.url()))) {
40856
+ const busyText = gone ? null : await page.evaluate(visibleBusyIndicatorText).catch(() => null);
40857
+ if (!gone && !ignoreNoProgress(busy) && !(busyText !== null && ignoreDeclared(busyText)) && !ignoreNoProgress(hangRoute(page.url()))) {
40832
40858
  stuckBusy = busy;
40833
40859
  stuckBusyContainer = await page.evaluate(visibleBusyIndicatorContainer).catch(() => null);
40834
40860
  const now2 = await page.evaluate(visibleBusyIndicator).catch(() => null);
@@ -44111,6 +44137,7 @@ var MAX_REPEAT_TYPE_SIGNALS = 3;
44111
44137
  var MAX_QUIET_WAITS = 3;
44112
44138
  var MAX_MOVING_SCROLLS = 12;
44113
44139
  var LAST_CHANCE_NOTE = "no progress: the last steps left the page unchanged and you have seen the whole page \u2014 act on a visible control, report the answer, or say done/blocked now";
44140
+ var LAST_CHANCE_NO_WRITE_NOTE = "no progress: the last steps left the page unchanged and you have seen the whole page, but no write of this run has succeeded yet \u2014 the goal asks for a change the app must receive: go back and press the control that submits it (Import, Save, Send\u2026), or say blocked if the app offers none";
44114
44141
 
44115
44142
  // ../explore/dist/run-depth.js
44116
44143
  var OPEN_ENDED_MIN_ACTIONS = 12;
@@ -44985,11 +45012,11 @@ function hostProbe(signals = createResourceSignals(), thresholds = DEFAULT_PRESS
44985
45012
  function initHost(ctx, cfg) {
44986
45013
  ctx.probeHost = cfg.hostProbe ?? hostProbe();
44987
45014
  ctx.judgeHost = async () => cfg.hostHealth === void 0 ? { host: await ctx.probeHost(), starved: null } : cfg.hostHealth.judge();
44988
- ctx.degradedStop = (finding3, detail, starved) => {
44989
- cfg.hostHealth?.markDegraded({ finding: finding3, detail, step: Math.max(0, ctx.transcript.entries().length - 1) }, starved);
45015
+ ctx.degradedStop = (finding4, detail, starved) => {
45016
+ cfg.hostHealth?.markDegraded({ finding: finding4, detail, step: Math.max(0, ctx.transcript.entries().length - 1) }, starved);
44990
45017
  ctx.failure = {
44991
45018
  kind: "degraded-environment",
44992
- message: `environment-degraded ${finding3} (${detail}) while the host was starved: ${starved} \u2014 not an app finding`
45019
+ message: `environment-degraded ${finding4} (${detail}) while the host was starved: ${starved} \u2014 not an app finding`
44993
45020
  };
44994
45021
  ctx.stop = "inconclusive";
44995
45022
  };
@@ -45400,6 +45427,7 @@ function initLoopState(ctx) {
45400
45427
  ctx.fixtureAttached = false;
45401
45428
  ctx.hang = void 0;
45402
45429
  ctx.outcome = null;
45430
+ ctx.actionableControl = null;
45403
45431
  ctx.incomplete = null;
45404
45432
  ctx.endedOnRejectedDone = false;
45405
45433
  }
@@ -46766,7 +46794,7 @@ function busyIndicatorOf(hang) {
46766
46794
  return null;
46767
46795
  if (hang.element !== void 0 && hang.element !== "")
46768
46796
  return hang.element;
46769
- const m = /^a busy indicator \((.+)\) never went away within \d+ms(?: \(still so after .*\))?$/.exec(hang.detail.trim());
46797
+ const m = /^a busy indicator \((.+)\) never went away within \d+ms(?: \((?:still so after|the page stopped showing work) .*\))?$/.exec(hang.detail.trim());
46770
46798
  return m?.[1] ?? null;
46771
46799
  }
46772
46800
  function legacySelectorForm(indicator) {
@@ -46987,8 +47015,8 @@ async function recordCoverageHang(p) {
46987
47015
  ...p.perceive === void 0 ? {} : { perceive: p.perceive },
46988
47016
  ...p.safety === void 0 ? {} : { safety: p.safety }
46989
47017
  });
46990
- const finding3 = hangFinding(hang, p.steps, index, reproduction);
46991
- p.found.set(fingerprint2, { ...finding3, repro: { ...finding3.repro, recording: p.recording } });
47018
+ const finding4 = hangFinding(hang, p.steps, index, reproduction);
47019
+ p.found.set(fingerprint2, { ...finding4, repro: { ...finding4.repro, recording: p.recording } });
46992
47020
  return "recorded";
46993
47021
  }
46994
47022
 
@@ -48178,6 +48206,7 @@ function initGuards(ctx, cfg) {
48178
48206
  ctx.jobWaitedMs = 0;
48179
48207
  ctx.overlayWaitedMs = 0;
48180
48208
  ctx.hangWorkWaitedMs = 0;
48209
+ ctx.hangWorkSince = null;
48181
48210
  ctx.documentedBudgetMs = 0;
48182
48211
  }
48183
48212
 
@@ -48418,86 +48447,6 @@ function unreadShown(ctx, step) {
48418
48447
  return ctx.observed.unreadShownIn(shownOn(step));
48419
48448
  }
48420
48449
 
48421
- // ../explore/dist/goal-loop/handle-report.js
48422
- async function handleReport(ctx, step) {
48423
- const { cfg } = ctx;
48424
- const { snap, record: record2 } = step;
48425
- if (ctx.replyGoal) {
48426
- if (ctx.awaitingReply && ctx.lastTurn !== null && ctx.replyWaitedMs < ctx.replyWaitMs) {
48427
- const t0 = ctx.now();
48428
- const listen = ctx.replyWaitMs - ctx.replyWaitedMs;
48429
- const reply = await waitForReply(ctx.page, { secrets: ctx.secrets, ...ctx.lastTurn, timeoutMs: listen, ceilingMs: listen, quietMs: ctx.replyQuietMs });
48430
- ctx.replyWaitedMs += ctx.now() - t0;
48431
- if (reply.received) {
48432
- ctx.conversation.latestReply = reply.text;
48433
- ctx.replies.add(snap.url, reply.text);
48434
- ctx.awaitingReply = false;
48435
- ctx.busyWaitedMs = 0;
48436
- ctx.history.push(`waited for the reply \u2192 reply: ${quote(reply.text, 300)}`);
48437
- }
48438
- }
48439
- ctx.noteReplyText(snap.url, await readPageText(ctx.page, ctx.secrets));
48440
- }
48441
- const replyPages = ctx.replyGoal ? ctx.replies.pages() : null;
48442
- if (replyPages === null)
48443
- await readBackProbe(ctx, step);
48444
- const verdict = replyPages !== null && replyPages.length === 0 ? {
48445
- accept: false,
48446
- reason: ctx.preSend === null ? "no reply observed: no message was sent yet \u2014 text on the page before the conversation is not a reply" : `no reply observed: no new message appeared after the send within the reply wait (${Math.round(ctx.replyWaitMs / 1e3)}s)`,
48447
- answer: null
48448
- } : await reportAnswer(cfg.gen, {
48449
- goal: cfg.goal,
48450
- url: snap.url,
48451
- pages: replyPages ?? ctx.observed.pages(),
48452
- history: ctx.history,
48453
- secrets: ctx.secrets,
48454
- judge: cfg.judge,
48455
- vetoes: ctx.vetoes,
48456
- // #238: "none exists" is an answer only on observed pages that cover the app enough.
48457
- ...replyPages === null ? { topNav: ctx.observed.topNavigation(), ownInputs: ctx.observed.ownInputs(), unreadWrites: ctx.observed.unreadWrites() } : {}
48458
- }).then((v) => v.accept && ctx.writeGoal && !ctx.wroteOk && v.answer.absent !== true ? { accept: false, reason: UNSAVED_WRITE_REASON, answer: v.answer } : v).catch((e) => ({ accept: false, reason: `no answer could be generated: ${firstLine(e)}`, answer: null }));
48459
- if (verdict.accept) {
48460
- const deferred = deferEnding(ctx, step, "report");
48461
- if (deferred !== null) {
48462
- ctx.deferredAnswer = verdict.answer;
48463
- record2(false, deferred, { answer: verdict.answer, origin: "engine" });
48464
- return "continue";
48465
- }
48466
- }
48467
- if (verdict.accept) {
48468
- const on = replyPages === null ? "the observed pages" : "the reply observed after the send";
48469
- record2(true, `report accepted: answer grounded on ${on} (${verdict.answer.evidence.length} claim(s))`, {
48470
- answer: verdict.answer
48471
- });
48472
- ctx.answer = verdict.answer;
48473
- ctx.outcome = { status: "completed", verifiedBy: "grounded-answer" };
48474
- ctx.stop = "done";
48475
- return "stop";
48476
- }
48477
- if (verdict.unconfirmedWrite === true && !ctx.readBackAsked) {
48478
- ctx.readBackAsked = true;
48479
- ctx.history.push(`report deferred: ${verdict.reason}`);
48480
- record2(false, `report deferred: ${verdict.reason}`, { answer: verdict.answer, origin: "engine" });
48481
- return "continue";
48482
- }
48483
- ctx.reportRejections += 1;
48484
- for (const e of verdict.answer?.evidence ?? [])
48485
- if (e.grounded)
48486
- ctx.reportClaims.push(e);
48487
- ctx.lastReportNotFound = verdict.answer === null && verdict.reason === NO_ANSWER_REASON || verdict.notAnswer === true;
48488
- ctx.lastAbsenceUncovered = verdict.absenceUncovered === true ? verdict.reason : null;
48489
- ctx.history.push(`report rejected: ${verdict.reason} \u2014 find the answer on the page before reporting`);
48490
- record2(false, `report rejected (${ctx.reportRejections}/${MAX_REPORT_REJECTIONS}): ${verdict.reason}`, {
48491
- answer: verdict.answer
48492
- });
48493
- if (ctx.reportRejections >= MAX_REPORT_REJECTIONS && deferEnding(ctx, step, "report") === null) {
48494
- ctx.incomplete = `the model reported an answer ${ctx.reportRejections} times, but ${verdict.reason}`;
48495
- ctx.stop = "blocked";
48496
- return "stop";
48497
- }
48498
- return "continue";
48499
- }
48500
-
48501
48450
  // ../explore/dist/goal-loop/handle-done.js
48502
48451
  var NO_WRITE_YET = "no write of this run has succeeded yet \u2014 the goal asks for a change the app must receive: go back and press the control that submits it (Import, Save, Send\u2026)";
48503
48452
  async function unreadWriteHold(ctx, step, outcome) {
@@ -48607,7 +48556,146 @@ async function handleDone(ctx, step) {
48607
48556
  return "next";
48608
48557
  }
48609
48558
 
48559
+ // ../explore/dist/goal-loop/handle-report.js
48560
+ async function handleReport(ctx, step) {
48561
+ const { cfg } = ctx;
48562
+ const { snap, record: record2 } = step;
48563
+ if (ctx.replyGoal) {
48564
+ if (ctx.awaitingReply && ctx.lastTurn !== null && ctx.replyWaitedMs < ctx.replyWaitMs) {
48565
+ const t0 = ctx.now();
48566
+ const listen = ctx.replyWaitMs - ctx.replyWaitedMs;
48567
+ const reply = await waitForReply(ctx.page, { secrets: ctx.secrets, ...ctx.lastTurn, timeoutMs: listen, ceilingMs: listen, quietMs: ctx.replyQuietMs });
48568
+ ctx.replyWaitedMs += ctx.now() - t0;
48569
+ if (reply.received) {
48570
+ ctx.conversation.latestReply = reply.text;
48571
+ ctx.replies.add(snap.url, reply.text);
48572
+ ctx.awaitingReply = false;
48573
+ ctx.busyWaitedMs = 0;
48574
+ ctx.history.push(`waited for the reply \u2192 reply: ${quote(reply.text, 300)}`);
48575
+ }
48576
+ }
48577
+ ctx.noteReplyText(snap.url, await readPageText(ctx.page, ctx.secrets));
48578
+ }
48579
+ const replyPages = ctx.replyGoal ? ctx.replies.pages() : null;
48580
+ if (replyPages === null)
48581
+ await readBackProbe(ctx, step);
48582
+ const verdict = replyPages !== null && replyPages.length === 0 ? {
48583
+ accept: false,
48584
+ reason: ctx.preSend === null ? "no reply observed: no message was sent yet \u2014 text on the page before the conversation is not a reply" : `no reply observed: no new message appeared after the send within the reply wait (${Math.round(ctx.replyWaitMs / 1e3)}s)`,
48585
+ answer: null
48586
+ } : await reportAnswer(cfg.gen, {
48587
+ goal: cfg.goal,
48588
+ url: snap.url,
48589
+ pages: replyPages ?? ctx.observed.pages(),
48590
+ history: ctx.history,
48591
+ secrets: ctx.secrets,
48592
+ judge: cfg.judge,
48593
+ vetoes: ctx.vetoes,
48594
+ // #238: "none exists" is an answer only on observed pages that cover the app enough.
48595
+ ...replyPages === null ? { topNav: ctx.observed.topNavigation(), ownInputs: ctx.observed.ownInputs(), unreadWrites: ctx.observed.unreadWrites() } : {}
48596
+ }).then((v) => v.accept && ctx.writeGoal && !ctx.wroteOk && v.answer.absent !== true ? { accept: false, reason: UNSAVED_WRITE_REASON, answer: v.answer } : v).catch((e) => ({ accept: false, reason: `no answer could be generated: ${firstLine(e)}`, answer: null }));
48597
+ if (verdict.accept) {
48598
+ const deferred = deferEnding(ctx, step, "report");
48599
+ if (deferred !== null) {
48600
+ ctx.deferredAnswer = verdict.answer;
48601
+ record2(false, deferred, { answer: verdict.answer, origin: "engine" });
48602
+ return "continue";
48603
+ }
48604
+ }
48605
+ if (verdict.accept) {
48606
+ const on = replyPages === null ? "the observed pages" : "the reply observed after the send";
48607
+ record2(true, `report accepted: answer grounded on ${on} (${verdict.answer.evidence.length} claim(s))`, {
48608
+ answer: verdict.answer
48609
+ });
48610
+ ctx.answer = verdict.answer;
48611
+ ctx.outcome = { status: "completed", verifiedBy: "grounded-answer" };
48612
+ ctx.stop = "done";
48613
+ return "stop";
48614
+ }
48615
+ if (verdict.unconfirmedWrite === true && !ctx.readBackAsked) {
48616
+ ctx.readBackAsked = true;
48617
+ ctx.history.push(`report deferred: ${verdict.reason}`);
48618
+ record2(false, `report deferred: ${verdict.reason}`, { answer: verdict.answer, origin: "engine" });
48619
+ return "continue";
48620
+ }
48621
+ ctx.reportRejections += 1;
48622
+ for (const e of verdict.answer?.evidence ?? [])
48623
+ if (e.grounded)
48624
+ ctx.reportClaims.push(e);
48625
+ ctx.lastReportNotFound = verdict.answer === null && verdict.reason === NO_ANSWER_REASON || verdict.notAnswer === true;
48626
+ ctx.lastAbsenceUncovered = verdict.absenceUncovered === true ? verdict.reason : null;
48627
+ const noWrite = ctx.writeGoal && !ctx.wroteOk && verdict.reason !== UNSAVED_WRITE_REASON;
48628
+ const why = noWrite ? `${verdict.reason}; ${NO_WRITE_YET}` : verdict.reason;
48629
+ ctx.history.push(`report rejected: ${why} \u2014 ${noWrite ? "make the change first, then report" : "find the answer on the page before reporting"}`);
48630
+ record2(false, `report rejected (${ctx.reportRejections}/${MAX_REPORT_REJECTIONS}): ${why}`, {
48631
+ answer: verdict.answer
48632
+ });
48633
+ if (ctx.reportRejections >= MAX_REPORT_REJECTIONS && deferEnding(ctx, step, "report") === null) {
48634
+ ctx.incomplete = `the model reported an answer ${ctx.reportRejections} times, but ${verdict.reason}`;
48635
+ ctx.stop = "blocked";
48636
+ return "stop";
48637
+ }
48638
+ return "continue";
48639
+ }
48640
+
48610
48641
  // ../explore/dist/goal-loop/handle-blocked.js
48642
+ var RECOVERY_TURNS = /* @__PURE__ */ new WeakMap();
48643
+ function takeRecoveryTurn(ctx) {
48644
+ const turn = RECOVERY_TURNS.get(ctx);
48645
+ if (turn === void 0 || !turn.pending)
48646
+ return null;
48647
+ turn.pending = false;
48648
+ return turn.safe;
48649
+ }
48650
+ function resumeIdentity(e) {
48651
+ return JSON.stringify(e.resume);
48652
+ }
48653
+ var NON_FORWARD = /^(?:back|go back|previous|return|cancel|close|dismiss|×|x|←|‹|arrow[- ]?left)\b/i;
48654
+ var DIRECTIVE = /\b(?:choose|select|pick|apply|submit|continue|confirm|save|open|click|press|start|create|send|next|review|accept|approve|add|complete|proceed|generate|upload|finish|sign|log|book|buy|upgrade|run|try|edit|set)\b/i;
48655
+ var normalized = (text2) => text2.replace(/\s+/g, " ").trim().toLowerCase();
48656
+ var escaped = (text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
48657
+ function instructionText(controls, visibleText) {
48658
+ const counts2 = /* @__PURE__ */ new Map();
48659
+ for (const c of controls) {
48660
+ const label2 = normalized(c.name);
48661
+ if (label2 !== "")
48662
+ counts2.set(label2, (counts2.get(label2) ?? 0) + 1);
48663
+ }
48664
+ let text2 = normalized(visibleText);
48665
+ for (const [label2, count] of counts2)
48666
+ for (let i = 0; i < count; i++) {
48667
+ const matches2 = [...text2.matchAll(new RegExp(`\\b${escaped(label2)}\\b`, "g"))];
48668
+ const last = matches2.at(-1);
48669
+ if (last?.index === void 0)
48670
+ break;
48671
+ text2 = `${text2.slice(0, last.index)} ${text2.slice(last.index + last[0].length)}`.replace(/\s+/g, " ").trim();
48672
+ }
48673
+ return text2;
48674
+ }
48675
+ function primaryForwardControl(ctx, step) {
48676
+ const goalTerms = terms(ctx.goalText);
48677
+ if (goalTerms.size === 0)
48678
+ return null;
48679
+ const instruction = instructionText(step.modelControls, step.visibleText);
48680
+ const instructionTerms = terms(instruction);
48681
+ if (instructionTerms.size === 0 || !DIRECTIVE.test(instruction))
48682
+ return null;
48683
+ for (const control of step.modelControls) {
48684
+ if (!control.enabled)
48685
+ continue;
48686
+ const name = control.name.trim();
48687
+ if (name === "" || NON_FORWARD.test(name))
48688
+ continue;
48689
+ if (ctx.depth.tried(step.snap.url, name))
48690
+ continue;
48691
+ const controlTerms = terms(name);
48692
+ if (controlTerms.size === 0)
48693
+ continue;
48694
+ if ([...controlTerms].some((t) => goalTerms.has(t)) && [...controlTerms].every((t) => instructionTerms.has(t)))
48695
+ return control;
48696
+ }
48697
+ return null;
48698
+ }
48611
48699
  async function handleBlocked(ctx, step) {
48612
48700
  const { modelControls, record: record2 } = step;
48613
48701
  const job = await readInProgressStatus(ctx.page);
@@ -48657,16 +48745,221 @@ async function handleBlocked(ctx, step) {
48657
48745
  ctx.stop = "inconclusive";
48658
48746
  return "stop";
48659
48747
  }
48748
+ const forward = ctx.actionAttempts > 0 ? primaryForwardControl(ctx, step) : null;
48749
+ if (forward !== null) {
48750
+ ctx.earlyBlocked += 1;
48751
+ if (ctx.earlyBlocked <= MAX_EARLY_BLOCKED_REFUSALS) {
48752
+ const reason = `blocked refused: ${quote(forward.name || forward.summary, 60)} is an obvious forward control, still untried on this page, and it matches the goal and the page's instruction \u2014 use it before giving up`;
48753
+ ctx.history.push(reason);
48754
+ record2(false, reason, { origin: "engine" });
48755
+ return "continue";
48756
+ }
48757
+ record2(false, `model blocked while ${quote(forward.name || forward.summary, 60)} remained untried`, { origin: "engine" });
48758
+ ctx.failure = {
48759
+ kind: "insufficient-coverage",
48760
+ message: `the model gave up while ${quote(forward.name || forward.summary, 60)} \u2014 the obvious forward control the goal and the page's instruction name \u2014 was still untried after earlier actions \u2014 too little exploration to conclude the goal cannot be done`
48761
+ };
48762
+ ctx.stop = "inconclusive";
48763
+ return "stop";
48764
+ }
48660
48765
  const deferred = deferEnding(ctx, step, "blocked");
48661
48766
  if (deferred !== null) {
48662
48767
  record2(false, deferred, { origin: "engine" });
48663
48768
  return "continue";
48664
48769
  }
48770
+ const evidence = await collectActionableControlEvidence(ctx.page, { screenshots: false }).catch(() => []);
48771
+ const safe = evidence.filter((e) => e.kind === "safe" && e.name !== "");
48772
+ const turn = RECOVERY_TURNS.get(ctx);
48773
+ if ((turn === void 0 || !turn.given) && safe.length > 0) {
48774
+ RECOVERY_TURNS.set(ctx, { safe, given: true, pending: true });
48775
+ const note = `blocked: one recovery turn is available through the safe control(s) ${safe.map((e) => `${quote(e.name, 40)} (${e.locator})`).join(", ")} \u2014 act through one of them or the run ends blocked`;
48776
+ ctx.history.push(note);
48777
+ record2(false, note, { origin: "engine" });
48778
+ return "continue";
48779
+ }
48665
48780
  record2(true, "model blocked");
48666
- ctx.incomplete = `the model reported the goal cannot be advanced from this page${ctx.lastRefusal === null ? "" : ` (${ctx.lastRefusal})`}`;
48781
+ const turnSafe = turn !== void 0 && turn.given && turn.safe.length > 0 ? turn.safe : null;
48782
+ const excluded = evidence.filter((e) => e.kind !== "safe");
48783
+ const classes = [...new Set(excluded.map((e) => e.kind))].join(", ");
48784
+ const best = turnSafe !== null ? turnSafe[0] : excluded.find((e) => e.kind !== "hidden" && e.kind !== "disabled") ?? evidence[0];
48785
+ if (best !== void 0) {
48786
+ const captured = (await collectActionableControlEvidence(ctx.page).catch(() => evidence)).find((e) => e.locator === best.locator);
48787
+ ctx.actionableControl = actionableControlOutcome(captured ?? best);
48788
+ }
48789
+ const refusal = ctx.lastRefusal === null ? "" : ` (${ctx.lastRefusal})`;
48790
+ const base = `the model reported the goal cannot be advanced from this page${refusal}`;
48791
+ const recovery = turnSafe !== null ? `the one recovery turn through the safe control ${turnSafe.map(resumeIdentity).join(", ")} did not advance the goal` : `no visible enabled safe control is available to recover through${classes === "" ? "" : ` (excluded control classes: ${classes})`}${best === void 0 ? "" : `; resume identity ${resumeIdentity(best)}`}`;
48792
+ const cause = ctx.blockingCause();
48793
+ ctx.incomplete = cause === null || base.includes(cause) ? `${base}; ${recovery}` : `${base} \u2014 last blocker: ${cause}; ${recovery}`;
48667
48794
  ctx.stop = "blocked";
48668
48795
  return "stop";
48669
48796
  }
48797
+ var RECOVERY_SELECTOR = [
48798
+ "a[href]",
48799
+ "button",
48800
+ "input:not([type=hidden])",
48801
+ "select",
48802
+ "textarea",
48803
+ "[role=button]",
48804
+ "[role=link]",
48805
+ "[role=menuitem]",
48806
+ "[role=tab]",
48807
+ "[role=checkbox]",
48808
+ "[role=radio]",
48809
+ "[role=switch]",
48810
+ "[contenteditable=true]"
48811
+ ].join(", ");
48812
+ var APPROVAL = /\b(?:approve|approval|authorize|authorise|accept|grant|sign[ -]?off|confirm)\b/i;
48813
+ var SAFE_PREDICATE = "visible && enabled && !ambiguous && !destructive && !financial && !approval";
48814
+ function readRecoveryCandidates(els) {
48815
+ const nameOf = (el) => {
48816
+ const aria = el.getAttribute("aria-label");
48817
+ if (aria !== null && aria.trim() !== "")
48818
+ return aria.replace(/\s+/g, " ").trim();
48819
+ const labelled = el.getAttribute("aria-labelledby");
48820
+ if (labelled !== null) {
48821
+ const text2 = labelled.split(/\s+/).map((id) => document.getElementById(id)?.textContent ?? "").join(" ");
48822
+ if (text2.trim() !== "")
48823
+ return text2.replace(/\s+/g, " ").trim();
48824
+ }
48825
+ const title = el.getAttribute("title");
48826
+ if (title !== null && title.trim() !== "")
48827
+ return title.replace(/\s+/g, " ").trim();
48828
+ if (el instanceof HTMLInputElement && el.type !== "password" && el.value !== "")
48829
+ return el.value.replace(/\s+/g, " ").trim();
48830
+ return (el.textContent ?? "").replace(/\s+/g, " ").trim();
48831
+ };
48832
+ return els.map((el) => {
48833
+ const style = getComputedStyle(el);
48834
+ const rect = el.getBoundingClientRect();
48835
+ const visible3 = style.display !== "none" && style.visibility !== "hidden" && el.getClientRects().length > 0 && rect.width > 0 && rect.height > 0;
48836
+ const disabled = el.disabled === true || el.getAttribute("aria-disabled") === "true";
48837
+ return {
48838
+ name: nameOf(el),
48839
+ role: el.getAttribute("role") ?? el.tagName.toLowerCase(),
48840
+ testId: el.getAttribute("data-testid") ?? el.getAttribute("data-test"),
48841
+ id: el.id === "" ? null : el.id,
48842
+ visible: visible3,
48843
+ enabled: !disabled,
48844
+ box: visible3 ? { x: rect.x + window.scrollX, y: rect.y + window.scrollY, width: rect.width, height: rect.height } : null
48845
+ };
48846
+ });
48847
+ }
48848
+ function classifyActionableControl(input2) {
48849
+ if (!input2.visible)
48850
+ return "hidden";
48851
+ if (!input2.enabled)
48852
+ return "disabled";
48853
+ if (input2.duplicateName)
48854
+ return "ambiguous";
48855
+ const risk = controlRisk(input2.name, input2.role);
48856
+ if (risk !== null && (risk.risk === "destructive" || risk.risk === "session-end"))
48857
+ return "destructive";
48858
+ if (risk !== null && risk.risk === "paid")
48859
+ return "financial";
48860
+ if (APPROVAL.test(input2.name))
48861
+ return "approval";
48862
+ return "safe";
48863
+ }
48864
+ function recoveryPredicate(kind) {
48865
+ switch (kind) {
48866
+ case "safe":
48867
+ return SAFE_PREDICATE;
48868
+ case "hidden":
48869
+ return "visible";
48870
+ case "disabled":
48871
+ return "enabled";
48872
+ case "ambiguous":
48873
+ return "uniquely named";
48874
+ case "destructive":
48875
+ return "!destructive";
48876
+ case "financial":
48877
+ return "!financial";
48878
+ case "approval":
48879
+ return "!approval";
48880
+ }
48881
+ }
48882
+ function recoveryLocator(c) {
48883
+ if (c.testId !== null && c.testId !== "")
48884
+ return `[data-testid="${c.testId}"]`;
48885
+ if (c.id !== null && c.id !== "")
48886
+ return `#${c.id}`;
48887
+ const role = c.role === "" ? "*" : c.role;
48888
+ return `${role}[name="${c.name.replace(/"/g, '\\"')}"]`;
48889
+ }
48890
+ var SAFE_CLAUSES = ["visible", "enabled", "!ambiguous", "!destructive", "!financial", "!approval"];
48891
+ function failingClause(kind) {
48892
+ switch (kind) {
48893
+ case "safe":
48894
+ return null;
48895
+ case "hidden":
48896
+ return "visible";
48897
+ case "disabled":
48898
+ return "enabled";
48899
+ case "ambiguous":
48900
+ return "!ambiguous";
48901
+ case "destructive":
48902
+ return "!destructive";
48903
+ case "financial":
48904
+ return "!financial";
48905
+ case "approval":
48906
+ return "!approval";
48907
+ }
48908
+ }
48909
+ function actionableControlOutcome(e) {
48910
+ const missing = failingClause(e.kind);
48911
+ return {
48912
+ kind: "author_stopped_with_actionable_control",
48913
+ candidate: {
48914
+ locator: e.locator,
48915
+ label: e.name,
48916
+ visible: e.kind !== "hidden",
48917
+ enabled: e.kind !== "disabled",
48918
+ screenshot: e.screenshot,
48919
+ resume: e.resume
48920
+ },
48921
+ satisfiedPredicates: SAFE_CLAUSES.filter((c) => c !== missing),
48922
+ unsatisfiedPredicates: missing === null ? [] : [missing]
48923
+ };
48924
+ }
48925
+ async function recoveryScreenshot(page, c) {
48926
+ try {
48927
+ const buf = c.box === null ? await page.screenshot({ animations: "disabled" }) : await page.screenshot({ clip: c.box, animations: "disabled" });
48928
+ return `data:image/png;base64,${buf.toString("base64")}`;
48929
+ } catch {
48930
+ return null;
48931
+ }
48932
+ }
48933
+ async function collectActionableControlEvidence(page, opts = {}) {
48934
+ const candidates = await page.locator(RECOVERY_SELECTOR).evaluateAll(readRecoveryCandidates).catch(() => []);
48935
+ const url2 = page.url();
48936
+ const state = await snapshot(page).then((s3) => s3.signature, () => url2);
48937
+ const visibleNames = /* @__PURE__ */ new Map();
48938
+ for (const c of candidates) {
48939
+ if (c.visible && c.enabled && c.name !== "")
48940
+ visibleNames.set(c.name, (visibleNames.get(c.name) ?? 0) + 1);
48941
+ }
48942
+ const out = [];
48943
+ for (const c of candidates) {
48944
+ const kind = classifyActionableControl({
48945
+ name: c.name,
48946
+ role: c.role,
48947
+ visible: c.visible,
48948
+ enabled: c.enabled,
48949
+ duplicateName: c.name !== "" && (visibleNames.get(c.name) ?? 0) > 1
48950
+ });
48951
+ const locator = recoveryLocator(c);
48952
+ out.push({
48953
+ kind,
48954
+ name: c.name,
48955
+ locator,
48956
+ screenshot: opts.screenshots === false ? null : await recoveryScreenshot(page, c),
48957
+ predicate: recoveryPredicate(kind),
48958
+ resume: { url: url2, state, locator }
48959
+ });
48960
+ }
48961
+ return out;
48962
+ }
48670
48963
 
48671
48964
  // ../explore/dist/goal-loop/handle-idle.js
48672
48965
  async function handleWaitOrScroll(ctx, step) {
@@ -49664,6 +49957,21 @@ async function statusWhilePending(page) {
49664
49957
  const status = await readInProgressStatus(page);
49665
49958
  return status === null ? null : `${status} while its request is in flight (within --job-wait-ms)`;
49666
49959
  }
49960
+ async function declaredBusy(page) {
49961
+ const status = await readInProgressStatus(page);
49962
+ return status?.startsWith("aria-busy region") === true ? `${status}, declared by the app (within --job-wait-ms)` : null;
49963
+ }
49964
+ function stoodAfterWork(ctx, workBudgetMs) {
49965
+ const s3 = (ms) => `${Math.round(ms / 1e3)}s`;
49966
+ const now2 = clock.now();
49967
+ const since = ctx.hangWorkSince === null ? "" : `, ${s3(now2 - ctx.hangWorkSince)} since the first deferral`;
49968
+ const last = monitorFor(ctx.page).completedSince(ctx.hangWorkSince ?? 0).filter((r) => r.resourceType !== "document" && r.ignored !== true).at(-1);
49969
+ const activity = last === void 0 ? "no app request completed since" : `the app's last request completed ${s3(now2 - last.endedAt)} ago`;
49970
+ if (ctx.hangWorkWaitedMs >= workBudgetMs) {
49971
+ return `still so after ${s3(ctx.hangWorkWaitedMs)} of the page showing work${since} \u2014 past the ${s3(workBudgetMs)} job-wait budget; raise --job-wait-ms for longer jobs; ${activity}`;
49972
+ }
49973
+ return `the page stopped showing work code recognizes (an acknowledged or declared in-progress status, a documented wait, a pending request under a status, or requests completing) after ${s3(ctx.hangWorkWaitedMs)} of the ${s3(workBudgetMs)} job-wait budget${since}; ${activity}`;
49974
+ }
49667
49975
  async function checkHang(ctx, step) {
49668
49976
  const { perceiveStartedAt, perception, snap } = step;
49669
49977
  const documented = perception.hang !== null && perception.hang.kind !== "main-thread-unresponsive" ? await readDocumentedWait(ctx.page) : null;
@@ -49674,8 +49982,9 @@ async function checkHang(ctx, step) {
49674
49982
  const working = await readWorkingStatus(ctx.page) ?? (documented === null ? null : `a documented wait ("${documented.text}")`) ?? (perception.hang.kind === "ui-no-progress" ? await liveBusyWork(ctx.page, perception.busyWait) : null) ?? // #330: a request still in flight while the page SAYS it is working ("Designing variations…")
49675
49983
  // is the long job the operator sized with --job-wait-ms: waited out within that budget. Without
49676
49984
  // the flag a bare status is not enough (a stuck page looks exactly like that, #153).
49677
- (perception.hang.kind === "request-pending" && ctx.jobWaitExplicit ? await statusWhilePending(ctx.page) : null);
49985
+ (perception.hang.kind === "request-pending" && ctx.jobWaitExplicit ? await statusWhilePending(ctx.page) : null) ?? (perception.hang.kind === "ui-no-progress" && ctx.jobWaitExplicit ? await declaredBusy(ctx.page) : null);
49678
49986
  if (working !== null) {
49987
+ ctx.hangWorkSince ??= perceiveStartedAt;
49679
49988
  const w = await waitOutJob(ctx.page, Math.min(workBudgetMs - ctx.hangWorkWaitedMs, JOB_WAIT_SLICE_MS), stillShowsWork);
49680
49989
  ctx.hangWorkWaitedMs += clock.now() - perceiveStartedAt;
49681
49990
  const note = `not a hang yet (${perception.hang.kind}): the page shows ${working} \u2014 the app is still working; waited ${(w.waitedMs / 1e3).toFixed(1)}s (${w.cleared ? "the status cleared" : `still in progress; ${Math.round(ctx.hangWorkWaitedMs / 1e3)}s of the ${Math.round(workBudgetMs / 1e3)}s job-wait budget used`})`;
@@ -49715,7 +50024,7 @@ async function checkHang(ctx, step) {
49715
50024
  const heapNow = await sampleHeap(ctx.page, 1e3);
49716
50025
  const stood = ctx.hangWorkWaitedMs > 0 ? {
49717
50026
  ...perception.hang,
49718
- detail: `${perception.hang.detail} (still so after ${Math.round(ctx.hangWorkWaitedMs / 1e3)}s of the page showing work \u2014 past the ${Math.round(workBudgetMs / 1e3)}s job-wait budget; raise --job-wait-ms for longer jobs)`
50027
+ detail: `${perception.hang.detail} (${stoodAfterWork(ctx, workBudgetMs)})`
49719
50028
  } : perception.hang;
49720
50029
  const withHost = { ...stood, host: judged.host };
49721
50030
  ctx.hang = {
@@ -49992,7 +50301,7 @@ async function checkProgress(ctx, step) {
49992
50301
  if (!ctx.lastChanceGiven) {
49993
50302
  ctx.lastChanceGiven = true;
49994
50303
  ctx.lastChanceTurn = true;
49995
- ctx.history.push(LAST_CHANCE_NOTE);
50304
+ ctx.history.push(ctx.writeGoal && !ctx.wroteOk ? LAST_CHANCE_NO_WRITE_NOTE : LAST_CHANCE_NOTE);
49996
50305
  } else {
49997
50306
  ctx.stop = "no-progress";
49998
50307
  return "stop";
@@ -50196,7 +50505,12 @@ async function viewPage(ctx, step) {
50196
50505
  async function decideStep(ctx, step, view) {
50197
50506
  const { cfg } = ctx;
50198
50507
  const { perception, snap } = step;
50199
- const { modelControls, offered, unsubmitted, visibleText } = view;
50508
+ const { offered, unsubmitted, visibleText } = view;
50509
+ const recovery = takeRecoveryTurn(ctx);
50510
+ const safeNames = new Set((recovery ?? []).map((e) => e.name).filter((n3) => n3 !== ""));
50511
+ const modelControls = recovery === null ? view.modelControls : view.modelControls.filter((c) => safeNames.has(c.name));
50512
+ if (recovery !== null)
50513
+ ctx.history.push("recovery turn: act through one of the safe controls offered, or the run ends blocked");
50200
50514
  let decision;
50201
50515
  ctx.goalFocus.noteUrl(snap.url);
50202
50516
  const offGoal = ctx.goalFocus.offGoal(modelControls, ctx.chrome);
@@ -50447,6 +50761,7 @@ async function finishRun(ctx) {
50447
50761
  ...ctx.answer !== void 0 && finalOutcome.status === "completed" ? { answer: ctx.answer } : {},
50448
50762
  ...cause === null ? {} : { blockingCause: cause },
50449
50763
  ...missCause === void 0 ? {} : { missCause },
50764
+ ...ctx.actionableControl === null ? {} : { actionableControl: ctx.actionableControl },
50450
50765
  ...ctx.endedOnRejectedDone && ctx.stop === "done" ? { doneRejected: true } : {},
50451
50766
  ...ctx.stop === "crashed" && ctx.failure !== void 0 ? { crash: buildCrashReport(ctx.failure, ctx.crashWatch.signals(), ctx.heap.samples(), { host: await ctx.probeHost() }) } : {}
50452
50767
  };
@@ -52502,7 +52817,7 @@ async function reproduceSeedHang(cfg, run, h) {
52502
52817
  });
52503
52818
  }
52504
52819
  function hangResult(run, h, reproduction) {
52505
- const finding3 = hangFinding(h.signal, run.transcript, h.recordingStepIndex, reproduction);
52820
+ const finding4 = hangFinding(h.signal, run.transcript, h.recordingStepIndex, reproduction);
52506
52821
  return {
52507
52822
  // A hang whose replays could not run at all is `inconclusive`, never a non-reproduction.
52508
52823
  outcome: reproduction.status === "reproduced" ? "hang" : reproduction.status,
@@ -52512,8 +52827,8 @@ function hangResult(run, h, reproduction) {
52512
52827
  recording: run.recording,
52513
52828
  transcript: run.transcript,
52514
52829
  finalUrl: run.finalUrl,
52515
- hang: finding3,
52516
- reason: reproduction.withheld !== void 0 ? `${finding3.title} (${withheldReason(reproduction.withheld)})` : reproduction.attempts === 0 ? `${finding3.title} (unconfirmed: not replayed)` : `${finding3.title} (reproduced ${reproduction.reproduced}/${reproduction.attempts})`
52830
+ hang: finding4,
52831
+ reason: reproduction.withheld !== void 0 ? `${finding4.title} (${withheldReason(reproduction.withheld)})` : reproduction.attempts === 0 ? `${finding4.title} (unconfirmed: not replayed)` : `${finding4.title} (reproduced ${reproduction.reproduced}/${reproduction.attempts})`
52517
52832
  };
52518
52833
  }
52519
52834
 
@@ -58132,23 +58447,23 @@ function createFrontierContext(params, declared, safety, budget, overlay = null,
58132
58447
  const vp = ctx.sessions.page.viewportSize();
58133
58448
  if (!shouldCheckOverflow(vp?.width, params.overflow?.checkOverflow ?? false))
58134
58449
  return;
58135
- const finding3 = await detectOverflow(ctx.sessions.page, {
58450
+ const finding4 = await detectOverflow(ctx.sessions.page, {
58136
58451
  viewport: vp ?? { width: 1280, height: 720 },
58137
58452
  ...params.overflow?.device === void 0 ? {} : { device: params.overflow.device },
58138
58453
  ...params.overflow?.toleranceCss === void 0 ? {} : { toleranceCss: params.overflow.toleranceCss },
58139
58454
  ...params.overflow?.ignoreSelectors === void 0 ? {} : { ignoreSelectors: params.overflow.ignoreSelectors },
58140
58455
  ...params.overflow?.secrets === void 0 ? {} : { secrets: params.overflow.secrets }
58141
58456
  });
58142
- if (finding3 !== null && !ctx.seenOverflow.has(finding3.fingerprint)) {
58143
- ctx.seenOverflow.add(finding3.fingerprint);
58457
+ if (finding4 !== null && !ctx.seenOverflow.has(finding4.fingerprint)) {
58458
+ ctx.seenOverflow.add(finding4.fingerprint);
58144
58459
  ctx.defects.push({
58145
- fingerprint: finding3.fingerprint,
58460
+ fingerprint: finding4.fingerprint,
58146
58461
  kind: "horizontal-overflow",
58147
58462
  stateFingerprint: stateFp,
58148
58463
  url: url2,
58149
- reason: `horizontal-overflow: ${finding3.element.descriptor} overflows the ${finding3.viewport.width}px viewport by ${finding3.overflowPx}px at ${finding3.route}`,
58464
+ reason: `horizontal-overflow: ${finding4.element.descriptor} overflows the ${finding4.viewport.width}px viewport by ${finding4.overflowPx}px at ${finding4.route}`,
58150
58465
  recording,
58151
- overflow: finding3
58466
+ overflow: finding4
58152
58467
  });
58153
58468
  }
58154
58469
  const clipped = await detectClipping(ctx.sessions.page, {
@@ -60719,22 +61034,22 @@ function installOracle(ctx, params) {
60719
61034
  url: c.url,
60720
61035
  descriptor: c.element.descriptor
60721
61036
  }));
60722
- const finding3 = await detectOverflow(ctx.sessions.page, {
61037
+ const finding4 = await detectOverflow(ctx.sessions.page, {
60723
61038
  viewport: vp ?? { width: 1280, height: 720 },
60724
61039
  ...params.overflow?.device === void 0 ? {} : { device: params.overflow.device },
60725
61040
  ...params.overflow?.toleranceCss === void 0 ? {} : { toleranceCss: params.overflow.toleranceCss },
60726
61041
  ...params.overflow?.ignoreSelectors === void 0 ? {} : { ignoreSelectors: params.overflow.ignoreSelectors },
60727
61042
  ...params.overflow?.secrets === void 0 ? {} : { secrets: params.overflow.secrets }
60728
61043
  });
60729
- if (finding3 === null)
61044
+ if (finding4 === null)
60730
61045
  return clipped;
60731
61046
  const horizontal = {
60732
61047
  kind: "horizontal-overflow",
60733
- detail: `horizontal-overflow: ${finding3.element.descriptor} overflows the ${finding3.viewport.width}px viewport by ${finding3.overflowPx}px at ${finding3.route}`,
60734
- overflowPx: finding3.overflowPx,
60735
- route: finding3.route,
60736
- url: finding3.url,
60737
- descriptor: finding3.element.descriptor
61048
+ detail: `horizontal-overflow: ${finding4.element.descriptor} overflows the ${finding4.viewport.width}px viewport by ${finding4.overflowPx}px at ${finding4.route}`,
61049
+ overflowPx: finding4.overflowPx,
61050
+ route: finding4.route,
61051
+ url: finding4.url,
61052
+ descriptor: finding4.element.descriptor
60738
61053
  };
60739
61054
  return [horizontal, ...clipped];
60740
61055
  };
@@ -60903,9 +61218,9 @@ function installOracle(ctx, params) {
60903
61218
  }
60904
61219
  const summary2 = f2.kind === "invariant" ? f2.invariantReason ?? f2.title : f2.kind === "markup-injection" ? f2.title : f2.signals.map((s3) => s3.detail).join("; ");
60905
61220
  const triage = await tryTriage(params.generation, { failureSummary: summary2, url: f2.url });
60906
- const { origin: _origin, ...finding3 } = f2;
61221
+ const { origin: _origin, ...finding4 } = f2;
60907
61222
  ctx.defects.set(f2.fingerprint, {
60908
- ...finding3,
61223
+ ...finding4,
60909
61224
  related: new Set(f2.related),
60910
61225
  epoch: ctx.segments.indexOf(ctx.recorder),
60911
61226
  firstSeenStep: step,
@@ -60983,9 +61298,9 @@ function installSessions(ctx, params) {
60983
61298
  perceive: ctx.perceiveOpts,
60984
61299
  ...params.safety === void 0 ? {} : { safety: params.safety }
60985
61300
  });
60986
- const finding3 = hangFinding(h, ctx.transcript.entries(), recordingStepIndex, reproduction);
61301
+ const finding4 = hangFinding(h, ctx.transcript.entries(), recordingStepIndex, reproduction);
60987
61302
  const segment = ctx.segments.indexOf(ctx.recorder);
60988
- ctx.hangs.set(finding3.fingerprint, segment > 0 && partial2.ok ? { ...finding3, repro: { ...finding3.repro, recording: partial2.recording } } : finding3);
61303
+ ctx.hangs.set(finding4.fingerprint, segment > 0 && partial2.ok ? { ...finding4, repro: { ...finding4.repro, recording: partial2.recording } } : finding4);
60989
61304
  };
60990
61305
  ctx.restartSpend = 0;
60991
61306
  ctx.restartAtSeed = async () => {
@@ -61125,21 +61440,21 @@ function createHuntContext(params) {
61125
61440
  }
61126
61441
  return warning;
61127
61442
  };
61128
- ctx.blockedConsequence = (finding3, candidate) => {
61443
+ ctx.blockedConsequence = (finding4, candidate) => {
61129
61444
  const causedBy = blockedCauseOf(ctx.blockedRoots, candidate);
61130
61445
  if (causedBy === void 0)
61131
61446
  return false;
61132
- const known = ctx.blockedConsequences.get(finding3.fingerprint);
61447
+ const known = ctx.blockedConsequences.get(finding4.fingerprint);
61133
61448
  if (known !== void 0) {
61134
61449
  if (!known.occurrenceSteps.includes(candidate.step))
61135
61450
  known.occurrenceSteps.push(candidate.step);
61136
61451
  return true;
61137
61452
  }
61138
- ctx.blockedConsequences.set(finding3.fingerprint, {
61139
- fingerprint: finding3.fingerprint,
61140
- kind: finding3.kind,
61141
- title: finding3.title,
61142
- route: finding3.route,
61453
+ ctx.blockedConsequences.set(finding4.fingerprint, {
61454
+ fingerprint: finding4.fingerprint,
61455
+ kind: finding4.kind,
61456
+ title: finding4.title,
61457
+ route: finding4.route,
61143
61458
  firstSeenStep: candidate.step,
61144
61459
  occurrenceSteps: [candidate.step],
61145
61460
  causedBy
@@ -63118,9 +63433,9 @@ function hostStarvedFailure(failure3, health) {
63118
63433
  }
63119
63434
 
63120
63435
  // src/build-info.generated.ts
63121
- var GENERATED_COMMIT = "e0f6f96";
63122
- var GENERATED_TREE = "d0b472e";
63123
- var GENERATED_BUILT_AT = "2026-10-24T16:37:57.792Z";
63436
+ var GENERATED_COMMIT = "ffe0da9";
63437
+ var GENERATED_TREE = "bc64c13";
63438
+ var GENERATED_BUILT_AT = "2026-10-26T13:27:18.971Z";
63124
63439
 
63125
63440
  // src/version.ts
63126
63441
  import { readFileSync as readFileSync5 } from "node:fs";
@@ -63142,13 +63457,13 @@ function readCliVersion() {
63142
63457
 
63143
63458
  // src/engine.ts
63144
63459
  function bundledCommit() {
63145
- return true ? "e0f6f96" : void 0;
63460
+ return true ? "ffe0da9" : void 0;
63146
63461
  }
63147
63462
  function bundledBuiltAt() {
63148
- return true ? "2026-10-24T16:37:57.792Z" : void 0;
63463
+ return true ? "2026-10-26T13:27:18.971Z" : void 0;
63149
63464
  }
63150
63465
  function bundledTree() {
63151
- return true ? "d0b472e" : void 0;
63466
+ return true ? "bc64c13" : void 0;
63152
63467
  }
63153
63468
  function currentBuildTree() {
63154
63469
  return bundledTree() ?? GENERATED_TREE;
@@ -66594,10 +66909,10 @@ var QueryNode = freeze2({
66594
66909
  where: node2.where ? WhereNode.cloneWithOperation(node2.where, "And", operation) : WhereNode.create(operation)
66595
66910
  });
66596
66911
  },
66597
- cloneWithJoin(node2, join83) {
66912
+ cloneWithJoin(node2, join84) {
66598
66913
  return freeze2({
66599
66914
  ...node2,
66600
- joins: node2.joins ? freeze2([...node2.joins, join83]) : freeze2([join83])
66915
+ joins: node2.joins ? freeze2([...node2.joins, join84]) : freeze2([join84])
66601
66916
  });
66602
66917
  },
66603
66918
  cloneWithReturning(node2, selections) {
@@ -70253,8 +70568,8 @@ var WithSchemaTransformer = class extends OperationNodeTransformer {
70253
70568
  this.#collectSchemableIdsFromTableExpr(node2.table, schemableIds);
70254
70569
  }
70255
70570
  if ("joins" in node2 && node2.joins) {
70256
- for (const join83 of node2.joins) {
70257
- this.#collectSchemableIdsFromTableExpr(join83.table, schemableIds);
70571
+ for (const join84 of node2.joins) {
70572
+ this.#collectSchemableIdsFromTableExpr(join84.table, schemableIds);
70258
70573
  }
70259
70574
  }
70260
70575
  if ("using" in node2 && node2.using) {
@@ -83967,15 +84282,15 @@ function originOnly(url2) {
83967
84282
  var LOCKFILES = /* @__PURE__ */ new Set(["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "Cargo.lock", "poetry.lock", "Gemfile.lock", "composer.lock"]);
83968
84283
  var SECRET_EXTENSION = /\.(pem|key|p12|pfx)$/i;
83969
84284
  function isSkippedChangePath(path2) {
83970
- const normalized = path2.replace(/\\/g, "/");
83971
- const base = normalized.split("/").pop() ?? normalized;
84285
+ const normalized2 = path2.replace(/\\/g, "/");
84286
+ const base = normalized2.split("/").pop() ?? normalized2;
83972
84287
  if (base === ".env" || base.startsWith(".env."))
83973
84288
  return true;
83974
84289
  if (SECRET_EXTENSION.test(base))
83975
84290
  return true;
83976
84291
  if (LOCKFILES.has(base))
83977
84292
  return true;
83978
- return normalized.toLowerCase().split("/").some((segment) => segment.includes("secret"));
84293
+ return normalized2.toLowerCase().split("/").some((segment) => segment.includes("secret"));
83979
84294
  }
83980
84295
  function extractChangeEvidence(diff2, notes) {
83981
84296
  const evidence = [];
@@ -91016,9 +91331,9 @@ async function attachDefectEvidence(result, opts) {
91016
91331
  continue;
91017
91332
  }
91018
91333
  const signal = defectSignalText(d, secrets);
91019
- const finding3 = findFinding(mission, fp);
91020
- const recording = finding3?.recording ?? mission.recording;
91021
- if (finding3 === void 0 || recording === null) {
91334
+ const finding4 = findFinding(mission, fp);
91335
+ const recording = finding4?.recording ?? mission.recording;
91336
+ if (finding4 === void 0 || recording === null) {
91022
91337
  byFp.set(fp, { screenshots: [], signal, skipped: "the defect has no repro Recording to replay" });
91023
91338
  continue;
91024
91339
  }
@@ -91036,9 +91351,9 @@ async function attachDefectEvidence(result, opts) {
91036
91351
  fp,
91037
91352
  await replayWithEvidence({
91038
91353
  recording,
91039
- stepIndex: finding3.repro.recordingStepIndex,
91040
- fingerprints: [fp, ...finding3.related ?? []],
91041
- signalCheckable: SIGNAL_KINDS.has(finding3.kind),
91354
+ stepIndex: finding4.repro.recordingStepIndex,
91355
+ fingerprints: [fp, ...finding4.related ?? []],
91356
+ signalCheckable: SIGNAL_KINDS.has(finding4.kind),
91042
91357
  signal,
91043
91358
  seedUrl: mission.target.seedUrl,
91044
91359
  allowlist: mission.target.allowlist,
@@ -92625,6 +92940,7 @@ async function runExploration(opts) {
92625
92940
  timing: mission.run.timing,
92626
92941
  outcome: goalOutcome,
92627
92942
  runOutcome: mission.run.outcome,
92943
+ ...mission.run.actionableControl === void 0 ? {} : { actionableControl: mission.run.actionableControl },
92628
92944
  ...mission.run.answer === void 0 ? {} : { answer: mission.run.answer },
92629
92945
  ...mission.run.partialReport === void 0 ? {} : { partialReport: mission.run.partialReport },
92630
92946
  depth: mission.run.depth,
@@ -96336,8 +96652,8 @@ async function runVerifyFix(opts) {
96336
96652
  const persisted = parsePersistedMission(raw);
96337
96653
  const replay = replayAllowlist(persisted.target.allowlist, opts);
96338
96654
  const mission = replay.override === void 0 ? persisted : { ...persisted, target: { ...persisted.target, allowlist: [...replay.allowlist] } };
96339
- const finding3 = findFinding(mission, opts.fingerprint);
96340
- if (finding3 === void 0) {
96655
+ const finding4 = findFinding(mission, opts.fingerprint);
96656
+ if (finding4 === void 0) {
96341
96657
  throw new VerifyFixInputError(`no finding with fingerprint ${opts.fingerprint} in ${opts.resultPath}`);
96342
96658
  }
96343
96659
  const origin = assertAuthorizedExploreTarget(mission.target.seedUrl, mission.target.allowlist);
@@ -96355,8 +96671,8 @@ async function runVerifyFix(opts) {
96355
96671
  ...target.settle === void 0 ? {} : { settleConfig: target.settle },
96356
96672
  ...target.hangs === void 0 ? {} : { hangConfig: target.hangs }
96357
96673
  };
96358
- const found = finding3.recording ?? mission.recording;
96359
- if (found === null) throw new VerifyFixInputError(`finding ${finding3.fingerprint} has no Recording to replay`);
96674
+ const found = finding4.recording ?? mission.recording;
96675
+ if (found === null) throw new VerifyFixInputError(`finding ${finding4.fingerprint} has no Recording to replay`);
96360
96676
  const branch = recordedBranchOf(raw?.result);
96361
96677
  let prefix;
96362
96678
  if (branch !== void 0) {
@@ -96369,7 +96685,7 @@ async function runVerifyFix(opts) {
96369
96685
  }
96370
96686
  const recording = prefix === void 0 ? found : anchoredRecording(found);
96371
96687
  try {
96372
- assertSameExtensionBuild(recording.extensions, opts.browser, `finding ${finding3.fingerprint}`);
96688
+ assertSameExtensionBuild(recording.extensions, opts.browser, `finding ${finding4.fingerprint}`);
96373
96689
  } catch (e) {
96374
96690
  throw new VerifyFixInputError(e instanceof Error ? e.message : String(e));
96375
96691
  }
@@ -96402,9 +96718,9 @@ async function runVerifyFix(opts) {
96402
96718
  const authTokenValues = [...invariantAuthTokens?.values() ?? []];
96403
96719
  const primaryActor = mission.target.actors?.find((a) => a.role === "primary")?.name;
96404
96720
  const fx = missionFixtures(mission, opts.fixtureFlags ?? {}, storageState, [...opts.secrets ?? [], ...authTokenValues], target.personas);
96405
- const declared = finding3.kind === "invariant" && finding3.invariantId !== void 0 && invariantSpec !== void 0 ? {
96721
+ const declared = finding4.kind === "invariant" && finding4.invariantId !== void 0 && invariantSpec !== void 0 ? {
96406
96722
  spec: invariantSpec,
96407
- id: finding3.invariantId,
96723
+ id: finding4.invariantId,
96408
96724
  allowlist: mission.target.allowlist,
96409
96725
  baseUrl: mission.target.seedUrl,
96410
96726
  ...invariantAuthTokens === void 0 || invariantAuthTokens.size === 0 ? {} : { authTokens: invariantAuthTokens },
@@ -96433,10 +96749,10 @@ async function runVerifyFix(opts) {
96433
96749
  const replaySession = fx === void 0 ? branchOpen : fixtureReplayOpener(branchOpen, fx, recording.fixture?.outputs ?? mission.fixtures?.outputs ?? {});
96434
96750
  let result;
96435
96751
  try {
96436
- if (finding3.kind === "server-log") {
96437
- if (finding3.serverLog === void 0) {
96752
+ if (finding4.kind === "server-log") {
96753
+ if (finding4.serverLog === void 0) {
96438
96754
  result = {
96439
- fingerprint: finding3.fingerprint,
96755
+ fingerprint: finding4.fingerprint,
96440
96756
  verdict: "inconclusive",
96441
96757
  observedFingerprints: [],
96442
96758
  replay: { outcome: "failed", at: -1, error: "not replayed" },
@@ -96445,13 +96761,13 @@ async function runVerifyFix(opts) {
96445
96761
  } else {
96446
96762
  result = await verifyServerLogDefect({
96447
96763
  recording,
96448
- recordingStepIndex: finding3.repro.recordingStepIndex,
96449
- fingerprint: finding3.fingerprint,
96450
- sources: finding3.serverLog.sources,
96451
- matcher: finding3.serverLog.matcher,
96452
- normalizedMessage: finding3.serverLog.normalizedMessage,
96453
- drainMs: finding3.serverLog.drainMs,
96454
- ...finding3.serverLog.scope === void 0 ? {} : { scope: finding3.serverLog.scope },
96764
+ recordingStepIndex: finding4.repro.recordingStepIndex,
96765
+ fingerprint: finding4.fingerprint,
96766
+ sources: finding4.serverLog.sources,
96767
+ matcher: finding4.serverLog.matcher,
96768
+ normalizedMessage: finding4.serverLog.normalizedMessage,
96769
+ drainMs: finding4.serverLog.drainMs,
96770
+ ...finding4.serverLog.scope === void 0 ? {} : { scope: finding4.serverLog.scope },
96455
96771
  // #142 follow-up: an explicit --allow-log-cmd wins; otherwise the operator's own
96456
96772
  // ~/.jevitate/targets.json entry for this origin may opt in (never an MCP argument).
96457
96773
  allowLogCmd: opts.allowLogCmd === true || target.allowLogCmd === true,
@@ -96465,15 +96781,15 @@ async function runVerifyFix(opts) {
96465
96781
  fixtureBaseDir: journeyFixtureBaseDir(),
96466
96782
  perceive: perceiveOpts,
96467
96783
  recording,
96468
- recordingStepIndex: finding3.repro.recordingStepIndex,
96469
- fingerprint: finding3.fingerprint,
96470
- defectKind: finding3.kind,
96784
+ recordingStepIndex: finding4.repro.recordingStepIndex,
96785
+ fingerprint: finding4.fingerprint,
96786
+ defectKind: finding4.kind,
96471
96787
  ...declared === void 0 ? {} : { invariant: declared },
96472
- ...finding3.hang === void 0 ? {} : { hang: finding3.hang },
96788
+ ...finding4.hang === void 0 ? {} : { hang: finding4.hang },
96473
96789
  // #153: never re-send a paid/destructive write unless the operator opted in.
96474
96790
  safety: { ...target.safety ?? {}, ...opts.hangReplayWrites === true ? { hangReplayWrites: true } : {} },
96475
96791
  writtenBy: recordedWriters(raw),
96476
- ...finding3.occurrences === void 0 ? {} : { occurrences: finding3.occurrences },
96792
+ ...finding4.occurrences === void 0 ? {} : { occurrences: finding4.occurrences },
96477
96793
  ...opts.settleCeilingMs === void 0 ? {} : { settleCeilingMs: opts.settleCeilingMs },
96478
96794
  ...opts.replays === void 0 ? {} : { replays: opts.replays },
96479
96795
  ...opts.actionDeltas === true ? { actionDeltas: true, secrets: [...opts.secrets ?? [], ...authTokenValues] } : {},
@@ -96491,18 +96807,18 @@ async function runVerifyFix(opts) {
96491
96807
  let shotFields = {};
96492
96808
  if (opts.browser?.recordVideo !== void 0 || opts.screenshots !== void 0) {
96493
96809
  const secrets = [...opts.secrets ?? [], ...authTokenValues];
96494
- const stored = persistedDefect(raw, finding3.fingerprint);
96810
+ const stored = persistedDefect(raw, finding4.fingerprint);
96495
96811
  const storedEvidence = stored !== void 0 && isRecord(stored.evidence) ? stored.evidence : void 0;
96496
96812
  const beforeVideo = typeof storedEvidence?.videoPath === "string" ? storedEvidence.videoPath : void 0;
96497
96813
  const beforeShots = Array.isArray(storedEvidence?.screenshots) ? storedEvidence.screenshots.filter((x) => typeof x === "string") : [];
96498
- const shots = opts.screenshots === void 0 ? void 0 : new RunScreenshots({ spec: opts.screenshots, dir: screenshotsDirFor(opts.screenshots, vfArtifact), secrets, title: `verify-fix ${finding3.fingerprint}` });
96814
+ const shots = opts.screenshots === void 0 ? void 0 : new RunScreenshots({ spec: opts.screenshots, dir: screenshotsDirFor(opts.screenshots, vfArtifact), secrets, title: `verify-fix ${finding4.fingerprint}` });
96499
96815
  const verdictCard = () => result.verdict === "fixed" ? { text: "After the fix: the defect no longer reproduces", ok: true } : { text: `After: ${result.verdict === "still-reproduces" ? "still reproduces" : result.verdict}`, ok: false };
96500
96816
  const after = prefix !== void 0 ? { screenshots: [], skipped: "a finding from a Journey branch point: the after-clip replay does not replay the prefix" } : fx !== void 0 || observers.length > 0 ? { screenshots: [], skipped: fx !== void 0 ? "the run used mission fixtures: the after-clip replay does not restore them" : "a cross-actor defect: no after-clip replay" } : await replayWithEvidence({
96501
96817
  recording,
96502
- stepIndex: finding3.repro.recordingStepIndex,
96503
- fingerprints: [finding3.fingerprint, ...finding3.related ?? []],
96504
- signalCheckable: signalCheckable(finding3.kind),
96505
- signal: defectSignalText(stored ?? { kind: finding3.kind, ...finding3.title === void 0 ? {} : { title: finding3.title } }, secrets),
96818
+ stepIndex: finding4.repro.recordingStepIndex,
96819
+ fingerprints: [finding4.fingerprint, ...finding4.related ?? []],
96820
+ signalCheckable: signalCheckable(finding4.kind),
96821
+ signal: defectSignalText(stored ?? { kind: finding4.kind, ...finding4.title === void 0 ? {} : { title: finding4.title } }, secrets),
96506
96822
  seedUrl: mission.target.seedUrl,
96507
96823
  allowlist: mission.target.allowlist,
96508
96824
  ...storageState === void 0 ? {} : { storageState },
@@ -96531,7 +96847,7 @@ async function runVerifyFix(opts) {
96531
96847
  ...stale === void 0 ? {} : { failure: { kind: "journey-stale", message: stale.message } },
96532
96848
  ...evidence === void 0 ? {} : { evidence },
96533
96849
  ...shotFields,
96534
- ...finding3.title === void 0 ? {} : { title: finding3.title },
96850
+ ...finding4.title === void 0 ? {} : { title: finding4.title },
96535
96851
  ...replay.override === void 0 ? {} : { allowlistOverride: replay.override },
96536
96852
  ...fx === void 0 ? {} : { fixtures: { ...fx.record(), missionIdentity: mission.fixtures?.identity ?? "none" } },
96537
96853
  // Every replay closed its own session (finalizing its video) before verifyFix returned.
@@ -96631,17 +96947,17 @@ function ledgerAdd(opts) {
96631
96947
  } catch (e) {
96632
96948
  throw new LedgerError("E_LEDGER_INPUT", `${opts.resultPath}: ${e instanceof Error ? e.message : String(e)}`);
96633
96949
  }
96634
- const finding3 = findFinding(mission, opts.fingerprint);
96950
+ const finding4 = findFinding(mission, opts.fingerprint);
96635
96951
  const result = isRecord(raw) && isRecord(raw.result) ? raw.result : {};
96636
- const item = finding3 === void 0 ? void 0 : rawFinding(result, finding3.fingerprint);
96637
- if (finding3 === void 0 || item === void 0) {
96952
+ const item = finding4 === void 0 ? void 0 : rawFinding(result, finding4.fingerprint);
96953
+ if (finding4 === void 0 || item === void 0) {
96638
96954
  throw new LedgerError("E_LEDGER_INPUT", `no finding with fingerprint ${opts.fingerprint} in ${opts.resultPath}`);
96639
96955
  }
96640
96956
  const ownRecording = isRecord(item.repro) && item.repro.recording !== void 0;
96641
96957
  if (!ownRecording && mission.recording === null) {
96642
- throw new LedgerError("E_LEDGER_INPUT", `finding ${finding3.fingerprint} has no Recording to replay \u2014 nothing verify-fix could re-check`);
96958
+ throw new LedgerError("E_LEDGER_INPUT", `finding ${finding4.fingerprint} has no Recording to replay \u2014 nothing verify-fix could re-check`);
96643
96959
  }
96644
- const list2 = finding3.kind === "hang" ? "hangs" : "defects";
96960
+ const list2 = finding4.kind === "hang" ? "hangs" : "defects";
96645
96961
  const persistedFixtures = isRecord(result.fixtures) ? result.fixtures : void 0;
96646
96962
  const material = {
96647
96963
  ...typeof result.schemaVersion === "number" ? { schemaVersion: result.schemaVersion } : {},
@@ -96651,7 +96967,7 @@ function ledgerAdd(opts) {
96651
96967
  recording: ownRecording ? null : mission.recording,
96652
96968
  defects: list2 === "defects" ? [item] : [],
96653
96969
  hangs: list2 === "hangs" ? [item] : [],
96654
- ...finding3.kind === "invariant" && mission.invariantSpec !== void 0 ? { invariantSpec: mission.invariantSpec } : {},
96970
+ ...finding4.kind === "invariant" && mission.invariantSpec !== void 0 ? { invariantSpec: mission.invariantSpec } : {},
96655
96971
  // The fixture's spec, hook HASHES and non-secret outputs — what a replay restores; hooks are never stored.
96656
96972
  ...persistedFixtures === void 0 ? {} : {
96657
96973
  fixtures: {
@@ -96668,14 +96984,14 @@ function ledgerAdd(opts) {
96668
96984
  };
96669
96985
  assertNoSecrets(material, opts.secrets ?? []);
96670
96986
  const dir = ledgerDir(opts.regressionsDir);
96671
- const path2 = entryPath(dir, finding3.fingerprint);
96987
+ const path2 = entryPath(dir, finding4.fingerprint);
96672
96988
  const previous = existsSync20(path2) ? readEntry(path2) : void 0;
96673
96989
  const ticket = opts.ticket ?? previous?.ledger.ticket;
96674
96990
  const meta3 = {
96675
96991
  version: LEDGER_ENTRY_VERSION,
96676
- fingerprint: finding3.fingerprint,
96677
- kind: finding3.kind,
96678
- ...finding3.title === void 0 ? {} : { title: finding3.title },
96992
+ fingerprint: finding4.fingerprint,
96993
+ kind: finding4.kind,
96994
+ ...finding4.title === void 0 ? {} : { title: finding4.title },
96679
96995
  ...ticket === void 0 ? {} : { ticket },
96680
96996
  addedAt: previous?.ledger.addedAt ?? (opts.nowIso ?? (() => clock.nowIso()))(),
96681
96997
  source: {
@@ -96687,16 +97003,16 @@ function ledgerAdd(opts) {
96687
97003
  addedBy: currentEngineInfo()
96688
97004
  };
96689
97005
  const entry = { ledger: meta3, result: material };
96690
- const check3 = findFinding(parsePersistedMission(entry), finding3.fingerprint);
96691
- if (check3 === void 0) throw new LedgerError("E_LEDGER_INPUT", `finding ${finding3.fingerprint} did not survive into its ledger entry`);
97006
+ const check3 = findFinding(parsePersistedMission(entry), finding4.fingerprint);
97007
+ if (check3 === void 0) throw new LedgerError("E_LEDGER_INPUT", `finding ${finding4.fingerprint} did not survive into its ledger entry`);
96692
97008
  mkdirSync9(dir, { recursive: true });
96693
97009
  writeFileSync12(path2, `${JSON.stringify(entry, null, 2)}
96694
97010
  `, "utf8");
96695
97011
  return {
96696
97012
  entryPath: path2,
96697
- fingerprint: finding3.fingerprint,
96698
- kind: finding3.kind,
96699
- ...finding3.title === void 0 ? {} : { title: finding3.title },
97013
+ fingerprint: finding4.fingerprint,
97014
+ kind: finding4.kind,
97015
+ ...finding4.title === void 0 ? {} : { title: finding4.title },
96700
97016
  ...ticket === void 0 ? {} : { ticket },
96701
97017
  updated: previous !== void 0
96702
97018
  };
@@ -96914,14 +97230,14 @@ function realSecureIO() {
96914
97230
  return readMaskedLine(process.stdin, process.stdout, message2);
96915
97231
  },
96916
97232
  async persist(key2, value) {
96917
- const { mkdir: mkdir38, writeFile: writeFile45, readFile: readFile41 } = await import("node:fs/promises");
97233
+ const { mkdir: mkdir40, writeFile: writeFile47, readFile: readFile46 } = await import("node:fs/promises");
96918
97234
  const { dirname: dirname34 } = await import("node:path");
96919
97235
  const path2 = resolveDataDir(["credentials.json"]);
96920
- await mkdir38(dirname34(path2), { recursive: true });
97236
+ await mkdir40(dirname34(path2), { recursive: true });
96921
97237
  let existing = {};
96922
97238
  let raw;
96923
97239
  try {
96924
- raw = await readFile41(path2, "utf8");
97240
+ raw = await readFile46(path2, "utf8");
96925
97241
  } catch (err) {
96926
97242
  const missing = typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
96927
97243
  if (!missing) throw err;
@@ -96934,7 +97250,7 @@ function realSecureIO() {
96934
97250
  }
96935
97251
  }
96936
97252
  existing[key2] = value;
96937
- await writeFile45(path2, JSON.stringify(existing, null, 2), { mode: 384 });
97253
+ await writeFile47(path2, JSON.stringify(existing, null, 2), { mode: 384 });
96938
97254
  }
96939
97255
  };
96940
97256
  }
@@ -97318,7 +97634,7 @@ function makeFinding(input2, rubric, evidence) {
97318
97634
  if (!Number.isInteger(occurrences3) || occurrences3 < 1) {
97319
97635
  throw new UxFindingError(`occurrences for '${entry.id}' must be a positive integer, got ${occurrences3}`);
97320
97636
  }
97321
- const finding3 = {
97637
+ const finding4 = {
97322
97638
  rubricItemId: entry.id,
97323
97639
  citation: { source: entry.citation.source, ref: entry.citation.ref },
97324
97640
  severity: input2.severity,
@@ -97338,7 +97654,7 @@ function makeFinding(input2, rubric, evidence) {
97338
97654
  ...input2.quality ? { quality: { ...input2.quality } } : {},
97339
97655
  ...input2.predictedAttention ? { predictedAttention: input2.predictedAttention } : {}
97340
97656
  };
97341
- return Object.freeze(finding3);
97657
+ return Object.freeze(finding4);
97342
97658
  }
97343
97659
 
97344
97660
  // ../ux/dist/redact.js
@@ -97799,9 +98115,9 @@ function dedupeObjectiveFindings(items, rubric) {
97799
98115
  }
97800
98116
 
97801
98117
  // ../ux/dist/recommend.js
97802
- function evidenceSummaryFor(finding3, evidence) {
98118
+ function evidenceSummaryFor(finding4, evidence) {
97803
98119
  const parts2 = [];
97804
- for (const ref of finding3.evidenceRefs) {
98120
+ for (const ref of finding4.evidenceRefs) {
97805
98121
  if (ref.id.startsWith("control:")) {
97806
98122
  const idx = Number(ref.id.slice("control:".length));
97807
98123
  const c = evidence.controls.find((ctrl) => ctrl.index === idx);
@@ -97813,19 +98129,19 @@ function evidenceSummaryFor(finding3, evidence) {
97813
98129
  }
97814
98130
  return parts2.join(" | ").slice(0, 4e3);
97815
98131
  }
97816
- async function recommend(gen, finding3, evidence, secrets = []) {
98132
+ async function recommend(gen, finding4, evidence, secrets = []) {
97817
98133
  const input2 = {
97818
- principle: finding3.rubricItemId,
97819
- citationSource: finding3.citation.source,
97820
- citationRef: finding3.citation.ref,
97821
- judgmentSummary: finding3.recommendation,
97822
- evidenceSummary: evidenceSummaryFor(finding3, evidence),
98134
+ principle: finding4.rubricItemId,
98135
+ citationSource: finding4.citation.source,
98136
+ citationRef: finding4.citation.ref,
98137
+ judgmentSummary: finding4.recommendation,
98138
+ evidenceSummary: evidenceSummaryFor(finding4, evidence),
97823
98139
  appClass: evidence.appContext.appClass
97824
98140
  };
97825
98141
  assertNoSecretInPayload(input2, secrets);
97826
98142
  const result = await gen.generate("ux.recommendation", input2);
97827
98143
  const body = result.output.recommendation.trim();
97828
- return `${body} (${finding3.citation.source}: ${finding3.citation.ref})`;
98144
+ return `${body} (${finding4.citation.source}: ${finding4.citation.ref})`;
97829
98145
  }
97830
98146
 
97831
98147
  // ../ux/dist/a11y.js
@@ -98172,7 +98488,7 @@ function makeSignalFinding(input2) {
98172
98488
  ...input2.evidence.requests.map((r) => ({ id: `request:${r.id}` })),
98173
98489
  ...input2.evidence.screenshot === void 0 ? [] : [{ id: `screenshot:${input2.evidence.screenshot}` }]
98174
98490
  ];
98175
- const finding3 = {
98491
+ const finding4 = {
98176
98492
  rubricItemId: rule.id,
98177
98493
  citation: { ...rule.citation },
98178
98494
  severity: rule.severity,
@@ -98190,7 +98506,7 @@ function makeSignalFinding(input2) {
98190
98506
  screenIds: [...input2.screenIds ?? [input2.screenId]],
98191
98507
  signal: input2.evidence
98192
98508
  };
98193
- return Object.freeze(finding3);
98509
+ return Object.freeze(finding4);
98194
98510
  }
98195
98511
 
98196
98512
  // ../ux/dist/signals/shared.js
@@ -99948,9 +100264,9 @@ async function analyzeClaims(request, deps) {
99948
100264
  const dupOptions = /* @__PURE__ */ new Map();
99949
100265
  for (const d of group) {
99950
100266
  const i = indexOf.get(d);
99951
- const finding3 = describeDraft(d, prose[i]);
99952
- questions[`grade::${i}::need`] = { kind: "noul", instructions: prompts.grade.need.replace("{finding}", finding3).replace("{persona}", persona2) };
99953
- questions[`grade::${i}::ship`] = { kind: "noul", instructions: prompts.grade.ship.replace("{finding}", finding3).replace("{feature}", feature(d)) };
100267
+ const finding4 = describeDraft(d, prose[i]);
100268
+ questions[`grade::${i}::need`] = { kind: "noul", instructions: prompts.grade.need.replace("{finding}", finding4).replace("{persona}", persona2) };
100269
+ questions[`grade::${i}::ship`] = { kind: "noul", instructions: prompts.grade.ship.replace("{finding}", finding4).replace("{feature}", feature(d)) };
99954
100270
  const earlier = drafts.slice(0, i).map((e, j) => ({ e, j })).filter(({ e }) => d.target !== void 0 && e.target !== void 0 && controlKey2(d.target) === controlKey2(e.target) && e.screen.route === d.screen.route || e.type === d.type && e.screen.route === d.screen.route).map(({ j }) => j).slice(0, MAX_CHOICE_OPTIONS - 1);
99955
100271
  if (earlier.length > 0) {
99956
100272
  dupOptions.set(i, earlier);
@@ -99958,7 +100274,7 @@ async function analyzeClaims(request, deps) {
99958
100274
  kind: "choice",
99959
100275
  options: ["new", ...earlier.map((j) => `same:${j}`)],
99960
100276
  descriptions: Object.fromEntries([["new", "a different problem"], ...earlier.map((j) => [`same:${j}`, describeDraft(drafts[j], prose[j])])]),
99961
- instructions: prompts.duplicate.replace("{finding}", finding3)
100277
+ instructions: prompts.duplicate.replace("{finding}", finding4)
99962
100278
  };
99963
100279
  }
99964
100280
  }
@@ -100007,9 +100323,9 @@ async function analyzeClaims(request, deps) {
100007
100323
  const impact = d.friction?.impact ?? v.impact ?? "confused";
100008
100324
  const severity2 = v.severity ?? (d.friction === void 0 ? "minor" : IMPACT_SEVERITY2[d.friction.impact]);
100009
100325
  const evidenceRefs = d.target !== void 0 ? [{ id: `control:${d.target.index}` }] : (v.quotes ?? []).length > 0 ? [{ id: "visibleText" }] : [{ id: "url" }];
100010
- let finding3;
100326
+ let finding4;
100011
100327
  try {
100012
- finding3 = makeFinding({
100328
+ finding4 = makeFinding({
100013
100329
  rubricItemId,
100014
100330
  evidenceRefs,
100015
100331
  severity: severity2,
@@ -100028,8 +100344,8 @@ async function analyzeClaims(request, deps) {
100028
100344
  if (deps.polish === true) {
100029
100345
  if (deps.gen === void 0)
100030
100346
  throw new Error("polish requested without a generation gateway");
100031
- const polished = await recommend(deps.gen, finding3, d.screen.red, secrets);
100032
- finding3 = Object.freeze({ ...finding3, recommendation: polished });
100347
+ const polished = await recommend(deps.gen, finding4, d.screen.red, secrets);
100348
+ finding4 = Object.freeze({ ...finding4, recommendation: polished });
100033
100349
  }
100034
100350
  } catch (cause) {
100035
100351
  return { kind: "failed", reason: `claim finding failed: ${message(cause)}`, screenId: d.screen.raw.screenId, rubricItemId };
@@ -100042,7 +100358,7 @@ async function analyzeClaims(request, deps) {
100042
100358
  ...d.target === void 0 ? {} : { target: label(d.target) }
100043
100359
  };
100044
100360
  built.set(i, Object.freeze({
100045
- ...finding3,
100361
+ ...finding4,
100046
100362
  claim: claim2,
100047
100363
  impact,
100048
100364
  ...grade === void 0 ? {} : { grade },
@@ -100051,7 +100367,7 @@ async function analyzeClaims(request, deps) {
100051
100367
  const r = root2(i);
100052
100368
  if (r !== i) {
100053
100369
  const list2 = contributions.get(r) ?? [];
100054
- list2.push({ rubricItemId, observation: finding3.observation, confidence: finding3.confidence, occurrences: finding3.occurrences, citation: finding3.citation, ...finding3.quality ? { quality: finding3.quality } : {} });
100370
+ list2.push({ rubricItemId, observation: finding4.observation, confidence: finding4.confidence, occurrences: finding4.occurrences, citation: finding4.citation, ...finding4.quality ? { quality: finding4.quality } : {} });
100055
100371
  contributions.set(r, list2);
100056
100372
  }
100057
100373
  }
@@ -100082,8 +100398,8 @@ async function analyzeClaims(request, deps) {
100082
100398
  if (ok2.length === 0)
100083
100399
  continue;
100084
100400
  a11yEvaluated += ok2.length;
100085
- for (const finding3 of deps.a11yChecker(s3.red, request.rubric).findings)
100086
- a11yFound.push({ finding: finding3, refs: s3.red.refs });
100401
+ for (const finding4 of deps.a11yChecker(s3.red, request.rubric).findings)
100402
+ a11yFound.push({ finding: finding4, refs: s3.red.refs });
100087
100403
  }
100088
100404
  }
100089
100405
  const ledger = summarizeLedger(records);
@@ -105616,7 +105932,7 @@ ${route ?? ""}
105616
105932
  ${key2}`, "utf8").digest("hex").slice(0, 16);
105617
105933
  const step = first.index + 1;
105618
105934
  const fix = s3.route !== void 0 && route !== void 0 && s3.route !== route ? s3.fix.split(s3.route).join(route) : s3.fix;
105619
- const finding3 = {
105935
+ const finding4 = {
105620
105936
  fingerprint: fingerprint2,
105621
105937
  kind: "ux",
105622
105938
  claim: "other",
@@ -105632,12 +105948,12 @@ ${key2}`, "utf8").digest("hex").slice(0, 16);
105632
105948
  recommendation: fix,
105633
105949
  locator: { element: s3.element, attribute: s3.attribute, testId: s3.testId, fix, steps: s3.occurrences.length }
105634
105950
  };
105635
- const issues = findingIssues(finding3);
105951
+ const issues = findingIssues(finding4);
105636
105952
  if (issues.length > 0) {
105637
- warnings.push(findingDropWarning(fingerprint2, finding3.kind, issues));
105953
+ warnings.push(findingDropWarning(fingerprint2, finding4.kind, issues));
105638
105954
  continue;
105639
105955
  }
105640
- findings.push(finding3);
105956
+ findings.push(finding4);
105641
105957
  }
105642
105958
  return { findings, warnings };
105643
105959
  }
@@ -107619,8 +107935,51 @@ function registerLocatorHealthCommand(program2, deps) {
107619
107935
  }
107620
107936
 
107621
107937
  // src/report-cli.ts
107622
- import { mkdir as mkdir27, writeFile as writeFile32 } from "node:fs/promises";
107938
+ import { mkdir as mkdir28, readFile as readFile28, writeFile as writeFile33 } from "node:fs/promises";
107623
107939
  import { join as join58 } from "node:path";
107940
+
107941
+ // src/journeeze-hint.ts
107942
+ import { createHash as createHash18 } from "node:crypto";
107943
+ import { mkdir as mkdir27, readFile as readFile27, rename as rename6, writeFile as writeFile32 } from "node:fs/promises";
107944
+ async function loadJourneezeHintPreference(path2 = resolveDataDir(["config.json"])) {
107945
+ try {
107946
+ const parsed = JSON.parse(await readFile27(path2, "utf8"));
107947
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
107948
+ const hints = parsed.hints;
107949
+ if (hints === null || typeof hints !== "object" || Array.isArray(hints)) return {};
107950
+ const value = hints.journeeze;
107951
+ if (value === false) return { enabled: false };
107952
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
107953
+ const record2 = value;
107954
+ return { ...record2.enabled === false ? { enabled: false } : {}, ...typeof record2.dismissedSignal === "string" ? { dismissedSignal: record2.dismissedSignal } : {} };
107955
+ } catch (err) {
107956
+ if (err.code === "ENOENT") return {};
107957
+ throw err;
107958
+ }
107959
+ }
107960
+ function decideJourneezeHint(signal, preference = {}) {
107961
+ const authors = [...new Set(signal.journeyAuthors.map((author) => author.trim()).filter(Boolean))].sort();
107962
+ const reasons = [
107963
+ ...signal.sharedSource ? ["shared Journey source configured"] : [],
107964
+ ...authors.length >= 2 ? [`${authors.length} Journey/approval authors`] : [],
107965
+ ...signal.unresolvedFindings >= 3 ? [`${signal.unresolvedFindings} persistent unresolved findings`] : [],
107966
+ ...signal.strictReviewApproved ? ["human-approved strict Journey review"] : []
107967
+ ];
107968
+ const eligible = signal.sharedSource || reasons.length >= 2;
107969
+ const hash3 = createHash18("sha256").update(JSON.stringify({ sharedSource: signal.sharedSource, authors, unresolvedFindings: signal.unresolvedFindings, strictReviewApproved: signal.strictReviewApproved })).digest("hex");
107970
+ return { show: preference.enabled !== false && eligible && preference.dismissedSignal !== hash3, signal: hash3, reasons };
107971
+ }
107972
+ function renderJourneezeHint(signal, preference = {}, context = { isTTY: process.stdout.isTTY === true }) {
107973
+ const env = context.env ?? process.env;
107974
+ if (context.isTTY !== true || context.json === true || (context.mcp ?? inMcpInvocation()) || CI_ENV_MARKERS.some((key2) => env[key2] !== void 0)) return null;
107975
+ const decision = decideJourneezeHint(signal, preference);
107976
+ if (!decision.show) return null;
107977
+ return `
107978
+ Tip: Journeeze can coordinate this shared testing work (${decision.reasons.join(", ")}). Run \`jevitate connect journeeze\` to connect it.
107979
+ `;
107980
+ }
107981
+
107982
+ // src/report-cli.ts
107624
107983
  function emit3(program2, envelope, exitCode) {
107625
107984
  emitJsonOrRefusal(program2, envelope, exitCode);
107626
107985
  }
@@ -107629,6 +107988,25 @@ function failure2(program2, err, code) {
107629
107988
  else emit3(program2, fail(code, err instanceof Error ? err.message : String(err)));
107630
107989
  }
107631
107990
  var collect3 = (v, prev) => [...prev, v];
107991
+ async function hasSharedSource() {
107992
+ try {
107993
+ const raw = JSON.parse(await readFile28(join58(process.cwd(), "jevitate.lock"), "utf8"));
107994
+ return raw !== null && typeof raw === "object" && !Array.isArray(raw) && Array.isArray(raw.sources) && raw.sources.length > 0;
107995
+ } catch {
107996
+ return false;
107997
+ }
107998
+ }
107999
+ async function reportJourneezeHint(json2) {
108000
+ try {
108001
+ return renderJourneezeHint(
108002
+ { sharedSource: await hasSharedSource(), journeyAuthors: [], unresolvedFindings: 0, strictReviewApproved: false },
108003
+ await loadJourneezeHintPreference(),
108004
+ { isTTY: process.stdout.isTTY === true, json: json2 }
108005
+ ) ?? "";
108006
+ } catch {
108007
+ return "";
108008
+ }
108009
+ }
107632
108010
  function registerReportCommands(program2, deps) {
107633
108011
  program2.command("report").description("one deduped defect list for a target across every mode and run (markdown + JSON envelope)").option("--target <origin|name>", "the target: an origin (or URL on it), a suite target name, or a registered mission target").option("--since <run|date>", "only runs that started at/after this ISO date or this run").option("--dir <dir>", "results dir to read (repeatable). Default: this project's runs \u2014 its .jevitate/logs plus every run recorded for it in ~/.jevitate/run-index.jsonl (including --out dirs); with --target, every .jevitate/logs dir (project and ~/.jevitate) and the 0.1.0 recordings/ux-reports dirs too", collect3, []).option("--baseline <run|tag|last>", "add a diff section against a baseline: a run, a `baseline tag`, or `last` (the previous run per target+mode)").option("--tag <key=value>", "only runs carrying this tag (repeatable; every tag must match)", collect3, []).option("--out <dir>", "also write report.md and report.json here").option("--json", "emit the JSON envelope instead of markdown").action(async function() {
107634
108012
  const o = this.opts();
@@ -107644,14 +108022,14 @@ function registerReportCommands(program2, deps) {
107644
108022
  ...deps.baselinesDir === void 0 ? {} : { baselinesDir: deps.baselinesDir }
107645
108023
  });
107646
108024
  if (o.out !== void 0) {
107647
- await mkdir27(o.out, { recursive: true });
107648
- await writeFile32(join58(o.out, "report.md"), report2.markdown, "utf8");
107649
- await writeFile32(join58(o.out, "report.json"), `${JSON.stringify(ok(report2), null, 2)}
108025
+ await mkdir28(o.out, { recursive: true });
108026
+ await writeFile33(join58(o.out, "report.md"), report2.markdown, "utf8");
108027
+ await writeFile33(join58(o.out, "report.json"), `${JSON.stringify(ok(report2), null, 2)}
107650
108028
  `, "utf8");
107651
108029
  }
107652
108030
  if (o.json) emit3(program2, ok(report2), 0);
107653
108031
  else {
107654
- program2.configureOutput().writeOut?.(report2.markdown);
108032
+ program2.configureOutput().writeOut?.(`${report2.markdown}${await reportJourneezeHint(o.json)}`);
107655
108033
  process.exitCode = 0;
107656
108034
  }
107657
108035
  } catch (err) {
@@ -107832,7 +108210,7 @@ async function collectAllMissingKeys(store, io, opts = {}) {
107832
108210
  }
107833
108211
 
107834
108212
  // src/init-mcp.ts
107835
- import { readFile as readFile27, writeFile as writeFile33, mkdir as mkdir28 } from "node:fs/promises";
108213
+ import { readFile as readFile29, writeFile as writeFile34, mkdir as mkdir29 } from "node:fs/promises";
107836
108214
  import { homedir as realHomedir2 } from "node:os";
107837
108215
  import { join as join59, dirname as dirname26 } from "node:path";
107838
108216
  var MCP_SERVER_NAME = "jevitate";
@@ -107965,7 +108343,7 @@ async function registerMcp(runtimes, paths, opts = {}) {
107965
108343
  for (const unit of units) {
107966
108344
  let existing;
107967
108345
  try {
107968
- existing = await readFile27(unit.path, "utf8");
108346
+ existing = await readFile29(unit.path, "utf8");
107969
108347
  } catch {
107970
108348
  existing = void 0;
107971
108349
  }
@@ -107975,8 +108353,8 @@ async function registerMcp(runtimes, paths, opts = {}) {
107975
108353
  entry.instruction = renderPrintConfig(unit.harness);
107976
108354
  }
107977
108355
  if (!opts.dryRun && result.content !== void 0) {
107978
- await mkdir28(dirname26(unit.path), { recursive: true });
107979
- await writeFile33(unit.path, result.content, "utf8");
108356
+ await mkdir29(dirname26(unit.path), { recursive: true });
108357
+ await writeFile34(unit.path, result.content, "utf8");
107980
108358
  }
107981
108359
  report2.push(entry);
107982
108360
  }
@@ -108296,7 +108674,7 @@ function registerProfileCommands(program2, deps) {
108296
108674
  }
108297
108675
 
108298
108676
  // src/site-cli.ts
108299
- import { readFile as readFile28 } from "node:fs/promises";
108677
+ import { readFile as readFile30 } from "node:fs/promises";
108300
108678
  function registerSiteCommands(program2, deps) {
108301
108679
  const site = program2.command("site").description("per-site policies for Journey runs: human-like pacing, throttles, run budgets and quiet hours");
108302
108680
  const sitePolicy = site.command("policy").description("read or set a site's policy (the site is the Journey's origin, e.g. https://app.example.com)");
@@ -108354,7 +108732,7 @@ function registerSiteCommands(program2, deps) {
108354
108732
  const { account, db, file: file2, json: json2 } = this.opts();
108355
108733
  let policy;
108356
108734
  try {
108357
- const raw = await readFile28(file2, "utf8");
108735
+ const raw = await readFile30(file2, "utf8");
108358
108736
  policy = SitePolicySchema.parse(JSON.parse(raw));
108359
108737
  } catch (err) {
108360
108738
  emitJson(program2, fail("E_INVALID_POLICY", String(err)));
@@ -108382,7 +108760,7 @@ function registerSiteCommands(program2, deps) {
108382
108760
  const { account, db, script, seed, json: json2 } = this.opts();
108383
108761
  let plannedScript;
108384
108762
  try {
108385
- const raw = await readFile28(script, "utf8");
108763
+ const raw = await readFile30(script, "utf8");
108386
108764
  plannedScript = parsePlannedScript(raw);
108387
108765
  } catch (err) {
108388
108766
  emitJson(program2, fail("E_INVALID_SCRIPT", String(err)));
@@ -108418,12 +108796,12 @@ function registerSiteCommands(program2, deps) {
108418
108796
  }
108419
108797
 
108420
108798
  // src/recording-cli.ts
108421
- import { readFile as readFile29, writeFile as writeFile34 } from "node:fs/promises";
108799
+ import { readFile as readFile31, writeFile as writeFile35 } from "node:fs/promises";
108422
108800
  import { resolve as resolvePath14 } from "node:path";
108423
108801
  import * as clack from "@clack/prompts";
108424
108802
  function registerRecordingCommands(program2, deps) {
108425
108803
  async function readTakeJson(file2) {
108426
- const raw = await readFile29(file2, "utf8");
108804
+ const raw = await readFile31(file2, "utf8");
108427
108805
  let json2;
108428
108806
  try {
108429
108807
  json2 = JSON.parse(raw);
@@ -108442,7 +108820,7 @@ function registerRecordingCommands(program2, deps) {
108442
108820
  return { recording: parsed.recording, values: new Map(Object.entries(parsed.values)) };
108443
108821
  }
108444
108822
  async function readRecordingOrTake(file2) {
108445
- const raw = await readFile29(file2, "utf8");
108823
+ const raw = await readFile31(file2, "utf8");
108446
108824
  const json2 = JSON.parse(raw);
108447
108825
  if (json2 !== null && typeof json2 === "object" && !Array.isArray(json2) && "recording" in json2) {
108448
108826
  return AuthoringTakeSchema.parse(json2).recording;
@@ -108517,7 +108895,7 @@ function registerRecordingCommands(program2, deps) {
108517
108895
  }
108518
108896
  const result = applyPostdoc(takes[0], diff2, decisions);
108519
108897
  if (out !== void 0) {
108520
- await writeFile34(out, `${JSON.stringify(result, null, 2)}
108898
+ await writeFile35(out, `${JSON.stringify(result, null, 2)}
108521
108899
  `, "utf8");
108522
108900
  }
108523
108901
  if (json2) {
@@ -108595,7 +108973,7 @@ var DraftSaveError = class extends Error {
108595
108973
  async function saveDraft(draft, out, journeysDir) {
108596
108974
  const steps = draft.recording.pages.reduce((n3, p) => n3 + p.steps.length, 0);
108597
108975
  if (out !== void 0) {
108598
- await writeFile34(out, `${JSON.stringify(draft, null, 2)}
108976
+ await writeFile35(out, `${JSON.stringify(draft, null, 2)}
108599
108977
  `, "utf8");
108600
108978
  return { id: draft.metadata.id, path: resolvePath14(out), savedTo: "file", promoted: false, steps };
108601
108979
  }
@@ -108694,7 +109072,7 @@ async function loadDecisions(file2) {
108694
109072
  let raw;
108695
109073
  let parsed;
108696
109074
  try {
108697
- raw = await readFile29(file2, "utf8");
109075
+ raw = await readFile31(file2, "utf8");
108698
109076
  parsed = JSON.parse(raw);
108699
109077
  } catch (err) {
108700
109078
  throw new DecisionsParseError(err);
@@ -108771,9 +109149,9 @@ function targetsOpts(deps) {
108771
109149
  const file2 = deps.explore?.targetsConfigPath;
108772
109150
  return file2 === void 0 ? {} : { targetsFile: file2 };
108773
109151
  }
108774
- function lintFindingLine(finding3) {
108775
- const message2 = finding3.message.replace(/^step \d+: /, "");
108776
- return `${finding3.level} ${finding3.step === void 0 ? "" : `step ${finding3.step} `}${finding3.rule} ${message2}${finding3.fix === void 0 ? "" : ` \u2014 fix: ${finding3.fix}`}`;
109152
+ function lintFindingLine(finding4) {
109153
+ const message2 = finding4.message.replace(/^step \d+: /, "");
109154
+ return `${finding4.level} ${finding4.step === void 0 ? "" : `step ${finding4.step} `}${finding4.rule} ${message2}${finding4.fix === void 0 ? "" : ` \u2014 fix: ${finding4.fix}`}`;
108777
109155
  }
108778
109156
  var FULL_FLAG_HELP = "#496: show every detail section: catalog analysis, the full lint list, locator health, provenance, the full change diff";
108779
109157
  var DEPRECATED_DIR_HELP = "deprecated: use --journeys-dir <path> (same meaning); default: the repo's .jevitate/journeys, outside a repo ~/.jevitate/journeys";
@@ -108865,7 +109243,7 @@ function registerJourneyFind(journey, program2, deps) {
108865
109243
  import { existsSync as existsSync27 } from "node:fs";
108866
109244
 
108867
109245
  // src/journey-annotate-api.ts
108868
- import { mkdir as mkdir29, readFile as readFile30, rm as rm12, writeFile as writeFile35 } from "node:fs/promises";
109246
+ import { mkdir as mkdir30, readFile as readFile32, rm as rm12, writeFile as writeFile36 } from "node:fs/promises";
108869
109247
  import { dirname as dirname28, join as join62 } from "node:path";
108870
109248
  var EVIDENCE_TEXT_CHARS = 1500;
108871
109249
  var AnnotationDraftNotFoundError = class extends Error {
@@ -109013,8 +109391,8 @@ async function annotateJourney(opts) {
109013
109391
  });
109014
109392
  assertNoSecretInPayload(draft, secrets, "journey annotation draft");
109015
109393
  const draftPath = annotationDraftPath(opts.dir, opts.id);
109016
- await mkdir29(dirname28(draftPath), { recursive: true, mode: 448 });
109017
- await writeFile35(draftPath, `${JSON.stringify(draft, null, 2)}
109394
+ await mkdir30(dirname28(draftPath), { recursive: true, mode: 448 });
109395
+ await writeFile36(draftPath, `${JSON.stringify(draft, null, 2)}
109018
109396
  `, { mode: 384 });
109019
109397
  return {
109020
109398
  id: opts.id,
@@ -109037,7 +109415,7 @@ async function readAnnotationDraft(dir, id) {
109037
109415
  const draftPath = annotationDraftPath(dir, id);
109038
109416
  let raw;
109039
109417
  try {
109040
- raw = await readFile30(draftPath, "utf8");
109418
+ raw = await readFile32(draftPath, "utf8");
109041
109419
  } catch (err) {
109042
109420
  if (err.code === "ENOENT") {
109043
109421
  throw new AnnotationDraftNotFoundError(`no annotation draft for journey '${id}' (${draftPath}) \u2014 run \`jevitate journey annotate ${id}\` first`);
@@ -109976,6 +110354,35 @@ async function reviewJourneyById(journeysDir, id, opts = {}) {
109976
110354
  }
109977
110355
 
109978
110356
  // src/journey-verify.ts
110357
+ async function planJourneyVerification(opts) {
110358
+ const journey = await new JourneyRegistry(new FsJourneyStore(opts.dir)).get(opts.id);
110359
+ if (!journey) throw new UnknownJourneyError(`unknown journey '${opts.id}'`);
110360
+ const sites = journeyAssertions(journey);
110361
+ const checkOf = new Map(sites.map((site) => [assertionSiteKey(site), describeCheck(site.check)]));
110362
+ const plan2 = planJourneyMutations(journey, {
110363
+ ...opts.readRequests === void 0 ? {} : { readRequests: opts.readRequests },
110364
+ params: opts.params
110365
+ });
110366
+ const pairs = plan2.pairs.map((pair) => ({ ...pair, check: checkOf.get(pair.site) ?? "unknown assertion" }));
110367
+ const assertions = sites.map((site) => {
110368
+ const key2 = assertionSiteKey(site);
110369
+ return { site: key2, check: describeCheck(site.check), mutations: pairs.filter((pair) => pair.site === key2).map((pair) => pair.mutation) };
110370
+ });
110371
+ return {
110372
+ journeyId: journey.metadata.id,
110373
+ journeyHash: journeyContentHash(journey),
110374
+ mutations: plan2.mutations,
110375
+ pairs,
110376
+ assertions,
110377
+ summary: {
110378
+ mutations: plan2.mutations.length,
110379
+ pairs: pairs.length,
110380
+ assertions: assertions.length,
110381
+ unpaired: assertions.filter((assertion) => assertion.mutations.length === 0).length,
110382
+ minimumReplays: plan2.mutations.length + 1
110383
+ }
110384
+ };
110385
+ }
109979
110386
  function verifyExitCode(verdict) {
109980
110387
  return verdict === "proven" ? 0 : verdict === "insensitive" ? 1 : 2;
109981
110388
  }
@@ -110678,7 +111085,7 @@ function registerJourneyReadiness(journey, program2, deps) {
110678
111085
 
110679
111086
  // src/journey-bundle.ts
110680
111087
  import { existsSync as existsSync30 } from "node:fs";
110681
- import { mkdir as mkdir30, readFile as readFile31, writeFile as writeFile36 } from "node:fs/promises";
111088
+ import { mkdir as mkdir31, readFile as readFile33, writeFile as writeFile37 } from "node:fs/promises";
110682
111089
  import { join as join63, resolve as resolve19 } from "node:path";
110683
111090
  var BundleStaleError = class extends Error {
110684
111091
  constructor(changed) {
@@ -110869,9 +111276,9 @@ async function buildJourneyBundle(opts, deps = {}) {
110869
111276
  verdict: receipt.verdict,
110870
111277
  generatedAt: clock.nowIso()
110871
111278
  };
110872
- await mkdir30(out, { recursive: true });
110873
- await writeFile36(bundlePath, renderJourneyBundle(receipt, entries, catalog, drafts), "utf8");
110874
- await writeFile36(manifestPath, `${JSON.stringify(manifest, null, 2)}
111279
+ await mkdir31(out, { recursive: true });
111280
+ await writeFile37(bundlePath, renderJourneyBundle(receipt, entries, catalog, drafts), "utf8");
111281
+ await writeFile37(manifestPath, `${JSON.stringify(manifest, null, 2)}
110875
111282
  `, "utf8");
110876
111283
  return { out, files: [bundlePath, manifestPath], verdict: receipt.verdict };
110877
111284
  }
@@ -110879,7 +111286,7 @@ async function checkJourneyBundle(dir, opts, deps = {}) {
110879
111286
  const review = deps.review ?? reviewJourneyById;
110880
111287
  const catalogOf = deps.loadCatalog ?? loadCatalog;
110881
111288
  const manifestPath = join63(resolve19(dir), "manifest.json");
110882
- const manifest = JSON.parse(await readFile31(manifestPath, "utf8"));
111289
+ const manifest = JSON.parse(await readFile33(manifestPath, "utf8"));
110883
111290
  const reviewOpts = { strict: true, ...opts.catalogDir === void 0 ? {} : { catalogDir: opts.catalogDir }, ...opts.targetsFile === void 0 ? {} : { targetsFile: opts.targetsFile } };
110884
111291
  const changed = [];
110885
111292
  for (const recorded of manifest.journeys) {
@@ -110946,7 +111353,7 @@ function registerJourneyBundle(journey, program2, deps) {
110946
111353
  }
110947
111354
 
110948
111355
  // src/journey-cli/promote.ts
110949
- import { readFile as readFile32 } from "node:fs/promises";
111356
+ import { readFile as readFile34 } from "node:fs/promises";
110950
111357
 
110951
111358
  // src/review-render-options.ts
110952
111359
  function processTerminal2() {
@@ -111018,7 +111425,7 @@ function registerJourneyPromote(journey, program2, deps) {
111018
111425
  if (reviewSheet !== void 0) {
111019
111426
  let text2;
111020
111427
  try {
111021
- text2 = await readFile32(reviewSheet, "utf8");
111428
+ text2 = await readFile34(reviewSheet, "utf8");
111022
111429
  } catch (e) {
111023
111430
  emitJson(program2, fail("E_JOURNEY_REVIEW_ARGS", `cannot read --review-sheet ${reviewSheet}: ${e instanceof Error ? e.message : String(e)}`));
111024
111431
  return;
@@ -111075,7 +111482,7 @@ function registerJourneyPromote(journey, program2, deps) {
111075
111482
  } else if (err instanceof WeakJourneyError) {
111076
111483
  if (!json2) {
111077
111484
  const out = program2.configureOutput().writeOut;
111078
- for (const finding3 of err.findings) out?.(`${lintFindingLine(finding3)}
111485
+ for (const finding4 of err.findings) out?.(`${lintFindingLine(finding4)}
111079
111486
  `);
111080
111487
  }
111081
111488
  emitJson(program2, fail("E_JOURNEY_WEAK", String(err.message)));
@@ -111095,7 +111502,22 @@ function registerJourneyPromote(journey, program2, deps) {
111095
111502
  }
111096
111503
 
111097
111504
  // src/journey-cli/review.ts
111098
- import { writeFile as writeFile37 } from "node:fs/promises";
111505
+ import { readFile as readFile35, writeFile as writeFile38 } from "node:fs/promises";
111506
+ import { join as join64 } from "node:path";
111507
+ async function reviewJourneezeHint(json2, outFile) {
111508
+ if (json2 || outFile !== void 0) return "";
111509
+ try {
111510
+ const raw = JSON.parse(await readFile35(join64(process.cwd(), "jevitate.lock"), "utf8"));
111511
+ const sharedSource = raw !== null && typeof raw === "object" && !Array.isArray(raw) && Array.isArray(raw.sources) && raw.sources.length > 0;
111512
+ return renderJourneezeHint(
111513
+ { sharedSource, journeyAuthors: [], unresolvedFindings: 0, strictReviewApproved: false },
111514
+ await loadJourneezeHintPreference(),
111515
+ { isTTY: process.stdout.isTTY === true, json: json2 }
111516
+ ) ?? "";
111517
+ } catch {
111518
+ return "";
111519
+ }
111520
+ }
111099
111521
  function registerJourneyReview(journey, program2, deps) {
111100
111522
  withProjectFlags(journey.command("review [id]")).description(
111101
111523
  "a human-readable review sheet for promotion sign-off: summary, steps, side effects, inputs (names only), proof, change since last approval, content hash. #467: --stale (no id) lists every promoted Journey whose approval is stale, labelling step-id-only changes"
@@ -111136,14 +111558,14 @@ function registerJourneyReview(journey, program2, deps) {
111136
111558
  const renderOpts = reviewRenderOptions({ full: full === true, toFile: outFile !== void 0 });
111137
111559
  const rendered = json2 ? `${JSON.stringify(review, null, 2)}
111138
111560
  ` : markdown ? renderReviewMarkdown(review, renderOpts) : renderReviewText(review, renderOpts);
111139
- if (outFile !== void 0) await writeFile37(outFile, rendered, { mode: 384 });
111561
+ if (outFile !== void 0) await writeFile38(outFile, rendered, { mode: 384 });
111140
111562
  if (json2) {
111141
111563
  emitJson(program2, strict === true ? Object.assign(ok(review), { strict: strictVerdict }) : ok(review));
111142
111564
  } else if (outFile !== void 0) {
111143
111565
  program2.configureOutput().writeOut?.(`review sheet for journey '${review.id}' written to ${outFile} (content hash ${review.contentHash})
111144
111566
  `);
111145
111567
  } else {
111146
- program2.configureOutput().writeOut?.(rendered);
111568
+ program2.configureOutput().writeOut?.(`${rendered}${await reviewJourneezeHint(false, outFile)}`);
111147
111569
  }
111148
111570
  emitUsageLine(program2, review.jev);
111149
111571
  process.exitCode = strict === true && !strictVerdict.passed ? EXIT_CODES.defects : 0;
@@ -111169,8 +111591,8 @@ function registerJourneyReview(journey, program2, deps) {
111169
111591
  import { existsSync as existsSync31 } from "node:fs";
111170
111592
 
111171
111593
  // src/journey-migrate-api.ts
111172
- import { readdir as readdir11, readFile as readFile33, writeFile as writeFile38 } from "node:fs/promises";
111173
- import { join as join64 } from "node:path";
111594
+ import { readdir as readdir11, readFile as readFile36, writeFile as writeFile39 } from "node:fs/promises";
111595
+ import { join as join65 } from "node:path";
111174
111596
  async function migrateStepIds(req) {
111175
111597
  const store = new FsJourneyStore(req.journeysDir);
111176
111598
  const journeys = [];
@@ -111185,7 +111607,7 @@ async function migrateStepIds(req) {
111185
111607
  if (!req.dryRun) await store.put(after);
111186
111608
  journeys.push({ id: meta3.id, stepsMinted, anchorsLinked, needsReapproval: before.metadata.promoted });
111187
111609
  }
111188
- const recordings = req.projectDir === null ? [] : await migrateRegressions(join64(req.projectDir, "regressions"), req.dryRun);
111610
+ const recordings = req.projectDir === null ? [] : await migrateRegressions(join65(req.projectDir, "regressions"), req.dryRun);
111189
111611
  return {
111190
111612
  dryRun: req.dryRun,
111191
111613
  journeys,
@@ -111206,11 +111628,11 @@ async function migrateRegressions(dir, dryRun) {
111206
111628
  }
111207
111629
  const out = [];
111208
111630
  for (const name of names) {
111209
- const path2 = join64(dir, name);
111210
- const rec = RecordingSchema.parse(JSON.parse(await readFile33(path2, "utf8")));
111631
+ const path2 = join65(dir, name);
111632
+ const rec = RecordingSchema.parse(JSON.parse(await readFile36(path2, "utf8")));
111211
111633
  const next = ensureStepIds(rec);
111212
111634
  if (next === rec) continue;
111213
- if (!dryRun) await writeFile38(path2, `${JSON.stringify(RecordingSchema.parse(next), null, 2)}
111635
+ if (!dryRun) await writeFile39(path2, `${JSON.stringify(RecordingSchema.parse(next), null, 2)}
111214
111636
  `);
111215
111637
  out.push({ path: path2, stepsMinted: countRecIds(next) - countRecIds(rec) });
111216
111638
  }
@@ -111464,7 +111886,7 @@ function registerJourneyMigrate(journey, program2, deps) {
111464
111886
  }
111465
111887
 
111466
111888
  // src/journey-cli/lint.ts
111467
- import { writeFile as writeFile39 } from "node:fs/promises";
111889
+ import { writeFile as writeFile40 } from "node:fs/promises";
111468
111890
 
111469
111891
  // src/journey-anchor-api.ts
111470
111892
  async function load(dir, id) {
@@ -111631,16 +112053,16 @@ function renderJourneyLintSarif(input2) {
111631
112053
  shortDescription: { text: rule.id },
111632
112054
  defaultConfiguration: { level: rule.level }
111633
112055
  }));
111634
- const results = input2.findings.map((finding3) => ({
111635
- ruleId: finding3.rule,
111636
- level: finding3.level,
111637
- message: { text: finding3.fix === void 0 ? finding3.message : `${finding3.message} \u2014 fix: ${finding3.fix}` },
111638
- ...finding3.fix === void 0 ? {} : { properties: { fix: finding3.fix } },
112056
+ const results = input2.findings.map((finding4) => ({
112057
+ ruleId: finding4.rule,
112058
+ level: finding4.level,
112059
+ message: { text: finding4.fix === void 0 ? finding4.message : `${finding4.message} \u2014 fix: ${finding4.fix}` },
112060
+ ...finding4.fix === void 0 ? {} : { properties: { fix: finding4.fix } },
111639
112061
  locations: [
111640
112062
  {
111641
112063
  logicalLocations: [
111642
112064
  { name: input2.id, kind: "module" },
111643
- ...finding3.step === void 0 ? [] : [{ name: `step ${finding3.step}`, kind: "function" }]
112065
+ ...finding4.step === void 0 ? [] : [{ name: `step ${finding4.step}`, kind: "function" }]
111644
112066
  ]
111645
112067
  }
111646
112068
  ]
@@ -111658,7 +112080,7 @@ function renderJourneyLintSarif(input2) {
111658
112080
  }
111659
112081
 
111660
112082
  // src/journey-link-hints.ts
111661
- import { join as join65 } from "node:path";
112083
+ import { join as join66 } from "node:path";
111662
112084
  var MAX_LISTED = 10;
111663
112085
  function listed(ids) {
111664
112086
  return ids.length <= MAX_LISTED ? ids.join(", ") : `${ids.slice(0, MAX_LISTED).join(", ")}, \u2026 (${ids.length - MAX_LISTED} more: jevitate catalog status)`;
@@ -111667,7 +112089,7 @@ async function loadLinkCatalog(catalogDir) {
111667
112089
  const empty = { dir: catalogDir, personasFile: null, jobsFile: null, personas: [], jobs: [], journeys: [] };
111668
112090
  if (catalogDir === null) return empty;
111669
112091
  try {
111670
- const loader = new CatalogLoader({ catalogDir, journeysDir: join65(catalogDir, "journeys") });
112092
+ const loader = new CatalogLoader({ catalogDir, journeysDir: join66(catalogDir, "journeys") });
111671
112093
  const [{ file: personasFile, personas }, { file: jobsFile, jobs }] = await Promise.all([loader.loadPersonas(), loader.loadJobs()]);
111672
112094
  return { ...empty, personasFile, jobsFile, personas, jobs };
111673
112095
  } catch {
@@ -111734,7 +112156,7 @@ function registerJourneyLint(journey, program2, deps) {
111734
112156
  const result = { ...linted, findings: [...linted.findings, ...linkFindings], warnings: linted.warnings + linkFindings.length };
111735
112157
  if (sarif !== void 0) {
111736
112158
  const log = renderJourneyLintSarif({ id: result.id, findings: result.findings, version: currentEngineInfo().version });
111737
- await writeFile39(sarif, `${JSON.stringify(log, null, 2)}
112159
+ await writeFile40(sarif, `${JSON.stringify(log, null, 2)}
111738
112160
  `);
111739
112161
  }
111740
112162
  if (json2) {
@@ -111752,7 +112174,7 @@ function registerJourneyLint(journey, program2, deps) {
111752
112174
  `);
111753
112175
  }
111754
112176
  }
111755
- for (const finding3 of result.findings) out?.(`${lintFindingLine(finding3)}
112177
+ for (const finding4 of result.findings) out?.(`${lintFindingLine(finding4)}
111756
112178
  `);
111757
112179
  out?.(`${result.errors} error(s), ${result.warnings} warning(s) \u2014 journey '${result.id}'
111758
112180
  `);
@@ -111771,14 +112193,38 @@ function registerJourneyLint(journey, program2, deps) {
111771
112193
  // src/journey-cli/verify.ts
111772
112194
  import { existsSync as existsSync32 } from "node:fs";
111773
112195
  function registerJourneyVerify(journey, program2, deps) {
111774
- withReplayAllowFlag(withEnvironmentFlags(withBrowserLaunchFlags(withFixtureFlags(withProjectFlags(journey.command("verify <id>")))))).description("prove each assertion of a Journey can fail: --mutate replays it with each write step skipped or blocked, and each checked fill emptied").option("--mutate", "run the mutation proof (required)").option("--dir <path>", DEPRECATED_DIR_HELP).option("--param <kv>", "param as key=value (repeatable)", collectParam, {}).option("--storage-state <file>", "Playwright storageState JSON to start each replay authenticated (as journey run)").option("--json", "emit a JSON envelope").action(async function(id) {
112196
+ withReplayAllowFlag(withEnvironmentFlags(withBrowserLaunchFlags(withFixtureFlags(withProjectFlags(journey.command("verify <id>")))))).description("preview or prove that each Journey assertion fails when its outcome is absent").option("--mutate", "run the mutation proof").option("--plan", "print the static mutation/assertion plan without opening a browser").option("--dir <path>", DEPRECATED_DIR_HELP).option("--param <kv>", "param as key=value (repeatable)", collectParam, {}).option("--storage-state <file>", "Playwright storageState JSON to start each replay authenticated (as journey run)").option("--json", "emit a JSON envelope").action(async function(id) {
111775
112197
  const locationFlags = this.opts();
111776
112198
  const layout = journeyLayout(program2, deps, { dir: locationFlags.dir, project: locationFlags.project, journeysDir: locationFlags.journeysDir });
111777
112199
  if (layout === null) return;
111778
112200
  const verifyJourneysDir = layout.journeysDir;
111779
- const { mutate, param, storageState: storageStateFlag, json: json2, env: envName, baseUrl } = this.opts();
111780
- if (mutate !== true) {
111781
- emitJson(program2, fail("E_JOURNEY_VERIFY_ARGS", "journey verify needs --mutate (the mutation proof is the only verification it runs today)"));
112201
+ const { mutate, plan: plan2, param, storageState: storageStateFlag, json: json2, env: envName, baseUrl } = this.opts();
112202
+ if (mutate === true === (plan2 === true)) {
112203
+ emitJson(program2, fail("E_JOURNEY_VERIFY_ARGS", "journey verify needs exactly one of --plan or --mutate"));
112204
+ return;
112205
+ }
112206
+ if (plan2 === true) {
112207
+ try {
112208
+ const report2 = await planJourneyVerification({ dir: verifyJourneysDir, id, params: param });
112209
+ if (json2) emitJson(program2, ok(withEngine(report2)));
112210
+ else {
112211
+ const out = program2.configureOutput().writeOut;
112212
+ for (const mutation2 of report2.mutations) {
112213
+ const sites = report2.pairs.filter((pair) => pair.mutation === mutation2.id).map((pair) => pair.site);
112214
+ out?.(`${mutation2.id} (step ${mutation2.step}) -> ${sites.length === 0 ? "unpaired" : sites.join(", ")}
112215
+ `);
112216
+ }
112217
+ for (const assertion of report2.assertions) if (assertion.mutations.length === 0) out?.(`unpaired ${assertion.site} ${assertion.check}
112218
+ `);
112219
+ out?.(
112220
+ `planned \u2014 journey '${report2.journeyId}': ${report2.summary.mutations} mutations, ${report2.summary.pairs} pairs, ${report2.summary.assertions} assertions, ${report2.summary.unpaired} unpaired, at least ${report2.summary.minimumReplays} replays
112221
+ `
112222
+ );
112223
+ }
112224
+ } catch (err) {
112225
+ if (err instanceof UnknownJourneyError) emitJson(program2, fail("E_UNKNOWN_JOURNEY", String(err.message)));
112226
+ else throw err;
112227
+ }
111782
112228
  return;
111783
112229
  }
111784
112230
  let environment;
@@ -111920,7 +112366,7 @@ function registerJourneyAnchors(journey, program2, deps) {
111920
112366
 
111921
112367
  // src/source-api.ts
111922
112368
  import { execFile as execFile6 } from "node:child_process";
111923
- import { mkdir as mkdir31 } from "node:fs/promises";
112369
+ import { mkdir as mkdir32 } from "node:fs/promises";
111924
112370
  import { promisify as promisify5 } from "node:util";
111925
112371
  var execFileAsync5 = promisify5(execFile6);
111926
112372
  var NotPromotedError = class extends Error {
@@ -111958,7 +112404,7 @@ async function requireEntry(lockPath, name) {
111958
112404
  }
111959
112405
  async function addSource(deps, req) {
111960
112406
  const mgr = mgrOf(deps);
111961
- await mkdir31(deps.sourcesDir, { recursive: true });
112407
+ await mkdir32(deps.sourcesDir, { recursive: true });
111962
112408
  const pinnedCommit = await mgr.add(req.name, req.gitUrl);
111963
112409
  const manifest = await loadManifest(mgr.resolveDir(req.name));
111964
112410
  const touSurface = surfaceForAck(manifest, req.gitUrl);
@@ -112375,7 +112821,7 @@ review or edit it, then apply it: jevitate journey annotate ${id} --approve
112375
112821
 
112376
112822
  // src/journey-demo-cli.ts
112377
112823
  import { existsSync as existsSync34 } from "node:fs";
112378
- import { join as join66, resolve as resolvePath15 } from "node:path";
112824
+ import { join as join67, resolve as resolvePath15 } from "node:path";
112379
112825
  function registerJourneyDemoCommand(journey, program2, deps) {
112380
112826
  withScreenshotsFlag(withEnvironmentFlags(withDemoFlags(withBrowserLaunchFlags(withEmulationFlags(withFixtureFlags(withProjectFlags(journey.command("demo <id>")))))))).description(
112381
112827
  "replay a Journey as a narrated demo (goal, step objectives as captions, target highlights) \u2192 a WebM video with .vtt subtitles and/or a Markdown step-by-step guide with screenshots; a Journey that no longer replays fails (exit 1)"
@@ -112418,9 +112864,9 @@ function registerJourneyDemoCommand(journey, program2, deps) {
112418
112864
  let video = videoFlag === void 0 ? void 0 : resolvePath15(videoFlag);
112419
112865
  let guide = guideFlag === void 0 ? void 0 : resolvePath15(guideFlag);
112420
112866
  if (video === void 0 && guide === void 0) {
112421
- const folder = join66(logsDirFor(), `journey-demo-${id.replace(/[^A-Za-z0-9._-]/g, "_")}-${artifactStamp(clock.nowIso())}`);
112422
- video = join66(folder, "demo.webm");
112423
- guide = join66(folder, "guide.md");
112867
+ const folder = join67(logsDirFor(), `journey-demo-${id.replace(/[^A-Za-z0-9._-]/g, "_")}-${artifactStamp(clock.nowIso())}`);
112868
+ video = join67(folder, "demo.webm");
112869
+ guide = join67(folder, "guide.md");
112424
112870
  }
112425
112871
  try {
112426
112872
  const result = await withSiteGate(
@@ -112513,7 +112959,7 @@ function registerJourneyCommands(program2, deps) {
112513
112959
  }
112514
112960
 
112515
112961
  // src/project-where.ts
112516
- import { join as join67 } from "node:path";
112962
+ import { join as join68 } from "node:path";
112517
112963
  function render(data) {
112518
112964
  const dir = (p) => p ?? "(none)";
112519
112965
  return [
@@ -112530,7 +112976,7 @@ function registerWhere(program2, deps) {
112530
112976
  const { project, journeysDir, json: json2 } = this.opts();
112531
112977
  const layout = journeyLayout(program2, deps, { project, journeysDir });
112532
112978
  if (layout === null) return;
112533
- const logsDir = layout.projectDir === null ? logsRoot() : join67(layout.projectDir, "logs");
112979
+ const logsDir = layout.projectDir === null ? logsRoot() : join68(layout.projectDir, "logs");
112534
112980
  const data = { ...layout, logsDir };
112535
112981
  if (json2 === true) {
112536
112982
  emitJson(program2, ok(data));
@@ -113348,7 +113794,7 @@ function resolveSecretArgs(values2, env, flag) {
113348
113794
  }
113349
113795
 
113350
113796
  // src/multi-run-cli.ts
113351
- import { join as join68 } from "node:path";
113797
+ import { join as join69 } from "node:path";
113352
113798
  var MULTI_RUN_ATTRS = /* @__PURE__ */ new Set(["repeat", "minAgreement", "persona", "personas", "out", "json"]);
113353
113799
  function forwardedArgv(cmd, omit3 = /* @__PURE__ */ new Set()) {
113354
113800
  const opts = cmd.opts();
@@ -113396,7 +113842,7 @@ function lastEnvelope(lines) {
113396
113842
  }
113397
113843
  async function runExploreMultiRun(args) {
113398
113844
  const { cmd, plan: plan2 } = args;
113399
- const outDir = args.out ?? join68(logsDirFor((args.nowIso ?? (() => clock.nowIso()))()), `multi-${artifactStamp((args.nowIso ?? (() => clock.nowIso()))())}`);
113845
+ const outDir = args.out ?? join69(logsDirFor((args.nowIso ?? (() => clock.nowIso()))()), `multi-${artifactStamp((args.nowIso ?? (() => clock.nowIso()))())}`);
113400
113846
  const omit3 = /* @__PURE__ */ new Set([...plan2.personas === null ? [] : ["storageState"], ...args.authPreflight === void 0 ? [] : ["authCheck"]]);
113401
113847
  const base = [...forwardedArgv(cmd, omit3), ...args.authPreflight === void 0 ? [] : ["--auth-check", "off"]];
113402
113848
  return runMultiRun({
@@ -113435,9 +113881,9 @@ import { resolve as resolvePath16 } from "node:path";
113435
113881
 
113436
113882
  // src/campaign-api.ts
113437
113883
  import { existsSync as existsSync37, readFileSync as readFileSync36 } from "node:fs";
113438
- import { mkdir as mkdir32, writeFile as writeFile40 } from "node:fs/promises";
113884
+ import { mkdir as mkdir33, writeFile as writeFile41 } from "node:fs/promises";
113439
113885
  import { homedir as homedir5 } from "node:os";
113440
- import { dirname as dirname29, isAbsolute as isAbsolute11, join as join69, resolve as resolve21 } from "node:path";
113886
+ import { dirname as dirname29, isAbsolute as isAbsolute11, join as join70, resolve as resolve21 } from "node:path";
113441
113887
 
113442
113888
  // src/campaign-preflight.ts
113443
113889
  import { basename as basename17 } from "node:path";
@@ -113562,7 +114008,7 @@ async function validateCampaignSpec(raw, specPath, opts) {
113562
114008
  if (!parsed.success) throw new CampaignSpecError(parsed.error.issues.map((i) => `${at2(i.path)}: ${i.message}`));
113563
114009
  const spec = parsed.data;
113564
114010
  const base = dirname29(resolve21(specPath));
113565
- const abs = (p) => p.startsWith("~/") ? join69(homedir5(), p.slice(2)) : isAbsolute11(p) ? p : resolve21(base, p);
114011
+ const abs = (p) => p.startsWith("~/") ? join70(homedir5(), p.slice(2)) : isAbsolute11(p) ? p : resolve21(base, p);
113566
114012
  const problems = [];
113567
114013
  let environment;
113568
114014
  try {
@@ -113748,8 +114194,8 @@ function commonArgs(plan2, job) {
113748
114194
  async function runCampaign(plan2, opts) {
113749
114195
  const now2 = opts.nowIso ?? (() => clock.nowIso());
113750
114196
  const startedAt = now2();
113751
- const outDir = opts.outDir ?? join69(logsDirFor(startedAt), `campaign-${artifactStamp(startedAt)}`);
113752
- await mkdir32(outDir, { recursive: true });
114197
+ const outDir = opts.outDir ?? join70(logsDirFor(startedAt), `campaign-${artifactStamp(startedAt)}`);
114198
+ await mkdir33(outDir, { recursive: true });
113753
114199
  const ai = opts.real === true ? ["--real"] : opts.fakeAi === true ? ["--fake-ai"] : [];
113754
114200
  const discovery = [];
113755
114201
  const replaySlowness = [];
@@ -113807,8 +114253,8 @@ async function runCampaign(plan2, opts) {
113807
114253
  }
113808
114254
  }
113809
114255
  const preflight = opts.preflight === void 0 ? void 0 : opts.preflight === "skip" ? { ok: true, problems: [], skipped: true } : preflightOf(preflightProblems);
113810
- const resultPath = join69(outDir, "campaign.json");
113811
- const reportPath = join69(outDir, "campaign.md");
114256
+ const resultPath = join70(outDir, "campaign.json");
114257
+ const reportPath = join70(outDir, "campaign.md");
113812
114258
  if (preflight !== void 0 && !preflight.ok) {
113813
114259
  const refused = {
113814
114260
  ...plan2.name === void 0 ? {} : { name: plan2.name },
@@ -113821,7 +114267,7 @@ async function runCampaign(plan2, opts) {
113821
114267
  missions: [],
113822
114268
  resultPath
113823
114269
  };
113824
- await writeFile40(resultPath, `${JSON.stringify(refused, null, 2)}
114270
+ await writeFile41(resultPath, `${JSON.stringify(refused, null, 2)}
113825
114271
  `, "utf8");
113826
114272
  throw new CampaignPreflightError(preflight.problems, resultPath);
113827
114273
  }
@@ -113842,7 +114288,7 @@ async function runCampaign(plan2, opts) {
113842
114288
  });
113843
114289
  continue;
113844
114290
  }
113845
- const runDir = join69(outDir, job.id, `${anchor2.atStep}-${strategy}`);
114291
+ const runDir = join70(outDir, job.id, `${anchor2.atStep}-${strategy}`);
113846
114292
  const argv = [
113847
114293
  "explore",
113848
114294
  "--from-journey",
@@ -113934,9 +114380,9 @@ async function runCampaign(plan2, opts) {
113934
114380
  missionOutcome,
113935
114381
  exitCode: exitCodeForOutcome(missionOutcome)
113936
114382
  };
113937
- await writeFile40(resultPath, `${JSON.stringify(result, null, 2)}
114383
+ await writeFile41(resultPath, `${JSON.stringify(result, null, 2)}
113938
114384
  `, "utf8");
113939
- await writeFile40(reportPath, campaignMarkdown(result, report2.markdown), "utf8");
114385
+ await writeFile41(reportPath, campaignMarkdown(result, report2.markdown), "utf8");
113940
114386
  return result;
113941
114387
  }
113942
114388
  function campaignMarkdown(r, reportMarkdown) {
@@ -114101,7 +114547,7 @@ var GOAL_RUN_OPTIONS = {
114101
114547
  ),
114102
114548
  jobWaitMs: () => new Option(
114103
114549
  "--job-wait-ms <ms>",
114104
- `goal and usability: while the page shows an in-progress status ("Simulating\u2026", aria-busy, a job "is running"), waits keep waiting with backoff \u2014 and a model 'blocked' is deferred \u2014 up to this budget (default: --reply-ceiling-ms, 180000); it also bounds a busy indicator the app visibly keeps working behind (live progress, a job poll) before it is a hang, and a wait the page documents ("usually takes a minute") can raise it`
114550
+ `goal and usability: while the page shows an in-progress status ("Simulating\u2026", aria-busy, a job "is running"), waits keep waiting with backoff \u2014 and a model 'blocked' is deferred \u2014 up to this budget (default: --reply-ceiling-ms, 180000); it also bounds a busy indicator the app visibly keeps working behind (live progress, a job poll) or declares (aria-busy, when this flag is set) before it is a hang, and a wait the page documents ("usually takes a minute") can raise it`
114105
114551
  ).argParser(positiveIntArg),
114106
114552
  deny: () => new Option(
114107
114553
  "--deny <pattern>",
@@ -114137,7 +114583,7 @@ var GOAL_RUN_OPTIONS = {
114137
114583
  settleIgnore: () => new Option("--settle-ignore <pattern>", "a request URL pattern the target marks as background (never pending work; repeatable, * wildcard)").default([]).argParser(collect7),
114138
114584
  longPollMs: () => new Option("--long-poll-ms <n>", "a request pending this long on an interactive page is a long-poll (default 5000)").argParser(nonNegativeIntArg),
114139
114585
  apiPrefix: () => new Option("--api-prefix <path>", "a path prefix whose requests are the app's API in the timing summary (repeatable), e.g. /api/").default([]).argParser(collect7),
114140
- ignoreNoProgress: () => new Option("--ignore-no-progress <pattern>", "a route / action label / busy indicator where ui-no-progress is expected (repeatable, * wildcard)").default([]).argParser(collect7),
114586
+ ignoreNoProgress: () => new Option("--ignore-no-progress <pattern>", "a route / action label / busy indicator (its description or visible words) where ui-no-progress is expected (repeatable, * wildcard)").default([]).argParser(collect7),
114141
114587
  allowSecretCmd: () => new Option(
114142
114588
  "--allow-secret-cmd",
114143
114589
  "opt-in: a --secret-field <descriptor>=cmd:<command> may run its command (in a shell, at type time, 60s timeout) and type its output (operator-declared only; refused otherwise)"
@@ -115662,11 +116108,11 @@ import { resolve as resolve24 } from "node:path";
115662
116108
 
115663
116109
  // src/sweep-api.ts
115664
116110
  import { existsSync as existsSync40, mkdirSync as mkdirSync11, readFileSync as readFileSync37, realpathSync as realpathSync5, statSync as statSync12, writeFileSync as writeFileSync13 } from "node:fs";
115665
- import { basename as basename19, dirname as dirname31, extname as extname2, join as join71, resolve as resolve23 } from "node:path";
116111
+ import { basename as basename19, dirname as dirname31, extname as extname2, join as join72, resolve as resolve23 } from "node:path";
115666
116112
 
115667
116113
  // src/mcp-paths.ts
115668
116114
  import { existsSync as existsSync39, realpathSync as realpathSync4 } from "node:fs";
115669
- import { basename as basename18, dirname as dirname30, isAbsolute as isAbsolute12, join as join70, relative as relative10, resolve as resolve22, sep as sep10 } from "node:path";
116115
+ import { basename as basename18, dirname as dirname30, isAbsolute as isAbsolute12, join as join71, relative as relative10, resolve as resolve22, sep as sep10 } from "node:path";
115670
116116
  var McpPathError = class extends Error {
115671
116117
  error = "invalid_args";
115672
116118
  constructor(message2) {
@@ -115685,7 +116131,7 @@ function realish(p) {
115685
116131
  for (; ; ) {
115686
116132
  if (existsSync39(head2)) {
115687
116133
  try {
115688
- return join70(realpathSync4(head2), ...tail);
116134
+ return join71(realpathSync4(head2), ...tail);
115689
116135
  } catch {
115690
116136
  return p;
115691
116137
  }
@@ -116045,13 +116491,14 @@ var CLI_TOOL_SPECS = [
116045
116491
  },
116046
116492
  {
116047
116493
  name: "verify_journey",
116048
- description: "`jevitate journey verify <id> --mutate` (#402): prove each assertion can fail. Replays the Journey once (it must pass), then once per mutation \u2014 a write step skipped, its write request aborted, a typed value left empty \u2014 and reports each assertion as sensitive (failed for the right reason), insensitive (still passed: vacuous), cascade, not-applied or unpaired. Mutations only skip steps or abort the app's own writes; nothing is fabricated. Exit 1 when any paired assertion is insensitive, 2 when nothing could be proven. Set mutate: true.",
116494
+ description: "`jevitate journey verify <id> --plan|--mutate` (#402): plan previews the exact mutations, assertion pairings, unpaired assertions and minimum replay count without opening a browser; mutate proves each assertion can fail by replaying the Journey once, then once per mutation. Mutations only skip steps, abort the app's own writes or alter checked non-secret inputs; nothing is fabricated. Set exactly one of plan or mutate.",
116049
116495
  command: {
116050
116496
  path: "journey verify",
116051
116497
  params: {
116052
116498
  id: pos(),
116053
116499
  ...EXTENSION,
116054
116500
  mutate: b("--mutate"),
116501
+ plan: b("--plan"),
116055
116502
  params: { kind: "params", flag: "--param" },
116056
116503
  storageState: session("--storage-state"),
116057
116504
  fixtures: path("--fixtures"),
@@ -117510,7 +117957,7 @@ function aggregateSweep(args) {
117510
117957
  kind: "sweep",
117511
117958
  targetsPath: plan2.targetsPath,
117512
117959
  outDir: plan2.outDir,
117513
- resultPath: join71(plan2.outDir, "sweep.result.json"),
117960
+ resultPath: join72(plan2.outDir, "sweep.result.json"),
117514
117961
  startedAt: args.startedAt,
117515
117962
  ...args.finishedAt === void 0 ? {} : { finishedAt: args.finishedAt },
117516
117963
  complete: args.complete && args.aborted === void 0,
@@ -117551,19 +117998,19 @@ async function awaitLowHostLoad(probe = hostProbe(), waitMs = HOST_STARVED_WAIT_
117551
117998
  }
117552
117999
  }
117553
118000
  function defaultSweepOutDir(iso = clock.nowIso()) {
117554
- return join71(logsDirFor(iso), `sweep-${artifactStamp(iso)}`);
118001
+ return join72(logsDirFor(iso), `sweep-${artifactStamp(iso)}`);
117555
118002
  }
117556
118003
  async function runSweep(opts) {
117557
118004
  const { plan: plan2 } = opts;
117558
118005
  const now2 = opts.nowIso ?? (() => clock.nowIso());
117559
118006
  const startedAt = now2();
117560
118007
  mkdirSync11(plan2.outDir, { recursive: true });
117561
- const resultPath = join71(plan2.outDir, "sweep.result.json");
118008
+ const resultPath = join72(plan2.outDir, "sweep.result.json");
117562
118009
  const rows = plan2.targets.map(() => void 0);
117563
118010
  const finished = [];
117564
118011
  const queue = [];
117565
118012
  plan2.targets.forEach((t, i) => {
117566
- const envelopePath = join71(plan2.outDir, t.id, "run.envelope.json");
118013
+ const envelopePath = join72(plan2.outDir, t.id, "run.envelope.json");
117567
118014
  const prior = plan2.resume ? finishedEnvelope(envelopePath) : void 0;
117568
118015
  if (prior === void 0) {
117569
118016
  queue.push(i);
@@ -117585,9 +118032,9 @@ async function runSweep(opts) {
117585
118032
  });
117586
118033
  const runOne = async (i) => {
117587
118034
  const target = plan2.targets[i];
117588
- const runDir = join71(plan2.outDir, target.id);
118035
+ const runDir = join72(plan2.outDir, target.id);
117589
118036
  mkdirSync11(runDir, { recursive: true });
117590
- const envelopePath = join71(runDir, "run.envelope.json");
118037
+ const envelopePath = join72(runDir, "run.envelope.json");
117591
118038
  const attempt2 = async () => {
117592
118039
  try {
117593
118040
  return await opts.runOnce({ target, argv: targetArgv(plan2, target, runDir), runDir });
@@ -118063,13 +118510,13 @@ function registerAuthorJourneyCommands(program2, deps) {
118063
118510
 
118064
118511
  // src/demo-aspect-cli.ts
118065
118512
  import { existsSync as existsSync43 } from "node:fs";
118066
- import { join as join74, resolve as resolvePath18 } from "node:path";
118513
+ import { join as join75, resolve as resolvePath18 } from "node:path";
118067
118514
 
118068
118515
  // src/demo-aspect-api.ts
118069
118516
  import { existsSync as existsSync42 } from "node:fs";
118070
- import { mkdir as mkdir34, mkdtemp as mkdtemp5, readFile as readFile34, rm as rm13, writeFile as writeFile42 } from "node:fs/promises";
118517
+ import { mkdir as mkdir35, mkdtemp as mkdtemp5, readFile as readFile37, rm as rm13, writeFile as writeFile43 } from "node:fs/promises";
118071
118518
  import { tmpdir as tmpdir5 } from "node:os";
118072
- import { dirname as dirname32, join as join73 } from "node:path";
118519
+ import { dirname as dirname32, join as join74 } from "node:path";
118073
118520
 
118074
118521
  // ../regression/dist/fingerprint.js
118075
118522
  function flattenWithUrls(rec) {
@@ -118214,8 +118661,8 @@ async function minimizeRecording(recording, reproduces) {
118214
118661
  }
118215
118662
 
118216
118663
  // ../regression/dist/commit.js
118217
- import { join as join72 } from "node:path";
118218
- import { mkdir as mkdir33, writeFile as writeFile41 } from "node:fs/promises";
118664
+ import { join as join73 } from "node:path";
118665
+ import { mkdir as mkdir34, writeFile as writeFile42 } from "node:fs/promises";
118219
118666
  var FlakyNotCommittableError = class extends Error {
118220
118667
  };
118221
118668
  async function commitRegression(dir, id, minimized, report2, bugSummary, oracle) {
@@ -118223,9 +118670,9 @@ async function commitRegression(dir, id, minimized, report2, bugSummary, oracle)
118223
118670
  throw new FlakyNotCommittableError(`refusing to commit '${id}': reproduction rate ${report2.rate} (${report2.reproducedCount}/${report2.attempts}) is flaky, not reproducible`);
118224
118671
  }
118225
118672
  const validated = RecordingSchema.parse(minimized);
118226
- await mkdir33(dir, { recursive: true });
118227
- const recordingPath = join72(dir, `${id}.recording.json`);
118228
- const metaPath = join72(dir, `${id}.meta.json`);
118673
+ await mkdir34(dir, { recursive: true });
118674
+ const recordingPath = join73(dir, `${id}.recording.json`);
118675
+ const metaPath = join73(dir, `${id}.meta.json`);
118229
118676
  const meta3 = {
118230
118677
  id,
118231
118678
  capturedAtIso: clock.nowIso(),
@@ -118234,8 +118681,8 @@ async function commitRegression(dir, id, minimized, report2, bugSummary, oracle)
118234
118681
  bugSummary,
118235
118682
  ...oracle === void 0 ? {} : { oracle }
118236
118683
  };
118237
- await writeFile41(recordingPath, JSON.stringify(validated, null, 2) + "\n");
118238
- await writeFile41(metaPath, JSON.stringify(meta3, null, 2) + "\n");
118684
+ await writeFile42(recordingPath, JSON.stringify(validated, null, 2) + "\n");
118685
+ await writeFile42(metaPath, JSON.stringify(meta3, null, 2) + "\n");
118239
118686
  return { recordingPath, metaPath };
118240
118687
  }
118241
118688
 
@@ -118308,7 +118755,7 @@ function demoJourneyId(aspect) {
118308
118755
  }
118309
118756
  var DEMO_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
118310
118757
  function demoDraftPath(journeysDir, id) {
118311
- return join73(journeysDir, ".drafts", `${id}.demo.json`);
118758
+ return join74(journeysDir, ".drafts", `${id}.demo.json`);
118312
118759
  }
118313
118760
  function demoDraftProblem(v) {
118314
118761
  if (v === null || typeof v !== "object" || Array.isArray(v)) return "not an object";
@@ -118329,7 +118776,7 @@ async function readDemoDraft(journeysDir, id) {
118329
118776
  const path2 = demoDraftPath(journeysDir, id);
118330
118777
  let raw;
118331
118778
  try {
118332
- raw = await readFile34(path2, "utf8");
118779
+ raw = await readFile37(path2, "utf8");
118333
118780
  } catch (err) {
118334
118781
  if (err.code === "ENOENT") throw new DemoDraftNotFoundError(`no demo draft '${id}' (${path2}) \u2014 make one with \`jevitate demo "<aspect>" --env <name>\``);
118335
118782
  throw err;
@@ -118409,16 +118856,16 @@ async function demoAspect(opts) {
118409
118856
  const maxReplays = opts.maxReplays ?? DEMO_MINIMIZE_MAX_REPLAYS;
118410
118857
  const now2 = opts.now ?? (() => clock.nowIso());
118411
118858
  const base = { id, aspect, environment: env.name ?? env.baseUrl };
118412
- const work = await mkdtemp5(join73(tmpdir5(), "jevitate-demo-aspect-"));
118859
+ const work = await mkdtemp5(join74(tmpdir5(), "jevitate-demo-aspect-"));
118413
118860
  try {
118414
118861
  const authored = await runAuthorJourney({
118415
118862
  url: new URL(start, env.baseUrl).toString(),
118416
118863
  goal: aspect,
118417
118864
  successAssertion: opts.successAssertion,
118418
118865
  allowlist: env.allowedOrigins,
118419
- journeysDir: join73(work, "explored"),
118866
+ journeysDir: join74(work, "explored"),
118420
118867
  // #369: the discovery take's own artifacts stay in the scratch dir with the rest.
118421
- outDir: join73(work, "explored-runs"),
118868
+ outDir: join74(work, "explored-runs"),
118422
118869
  journeyId: id,
118423
118870
  journeyName: aspect,
118424
118871
  takes: 1,
@@ -118439,10 +118886,10 @@ async function demoAspect(opts) {
118439
118886
  ...opts.persona === void 0 ? {} : { persona: opts.persona },
118440
118887
  successCriteria: [{ description: `The success check holds: ${opts.successSpec}`, check: opts.successAssertion }]
118441
118888
  };
118442
- const scratch = new FsJourneyStore(join73(work, "candidates"));
118889
+ const scratch = new FsJourneyStore(join74(work, "candidates"));
118443
118890
  const replayOk = async (recording2) => {
118444
118891
  await scratch.put({ metadata, recording: withSuccessAssert(recording2, opts.successAssertion) });
118445
- const run = await runJourneyProgrammatically({ dir: join73(work, "candidates"), id, params: {}, ...replayOpts(opts) });
118892
+ const run = await runJourneyProgrammatically({ dir: join74(work, "candidates"), id, params: {}, ...replayOpts(opts) });
118446
118893
  return run.outcome === "ok";
118447
118894
  };
118448
118895
  const explored = withoutSuccessAssert(authored.journey.recording, opts.successAssertion);
@@ -118473,7 +118920,7 @@ async function demoAspect(opts) {
118473
118920
  };
118474
118921
  const recording = withSuccessAssert(minimized, opts.successAssertion);
118475
118922
  const journey = { metadata: { ...metadata, params: deriveParamSchema(recording).required, createdAtIso: now2() }, recording };
118476
- const stageDir = join73(work, "stage");
118923
+ const stageDir = join74(work, "stage");
118477
118924
  await new FsJourneyStore(stageDir).put(journey);
118478
118925
  const annotated = await annotateJourney({ dir: stageDir, id, params: {}, gen: opts.gen, now: now2, ...replayOpts(opts) });
118479
118926
  if (annotated.replay.outcome !== "completed") {
@@ -118486,8 +118933,8 @@ async function demoAspect(opts) {
118486
118933
  }
118487
118934
  await store.put(journey);
118488
118935
  const draftPath = annotationDraftPath(opts.journeysDir, id);
118489
- await mkdir34(dirname32(draftPath), { recursive: true, mode: 448 });
118490
- await writeFile42(draftPath, `${JSON.stringify(annotations, null, 2)}
118936
+ await mkdir35(dirname32(draftPath), { recursive: true, mode: 448 });
118937
+ await writeFile43(draftPath, `${JSON.stringify(annotations, null, 2)}
118491
118938
  `, { mode: 384 });
118492
118939
  const record2 = {
118493
118940
  kind: "jevitate.demo.draft",
@@ -118501,7 +118948,7 @@ async function demoAspect(opts) {
118501
118948
  draft: outputsOf(demo)
118502
118949
  };
118503
118950
  assertNoSecretInPayload(record2, [], "demo draft record");
118504
- await writeFile42(recordPath, `${JSON.stringify(record2, null, 2)}
118951
+ await writeFile43(recordPath, `${JSON.stringify(record2, null, 2)}
118505
118952
  `, { mode: 384 });
118506
118953
  return {
118507
118954
  ...base,
@@ -118530,8 +118977,8 @@ function renderDemo(dir, id, opts, annotations, draft, outDir, jzMask = false) {
118530
118977
  dir,
118531
118978
  id,
118532
118979
  params: {},
118533
- video: join73(outDir, "demo.webm"),
118534
- guide: join73(outDir, "guide.md"),
118980
+ video: join74(outDir, "demo.webm"),
118981
+ guide: join74(outDir, "guide.md"),
118535
118982
  annotations,
118536
118983
  draft,
118537
118984
  environment: opts.environment,
@@ -118609,7 +119056,7 @@ async function approveDemo(opts) {
118609
119056
  ...title === "" ? {} : { title },
118610
119057
  demo
118611
119058
  });
118612
- journeeze = { media: true, record: join73(opts.journeysDir, ".demos", opts.id), ...record2.leftOut.length === 0 ? {} : { leftOut: record2.leftOut } };
119059
+ journeeze = { media: true, record: join74(opts.journeysDir, ".demos", opts.id), ...record2.leftOut.length === 0 ? {} : { leftOut: record2.leftOut } };
118613
119060
  } else {
118614
119061
  await clearApprovedDemo(opts.journeysDir, opts.id);
118615
119062
  journeeze = { media: false, reason: `environment '${environment}' does not declare synthetic: true \u2014 no demo media is exported to Journeeze` };
@@ -118679,7 +119126,7 @@ function withReplayFlags(cmd) {
118679
119126
  }
118680
119127
  function outDirFor(flag, id, kind) {
118681
119128
  if (flag !== void 0) return resolvePath18(flag);
118682
- return join74(logsDirFor(), `demo-${id}-${kind}-${artifactStamp(clock.nowIso())}`);
119129
+ return join75(logsDirFor(), `demo-${id}-${kind}-${artifactStamp(clock.nowIso())}`);
118683
119130
  }
118684
119131
  function stepLines2(steps) {
118685
119132
  return steps.map((s3) => ` ${s3.number}. ${s3.objective ?? s3.step}${s3.expectedResult === void 0 ? "" : `
@@ -118864,8 +119311,8 @@ ${(j.leftOut ?? []).map((l2) => ` left out: ${l2}
118864
119311
  }
118865
119312
 
118866
119313
  // src/record-api.ts
118867
- import { mkdir as mkdir35, writeFile as writeFile43 } from "node:fs/promises";
118868
- import { join as join75 } from "node:path";
119314
+ import { mkdir as mkdir36, writeFile as writeFile44 } from "node:fs/promises";
119315
+ import { join as join76 } from "node:path";
118869
119316
  async function runRecording(opts) {
118870
119317
  const origin = assertAuthorizedExploreTarget(opts.url, opts.allowlist);
118871
119318
  const portFactory = opts.browserPortFactory ?? (() => new PlaywrightBrowserPort());
@@ -118888,10 +119335,10 @@ async function runRecording(opts) {
118888
119335
  const values2 = Object.fromEntries(authoring.values);
118889
119336
  const take = AuthoringTakeSchema.parse({ recording, values: values2 });
118890
119337
  const outDir = opts.outDir ?? logsDirFor();
118891
- await mkdir35(outDir, { recursive: true });
119338
+ await mkdir36(outDir, { recursive: true });
118892
119339
  const iso = (opts.nowIso ?? (() => clock.nowIso()))();
118893
- const recordingPath = join75(outDir, `record-${iso.replace(/[:.]/g, "-")}.json`);
118894
- await writeFile43(recordingPath, `${JSON.stringify(take, null, 2)}
119340
+ const recordingPath = join76(outDir, `record-${iso.replace(/[:.]/g, "-")}.json`);
119341
+ await writeFile44(recordingPath, `${JSON.stringify(take, null, 2)}
118895
119342
  `, "utf8");
118896
119343
  const steps = recording.pages.reduce((n3, p) => n3 + p.steps.length, 0);
118897
119344
  return { recording, recordingPath, steps, pages: recording.pages.length, finalUrl };
@@ -119004,12 +119451,12 @@ function registerRecordCommands(program2, deps) {
119004
119451
 
119005
119452
  // src/regression-cli.ts
119006
119453
  import { existsSync as existsSync45 } from "node:fs";
119007
- import { readFile as readFile36, writeFile as writeFile44 } from "node:fs/promises";
119008
- import { join as join76 } from "node:path";
119454
+ import { readFile as readFile39, writeFile as writeFile45 } from "node:fs/promises";
119455
+ import { join as join77 } from "node:path";
119009
119456
 
119010
119457
  // src/regression-api.ts
119011
119458
  import { existsSync as existsSync44 } from "node:fs";
119012
- import { readFile as readFile35 } from "node:fs/promises";
119459
+ import { readFile as readFile38 } from "node:fs/promises";
119013
119460
  var RegressionExistsError = class extends Error {
119014
119461
  code = "E_REGRESSION_EXISTS";
119015
119462
  constructor(id, dir) {
@@ -119035,9 +119482,9 @@ var NoOracleInResultError = class extends Error {
119035
119482
  };
119036
119483
  var RegressionHardSignalOracleError = class extends Error {
119037
119484
  code = "E_REGRESSION_HARD_SIGNAL";
119038
- constructor(resultPath, finding3) {
119485
+ constructor(resultPath, finding4) {
119039
119486
  super(
119040
- `--fingerprint ${finding3.fingerprint} is a ${finding3.kind} defect in ${resultPath} \u2014 a hard signal with its own oracle, which 'regression capture' cannot turn into a page/success-check regression. Keep its repro material with 'jevitate ledger add ${resultPath} ${finding3.fingerprint}', then re-check it with 'jevitate ledger verify' or 'jevitate verify-fix ${finding3.fingerprint}'.`
119487
+ `--fingerprint ${finding4.fingerprint} is a ${finding4.kind} defect in ${resultPath} \u2014 a hard signal with its own oracle, which 'regression capture' cannot turn into a page/success-check regression. Keep its repro material with 'jevitate ledger add ${resultPath} ${finding4.fingerprint}', then re-check it with 'jevitate ledger verify' or 'jevitate verify-fix ${finding4.fingerprint}'.`
119041
119488
  );
119042
119489
  this.name = "RegressionHardSignalOracleError";
119043
119490
  }
@@ -119099,14 +119546,14 @@ async function runRegressionCapture(opts) {
119099
119546
  if (opts.force !== true && existsSync44(recordingPath)) {
119100
119547
  throw new RegressionExistsError(opts.id, opts.regressionsDir);
119101
119548
  }
119102
- const raw = JSON.parse(await readFile35(opts.failingRecordingPath, "utf8"));
119549
+ const raw = JSON.parse(await readFile38(opts.failingRecordingPath, "utf8"));
119103
119550
  const parsed = RecordingSchema.parse(raw);
119104
119551
  const recording = opts.anchored === true ? anchoredRecording(parsed) : parsed;
119105
119552
  const attempts = opts.attempts ?? 3;
119106
119553
  let missionResult;
119107
119554
  let persistedMission;
119108
119555
  if (opts.resultPath) {
119109
- const rawResult = JSON.parse(await readFile35(opts.resultPath, "utf8"));
119556
+ const rawResult = JSON.parse(await readFile38(opts.resultPath, "utf8"));
119110
119557
  missionResult = rawResult;
119111
119558
  try {
119112
119559
  persistedMission = parsePersistedMission(rawResult);
@@ -119115,12 +119562,12 @@ async function runRegressionCapture(opts) {
119115
119562
  }
119116
119563
  }
119117
119564
  if (opts.fingerprint !== void 0 && persistedMission !== void 0) {
119118
- const finding3 = findFinding(persistedMission, opts.fingerprint);
119119
- if (finding3 !== void 0 && finding3.kind === "invariant" && finding3.invariantId !== void 0 && persistedMission.invariantSpec !== void 0) {
119120
- return captureInvariantRegression(opts, recording, persistedMission, finding3, attempts);
119565
+ const finding4 = findFinding(persistedMission, opts.fingerprint);
119566
+ if (finding4 !== void 0 && finding4.kind === "invariant" && finding4.invariantId !== void 0 && persistedMission.invariantSpec !== void 0) {
119567
+ return captureInvariantRegression(opts, recording, persistedMission, finding4, attempts);
119121
119568
  }
119122
- if (finding3 !== void 0 && finding3.kind !== "invariant") {
119123
- throw new RegressionHardSignalOracleError(opts.resultPath, finding3);
119569
+ if (finding4 !== void 0 && finding4.kind !== "invariant") {
119570
+ throw new RegressionHardSignalOracleError(opts.resultPath, finding4);
119124
119571
  }
119125
119572
  }
119126
119573
  let working = recording;
@@ -119199,16 +119646,16 @@ async function captureNetworkCheckRegression(opts, recording, check3, attempts)
119199
119646
  };
119200
119647
  return commitRegression(opts.regressionsDir, opts.id, recording, report2, opts.bugSummary, { kind: "network", check: check3 });
119201
119648
  }
119202
- async function captureInvariantRegression(opts, fallbackRecording, mission, finding3, attempts) {
119649
+ async function captureInvariantRegression(opts, fallbackRecording, mission, finding4, attempts) {
119203
119650
  const invariantSpec = mission.invariantSpec;
119204
- const invariantId = finding3.invariantId;
119205
- const recording = finding3.recording ?? mission.recording ?? fallbackRecording;
119651
+ const invariantId = finding4.invariantId;
119652
+ const recording = finding4.recording ?? mission.recording ?? fallbackRecording;
119206
119653
  assertAuthorizedExploreTarget(mission.target.seedUrl, mission.target.allowlist);
119207
119654
  const verifyResult = await verifyFix({
119208
119655
  fixtureBaseDir: journeyFixtureBaseDir(),
119209
119656
  recording,
119210
- recordingStepIndex: finding3.repro.recordingStepIndex,
119211
- fingerprint: finding3.fingerprint,
119657
+ recordingStepIndex: finding4.repro.recordingStepIndex,
119658
+ fingerprint: finding4.fingerprint,
119212
119659
  defectKind: "invariant",
119213
119660
  invariant: { spec: invariantSpec, id: invariantId, allowlist: mission.target.allowlist, baseUrl: mission.target.seedUrl },
119214
119661
  replays: attempts,
@@ -119226,16 +119673,16 @@ async function captureInvariantRegression(opts, fallbackRecording, mission, find
119226
119673
  rate: 1,
119227
119674
  label: "reproducible",
119228
119675
  fingerprint: { stepSignature: `invariant:${invariantId}` },
119229
- firstFailureAt: finding3.repro.recordingStepIndex
119676
+ firstFailureAt: finding4.repro.recordingStepIndex
119230
119677
  };
119231
119678
  return commitRegression(opts.regressionsDir, opts.id, recording, report2, opts.bugSummary, {
119232
119679
  kind: "invariant",
119233
119680
  invariantId,
119234
119681
  invariantSpec,
119235
- recordingStepIndex: finding3.repro.recordingStepIndex,
119682
+ recordingStepIndex: finding4.repro.recordingStepIndex,
119236
119683
  allowlist: mission.target.allowlist,
119237
119684
  baseUrl: mission.target.seedUrl,
119238
- defectFingerprint: finding3.fingerprint
119685
+ defectFingerprint: finding4.fingerprint
119239
119686
  });
119240
119687
  }
119241
119688
  if (verifyResult.verdict === "intermittent") {
@@ -119258,8 +119705,8 @@ async function runRegressionRun(opts) {
119258
119705
  let recording;
119259
119706
  let meta3;
119260
119707
  try {
119261
- recording = RecordingSchema.parse(JSON.parse(await readFile35(recordingPath, "utf8")));
119262
- meta3 = JSON.parse(await readFile35(metaPath, "utf8"));
119708
+ recording = RecordingSchema.parse(JSON.parse(await readFile38(recordingPath, "utf8")));
119709
+ meta3 = JSON.parse(await readFile38(metaPath, "utf8"));
119263
119710
  } catch {
119264
119711
  throw new RegressionNotFoundError(opts.id, opts.regressionsDir);
119265
119712
  }
@@ -119357,17 +119804,17 @@ function registerRegressionCommands(program2, deps) {
119357
119804
  const opened = [];
119358
119805
  let fx;
119359
119806
  try {
119360
- const raw = JSON.parse(await readFile36(from, "utf8"));
119807
+ const raw = JSON.parse(await readFile39(from, "utf8"));
119361
119808
  const recording = RecordingSchema.parse(raw);
119362
119809
  const captureEmulation = captureEmulationFlag ?? (recording.emulation === void 0 ? void 0 : recording.emulation.device !== void 0 ? { device: recording.emulation.device } : { viewport: recording.emulation.viewport });
119363
119810
  fx = regressionFixtures(
119364
119811
  flags,
119365
119812
  recording,
119366
- resultPath === void 0 ? void 0 : JSON.parse(await readFile36(resultPath, "utf8")),
119813
+ resultPath === void 0 ? void 0 : JSON.parse(await readFile39(resultPath, "utf8")),
119367
119814
  storageState
119368
119815
  );
119369
119816
  const replayFixture = fx;
119370
- const branch = resultPath === void 0 ? void 0 : recordedBranchOf(JSON.parse(await readFile36(resultPath, "utf8")).result);
119817
+ const branch = resultPath === void 0 ? void 0 : recordedBranchOf(JSON.parse(await readFile39(resultPath, "utf8")).result);
119371
119818
  const prefix = branch === void 0 ? void 0 : await branchPrefixOrRefuse(branch, param, storageState);
119372
119819
  if (prefix === null) return;
119373
119820
  const result = await runRegressionCapture({
@@ -119393,8 +119840,8 @@ function registerRegressionCommands(program2, deps) {
119393
119840
  }).then((r) => withEngine(r));
119394
119841
  await fx?.restore();
119395
119842
  if (branch !== void 0 && "metaPath" in result) {
119396
- const meta3 = JSON.parse(await readFile36(result.metaPath, "utf8"));
119397
- await writeFile44(result.metaPath, `${JSON.stringify({ ...meta3, branch }, null, 2)}
119843
+ const meta3 = JSON.parse(await readFile39(result.metaPath, "utf8"));
119844
+ await writeFile45(result.metaPath, `${JSON.stringify({ ...meta3, branch }, null, 2)}
119398
119845
  `);
119399
119846
  }
119400
119847
  const envelope = ok(fx === void 0 ? result : { ...result, fixtures: fx.record() });
@@ -119453,17 +119900,17 @@ function registerRegressionCommands(program2, deps) {
119453
119900
  }
119454
119901
  if (refuseUnsafeName(program2, id, "regression id")) return;
119455
119902
  const regressionsDir = resolveRegressionsDir(dir);
119456
- if (!existsSync45(join76(regressionsDir, `${id}.recording.json`))) {
119903
+ if (!existsSync45(join77(regressionsDir, `${id}.recording.json`))) {
119457
119904
  const notFound = new RegressionNotFoundError(id, regressionsDir);
119458
119905
  emitJson(program2, fail(notFound.code, notFound.message));
119459
119906
  return;
119460
119907
  }
119461
119908
  const opened = [];
119462
119909
  try {
119463
- const recording = RecordingSchema.parse(JSON.parse(await readFile36(join76(regressionsDir, `${id}.recording.json`), "utf8")));
119910
+ const recording = RecordingSchema.parse(JSON.parse(await readFile39(join77(regressionsDir, `${id}.recording.json`), "utf8")));
119464
119911
  let branch;
119465
119912
  try {
119466
- branch = recordedBranchOf(JSON.parse(await readFile36(join76(regressionsDir, `${id}.meta.json`), "utf8")));
119913
+ branch = recordedBranchOf(JSON.parse(await readFile39(join77(regressionsDir, `${id}.meta.json`), "utf8")));
119467
119914
  } catch {
119468
119915
  branch = void 0;
119469
119916
  }
@@ -119737,9 +120184,9 @@ async function queueExploration(targets, queue, args) {
119737
120184
  }
119738
120185
 
119739
120186
  // ../inbox/dist/atomic.js
119740
- import { open as open4, rename as rename6, rm as rm14 } from "node:fs/promises";
120187
+ import { open as open4, rename as rename7, rm as rm14 } from "node:fs/promises";
119741
120188
  import { randomBytes as randomBytes6 } from "node:crypto";
119742
- import { dirname as dirname33, join as join77 } from "node:path";
120189
+ import { dirname as dirname33, join as join78 } from "node:path";
119743
120190
 
119744
120191
  // ../inbox/dist/durability.js
119745
120192
  function durableWritesEnabled(env = process.env) {
@@ -119752,7 +120199,7 @@ async function flushIfDurable(handle) {
119752
120199
 
119753
120200
  // ../inbox/dist/atomic.js
119754
120201
  async function writeFileAtomic(path2, data, mode = 384) {
119755
- const tmp = join77(dirname33(path2), `.${randomBytes6(8).toString("hex")}.tmp`);
120202
+ const tmp = join78(dirname33(path2), `.${randomBytes6(8).toString("hex")}.tmp`);
119756
120203
  try {
119757
120204
  const handle = await open4(tmp, "wx", mode);
119758
120205
  try {
@@ -119761,7 +120208,7 @@ async function writeFileAtomic(path2, data, mode = 384) {
119761
120208
  } finally {
119762
120209
  await handle.close();
119763
120210
  }
119764
- await rename6(tmp, path2);
120211
+ await rename7(tmp, path2);
119765
120212
  } catch (err) {
119766
120213
  await rm14(tmp, { force: true });
119767
120214
  throw err;
@@ -119769,9 +120216,9 @@ async function writeFileAtomic(path2, data, mode = 384) {
119769
120216
  }
119770
120217
 
119771
120218
  // ../inbox/dist/lock.js
119772
- import { mkdir as mkdir36, open as open5, rename as rename7, rm as rm15, stat as stat6 } from "node:fs/promises";
120219
+ import { mkdir as mkdir37, open as open5, rename as rename8, rm as rm15, stat as stat6 } from "node:fs/promises";
119773
120220
  import { randomBytes as randomBytes7 } from "node:crypto";
119774
- import { join as join78 } from "node:path";
120221
+ import { join as join79 } from "node:path";
119775
120222
  var LockTimeoutError = class extends Error {
119776
120223
  constructor(id) {
119777
120224
  super(`could not acquire inbox lock for '${id}'`);
@@ -119782,8 +120229,8 @@ async function withIdLock(dir, id, fn, opts = {}) {
119782
120229
  const staleMs = opts.staleMs ?? 3e4;
119783
120230
  const retryMs = opts.retryMs ?? 20;
119784
120231
  const maxWaitMs = opts.maxWaitMs ?? 1e4;
119785
- const lockPath = join78(dir, `${id}.lock`);
119786
- await mkdir36(dir, { recursive: true, mode: 448 });
120232
+ const lockPath = join79(dir, `${id}.lock`);
120233
+ await mkdir37(dir, { recursive: true, mode: 448 });
119787
120234
  const deadline = clock.now() + maxWaitMs;
119788
120235
  for (; ; ) {
119789
120236
  try {
@@ -119797,7 +120244,7 @@ async function withIdLock(dir, id, fn, opts = {}) {
119797
120244
  const st = await stat6(lockPath);
119798
120245
  if (clock.now() - st.mtimeMs > staleMs) {
119799
120246
  const claimed = `${lockPath}.reclaim.${randomBytes7(6).toString("hex")}`;
119800
- await rename7(lockPath, claimed);
120247
+ await rename8(lockPath, claimed);
119801
120248
  await rm15(claimed, { force: true });
119802
120249
  continue;
119803
120250
  }
@@ -119890,8 +120337,8 @@ function resolveTransition(kind, action) {
119890
120337
  }
119891
120338
 
119892
120339
  // ../inbox/dist/store.js
119893
- import { mkdir as mkdir37, open as open6, readFile as readFile37, readdir as readdir12, stat as stat7, unlink } from "node:fs/promises";
119894
- import { join as join79 } from "node:path";
120340
+ import { mkdir as mkdir38, open as open6, readFile as readFile40, readdir as readdir12, stat as stat7, unlink } from "node:fs/promises";
120341
+ import { join as join80 } from "node:path";
119895
120342
  var InboxItemNotFoundError = class extends Error {
119896
120343
  constructor(id) {
119897
120344
  super(`inbox item not found: ${id}`);
@@ -119932,19 +120379,19 @@ var FsInboxStore = class {
119932
120379
  this.identity = typeof build === "string" ? { version: build } : { version: build.version, commit: build.commit, builtAt: build.builtAt };
119933
120380
  }
119934
120381
  hotPath(id) {
119935
- return join79(this.dir, `${id}.json`);
120382
+ return join80(this.dir, `${id}.json`);
119936
120383
  }
119937
120384
  archivePath(id) {
119938
- return join79(this.dir, "archive", `${id}.json`);
120385
+ return join80(this.dir, "archive", `${id}.json`);
119939
120386
  }
119940
120387
  archiveDir() {
119941
- return join79(this.dir, "archive");
120388
+ return join80(this.dir, "archive");
119942
120389
  }
119943
120390
  /** Reads and validates the hot file, or null on ENOENT. Throws on malformed content (fail-closed). */
119944
120391
  async readHot(id) {
119945
120392
  let raw;
119946
120393
  try {
119947
- raw = await readFile37(this.hotPath(id), "utf8");
120394
+ raw = await readFile40(this.hotPath(id), "utf8");
119948
120395
  } catch (err) {
119949
120396
  if (isNodeError(err, "ENOENT"))
119950
120397
  return null;
@@ -119956,7 +120403,7 @@ var FsInboxStore = class {
119956
120403
  async readArchive(id) {
119957
120404
  let raw;
119958
120405
  try {
119959
- raw = await readFile37(this.archivePath(id), "utf8");
120406
+ raw = await readFile40(this.archivePath(id), "utf8");
119960
120407
  } catch (err) {
119961
120408
  if (isNodeError(err, "ENOENT"))
119962
120409
  return null;
@@ -119968,7 +120415,7 @@ var FsInboxStore = class {
119968
120415
  const validated = InboxItemSchema.parse(item);
119969
120416
  assertSafeInboxId(validated.id);
119970
120417
  await withIdLock(this.dir, validated.id, async () => {
119971
- await mkdir37(this.dir, { recursive: true, mode: 448 });
120418
+ await mkdir38(this.dir, { recursive: true, mode: 448 });
119972
120419
  const serialized = JSON.stringify(validated);
119973
120420
  let fh;
119974
120421
  try {
@@ -120002,7 +120449,7 @@ var FsInboxStore = class {
120002
120449
  if (entry.startsWith("."))
120003
120450
  continue;
120004
120451
  try {
120005
- const raw = await readFile37(join79(this.dir, entry), "utf8");
120452
+ const raw = await readFile40(join80(this.dir, entry), "utf8");
120006
120453
  const parsed = InboxItemSchema.parse(JSON.parse(raw));
120007
120454
  if (parsed.status === "pending")
120008
120455
  items.push(parsed);
@@ -120071,7 +120518,7 @@ var FsInboxStore = class {
120071
120518
  const decision = transition === "approved" || transition === "rejected" || transition === "resolved" ? transition : "resolved";
120072
120519
  updated.resolution = { by: "human", decision, at: now2 };
120073
120520
  const validated = InboxItemSchema.parse(updated);
120074
- await mkdir37(this.archiveDir(), { recursive: true, mode: 448 });
120521
+ await mkdir38(this.archiveDir(), { recursive: true, mode: 448 });
120075
120522
  await writeFileAtomic(this.archivePath(id), JSON.stringify(validated));
120076
120523
  await unlink(this.hotPath(id));
120077
120524
  return validated;
@@ -120123,7 +120570,7 @@ var FsInboxStore = class {
120123
120570
  const updated = { ...item, status: "expired", resolvedAt: at3 };
120124
120571
  delete updated.humanInput;
120125
120572
  const validated = InboxItemSchema.parse(updated);
120126
- await mkdir37(this.archiveDir(), { recursive: true, mode: 448 });
120573
+ await mkdir38(this.archiveDir(), { recursive: true, mode: 448 });
120127
120574
  await writeFileAtomic(this.archivePath(id), JSON.stringify(validated));
120128
120575
  await unlink(this.hotPath(id));
120129
120576
  return true;
@@ -120151,7 +120598,7 @@ var FsInboxStore = class {
120151
120598
  for (const entry of entries) {
120152
120599
  if (!entry.endsWith(".json") || entry.startsWith("."))
120153
120600
  continue;
120154
- const full = join79(this.dir, entry);
120601
+ const full = join80(this.dir, entry);
120155
120602
  try {
120156
120603
  const st = await stat7(full);
120157
120604
  if (!st.isFile())
@@ -120430,8 +120877,8 @@ var tagsArg = (args) => {
120430
120877
 
120431
120878
  // src/mcp-tools/findings.ts
120432
120879
  import { existsSync as existsSync47 } from "node:fs";
120433
- import { access, readFile as readFile38 } from "node:fs/promises";
120434
- import { join as join80 } from "node:path";
120880
+ import { access, readFile as readFile41 } from "node:fs/promises";
120881
+ import { join as join81 } from "node:path";
120435
120882
  function findingTools(ctx) {
120436
120883
  const { deps, pathRoots, verifyFixImpl } = ctx;
120437
120884
  const resolveResultId = async (id, tool) => {
@@ -120474,7 +120921,7 @@ function findingTools(ctx) {
120474
120921
  const resultPathsFor = (resultId) => {
120475
120922
  const file2 = missionResultFileName(resultId);
120476
120923
  const dirs = deps.resultDirsFor?.(resultId) ?? [deps.recordingsDir, ...resultId.startsWith("usability-") ? [deps.uxReportsDir] : []];
120477
- return dirs.filter((d) => d !== void 0).map((d) => join80(d, file2));
120924
+ return dirs.filter((d) => d !== void 0).map((d) => join81(d, file2));
120478
120925
  };
120479
120926
  const getMissionResult = async (args) => {
120480
120927
  if (!deps.recordingsDir) {
@@ -120486,7 +120933,7 @@ function findingTools(ctx) {
120486
120933
  let raw;
120487
120934
  for (const path2 of resultPathsFor(ref.resultId)) {
120488
120935
  try {
120489
- raw = await readFile38(path2, "utf8");
120936
+ raw = await readFile41(path2, "utf8");
120490
120937
  break;
120491
120938
  } catch {
120492
120939
  }
@@ -121552,9 +121999,9 @@ function registerMissionCommands(program2, deps) {
121552
121999
  // src/ui-api.ts
121553
122000
  import { createServer } from "node:http";
121554
122001
  import { randomBytes as randomBytes8 } from "node:crypto";
121555
- import { readFile as readFile39, realpath as realpath2 } from "node:fs/promises";
122002
+ import { readFile as readFile42, realpath as realpath2 } from "node:fs/promises";
121556
122003
  import { fileURLToPath as fileURLToPath4 } from "node:url";
121557
- import { join as join81, sep as sep11 } from "node:path";
122004
+ import { join as join82, sep as sep11 } from "node:path";
121558
122005
  import { spawn as spawn6 } from "node:child_process";
121559
122006
  var DEFAULT_PORT = 4180;
121560
122007
  var MAX_PORT_ATTEMPTS = 20;
@@ -121656,7 +122103,7 @@ async function startUiServer(deps) {
121656
122103
  const shouldOpen = deps.open ?? true;
121657
122104
  const indexHtmlPath = fileURLToPath4(new URL("../ui/index.html", import.meta.url));
121658
122105
  const appJsPath = fileURLToPath4(new URL("../ui/app.js", import.meta.url));
121659
- const screenshotsDir = join81(deps.inboxDir, "screenshots");
122106
+ const screenshotsDir = join82(deps.inboxDir, "screenshots");
121660
122107
  const server = createServer();
121661
122108
  let boundPort = 0;
121662
122109
  function send(res, status, body, headers = {}) {
@@ -121668,7 +122115,7 @@ async function startUiServer(deps) {
121668
122115
  async function serveIndex(req, res) {
121669
122116
  const reqUrl = new URL(req.url ?? "/", `http://127.0.0.1:${boundPort}`);
121670
122117
  const t = reqUrl.searchParams.get("t");
121671
- const html = await readFile39(indexHtmlPath, "utf8");
122118
+ const html = await readFile42(indexHtmlPath, "utf8");
121672
122119
  const headers = {
121673
122120
  "Content-Type": "text/html; charset=utf-8",
121674
122121
  "Content-Security-Policy": "default-src 'self'; img-src 'self'; style-src 'self' 'unsafe-inline'"
@@ -121681,7 +122128,7 @@ async function startUiServer(deps) {
121681
122128
  return 200;
121682
122129
  }
121683
122130
  async function serveAppJs(res) {
121684
- const js = await readFile39(appJsPath, "utf8");
122131
+ const js = await readFile42(appJsPath, "utf8");
121685
122132
  res.writeHead(200, { "Content-Type": "text/javascript" });
121686
122133
  res.end(js);
121687
122134
  return 200;
@@ -121694,7 +122141,7 @@ async function startUiServer(deps) {
121694
122141
  } catch {
121695
122142
  return send(res, 404, { error: "not_found" });
121696
122143
  }
121697
- const candidate = join81(screenshotsDir, `${id}.png`);
122144
+ const candidate = join82(screenshotsDir, `${id}.png`);
121698
122145
  let resolved;
121699
122146
  try {
121700
122147
  resolved = await realpath2(candidate);
@@ -121705,7 +122152,7 @@ async function startUiServer(deps) {
121705
122152
  return send(res, 404, { error: "not_found" });
121706
122153
  }
121707
122154
  try {
121708
- const data = await readFile39(resolved);
122155
+ const data = await readFile42(resolved);
121709
122156
  res.writeHead(200, { "Content-Type": "image/png" });
121710
122157
  res.end(data);
121711
122158
  return 200;
@@ -121945,7 +122392,7 @@ function registerServeCommands(program2, deps, buildProgram2) {
121945
122392
  }
121946
122393
 
121947
122394
  // src/ux-cli.ts
121948
- import { readFile as readFile40 } from "node:fs/promises";
122395
+ import { readFile as readFile43 } from "node:fs/promises";
121949
122396
  function registerUxCommands(program2, deps) {
121950
122397
  program2.command("ux <recording>").description("offline UX review of a saved Recording \u2014 ranked, cited usability findings").option("--app-class <class>", "app class for calibration (required), e.g. consumer|admin|internal").option(
121951
122398
  "--show <labels>",
@@ -121973,7 +122420,7 @@ function registerUxCommands(program2, deps) {
121973
122420
  }
121974
122421
  let recording;
121975
122422
  try {
121976
- recording = RecordingSchema.parse(JSON.parse(await readFile40(recordingPath, "utf8")));
122423
+ recording = RecordingSchema.parse(JSON.parse(await readFile43(recordingPath, "utf8")));
121977
122424
  } catch (err) {
121978
122425
  emitJson(program2, fail("E_UX_INPUT", String(err instanceof Error ? err.message : err)));
121979
122426
  return;
@@ -122033,7 +122480,7 @@ function registerUxCommands(program2, deps) {
122033
122480
 
122034
122481
  // src/logs-retention.ts
122035
122482
  import { readFileSync as readFileSync39, readdirSync as readdirSync11, rmSync as rmSync2, rmdirSync as rmdirSync2, statSync as statSync13 } from "node:fs";
122036
- import { join as join82 } from "node:path";
122483
+ import { join as join83 } from "node:path";
122037
122484
  var DEFAULT_LOGS_RETENTION = { ttlDays: 14, keepLatest: 50 };
122038
122485
  var LogsConfigError = class extends Error {
122039
122486
  code = "E_LOGS_CONFIG";
@@ -122075,8 +122522,8 @@ function runsIn(root2) {
122075
122522
  }
122076
122523
  const byKey = /* @__PURE__ */ new Map();
122077
122524
  for (const date5 of dates) {
122078
- for (const name of readdirSync11(join82(root2, date5))) {
122079
- const path2 = join82(root2, date5, name);
122525
+ for (const name of readdirSync11(join83(root2, date5))) {
122526
+ const path2 = join83(root2, date5, name);
122080
122527
  const key2 = `${date5}/${RUN_KEY.exec(name)?.[1] ?? name}`;
122081
122528
  const run = byKey.get(key2) ?? { paths: [], mtimeMs: 0 };
122082
122529
  run.paths.push(path2);
@@ -122084,7 +122531,7 @@ function runsIn(root2) {
122084
122531
  byKey.set(key2, run);
122085
122532
  }
122086
122533
  }
122087
- return { runs: [...byKey].map(([key2, r]) => ({ key: key2, ...r })), dateDirs: dates.map((d) => join82(root2, d)) };
122534
+ return { runs: [...byKey].map(([key2, r]) => ({ key: key2, ...r })), dateDirs: dates.map((d) => join83(root2, d)) };
122088
122535
  }
122089
122536
  function pruneLogs(root2, retention, opts = {}) {
122090
122537
  const nowMs = opts.nowMs ?? clock.now();
@@ -122332,6 +122779,119 @@ function registerInboxCommands(program2, deps) {
122332
122779
  }
122333
122780
  }
122334
122781
 
122782
+ // src/acceptance-audit-cli.ts
122783
+ import { readFile as readFile45, mkdir as mkdir39, writeFile as writeFile46 } from "node:fs/promises";
122784
+
122785
+ // src/acceptance-audit.ts
122786
+ import { access as access2, readFile as readFile44 } from "node:fs/promises";
122787
+ var CORE = [
122788
+ ["requirements", "requirements"],
122789
+ ["persona", "persona"],
122790
+ ["source-journey", "sourceJourney"],
122791
+ ["approved-hash", "approvedHash"],
122792
+ ["verify-fix", "verifyFix"],
122793
+ ["strict-review", "strictReview"]
122794
+ ];
122795
+ async function finding3(asset, path2) {
122796
+ if (path2 === void 0 || path2 === "") return { asset, state: "missing", reason: "no explicit local path supplied" };
122797
+ try {
122798
+ await access2(path2);
122799
+ } catch {
122800
+ return { asset, state: "missing", path: path2, reason: "local file does not exist" };
122801
+ }
122802
+ try {
122803
+ if ((await readFile44(path2, "utf8")).trim() === "") return { asset, state: "inconclusive", path: path2, reason: "local file is empty" };
122804
+ } catch {
122805
+ return { asset, state: "inconclusive", path: path2, reason: "local file cannot be read as text" };
122806
+ }
122807
+ return { asset, state: "complete", path: path2 };
122808
+ }
122809
+ async function auditCoreAcceptanceAssets(assets) {
122810
+ return Promise.all(CORE.map(([asset, key2]) => finding3(asset, assets[key2])));
122811
+ }
122812
+
122813
+ // src/acceptance-audit-schema.ts
122814
+ var ACCEPTANCE_AUDIT_SCHEMA_VERSION = 1;
122815
+ function acceptanceAuditResult(request, findings) {
122816
+ const passed = findings.every((finding4) => finding4.state === "complete");
122817
+ return { schemaVersion: ACCEPTANCE_AUDIT_SCHEMA_VERSION, request, findings, passed, exitCode: passed ? 0 : 1 };
122818
+ }
122819
+
122820
+ // src/acceptance-audit-suite.ts
122821
+ function auditSuiteIntegrity(entries) {
122822
+ const duplicates = entries.filter((entry, index) => entries.findIndex((other) => other.id === entry.id) !== index).map((entry) => entry.id);
122823
+ const missingSidecar = entries.filter((entry) => !entry.sidecar).map((entry) => entry.id);
122824
+ const missingPartition = entries.filter((entry) => entry.partition.trim() === "").map((entry) => entry.id);
122825
+ const missingRevision = entries.filter((entry) => entry.revision.trim() === "").map((entry) => entry.id);
122826
+ const hasNegativeControl = entries.some((entry) => entry.negativeControl);
122827
+ return [
122828
+ {
122829
+ asset: "suite-sidecar",
122830
+ state: missingSidecar.length === 0 ? "complete" : "missing",
122831
+ ...missingSidecar.length === 0 ? {} : { reason: `missing sidecar: ${missingSidecar.join(", ")}` }
122832
+ },
122833
+ {
122834
+ asset: "negative-control",
122835
+ state: hasNegativeControl ? "complete" : "missing",
122836
+ ...hasNegativeControl ? {} : { reason: "suite has no declared negative control" }
122837
+ },
122838
+ {
122839
+ asset: "partition",
122840
+ state: duplicates.length === 0 && missingPartition.length === 0 ? "complete" : "inconclusive",
122841
+ ...duplicates.length === 0 && missingPartition.length === 0 ? {} : { reason: [...duplicates.length === 0 ? [] : [`duplicate ids: ${duplicates.join(", ")}`], ...missingPartition.length === 0 ? [] : [`unpartitioned: ${missingPartition.join(", ")}`]].join("; ") }
122842
+ },
122843
+ {
122844
+ asset: "exact-revision",
122845
+ state: missingRevision.length === 0 ? "complete" : "stale",
122846
+ ...missingRevision.length === 0 ? {} : { reason: `missing exact revision: ${missingRevision.join(", ")}` }
122847
+ }
122848
+ ];
122849
+ }
122850
+
122851
+ // src/acceptance-audit-render.ts
122852
+ var xml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
122853
+ function renderAcceptanceAuditMarkdown(result) {
122854
+ return `# Acceptance audit
122855
+
122856
+ ${result.passed ? "Passed" : "Failed"} (exit ${result.exitCode}).
122857
+
122858
+ | asset | state | reason |
122859
+ | --- | --- | --- |
122860
+ ${result.findings.map((f2) => `| ${f2.asset} | ${f2.state} | ${f2.reason ?? ""} |`).join("\n")}
122861
+ `;
122862
+ }
122863
+ function renderAcceptanceAuditJunit(result) {
122864
+ const failures = result.findings.filter((f2) => f2.state !== "complete");
122865
+ return `<?xml version="1.0" encoding="UTF-8"?><testsuite name="jevitate.acceptance-audit" tests="${result.findings.length}" failures="${failures.length}">${result.findings.map((f2) => `<testcase name="${xml(f2.asset)}">${f2.state === "complete" ? "" : `<failure message="${xml(f2.state)}">${xml(f2.reason ?? "")}</failure>`}</testcase>`).join("")}</testsuite>
122866
+ `;
122867
+ }
122868
+ function renderAcceptanceAuditSarif(result) {
122869
+ return `${JSON.stringify({ version: "2.1.0", runs: [{ tool: { driver: { name: "jevitate acceptance audit", rules: result.findings.map((f2) => ({ id: f2.asset })) } }, results: result.findings.filter((f2) => f2.state !== "complete").map((f2) => ({ ruleId: f2.asset, level: "error", message: { text: f2.reason ?? f2.state } })) }] }, null, 2)}
122870
+ `;
122871
+ }
122872
+
122873
+ // src/acceptance-audit-cli.ts
122874
+ function registerAcceptanceAuditCommands(program2) {
122875
+ const acceptance = program2.command("acceptance").description("read-only acceptance evidence tools");
122876
+ acceptance.command("audit").description("audit release acceptance evidence and emit CI artifacts").requiredOption("--requirements <file>").requiredOption("--suite <file>").option("--persona <file>").option("--journey <file>").option("--approval <file>").option("--verify <file>").option("--review <file>").option("--out <dir>").option("--json").action(async function() {
122877
+ const o = this.opts();
122878
+ try {
122879
+ const core = await auditCoreAcceptanceAssets({ requirements: o.requirements, persona: o.persona, sourceJourney: o.journey, approvedHash: o.approval, verifyFix: o.verify, strictReview: o.review });
122880
+ const entries = JSON.parse(await readFile45(o.suite, "utf8"));
122881
+ if (!Array.isArray(entries)) throw new Error("suite must be a JSON array of acceptance entries");
122882
+ const result = acceptanceAuditResult({ requirementsFile: o.requirements, suiteFile: o.suite, strict: true }, [...core, ...auditSuiteIntegrity(entries)]);
122883
+ if (o.out !== void 0) {
122884
+ await mkdir39(o.out, { recursive: true });
122885
+ await Promise.all([writeFile46(`${o.out}/acceptance-audit.md`, renderAcceptanceAuditMarkdown(result)), writeFile46(`${o.out}/acceptance-audit.junit.xml`, renderAcceptanceAuditJunit(result)), writeFile46(`${o.out}/acceptance-audit.sarif`, renderAcceptanceAuditSarif(result)), writeFile46(`${o.out}/acceptance-audit.json`, `${JSON.stringify(result, null, 2)}
122886
+ `)]);
122887
+ }
122888
+ emitEnvelope(program2, ok(result), { json: o.json === true, exitCode: result.exitCode, command: "acceptance audit", human: (r) => renderAcceptanceAuditMarkdown(r) });
122889
+ } catch (err) {
122890
+ emitEnvelope(program2, fail("E_ACCEPTANCE_AUDIT", err instanceof Error ? err.message : String(err)), { json: o.json === true, command: "acceptance audit" });
122891
+ }
122892
+ });
122893
+ }
122894
+
122335
122895
  // src/program.ts
122336
122896
  function versionString() {
122337
122897
  return formatVersion(currentEngineInfo());
@@ -122371,6 +122931,7 @@ function buildProgram(deps) {
122371
122931
  registerMissionCommands(program2, deps);
122372
122932
  registerServeCommands(program2, deps, buildProgram);
122373
122933
  registerInboxCommands(program2, deps);
122934
+ registerAcceptanceAuditCommands(program2);
122374
122935
  registerUxCommands(program2, deps);
122375
122936
  registerAiCommands(program2, deps);
122376
122937
  registerCheckCommand(