@fairfox/polly 0.71.0 → 0.72.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/src/client/index.js +2 -2
- package/dist/src/client/index.js.map +2 -2
- package/dist/src/polly-ui/ActionInput.d.ts +10 -1
- package/dist/src/polly-ui/ActionSelect.d.ts +35 -0
- package/dist/src/polly-ui/Cluster.d.ts +35 -0
- package/dist/src/polly-ui/Code.d.ts +17 -0
- package/dist/src/polly-ui/Text.d.ts +31 -0
- package/dist/src/polly-ui/index.css +278 -185
- package/dist/src/polly-ui/index.d.ts +5 -1
- package/dist/src/polly-ui/index.js +480 -284
- package/dist/src/polly-ui/index.js.map +11 -6
- package/dist/src/polly-ui/internal/dispatch-action.d.ts +13 -0
- package/dist/src/polly-ui/markdown.js +3 -3
- package/dist/src/polly-ui/markdown.js.map +2 -2
- package/dist/src/polly-ui/styles.css +288 -185
- package/dist/tools/quality/src/cli.js +6 -2
- package/dist/tools/quality/src/cli.js.map +3 -3
- package/dist/tools/quality/src/index.js +6 -2
- package/dist/tools/quality/src/index.js.map +3 -3
- package/dist/tools/test/src/browser/run.js +75 -44
- package/dist/tools/test/src/browser/run.js.map +3 -3
- package/dist/tools/test/src/visual/index.js +24 -24
- package/dist/tools/test/src/visual/index.js.map +2 -2
- package/dist/tools/verify/src/cli.js +82 -51
- package/dist/tools/verify/src/cli.js.map +7 -6
- package/package.json +1 -1
|
@@ -89,10 +89,12 @@ function isNullable(configEntry) {
|
|
|
89
89
|
}
|
|
90
90
|
return false;
|
|
91
91
|
}
|
|
92
|
-
function checkUnmodeledFields(expression, configKeys, stateConfig, messageType, conditionType, location) {
|
|
92
|
+
function checkUnmodeledFields(expression, configKeys, stateConfig, messageType, conditionType, location, meshSignalNames) {
|
|
93
93
|
const warnings = [];
|
|
94
94
|
const refs = extractFieldRefs(expression);
|
|
95
95
|
for (const ref of refs) {
|
|
96
|
+
if (meshSignalNames.has(ref.split(".")[0] ?? ref))
|
|
97
|
+
continue;
|
|
96
98
|
if (!fieldInConfig(ref, configKeys, stateConfig)) {
|
|
97
99
|
warnings.push({
|
|
98
100
|
kind: "unmodeled_field",
|
|
@@ -195,7 +197,7 @@ function checkWeakPostconditions(expression, handler, messageType, conditionType
|
|
|
195
197
|
}
|
|
196
198
|
return [];
|
|
197
199
|
}
|
|
198
|
-
function validateExpressions(handlers, stateConfig) {
|
|
200
|
+
function validateExpressions(handlers, stateConfig, meshSignalNames = new Set) {
|
|
199
201
|
const warnings = [];
|
|
200
202
|
let validCount = 0;
|
|
201
203
|
const configKeys = new Set(Object.keys(stateConfig));
|
|
@@ -220,7 +222,7 @@ function validateExpressions(handlers, stateConfig) {
|
|
|
220
222
|
};
|
|
221
223
|
const condWarnings = [];
|
|
222
224
|
if (!isPayloadOnly) {
|
|
223
|
-
condWarnings.push(...checkUnmodeledFields(cond.expression, configKeys, stateConfig, handler.messageType, type, loc));
|
|
225
|
+
condWarnings.push(...checkUnmodeledFields(cond.expression, configKeys, stateConfig, handler.messageType, type, loc, meshSignalNames));
|
|
224
226
|
}
|
|
225
227
|
condWarnings.push(...checkUnsupportedMethods(cond.expression, configKeys, stateConfig, handler.messageType, type, loc));
|
|
226
228
|
condWarnings.push(...checkOptionalChaining(cond.expression, handler.messageType, type, loc));
|
|
@@ -1216,13 +1218,15 @@ var init_tla = __esm(() => {
|
|
|
1216
1218
|
fieldLines.push(`${this.sanitizeFieldName(fieldName)}: ${tlaType}`);
|
|
1217
1219
|
}
|
|
1218
1220
|
this.line(`\\* Document type for ${docId}`);
|
|
1219
|
-
this.line(`MeshDoc_${this.
|
|
1221
|
+
this.line(`MeshDoc_${this.sanitizeIdentifier(docId)} == [${fieldLines.join(", ")}]`);
|
|
1220
1222
|
this.line("");
|
|
1221
1223
|
}
|
|
1222
|
-
this.line("\\* Initial mesh-document values
|
|
1223
|
-
this.line("InitialMesh ==
|
|
1224
|
+
this.line("\\* Initial mesh-document values — a function keyed by docId");
|
|
1225
|
+
this.line("InitialMesh ==");
|
|
1226
|
+
this.indent++;
|
|
1227
|
+
this.line("[d \\in MeshDocs |->");
|
|
1224
1228
|
this.indent++;
|
|
1225
|
-
const
|
|
1229
|
+
const caseArms = [];
|
|
1226
1230
|
for (const docId of docIds) {
|
|
1227
1231
|
const fields = mesh[docId];
|
|
1228
1232
|
if (!fields)
|
|
@@ -1232,15 +1236,19 @@ var init_tla = __esm(() => {
|
|
|
1232
1236
|
const initVal = this.fieldConfigInitialValue(`${docId}_${fieldName}`, fieldConfig, config);
|
|
1233
1237
|
inner.push(`${this.sanitizeFieldName(fieldName)} |-> ${initVal}`);
|
|
1234
1238
|
}
|
|
1235
|
-
|
|
1239
|
+
caseArms.push(`d = "${docId}" -> [${inner.join(", ")}]`);
|
|
1236
1240
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1241
|
+
caseArms.forEach((arm, i) => {
|
|
1242
|
+
const prefix = i === 0 ? "CASE " : " [] ";
|
|
1243
|
+
const suffix = i === caseArms.length - 1 ? "]" : "";
|
|
1244
|
+
this.line(prefix + arm + suffix);
|
|
1239
1245
|
});
|
|
1240
|
-
this.indent
|
|
1241
|
-
this.line("]");
|
|
1246
|
+
this.indent -= 2;
|
|
1242
1247
|
this.line("");
|
|
1243
1248
|
}
|
|
1249
|
+
sanitizeIdentifier(key) {
|
|
1250
|
+
return key.replace(/[^A-Za-z0-9_]/g, "_");
|
|
1251
|
+
}
|
|
1244
1252
|
fieldConfigInitialValue(_path, fieldConfig, _config) {
|
|
1245
1253
|
const fc = fieldConfig;
|
|
1246
1254
|
if (fc["type"] === "boolean")
|
|
@@ -2049,7 +2057,7 @@ var init_tla = __esm(() => {
|
|
|
2049
2057
|
if (typeof expr !== "string")
|
|
2050
2058
|
return null;
|
|
2051
2059
|
const trimmed = expr.trim();
|
|
2052
|
-
const match = trimmed.match(/^(forAllPeers|somePeer)\s*\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*=>\s*([\s\S]+)\)\s*$/);
|
|
2060
|
+
const match = trimmed.match(/^(forAllPeers|somePeer)\s*\(\s*\(?\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)?\s*=>\s*([\s\S]+)\)\s*$/);
|
|
2053
2061
|
if (!match)
|
|
2054
2062
|
return null;
|
|
2055
2063
|
const fn = match[1];
|
|
@@ -3217,6 +3225,42 @@ init_expression_validator();
|
|
|
3217
3225
|
import * as fs4 from "node:fs";
|
|
3218
3226
|
import * as path4 from "node:path";
|
|
3219
3227
|
|
|
3228
|
+
// tools/verify/src/analysis/mesh-signal-warnings.ts
|
|
3229
|
+
function computeMeshOrPeerSignalFindings(analysis, declaredMeshDocs) {
|
|
3230
|
+
const signals = analysis.meshOrPeerSignals ?? [];
|
|
3231
|
+
if (signals.length === 0)
|
|
3232
|
+
return [];
|
|
3233
|
+
const unverified = signals.filter((s) => !(s.kind === "mesh" && declaredMeshDocs.has(s.key)));
|
|
3234
|
+
if (unverified.length === 0)
|
|
3235
|
+
return [];
|
|
3236
|
+
const findings = [];
|
|
3237
|
+
for (const handler of analysis.handlers) {
|
|
3238
|
+
const scan = (conditions, kind) => {
|
|
3239
|
+
for (const cond of conditions) {
|
|
3240
|
+
for (const sig of unverified) {
|
|
3241
|
+
const pattern = new RegExp(`\\b${sig.variableName}\\.value\\b`);
|
|
3242
|
+
if (pattern.test(cond.expression)) {
|
|
3243
|
+
findings.push({
|
|
3244
|
+
handler: handler.messageType,
|
|
3245
|
+
kind,
|
|
3246
|
+
expression: cond.expression,
|
|
3247
|
+
signalName: sig.variableName,
|
|
3248
|
+
signalKind: sig.kind,
|
|
3249
|
+
location: {
|
|
3250
|
+
file: handler.location?.file ?? sig.filePath,
|
|
3251
|
+
line: cond.location?.line ?? handler.location?.line ?? sig.line
|
|
3252
|
+
}
|
|
3253
|
+
});
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
};
|
|
3258
|
+
scan(handler.preconditions, "precondition");
|
|
3259
|
+
scan(handler.postconditions, "postcondition");
|
|
3260
|
+
}
|
|
3261
|
+
return findings;
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3220
3264
|
// tools/verify/src/config/types.ts
|
|
3221
3265
|
function isAdapterConfig(config) {
|
|
3222
3266
|
return "adapter" in config;
|
|
@@ -7657,7 +7701,8 @@ async function estimateCommand() {
|
|
|
7657
7701
|
const analysis = await runCodebaseAnalysis();
|
|
7658
7702
|
const typedConfig = config;
|
|
7659
7703
|
const typedAnalysis = analysis;
|
|
7660
|
-
const
|
|
7704
|
+
const meshSignalNames = new Set((typedAnalysis.meshOrPeerSignals ?? []).map((s) => s.variableName));
|
|
7705
|
+
const exprValidation = validateExpressions(typedAnalysis.handlers, typedConfig.state, meshSignalNames);
|
|
7661
7706
|
if (exprValidation.warnings.length > 0) {
|
|
7662
7707
|
displayExpressionWarnings(exprValidation);
|
|
7663
7708
|
}
|
|
@@ -7744,44 +7789,28 @@ function displayExpressionWarnings(result) {
|
|
|
7744
7789
|
console.log();
|
|
7745
7790
|
}
|
|
7746
7791
|
}
|
|
7747
|
-
function displayMeshOrPeerSignalWarnings(analysis) {
|
|
7748
|
-
const
|
|
7749
|
-
if (signals.length === 0)
|
|
7750
|
-
return;
|
|
7751
|
-
const findings = [];
|
|
7752
|
-
for (const handler of analysis.handlers) {
|
|
7753
|
-
const scanConditions = (conditions, kind) => {
|
|
7754
|
-
for (const cond of conditions) {
|
|
7755
|
-
for (const sig of signals) {
|
|
7756
|
-
const pattern = new RegExp(`\\b${sig.variableName}\\.value\\b`);
|
|
7757
|
-
if (pattern.test(cond.expression)) {
|
|
7758
|
-
findings.push({
|
|
7759
|
-
handler: handler.messageType,
|
|
7760
|
-
kind,
|
|
7761
|
-
expression: cond.expression,
|
|
7762
|
-
signalName: sig.variableName,
|
|
7763
|
-
signalKind: sig.kind,
|
|
7764
|
-
location: {
|
|
7765
|
-
file: handler.location.file,
|
|
7766
|
-
line: cond.location?.line ?? handler.location.line
|
|
7767
|
-
}
|
|
7768
|
-
});
|
|
7769
|
-
}
|
|
7770
|
-
}
|
|
7771
|
-
}
|
|
7772
|
-
};
|
|
7773
|
-
scanConditions(handler.preconditions, "precondition");
|
|
7774
|
-
scanConditions(handler.postconditions, "postcondition");
|
|
7775
|
-
}
|
|
7792
|
+
function displayMeshOrPeerSignalWarnings(analysis, declaredMeshDocs) {
|
|
7793
|
+
const findings = computeMeshOrPeerSignalFindings(analysis, declaredMeshDocs);
|
|
7776
7794
|
if (findings.length === 0)
|
|
7777
7795
|
return;
|
|
7796
|
+
const hasUndeclaredMesh = findings.some((f) => f.signalKind === "mesh");
|
|
7797
|
+
const hasPeer = findings.some((f) => f.signalKind === "peer");
|
|
7778
7798
|
console.log(color(`
|
|
7779
|
-
⚠️ ${findings.length} predicate(s) reference
|
|
7780
|
-
console.log(color("
|
|
7781
|
-
console.log(color(
|
|
7782
|
-
|
|
7799
|
+
⚠️ ${findings.length} predicate(s) reference an unverified $meshState/$peerState signal (polly#117):`, COLORS.yellow));
|
|
7800
|
+
console.log(color(" These signals are flattened to single-context local state, so a green run", COLORS.gray));
|
|
7801
|
+
console.log(color(" does NOT prove the claim holds across peers.", COLORS.gray));
|
|
7802
|
+
if (hasUndeclaredMesh) {
|
|
7803
|
+
console.log(color(" Fix ($meshState): declare the document key under `mesh: { ... }` in your", COLORS.gray));
|
|
7804
|
+
console.log(color(" verification config. Declared mesh docs are routed through the meshState", COLORS.gray));
|
|
7805
|
+
console.log(color(" namespace with a PropagateMeshOp action, so `forAllPeers(...)` claims are checked.", COLORS.gray));
|
|
7806
|
+
}
|
|
7807
|
+
if (hasPeer) {
|
|
7808
|
+
console.log(color(" $peerState signals have no cross-peer verification model yet.", COLORS.gray));
|
|
7809
|
+
}
|
|
7810
|
+
console.log();
|
|
7783
7811
|
for (const f of findings) {
|
|
7784
|
-
|
|
7812
|
+
const remedy = f.signalKind === "mesh" ? "not declared in config.mesh" : "$peerState — no cross-peer model";
|
|
7813
|
+
console.log(color(` ⚠ ${f.handler} ${f.kind}: ${f.signalName} (${remedy})`, COLORS.yellow));
|
|
7785
7814
|
console.log(color(` ${f.expression}`, COLORS.gray));
|
|
7786
7815
|
console.log(color(` at ${f.location.file}:${f.location.line}`, COLORS.gray));
|
|
7787
7816
|
console.log();
|
|
@@ -7851,11 +7880,13 @@ async function runFullVerification(configPath) {
|
|
|
7851
7880
|
const analysis = await runCodebaseAnalysis();
|
|
7852
7881
|
const typedConfig = config;
|
|
7853
7882
|
const typedAnalysis = analysis;
|
|
7854
|
-
const
|
|
7883
|
+
const meshSignalNames = new Set((typedAnalysis.meshOrPeerSignals ?? []).map((s) => s.variableName));
|
|
7884
|
+
const exprValidation = validateExpressions(typedAnalysis.handlers, typedConfig.state, meshSignalNames);
|
|
7855
7885
|
if (exprValidation.warnings.length > 0) {
|
|
7856
7886
|
displayExpressionWarnings(exprValidation);
|
|
7857
7887
|
}
|
|
7858
|
-
|
|
7888
|
+
const declaredMeshDocs = new Set(Object.keys(typedConfig.mesh ?? {}));
|
|
7889
|
+
displayMeshOrPeerSignalWarnings(typedAnalysis, declaredMeshDocs);
|
|
7859
7890
|
if (typedConfig.subsystems && Object.keys(typedConfig.subsystems).length > 0) {
|
|
7860
7891
|
await runSubsystemVerification(typedConfig, typedAnalysis);
|
|
7861
7892
|
return;
|
|
@@ -8256,4 +8287,4 @@ main().catch((error) => {
|
|
|
8256
8287
|
process.exit(1);
|
|
8257
8288
|
});
|
|
8258
8289
|
|
|
8259
|
-
//# debugId=
|
|
8290
|
+
//# debugId=15266168B7768F5064756E2164756E21
|