@jevitate/cli 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +359 -90
- package/dist/build-info.generated.js +3 -3
- package/dist/journeeze-api.d.ts +5 -0
- package/dist/journeeze-api.js +24 -9
- package/dist/journeeze-api.js.map +1 -1
- package/dist/journeeze-connect.d.ts +2 -0
- package/dist/journeeze-connect.js +9 -1
- package/dist/journeeze-connect.js.map +1 -1
- package/dist/journey-api.js +5 -1
- package/dist/journey-api.js.map +1 -1
- package/dist/journey-bundle.js +35 -20
- package/dist/journey-bundle.js.map +1 -1
- package/dist/journey-chain.d.ts +6 -1
- package/dist/journey-chain.js +27 -10
- package/dist/journey-chain.js.map +1 -1
- package/dist/journey-cli/chain.js +4 -2
- package/dist/journey-cli/chain.js.map +1 -1
- package/dist/journey-cli/lint.js +2 -2
- package/dist/journey-cli/lint.js.map +1 -1
- package/dist/journey-cli/migrate.js +2 -2
- package/dist/journey-cli/migrate.js.map +1 -1
- package/dist/journey-cli/promote.js +4 -4
- package/dist/journey-cli/promote.js.map +1 -1
- package/dist/journey-cli/review.js +4 -4
- package/dist/journey-cli/review.js.map +1 -1
- package/dist/journey-cli/shared.d.ts +6 -0
- package/dist/journey-cli/shared.js +8 -0
- package/dist/journey-cli/shared.js.map +1 -1
- package/dist/journey-cli/verify.js +2 -1
- package/dist/journey-cli/verify.js.map +1 -1
- package/dist/journey-cli-bundle.js +2 -2
- package/dist/journey-cli-bundle.js.map +1 -1
- package/dist/journey-cli-readiness.js +2 -2
- package/dist/journey-cli-readiness.js.map +1 -1
- package/dist/journey-handback.js +3 -1
- package/dist/journey-handback.js.map +1 -1
- package/dist/journey-readiness.js +90 -8
- package/dist/journey-readiness.js.map +1 -1
- package/dist/journey-review.js +4 -3
- package/dist/journey-review.js.map +1 -1
- package/dist/journey-verify.d.ts +8 -2
- package/dist/journey-verify.js +41 -7
- 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/package.json +10 -10
package/dist/bin.js
CHANGED
|
@@ -53836,13 +53836,14 @@ function relaxedMutationReplay(journey, m, opts = {}) {
|
|
|
53836
53836
|
return { journey, skipIndex: index };
|
|
53837
53837
|
if (m.kind === "swap-param" || m.kind === "truncate-param" || m.kind === "guard-param") {
|
|
53838
53838
|
const how = paramReplay(journey, m, opts.params ?? {});
|
|
53839
|
-
const
|
|
53839
|
+
const copy2 = how.journey === journey ? structuredClone(journey) : how.journey;
|
|
53840
|
+
const target2 = flat(copy2)[index];
|
|
53840
53841
|
if (target2 !== void 0 && "expect" in target2.step) {
|
|
53841
53842
|
const step = { ...target2.step, expect: relaxedExpect(target2.step) };
|
|
53842
53843
|
delete step.waitFor;
|
|
53843
53844
|
target2.step = step;
|
|
53844
53845
|
}
|
|
53845
|
-
return how;
|
|
53846
|
+
return { ...how, journey: copy2 };
|
|
53846
53847
|
}
|
|
53847
53848
|
if (m.kind === "block-write") {
|
|
53848
53849
|
const requests = journeyWriteSteps(journey, opts).find((w) => w.step === m.step)?.requests ?? [];
|
|
@@ -53866,6 +53867,119 @@ function relaxedMutationReplay(journey, m, opts = {}) {
|
|
|
53866
53867
|
}
|
|
53867
53868
|
return { journey: copy };
|
|
53868
53869
|
}
|
|
53870
|
+
var MAX_TARGETED_RELAXATIONS = 4;
|
|
53871
|
+
function failedSiteKey(f2) {
|
|
53872
|
+
if (f2.where === "step")
|
|
53873
|
+
return `step:${f2.step}`;
|
|
53874
|
+
if (f2.where === "step-request")
|
|
53875
|
+
return `step-request:${f2.step}:${f2.checkIndex}`;
|
|
53876
|
+
if (f2.where === "end-state")
|
|
53877
|
+
return `end-state:${f2.index}`;
|
|
53878
|
+
return "unevaluable";
|
|
53879
|
+
}
|
|
53880
|
+
function targetedMutationReplay(journey, m, targetSiteKey, relaxedSiteKeys, opts = {}) {
|
|
53881
|
+
if (relaxedSiteKeys.includes(targetSiteKey))
|
|
53882
|
+
throw new Error(`${targetSiteKey} is the target assertion \u2014 a targeted proof never relaxes it`);
|
|
53883
|
+
const base = relaxedMutationReplay(journey, m, opts);
|
|
53884
|
+
if (relaxedSiteKeys.length === 0)
|
|
53885
|
+
return base;
|
|
53886
|
+
return { ...base, journey: relaxStepSites(journey, base.journey, relaxedSiteKeys) };
|
|
53887
|
+
}
|
|
53888
|
+
function targetedControlReplay(journey, m, relaxedSiteKeys) {
|
|
53889
|
+
const own2 = flat(journey)[m.step - 1]?.step;
|
|
53890
|
+
const ownKey = own2 !== void 0 && (own2.kind === "assert" || "expect" in own2) ? [`step:${m.step}`] : [];
|
|
53891
|
+
return { journey: relaxStepSites(journey, journey, [.../* @__PURE__ */ new Set([...ownKey, ...relaxedSiteKeys])]) };
|
|
53892
|
+
}
|
|
53893
|
+
function relaxStepSites(journey, from, keys) {
|
|
53894
|
+
const byKey = new Map(journeyAssertions(journey).map((s3) => [assertionSiteKey(s3), s3]));
|
|
53895
|
+
const copy = structuredClone(from);
|
|
53896
|
+
const steps = flat(copy);
|
|
53897
|
+
for (const key2 of keys) {
|
|
53898
|
+
const match2 = /^step:([1-9][0-9]*)$/.exec(key2);
|
|
53899
|
+
if (match2 === null)
|
|
53900
|
+
throw new Error(`${key2} is not a step assertion site \u2014 a targeted proof only relaxes step:<n>`);
|
|
53901
|
+
const site = byKey.get(key2);
|
|
53902
|
+
if (site !== void 0 && site.where === "step" && site.field === "resume")
|
|
53903
|
+
throw new Error(`${key2} is a resume assertion \u2014 a targeted proof never relaxes it`);
|
|
53904
|
+
const recorded = steps[Number(match2[1]) - 1];
|
|
53905
|
+
if (recorded === void 0)
|
|
53906
|
+
throw new Error(`journey '${journey.metadata.id}' has no step ${match2[1]} to relax`);
|
|
53907
|
+
const step = { ...recorded.step };
|
|
53908
|
+
if (recorded.step.kind === "assert")
|
|
53909
|
+
step.check = { kind: "urlIncludes", text: "" };
|
|
53910
|
+
else if ("expect" in recorded.step)
|
|
53911
|
+
step.expect = relaxedExpect(recorded.step);
|
|
53912
|
+
else
|
|
53913
|
+
throw new Error(`${key2} has no page postcondition to relax`);
|
|
53914
|
+
delete step.waitFor;
|
|
53915
|
+
recorded.step = step;
|
|
53916
|
+
}
|
|
53917
|
+
return copy;
|
|
53918
|
+
}
|
|
53919
|
+
function nextTargetedRelaxation(journey, plan2, m, targetSiteKey, result, relaxedSoFar) {
|
|
53920
|
+
const target = journeyAssertions(journey).find((s3) => assertionSiteKey(s3) === targetSiteKey);
|
|
53921
|
+
if (target === void 0)
|
|
53922
|
+
return { refuse: `${targetSiteKey} is not an assertion site of this Journey` };
|
|
53923
|
+
if (classifyMutation(target, m, result) !== "cascade")
|
|
53924
|
+
return null;
|
|
53925
|
+
const first = result.failedSites[0];
|
|
53926
|
+
if (first === void 0)
|
|
53927
|
+
return { refuse: "no failed site to relax" };
|
|
53928
|
+
if (first.where === "unevaluable")
|
|
53929
|
+
return { refuse: "unevaluable end state" };
|
|
53930
|
+
if (first.where !== "step")
|
|
53931
|
+
return { refuse: `${failedSiteKey(first)} is not a step postcondition` };
|
|
53932
|
+
const key2 = `step:${first.step}`;
|
|
53933
|
+
if (!first.postcondition)
|
|
53934
|
+
return { refuse: `${key2} action failed (not a postcondition)` };
|
|
53935
|
+
if (first.step < m.step)
|
|
53936
|
+
return { refuse: "failure before the mutated step" };
|
|
53937
|
+
const step = flat(journey)[first.step - 1]?.step;
|
|
53938
|
+
if (step === void 0)
|
|
53939
|
+
return { refuse: `${key2} does not exist` };
|
|
53940
|
+
if (step.kind === "handback")
|
|
53941
|
+
return { refuse: `${key2} is a resume assertion (not a relaxable page postcondition)` };
|
|
53942
|
+
if (target.where === "step" && target.step <= first.step)
|
|
53943
|
+
return { refuse: `${key2} is not before the target ${targetSiteKey}` };
|
|
53944
|
+
if (!plan2.pairs.some((p) => p.site === key2 && p.mutation === m.id))
|
|
53945
|
+
return { refuse: `${key2} is not paired with ${m.id}` };
|
|
53946
|
+
if (relaxedSoFar.includes(key2))
|
|
53947
|
+
return { refuse: `${key2} is already relaxed` };
|
|
53948
|
+
if (relaxedSoFar.length >= MAX_TARGETED_RELAXATIONS)
|
|
53949
|
+
return { refuse: `relaxation bound ${MAX_TARGETED_RELAXATIONS} reached` };
|
|
53950
|
+
return { relax: key2 };
|
|
53951
|
+
}
|
|
53952
|
+
async function runTargetedProof(journey, plan2, m, target, initial, replay, opts = {}) {
|
|
53953
|
+
const site = assertionSiteKey(target);
|
|
53954
|
+
const relaxedSites = [];
|
|
53955
|
+
const relaxedFirst = initial.relaxed !== void 0 && initial.failedSites.some((f2) => f2.where === "step" && f2.step === m.step && f2.postcondition) && !(target.where === "step" && target.step === m.step);
|
|
53956
|
+
let run = relaxedFirst ? { outcome: initial.relaxed.outcome, failedSites: initial.relaxed.failedSites } : { outcome: initial.outcome, failedSites: initial.failedSites };
|
|
53957
|
+
for (; ; ) {
|
|
53958
|
+
const next = nextTargetedRelaxation(journey, plan2, m, site, run, relaxedSites);
|
|
53959
|
+
if (next === null)
|
|
53960
|
+
break;
|
|
53961
|
+
if ("refuse" in next)
|
|
53962
|
+
return { site, relaxedSites, outcome: run.outcome, failedSites: run.failedSites, refused: next.refuse };
|
|
53963
|
+
relaxedSites.push(next.relax);
|
|
53964
|
+
const relaxedNow = [...relaxedSites];
|
|
53965
|
+
run = await replay((j) => targetedMutationReplay(j, m, site, relaxedNow, opts), "targeted");
|
|
53966
|
+
}
|
|
53967
|
+
if (relaxedSites.length > 0 && classifyMutationPlain(target, m, run) === "sensitive") {
|
|
53968
|
+
const control = await replay((j) => targetedControlReplay(j, m, relaxedSites), "control");
|
|
53969
|
+
if (control.outcome !== "passed") {
|
|
53970
|
+
return {
|
|
53971
|
+
site,
|
|
53972
|
+
relaxedSites,
|
|
53973
|
+
outcome: run.outcome,
|
|
53974
|
+
failedSites: run.failedSites,
|
|
53975
|
+
control: control.outcome,
|
|
53976
|
+
refused: `the unmutated control replay with ${relaxedSites.join(", ")} relaxed did not pass (${control.outcome})`
|
|
53977
|
+
};
|
|
53978
|
+
}
|
|
53979
|
+
return { site, relaxedSites, outcome: run.outcome, failedSites: run.failedSites, control: control.outcome };
|
|
53980
|
+
}
|
|
53981
|
+
return { site, relaxedSites, outcome: run.outcome, failedSites: run.failedSites };
|
|
53982
|
+
}
|
|
53869
53983
|
function needsRelaxedReplay(plan2, m, result) {
|
|
53870
53984
|
if (result.outcome !== "failed")
|
|
53871
53985
|
return false;
|
|
@@ -53875,8 +53989,18 @@ function needsRelaxedReplay(plan2, m, result) {
|
|
|
53875
53989
|
return plan2.pairs.some((p) => p.mutation === m.id && p.site !== own2);
|
|
53876
53990
|
}
|
|
53877
53991
|
function classifyMutation(site, m, result) {
|
|
53992
|
+
const verdict = classifyMutationPlain(site, m, result);
|
|
53993
|
+
if (verdict !== "cascade")
|
|
53994
|
+
return verdict;
|
|
53995
|
+
const proof = result.targeted?.find((t) => t.site === assertionSiteKey(site));
|
|
53996
|
+
if (proof === void 0 || proof.refused !== void 0)
|
|
53997
|
+
return "cascade";
|
|
53998
|
+
const targeted = classifyMutationPlain(site, m, { outcome: proof.outcome, failedSites: proof.failedSites });
|
|
53999
|
+
return targeted === "sensitive" || targeted === "insensitive" ? targeted : "cascade";
|
|
54000
|
+
}
|
|
54001
|
+
function classifyMutationPlain(site, m, result) {
|
|
53878
54002
|
if (result.relaxed !== void 0 && result.failedSites.some((f2) => f2.where === "step" && f2.step === m.step && f2.postcondition) && !(site.where === "step" && site.step === m.step)) {
|
|
53879
|
-
return
|
|
54003
|
+
return classifyMutationPlain(site, m, { outcome: result.relaxed.outcome, failedSites: result.relaxed.failedSites });
|
|
53880
54004
|
}
|
|
53881
54005
|
if (result.outcome === "passed")
|
|
53882
54006
|
return "insensitive";
|
|
@@ -53902,6 +54026,7 @@ function classifyMutation(site, m, result) {
|
|
|
53902
54026
|
}
|
|
53903
54027
|
var PRECEDENCE = ["sensitive", "insensitive", "cascade", "not-applied", "error"];
|
|
53904
54028
|
function judgeAssertions(journey, plan2, results) {
|
|
54029
|
+
const proofFor = (m, key2) => results.get(m.id)?.targeted?.find((t) => t.site === key2);
|
|
53905
54030
|
return journeyAssertions(journey).map((site) => {
|
|
53906
54031
|
const key2 = assertionSiteKey(site);
|
|
53907
54032
|
const paired = plan2.mutations.filter((m) => plan2.pairs.some((p) => p.site === key2 && p.mutation === m.id));
|
|
@@ -53912,7 +54037,16 @@ function judgeAssertions(journey, plan2, results) {
|
|
|
53912
54037
|
const first = verdicts.find((x) => x.v === v);
|
|
53913
54038
|
if (first === void 0)
|
|
53914
54039
|
continue;
|
|
53915
|
-
|
|
54040
|
+
if (v === "sensitive") {
|
|
54041
|
+
const proof = proofFor(first.m, key2);
|
|
54042
|
+
return proof !== void 0 && proof.refused === void 0 ? { site: key2, verdict: v, provedBy: first.m.id, relaxedSites: proof.relaxedSites } : { site: key2, verdict: v, provedBy: first.m.id };
|
|
54043
|
+
}
|
|
54044
|
+
if (v === "cascade") {
|
|
54045
|
+
const refusal = paired.map((m) => proofFor(m, key2)).find((t) => t !== void 0 && t.refused !== void 0);
|
|
54046
|
+
if (refusal !== void 0)
|
|
54047
|
+
return { site: key2, verdict: v, targetedRefusal: refusal.refused };
|
|
54048
|
+
}
|
|
54049
|
+
return { site: key2, verdict: v };
|
|
53916
54050
|
}
|
|
53917
54051
|
return { site: key2, verdict: "error" };
|
|
53918
54052
|
});
|
|
@@ -53930,6 +54064,10 @@ function mutationProofVerdict(input2) {
|
|
|
53930
54064
|
return { verdict: "insensitive" };
|
|
53931
54065
|
if (paired.every((a) => a.verdict === "sensitive"))
|
|
53932
54066
|
return { verdict: "proven" };
|
|
54067
|
+
const refusals = input2.assertions.map((a) => a.targetedRefusal).filter((r) => r !== void 0);
|
|
54068
|
+
if (refusals.length > 0) {
|
|
54069
|
+
return { verdict: "inconclusive", reason: `some paired assertions were neither proven nor shown vacuous (cascade, not-applied or error); a targeted proof was refused: ${refusals[0]}` };
|
|
54070
|
+
}
|
|
53933
54071
|
return { verdict: "inconclusive", reason: "some paired assertions were neither proven nor shown vacuous (cascade, not-applied or error)" };
|
|
53934
54072
|
}
|
|
53935
54073
|
|
|
@@ -55383,7 +55521,13 @@ var ReviewChangeSchema = external_exports.discriminatedUnion("kind", [
|
|
|
55383
55521
|
sideEffects: DiffSchema
|
|
55384
55522
|
}).strict()
|
|
55385
55523
|
]);
|
|
55386
|
-
var ReviewAttentionSchema = external_exports.object({
|
|
55524
|
+
var ReviewAttentionSchema = external_exports.object({
|
|
55525
|
+
severity: external_exports.enum(["warn", "info"]),
|
|
55526
|
+
text: external_exports.string(),
|
|
55527
|
+
hint: external_exports.string().optional(),
|
|
55528
|
+
/** #608: a person's catalog approval settles this item — readiness lists it as awaiting-human, never as a failure. */
|
|
55529
|
+
settledBy: external_exports.enum(["catalog-approval"]).optional()
|
|
55530
|
+
}).strict();
|
|
55387
55531
|
var JourneyReviewSchema = external_exports.object({
|
|
55388
55532
|
id: external_exports.string(),
|
|
55389
55533
|
name: external_exports.string(),
|
|
@@ -62390,9 +62534,9 @@ function hostStarvedFailure(failure3, health) {
|
|
|
62390
62534
|
}
|
|
62391
62535
|
|
|
62392
62536
|
// src/build-info.generated.ts
|
|
62393
|
-
var GENERATED_COMMIT = "
|
|
62394
|
-
var GENERATED_TREE = "
|
|
62395
|
-
var GENERATED_BUILT_AT = "2026-10-
|
|
62537
|
+
var GENERATED_COMMIT = "0a3d2f5";
|
|
62538
|
+
var GENERATED_TREE = "963f59f";
|
|
62539
|
+
var GENERATED_BUILT_AT = "2026-10-19T17:41:04.191Z";
|
|
62396
62540
|
|
|
62397
62541
|
// src/version.ts
|
|
62398
62542
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
@@ -62414,13 +62558,13 @@ function readCliVersion() {
|
|
|
62414
62558
|
|
|
62415
62559
|
// src/engine.ts
|
|
62416
62560
|
function bundledCommit() {
|
|
62417
|
-
return true ? "
|
|
62561
|
+
return true ? "0a3d2f5" : void 0;
|
|
62418
62562
|
}
|
|
62419
62563
|
function bundledBuiltAt() {
|
|
62420
|
-
return true ? "2026-10-
|
|
62564
|
+
return true ? "2026-10-19T17:41:04.191Z" : void 0;
|
|
62421
62565
|
}
|
|
62422
62566
|
function bundledTree() {
|
|
62423
|
-
return true ? "
|
|
62567
|
+
return true ? "963f59f" : void 0;
|
|
62424
62568
|
}
|
|
62425
62569
|
function currentBuildTree() {
|
|
62426
62570
|
return bundledTree() ?? GENERATED_TREE;
|
|
@@ -80381,7 +80525,7 @@ function describeFile(step) {
|
|
|
80381
80525
|
}
|
|
80382
80526
|
function buildAttention(r, weakEndState, goal) {
|
|
80383
80527
|
const out = [];
|
|
80384
|
-
const warn = (text2, hint2) => void out.push({ severity: "warn", text: text2, ...hint2 === void 0 ? {} : { hint: hint2 } });
|
|
80528
|
+
const warn = (text2, hint2, settledBy) => void out.push({ severity: "warn", text: text2, ...hint2 === void 0 ? {} : { hint: hint2 }, ...settledBy === void 0 ? {} : { settledBy } });
|
|
80385
80529
|
const info = (text2, hint2) => void out.push({ severity: "info", text: text2, ...hint2 === void 0 ? {} : { hint: hint2 } });
|
|
80386
80530
|
const weakSteps = [...new Set(r.proof.stepAssertions.filter((a) => a.weak).map((a) => a.step))];
|
|
80387
80531
|
if (weakSteps.length === 1) warn(`Step ${stepRanges(weakSteps)} proves nothing: its check passes even if the step fails`);
|
|
@@ -80402,12 +80546,12 @@ function buildAttention(r, weakEndState, goal) {
|
|
|
80402
80546
|
else info(`${text2}, exempted by allowControl`);
|
|
80403
80547
|
}
|
|
80404
80548
|
if (r.catalog !== void 0) {
|
|
80405
|
-
if (r.catalog.unvetted.length > 0) warn(`Unvetted catalog links: ${r.catalog.unvetted.join(", ")}`, `approve them, or promote with --accept-unvetted "<reason>"
|
|
80549
|
+
if (r.catalog.unvetted.length > 0) warn(`Unvetted catalog links: ${r.catalog.unvetted.join(", ")}`, `approve them, or promote with --accept-unvetted "<reason>"`, "catalog-approval");
|
|
80406
80550
|
if (r.catalog.needsReReview.length > 0) warn(`Catalog links need re-review: ${r.catalog.needsReReview.join("; ")}`);
|
|
80407
80551
|
}
|
|
80408
80552
|
for (const f2 of r.findings ?? []) {
|
|
80409
80553
|
if (f2.analyzer === "catalog-analysis") continue;
|
|
80410
|
-
if (f2.severity !== "info") warn(f2.message, f2.fix);
|
|
80554
|
+
if (f2.severity !== "info") warn(f2.message, f2.fix, f2.analyzer === "catalog-links" && f2.code === "journey.unvetted" ? "catalog-approval" : void 0);
|
|
80411
80555
|
else if (f2.analyzer === "readiness" || f2.analyzer === "readiness-jev") info(f2.message, f2.fix);
|
|
80412
80556
|
}
|
|
80413
80557
|
const c = r.changeSinceApproval;
|
|
@@ -94790,7 +94934,9 @@ function handbackFacts(a) {
|
|
|
94790
94934
|
const failedKind = at3 === void 0 ? void 0 : /postcondition failed/.test(reason ?? "") ? "postcondition" : "other";
|
|
94791
94935
|
return {
|
|
94792
94936
|
outcome: a.result.outcome,
|
|
94793
|
-
|
|
94937
|
+
// `at` is the run's 0-based step index; `failedAt` names the step as a person counts it (1-based,
|
|
94938
|
+
// as the run's own "step N failed" reason does).
|
|
94939
|
+
...at3 === void 0 ? {} : { failedAt: at3 + 1 },
|
|
94794
94940
|
...failedKind === void 0 ? {} : { failedKind },
|
|
94795
94941
|
steps: a.steps,
|
|
94796
94942
|
checksPassed: a.result.outcome === "ok" ? a.steps : at3 ?? 0,
|
|
@@ -95097,7 +95243,8 @@ async function runJourneyProgrammatically(opts, mutation2 = {}) {
|
|
|
95097
95243
|
let result;
|
|
95098
95244
|
let runSecrets2 = secrets;
|
|
95099
95245
|
try {
|
|
95100
|
-
const
|
|
95246
|
+
const replayParams = replayed === journey ? params : prefixParams(journey, replayed, params);
|
|
95247
|
+
const raw = await outcomeChecks.run(actor, () => runner.run({ journey: replayed, params: replayParams, policy }));
|
|
95101
95248
|
runSecrets2 = runSecretValues(secrets, raw);
|
|
95102
95249
|
result = redactSecretValues(raw, runSecrets2);
|
|
95103
95250
|
} finally {
|
|
@@ -105344,7 +105491,14 @@ async function whoami(http, origin, key2) {
|
|
|
105344
105491
|
}
|
|
105345
105492
|
const tenant = body.tenant?.name;
|
|
105346
105493
|
const prefix = typeof body.keyPrefix === "string" && /^jzu_[a-z2-7]{4}$/.test(body.keyPrefix) ? body.keyPrefix : key2.slice(0, 8);
|
|
105347
|
-
|
|
105494
|
+
const max = body.limits?.maxUploadBytes;
|
|
105495
|
+
const maxUploadBytes = typeof max === "number" && Number.isSafeInteger(max) && max > 0 ? max : void 0;
|
|
105496
|
+
return {
|
|
105497
|
+
product: { id: product.id, name: product.name },
|
|
105498
|
+
tenant: typeof tenant === "string" ? tenant : "",
|
|
105499
|
+
keyPrefix: prefix,
|
|
105500
|
+
...maxUploadBytes === void 0 ? {} : { maxUploadBytes }
|
|
105501
|
+
};
|
|
105348
105502
|
}
|
|
105349
105503
|
function processTerminal() {
|
|
105350
105504
|
return {
|
|
@@ -105464,7 +105618,7 @@ async function resolvePublishKey(req, deps) {
|
|
|
105464
105618
|
throw err;
|
|
105465
105619
|
}
|
|
105466
105620
|
}
|
|
105467
|
-
var
|
|
105621
|
+
var DEFAULT_MAX_UPLOAD_BYTES = 95e6;
|
|
105468
105622
|
var MEDIA_PATH_RE = /^media\/(?:[A-Za-z0-9][A-Za-z0-9._-]{0,63}\/(?:step-[0-9]{2,3}\.(?:png|webp)|demo\.webm|demo\.vtt)|findings\/[0-9a-f]{16}\.(?:png|webp))$/u;
|
|
105469
105623
|
async function filesUnder(dir, base) {
|
|
105470
105624
|
let entries;
|
|
@@ -105504,9 +105658,6 @@ async function zipBundle(outDir, bundleJson, files, key2) {
|
|
|
105504
105658
|
const unlisted = (await filesUnder(join57(outDir, "media"), outDir)).filter((p) => !listed2.has(p));
|
|
105505
105659
|
if (unlisted.length > 0) throw new JourneezeError("E_JOURNEEZE_BUNDLE", `the export wrote media the bundle does not list (${unlisted.slice(0, 3).join(", ")}) \u2014 refusing to send it`);
|
|
105506
105660
|
const body = zipStore(entries);
|
|
105507
|
-
if (body.byteLength > MAX_UPLOAD_BYTES) {
|
|
105508
|
-
throw new JourneezeError("E_JOURNEEZE_BUNDLE", `the bundle with its media is ${body.byteLength} bytes \u2014 over Journeeze's ${MAX_UPLOAD_BYTES}-byte upload limit`);
|
|
105509
|
-
}
|
|
105510
105661
|
return new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
|
|
105511
105662
|
}
|
|
105512
105663
|
async function resolveProductName(req, resolved, deps) {
|
|
@@ -105521,9 +105672,20 @@ async function resolveProductName(req, resolved, deps) {
|
|
|
105521
105672
|
);
|
|
105522
105673
|
}
|
|
105523
105674
|
}
|
|
105524
|
-
if (req.productName !== void 0) return req.productName;
|
|
105525
|
-
if (resolved.fromSaved) return resolved.savedProductName;
|
|
105526
|
-
return verified.product.name;
|
|
105675
|
+
if (req.productName !== void 0) return { productName: req.productName, verified };
|
|
105676
|
+
if (resolved.fromSaved) return { productName: resolved.savedProductName, verified };
|
|
105677
|
+
return { productName: verified.product.name, verified };
|
|
105678
|
+
}
|
|
105679
|
+
async function uploadLimit(verified, resolved, deps) {
|
|
105680
|
+
const info = verified ?? await whoami(deps.http ?? fetchJourneezeHttp, resolved.origin, resolved.key);
|
|
105681
|
+
return info.maxUploadBytes ?? DEFAULT_MAX_UPLOAD_BYTES;
|
|
105682
|
+
}
|
|
105683
|
+
function assertUploadSize(bundle, maxUploadBytes) {
|
|
105684
|
+
if (bundle.body.byteLength <= maxUploadBytes) return;
|
|
105685
|
+
throw new JourneezeError(
|
|
105686
|
+
"E_JOURNEEZE_BUNDLE",
|
|
105687
|
+
`the bundle${bundle.contentType === "application/zip" ? " with its media" : ""} is ${bundle.body.byteLength} bytes \u2014 over Journeeze's ${maxUploadBytes}-byte upload limit; nothing was sent (approve fewer demos with media, or publish without them)`
|
|
105688
|
+
);
|
|
105527
105689
|
}
|
|
105528
105690
|
async function buildBundle(req, deps, key2, productName) {
|
|
105529
105691
|
const outDir = await mkdtemp4(join57(tmpdir4(), "jev-journeeze-"));
|
|
@@ -105687,8 +105849,9 @@ async function publishToJourneeze(req, deps = {}) {
|
|
|
105687
105849
|
try {
|
|
105688
105850
|
const resolved = await resolvePublishKey(req, deps);
|
|
105689
105851
|
key2 = resolved.key;
|
|
105690
|
-
const productName = await resolveProductName(req, resolved, deps);
|
|
105852
|
+
const { productName, verified } = await resolveProductName(req, resolved, deps);
|
|
105691
105853
|
const bundle = await buildBundle(req, deps, key2, productName);
|
|
105854
|
+
assertUploadSize(bundle, await uploadLimit(verified, resolved, deps));
|
|
105692
105855
|
const result = req.dryRun ? { dryRun: true, baseUrl: resolved.origin, productName, idempotencyKey: bundle.idempotencyKey, status: "dry-run", warnings: [], errors: [] } : await upload(resolved.origin, key2, bundle, productName, deps);
|
|
105693
105856
|
const text2 = JSON.stringify(result);
|
|
105694
105857
|
return text2.includes(key2) ? JSON.parse(redactText(text2, [key2])) : result;
|
|
@@ -107856,6 +108019,9 @@ function journeyLayout(program2, deps, flags) {
|
|
|
107856
108019
|
return null;
|
|
107857
108020
|
}
|
|
107858
108021
|
}
|
|
108022
|
+
function flaggedCatalogDir(layout) {
|
|
108023
|
+
return layout.from.project === "flag" || layout.from.project === "dir" ? layout.catalogDir ?? void 0 : void 0;
|
|
108024
|
+
}
|
|
107859
108025
|
|
|
107860
108026
|
// src/journey-cli/list.ts
|
|
107861
108027
|
function registerJourneyList(journey, program2, deps) {
|
|
@@ -108442,6 +108608,9 @@ function redactReason(reason, params, names = TEXT_PARAMS) {
|
|
|
108442
108608
|
}
|
|
108443
108609
|
return out;
|
|
108444
108610
|
}
|
|
108611
|
+
function redactProducerParams(reason, params) {
|
|
108612
|
+
return params === void 0 ? reason : redactReason(reason, params, Object.keys(params));
|
|
108613
|
+
}
|
|
108445
108614
|
function chainSucceeded(result) {
|
|
108446
108615
|
return result.unmatched.length === 0 && result.runs.every((run) => run.outcome === "ok");
|
|
108447
108616
|
}
|
|
@@ -108532,10 +108701,10 @@ async function runHandoffChain(inputs, outputs, fromJourney, fromOutput, request
|
|
|
108532
108701
|
...identity,
|
|
108533
108702
|
outcome: result.outcome,
|
|
108534
108703
|
...stamp,
|
|
108535
|
-
..."reason" in result ? { reason: redactReason(result.reason, params, redactors) } : {}
|
|
108704
|
+
..."reason" in result ? { reason: redactProducerParams(redactReason(result.reason, params, redactors), opts.params) } : {}
|
|
108536
108705
|
});
|
|
108537
108706
|
} catch (err) {
|
|
108538
|
-
runs.push({ ...identity, outcome: "error", ...stamp, reason: redactReason(err instanceof Error ? err.message : String(err), params, redactors) });
|
|
108707
|
+
runs.push({ ...identity, outcome: "error", ...stamp, reason: redactProducerParams(redactReason(err instanceof Error ? err.message : String(err), params, redactors), opts.params) });
|
|
108539
108708
|
}
|
|
108540
108709
|
}
|
|
108541
108710
|
return { from: { journey: opts.from, outcome: "ok", records: records.length }, runs, unmatched, rejected };
|
|
@@ -108547,9 +108716,15 @@ async function runJourneyChain(opts, deps = {}) {
|
|
|
108547
108716
|
const toJourney = await registry3.get(opts.to);
|
|
108548
108717
|
if (toJourney === null) throw new UnknownJourneyError(`unknown journey '${opts.to}'`);
|
|
108549
108718
|
const inputs = toJourney.metadata.inputs;
|
|
108550
|
-
const
|
|
108551
|
-
|
|
108552
|
-
|
|
108719
|
+
const fromJourney = await registry3.get(opts.from);
|
|
108720
|
+
if (fromJourney === null) throw new UnknownJourneyError(`unknown journey '${opts.from}'`);
|
|
108721
|
+
const producerParams = { ...opts.params ?? {} };
|
|
108722
|
+
try {
|
|
108723
|
+
validateParams(journeyParamSchema(fromJourney), producerParams);
|
|
108724
|
+
} catch (err) {
|
|
108725
|
+
if (err instanceof ParamValidationError) throw new JourneyChainError("E_CHAIN_PARAMS", err.message);
|
|
108726
|
+
throw err;
|
|
108727
|
+
}
|
|
108553
108728
|
if (opts.requirePromoted === true) {
|
|
108554
108729
|
for (const j of [fromJourney, toJourney]) {
|
|
108555
108730
|
if (!j.metadata.promoted) throw new JourneyChainError("E_CHAIN_NOT_PROMOTED", `journey '${j.metadata.id}' is not promoted \u2014 a chain runs promoted Journeys (journey chain --allow-draft runs drafts on the CLI)`);
|
|
@@ -108564,9 +108739,9 @@ async function runJourneyChain(opts, deps = {}) {
|
|
|
108564
108739
|
handoff2 = { inputs, outputs };
|
|
108565
108740
|
}
|
|
108566
108741
|
const requests = handoff2 === void 0 ? parseChainRequests(opts.requests) : void 0;
|
|
108567
|
-
const fromResult = await runner({ ...shared3, id: opts.from, params:
|
|
108742
|
+
const fromResult = await runner({ ...shared3, id: opts.from, params: producerParams });
|
|
108568
108743
|
if (fromResult.outcome !== "ok") {
|
|
108569
|
-
const reason = "reason" in fromResult ? fromResult.reason : void 0;
|
|
108744
|
+
const reason = "reason" in fromResult ? redactProducerParams(fromResult.reason, opts.params) : void 0;
|
|
108570
108745
|
throw new JourneyChainError(
|
|
108571
108746
|
"E_CHAIN_FROM_FAILED",
|
|
108572
108747
|
`discover Journey '${opts.from}' did not run ok (${fromResult.outcome})${reason === void 0 ? "" : `: ${reason}`}`
|
|
@@ -108600,10 +108775,10 @@ async function runJourneyChain(opts, deps = {}) {
|
|
|
108600
108775
|
commentId: request.commentId,
|
|
108601
108776
|
account: request.account,
|
|
108602
108777
|
outcome: result.outcome,
|
|
108603
|
-
..."reason" in result ? { reason: redactReason(result.reason, mapped.params) } : {}
|
|
108778
|
+
..."reason" in result ? { reason: redactProducerParams(redactReason(result.reason, mapped.params), opts.params) } : {}
|
|
108604
108779
|
});
|
|
108605
108780
|
} catch (err) {
|
|
108606
|
-
runs.push({ commentId: request.commentId, account: request.account, outcome: "error", reason: redactReason(err instanceof Error ? err.message : String(err), mapped.params) });
|
|
108781
|
+
runs.push({ commentId: request.commentId, account: request.account, outcome: "error", reason: redactProducerParams(redactReason(err instanceof Error ? err.message : String(err), mapped.params), opts.params) });
|
|
108607
108782
|
}
|
|
108608
108783
|
}
|
|
108609
108784
|
return { from: { journey: opts.from, outcome: "ok", records: count }, runs, unmatched };
|
|
@@ -108611,12 +108786,12 @@ async function runJourneyChain(opts, deps = {}) {
|
|
|
108611
108786
|
|
|
108612
108787
|
// src/journey-cli/chain.ts
|
|
108613
108788
|
function registerJourneyChain(journey, program2, deps) {
|
|
108614
|
-
withReplayAllowFlag(withEnvironmentFlags(withBrowserLaunchFlags(withEmulationFlags(withProjectFlags(journey.command("chain")))))).requiredOption("--from <id>", "the discover Journey, run once to produce the records").requiredOption("--to <id>", "the prepare-only reply Journey, run once per matched request").requiredOption("--requests <file>", "JSON Lines file of approved requests ({commentId, account, replyText})").option("--dir <path>", DEPRECATED_DIR_HELP).option("--storage-state <file>", "Playwright storageState JSON to start each run authenticated; must exist").option("--allow-draft", "also run Journeys that are not promoted (authoring); without it both must be promoted").option("--json", "emit a JSON envelope").action(async function() {
|
|
108789
|
+
withReplayAllowFlag(withEnvironmentFlags(withBrowserLaunchFlags(withEmulationFlags(withProjectFlags(journey.command("chain")))))).requiredOption("--from <id>", "the discover Journey, run once to produce the records").requiredOption("--to <id>", "the prepare-only reply Journey, run once per matched request").requiredOption("--requests <file>", "JSON Lines file of approved requests ({commentId, account, replyText})").option("--param <kv>", "param as key=value (repeatable)", collectParam, {}).option("--dir <path>", DEPRECATED_DIR_HELP).option("--storage-state <file>", "Playwright storageState JSON to start each run authenticated; must exist").option("--allow-draft", "also run Journeys that are not promoted (authoring); without it both must be promoted").option("--json", "emit a JSON envelope").action(async function() {
|
|
108615
108790
|
const locationFlags = this.opts();
|
|
108616
108791
|
const layout = journeyLayout(program2, deps, { dir: locationFlags.dir, project: locationFlags.project, journeysDir: locationFlags.journeysDir });
|
|
108617
108792
|
if (layout === null) return;
|
|
108618
108793
|
const chainJourneysDir = layout.journeysDir;
|
|
108619
|
-
const { from, to, requests: requestsFile, storageState: storageStateFlag, json: json2, allowDraft } = this.opts();
|
|
108794
|
+
const { from, to, requests: requestsFile, storageState: storageStateFlag, json: json2, allowDraft, param } = this.opts();
|
|
108620
108795
|
const { env: envName, baseUrl } = this.opts();
|
|
108621
108796
|
let environment;
|
|
108622
108797
|
let allow;
|
|
@@ -108664,6 +108839,7 @@ function registerJourneyChain(journey, program2, deps) {
|
|
|
108664
108839
|
from,
|
|
108665
108840
|
to,
|
|
108666
108841
|
requests,
|
|
108842
|
+
...Object.keys(param).length === 0 ? {} : { params: param },
|
|
108667
108843
|
dir: chainJourneysDir,
|
|
108668
108844
|
requirePromoted: allowDraft !== true,
|
|
108669
108845
|
...siteGate === void 0 ? {} : { siteGate },
|
|
@@ -109063,12 +109239,12 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
|
|
|
109063
109239
|
const mutations = [];
|
|
109064
109240
|
if (base.outcome === "ok") {
|
|
109065
109241
|
const replayOpts2 = { ...lintOpts, params: opts.params };
|
|
109066
|
-
const
|
|
109242
|
+
const runReplay = async (m, describe6) => {
|
|
109067
109243
|
try {
|
|
109068
|
-
const how = describe6(journey
|
|
109244
|
+
const how = describe6(journey);
|
|
109069
109245
|
const r = await run(how.params === void 0 ? runOpts : { ...runOpts, params: { ...how.params } }, {
|
|
109070
109246
|
structuredFailures: true,
|
|
109071
|
-
...how.journey === journey ? {} : { mutateJourney: (j) => describe6(j
|
|
109247
|
+
...how.journey === journey ? {} : { mutateJourney: (j) => describe6(j).journey },
|
|
109072
109248
|
...how.skipIndex === void 0 ? {} : { skipStep: (i) => i === how.skipIndex },
|
|
109073
109249
|
...how.swapSelectAtStep === void 0 ? {} : { selectAlternativeAtStep: how.swapSelectAtStep },
|
|
109074
109250
|
...how.blockIndex === void 0 ? {} : { blockWritesAtStep: how.blockIndex }
|
|
@@ -109096,9 +109272,19 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
|
|
|
109096
109272
|
return { outcome: "error", failedSites: [], reason: err instanceof Error ? err.message : String(err) };
|
|
109097
109273
|
}
|
|
109098
109274
|
};
|
|
109275
|
+
const replay = (m, describe6) => runReplay(m, (j) => describe6(j, m, replayOpts2));
|
|
109276
|
+
const siteByKey = new Map(sites.map((s3) => [assertionSiteKey(s3), s3]));
|
|
109099
109277
|
for (const m of plan2.mutations) {
|
|
109100
109278
|
const first = await replay(m, mutationReplay);
|
|
109101
109279
|
const relaxed2 = needsRelaxedReplay(plan2, m, first) ? await replay(m, relaxedMutationReplay) : void 0;
|
|
109280
|
+
const initial = { outcome: first.outcome, failedSites: first.failedSites, ...relaxed2 === void 0 ? {} : { relaxed: relaxed2 } };
|
|
109281
|
+
const targeted = [];
|
|
109282
|
+
for (const pair of plan2.pairs) {
|
|
109283
|
+
if (pair.mutation !== m.id) continue;
|
|
109284
|
+
const site = siteByKey.get(pair.site);
|
|
109285
|
+
if (site === void 0 || classifyMutation(site, m, initial) !== "cascade") continue;
|
|
109286
|
+
targeted.push(await runTargetedProof(journey, plan2, m, site, initial, (describe6) => runReplay(m, describe6), replayOpts2));
|
|
109287
|
+
}
|
|
109102
109288
|
mutations.push({
|
|
109103
109289
|
kind: m.kind,
|
|
109104
109290
|
step: m.step,
|
|
@@ -109106,15 +109292,26 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
|
|
|
109106
109292
|
...m.param === void 0 ? {} : { param: m.param },
|
|
109107
109293
|
...m.source === void 0 ? {} : { source: m.source },
|
|
109108
109294
|
...first,
|
|
109109
|
-
...relaxed2 === void 0 ? {} : { relaxed: relaxed2 }
|
|
109295
|
+
...relaxed2 === void 0 ? {} : { relaxed: relaxed2 },
|
|
109296
|
+
...targeted.length === 0 ? {} : { targeted }
|
|
109110
109297
|
});
|
|
109111
109298
|
}
|
|
109112
109299
|
}
|
|
109113
109300
|
const results = new Map(
|
|
109114
|
-
mutations.map((m) => [
|
|
109301
|
+
mutations.map((m) => [
|
|
109302
|
+
m.id,
|
|
109303
|
+
{ outcome: m.outcome, failedSites: m.failedSites, ...m.relaxed === void 0 ? {} : { relaxed: m.relaxed }, ...m.targeted === void 0 ? {} : { targeted: m.targeted } }
|
|
109304
|
+
])
|
|
109115
109305
|
);
|
|
109116
109306
|
const judged = judgeAssertions(journey, plan2, results);
|
|
109117
|
-
const assertions = judged.map((a) => ({
|
|
109307
|
+
const assertions = judged.map((a) => ({
|
|
109308
|
+
site: a.site,
|
|
109309
|
+
check: checkOf.get(a.site) ?? a.site,
|
|
109310
|
+
verdict: a.verdict,
|
|
109311
|
+
...a.provedBy === void 0 ? {} : { provedBy: a.provedBy },
|
|
109312
|
+
...a.relaxedSites === void 0 ? {} : { relaxedSites: a.relaxedSites },
|
|
109313
|
+
...a.targetedRefusal === void 0 ? {} : { targetedRefusal: a.targetedRefusal }
|
|
109314
|
+
}));
|
|
109118
109315
|
const { verdict, reason } = mutationProofVerdict({ basePassed: base.outcome === "ok", mutations, assertions: judged });
|
|
109119
109316
|
const count = (v) => assertions.filter((a) => a.verdict === v).length;
|
|
109120
109317
|
const report2 = {
|
|
@@ -109131,7 +109328,9 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
|
|
|
109131
109328
|
cascade: count("cascade"),
|
|
109132
109329
|
notApplied: count("not-applied"),
|
|
109133
109330
|
error: count("error"),
|
|
109134
|
-
unpaired: count("unpaired")
|
|
109331
|
+
unpaired: count("unpaired"),
|
|
109332
|
+
// #609: assertions proven sensitive through a targeted proof that relaxed earlier sites.
|
|
109333
|
+
targeted: assertions.filter((a) => (a.relaxedSites?.length ?? 0) > 0).length
|
|
109135
109334
|
}
|
|
109136
109335
|
};
|
|
109137
109336
|
const redacted = redactSecretParams(report2, journey, opts.params);
|
|
@@ -109147,7 +109346,10 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
|
|
|
109147
109346
|
return redacted;
|
|
109148
109347
|
}
|
|
109149
109348
|
function verifyAssertionLine(a) {
|
|
109150
|
-
|
|
109349
|
+
const relaxed2 = a.relaxedSites === void 0 || a.relaxedSites.length === 0 ? "" : `, relaxed ${a.relaxedSites.join(", ")}`;
|
|
109350
|
+
const proved = a.provedBy === void 0 ? "" : ` (proved by ${a.provedBy}${relaxed2})`;
|
|
109351
|
+
const refused = a.targetedRefusal === void 0 ? "" : ` (targeted proof refused: ${a.targetedRefusal})`;
|
|
109352
|
+
return `${a.verdict.padEnd(11)} ${a.site.padEnd(18)} ${a.check}${proved}${refused}`;
|
|
109151
109353
|
}
|
|
109152
109354
|
|
|
109153
109355
|
// src/journey-readiness.ts
|
|
@@ -109160,7 +109362,7 @@ var ReadinessError = class extends Error {
|
|
|
109160
109362
|
}
|
|
109161
109363
|
};
|
|
109162
109364
|
var REQUIRED_PROOF_CHECK = "proof";
|
|
109163
|
-
function resolveTarget2(opts, deps, required2) {
|
|
109365
|
+
function resolveTarget2(opts, deps, required2, declared) {
|
|
109164
109366
|
let environment;
|
|
109165
109367
|
if (opts.env !== void 0 || opts.baseUrl !== void 0) {
|
|
109166
109368
|
environment = environmentFromFlags(
|
|
@@ -109178,6 +109380,10 @@ function resolveTarget2(opts, deps, required2) {
|
|
|
109178
109380
|
if (missing.length > 0) {
|
|
109179
109381
|
throw new ReadinessError("E_READINESS_PARAMS", `missing required param(s): ${missing.join(", ")}`);
|
|
109180
109382
|
}
|
|
109383
|
+
const unknown2 = Object.keys(opts.params ?? {}).filter((name) => !declared.includes(name));
|
|
109384
|
+
if (unknown2.length > 0) {
|
|
109385
|
+
throw new ReadinessError("E_READINESS_PARAMS", `unknown param(s): ${unknown2.join(", ")}`);
|
|
109386
|
+
}
|
|
109181
109387
|
return environment;
|
|
109182
109388
|
}
|
|
109183
109389
|
function requiredParams(journey, pair, hasRequests) {
|
|
@@ -109220,7 +109426,7 @@ function boundaryCheck(ctx) {
|
|
|
109220
109426
|
return ctx.journey.metadata.boundary === void 0 ? { check: "boundary", journey: ctx.id, state: "not-run", detail: "no prepare-only boundary declared" } : { check: "boundary", journey: ctx.id, state: "pass", detail: "a prepare-only boundary is declared" };
|
|
109221
109427
|
}
|
|
109222
109428
|
function reviewStrictCheck(ctx) {
|
|
109223
|
-
const warns = (ctx.review.attention ?? []).filter((a) => a.severity === "warn");
|
|
109429
|
+
const warns = (ctx.review.attention ?? []).filter((a) => a.severity === "warn" && !(a.settledBy === "catalog-approval" && ctx.review.catalog !== void 0));
|
|
109224
109430
|
if (warns.length > 0) {
|
|
109225
109431
|
return {
|
|
109226
109432
|
check: { check: "review-strict", journey: ctx.id, state: "fail", detail: warns.map((a) => a.text).join("; ") },
|
|
@@ -109287,8 +109493,10 @@ function proofCheck(ctx, journeysDir, cwd) {
|
|
|
109287
109493
|
};
|
|
109288
109494
|
}
|
|
109289
109495
|
if (verify2.verdict === "proven") {
|
|
109290
|
-
const
|
|
109291
|
-
|
|
109496
|
+
const targeted = verify2.summary.targeted ?? 0;
|
|
109497
|
+
const passed = Object.entries(verify2.summary).filter(([k]) => k !== "targeted").reduce((a, [, b2]) => a + b2, 0);
|
|
109498
|
+
const detail = targeted === 0 ? `proven ${passed}/${passed}` : `proven ${passed}/${passed}, ${targeted} through a targeted proof (earlier defenses relaxed one at a time; see the verify report)`;
|
|
109499
|
+
return { check: { check: "proof", journey: ctx.id, state: "pass", detail, evidence } };
|
|
109292
109500
|
}
|
|
109293
109501
|
if (verify2.verdict === "insensitive") {
|
|
109294
109502
|
return {
|
|
@@ -109296,7 +109504,31 @@ function proofCheck(ctx, journeysDir, cwd) {
|
|
|
109296
109504
|
blocker: { check: "proof", journey: ctx.id, why: verify2.reason ?? "some checks cannot fail", next: `jevitate journey verify ${ctx.id} --mutate` }
|
|
109297
109505
|
};
|
|
109298
109506
|
}
|
|
109299
|
-
|
|
109507
|
+
if (nothingToMutate(ctx.journey, verify2.summary)) {
|
|
109508
|
+
return { check: { check: "proof", journey: ctx.id, state: "pass", detail: "read-only with no inputs: nothing a mutation could break", evidence } };
|
|
109509
|
+
}
|
|
109510
|
+
return {
|
|
109511
|
+
check: { check: "proof", journey: ctx.id, state: "inconclusive", detail: verify2.reason ?? `proof verdict ${verify2.verdict}`, evidence },
|
|
109512
|
+
blocker: { check: "proof", journey: ctx.id, why: inconclusiveProofGuidance(verify2.reason ?? ""), next: `jevitate journey verify ${ctx.id} --mutate` }
|
|
109513
|
+
};
|
|
109514
|
+
}
|
|
109515
|
+
var TARGETED_REFUSAL_MARKER = "a targeted proof was refused: ";
|
|
109516
|
+
function inconclusiveProofGuidance(reason) {
|
|
109517
|
+
const at3 = reason.indexOf(TARGETED_REFUSAL_MARKER);
|
|
109518
|
+
if (at3 < 0) return "inspect the cascade assertions in the verify report";
|
|
109519
|
+
const refusal = reason.slice(at3 + TARGETED_REFUSAL_MARKER.length).trim();
|
|
109520
|
+
const unpaired = /^(\S+) is not paired with (\S+)$/.exec(refusal);
|
|
109521
|
+
if (unpaired !== null) {
|
|
109522
|
+
return `declare the earlier check ${unpaired[1]} as a defense of ${unpaired[2]} with a metadata.mutationPairs entry {check:'${unpaired[1]}', mustFailWhen:'${unpaired[2]}'}, then re-run verify`;
|
|
109523
|
+
}
|
|
109524
|
+
const bound2 = /relaxation bound (\d+)/.exec(refusal);
|
|
109525
|
+
if (bound2 !== null) return `reduce the layered checks between the mutation and the target (relaxation bound ${bound2[1]})`;
|
|
109526
|
+
if (refusal.includes("action failed")) return "the mutated value makes an earlier action impossible, so that target cannot be proven independently \u2014 keep the check; it stays inconclusive by design";
|
|
109527
|
+
return "inspect the cascade assertions in the verify report";
|
|
109528
|
+
}
|
|
109529
|
+
function nothingToMutate(journey, summary2) {
|
|
109530
|
+
const paired = Object.entries(summary2).filter(([k]) => k !== "unpaired" && k !== "targeted").reduce((n3, [, v]) => n3 + v, 0);
|
|
109531
|
+
return (summary2.unpaired ?? 0) > 0 && paired === 0 && journeyWriteSteps(journey).length === 0 && journeyParamSchema(journey).required.length === 0;
|
|
109300
109532
|
}
|
|
109301
109533
|
function relativeEvidence(cwd, path2) {
|
|
109302
109534
|
const r = relative9(cwd, path2);
|
|
@@ -109381,21 +109613,38 @@ async function runJourneyReadiness(opts, deps = {}) {
|
|
|
109381
109613
|
const pair = opts.with !== void 0;
|
|
109382
109614
|
let environment;
|
|
109383
109615
|
let replay;
|
|
109616
|
+
let boundConsumerParams;
|
|
109384
109617
|
if (mode !== "report") {
|
|
109385
|
-
|
|
109618
|
+
const producerSchema = journeyParamSchema(contexts[0].journey).required;
|
|
109619
|
+
environment = resolveTarget2(opts, deps, requiredParams(contexts[0].journey, pair, opts.requests !== void 0), producerSchema);
|
|
109386
109620
|
const params = { ...opts.params ?? {} };
|
|
109387
109621
|
try {
|
|
109388
109622
|
if (pair) {
|
|
109389
|
-
const chained = await chain(
|
|
109390
|
-
|
|
109391
|
-
|
|
109392
|
-
|
|
109393
|
-
|
|
109394
|
-
|
|
109395
|
-
|
|
109396
|
-
|
|
109397
|
-
|
|
109398
|
-
|
|
109623
|
+
const chained = await chain(
|
|
109624
|
+
{
|
|
109625
|
+
from: opts.id,
|
|
109626
|
+
to: opts.with,
|
|
109627
|
+
requests: opts.requests ?? "",
|
|
109628
|
+
params: { ...opts.params ?? {} },
|
|
109629
|
+
dir: opts.journeysDir,
|
|
109630
|
+
environment,
|
|
109631
|
+
...opts.storageState === void 0 ? {} : { storageState: opts.storageState },
|
|
109632
|
+
...opts.browser === void 0 ? {} : { browser: opts.browser },
|
|
109633
|
+
...opts.browserPortFactory === void 0 ? {} : { browserPortFactory: opts.browserPortFactory }
|
|
109634
|
+
},
|
|
109635
|
+
{
|
|
109636
|
+
// #608: the consumer's proof must run with the params the chain actually bound for it
|
|
109637
|
+
// (record fields + request), captured from the first consumer run; names and outcomes
|
|
109638
|
+
// are all the chain itself returns.
|
|
109639
|
+
runJourney: async (chainOpts) => {
|
|
109640
|
+
const result = await run(chainOpts);
|
|
109641
|
+
if (chainOpts.id === opts.with && boundConsumerParams === void 0) {
|
|
109642
|
+
boundConsumerParams = { ...chainOpts.params };
|
|
109643
|
+
}
|
|
109644
|
+
return result;
|
|
109645
|
+
}
|
|
109646
|
+
}
|
|
109647
|
+
);
|
|
109399
109648
|
replay = pairReplay(chained);
|
|
109400
109649
|
} else {
|
|
109401
109650
|
const result = await run({
|
|
@@ -109415,11 +109664,23 @@ async function runJourneyReadiness(opts, deps = {}) {
|
|
|
109415
109664
|
}
|
|
109416
109665
|
if (mode === "prove") {
|
|
109417
109666
|
for (const ctx of contexts) {
|
|
109667
|
+
const consumer = pair && ctx.id === opts.with;
|
|
109668
|
+
if (consumer && boundConsumerParams === void 0) {
|
|
109669
|
+
ctx.review = {
|
|
109670
|
+
...ctx.review,
|
|
109671
|
+
proof: {
|
|
109672
|
+
...ctx.review.proof,
|
|
109673
|
+
verify: { status: "not-verified", hint: `the chain bound no request for '${ctx.id}' \u2014 supply a --requests line that matches a discovered record` }
|
|
109674
|
+
}
|
|
109675
|
+
};
|
|
109676
|
+
continue;
|
|
109677
|
+
}
|
|
109678
|
+
const proofParams = consumer ? boundConsumerParams : params;
|
|
109418
109679
|
try {
|
|
109419
109680
|
await verify2({
|
|
109420
109681
|
dir: opts.journeysDir,
|
|
109421
109682
|
id: ctx.id,
|
|
109422
|
-
params,
|
|
109683
|
+
params: proofParams,
|
|
109423
109684
|
environment,
|
|
109424
109685
|
...opts.storageState === void 0 ? {} : { storageState: opts.storageState },
|
|
109425
109686
|
...opts.browser === void 0 ? {} : { browser: opts.browser },
|
|
@@ -109572,7 +109833,7 @@ function registerJourneyReadiness(journey, program2, deps) {
|
|
|
109572
109833
|
...browser === void 0 ? {} : { browser },
|
|
109573
109834
|
browserPortFactory: deps.explore?.browserPortFactory,
|
|
109574
109835
|
journeysDir: layout.journeysDir,
|
|
109575
|
-
catalogDir: resolveCatalogDir(deps.catalogDir, layout
|
|
109836
|
+
catalogDir: resolveCatalogDir(deps.catalogDir, flaggedCatalogDir(layout)),
|
|
109576
109837
|
...deps.explore?.targetsConfigPath === void 0 ? {} : { targetsFile: deps.explore.targetsConfigPath },
|
|
109577
109838
|
...layout.projectDir === null ? {} : { projectDir: layout.projectDir },
|
|
109578
109839
|
...mode === "report" ? {} : { fixtures }
|
|
@@ -109658,21 +109919,20 @@ function inputsLines(inputs) {
|
|
|
109658
109919
|
}
|
|
109659
109920
|
return lines;
|
|
109660
109921
|
}
|
|
109661
|
-
function
|
|
109662
|
-
const
|
|
109663
|
-
const
|
|
109664
|
-
const
|
|
109665
|
-
if (
|
|
109666
|
-
const hash3 = catalog.personas.find((p) => p.id ===
|
|
109667
|
-
|
|
109922
|
+
function pendingHumanSteps(entry, catalog, draft) {
|
|
109923
|
+
const approvals = [];
|
|
109924
|
+
const persona2 = entry.review.catalog?.persona;
|
|
109925
|
+
const job = entry.review.catalog?.job;
|
|
109926
|
+
if (persona2 !== void 0 && persona2.status !== "approved") {
|
|
109927
|
+
const hash3 = catalog.personas.find((p) => p.id === persona2.id)?.contentHash;
|
|
109928
|
+
approvals.push(`jevitate persona approve ${persona2.id}${hash3 === void 0 ? "" : ` --reviewed-hash ${hash3}`}`);
|
|
109668
109929
|
}
|
|
109669
|
-
if (
|
|
109670
|
-
const hash3 = catalog.jobs.find((j) => j.id ===
|
|
109671
|
-
|
|
109930
|
+
if (job !== void 0 && job.status !== "approved") {
|
|
109931
|
+
const hash3 = catalog.jobs.find((j) => j.id === job.id)?.contentHash;
|
|
109932
|
+
approvals.push(`jevitate job approve ${job.id}${hash3 === void 0 ? "" : ` --reviewed-hash ${hash3}`}`);
|
|
109672
109933
|
}
|
|
109673
|
-
if (draft)
|
|
109674
|
-
|
|
109675
|
-
return commands;
|
|
109934
|
+
if (draft) approvals.push(`jevitate journey annotate ${entry.id} --approve`);
|
|
109935
|
+
return { approvals, promote: journeyApprovalLabel(entry) === "current authorization" ? null : entry.id };
|
|
109676
109936
|
}
|
|
109677
109937
|
function journeySection(entry, receipt) {
|
|
109678
109938
|
const m = entry.journey.metadata;
|
|
@@ -109721,14 +109981,21 @@ function renderJourneyBundle(receipt, entries, catalog, drafts) {
|
|
|
109721
109981
|
lines.push(...journeySection(entry, receipt), "");
|
|
109722
109982
|
}
|
|
109723
109983
|
const ready = receipt.verdict === "technically-ready";
|
|
109724
|
-
|
|
109984
|
+
const approvals = [];
|
|
109985
|
+
const promotes = [];
|
|
109725
109986
|
for (const entry of entries) {
|
|
109726
|
-
|
|
109987
|
+
const pending = pendingHumanSteps(entry, catalog, drafts.has(entry.id));
|
|
109988
|
+
for (const command of pending.approvals) if (!approvals.includes(command)) approvals.push(command);
|
|
109989
|
+
if (pending.promote !== null) promotes.push(entry);
|
|
109727
109990
|
}
|
|
109728
|
-
|
|
109991
|
+
lines.push("## Human-only commands", "");
|
|
109992
|
+
const now2 = [...approvals, ...ready ? promotes.map((e) => `jevitate journey promote ${e.id} --reviewed-hash ${e.review.contentHash}`) : []];
|
|
109993
|
+
if (now2.length === 0 && (ready || promotes.length === 0)) lines.push("Nothing is waiting for a person: every linked persona and job is approved and every Journey is promoted with a current approval.");
|
|
109994
|
+
for (const command of now2) lines.push(`- ${command}`);
|
|
109995
|
+
if (!ready && promotes.length > 0) {
|
|
109729
109996
|
lines.push("");
|
|
109730
109997
|
lines.push("## After the blockers are fixed", "");
|
|
109731
|
-
for (const entry of
|
|
109998
|
+
for (const entry of promotes) lines.push(`- jevitate journey promote ${entry.id}`);
|
|
109732
109999
|
}
|
|
109733
110000
|
lines.push("");
|
|
109734
110001
|
return `${lines.join("\n")}
|
|
@@ -109826,7 +110093,7 @@ function registerJourneyBundle(journey, program2, deps) {
|
|
|
109826
110093
|
const layout = journeyLayout(program2, deps, { dir: locationFlags.dir, project: locationFlags.project, journeysDir: locationFlags.journeysDir });
|
|
109827
110094
|
if (layout === null) return;
|
|
109828
110095
|
const { with: withId, out, check: check3, json: json2 } = this.opts();
|
|
109829
|
-
const catalogDir = resolveCatalogDir(deps.catalogDir, layout
|
|
110096
|
+
const catalogDir = resolveCatalogDir(deps.catalogDir, flaggedCatalogDir(layout));
|
|
109830
110097
|
const location = { journeysDir: layout.journeysDir, catalogDir, ...targetsOpts(deps) };
|
|
109831
110098
|
const bundleDeps = { cwd: () => process.cwd() };
|
|
109832
110099
|
if (check3 !== void 0) {
|
|
@@ -109946,10 +110213,10 @@ function registerJourneyPromote(journey, program2, deps) {
|
|
|
109946
110213
|
}
|
|
109947
110214
|
reviewedHash = fromSheet;
|
|
109948
110215
|
}
|
|
109949
|
-
const jev = await jevSetupFor(deps, resolveCatalogDir(deps.catalogDir), { real, jevProvider });
|
|
110216
|
+
const jev = await jevSetupFor(deps, resolveCatalogDir(deps.catalogDir, flaggedCatalogDir(layout)), { real, jevProvider });
|
|
109950
110217
|
let attention;
|
|
109951
110218
|
if (!json2) {
|
|
109952
|
-
const { review } = await reviewJourneyById(journeysDir, id, { ...targetsOpts(deps), catalogDir: resolveCatalogDir(deps.catalogDir), readiness: true, jev, action: "journey promote" });
|
|
110219
|
+
const { review } = await reviewJourneyById(journeysDir, id, { ...targetsOpts(deps), catalogDir: resolveCatalogDir(deps.catalogDir, flaggedCatalogDir(layout)), readiness: true, jev, action: "journey promote" });
|
|
109953
110220
|
program2.configureOutput().writeOut?.(`${renderReviewText(review, reviewRenderOptions({ full: full === true }))}
|
|
109954
110221
|
`);
|
|
109955
110222
|
attention = review.attention;
|
|
@@ -109962,7 +110229,7 @@ function registerJourneyPromote(journey, program2, deps) {
|
|
|
109962
110229
|
...reviewedHash === void 0 ? {} : { reviewedHash },
|
|
109963
110230
|
...acceptUnvetted === void 0 ? {} : { acceptUnvetted },
|
|
109964
110231
|
...acceptFindings === void 0 ? {} : { acceptFindings },
|
|
109965
|
-
catalogDir: resolveCatalogDir(deps.catalogDir),
|
|
110232
|
+
catalogDir: resolveCatalogDir(deps.catalogDir, flaggedCatalogDir(layout)),
|
|
109966
110233
|
// #437: a typed confirmation on a TTY (or the escape hatch, or the MCP channel), recorded as provenance.
|
|
109967
110234
|
confirm: /* @__PURE__ */ ((confirm2) => (request) => confirm2(attention === void 0 ? request : { ...request, attention }))(
|
|
109968
110235
|
makeApprovalConfirm(deps.approval, nonInteractiveApproval === void 0 ? {} : { nonInteractiveReason: nonInteractiveApproval })
|
|
@@ -110031,7 +110298,7 @@ function registerJourneyReview(journey, program2, deps) {
|
|
|
110031
110298
|
return;
|
|
110032
110299
|
}
|
|
110033
110300
|
try {
|
|
110034
|
-
const result = await listStaleJourneys({ journeysDir: reviewJourneysDir, catalogDir: resolveCatalogDir(deps.catalogDir) });
|
|
110301
|
+
const result = await listStaleJourneys({ journeysDir: reviewJourneysDir, catalogDir: resolveCatalogDir(deps.catalogDir, flaggedCatalogDir(layout)) });
|
|
110035
110302
|
if (json2) emitJson(program2, ok(result));
|
|
110036
110303
|
else program2.configureOutput().writeOut?.(renderStaleJourneys(result));
|
|
110037
110304
|
process.exitCode = 0;
|
|
@@ -110045,8 +110312,8 @@ function registerJourneyReview(journey, program2, deps) {
|
|
|
110045
110312
|
return;
|
|
110046
110313
|
}
|
|
110047
110314
|
try {
|
|
110048
|
-
const jev = await jevSetupFor(deps, resolveCatalogDir(deps.catalogDir), { real, jevProvider });
|
|
110049
|
-
const { review } = await reviewJourneyById(reviewJourneysDir, id, { ...targetsOpts(deps), catalogDir: resolveCatalogDir(deps.catalogDir), readiness: readiness === true, strict: strict === true, jev });
|
|
110315
|
+
const jev = await jevSetupFor(deps, resolveCatalogDir(deps.catalogDir, flaggedCatalogDir(layout)), { real, jevProvider });
|
|
110316
|
+
const { review } = await reviewJourneyById(reviewJourneysDir, id, { ...targetsOpts(deps), catalogDir: resolveCatalogDir(deps.catalogDir, flaggedCatalogDir(layout)), readiness: readiness === true, strict: strict === true, jev });
|
|
110050
110317
|
const warnings = (review.attention ?? []).filter((a) => a.severity === "warn").length;
|
|
110051
110318
|
const strictVerdict = { passed: warnings === 0, warnings };
|
|
110052
110319
|
const renderOpts = reviewRenderOptions({ full: full === true, toFile: outFile !== void 0 });
|
|
@@ -110323,7 +110590,7 @@ function registerJourneyMigrate(journey, program2, deps) {
|
|
|
110323
110590
|
return;
|
|
110324
110591
|
}
|
|
110325
110592
|
try {
|
|
110326
|
-
const result = await migrateStepIds({ journeysDir: migrateJourneysDir, projectDir: resolveCatalogDir(deps.catalogDir), dryRun: dryRun === true });
|
|
110593
|
+
const result = await migrateStepIds({ journeysDir: migrateJourneysDir, projectDir: resolveCatalogDir(deps.catalogDir, flaggedCatalogDir(layout)), dryRun: dryRun === true });
|
|
110327
110594
|
if (json2) emitJson(program2, ok(result));
|
|
110328
110595
|
else program2.configureOutput().writeOut?.(renderMigrateStepIds(result));
|
|
110329
110596
|
process.exitCode = 0;
|
|
@@ -110643,7 +110910,7 @@ function registerJourneyLint(journey, program2, deps) {
|
|
|
110643
110910
|
}
|
|
110644
110911
|
const fixed = fixResult?.stamped ?? [];
|
|
110645
110912
|
const reapproval = fixResult?.needsReapproval === true ? { needsReapproval: true, reapproveCommand: fixResult.reapproveCommand } : {};
|
|
110646
|
-
const catalogDir = resolveCatalogDir(deps.catalogDir);
|
|
110913
|
+
const catalogDir = resolveCatalogDir(deps.catalogDir, flaggedCatalogDir(layout));
|
|
110647
110914
|
const linted = await lintJourneyById(resolvedJourneysDir, id, { catalogDir });
|
|
110648
110915
|
const stored = await new FsJourneyStore(resolvedJourneysDir).get(id);
|
|
110649
110916
|
const linkFindings = stored === null ? [] : await journeyLinkWarnings(catalogDir, stored);
|
|
@@ -110766,8 +111033,9 @@ function registerJourneyVerify(journey, program2, deps) {
|
|
|
110766
111033
|
for (const a of report2.assertions) out?.(`${verifyAssertionLine(a)}
|
|
110767
111034
|
`);
|
|
110768
111035
|
const s3 = report2.summary;
|
|
111036
|
+
const targeted = s3.targeted > 0 ? `, ${s3.targeted} targeted` : "";
|
|
110769
111037
|
out?.(
|
|
110770
|
-
`${report2.verdict} \u2014 journey '${report2.journeyId}': ${s3.sensitive} sensitive, ${s3.insensitive} insensitive, ${s3.cascade} cascade, ${s3.notApplied} not applied, ${s3.error} error, ${s3.unpaired} unpaired${report2.reason === void 0 ? "" : ` (${report2.reason})`}
|
|
111038
|
+
`${report2.verdict} \u2014 journey '${report2.journeyId}': ${s3.sensitive} sensitive, ${s3.insensitive} insensitive, ${s3.cascade} cascade, ${s3.notApplied} not applied, ${s3.error} error, ${s3.unpaired} unpaired${targeted}${report2.reason === void 0 ? "" : ` (${report2.reason})`}
|
|
110771
111039
|
`
|
|
110772
111040
|
);
|
|
110773
111041
|
}
|
|
@@ -115012,13 +115280,14 @@ var CLI_TOOL_SPECS = [
|
|
|
115012
115280
|
},
|
|
115013
115281
|
{
|
|
115014
115282
|
name: "chain_journeys",
|
|
115015
|
-
description: "`jevitate journey chain --from <discover> --to <reply> --requests <file>` (#580): run a discover Journey once, then a prepare-only reply Journey once per approved request in the JSON Lines file ({commentId, account, replyText}) matched to a discovered record by commentId AND account. Both Journeys must be promoted. Unmatched requests never run; an empty discovery stops the chain (no target is guessed). The result names ids and outcomes only, never record or reply text. Exit 0 only when every request matched and its run was ok.",
|
|
115283
|
+
description: "`jevitate journey chain --from <discover> --to <reply> --requests <file>` (#580): run a discover Journey once, then a prepare-only reply Journey once per approved request in the JSON Lines file ({commentId, account, replyText}) matched to a discovered record by commentId AND account. Both Journeys must be promoted. #608: params supplies explicit producer (discover) params, validated against the producer's schema before anything runs. Unmatched requests never run; an empty discovery stops the chain (no target is guessed). The result names ids and outcomes only, never record or reply text. Exit 0 only when every request matched and its run was ok.",
|
|
115016
115284
|
command: {
|
|
115017
115285
|
path: "journey chain",
|
|
115018
115286
|
params: {
|
|
115019
115287
|
from: s2("--from", { required: true }),
|
|
115020
115288
|
to: s2("--to", { required: true }),
|
|
115021
115289
|
requests: path("--requests", { required: true }),
|
|
115290
|
+
params: { kind: "params", flag: "--param" },
|
|
115022
115291
|
storageState: session("--storage-state"),
|
|
115023
115292
|
...EXTENSION,
|
|
115024
115293
|
...EMULATION,
|