@jevitate/cli 0.18.1 → 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 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 target2 = flat(how.journey)[index];
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 classifyMutation(site, m, { outcome: result.relaxed.outcome, failedSites: result.relaxed.failedSites });
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
- return v === "sensitive" ? { site: key2, verdict: v, provedBy: first.m.id } : { site: key2, verdict: v };
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({ severity: external_exports.enum(["warn", "info"]), text: external_exports.string(), hint: external_exports.string().optional() }).strict();
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 = "ec540dd";
62394
- var GENERATED_TREE = "d9818ad";
62395
- var GENERATED_BUILT_AT = "2026-10-18T23:04:28.655Z";
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 ? "ec540dd" : void 0;
62561
+ return true ? "0a3d2f5" : void 0;
62418
62562
  }
62419
62563
  function bundledBuiltAt() {
62420
- return true ? "2026-10-18T23:04:28.655Z" : void 0;
62564
+ return true ? "2026-10-19T17:41:04.191Z" : void 0;
62421
62565
  }
62422
62566
  function bundledTree() {
62423
- return true ? "d9818ad" : void 0;
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;
@@ -95099,7 +95243,8 @@ async function runJourneyProgrammatically(opts, mutation2 = {}) {
95099
95243
  let result;
95100
95244
  let runSecrets2 = secrets;
95101
95245
  try {
95102
- const raw = await outcomeChecks.run(actor, () => runner.run({ journey: replayed, params, policy }));
95246
+ const replayParams = replayed === journey ? params : prefixParams(journey, replayed, params);
95247
+ const raw = await outcomeChecks.run(actor, () => runner.run({ journey: replayed, params: replayParams, policy }));
95103
95248
  runSecrets2 = runSecretValues(secrets, raw);
95104
95249
  result = redactSecretValues(raw, runSecrets2);
95105
95250
  } finally {
@@ -108463,6 +108608,9 @@ function redactReason(reason, params, names = TEXT_PARAMS) {
108463
108608
  }
108464
108609
  return out;
108465
108610
  }
108611
+ function redactProducerParams(reason, params) {
108612
+ return params === void 0 ? reason : redactReason(reason, params, Object.keys(params));
108613
+ }
108466
108614
  function chainSucceeded(result) {
108467
108615
  return result.unmatched.length === 0 && result.runs.every((run) => run.outcome === "ok");
108468
108616
  }
@@ -108553,10 +108701,10 @@ async function runHandoffChain(inputs, outputs, fromJourney, fromOutput, request
108553
108701
  ...identity,
108554
108702
  outcome: result.outcome,
108555
108703
  ...stamp,
108556
- ..."reason" in result ? { reason: redactReason(result.reason, params, redactors) } : {}
108704
+ ..."reason" in result ? { reason: redactProducerParams(redactReason(result.reason, params, redactors), opts.params) } : {}
108557
108705
  });
108558
108706
  } catch (err) {
108559
- 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) });
108560
108708
  }
108561
108709
  }
108562
108710
  return { from: { journey: opts.from, outcome: "ok", records: records.length }, runs, unmatched, rejected };
