libpetri 2.5.0 → 2.7.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.
@@ -1,4 +1,4 @@
1
- import { P as PetriNet } from '../petri-net-DXdpd0r5.js';
1
+ import { P as PetriNet } from '../petri-net-D73-PO6d.js';
2
2
 
3
3
  /**
4
4
  * Format-agnostic typed graph model.
@@ -8,8 +8,8 @@ import {
8
8
  nodeStyle,
9
9
  renderDot,
10
10
  sanitize
11
- } from "../chunk-2GAZLDH3.js";
12
- import "../chunk-FN773SSE.js";
11
+ } from "../chunk-ULX3OG6H.js";
12
+ import "../chunk-ATT7U5H5.js";
13
13
  export {
14
14
  DEFAULT_DOT_CONFIG,
15
15
  FONT,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { P as PetriNet, S as SubnetDef, a as Place, T as Token, b as Transition, E as EnvironmentPlace, c as TransitionContext, O as Out } from './petri-net-DXdpd0r5.js';
2
- export { A as Arc, d as ArcInhibitor, e as ArcInput, f as ArcOutput, g as ArcRead, h as ArcReset, C as Channel, i as ComposeBindings, F as FusionSet, j as FusionSetBuilder, I as In, k as InAll, l as InAtLeast, m as InExactly, n as InOne, o as Instance, p as Interface, q as InterfaceBuilder, L as LogFn, M as MAX_DURATION_MS, r as OutAnd, s as OutForwardInput, t as OutPlace, u as OutTimeout, v as OutXor, w as OutputEntry, x as PetriNetBuilder, y as Port, z as PortDirection, B as SubnetDefBuilder, D as SubnetInstance, G as Timing, H as TimingDeadline, J as TimingDelayed, K as TimingExact, N as TimingImmediate, Q as TimingWindow, R as TokenInput, U as TokenOutput, V as TransitionAction, W as TransitionBuilder, X as VerificationHarness, Y as VerificationResult, Z as all, _ as allPlaces, $ as and, a0 as andPlaces, a1 as arcPlace, a2 as atLeast, a3 as consumptionCount, a4 as deadline, a5 as delayed, a6 as earliest, a7 as enumerateBranches, a8 as environmentPlace, a9 as exact, aa as exactly, ab as fork, ac as forwardInput, ad as hasDeadline, ae as hasGuard, af as immediate, ag as inhibitorArc, ah as inputArc, ai as isUnit, aj as latest, ak as matchesGuard, al as one, am as outPlace, an as outputArc, ao as passthrough, ap as place, aq as produce, ar as readArc, as as requiredCount, at as resetArc, au as timeout, av as timeoutPlace, aw as tokenAt, ax as tokenOf, ay as transform, az as transformAsync, aA as transformFrom, aB as unitToken, aC as window, aD as withTimeout, aE as xor, aF as xorPlaces } from './petri-net-DXdpd0r5.js';
3
- import { E as EventStore } from './event-store-z30VMn0T.js';
4
- export { A as ActionTimedOut, a as ExecutionCompleted, b as ExecutionStarted, I as InMemoryEventStore, L as LogMessage, M as MarkingSnapshot, N as NetEvent, T as TokenAdded, c as TokenRemoved, d as TransitionClockRestarted, e as TransitionCompleted, f as TransitionEnabled, g as TransitionFailed, h as TransitionStarted, i as TransitionTimedOut, j as eventTransitionName, k as eventsOfType, l as failures, m as filterEvents, n as inMemoryEventStore, o as isFailureEvent, p as noopEventStore, t as transitionEvents } from './event-store-z30VMn0T.js';
1
+ import { P as PetriNet, S as SubnetDef, a as Place, T as Token, b as Transition, E as EnvironmentPlace, c as TransitionContext, O as Out } from './petri-net-D73-PO6d.js';
2
+ export { A as Arc, d as ArcInhibitor, e as ArcInput, f as ArcOutput, g as ArcRead, h as ArcReset, C as Channel, i as ComposeBindings, F as FusionSet, j as FusionSetBuilder, I as In, k as InAll, l as InAtLeast, m as InExactly, n as InOne, o as Instance, p as Interface, q as InterfaceBuilder, L as LogFn, M as MAX_DURATION_MS, r as OutAnd, s as OutForwardInput, t as OutPlace, u as OutTimeout, v as OutXor, w as OutputEntry, x as PetriNetBuilder, y as Port, z as PortDirection, B as SubnetDefBuilder, D as SubnetInstance, G as Timing, H as TimingDeadline, J as TimingDelayed, K as TimingExact, N as TimingImmediate, Q as TimingWindow, R as TokenInput, U as TokenOutput, V as TransitionAction, W as TransitionBuilder, X as VerificationHarness, Y as VerificationResult, Z as all, _ as allPlaces, $ as and, a0 as andPlaces, a1 as arcPlace, a2 as atLeast, a3 as consumptionCount, a4 as deadline, a5 as delayed, a6 as earliest, a7 as enumerateBranches, a8 as environmentPlace, a9 as exact, aa as exactly, ab as fork, ac as forwardInput, ad as hasDeadline, ae as hasGuard, af as immediate, ag as inhibitorArc, ah as inputArc, ai as isUnit, aj as latest, ak as matchesGuard, al as one, am as outPlace, an as outputArc, ao as passthrough, ap as place, aq as produce, ar as readArc, as as requiredCount, at as resetArc, au as timeout, av as timeoutPlace, aw as tokenAt, ax as tokenOf, ay as transform, az as transformAsync, aA as transformFrom, aB as unitToken, aC as window, aD as withTimeout, aE as xor, aF as xorPlaces } from './petri-net-D73-PO6d.js';
3
+ import { E as EventStore } from './event-store-8XkpYUeU.js';
4
+ export { A as ActionTimedOut, a as ExecutionCompleted, b as ExecutionStarted, I as InMemoryEventStore, L as LogMessage, M as MarkingSnapshot, N as NetEvent, T as TokenAdded, c as TokenRemoved, d as TransitionClockRestarted, e as TransitionCompleted, f as TransitionEnabled, g as TransitionFailed, h as TransitionStarted, i as TransitionTimedOut, j as eventTransitionName, k as eventsOfType, l as failures, m as filterEvents, n as inMemoryEventStore, o as isFailureEvent, p as noopEventStore, t as transitionEvents } from './event-store-8XkpYUeU.js';
5
5
 
6
6
  /**
7
7
  * Discriminated sum-type abstraction over Petri nets, distinguishing **closed**
@@ -221,6 +221,13 @@ interface BitmapNetExecutorOptions {
221
221
  environmentPlaces?: Set<EnvironmentPlace<any>>;
222
222
  /** Provides execution context data for each transition firing. */
