@jevitate/cli 0.23.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.
- package/contracts/jevitate-catalog/v1/README.md +10 -0
- package/contracts/jevitate-catalog/v1/approval-hash-vectors.json +64 -0
- package/contracts/jevitate-catalog/v1/fixtures/invalid/jobs-unknown-field.json +12 -0
- package/contracts/jevitate-catalog/v1/fixtures/invalid/journey-unknown-field.json +15 -0
- package/contracts/jevitate-catalog/v1/fixtures/valid/jobs.json +11 -0
- package/contracts/jevitate-catalog/v1/fixtures/valid/journey.json +14 -0
- package/contracts/jevitate-catalog/v1/fixtures/valid/personas.json +7 -0
- package/contracts/jevitate-catalog/v1/manifest.json +69 -0
- package/contracts/jevitate-catalog/v1/schemas/job.schema.json +605 -0
- package/contracts/jevitate-catalog/v1/schemas/jobs.schema.json +28 -0
- package/contracts/jevitate-catalog/v1/schemas/journey.schema.json +10871 -0
- package/contracts/jevitate-catalog/v1/schemas/personas.schema.json +223 -0
- package/contracts/jevitate-catalog/v2/README.md +18 -0
- package/contracts/jevitate-catalog/v2/approval-hash-vectors.json +75 -0
- package/contracts/jevitate-catalog/v2/fixtures/invalid/jobs-unknown-field.json +12 -0
- package/contracts/jevitate-catalog/v2/fixtures/invalid/journey-unknown-field.json +15 -0
- package/contracts/jevitate-catalog/v2/fixtures/valid/jobs.json +11 -0
- package/contracts/jevitate-catalog/v2/fixtures/valid/journey.json +14 -0
- package/contracts/jevitate-catalog/v2/fixtures/valid/personas.json +7 -0
- package/contracts/jevitate-catalog/v2/manifest.json +78 -0
- package/contracts/jevitate-catalog/v2/schemas/job.schema.json +605 -0
- package/contracts/jevitate-catalog/v2/schemas/jobs.schema.json +28 -0
- package/contracts/jevitate-catalog/v2/schemas/journey.schema.json +10871 -0
- package/contracts/jevitate-catalog/v2/schemas/personas.schema.json +223 -0
- package/dist/acceptance-audit-approval-script.d.ts +6 -0
- package/dist/acceptance-audit-approval-script.js +20 -0
- package/dist/acceptance-audit-approval-script.js.map +1 -0
- package/dist/acceptance-audit-cli.d.ts +2 -0
- package/dist/acceptance-audit-cli.js +34 -0
- package/dist/acceptance-audit-cli.js.map +1 -0
- package/dist/acceptance-audit-render.d.ts +4 -0
- package/dist/acceptance-audit-render.js +12 -0
- package/dist/acceptance-audit-render.js.map +1 -0
- package/dist/acceptance-audit-schema.d.ts +32 -0
- package/dist/acceptance-audit-schema.js +19 -0
- package/dist/acceptance-audit-schema.js.map +1 -0
- package/dist/acceptance-audit-suite.d.ts +14 -0
- package/dist/acceptance-audit-suite.js +34 -0
- package/dist/acceptance-audit-suite.js.map +1 -0
- package/dist/acceptance-audit.d.ts +16 -0
- package/dist/acceptance-audit.js +36 -0
- package/dist/acceptance-audit.js.map +1 -0
- package/dist/bin.js +1155 -509
- package/dist/build-info.generated.js +3 -3
- package/dist/catalog-bundle.d.ts +4 -2
- package/dist/catalog-bundle.js +8 -6
- package/dist/catalog-bundle.js.map +1 -1
- package/dist/catalog-contract-identity.d.ts +5 -0
- package/dist/catalog-contract-identity.js +7 -0
- package/dist/catalog-contract-identity.js.map +1 -0
- package/dist/catalog.js +18 -7
- package/dist/catalog.js.map +1 -1
- package/dist/explore-goal.d.ts +3 -1
- package/dist/explore-goal.js +1 -0
- package/dist/explore-goal.js.map +1 -1
- package/dist/goal-run-flags.js +2 -2
- package/dist/goal-run-flags.js.map +1 -1
- package/dist/journeeze-hint.d.ts +35 -0
- package/dist/journeeze-hint.js +77 -0
- package/dist/journeeze-hint.js.map +1 -0
- package/dist/journey-cli/review.js +16 -2
- package/dist/journey-cli/review.js.map +1 -1
- package/dist/journey-cli/verify.js +33 -7
- package/dist/journey-cli/verify.js.map +1 -1
- package/dist/journey-review.d.ts +3 -0
- package/dist/journey-review.js +13 -2
- package/dist/journey-review.js.map +1 -1
- package/dist/journey-stale-api.js +4 -4
- package/dist/journey-stale-api.js.map +1 -1
- package/dist/journey-verify.d.ts +23 -0
- package/dist/journey-verify.js +34 -0
- package/dist/journey-verify.js.map +1 -1
- package/dist/mcp-cli-tools.js +2 -1
- package/dist/mcp-cli-tools.js.map +1 -1
- package/dist/program.js +2 -0
- package/dist/program.js.map +1 -1
- package/dist/report-cli.js +21 -2
- package/dist/report-cli.js.map +1 -1
- package/dist/ux-usability/done-adjudication.d.ts +2 -2
- package/package.json +14 -11
- package/skills/jevitate-promotion-ready/SKILL.md +5 -1
- package/skills/jevitate-record/SKILL.md +3 -2
package/dist/bin.js
CHANGED
|
@@ -10,16 +10,36 @@ import { mkdir, stat } from "node:fs/promises";
|
|
|
10
10
|
|
|
11
11
|
// ../domain/dist/content-hash.js
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
13
|
-
function
|
|
13
|
+
function compareUtf16CodeUnits(a, b2) {
|
|
14
|
+
const length = Math.min(a.length, b2.length);
|
|
15
|
+
for (let index = 0; index < length; index += 1) {
|
|
16
|
+
const difference = a.charCodeAt(index) - b2.charCodeAt(index);
|
|
17
|
+
if (difference !== 0)
|
|
18
|
+
return difference < 0 ? -1 : 1;
|
|
19
|
+
}
|
|
20
|
+
return a.length === b2.length ? 0 : a.length < b2.length ? -1 : 1;
|
|
21
|
+
}
|
|
22
|
+
function canonicalJson(value) {
|
|
14
23
|
if (value === null || typeof value !== "object")
|
|
15
24
|
return JSON.stringify(value);
|
|
16
25
|
if (Array.isArray(value))
|
|
17
|
-
return `[${value.map(
|
|
26
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
27
|
+
const entries = Object.entries(value).sort(([a], [b2]) => compareUtf16CodeUnits(a, b2));
|
|
28
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(",")}}`;
|
|
29
|
+
}
|
|
30
|
+
function legacyCanonicalJson(value) {
|
|
31
|
+
if (value === null || typeof value !== "object")
|
|
32
|
+
return JSON.stringify(value);
|
|
33
|
+
if (Array.isArray(value))
|
|
34
|
+
return `[${value.map(legacyCanonicalJson).join(",")}]`;
|
|
18
35
|
const entries = Object.entries(value).sort(([a], [b2]) => a.localeCompare(b2));
|
|
19
|
-
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${
|
|
36
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${legacyCanonicalJson(v)}`).join(",")}}`;
|
|
20
37
|
}
|
|
21
38
|
function contentHash(value) {
|
|
22
|
-
return createHash("sha256").update(
|
|
39
|
+
return createHash("sha256").update(canonicalJson(value)).digest("hex");
|
|
40
|
+
}
|
|
41
|
+
function legacyContentHash(value) {
|
|
42
|
+
return createHash("sha256").update(legacyCanonicalJson(value)).digest("hex");
|
|
23
43
|
}
|
|
24
44
|
function sha256(text2) {
|
|
25
45
|
return createHash("sha256").update(text2, "utf8").digest("hex");
|
|
@@ -3611,8 +3631,8 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
3611
3631
|
const _normalized = cached(() => normalizeDef(def));
|
|
3612
3632
|
const memo2 = globalConfig.memoizer;
|
|
3613
3633
|
const generateFastpass = (shape3) => {
|
|
3614
|
-
const
|
|
3615
|
-
const syms =
|
|
3634
|
+
const normalized2 = _normalized.value;
|
|
3635
|
+
const syms = normalized2.symbolKeys;
|
|
3616
3636
|
const doc = new Doc(["payload", "ctx"], { shape: shape3, inst, memo: memo2, syms });
|
|
3617
3637
|
const parseStr = (k) => `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
|
|
3618
3638
|
const prefixStr = (id, k) => `
|
|
@@ -3630,11 +3650,11 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
3630
3650
|
doc.write(`const input = payload.value;`);
|
|
3631
3651
|
const ids = /* @__PURE__ */ Object.create(null);
|
|
3632
3652
|
let counter = 0;
|
|
3633
|
-
for (const key2 of
|
|
3653
|
+
for (const key2 of normalized2.allKeys) {
|
|
3634
3654
|
ids[key2] = `key_${counter++}`;
|
|
3635
3655
|
}
|
|
3636
3656
|
doc.write(memo2 ? `const newResult = memo.alloc(inst, payload, {}, ctx);` : `const newResult = {};`);
|
|
3637
|
-
for (const key2 of
|
|
3657
|
+
for (const key2 of normalized2.allKeys) {
|
|
3638
3658
|
if (key2 === "__proto__")
|
|
3639
3659
|
continue;
|
|
3640
3660
|
const id = ids[key2];
|
|
@@ -19463,23 +19483,23 @@ function fromJSONSchema(schema, params) {
|
|
|
19463
19483
|
if (typeof schema === "boolean") {
|
|
19464
19484
|
return schema ? z.any() : z.never();
|
|
19465
19485
|
}
|
|
19466
|
-
let
|
|
19486
|
+
let normalized2;
|
|
19467
19487
|
try {
|
|
19468
|
-
|
|
19488
|
+
normalized2 = JSON.parse(JSON.stringify(schema));
|
|
19469
19489
|
} catch {
|
|
19470
19490
|
throw new Error("fromJSONSchema input is not valid JSON (possibly cyclic); use $defs/$ref for recursive schemas");
|
|
19471
19491
|
}
|
|
19472
|
-
const version2 = detectVersion(
|
|
19473
|
-
const defs =
|
|
19492
|
+
const version2 = detectVersion(normalized2, params?.defaultTarget);
|
|
19493
|
+
const defs = normalized2.$defs || normalized2.definitions || {};
|
|
19474
19494
|
const ctx = {
|
|
19475
19495
|
version: version2,
|
|
19476
19496
|
defs,
|
|
19477
19497
|
refs: /* @__PURE__ */ new Map(),
|
|
19478
19498
|
processing: /* @__PURE__ */ new Set(),
|
|
19479
|
-
rootSchema:
|
|
19499
|
+
rootSchema: normalized2,
|
|
19480
19500
|
registry: params?.registry ?? globalRegistry
|
|
19481
19501
|
};
|
|
19482
|
-
return convertSchema(
|
|
19502
|
+
return convertSchema(normalized2, ctx);
|
|
19483
19503
|
}
|
|
19484
19504
|
|
|
19485
19505
|
// ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/visit.js
|
|
@@ -21403,15 +21423,15 @@ function asNumber(value) {
|
|
|
21403
21423
|
return numericValue;
|
|
21404
21424
|
}
|
|
21405
21425
|
function normalizeObject(obj, normalizer) {
|
|
21406
|
-
const
|
|
21426
|
+
const normalized2 = {};
|
|
21407
21427
|
for (const u in obj) {
|
|
21408
21428
|
if (hasOwnProperty(obj, u)) {
|
|
21409
21429
|
const v = obj[u];
|
|
21410
21430
|
if (v === void 0 || v === null) continue;
|
|
21411
|
-
|
|
21431
|
+
normalized2[normalizer(u)] = asNumber(v);
|
|
21412
21432
|
}
|
|
21413
21433
|
}
|
|
21414
|
-
return
|
|
21434
|
+
return normalized2;
|
|
21415
21435
|
}
|
|
21416
21436
|
function formatOffset(offset2, format) {
|
|
21417
21437
|
const hours = Math.trunc(Math.abs(offset2 / 60)), minutes = Math.trunc(Math.abs(offset2 % 60)), sign = offset2 >= 0 ? "+" : "-";
|
|
@@ -22412,7 +22432,7 @@ var Duration = class _Duration {
|
|
|
22412
22432
|
* @private
|
|
22413
22433
|
*/
|
|
22414
22434
|
static normalizeUnit(unit) {
|
|
22415
|
-
const
|
|
22435
|
+
const normalized2 = {
|
|
22416
22436
|
year: "years",
|
|
22417
22437
|
years: "years",
|
|
22418
22438
|
quarter: "quarters",
|
|
@@ -22432,8 +22452,8 @@ var Duration = class _Duration {
|
|
|
22432
22452
|
millisecond: "milliseconds",
|
|
22433
22453
|
milliseconds: "milliseconds"
|
|
22434
22454
|
}[unit ? unit.toLowerCase() : unit];
|
|
22435
|
-
if (!
|
|
22436
|
-
return
|
|
22455
|
+
if (!normalized2) throw new InvalidUnitError(unit);
|
|
22456
|
+
return normalized2;
|
|
22437
22457
|
}
|
|
22438
22458
|
/**
|
|
22439
22459
|
* Check if an object is a Duration. Works across context boundaries
|
|
@@ -24336,7 +24356,7 @@ var orderedWeekUnits = [
|
|
|
24336
24356
|
];
|
|
24337
24357
|
var orderedOrdinalUnits = ["year", "ordinal", "hour", "minute", "second", "millisecond"];
|
|
24338
24358
|
function normalizeUnit(unit) {
|
|
24339
|
-
const
|
|
24359
|
+
const normalized2 = {
|
|
24340
24360
|
year: "year",
|
|
24341
24361
|
years: "year",
|
|
24342
24362
|
month: "month",
|
|
@@ -24362,8 +24382,8 @@ function normalizeUnit(unit) {
|
|
|
24362
24382
|
weekyears: "weekYear",
|
|
24363
24383
|
ordinal: "ordinal"
|
|
24364
24384
|
}[unit.toLowerCase()];
|
|
24365
|
-
if (!
|
|
24366
|
-
return
|
|
24385
|
+
if (!normalized2) throw new InvalidUnitError(unit);
|
|
24386
|
+
return normalized2;
|
|
24367
24387
|
}
|
|
24368
24388
|
function normalizeUnitWithLocalWeeks(unit) {
|
|
24369
24389
|
switch (unit.toLowerCase()) {
|
|
@@ -24662,9 +24682,9 @@ var DateTime = class _DateTime {
|
|
|
24662
24682
|
return _DateTime.invalid(unsupportedZone(zoneToUse));
|
|
24663
24683
|
}
|
|
24664
24684
|
const loc = Locale.fromObject(opts);
|
|
24665
|
-
const
|
|
24666
|
-
const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(
|
|
24667
|
-
const tsNow = Settings.now(), offsetProvis = !isUndefined(opts.specificOffset) ? opts.specificOffset : zoneToUse.offset(tsNow), containsOrdinal = !isUndefined(
|
|
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;
|
|
24668
24688
|
if ((containsGregor || containsOrdinal) && definiteWeekDef) {
|
|
24669
24689
|
throw new ConflictingSpecificationError(
|
|
24670
24690
|
"Can't mix weekYear/weekNumber units with year/month/day or ordinals"
|
|
@@ -24673,7 +24693,7 @@ var DateTime = class _DateTime {
|
|
|
24673
24693
|
if (containsGregorMD && containsOrdinal) {
|
|
24674
24694
|
throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day");
|
|
24675
24695
|
}
|
|
24676
|
-
const useWeekData = definiteWeekDef ||
|
|
24696
|
+
const useWeekData = definiteWeekDef || normalized2.weekday && !containsGregor;
|
|
24677
24697
|
let units, defaultValues, objNow = tsToObj(tsNow, offsetProvis);
|
|
24678
24698
|
if (useWeekData) {
|
|
24679
24699
|
units = orderedWeekUnits;
|
|
@@ -24689,29 +24709,29 @@ var DateTime = class _DateTime {
|
|
|
24689
24709
|
}
|
|
24690
24710
|
let foundFirst = false;
|
|
24691
24711
|
for (const u of units) {
|
|
24692
|
-
const v =
|
|
24712
|
+
const v = normalized2[u];
|
|
24693
24713
|
if (!isUndefined(v)) {
|
|
24694
24714
|
foundFirst = true;
|
|
24695
24715
|
} else if (foundFirst) {
|
|
24696
|
-
|
|
24716
|
+
normalized2[u] = defaultValues[u];
|
|
24697
24717
|
} else {
|
|
24698
|
-
|
|
24718
|
+
normalized2[u] = objNow[u];
|
|
24699
24719
|
}
|
|
24700
24720
|
}
|
|
24701
|
-
const higherOrderInvalid = useWeekData ? hasInvalidWeekData(
|
|
24721
|
+
const higherOrderInvalid = useWeekData ? hasInvalidWeekData(normalized2, minDaysInFirstWeek, startOfWeek) : containsOrdinal ? hasInvalidOrdinalData(normalized2) : hasInvalidGregorianData(normalized2), invalid = higherOrderInvalid || hasInvalidTimeData(normalized2);
|
|
24702
24722
|
if (invalid) {
|
|
24703
24723
|
return _DateTime.invalid(invalid);
|
|
24704
24724
|
}
|
|
24705
|
-
const gregorian = useWeekData ? weekToGregorian(
|
|
24725
|
+
const gregorian = useWeekData ? weekToGregorian(normalized2, minDaysInFirstWeek, startOfWeek) : containsOrdinal ? ordinalToGregorian(normalized2) : normalized2, [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse), inst = new _DateTime({
|
|
24706
24726
|
ts: tsFinal,
|
|
24707
24727
|
zone: zoneToUse,
|
|
24708
24728
|
o: offsetFinal,
|
|
24709
24729
|
loc
|
|
24710
24730
|
});
|
|
24711
|
-
if (
|
|
24731
|
+
if (normalized2.weekday && containsGregor && obj.weekday !== inst.weekday) {
|
|
24712
24732
|
return _DateTime.invalid(
|
|
24713
24733
|
"mismatched weekday",
|
|
24714
|
-
`you can't specify both a weekday of ${
|
|
24734
|
+
`you can't specify both a weekday of ${normalized2.weekday} and a date of ${inst.toISO()}`
|
|
24715
24735
|
);
|
|
24716
24736
|
}
|
|
24717
24737
|
if (!inst.isValid) {
|
|
@@ -25360,9 +25380,9 @@ var DateTime = class _DateTime {
|
|
|
25360
25380
|
*/
|
|
25361
25381
|
set(values2) {
|
|
25362
25382
|
if (!this.isValid) return this;
|
|
25363
|
-
const
|
|
25364
|
-
const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(
|
|
25365
|
-
const settingWeekStuff = !isUndefined(
|
|
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;
|
|
25366
25386
|
if ((containsGregor || containsOrdinal) && definiteWeekDef) {
|
|
25367
25387
|
throw new ConflictingSpecificationError(
|
|
25368
25388
|
"Can't mix weekYear/weekNumber units with year/month/day or ordinals"
|
|
@@ -25374,15 +25394,15 @@ var DateTime = class _DateTime {
|
|
|
25374
25394
|
let mixed;
|
|
25375
25395
|
if (settingWeekStuff) {
|
|
25376
25396
|
mixed = weekToGregorian(
|
|
25377
|
-
{ ...gregorianToWeek(this.c, minDaysInFirstWeek, startOfWeek), ...
|
|
25397
|
+
{ ...gregorianToWeek(this.c, minDaysInFirstWeek, startOfWeek), ...normalized2 },
|
|
25378
25398
|
minDaysInFirstWeek,
|
|
25379
25399
|
startOfWeek
|
|
25380
25400
|
);
|
|
25381
|
-
} else if (!isUndefined(
|
|
25382
|
-
mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...
|
|
25401
|
+
} else if (!isUndefined(normalized2.ordinal)) {
|
|
25402
|
+
mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...normalized2 });
|
|
25383
25403
|
} else {
|
|
25384
|
-
mixed = { ...this.toObject(), ...
|
|
25385
|
-
if (isUndefined(
|
|
25404
|
+
mixed = { ...this.toObject(), ...normalized2 };
|
|
25405
|
+
if (isUndefined(normalized2.day)) {
|
|
25386
25406
|
mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day);
|
|
25387
25407
|
}
|
|
25388
25408
|
}
|
|
@@ -28116,8 +28136,8 @@ function idFromDigest(bytes) {
|
|
|
28116
28136
|
function extensionIdForPath(absDir, platform2 = process.platform) {
|
|
28117
28137
|
if (platform2 !== "win32")
|
|
28118
28138
|
return idFromDigest(Buffer.from(absDir, "utf8"));
|
|
28119
|
-
const
|
|
28120
|
-
return idFromDigest(Buffer.from(
|
|
28139
|
+
const normalized2 = /^[a-z]:/.test(absDir) ? absDir[0].toUpperCase() + absDir.slice(1) : absDir;
|
|
28140
|
+
return idFromDigest(Buffer.from(normalized2, "utf16le"));
|
|
28121
28141
|
}
|
|
28122
28142
|
function extensionIdForKey(base64Key) {
|
|
28123
28143
|
return idFromDigest(Buffer.from(base64Key.replace(/\s+/g, ""), "base64"));
|
|
@@ -33945,18 +33965,18 @@ function stepIdsOf(rec) {
|
|
|
33945
33965
|
ids.add(s3.stepId);
|
|
33946
33966
|
return ids;
|
|
33947
33967
|
}
|
|
33948
|
-
function
|
|
33968
|
+
function canonicalJson2(v) {
|
|
33949
33969
|
if (Array.isArray(v))
|
|
33950
|
-
return `[${v.map(
|
|
33970
|
+
return `[${v.map(canonicalJson2).join(",")}]`;
|
|
33951
33971
|
if (v !== null && typeof v === "object") {
|
|
33952
33972
|
const o = v;
|
|
33953
|
-
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${
|
|
33973
|
+
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson2(o[k])}`).join(",")}}`;
|
|
33954
33974
|
}
|
|
33955
33975
|
return JSON.stringify(v) ?? "null";
|
|
33956
33976
|
}
|
|
33957
33977
|
function seededRandom(rec) {
|
|
33958
33978
|
let h = 2166136261;
|
|
33959
|
-
const text2 =
|
|
33979
|
+
const text2 = canonicalJson2(rec);
|
|
33960
33980
|
for (let i = 0; i < text2.length; i++) {
|
|
33961
33981
|
h ^= text2.charCodeAt(i);
|
|
33962
33982
|
h = Math.imul(h, 16777619) >>> 0;
|
|
@@ -37195,8 +37215,8 @@ function isSecretField(inputType, autocomplete, hints) {
|
|
|
37195
37215
|
const raw = list2[i];
|
|
37196
37216
|
if (typeof raw !== "string" || raw === "")
|
|
37197
37217
|
continue;
|
|
37198
|
-
const
|
|
37199
|
-
if (credential.test(
|
|
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))
|
|
37200
37220
|
return true;
|
|
37201
37221
|
}
|
|
37202
37222
|
return false;
|
|
@@ -38621,8 +38641,8 @@ async function readSnapshot(page, opts) {
|
|
|
38621
38641
|
// ../explore/dist/settle-config.js
|
|
38622
38642
|
var DEFAULT_LONG_POLL_MS = 5e3;
|
|
38623
38643
|
function globToRegExp2(glob) {
|
|
38624
|
-
const
|
|
38625
|
-
return new RegExp(`^${
|
|
38644
|
+
const escaped2 = glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
38645
|
+
return new RegExp(`^${escaped2}$`);
|
|
38626
38646
|
}
|
|
38627
38647
|
function urlMatcher(patterns) {
|
|
38628
38648
|
const compiled = (patterns ?? []).map((p) => ({ full: p.includes("://"), re: globToRegExp2(p) }));
|
|
@@ -39557,6 +39577,31 @@ function visibleBusyIndicatorContainer() {
|
|
|
39557
39577
|
}
|
|
39558
39578
|
return null;
|
|
39559
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
|
+
}
|
|
39560
39605
|
function pendingEvidence(pending, now2) {
|
|
39561
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) }));
|
|
39562
39607
|
}
|
|
@@ -40808,7 +40853,8 @@ async function perceive(page, opts = {}) {
|
|
|
40808
40853
|
const busySince = clock.now();
|
|
40809
40854
|
const remaining = Math.max(1, ceiling - (clock.now() - started));
|
|
40810
40855
|
const gone = await clockBounded(page.waitForFunction(visibleBusyIndicator, true, { timeout: remaining, polling: "raf" }).then(() => true, (e) => !(e instanceof Error && e.name === "TimeoutError")), remaining, false);
|
|
40811
|
-
|
|
40856
|
+
const busyText = gone ? null : await page.evaluate(visibleBusyIndicatorText).catch(() => null);
|
|
40857
|
+
if (!gone && !ignoreNoProgress(busy) && !(busyText !== null && ignoreDeclared(busyText)) && !ignoreNoProgress(hangRoute(page.url()))) {
|
|
40812
40858
|
stuckBusy = busy;
|
|
40813
40859
|
stuckBusyContainer = await page.evaluate(visibleBusyIndicatorContainer).catch(() => null);
|
|
40814
40860
|
const now2 = await page.evaluate(visibleBusyIndicator).catch(() => null);
|
|
@@ -42172,6 +42218,7 @@ async function judgeGoalCompletion(judge, input2) {
|
|
|
42172
42218
|
...input2.signInFacts === void 0 ? [] : [`SIGN-IN (observed by code, trusted): ${input2.signInFacts}`],
|
|
42173
42219
|
...input2.saveFacts === void 0 ? [] : [`SAVE (observed by code, trusted): ${input2.saveFacts}`],
|
|
42174
42220
|
...input2.writeFacts === void 0 ? [] : [`WRITES (observed by code, trusted): ${input2.writeFacts}`],
|
|
42221
|
+
...input2.readBackFacts === void 0 ? [] : [`READ-BACK (observed by code, trusted): ${input2.readBackFacts}`],
|
|
42175
42222
|
...input2.pageStatus === void 0 || input2.pageStatus === "" ? [] : [`PAGE STATUS (untrusted): ${input2.pageStatus}`],
|
|
42176
42223
|
`VISIBLE PAGE TEXT (untrusted): ${input2.pageText.replace(/\s+/g, " ").slice(0, GOAL_TEXT_CHARS)}`,
|
|
42177
42224
|
...input2.fieldValues === void 0 || input2.fieldValues.length === 0 ? [] : [
|
|
@@ -44090,6 +44137,7 @@ var MAX_REPEAT_TYPE_SIGNALS = 3;
|
|
|
44090
44137
|
var MAX_QUIET_WAITS = 3;
|
|
44091
44138
|
var MAX_MOVING_SCROLLS = 12;
|
|
44092
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";
|
|
44093
44141
|
|
|
44094
44142
|
// ../explore/dist/run-depth.js
|
|
44095
44143
|
var OPEN_ENDED_MIN_ACTIONS = 12;
|
|
@@ -44964,11 +45012,11 @@ function hostProbe(signals = createResourceSignals(), thresholds = DEFAULT_PRESS
|
|
|
44964
45012
|
function initHost(ctx, cfg) {
|
|
44965
45013
|
ctx.probeHost = cfg.hostProbe ?? hostProbe();
|
|
44966
45014
|
ctx.judgeHost = async () => cfg.hostHealth === void 0 ? { host: await ctx.probeHost(), starved: null } : cfg.hostHealth.judge();
|
|
44967
|
-
ctx.degradedStop = (
|
|
44968
|
-
cfg.hostHealth?.markDegraded({ finding:
|
|
45015
|
+
ctx.degradedStop = (finding4, detail, starved) => {
|
|
45016
|
+
cfg.hostHealth?.markDegraded({ finding: finding4, detail, step: Math.max(0, ctx.transcript.entries().length - 1) }, starved);
|
|
44969
45017
|
ctx.failure = {
|
|
44970
45018
|
kind: "degraded-environment",
|
|
44971
|
-
message: `environment-degraded ${
|
|
45019
|
+
message: `environment-degraded ${finding4} (${detail}) while the host was starved: ${starved} \u2014 not an app finding`
|
|
44972
45020
|
};
|
|
44973
45021
|
ctx.stop = "inconclusive";
|
|
44974
45022
|
};
|
|
@@ -45379,6 +45427,7 @@ function initLoopState(ctx) {
|
|
|
45379
45427
|
ctx.fixtureAttached = false;
|
|
45380
45428
|
ctx.hang = void 0;
|
|
45381
45429
|
ctx.outcome = null;
|
|
45430
|
+
ctx.actionableControl = null;
|
|
45382
45431
|
ctx.incomplete = null;
|
|
45383
45432
|
ctx.endedOnRejectedDone = false;
|
|
45384
45433
|
}
|
|
@@ -45607,6 +45656,7 @@ var OBSERVED_PAGE_CHARS = 2e4;
|
|
|
45607
45656
|
var MIN_QUOTE_CHARS = 3;
|
|
45608
45657
|
var MAX_OBSERVED_FIELDS = 40;
|
|
45609
45658
|
var OBSERVED_FIELD_CHARS = 500;
|
|
45659
|
+
var MAX_READ_BACK_VALUES = 50;
|
|
45610
45660
|
var MAX_OBSERVED_CONTROLS = 120;
|
|
45611
45661
|
var HEADING_CHARS = 200;
|
|
45612
45662
|
function controlFields(controls) {
|
|
@@ -45696,6 +45746,21 @@ var ObservedPages = class {
|
|
|
45696
45746
|
this.#sourceRoute.set(v, pending.route);
|
|
45697
45747
|
}
|
|
45698
45748
|
}
|
|
45749
|
+
/**
|
|
45750
|
+
* #671: values the run typed into a field on one document load. A field holding one shows the run's
|
|
45751
|
+
* own entry, never what the app kept: retyping a sent message into its composer after a reload is
|
|
45752
|
+
* not a read-back.
|
|
45753
|
+
*/
|
|
45754
|
+
#typedOn = null;
|
|
45755
|
+
/** #671: the run typed `value` into a field (a message composer too) on document load `document`. */
|
|
45756
|
+
noteTypedOn(value, document2) {
|
|
45757
|
+
const v = fold(redactContext(value, this.secrets).slice(0, OBSERVED_FIELD_CHARS));
|
|
45758
|
+
if (document2 === void 0 || v === "")
|
|
45759
|
+
return;
|
|
45760
|
+
if (this.#typedOn?.document !== document2)
|
|
45761
|
+
this.#typedOn = { document: document2, values: /* @__PURE__ */ new Set() };
|
|
45762
|
+
this.#typedOn.values.add(v);
|
|
45763
|
+
}
|
|
45699
45764
|
/** #647: the run's sent values (folded) that no page loaded from the server since has shown. */
|
|
45700
45765
|
unreadWrites() {
|
|
45701
45766
|
return this.#unread;
|
|
@@ -45720,10 +45785,19 @@ var ObservedPages = class {
|
|
|
45720
45785
|
this.#sourceRoute.delete(v);
|
|
45721
45786
|
}
|
|
45722
45787
|
this.#readBackCount += back.length;
|
|
45788
|
+
for (const v of back)
|
|
45789
|
+
if (this.#readBackValues.length < MAX_READ_BACK_VALUES)
|
|
45790
|
+
this.#readBackValues.push(v);
|
|
45723
45791
|
return back;
|
|
45724
45792
|
}
|
|
45725
45793
|
/** #645: how many sent values a page loaded from the server has shown again. */
|
|
45726
45794
|
#readBackCount = 0;
|
|
45795
|
+
/** #667: the sent values (folded) read back so far, bounded. */
|
|
45796
|
+
#readBackValues = [];
|
|
45797
|
+
/** #667: the sent values (folded) a page loaded from the server has shown again. */
|
|
45798
|
+
readBackValues() {
|
|
45799
|
+
return this.#readBackValues;
|
|
45800
|
+
}
|
|
45727
45801
|
/**
|
|
45728
45802
|
* #645: the app was seen to keep what the run's writes sent — at least one value was read back and
|
|
45729
45803
|
* none is still unread. Code evidence the page judgment lacks (see `groundDone`).
|
|
@@ -45766,7 +45840,9 @@ var ObservedPages = class {
|
|
|
45766
45840
|
this.#topNav = nav;
|
|
45767
45841
|
}
|
|
45768
45842
|
this.#settleSent(page.text);
|
|
45769
|
-
this.
|
|
45843
|
+
const typed = this.#typedOn !== null && this.#typedOn.document === headings2.document ? [...this.#typedOn.values] : [];
|
|
45844
|
+
const appFields = kept.map((f2) => f2.value).filter((value) => !typed.some((t) => fold(value).includes(t)));
|
|
45845
|
+
this.readBack(headings2.document, [page.text, ...appFields].join("\n"), url2);
|
|
45770
45846
|
if (page.text.trim() === "" && kept.length === 0)
|
|
45771
45847
|
return;
|
|
45772
45848
|
const same = (p) => JSON.stringify(p.fields ?? []) === JSON.stringify(page.fields ?? []) && p.heading === page.heading && p.title === page.title && p.status === page.status && JSON.stringify(p.controls ?? []) === JSON.stringify(page.controls ?? []) && JSON.stringify(p.contentLinks ?? []) === JSON.stringify(page.contentLinks ?? []) && p.inventoryIncomplete === page.inventoryIncomplete;
|
|
@@ -46431,7 +46507,7 @@ var ABSENCE_GOAL = /\bwhether\b|\b(?:is|are) there\b|\bthere (?:is|are) (?:no|no
|
|
|
46431
46507
|
function goalAdmitsAbsence(goal) {
|
|
46432
46508
|
return ABSENCE_GOAL.test(goal);
|
|
46433
46509
|
}
|
|
46434
|
-
var WRITE_GOAL = /(?:^\s*|[.!?;:]\s+|\b(?:then|and|please)\s+)(?:record|save|create|add|invite|submit|send|post|publish|register|book|schedule)\b/i;
|
|
46510
|
+
var WRITE_GOAL = /(?:^\s*|[.!?;:]\s+|\b(?:then|and|please)\s+)(?:record|save|create|add|invite|submit|send|post|publish|register|book|schedule|import|upload|update|change|edit|rename|delete|remove)\b/i;
|
|
46435
46511
|
function goalAsksToWrite(goal) {
|
|
46436
46512
|
return WRITE_GOAL.test(goal);
|
|
46437
46513
|
}
|
|
@@ -46718,7 +46794,7 @@ function busyIndicatorOf(hang) {
|
|
|
46718
46794
|
return null;
|
|
46719
46795
|
if (hang.element !== void 0 && hang.element !== "")
|
|
46720
46796
|
return hang.element;
|
|
46721
|
-
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());
|
|
46722
46798
|
return m?.[1] ?? null;
|
|
46723
46799
|
}
|
|
46724
46800
|
function legacySelectorForm(indicator) {
|
|
@@ -46939,8 +47015,8 @@ async function recordCoverageHang(p) {
|
|
|
46939
47015
|
...p.perceive === void 0 ? {} : { perceive: p.perceive },
|
|
46940
47016
|
...p.safety === void 0 ? {} : { safety: p.safety }
|
|
46941
47017
|
});
|
|
46942
|
-
const
|
|
46943
|
-
p.found.set(fingerprint2, { ...
|
|
47018
|
+
const finding4 = hangFinding(hang, p.steps, index, reproduction);
|
|
47019
|
+
p.found.set(fingerprint2, { ...finding4, repro: { ...finding4.repro, recording: p.recording } });
|
|
46944
47020
|
return "recorded";
|
|
46945
47021
|
}
|
|
46946
47022
|
|
|
@@ -48130,6 +48206,7 @@ function initGuards(ctx, cfg) {
|
|
|
48130
48206
|
ctx.jobWaitedMs = 0;
|
|
48131
48207
|
ctx.overlayWaitedMs = 0;
|
|
48132
48208
|
ctx.hangWorkWaitedMs = 0;
|
|
48209
|
+
ctx.hangWorkSince = null;
|
|
48133
48210
|
ctx.documentedBudgetMs = 0;
|
|
48134
48211
|
}
|
|
48135
48212
|
|
|
@@ -48275,6 +48352,7 @@ function newStep(ctx, input2) {
|
|
|
48275
48352
|
if (proposedDone)
|
|
48276
48353
|
writes = jobWritesOf(ctx);
|
|
48277
48354
|
const writeFacts = writes !== void 0 && saved === null && writesAllSucceeded(writes) ? writes.facts : void 0;
|
|
48355
|
+
const readBackFacts = ctx.observed.readBackConfirmed() ? readBackFactsOf(ctx.observed.readBackValues(), pageText) : void 0;
|
|
48278
48356
|
const judged = await judgeGoalCompletion(cfg.judge, {
|
|
48279
48357
|
goal: cfg.goal,
|
|
48280
48358
|
url: snap.url,
|
|
@@ -48285,6 +48363,7 @@ function newStep(ctx, input2) {
|
|
|
48285
48363
|
...signIn === null ? {} : { signInFacts: signIn.facts },
|
|
48286
48364
|
...saved === null ? {} : { saveFacts: saved.facts },
|
|
48287
48365
|
...writeFacts === void 0 ? {} : { writeFacts },
|
|
48366
|
+
...readBackFacts === void 0 ? {} : { readBackFacts },
|
|
48288
48367
|
fieldValues: fieldValuesOf(snap.controls, ctx.isBound)
|
|
48289
48368
|
}).catch(() => ({ goalMet: null, goalIsSignIn: null, goalIsSave: null, goalIsWrites: null }));
|
|
48290
48369
|
goalMet = judged.goalMet;
|
|
@@ -48315,6 +48394,11 @@ function newStep(ctx, input2) {
|
|
|
48315
48394
|
};
|
|
48316
48395
|
return { ...input2, record: record2, signIn, pendingNote, acceptedBy, groundGoal };
|
|
48317
48396
|
}
|
|
48397
|
+
function readBackFactsOf(values2, pageText) {
|
|
48398
|
+
const text2 = pageText.toLowerCase();
|
|
48399
|
+
const shown2 = values2.filter((v) => text2.includes(v)).length;
|
|
48400
|
+
return `every value this run's writes sent (${values2.length}) was shown again on a page loaded from the server after the write \u2014 the app kept them; this page shows ${shown2} of them`;
|
|
48401
|
+
}
|
|
48318
48402
|
|
|
48319
48403
|
// ../explore/dist/goal-loop/min-effort.js
|
|
48320
48404
|
function deferEnding(ctx, step, ending) {
|
|
@@ -48363,87 +48447,8 @@ function unreadShown(ctx, step) {
|
|
|
48363
48447
|
return ctx.observed.unreadShownIn(shownOn(step));
|
|
48364
48448
|
}
|
|
48365
48449
|
|
|
48366
|
-
// ../explore/dist/goal-loop/handle-report.js
|
|
48367
|
-
async function handleReport(ctx, step) {
|
|
48368
|
-
const { cfg } = ctx;
|
|
48369
|
-
const { snap, record: record2 } = step;
|
|
48370
|
-
if (ctx.replyGoal) {
|
|
48371
|
-
if (ctx.awaitingReply && ctx.lastTurn !== null && ctx.replyWaitedMs < ctx.replyWaitMs) {
|
|
48372
|
-
const t0 = ctx.now();
|
|
48373
|
-
const listen = ctx.replyWaitMs - ctx.replyWaitedMs;
|
|
48374
|
-
const reply = await waitForReply(ctx.page, { secrets: ctx.secrets, ...ctx.lastTurn, timeoutMs: listen, ceilingMs: listen, quietMs: ctx.replyQuietMs });
|
|
48375
|
-
ctx.replyWaitedMs += ctx.now() - t0;
|
|
48376
|
-
if (reply.received) {
|
|
48377
|
-
ctx.conversation.latestReply = reply.text;
|
|
48378
|
-
ctx.replies.add(snap.url, reply.text);
|
|
48379
|
-
ctx.awaitingReply = false;
|
|
48380
|
-
ctx.busyWaitedMs = 0;
|
|
48381
|
-
ctx.history.push(`waited for the reply \u2192 reply: ${quote(reply.text, 300)}`);
|
|
48382
|
-
}
|
|
48383
|
-
}
|
|
48384
|
-
ctx.noteReplyText(snap.url, await readPageText(ctx.page, ctx.secrets));
|
|
48385
|
-
}
|
|
48386
|
-
const replyPages = ctx.replyGoal ? ctx.replies.pages() : null;
|
|
48387
|
-
if (replyPages === null)
|
|
48388
|
-
await readBackProbe(ctx, step);
|
|
48389
|
-
const verdict = replyPages !== null && replyPages.length === 0 ? {
|
|
48390
|
-
accept: false,
|
|
48391
|
-
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)`,
|
|
48392
|
-
answer: null
|
|
48393
|
-
} : await reportAnswer(cfg.gen, {
|
|
48394
|
-
goal: cfg.goal,
|
|
48395
|
-
url: snap.url,
|
|
48396
|
-
pages: replyPages ?? ctx.observed.pages(),
|
|
48397
|
-
history: ctx.history,
|
|
48398
|
-
secrets: ctx.secrets,
|
|
48399
|
-
judge: cfg.judge,
|
|
48400
|
-
vetoes: ctx.vetoes,
|
|
48401
|
-
// #238: "none exists" is an answer only on observed pages that cover the app enough.
|
|
48402
|
-
...replyPages === null ? { topNav: ctx.observed.topNavigation(), ownInputs: ctx.observed.ownInputs(), unreadWrites: ctx.observed.unreadWrites() } : {}
|
|
48403
|
-
}).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 }));
|
|
48404
|
-
if (verdict.accept) {
|
|
48405
|
-
const deferred = deferEnding(ctx, step, "report");
|
|
48406
|
-
if (deferred !== null) {
|
|
48407
|
-
ctx.deferredAnswer = verdict.answer;
|
|
48408
|
-
record2(false, deferred, { answer: verdict.answer, origin: "engine" });
|
|
48409
|
-
return "continue";
|
|
48410
|
-
}
|
|
48411
|
-
}
|
|
48412
|
-
if (verdict.accept) {
|
|
48413
|
-
const on = replyPages === null ? "the observed pages" : "the reply observed after the send";
|
|
48414
|
-
record2(true, `report accepted: answer grounded on ${on} (${verdict.answer.evidence.length} claim(s))`, {
|
|
48415
|
-
answer: verdict.answer
|
|
48416
|
-
});
|
|
48417
|
-
ctx.answer = verdict.answer;
|
|
48418
|
-
ctx.outcome = { status: "completed", verifiedBy: "grounded-answer" };
|
|
48419
|
-
ctx.stop = "done";
|
|
48420
|
-
return "stop";
|
|
48421
|
-
}
|
|
48422
|
-
if (verdict.unconfirmedWrite === true && !ctx.readBackAsked) {
|
|
48423
|
-
ctx.readBackAsked = true;
|
|
48424
|
-
ctx.history.push(`report deferred: ${verdict.reason}`);
|
|
48425
|
-
record2(false, `report deferred: ${verdict.reason}`, { answer: verdict.answer, origin: "engine" });
|
|
48426
|
-
return "continue";
|
|
48427
|
-
}
|
|
48428
|
-
ctx.reportRejections += 1;
|
|
48429
|
-
for (const e of verdict.answer?.evidence ?? [])
|
|
48430
|
-
if (e.grounded)
|
|
48431
|
-
ctx.reportClaims.push(e);
|
|
48432
|
-
ctx.lastReportNotFound = verdict.answer === null && verdict.reason === NO_ANSWER_REASON || verdict.notAnswer === true;
|
|
48433
|
-
ctx.lastAbsenceUncovered = verdict.absenceUncovered === true ? verdict.reason : null;
|
|
48434
|
-
ctx.history.push(`report rejected: ${verdict.reason} \u2014 find the answer on the page before reporting`);
|
|
48435
|
-
record2(false, `report rejected (${ctx.reportRejections}/${MAX_REPORT_REJECTIONS}): ${verdict.reason}`, {
|
|
48436
|
-
answer: verdict.answer
|
|
48437
|
-
});
|
|
48438
|
-
if (ctx.reportRejections >= MAX_REPORT_REJECTIONS && deferEnding(ctx, step, "report") === null) {
|
|
48439
|
-
ctx.incomplete = `the model reported an answer ${ctx.reportRejections} times, but ${verdict.reason}`;
|
|
48440
|
-
ctx.stop = "blocked";
|
|
48441
|
-
return "stop";
|
|
48442
|
-
}
|
|
48443
|
-
return "continue";
|
|
48444
|
-
}
|
|
48445
|
-
|
|
48446
48450
|
// ../explore/dist/goal-loop/handle-done.js
|
|
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)";
|
|
48447
48452
|
async function unreadWriteHold(ctx, step, outcome) {
|
|
48448
48453
|
if (outcome.status === "completed" && outcome.verifiedBy === "success-condition")
|
|
48449
48454
|
return null;
|
|
@@ -48535,12 +48540,13 @@ async function handleDone(ctx, step) {
|
|
|
48535
48540
|
}
|
|
48536
48541
|
}
|
|
48537
48542
|
ctx.doneRejections += 1;
|
|
48538
|
-
ctx.
|
|
48539
|
-
|
|
48543
|
+
const why = ctx.writeGoal && !ctx.wroteOk ? `${verdict.reason}; ${NO_WRITE_YET}` : verdict.reason;
|
|
48544
|
+
ctx.history.push(`done rejected: ${why} \u2014 keep working toward the goal`);
|
|
48545
|
+
record2(false, `done rejected (${ctx.doneRejections}/${MAX_DONE_REJECTIONS}): ${why}`, {
|
|
48540
48546
|
...judgments === void 0 ? {} : { judgments }
|
|
48541
48547
|
});
|
|
48542
48548
|
if (ctx.doneRejections >= MAX_DONE_REJECTIONS) {
|
|
48543
|
-
ctx.incomplete = `the model proposed done ${ctx.doneRejections} times, but ${
|
|
48549
|
+
ctx.incomplete = `the model proposed done ${ctx.doneRejections} times, but ${why}`;
|
|
48544
48550
|
ctx.endedOnRejectedDone = true;
|
|
48545
48551
|
ctx.stop = "done";
|
|
48546
48552
|
return "stop";
|
|
@@ -48550,7 +48556,146 @@ async function handleDone(ctx, step) {
|
|
|
48550
48556
|
return "next";
|
|
48551
48557
|
}
|
|
48552
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
|
+
|
|
48553
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
|
+
}
|
|
48554
48699
|
async function handleBlocked(ctx, step) {
|
|
48555
48700
|
const { modelControls, record: record2 } = step;
|
|
48556
48701
|
const job = await readInProgressStatus(ctx.page);
|
|
@@ -48600,16 +48745,221 @@ async function handleBlocked(ctx, step) {
|
|
|
48600
48745
|
ctx.stop = "inconclusive";
|
|
48601
48746
|
return "stop";
|
|
48602
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
|
+
}
|
|
48603
48765
|
const deferred = deferEnding(ctx, step, "blocked");
|
|
48604
48766
|
if (deferred !== null) {
|
|
48605
48767
|
record2(false, deferred, { origin: "engine" });
|
|
48606
48768
|
return "continue";
|
|
48607
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
|
+
}
|
|
48608
48780
|
record2(true, "model blocked");
|
|
48609
|
-
|
|
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}`;
|
|
48610
48794
|
ctx.stop = "blocked";
|
|
48611
48795
|
return "stop";
|
|
48612
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
|
+
}
|
|
48613
48963
|
|
|
48614
48964
|
// ../explore/dist/goal-loop/handle-idle.js
|
|
48615
48965
|
async function handleWaitOrScroll(ctx, step) {
|
|
@@ -48922,6 +49272,7 @@ async function handleCodeTypedField(ctx, step) {
|
|
|
48922
49272
|
ctx.valueLog.typed(control.name || control.summary, value);
|
|
48923
49273
|
ctx.save.noteTyped(control.name || control.summary, value);
|
|
48924
49274
|
ctx.observed.noteOwnInput(value);
|
|
49275
|
+
ctx.observed.noteTypedOn(value, await documentOf(ctx.page));
|
|
48925
49276
|
ctx.noteMutation(`type ${control.name}`, control.descriptor, snap.signature, at3, { field: keyOf(control), value });
|
|
48926
49277
|
ctx.tracker.countAction();
|
|
48927
49278
|
ctx.cleared(control);
|
|
@@ -49484,6 +49835,7 @@ async function handleFill(ctx, step) {
|
|
|
49484
49835
|
ctx.typeProbe = { key: keyOf(control), label: quote(control.name || control.summary, 60), at: typedAt, background: typedBackground, state: typedState };
|
|
49485
49836
|
ctx.recorder.fill(control.descriptor, text2, at3);
|
|
49486
49837
|
ctx.valueLog.typed(control.name || control.summary, text2);
|
|
49838
|
+
ctx.observed.noteTypedOn(text2, await documentOf(ctx.page));
|
|
49487
49839
|
if (!ctx.isBound(control) && !isCredentialField(control) && !sendable(control)) {
|
|
49488
49840
|
ctx.save.noteTyped(control.name || control.summary, text2);
|
|
49489
49841
|
ctx.observed.noteOwnInput(text2);
|
|
@@ -49605,6 +49957,21 @@ async function statusWhilePending(page) {
|
|
|
49605
49957
|
const status = await readInProgressStatus(page);
|
|
49606
49958
|
return status === null ? null : `${status} while its request is in flight (within --job-wait-ms)`;
|
|
49607
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
|
+
}
|
|
49608
49975
|
async function checkHang(ctx, step) {
|
|
49609
49976
|
const { perceiveStartedAt, perception, snap } = step;
|
|
49610
49977
|
const documented = perception.hang !== null && perception.hang.kind !== "main-thread-unresponsive" ? await readDocumentedWait(ctx.page) : null;
|
|
@@ -49615,8 +49982,9 @@ async function checkHang(ctx, step) {
|
|
|
49615
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…")
|
|
49616
49983
|
// is the long job the operator sized with --job-wait-ms: waited out within that budget. Without
|
|
49617
49984
|
// the flag a bare status is not enough (a stuck page looks exactly like that, #153).
|
|
49618
|
-
(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);
|
|
49619
49986
|
if (working !== null) {
|
|
49987
|
+
ctx.hangWorkSince ??= perceiveStartedAt;
|
|
49620
49988
|
const w = await waitOutJob(ctx.page, Math.min(workBudgetMs - ctx.hangWorkWaitedMs, JOB_WAIT_SLICE_MS), stillShowsWork);
|
|
49621
49989
|
ctx.hangWorkWaitedMs += clock.now() - perceiveStartedAt;
|
|
49622
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`})`;
|
|
@@ -49656,7 +50024,7 @@ async function checkHang(ctx, step) {
|
|
|
49656
50024
|
const heapNow = await sampleHeap(ctx.page, 1e3);
|
|
49657
50025
|
const stood = ctx.hangWorkWaitedMs > 0 ? {
|
|
49658
50026
|
...perception.hang,
|
|
49659
|
-
detail: `${perception.hang.detail} (
|
|
50027
|
+
detail: `${perception.hang.detail} (${stoodAfterWork(ctx, workBudgetMs)})`
|
|
49660
50028
|
} : perception.hang;
|
|
49661
50029
|
const withHost = { ...stood, host: judged.host };
|
|
49662
50030
|
ctx.hang = {
|
|
@@ -49933,7 +50301,7 @@ async function checkProgress(ctx, step) {
|
|
|
49933
50301
|
if (!ctx.lastChanceGiven) {
|
|
49934
50302
|
ctx.lastChanceGiven = true;
|
|
49935
50303
|
ctx.lastChanceTurn = true;
|
|
49936
|
-
ctx.history.push(LAST_CHANCE_NOTE);
|
|
50304
|
+
ctx.history.push(ctx.writeGoal && !ctx.wroteOk ? LAST_CHANCE_NO_WRITE_NOTE : LAST_CHANCE_NOTE);
|
|
49937
50305
|
} else {
|
|
49938
50306
|
ctx.stop = "no-progress";
|
|
49939
50307
|
return "stop";
|
|
@@ -50137,7 +50505,12 @@ async function viewPage(ctx, step) {
|
|
|
50137
50505
|
async function decideStep(ctx, step, view) {
|
|
50138
50506
|
const { cfg } = ctx;
|
|
50139
50507
|
const { perception, snap } = step;
|
|
50140
|
-
const {
|
|
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");
|
|
50141
50514
|
let decision;
|
|
50142
50515
|
ctx.goalFocus.noteUrl(snap.url);
|
|
50143
50516
|
const offGoal = ctx.goalFocus.offGoal(modelControls, ctx.chrome);
|
|
@@ -50388,6 +50761,7 @@ async function finishRun(ctx) {
|
|
|
50388
50761
|
...ctx.answer !== void 0 && finalOutcome.status === "completed" ? { answer: ctx.answer } : {},
|
|
50389
50762
|
...cause === null ? {} : { blockingCause: cause },
|
|
50390
50763
|
...missCause === void 0 ? {} : { missCause },
|
|
50764
|
+
...ctx.actionableControl === null ? {} : { actionableControl: ctx.actionableControl },
|
|
50391
50765
|
...ctx.endedOnRejectedDone && ctx.stop === "done" ? { doneRejected: true } : {},
|
|
50392
50766
|
...ctx.stop === "crashed" && ctx.failure !== void 0 ? { crash: buildCrashReport(ctx.failure, ctx.crashWatch.signals(), ctx.heap.samples(), { host: await ctx.probeHost() }) } : {}
|
|
50393
50767
|
};
|
|
@@ -52443,7 +52817,7 @@ async function reproduceSeedHang(cfg, run, h) {
|
|
|
52443
52817
|
});
|
|
52444
52818
|
}
|
|
52445
52819
|
function hangResult(run, h, reproduction) {
|
|
52446
|
-
const
|
|
52820
|
+
const finding4 = hangFinding(h.signal, run.transcript, h.recordingStepIndex, reproduction);
|
|
52447
52821
|
return {
|
|
52448
52822
|
// A hang whose replays could not run at all is `inconclusive`, never a non-reproduction.
|
|
52449
52823
|
outcome: reproduction.status === "reproduced" ? "hang" : reproduction.status,
|
|
@@ -52453,8 +52827,8 @@ function hangResult(run, h, reproduction) {
|
|
|
52453
52827
|
recording: run.recording,
|
|
52454
52828
|
transcript: run.transcript,
|
|
52455
52829
|
finalUrl: run.finalUrl,
|
|
52456
|
-
hang:
|
|
52457
|
-
reason: reproduction.withheld !== void 0 ? `${
|
|
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})`
|
|
52458
52832
|
};
|
|
52459
52833
|
}
|
|
52460
52834
|
|
|
@@ -53033,14 +53407,14 @@ function ownAssertion(s3) {
|
|
|
53033
53407
|
return { field: "resume", assertion: s3.resume };
|
|
53034
53408
|
return "expect" in s3 ? { field: "expect", assertion: s3.expect } : null;
|
|
53035
53409
|
}
|
|
53036
|
-
function
|
|
53410
|
+
function canonical(d) {
|
|
53037
53411
|
const sort = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).sort(([a], [b2]) => a.localeCompare(b2)).map(([k, x]) => [k, sort(x)])) : v;
|
|
53038
53412
|
return JSON.stringify(sort(d));
|
|
53039
53413
|
}
|
|
53040
53414
|
function isOwnTargetVisible(step) {
|
|
53041
53415
|
if (!("expect" in step) || !("target" in step))
|
|
53042
53416
|
return false;
|
|
53043
|
-
return step.expect.kind === "visible" &&
|
|
53417
|
+
return step.expect.kind === "visible" && canonical(step.expect.target) === canonical(step.target);
|
|
53044
53418
|
}
|
|
53045
53419
|
function isNoClaimExpect(step) {
|
|
53046
53420
|
if (!("expect" in step))
|
|
@@ -54930,10 +55304,10 @@ function bindParams(inputs, record2, request) {
|
|
|
54930
55304
|
return params;
|
|
54931
55305
|
}
|
|
54932
55306
|
function recordFingerprint(outputs, record2) {
|
|
54933
|
-
const
|
|
55307
|
+
const canonical6 = {};
|
|
54934
55308
|
for (const name of Object.keys(outputs.fields).sort())
|
|
54935
|
-
|
|
54936
|
-
return `sha256:${createHash5("sha256").update(JSON.stringify(
|
|
55309
|
+
canonical6[name] = record2[name] ?? null;
|
|
55310
|
+
return `sha256:${createHash5("sha256").update(JSON.stringify(canonical6)).digest("hex")}`;
|
|
54937
55311
|
}
|
|
54938
55312
|
function extractedNames(journey) {
|
|
54939
55313
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -58073,23 +58447,23 @@ function createFrontierContext(params, declared, safety, budget, overlay = null,
|
|
|
58073
58447
|
const vp = ctx.sessions.page.viewportSize();
|
|
58074
58448
|
if (!shouldCheckOverflow(vp?.width, params.overflow?.checkOverflow ?? false))
|
|
58075
58449
|
return;
|
|
58076
|
-
const
|
|
58450
|
+
const finding4 = await detectOverflow(ctx.sessions.page, {
|
|
58077
58451
|
viewport: vp ?? { width: 1280, height: 720 },
|
|
58078
58452
|
...params.overflow?.device === void 0 ? {} : { device: params.overflow.device },
|
|
58079
58453
|
...params.overflow?.toleranceCss === void 0 ? {} : { toleranceCss: params.overflow.toleranceCss },
|
|
58080
58454
|
...params.overflow?.ignoreSelectors === void 0 ? {} : { ignoreSelectors: params.overflow.ignoreSelectors },
|
|
58081
58455
|
...params.overflow?.secrets === void 0 ? {} : { secrets: params.overflow.secrets }
|
|
58082
58456
|
});
|
|
58083
|
-
if (
|
|
58084
|
-
ctx.seenOverflow.add(
|
|
58457
|
+
if (finding4 !== null && !ctx.seenOverflow.has(finding4.fingerprint)) {
|
|
58458
|
+
ctx.seenOverflow.add(finding4.fingerprint);
|
|
58085
58459
|
ctx.defects.push({
|
|
58086
|
-
fingerprint:
|
|
58460
|
+
fingerprint: finding4.fingerprint,
|
|
58087
58461
|
kind: "horizontal-overflow",
|
|
58088
58462
|
stateFingerprint: stateFp,
|
|
58089
58463
|
url: url2,
|
|
58090
|
-
reason: `horizontal-overflow: ${
|
|
58464
|
+
reason: `horizontal-overflow: ${finding4.element.descriptor} overflows the ${finding4.viewport.width}px viewport by ${finding4.overflowPx}px at ${finding4.route}`,
|
|
58091
58465
|
recording,
|
|
58092
|
-
overflow:
|
|
58466
|
+
overflow: finding4
|
|
58093
58467
|
});
|
|
58094
58468
|
}
|
|
58095
58469
|
const clipped = await detectClipping(ctx.sessions.page, {
|
|
@@ -60660,22 +61034,22 @@ function installOracle(ctx, params) {
|
|
|
60660
61034
|
url: c.url,
|
|
60661
61035
|
descriptor: c.element.descriptor
|
|
60662
61036
|
}));
|
|
60663
|
-
const
|
|
61037
|
+
const finding4 = await detectOverflow(ctx.sessions.page, {
|
|
60664
61038
|
viewport: vp ?? { width: 1280, height: 720 },
|
|
60665
61039
|
...params.overflow?.device === void 0 ? {} : { device: params.overflow.device },
|
|
60666
61040
|
...params.overflow?.toleranceCss === void 0 ? {} : { toleranceCss: params.overflow.toleranceCss },
|
|
60667
61041
|
...params.overflow?.ignoreSelectors === void 0 ? {} : { ignoreSelectors: params.overflow.ignoreSelectors },
|
|
60668
61042
|
...params.overflow?.secrets === void 0 ? {} : { secrets: params.overflow.secrets }
|
|
60669
61043
|
});
|
|
60670
|
-
if (
|
|
61044
|
+
if (finding4 === null)
|
|
60671
61045
|
return clipped;
|
|
60672
61046
|
const horizontal = {
|
|
60673
61047
|
kind: "horizontal-overflow",
|
|
60674
|
-
detail: `horizontal-overflow: ${
|
|
60675
|
-
overflowPx:
|
|
60676
|
-
route:
|
|
60677
|
-
url:
|
|
60678
|
-
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
|
|
60679
61053
|
};
|
|
60680
61054
|
return [horizontal, ...clipped];
|
|
60681
61055
|
};
|
|
@@ -60844,9 +61218,9 @@ function installOracle(ctx, params) {
|
|
|
60844
61218
|
}
|
|
60845
61219
|
const summary2 = f2.kind === "invariant" ? f2.invariantReason ?? f2.title : f2.kind === "markup-injection" ? f2.title : f2.signals.map((s3) => s3.detail).join("; ");
|
|
60846
61220
|
const triage = await tryTriage(params.generation, { failureSummary: summary2, url: f2.url });
|
|
60847
|
-
const { origin: _origin, ...
|
|
61221
|
+
const { origin: _origin, ...finding4 } = f2;
|
|
60848
61222
|
ctx.defects.set(f2.fingerprint, {
|
|
60849
|
-
...
|
|
61223
|
+
...finding4,
|
|
60850
61224
|
related: new Set(f2.related),
|
|
60851
61225
|
epoch: ctx.segments.indexOf(ctx.recorder),
|
|
60852
61226
|
firstSeenStep: step,
|
|
@@ -60924,9 +61298,9 @@ function installSessions(ctx, params) {
|
|
|
60924
61298
|
perceive: ctx.perceiveOpts,
|
|
60925
61299
|
...params.safety === void 0 ? {} : { safety: params.safety }
|
|
60926
61300
|
});
|
|
60927
|
-
const
|
|
61301
|
+
const finding4 = hangFinding(h, ctx.transcript.entries(), recordingStepIndex, reproduction);
|
|
60928
61302
|
const segment = ctx.segments.indexOf(ctx.recorder);
|
|
60929
|
-
ctx.hangs.set(
|
|
61303
|
+
ctx.hangs.set(finding4.fingerprint, segment > 0 && partial2.ok ? { ...finding4, repro: { ...finding4.repro, recording: partial2.recording } } : finding4);
|
|
60930
61304
|
};
|
|
60931
61305
|
ctx.restartSpend = 0;
|
|
60932
61306
|
ctx.restartAtSeed = async () => {
|
|
@@ -61066,21 +61440,21 @@ function createHuntContext(params) {
|
|
|
61066
61440
|
}
|
|
61067
61441
|
return warning;
|
|
61068
61442
|
};
|
|
61069
|
-
ctx.blockedConsequence = (
|
|
61443
|
+
ctx.blockedConsequence = (finding4, candidate) => {
|
|
61070
61444
|
const causedBy = blockedCauseOf(ctx.blockedRoots, candidate);
|
|
61071
61445
|
if (causedBy === void 0)
|
|
61072
61446
|
return false;
|
|
61073
|
-
const known = ctx.blockedConsequences.get(
|
|
61447
|
+
const known = ctx.blockedConsequences.get(finding4.fingerprint);
|
|
61074
61448
|
if (known !== void 0) {
|
|
61075
61449
|
if (!known.occurrenceSteps.includes(candidate.step))
|
|
61076
61450
|
known.occurrenceSteps.push(candidate.step);
|
|
61077
61451
|
return true;
|
|
61078
61452
|
}
|
|
61079
|
-
ctx.blockedConsequences.set(
|
|
61080
|
-
fingerprint:
|
|
61081
|
-
kind:
|
|
61082
|
-
title:
|
|
61083
|
-
route:
|
|
61453
|
+
ctx.blockedConsequences.set(finding4.fingerprint, {
|
|
61454
|
+
fingerprint: finding4.fingerprint,
|
|
61455
|
+
kind: finding4.kind,
|
|
61456
|
+
title: finding4.title,
|
|
61457
|
+
route: finding4.route,
|
|
61084
61458
|
firstSeenStep: candidate.step,
|
|
61085
61459
|
occurrenceSteps: [candidate.step],
|
|
61086
61460
|
causedBy
|
|
@@ -63059,9 +63433,9 @@ function hostStarvedFailure(failure3, health) {
|
|
|
63059
63433
|
}
|
|
63060
63434
|
|
|
63061
63435
|
// src/build-info.generated.ts
|
|
63062
|
-
var GENERATED_COMMIT = "
|
|
63063
|
-
var GENERATED_TREE = "
|
|
63064
|
-
var GENERATED_BUILT_AT = "2026-10-
|
|
63436
|
+
var GENERATED_COMMIT = "ffe0da9";
|
|
63437
|
+
var GENERATED_TREE = "bc64c13";
|
|
63438
|
+
var GENERATED_BUILT_AT = "2026-10-26T13:27:18.971Z";
|
|
63065
63439
|
|
|
63066
63440
|
// src/version.ts
|
|
63067
63441
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
@@ -63083,13 +63457,13 @@ function readCliVersion() {
|
|
|
63083
63457
|
|
|
63084
63458
|
// src/engine.ts
|
|
63085
63459
|
function bundledCommit() {
|
|
63086
|
-
return true ? "
|
|
63460
|
+
return true ? "ffe0da9" : void 0;
|
|
63087
63461
|
}
|
|
63088
63462
|
function bundledBuiltAt() {
|
|
63089
|
-
return true ? "2026-10-
|
|
63463
|
+
return true ? "2026-10-26T13:27:18.971Z" : void 0;
|
|
63090
63464
|
}
|
|
63091
63465
|
function bundledTree() {
|
|
63092
|
-
return true ? "
|
|
63466
|
+
return true ? "bc64c13" : void 0;
|
|
63093
63467
|
}
|
|
63094
63468
|
function currentBuildTree() {
|
|
63095
63469
|
return bundledTree() ?? GENERATED_TREE;
|
|
@@ -63732,12 +64106,12 @@ function substituteDeep(v, sub) {
|
|
|
63732
64106
|
return v;
|
|
63733
64107
|
}
|
|
63734
64108
|
function fixtureSpecHash(spec, hooks) {
|
|
63735
|
-
return sha256(
|
|
64109
|
+
return sha256(canonical2({ spec: spec ?? null, hooks: hooks ?? null })).slice(0, 16);
|
|
63736
64110
|
}
|
|
63737
|
-
function
|
|
63738
|
-
if (Array.isArray(v)) return `[${v.map(
|
|
64111
|
+
function canonical2(v) {
|
|
64112
|
+
if (Array.isArray(v)) return `[${v.map(canonical2).join(",")}]`;
|
|
63739
64113
|
if (isRecord(v)) {
|
|
63740
|
-
return `{${Object.keys(v).sort().filter((k) => v[k] !== void 0).map((k) => `${JSON.stringify(k)}:${
|
|
64114
|
+
return `{${Object.keys(v).sort().filter((k) => v[k] !== void 0).map((k) => `${JSON.stringify(k)}:${canonical2(v[k])}`).join(",")}}`;
|
|
63741
64115
|
}
|
|
63742
64116
|
return JSON.stringify(v);
|
|
63743
64117
|
}
|
|
@@ -63859,7 +64233,7 @@ var MissionFixtures = class {
|
|
|
63859
64233
|
return redactUrl(redactText(text2, [...this.#opts.secrets ?? [], ...bound2, ...this.secrets()]));
|
|
63860
64234
|
}
|
|
63861
64235
|
identity() {
|
|
63862
|
-
return `fx-${sha256(
|
|
64236
|
+
return `fx-${sha256(canonical2({ spec: this.#specHash, outputs: this.publicOutputs(), secret: [...this.#secretNames].sort() })).slice(0, 16)}`;
|
|
63863
64237
|
}
|
|
63864
64238
|
record() {
|
|
63865
64239
|
return {
|
|
@@ -66535,10 +66909,10 @@ var QueryNode = freeze2({
|
|
|
66535
66909
|
where: node2.where ? WhereNode.cloneWithOperation(node2.where, "And", operation) : WhereNode.create(operation)
|
|
66536
66910
|
});
|
|
66537
66911
|
},
|
|
66538
|
-
cloneWithJoin(node2,
|
|
66912
|
+
cloneWithJoin(node2, join84) {
|
|
66539
66913
|
return freeze2({
|
|
66540
66914
|
...node2,
|
|
66541
|
-
joins: node2.joins ? freeze2([...node2.joins,
|
|
66915
|
+
joins: node2.joins ? freeze2([...node2.joins, join84]) : freeze2([join84])
|
|
66542
66916
|
});
|
|
66543
66917
|
},
|
|
66544
66918
|
cloneWithReturning(node2, selections) {
|
|
@@ -70194,8 +70568,8 @@ var WithSchemaTransformer = class extends OperationNodeTransformer {
|
|
|
70194
70568
|
this.#collectSchemableIdsFromTableExpr(node2.table, schemableIds);
|
|
70195
70569
|
}
|
|
70196
70570
|
if ("joins" in node2 && node2.joins) {
|
|
70197
|
-
for (const
|
|
70198
|
-
this.#collectSchemableIdsFromTableExpr(
|
|
70571
|
+
for (const join84 of node2.joins) {
|
|
70572
|
+
this.#collectSchemableIdsFromTableExpr(join84.table, schemableIds);
|
|
70199
70573
|
}
|
|
70200
70574
|
}
|
|
70201
70575
|
if ("using" in node2 && node2.using) {
|
|
@@ -78957,14 +79331,14 @@ var GitSourceManager = class {
|
|
|
78957
79331
|
|
|
78958
79332
|
// ../sources/dist/hash.js
|
|
78959
79333
|
import { createHash as createHash9 } from "node:crypto";
|
|
78960
|
-
function
|
|
79334
|
+
function canonicalJson3(value) {
|
|
78961
79335
|
if (value === null)
|
|
78962
79336
|
return "null";
|
|
78963
79337
|
if (Array.isArray(value))
|
|
78964
|
-
return `[${value.map(
|
|
79338
|
+
return `[${value.map(canonicalJson3).join(",")}]`;
|
|
78965
79339
|
if (typeof value === "object") {
|
|
78966
79340
|
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b2]) => a < b2 ? -1 : a > b2 ? 1 : 0);
|
|
78967
|
-
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${
|
|
79341
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson3(v)}`).join(",")}}`;
|
|
78968
79342
|
}
|
|
78969
79343
|
if (typeof value === "number") {
|
|
78970
79344
|
if (!Number.isFinite(value))
|
|
@@ -78975,7 +79349,7 @@ function canonicalJson2(value) {
|
|
|
78975
79349
|
}
|
|
78976
79350
|
function canonicalJourneyHash(file2) {
|
|
78977
79351
|
const validated = SharedJourneyFileSchema.parse(file2);
|
|
78978
|
-
const hex3 = createHash9("sha256").update(
|
|
79352
|
+
const hex3 = createHash9("sha256").update(canonicalJson3(validated), "utf8").digest("hex");
|
|
78979
79353
|
return `sha256:${hex3}`;
|
|
78980
79354
|
}
|
|
78981
79355
|
|
|
@@ -80681,6 +81055,15 @@ function journeyReviewHash(journey) {
|
|
|
80681
81055
|
for (const k of BOOKKEEPING) delete metadata[k];
|
|
80682
81056
|
return contentHash({ metadata, recording: journey.recording });
|
|
80683
81057
|
}
|
|
81058
|
+
function journeyLegacyReviewHash(journey) {
|
|
81059
|
+
const metadata = { ...journey.metadata };
|
|
81060
|
+
for (const k of BOOKKEEPING) delete metadata[k];
|
|
81061
|
+
return legacyContentHash({ metadata, recording: journey.recording });
|
|
81062
|
+
}
|
|
81063
|
+
function journeyApprovalMatches(journey) {
|
|
81064
|
+
const approved = journey.metadata.approval?.contentHash;
|
|
81065
|
+
return approved !== void 0 && (approved === journeyReviewHash(journey) || approved === journeyLegacyReviewHash(journey));
|
|
81066
|
+
}
|
|
80684
81067
|
function targetOf3(step) {
|
|
80685
81068
|
if (step.kind === "forEach") return step.items;
|
|
80686
81069
|
return "target" in step ? step.target : void 0;
|
|
@@ -80887,7 +81270,7 @@ function buildChange(journey, hash3, snapshot2, stepIdOnly) {
|
|
|
80887
81270
|
const idOnly = stepIdOnly ? { stepIdOnly: true } : {};
|
|
80888
81271
|
if (snapshot2 === null || snapshot2 === void 0) {
|
|
80889
81272
|
if (approval !== void 0) {
|
|
80890
|
-
return { kind: "snapshot-missing", approvedHash: approval.contentHash, approvedAt: approval.at, changed:
|
|
81273
|
+
return { kind: "snapshot-missing", approvedHash: approval.contentHash, approvedAt: approval.at, changed: !journeyApprovalMatches(journey), ...idOnly };
|
|
80891
81274
|
}
|
|
80892
81275
|
return journey.metadata.promoted ? { kind: "no-record" } : { kind: "first-approval" };
|
|
80893
81276
|
}
|
|
@@ -81241,9 +81624,9 @@ var UnknownCatalogItemError = class extends Error {
|
|
|
81241
81624
|
return this.kind === "persona" ? "E_UNKNOWN_PERSONA" : "E_UNKNOWN_JOB";
|
|
81242
81625
|
}
|
|
81243
81626
|
};
|
|
81244
|
-
function statusOf3(approval, hash3) {
|
|
81627
|
+
function statusOf3(approval, hash3, legacyHash) {
|
|
81245
81628
|
if (approval === void 0) return "draft";
|
|
81246
|
-
return approval.contentHash === hash3 ? "approved" : "stale";
|
|
81629
|
+
return approval.contentHash === hash3 || approval.contentHash === legacyHash ? "approved" : "stale";
|
|
81247
81630
|
}
|
|
81248
81631
|
async function readJsonFile(path2) {
|
|
81249
81632
|
const raw = await readFile8(path2, "utf8");
|
|
@@ -81308,7 +81691,7 @@ function catalogPersona(p, path2) {
|
|
|
81308
81691
|
hasLogin: p.fields.login !== void 0,
|
|
81309
81692
|
...approval === void 0 ? {} : { approval },
|
|
81310
81693
|
contentHash: hash3,
|
|
81311
|
-
status: statusOf3(approval, hash3)
|
|
81694
|
+
status: statusOf3(approval, hash3, personaLegacyContentHash(p.id, p.fields))
|
|
81312
81695
|
};
|
|
81313
81696
|
}
|
|
81314
81697
|
function personaContentHash(id, fields) {
|
|
@@ -81316,6 +81699,11 @@ function personaContentHash(id, fields) {
|
|
|
81316
81699
|
for (const [k, v] of Object.entries(fields)) if (k !== "approval" && !SESSION_KEYS.has(k)) described[k] = v;
|
|
81317
81700
|
return contentHash(described);
|
|
81318
81701
|
}
|
|
81702
|
+
function personaLegacyContentHash(id, fields) {
|
|
81703
|
+
const described = { id };
|
|
81704
|
+
for (const [k, v] of Object.entries(fields)) if (k !== "approval" && !SESSION_KEYS.has(k)) described[k] = v;
|
|
81705
|
+
return legacyContentHash(described);
|
|
81706
|
+
}
|
|
81319
81707
|
function jobList(raw, path2) {
|
|
81320
81708
|
if (Array.isArray(raw)) return raw;
|
|
81321
81709
|
if (isRecord(raw) && Array.isArray(raw.jobs)) return raw.jobs;
|
|
@@ -81338,13 +81726,17 @@ function catalogJobOf(job) {
|
|
|
81338
81726
|
personas: [.../* @__PURE__ */ new Set([...job.personas ?? [], ...job.persona === void 0 ? [] : [job.persona]])],
|
|
81339
81727
|
...job.approval === void 0 ? {} : { approval: job.approval },
|
|
81340
81728
|
contentHash: hash3,
|
|
81341
|
-
status: statusOf3(job.approval, hash3)
|
|
81729
|
+
status: statusOf3(job.approval, hash3, jobLegacyContentHash(job))
|
|
81342
81730
|
};
|
|
81343
81731
|
}
|
|
81344
81732
|
function jobContentHash(job) {
|
|
81345
81733
|
const { approval: _a3, ...rest } = job;
|
|
81346
81734
|
return contentHash(rest);
|
|
81347
81735
|
}
|
|
81736
|
+
function jobLegacyContentHash(job) {
|
|
81737
|
+
const { approval: _a3, ...rest } = job;
|
|
81738
|
+
return legacyContentHash(rest);
|
|
81739
|
+
}
|
|
81348
81740
|
function catalogJourney(journey) {
|
|
81349
81741
|
const m = journey.metadata;
|
|
81350
81742
|
const hash3 = journeyReviewHash(journey);
|
|
@@ -81357,7 +81749,7 @@ function catalogJourney(journey) {
|
|
|
81357
81749
|
...m.persona === void 0 ? {} : { personaText: m.persona },
|
|
81358
81750
|
...m.approval === void 0 ? {} : { approval: m.approval },
|
|
81359
81751
|
contentHash: hash3,
|
|
81360
|
-
changedSinceApproval: m.promoted && m.approval !== void 0 &&
|
|
81752
|
+
changedSinceApproval: m.promoted && m.approval !== void 0 && !journeyApprovalMatches(journey)
|
|
81361
81753
|
};
|
|
81362
81754
|
}
|
|
81363
81755
|
var CatalogLoader = class {
|
|
@@ -82860,12 +83252,12 @@ function healFloor(recorded, riskOf) {
|
|
|
82860
83252
|
}
|
|
82861
83253
|
return { floor: "healable", reason: "" };
|
|
82862
83254
|
}
|
|
82863
|
-
function
|
|
83255
|
+
function canonical3(v) {
|
|
82864
83256
|
if (Array.isArray(v))
|
|
82865
|
-
return `[${v.map(
|
|
83257
|
+
return `[${v.map(canonical3).join(",")}]`;
|
|
82866
83258
|
if (v !== null && typeof v === "object") {
|
|
82867
83259
|
const o = v;
|
|
82868
|
-
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${
|
|
83260
|
+
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${canonical3(o[k])}`).join(",")}}`;
|
|
82869
83261
|
}
|
|
82870
83262
|
return JSON.stringify(v) ?? "undefined";
|
|
82871
83263
|
}
|
|
@@ -82887,7 +83279,7 @@ function assertProofUntouched(before, after) {
|
|
|
82887
83279
|
for (const k of /* @__PURE__ */ new Set([...Object.keys(b2), ...Object.keys(a)])) {
|
|
82888
83280
|
if (free.has(k))
|
|
82889
83281
|
continue;
|
|
82890
|
-
if (
|
|
83282
|
+
if (canonical3(b2[k]) !== canonical3(a[k]))
|
|
82891
83283
|
return { code: "proof-field-changed", detail: `the candidate changes the step's \`${k}\`` };
|
|
82892
83284
|
}
|
|
82893
83285
|
return null;
|
|
@@ -82898,15 +83290,15 @@ function assertRecordedProofUntouched(before, after) {
|
|
|
82898
83290
|
for (const k of /* @__PURE__ */ new Set([...Object.keys(b2), ...Object.keys(a)])) {
|
|
82899
83291
|
if (k === "step")
|
|
82900
83292
|
continue;
|
|
82901
|
-
if (
|
|
83293
|
+
if (canonical3(b2[k]) !== canonical3(a[k]))
|
|
82902
83294
|
return { code: "proof-field-changed", detail: `the candidate changes the recorded step's \`${k}\`` };
|
|
82903
83295
|
}
|
|
82904
|
-
return
|
|
83296
|
+
return canonical3(before.step) === canonical3(after.step) ? null : assertProofUntouched(before.step, after.step);
|
|
82905
83297
|
}
|
|
82906
83298
|
function assertRecordingProofUntouched(base, proposed) {
|
|
82907
83299
|
const { pages: bp, ...bRest } = base;
|
|
82908
83300
|
const { pages: pp, ...pRest } = proposed;
|
|
82909
|
-
if (
|
|
83301
|
+
if (canonical3(bRest) !== canonical3(pRest))
|
|
82910
83302
|
return { code: "proof-field-changed", detail: "the revision changes the recording's own fields" };
|
|
82911
83303
|
if (bp.length !== pp.length)
|
|
82912
83304
|
return { code: "shape-changed", detail: `the revision has ${pp.length} pages; the Journey has ${bp.length}` };
|
|
@@ -82914,7 +83306,7 @@ function assertRecordingProofUntouched(base, proposed) {
|
|
|
82914
83306
|
for (let i = 0; i < bp.length; i++) {
|
|
82915
83307
|
const { steps: bs, ...bPage } = bp[i];
|
|
82916
83308
|
const { steps: ps, ...pPage } = pp[i];
|
|
82917
|
-
if (
|
|
83309
|
+
if (canonical3(bPage) !== canonical3(pPage))
|
|
82918
83310
|
return { code: "proof-field-changed", detail: `the revision changes page ${i + 1}` };
|
|
82919
83311
|
if (bs.length !== ps.length)
|
|
82920
83312
|
return { code: "shape-changed", detail: `the revision has ${ps.length} steps on page ${i + 1}; the Journey has ${bs.length}` };
|
|
@@ -83890,15 +84282,15 @@ function originOnly(url2) {
|
|
|
83890
84282
|
var LOCKFILES = /* @__PURE__ */ new Set(["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "Cargo.lock", "poetry.lock", "Gemfile.lock", "composer.lock"]);
|
|
83891
84283
|
var SECRET_EXTENSION = /\.(pem|key|p12|pfx)$/i;
|
|
83892
84284
|
function isSkippedChangePath(path2) {
|
|
83893
|
-
const
|
|
83894
|
-
const base =
|
|
84285
|
+
const normalized2 = path2.replace(/\\/g, "/");
|
|
84286
|
+
const base = normalized2.split("/").pop() ?? normalized2;
|
|
83895
84287
|
if (base === ".env" || base.startsWith(".env."))
|
|
83896
84288
|
return true;
|
|
83897
84289
|
if (SECRET_EXTENSION.test(base))
|
|
83898
84290
|
return true;
|
|
83899
84291
|
if (LOCKFILES.has(base))
|
|
83900
84292
|
return true;
|
|
83901
|
-
return
|
|
84293
|
+
return normalized2.toLowerCase().split("/").some((segment) => segment.includes("secret"));
|
|
83902
84294
|
}
|
|
83903
84295
|
function extractChangeEvidence(diff2, notes) {
|
|
83904
84296
|
const evidence = [];
|
|
@@ -84500,11 +84892,11 @@ async function removeFile(path2) {
|
|
|
84500
84892
|
await assertNoSymlink(path2);
|
|
84501
84893
|
await rm4(path2, { force: true });
|
|
84502
84894
|
}
|
|
84503
|
-
function
|
|
84504
|
-
if (Array.isArray(v)) return `[${v.map(
|
|
84895
|
+
function canonical4(v) {
|
|
84896
|
+
if (Array.isArray(v)) return `[${v.map(canonical4).join(",")}]`;
|
|
84505
84897
|
if (v !== null && typeof v === "object") {
|
|
84506
84898
|
const o = v;
|
|
84507
|
-
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${
|
|
84899
|
+
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${canonical4(o[k])}`).join(",")}}`;
|
|
84508
84900
|
}
|
|
84509
84901
|
return JSON.stringify(v) ?? "undefined";
|
|
84510
84902
|
}
|
|
@@ -84529,7 +84921,7 @@ function proposalFloorViolation(stored, proposed) {
|
|
|
84529
84921
|
for (let i = 0; i < after.length; i++) {
|
|
84530
84922
|
const b2 = before[i];
|
|
84531
84923
|
const a = after[i];
|
|
84532
|
-
if (b2 !== void 0 &&
|
|
84924
|
+
if (b2 !== void 0 && canonical4(b2.step) === canonical4(a.step)) continue;
|
|
84533
84925
|
for (const [which, rec] of [["before", b2?.recorded], ["after", a.recorded]]) {
|
|
84534
84926
|
if (rec === void 0) return `step ${i + 1} is not in the stored Journey`;
|
|
84535
84927
|
const floor = healFloor(rec, riskOf);
|
|
@@ -90939,9 +91331,9 @@ async function attachDefectEvidence(result, opts) {
|
|
|
90939
91331
|
continue;
|
|
90940
91332
|
}
|
|
90941
91333
|
const signal = defectSignalText(d, secrets);
|
|
90942
|
-
const
|
|
90943
|
-
const recording =
|
|
90944
|
-
if (
|
|
91334
|
+
const finding4 = findFinding(mission, fp);
|
|
91335
|
+
const recording = finding4?.recording ?? mission.recording;
|
|
91336
|
+
if (finding4 === void 0 || recording === null) {
|
|
90945
91337
|
byFp.set(fp, { screenshots: [], signal, skipped: "the defect has no repro Recording to replay" });
|
|
90946
91338
|
continue;
|
|
90947
91339
|
}
|
|
@@ -90959,9 +91351,9 @@ async function attachDefectEvidence(result, opts) {
|
|
|
90959
91351
|
fp,
|
|
90960
91352
|
await replayWithEvidence({
|
|
90961
91353
|
recording,
|
|
90962
|
-
stepIndex:
|
|
90963
|
-
fingerprints: [fp, ...
|
|
90964
|
-
signalCheckable: SIGNAL_KINDS.has(
|
|
91354
|
+
stepIndex: finding4.repro.recordingStepIndex,
|
|
91355
|
+
fingerprints: [fp, ...finding4.related ?? []],
|
|
91356
|
+
signalCheckable: SIGNAL_KINDS.has(finding4.kind),
|
|
90965
91357
|
signal,
|
|
90966
91358
|
seedUrl: mission.target.seedUrl,
|
|
90967
91359
|
allowlist: mission.target.allowlist,
|
|
@@ -92548,6 +92940,7 @@ async function runExploration(opts) {
|
|
|
92548
92940
|
timing: mission.run.timing,
|
|
92549
92941
|
outcome: goalOutcome,
|
|
92550
92942
|
runOutcome: mission.run.outcome,
|
|
92943
|
+
...mission.run.actionableControl === void 0 ? {} : { actionableControl: mission.run.actionableControl },
|
|
92551
92944
|
...mission.run.answer === void 0 ? {} : { answer: mission.run.answer },
|
|
92552
92945
|
...mission.run.partialReport === void 0 ? {} : { partialReport: mission.run.partialReport },
|
|
92553
92946
|
depth: mission.run.depth,
|
|
@@ -96259,8 +96652,8 @@ async function runVerifyFix(opts) {
|
|
|
96259
96652
|
const persisted = parsePersistedMission(raw);
|
|
96260
96653
|
const replay = replayAllowlist(persisted.target.allowlist, opts);
|
|
96261
96654
|
const mission = replay.override === void 0 ? persisted : { ...persisted, target: { ...persisted.target, allowlist: [...replay.allowlist] } };
|
|
96262
|
-
const
|
|
96263
|
-
if (
|
|
96655
|
+
const finding4 = findFinding(mission, opts.fingerprint);
|
|
96656
|
+
if (finding4 === void 0) {
|
|
96264
96657
|
throw new VerifyFixInputError(`no finding with fingerprint ${opts.fingerprint} in ${opts.resultPath}`);
|
|
96265
96658
|
}
|
|
96266
96659
|
const origin = assertAuthorizedExploreTarget(mission.target.seedUrl, mission.target.allowlist);
|
|
@@ -96278,8 +96671,8 @@ async function runVerifyFix(opts) {
|
|
|
96278
96671
|
...target.settle === void 0 ? {} : { settleConfig: target.settle },
|
|
96279
96672
|
...target.hangs === void 0 ? {} : { hangConfig: target.hangs }
|
|
96280
96673
|
};
|
|
96281
|
-
const found =
|
|
96282
|
-
if (found === null) throw new VerifyFixInputError(`finding ${
|
|
96674
|
+
const found = finding4.recording ?? mission.recording;
|
|
96675
|
+
if (found === null) throw new VerifyFixInputError(`finding ${finding4.fingerprint} has no Recording to replay`);
|
|
96283
96676
|
const branch = recordedBranchOf(raw?.result);
|
|
96284
96677
|
let prefix;
|
|
96285
96678
|
if (branch !== void 0) {
|
|
@@ -96292,7 +96685,7 @@ async function runVerifyFix(opts) {
|
|
|
96292
96685
|
}
|
|
96293
96686
|
const recording = prefix === void 0 ? found : anchoredRecording(found);
|
|
96294
96687
|
try {
|
|
96295
|
-
assertSameExtensionBuild(recording.extensions, opts.browser, `finding ${
|
|
96688
|
+
assertSameExtensionBuild(recording.extensions, opts.browser, `finding ${finding4.fingerprint}`);
|
|
96296
96689
|
} catch (e) {
|
|
96297
96690
|
throw new VerifyFixInputError(e instanceof Error ? e.message : String(e));
|
|
96298
96691
|
}
|
|
@@ -96325,9 +96718,9 @@ async function runVerifyFix(opts) {
|
|
|
96325
96718
|
const authTokenValues = [...invariantAuthTokens?.values() ?? []];
|
|
96326
96719
|
const primaryActor = mission.target.actors?.find((a) => a.role === "primary")?.name;
|
|
96327
96720
|
const fx = missionFixtures(mission, opts.fixtureFlags ?? {}, storageState, [...opts.secrets ?? [], ...authTokenValues], target.personas);
|
|
96328
|
-
const declared =
|
|
96721
|
+
const declared = finding4.kind === "invariant" && finding4.invariantId !== void 0 && invariantSpec !== void 0 ? {
|
|
96329
96722
|
spec: invariantSpec,
|
|
96330
|
-
id:
|
|
96723
|
+
id: finding4.invariantId,
|
|
96331
96724
|
allowlist: mission.target.allowlist,
|
|
96332
96725
|
baseUrl: mission.target.seedUrl,
|
|
96333
96726
|
...invariantAuthTokens === void 0 || invariantAuthTokens.size === 0 ? {} : { authTokens: invariantAuthTokens },
|
|
@@ -96356,10 +96749,10 @@ async function runVerifyFix(opts) {
|
|
|
96356
96749
|
const replaySession = fx === void 0 ? branchOpen : fixtureReplayOpener(branchOpen, fx, recording.fixture?.outputs ?? mission.fixtures?.outputs ?? {});
|
|
96357
96750
|
let result;
|
|
96358
96751
|
try {
|
|
96359
|
-
if (
|
|
96360
|
-
if (
|
|
96752
|
+
if (finding4.kind === "server-log") {
|
|
96753
|
+
if (finding4.serverLog === void 0) {
|
|
96361
96754
|
result = {
|
|
96362
|
-
fingerprint:
|
|
96755
|
+
fingerprint: finding4.fingerprint,
|
|
96363
96756
|
verdict: "inconclusive",
|
|
96364
96757
|
observedFingerprints: [],
|
|
96365
96758
|
replay: { outcome: "failed", at: -1, error: "not replayed" },
|
|
@@ -96368,13 +96761,13 @@ async function runVerifyFix(opts) {
|
|
|
96368
96761
|
} else {
|
|
96369
96762
|
result = await verifyServerLogDefect({
|
|
96370
96763
|
recording,
|
|
96371
|
-
recordingStepIndex:
|
|
96372
|
-
fingerprint:
|
|
96373
|
-
sources:
|
|
96374
|
-
matcher:
|
|
96375
|
-
normalizedMessage:
|
|
96376
|
-
drainMs:
|
|
96377
|
-
...
|
|
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 },
|
|
96378
96771
|
// #142 follow-up: an explicit --allow-log-cmd wins; otherwise the operator's own
|
|
96379
96772
|
// ~/.jevitate/targets.json entry for this origin may opt in (never an MCP argument).
|
|
96380
96773
|
allowLogCmd: opts.allowLogCmd === true || target.allowLogCmd === true,
|
|
@@ -96388,15 +96781,15 @@ async function runVerifyFix(opts) {
|
|
|
96388
96781
|
fixtureBaseDir: journeyFixtureBaseDir(),
|
|
96389
96782
|
perceive: perceiveOpts,
|
|
96390
96783
|
recording,
|
|
96391
|
-
recordingStepIndex:
|
|
96392
|
-
fingerprint:
|
|
96393
|
-
defectKind:
|
|
96784
|
+
recordingStepIndex: finding4.repro.recordingStepIndex,
|
|
96785
|
+
fingerprint: finding4.fingerprint,
|
|
96786
|
+
defectKind: finding4.kind,
|
|
96394
96787
|
...declared === void 0 ? {} : { invariant: declared },
|
|
96395
|
-
...
|
|
96788
|
+
...finding4.hang === void 0 ? {} : { hang: finding4.hang },
|
|
96396
96789
|
// #153: never re-send a paid/destructive write unless the operator opted in.
|
|
96397
96790
|
safety: { ...target.safety ?? {}, ...opts.hangReplayWrites === true ? { hangReplayWrites: true } : {} },
|
|
96398
96791
|
writtenBy: recordedWriters(raw),
|
|
96399
|
-
...
|
|
96792
|
+
...finding4.occurrences === void 0 ? {} : { occurrences: finding4.occurrences },
|
|
96400
96793
|
...opts.settleCeilingMs === void 0 ? {} : { settleCeilingMs: opts.settleCeilingMs },
|
|
96401
96794
|
...opts.replays === void 0 ? {} : { replays: opts.replays },
|
|
96402
96795
|
...opts.actionDeltas === true ? { actionDeltas: true, secrets: [...opts.secrets ?? [], ...authTokenValues] } : {},
|
|
@@ -96414,18 +96807,18 @@ async function runVerifyFix(opts) {
|
|
|
96414
96807
|
let shotFields = {};
|
|
96415
96808
|
if (opts.browser?.recordVideo !== void 0 || opts.screenshots !== void 0) {
|
|
96416
96809
|
const secrets = [...opts.secrets ?? [], ...authTokenValues];
|
|
96417
|
-
const stored = persistedDefect(raw,
|
|
96810
|
+
const stored = persistedDefect(raw, finding4.fingerprint);
|
|
96418
96811
|
const storedEvidence = stored !== void 0 && isRecord(stored.evidence) ? stored.evidence : void 0;
|
|
96419
96812
|
const beforeVideo = typeof storedEvidence?.videoPath === "string" ? storedEvidence.videoPath : void 0;
|
|
96420
96813
|
const beforeShots = Array.isArray(storedEvidence?.screenshots) ? storedEvidence.screenshots.filter((x) => typeof x === "string") : [];
|
|
96421
|
-
const shots = opts.screenshots === void 0 ? void 0 : new RunScreenshots({ spec: opts.screenshots, dir: screenshotsDirFor(opts.screenshots, vfArtifact), secrets, title: `verify-fix ${
|
|
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}` });
|
|
96422
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 };
|
|
96423
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({
|
|
96424
96817
|
recording,
|
|
96425
|
-
stepIndex:
|
|
96426
|
-
fingerprints: [
|
|
96427
|
-
signalCheckable: signalCheckable(
|
|
96428
|
-
signal: defectSignalText(stored ?? { kind:
|
|
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),
|
|
96429
96822
|
seedUrl: mission.target.seedUrl,
|
|
96430
96823
|
allowlist: mission.target.allowlist,
|
|
96431
96824
|
...storageState === void 0 ? {} : { storageState },
|
|
@@ -96454,7 +96847,7 @@ async function runVerifyFix(opts) {
|
|
|
96454
96847
|
...stale === void 0 ? {} : { failure: { kind: "journey-stale", message: stale.message } },
|
|
96455
96848
|
...evidence === void 0 ? {} : { evidence },
|
|
96456
96849
|
...shotFields,
|
|
96457
|
-
...
|
|
96850
|
+
...finding4.title === void 0 ? {} : { title: finding4.title },
|
|
96458
96851
|
...replay.override === void 0 ? {} : { allowlistOverride: replay.override },
|
|
96459
96852
|
...fx === void 0 ? {} : { fixtures: { ...fx.record(), missionIdentity: mission.fixtures?.identity ?? "none" } },
|
|
96460
96853
|
// Every replay closed its own session (finalizing its video) before verifyFix returned.
|
|
@@ -96554,17 +96947,17 @@ function ledgerAdd(opts) {
|
|
|
96554
96947
|
} catch (e) {
|
|
96555
96948
|
throw new LedgerError("E_LEDGER_INPUT", `${opts.resultPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
96556
96949
|
}
|
|
96557
|
-
const
|
|
96950
|
+
const finding4 = findFinding(mission, opts.fingerprint);
|
|
96558
96951
|
const result = isRecord(raw) && isRecord(raw.result) ? raw.result : {};
|
|
96559
|
-
const item =
|
|
96560
|
-
if (
|
|
96952
|
+
const item = finding4 === void 0 ? void 0 : rawFinding(result, finding4.fingerprint);
|
|
96953
|
+
if (finding4 === void 0 || item === void 0) {
|
|
96561
96954
|
throw new LedgerError("E_LEDGER_INPUT", `no finding with fingerprint ${opts.fingerprint} in ${opts.resultPath}`);
|
|
96562
96955
|
}
|
|
96563
96956
|
const ownRecording = isRecord(item.repro) && item.repro.recording !== void 0;
|
|
96564
96957
|
if (!ownRecording && mission.recording === null) {
|
|
96565
|
-
throw new LedgerError("E_LEDGER_INPUT", `finding ${
|
|
96958
|
+
throw new LedgerError("E_LEDGER_INPUT", `finding ${finding4.fingerprint} has no Recording to replay \u2014 nothing verify-fix could re-check`);
|
|
96566
96959
|
}
|
|
96567
|
-
const list2 =
|
|
96960
|
+
const list2 = finding4.kind === "hang" ? "hangs" : "defects";
|
|
96568
96961
|
const persistedFixtures = isRecord(result.fixtures) ? result.fixtures : void 0;
|
|
96569
96962
|
const material = {
|
|
96570
96963
|
...typeof result.schemaVersion === "number" ? { schemaVersion: result.schemaVersion } : {},
|
|
@@ -96574,7 +96967,7 @@ function ledgerAdd(opts) {
|
|
|
96574
96967
|
recording: ownRecording ? null : mission.recording,
|
|
96575
96968
|
defects: list2 === "defects" ? [item] : [],
|
|
96576
96969
|
hangs: list2 === "hangs" ? [item] : [],
|
|
96577
|
-
...
|
|
96970
|
+
...finding4.kind === "invariant" && mission.invariantSpec !== void 0 ? { invariantSpec: mission.invariantSpec } : {},
|
|
96578
96971
|
// The fixture's spec, hook HASHES and non-secret outputs — what a replay restores; hooks are never stored.
|
|
96579
96972
|
...persistedFixtures === void 0 ? {} : {
|
|
96580
96973
|
fixtures: {
|
|
@@ -96591,14 +96984,14 @@ function ledgerAdd(opts) {
|
|
|
96591
96984
|
};
|
|
96592
96985
|
assertNoSecrets(material, opts.secrets ?? []);
|
|
96593
96986
|
const dir = ledgerDir(opts.regressionsDir);
|
|
96594
|
-
const path2 = entryPath(dir,
|
|
96987
|
+
const path2 = entryPath(dir, finding4.fingerprint);
|
|
96595
96988
|
const previous = existsSync20(path2) ? readEntry(path2) : void 0;
|
|
96596
96989
|
const ticket = opts.ticket ?? previous?.ledger.ticket;
|
|
96597
96990
|
const meta3 = {
|
|
96598
96991
|
version: LEDGER_ENTRY_VERSION,
|
|
96599
|
-
fingerprint:
|
|
96600
|
-
kind:
|
|
96601
|
-
...
|
|
96992
|
+
fingerprint: finding4.fingerprint,
|
|
96993
|
+
kind: finding4.kind,
|
|
96994
|
+
...finding4.title === void 0 ? {} : { title: finding4.title },
|
|
96602
96995
|
...ticket === void 0 ? {} : { ticket },
|
|
96603
96996
|
addedAt: previous?.ledger.addedAt ?? (opts.nowIso ?? (() => clock.nowIso()))(),
|
|
96604
96997
|
source: {
|
|
@@ -96610,16 +97003,16 @@ function ledgerAdd(opts) {
|
|
|
96610
97003
|
addedBy: currentEngineInfo()
|
|
96611
97004
|
};
|
|
96612
97005
|
const entry = { ledger: meta3, result: material };
|
|
96613
|
-
const check3 = findFinding(parsePersistedMission(entry),
|
|
96614
|
-
if (check3 === void 0) throw new LedgerError("E_LEDGER_INPUT", `finding ${
|
|
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`);
|
|
96615
97008
|
mkdirSync9(dir, { recursive: true });
|
|
96616
97009
|
writeFileSync12(path2, `${JSON.stringify(entry, null, 2)}
|
|
96617
97010
|
`, "utf8");
|
|
96618
97011
|
return {
|
|
96619
97012
|
entryPath: path2,
|
|
96620
|
-
fingerprint:
|
|
96621
|
-
kind:
|
|
96622
|
-
...
|
|
97013
|
+
fingerprint: finding4.fingerprint,
|
|
97014
|
+
kind: finding4.kind,
|
|
97015
|
+
...finding4.title === void 0 ? {} : { title: finding4.title },
|
|
96623
97016
|
...ticket === void 0 ? {} : { ticket },
|
|
96624
97017
|
updated: previous !== void 0
|
|
96625
97018
|
};
|
|
@@ -96837,14 +97230,14 @@ function realSecureIO() {
|
|
|
96837
97230
|
return readMaskedLine(process.stdin, process.stdout, message2);
|
|
96838
97231
|
},
|
|
96839
97232
|
async persist(key2, value) {
|
|
96840
|
-
const { mkdir:
|
|
97233
|
+
const { mkdir: mkdir40, writeFile: writeFile47, readFile: readFile46 } = await import("node:fs/promises");
|
|
96841
97234
|
const { dirname: dirname34 } = await import("node:path");
|
|
96842
97235
|
const path2 = resolveDataDir(["credentials.json"]);
|
|
96843
|
-
await
|
|
97236
|
+
await mkdir40(dirname34(path2), { recursive: true });
|
|
96844
97237
|
let existing = {};
|
|
96845
97238
|
let raw;
|
|
96846
97239
|
try {
|
|
96847
|
-
raw = await
|
|
97240
|
+
raw = await readFile46(path2, "utf8");
|
|
96848
97241
|
} catch (err) {
|
|
96849
97242
|
const missing = typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
96850
97243
|
if (!missing) throw err;
|
|
@@ -96857,7 +97250,7 @@ function realSecureIO() {
|
|
|
96857
97250
|
}
|
|
96858
97251
|
}
|
|
96859
97252
|
existing[key2] = value;
|
|
96860
|
-
await
|
|
97253
|
+
await writeFile47(path2, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
96861
97254
|
}
|
|
96862
97255
|
};
|
|
96863
97256
|
}
|
|
@@ -97241,7 +97634,7 @@ function makeFinding(input2, rubric, evidence) {
|
|
|
97241
97634
|
if (!Number.isInteger(occurrences3) || occurrences3 < 1) {
|
|
97242
97635
|
throw new UxFindingError(`occurrences for '${entry.id}' must be a positive integer, got ${occurrences3}`);
|
|
97243
97636
|
}
|
|
97244
|
-
const
|
|
97637
|
+
const finding4 = {
|
|
97245
97638
|
rubricItemId: entry.id,
|
|
97246
97639
|
citation: { source: entry.citation.source, ref: entry.citation.ref },
|
|
97247
97640
|
severity: input2.severity,
|
|
@@ -97261,7 +97654,7 @@ function makeFinding(input2, rubric, evidence) {
|
|
|
97261
97654
|
...input2.quality ? { quality: { ...input2.quality } } : {},
|
|
97262
97655
|
...input2.predictedAttention ? { predictedAttention: input2.predictedAttention } : {}
|
|
97263
97656
|
};
|
|
97264
|
-
return Object.freeze(
|
|
97657
|
+
return Object.freeze(finding4);
|
|
97265
97658
|
}
|
|
97266
97659
|
|
|
97267
97660
|
// ../ux/dist/redact.js
|
|
@@ -97722,9 +98115,9 @@ function dedupeObjectiveFindings(items, rubric) {
|
|
|
97722
98115
|
}
|
|
97723
98116
|
|
|
97724
98117
|
// ../ux/dist/recommend.js
|
|
97725
|
-
function evidenceSummaryFor(
|
|
98118
|
+
function evidenceSummaryFor(finding4, evidence) {
|
|
97726
98119
|
const parts2 = [];
|
|
97727
|
-
for (const ref of
|
|
98120
|
+
for (const ref of finding4.evidenceRefs) {
|
|
97728
98121
|
if (ref.id.startsWith("control:")) {
|
|
97729
98122
|
const idx = Number(ref.id.slice("control:".length));
|
|
97730
98123
|
const c = evidence.controls.find((ctrl) => ctrl.index === idx);
|
|
@@ -97736,19 +98129,19 @@ function evidenceSummaryFor(finding3, evidence) {
|
|
|
97736
98129
|
}
|
|
97737
98130
|
return parts2.join(" | ").slice(0, 4e3);
|
|
97738
98131
|
}
|
|
97739
|
-
async function recommend(gen,
|
|
98132
|
+
async function recommend(gen, finding4, evidence, secrets = []) {
|
|
97740
98133
|
const input2 = {
|
|
97741
|
-
principle:
|
|
97742
|
-
citationSource:
|
|
97743
|
-
citationRef:
|
|
97744
|
-
judgmentSummary:
|
|
97745
|
-
evidenceSummary: evidenceSummaryFor(
|
|
98134
|
+
principle: finding4.rubricItemId,
|
|
98135
|
+
citationSource: finding4.citation.source,
|
|
98136
|
+
citationRef: finding4.citation.ref,
|
|
98137
|
+
judgmentSummary: finding4.recommendation,
|
|
98138
|
+
evidenceSummary: evidenceSummaryFor(finding4, evidence),
|
|
97746
98139
|
appClass: evidence.appContext.appClass
|
|
97747
98140
|
};
|
|
97748
98141
|
assertNoSecretInPayload(input2, secrets);
|
|
97749
98142
|
const result = await gen.generate("ux.recommendation", input2);
|
|
97750
98143
|
const body = result.output.recommendation.trim();
|
|
97751
|
-
return `${body} (${
|
|
98144
|
+
return `${body} (${finding4.citation.source}: ${finding4.citation.ref})`;
|
|
97752
98145
|
}
|
|
97753
98146
|
|
|
97754
98147
|
// ../ux/dist/a11y.js
|
|
@@ -98095,7 +98488,7 @@ function makeSignalFinding(input2) {
|
|
|
98095
98488
|
...input2.evidence.requests.map((r) => ({ id: `request:${r.id}` })),
|
|
98096
98489
|
...input2.evidence.screenshot === void 0 ? [] : [{ id: `screenshot:${input2.evidence.screenshot}` }]
|
|
98097
98490
|
];
|
|
98098
|
-
const
|
|
98491
|
+
const finding4 = {
|
|
98099
98492
|
rubricItemId: rule.id,
|
|
98100
98493
|
citation: { ...rule.citation },
|
|
98101
98494
|
severity: rule.severity,
|
|
@@ -98113,7 +98506,7 @@ function makeSignalFinding(input2) {
|
|
|
98113
98506
|
screenIds: [...input2.screenIds ?? [input2.screenId]],
|
|
98114
98507
|
signal: input2.evidence
|
|
98115
98508
|
};
|
|
98116
|
-
return Object.freeze(
|
|
98509
|
+
return Object.freeze(finding4);
|
|
98117
98510
|
}
|
|
98118
98511
|
|
|
98119
98512
|
// ../ux/dist/signals/shared.js
|
|
@@ -99871,9 +100264,9 @@ async function analyzeClaims(request, deps) {
|
|
|
99871
100264
|
const dupOptions = /* @__PURE__ */ new Map();
|
|
99872
100265
|
for (const d of group) {
|
|
99873
100266
|
const i = indexOf.get(d);
|
|
99874
|
-
const
|
|
99875
|
-
questions[`grade::${i}::need`] = { kind: "noul", instructions: prompts.grade.need.replace("{finding}",
|
|
99876
|
-
questions[`grade::${i}::ship`] = { kind: "noul", instructions: prompts.grade.ship.replace("{finding}",
|
|
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)) };
|
|
99877
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);
|
|
99878
100271
|
if (earlier.length > 0) {
|
|
99879
100272
|
dupOptions.set(i, earlier);
|
|
@@ -99881,7 +100274,7 @@ async function analyzeClaims(request, deps) {
|
|
|
99881
100274
|
kind: "choice",
|
|
99882
100275
|
options: ["new", ...earlier.map((j) => `same:${j}`)],
|
|
99883
100276
|
descriptions: Object.fromEntries([["new", "a different problem"], ...earlier.map((j) => [`same:${j}`, describeDraft(drafts[j], prose[j])])]),
|
|
99884
|
-
instructions: prompts.duplicate.replace("{finding}",
|
|
100277
|
+
instructions: prompts.duplicate.replace("{finding}", finding4)
|
|
99885
100278
|
};
|
|
99886
100279
|
}
|
|
99887
100280
|
}
|
|
@@ -99930,9 +100323,9 @@ async function analyzeClaims(request, deps) {
|
|
|
99930
100323
|
const impact = d.friction?.impact ?? v.impact ?? "confused";
|
|
99931
100324
|
const severity2 = v.severity ?? (d.friction === void 0 ? "minor" : IMPACT_SEVERITY2[d.friction.impact]);
|
|
99932
100325
|
const evidenceRefs = d.target !== void 0 ? [{ id: `control:${d.target.index}` }] : (v.quotes ?? []).length > 0 ? [{ id: "visibleText" }] : [{ id: "url" }];
|
|
99933
|
-
let
|
|
100326
|
+
let finding4;
|
|
99934
100327
|
try {
|
|
99935
|
-
|
|
100328
|
+
finding4 = makeFinding({
|
|
99936
100329
|
rubricItemId,
|
|
99937
100330
|
evidenceRefs,
|
|
99938
100331
|
severity: severity2,
|
|
@@ -99951,8 +100344,8 @@ async function analyzeClaims(request, deps) {
|
|
|
99951
100344
|
if (deps.polish === true) {
|
|
99952
100345
|
if (deps.gen === void 0)
|
|
99953
100346
|
throw new Error("polish requested without a generation gateway");
|
|
99954
|
-
const polished = await recommend(deps.gen,
|
|
99955
|
-
|
|
100347
|
+
const polished = await recommend(deps.gen, finding4, d.screen.red, secrets);
|
|
100348
|
+
finding4 = Object.freeze({ ...finding4, recommendation: polished });
|
|
99956
100349
|
}
|
|
99957
100350
|
} catch (cause) {
|
|
99958
100351
|
return { kind: "failed", reason: `claim finding failed: ${message(cause)}`, screenId: d.screen.raw.screenId, rubricItemId };
|
|
@@ -99965,7 +100358,7 @@ async function analyzeClaims(request, deps) {
|
|
|
99965
100358
|
...d.target === void 0 ? {} : { target: label(d.target) }
|
|
99966
100359
|
};
|
|
99967
100360
|
built.set(i, Object.freeze({
|
|
99968
|
-
...
|
|
100361
|
+
...finding4,
|
|
99969
100362
|
claim: claim2,
|
|
99970
100363
|
impact,
|
|
99971
100364
|
...grade === void 0 ? {} : { grade },
|
|
@@ -99974,7 +100367,7 @@ async function analyzeClaims(request, deps) {
|
|
|
99974
100367
|
const r = root2(i);
|
|
99975
100368
|
if (r !== i) {
|
|
99976
100369
|
const list2 = contributions.get(r) ?? [];
|
|
99977
|
-
list2.push({ rubricItemId, observation:
|
|
100370
|
+
list2.push({ rubricItemId, observation: finding4.observation, confidence: finding4.confidence, occurrences: finding4.occurrences, citation: finding4.citation, ...finding4.quality ? { quality: finding4.quality } : {} });
|
|
99978
100371
|
contributions.set(r, list2);
|
|
99979
100372
|
}
|
|
99980
100373
|
}
|
|
@@ -100005,8 +100398,8 @@ async function analyzeClaims(request, deps) {
|
|
|
100005
100398
|
if (ok2.length === 0)
|
|
100006
100399
|
continue;
|
|
100007
100400
|
a11yEvaluated += ok2.length;
|
|
100008
|
-
for (const
|
|
100009
|
-
a11yFound.push({ finding:
|
|
100401
|
+
for (const finding4 of deps.a11yChecker(s3.red, request.rubric).findings)
|
|
100402
|
+
a11yFound.push({ finding: finding4, refs: s3.red.refs });
|
|
100010
100403
|
}
|
|
100011
100404
|
}
|
|
100012
100405
|
const ledger = summarizeLedger(records);
|
|
@@ -100811,14 +101204,14 @@ var CREDENTIAL_KEY2 = /pass|secret|token|otp|totp|pin|cvc|cvv|card|auth|session|
|
|
|
100811
101204
|
var MAX_DIGEST_BODY = 64e3;
|
|
100812
101205
|
var CredentialBody = class extends Error {
|
|
100813
101206
|
};
|
|
100814
|
-
function
|
|
100815
|
-
if (Array.isArray(v)) return v.map(
|
|
101207
|
+
function canonical5(v) {
|
|
101208
|
+
if (Array.isArray(v)) return v.map(canonical5);
|
|
100816
101209
|
if (v !== null && typeof v === "object") {
|
|
100817
101210
|
const out = {};
|
|
100818
101211
|
for (const k of Object.keys(v).sort()) {
|
|
100819
101212
|
if (CREDENTIAL_KEY2.test(k)) throw new CredentialBody();
|
|
100820
101213
|
if (VOLATILE_KEY.test(k)) continue;
|
|
100821
|
-
out[k] =
|
|
101214
|
+
out[k] = canonical5(v[k]);
|
|
100822
101215
|
}
|
|
100823
101216
|
return out;
|
|
100824
101217
|
}
|
|
@@ -100835,7 +101228,7 @@ function payloadDigest(method, endpoint2, body, secrets) {
|
|
|
100835
101228
|
value = Object.fromEntries(new URLSearchParams(body));
|
|
100836
101229
|
}
|
|
100837
101230
|
try {
|
|
100838
|
-
const canon = JSON.stringify(
|
|
101231
|
+
const canon = JSON.stringify(canonical5(value));
|
|
100839
101232
|
return createHash12("sha256").update(`${method.toUpperCase()} ${endpoint2}
|
|
100840
101233
|
${canon}`).digest("hex").slice(0, 32);
|
|
100841
101234
|
} catch {
|
|
@@ -104264,10 +104657,17 @@ import { existsSync as existsSync25 } from "node:fs";
|
|
|
104264
104657
|
import { copyFile as copyFile4, lstat as lstat4, mkdir as mkdir25, open as open3, readdir as readdir9, readFile as readFile24, rename as rename4, rm as rm10, writeFile as writeFile29 } from "node:fs/promises";
|
|
104265
104658
|
import { basename as basename15, dirname as dirname24, join as join56, resolve as resolve18, sep as sep7 } from "node:path";
|
|
104266
104659
|
|
|
104660
|
+
// src/catalog-contract-identity.ts
|
|
104661
|
+
var CATALOG_CONTRACT_IDENTITY = {
|
|
104662
|
+
"kind": "jevitate.catalog-contract",
|
|
104663
|
+
"version": "2.0.0",
|
|
104664
|
+
"manifestSha256": "c1292a8727d7dc0dbe1d63dc768d34b6cc94ae60fab3a67aeb0a53837e1735ad"
|
|
104665
|
+
};
|
|
104666
|
+
|
|
104267
104667
|
// src/catalog-bundle.ts
|
|
104268
104668
|
var BUNDLE_KIND = "journeeze.catalog-bundle";
|
|
104269
104669
|
var BUNDLE_VERSION = 1;
|
|
104270
|
-
var BUNDLE_MINOR =
|
|
104670
|
+
var BUNDLE_MINOR = 2;
|
|
104271
104671
|
var CatalogBundleInputError = class extends CatalogInputError {
|
|
104272
104672
|
constructor(issues) {
|
|
104273
104673
|
super(`the catalog cannot be exported as a Journeeze bundle (${issues.length} problem(s)): ${issues.join("; ")}`);
|
|
@@ -104632,7 +105032,7 @@ function bundleChecks(input2, journeys, warn) {
|
|
|
104632
105032
|
if (outcome.outcome !== "clean") why = `the run was ${outcome.outcome}, and a baseline comes only from a clean run`;
|
|
104633
105033
|
else if (journeyId === void 0 || journeyHash === void 0) why = "the item names no Journey review hash";
|
|
104634
105034
|
else if (cj === void 0) why = `Journey ${journeyId} is not in the bundle`;
|
|
104635
|
-
else if (cj.approval?.contentHash !== journeyHash || cj.
|
|
105035
|
+
else if (cj.approval?.contentHash !== journeyHash || cj.changedSinceApproval) why = `it was measured on a Journey revision (${journeyHash.slice(0, 12)}\u2026) that is not ${journeyId}'s approved one`;
|
|
104636
105036
|
else {
|
|
104637
105037
|
const b2 = bundleBaseline(item.baseline, cj.journey);
|
|
104638
105038
|
if ("drop" in b2) why = b2.drop;
|
|
@@ -104686,13 +105086,13 @@ function bundleDemos(input2, journeys, warn) {
|
|
|
104686
105086
|
warn.push(`${where}: its Journey is not in the bundle (not promoted, or left out) \u2014 the demo is left out`);
|
|
104687
105087
|
continue;
|
|
104688
105088
|
}
|
|
104689
|
-
const approved = ex.cj.approval !== void 0 && ex.cj.
|
|
105089
|
+
const approved = ex.cj.approval !== void 0 && !ex.cj.changedSinceApproval;
|
|
104690
105090
|
if (!approved) {
|
|
104691
105091
|
warn.push(`${where}: its Journey changed since it was approved \u2014 the demo is left out (approve the Journey and its demo again)`);
|
|
104692
105092
|
continue;
|
|
104693
105093
|
}
|
|
104694
|
-
if (d.renderedFrom !== ex.cj.contentHash) {
|
|
104695
|
-
warn.push(`${where}: rendered from another version of its Journey (${d.renderedFrom.slice(0, 12)}, approved ${ex.cj.contentHash.slice(0, 12)}) \u2014 the demo is left out (run \`jevitate demo approve\` again)`);
|
|
105094
|
+
if (d.renderedFrom !== ex.cj.approval.contentHash) {
|
|
105095
|
+
warn.push(`${where}: rendered from another version of its Journey (${d.renderedFrom.slice(0, 12)}, approved ${ex.cj.approval.contentHash.slice(0, 12)}) \u2014 the demo is left out (run \`jevitate demo approve\` again)`);
|
|
104696
105096
|
continue;
|
|
104697
105097
|
}
|
|
104698
105098
|
if (d.steps.length === 0 || d.steps.length > MEDIA_LIMITS.demoSteps) {
|
|
@@ -104844,6 +105244,7 @@ function buildCatalogBundle(input2) {
|
|
|
104844
105244
|
version: BUNDLE_VERSION,
|
|
104845
105245
|
minor: BUNDLE_MINOR,
|
|
104846
105246
|
producer: { tool: "jevitate", version: input2.producer.version, ...commit === void 0 ? {} : { commit } },
|
|
105247
|
+
catalogContract: CATALOG_CONTRACT_IDENTITY,
|
|
104847
105248
|
product: { name: input2.productName },
|
|
104848
105249
|
catalog: { personas, jobs, journeys: journeys.map((j) => j.entry) },
|
|
104849
105250
|
...shown2.demos.length === 0 ? {} : { demos: shown2.demos },
|
|
@@ -105531,7 +105932,7 @@ ${route ?? ""}
|
|
|
105531
105932
|
${key2}`, "utf8").digest("hex").slice(0, 16);
|
|
105532
105933
|
const step = first.index + 1;
|
|
105533
105934
|
const fix = s3.route !== void 0 && route !== void 0 && s3.route !== route ? s3.fix.split(s3.route).join(route) : s3.fix;
|
|
105534
|
-
const
|
|
105935
|
+
const finding4 = {
|
|
105535
105936
|
fingerprint: fingerprint2,
|
|
105536
105937
|
kind: "ux",
|
|
105537
105938
|
claim: "other",
|
|
@@ -105547,12 +105948,12 @@ ${key2}`, "utf8").digest("hex").slice(0, 16);
|
|
|
105547
105948
|
recommendation: fix,
|
|
105548
105949
|
locator: { element: s3.element, attribute: s3.attribute, testId: s3.testId, fix, steps: s3.occurrences.length }
|
|
105549
105950
|
};
|
|
105550
|
-
const issues = findingIssues(
|
|
105951
|
+
const issues = findingIssues(finding4);
|
|
105551
105952
|
if (issues.length > 0) {
|
|
105552
|
-
warnings.push(findingDropWarning(fingerprint2,
|
|
105953
|
+
warnings.push(findingDropWarning(fingerprint2, finding4.kind, issues));
|
|
105553
105954
|
continue;
|
|
105554
105955
|
}
|
|
105555
|
-
findings.push(
|
|
105956
|
+
findings.push(finding4);
|
|
105556
105957
|
}
|
|
105557
105958
|
return { findings, warnings };
|
|
105558
105959
|
}
|
|
@@ -107534,8 +107935,51 @@ function registerLocatorHealthCommand(program2, deps) {
|
|
|
107534
107935
|
}
|
|
107535
107936
|
|
|
107536
107937
|
// src/report-cli.ts
|
|
107537
|
-
import { mkdir as
|
|
107938
|
+
import { mkdir as mkdir28, readFile as readFile28, writeFile as writeFile33 } from "node:fs/promises";
|
|
107538
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
|
|
107539
107983
|
function emit3(program2, envelope, exitCode) {
|
|
107540
107984
|
emitJsonOrRefusal(program2, envelope, exitCode);
|
|
107541
107985
|
}
|
|
@@ -107544,6 +107988,25 @@ function failure2(program2, err, code) {
|
|
|
107544
107988
|
else emit3(program2, fail(code, err instanceof Error ? err.message : String(err)));
|
|
107545
107989
|
}
|
|
107546
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
|
+
}
|
|
107547
108010
|
function registerReportCommands(program2, deps) {
|
|
107548
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() {
|
|
107549
108012
|
const o = this.opts();
|
|
@@ -107559,14 +108022,14 @@ function registerReportCommands(program2, deps) {
|
|
|
107559
108022
|
...deps.baselinesDir === void 0 ? {} : { baselinesDir: deps.baselinesDir }
|
|
107560
108023
|
});
|
|
107561
108024
|
if (o.out !== void 0) {
|
|
107562
|
-
await
|
|
107563
|
-
await
|
|
107564
|
-
await
|
|
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)}
|
|
107565
108028
|
`, "utf8");
|
|
107566
108029
|
}
|
|
107567
108030
|
if (o.json) emit3(program2, ok(report2), 0);
|
|
107568
108031
|
else {
|
|
107569
|
-
program2.configureOutput().writeOut?.(report2.markdown);
|
|
108032
|
+
program2.configureOutput().writeOut?.(`${report2.markdown}${await reportJourneezeHint(o.json)}`);
|
|
107570
108033
|
process.exitCode = 0;
|
|
107571
108034
|
}
|
|
107572
108035
|
} catch (err) {
|
|
@@ -107747,7 +108210,7 @@ async function collectAllMissingKeys(store, io, opts = {}) {
|
|
|
107747
108210
|
}
|
|
107748
108211
|
|
|
107749
108212
|
// src/init-mcp.ts
|
|
107750
|
-
import { readFile as
|
|
108213
|
+
import { readFile as readFile29, writeFile as writeFile34, mkdir as mkdir29 } from "node:fs/promises";
|
|
107751
108214
|
import { homedir as realHomedir2 } from "node:os";
|
|
107752
108215
|
import { join as join59, dirname as dirname26 } from "node:path";
|
|
107753
108216
|
var MCP_SERVER_NAME = "jevitate";
|
|
@@ -107880,7 +108343,7 @@ async function registerMcp(runtimes, paths, opts = {}) {
|
|
|
107880
108343
|
for (const unit of units) {
|
|
107881
108344
|
let existing;
|
|
107882
108345
|
try {
|
|
107883
|
-
existing = await
|
|
108346
|
+
existing = await readFile29(unit.path, "utf8");
|
|
107884
108347
|
} catch {
|
|
107885
108348
|
existing = void 0;
|
|
107886
108349
|
}
|
|
@@ -107890,8 +108353,8 @@ async function registerMcp(runtimes, paths, opts = {}) {
|
|
|
107890
108353
|
entry.instruction = renderPrintConfig(unit.harness);
|
|
107891
108354
|
}
|
|
107892
108355
|
if (!opts.dryRun && result.content !== void 0) {
|
|
107893
|
-
await
|
|
107894
|
-
await
|
|
108356
|
+
await mkdir29(dirname26(unit.path), { recursive: true });
|
|
108357
|
+
await writeFile34(unit.path, result.content, "utf8");
|
|
107895
108358
|
}
|
|
107896
108359
|
report2.push(entry);
|
|
107897
108360
|
}
|
|
@@ -108211,7 +108674,7 @@ function registerProfileCommands(program2, deps) {
|
|
|
108211
108674
|
}
|
|
108212
108675
|
|
|
108213
108676
|
// src/site-cli.ts
|
|
108214
|
-
import { readFile as
|
|
108677
|
+
import { readFile as readFile30 } from "node:fs/promises";
|
|
108215
108678
|
function registerSiteCommands(program2, deps) {
|
|
108216
108679
|
const site = program2.command("site").description("per-site policies for Journey runs: human-like pacing, throttles, run budgets and quiet hours");
|
|
108217
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)");
|
|
@@ -108269,7 +108732,7 @@ function registerSiteCommands(program2, deps) {
|
|
|
108269
108732
|
const { account, db, file: file2, json: json2 } = this.opts();
|
|
108270
108733
|
let policy;
|
|
108271
108734
|
try {
|
|
108272
|
-
const raw = await
|
|
108735
|
+
const raw = await readFile30(file2, "utf8");
|
|
108273
108736
|
policy = SitePolicySchema.parse(JSON.parse(raw));
|
|
108274
108737
|
} catch (err) {
|
|
108275
108738
|
emitJson(program2, fail("E_INVALID_POLICY", String(err)));
|
|
@@ -108297,7 +108760,7 @@ function registerSiteCommands(program2, deps) {
|
|
|
108297
108760
|
const { account, db, script, seed, json: json2 } = this.opts();
|
|
108298
108761
|
let plannedScript;
|
|
108299
108762
|
try {
|
|
108300
|
-
const raw = await
|
|
108763
|
+
const raw = await readFile30(script, "utf8");
|
|
108301
108764
|
plannedScript = parsePlannedScript(raw);
|
|
108302
108765
|
} catch (err) {
|
|
108303
108766
|
emitJson(program2, fail("E_INVALID_SCRIPT", String(err)));
|
|
@@ -108333,12 +108796,12 @@ function registerSiteCommands(program2, deps) {
|
|
|
108333
108796
|
}
|
|
108334
108797
|
|
|
108335
108798
|
// src/recording-cli.ts
|
|
108336
|
-
import { readFile as
|
|
108799
|
+
import { readFile as readFile31, writeFile as writeFile35 } from "node:fs/promises";
|
|
108337
108800
|
import { resolve as resolvePath14 } from "node:path";
|
|
108338
108801
|
import * as clack from "@clack/prompts";
|
|
108339
108802
|
function registerRecordingCommands(program2, deps) {
|
|
108340
108803
|
async function readTakeJson(file2) {
|
|
108341
|
-
const raw = await
|
|
108804
|
+
const raw = await readFile31(file2, "utf8");
|
|
108342
108805
|
let json2;
|
|
108343
108806
|
try {
|
|
108344
108807
|
json2 = JSON.parse(raw);
|
|
@@ -108357,7 +108820,7 @@ function registerRecordingCommands(program2, deps) {
|
|
|
108357
108820
|
return { recording: parsed.recording, values: new Map(Object.entries(parsed.values)) };
|
|
108358
108821
|
}
|
|
108359
108822
|
async function readRecordingOrTake(file2) {
|
|
108360
|
-
const raw = await
|
|
108823
|
+
const raw = await readFile31(file2, "utf8");
|
|
108361
108824
|
const json2 = JSON.parse(raw);
|
|
108362
108825
|
if (json2 !== null && typeof json2 === "object" && !Array.isArray(json2) && "recording" in json2) {
|
|
108363
108826
|
return AuthoringTakeSchema.parse(json2).recording;
|
|
@@ -108432,7 +108895,7 @@ function registerRecordingCommands(program2, deps) {
|
|
|
108432
108895
|
}
|
|
108433
108896
|
const result = applyPostdoc(takes[0], diff2, decisions);
|
|
108434
108897
|
if (out !== void 0) {
|
|
108435
|
-
await
|
|
108898
|
+
await writeFile35(out, `${JSON.stringify(result, null, 2)}
|
|
108436
108899
|
`, "utf8");
|
|
108437
108900
|
}
|
|
108438
108901
|
if (json2) {
|
|
@@ -108510,7 +108973,7 @@ var DraftSaveError = class extends Error {
|
|
|
108510
108973
|
async function saveDraft(draft, out, journeysDir) {
|
|
108511
108974
|
const steps = draft.recording.pages.reduce((n3, p) => n3 + p.steps.length, 0);
|
|
108512
108975
|
if (out !== void 0) {
|
|
108513
|
-
await
|
|
108976
|
+
await writeFile35(out, `${JSON.stringify(draft, null, 2)}
|
|
108514
108977
|
`, "utf8");
|
|
108515
108978
|
return { id: draft.metadata.id, path: resolvePath14(out), savedTo: "file", promoted: false, steps };
|
|
108516
108979
|
}
|
|
@@ -108609,7 +109072,7 @@ async function loadDecisions(file2) {
|
|
|
108609
109072
|
let raw;
|
|
108610
109073
|
let parsed;
|
|
108611
109074
|
try {
|
|
108612
|
-
raw = await
|
|
109075
|
+
raw = await readFile31(file2, "utf8");
|
|
108613
109076
|
parsed = JSON.parse(raw);
|
|
108614
109077
|
} catch (err) {
|
|
108615
109078
|
throw new DecisionsParseError(err);
|
|
@@ -108686,9 +109149,9 @@ function targetsOpts(deps) {
|
|
|
108686
109149
|
const file2 = deps.explore?.targetsConfigPath;
|
|
108687
109150
|
return file2 === void 0 ? {} : { targetsFile: file2 };
|
|
108688
109151
|
}
|
|
108689
|
-
function lintFindingLine(
|
|
108690
|
-
const message2 =
|
|
108691
|
-
return `${
|
|
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}`}`;
|
|
108692
109155
|
}
|
|
108693
109156
|
var FULL_FLAG_HELP = "#496: show every detail section: catalog analysis, the full lint list, locator health, provenance, the full change diff";
|
|
108694
109157
|
var DEPRECATED_DIR_HELP = "deprecated: use --journeys-dir <path> (same meaning); default: the repo's .jevitate/journeys, outside a repo ~/.jevitate/journeys";
|
|
@@ -108780,7 +109243,7 @@ function registerJourneyFind(journey, program2, deps) {
|
|
|
108780
109243
|
import { existsSync as existsSync27 } from "node:fs";
|
|
108781
109244
|
|
|
108782
109245
|
// src/journey-annotate-api.ts
|
|
108783
|
-
import { mkdir as
|
|
109246
|
+
import { mkdir as mkdir30, readFile as readFile32, rm as rm12, writeFile as writeFile36 } from "node:fs/promises";
|
|
108784
109247
|
import { dirname as dirname28, join as join62 } from "node:path";
|
|
108785
109248
|
var EVIDENCE_TEXT_CHARS = 1500;
|
|
108786
109249
|
var AnnotationDraftNotFoundError = class extends Error {
|
|
@@ -108928,8 +109391,8 @@ async function annotateJourney(opts) {
|
|
|
108928
109391
|
});
|
|
108929
109392
|
assertNoSecretInPayload(draft, secrets, "journey annotation draft");
|
|
108930
109393
|
const draftPath = annotationDraftPath(opts.dir, opts.id);
|
|
108931
|
-
await
|
|
108932
|
-
await
|
|
109394
|
+
await mkdir30(dirname28(draftPath), { recursive: true, mode: 448 });
|
|
109395
|
+
await writeFile36(draftPath, `${JSON.stringify(draft, null, 2)}
|
|
108933
109396
|
`, { mode: 384 });
|
|
108934
109397
|
return {
|
|
108935
109398
|
id: opts.id,
|
|
@@ -108952,7 +109415,7 @@ async function readAnnotationDraft(dir, id) {
|
|
|
108952
109415
|
const draftPath = annotationDraftPath(dir, id);
|
|
108953
109416
|
let raw;
|
|
108954
109417
|
try {
|
|
108955
|
-
raw = await
|
|
109418
|
+
raw = await readFile32(draftPath, "utf8");
|
|
108956
109419
|
} catch (err) {
|
|
108957
109420
|
if (err.code === "ENOENT") {
|
|
108958
109421
|
throw new AnnotationDraftNotFoundError(`no annotation draft for journey '${id}' (${draftPath}) \u2014 run \`jevitate journey annotate ${id}\` first`);
|
|
@@ -109807,8 +110270,8 @@ var jevReadinessAnalyzer = {
|
|
|
109807
110270
|
// src/journey-stale-api.ts
|
|
109808
110271
|
function isStepIdOnlyStale(journey, snapshot2) {
|
|
109809
110272
|
const approval = journey.metadata.approval;
|
|
109810
|
-
if (approval === void 0 ||
|
|
109811
|
-
if (snapshot2 !== null && journeyReviewHash(snapshot2)
|
|
110273
|
+
if (approval === void 0 || journeyApprovalMatches(journey)) return false;
|
|
110274
|
+
if (snapshot2 !== null && [journeyReviewHash(snapshot2), journeyLegacyReviewHash(snapshot2)].includes(approval.contentHash)) return isStepIdOnlyChange(snapshot2, journey);
|
|
109812
110275
|
return isStepIdOnlyChange(approval.contentHash, journey);
|
|
109813
110276
|
}
|
|
109814
110277
|
async function listStaleJourneys(req) {
|
|
@@ -109822,7 +110285,7 @@ async function listStaleJourneys(req) {
|
|
|
109822
110285
|
const approval = journey.metadata.approval;
|
|
109823
110286
|
if (approval === void 0) continue;
|
|
109824
110287
|
const contentHash2 = journeyReviewHash(journey);
|
|
109825
|
-
const hashStale =
|
|
110288
|
+
const hashStale = !journeyApprovalMatches(journey);
|
|
109826
110289
|
const links = journeyLinks(catalog, catalogJourney(journey));
|
|
109827
110290
|
const catalogStale = !hashStale && links.linked && [links.job?.status, links.persona?.status].includes("stale");
|
|
109828
110291
|
if (!hashStale && !catalogStale) continue;
|
|
@@ -109891,6 +110354,35 @@ async function reviewJourneyById(journeysDir, id, opts = {}) {
|
|
|
109891
110354
|
}
|
|
109892
110355
|
|
|
109893
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
|
+
}
|
|
109894
110386
|
function verifyExitCode(verdict) {
|
|
109895
110387
|
return verdict === "proven" ? 0 : verdict === "insensitive" ? 1 : 2;
|
|
109896
110388
|
}
|
|
@@ -110593,7 +111085,7 @@ function registerJourneyReadiness(journey, program2, deps) {
|
|
|
110593
111085
|
|
|
110594
111086
|
// src/journey-bundle.ts
|
|
110595
111087
|
import { existsSync as existsSync30 } from "node:fs";
|
|
110596
|
-
import { mkdir as
|
|
111088
|
+
import { mkdir as mkdir31, readFile as readFile33, writeFile as writeFile37 } from "node:fs/promises";
|
|
110597
111089
|
import { join as join63, resolve as resolve19 } from "node:path";
|
|
110598
111090
|
var BundleStaleError = class extends Error {
|
|
110599
111091
|
constructor(changed) {
|
|
@@ -110784,9 +111276,9 @@ async function buildJourneyBundle(opts, deps = {}) {
|
|
|
110784
111276
|
verdict: receipt.verdict,
|
|
110785
111277
|
generatedAt: clock.nowIso()
|
|
110786
111278
|
};
|
|
110787
|
-
await
|
|
110788
|
-
await
|
|
110789
|
-
await
|
|
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)}
|
|
110790
111282
|
`, "utf8");
|
|
110791
111283
|
return { out, files: [bundlePath, manifestPath], verdict: receipt.verdict };
|
|
110792
111284
|
}
|
|
@@ -110794,7 +111286,7 @@ async function checkJourneyBundle(dir, opts, deps = {}) {
|
|
|
110794
111286
|
const review = deps.review ?? reviewJourneyById;
|
|
110795
111287
|
const catalogOf = deps.loadCatalog ?? loadCatalog;
|
|
110796
111288
|
const manifestPath = join63(resolve19(dir), "manifest.json");
|
|
110797
|
-
const manifest = JSON.parse(await
|
|
111289
|
+
const manifest = JSON.parse(await readFile33(manifestPath, "utf8"));
|
|
110798
111290
|
const reviewOpts = { strict: true, ...opts.catalogDir === void 0 ? {} : { catalogDir: opts.catalogDir }, ...opts.targetsFile === void 0 ? {} : { targetsFile: opts.targetsFile } };
|
|
110799
111291
|
const changed = [];
|
|
110800
111292
|
for (const recorded of manifest.journeys) {
|
|
@@ -110861,7 +111353,7 @@ function registerJourneyBundle(journey, program2, deps) {
|
|
|
110861
111353
|
}
|
|
110862
111354
|
|
|
110863
111355
|
// src/journey-cli/promote.ts
|
|
110864
|
-
import { readFile as
|
|
111356
|
+
import { readFile as readFile34 } from "node:fs/promises";
|
|
110865
111357
|
|
|
110866
111358
|
// src/review-render-options.ts
|
|
110867
111359
|
function processTerminal2() {
|
|
@@ -110933,7 +111425,7 @@ function registerJourneyPromote(journey, program2, deps) {
|
|
|
110933
111425
|
if (reviewSheet !== void 0) {
|
|
110934
111426
|
let text2;
|
|
110935
111427
|
try {
|
|
110936
|
-
text2 = await
|
|
111428
|
+
text2 = await readFile34(reviewSheet, "utf8");
|
|
110937
111429
|
} catch (e) {
|
|
110938
111430
|
emitJson(program2, fail("E_JOURNEY_REVIEW_ARGS", `cannot read --review-sheet ${reviewSheet}: ${e instanceof Error ? e.message : String(e)}`));
|
|
110939
111431
|
return;
|
|
@@ -110990,7 +111482,7 @@ function registerJourneyPromote(journey, program2, deps) {
|
|
|
110990
111482
|
} else if (err instanceof WeakJourneyError) {
|
|
110991
111483
|
if (!json2) {
|
|
110992
111484
|
const out = program2.configureOutput().writeOut;
|
|
110993
|
-
for (const
|
|
111485
|
+
for (const finding4 of err.findings) out?.(`${lintFindingLine(finding4)}
|
|
110994
111486
|
`);
|
|
110995
111487
|
}
|
|
110996
111488
|
emitJson(program2, fail("E_JOURNEY_WEAK", String(err.message)));
|
|
@@ -111010,7 +111502,22 @@ function registerJourneyPromote(journey, program2, deps) {
|
|
|
111010
111502
|
}
|
|
111011
111503
|
|
|
111012
111504
|
// src/journey-cli/review.ts
|
|
111013
|
-
import { writeFile as
|
|
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
|
+
}
|
|
111014
111521
|
function registerJourneyReview(journey, program2, deps) {
|
|
111015
111522
|
withProjectFlags(journey.command("review [id]")).description(
|
|
111016
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"
|
|
@@ -111051,14 +111558,14 @@ function registerJourneyReview(journey, program2, deps) {
|
|
|
111051
111558
|
const renderOpts = reviewRenderOptions({ full: full === true, toFile: outFile !== void 0 });
|
|
111052
111559
|
const rendered = json2 ? `${JSON.stringify(review, null, 2)}
|
|
111053
111560
|
` : markdown ? renderReviewMarkdown(review, renderOpts) : renderReviewText(review, renderOpts);
|
|
111054
|
-
if (outFile !== void 0) await
|
|
111561
|
+
if (outFile !== void 0) await writeFile38(outFile, rendered, { mode: 384 });
|
|
111055
111562
|
if (json2) {
|
|
111056
111563
|
emitJson(program2, strict === true ? Object.assign(ok(review), { strict: strictVerdict }) : ok(review));
|
|
111057
111564
|
} else if (outFile !== void 0) {
|
|
111058
111565
|
program2.configureOutput().writeOut?.(`review sheet for journey '${review.id}' written to ${outFile} (content hash ${review.contentHash})
|
|
111059
111566
|
`);
|
|
111060
111567
|
} else {
|
|
111061
|
-
program2.configureOutput().writeOut?.(rendered);
|
|
111568
|
+
program2.configureOutput().writeOut?.(`${rendered}${await reviewJourneezeHint(false, outFile)}`);
|
|
111062
111569
|
}
|
|
111063
111570
|
emitUsageLine(program2, review.jev);
|
|
111064
111571
|
process.exitCode = strict === true && !strictVerdict.passed ? EXIT_CODES.defects : 0;
|
|
@@ -111084,8 +111591,8 @@ function registerJourneyReview(journey, program2, deps) {
|
|
|
111084
111591
|
import { existsSync as existsSync31 } from "node:fs";
|
|
111085
111592
|
|
|
111086
111593
|
// src/journey-migrate-api.ts
|
|
111087
|
-
import { readdir as readdir11, readFile as
|
|
111088
|
-
import { join as
|
|
111594
|
+
import { readdir as readdir11, readFile as readFile36, writeFile as writeFile39 } from "node:fs/promises";
|
|
111595
|
+
import { join as join65 } from "node:path";
|
|
111089
111596
|
async function migrateStepIds(req) {
|
|
111090
111597
|
const store = new FsJourneyStore(req.journeysDir);
|
|
111091
111598
|
const journeys = [];
|
|
@@ -111100,7 +111607,7 @@ async function migrateStepIds(req) {
|
|
|
111100
111607
|
if (!req.dryRun) await store.put(after);
|
|
111101
111608
|
journeys.push({ id: meta3.id, stepsMinted, anchorsLinked, needsReapproval: before.metadata.promoted });
|
|
111102
111609
|
}
|
|
111103
|
-
const recordings = req.projectDir === null ? [] : await migrateRegressions(
|
|
111610
|
+
const recordings = req.projectDir === null ? [] : await migrateRegressions(join65(req.projectDir, "regressions"), req.dryRun);
|
|
111104
111611
|
return {
|
|
111105
111612
|
dryRun: req.dryRun,
|
|
111106
111613
|
journeys,
|
|
@@ -111121,11 +111628,11 @@ async function migrateRegressions(dir, dryRun) {
|
|
|
111121
111628
|
}
|
|
111122
111629
|
const out = [];
|
|
111123
111630
|
for (const name of names) {
|
|
111124
|
-
const path2 =
|
|
111125
|
-
const rec = RecordingSchema.parse(JSON.parse(await
|
|
111631
|
+
const path2 = join65(dir, name);
|
|
111632
|
+
const rec = RecordingSchema.parse(JSON.parse(await readFile36(path2, "utf8")));
|
|
111126
111633
|
const next = ensureStepIds(rec);
|
|
111127
111634
|
if (next === rec) continue;
|
|
111128
|
-
if (!dryRun) await
|
|
111635
|
+
if (!dryRun) await writeFile39(path2, `${JSON.stringify(RecordingSchema.parse(next), null, 2)}
|
|
111129
111636
|
`);
|
|
111130
111637
|
out.push({ path: path2, stepsMinted: countRecIds(next) - countRecIds(rec) });
|
|
111131
111638
|
}
|
|
@@ -111379,7 +111886,7 @@ function registerJourneyMigrate(journey, program2, deps) {
|
|
|
111379
111886
|
}
|
|
111380
111887
|
|
|
111381
111888
|
// src/journey-cli/lint.ts
|
|
111382
|
-
import { writeFile as
|
|
111889
|
+
import { writeFile as writeFile40 } from "node:fs/promises";
|
|
111383
111890
|
|
|
111384
111891
|
// src/journey-anchor-api.ts
|
|
111385
111892
|
async function load(dir, id) {
|
|
@@ -111546,16 +112053,16 @@ function renderJourneyLintSarif(input2) {
|
|
|
111546
112053
|
shortDescription: { text: rule.id },
|
|
111547
112054
|
defaultConfiguration: { level: rule.level }
|
|
111548
112055
|
}));
|
|
111549
|
-
const results = input2.findings.map((
|
|
111550
|
-
ruleId:
|
|
111551
|
-
level:
|
|
111552
|
-
message: { text:
|
|
111553
|
-
...
|
|
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 } },
|
|
111554
112061
|
locations: [
|
|
111555
112062
|
{
|
|
111556
112063
|
logicalLocations: [
|
|
111557
112064
|
{ name: input2.id, kind: "module" },
|
|
111558
|
-
...
|
|
112065
|
+
...finding4.step === void 0 ? [] : [{ name: `step ${finding4.step}`, kind: "function" }]
|
|
111559
112066
|
]
|
|
111560
112067
|
}
|
|
111561
112068
|
]
|
|
@@ -111573,7 +112080,7 @@ function renderJourneyLintSarif(input2) {
|
|
|
111573
112080
|
}
|
|
111574
112081
|
|
|
111575
112082
|
// src/journey-link-hints.ts
|
|
111576
|
-
import { join as
|
|
112083
|
+
import { join as join66 } from "node:path";
|
|
111577
112084
|
var MAX_LISTED = 10;
|
|
111578
112085
|
function listed(ids) {
|
|
111579
112086
|
return ids.length <= MAX_LISTED ? ids.join(", ") : `${ids.slice(0, MAX_LISTED).join(", ")}, \u2026 (${ids.length - MAX_LISTED} more: jevitate catalog status)`;
|
|
@@ -111582,7 +112089,7 @@ async function loadLinkCatalog(catalogDir) {
|
|
|
111582
112089
|
const empty = { dir: catalogDir, personasFile: null, jobsFile: null, personas: [], jobs: [], journeys: [] };
|
|
111583
112090
|
if (catalogDir === null) return empty;
|
|
111584
112091
|
try {
|
|
111585
|
-
const loader = new CatalogLoader({ catalogDir, journeysDir:
|
|
112092
|
+
const loader = new CatalogLoader({ catalogDir, journeysDir: join66(catalogDir, "journeys") });
|
|
111586
112093
|
const [{ file: personasFile, personas }, { file: jobsFile, jobs }] = await Promise.all([loader.loadPersonas(), loader.loadJobs()]);
|
|
111587
112094
|
return { ...empty, personasFile, jobsFile, personas, jobs };
|
|
111588
112095
|
} catch {
|
|
@@ -111649,7 +112156,7 @@ function registerJourneyLint(journey, program2, deps) {
|
|
|
111649
112156
|
const result = { ...linted, findings: [...linted.findings, ...linkFindings], warnings: linted.warnings + linkFindings.length };
|
|
111650
112157
|
if (sarif !== void 0) {
|
|
111651
112158
|
const log = renderJourneyLintSarif({ id: result.id, findings: result.findings, version: currentEngineInfo().version });
|
|
111652
|
-
await
|
|
112159
|
+
await writeFile40(sarif, `${JSON.stringify(log, null, 2)}
|
|
111653
112160
|
`);
|
|
111654
112161
|
}
|
|
111655
112162
|
if (json2) {
|
|
@@ -111667,7 +112174,7 @@ function registerJourneyLint(journey, program2, deps) {
|
|
|
111667
112174
|
`);
|
|
111668
112175
|
}
|
|
111669
112176
|
}
|
|
111670
|
-
for (const
|
|
112177
|
+
for (const finding4 of result.findings) out?.(`${lintFindingLine(finding4)}
|
|
111671
112178
|
`);
|
|
111672
112179
|
out?.(`${result.errors} error(s), ${result.warnings} warning(s) \u2014 journey '${result.id}'
|
|
111673
112180
|
`);
|
|
@@ -111686,14 +112193,38 @@ function registerJourneyLint(journey, program2, deps) {
|
|
|
111686
112193
|
// src/journey-cli/verify.ts
|
|
111687
112194
|
import { existsSync as existsSync32 } from "node:fs";
|
|
111688
112195
|
function registerJourneyVerify(journey, program2, deps) {
|
|
111689
|
-
withReplayAllowFlag(withEnvironmentFlags(withBrowserLaunchFlags(withFixtureFlags(withProjectFlags(journey.command("verify <id>")))))).description("prove each assertion
|
|
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) {
|
|
111690
112197
|
const locationFlags = this.opts();
|
|
111691
112198
|
const layout = journeyLayout(program2, deps, { dir: locationFlags.dir, project: locationFlags.project, journeysDir: locationFlags.journeysDir });
|
|
111692
112199
|
if (layout === null) return;
|
|
111693
112200
|
const verifyJourneysDir = layout.journeysDir;
|
|
111694
|
-
const { mutate, param, storageState: storageStateFlag, json: json2, env: envName, baseUrl } = this.opts();
|
|
111695
|
-
if (mutate
|
|
111696
|
-
emitJson(program2, fail("E_JOURNEY_VERIFY_ARGS", "journey verify needs
|
|
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
|
+
}
|
|
111697
112228
|
return;
|
|
111698
112229
|
}
|
|
111699
112230
|
let environment;
|
|
@@ -111835,7 +112366,7 @@ function registerJourneyAnchors(journey, program2, deps) {
|
|
|
111835
112366
|
|
|
111836
112367
|
// src/source-api.ts
|
|
111837
112368
|
import { execFile as execFile6 } from "node:child_process";
|
|
111838
|
-
import { mkdir as
|
|
112369
|
+
import { mkdir as mkdir32 } from "node:fs/promises";
|
|
111839
112370
|
import { promisify as promisify5 } from "node:util";
|
|
111840
112371
|
var execFileAsync5 = promisify5(execFile6);
|
|
111841
112372
|
var NotPromotedError = class extends Error {
|
|
@@ -111873,7 +112404,7 @@ async function requireEntry(lockPath, name) {
|
|
|
111873
112404
|
}
|
|
111874
112405
|
async function addSource(deps, req) {
|
|
111875
112406
|
const mgr = mgrOf(deps);
|
|
111876
|
-
await
|
|
112407
|
+
await mkdir32(deps.sourcesDir, { recursive: true });
|
|
111877
112408
|
const pinnedCommit = await mgr.add(req.name, req.gitUrl);
|
|
111878
112409
|
const manifest = await loadManifest(mgr.resolveDir(req.name));
|
|
111879
112410
|
const touSurface = surfaceForAck(manifest, req.gitUrl);
|
|
@@ -112290,7 +112821,7 @@ review or edit it, then apply it: jevitate journey annotate ${id} --approve
|
|
|
112290
112821
|
|
|
112291
112822
|
// src/journey-demo-cli.ts
|
|
112292
112823
|
import { existsSync as existsSync34 } from "node:fs";
|
|
112293
|
-
import { join as
|
|
112824
|
+
import { join as join67, resolve as resolvePath15 } from "node:path";
|
|
112294
112825
|
function registerJourneyDemoCommand(journey, program2, deps) {
|
|
112295
112826
|
withScreenshotsFlag(withEnvironmentFlags(withDemoFlags(withBrowserLaunchFlags(withEmulationFlags(withFixtureFlags(withProjectFlags(journey.command("demo <id>")))))))).description(
|
|
112296
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)"
|
|
@@ -112333,9 +112864,9 @@ function registerJourneyDemoCommand(journey, program2, deps) {
|
|
|
112333
112864
|
let video = videoFlag === void 0 ? void 0 : resolvePath15(videoFlag);
|
|
112334
112865
|
let guide = guideFlag === void 0 ? void 0 : resolvePath15(guideFlag);
|
|
112335
112866
|
if (video === void 0 && guide === void 0) {
|
|
112336
|
-
const folder =
|
|
112337
|
-
video =
|
|
112338
|
-
guide =
|
|
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");
|
|
112339
112870
|
}
|
|
112340
112871
|
try {
|
|
112341
112872
|
const result = await withSiteGate(
|
|
@@ -112428,7 +112959,7 @@ function registerJourneyCommands(program2, deps) {
|
|
|
112428
112959
|
}
|
|
112429
112960
|
|
|
112430
112961
|
// src/project-where.ts
|
|
112431
|
-
import { join as
|
|
112962
|
+
import { join as join68 } from "node:path";
|
|
112432
112963
|
function render(data) {
|
|
112433
112964
|
const dir = (p) => p ?? "(none)";
|
|
112434
112965
|
return [
|
|
@@ -112445,7 +112976,7 @@ function registerWhere(program2, deps) {
|
|
|
112445
112976
|
const { project, journeysDir, json: json2 } = this.opts();
|
|
112446
112977
|
const layout = journeyLayout(program2, deps, { project, journeysDir });
|
|
112447
112978
|
if (layout === null) return;
|
|
112448
|
-
const logsDir = layout.projectDir === null ? logsRoot() :
|
|
112979
|
+
const logsDir = layout.projectDir === null ? logsRoot() : join68(layout.projectDir, "logs");
|
|
112449
112980
|
const data = { ...layout, logsDir };
|
|
112450
112981
|
if (json2 === true) {
|
|
112451
112982
|
emitJson(program2, ok(data));
|
|
@@ -113263,7 +113794,7 @@ function resolveSecretArgs(values2, env, flag) {
|
|
|
113263
113794
|
}
|
|
113264
113795
|
|
|
113265
113796
|
// src/multi-run-cli.ts
|
|
113266
|
-
import { join as
|
|
113797
|
+
import { join as join69 } from "node:path";
|
|
113267
113798
|
var MULTI_RUN_ATTRS = /* @__PURE__ */ new Set(["repeat", "minAgreement", "persona", "personas", "out", "json"]);
|
|
113268
113799
|
function forwardedArgv(cmd, omit3 = /* @__PURE__ */ new Set()) {
|
|
113269
113800
|
const opts = cmd.opts();
|
|
@@ -113311,7 +113842,7 @@ function lastEnvelope(lines) {
|
|
|
113311
113842
|
}
|
|
113312
113843
|
async function runExploreMultiRun(args) {
|
|
113313
113844
|
const { cmd, plan: plan2 } = args;
|
|
113314
|
-
const outDir = args.out ??
|
|
113845
|
+
const outDir = args.out ?? join69(logsDirFor((args.nowIso ?? (() => clock.nowIso()))()), `multi-${artifactStamp((args.nowIso ?? (() => clock.nowIso()))())}`);
|
|
113315
113846
|
const omit3 = /* @__PURE__ */ new Set([...plan2.personas === null ? [] : ["storageState"], ...args.authPreflight === void 0 ? [] : ["authCheck"]]);
|
|
113316
113847
|
const base = [...forwardedArgv(cmd, omit3), ...args.authPreflight === void 0 ? [] : ["--auth-check", "off"]];
|
|
113317
113848
|
return runMultiRun({
|
|
@@ -113350,9 +113881,9 @@ import { resolve as resolvePath16 } from "node:path";
|
|
|
113350
113881
|
|
|
113351
113882
|
// src/campaign-api.ts
|
|
113352
113883
|
import { existsSync as existsSync37, readFileSync as readFileSync36 } from "node:fs";
|
|
113353
|
-
import { mkdir as
|
|
113884
|
+
import { mkdir as mkdir33, writeFile as writeFile41 } from "node:fs/promises";
|
|
113354
113885
|
import { homedir as homedir5 } from "node:os";
|
|
113355
|
-
import { dirname as dirname29, isAbsolute as isAbsolute11, join as
|
|
113886
|
+
import { dirname as dirname29, isAbsolute as isAbsolute11, join as join70, resolve as resolve21 } from "node:path";
|
|
113356
113887
|
|
|
113357
113888
|
// src/campaign-preflight.ts
|
|
113358
113889
|
import { basename as basename17 } from "node:path";
|
|
@@ -113477,7 +114008,7 @@ async function validateCampaignSpec(raw, specPath, opts) {
|
|
|
113477
114008
|
if (!parsed.success) throw new CampaignSpecError(parsed.error.issues.map((i) => `${at2(i.path)}: ${i.message}`));
|
|
113478
114009
|
const spec = parsed.data;
|
|
113479
114010
|
const base = dirname29(resolve21(specPath));
|
|
113480
|
-
const abs = (p) => p.startsWith("~/") ?
|
|
114011
|
+
const abs = (p) => p.startsWith("~/") ? join70(homedir5(), p.slice(2)) : isAbsolute11(p) ? p : resolve21(base, p);
|
|
113481
114012
|
const problems = [];
|
|
113482
114013
|
let environment;
|
|
113483
114014
|
try {
|
|
@@ -113663,8 +114194,8 @@ function commonArgs(plan2, job) {
|
|
|
113663
114194
|
async function runCampaign(plan2, opts) {
|
|
113664
114195
|
const now2 = opts.nowIso ?? (() => clock.nowIso());
|
|
113665
114196
|
const startedAt = now2();
|
|
113666
|
-
const outDir = opts.outDir ??
|
|
113667
|
-
await
|
|
114197
|
+
const outDir = opts.outDir ?? join70(logsDirFor(startedAt), `campaign-${artifactStamp(startedAt)}`);
|
|
114198
|
+
await mkdir33(outDir, { recursive: true });
|
|
113668
114199
|
const ai = opts.real === true ? ["--real"] : opts.fakeAi === true ? ["--fake-ai"] : [];
|
|
113669
114200
|
const discovery = [];
|
|
113670
114201
|
const replaySlowness = [];
|
|
@@ -113722,8 +114253,8 @@ async function runCampaign(plan2, opts) {
|
|
|
113722
114253
|
}
|
|
113723
114254
|
}
|
|
113724
114255
|
const preflight = opts.preflight === void 0 ? void 0 : opts.preflight === "skip" ? { ok: true, problems: [], skipped: true } : preflightOf(preflightProblems);
|
|
113725
|
-
const resultPath =
|
|
113726
|
-
const reportPath =
|
|
114256
|
+
const resultPath = join70(outDir, "campaign.json");
|
|
114257
|
+
const reportPath = join70(outDir, "campaign.md");
|
|
113727
114258
|
if (preflight !== void 0 && !preflight.ok) {
|
|
113728
114259
|
const refused = {
|
|
113729
114260
|
...plan2.name === void 0 ? {} : { name: plan2.name },
|
|
@@ -113736,7 +114267,7 @@ async function runCampaign(plan2, opts) {
|
|
|
113736
114267
|
missions: [],
|
|
113737
114268
|
resultPath
|
|
113738
114269
|
};
|
|
113739
|
-
await
|
|
114270
|
+
await writeFile41(resultPath, `${JSON.stringify(refused, null, 2)}
|
|
113740
114271
|
`, "utf8");
|
|
113741
114272
|
throw new CampaignPreflightError(preflight.problems, resultPath);
|
|
113742
114273
|
}
|
|
@@ -113757,7 +114288,7 @@ async function runCampaign(plan2, opts) {
|
|
|
113757
114288
|
});
|
|
113758
114289
|
continue;
|
|
113759
114290
|
}
|
|
113760
|
-
const runDir =
|
|
114291
|
+
const runDir = join70(outDir, job.id, `${anchor2.atStep}-${strategy}`);
|
|
113761
114292
|
const argv = [
|
|
113762
114293
|
"explore",
|
|
113763
114294
|
"--from-journey",
|
|
@@ -113849,9 +114380,9 @@ async function runCampaign(plan2, opts) {
|
|
|
113849
114380
|
missionOutcome,
|
|
113850
114381
|
exitCode: exitCodeForOutcome(missionOutcome)
|
|
113851
114382
|
};
|
|
113852
|
-
await
|
|
114383
|
+
await writeFile41(resultPath, `${JSON.stringify(result, null, 2)}
|
|
113853
114384
|
`, "utf8");
|
|
113854
|
-
await
|
|
114385
|
+
await writeFile41(reportPath, campaignMarkdown(result, report2.markdown), "utf8");
|
|
113855
114386
|
return result;
|
|
113856
114387
|
}
|
|
113857
114388
|
function campaignMarkdown(r, reportMarkdown) {
|
|
@@ -114016,7 +114547,7 @@ var GOAL_RUN_OPTIONS = {
|
|
|
114016
114547
|
),
|
|
114017
114548
|
jobWaitMs: () => new Option(
|
|
114018
114549
|
"--job-wait-ms <ms>",
|
|
114019
|
-
`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`
|
|
114020
114551
|
).argParser(positiveIntArg),
|
|
114021
114552
|
deny: () => new Option(
|
|
114022
114553
|
"--deny <pattern>",
|
|
@@ -114052,7 +114583,7 @@ var GOAL_RUN_OPTIONS = {
|
|
|
114052
114583
|
settleIgnore: () => new Option("--settle-ignore <pattern>", "a request URL pattern the target marks as background (never pending work; repeatable, * wildcard)").default([]).argParser(collect7),
|
|
114053
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),
|
|
114054
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),
|
|
114055
|
-
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),
|
|
114056
114587
|
allowSecretCmd: () => new Option(
|
|
114057
114588
|
"--allow-secret-cmd",
|
|
114058
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)"
|
|
@@ -115577,11 +116108,11 @@ import { resolve as resolve24 } from "node:path";
|
|
|
115577
116108
|
|
|
115578
116109
|
// src/sweep-api.ts
|
|
115579
116110
|
import { existsSync as existsSync40, mkdirSync as mkdirSync11, readFileSync as readFileSync37, realpathSync as realpathSync5, statSync as statSync12, writeFileSync as writeFileSync13 } from "node:fs";
|
|
115580
|
-
import { basename as basename19, dirname as dirname31, extname as extname2, join as
|
|
116111
|
+
import { basename as basename19, dirname as dirname31, extname as extname2, join as join72, resolve as resolve23 } from "node:path";
|
|
115581
116112
|
|
|
115582
116113
|
// src/mcp-paths.ts
|
|
115583
116114
|
import { existsSync as existsSync39, realpathSync as realpathSync4 } from "node:fs";
|
|
115584
|
-
import { basename as basename18, dirname as dirname30, isAbsolute as isAbsolute12, join as
|
|
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";
|
|
115585
116116
|
var McpPathError = class extends Error {
|
|
115586
116117
|
error = "invalid_args";
|
|
115587
116118
|
constructor(message2) {
|
|
@@ -115600,7 +116131,7 @@ function realish(p) {
|
|
|
115600
116131
|
for (; ; ) {
|
|
115601
116132
|
if (existsSync39(head2)) {
|
|
115602
116133
|
try {
|
|
115603
|
-
return
|
|
116134
|
+
return join71(realpathSync4(head2), ...tail);
|
|
115604
116135
|
} catch {
|
|
115605
116136
|
return p;
|
|
115606
116137
|
}
|
|
@@ -115960,13 +116491,14 @@ var CLI_TOOL_SPECS = [
|
|
|
115960
116491
|
},
|
|
115961
116492
|
{
|
|
115962
116493
|
name: "verify_journey",
|
|
115963
|
-
description: "`jevitate journey verify <id> --mutate` (#402):
|
|
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.",
|
|
115964
116495
|
command: {
|
|
115965
116496
|
path: "journey verify",
|
|
115966
116497
|
params: {
|
|
115967
116498
|
id: pos(),
|
|
115968
116499
|
...EXTENSION,
|
|
115969
116500
|
mutate: b("--mutate"),
|
|
116501
|
+
plan: b("--plan"),
|
|
115970
116502
|
params: { kind: "params", flag: "--param" },
|
|
115971
116503
|
storageState: session("--storage-state"),
|
|
115972
116504
|
fixtures: path("--fixtures"),
|
|
@@ -117425,7 +117957,7 @@ function aggregateSweep(args) {
|
|
|
117425
117957
|
kind: "sweep",
|
|
117426
117958
|
targetsPath: plan2.targetsPath,
|
|
117427
117959
|
outDir: plan2.outDir,
|
|
117428
|
-
resultPath:
|
|
117960
|
+
resultPath: join72(plan2.outDir, "sweep.result.json"),
|
|
117429
117961
|
startedAt: args.startedAt,
|
|
117430
117962
|
...args.finishedAt === void 0 ? {} : { finishedAt: args.finishedAt },
|
|
117431
117963
|
complete: args.complete && args.aborted === void 0,
|
|
@@ -117466,19 +117998,19 @@ async function awaitLowHostLoad(probe = hostProbe(), waitMs = HOST_STARVED_WAIT_
|
|
|
117466
117998
|
}
|
|
117467
117999
|
}
|
|
117468
118000
|
function defaultSweepOutDir(iso = clock.nowIso()) {
|
|
117469
|
-
return
|
|
118001
|
+
return join72(logsDirFor(iso), `sweep-${artifactStamp(iso)}`);
|
|
117470
118002
|
}
|
|
117471
118003
|
async function runSweep(opts) {
|
|
117472
118004
|
const { plan: plan2 } = opts;
|
|
117473
118005
|
const now2 = opts.nowIso ?? (() => clock.nowIso());
|
|
117474
118006
|
const startedAt = now2();
|
|
117475
118007
|
mkdirSync11(plan2.outDir, { recursive: true });
|
|
117476
|
-
const resultPath =
|
|
118008
|
+
const resultPath = join72(plan2.outDir, "sweep.result.json");
|
|
117477
118009
|
const rows = plan2.targets.map(() => void 0);
|
|
117478
118010
|
const finished = [];
|
|
117479
118011
|
const queue = [];
|
|
117480
118012
|
plan2.targets.forEach((t, i) => {
|
|
117481
|
-
const envelopePath =
|
|
118013
|
+
const envelopePath = join72(plan2.outDir, t.id, "run.envelope.json");
|
|
117482
118014
|
const prior = plan2.resume ? finishedEnvelope(envelopePath) : void 0;
|
|
117483
118015
|
if (prior === void 0) {
|
|
117484
118016
|
queue.push(i);
|
|
@@ -117500,9 +118032,9 @@ async function runSweep(opts) {
|
|
|
117500
118032
|
});
|
|
117501
118033
|
const runOne = async (i) => {
|
|
117502
118034
|
const target = plan2.targets[i];
|
|
117503
|
-
const runDir =
|
|
118035
|
+
const runDir = join72(plan2.outDir, target.id);
|
|
117504
118036
|
mkdirSync11(runDir, { recursive: true });
|
|
117505
|
-
const envelopePath =
|
|
118037
|
+
const envelopePath = join72(runDir, "run.envelope.json");
|
|
117506
118038
|
const attempt2 = async () => {
|
|
117507
118039
|
try {
|
|
117508
118040
|
return await opts.runOnce({ target, argv: targetArgv(plan2, target, runDir), runDir });
|
|
@@ -117978,13 +118510,13 @@ function registerAuthorJourneyCommands(program2, deps) {
|
|
|
117978
118510
|
|
|
117979
118511
|
// src/demo-aspect-cli.ts
|
|
117980
118512
|
import { existsSync as existsSync43 } from "node:fs";
|
|
117981
|
-
import { join as
|
|
118513
|
+
import { join as join75, resolve as resolvePath18 } from "node:path";
|
|
117982
118514
|
|
|
117983
118515
|
// src/demo-aspect-api.ts
|
|
117984
118516
|
import { existsSync as existsSync42 } from "node:fs";
|
|
117985
|
-
import { mkdir as
|
|
118517
|
+
import { mkdir as mkdir35, mkdtemp as mkdtemp5, readFile as readFile37, rm as rm13, writeFile as writeFile43 } from "node:fs/promises";
|
|
117986
118518
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
117987
|
-
import { dirname as dirname32, join as
|
|
118519
|
+
import { dirname as dirname32, join as join74 } from "node:path";
|
|
117988
118520
|
|
|
117989
118521
|
// ../regression/dist/fingerprint.js
|
|
117990
118522
|
function flattenWithUrls(rec) {
|
|
@@ -118129,8 +118661,8 @@ async function minimizeRecording(recording, reproduces) {
|
|
|
118129
118661
|
}
|
|
118130
118662
|
|
|
118131
118663
|
// ../regression/dist/commit.js
|
|
118132
|
-
import { join as
|
|
118133
|
-
import { mkdir as
|
|
118664
|
+
import { join as join73 } from "node:path";
|
|
118665
|
+
import { mkdir as mkdir34, writeFile as writeFile42 } from "node:fs/promises";
|
|
118134
118666
|
var FlakyNotCommittableError = class extends Error {
|
|
118135
118667
|
};
|
|
118136
118668
|
async function commitRegression(dir, id, minimized, report2, bugSummary, oracle) {
|
|
@@ -118138,9 +118670,9 @@ async function commitRegression(dir, id, minimized, report2, bugSummary, oracle)
|
|
|
118138
118670
|
throw new FlakyNotCommittableError(`refusing to commit '${id}': reproduction rate ${report2.rate} (${report2.reproducedCount}/${report2.attempts}) is flaky, not reproducible`);
|
|
118139
118671
|
}
|
|
118140
118672
|
const validated = RecordingSchema.parse(minimized);
|
|
118141
|
-
await
|
|
118142
|
-
const recordingPath =
|
|
118143
|
-
const metaPath =
|
|
118673
|
+
await mkdir34(dir, { recursive: true });
|
|
118674
|
+
const recordingPath = join73(dir, `${id}.recording.json`);
|
|
118675
|
+
const metaPath = join73(dir, `${id}.meta.json`);
|
|
118144
118676
|
const meta3 = {
|
|
118145
118677
|
id,
|
|
118146
118678
|
capturedAtIso: clock.nowIso(),
|
|
@@ -118149,8 +118681,8 @@ async function commitRegression(dir, id, minimized, report2, bugSummary, oracle)
|
|
|
118149
118681
|
bugSummary,
|
|
118150
118682
|
...oracle === void 0 ? {} : { oracle }
|
|
118151
118683
|
};
|
|
118152
|
-
await
|
|
118153
|
-
await
|
|
118684
|
+
await writeFile42(recordingPath, JSON.stringify(validated, null, 2) + "\n");
|
|
118685
|
+
await writeFile42(metaPath, JSON.stringify(meta3, null, 2) + "\n");
|
|
118154
118686
|
return { recordingPath, metaPath };
|
|
118155
118687
|
}
|
|
118156
118688
|
|
|
@@ -118223,7 +118755,7 @@ function demoJourneyId(aspect) {
|
|
|
118223
118755
|
}
|
|
118224
118756
|
var DEMO_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
118225
118757
|
function demoDraftPath(journeysDir, id) {
|
|
118226
|
-
return
|
|
118758
|
+
return join74(journeysDir, ".drafts", `${id}.demo.json`);
|
|
118227
118759
|
}
|
|
118228
118760
|
function demoDraftProblem(v) {
|
|
118229
118761
|
if (v === null || typeof v !== "object" || Array.isArray(v)) return "not an object";
|
|
@@ -118244,7 +118776,7 @@ async function readDemoDraft(journeysDir, id) {
|
|
|
118244
118776
|
const path2 = demoDraftPath(journeysDir, id);
|
|
118245
118777
|
let raw;
|
|
118246
118778
|
try {
|
|
118247
|
-
raw = await
|
|
118779
|
+
raw = await readFile37(path2, "utf8");
|
|
118248
118780
|
} catch (err) {
|
|
118249
118781
|
if (err.code === "ENOENT") throw new DemoDraftNotFoundError(`no demo draft '${id}' (${path2}) \u2014 make one with \`jevitate demo "<aspect>" --env <name>\``);
|
|
118250
118782
|
throw err;
|
|
@@ -118324,16 +118856,16 @@ async function demoAspect(opts) {
|
|
|
118324
118856
|
const maxReplays = opts.maxReplays ?? DEMO_MINIMIZE_MAX_REPLAYS;
|
|
118325
118857
|
const now2 = opts.now ?? (() => clock.nowIso());
|
|
118326
118858
|
const base = { id, aspect, environment: env.name ?? env.baseUrl };
|
|
118327
|
-
const work = await mkdtemp5(
|
|
118859
|
+
const work = await mkdtemp5(join74(tmpdir5(), "jevitate-demo-aspect-"));
|
|
118328
118860
|
try {
|
|
118329
118861
|
const authored = await runAuthorJourney({
|
|
118330
118862
|
url: new URL(start, env.baseUrl).toString(),
|
|
118331
118863
|
goal: aspect,
|
|
118332
118864
|
successAssertion: opts.successAssertion,
|
|
118333
118865
|
allowlist: env.allowedOrigins,
|
|
118334
|
-
journeysDir:
|
|
118866
|
+
journeysDir: join74(work, "explored"),
|
|
118335
118867
|
// #369: the discovery take's own artifacts stay in the scratch dir with the rest.
|
|
118336
|
-
outDir:
|
|
118868
|
+
outDir: join74(work, "explored-runs"),
|
|
118337
118869
|
journeyId: id,
|
|
118338
118870
|
journeyName: aspect,
|
|
118339
118871
|
takes: 1,
|
|
@@ -118354,10 +118886,10 @@ async function demoAspect(opts) {
|
|
|
118354
118886
|
...opts.persona === void 0 ? {} : { persona: opts.persona },
|
|
118355
118887
|
successCriteria: [{ description: `The success check holds: ${opts.successSpec}`, check: opts.successAssertion }]
|
|
118356
118888
|
};
|
|
118357
|
-
const scratch = new FsJourneyStore(
|
|
118889
|
+
const scratch = new FsJourneyStore(join74(work, "candidates"));
|
|
118358
118890
|
const replayOk = async (recording2) => {
|
|
118359
118891
|
await scratch.put({ metadata, recording: withSuccessAssert(recording2, opts.successAssertion) });
|
|
118360
|
-
const run = await runJourneyProgrammatically({ dir:
|
|
118892
|
+
const run = await runJourneyProgrammatically({ dir: join74(work, "candidates"), id, params: {}, ...replayOpts(opts) });
|
|
118361
118893
|
return run.outcome === "ok";
|
|
118362
118894
|
};
|
|
118363
118895
|
const explored = withoutSuccessAssert(authored.journey.recording, opts.successAssertion);
|
|
@@ -118388,7 +118920,7 @@ async function demoAspect(opts) {
|
|
|
118388
118920
|
};
|
|
118389
118921
|
const recording = withSuccessAssert(minimized, opts.successAssertion);
|
|
118390
118922
|
const journey = { metadata: { ...metadata, params: deriveParamSchema(recording).required, createdAtIso: now2() }, recording };
|
|
118391
|
-
const stageDir =
|
|
118923
|
+
const stageDir = join74(work, "stage");
|
|
118392
118924
|
await new FsJourneyStore(stageDir).put(journey);
|
|
118393
118925
|
const annotated = await annotateJourney({ dir: stageDir, id, params: {}, gen: opts.gen, now: now2, ...replayOpts(opts) });
|
|
118394
118926
|
if (annotated.replay.outcome !== "completed") {
|
|
@@ -118401,8 +118933,8 @@ async function demoAspect(opts) {
|
|
|
118401
118933
|
}
|
|
118402
118934
|
await store.put(journey);
|
|
118403
118935
|
const draftPath = annotationDraftPath(opts.journeysDir, id);
|
|
118404
|
-
await
|
|
118405
|
-
await
|
|
118936
|
+
await mkdir35(dirname32(draftPath), { recursive: true, mode: 448 });
|
|
118937
|
+
await writeFile43(draftPath, `${JSON.stringify(annotations, null, 2)}
|
|
118406
118938
|
`, { mode: 384 });
|
|
118407
118939
|
const record2 = {
|
|
118408
118940
|
kind: "jevitate.demo.draft",
|
|
@@ -118416,7 +118948,7 @@ async function demoAspect(opts) {
|
|
|
118416
118948
|
draft: outputsOf(demo)
|
|
118417
118949
|
};
|
|
118418
118950
|
assertNoSecretInPayload(record2, [], "demo draft record");
|
|
118419
|
-
await
|
|
118951
|
+
await writeFile43(recordPath, `${JSON.stringify(record2, null, 2)}
|
|
118420
118952
|
`, { mode: 384 });
|
|
118421
118953
|
return {
|
|
118422
118954
|
...base,
|
|
@@ -118445,8 +118977,8 @@ function renderDemo(dir, id, opts, annotations, draft, outDir, jzMask = false) {
|
|
|
118445
118977
|
dir,
|
|
118446
118978
|
id,
|
|
118447
118979
|
params: {},
|
|
118448
|
-
video:
|
|
118449
|
-
guide:
|
|
118980
|
+
video: join74(outDir, "demo.webm"),
|
|
118981
|
+
guide: join74(outDir, "guide.md"),
|
|
118450
118982
|
annotations,
|
|
118451
118983
|
draft,
|
|
118452
118984
|
environment: opts.environment,
|
|
@@ -118524,7 +119056,7 @@ async function approveDemo(opts) {
|
|
|
118524
119056
|
...title === "" ? {} : { title },
|
|
118525
119057
|
demo
|
|
118526
119058
|
});
|
|
118527
|
-
journeeze = { media: true, record:
|
|
119059
|
+
journeeze = { media: true, record: join74(opts.journeysDir, ".demos", opts.id), ...record2.leftOut.length === 0 ? {} : { leftOut: record2.leftOut } };
|
|
118528
119060
|
} else {
|
|
118529
119061
|
await clearApprovedDemo(opts.journeysDir, opts.id);
|
|
118530
119062
|
journeeze = { media: false, reason: `environment '${environment}' does not declare synthetic: true \u2014 no demo media is exported to Journeeze` };
|
|
@@ -118594,7 +119126,7 @@ function withReplayFlags(cmd) {
|
|
|
118594
119126
|
}
|
|
118595
119127
|
function outDirFor(flag, id, kind) {
|
|
118596
119128
|
if (flag !== void 0) return resolvePath18(flag);
|
|
118597
|
-
return
|
|
119129
|
+
return join75(logsDirFor(), `demo-${id}-${kind}-${artifactStamp(clock.nowIso())}`);
|
|
118598
119130
|
}
|
|
118599
119131
|
function stepLines2(steps) {
|
|
118600
119132
|
return steps.map((s3) => ` ${s3.number}. ${s3.objective ?? s3.step}${s3.expectedResult === void 0 ? "" : `
|
|
@@ -118779,8 +119311,8 @@ ${(j.leftOut ?? []).map((l2) => ` left out: ${l2}
|
|
|
118779
119311
|
}
|
|
118780
119312
|
|
|
118781
119313
|
// src/record-api.ts
|
|
118782
|
-
import { mkdir as
|
|
118783
|
-
import { join as
|
|
119314
|
+
import { mkdir as mkdir36, writeFile as writeFile44 } from "node:fs/promises";
|
|
119315
|
+
import { join as join76 } from "node:path";
|
|
118784
119316
|
async function runRecording(opts) {
|
|
118785
119317
|
const origin = assertAuthorizedExploreTarget(opts.url, opts.allowlist);
|
|
118786
119318
|
const portFactory = opts.browserPortFactory ?? (() => new PlaywrightBrowserPort());
|
|
@@ -118803,10 +119335,10 @@ async function runRecording(opts) {
|
|
|
118803
119335
|
const values2 = Object.fromEntries(authoring.values);
|
|
118804
119336
|
const take = AuthoringTakeSchema.parse({ recording, values: values2 });
|
|
118805
119337
|
const outDir = opts.outDir ?? logsDirFor();
|
|
118806
|
-
await
|
|
119338
|
+
await mkdir36(outDir, { recursive: true });
|
|
118807
119339
|
const iso = (opts.nowIso ?? (() => clock.nowIso()))();
|
|
118808
|
-
const recordingPath =
|
|
118809
|
-
await
|
|
119340
|
+
const recordingPath = join76(outDir, `record-${iso.replace(/[:.]/g, "-")}.json`);
|
|
119341
|
+
await writeFile44(recordingPath, `${JSON.stringify(take, null, 2)}
|
|
118810
119342
|
`, "utf8");
|
|
118811
119343
|
const steps = recording.pages.reduce((n3, p) => n3 + p.steps.length, 0);
|
|
118812
119344
|
return { recording, recordingPath, steps, pages: recording.pages.length, finalUrl };
|
|
@@ -118919,12 +119451,12 @@ function registerRecordCommands(program2, deps) {
|
|
|
118919
119451
|
|
|
118920
119452
|
// src/regression-cli.ts
|
|
118921
119453
|
import { existsSync as existsSync45 } from "node:fs";
|
|
118922
|
-
import { readFile as
|
|
118923
|
-
import { join as
|
|
119454
|
+
import { readFile as readFile39, writeFile as writeFile45 } from "node:fs/promises";
|
|
119455
|
+
import { join as join77 } from "node:path";
|
|
118924
119456
|
|
|
118925
119457
|
// src/regression-api.ts
|
|
118926
119458
|
import { existsSync as existsSync44 } from "node:fs";
|
|
118927
|
-
import { readFile as
|
|
119459
|
+
import { readFile as readFile38 } from "node:fs/promises";
|
|
118928
119460
|
var RegressionExistsError = class extends Error {
|
|
118929
119461
|
code = "E_REGRESSION_EXISTS";
|
|
118930
119462
|
constructor(id, dir) {
|
|
@@ -118950,9 +119482,9 @@ var NoOracleInResultError = class extends Error {
|
|
|
118950
119482
|
};
|
|
118951
119483
|
var RegressionHardSignalOracleError = class extends Error {
|
|
118952
119484
|
code = "E_REGRESSION_HARD_SIGNAL";
|
|
118953
|
-
constructor(resultPath,
|
|
119485
|
+
constructor(resultPath, finding4) {
|
|
118954
119486
|
super(
|
|
118955
|
-
`--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}'.`
|
|
118956
119488
|
);
|
|
118957
119489
|
this.name = "RegressionHardSignalOracleError";
|
|
118958
119490
|
}
|
|
@@ -119014,14 +119546,14 @@ async function runRegressionCapture(opts) {
|
|
|
119014
119546
|
if (opts.force !== true && existsSync44(recordingPath)) {
|
|
119015
119547
|
throw new RegressionExistsError(opts.id, opts.regressionsDir);
|
|
119016
119548
|
}
|
|
119017
|
-
const raw = JSON.parse(await
|
|
119549
|
+
const raw = JSON.parse(await readFile38(opts.failingRecordingPath, "utf8"));
|
|
119018
119550
|
const parsed = RecordingSchema.parse(raw);
|
|
119019
119551
|
const recording = opts.anchored === true ? anchoredRecording(parsed) : parsed;
|
|
119020
119552
|
const attempts = opts.attempts ?? 3;
|
|
119021
119553
|
let missionResult;
|
|
119022
119554
|
let persistedMission;
|
|
119023
119555
|
if (opts.resultPath) {
|
|
119024
|
-
const rawResult = JSON.parse(await
|
|
119556
|
+
const rawResult = JSON.parse(await readFile38(opts.resultPath, "utf8"));
|
|
119025
119557
|
missionResult = rawResult;
|
|
119026
119558
|
try {
|
|
119027
119559
|
persistedMission = parsePersistedMission(rawResult);
|
|
@@ -119030,12 +119562,12 @@ async function runRegressionCapture(opts) {
|
|
|
119030
119562
|
}
|
|
119031
119563
|
}
|
|
119032
119564
|
if (opts.fingerprint !== void 0 && persistedMission !== void 0) {
|
|
119033
|
-
const
|
|
119034
|
-
if (
|
|
119035
|
-
return captureInvariantRegression(opts, recording, persistedMission,
|
|
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);
|
|
119036
119568
|
}
|
|
119037
|
-
if (
|
|
119038
|
-
throw new RegressionHardSignalOracleError(opts.resultPath,
|
|
119569
|
+
if (finding4 !== void 0 && finding4.kind !== "invariant") {
|
|
119570
|
+
throw new RegressionHardSignalOracleError(opts.resultPath, finding4);
|
|
119039
119571
|
}
|
|
119040
119572
|
}
|
|
119041
119573
|
let working = recording;
|
|
@@ -119114,16 +119646,16 @@ async function captureNetworkCheckRegression(opts, recording, check3, attempts)
|
|
|
119114
119646
|
};
|
|
119115
119647
|
return commitRegression(opts.regressionsDir, opts.id, recording, report2, opts.bugSummary, { kind: "network", check: check3 });
|
|
119116
119648
|
}
|
|
119117
|
-
async function captureInvariantRegression(opts, fallbackRecording, mission,
|
|
119649
|
+
async function captureInvariantRegression(opts, fallbackRecording, mission, finding4, attempts) {
|
|
119118
119650
|
const invariantSpec = mission.invariantSpec;
|
|
119119
|
-
const invariantId =
|
|
119120
|
-
const recording =
|
|
119651
|
+
const invariantId = finding4.invariantId;
|
|
119652
|
+
const recording = finding4.recording ?? mission.recording ?? fallbackRecording;
|
|
119121
119653
|
assertAuthorizedExploreTarget(mission.target.seedUrl, mission.target.allowlist);
|
|
119122
119654
|
const verifyResult = await verifyFix({
|
|
119123
119655
|
fixtureBaseDir: journeyFixtureBaseDir(),
|
|
119124
119656
|
recording,
|
|
119125
|
-
recordingStepIndex:
|
|
119126
|
-
fingerprint:
|
|
119657
|
+
recordingStepIndex: finding4.repro.recordingStepIndex,
|
|
119658
|
+
fingerprint: finding4.fingerprint,
|
|
119127
119659
|
defectKind: "invariant",
|
|
119128
119660
|
invariant: { spec: invariantSpec, id: invariantId, allowlist: mission.target.allowlist, baseUrl: mission.target.seedUrl },
|
|
119129
119661
|
replays: attempts,
|
|
@@ -119141,16 +119673,16 @@ async function captureInvariantRegression(opts, fallbackRecording, mission, find
|
|
|
119141
119673
|
rate: 1,
|
|
119142
119674
|
label: "reproducible",
|
|
119143
119675
|
fingerprint: { stepSignature: `invariant:${invariantId}` },
|
|
119144
|
-
firstFailureAt:
|
|
119676
|
+
firstFailureAt: finding4.repro.recordingStepIndex
|
|
119145
119677
|
};
|
|
119146
119678
|
return commitRegression(opts.regressionsDir, opts.id, recording, report2, opts.bugSummary, {
|
|
119147
119679
|
kind: "invariant",
|
|
119148
119680
|
invariantId,
|
|
119149
119681
|
invariantSpec,
|
|
119150
|
-
recordingStepIndex:
|
|
119682
|
+
recordingStepIndex: finding4.repro.recordingStepIndex,
|
|
119151
119683
|
allowlist: mission.target.allowlist,
|
|
119152
119684
|
baseUrl: mission.target.seedUrl,
|
|
119153
|
-
defectFingerprint:
|
|
119685
|
+
defectFingerprint: finding4.fingerprint
|
|
119154
119686
|
});
|
|
119155
119687
|
}
|
|
119156
119688
|
if (verifyResult.verdict === "intermittent") {
|
|
@@ -119173,8 +119705,8 @@ async function runRegressionRun(opts) {
|
|
|
119173
119705
|
let recording;
|
|
119174
119706
|
let meta3;
|
|
119175
119707
|
try {
|
|
119176
|
-
recording = RecordingSchema.parse(JSON.parse(await
|
|
119177
|
-
meta3 = JSON.parse(await
|
|
119708
|
+
recording = RecordingSchema.parse(JSON.parse(await readFile38(recordingPath, "utf8")));
|
|
119709
|
+
meta3 = JSON.parse(await readFile38(metaPath, "utf8"));
|
|
119178
119710
|
} catch {
|
|
119179
119711
|
throw new RegressionNotFoundError(opts.id, opts.regressionsDir);
|
|
119180
119712
|
}
|
|
@@ -119272,17 +119804,17 @@ function registerRegressionCommands(program2, deps) {
|
|
|
119272
119804
|
const opened = [];
|
|
119273
119805
|
let fx;
|
|
119274
119806
|
try {
|
|
119275
|
-
const raw = JSON.parse(await
|
|
119807
|
+
const raw = JSON.parse(await readFile39(from, "utf8"));
|
|
119276
119808
|
const recording = RecordingSchema.parse(raw);
|
|
119277
119809
|
const captureEmulation = captureEmulationFlag ?? (recording.emulation === void 0 ? void 0 : recording.emulation.device !== void 0 ? { device: recording.emulation.device } : { viewport: recording.emulation.viewport });
|
|
119278
119810
|
fx = regressionFixtures(
|
|
119279
119811
|
flags,
|
|
119280
119812
|
recording,
|
|
119281
|
-
resultPath === void 0 ? void 0 : JSON.parse(await
|
|
119813
|
+
resultPath === void 0 ? void 0 : JSON.parse(await readFile39(resultPath, "utf8")),
|
|
119282
119814
|
storageState
|
|
119283
119815
|
);
|
|
119284
119816
|
const replayFixture = fx;
|
|
119285
|
-
const branch = resultPath === void 0 ? void 0 : recordedBranchOf(JSON.parse(await
|
|
119817
|
+
const branch = resultPath === void 0 ? void 0 : recordedBranchOf(JSON.parse(await readFile39(resultPath, "utf8")).result);
|
|
119286
119818
|
const prefix = branch === void 0 ? void 0 : await branchPrefixOrRefuse(branch, param, storageState);
|
|
119287
119819
|
if (prefix === null) return;
|
|
119288
119820
|
const result = await runRegressionCapture({
|
|
@@ -119308,8 +119840,8 @@ function registerRegressionCommands(program2, deps) {
|
|
|
119308
119840
|
}).then((r) => withEngine(r));
|
|
119309
119841
|
await fx?.restore();
|
|
119310
119842
|
if (branch !== void 0 && "metaPath" in result) {
|
|
119311
|
-
const meta3 = JSON.parse(await
|
|
119312
|
-
await
|
|
119843
|
+
const meta3 = JSON.parse(await readFile39(result.metaPath, "utf8"));
|
|
119844
|
+
await writeFile45(result.metaPath, `${JSON.stringify({ ...meta3, branch }, null, 2)}
|
|
119313
119845
|
`);
|
|
119314
119846
|
}
|
|
119315
119847
|
const envelope = ok(fx === void 0 ? result : { ...result, fixtures: fx.record() });
|
|
@@ -119368,17 +119900,17 @@ function registerRegressionCommands(program2, deps) {
|
|
|
119368
119900
|
}
|
|
119369
119901
|
if (refuseUnsafeName(program2, id, "regression id")) return;
|
|
119370
119902
|
const regressionsDir = resolveRegressionsDir(dir);
|
|
119371
|
-
if (!existsSync45(
|
|
119903
|
+
if (!existsSync45(join77(regressionsDir, `${id}.recording.json`))) {
|
|
119372
119904
|
const notFound = new RegressionNotFoundError(id, regressionsDir);
|
|
119373
119905
|
emitJson(program2, fail(notFound.code, notFound.message));
|
|
119374
119906
|
return;
|
|
119375
119907
|
}
|
|
119376
119908
|
const opened = [];
|
|
119377
119909
|
try {
|
|
119378
|
-
const recording = RecordingSchema.parse(JSON.parse(await
|
|
119910
|
+
const recording = RecordingSchema.parse(JSON.parse(await readFile39(join77(regressionsDir, `${id}.recording.json`), "utf8")));
|
|
119379
119911
|
let branch;
|
|
119380
119912
|
try {
|
|
119381
|
-
branch = recordedBranchOf(JSON.parse(await
|
|
119913
|
+
branch = recordedBranchOf(JSON.parse(await readFile39(join77(regressionsDir, `${id}.meta.json`), "utf8")));
|
|
119382
119914
|
} catch {
|
|
119383
119915
|
branch = void 0;
|
|
119384
119916
|
}
|
|
@@ -119652,9 +120184,9 @@ async function queueExploration(targets, queue, args) {
|
|
|
119652
120184
|
}
|
|
119653
120185
|
|
|
119654
120186
|
// ../inbox/dist/atomic.js
|
|
119655
|
-
import { open as open4, rename as
|
|
120187
|
+
import { open as open4, rename as rename7, rm as rm14 } from "node:fs/promises";
|
|
119656
120188
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
119657
|
-
import { dirname as dirname33, join as
|
|
120189
|
+
import { dirname as dirname33, join as join78 } from "node:path";
|
|
119658
120190
|
|
|
119659
120191
|
// ../inbox/dist/durability.js
|
|
119660
120192
|
function durableWritesEnabled(env = process.env) {
|
|
@@ -119667,7 +120199,7 @@ async function flushIfDurable(handle) {
|
|
|
119667
120199
|
|
|
119668
120200
|
// ../inbox/dist/atomic.js
|
|
119669
120201
|
async function writeFileAtomic(path2, data, mode = 384) {
|
|
119670
|
-
const tmp =
|
|
120202
|
+
const tmp = join78(dirname33(path2), `.${randomBytes6(8).toString("hex")}.tmp`);
|
|
119671
120203
|
try {
|
|
119672
120204
|
const handle = await open4(tmp, "wx", mode);
|
|
119673
120205
|
try {
|
|
@@ -119676,7 +120208,7 @@ async function writeFileAtomic(path2, data, mode = 384) {
|
|
|
119676
120208
|
} finally {
|
|
119677
120209
|
await handle.close();
|
|
119678
120210
|
}
|
|
119679
|
-
await
|
|
120211
|
+
await rename7(tmp, path2);
|
|
119680
120212
|
} catch (err) {
|
|
119681
120213
|
await rm14(tmp, { force: true });
|
|
119682
120214
|
throw err;
|
|
@@ -119684,9 +120216,9 @@ async function writeFileAtomic(path2, data, mode = 384) {
|
|
|
119684
120216
|
}
|
|
119685
120217
|
|
|
119686
120218
|
// ../inbox/dist/lock.js
|
|
119687
|
-
import { mkdir as
|
|
120219
|
+
import { mkdir as mkdir37, open as open5, rename as rename8, rm as rm15, stat as stat6 } from "node:fs/promises";
|
|
119688
120220
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
119689
|
-
import { join as
|
|
120221
|
+
import { join as join79 } from "node:path";
|
|
119690
120222
|
var LockTimeoutError = class extends Error {
|
|
119691
120223
|
constructor(id) {
|
|
119692
120224
|
super(`could not acquire inbox lock for '${id}'`);
|
|
@@ -119697,8 +120229,8 @@ async function withIdLock(dir, id, fn, opts = {}) {
|
|
|
119697
120229
|
const staleMs = opts.staleMs ?? 3e4;
|
|
119698
120230
|
const retryMs = opts.retryMs ?? 20;
|
|
119699
120231
|
const maxWaitMs = opts.maxWaitMs ?? 1e4;
|
|
119700
|
-
const lockPath =
|
|
119701
|
-
await
|
|
120232
|
+
const lockPath = join79(dir, `${id}.lock`);
|
|
120233
|
+
await mkdir37(dir, { recursive: true, mode: 448 });
|
|
119702
120234
|
const deadline = clock.now() + maxWaitMs;
|
|
119703
120235
|
for (; ; ) {
|
|
119704
120236
|
try {
|
|
@@ -119712,7 +120244,7 @@ async function withIdLock(dir, id, fn, opts = {}) {
|
|
|
119712
120244
|
const st = await stat6(lockPath);
|
|
119713
120245
|
if (clock.now() - st.mtimeMs > staleMs) {
|
|
119714
120246
|
const claimed = `${lockPath}.reclaim.${randomBytes7(6).toString("hex")}`;
|
|
119715
|
-
await
|
|
120247
|
+
await rename8(lockPath, claimed);
|
|
119716
120248
|
await rm15(claimed, { force: true });
|
|
119717
120249
|
continue;
|
|
119718
120250
|
}
|
|
@@ -119805,8 +120337,8 @@ function resolveTransition(kind, action) {
|
|
|
119805
120337
|
}
|
|
119806
120338
|
|
|
119807
120339
|
// ../inbox/dist/store.js
|
|
119808
|
-
import { mkdir as
|
|
119809
|
-
import { join as
|
|
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";
|
|
119810
120342
|
var InboxItemNotFoundError = class extends Error {
|
|
119811
120343
|
constructor(id) {
|
|
119812
120344
|
super(`inbox item not found: ${id}`);
|
|
@@ -119847,19 +120379,19 @@ var FsInboxStore = class {
|
|
|
119847
120379
|
this.identity = typeof build === "string" ? { version: build } : { version: build.version, commit: build.commit, builtAt: build.builtAt };
|
|
119848
120380
|
}
|
|
119849
120381
|
hotPath(id) {
|
|
119850
|
-
return
|
|
120382
|
+
return join80(this.dir, `${id}.json`);
|
|
119851
120383
|
}
|
|
119852
120384
|
archivePath(id) {
|
|
119853
|
-
return
|
|
120385
|
+
return join80(this.dir, "archive", `${id}.json`);
|
|
119854
120386
|
}
|
|
119855
120387
|
archiveDir() {
|
|
119856
|
-
return
|
|
120388
|
+
return join80(this.dir, "archive");
|
|
119857
120389
|
}
|
|
119858
120390
|
/** Reads and validates the hot file, or null on ENOENT. Throws on malformed content (fail-closed). */
|
|
119859
120391
|
async readHot(id) {
|
|
119860
120392
|
let raw;
|
|
119861
120393
|
try {
|
|
119862
|
-
raw = await
|
|
120394
|
+
raw = await readFile40(this.hotPath(id), "utf8");
|
|
119863
120395
|
} catch (err) {
|
|
119864
120396
|
if (isNodeError(err, "ENOENT"))
|
|
119865
120397
|
return null;
|
|
@@ -119871,7 +120403,7 @@ var FsInboxStore = class {
|
|
|
119871
120403
|
async readArchive(id) {
|
|
119872
120404
|
let raw;
|
|
119873
120405
|
try {
|
|
119874
|
-
raw = await
|
|
120406
|
+
raw = await readFile40(this.archivePath(id), "utf8");
|
|
119875
120407
|
} catch (err) {
|
|
119876
120408
|
if (isNodeError(err, "ENOENT"))
|
|
119877
120409
|
return null;
|
|
@@ -119883,7 +120415,7 @@ var FsInboxStore = class {
|
|
|
119883
120415
|
const validated = InboxItemSchema.parse(item);
|
|
119884
120416
|
assertSafeInboxId(validated.id);
|
|
119885
120417
|
await withIdLock(this.dir, validated.id, async () => {
|
|
119886
|
-
await
|
|
120418
|
+
await mkdir38(this.dir, { recursive: true, mode: 448 });
|
|
119887
120419
|
const serialized = JSON.stringify(validated);
|
|
119888
120420
|
let fh;
|
|
119889
120421
|
try {
|
|
@@ -119917,7 +120449,7 @@ var FsInboxStore = class {
|
|
|
119917
120449
|
if (entry.startsWith("."))
|
|
119918
120450
|
continue;
|
|
119919
120451
|
try {
|
|
119920
|
-
const raw = await
|
|
120452
|
+
const raw = await readFile40(join80(this.dir, entry), "utf8");
|
|
119921
120453
|
const parsed = InboxItemSchema.parse(JSON.parse(raw));
|
|
119922
120454
|
if (parsed.status === "pending")
|
|
119923
120455
|
items.push(parsed);
|
|
@@ -119986,7 +120518,7 @@ var FsInboxStore = class {
|
|
|
119986
120518
|
const decision = transition === "approved" || transition === "rejected" || transition === "resolved" ? transition : "resolved";
|
|
119987
120519
|
updated.resolution = { by: "human", decision, at: now2 };
|
|
119988
120520
|
const validated = InboxItemSchema.parse(updated);
|
|
119989
|
-
await
|
|
120521
|
+
await mkdir38(this.archiveDir(), { recursive: true, mode: 448 });
|
|
119990
120522
|
await writeFileAtomic(this.archivePath(id), JSON.stringify(validated));
|
|
119991
120523
|
await unlink(this.hotPath(id));
|
|
119992
120524
|
return validated;
|
|
@@ -120038,7 +120570,7 @@ var FsInboxStore = class {
|
|
|
120038
120570
|
const updated = { ...item, status: "expired", resolvedAt: at3 };
|
|
120039
120571
|
delete updated.humanInput;
|
|
120040
120572
|
const validated = InboxItemSchema.parse(updated);
|
|
120041
|
-
await
|
|
120573
|
+
await mkdir38(this.archiveDir(), { recursive: true, mode: 448 });
|
|
120042
120574
|
await writeFileAtomic(this.archivePath(id), JSON.stringify(validated));
|
|
120043
120575
|
await unlink(this.hotPath(id));
|
|
120044
120576
|
return true;
|
|
@@ -120066,7 +120598,7 @@ var FsInboxStore = class {
|
|
|
120066
120598
|
for (const entry of entries) {
|
|
120067
120599
|
if (!entry.endsWith(".json") || entry.startsWith("."))
|
|
120068
120600
|
continue;
|
|
120069
|
-
const full =
|
|
120601
|
+
const full = join80(this.dir, entry);
|
|
120070
120602
|
try {
|
|
120071
120603
|
const st = await stat7(full);
|
|
120072
120604
|
if (!st.isFile())
|
|
@@ -120261,17 +120793,17 @@ function isDefectStatus(v) {
|
|
|
120261
120793
|
return typeof v === "string" && DEFECT_OUTCOME_STATUSES.includes(v);
|
|
120262
120794
|
}
|
|
120263
120795
|
function parseResultOutcome(missionOutcome, goalOutcome, defectStatus) {
|
|
120264
|
-
const
|
|
120265
|
-
if (
|
|
120796
|
+
const canonical6 = parseMissionOutcome(missionOutcome);
|
|
120797
|
+
if (canonical6 !== null) {
|
|
120266
120798
|
if (goalOutcome === void 0)
|
|
120267
|
-
return { outcome:
|
|
120799
|
+
return { outcome: canonical6 };
|
|
120268
120800
|
if (!isGoalOutcome(goalOutcome))
|
|
120269
120801
|
return null;
|
|
120270
120802
|
if (defectStatus === void 0)
|
|
120271
|
-
return foldGoalOutcome(goalOutcome) ===
|
|
120803
|
+
return foldGoalOutcome(goalOutcome) === canonical6 ? { outcome: canonical6, goalOutcome } : null;
|
|
120272
120804
|
if (!isDefectStatus(defectStatus))
|
|
120273
120805
|
return null;
|
|
120274
|
-
return goalMissionOutcome(goalOutcome, defectStatus) ===
|
|
120806
|
+
return goalMissionOutcome(goalOutcome, defectStatus) === canonical6 ? { outcome: canonical6, goalOutcome } : null;
|
|
120275
120807
|
}
|
|
120276
120808
|
if (goalOutcome === void 0 && typeof missionOutcome === "string" && Object.hasOwn(LEGACY_GOAL_OUTCOMES, missionOutcome)) {
|
|
120277
120809
|
return { outcome: LEGACY_GOAL_OUTCOMES[missionOutcome], goalOutcome: missionOutcome };
|
|
@@ -120345,8 +120877,8 @@ var tagsArg = (args) => {
|
|
|
120345
120877
|
|
|
120346
120878
|
// src/mcp-tools/findings.ts
|
|
120347
120879
|
import { existsSync as existsSync47 } from "node:fs";
|
|
120348
|
-
import { access, readFile as
|
|
120349
|
-
import { join as
|
|
120880
|
+
import { access, readFile as readFile41 } from "node:fs/promises";
|
|
120881
|
+
import { join as join81 } from "node:path";
|
|
120350
120882
|
function findingTools(ctx) {
|
|
120351
120883
|
const { deps, pathRoots, verifyFixImpl } = ctx;
|
|
120352
120884
|
const resolveResultId = async (id, tool) => {
|
|
@@ -120389,7 +120921,7 @@ function findingTools(ctx) {
|
|
|
120389
120921
|
const resultPathsFor = (resultId) => {
|
|
120390
120922
|
const file2 = missionResultFileName(resultId);
|
|
120391
120923
|
const dirs = deps.resultDirsFor?.(resultId) ?? [deps.recordingsDir, ...resultId.startsWith("usability-") ? [deps.uxReportsDir] : []];
|
|
120392
|
-
return dirs.filter((d) => d !== void 0).map((d) =>
|
|
120924
|
+
return dirs.filter((d) => d !== void 0).map((d) => join81(d, file2));
|
|
120393
120925
|
};
|
|
120394
120926
|
const getMissionResult = async (args) => {
|
|
120395
120927
|
if (!deps.recordingsDir) {
|
|
@@ -120401,7 +120933,7 @@ function findingTools(ctx) {
|
|
|
120401
120933
|
let raw;
|
|
120402
120934
|
for (const path2 of resultPathsFor(ref.resultId)) {
|
|
120403
120935
|
try {
|
|
120404
|
-
raw = await
|
|
120936
|
+
raw = await readFile41(path2, "utf8");
|
|
120405
120937
|
break;
|
|
120406
120938
|
} catch {
|
|
120407
120939
|
}
|
|
@@ -121467,9 +121999,9 @@ function registerMissionCommands(program2, deps) {
|
|
|
121467
121999
|
// src/ui-api.ts
|
|
121468
122000
|
import { createServer } from "node:http";
|
|
121469
122001
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
121470
|
-
import { readFile as
|
|
122002
|
+
import { readFile as readFile42, realpath as realpath2 } from "node:fs/promises";
|
|
121471
122003
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
121472
|
-
import { join as
|
|
122004
|
+
import { join as join82, sep as sep11 } from "node:path";
|
|
121473
122005
|
import { spawn as spawn6 } from "node:child_process";
|
|
121474
122006
|
var DEFAULT_PORT = 4180;
|
|
121475
122007
|
var MAX_PORT_ATTEMPTS = 20;
|
|
@@ -121571,7 +122103,7 @@ async function startUiServer(deps) {
|
|
|
121571
122103
|
const shouldOpen = deps.open ?? true;
|
|
121572
122104
|
const indexHtmlPath = fileURLToPath4(new URL("../ui/index.html", import.meta.url));
|
|
121573
122105
|
const appJsPath = fileURLToPath4(new URL("../ui/app.js", import.meta.url));
|
|
121574
|
-
const screenshotsDir =
|
|
122106
|
+
const screenshotsDir = join82(deps.inboxDir, "screenshots");
|
|
121575
122107
|
const server = createServer();
|
|
121576
122108
|
let boundPort = 0;
|
|
121577
122109
|
function send(res, status, body, headers = {}) {
|
|
@@ -121583,7 +122115,7 @@ async function startUiServer(deps) {
|
|
|
121583
122115
|
async function serveIndex(req, res) {
|
|
121584
122116
|
const reqUrl = new URL(req.url ?? "/", `http://127.0.0.1:${boundPort}`);
|
|
121585
122117
|
const t = reqUrl.searchParams.get("t");
|
|
121586
|
-
const html = await
|
|
122118
|
+
const html = await readFile42(indexHtmlPath, "utf8");
|
|
121587
122119
|
const headers = {
|
|
121588
122120
|
"Content-Type": "text/html; charset=utf-8",
|
|
121589
122121
|
"Content-Security-Policy": "default-src 'self'; img-src 'self'; style-src 'self' 'unsafe-inline'"
|
|
@@ -121596,7 +122128,7 @@ async function startUiServer(deps) {
|
|
|
121596
122128
|
return 200;
|
|
121597
122129
|
}
|
|
121598
122130
|
async function serveAppJs(res) {
|
|
121599
|
-
const js = await
|
|
122131
|
+
const js = await readFile42(appJsPath, "utf8");
|
|
121600
122132
|
res.writeHead(200, { "Content-Type": "text/javascript" });
|
|
121601
122133
|
res.end(js);
|
|
121602
122134
|
return 200;
|
|
@@ -121609,7 +122141,7 @@ async function startUiServer(deps) {
|
|
|
121609
122141
|
} catch {
|
|
121610
122142
|
return send(res, 404, { error: "not_found" });
|
|
121611
122143
|
}
|
|
121612
|
-
const candidate =
|
|
122144
|
+
const candidate = join82(screenshotsDir, `${id}.png`);
|
|
121613
122145
|
let resolved;
|
|
121614
122146
|
try {
|
|
121615
122147
|
resolved = await realpath2(candidate);
|
|
@@ -121620,7 +122152,7 @@ async function startUiServer(deps) {
|
|
|
121620
122152
|
return send(res, 404, { error: "not_found" });
|
|
121621
122153
|
}
|
|
121622
122154
|
try {
|
|
121623
|
-
const data = await
|
|
122155
|
+
const data = await readFile42(resolved);
|
|
121624
122156
|
res.writeHead(200, { "Content-Type": "image/png" });
|
|
121625
122157
|
res.end(data);
|
|
121626
122158
|
return 200;
|
|
@@ -121860,7 +122392,7 @@ function registerServeCommands(program2, deps, buildProgram2) {
|
|
|
121860
122392
|
}
|
|
121861
122393
|
|
|
121862
122394
|
// src/ux-cli.ts
|
|
121863
|
-
import { readFile as
|
|
122395
|
+
import { readFile as readFile43 } from "node:fs/promises";
|
|
121864
122396
|
function registerUxCommands(program2, deps) {
|
|
121865
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(
|
|
121866
122398
|
"--show <labels>",
|
|
@@ -121888,7 +122420,7 @@ function registerUxCommands(program2, deps) {
|
|
|
121888
122420
|
}
|
|
121889
122421
|
let recording;
|
|
121890
122422
|
try {
|
|
121891
|
-
recording = RecordingSchema.parse(JSON.parse(await
|
|
122423
|
+
recording = RecordingSchema.parse(JSON.parse(await readFile43(recordingPath, "utf8")));
|
|
121892
122424
|
} catch (err) {
|
|
121893
122425
|
emitJson(program2, fail("E_UX_INPUT", String(err instanceof Error ? err.message : err)));
|
|
121894
122426
|
return;
|
|
@@ -121948,7 +122480,7 @@ function registerUxCommands(program2, deps) {
|
|
|
121948
122480
|
|
|
121949
122481
|
// src/logs-retention.ts
|
|
121950
122482
|
import { readFileSync as readFileSync39, readdirSync as readdirSync11, rmSync as rmSync2, rmdirSync as rmdirSync2, statSync as statSync13 } from "node:fs";
|
|
121951
|
-
import { join as
|
|
122483
|
+
import { join as join83 } from "node:path";
|
|
121952
122484
|
var DEFAULT_LOGS_RETENTION = { ttlDays: 14, keepLatest: 50 };
|
|
121953
122485
|
var LogsConfigError = class extends Error {
|
|
121954
122486
|
code = "E_LOGS_CONFIG";
|
|
@@ -121990,8 +122522,8 @@ function runsIn(root2) {
|
|
|
121990
122522
|
}
|
|
121991
122523
|
const byKey = /* @__PURE__ */ new Map();
|
|
121992
122524
|
for (const date5 of dates) {
|
|
121993
|
-
for (const name of readdirSync11(
|
|
121994
|
-
const path2 =
|
|
122525
|
+
for (const name of readdirSync11(join83(root2, date5))) {
|
|
122526
|
+
const path2 = join83(root2, date5, name);
|
|
121995
122527
|
const key2 = `${date5}/${RUN_KEY.exec(name)?.[1] ?? name}`;
|
|
121996
122528
|
const run = byKey.get(key2) ?? { paths: [], mtimeMs: 0 };
|
|
121997
122529
|
run.paths.push(path2);
|
|
@@ -121999,7 +122531,7 @@ function runsIn(root2) {
|
|
|
121999
122531
|
byKey.set(key2, run);
|
|
122000
122532
|
}
|
|
122001
122533
|
}
|
|
122002
|
-
return { runs: [...byKey].map(([key2, r]) => ({ key: key2, ...r })), dateDirs: dates.map((d) =>
|
|
122534
|
+
return { runs: [...byKey].map(([key2, r]) => ({ key: key2, ...r })), dateDirs: dates.map((d) => join83(root2, d)) };
|
|
122003
122535
|
}
|
|
122004
122536
|
function pruneLogs(root2, retention, opts = {}) {
|
|
122005
122537
|
const nowMs = opts.nowMs ?? clock.now();
|
|
@@ -122247,6 +122779,119 @@ function registerInboxCommands(program2, deps) {
|
|
|
122247
122779
|
}
|
|
122248
122780
|
}
|
|
122249
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, "&").replace(/</g, "<").replace(/"/g, """);
|
|
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
|
+
|
|
122250
122895
|
// src/program.ts
|
|
122251
122896
|
function versionString() {
|
|
122252
122897
|
return formatVersion(currentEngineInfo());
|
|
@@ -122286,6 +122931,7 @@ function buildProgram(deps) {
|
|
|
122286
122931
|
registerMissionCommands(program2, deps);
|
|
122287
122932
|
registerServeCommands(program2, deps, buildProgram);
|
|
122288
122933
|
registerInboxCommands(program2, deps);
|
|
122934
|
+
registerAcceptanceAuditCommands(program2);
|
|
122289
122935
|
registerUxCommands(program2, deps);
|
|
122290
122936
|
registerAiCommands(program2, deps);
|
|
122291
122937
|
registerCheckCommand(
|