@@ -108568,9 +108716,15 @@ async function runJourneyChain(opts, deps = {}) {
108568
108716
  const toJourney = await registry3.get(opts.to);
108569
108717
  if (toJourney === null) throw new UnknownJourneyError(`unknown journey '${opts.to}'`);
108570
108718
  const inputs = toJourney.metadata.inputs;
108571
- const needsFrom = inputs !== void 0 || opts.requirePromoted === true;
108572
- const fromJourney = needsFrom ? await registry3.get(opts.from) : null;
108573
- if (needsFrom && fromJourney === null) throw new UnknownJourneyError(`unknown journey '${opts.from}'`);
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
+ }
108574
108728
  if (opts.requirePromoted === true) {
108575
108729
  for (const j of [fromJourney, toJourney]) {
108576
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)`);
@@ -108585,9 +108739,9 @@ async function runJourneyChain(opts, deps = {}) {
108585
108739
  handoff2 = { inputs, outputs };
108586
108740
  }
108587
108741
  const requests = handoff2 === void 0 ? parseChainRequests(opts.requests) : void 0;
108588
- const fromResult = await runner({ ...shared3, id: opts.from, params: {} });
108742
+ const fromResult = await runner({ ...shared3, id: opts.from, params: producerParams });
108589
108743
  if (fromResult.outcome !== "ok") {
108590
- const reason = "reason" in fromResult ? fromResult.reason : void 0;
108744
+ const reason = "reason" in fromResult ? redactProducerParams(fromResult.reason, opts.params) : void 0;
108591
108745
  throw new JourneyChainError(
108592
108746
  "E_CHAIN_FROM_FAILED",
108593
108747
  `discover Journey '${opts.from}' did not run ok (${fromResult.outcome})${reason === void 0 ? "" : `: ${reason}`}`
@@ -108621,10 +108775,10 @@ async function runJourneyChain(opts, deps = {}) {
108621
108775
  commentId: request.commentId,
108622
108776
  account: request.account,
108623
108777
  outcome: result.outcome,
108624
- ..."reason" in result ? { reason: redactReason(result.reason, mapped.params) } : {}
108778
+ ..."reason" in result ? { reason: redactProducerParams(redactReason(result.reason, mapped.params), opts.params) } : {}
108625
108779
  });
108626
108780
  } catch (err) {
108627
- 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) });
108628
108782
  }
108629
108783
  }
108630
108784
  return { from: { journey: opts.from, outcome: "ok", records: count }, runs, unmatched };
@@ -108632,12 +108786,12 @@ async function runJourneyChain(opts, deps = {}) {
108632
108786
 
108633
108787
  // src/journey-cli/chain.ts
108634
108788
  function registerJourneyChain(journey, program2, deps) {
108635
- 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() {
108636
108790
  const locationFlags = this.opts();
108637
108791
  const layout = journeyLayout(program2, deps, { dir: locationFlags.dir, project: locationFlags.project, journeysDir: locationFlags.journeysDir });
108638
108792
  if (layout === null) return;
108639
108793
  const chainJourneysDir = layout.journeysDir;
108640
- 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();
108641
108795
  const { env: envName, baseUrl } = this.opts();
108642
108796
  let environment;
108643
108797
  let allow;
@@ -108685,6 +108839,7 @@ function registerJourneyChain(journey, program2, deps) {
108685
108839
  from,
108686
108840
  to,
108687
108841
  requests,
108842
+ ...Object.keys(param).length === 0 ? {} : { params: param },
108688
108843
  dir: chainJourneysDir,
108689
108844
  requirePromoted: allowDraft !== true,
108690
108845
  ...siteGate === void 0 ? {} : { siteGate },
@@ -109084,12 +109239,12 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
109084
109239
  const mutations = [];
109085
109240
  if (base.outcome === "ok") {
109086
109241
  const replayOpts2 = { ...lintOpts, params: opts.params };
109087
- const replay = async (m, describe6) => {
109242
+ const runReplay = async (m, describe6) => {
109088
109243
  try {
109089
- const how = describe6(journey, m, replayOpts2);
109244
+ const how = describe6(journey);
109090
109245
  const r = await run(how.params === void 0 ? runOpts : { ...runOpts, params: { ...how.params } }, {
109091
109246
  structuredFailures: true,
109092
- ...how.journey === journey ? {} : { mutateJourney: (j) => describe6(j, m, replayOpts2).journey },
109247
+ ...how.journey === journey ? {} : { mutateJourney: (j) => describe6(j).journey },
109093
109248
  ...how.skipIndex === void 0 ? {} : { skipStep: (i) => i === how.skipIndex },
109094
109249
  ...how.swapSelectAtStep === void 0 ? {} : { selectAlternativeAtStep: how.swapSelectAtStep },
109095
109250
  ...how.blockIndex === void 0 ? {} : { blockWritesAtStep: how.blockIndex }
@@ -109117,9 +109272,19 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
109117
109272
  return { outcome: "error", failedSites: [], reason: err instanceof Error ? err.message : String(err) };
109118
109273
  }
109119
109274
  };
109275
+ const replay = (m, describe6) => runReplay(m, (j) => describe6(j, m, replayOpts2));
109276
+ const siteByKey = new Map(sites.map((s3) => [assertionSiteKey(s3), s3]));
109120
109277
  for (const m of plan2.mutations) {
109121
109278
  const first = await replay(m, mutationReplay);
109122
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
+ }
109123
109288
  mutations.push({
109124
109289
  kind: m.kind,
109125
109290
  step: m.step,
@@ -109127,15 +109292,26 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
109127
109292
  ...m.param === void 0 ? {} : { param: m.param },
109128
109293
  ...m.source === void 0 ? {} : { source: m.source },
109129
109294
  ...first,
109130
- ...relaxed2 === void 0 ? {} : { relaxed: relaxed2 }
109295
+ ...relaxed2 === void 0 ? {} : { relaxed: relaxed2 },
109296
+ ...targeted.length === 0 ? {} : { targeted }
109131
109297
  });
109132
109298
  }
109133
109299
  }
109134
109300
  const results = new Map(
109135
- mutations.map((m) => [m.id, { outcome: m.outcome, failedSites: m.failedSites, ...m.relaxed === void 0 ? {} : { relaxed: m.relaxed } }])
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
+ ])
109136
109305
  );
109137
109306
  const judged = judgeAssertions(journey, plan2, results);
109138
- const assertions = judged.map((a) => ({ site: a.site, check: checkOf.get(a.site) ?? a.site, verdict: a.verdict, ...a.provedBy === void 0 ? {} : { provedBy: a.provedBy } }));
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
+ }));
109139
109315
  const { verdict, reason } = mutationProofVerdict({ basePassed: base.outcome === "ok", mutations, assertions: judged });
109140
109316
  const count = (v) => assertions.filter((a) => a.verdict === v).length;
109141
109317
  const report2 = {
@@ -109152,7 +109328,9 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
109152
109328
  cascade: count("cascade"),
109153
109329
  notApplied: count("not-applied"),
109154
109330
  error: count("error"),
109155
- 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
109156
109334
  }
109157
109335
  };
109158
109336
  const redacted = redactSecretParams(report2, journey, opts.params);
@@ -109168,7 +109346,10 @@ async function verifyJourneyMutations(opts, run = runJourneyProgrammatically) {
109168
109346
  return redacted;
109169
109347
  }
109170
109348
  function verifyAssertionLine(a) {
109171
- return `${a.verdict.padEnd(11)} ${a.site.padEnd(18)} ${a.check}${a.provedBy === void 0 ? "" : ` (proved by ${a.provedBy})`}`;
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}`;
109172
109353
  }
