nudojs 1.3.20 → 1.3.21

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 (2) hide show
  1. package/dist/index.js +221 -36
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -19039,6 +19039,18 @@ function withExecPhi(p, body) {
19039
19039
  phi = prev;
19040
19040
  }
19041
19041
  }
19042
+ function currentAssumePremises() {
19043
+ return assumePremises;
19044
+ }
19045
+ function withAssumePremises(p, body) {
19046
+ const prev = assumePremises;
19047
+ assumePremises = p;
19048
+ try {
19049
+ return body();
19050
+ } finally {
19051
+ assumePremises = prev;
19052
+ }
19053
+ }
19042
19054
  function setHostGlobalFnCall(bridge) {
19043
19055
  hostGlobalFnCall = bridge;
19044
19056
  }
@@ -19075,6 +19087,12 @@ function $absVal(v2) {
19075
19087
  return asAbsVal(v2);
19076
19088
  }
19077
19089
  function callAtFunctionBoundary(body) {
19090
+ if (assumePremises !== pTrue) {
19091
+ return withAssumePremises(pTrue, () => boundaryBody(body));
19092
+ }
19093
+ return boundaryBody(body);
19094
+ }
19095
+ function boundaryBody(body) {
19078
19096
  let forwarded = [];
19079
19097
  try {
19080
19098
  return runWithLoopExits(() => {
@@ -19383,7 +19401,7 @@ function $isBreakTo(e, label) {
19383
19401
  function $rethrowIfNudoReturn(e) {
19384
19402
  if (isNudoReturn(e)) throw e;
19385
19403
  }
19386
- var phi, noBody2, hostGlobalFnCall, newTargetStack, loopExitsAls, throwExitsAls, tryMarksAls, softFrameActiveAls, NudoReturn, loopSignalFromAbstractArm, NudoLoopSignal;
19404
+ var phi, assumePremises, noBody2, hostGlobalFnCall, newTargetStack, loopExitsAls, throwExitsAls, tryMarksAls, softFrameActiveAls, NudoReturn, loopSignalFromAbstractArm, NudoLoopSignal;
19387
19405
  var init_state = __esm({
19388
19406
  "../core/src/algebra/exec/runtime/state.ts"() {
19389
19407
  "use strict";
@@ -19396,6 +19414,7 @@ var init_state = __esm({
19396
19414
  init_containers();
19397
19415
  init_objects();
19398
19416
  phi = pTrue;
19417
+ assumePremises = pTrue;
19399
19418
  noBody2 = { type: "BlockStatement", body: [], directives: [] };
19400
19419
  newTargetStack = [];
19401
19420
  loopExitsAls = new AsyncLocalStorage3();
@@ -23800,7 +23819,12 @@ function emitArrMutatorRebinds(expr, opts, pad) {
23800
23819
  if (node.type === "CallExpression" && node.callee?.type === "MemberExpression" && node.callee.computed !== true && node.callee.property?.type === "Identifier" && ARR_MUTATOR_NAMES.has(node.callee.property.name)) {
23801
23820
  const methodName = node.callee.property.name;
23802
23821
  const argSrcs = (node.arguments ?? []).map(
23803
- (a) => a.type === "SpreadElement" ? "$unknown()" : isExpression(a) ? emitTranspileExpression(a, opts) : "$unknown()"
23822
+ (a) => (
23823
+ // spread 实参展开(issue #153):$elems → JS Abs[],数组字面量 JS
23824
+ // spread 平铺(与 expr.ts 普通调用同口径);此前折 $unknown() 单参
23825
+ // 让重绑行以 unknown 实参执行真实变更,污染容器元素域
23826
+ a.type === "SpreadElement" ? `...$elems(${emitTranspileExpression(a.argument, opts)})` : isExpression(a) ? emitTranspileExpression(a, opts) : "$unknown()"
23827
+ )
23804
23828
  ).join(", ");
23805
23829
  const objNode = node.callee.object;
23806
23830
  if (objNode.type === "Identifier") {
@@ -24947,7 +24971,12 @@ ${pad}export default ${name};`;
24947
24971
  const methodName = expr.callee.property.name;
24948
24972
  if (["push", "unshift", "splice", "pop", "shift", "reverse", "sort"].includes(methodName)) {
24949
24973
  const argSrcs = expr.arguments.map(
24950
- (a) => a.type === "SpreadElement" ? "$unknown()" : emitTranspileExpression(a, opts)
24974
+ (a) => (
24975
+ // spread 实参展开(issue #153):$elems → JS Abs[],数组字面量 JS spread
24976
+ // 平铺(与普通调用 $callNamed/$invoke 同口径,DEC-006 K5);
24977
+ // 此前折 $unknown() 单参会污染容器元素域
24978
+ a.type === "SpreadElement" ? `...$elems(${emitTranspileExpression(a.argument, opts)})` : emitTranspileExpression(a, opts)
24979
+ )
24951
24980
  ).join(", ");
24952
24981
  const objNode = expr.callee.object;
24953
24982
  if (objNode.type === "Identifier") {
@@ -27251,7 +27280,7 @@ function $mul(a, b) {
27251
27280
  return mul(a, b, currentExecPhi());
27252
27281
  }
27253
27282
  function $div(a, b) {
27254
- return div(a, b, currentExecPhi());
27283
+ return div(a, b, currentExecPhi(), currentAssumePremises());
27255
27284
  }
27256
27285
  function $mod(a, b) {
27257
27286
  return mod(a, b, currentExecPhi());
@@ -31822,6 +31851,7 @@ __export(runtime_exports, {
31822
31851
  callAtFunctionBoundary: () => callAtFunctionBoundary,
31823
31852
  clearStaleTermPred: () => clearStaleTermPred,
31824
31853
  confPartial: () => confPartial,
31854
+ currentAssumePremises: () => currentAssumePremises,
31825
31855
  currentExecPhi: () => currentExecPhi,
31826
31856
  fillTuple: () => fillTuple,
31827
31857
  isArrMutator: () => isArrMutator,
@@ -31839,6 +31869,7 @@ __export(runtime_exports, {
31839
31869
  runWithLoopExits: () => runWithLoopExits,
31840
31870
  takeLoopExits: () => takeLoopExits,
31841
31871
  takeThrowExits: () => takeThrowExits,
31872
+ withAssumePremises: () => withAssumePremises,
31842
31873
  withExecPhi: () => withExecPhi,
31843
31874
  withNewTargetReset: () => withNewTargetReset
31844
31875
  });
@@ -31932,6 +31963,15 @@ function isNudoConstraint(x) {
31932
31963
  function isIntFlag(c) {
31933
31964
  return c.int === true || intFlaggedBuilders.has(c);
31934
31965
  }
31966
+ function finiteBoundArg(method, n) {
31967
+ if (typeof n !== "number" || !Number.isFinite(n)) {
31968
+ const shown = typeof n === "string" ? JSON.stringify(n) : String(n);
31969
+ throw new Error(
31970
+ `nudo ${method}(): finite numeric bound expected, got ${shown} \u2014 variable references are not supported; express cross-variable lemmas with @nudo:assume (issue #152)`
31971
+ );
31972
+ }
31973
+ return n;
31974
+ }
31935
31975
  function makeBuilder(prim2, preds, extra) {
31936
31976
  const fields = extra?.fields;
31937
31977
  const element = extra?.element;
@@ -31956,17 +31996,16 @@ function makeBuilder(prim2, preds, extra) {
31956
31996
  /* @__PURE__ */ Object.create(null),
31957
31997
  base,
31958
31998
  {
31959
- gt: (n) => add2(gt(selfTerm(), lit(n))),
31960
- ge: (n) => add2(ge(selfTerm(), lit(n))),
31961
- lt: (n) => add2(lt(selfTerm(), lit(n))),
31962
- le: (n) => add2(le(selfTerm(), lit(n))),
31999
+ gt: (n) => add2(gt(selfTerm(), lit(finiteBoundArg("gt", n)))),
32000
+ ge: (n) => add2(ge(selfTerm(), lit(finiteBoundArg("ge", n)))),
32001
+ lt: (n) => add2(lt(selfTerm(), lit(finiteBoundArg("lt", n)))),
32002
+ le: (n) => add2(le(selfTerm(), lit(finiteBoundArg("le", n)))),
31963
32003
  int: () => makeBuilder(prim2 ?? "number", preds, { ...extra, int: true }),
31964
- min: (n) => add2(ge(lenTerm(selfTerm()), lit(n))),
31965
- max: (n) => add2(le(lenTerm(selfTerm()), lit(n))),
32004
+ min: (n) => add2(ge(lenTerm(selfTerm()), lit(finiteBoundArg("min", n)))),
32005
+ max: (n) => add2(le(lenTerm(selfTerm()), lit(finiteBoundArg("max", n)))),
31966
32006
  length: (n) => add2(and(ge(lenTerm(selfTerm()), lit(n)), le(lenTerm(selfTerm()), lit(n)))),
31967
32007
  shift: (n) => {
31968
- if (!Number.isFinite(n))
31969
- throw new Error("nudo shift(): offset must be a finite number");
32008
+ finiteBoundArg("shift", n);
31970
32009
  if (fields || element || members || fnSlot || lazySlot)
31971
32010
  throw new Error("nudo shift(): only numeric scalar constraint chains are allowed (shape/array/union/fn are not supported)");
31972
32011
  if (prim2 !== void 0 && prim2 !== "number")
@@ -37078,7 +37117,9 @@ function callTranspiledExportFullInner(exports, name, args, opts) {
37078
37117
  return runWithLoopExits(() => {
37079
37118
  try {
37080
37119
  const invoke = () => withNewTargetReset(() => fn2(...callArgs));
37081
- const r = opts?.phi ? withExecPhi(opts.phi, invoke) : invoke();
37120
+ const ap = opts?.assumePremises;
37121
+ const seeded = ap !== void 0 ? () => withAssumePremises(ap, invoke) : invoke;
37122
+ const r = opts?.phi ? withExecPhi(opts.phi, seeded) : seeded();
37082
37123
  drainPromiseMicros();
37083
37124
  if (!isAbsVal(r)) return joinControlExits(asAbsVal(r));
37084
37125
  return joinControlExits(r);
@@ -40816,7 +40857,50 @@ function mul(a, b, phi2 = pTrue) {
40816
40857
  noteAbstractArithMayThrow(a, b);
40817
40858
  return abs({ k: "unknown" }, void 0, void 0, "partial");
40818
40859
  }
40819
- function div(a, b, phi2 = pTrue) {
40860
+ function assumePremiseList(assume) {
40861
+ if (!assume || assume.op === "true") return [];
40862
+ const out = [];
40863
+ const walk = (p) => {
40864
+ if (p.op === "and") {
40865
+ p.args.forEach(walk);
40866
+ return;
40867
+ }
40868
+ if (p.op !== "true") out.push(p);
40869
+ };
40870
+ walk(assume);
40871
+ return out;
40872
+ }
40873
+ function divAssumeBounds(a, b, resultTerm, phi2, assume) {
40874
+ const premises = assumePremiseList(assume);
40875
+ if (premises.length === 0) return void 0;
40876
+ if (!a.term || !b.term) return void 0;
40877
+ const bb = numericBounds(b, phi2);
40878
+ const posDen = bb?.lo !== void 0 && (bb.lo.value > 0 || bb.lo.value === 0 && bb.lo.strict);
40879
+ const negDen = bb?.hi !== void 0 && (bb.hi.value < 0 || bb.hi.value === 0 && bb.hi.strict);
40880
+ if (!posDen && !negDen) return void 0;
40881
+ let aLeB = false;
40882
+ let bLeA = false;
40883
+ for (const p of premises) {
40884
+ if (p.op !== "le") continue;
40885
+ if (termEquals(p.a, a.term) && termEquals(p.b, b.term)) aLeB = true;
40886
+ else if (termEquals(p.a, b.term) && termEquals(p.b, a.term)) bLeA = true;
40887
+ }
40888
+ if (!aLeB && !bLeA) return void 0;
40889
+ const facts = [];
40890
+ if (aLeB) facts.push(posDen ? le(resultTerm, lit(1)) : ge(resultTerm, lit(1)));
40891
+ if (bLeA) facts.push(posDen ? ge(resultTerm, lit(1)) : le(resultTerm, lit(1)));
40892
+ const ab = numericBounds(a, phi2);
40893
+ if (ab?.lo !== void 0 && (ab.lo.value > 0 || ab.lo.value === 0)) {
40894
+ if (posDen) facts.push(ab.lo.strict ? gt(resultTerm, lit(0)) : ge(resultTerm, lit(0)));
40895
+ else facts.push(ab.lo.strict ? lt(resultTerm, lit(0)) : le(resultTerm, lit(0)));
40896
+ }
40897
+ if (ab?.hi !== void 0 && (ab.hi.value < 0 || ab.hi.value === 0)) {
40898
+ if (posDen) facts.push(ab.hi.strict ? lt(resultTerm, lit(0)) : le(resultTerm, lit(0)));
40899
+ else facts.push(ab.hi.strict ? gt(resultTerm, lit(0)) : ge(resultTerm, lit(0)));
40900
+ }
40901
+ return facts.length ? and(...facts) : void 0;
40902
+ }
40903
+ function div(a, b, phi2 = pTrue, assume) {
40820
40904
  {
40821
40905
  const fa = builtinBrandToPrimitive(a, "number");
40822
40906
  const fb = builtinBrandToPrimitive(b, "number");
@@ -40871,7 +40955,13 @@ function div(a, b, phi2 = pTrue) {
40871
40955
  term.op === "lit" ? "exact" : confJoin(confJoin(a.conf, b.conf), "path")
40872
40956
  );
40873
40957
  }
40874
- return abs(num().shape, term, void 0, confJoin(confJoin(a.conf, b.conf), "path"));
40958
+ const assumePred = divAssumeBounds(a, b, term, phi2, assume);
40959
+ return abs(
40960
+ num().shape,
40961
+ term,
40962
+ assumePred,
40963
+ confJoin(confJoin(a.conf, b.conf), "path")
40964
+ );
40875
40965
  }
40876
40966
  if (isNumericLike(a) && isNumericLike(b)) {
40877
40967
  return abs(num().shape, void 0, void 0, "partial");
@@ -40882,7 +40972,7 @@ function div(a, b, phi2 = pTrue) {
40882
40972
  if (isAnyLike(a) || isAnyLike(b)) {
40883
40973
  return toNumberResult(a, b, "/");
40884
40974
  }
40885
- const distributed = distributeSumBinOp("/", a, b, phi2, div);
40975
+ const distributed = distributeSumBinOp("/", a, b, phi2, (x, y, p) => div(x, y, p, assume));
40886
40976
  if (distributed) return distributed;
40887
40977
  noteAbstractArithMayThrow(a, b);
40888
40978
  return abs({ k: "unknown" }, void 0, void 0, "partial");
@@ -41692,6 +41782,14 @@ function scanContractSegments(lines) {
41692
41782
  }
41693
41783
  return reqs;
41694
41784
  }
41785
+ function scanAssumeSegments(lines) {
41786
+ const out = [];
41787
+ for (const line of lines) {
41788
+ const m = line.match(/^@nudo:assume\s+(.+)$/);
41789
+ if (m) out.push(m[1].trim().replace(/\*\/$/, "").trim());
41790
+ }
41791
+ return out;
41792
+ }
41695
41793
  function scanThrowsDecl(lines) {
41696
41794
  const kinds = /* @__PURE__ */ new Set();
41697
41795
  let any2 = false;
@@ -42566,6 +42664,24 @@ function extractRefineLines(source, fnName) {
42566
42664
  const lines = fnDirectiveCommentLines(source, fnName);
42567
42665
  return scanContractSegments(lines);
42568
42666
  }
42667
+ function extractAssumeSegs(source, fnName) {
42668
+ if (!stripStringsKeepComments(source).includes("@nudo:assume")) return [];
42669
+ const out = [];
42670
+ for (const seg of scanAssumeSegments(fnDirectiveCommentLines(source, fnName))) {
42671
+ const geSeg = seg.match(/^([A-Za-z_$][\w$]*)\s*>=\s*([A-Za-z_$][\w$]*)$/);
42672
+ if (geSeg) {
42673
+ out.push({ kind: "ok", lhs: geSeg[2], rhs: geSeg[1] });
42674
+ continue;
42675
+ }
42676
+ const leSeg = seg.match(/^([A-Za-z_$][\w$]*)\s*<=\s*([A-Za-z_$][\w$]*)$/);
42677
+ if (leSeg) {
42678
+ out.push({ kind: "ok", lhs: leSeg[1], rhs: leSeg[2] });
42679
+ continue;
42680
+ }
42681
+ out.push({ kind: "bad", raw: seg });
42682
+ }
42683
+ return out;
42684
+ }
42569
42685
  function extractRefinesFromSource(source, fnName, opts = {}) {
42570
42686
  if (!stripStringsKeepComments(source).includes("@nudo:contract")) return [];
42571
42687
  const constraints = collectConstraints(source, opts);
@@ -45369,7 +45485,7 @@ function flattenSumArms(ret) {
45369
45485
  return out;
45370
45486
  }
45371
45487
  function isWidenedSumArm(m, parent) {
45372
- return parent.conf === "partial" && m.shape.k === "prim" && !m.term && (!m.pred || m.pred.op === "true");
45488
+ return (parent.conf === "partial" || m.conf === "widened") && m.shape.k === "prim" && !m.term && (!m.pred || m.pred.op === "true");
45373
45489
  }
45374
45490
  function provesBarePrimDomain(arm, constraint) {
45375
45491
  if (constraint.members) {
@@ -45396,7 +45512,7 @@ function assertImplies(ret, constraint, opts) {
45396
45512
  const phi2 = opts?.phi ?? pTrue;
45397
45513
  if (ret.shape.k === "never") return proved();
45398
45514
  if (isAnyConstraint(constraint)) return proved();
45399
- if (isValueSetUnknown(ret)) {
45515
+ if (isValueSetUnknown(ret) && ret.conf !== "widened") {
45400
45516
  return unprovable(
45401
45517
  ret.shape.k === "any" ? "any does not establish the return contract" : "inference failure (unknown/opaque) \u2014 cannot prove the return contract"
45402
45518
  );
@@ -45452,6 +45568,9 @@ function assertImpliesSingle(arm, constraint, opts, phi2) {
45452
45568
  return constraintAdmitsNullish(constraint) ? proved() : disproved(`nullish return \u22AD ${formatConstraint(constraint)}`);
45453
45569
  }
45454
45570
  if (isValueSetUnknown(arm)) {
45571
+ if (arm.conf === "widened" && arm.shape.k === "prim" && !arm.term && (!arm.pred || arm.pred.op === "true") && provesBarePrimDomain(arm, constraint)) {
45572
+ return proved();
45573
+ }
45455
45574
  return unprovable(
45456
45575
  arm.shape.k === "any" ? "any does not establish the return contract" : "inference failure (unknown/opaque) \u2014 cannot prove the return contract"
45457
45576
  );
@@ -47509,6 +47628,7 @@ function numericBound(p, self) {
47509
47628
  if (p.op !== "gt" && p.op !== "ge" && p.op !== "lt" && p.op !== "le") return void 0;
47510
47629
  if (!self) return void 0;
47511
47630
  if (termEquals(p.a, self) && p.b.op === "lit" && typeof p.b.value === "number") {
47631
+ if (!Number.isFinite(p.b.value)) return void 0;
47512
47632
  return { op: p.op, n: p.b.value };
47513
47633
  }
47514
47634
  return void 0;
@@ -48578,6 +48698,49 @@ function sidecarDiagIssues(diags) {
48578
48698
  }
48579
48699
  return out;
48580
48700
  }
48701
+ function assumePremisesPhiOf(name, source, file, issues) {
48702
+ const segs = extractAssumeSegs(source, name);
48703
+ if (segs.length === 0) return void 0;
48704
+ const meta = extractFn(source, name, file);
48705
+ const bindable = /* @__PURE__ */ new Set([
48706
+ ...contractParamNameSet(meta?.formals ?? []),
48707
+ ...meta?.params ?? []
48708
+ ]);
48709
+ const surface = [...bindable].join(", ");
48710
+ const preds = [];
48711
+ const display = [];
48712
+ for (const seg of segs) {
48713
+ if (seg.kind === "bad") {
48714
+ issues.push({
48715
+ severity: "error",
48716
+ code: "nudo:assume-invalid",
48717
+ message: `${name}: malformed @nudo:assume '${seg.raw}' (expected <param> <= <param> or <param> >= <param>; constant bounds belong to sidecar contracts)`,
48718
+ actual: seg.raw,
48719
+ expected: "<param> <= <param>",
48720
+ suggestion: "cross-variable lemmas: @nudo:assume d <= max \u2014 constant bounds: sidecar number().le(10)",
48721
+ fn: name
48722
+ });
48723
+ continue;
48724
+ }
48725
+ const bad = [seg.lhs, seg.rhs].filter((n) => !bindable.has(n));
48726
+ if (bad.length > 0) {
48727
+ issues.push({
48728
+ severity: "error",
48729
+ code: "nudo:assume-invalid",
48730
+ message: `${name}: @nudo:assume parameter name(s) not in the formal parameter list (${bad.join(", ")})`,
48731
+ actual: bad.join(", "),
48732
+ expected: surface || "(no params)",
48733
+ suggestion: `reference one of: ${surface || "(function has no params)"}`,
48734
+ fn: name
48735
+ });
48736
+ continue;
48737
+ }
48738
+ preds.push(le(v(seg.lhs), v(seg.rhs)));
48739
+ display.push(`${seg.lhs} <= ${seg.rhs}`);
48740
+ }
48741
+ if (preds.length === 0) return void 0;
48742
+ return { phi: and(...preds), display };
48743
+ }
48581
48744
  function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures, names2, truncated, identityOpts = opts, depsFp, sidecarFp) {
48582
48745
  const hasRefineDirective = stripStringsKeepComments(source).includes("@nudo:contract");
48583
48746
  const entryNames = localNamedExports(source);
@@ -48605,6 +48768,8 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
48605
48768
  return eff;
48606
48769
  };
48607
48770
  for (const name of names2) {
48771
+ const assumePhi = assumePremisesPhiOf(name, source, file, issues);
48772
+ const runSeeded = (body) => assumePhi === void 0 ? body() : withAssumePremises(assumePhi.phi, body);
48608
48773
  if (opts.skips?.has(name)) {
48609
48774
  const declared = opts.skips.get(name) ?? null;
48610
48775
  const meta = extractFn(source, name, file);
@@ -48649,19 +48814,21 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
48649
48814
  }
48650
48815
  continue;
48651
48816
  }
48652
- const g = generalizeFromAst(name, source, {
48653
- file,
48654
- refine: {
48655
- loadModule: refineLoad,
48656
- fromFile: refineFrom,
48657
- ...autoBind !== void 0 ? { autoBind } : {},
48658
- ...projectDir !== void 0 ? { projectDir } : {}
48659
- },
48660
- depsFp,
48661
- sidecarFp,
48662
- modules: opts.modules,
48663
- ...opts.inject ? { inject: opts.inject } : {}
48664
- });
48817
+ const g = runSeeded(
48818
+ () => generalizeFromAst(name, source, {
48819
+ file,
48820
+ refine: {
48821
+ loadModule: refineLoad,
48822
+ fromFile: refineFrom,
48823
+ ...autoBind !== void 0 ? { autoBind } : {},
48824
+ ...projectDir !== void 0 ? { projectDir } : {}
48825
+ },
48826
+ depsFp,
48827
+ sidecarFp,
48828
+ modules: opts.modules,
48829
+ ...opts.inject ? { inject: opts.inject } : {}
48830
+ })
48831
+ );
48665
48832
  if (!g) {
48666
48833
  const isEntryCandidate = entryNames.has(name) || name === "default" || entryNames.has("default") && isDefaultExportName(source, name);
48667
48834
  if (isEntryCandidate && entryThrowsMode !== "off") {
@@ -48721,7 +48888,7 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
48721
48888
  const isEntry = entryNames.has(name) || name === "default" || entryNames.has("default") && isDefaultExportName(source, name) || name.includes(".") && entryNames.has(name);
48722
48889
  let throwsDisplay;
48723
48890
  if (isEntry && entryThrowsMode !== "off") {
48724
- const effects = collectEntryMayThrows(source, name, g, file, phi2, opts);
48891
+ const effects = runSeeded(() => collectEntryMayThrows(source, name, g, file, phi2, opts));
48725
48892
  throwsDisplay = formatThrowsAbs(mayThrowEffectsToAbs(effects));
48726
48893
  const declared = extractDeclaredThrows(source, name) ?? effectiveInterfaceCached(name)?.throws?.kinds;
48727
48894
  const remaining = filterGateThrows(effects, declared, ignoreThrows);
@@ -48742,7 +48909,7 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
48742
48909
  });
48743
48910
  }
48744
48911
  } else if (isEntry) {
48745
- const effects = collectEntryMayThrows(source, name, g, file, phi2, opts);
48912
+ const effects = runSeeded(() => collectEntryMayThrows(source, name, g, file, phi2, opts));
48746
48913
  throwsDisplay = formatThrowsAbs(mayThrowEffectsToAbs(effects));
48747
48914
  }
48748
48915
  const fmt = formatSigCached(g.symbolic, name);
@@ -48836,7 +49003,8 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
48836
49003
  name,
48837
49004
  display,
48838
49005
  eff.returns.constraint,
48839
- g.symbolic
49006
+ g.symbolic,
49007
+ assumePhi?.display
48840
49008
  );
48841
49009
  if (retIssues.some((i) => i.severity === "error")) returnViolated.add(name);
48842
49010
  issues.push(...retIssues);
@@ -49032,9 +49200,24 @@ function checkSourceInner(filePath, source, file, phi2, opts, issues, signatures
49032
49200
  }
49033
49201
  };
49034
49202
  }
49035
- function checkReturnConstraint(fnName, cName, constraint, ret) {
49203
+ function checkReturnConstraint(fnName, cName, constraint, ret, assumeDisplay) {
49036
49204
  const proof = assertImplies(ret, constraint);
49037
- if (proof.status === "proved") return [];
49205
+ if (proof.status === "proved") {
49206
+ if (assumeDisplay && assumeDisplay.length > 0) {
49207
+ return [
49208
+ {
49209
+ severity: "info",
49210
+ code: "nudo:assume-relied",
49211
+ message: `${fnName}: return contract proved via @nudo:assume ${assumeDisplay.join("; ")}`,
49212
+ actual: `proof relies on user lemma${assumeDisplay.length > 1 ? "s" : ""}`,
49213
+ expected: "engine verifies the obligation, not the lemma itself",
49214
+ suggestion: "@nudo:assume is a user-supplied axiom \u2014 falsify it with concrete @nudo:case runs (nudo test)",
49215
+ fn: fnName
49216
+ }
49217
+ ];
49218
+ }
49219
+ return [];
49220
+ }
49038
49221
  const push = (severity, actual, expected, suggestion) => [
49039
49222
  {
49040
49223
  severity,
@@ -49082,6 +49265,7 @@ var init_check = __esm({
49082
49265
  init_abs();
49083
49266
  init_abs();
49084
49267
  init_pred();
49268
+ init_term();
49085
49269
  init_format();
49086
49270
  init_scan();
49087
49271
  init_check_interface_drift();
@@ -49090,6 +49274,7 @@ var init_check = __esm({
49090
49274
  init_check_assign();
49091
49275
  init_check_case_scan();
49092
49276
  init_run();
49277
+ init_runtime();
49093
49278
  init_calls();
49094
49279
  init_may_throw2();
49095
49280
  init_check_memo();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nudojs",
3
- "version": "1.3.20",
3
+ "version": "1.3.21",
4
4
  "engines": {
5
5
  "node": ">=20"
6
6
  },
@@ -46,11 +46,11 @@
46
46
  "dependencies": {
47
47
  "@babel/generator": "^8.0.6",
48
48
  "@babel/types": "^8.0.6",
49
- "@nudojs/core": "1.10.2",
50
- "@nudojs/env": "0.4.34",
51
- "@nudojs/harvester": "0.3.20",
52
- "@nudojs/parser": "1.4.16",
53
- "@nudojs/service": "1.6.19",
49
+ "@nudojs/core": "1.11.0",
50
+ "@nudojs/env": "0.4.35",
51
+ "@nudojs/harvester": "0.3.21",
52
+ "@nudojs/parser": "1.4.17",
53
+ "@nudojs/service": "1.6.20",
54
54
  "commander": "^15.0.0"
55
55
  },
56
56
  "devDependencies": {