@fairfox/polly 0.38.0 → 0.38.1

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.
@@ -3631,7 +3631,7 @@ class ConfigValidator {
3631
3631
  this.validateTier2Optimizations(config.tier2);
3632
3632
  }
3633
3633
  if (config.subsystems) {
3634
- this.validateSubsystems(config.subsystems, config.state);
3634
+ this.validateSubsystems(config.subsystems, config.state, config.messages);
3635
3635
  }
3636
3636
  }
3637
3637
  findNullPlaceholders(obj, path2) {
@@ -3949,7 +3949,7 @@ class ConfigValidator {
3949
3949
  }
3950
3950
  }
3951
3951
  }
3952
- validateSubsystems(subsystems, stateConfig) {
3952
+ validateSubsystems(subsystems, stateConfig, messages) {
3953
3953
  const stateFieldNames = Object.keys(stateConfig);
3954
3954
  const allAssignedHandlers = new Map;
3955
3955
  const allAssignedFields = new Map;
@@ -4009,6 +4009,69 @@ class ConfigValidator {
4009
4009
  suggestion: "Add at least one state field to the subsystem"
4010
4010
  });
4011
4011
  }
4012
+ if (subsystem.bounds) {
4013
+ this.validateSubsystemBounds(subsystemName, subsystem, messages);
4014
+ }
4015
+ }
4016
+ }
4017
+ validateSubsystemBounds(subsystemName, subsystem, messages) {
4018
+ const bounds = subsystem.bounds;
4019
+ if (!bounds)
4020
+ return;
4021
+ const effectiveMaxInFlight = bounds.maxInFlight === undefined ? messages.maxInFlight ?? 3 : bounds.maxInFlight;
4022
+ if (bounds.maxInFlight !== undefined && bounds.maxInFlight < 1) {
4023
+ this.issues.push({
4024
+ type: "invalid_value",
4025
+ severity: "error",
4026
+ field: `subsystems.${subsystemName}.bounds.maxInFlight`,
4027
+ message: `bounds.maxInFlight must be at least 1, got ${bounds.maxInFlight}`,
4028
+ suggestion: "Use 1 to keep the global value, or 2+ to exercise multi-step ensures"
4029
+ });
4030
+ }
4031
+ if (bounds.maxInFlight !== undefined && bounds.maxInFlight > 20) {
4032
+ this.issues.push({
4033
+ type: "unrealistic_bound",
4034
+ severity: "warning",
4035
+ field: `subsystems.${subsystemName}.bounds.maxInFlight`,
4036
+ message: `Very high bounds.maxInFlight (${bounds.maxInFlight}) will slow this subsystem's verification significantly`,
4037
+ suggestion: "Use 2-4 for most multi-step ensures cases"
4038
+ });
4039
+ }
4040
+ if (bounds.perMessageBounds) {
4041
+ const handlerSet = new Set(subsystem.handlers);
4042
+ for (const [msg, bound] of Object.entries(bounds.perMessageBounds)) {
4043
+ this.validatePerMessageBound(subsystemName, msg, bound, effectiveMaxInFlight, handlerSet);
4044
+ }
4045
+ }
4046
+ }
4047
+ validatePerMessageBound(subsystemName, msg, bound, effectiveMaxInFlight, handlerSet) {
4048
+ const field = `subsystems.${subsystemName}.bounds.perMessageBounds.${msg}`;
4049
+ if (bound < 1) {
4050
+ this.issues.push({
4051
+ type: "invalid_value",
4052
+ severity: "error",
4053
+ field,
4054
+ message: `perMessageBounds[${msg}] must be at least 1, got ${bound}`,
4055
+ suggestion: "Remove the entry to inherit from the global bound, or set 1+"
4056
+ });
4057
+ }
4058
+ if (bound > effectiveMaxInFlight) {
4059
+ this.issues.push({
4060
+ type: "invalid_value",
4061
+ severity: "error",
4062
+ field,
4063
+ message: `perMessageBounds[${msg}] = ${bound} exceeds effective maxInFlight = ${effectiveMaxInFlight}; the bound is unreachable and ${msg} will not be explored`,
4064
+ suggestion: `Lower perMessageBounds[${msg}] to ≤ ${effectiveMaxInFlight}, or raise bounds.maxInFlight`
4065
+ });
4066
+ }
4067
+ if (!handlerSet.has(msg)) {
4068
+ this.issues.push({
4069
+ type: "invalid_value",
4070
+ severity: "warning",
4071
+ field,
4072
+ message: `perMessageBounds[${msg}] is set on subsystem "${subsystemName}" but ${msg} is not in its handlers list; the override will be silently dropped`,
4073
+ suggestion: `Move ${msg} into subsystems.${subsystemName}.handlers, or remove the override`
4074
+ });
4012
4075
  }
4013
4076
  }
