@fairfox/polly 0.89.1 → 0.90.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.
- package/dist/tools/verify/src/cli.js +342 -84
- package/dist/tools/verify/src/cli.js.map +11 -9
- package/dist/tools/verify/src/config.d.ts +7 -0
- package/dist/tools/verify/src/config.js.map +2 -2
- package/dist/tools/visualize/src/cli.js +85 -13
- package/dist/tools/visualize/src/cli.js.map +5 -4
- package/package.json +1 -1
|
@@ -245,6 +245,66 @@ var init_expression_validator = __esm(() => {
|
|
|
245
245
|
WEAK_NEGATION = /([a-zA-Z_][\w.]*(?:\.value\.[\w.]+|\.value\b|))?\s*!==?\s*("[^"]*"|'[^']*'|\d+|true|false|null)/;
|
|
246
246
|
});
|
|
247
247
|
|
|
248
|
+
// tools/verify/src/codegen/model-constants.ts
|
|
249
|
+
function hasProjectConstant(messages) {
|
|
250
|
+
return messages.maxWorkers !== undefined || messages.maxRenderers !== undefined || messages.maxContexts !== undefined || messages.maxClients !== undefined;
|
|
251
|
+
}
|
|
252
|
+
function generatedTabValues(messages) {
|
|
253
|
+
if (messages.tabSymmetry) {
|
|
254
|
+
const maxTabs = messages.maxTabs ?? 1;
|
|
255
|
+
return Array.from({ length: maxTabs + 1 }, (_, i) => `Tab${i}`);
|
|
256
|
+
}
|
|
257
|
+
if (messages.maxTabs !== undefined && messages.maxTabs !== null) {
|
|
258
|
+
return Array.from({ length: messages.maxTabs + 1 }, (_, i) => String(i));
|
|
259
|
+
}
|
|
260
|
+
return hasProjectConstant(messages) ? ["0"] : ["0", "1"];
|
|
261
|
+
}
|
|
262
|
+
function generatedTabCount(messages) {
|
|
263
|
+
return generatedTabValues(messages).length;
|
|
264
|
+
}
|
|
265
|
+
function sendBranchingFactor(handlerCount, tabCount) {
|
|
266
|
+
return CONTEXT_COUNT * TARGET_SET_COUNT * tabCount * handlerCount;
|
|
267
|
+
}
|
|
268
|
+
var GENERATED_CONTEXTS, CONTEXT_COUNT, TARGET_SET_COUNT;
|
|
269
|
+
var init_model_constants = __esm(() => {
|
|
270
|
+
GENERATED_CONTEXTS = ["background", "content", "popup"];
|
|
271
|
+
CONTEXT_COUNT = GENERATED_CONTEXTS.length;
|
|
272
|
+
TARGET_SET_COUNT = 2 ** CONTEXT_COUNT - 1;
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// tools/verify/src/runner/memory.ts
|
|
276
|
+
function parseMemoryBytes(spec) {
|
|
277
|
+
const match = /^(\d+)\s*([bkmg])?$/i.exec(spec.trim());
|
|
278
|
+
if (!match?.[1])
|
|
279
|
+
return null;
|
|
280
|
+
const value = Number.parseInt(match[1], 10);
|
|
281
|
+
if (value <= 0)
|
|
282
|
+
return null;
|
|
283
|
+
const unit = (match[2] ?? "b").toLowerCase();
|
|
284
|
+
const multiplier = UNIT_BYTES[unit];
|
|
285
|
+
if (multiplier === undefined)
|
|
286
|
+
return null;
|
|
287
|
+
return value * multiplier;
|
|
288
|
+
}
|
|
289
|
+
function jvmHeapArg(memoryBytes) {
|
|
290
|
+
const megabytes = Math.max(64, Math.floor(memoryBytes * JVM_HEAP_FRACTION / MEGABYTE));
|
|
291
|
+
return `${megabytes}m`;
|
|
292
|
+
}
|
|
293
|
+
function javaToolOptions(memoryBytes) {
|
|
294
|
+
return `-Xmx${jvmHeapArg(memoryBytes)} -XX:+UseParallelGC`;
|
|
295
|
+
}
|
|
296
|
+
var DEFAULT_TLC_MEMORY = "4g", MIN_TLC_MEMORY = "512m", JVM_HEAP_FRACTION = 0.75, MEGABYTE, UNIT_BYTES, MIN_TLC_MEMORY_BYTES;
|
|
297
|
+
var init_memory = __esm(() => {
|
|
298
|
+
MEGABYTE = 1024 ** 2;
|
|
299
|
+
UNIT_BYTES = {
|
|
300
|
+
b: 1,
|
|
301
|
+
k: 1024,
|
|
302
|
+
m: MEGABYTE,
|
|
303
|
+
g: 1024 ** 3
|
|
304
|
+
};
|
|
305
|
+
MIN_TLC_MEMORY_BYTES = parseMemoryBytes(MIN_TLC_MEMORY) ?? 512 * 1024 ** 2;
|
|
306
|
+
});
|
|
307
|
+
|
|
248
308
|
// tools/verify/src/analysis/model-coverage.ts
|
|
249
309
|
function norm(field) {
|
|
250
310
|
return field.replace(/_/g, ".");
|
|
@@ -751,6 +811,7 @@ async function generateSubsystemTLA(_subsystemName, subsystem, config, analysis)
|
|
|
751
811
|
}
|
|
752
812
|
var TLAValidationError, TLAGenerator;
|
|
753
813
|
var init_tla = __esm(() => {
|
|
814
|
+
init_model_constants();
|
|
754
815
|
init_temporal();
|
|
755
816
|
TLAValidationError = class TLAValidationError extends Error {
|
|
756
817
|
report;
|
|
@@ -770,7 +831,6 @@ var init_tla = __esm(() => {
|
|
|
770
831
|
symmetrySets = [];
|
|
771
832
|
resolvedActionNames = new Map;
|
|
772
833
|
paramDomains = new Map;
|
|
773
|
-
tabSymmetryEnabled = false;
|
|
774
834
|
tabCount = 0;
|
|
775
835
|
moduleName = "UserApp";
|
|
776
836
|
currentConfig;
|
|
@@ -981,7 +1041,7 @@ var init_tla = __esm(() => {
|
|
|
981
1041
|
lines.push("CONSTANTS");
|
|
982
1042
|
}
|
|
983
1043
|
addBasicConstants(lines, config) {
|
|
984
|
-
lines.push(
|
|
1044
|
+
lines.push(` Contexts = {${GENERATED_CONTEXTS.join(", ")}}`);
|
|
985
1045
|
lines.push(` MaxMessages = ${config.messages.maxInFlight || 3}`);
|
|
986
1046
|
lines.push(" NULL = NULL");
|
|
987
1047
|
if (config.messages.perMessageBounds) {
|
|
@@ -1007,22 +1067,14 @@ var init_tla = __esm(() => {
|
|
|
1007
1067
|
}
|
|
1008
1068
|
if ("maxClients" in messages && messages.maxClients !== undefined && !hasProjectConstant) {
|
|
1009
1069
|
lines.push(` MaxClients = ${messages.maxClients}`);
|
|
1010
|
-
hasProjectConstant = true;
|
|
1011
1070
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
lines.push(` Tabs = {${tabValues}}`);
|
|
1018
|
-
} else if ("maxTabs" in messages && messages.maxTabs !== undefined && messages.maxTabs !== null) {
|
|
1019
|
-
const tabValues = Array.from({ length: messages.maxTabs + 1 }, (_, i) => i).join(", ");
|
|
1020
|
-
lines.push(` Tabs = {${tabValues}}`);
|
|
1021
|
-
} else if (hasProjectConstant) {
|
|
1022
|
-
lines.push(" Tabs = {0}");
|
|
1023
|
-
} else {
|
|
1024
|
-
lines.push(" Tabs = {0, 1}");
|
|
1071
|
+
const tabValues = generatedTabValues(messages);
|
|
1072
|
+
if (messages.tabSymmetry) {
|
|
1073
|
+
for (const tab of tabValues) {
|
|
1074
|
+
lines.push(` ${tab} = ${tab}`);
|
|
1075
|
+
}
|
|
1025
1076
|
}
|
|
1077
|
+
lines.push(` Tabs = {${tabValues.join(", ")}}`);
|
|
1026
1078
|
lines.push(" TimeoutLimit = 3");
|
|
1027
1079
|
}
|
|
1028
1080
|
addStateBoundsConstants(lines, state) {
|
|
@@ -1442,7 +1494,6 @@ var init_tla = __esm(() => {
|
|
|
1442
1494
|
}
|
|
1443
1495
|
const maxTabs = config.messages.maxTabs ?? 1;
|
|
1444
1496
|
this.tabCount = maxTabs + 1;
|
|
1445
|
-
this.tabSymmetryEnabled = true;
|
|
1446
1497
|
this.line("\\* Tab symmetry constants for state space reduction (Tier 1 optimization)");
|
|
1447
1498
|
this.line("CONSTANTS");
|
|
1448
1499
|
this.indent++;
|
|
@@ -3562,11 +3613,20 @@ class DockerRunner {
|
|
|
3562
3613
|
}
|
|
3563
3614
|
const containerName = mintContainerName("tlc");
|
|
3564
3615
|
sweepOrphanedContainersOnce();
|
|
3616
|
+
const memory = options?.memory ?? DEFAULT_TLC_MEMORY;
|
|
3617
|
+
const memoryBytes = parseMemoryBytes(memory);
|
|
3618
|
+
if (memoryBytes === null) {
|
|
3619
|
+
throw new Error(`Invalid verification.memory: "${memory}". Use a docker-style size such as "512m" or "8g".`);
|
|
3620
|
+
}
|
|
3565
3621
|
const args = [
|
|
3566
3622
|
"run",
|
|
3567
3623
|
"--rm",
|
|
3568
3624
|
"--name",
|
|
3569
3625
|
containerName,
|
|
3626
|
+
"--memory",
|
|
3627
|
+
memory,
|
|
3628
|
+
"-e",
|
|
3629
|
+
`JAVA_TOOL_OPTIONS=${javaToolOptions(memoryBytes)}`,
|
|
3570
3630
|
"-v",
|
|
3571
3631
|
`${specDir}:/work`,
|
|
3572
3632
|
this.IMAGE_NAME,
|
|
@@ -3585,9 +3645,9 @@ class DockerRunner {
|
|
|
3585
3645
|
timeout: options?.timeout,
|
|
3586
3646
|
containerName
|
|
3587
3647
|
});
|
|
3588
|
-
return this.parseTLCOutput(result);
|
|
3648
|
+
return this.parseTLCOutput(result, memory);
|
|
3589
3649
|
}
|
|
3590
|
-
parseTLCOutput(result) {
|
|
3650
|
+
parseTLCOutput(result, memory) {
|
|
3591
3651
|
const output = result.stdout + result.stderr;
|
|
3592
3652
|
const violationMatch = output.match(/Error: Invariant (.*?) is violated/);
|
|
3593
3653
|
if (violationMatch) {
|
|
@@ -3604,18 +3664,13 @@ class DockerRunner {
|
|
|
3604
3664
|
if (result.exitCode !== 0 || output.includes("Error:")) {
|
|
3605
3665
|
return {
|
|
3606
3666
|
success: false,
|
|
3607
|
-
error: this.extractError(output),
|
|
3667
|
+
error: this.extractError(output, result.exitCode, memory),
|
|
3608
3668
|
output
|
|
3609
3669
|
};
|
|
3610
3670
|
}
|
|
3611
|
-
const statesMatch = output.match(/(\d+) states generated/);
|
|
3612
|
-
const distinctMatch = output.match(/(\d+) distinct states/);
|
|
3613
3671
|
return {
|
|
3614
3672
|
success: true,
|
|
3615
|
-
|
|
3616
|
-
statesGenerated: statesMatch?.[1] ? Number.parseInt(statesMatch[1], 10) : 0,
|
|
3617
|
-
distinctStates: distinctMatch?.[1] ? Number.parseInt(distinctMatch[1], 10) : 0
|
|
3618
|
-
},
|
|
3673
|
+
...parseTLCStats(output) ?? {},
|
|
3619
3674
|
output
|
|
3620
3675
|
};
|
|
3621
3676
|
}
|
|
@@ -3637,7 +3692,14 @@ class DockerRunner {
|
|
|
3637
3692
|
}
|
|
3638
3693
|
return trace;
|
|
3639
3694
|
}
|
|
3640
|
-
extractError(output) {
|
|
3695
|
+
extractError(output, exitCode, memory) {
|
|
3696
|
+
const cap = memory ? ` (--memory ${memory})` : "";
|
|
3697
|
+
if (/java\.lang\.OutOfMemoryError/.test(output)) {
|
|
3698
|
+
return `TLC ran out of heap${cap}. The model is too large for this ceiling — raise verification.memory, lower messages.maxInFlight, or split the subsystem.`;
|
|
3699
|
+
}
|
|
3700
|
+
if (exitCode === 137) {
|
|
3701
|
+
return `TLC was killed for exceeding the container memory limit${cap}. Raise verification.memory, lower messages.maxInFlight, or split the subsystem.`;
|
|
3702
|
+
}
|
|
3641
3703
|
if (/StatePool\w*\.run/.test(output) && /states\/.*No such file or directory/.test(output)) {
|
|
3642
3704
|
return "TLC state-pool IO failure (states/.../<n>: No such file or directory). This is a Docker filesystem flake, not a spec error — re-run the verification. See issue #152.";
|
|
3643
3705
|
}
|
|
@@ -3734,8 +3796,36 @@ class DockerRunner {
|
|
|
3734
3796
|
});
|
|
3735
3797
|
}
|
|
3736
3798
|
}
|
|
3737
|
-
|
|
3738
|
-
|
|
3799
|
+
function parseTLCStats(output) {
|
|
3800
|
+
const summary = TLC_SUMMARY.exec(output);
|
|
3801
|
+
if (!summary?.[1] || !summary[2] || !summary[3])
|
|
3802
|
+
return;
|
|
3803
|
+
const statesGenerated = Number.parseInt(summary[1], 10);
|
|
3804
|
+
const distinctStates = Number.parseInt(summary[2], 10);
|
|
3805
|
+
const statesLeftOnQueue = Number.parseInt(summary[3], 10);
|
|
3806
|
+
const initial = TLC_INITIAL_STATES.exec(output)?.[1];
|
|
3807
|
+
const initialStates = initial === undefined ? undefined : Number.parseInt(initial, 10);
|
|
3808
|
+
const depth = TLC_SEARCH_DEPTH.exec(output)?.[1];
|
|
3809
|
+
const searchDepth = depth === undefined ? undefined : Number.parseInt(depth, 10);
|
|
3810
|
+
return {
|
|
3811
|
+
stats: {
|
|
3812
|
+
statesGenerated,
|
|
3813
|
+
distinctStates,
|
|
3814
|
+
statesLeftOnQueue,
|
|
3815
|
+
...initialStates === undefined ? {} : { initialStates },
|
|
3816
|
+
...searchDepth === undefined ? {} : { searchDepth },
|
|
3817
|
+
exhaustive: statesLeftOnQueue === 0,
|
|
3818
|
+
noActionEnabled: initialStates !== undefined && distinctStates === initialStates
|
|
3819
|
+
}
|
|
3820
|
+
};
|
|
3821
|
+
}
|
|
3822
|
+
var __dirname = "/Users/AJT/projects/polly/packages/polly/tools/verify/src/runner", swept = false, TLC_SUMMARY, TLC_INITIAL_STATES, TLC_SEARCH_DEPTH;
|
|
3823
|
+
var init_docker = __esm(() => {
|
|
3824
|
+
init_memory();
|
|
3825
|
+
TLC_SUMMARY = /^(\d+) states generated, (\d+) distinct states found, (\d+) states left on queue/m;
|
|
3826
|
+
TLC_INITIAL_STATES = /Finished computing initial states: (\d+) distinct states generated/;
|
|
3827
|
+
TLC_SEARCH_DEPTH = /The depth of the complete state graph search is (\d+)/;
|
|
3828
|
+
});
|
|
3739
3829
|
|
|
3740
3830
|
// tools/verify/src/cli.ts
|
|
3741
3831
|
init_expression_validator();
|
|
@@ -3778,6 +3868,9 @@ function computeMeshOrPeerSignalFindings(analysis, declaredMeshDocs) {
|
|
|
3778
3868
|
return findings;
|
|
3779
3869
|
}
|
|
3780
3870
|
|
|
3871
|
+
// tools/verify/src/analysis/state-space-estimator.ts
|
|
3872
|
+
init_model_constants();
|
|
3873
|
+
|
|
3781
3874
|
// tools/verify/src/config/types.ts
|
|
3782
3875
|
function isAdapterConfig(config) {
|
|
3783
3876
|
return "adapter" in config;
|
|
@@ -3790,17 +3883,20 @@ function isLegacyConfig(config) {
|
|
|
3790
3883
|
function typedFieldCardinality(name, obj) {
|
|
3791
3884
|
if (!("type" in obj))
|
|
3792
3885
|
return null;
|
|
3793
|
-
|
|
3886
|
+
const values = obj["values"];
|
|
3887
|
+
const min = obj["min"];
|
|
3888
|
+
const max = obj["max"];
|
|
3889
|
+
switch (obj["type"]) {
|
|
3794
3890
|
case "boolean":
|
|
3795
3891
|
return { name, cardinality: 2, kind: "boolean" };
|
|
3796
3892
|
case "enum":
|
|
3797
|
-
if (Array.isArray(
|
|
3798
|
-
return { name, cardinality:
|
|
3893
|
+
if (Array.isArray(values)) {
|
|
3894
|
+
return { name, cardinality: values.length, kind: "enum" };
|
|
3799
3895
|
}
|
|
3800
3896
|
return { name, cardinality: "unbounded", kind: "enum" };
|
|
3801
3897
|
case "number":
|
|
3802
|
-
if (typeof
|
|
3803
|
-
return { name, cardinality:
|
|
3898
|
+
if (typeof min === "number" && typeof max === "number") {
|
|
3899
|
+
return { name, cardinality: max - min + 1, kind: "number" };
|
|
3804
3900
|
}
|
|
3805
3901
|
return { name, cardinality: "unbounded", kind: "number" };
|
|
3806
3902
|
case "array":
|
|
@@ -3812,20 +3908,23 @@ function typedFieldCardinality(name, obj) {
|
|
|
3812
3908
|
}
|
|
3813
3909
|
}
|
|
3814
3910
|
function legacyFieldCardinality(name, obj) {
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3911
|
+
const values = obj["values"];
|
|
3912
|
+
const abstract = obj["abstract"];
|
|
3913
|
+
const min = obj["min"];
|
|
3914
|
+
const max = obj["max"];
|
|
3915
|
+
if (Array.isArray(values)) {
|
|
3916
|
+
const extra = abstract === true ? 1 : 0;
|
|
3818
3917
|
return {
|
|
3819
3918
|
name,
|
|
3820
|
-
cardinality:
|
|
3821
|
-
kind:
|
|
3919
|
+
cardinality: values.length + extra,
|
|
3920
|
+
kind: abstract === true ? "enum (abstract)" : "enum (values)"
|
|
3822
3921
|
};
|
|
3823
3922
|
}
|
|
3824
3923
|
if ("maxLength" in obj && !("type" in obj)) {
|
|
3825
3924
|
return { name, cardinality: "unbounded", kind: "array" };
|
|
3826
3925
|
}
|
|
3827
|
-
if (
|
|
3828
|
-
return { name, cardinality:
|
|
3926
|
+
if (typeof min === "number" && typeof max === "number" && !("type" in obj)) {
|
|
3927
|
+
return { name, cardinality: max - min + 1, kind: "number" };
|
|
3829
3928
|
}
|
|
3830
3929
|
return null;
|
|
3831
3930
|
}
|
|
@@ -3839,15 +3938,6 @@ function fieldCardinality(name, value) {
|
|
|
3839
3938
|
const obj = value;
|
|
3840
3939
|
return typedFieldCardinality(name, obj) ?? legacyFieldCardinality(name, obj) ?? { name, cardinality: "unbounded", kind: "unknown" };
|
|
3841
3940
|
}
|
|
3842
|
-
function permutations(n, k) {
|
|
3843
|
-
if (k > n)
|
|
3844
|
-
return 1;
|
|
3845
|
-
let result = 1;
|
|
3846
|
-
for (let i = 0;i < k; i++) {
|
|
3847
|
-
result *= n - i;
|
|
3848
|
-
}
|
|
3849
|
-
return result;
|
|
3850
|
-
}
|
|
3851
3941
|
function getHandlerCount(config, analysis) {
|
|
3852
3942
|
if (isLegacyConfig(config)) {
|
|
3853
3943
|
const msgs = config.messages;
|
|
@@ -3869,11 +3959,11 @@ function getMaxInFlight(config) {
|
|
|
3869
3959
|
}
|
|
3870
3960
|
return 1;
|
|
3871
3961
|
}
|
|
3872
|
-
function
|
|
3962
|
+
function getTabSetInput(config) {
|
|
3873
3963
|
if (isLegacyConfig(config)) {
|
|
3874
|
-
return config.messages
|
|
3964
|
+
return config.messages;
|
|
3875
3965
|
}
|
|
3876
|
-
return
|
|
3966
|
+
return {};
|
|
3877
3967
|
}
|
|
3878
3968
|
function getFeasibility(states) {
|
|
3879
3969
|
if (states < 1e5)
|
|
@@ -3896,12 +3986,35 @@ function feasibilityLabel(f) {
|
|
|
3896
3986
|
return "infeasible (likely won't terminate)";
|
|
3897
3987
|
}
|
|
3898
3988
|
}
|
|
3899
|
-
|
|
3989
|
+
var OMITTED_VARIABLES = "ports, status, deliveredTo, time and payload";
|
|
3990
|
+
function dominantTerm(e) {
|
|
3991
|
+
const log10 = (n) => n > 0 ? Math.log10(n) : 0;
|
|
3992
|
+
const topology = e.contextCount * TARGET_SET_COUNT * e.tabCount;
|
|
3993
|
+
const terms = [
|
|
3994
|
+
{
|
|
3995
|
+
label: `state fields (${e.fieldProduct} combinations across ${e.contextCount} contexts)`,
|
|
3996
|
+
decades: e.contextCount * log10(e.fieldProduct)
|
|
3997
|
+
},
|
|
3998
|
+
{
|
|
3999
|
+
label: `handler count (${e.handlerCount} handlers at maxInFlight ${e.maxInFlight})`,
|
|
4000
|
+
decades: e.maxInFlight * log10(e.handlerCount)
|
|
4001
|
+
},
|
|
4002
|
+
{
|
|
4003
|
+
label: `send topology (${e.contextCount} sources x ${TARGET_SET_COUNT} target sets x ${e.tabCount} tabs)`,
|
|
4004
|
+
decades: e.maxInFlight * log10(topology)
|
|
4005
|
+
}
|
|
4006
|
+
];
|
|
4007
|
+
return terms.reduce((a, b) => b.decades > a.decades ? b : a);
|
|
4008
|
+
}
|
|
4009
|
+
function estimateForScope(config, scope) {
|
|
3900
4010
|
const state = config.state;
|
|
3901
4011
|
const fields = [];
|
|
3902
4012
|
const warnings = [];
|
|
3903
4013
|
const suggestions = [];
|
|
4014
|
+
const included = scope.stateFields ? new Set(scope.stateFields) : undefined;
|
|
3904
4015
|
for (const [name, value] of Object.entries(state)) {
|
|
4016
|
+
if (included && !included.has(name))
|
|
4017
|
+
continue;
|
|
3905
4018
|
fields.push(fieldCardinality(name, value));
|
|
3906
4019
|
}
|
|
3907
4020
|
const unboundedFields = fields.filter((f) => f.cardinality === "unbounded");
|
|
@@ -3911,33 +4024,47 @@ function estimateStateSpace(config, analysis) {
|
|
|
3911
4024
|
warnings.push(`${unboundedFields.length} field(s) have unbounded domains: ${unboundedFields.map((f) => f.name).join(", ")}`);
|
|
3912
4025
|
suggestions.push(`Fields ${unboundedFields.map((f) => f.name).join(", ")} have unbounded domains — their actual impact depends on handler logic`);
|
|
3913
4026
|
}
|
|
3914
|
-
const handlerCount =
|
|
3915
|
-
const
|
|
3916
|
-
const
|
|
3917
|
-
const contextCount =
|
|
4027
|
+
const { handlerCount, maxInFlight } = scope;
|
|
4028
|
+
const tabSetInput = getTabSetInput(config);
|
|
4029
|
+
const tabCount = generatedTabCount(tabSetInput);
|
|
4030
|
+
const contextCount = CONTEXT_COUNT;
|
|
3918
4031
|
const totalStateSpace = fieldProduct ** contextCount;
|
|
3919
|
-
const
|
|
4032
|
+
const sendBranching = sendBranchingFactor(handlerCount, tabCount);
|
|
4033
|
+
const interleavingFactor = sendBranching ** maxInFlight;
|
|
3920
4034
|
const estimatedStates = totalStateSpace * interleavingFactor;
|
|
3921
4035
|
const feasibility = getFeasibility(estimatedStates);
|
|
4036
|
+
warnings.push(`Lower bound: the generated spec also carries ${OMITTED_VARIABLES}, none of which this figure models.`);
|
|
4037
|
+
if (tabSetInput.tabSymmetry) {
|
|
4038
|
+
warnings.push("tabSymmetry is enabled — TLC's symmetry reduction cuts the reachable set, so this is an upper bound on the tab dimension.");
|
|
4039
|
+
}
|
|
4040
|
+
const dominant = dominantTerm({
|
|
4041
|
+
fieldProduct,
|
|
4042
|
+
handlerCount,
|
|
4043
|
+
contextCount,
|
|
4044
|
+
tabCount,
|
|
4045
|
+
maxInFlight
|
|
4046
|
+
});
|
|
4047
|
+
suggestions.push(`Dominant term: ${dominant.label} — ~10^${dominant.decades.toFixed(1)} of ~10^${Math.log10(Math.max(estimatedStates, 1)).toFixed(1)}`);
|
|
4048
|
+
if (maxInFlight > 1) {
|
|
4049
|
+
suggestions.push(`maxInFlight ${maxInFlight} → ${maxInFlight - 1} divides the estimate by ${sendBranching.toLocaleString()}x (one fewer send to branch over)`);
|
|
4050
|
+
}
|
|
3922
4051
|
if (handlerCount > 15) {
|
|
3923
|
-
suggestions.push(
|
|
4052
|
+
suggestions.push(`${handlerCount} handlers enter the estimate as ${handlerCount}^${maxInFlight} — splitting into subsystems is the cheapest cut`);
|
|
3924
4053
|
}
|
|
3925
4054
|
for (const f of boundedFields) {
|
|
3926
4055
|
if (f.cardinality > 50) {
|
|
3927
|
-
suggestions.push(`
|
|
4056
|
+
suggestions.push(`Field "${f.name}" (${f.cardinality} values) is raised to the power of ${contextCount} contexts — reducing its bounds compounds`);
|
|
3928
4057
|
}
|
|
3929
4058
|
}
|
|
3930
|
-
if (maxInFlight > 2) {
|
|
3931
|
-
const reducedInterleaving = permutations(handlerCount, 2);
|
|
3932
|
-
const reduction = reducedInterleaving > 0 ? Math.round(interleavingFactor / reducedInterleaving) : 1;
|
|
3933
|
-
suggestions.push(`Reducing maxInFlight from ${maxInFlight} to 2 would reduce interleaving by ~${reduction}x`);
|
|
3934
|
-
}
|
|
3935
4059
|
return {
|
|
4060
|
+
...scope.name === undefined ? {} : { subsystem: scope.name },
|
|
3936
4061
|
fields,
|
|
3937
4062
|
fieldProduct,
|
|
3938
4063
|
handlerCount,
|
|
3939
4064
|
maxInFlight,
|
|
3940
4065
|
contextCount,
|
|
4066
|
+
tabCount,
|
|
4067
|
+
sendBranching,
|
|
3941
4068
|
totalStateSpace,
|
|
3942
4069
|
interleavingFactor,
|
|
3943
4070
|
estimatedStates,
|
|
@@ -3946,6 +4073,24 @@ function estimateStateSpace(config, analysis) {
|
|
|
3946
4073
|
suggestions
|
|
3947
4074
|
};
|
|
3948
4075
|
}
|
|
4076
|
+
function estimateStateSpace(config, analysis) {
|
|
4077
|
+
return estimateForScope(config, {
|
|
4078
|
+
handlerCount: getHandlerCount(config, analysis),
|
|
4079
|
+
maxInFlight: getMaxInFlight(config)
|
|
4080
|
+
});
|
|
4081
|
+
}
|
|
4082
|
+
function estimateSubsystems(config) {
|
|
4083
|
+
const subsystems = config.subsystems;
|
|
4084
|
+
if (!subsystems)
|
|
4085
|
+
return [];
|
|
4086
|
+
const topLevelMaxInFlight = getMaxInFlight(config);
|
|
4087
|
+
return Object.entries(subsystems).map(([name, sub]) => estimateForScope(config, {
|
|
4088
|
+
name,
|
|
4089
|
+
stateFields: sub.state,
|
|
4090
|
+
handlerCount: sub.handlers.length,
|
|
4091
|
+
maxInFlight: sub.bounds?.maxInFlight ?? topLevelMaxInFlight
|
|
4092
|
+
}));
|
|
4093
|
+
}
|
|
3949
4094
|
|
|
3950
4095
|
// tools/verify/src/codegen/config.ts
|
|
3951
4096
|
class ConfigGenerator {
|
|
@@ -4402,6 +4547,7 @@ function generateConfig(analysis, projectType = "chrome-extension") {
|
|
|
4402
4547
|
}
|
|
4403
4548
|
|
|
4404
4549
|
// tools/verify/src/config/parser.ts
|
|
4550
|
+
init_memory();
|
|
4405
4551
|
import * as fs from "node:fs";
|
|
4406
4552
|
import * as path from "node:path";
|
|
4407
4553
|
|
|
@@ -4875,6 +5021,31 @@ class ConfigValidator {
|
|
|
4875
5021
|
});
|
|
4876
5022
|
}
|
|
4877
5023
|
}
|
|
5024
|
+
this.validateMemory(verification.memory);
|
|
5025
|
+
}
|
|
5026
|
+
validateMemory(memory) {
|
|
5027
|
+
if (memory === undefined)
|
|
5028
|
+
return;
|
|
5029
|
+
const bytes = parseMemoryBytes(memory);
|
|
5030
|
+
if (bytes === null) {
|
|
5031
|
+
this.issues.push({
|
|
5032
|
+
type: "invalid_value",
|
|
5033
|
+
severity: "error",
|
|
5034
|
+
field: "verification.memory",
|
|
5035
|
+
message: `"${memory}" is not a docker-style memory size`,
|
|
5036
|
+
suggestion: 'Use a number with an optional b/k/m/g suffix, e.g. "8g"'
|
|
5037
|
+
});
|
|
5038
|
+
return;
|
|
5039
|
+
}
|
|
5040
|
+
if (bytes < MIN_TLC_MEMORY_BYTES) {
|
|
5041
|
+
this.issues.push({
|
|
5042
|
+
type: "invalid_value",
|
|
5043
|
+
severity: "error",
|
|
5044
|
+
field: "verification.memory",
|
|
5045
|
+
message: `${memory} is below the ${MIN_TLC_MEMORY} minimum TLC can run in`,
|
|
5046
|
+
suggestion: `Use ${MIN_TLC_MEMORY} or more`
|
|
5047
|
+
});
|
|
5048
|
+
}
|
|
4878
5049
|
}
|
|
4879
5050
|
validateTier2Optimizations(tier2) {
|
|
4880
5051
|
if (tier2.temporalConstraints) {
|
|
@@ -8427,6 +8598,9 @@ async function analyzeCodebase(options) {
|
|
|
8427
8598
|
const extractor = new TypeExtractor(options.tsConfigPath);
|
|
8428
8599
|
return extractor.analyzeCodebase(options.stateFilePath);
|
|
8429
8600
|
}
|
|
8601
|
+
// tools/verify/src/cli.ts
|
|
8602
|
+
init_memory();
|
|
8603
|
+
|
|
8430
8604
|
// tools/verify/src/runner/mesh-seed.ts
|
|
8431
8605
|
function meshSeedCfg(baseCfg, opts) {
|
|
8432
8606
|
if (!opts.disableFix)
|
|
@@ -8612,11 +8786,30 @@ async function estimateCommand() {
|
|
|
8612
8786
|
if (exprValidation.warnings.length > 0) {
|
|
8613
8787
|
displayExpressionWarnings(exprValidation);
|
|
8614
8788
|
}
|
|
8615
|
-
const
|
|
8616
|
-
|
|
8789
|
+
const perSubsystem = estimateSubsystems(typedConfig);
|
|
8790
|
+
if (perSubsystem.length > 0) {
|
|
8791
|
+
for (const estimate of perSubsystem) {
|
|
8792
|
+
displayEstimate(estimate);
|
|
8793
|
+
}
|
|
8794
|
+
displaySubsystemEstimateSummary(perSubsystem);
|
|
8795
|
+
return;
|
|
8796
|
+
}
|
|
8797
|
+
displayEstimate(estimateStateSpace(typedConfig, typedAnalysis));
|
|
8798
|
+
}
|
|
8799
|
+
function displaySubsystemEstimateSummary(estimates) {
|
|
8800
|
+
console.log(color(`Per-subsystem summary:
|
|
8801
|
+
`, COLORS.blue));
|
|
8802
|
+
for (const e of estimates) {
|
|
8803
|
+
const name = (e.subsystem ?? "?").padEnd(20);
|
|
8804
|
+
const states = `~${e.estimatedStates.toLocaleString()}`.padEnd(20);
|
|
8805
|
+
const feasColor = e.feasibility === "trivial" || e.feasibility === "feasible" ? COLORS.green : e.feasibility === "slow" ? COLORS.yellow : COLORS.red;
|
|
8806
|
+
console.log(` ${name} ${states} ${color(e.feasibility, feasColor)}`);
|
|
8807
|
+
}
|
|
8808
|
+
console.log();
|
|
8617
8809
|
}
|
|
8618
8810
|
function displayEstimate(estimate) {
|
|
8619
|
-
|
|
8811
|
+
const heading = estimate.subsystem ? `State space estimate — subsystem ${estimate.subsystem}:` : "State space estimate:";
|
|
8812
|
+
console.log(color(`${heading}
|
|
8620
8813
|
`, COLORS.blue));
|
|
8621
8814
|
console.log(color(" Fields:", COLORS.blue));
|
|
8622
8815
|
for (const field of estimate.fields) {
|
|
@@ -8629,8 +8822,10 @@ function displayEstimate(estimate) {
|
|
|
8629
8822
|
console.log(` Field combinations: ${color(String(estimate.fieldProduct), COLORS.green)}`);
|
|
8630
8823
|
console.log(` Handlers: ${estimate.handlerCount}`);
|
|
8631
8824
|
console.log(` Max in-flight: ${estimate.maxInFlight}`);
|
|
8632
|
-
console.log(` Contexts: ${estimate.contextCount} (
|
|
8633
|
-
console.log(`
|
|
8825
|
+
console.log(` Contexts: ${estimate.contextCount} (fixed by the generated .cfg)`);
|
|
8826
|
+
console.log(` Tabs: ${estimate.tabCount}`);
|
|
8827
|
+
console.log(` State across contexts: ${estimate.fieldProduct}^${estimate.contextCount} = ${estimate.totalStateSpace.toLocaleString()}`);
|
|
8828
|
+
console.log(` Send branching: ${estimate.sendBranching.toLocaleString()} per message (^${estimate.maxInFlight} = ${estimate.interleavingFactor.toLocaleString()})`);
|
|
8634
8829
|
console.log();
|
|
8635
8830
|
console.log(` Estimated states: ${color(`~${estimate.estimatedStates.toLocaleString()}`, COLORS.green)}`);
|
|
8636
8831
|
const feasColor = estimate.feasibility === "trivial" || estimate.feasibility === "feasible" ? COLORS.green : estimate.feasibility === "slow" ? COLORS.yellow : COLORS.red;
|
|
@@ -8854,6 +9049,9 @@ function getTimeout(config) {
|
|
|
8854
9049
|
function getWorkers(config) {
|
|
8855
9050
|
return config.verification?.workers ?? 1;
|
|
8856
9051
|
}
|
|
9052
|
+
function getMemory(config) {
|
|
9053
|
+
return config.verification?.memory;
|
|
9054
|
+
}
|
|
8857
9055
|
function getMaxDepth(config) {
|
|
8858
9056
|
if ("tier2" in config && config.tier2?.boundedExploration?.maxDepth) {
|
|
8859
9057
|
return config.tier2.boundedExploration.maxDepth;
|
|
@@ -8927,6 +9125,7 @@ async function runMonolithicVerification(config, analysis) {
|
|
|
8927
9125
|
const timeoutSeconds = getTimeout(typedConfig);
|
|
8928
9126
|
const workers = getWorkers(typedConfig);
|
|
8929
9127
|
const maxDepth = getMaxDepth(typedConfig);
|
|
9128
|
+
const memory = getMemory(typedConfig);
|
|
8930
9129
|
console.log(color("⚙️ Running TLC model checker...", COLORS.blue));
|
|
8931
9130
|
if (timeoutSeconds === 0) {
|
|
8932
9131
|
console.log(color(" No timeout set - will run until completion", COLORS.gray));
|
|
@@ -8938,13 +9137,15 @@ async function runMonolithicVerification(config, analysis) {
|
|
|
8938
9137
|
if (maxDepth !== undefined) {
|
|
8939
9138
|
console.log(color(` Max depth: ${maxDepth}`, COLORS.gray));
|
|
8940
9139
|
}
|
|
9140
|
+
console.log(color(` Memory: ${memory ?? DEFAULT_TLC_MEMORY}`, COLORS.gray));
|
|
8941
9141
|
console.log();
|
|
8942
9142
|
const result = await docker.runTLC(specPath, {
|
|
8943
9143
|
workers,
|
|
8944
9144
|
timeout: timeoutSeconds > 0 ? timeoutSeconds * 1000 : undefined,
|
|
8945
|
-
maxDepth
|
|
9145
|
+
maxDepth,
|
|
9146
|
+
memory
|
|
8946
9147
|
});
|
|
8947
|
-
const meshSeed = await runMeshSeedGuard(docker, specDir, config);
|
|
9148
|
+
const meshSeed = await runMeshSeedGuard(docker, specDir, config, memory);
|
|
8948
9149
|
if (meshSeed && !meshSeed.success) {
|
|
8949
9150
|
console.log(color(`
|
|
8950
9151
|
❌ Mesh seed-race guard failed (polly#113 / polly#114)
|
|
@@ -9011,6 +9212,7 @@ async function runSubsystemVerification(config, analysis) {
|
|
|
9011
9212
|
const timeoutSeconds = getTimeout(config);
|
|
9012
9213
|
const workers = getWorkers(config);
|
|
9013
9214
|
const maxDepth = getMaxDepth(config);
|
|
9215
|
+
const memory = getMemory(config);
|
|
9014
9216
|
await Promise.resolve().then(() => init_tla());
|
|
9015
9217
|
const results = [];
|
|
9016
9218
|
const customTLAPaths = getCustomTLAPaths(config);
|
|
@@ -9036,7 +9238,8 @@ async function runSubsystemVerification(config, analysis) {
|
|
|
9036
9238
|
const result = await docker.runTLC(specPath, {
|
|
9037
9239
|
workers,
|
|
9038
9240
|
timeout: timeoutSeconds > 0 ? timeoutSeconds * 1000 : undefined,
|
|
9039
|
-
maxDepth
|
|
9241
|
+
maxDepth,
|
|
9242
|
+
memory
|
|
9040
9243
|
});
|
|
9041
9244
|
const elapsed = (Date.now() - startTime) / 1000;
|
|
9042
9245
|
results.push({
|
|
@@ -9044,9 +9247,8 @@ async function runSubsystemVerification(config, analysis) {
|
|
|
9044
9247
|
success: result.success,
|
|
9045
9248
|
handlerCount: sub.handlers.length,
|
|
9046
9249
|
ensuresCount,
|
|
9047
|
-
stateCount: result.stats?.distinctStates ?? 0,
|
|
9048
9250
|
elapsed,
|
|
9049
|
-
stats: result.stats,
|
|
9251
|
+
...result.stats === undefined ? {} : { stats: result.stats },
|
|
9050
9252
|
error: result.error
|
|
9051
9253
|
});
|
|
9052
9254
|
if (result.success) {
|
|
@@ -9065,6 +9267,14 @@ async function runSubsystemVerification(config, analysis) {
|
|
|
9065
9267
|
displayCompositionalReport(results, interference.valid);
|
|
9066
9268
|
}
|
|
9067
9269
|
var DEFAULT_WITNESS_TIMEOUT_MS = 180000;
|
|
9270
|
+
function witnessExplorationNote(stats) {
|
|
9271
|
+
if (!stats)
|
|
9272
|
+
return "TLC printed no summary line — exploration incomplete, verdict unsound";
|
|
9273
|
+
if (!stats.exhaustive) {
|
|
9274
|
+
return `${stats.distinctStates} distinct states explored, ${stats.statesLeftOnQueue} left on queue — not exhaustive`;
|
|
9275
|
+
}
|
|
9276
|
+
return `${stats.distinctStates} distinct states explored`;
|
|
9277
|
+
}
|
|
9068
9278
|
async function globAbsolute(cwd, pattern) {
|
|
9069
9279
|
const out = [];
|
|
9070
9280
|
for await (const f of new Bun.Glob(pattern).scan({ cwd, absolute: true, onlyFiles: true })) {
|
|
@@ -9097,6 +9307,7 @@ async function runWitnessVerification(config) {
|
|
|
9097
9307
|
const timeoutSeconds = getTimeout(config);
|
|
9098
9308
|
const timeout = timeoutSeconds > 0 ? timeoutSeconds * 1000 : DEFAULT_WITNESS_TIMEOUT_MS;
|
|
9099
9309
|
const workers = getWorkers(config);
|
|
9310
|
+
const memory = getMemory(config);
|
|
9100
9311
|
const results = [];
|
|
9101
9312
|
let idx = 0;
|
|
9102
9313
|
for (const w of witnesses) {
|
|
@@ -9183,7 +9394,7 @@ async function runWitnessVerification(config) {
|
|
|
9183
9394
|
console.log(color(` ${subsystem} ⊨ ${w.predicate}`, COLORS.gray));
|
|
9184
9395
|
let tlc;
|
|
9185
9396
|
try {
|
|
9186
|
-
tlc = await docker.runTLC(witnessTla, { workers, timeout });
|
|
9397
|
+
tlc = await docker.runTLC(witnessTla, { workers, timeout, memory });
|
|
9187
9398
|
} catch (err) {
|
|
9188
9399
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9189
9400
|
results.push({
|
|
@@ -9212,7 +9423,7 @@ async function runWitnessVerification(config) {
|
|
|
9212
9423
|
continue;
|
|
9213
9424
|
}
|
|
9214
9425
|
const verdict = witnessVerdict(polarity, reachable);
|
|
9215
|
-
const note = reachable ? undefined :
|
|
9426
|
+
const note = reachable ? undefined : witnessExplorationNote(tlc.stats);
|
|
9216
9427
|
results.push({
|
|
9217
9428
|
id,
|
|
9218
9429
|
status: verdict.status,
|
|
@@ -9278,6 +9489,53 @@ function displayEnsuresSummary(results) {
|
|
|
9278
9489
|
console.log(color(" A property only fires at states where its handler is enabled — raise", COLORS.gray));
|
|
9279
9490
|
console.log(color(" bounds.maxInFlight on a subsystem to reach multi-step-reachable handlers.", COLORS.gray));
|
|
9280
9491
|
}
|
|
9492
|
+
function stateColumn(stats) {
|
|
9493
|
+
if (!stats)
|
|
9494
|
+
return "? states";
|
|
9495
|
+
if (!stats.exhaustive)
|
|
9496
|
+
return `${stats.distinctStates}+ states`;
|
|
9497
|
+
return `${stats.distinctStates} states`;
|
|
9498
|
+
}
|
|
9499
|
+
function displayTLCStats(stats) {
|
|
9500
|
+
if (!stats) {
|
|
9501
|
+
console.log(color(" States explored: unknown (TLC printed no summary line)", COLORS.yellow));
|
|
9502
|
+
return;
|
|
9503
|
+
}
|
|
9504
|
+
console.log(color(` States explored: ${stats.statesGenerated}`, COLORS.gray));
|
|
9505
|
+
console.log(color(` Distinct states: ${stats.distinctStates}`, COLORS.gray));
|
|
9506
|
+
if (stats.searchDepth !== undefined) {
|
|
9507
|
+
console.log(color(` Search depth: ${stats.searchDepth}`, COLORS.gray));
|
|
9508
|
+
}
|
|
9509
|
+
if (!stats.exhaustive) {
|
|
9510
|
+
console.log(color(` ⚠ incomplete: ${stats.statesLeftOnQueue} states left on queue — the model was not exhausted`, COLORS.yellow));
|
|
9511
|
+
}
|
|
9512
|
+
if (stats.noActionEnabled) {
|
|
9513
|
+
console.log(color(` ⚠ no action was ever enabled: distinct states equal the ${stats.initialStates} initial states`, COLORS.yellow));
|
|
9514
|
+
}
|
|
9515
|
+
}
|
|
9516
|
+
function displayIncompleteSubsystems(results) {
|
|
9517
|
+
const notes = [];
|
|
9518
|
+
for (const r of results) {
|
|
9519
|
+
if (!r.success)
|
|
9520
|
+
continue;
|
|
9521
|
+
if (!r.stats) {
|
|
9522
|
+
notes.push(`${r.name}: TLC printed no summary line — state count unknown`);
|
|
9523
|
+
continue;
|
|
9524
|
+
}
|
|
9525
|
+
if (!r.stats.exhaustive) {
|
|
9526
|
+
notes.push(`${r.name}: ${r.stats.statesLeftOnQueue} states left on queue — not exhausted`);
|
|
9527
|
+
}
|
|
9528
|
+
if (r.stats.noActionEnabled) {
|
|
9529
|
+
notes.push(`${r.name}: no action was ever enabled — the ${r.stats.initialStates} states explored are the initial states`);
|
|
9530
|
+
}
|
|
9531
|
+
}
|
|
9532
|
+
if (notes.length === 0)
|
|
9533
|
+
return;
|
|
9534
|
+
console.log();
|
|
9535
|
+
for (const note of notes) {
|
|
9536
|
+
console.log(color(` ⚠ ${note}`, COLORS.yellow));
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9281
9539
|
function displayCompositionalReport(results, nonInterferenceValid) {
|
|
9282
9540
|
console.log(color(`Subsystem verification results:
|
|
9283
9541
|
`, COLORS.blue));
|
|
@@ -9286,10 +9544,11 @@ function displayCompositionalReport(results, nonInterferenceValid) {
|
|
|
9286
9544
|
const name = r.name.padEnd(20);
|
|
9287
9545
|
const handlers = `${r.handlerCount} handler${r.handlerCount === 1 ? "" : "s"}`;
|
|
9288
9546
|
const ensures = `${r.ensuresCount} ensures`;
|
|
9289
|
-
const states =
|
|
9547
|
+
const states = stateColumn(r.stats);
|
|
9290
9548
|
const time = `${r.elapsed.toFixed(1)}s`;
|
|
9291
9549
|
console.log(` ${status} ${name} ${handlers.padEnd(14)} ${ensures.padEnd(12)} ${states.padEnd(14)} ${time}`);
|
|
9292
9550
|
}
|
|
9551
|
+
displayIncompleteSubsystems(results);
|
|
9293
9552
|
displayEnsuresSummary(results);
|
|
9294
9553
|
console.log();
|
|
9295
9554
|
const nonIntLabel = nonInterferenceValid ? color("✓ verified (no cross-subsystem state writes)", COLORS.green) : color("⚠ violations detected", COLORS.yellow);
|
|
@@ -9387,7 +9646,7 @@ function findMeshSeedSpecDir() {
|
|
|
9387
9646
|
}
|
|
9388
9647
|
return null;
|
|
9389
9648
|
}
|
|
9390
|
-
async function runMeshSeedGuard(docker, specDir, config) {
|
|
9649
|
+
async function runMeshSeedGuard(docker, specDir, config, memory) {
|
|
9391
9650
|
if (getMeshDocIds(config).length === 0)
|
|
9392
9651
|
return;
|
|
9393
9652
|
const sourceDir = findMeshSeedSpecDir();
|
|
@@ -9400,7 +9659,7 @@ async function runMeshSeedGuard(docker, specDir, config) {
|
|
|
9400
9659
|
const baseCfg = fs4.readFileSync(path5.join(sourceDir, "MeshSeed.cfg"), "utf8");
|
|
9401
9660
|
fs4.writeFileSync(path5.join(specDir, "MeshSeed.cfg"), meshSeedCfg(baseCfg, { disableFix: fixDisabled }));
|
|
9402
9661
|
console.log(color(`⚙️ Running mesh seed-race guard (MeshSeed.tla, SeedDeterministic = ${fixDisabled ? "FALSE" : "TRUE"})...`, COLORS.blue));
|
|
9403
|
-
return docker.runTLC(path5.join(specDir, "MeshSeed.tla"), { workers: 1 });
|
|
9662
|
+
return docker.runTLC(path5.join(specDir, "MeshSeed.tla"), { workers: 1, memory });
|
|
9404
9663
|
}
|
|
9405
9664
|
async function setupDocker() {
|
|
9406
9665
|
await Promise.resolve().then(() => init_docker());
|
|
@@ -9460,8 +9719,7 @@ function displayVerificationResults(result, specDir) {
|
|
|
9460
9719
|
console.log(color(`✅ Verification passed!
|
|
9461
9720
|
`, COLORS.green));
|
|
9462
9721
|
console.log(color("Statistics:", COLORS.blue));
|
|
9463
|
-
|
|
9464
|
-
console.log(color(` Distinct states: ${result.stats?.distinctStates || 0}`, COLORS.gray));
|
|
9722
|
+
displayTLCStats(result.stats);
|
|
9465
9723
|
console.log();
|
|
9466
9724
|
return;
|
|
9467
9725
|
}
|
|
@@ -9574,4 +9832,4 @@ main().catch((error) => {
|
|
|
9574
9832
|
process.exit(1);
|
|
9575
9833
|
});
|
|
9576
9834
|
|
|
9577
|
-
//# debugId=
|
|
9835
|
+
//# debugId=10A3CEC0EDCA40C864756E2164756E21
|