libpetri 4.0.0 → 5.0.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.
@@ -762,7 +762,7 @@ function stripClusterEdgeAttrs(line) {
762
762
  }
763
763
 
764
764
  // src/viewer/version.ts
765
- var VERSION = true ? "4.0.0" : "0.0.0-dev";
765
+ var VERSION = true ? "5.0.0" : "0.0.0-dev";
766
766
 
767
767
  // src/viewer/index.ts
768
768
  async function mount(dotSource, container, opts = {}) {
@@ -1013,4 +1013,4 @@ export {
1013
1013
  VERSION,
1014
1014
  mount
1015
1015
  };
1016
- //# sourceMappingURL=chunk-KO6TSB47.js.map
1016
+ //# sourceMappingURL=chunk-4HMFNYTH.js.map
@@ -331,6 +331,9 @@ var MarkingStateBuilder = class {
331
331
  function deadlockFree() {
332
332
  return { type: "deadlock-free" };
333
333
  }
334
+ function terminatesAtSink() {
335
+ return { type: "terminates-at-sink" };
336
+ }
334
337
  function mutualExclusion(p1, p2) {
335
338
  return { type: "mutual-exclusion", p1, p2 };
336
339
  }
@@ -350,6 +353,8 @@ function propertyDescription(prop) {
350
353
  switch (prop.type) {
351
354
  case "deadlock-free":
352
355
  return "Deadlock-freedom";
356
+ case "terminates-at-sink":
357
+ return "Terminates at a declared sink";
353
358
  case "mutual-exclusion":
354
359
  return `Mutual exclusion of ${prop.p1.name} and ${prop.p2.name}`;
355
360
  case "place-bound":
@@ -1545,8 +1550,31 @@ function indexOrdered(flatNet, places) {
1545
1550
  }
1546
1551
  function encodePropertyViolation(flatNet, property, mVars, sinkPlaces, envInject) {
1547
1552
  switch (property.type) {
1548
- case "deadlock-free":
1549
- return encodeDeadlock(flatNet, mVars, sinkPlaces, envInject);
1553
+ // DeadlockFree (VER-002): a quiescent marking that STRANDS a token — holds one
1554
+ // in a place that is not a declared sink. The empty marking strands nothing and
1555
+ // is therefore not a violation (AC4).
1556
+ case "deadlock-free": {
1557
+ const conditions = encodeQuiescent(flatNet, mVars, envInject);
1558
+ if (conditions == null) return "false";
1559
+ const sinks = new Set(indexOrdered(flatNet, sinkPlaces));
1560
+ const stranded = [];
1561
+ for (let pid = 0; pid < flatNet.places.length; pid++) {
1562
+ if (!sinks.has(pid)) stranded.push(`(>= ${mVars[pid]} 1)`);
1563
+ }
1564
+ if (stranded.length === 0) return "false";
1565
+ conditions.push(`(or ${stranded.join(" ")})`);
1566
+ return joinConditions(conditions);
1567
+ }
1568
+ // TerminatesAtSink (VER-002): a quiescent marking that reached NO declared sink.
1569
+ // This is the predicate DeadlockFree carried before the VER-002 split, unchanged.
1570
+ case "terminates-at-sink": {
1571
+ const conditions = encodeQuiescent(flatNet, mVars, envInject);
1572
+ if (conditions == null) return "false";
1573
+ for (const pid of indexOrdered(flatNet, sinkPlaces)) {
1574
+ conditions.push(`(= ${mVars[pid]} 0)`);
1575
+ }
1576
+ return joinConditions(conditions);
1577
+ }
1550
1578
  case "mutual-exclusion": {
1551
1579
  const conditions = indexOrdered(flatNet, [property.p1, property.p2]).map((i) => `(>= ${mVars[i]} 1)`);
1552
1580
  return conditions.length === 0 ? "false" : `(and ${conditions.join(" ")})`;
@@ -1560,14 +1588,23 @@ function encodePropertyViolation(flatNet, property, mVars, sinkPlaces, envInject
1560
1588
  const conditions = indexOrdered(flatNet, property.places).map((i) => `(>= ${mVars[i]} 1)`);
1561
1589
  return conditions.length === 0 ? "false" : `(and ${conditions.join(" ")})`;
1562
1590
  }
1591
+ // JoinedOrDeadLettered (NU-040 AC4): a quiescent state that still holds a
1592
+ // `pending` token is a stranded correlation group. Carries NO sink clause — a
1593
+ // declared sink must not excuse a stranded group.
1563
1594
  case "joined-or-dead-lettered": {
1564
- const deadlock = encodeDeadlock(flatNet, mVars, sinkPlaces, envInject);
1565
1595
  const pid = flatNet.placeIndex.get(property.pending.name);
1566
- return pid == null ? "false" : `(and ${deadlock} (>= ${mVars[pid]} 1))`;
1596
+ if (pid == null) return "false";
1597
+ const conditions = encodeQuiescent(flatNet, mVars, envInject);
1598
+ if (conditions == null) return "false";
1599
+ conditions.push(`(>= ${mVars[pid]} 1)`);
1600
+ return joinConditions(conditions);
1567
1601
  }
1568
1602
  }
1569
1603
  }
1570
- function encodeDeadlock(flatNet, mVars, sinkPlaces, envInject) {
1604
+ function joinConditions(conditions) {
1605
+ return conditions.length === 0 ? "true" : `(and ${conditions.join("\n ")})`;
1606
+ }
1607
+ function encodeQuiescent(flatNet, mVars, envInject) {
1571
1608
  const envBound = /* @__PURE__ */ new Map();
1572
1609
  for (const inj of envInject) envBound.set(inj.pid, inj.bound);
1573
1610
  const disabledConditions = [];
@@ -1597,13 +1634,10 @@ function encodeDeadlock(flatNet, mVars, sinkPlaces, envInject) {
1597
1634
  disabledConditions.push("true");
1598
1635
  continue;
1599
1636
  }
1600
- if (disableReasons.length === 0) return "false";
1637
+ if (disableReasons.length === 0) return null;
1601
1638
  disabledConditions.push(`(or ${disableReasons.join(" ")})`);
1602
1639
  }
1603
- for (const pid of indexOrdered(flatNet, sinkPlaces)) {
1604
- disabledConditions.push(`(= ${mVars[pid]} 0)`);
1605
- }
1606
- return disabledConditions.length === 0 ? "true" : `(and ${disabledConditions.join("\n ")})`;
1640
+ return disabledConditions;
1607
1641
  }
1608
1642
  function injectionMap(flatNet) {
1609
1643
  const out = /* @__PURE__ */ new Map();
@@ -2511,20 +2545,38 @@ function enabledRelaxEnv(state, ft, envInj) {
2511
2545
  }
2512
2546
  return true;
2513
2547
  }
2514
- function isDeadlockA(index, state) {
2548
+ function isQuiescent(index, state) {
2515
2549
  for (const ft of index.flatNet.transitions) {
2516
2550
  if (enabledRelaxEnv(state, ft, index.envInj)) return false;
2517
2551
  }
2518
2552
  return true;
2519
2553
  }
2554
+ function sinkIndices(flatNet, sinkPlaces) {
2555
+ const idx = /* @__PURE__ */ new Set();
2556
+ for (const sink of sinkPlaces) {
2557
+ const i = flatNetIndexOf(flatNet, sink);
2558
+ if (i >= 0) idx.add(i);
2559
+ }
2560
+ return idx;
2561
+ }
2520
2562
  function satisfiesBadIndexed(index, state, property, sinkPlaces) {
2521
2563
  const flatNet = index.flatNet;
2522
2564
  switch (property.type) {
2565
+ // DeadlockFree (VER-002): quiescent AND some marked place is not a declared
2566
+ // sink. Mirrors the encoder's `stranded` disjunction.
2523
2567
  case "deadlock-free": {
2524
- if (!isDeadlockA(index, state)) return false;
2525
- for (const sink of sinkPlaces) {
2526
- const idx = flatNetIndexOf(flatNet, sink);
2527
- if (idx >= 0 && state[idx] > 0) return false;
2568
+ if (!isQuiescent(index, state)) return false;
2569
+ const sinks = sinkIndices(flatNet, sinkPlaces);
2570
+ for (let pid = 0; pid < flatNet.places.length; pid++) {
2571
+ if (!sinks.has(pid) && state[pid] >= 1) return true;
2572
+ }
2573
+ return false;
2574
+ }
2575
+ // TerminatesAtSink (VER-002): quiescent AND no declared sink marked.
2576
+ case "terminates-at-sink": {
2577
+ if (!isQuiescent(index, state)) return false;
2578
+ for (const pid of sinkIndices(flatNet, sinkPlaces)) {
2579
+ if (state[pid] !== 0) return false;
2528
2580
  }
2529
2581
  return true;
2530
2582
  }
@@ -2540,10 +2592,12 @@ function satisfiesBadIndexed(index, state, property, sinkPlaces) {
2540
2592
  if (idx < 0) return false;
2541
2593
  return state[idx] > property.bound;
2542
2594
  }
2595
+ // JoinedOrDeadLettered (NU-040 AC4): quiescent AND `pending` marked. No sink
2596
+ // clause — a marked sink must not excuse a stranded group.
2543
2597
  case "joined-or-dead-lettered": {
2544
2598
  const idx = flatNetIndexOf(flatNet, property.pending);
2545
2599
  if (idx < 0) return false;
2546
- return isDeadlockA(index, state) && state[idx] >= 1;
2600
+ return isQuiescent(index, state) && state[idx] >= 1;
2547
2601
  }
2548
2602
  case "unreachable": {
2549
2603
  let resolved = 0;
@@ -2914,13 +2968,38 @@ function encodeViolation(plan, lay, flat, property, sinkPlaces, envInj) {
2914
2968
  return anyPlacePresent([property.p1, property.p2]);
2915
2969
  case "unreachable":
2916
2970
  return anyPlacePresent(property.places);
2917
- case "deadlock-free":
2918
- return encodeColouredDeadlock(plan, lay, flat, sinkPlaces, envInj);
2971
+ // DeadlockFree (VER-002): quiescent AND some marked place is not a declared
2972
+ // sink. Mirrors the flat encoder's `stranded` disjunction.
2973
+ case "deadlock-free": {
2974
+ const conds = encodeColouredQuiescent(plan, lay, flat, envInj);
2975
+ if (conds == null) return "false";
2976
+ const sinks = new Set(indexOrdered(flat, sinkPlaces));
2977
+ const stranded = [];
2978
+ for (let pid = 0; pid < flat.places.length; pid++) {
2979
+ if (!sinks.has(pid)) stranded.push(`(>= ${aggregate(plan, lay, pid, lay.cur)} 1)`);
2980
+ }
2981
+ if (stranded.length === 0) return "false";
2982
+ conds.push(`(or ${stranded.join(" ")})`);
2983
+ return joinColoured(conds);
2984
+ }
2985
+ // TerminatesAtSink (VER-002): quiescent AND no declared sink marked.
2986
+ case "terminates-at-sink": {
2987
+ const conds = encodeColouredQuiescent(plan, lay, flat, envInj);
2988
+ if (conds == null) return "false";
2989
+ for (const pid of indexOrdered(flat, sinkPlaces)) {
2990
+ conds.push(`(= ${aggregate(plan, lay, pid, lay.cur)} 0)`);
2991
+ }
2992
+ return joinColoured(conds);
2993
+ }
2994
+ // JoinedOrDeadLettered (NU-040 AC4): quiescent AND `pending` marked, with NO
2995
+ // sink clause.
2919
2996
  case "joined-or-dead-lettered": {
2920
2997
  const pid = flat.placeIndex.get(property.pending.name);
2921
2998
  if (pid == null) return null;
2922
- const deadlock = encodeColouredDeadlock(plan, lay, flat, sinkPlaces, envInj);
2923
- return `(and ${deadlock} (>= ${aggregate(plan, lay, pid, lay.cur)} 1))`;
2999
+ const conds = encodeColouredQuiescent(plan, lay, flat, envInj);
3000
+ if (conds == null) return "false";
3001
+ conds.push(`(>= ${aggregate(plan, lay, pid, lay.cur)} 1)`);
3002
+ return joinColoured(conds);
2924
3003
  }
2925
3004
  }
2926
3005
  }
@@ -2978,7 +3057,10 @@ function colouredDisabledTerm(cls, plan, lay) {
2978
3057
  }
2979
3058
  }
2980
3059
  }
2981
- function encodeColouredDeadlock(plan, lay, flat, sinkPlaces, envInj) {
3060
+ function joinColoured(conds) {
3061
+ return conds.length === 0 ? "true" : `(and ${conds.join(" ")})`;
3062
+ }
3063
+ function encodeColouredQuiescent(plan, lay, flat, envInj) {
2982
3064
  const disabledConditions = [];
2983
3065
  for (let ti = 0; ti < plan.classes.length; ti++) {
2984
3066
  const cls = plan.classes[ti];
@@ -2991,13 +3073,10 @@ function encodeColouredDeadlock(plan, lay, flat, sinkPlaces, envInj) {
2991
3073
  }
2992
3074
  const term = colouredDisabledTerm(cls, plan, lay);
2993
3075
  if (term != null) reasons.push(term);
2994
- if (reasons.length === 0) return "false";
3076
+ if (reasons.length === 0) return null;
2995
3077
  disabledConditions.push(reasons.length === 1 ? reasons[0] : `(or ${reasons.join(" ")})`);
2996
3078
  }
2997
- for (const pid of indexOrdered(flat, sinkPlaces)) {
2998
- disabledConditions.push(`(= ${aggregate(plan, lay, pid, lay.cur)} 0)`);
2999
- }
3000
- return disabledConditions.length === 0 ? "true" : `(and ${disabledConditions.join(" ")})`;
3079
+ return disabledConditions;
3001
3080
  }
3002
3081
 
3003
3082
  // src/verification/analysis/name-fragment.ts
@@ -3484,8 +3563,16 @@ function decide(scg, property, sinkPlaces) {
3484
3563
  const m = scg.markingOf(i);
3485
3564
  return m.hasTokens(property.p1) && m.hasTokens(property.p2);
3486
3565
  });
3566
+ // DeadlockFree (VER-002): a quiescent class that strands a token — some marked
3567
+ // place is not a declared sink. The empty marking strands nothing (AC4).
3487
3568
  case "deadlock-free":
3488
3569
  return firstWhere((i) => scg.successorsOf(i).length === 0 && !allTokensInSinks(scg.markingOf(i), sinkPlaces));
3570
+ // TerminatesAtSink (VER-002): a quiescent class that marks NO declared sink.
3571
+ // Inverts with DeadlockFree on the empty marking, by design.
3572
+ case "terminates-at-sink":
3573
+ return firstWhere((i) => scg.successorsOf(i).length === 0 && !anySinkMarked(scg.markingOf(i), sinkPlaces));
3574
+ // JoinedOrDeadLettered (NU-040 AC4): a quiescent class still holding a pending
3575
+ // token. No sink clause.
3489
3576
  case "joined-or-dead-lettered":
3490
3577
  return firstWhere((i) => scg.successorsOf(i).length === 0 && scg.markingOf(i).hasTokens(property.pending));
3491
3578
  }
@@ -3498,6 +3585,14 @@ function allTokensInSinks(m, sinks) {
3498
3585
  }
3499
3586
  return true;
3500
3587
  }
3588
+ function anySinkMarked(m, sinks) {
3589
+ const sinkNames = /* @__PURE__ */ new Set();
3590
+ for (const s of sinks) sinkNames.add(s.name);
3591
+ for (const p of m.placesWithTokens()) {
3592
+ if (sinkNames.has(p.name)) return true;
3593
+ }
3594
+ return false;
3595
+ }
3501
3596
  function counterexamplePath(scg, target) {
3502
3597
  const n = scg.classCount();
3503
3598
  const parent = new Array(n).fill(-1);
@@ -3528,6 +3623,7 @@ function counterexamplePath(scg, target) {
3528
3623
  }
3529
3624
 
3530
3625
  // src/verification/smt-verifier.ts
3626
+ var IGNORE_MODE_VACUITY_REASON = "environment places present but not modeled (mode=ignore); a proof would be vacuous \u2014 use alwaysAvailable() or bounded(k) to model external injection";
3531
3627
  var SmtVerifier = class _SmtVerifier {
3532
3628
  constructor(net) {
3533
3629
  this.net = net;
@@ -3636,13 +3732,26 @@ var SmtVerifier = class _SmtVerifier {
3636
3732
  * into a chain whose other combinations avoid it (dropped by the H1 guard). On a
3637
3733
  * net with a few reset arcs that can lose every law of the chains those arcs
3638
3734
  * touch, and without them IC3 has to rediscover the conservation of each chain —
3639
- * on a ~100-place net it does not within any practical budget. With the semiflows
3640
- * in, the same reachability-safety queries close in about a second.
3735
+ * on a ~100-place net it does not within any practical budget.
3736
+ *
3737
+ * **Turn this on if the net has any `all()` / `atLeast(n)` or reset arc on a busy
3738
+ * place** — draining an input queue is the everyday case. Every basis row whose
3739
+ * support touches such a place fails the H1 guard and is dropped, so the encoders
3740
+ * run on a deficient invariant set and nothing in the report says a law is missing
3741
+ * beyond the `Dropped` lines.
3742
+ *
3743
+ * This reaches the **name-coloured** encoder (NU-050) as well as the flat one, and
3744
+ * it matters most there. On a 113-place ν-net, whole-net deadlock-freedom went from
3745
+ * `unknown` after 50 minutes to `proven` in about 15 seconds with this option as the
3746
+ * only change; on the flat path, reachability-safety queries that timed out at 120 s
3747
+ * close in about a second.
3641
3748
  *
3642
3749
  * Soundness is unchanged: the semiflows pass the same exact re-validation as the
3643
3750
  * basis rows, the union is pure strengthening (`Semiflow.lean`,
3644
3751
  * `semiflow_union_sound`), and the certificate check re-proves the strengthened
3645
- * invariant. Off by default so reports stay byte-equal.
3752
+ * invariant that check is flat-path only, so a coloured `proven` reports
3753
+ * `Certificate check: not applicable (name-coloured encoding)`. Off by default so
3754
+ * reports stay byte-equal.
3646
3755
  */
3647
3756
  semiflowInvariants(enabled) {
3648
3757
  this._semiflowInvariants = enabled;
@@ -3702,6 +3811,48 @@ var SmtVerifier = class _SmtVerifier {
3702
3811
  this._prioritySemantics = semantics;
3703
3812
  return this;
3704
3813
  }
3814
+ /**
3815
+ * The name-coloured plan and its encoding, or a null plan when the net is outside
3816
+ * the fragment (NU-050) and a null encoding when the property names a place the net
3817
+ * does not resolve.
3818
+ *
3819
+ * {@link verify} and {@link encodeScripts} share this deliberately. They used to
3820
+ * invoke `buildColouredPlan` and `encodeColoured` separately, so handing the encoder
3821
+ * the wrong one of the two lists changed only one of them — and the script-parity
3822
+ * goldens are generated from `encodeScripts`. Unifying the invocation closes that. It
3823
+ * does not make the two paths identical: each still computes its own invariant and
3824
+ * semiflow lists, so they can still drift through the arguments rather than the call.
3825
+ *
3826
+ * `invariants` is what the encoder conjoins into every rule body (the null-space
3827
+ * basis, unioned with the semiflows when VER-007 is enabled); `semiflows` sets the
3828
+ * colour-slot bound k (NU-053). They are not the same list.
3829
+ */
3830
+ colouredAttempt(flatNet, invariants, semiflows) {
3831
+ const hasMatch = [...this.net.transitions].some((t) => t.matchSpec !== null);
3832
+ const nuBounded = this._budgetPlaces.size > 0;
3833
+ if (!hasMatch || !nuBounded) return { plan: null, encoding: null };
3834
+ const plan = buildColouredPlan(
3835
+ this.net,
3836
+ flatNet,
3837
+ this._initialMarking,
3838
+ this._budgetPlaces,
3839
+ this._fragmentMode,
3840
+ this._carrierPlaces,
3841
+ semiflows
3842
+ );
3843
+ if (plan == null) return { plan: null, encoding: null };
3844
+ return {
3845
+ plan,
3846
+ encoding: encodeColoured(
3847
+ plan,
3848
+ flatNet,
3849
+ this._initialMarking,
3850
+ this._property,
3851
+ invariants,
3852
+ this._sinkPlaces
3853
+ )
3854
+ };
3855
+ }
3705
3856
  /**
3706
3857
  * The SMT-LIB2 scripts {@link verify} would send to z3 for this configuration,
3707
3858
  * without running a solver (VER-013 AC1): the HORN query (flat, or name-coloured
@@ -3713,8 +3864,6 @@ var SmtVerifier = class _SmtVerifier {
3713
3864
  */
3714
3865
  encodeScripts() {
3715
3866
  requireOutputProducingActions(this.net);
3716
- const hasMatch = [...this.net.transitions].some((t) => t.matchSpec !== null);
3717
- const nuBounded = this._budgetPlaces.size > 0;
3718
3867
  const flatNet = flatten(this.net, this._environmentPlaces, this._environmentMode);
3719
3868
  const matrix = IncidenceMatrix.from(flatNet);
3720
3869
  const { valid: basis } = validateInvariantsExact(
@@ -3732,20 +3881,9 @@ var SmtVerifier = class _SmtVerifier {
3732
3881
  let invariants = basis;
3733
3882
  if (this._semiflowInvariants) invariants = strengthenWithSemiflows(basis, semiflows).invariants;
3734
3883
  invariants = canonicalInvariantOrder(invariants);
3735
- if (hasMatch && nuBounded) {
3736
- const plan = buildColouredPlan(
3737
- this.net,
3738
- flatNet,
3739
- this._initialMarking,
3740
- this._budgetPlaces,
3741
- this._fragmentMode,
3742
- this._carrierPlaces,
3743
- semiflows
3744
- );
3745
- if (plan != null) {
3746
- const coloured = encodeColoured(plan, flatNet, this._initialMarking, this._property, invariants, this._sinkPlaces);
3747
- if (coloured != null) return { horn: coloured.smt2, certificate: null, coloured: true };
3748
- }
3884
+ const attempt = this.colouredAttempt(flatNet, invariants, semiflows);
3885
+ if (attempt.encoding != null) {
3886
+ return { horn: attempt.encoding.smt2, certificate: null, coloured: true };
3749
3887
  }
3750
3888
  const horn = encode(flatNet, this._initialMarking, this._property, invariants, this._sinkPlaces, this._counterexampleReplay).smt2;
3751
3889
  const certificate = vcScript(
@@ -3796,8 +3934,13 @@ var SmtVerifier = class _SmtVerifier {
3796
3934
  if (outcome.transitions.length > 0) {
3797
3935
  report.push(` Counterexample trace: ${outcome.trace.length} states, ${outcome.transitions.length} transitions`);
3798
3936
  }
3937
+ let routeBVerdict = outcome.verdict;
3938
+ if (routeBVerdict.type === "proven" && this._environmentPlaces.size > 0 && this._environmentMode.type === "ignore") {
3939
+ report.push(` Downgraded to UNKNOWN: ${IGNORE_MODE_VACUITY_REASON}`);
3940
+ routeBVerdict = { type: "unknown", reason: IGNORE_MODE_VACUITY_REASON };
3941
+ }
3799
3942
  return buildResult(
3800
- outcome.verdict,
3943
+ routeBVerdict,
3801
3944
  report.join("\n"),
3802
3945
  [],
3803
3946
  [],
@@ -3903,15 +4046,6 @@ var SmtVerifier = class _SmtVerifier {
3903
4046
  }
3904
4047
  report.push("");
3905
4048
  report.push("Phase 4: IC3/PDR verification via Z3 Spacer...");
3906
- const colouredPlan = hasMatch && nuBounded ? buildColouredPlan(
3907
- this.net,
3908
- flatNet,
3909
- this._initialMarking,
3910
- this._budgetPlaces,
3911
- this._fragmentMode,
3912
- this._carrierPlaces,
3913
- semiflows
3914
- ) : null;
3915
4049
  const stats = {
3916
4050
  places: flatNet.places.length,
3917
4051
  transitions: flatNet.transitions.length,
@@ -3932,12 +4066,14 @@ var SmtVerifier = class _SmtVerifier {
3932
4066
  return buildResult({ type: "unknown", reason }, report.join("\n"), invariants, [], [], [], performance.now() - start, stats);
3933
4067
  }
3934
4068
  report.push(` Solver: z3 ${formatZ3Version(solver.version)}`);
4069
+ const colouredAttempt = this.colouredAttempt(flatNet, invariants, semiflows);
4070
+ const colouredPlan = colouredAttempt.plan;
3935
4071
  let encoding;
3936
4072
  if (colouredPlan != null) {
3937
4073
  report.push(
3938
4074
  ` \u03BD-encoding: name-coloured (exact within budget k=${colouredPlan.k}; ${colouredPlan.coloured.length} coloured place(s))`
3939
4075
  );
3940
- const coloured = encodeColoured(colouredPlan, flatNet, this._initialMarking, this._property, invariants, this._sinkPlaces);
4076
+ const coloured = colouredAttempt.encoding;
3941
4077
  if (coloured == null) {
3942
4078
  const reason = "property names a place that does not resolve in the net; refusing to certify (the encoding would be vacuously proven)";
3943
4079
  report.push(" Status: UNKNOWN (unresolved property place)\n");
@@ -3966,7 +4102,7 @@ var SmtVerifier = class _SmtVerifier {
3966
4102
  switch (queryResult.type) {
3967
4103
  case "proven": {
3968
4104
  if (this._environmentPlaces.size > 0 && this._environmentMode.type === "ignore") {
3969
- const reason = "environment places present but not modeled (mode=ignore); a proof would be vacuous \u2014 use alwaysAvailable() or bounded(k) to model external injection";
4105
+ const reason = IGNORE_MODE_VACUITY_REASON;
3970
4106
  report.push(` Status: UNSAT, but vacuous under ignore mode
3971
4107
  `);
3972
4108
  report.push("=== RESULT ===\n");
@@ -4165,6 +4301,7 @@ function isReachabilitySafety(property) {
4165
4301
  case "unreachable":
4166
4302
  return true;
4167
4303
  case "deadlock-free":
4304
+ case "terminates-at-sink":
4168
4305
  case "joined-or-dead-lettered":
4169
4306
  return false;
4170
4307
  }
@@ -4241,6 +4378,8 @@ function unresolvedPropertyPlace(flatNet, property) {
4241
4378
  switch (property.type) {
4242
4379
  case "deadlock-free":
4243
4380
  return [];
4381
+ case "terminates-at-sink":
4382
+ return [];
4244
4383
  case "mutual-exclusion":
4245
4384
  return [property.p1, property.p2];
4246
4385
  case "place-bound":
@@ -4309,6 +4448,7 @@ export {
4309
4448
  MarkingState,
4310
4449
  MarkingStateBuilder,
4311
4450
  deadlockFree,
4451
+ terminatesAtSink,
4312
4452
  mutualExclusion,
4313
4453
  placeBound,
4314
4454
  unreachable,
@@ -4362,4 +4502,4 @@ export {
4362
4502
  isProven,
4363
4503
  isViolated
4364
4504
  };
4365
- //# sourceMappingURL=chunk-WYCGGQAW.js.map
4505
+ //# sourceMappingURL=chunk-75KEJQGC.js.map