@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.
Files changed (46) hide show
  1. package/dist/bin.js +359 -90
  2. package/dist/build-info.generated.js +3 -3
  3. package/dist/journeeze-api.d.ts +5 -0
  4. package/dist/journeeze-api.js +24 -9
  5. package/dist/journeeze-api.js.map +1 -1
  6. package/dist/journeeze-connect.d.ts +2 -0
  7. package/dist/journeeze-connect.js +9 -1
  8. package/dist/journeeze-connect.js.map +1 -1
  9. package/dist/journey-api.js +5 -1
  10. package/dist/journey-api.js.map +1 -1
  11. package/dist/journey-bundle.js +35 -20
  12. package/dist/journey-bundle.js.map +1 -1
  13. package/dist/journey-chain.d.ts +6 -1
  14. package/dist/journey-chain.js +27 -10
  15. package/dist/journey-chain.js.map +1 -1
  16. package/dist/journey-cli/chain.js +4 -2
  17. package/dist/journey-cli/chain.js.map +1 -1
  18. package/dist/journey-cli/lint.js +2 -2
  19. package/dist/journey-cli/lint.js.map +1 -1
  20. package/dist/journey-cli/migrate.js +2 -2
  21. package/dist/journey-cli/migrate.js.map +1 -1
  22. package/dist/journey-cli/promote.js +4 -4
  23. package/dist/journey-cli/promote.js.map +1 -1
  24. package/dist/journey-cli/review.js +4 -4
  25. package/dist/journey-cli/review.js.map +1 -1
  26. package/dist/journey-cli/shared.d.ts +6 -0
  27. package/dist/journey-cli/shared.js +8 -0
  28. package/dist/journey-cli/shared.js.map +1 -1
  29. package/dist/journey-cli/verify.js +2 -1
  30. package/dist/journey-cli/verify.js.map +1 -1
  31. package/dist/journey-cli-bundle.js +2 -2
  32. package/dist/journey-cli-bundle.js.map +1 -1
  33. package/dist/journey-cli-readiness.js +2 -2
  34. package/dist/journey-cli-readiness.js.map +1 -1
  35. package/dist/journey-handback.js +3 -1
  36. package/dist/journey-handback.js.map +1 -1
  37. package/dist/journey-readiness.js +90 -8
  38. package/dist/journey-readiness.js.map +1 -1
  39. package/dist/journey-review.js +4 -3
  40. package/dist/journey-review.js.map +1 -1
  41. package/dist/journey-verify.d.ts +8 -2
  42. package/dist/journey-verify.js +41 -7
  43. package/dist/journey-verify.js.map +1 -1
  44. package/dist/mcp-cli-tools.js +2 -1
  45. package/dist/mcp-cli-tools.js.map +1 -1
  46. 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 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 = "60df60f";
62394
- var GENERATED_TREE = "dd8e50a";
62395
- var GENERATED_BUILT_AT = "2026-10-18T20:33:54.995Z";
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 ? "60df60f" : void 0;
62561
+ return true ? "0a3d2f5" : void 0;
62418
62562
  }
62419
62563
  function bundledBuiltAt() {
62420
- return true ? "2026-10-18T20:33:54.995Z" : void 0;
62564
+ return true ? "2026-10-19T17:41:04.191Z" : void 0;
62421
62565
  }
62422
62566
  function bundledTree() {
62423
- return true ? "dd8e50a" : 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;
@@ -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
- ...at3 === void 0 ? {} : { failedAt: at3 },
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 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 }));
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
- return { product: { id: product.id, name: product.name }, tenant: typeof tenant === "string" ? tenant : "", keyPrefix: prefix };
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 MAX_UPLOAD_BYTES = 256 * 1024 * 1024;
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 needsFrom = inputs !== void 0 || opts.requirePromoted === true;
108551
- const fromJourney = needsFrom ? await registry3.get(opts.from) : null;
108552
- 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
+ }
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 replay = async (m, describe6) => {
109242
+ const runReplay = async (m, describe6) => {
109067
109243
  try {
109068
- const how = describe6(journey, m, replayOpts2);
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, m, replayOpts2).journey },
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) => [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
+ ])
109115
109305
  );
109116
109306
  const judged = judgeAssertions(journey, plan2, results);
109117
- 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
+ }));
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
- 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}`;
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 passed = Object.values(verify2.summary).reduce((a, b2) => a + b2, 0);
109291
- 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 } };
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
- return { check: { check: "proof", journey: ctx.id, state: "inconclusive", detail: verify2.reason ?? `proof verdict ${verify2.verdict}`, evidence } };
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
- 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);
109386
109620
  const params = { ...opts.params ?? {} };
109387
109621
  try {
109388
109622
  if (pair) {
109389
- const chained = await chain({
109390
- from: opts.id,
109391
- to: opts.with,
109392
- requests: opts.requests ?? "",
109393
- dir: opts.journeysDir,
109394
- environment,
109395
- ...opts.storageState === void 0 ? {} : { storageState: opts.storageState },
109396
- ...opts.browser === void 0 ? {} : { browser: opts.browser },
109397
- ...opts.browserPortFactory === void 0 ? {} : { browserPortFactory: opts.browserPortFactory }
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.catalogDir ?? void 0),
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 humanCommands(entry, catalog, draft, ready) {
109662
- const commands = [];
109663
- const personaId = entry.review.catalog?.persona?.id;
109664
- const jobId = entry.review.catalog?.job?.id;
109665
- if (personaId !== void 0) {
109666
- const hash3 = catalog.personas.find((p) => p.id === personaId)?.contentHash;
109667
- commands.push(`jevitate persona approve ${personaId}${hash3 === void 0 ? "" : ` --reviewed-hash ${hash3}`}`);
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 (jobId !== void 0) {
109670
- const hash3 = catalog.jobs.find((j) => j.id === jobId)?.contentHash;
109671
- commands.push(`jevitate job approve ${jobId}${hash3 === void 0 ? "" : ` --reviewed-hash ${hash3}`}`);
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) commands.push(`jevitate journey annotate ${entry.id} --approve`);
109674
- if (ready) commands.push(`jevitate journey promote ${entry.id} --reviewed-hash ${entry.review.contentHash}`);
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
- lines.push("## Human-only commands", "");
109984
+ const approvals = [];
109985
+ const promotes = [];
109725
109986
  for (const entry of entries) {
109726
- for (const command of humanCommands(entry, catalog, drafts.has(entry.id), ready)) lines.push(`- ${command}`);
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
- if (!ready) {
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 entries) lines.push(`- jevitate journey promote ${entry.id}`);
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.catalogDir ?? void 0);
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,