@fairfox/polly 0.9.0 → 0.10.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.
@@ -5446,17 +5446,21 @@ class HandlerExtractor {
5446
5446
  const handlers = [];
5447
5447
  const messageTypes = new Set;
5448
5448
  const invalidMessageTypes = new Set;
5449
+ const stateConstraints = [];
5449
5450
  const sourceFiles = this.project.getSourceFiles();
5450
5451
  this.debugLogSourceFiles(sourceFiles);
5451
5452
  for (const sourceFile of sourceFiles) {
5452
5453
  const fileHandlers = this.extractFromFile(sourceFile);
5453
5454
  handlers.push(...fileHandlers);
5454
5455
  this.categorizeHandlerMessageTypes(fileHandlers, messageTypes, invalidMessageTypes);
5456
+ const fileConstraints = this.extractStateConstraintsFromFile(sourceFile);
5457
+ stateConstraints.push(...fileConstraints);
5455
5458
  }
5456
5459
  this.debugLogExtractionResults(handlers.length, invalidMessageTypes.size);
5457
5460
  return {
5458
5461
  handlers,
5459
- messageTypes
5462
+ messageTypes,
5463
+ stateConstraints
5460
5464
  };
5461
5465
  }
5462
5466
  debugLogSourceFiles(sourceFiles) {
@@ -6288,6 +6292,84 @@ class HandlerExtractor {
6288
6292
  mutations.push({ field, line, afterAwait });
6289
6293
  }
6290
6294
  }
6295
+ extractStateConstraintsFromFile(sourceFile) {
6296
+ const constraints = [];
6297
+ const filePath = sourceFile.getFilePath();
6298
+ sourceFile.forEachDescendant((node) => {
6299
+ const nodeConstraints = this.recognizeStateConstraint(node, filePath);
6300
+ constraints.push(...nodeConstraints);
6301
+ });
6302
+ return constraints;
6303
+ }
6304
+ recognizeStateConstraint(node, filePath) {
6305
+ if (!Node4.isCallExpression(node)) {
6306
+ return [];
6307
+ }
6308
+ const expression = node.getExpression();
6309
+ if (!Node4.isIdentifier(expression)) {
6310
+ return [];
6311
+ }
6312
+ const functionName = expression.getText();
6313
+ if (functionName !== "$constraints") {
6314
+ return [];
6315
+ }
6316
+ const args = node.getArguments();
6317
+ if (args.length < 2) {
6318
+ return [];
6319
+ }
6320
+ const fieldArg = args[0];
6321
+ if (!Node4.isStringLiteral(fieldArg)) {
6322
+ return [];
6323
+ }
6324
+ const field = fieldArg.getLiteralValue();
6325
+ const constraintsArg = args[1];
6326
+ if (!Node4.isObjectLiteralExpression(constraintsArg)) {
6327
+ return [];
6328
+ }
6329
+ const results = [];
6330
+ for (const property of constraintsArg.getProperties()) {
6331
+ if (!Node4.isPropertyAssignment(property)) {
6332
+ continue;
6333
+ }
6334
+ const messageType = property.getName();
6335
+ const initializer = property.getInitializer();
6336
+ if (!initializer || !Node4.isObjectLiteralExpression(initializer)) {
6337
+ continue;
6338
+ }
6339
+ let requires;
6340
+ let ensures;
6341
+ let message;
6342
+ for (const constraintProp of initializer.getProperties()) {
6343
+ if (!Node4.isPropertyAssignment(constraintProp)) {
6344
+ continue;
6345
+ }
6346
+ const propName = constraintProp.getName();
6347
+ const propValue = constraintProp.getInitializer();
6348
+ if (!propValue) {
6349
+ continue;
6350
+ }
6351
+ if (propName === "requires" && Node4.isStringLiteral(propValue)) {
6352
+ requires = propValue.getLiteralValue();
6353
+ } else if (propName === "ensures" && Node4.isStringLiteral(propValue)) {
6354
+ ensures = propValue.getLiteralValue();
6355
+ } else if (propName === "message" && Node4.isStringLiteral(propValue)) {
6356
+ message = propValue.getLiteralValue();
6357
+ }
6358
+ }
6359
+ results.push({
6360
+ field,
6361
+ messageType,
6362
+ requires,
6363
+ ensures,
6364
+ message,
6365
+ location: {
6366
+ file: filePath,
6367
+ line: property.getStartLineNumber()
6368
+ }
6369
+ });
6370
+ }
6371
+ return results;
6372
+ }
6291
6373
  }