223
223
  executionContextProvider?: (transitionName: string, consumed: Token<any>[]) => Map<string, unknown>;
224
+ /**
225
+ * Grace band (ms) beyond a hard deadline (`deadline()` / `window()`) before a transition is
226
+ * force-disabled with a `transition-timed-out` event (TIME-013). Defaults to {@link DEADLINE_TOLERANCE_MS}
227
+ * (5ms); `0` gives strict enforcement. Must be non-negative. Does not affect `exact()` transitions,
228
+ * which are enforced softly (TIME-006).
229
+ */
230
+ deadlineToleranceMs?: number;
224
231
  }
225
232
  /**
226
233
  * Async bitmap-based executor for Coloured Time Petri Nets.
@@ -262,6 +269,10 @@ declare class BitmapNetExecutor implements PetriNetExecutor {
262
269
  private readonly enabledFlags;
263
270
  /** Precomputed: 1 if transition has a finite deadline, 0 otherwise. */
264
271
  private readonly hasDeadlineFlags;
272
+ /** Precomputed: 1 for exact() transitions — enforced softly, never force-disabled (TIME-006). */
273
+ private readonly isExactFlags;
274
+ /** Grace band (ms) before a hard deadline force-disables (TIME-013). */
275
+ private readonly deadlineToleranceMs;
265
276
  private enabledTransitionCount;