109173
109354
 
109174
109355
  // src/journey-readiness.ts
@@ -109181,7 +109362,7 @@ var ReadinessError = class extends Error {
109181
109362
  }
109182
109363
  };
109183
109364
  var REQUIRED_PROOF_CHECK = "proof";
109184
- function resolveTarget2(opts, deps, required2) {
109365
+ function resolveTarget2(opts, deps, required2, declared) {
109185
109366
  let environment;
109186
109367
  if (opts.env !== void 0 || opts.baseUrl !== void 0) {
109187
109368
  environment = environmentFromFlags(
@@ -109199,6 +109380,10 @@ function resolveTarget2(opts, deps, required2) {
109199
109380
  if (missing.length > 0) {
109200
109381
  throw new ReadinessError("E_READINESS_PARAMS", `missing required param(s): ${missing.join(", ")}`);
109201
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
+ }
109202
109387
  return environment;
109203
109388
  }
109204
109389
  function requiredParams(journey, pair, hasRequests) {
@@ -109241,7 +109426,7 @@ function boundaryCheck(ctx) {
109241
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" };
109242
109427
  }
109243
109428
  function reviewStrictCheck(ctx) {
109244
- 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));
109245
109430
  if (warns.length > 0) {
109246
109431
  return {
109247
109432
  check: { check: "review-strict", journey: ctx.id, state: "fail", detail: warns.map((a) => a.text).join("; ") },
@@ -109308,8 +109493,10 @@ function proofCheck(ctx, journeysDir, cwd) {
109308
109493
  };
109309
109494
  }
109310
109495
  if (verify2.verdict === "proven") {
109311
- const passed = Object.values(verify2.summary).reduce((a, b2) => a + b2, 0);
109312
- return { check: { check: "proof", journey: ctx.id, state: "pass", detail: `proven ${passed}/${passed}`, evidence } };
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 } };
109313
109500
  }
109314
109501
  if (verify2.verdict === "insensitive") {
109315
109502
  return {
@@ -109320,10 +109507,27 @@ function proofCheck(ctx, journeysDir, cwd) {
109320
109507
  if (nothingToMutate(ctx.journey, verify2.summary)) {
109321
109508
  return { check: { check: "proof", journey: ctx.id, state: "pass", detail: "read-only with no inputs: nothing a mutation could break", evidence } };
109322
109509
  }
109323
- return { check: { check: "proof", journey: ctx.id, state: "inconclusive", detail: verify2.reason ?? `proof verdict ${verify2.verdict}`, evidence } };
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";
109324
109528
  }
109325
109529
  function nothingToMutate(journey, summary2) {
109326
- const paired = Object.entries(summary2).filter(([k]) => k !== "unpaired").reduce((n3, [, v]) => n3 + v, 0);
109530
+ const paired = Object.entries(summary2).filter(([k]) => k !== "unpaired" && k !== "targeted").reduce((n3, [, v]) => n3 + v, 0);
109327
109531
  return (summary2.unpaired ?? 0) > 0 && paired === 0 && journeyWriteSteps(journey).length === 0 && journeyParamSchema(journey).required.length === 0;
109328
109532
  }
109329
109533
  function relativeEvidence(cwd, path2) {
@@ -109409,21 +109613,38 @@ async function runJourneyReadiness(opts, deps = {}) {
109409
109613
  const pair = opts.with !== void 0;
109410
109614
  let environment;
109411
109615
  let replay;
109616
+ let boundConsumerParams;
109412
109617
  if (mode !== "report") {
109413
- environment = resolveTarget2(opts, deps, requiredParams(contexts[0].journey, pair, opts.requests !== void 0));
109618
+ const producerSchema = journeyParamSchema(contexts[0].journey).required;
109619
+ environment = resolveTarget2(opts, deps, requiredParams(contexts[0].journey, pair, opts.requests !== void 0), producerSchema);
109414
109620
  const params = { ...opts.params ?? {} };
109415
109621
  try {
109416
109622
  if (pair) {
109417
- const chained = await chain({
109418
- from: opts.id,
109419
- to: opts.with,
109420
- requests: opts.requests ?? "",
109421
- dir: opts.journeysDir,
109422
- environment,
109423
- ...opts.storageState === void 0 ? {} : { storageState: opts.storageState },
109424
- ...opts.browser === void 0 ? {} : { browser: opts.browser },
109425
- ...opts.browserPortFactory === void 0 ? {} : { browserPortFactory: opts.browserPortFactory }
109426
- });
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
+ );
109427
109648
  replay = pairReplay(chained);
109428
109649
  } else {
109429
109650
  const result = await run({
@@ -109443,11 +109664,23 @@ async function runJourneyReadiness(opts, deps = {}) {
109443
109664
  }
109444
109665
  if (mode === "prove") {
109445
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;
109446
109679
  try {
109447
109680
  await verify2({
109448
109681
  dir: opts.journeysDir,
109449
109682
  id: ctx.id,
109450
- params,
109683
+ params: proofParams,
109451
109684
  environment,
109452
109685
  ...opts.storageState === void 0 ? {} : { storageState: opts.storageState },
109453
109686
  ...opts.browser === void 0 ? {} : { browser: opts.browser },
@@ -110800,8 +111033,9 @@ function registerJourneyVerify(journey, program2, deps) {
110800
111033
  for (const a of report2.assertions) out?.(`${verifyAssertionLine(a)}
110801
111034
  `);
110802
111035
  const s3 = report2.summary;
111036
+ const targeted = s3.targeted > 0 ? `, ${s3.targeted} targeted` : "";
110803
111037
  out?.(
110804
- `${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})`}
110805
111039
  `
110806
111040
  );
110807
111041
  }
@@ -115046,13 +115280,14 @@ var CLI_TOOL_SPECS = [
115046
115280
  },
115047
115281
  {
115048
115282
  name: "chain_journeys",
115049
- 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.",
115050
115284
  command: {
115051
115285
  path: "journey chain",
115052
115286
  params: {
115053
115287
  from: s2("--from", { required: true }),
115054
115288
  to: s2("--to", { required: true }),
115055
115289
  requests: path("--requests", { required: true }),
115290
+ params: { kind: "params", flag: "--param" },
115056
115291
  storageState: session("--storage-state"),
115057
115292
  ...EXTENSION,
115058
115293
  ...EMULATION,
@@ -5,7 +5,7 @@
5
5
  // this plain `tsc --build` dist (no bundler substitution), so it needs real committed values
6
6
  // here instead. See ./engine.ts, which prefers the esbuild-injected value when present and
7
7
  // falls back to these.
8
- export const GENERATED_COMMIT = "ec540dd";
9
- export const GENERATED_TREE = "d9818ad";
10
- export const GENERATED_BUILT_AT = "2026-10-18T23:04:17.587Z";
8
+ export const GENERATED_COMMIT = "0a3d2f5";
9
+ export const GENERATED_TREE = "963f59f";
10
+ export const GENERATED_BUILT_AT = "2026-10-19T17:40:57.283Z";
11
11
  //# sourceMappingURL=build-info.generated.js.map
@@ -425,7 +425,11 @@ export async function runJourneyProgrammatically(opts, mutation = {}) {
425
425
  // `extract as: "apiKey"`), so the output, failures and artifacts below never carry it.
426
426
  let runSecrets = secrets;
427
427
  try {
428
- const raw = await outcomeChecks.run(actor, () => runner.run({ journey: replayed, params, policy }));
428
+ // #609: a mutated copy may no longer name a param the stored Journey takes (a targeted proof
429
+ // relaxed the only check naming an assertion-only `expected_*` param). The supplied set was
430
+ // validated against the stored Journey above; the copy runs with the subset it still reads.
431
+ const replayParams = replayed === journey ? params : prefixParams(journey, replayed, params);
432
+ const raw = await outcomeChecks.run(actor, () => runner.run({ journey: replayed, params: replayParams, policy }));
429
433
  runSecrets = runSecretValues(secrets, raw);
430
434
  result = redactSecretValues(raw, runSecrets);
431
435
  }