6292
6374
 
6293
6375
  // tools/analysis/src/extract/integrations.ts
@@ -6906,7 +6988,8 @@ class TypeExtractor {
6906
6988
  stateType,
6907
6989
  messageTypes: validMessageTypes,
6908
6990
  fields,
6909
- handlers: validHandlers
6991
+ handlers: validHandlers,
6992
+ stateConstraints: handlerAnalysis.stateConstraints
6910
6993
  };
6911
6994
  }
6912
6995
  extractHandlerAnalysis() {
@@ -8723,6 +8806,7 @@ ${generateVerificationSection(context)}
8723
8806
  - **Architecture**: How contexts, handlers, and message flows work in their project
8724
8807
  - **Translation**: How TypeScript code becomes TLA+ specifications
8725
8808
  - **Verification**: What properties are being verified and what they mean
8809
+ - **State-Level Constraints**: Using $constraints() to declare verification constraints alongside state
8726
8810
  - **Performance**: How to optimize verification speed and state space exploration
8727
8811
  - **Debugging**: Interpreting counterexamples and fixing violations
8728
8812
  - **Configuration**: Understanding maxInFlight, bounds, and other verification parameters
@@ -8733,6 +8817,11 @@ ${generateVerificationSection(context)}
8733
8817
  - If asked about verification performance, consider the verification config and results
8734
8818
  - Provide actionable suggestions, not just general advice
8735
8819
  - If you don't know something specific about their project, say so clearly
8820
+ - **State-Level Constraints**: Explain that $constraints() allows declaring verification constraints alongside state:
8821
+ - Constraints are automatically wired as preconditions in generated TLA+ handlers
8822
+ - Example: \`$constraints("loggedIn", { USER_LOGOUT: { requires: "state.loggedIn === true" } })\`
8823
+ - This eliminates duplication and creates a single source of truth for state invariants
8824
+ - Parser extracts constraints and adds them to all relevant message handlers automatically
8736
8825
 
8737
8826
  Begin by understanding their question and providing a clear, precise answer based on their project context.`;
8738
8827
  }
@@ -8917,6 +9006,20 @@ messages: {
8917
9006
  ### 2. Symmetry Reduction
8918
9007
  Treat identical or commutative message types as interchangeable to reduce state space.
8919
9008
 
9009
+ **Example**: For multiple independent symmetry groups (e.g., workers and replicas):
9010
+ \`\`\`typescript
9011
+ messages: {
9012
+ symmetry: [
9013
+ ['worker1', 'worker2', 'worker3'], // Workers are interchangeable
9014
+ ['replica1', 'replica2'], // Replicas are interchangeable
9015
+ ],
9016
+ }
9017
+ \`\`\`
9018
+
9019
+ Polly generates: \`Symmetry == Permutations(Set1) \\cup Permutations(Set2)\` which preserves
9020
+ independent group semantics (elements within each group are interchangeable, but not across groups).
9021
+ This is the standard TLA+ pattern used in Paxos and SimpleAllocator.
9022
+
8920
9023
  ### 3. Message-Specific Bounds
8921
9024
  Different maxInFlight per message type - auth messages should be sequential (1),
8922
9025
  but data queries might allow concurrency (3).
@@ -9181,4 +9284,4 @@ Goodbye!`);
9181
9284
  }
9182
9285
  main();
9183
9286
 
9184
- //# debugId=29E860FB6D1FD51164756E2164756E21
9287
+ //# debugId=8BD8EAFF3D45D43B64756E2164756E21