266
277
  private readonly inFlight;
267
278
  private readonly inFlightPromises;
@@ -394,6 +405,12 @@ declare class PrecompiledNet {
394
405
  readonly earliestMs: Float64Array;
395
406
  readonly latestMs: Float64Array;
396
407
  readonly hasDeadline: Uint8Array;
408
+ /**
409
+ * 1 for `exact()` transitions. Exact transitions fire at the first opportunity at/after their
410
+ * target time (delayed-style liveness) and are never force-disabled by deadline enforcement —
411
+ * their upper bound is observed, not enforced. See TIME-006.
412
+ */
413
+ readonly isExact: Uint8Array;
397
414
  readonly priorities: Int32Array;
398
415
  readonly transitionToPriorityIndex: Uint32Array;
399
416
  readonly priorityLevels: readonly number[];
@@ -456,6 +473,13 @@ interface PrecompiledNetExecutorOptions {
456
473
  skipOutputValidation?: boolean;
457
474
  /** Reuse a precompiled program (avoids recompilation). */
458
475
  program?: PrecompiledNet;
476
+ /**
477
+ * Grace band (ms) beyond a hard deadline (`deadline()` / `window()`) before a transition is
478
+ * force-disabled with a `transition-timed-out` event (TIME-013). Defaults to {@link DEADLINE_TOLERANCE_MS}
479
+ * (5ms); `0` gives strict enforcement. Must be non-negative. Does not affect `exact()` transitions,
480
+ * which are enforced softly (TIME-006).
481
+ */
482
+ deadlineToleranceMs?: number;
459
483
  }
460
484
  /**
461
485
  * High-performance executor using `PrecompiledNet`.
@@ -470,6 +494,7 @@ declare class PrecompiledNetExecutor implements PetriNetExecutor {
470
494
  private readonly hasEnvironmentPlaces;
471
495
  private readonly executionContextProvider?;
472
496
  private readonly skipOutputValidation;
497
+ private readonly deadlineToleranceMs;
473
498
  private readonly startMs;
474
499
  private readonly eventStoreEnabled;
475
500
  /** Per-place token arrays, indexed by pid. */
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  timeoutPlace,
13
13
  xor,
14
14
  xorPlaces
15
- } from "./chunk-H62Z76FY.js";
15
+ } from "./chunk-ETNHEAS6.js";
16
16
  import {
17
17
  eventTransitionName,
18
18
  isFailureEvent
@@ -27,7 +27,7 @@ import {
27
27
  immediate,
28
28
  latest,
29
29
  window
30
- } from "./chunk-FN773SSE.js";
30
+ } from "./chunk-ATT7U5H5.js";
31
31
 
32
32
  // src/core/token.ts