4014
4077
  }
@@ -7453,6 +7516,7 @@ async function runSubsystemVerification(config, analysis) {
7453
7516
  const startTime = Date.now();
7454
7517
  console.log(color(`⚙️ Verifying subsystem: ${name}...`, COLORS.blue));
7455
7518
  const { spec, cfg } = await generateSubsystemTLA2(name, sub, config, analysis);
7519
+ const ensuresCount = (spec.match(/^EnsuresAfter_\w+ ==/gm) ?? []).length;
7456
7520
  const specDir = path4.join(process.cwd(), "specs", "tla", "generated", name);
7457
7521
  if (!fs4.existsSync(specDir)) {
7458
7522
  fs4.mkdirSync(specDir, { recursive: true });
@@ -7472,6 +7536,7 @@ async function runSubsystemVerification(config, analysis) {
7472
7536
  name,
7473
7537
  success: result.success,
7474
7538
  handlerCount: sub.handlers.length,
7539
+ ensuresCount,
7475
7540
  stateCount: result.stats?.distinctStates ?? 0,
7476
7541
  elapsed,
7477
7542
  stats: result.stats,
@@ -7492,6 +7557,16 @@ async function runSubsystemVerification(config, analysis) {
7492
7557
  console.log();
7493
7558
  displayCompositionalReport(results, interference.valid);
7494
7559
  }
7560
+ function displayEnsuresSummary(results) {
7561
+ const totalEnsures = results.reduce((sum, r) => sum + r.ensuresCount, 0);
7562
+ if (totalEnsures === 0)
7563
+ return;
7564
+ const subsystemWord = `subsystem${results.length === 1 ? "" : "s"}`;
7565
+ console.log();
7566
+ console.log(color(` ${totalEnsures} ensures step-properties registered across ${results.length} ${subsystemWord}.`, COLORS.gray));
7567
+ console.log(color(" A property only fires at states where its handler is enabled — raise", COLORS.gray));
7568
+ console.log(color(" bounds.maxInFlight on a subsystem to reach multi-step-reachable handlers.", COLORS.gray));
7569
+ }
7495
7570
  function displayCompositionalReport(results, nonInterferenceValid) {
7496
7571
  console.log(color(`Subsystem verification results:
7497
7572
  `, COLORS.blue));
@@ -7499,10 +7574,12 @@ function displayCompositionalReport(results, nonInterferenceValid) {
7499
7574
  const status = r.success ? color("✓", COLORS.green) : color("✗", COLORS.red);
7500
7575
  const name = r.name.padEnd(20);
7501
7576
  const handlers = `${r.handlerCount} handler${r.handlerCount === 1 ? "" : "s"}`;
7577
+ const ensures = `${r.ensuresCount} ensures`;
7502
7578
  const states = `${r.stateCount} states`;
7503
7579
  const time = `${r.elapsed.toFixed(1)}s`;
7504
- console.log(` ${status} ${name} ${handlers.padEnd(14)} ${states.padEnd(14)} ${time}`);
7580
+ console.log(` ${status} ${name} ${handlers.padEnd(14)} ${ensures.padEnd(12)} ${states.padEnd(14)} ${time}`);
7505
7581
  }
7582
+ displayEnsuresSummary(results);
7506
7583
  console.log();
7507
7584
  const nonIntLabel = nonInterferenceValid ? color("✓ verified (no cross-subsystem state writes)", COLORS.green) : color("⚠ violations detected", COLORS.yellow);
7508
7585
  console.log(` Non-interference: ${nonIntLabel}`);
@@ -7742,4 +7819,4 @@ main().catch((error) => {
7742
7819
  process.exit(1);
7743
7820
  });
7744
7821
 
7745
- //# debugId=93D3E9EFDD87727A64756E2164756E21
7822
+ //# debugId=17E54E7016FB5CE064756E2164756E21