33
33
  var UNIT_TOKEN = Object.freeze({
@@ -203,6 +203,7 @@ function withTimeout(action, timeoutMs, timeoutPlace2, timeoutValue) {
203
203
  }
204
204
 
205
205
  // src/core/transition-context.ts
206
+ var EMPTY_ALIAS = /* @__PURE__ */ new Map();
206
207
  var TransitionContext = class {
207
208
  rawInput;
208
209
  _rawOutput;
@@ -215,7 +216,8 @@ var TransitionContext = class {
215
216
  _transitionName;
216
217
  executionCtx;
217
218
  _logFn;
218
- constructor(transitionName, rawInput, rawOutput, inputPlaces, readPlaces, outputPlaces, executionContext, logFn) {
219
+ placeAlias;
220
+ constructor(transitionName, rawInput, rawOutput, inputPlaces, readPlaces, outputPlaces, executionContext, logFn, placeAlias) {
219
221
  this._transitionName = transitionName;
220
222
  this.rawInput = rawInput;
221
223
  this._rawOutput = rawOutput;
@@ -233,28 +235,45 @@ var TransitionContext = class {
233
235
  this.allowedOutputs = ao;
234
236
  this.executionCtx = executionContext ?? /* @__PURE__ */ new Map();
235
237
  this._logFn = logFn;
238
+ this.placeAlias = placeAlias ?? EMPTY_ALIAS;
239
+ }
240
+ /**
241
+ * Resolves a place key through the transition's declared→actual place
242
+ * correspondence (per **MOD-031**). For a hand-written or directly-composed
243
+ * transition the correspondence is the identity, so this returns `place`
244
+ * unchanged; after instancing / port binding it maps a *declared* place
245
+ * constant the action hardcodes to the *actual* composed place. The result
246
+ * feeds both the declared-set check and the token-store access (both keyed by
247
+ * `place.name`), so `inputPlaces()`/`outputPlaces()` discovery is unaffected.
248
+ */
249
+ resolve(place2) {
250
+ const actual = this.placeAlias.get(place2.name);
251
+ return actual !== void 0 ? actual : place2;
236
252
  }
237
253
  // ==================== Input Access (consumed) ====================
238
254
  /** Get single consumed input value. Throws if place not declared or multiple tokens. */
239
255
  input(place2) {
240
- this.requireInput(place2);
241
- const values = this.rawInput.values(place2);
256
+ const actual = this.resolve(place2);
257
+ this.requireInput(actual);
258
+ const values = this.rawInput.values(actual);
242
259
  if (values.length !== 1) {
243
260
  throw new Error(
244
- `Place '${place2.name}' consumed ${values.length} tokens, use inputs() for batched access`
261
+ `Place '${actual.name}' consumed ${values.length} tokens, use inputs() for batched access`
245
262
  );
246
263
  }
247
264
  return values[0];
248
265
  }
249
266
  /** Get all consumed input values for a place. */
250
267
  inputs(place2) {
251
- this.requireInput(place2);
252
- return this.rawInput.values(place2);
268
+ const actual = this.resolve(place2);
269
+ this.requireInput(actual);
270
+ return this.rawInput.values(actual);
253
271
  }
254
272
  /** Get consumed input token with metadata. */
255
273
  inputToken(place2) {
256
- this.requireInput(place2);
257
- return this.rawInput.get(place2);
274
+ const actual = this.resolve(place2);
275
+ this.requireInput(actual);
276
+ return this.rawInput.get(actual);
258
277
  }
259
278
  /** Returns declared input places (consumed). */
260
279
  inputPlaces() {
@@ -270,13 +289,15 @@ var TransitionContext = class {
270
289
  // ==================== Read Access (not consumed) ====================
271
290
  /** Get read-only context value. Throws if place not declared as read. */
272
291
  read(place2) {
273
- this.requireRead(place2);
274
- return this.rawInput.value(place2);
292
+ const actual = this.resolve(place2);
293
+ this.requireRead(actual);
294
+ return this.rawInput.value(actual);
275
295
  }
276
296
  /** Get all read-only context values for a place. */
277
297
  reads(place2) {
278
- this.requireRead(place2);
279
- return this.rawInput.values(place2);
298
+ const actual = this.resolve(place2);
299
+ this.requireRead(actual);
300
+ return this.rawInput.values(actual);
280
301
  }
281
302
  /** Returns declared read places (context, not consumed). */
282
303
  readPlaces() {
@@ -303,9 +324,10 @@ var TransitionContext = class {
303
324
  * @throws if place not declared as output.
304
325
  */
305
326
  output(place2, ...values) {
306
- this.requireOutput(place2);
327
+ const actual = this.resolve(place2);
328
+ this.requireOutput(actual);
307
329
  for (const value of values) {
308
- this._rawOutput.add(place2, value);
330
+ this._rawOutput.add(actual, value);
309
331
  }
310
332
  return this;
311
333
  }
@@ -318,9 +340,10 @@ var TransitionContext = class {
318
340
  * @throws if place not declared as output.
319
341
  */
320
342
  outputToken(place2, ...tokens) {
321
- this.requireOutput(place2);
343
+ const actual = this.resolve(place2);
344
+ this.requireOutput(actual);
322
345
  for (const token of tokens) {
323
- this._rawOutput.addToken(place2, token);
346
+ this._rawOutput.addToken(actual, token);
324
347
  }
325
348
  return this;
326
349
  }
@@ -437,6 +460,7 @@ var TokenOutput = class {
437
460
 
438
461
  // src/core/transition.ts
439
462
  var TRANSITION_KEY = /* @__PURE__ */ Symbol("Transition.internal");
463
+ var EMPTY_PLACE_ALIAS = /* @__PURE__ */ new Map();
440
464
  var Transition = class {
441
465
  name;
442
466
  inputSpecs;
@@ -448,11 +472,23 @@ var Transition = class {
448
472
  actionTimeout;
449
473
  action;
450
474
  priority;
475
+ /**
476
+ * Per-transition **declared → actual** place correspondence (per
477
+ * **MOD-031**), keyed by the author-original declared place **name** →
478
+ * actual composed place. Empty for a hand-written or directly-composed
479
+ * ([MOD-025]) transition (identity). Populated by the subnet rewriter after
480
+ * instantiation ([MOD-010]) / port binding ([MOD-020]) so an action that
481
+ * hardcodes a declared place constant resolves to the composed place via
482
+ * {@link import('./transition-context.js').TransitionContext}. Consumed only
483
+ * by the action-facing context I/O — never by enablement, firing, the
484
+ * verifier, the exporter, or events (so [MOD-023] is unaffected).
485
+ */
486
+ placeAlias;
451
487
  _inputPlaces;
452
488
  _readPlaces;
453
489
  _outputPlaces;
454
490
  /** @internal Use {@link Transition.builder} to create instances. */
455
- constructor(key, name, inputSpecs, outputSpec, inhibitors, reads, resets, timing, action, priority) {
491
+ constructor(key, name, inputSpecs, outputSpec, inhibitors, reads, resets, timing, action, priority, placeAlias = EMPTY_PLACE_ALIAS) {
456
492
  if (key !== TRANSITION_KEY) throw new Error("Use Transition.builder() to create instances");
457
493
  this.name = name;
458
494
  this.inputSpecs = inputSpecs;
@@ -464,6 +500,7 @@ var Transition = class {
464
500
  this.actionTimeout = findTimeout(outputSpec);
465
501
  this.action = action;
466
502
  this.priority = priority;
503
+ this.placeAlias = placeAlias.size === 0 ? EMPTY_PLACE_ALIAS : placeAlias;
467
504
  const inputPlaces = /* @__PURE__ */ new Set();
468
505
  for (const spec of inputSpecs) {
469
506
  inputPlaces.add(spec.place);
@@ -515,6 +552,7 @@ var TransitionBuilder = class {
515
552
  _timing = immediate();
516
553
  _action = passthrough();
517
554
  _priority = 0;
555
+ _placeAlias = EMPTY_PLACE_ALIAS;
518
556
  constructor(name) {
519
557
  this._name = name;
520
558
  }
@@ -579,6 +617,16 @@ var TransitionBuilder = class {
579
617
  this._priority = priority;
580
618
  return this;
581
619
  }
620
+ /**
621
+ * Sets the per-transition declared→actual place correspondence (per
622
+ * **MOD-031**). Populated by the subnet rewriter during the compose-time
623
+ * rewrite; not normally called by hand-written nets, whose correspondence is
624
+ * the identity (empty map).
625
+ */
626
+ placeAlias(alias) {
627
+ this._placeAlias = alias;
628
+ return this;
629
+ }
582
630
  build() {
583
631
  if (this._outputSpec !== null) {
584
632
  const inputPlaceNames = new Set(this._inputSpecs.map((s) => s.place.name));
@@ -600,7 +648,8 @@ var TransitionBuilder = class {
600
648
  [...this._resets],
601
649
  this._timing,
602
650
  this._action,
603
- this._priority
651
+ this._priority,
652
+ this._placeAlias
604
653
  );
605
654
  }
606
655
  };
@@ -895,6 +944,10 @@ function substitutePlaces(t, remap) {
895
944
  }
896
945
  function rebuildWithName(t, name, remap) {
897
946
  const builder = Transition.builder(name).timing(t.timing).priority(t.priority).action(t.action);
947
+ const alias = buildPlaceAlias(t, remap);
948
+ if (alias.size > 0) {
949
+ builder.placeAlias(alias);
950
+ }
898
951
  if (t.inputSpecs.length > 0) {
899
952
  const rewrittenInputs = new Array(t.inputSpecs.length);
900
953
  for (let i = 0; i < t.inputSpecs.length; i++) {
@@ -916,6 +969,39 @@ function rebuildWithName(t, name, remap) {
916
969
  }
917
970
  return builder.build();
918
971
  }
972
+ function buildPlaceAlias(t, remap) {
973
+ const prev = t.placeAlias;
974
+ if (remap.size === 0 && prev.size === 0) {
975
+ return EMPTY_ALIAS2;
976
+ }
977
+ const alias = /* @__PURE__ */ new Map();
978
+ if (prev.size > 0) {
979
+ for (const [declaredName, prevActual] of prev) {
980
+ const replaced = remap.get(prevActual.name);
981
+ const finalActual = replaced !== void 0 ? replaced : prevActual;
982
+ if (finalActual.name !== declaredName) {
983
+ alias.set(declaredName, finalActual);
984
+ }
985
+ }
986
+ return alias;
987
+ }
988
+ const record = (p) => {
989
+ if (alias.has(p.name)) return;
990
+ const replaced = remap.get(p.name);
991
+ if (replaced !== void 0 && replaced.name !== p.name) {
992
+ alias.set(p.name, replaced);
993
+ }
994
+ };
995
+ for (const spec of t.inputSpecs) record(spec.place);
996
+ for (const rd of t.reads) record(rd.place);
997
+ for (const inh of t.inhibitors) record(inh.place);
998
+ for (const rs of t.resets) record(rs.place);
999
+ if (t.outputSpec !== null) {
1000
+ for (const p of allPlaces(t.outputSpec)) record(p);
1001
+ }
1002
+ return alias;
1003
+ }
1004
+ var EMPTY_ALIAS2 = /* @__PURE__ */ new Map();
919
1005
  function rewriteIn(spec, remap) {
920
1006
  switch (spec.type) {
921
1007
  case "one":
@@ -2529,6 +2615,7 @@ var OutViolationError = class extends Error {
2529
2615
  };
2530
2616
 
2531
2617
  // src/runtime/executor-support.ts
2618
+ var DEADLINE_TOLERANCE_MS = 5;
2532
2619
  function validateOutSpec(tName, spec, producedPlaceNames) {
2533
2620
  switch (spec.type) {
2534
2621
  case "place":
@@ -2589,7 +2676,6 @@ function produceTimeoutOutput(context, timeoutChild) {
2589
2676
  }
2590
2677
 
2591
2678
  // src/runtime/bitmap-net-executor.ts
2592
- var DEADLINE_TOLERANCE_MS = 5;
2593
2679
  var BitmapNetExecutor = class {
2594
2680
  compiled;
2595
2681
  marking;
@@ -2614,6 +2700,10 @@ var BitmapNetExecutor = class {
2614
2700
  enabledFlags;
2615
2701
  /** Precomputed: 1 if transition has a finite deadline, 0 otherwise. */
2616
2702
  hasDeadlineFlags;
2703
+ /** Precomputed: 1 for exact() transitions — enforced softly, never force-disabled (TIME-006). */
2704
+ isExactFlags;
2705
+ /** Grace band (ms) before a hard deadline force-disables (TIME-013). */
2706
+ deadlineToleranceMs;
2617
2707
  enabledTransitionCount = 0;
2618
2708
  // In-flight tracking
2619
2709
  inFlight = /* @__PURE__ */ new Map();
@@ -2641,6 +2731,10 @@ var BitmapNetExecutor = class {
2641
2731
  );
2642
2732
  this.hasEnvironmentPlaces = this.environmentPlaces.size > 0;
2643
2733
  this.executionContextProvider = options.executionContextProvider;
2734
+ this.deadlineToleranceMs = options.deadlineToleranceMs ?? DEADLINE_TOLERANCE_MS;
2735
+ if (this.deadlineToleranceMs < 0) {
2736
+ throw new Error(`Deadline tolerance must be non-negative: ${this.deadlineToleranceMs}`);
2737
+ }
2644
2738
  this.startMs = performance.now();
2645
2739
  const wordCount = this.compiled.wordCount;
2646
2740
  this.markedPlaces = new Uint32Array(wordCount);
@@ -2654,6 +2748,7 @@ var BitmapNetExecutor = class {
2654
2748
  this.inFlightFlags = new Uint8Array(this.compiled.transitionCount);
2655
2749
  this.enabledFlags = new Uint8Array(this.compiled.transitionCount);
2656
2750
  this.hasDeadlineFlags = new Uint8Array(this.compiled.transitionCount);
2751
+ this.isExactFlags = new Uint8Array(this.compiled.transitionCount);
2657
2752
  let anyDeadlines = false;
2658
2753
  let allImm = true;
2659
2754
  let samePrio = true;
@@ -2664,6 +2759,7 @@ var BitmapNetExecutor = class {
2664
2759
  this.hasDeadlineFlags[tid] = 1;
2665
2760
  anyDeadlines = true;
2666
2761
  }
2762
+ if (t.timing.type === "exact") this.isExactFlags[tid] = 1;
2667
2763
  if (t.timing.type !== "immediate") allImm = false;
2668
2764
  if (t.priority !== firstPriority) samePrio = false;
2669
2765
  }
@@ -2843,11 +2939,12 @@ var BitmapNetExecutor = class {
2843
2939
  enforceDeadlines(nowMs) {
2844
2940
  for (let tid = 0; tid < this.compiled.transitionCount; tid++) {
2845
2941
  if (!this.hasDeadlineFlags[tid]) continue;
2942
+ if (this.isExactFlags[tid]) continue;
2846
2943
  if (!this.enabledFlags[tid] || this.inFlightFlags[tid]) continue;
2847
2944
  const t = this.compiled.transition(tid);
2848
2945
  const elapsed = nowMs - this.enabledAtMs[tid];
2849
2946
  const latestMs = latest(t.timing);
2850
- if (elapsed > latestMs + DEADLINE_TOLERANCE_MS) {
2947
+ if (elapsed > latestMs + this.deadlineToleranceMs) {
2851
2948
  this.enabledFlags[tid] = 0;
2852
2949
  this.enabledTransitionCount--;
2853
2950
  this.emitEvent({
@@ -3033,7 +3130,8 @@ var BitmapNetExecutor = class {
3033
3130
  t.readPlaces(),
3034
3131
  t.outputPlaces(),
3035
3132
  execCtx,
3036
- logFn
3133
+ logFn,
3134
+ t.placeAlias
3037
3135
  );
3038
3136
  let actionPromise = t.action(context);
3039
3137
  if (t.hasActionTimeout()) {
@@ -3364,6 +3462,12 @@ var PrecompiledNet = class _PrecompiledNet {
3364
3462
  earliestMs;
3365
3463
  latestMs;
3366
3464
  hasDeadline;
3465
+ /**
3466
+ * 1 for `exact()` transitions. Exact transitions fire at the first opportunity at/after their
3467
+ * target time (delayed-style liveness) and are never force-disabled by deadline enforcement —
3468
+ * their upper bound is observed, not enforced. See TIME-006.
3469
+ */
3470
+ isExact;
3367
3471
  // ==================== Priority (CONC-023) ====================
3368
3472
  priorities;
3369
3473
  transitionToPriorityIndex;
@@ -3484,6 +3588,7 @@ var PrecompiledNet = class _PrecompiledNet {
3484
3588
  this.earliestMs = new Float64Array(tc);
3485
3589
  this.latestMs = new Float64Array(tc);
3486
3590
  this.hasDeadline = new Uint8Array(tc);
3591
+ this.isExact = new Uint8Array(tc);
3487
3592
  let anyDeadlines = false;
3488
3593
  let allImm = true;
3489
3594
  for (let tid = 0; tid < tc; tid++) {
@@ -3494,6 +3599,7 @@ var PrecompiledNet = class _PrecompiledNet {
3494
3599
  this.hasDeadline[tid] = 1;
3495
3600
  anyDeadlines = true;
3496
3601
  }
3602
+ if (t.timing.type === "exact") this.isExact[tid] = 1;
3497
3603
  if (t.timing.type !== "immediate") allImm = false;
3498
3604
  }
3499
3605
  this.anyDeadlines = anyDeadlines;
@@ -3661,7 +3767,6 @@ function simpleOutputPlace(spec, compiled) {
3661
3767
  }
3662
3768
 
3663
3769
  // src/runtime/precompiled-net-executor.ts
3664
- var DEADLINE_TOLERANCE_MS2 = 5;
3665
3770
  var PrecompiledNetExecutor = class {
3666
3771
  program;
3667
3772
  eventStore;
@@ -3669,6 +3774,7 @@ var PrecompiledNetExecutor = class {
3669
3774
  hasEnvironmentPlaces;
3670
3775
  executionContextProvider;
3671
3776
  skipOutputValidation;
3777
+ deadlineToleranceMs;
3672
3778
  startMs;
3673
3779
  eventStoreEnabled;
3674
3780
  // ==================== Token Storage ====================
@@ -3722,6 +3828,10 @@ var PrecompiledNetExecutor = class {
3722
3828
  this.hasEnvironmentPlaces = this.environmentPlaces.size > 0;
3723
3829
  this.executionContextProvider = options.executionContextProvider;
3724
3830
  this.skipOutputValidation = options.skipOutputValidation ?? false;
3831
+ this.deadlineToleranceMs = options.deadlineToleranceMs ?? DEADLINE_TOLERANCE_MS;
3832
+ if (this.deadlineToleranceMs < 0) {
3833
+ throw new Error(`Deadline tolerance must be non-negative: ${this.deadlineToleranceMs}`);
3834
+ }
3725
3835
  this.startMs = performance.now();
3726
3836
  this.eventStoreEnabled = this.eventStore.isEnabled();
3727
3837
  const prog = this.program;
@@ -3936,10 +4046,11 @@ var PrecompiledNetExecutor = class {
3936
4046
  const tc = prog.transitionCount;
3937
4047
  for (let tid = 0; tid < tc; tid++) {
3938
4048
  if (!prog.hasDeadline[tid]) continue;
4049
+ if (prog.isExact[tid]) continue;
3939
4050
  if (!this.enabledFlags[tid] || this.inFlightFlags[tid]) continue;
3940
4051
  const elapsed = nowMs - this.enabledAtMs[tid];
3941
4052
  const latestMs = prog.latestMs[tid];
3942
- if (elapsed > latestMs + DEADLINE_TOLERANCE_MS2) {
4053
+ if (elapsed > latestMs + this.deadlineToleranceMs) {
3943
4054
  this.enabledFlags[tid] = 0;
3944
4055
  this.enabledTransitionCount--;
3945
4056
  this.enabledAtMs[tid] = -Infinity;
@@ -4191,7 +4302,8 @@ var PrecompiledNetExecutor = class {
4191
4302
  t.readPlaces(),
4192
4303
  t.outputPlaces(),
4193
4304
  execCtx,
4194
- logFn
4305
+ logFn,
4306
+ t.placeAlias
4195
4307
  );
4196
4308
  let actionPromise = t.action(context);
4197
4309
  if (t.hasActionTimeout()) {