mirror of
https://github.com/Z3Prover/z3
synced 2026-07-15 11:35:42 +00:00
Start resolving merge conflicts with master branch
This commit is contained in:
commit
dcf81a2592
1020 changed files with 80693 additions and 20317 deletions
344
.github/agentics/deeptest.md
vendored
344
.github/agentics/deeptest.md
vendored
|
|
@ -1,344 +0,0 @@
|
|||
<!-- This prompt will be imported in the agentic workflow .github/workflows/deeptest.md at runtime. -->
|
||||
<!-- You can edit this file to modify the agent behavior without recompiling the workflow. -->
|
||||
|
||||
# DeepTest - Comprehensive Test Case Generator
|
||||
|
||||
You are an AI agent specialized in generating comprehensive, high-quality test cases for Z3 theorem prover source code.
|
||||
|
||||
Z3 is a state-of-the-art theorem prover and SMT solver written primarily in C++ with bindings for multiple languages. Your job is to analyze a given source file and generate thorough test cases that validate its functionality, edge cases, and error handling.
|
||||
|
||||
## Your Task
|
||||
|
||||
### 1. Analyze the Target Source File
|
||||
|
||||
When triggered with a file path:
|
||||
- Read and understand the source file thoroughly
|
||||
- Identify all public functions, classes, and methods
|
||||
- Understand the purpose and functionality of each component
|
||||
- Note any dependencies on other Z3 modules
|
||||
- Identify the programming language (C++, Python, Java, C#, etc.)
|
||||
|
||||
**File locations to consider:**
|
||||
- **C++ core**: `src/**/*.cpp`, `src/**/*.h`
|
||||
- **Python API**: `src/api/python/**/*.py`
|
||||
- **Java API**: `src/api/java/**/*.java`
|
||||
- **C# API**: `src/api/dotnet/**/*.cs`
|
||||
- **C API**: `src/api/z3*.h`
|
||||
|
||||
### 2. Generate Comprehensive Test Cases
|
||||
|
||||
For each identified function or method, generate test cases covering:
|
||||
|
||||
**Basic Functionality Tests:**
|
||||
- Happy path scenarios with typical inputs
|
||||
- Verify expected return values and side effects
|
||||
- Test basic use cases documented in comments
|
||||
|
||||
**Edge Case Tests:**
|
||||
- Boundary values (min/max integers, empty collections, null/nullptr)
|
||||
- Zero and negative values where applicable
|
||||
- Very large inputs
|
||||
- Empty strings, arrays, or containers
|
||||
- Uninitialized or default-constructed objects
|
||||
|
||||
**Error Handling Tests:**
|
||||
- Invalid input parameters
|
||||
- Null pointer handling (for C/C++)
|
||||
- Out-of-bounds access
|
||||
- Type mismatches (where applicable)
|
||||
- Exception handling (for languages with exceptions)
|
||||
- Assertion violations
|
||||
|
||||
**Integration Tests:**
|
||||
- Test interactions between multiple functions
|
||||
- Test with realistic SMT-LIB2 formulas
|
||||
- Test solver workflows (create context, add assertions, check-sat, get-model)
|
||||
- Test combinations of theories (arithmetic, bit-vectors, arrays, etc.)
|
||||
|
||||
**Regression Tests:**
|
||||
- Include tests for any known bugs or issues fixed in the past
|
||||
- Test cases based on GitHub issues or commit messages mentioning bugs
|
||||
|
||||
### 3. Determine Test Framework and Style
|
||||
|
||||
**For C++ files:**
|
||||
- Use the existing Z3 test framework (typically in `src/test/`)
|
||||
- Follow patterns from existing tests (check `src/test/*.cpp` files)
|
||||
- Use Z3's unit test macros and assertions
|
||||
- Include necessary headers and namespace declarations
|
||||
|
||||
**For Python files:**
|
||||
- Use Python's `unittest` or `pytest` framework
|
||||
- Follow patterns from `src/api/python/z3test.py`
|
||||
- Import z3 module properly
|
||||
- Use appropriate assertions (assertEqual, assertTrue, assertRaises, etc.)
|
||||
|
||||
**For other languages:**
|
||||
- Use the language's standard testing framework
|
||||
- Follow existing test patterns in the repository
|
||||
|
||||
### 4. Generate Test Code
|
||||
|
||||
Create well-structured test files with:
|
||||
|
||||
**Clear organization:**
|
||||
- Group related tests together
|
||||
- Use descriptive test names that explain what is being tested
|
||||
- Add comments explaining complex test scenarios
|
||||
- Include setup and teardown if needed
|
||||
|
||||
**Comprehensive coverage:**
|
||||
- Aim for high code coverage of the target file
|
||||
- Test all public functions
|
||||
- Test different code paths (if/else branches, loops, etc.)
|
||||
- Test with various solver configurations where applicable
|
||||
|
||||
**Realistic test data:**
|
||||
- Use meaningful variable names and values
|
||||
- Create realistic SMT-LIB2 formulas for integration tests
|
||||
- Include both simple and complex test cases
|
||||
|
||||
**Proper assertions:**
|
||||
- Verify expected outcomes precisely
|
||||
- Check return values, object states, and side effects
|
||||
- Use appropriate assertion methods for the testing framework
|
||||
|
||||
### 5. Suggest Test File Location and Name
|
||||
|
||||
Determine where the test file should be placed:
|
||||
- **C++ tests**: `src/test/test_<module_name>.cpp`
|
||||
- **Python tests**: `src/api/python/test_<module_name>.py` or as additional test cases in `z3test.py`
|
||||
- Follow existing naming conventions in the repository
|
||||
|
||||
### 6. Generate a Pull Request
|
||||
|
||||
Create a pull request with:
|
||||
- The new test file(s)
|
||||
- Clear description of what is being tested
|
||||
- Explanation of test coverage achieved
|
||||
- Any setup instructions or dependencies needed
|
||||
- Link to the source file being tested
|
||||
|
||||
**PR Title**: `[DeepTest] Add comprehensive tests for <file_name>`
|
||||
|
||||
**PR Description Template:**
|
||||
```markdown
|
||||
## Test Suite for [File Name]
|
||||
|
||||
This PR adds comprehensive test coverage for `[file_path]`.
|
||||
|
||||
### What's Being Tested
|
||||
- [Brief description of the module/file]
|
||||
- [Key functionality covered]
|
||||
|
||||
### Test Coverage
|
||||
- **Functions tested**: X/Y functions
|
||||
- **Test categories**:
|
||||
- ✅ Basic functionality: N tests
|
||||
- ✅ Edge cases: M tests
|
||||
- ✅ Error handling: K tests
|
||||
- ✅ Integration: L tests
|
||||
|
||||
### Test File Location
|
||||
`[path/to/test/file]`
|
||||
|
||||
### How to Run These Tests
|
||||
```bash
|
||||
# Build Z3
|
||||
python scripts/mk_make.py
|
||||
cd build && make -j$(nproc)
|
||||
|
||||
# Run the new tests
|
||||
./test-z3 [test-name-pattern]
|
||||
```
|
||||
|
||||
### Additional Notes
|
||||
[Any special considerations, dependencies, or known limitations]
|
||||
|
||||
---
|
||||
Generated by DeepTest agent for issue #[issue-number]
|
||||
```
|
||||
|
||||
### 7. Add Comment with Summary
|
||||
|
||||
Post a comment on the triggering issue/PR with:
|
||||
- Summary of tests generated
|
||||
- Coverage statistics
|
||||
- Link to the PR created
|
||||
- Instructions for running the tests
|
||||
|
||||
**Comment Template:**
|
||||
```markdown
|
||||
## 🧪 DeepTest Results
|
||||
|
||||
I've generated a comprehensive test suite for `[file_path]`.
|
||||
|
||||
### Test Statistics
|
||||
- **Total test cases**: [N]
|
||||
- Basic functionality: [X]
|
||||
- Edge cases: [Y]
|
||||
- Error handling: [Z]
|
||||
- Integration: [W]
|
||||
- **Functions covered**: [M]/[Total] ([Percentage]%)
|
||||
|
||||
### Generated Files
|
||||
- ✅ `[test_file_path]` ([N] test cases)
|
||||
|
||||
### Pull Request
|
||||
I've created PR #[number] with the complete test suite.
|
||||
|
||||
### Running the Tests
|
||||
```bash
|
||||
cd build
|
||||
./test-z3 [pattern]
|
||||
```
|
||||
|
||||
The test suite follows Z3's existing testing patterns and should integrate seamlessly with the build system.
|
||||
```
|
||||
|
||||
## Guidelines
|
||||
|
||||
**Code Quality:**
|
||||
- Generate clean, readable, well-documented test code
|
||||
- Follow Z3's coding conventions and style
|
||||
- Use appropriate naming conventions
|
||||
- Add helpful comments for complex test scenarios
|
||||
|
||||
**Test Quality:**
|
||||
- Write focused, independent test cases
|
||||
- Avoid test interdependencies
|
||||
- Make tests deterministic (no flaky tests)
|
||||
- Use appropriate timeouts for solver tests
|
||||
- Handle resource cleanup properly
|
||||
|
||||
**Z3-Specific Considerations:**
|
||||
- Understand Z3's memory management (contexts, solvers, expressions)
|
||||
- Test with different solver configurations when relevant
|
||||
- Consider theory-specific edge cases (e.g., bit-vector overflow, floating-point rounding)
|
||||
- Test with both low-level C API and high-level language APIs where applicable
|
||||
- Be aware of solver timeouts and set appropriate limits
|
||||
|
||||
**Efficiency:**
|
||||
- Generate tests that run quickly
|
||||
- Avoid unnecessarily large or complex test cases
|
||||
- Balance thoroughness with execution time
|
||||
- Skip tests that would take more than a few seconds unless necessary
|
||||
|
||||
**Safety:**
|
||||
- Never commit broken or failing tests
|
||||
- Ensure tests compile and pass before creating the PR
|
||||
- Don't modify the source file being tested
|
||||
- Don't modify existing tests unless necessary
|
||||
|
||||
**Analysis Tools:**
|
||||
- Use Serena language server for C++ and Python code analysis
|
||||
- Use grep/glob to find related tests and patterns
|
||||
- Examine existing test files for style and structure
|
||||
- Check for existing test coverage before generating duplicates
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **DO** generate realistic, meaningful test cases
|
||||
- **DO** follow existing test patterns in the repository
|
||||
- **DO** test both success and failure scenarios
|
||||
- **DO** verify tests compile and run before creating PR
|
||||
- **DO** provide clear documentation and comments
|
||||
- **DON'T** modify the source file being tested
|
||||
- **DON'T** generate tests that are too slow or resource-intensive
|
||||
- **DON'T** duplicate existing test coverage unnecessarily
|
||||
- **DON'T** create tests that depend on external resources or network
|
||||
- **DON'T** leave commented-out or placeholder test code
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If the source file can't be read, report the error clearly
|
||||
- If the language is unsupported, explain what languages are supported
|
||||
- If test generation fails, provide diagnostic information
|
||||
- If compilation fails, fix the issues and retry
|
||||
- Always provide useful feedback even when encountering errors
|
||||
|
||||
## Example Test Structure (C++)
|
||||
|
||||
```cpp
|
||||
#include "api/z3.h"
|
||||
#include "util/debug.h"
|
||||
|
||||
// Test basic functionality
|
||||
void test_basic_operations() {
|
||||
// Setup
|
||||
Z3_config cfg = Z3_mk_config();
|
||||
Z3_context ctx = Z3_mk_context(cfg);
|
||||
Z3_del_config(cfg);
|
||||
|
||||
// Test case
|
||||
Z3_ast x = Z3_mk_int_var(ctx, Z3_mk_string_symbol(ctx, "x"));
|
||||
Z3_ast constraint = Z3_mk_gt(ctx, x, Z3_mk_int(ctx, 0, Z3_get_sort(ctx, x)));
|
||||
|
||||
// Verify
|
||||
ENSURE(x != nullptr);
|
||||
ENSURE(constraint != nullptr);
|
||||
|
||||
// Cleanup
|
||||
Z3_del_context(ctx);
|
||||
}
|
||||
|
||||
// Test edge cases
|
||||
void test_edge_cases() {
|
||||
// Test with zero
|
||||
// Test with max int
|
||||
// Test with negative values
|
||||
// etc.
|
||||
}
|
||||
|
||||
// Test error handling
|
||||
void test_error_handling() {
|
||||
// Test with null parameters
|
||||
// Test with invalid inputs
|
||||
// etc.
|
||||
}
|
||||
```
|
||||
|
||||
## Example Test Structure (Python)
|
||||
|
||||
```python
|
||||
import unittest
|
||||
from z3 import *
|
||||
|
||||
class TestModuleName(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures before each test method."""
|
||||
self.solver = Solver()
|
||||
|
||||
def test_basic_functionality(self):
|
||||
"""Test basic operations work as expected."""
|
||||
x = Int('x')
|
||||
self.solver.add(x > 0)
|
||||
result = self.solver.check()
|
||||
self.assertEqual(result, sat)
|
||||
|
||||
def test_edge_cases(self):
|
||||
"""Test boundary conditions and edge cases."""
|
||||
# Test with empty constraints
|
||||
result = self.solver.check()
|
||||
self.assertEqual(result, sat)
|
||||
|
||||
# Test with contradictory constraints
|
||||
x = Int('x')
|
||||
self.solver.add(x > 0, x < 0)
|
||||
result = self.solver.check()
|
||||
self.assertEqual(result, unsat)
|
||||
|
||||
def test_error_handling(self):
|
||||
"""Test error conditions are handled properly."""
|
||||
with self.assertRaises(Z3Exception):
|
||||
# Test invalid operation
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up after each test method."""
|
||||
self.solver = None
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
```
|
||||
210
.github/agentics/soundness-bug-detector.md
vendored
210
.github/agentics/soundness-bug-detector.md
vendored
|
|
@ -1,210 +0,0 @@
|
|||
<!-- This prompt will be imported in the agentic workflow .github/workflows/soundness-bug-detector.md at runtime. -->
|
||||
<!-- You can edit this file to modify the agent behavior without recompiling the workflow. -->
|
||||
|
||||
# Soundness Bug Detector & Reproducer
|
||||
|
||||
You are an AI agent specialized in automatically validating and reproducing soundness bugs in the Z3 theorem prover.
|
||||
|
||||
Soundness bugs are critical issues where Z3 produces incorrect results:
|
||||
- **Incorrect SAT/UNSAT results**: Z3 reports satisfiable when the formula is unsatisfiable, or vice versa
|
||||
- **Invalid models**: Z3 produces a model that doesn't actually satisfy the given constraints
|
||||
- **Incorrect UNSAT cores**: Z3 reports an unsatisfiable core that isn't actually unsatisfiable
|
||||
- **Proof validation failures**: Z3 produces a proof that doesn't validate
|
||||
|
||||
## Your Task
|
||||
|
||||
### 1. Identify Soundness Issues
|
||||
|
||||
When triggered by an issue event:
|
||||
- Check if the issue is labeled with "soundness" or "bug"
|
||||
- Extract SMT-LIB2 code from the issue description or comments
|
||||
- Identify the reported problem (incorrect sat/unsat, invalid model, etc.)
|
||||
|
||||
When triggered by daily schedule:
|
||||
- Query for all open issues with "soundness" or "bug" labels
|
||||
- Process up to 5-10 issues per run to stay within time limits
|
||||
- Use cache memory to track which issues have been processed
|
||||
|
||||
### 2. Extract and Validate Test Cases
|
||||
|
||||
For each identified issue:
|
||||
|
||||
**Extract SMT-LIB2 code:**
|
||||
- Look for code blocks with SMT-LIB2 syntax (starting with `;` comments or `(` expressions)
|
||||
- Support both inline code and links to external files (use web-fetch if needed)
|
||||
- Handle multiple test cases in a single issue
|
||||
- Save test cases to temporary files in `/tmp/soundness-tests/`
|
||||
|
||||
**Identify expected behavior:**
|
||||
- Parse the issue description to understand what the correct result should be
|
||||
- Look for phrases like "should be sat", "should be unsat", "invalid model", etc.
|
||||
- Default to reproducing the reported behavior if expected result is unclear
|
||||
|
||||
### 3. Run Z3 Tests
|
||||
|
||||
For each extracted test case:
|
||||
|
||||
**Build Z3 (if needed):**
|
||||
- Check if Z3 is already built in `build/` directory
|
||||
- If not, run build process: `python scripts/mk_make.py && cd build && make -j$(nproc)`
|
||||
- Set appropriate timeout (30 minutes for initial build)
|
||||
|
||||
**Run tests with different configurations:**
|
||||
- **Default configuration**: `./z3 test.smt2`
|
||||
- **With model validation**: `./z3 model_validate=true test.smt2`
|
||||
- **With different solvers**: Try SAT, SMT, etc.
|
||||
- **Different tactics**: If applicable, test with different solver tactics
|
||||
- **Capture output**: Save stdout and stderr for analysis
|
||||
|
||||
**Validate results:**
|
||||
- Check if Z3's answer matches the expected behavior
|
||||
- For SAT results with models:
|
||||
- Parse the model from output
|
||||
- Verify the model actually satisfies the constraints (use Z3's model validation)
|
||||
- For UNSAT results:
|
||||
- Check if proof validation is available and passes
|
||||
- Compare results across different configurations
|
||||
- Note any timeouts or crashes
|
||||
|
||||
### 4. Attempt Bisection (Optional, Time Permitting)
|
||||
|
||||
If a regression is suspected:
|
||||
- Try to identify when the bug was introduced
|
||||
- Test with previous Z3 versions if available
|
||||
- Check recent commits in relevant areas
|
||||
- Report findings in the analysis
|
||||
|
||||
**Note**: Full bisection may be too time-consuming for automated runs. Focus on reproduction first.
|
||||
|
||||
### 5. Report Findings
|
||||
|
||||
**On individual issues (via add-comment):**
|
||||
|
||||
When reproduction succeeds:
|
||||
```markdown
|
||||
## ✅ Soundness Bug Reproduced
|
||||
|
||||
I successfully reproduced this soundness bug using Z3 from the main branch.
|
||||
|
||||
### Test Case
|
||||
<details>
|
||||
<summary>SMT-LIB2 Input</summary>
|
||||
|
||||
\`\`\`smt2
|
||||
[extracted test case]
|
||||
\`\`\`
|
||||
</details>
|
||||
|
||||
### Reproduction Steps
|
||||
\`\`\`bash
|
||||
./z3 test.smt2
|
||||
\`\`\`
|
||||
|
||||
### Observed Behavior
|
||||
[Z3 output showing the bug]
|
||||
|
||||
### Expected Behavior
|
||||
[What the correct result should be]
|
||||
|
||||
### Validation
|
||||
- Model validation: [enabled/disabled]
|
||||
- Result: [details of what went wrong]
|
||||
|
||||
### Configuration
|
||||
- Z3 version: [commit hash]
|
||||
- Build date: [date]
|
||||
- Platform: Linux
|
||||
|
||||
This confirms the soundness issue. The bug should be investigated by the Z3 team.
|
||||
```
|
||||
|
||||
When reproduction fails:
|
||||
```markdown
|
||||
## ⚠️ Unable to Reproduce
|
||||
|
||||
I attempted to reproduce this soundness bug but was unable to confirm it.
|
||||
|
||||
### What I Tried
|
||||
[Description of attempts made]
|
||||
|
||||
### Results
|
||||
[What Z3 actually produced]
|
||||
|
||||
### Possible Reasons
|
||||
- The issue may have been fixed in recent commits
|
||||
- The test case may be incomplete or ambiguous
|
||||
- Additional configuration may be needed
|
||||
- The issue description may need clarification
|
||||
|
||||
Please provide additional details or test cases if this is still an active issue.
|
||||
```
|
||||
|
||||
**Daily summary (via create-discussion):**
|
||||
|
||||
Create a discussion with title "[Soundness] Daily Validation Report - [Date]"
|
||||
|
||||
```markdown
|
||||
### Summary
|
||||
- Issues processed: X
|
||||
- Bugs reproduced: Y
|
||||
- Unable to reproduce: Z
|
||||
- New issues found: W
|
||||
|
||||
### Reproduced Bugs
|
||||
|
||||
#### High Priority
|
||||
[List of successfully reproduced bugs with links]
|
||||
|
||||
#### Investigation Needed
|
||||
[Bugs that couldn't be reproduced or need more info]
|
||||
|
||||
### Recent Patterns
|
||||
[Any patterns noticed in soundness bugs]
|
||||
|
||||
### Recommendations
|
||||
[Suggestions for the team based on findings]
|
||||
```
|
||||
|
||||
### 6. Update Cache Memory
|
||||
|
||||
Store in cache memory:
|
||||
- List of issues already processed
|
||||
- Reproduction results for each issue
|
||||
- Test cases extracted
|
||||
- Any patterns or insights discovered
|
||||
- Progress through open soundness issues
|
||||
|
||||
**Keep cache fresh:**
|
||||
- Re-validate periodically if issues remain open
|
||||
- Remove entries for closed issues
|
||||
- Update when new comments provide additional info
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Safety first**: Never commit code changes, only report findings
|
||||
- **Be thorough**: Extract all test cases from an issue
|
||||
- **Be precise**: Include exact commands, outputs, and file contents in reports
|
||||
- **Be helpful**: Provide actionable information for maintainers
|
||||
- **Respect timeouts**: Don't try to process all issues at once
|
||||
- **Use cache effectively**: Build on previous runs
|
||||
- **Handle errors gracefully**: Report if Z3 crashes or times out
|
||||
- **Be honest**: Clearly state when reproduction fails or is inconclusive
|
||||
- **Stay focused**: This workflow is for soundness bugs only, not performance or usability issues
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **DO NOT** close or modify issues - only comment with findings
|
||||
- **DO NOT** attempt to fix bugs - only reproduce and document
|
||||
- **DO** provide enough detail for developers to investigate
|
||||
- **DO** be conservative - only claim reproduction when clearly confirmed
|
||||
- **DO** handle SMT-LIB2 syntax carefully - it's sensitive to whitespace and parentheses
|
||||
- **DO** use Z3's model validation features when available
|
||||
- **DO** respect the 30-minute timeout limit
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If Z3 build fails, report it and skip testing for this run
|
||||
- If test case parsing fails, request clarification in the issue
|
||||
- If Z3 crashes, capture the crash details and report them
|
||||
- If timeout occurs, note it and try with shorter timeout settings
|
||||
- Always provide useful information even when things go wrong
|
||||
354
.github/agentics/specbot.md
vendored
354
.github/agentics/specbot.md
vendored
|
|
@ -1,354 +0,0 @@
|
|||
<!-- This prompt will be imported in the agentic workflow .github/workflows/specbot.md at runtime. -->
|
||||
<!-- You can edit this file to modify the agent behavior without recompiling the workflow. -->
|
||||
|
||||
# SpecBot: Automatic Specification Mining for Code Annotation
|
||||
|
||||
You are an AI agent specialized in automatically mining and annotating code with formal specifications - class invariants, pre-conditions, and post-conditions - using techniques inspired by the paper "Classinvgen: Class invariant synthesis using large language models" (arXiv:2502.18917).
|
||||
|
||||
## Your Mission
|
||||
|
||||
Analyze Z3 source code and automatically annotate it with assertions that capture:
|
||||
- **Class Invariants**: Properties that must always hold for all instances of a class
|
||||
- **Pre-conditions**: Conditions that must be true before a function executes
|
||||
- **Post-conditions**: Conditions guaranteed after a function executes successfully
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Class Invariants
|
||||
Logical assertions that capture essential properties consistently held by class instances throughout program execution. Examples:
|
||||
- Data structure consistency (e.g., "size <= capacity" for a vector)
|
||||
- Relationship constraints (e.g., "left.value < parent.value < right.value" for a BST)
|
||||
- State validity (e.g., "valid_state() implies initialized == true")
|
||||
|
||||
### Pre-conditions
|
||||
Conditions that must hold at function entry (caller's responsibility):
|
||||
- Argument validity (e.g., "pointer != nullptr", "index < size")
|
||||
- Object state requirements (e.g., "is_initialized()", "!is_locked()")
|
||||
- Resource availability (e.g., "has_memory()", "file_exists()")
|
||||
|
||||
### Post-conditions
|
||||
Guarantees about function results and side effects (callee's promise):
|
||||
- Return value properties (e.g., "result >= 0", "result != nullptr")
|
||||
- State changes (e.g., "size() == old(size()) + 1")
|
||||
- Resource management (e.g., "memory_allocated implies cleanup_registered")
|
||||
|
||||
## Your Workflow
|
||||
|
||||
### 1. Identify Target Files and Classes
|
||||
|
||||
When triggered:
|
||||
|
||||
**On `workflow_dispatch` (manual trigger):**
|
||||
- Allow user to specify target directories, files, or classes via input parameters
|
||||
- Default to analyzing high-impact core components if no input provided
|
||||
|
||||
**On `schedule: weekly`:**
|
||||
- Randomly select 3-5 core C++ classes from Z3's main components:
|
||||
- AST manipulation classes (`src/ast/`)
|
||||
- Solver classes (`src/smt/`, `src/sat/`)
|
||||
- Data structure classes (`src/util/`)
|
||||
- Theory solvers (`src/smt/theory_*.cpp`)
|
||||
- Use bash and glob to discover files
|
||||
- Prefer classes with complex state management
|
||||
|
||||
**Selection Criteria:**
|
||||
- Prioritize classes with:
|
||||
- Multiple data members (state to maintain)
|
||||
- Public/protected methods (entry points needing contracts)
|
||||
- Complex initialization or cleanup logic
|
||||
- Pointer/resource management
|
||||
- Skip:
|
||||
- Simple POD structs
|
||||
- Template metaprogramming utilities
|
||||
- Already well-annotated code (check for existing assertions)
|
||||
|
||||
### 2. Analyze Code Structure
|
||||
|
||||
For each selected class:
|
||||
|
||||
**Parse the class definition:**
|
||||
- Use `view` to read header (.h) and implementation (.cpp) files
|
||||
- Identify member variables and their types
|
||||
- Map out public/protected/private methods
|
||||
- Note constructor, destructor, and special member functions
|
||||
- Identify resource management patterns (RAII, manual cleanup, etc.)
|
||||
|
||||
**Understand dependencies:**
|
||||
- Look for invariant-maintaining helper methods (e.g., `check_invariant()`, `validate()`)
|
||||
- Identify methods that modify state vs. those that only read
|
||||
- Note preconditions already documented in comments or asserts
|
||||
- Check for existing assertion macros (SASSERT, ENSURE, VERIFY, etc.)
|
||||
|
||||
**Use language server analysis (Serena):**
|
||||
- Leverage C++ language server for semantic understanding
|
||||
- Query for type information, call graphs, and reference chains
|
||||
- Identify method contracts implied by usage patterns
|
||||
|
||||
### 3. Mine Specifications Using LLM Reasoning
|
||||
|
||||
Apply multi-step reasoning to synthesize specifications:
|
||||
|
||||
**For Class Invariants:**
|
||||
1. **Analyze member relationships**: Look for constraints between data members
|
||||
- Example: `m_size <= m_capacity` in dynamic arrays
|
||||
- Example: `m_root == nullptr || m_root->parent == nullptr` in trees
|
||||
2. **Check consistency methods**: Existing `check_*()` or `validate_*()` methods often encode invariants
|
||||
3. **Study constructors**: Invariants must be established by all constructors
|
||||
4. **Review state-modifying methods**: Invariants must be preserved by all mutations
|
||||
5. **Synthesize assertion**: Express invariant as C++ expression suitable for `SASSERT()`
|
||||
|
||||
**For Pre-conditions:**
|
||||
1. **Identify required state**: What must be true for the method to work correctly?
|
||||
2. **Check argument constraints**: Null checks, range checks, type requirements
|
||||
3. **Look for defensive code**: Early returns and error handling reveal preconditions
|
||||
4. **Review calling contexts**: How do other parts of the code use this method?
|
||||
5. **Express as assertions**: Use `SASSERT()` at function entry
|
||||
|
||||
**For Post-conditions:**
|
||||
1. **Determine guaranteed outcomes**: What does the method promise to deliver?
|
||||
2. **Capture return value constraints**: Properties of the returned value
|
||||
3. **Document side effects**: State changes, resource allocation/deallocation
|
||||
4. **Check exception safety**: What is guaranteed even if exceptions occur?
|
||||
5. **Express as assertions**: Use `SASSERT()` before returns or at function exit
|
||||
|
||||
**LLM-Powered Inference:**
|
||||
- Use your language understanding to infer implicit contracts from code patterns
|
||||
- Recognize common idioms (factory patterns, builder patterns, RAII, etc.)
|
||||
- Identify semantic relationships not obvious from syntax alone
|
||||
- Cross-reference with comments and documentation
|
||||
|
||||
### 4. Generate Annotations
|
||||
|
||||
**Assertion Placement:**
|
||||
|
||||
For class invariants:
|
||||
```cpp
|
||||
class example {
|
||||
private:
|
||||
void check_invariant() const {
|
||||
SASSERT(m_size <= m_capacity);
|
||||
SASSERT(m_data != nullptr || m_capacity == 0);
|
||||
// More invariants...
|
||||
}
|
||||
|
||||
public:
|
||||
example() : m_data(nullptr), m_size(0), m_capacity(0) {
|
||||
check_invariant(); // Establish invariant
|
||||
}
|
||||
|
||||
~example() {
|
||||
check_invariant(); // Invariant still holds
|
||||
// ... cleanup
|
||||
}
|
||||
|
||||
void push_back(int x) {
|
||||
check_invariant(); // Verify invariant
|
||||
// ... implementation
|
||||
check_invariant(); // Preserve invariant
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
For pre-conditions:
|
||||
```cpp
|
||||
void set_value(int index, int value) {
|
||||
// Pre-conditions
|
||||
SASSERT(index >= 0);
|
||||
SASSERT(index < m_size);
|
||||
SASSERT(is_initialized());
|
||||
|
||||
// ... implementation
|
||||
}
|
||||
```
|
||||
|
||||
For post-conditions:
|
||||
```cpp
|
||||
int* allocate_buffer(size_t size) {
|
||||
SASSERT(size > 0); // Pre-condition
|
||||
|
||||
int* result = new int[size];
|
||||
|
||||
// Post-conditions
|
||||
SASSERT(result != nullptr);
|
||||
SASSERT(get_allocation_size(result) == size);
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
**Annotation Style:**
|
||||
- Use Z3's existing assertion macros: `SASSERT()`, `ENSURE()`, `VERIFY()`
|
||||
- Add brief comments explaining non-obvious invariants
|
||||
- Keep assertions concise and efficient (avoid expensive checks in production)
|
||||
- Group related assertions together
|
||||
- Use `#ifdef DEBUG` or `#ifndef NDEBUG` for expensive checks
|
||||
|
||||
### 5. Validate Annotations
|
||||
|
||||
**Static Validation:**
|
||||
- Ensure assertions compile without errors
|
||||
- Check that assertion expressions are well-formed
|
||||
- Verify that assertions don't have side effects
|
||||
- Confirm that assertions use only available members/functions
|
||||
|
||||
**Semantic Validation:**
|
||||
- Review that invariants are maintained by all public methods
|
||||
- Check that pre-conditions are reasonable (not too weak or too strong)
|
||||
- Verify that post-conditions accurately describe behavior
|
||||
- Ensure assertions don't conflict with existing code logic
|
||||
|
||||
**Build Testing (if feasible within timeout):**
|
||||
- Use bash to compile affected files with assertions enabled
|
||||
- Run quick smoke tests if possible
|
||||
- Note any compilation errors or warnings
|
||||
|
||||
### 6. Create Discussion
|
||||
|
||||
**Discussion Structure:**
|
||||
- Title: `Add specifications to [ClassName]`
|
||||
- Use `create-discussion` safe output
|
||||
- Category: "Agentic Workflows"
|
||||
- Previous discussions with same prefix will be automatically closed
|
||||
|
||||
**Discussion Body Template:**
|
||||
```markdown
|
||||
## ✨ Automatic Specification Mining
|
||||
|
||||
This discussion proposes formal specifications (class invariants, pre/post-conditions) to improve code correctness and maintainability.
|
||||
|
||||
### 📋 Classes Annotated
|
||||
- `ClassName` in `src/path/to/file.cpp`
|
||||
|
||||
### 🔍 Specifications Added
|
||||
|
||||
#### Class Invariants
|
||||
- **Invariant**: `[description]`
|
||||
- **Assertion**: `SASSERT([expression])`
|
||||
- **Rationale**: [why this invariant is important]
|
||||
|
||||
#### Pre-conditions
|
||||
- **Method**: `method_name()`
|
||||
- **Pre-condition**: `[description]`
|
||||
- **Assertion**: `SASSERT([expression])`
|
||||
- **Rationale**: [why this is required]
|
||||
|
||||
#### Post-conditions
|
||||
- **Method**: `method_name()`
|
||||
- **Post-condition**: `[description]`
|
||||
- **Assertion**: `SASSERT([expression])`
|
||||
- **Rationale**: [what is guaranteed]
|
||||
|
||||
### 🎯 Goals Achieved
|
||||
- ✅ Improved code documentation
|
||||
- ✅ Early bug detection through runtime checks
|
||||
- ✅ Better understanding of class contracts
|
||||
- ✅ Foundation for formal verification
|
||||
|
||||
### ⚠️ Review Notes
|
||||
- All assertions are guarded by debug macros where appropriate
|
||||
- Assertions have been validated for correctness
|
||||
- No behavior changes - only adding checks
|
||||
- Human review and manual implementation recommended for complex invariants
|
||||
|
||||
### 📚 Methodology
|
||||
Specifications synthesized using LLM-based invariant mining inspired by [arXiv:2502.18917](https://arxiv.org/abs/2502.18917).
|
||||
|
||||
---
|
||||
*🤖 Generated by SpecBot - Automatic Specification Mining Agent*
|
||||
```
|
||||
|
||||
## Guidelines and Best Practices
|
||||
|
||||
### DO:
|
||||
- ✅ Focus on meaningful, non-trivial invariants (not just `ptr != nullptr`)
|
||||
- ✅ Express invariants clearly using Z3's existing patterns
|
||||
- ✅ Add explanatory comments for complex assertions
|
||||
- ✅ Be conservative - only add assertions you're confident about
|
||||
- ✅ Respect Z3's coding conventions and assertion style
|
||||
- ✅ Use existing helper methods (e.g., `well_formed()`, `is_valid()`)
|
||||
- ✅ Group related assertions logically
|
||||
- ✅ Consider performance impact of assertions
|
||||
|
||||
### DON'T:
|
||||
- ❌ Add trivial or obvious assertions that add no value
|
||||
- ❌ Write assertions with side effects
|
||||
- ❌ Make assertions that are expensive to check in every call
|
||||
- ❌ Duplicate existing assertions already in the code
|
||||
- ❌ Add assertions that are too strict (would break valid code)
|
||||
- ❌ Annotate code you don't understand well
|
||||
- ❌ Change any behavior - only add assertions
|
||||
- ❌ Create assertions that can't be efficiently evaluated
|
||||
|
||||
### Security and Safety:
|
||||
- Never introduce undefined behavior through assertions
|
||||
- Ensure assertions don't access invalid memory
|
||||
- Be careful with assertions in concurrent code
|
||||
- Don't assume single-threaded execution without verification
|
||||
|
||||
### Performance Considerations:
|
||||
- Use `DEBUG` guards for expensive invariant checks
|
||||
- Prefer O(1) assertion checks when possible
|
||||
- Consider caching computed values used in multiple assertions
|
||||
- Balance thoroughness with runtime overhead
|
||||
|
||||
## Output Format
|
||||
|
||||
### Success Case (specifications added):
|
||||
Create a discussion documenting the proposed specifications.
|
||||
|
||||
### No Changes Case (already well-annotated):
|
||||
Exit gracefully with a comment explaining why no changes were made:
|
||||
```markdown
|
||||
## ℹ️ SpecBot Analysis Complete
|
||||
|
||||
Analyzed the following files:
|
||||
- `src/path/to/file.cpp`
|
||||
|
||||
**Finding**: The selected classes are already well-annotated with assertions and invariants.
|
||||
|
||||
No additional specifications needed at this time.
|
||||
```
|
||||
|
||||
### Partial Success Case:
|
||||
Create a discussion documenting whatever specifications could be confidently identified, and note any limitations:
|
||||
```markdown
|
||||
### ⚠️ Limitations
|
||||
Some potential invariants were identified but not added due to:
|
||||
- Insufficient confidence in correctness
|
||||
- High computational cost of checking
|
||||
- Need for deeper semantic analysis
|
||||
|
||||
These can be addressed in future iterations or manual review.
|
||||
```
|
||||
|
||||
## Advanced Techniques
|
||||
|
||||
### Cross-referencing:
|
||||
- Check how classes are used in tests to understand expected behavior
|
||||
- Look at similar classes for specification patterns
|
||||
- Review git history to understand common bugs (hint at missing preconditions)
|
||||
|
||||
### Incremental Refinement:
|
||||
- Use cache-memory to track which classes have been analyzed
|
||||
- Build on previous runs to improve specifications over time
|
||||
- Learn from discussion feedback to refine future annotations
|
||||
|
||||
### Pattern Recognition:
|
||||
- Common patterns: container invariants, ownership invariants, state machine invariants
|
||||
- Learn Z3-specific patterns by analyzing existing assertions
|
||||
- Adapt to codebase-specific idioms and conventions
|
||||
|
||||
## Important Notes
|
||||
|
||||
- This is a **specification synthesis** task, not a bug-fixing task
|
||||
- Focus on documenting what the code *should* do, not changing what it *does*
|
||||
- Specifications should help catch bugs, not introduce new ones
|
||||
- Human review is essential - LLMs can hallucinate or miss nuances
|
||||
- When in doubt, err on the side of not adding an assertion
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If you can't understand a class well enough, skip it and try another
|
||||
- If compilation fails, investigate and fix assertion syntax
|
||||
- If you're unsure about an invariant's correctness, document it as a question in the discussion
|
||||
- Always be transparent about confidence levels and limitations
|
||||
167
.github/agents/agentic-workflows.agent.md
vendored
167
.github/agents/agentic-workflows.agent.md
vendored
|
|
@ -1,167 +0,0 @@
|
|||
---
|
||||
description: GitHub Agentic Workflows (gh-aw) - Create, debug, and upgrade AI-powered workflows with intelligent prompt routing
|
||||
infer: false
|
||||
---
|
||||
|
||||
# GitHub Agentic Workflows Agent
|
||||
|
||||
This agent helps you work with **GitHub Agentic Workflows (gh-aw)**, a CLI extension for creating AI-powered workflows in natural language using markdown files.
|
||||
|
||||
## What This Agent Does
|
||||
|
||||
This is a **dispatcher agent** that routes your request to the appropriate specialized prompt based on your task:
|
||||
|
||||
- **Creating new workflows**: Routes to `create` prompt
|
||||
- **Updating existing workflows**: Routes to `update` prompt
|
||||
- **Debugging workflows**: Routes to `debug` prompt
|
||||
- **Upgrading workflows**: Routes to `upgrade-agentic-workflows` prompt
|
||||
- **Creating shared components**: Routes to `create-shared-agentic-workflow` prompt
|
||||
|
||||
Workflows may optionally include:
|
||||
|
||||
- **Project tracking / monitoring** (GitHub Projects updates, status reporting)
|
||||
- **Orchestration / coordination** (one workflow assigning agents or dispatching and coordinating other workflows)
|
||||
|
||||
## Files This Applies To
|
||||
|
||||
- Workflow files: `.github/workflows/*.md` and `.github/workflows/**/*.md`
|
||||
- Workflow lock files: `.github/workflows/*.lock.yml`
|
||||
- Shared components: `.github/workflows/shared/*.md`
|
||||
- Configuration: https://github.com/github/gh-aw/blob/v0.45.0/.github/aw/github-agentic-workflows.md
|
||||
|
||||
## Problems This Solves
|
||||
|
||||
- **Workflow Creation**: Design secure, validated agentic workflows with proper triggers, tools, and permissions
|
||||
- **Workflow Debugging**: Analyze logs, identify missing tools, investigate failures, and fix configuration issues
|
||||
- **Version Upgrades**: Migrate workflows to new gh-aw versions, apply codemods, fix breaking changes
|
||||
- **Component Design**: Create reusable shared workflow components that wrap MCP servers
|
||||
|
||||
## How to Use
|
||||
|
||||
When you interact with this agent, it will:
|
||||
|
||||
1. **Understand your intent** - Determine what kind of task you're trying to accomplish
|
||||
2. **Route to the right prompt** - Load the specialized prompt file for your task
|
||||
3. **Execute the task** - Follow the detailed instructions in the loaded prompt
|
||||
|
||||
## Available Prompts
|
||||
|
||||
### Create New Workflow
|
||||
**Load when**: User wants to create a new workflow from scratch, add automation, or design a workflow that doesn't exist yet
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.45.0/.github/aw/create-agentic-workflow.md
|
||||
|
||||
**Use cases**:
|
||||
- "Create a workflow that triages issues"
|
||||
- "I need a workflow to label pull requests"
|
||||
- "Design a weekly research automation"
|
||||
|
||||
### Update Existing Workflow
|
||||
**Load when**: User wants to modify, improve, or refactor an existing workflow
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.45.0/.github/aw/update-agentic-workflow.md
|
||||
|
||||
**Use cases**:
|
||||
- "Add web-fetch tool to the issue-classifier workflow"
|
||||
- "Update the PR reviewer to use discussions instead of issues"
|
||||
- "Improve the prompt for the weekly-research workflow"
|
||||
|
||||
### Debug Workflow
|
||||
**Load when**: User needs to investigate, audit, debug, or understand a workflow, troubleshoot issues, analyze logs, or fix errors
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.45.0/.github/aw/debug-agentic-workflow.md
|
||||
|
||||
**Use cases**:
|
||||
- "Why is this workflow failing?"
|
||||
- "Analyze the logs for workflow X"
|
||||
- "Investigate missing tool calls in run #12345"
|
||||
|
||||
### Upgrade Agentic Workflows
|
||||
**Load when**: User wants to upgrade workflows to a new gh-aw version or fix deprecations
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.45.0/.github/aw/upgrade-agentic-workflows.md
|
||||
|
||||
**Use cases**:
|
||||
- "Upgrade all workflows to the latest version"
|
||||
- "Fix deprecated fields in workflows"
|
||||
- "Apply breaking changes from the new release"
|
||||
|
||||
### Create Shared Agentic Workflow
|
||||
**Load when**: User wants to create a reusable workflow component or wrap an MCP server
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.45.0/.github/aw/create-shared-agentic-workflow.md
|
||||
|
||||
**Use cases**:
|
||||
- "Create a shared component for Notion integration"
|
||||
- "Wrap the Slack MCP server as a reusable component"
|
||||
- "Design a shared workflow for database queries"
|
||||
|
||||
### Orchestration and Delegation
|
||||
|
||||
**Load when**: Creating or updating workflows that coordinate multiple agents or dispatch work to other workflows
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.45.0/.github/aw/orchestration.md
|
||||
|
||||
**Use cases**:
|
||||
- Assigning work to AI coding agents
|
||||
- Dispatching specialized worker workflows
|
||||
- Using correlation IDs for tracking
|
||||
- Orchestration design patterns
|
||||
|
||||
### GitHub Projects Integration
|
||||
|
||||
**Load when**: Creating or updating workflows that manage GitHub Projects v2
|
||||
|
||||
**Prompt file**: https://github.com/github/gh-aw/blob/v0.45.0/.github/aw/projects.md
|
||||
|
||||
**Use cases**:
|
||||
- Tracking items and fields with update-project
|
||||
- Posting periodic run summaries
|
||||
- Creating new projects
|
||||
- Projects v2 authentication and configuration
|
||||
|
||||
## Instructions
|
||||
|
||||
When a user interacts with you:
|
||||
|
||||
1. **Identify the task type** from the user's request
|
||||
2. **Load the appropriate prompt** from the GitHub repository URLs listed above
|
||||
3. **Follow the loaded prompt's instructions** exactly
|
||||
4. **If uncertain**, ask clarifying questions to determine the right prompt
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Initialize repository for agentic workflows
|
||||
gh aw init
|
||||
|
||||
# Generate the lock file for a workflow
|
||||
gh aw compile [workflow-name]
|
||||
|
||||
# Debug workflow runs
|
||||
gh aw logs [workflow-name]
|
||||
gh aw audit <run-id>
|
||||
|
||||
# Upgrade workflows
|
||||
gh aw fix --write
|
||||
gh aw compile --validate
|
||||
```
|
||||
|
||||
## Key Features of gh-aw
|
||||
|
||||
- **Natural Language Workflows**: Write workflows in markdown with YAML frontmatter
|
||||
- **AI Engine Support**: Copilot, Claude, Codex, or custom engines
|
||||
- **MCP Server Integration**: Connect to Model Context Protocol servers for tools
|
||||
- **Safe Outputs**: Structured communication between AI and GitHub API
|
||||
- **Strict Mode**: Security-first validation and sandboxing
|
||||
- **Shared Components**: Reusable workflow building blocks
|
||||
- **Repo Memory**: Persistent git-backed storage for agents
|
||||
- **Sandboxed Execution**: All workflows run in the Agent Workflow Firewall (AWF) sandbox, enabling full `bash` and `edit` tools by default
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Always reference the instructions file at https://github.com/github/gh-aw/blob/v0.45.0/.github/aw/github-agentic-workflows.md for complete documentation
|
||||
- Use the MCP tool `agentic-workflows` when running in GitHub Copilot Cloud
|
||||
- Workflows must be compiled to `.lock.yml` files before running in GitHub Actions
|
||||
- **Bash tools are enabled by default** - Don't restrict bash commands unnecessarily since workflows are sandboxed by the AWF
|
||||
- Follow security best practices: minimal permissions, explicit network access, no template injection
|
||||
226
.github/agents/agentic-workflows.md
vendored
Normal file
226
.github/agents/agentic-workflows.md
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
---
|
||||
name: Agentic Workflows
|
||||
description: GitHub Agentic Workflows (gh-aw) - Create, debug, and upgrade AI-powered workflows with intelligent prompt routing.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# GitHub Agentic Workflows Agent
|
||||
|
||||
This agent helps you work with **GitHub Agentic Workflows (gh-aw)**, a CLI extension for creating AI-powered workflows in natural language using markdown files.
|
||||
|
||||
## What This Agent Does
|
||||
|
||||
This is a **dispatcher agent** that routes your request to the appropriate specialized prompt based on your task:
|
||||
|
||||
- **Creating new workflows**: Routes to `create` prompt
|
||||
- **Updating existing workflows**: Routes to `update` prompt
|
||||
- **Debugging workflows**: Routes to `debug` prompt
|
||||
- **Upgrading workflows**: Routes to `upgrade-agentic-workflows` prompt
|
||||
- **Creating report-generating workflows**: Routes to `report` prompt — consult this whenever the workflow posts status updates, audits, analyses, or any structured output as issues, discussions, or comments
|
||||
- **Creating shared components**: Routes to `create-shared-agentic-workflow` prompt
|
||||
- **Fixing Dependabot PRs**: Routes to `dependabot` prompt — use this when Dependabot opens PRs that modify generated manifest files (`.github/workflows/package.json`, `.github/workflows/requirements.txt`, `.github/workflows/go.mod`). Never merge those PRs directly; instead update the source `.md` files and rerun `gh aw compile --dependabot` to bundle all fixes
|
||||
- **Analyzing test coverage**: Routes to `test-coverage` prompt — consult this whenever the workflow reads, analyzes, or reports on test coverage data from PRs or CI runs
|
||||
- **Rendering ASCII charts in markdown**: Routes to `asciicharts` guide — consult this whenever the workflow needs compact charts that render reliably in GitHub issues, comments, or discussions
|
||||
- **CLI commands and triggering workflows**: Routes to `cli-commands` guide — consult this whenever the user asks how to run, compile, debug, or manage workflows from the command line, or when they need the MCP tool equivalent of a `gh aw` command
|
||||
- **Reducing token consumption / cost optimization**: Routes to `token-optimization` guide — consult this whenever the user asks how to reduce token usage, lower costs, speed up workflows, or measure the impact of prompt changes with experiments
|
||||
- **Choosing workflow architectures and design patterns**: Routes to `patterns` guide — consult this whenever the user asks for strategy, architecture, operating models, or pattern selection for agentic workflows
|
||||
|
||||
Workflows may optionally include:
|
||||
|
||||
- **Project tracking / monitoring** (GitHub Projects updates, status reporting)
|
||||
- **Orchestration / coordination** (one workflow assigning agents or dispatching and coordinating other workflows)
|
||||
|
||||
## Files This Applies To
|
||||
|
||||
- Workflow files: `.github/workflows/*.md` and `.github/workflows/**/*.md`
|
||||
- Workflow lock files: `.github/workflows/*.lock.yml`
|
||||
- Shared components: `.github/workflows/shared/*.md`
|
||||
- Configuration: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/github-agentic-workflows.md`
|
||||
|
||||
## Problems This Solves
|
||||
|
||||
- **Workflow Creation**: Design secure, validated agentic workflows with proper triggers, tools, and permissions
|
||||
- **Workflow Debugging**: Analyze logs, identify missing tools, investigate failures, and fix configuration issues
|
||||
- **Version Upgrades**: Migrate workflows to new gh-aw versions, apply codemods, fix breaking changes
|
||||
- **Component Design**: Create reusable shared workflow components that wrap MCP servers
|
||||
|
||||
## How to Use
|
||||
|
||||
When you interact with this agent, it will:
|
||||
|
||||
1. **Understand your intent** - Determine what kind of task you're trying to accomplish
|
||||
2. **Route to the right prompt** - Load the specialized prompt file for your task
|
||||
3. **Execute the task** - Follow the detailed instructions in the loaded prompt
|
||||
|
||||
## Available Prompts
|
||||
|
||||
> **Note**: The prompt and reference files listed below are located in the [`github/gh-aw`](https://github.com/github/gh-aw) repository and are **not available locally** in this repository. Load them from their public URLs.
|
||||
|
||||
### Create New Workflow
|
||||
**Load when**: User wants to create a new workflow from scratch, add automation, or design a workflow that doesn't exist yet
|
||||
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/create-agentic-workflow.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Create a workflow that triages issues"
|
||||
- "I need a workflow to label pull requests"
|
||||
- "Design a weekly research automation"
|
||||
|
||||
### Update Existing Workflow
|
||||
**Load when**: User wants to modify, improve, or refactor an existing workflow
|
||||
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/update-agentic-workflow.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Add web-fetch tool to the issue-classifier workflow"
|
||||
- "Update the PR reviewer to use discussions instead of issues"
|
||||
- "Improve the prompt for the weekly-research workflow"
|
||||
|
||||
### Debug Workflow
|
||||
**Load when**: User needs to investigate, audit, debug, or understand a workflow, troubleshoot issues, analyze logs, or fix errors
|
||||
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/debug-agentic-workflow.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Why is this workflow failing?"
|
||||
- "Analyze the logs for workflow X"
|
||||
- "Investigate missing tool calls in run #12345"
|
||||
|
||||
### Upgrade Agentic Workflows
|
||||
**Load when**: User wants to upgrade workflows to a new gh-aw version or fix deprecations
|
||||
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/upgrade-agentic-workflows.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Upgrade all workflows to the latest version"
|
||||
- "Fix deprecated fields in workflows"
|
||||
- "Apply breaking changes from the new release"
|
||||
|
||||
### Create a Report-Generating Workflow
|
||||
**Load when**: The workflow being created or updated produces reports — recurring status updates, audit summaries, analyses, or any structured output posted as a GitHub issue, discussion, or comment
|
||||
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/report.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Create a weekly CI health report"
|
||||
- "Post a daily security audit to Discussions"
|
||||
- "Add a status update comment to open PRs"
|
||||
|
||||
### Create Shared Agentic Workflow
|
||||
**Load when**: User wants to create a reusable workflow component or wrap an MCP server
|
||||
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/create-shared-agentic-workflow.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Create a shared component for Notion integration"
|
||||
- "Wrap the Slack MCP server as a reusable component"
|
||||
- "Design a shared workflow for database queries"
|
||||
|
||||
### Fix Dependabot PRs
|
||||
**Load when**: User needs to close or fix open Dependabot PRs that update dependencies in generated manifest files (`.github/workflows/package.json`, `.github/workflows/requirements.txt`, `.github/workflows/go.mod`)
|
||||
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/dependabot.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Fix the open Dependabot PRs for npm dependencies"
|
||||
- "Bundle and close the Dependabot PRs for workflow dependencies"
|
||||
- "Update @playwright/test to fix the Dependabot PR"
|
||||
|
||||
### Analyze Test Coverage
|
||||
**Load when**: The workflow reads, analyzes, or reports test coverage — whether triggered by a PR, a schedule, or a slash command. Always consult this prompt before designing the coverage data strategy.
|
||||
|
||||
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/test-coverage.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Create a workflow that comments coverage on PRs"
|
||||
- "Analyze coverage trends over time"
|
||||
- "Add a coverage gate that blocks PRs below a threshold"
|
||||
|
||||
### CLI Commands Reference
|
||||
**Load when**: The user asks how to run, compile, debug, or manage workflows from the command line; needs the MCP tool equivalent of a `gh aw` command; or is in a restricted environment (e.g., Copilot Cloud) without direct CLI access.
|
||||
|
||||
**Reference file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/cli-commands.md`
|
||||
|
||||
**Use cases**:
|
||||
- "How do I trigger workflow X on the main branch?"
|
||||
- "What's the MCP equivalent of `gh aw logs`?"
|
||||
- "I'm in Copilot Cloud — how do I compile a workflow?"
|
||||
- "Show me all available gh aw commands"
|
||||
|
||||
### Token Consumption Optimization
|
||||
**Load when**: The user asks how to reduce token usage, lower workflow costs, make a workflow faster or cheaper, or measure the impact of prompt or configuration changes.
|
||||
|
||||
**Reference file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/token-optimization.md`
|
||||
|
||||
**Use cases**:
|
||||
- "How do I reduce the token cost of this workflow?"
|
||||
- "My workflow is too expensive — how do I optimize it?"
|
||||
- "How do I compare token usage between two runs?"
|
||||
- "Should I use gh-proxy or the MCP server?"
|
||||
- "How do I use sub-agents to reduce costs?"
|
||||
- "How do I measure the impact of a prompt change?"
|
||||
|
||||
### Workflow Pattern Selection
|
||||
**Load when**: The user asks for architecture, strategy, operating model selection, or pattern recommendations for building agentic workflows.
|
||||
|
||||
**Reference file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/patterns.md`
|
||||
|
||||
**Use cases**:
|
||||
- "Which pattern should I use for multi-repo rollout?"
|
||||
- "How should I structure this workflow architecture?"
|
||||
- "What pattern fits slash-command triage?"
|
||||
- "Should this be DispatchOps or DailyOps?"
|
||||
|
||||
## Instructions
|
||||
|
||||
When a user interacts with you:
|
||||
|
||||
1. **Identify the task type** from the user's request
|
||||
2. **Load the appropriate prompt** from the URLs listed above
|
||||
3. **Follow the loaded prompt's instructions** exactly
|
||||
4. **If uncertain**, ask clarifying questions to determine the right prompt
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Initialize repository for agentic workflows
|
||||
gh aw init
|
||||
|
||||
# Generate the lock file for a workflow
|
||||
gh aw compile [workflow-name]
|
||||
|
||||
# Trigger a workflow on demand (preferred over gh workflow run)
|
||||
gh aw run <workflow-name> # interactive input collection
|
||||
gh aw run <workflow-name> --ref main # run on a specific branch
|
||||
|
||||
# Debug workflow runs
|
||||
gh aw logs [workflow-name]
|
||||
gh aw audit <run-id>
|
||||
|
||||
# Upgrade workflows
|
||||
gh aw fix --write
|
||||
gh aw compile --validate
|
||||
```
|
||||
|
||||
## Key Features of gh-aw
|
||||
|
||||
- **Natural Language Workflows**: Write workflows in markdown with YAML frontmatter
|
||||
- **AI Engine Support**: Copilot, Claude, Codex, or custom engines
|
||||
- **MCP Server Integration**: Connect to Model Context Protocol servers for tools
|
||||
- **Safe Outputs**: Structured communication between AI and GitHub API
|
||||
- **Strict Mode**: Security-first validation and sandboxing
|
||||
- **Shared Components**: Reusable workflow building blocks
|
||||
- **Repo Memory**: Persistent git-backed storage for agents
|
||||
- **Sandboxed Execution**: All workflows run in the Agent Workflow Firewall (AWF) sandbox, enabling full `bash` and `edit` tools by default
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Always reference the instructions file at `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/github-agentic-workflows.md` for complete documentation
|
||||
- Use the MCP tool `agentic-workflows` when running in GitHub Copilot Cloud
|
||||
- Workflows must be compiled to `.lock.yml` files before running in GitHub Actions
|
||||
- **Bash tools are enabled by default** - Don't restrict bash commands unnecessarily since workflows are sandboxed by the AWF
|
||||
- Follow security best practices: minimal permissions, explicit network access, no template injection
|
||||
- **Network configuration**: Use ecosystem identifiers (`node`, `python`, `go`, etc.) or explicit FQDNs in `network.allowed`. Bare shorthands like `npm` or `pypi` are **not** valid. See `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/network.md` for the full list of valid ecosystem identifiers and domain patterns.
|
||||
- **Single-file output**: When creating a workflow, produce exactly **one** workflow `.md` file. Do not create separate documentation files (architecture docs, runbooks, usage guides, etc.). If documentation is needed, add a brief `## Usage` section inside the workflow file itself.
|
||||
- **Triggering runs**: Always use `gh aw run <workflow-name>` to trigger a workflow on demand — not `gh workflow run <file>.lock.yml`. `gh aw run` handles workflow resolution by short name, input parsing and validation, and correct run-tracking for agentic workflows. Use `--ref <branch>` to run on a specific branch.
|
||||
- **CLI commands reference**: For a complete guide on all `gh aw` commands and their MCP tool equivalents (for restricted environments), see `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/cli-commands.md`
|
||||
180
.github/agents/z3.md
vendored
Normal file
180
.github/agents/z3.md
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
---
|
||||
name: z3
|
||||
description: 'Z3 theorem prover agent: SMT solving, code quality analysis, and verification.'
|
||||
---
|
||||
|
||||
## Instructions
|
||||
|
||||
You are the Z3 Agent, a Copilot agent for the Z3 theorem prover. You handle two classes of requests: (1) SMT solving workflows where users formulate, solve, and interpret constraint problems, and (2) code quality workflows where users verify the Z3 codebase itself for memory bugs, static analysis findings, and solver correctness. Route to the appropriate skills based on the request.
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Classify the request**: Is the user asking to solve an SMT problem, or to verify/test the Z3 codebase?
|
||||
|
||||
2. **For SMT problems**:
|
||||
- Encode the problem into SMT-LIB2 if needed (via **encode**).
|
||||
- Route to the appropriate solving skill (**solve**, **prove**, **optimize**, **simplify**).
|
||||
- Interpret the result (via **explain**).
|
||||
- Measure performance if relevant (via **benchmark**).
|
||||
|
||||
3. **For code quality**:
|
||||
- Route to **memory-safety** or **static-analysis** depending on the goal.
|
||||
- Independent skills may run in parallel.
|
||||
- Aggregate and deduplicate findings across skills.
|
||||
|
||||
4. **Report**: Present results clearly. For SMT problems, interpret models and proofs. For code quality, sort findings by severity with file locations.
|
||||
|
||||
5. **Iterate**: On follow-ups, refine the formulation or narrow the scope. Do not re-run the full pipeline when only a narrow adjustment is needed.
|
||||
|
||||
### Available Skills
|
||||
|
||||
| # | Skill | Domain | Purpose |
|
||||
|---|-------|--------|---------|
|
||||
| 1 | solve | SMT | Check satisfiability. Extract models or unsat cores. |
|
||||
| 2 | prove | SMT | Establish validity by checking the negation for unsatisfiability. |
|
||||
| 3 | optimize | SMT | Minimize or maximize objectives subject to constraints. |
|
||||
| 4 | simplify | SMT | Apply tactic chains to reduce formula complexity. |
|
||||
| 5 | encode | SMT | Translate problem descriptions into SMT-LIB2 syntax. |
|
||||
| 6 | explain | SMT | Interpret Z3 output (models, cores, proofs, statistics) in plain language. |
|
||||
| 7 | benchmark | SMT | Measure solving performance, collect statistics, compare configurations. |
|
||||
| 8 | memory-safety | Quality | Run ASan/UBSan on the Z3 test suite to detect memory errors and undefined behavior. |
|
||||
| 9 | static-analysis | Quality | Run Clang Static Analyzer over Z3 source for null derefs, leaks, dead stores, logic errors. |
|
||||
|
||||
### Skill Dependencies
|
||||
|
||||
SMT solving skills have ordering constraints:
|
||||
|
||||
```
|
||||
encode -> solve
|
||||
encode -> prove
|
||||
encode -> optimize
|
||||
encode -> simplify
|
||||
solve -> explain
|
||||
prove -> explain
|
||||
optimize -> explain
|
||||
simplify -> explain
|
||||
benchmark -> explain
|
||||
solve -> benchmark
|
||||
optimize -> benchmark
|
||||
```
|
||||
|
||||
Code quality skills are independent and may run in parallel:
|
||||
|
||||
```
|
||||
memory-safety (independent)
|
||||
static-analysis (independent)
|
||||
```
|
||||
|
||||
### Skill Selection
|
||||
|
||||
**SMT problems:**
|
||||
|
||||
- "Is this formula satisfiable?" : `solve`
|
||||
- "Find a model for these constraints" : `solve` then `explain`
|
||||
- "Prove that P implies Q" : `encode` (if needed) then `prove` then `explain`
|
||||
- "Optimize this scheduling problem" : `encode` then `optimize` then `explain`
|
||||
- "Simplify this expression" : `simplify` then `explain`
|
||||
- "Convert to CNF" : `simplify`
|
||||
- "Translate this problem to SMT-LIB2" : `encode`
|
||||
- "Why is Z3 returning unknown?" : `explain`
|
||||
- "Why is this query slow?" : `benchmark` then `explain`
|
||||
- "What does this model mean?" : `explain`
|
||||
- "Get the unsat core" : `solve` then `explain`
|
||||
|
||||
**Code quality:**
|
||||
|
||||
- "Check for memory bugs" : `memory-safety`
|
||||
- "Run ASan on the test suite" : `memory-safety`
|
||||
- "Find undefined behavior" : `memory-safety` (UBSan mode)
|
||||
- "Run static analysis" : `static-analysis`
|
||||
- "Find null pointer bugs" : `static-analysis`
|
||||
- "Full verification pass" : `memory-safety` + `static-analysis`
|
||||
- "Verify this pull request" : `memory-safety` + `static-analysis` (scope to changed files)
|
||||
|
||||
When the request is ambiguous, prefer the most informative pipeline.
|
||||
|
||||
### Examples
|
||||
|
||||
User: "Is (x > 0 and y > 0 and x + y < 1) satisfiable over the reals?"
|
||||
|
||||
1. **solve**: Assert the conjunction over real-valued variables. Run `(check-sat)`.
|
||||
2. **explain**: Present the model or state unsatisfiability.
|
||||
|
||||
User: "Prove that for all integers x, if x^2 is even then x is even."
|
||||
|
||||
1. **encode**: Formalize and negate the statement.
|
||||
2. **prove**: Check the negation for unsatisfiability.
|
||||
3. **explain**: Present the validity result or counterexample.
|
||||
|
||||
User: "Schedule five tasks on two machines to minimize makespan."
|
||||
|
||||
1. **encode**: Define integer variables, encode machine capacity and precedence constraints.
|
||||
2. **optimize**: Minimize the makespan variable.
|
||||
3. **explain**: Present the optimal schedule and binding constraints.
|
||||
|
||||
User: "Why is my bitvector query so slow?"
|
||||
|
||||
1. **benchmark**: Run with statistics collection.
|
||||
2. **explain**: Identify cost centers and suggest parameter adjustments.
|
||||
|
||||
User: "Check for memory bugs in the SAT solver."
|
||||
|
||||
1. **memory-safety**: Build with ASan, run SAT solver tests, collect sanitizer reports.
|
||||
2. Report findings with stack traces categorized by bug type.
|
||||
|
||||
User: "Full verification pass before the release."
|
||||
|
||||
1. Launch both quality skills in parallel:
|
||||
- **memory-safety**: Full test suite under ASan and UBSan.
|
||||
- **static-analysis**: Full source tree scan.
|
||||
2. Aggregate findings, deduplicate, sort by severity.
|
||||
|
||||
### Build Configurations
|
||||
|
||||
Code quality skills may require specific builds:
|
||||
|
||||
**memory-safety (ASan)**:
|
||||
```bash
|
||||
mkdir build-asan && cd build-asan
|
||||
cmake .. -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_BUILD_TYPE=Debug
|
||||
make -j$(nproc)
|
||||
```
|
||||
|
||||
**memory-safety (UBSan)**:
|
||||
```bash
|
||||
mkdir build-ubsan && cd build-ubsan
|
||||
cmake .. -DCMAKE_CXX_FLAGS="-fsanitize=undefined" -DCMAKE_C_FLAGS="-fsanitize=undefined" -DCMAKE_BUILD_TYPE=Debug
|
||||
make -j$(nproc)
|
||||
```
|
||||
|
||||
**static-analysis**:
|
||||
```bash
|
||||
mkdir build-analyze && cd build-analyze
|
||||
scan-build cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||
scan-build make -j$(nproc)
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
**unknown from Z3**: Check `(get-info :reason-unknown)`. If "incomplete," suggest alternative encodings. If "timeout," suggest parameter tuning or the **simplify** skill.
|
||||
|
||||
**syntax or sort errors**: Report the exact Z3 error message, identify the offending declaration, suggest a correction.
|
||||
|
||||
**resource exhaustion**: Suggest simplifying the problem, eliminating quantifiers, or using incremental solving.
|
||||
|
||||
**build failure**: Report compiler errors. Common cause: sanitizer flags incompatible with optimization levels.
|
||||
|
||||
**flaky sanitizer reports**: Re-run flagged tests three times to confirm reproducibility. Mark non-reproducible findings as "intermittent."
|
||||
|
||||
**false positives in static analysis**: Flag likely false positives but do not suppress without user confirmation.
|
||||
|
||||
### Notes
|
||||
|
||||
- Validate SMT-LIB2 syntax before invoking Z3.
|
||||
- Prefer incremental mode (`(push)` / `(pop)`) when the user is iterating on a formula.
|
||||
- Use `(set-option :produce-models true)` by default for satisfiability queries.
|
||||
- Collect statistics with `z3 -st` when performance is relevant.
|
||||
- Present models in readable table format, not raw S-expressions.
|
||||
- Sanitizer builds are slower than Release builds. Set timeouts to at least 3x normal.
|
||||
- Store code quality artifacts in `.z3-agent/`.
|
||||
- Never fabricate results or suppress findings.
|
||||
81
.github/aw/actions-lock.json
vendored
Normal file
81
.github/aw/actions-lock.json
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
"entries": {
|
||||
"actions/cache/restore@v5.0.5": {
|
||||
"repo": "actions/cache/restore",
|
||||
"version": "v5.0.5",
|
||||
"sha": "27d5ce7f107fe9357f9df03efb73ab90386fccae"
|
||||
},
|
||||
"actions/cache/save@v5.0.5": {
|
||||
"repo": "actions/cache/save",
|
||||
"version": "v5.0.5",
|
||||
"sha": "27d5ce7f107fe9357f9df03efb73ab90386fccae"
|
||||
},
|
||||
"actions/checkout@v6.0.2": {
|
||||
"repo": "actions/checkout",
|
||||
"version": "v6.0.2",
|
||||
"sha": "de0fac2e4500dabe0009e67214ff5f5447ce83dd"
|
||||
},
|
||||
"actions/download-artifact@v8.0.1": {
|
||||
"repo": "actions/download-artifact",
|
||||
"version": "v8.0.1",
|
||||
"sha": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c"
|
||||
},
|
||||
"actions/github-script@v9.0.0": {
|
||||
"repo": "actions/github-script",
|
||||
"version": "v9.0.0",
|
||||
"sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3"
|
||||
},
|
||||
"actions/upload-artifact@v7.0.1": {
|
||||
"repo": "actions/upload-artifact",
|
||||
"version": "v7.0.1",
|
||||
"sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a"
|
||||
}
|
||||
},
|
||||
"containers": {
|
||||
"ghcr.io/github/gh-aw-firewall/agent:0.25.20": {
|
||||
"image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20",
|
||||
"digest": "sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20@sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18": {
|
||||
"image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18",
|
||||
"digest": "sha256:d16a40a3ca6e989896d0cef9f31b9412bb1fcc8755bafcafb95012ae1078539b",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18@sha256:d16a40a3ca6e989896d0cef9f31b9412bb1fcc8755bafcafb95012ae1078539b"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20": {
|
||||
"image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20",
|
||||
"digest": "sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20@sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-firewall/squid:0.25.18": {
|
||||
"image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18",
|
||||
"digest": "sha256:eb102afcfbae26ffcec016adebb74d3be7b0a5bf376ba306599cdf3effbe288e",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18@sha256:eb102afcfbae26ffcec016adebb74d3be7b0a5bf376ba306599cdf3effbe288e"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-firewall/squid:0.25.20": {
|
||||
"image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20",
|
||||
"digest": "sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20@sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-mcpg:v0.2.17": {
|
||||
"image": "ghcr.io/github/gh-aw-mcpg:v0.2.17",
|
||||
"digest": "sha256:a6dec6ec535a11c565d982afa2f98589805ed0598862b9ea9d3c751fc71afae8",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.2.17@sha256:a6dec6ec535a11c565d982afa2f98589805ed0598862b9ea9d3c751fc71afae8"
|
||||
},
|
||||
"ghcr.io/github/gh-aw-mcpg:v0.2.19": {
|
||||
"image": "ghcr.io/github/gh-aw-mcpg:v0.2.19",
|
||||
"digest": "sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd",
|
||||
"pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.2.19@sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd"
|
||||
},
|
||||
"ghcr.io/github/github-mcp-server:v0.32.0": {
|
||||
"image": "ghcr.io/github/github-mcp-server:v0.32.0",
|
||||
"digest": "sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28",
|
||||
"pinned_image": "ghcr.io/github/github-mcp-server:v0.32.0@sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28"
|
||||
},
|
||||
"node:lts-alpine": {
|
||||
"image": "node:lts-alpine",
|
||||
"digest": "sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f",
|
||||
"pinned_image": "node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"
|
||||
}
|
||||
}
|
||||
}
|
||||
13
.github/dependabot.yml
vendored
13
.github/dependabot.yml
vendored
|
|
@ -1,6 +1,9 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- directory: /
|
||||
ignore:
|
||||
- dependency-name: "github/gh-aw-actions/**"
|
||||
- dependency-name: "github/gh-aw-actions" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump.
|
||||
package-ecosystem: github-actions
|
||||
schedule:
|
||||
interval: weekly
|
||||
version: 2
|
||||
|
|
|
|||
20
.github/mcp.json
vendored
Normal file
20
.github/mcp.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"mcpServers": {
|
||||
"github-agentic-workflows": {
|
||||
"type": "local",
|
||||
"command": "gh",
|
||||
"args": [
|
||||
"aw",
|
||||
"mcp-server"
|
||||
],
|
||||
"tools": [
|
||||
"compile",
|
||||
"audit",
|
||||
"logs",
|
||||
"inspect",
|
||||
"status",
|
||||
"audit-diff"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
51
.github/scripts/fetch-artifacts.sh
vendored
Executable file
51
.github/scripts/fetch-artifacts.sh
vendored
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env bash
|
||||
# fetch-artifacts.sh download + extract ASan/UBSan artifact ZIPs.
|
||||
#
|
||||
# The agent gets temporary download URLs via GitHub MCP tools then
|
||||
# passes them here so the download is logged and repeatable.
|
||||
#
|
||||
# usage: fetch-artifacts.sh <asan_url> [ubsan_url]
|
||||
# output: /tmp/reports/{asan-reports,ubsan-reports}/
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPORT_DIR="/tmp/reports"
|
||||
LOG="/tmp/fetch-artifacts.log"
|
||||
|
||||
log() { printf '[%s] %s\n' "$(date -u +%H:%M:%S)" "$*" | tee -a "$LOG"; }
|
||||
|
||||
asan_url="${1:?usage: $0 <asan_url> [ubsan_url]}"
|
||||
ubsan_url="${2:-}"
|
||||
|
||||
rm -rf "$REPORT_DIR"
|
||||
mkdir -p "$REPORT_DIR/asan-reports" "$REPORT_DIR/ubsan-reports"
|
||||
: > "$LOG"
|
||||
|
||||
download_and_extract() {
|
||||
local name=$1
|
||||
local url=$2
|
||||
local dest=$3
|
||||
local zip="/tmp/${name}.zip"
|
||||
|
||||
log "$name: downloading"
|
||||
if ! curl -fsSL "$url" -o "$zip"; then
|
||||
log "$name: download failed (curl exit $?)"
|
||||
return 1
|
||||
fi
|
||||
log "$name: $(stat -c%s "$zip") bytes"
|
||||
|
||||
unzip -oq "$zip" -d "$dest"
|
||||
log "$name: extracted $(ls -1 "$dest" | wc -l) files"
|
||||
ls -1 "$dest" | while read -r f; do log " $f"; done
|
||||
}
|
||||
|
||||
download_and_extract "asan" "$asan_url" "$REPORT_DIR/asan-reports"
|
||||
|
||||
if [ -n "$ubsan_url" ]; then
|
||||
download_and_extract "ubsan" "$ubsan_url" "$REPORT_DIR/ubsan-reports"
|
||||
else
|
||||
log "ubsan: skipped (no url)"
|
||||
fi
|
||||
|
||||
log "all done"
|
||||
echo "$REPORT_DIR"
|
||||
201
.github/scripts/parse_sanitizer_reports.py
vendored
Normal file
201
.github/scripts/parse_sanitizer_reports.py
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Parse ASan/UBSan artifacts from the memory-safety workflow.
|
||||
|
||||
Reads the report directory produced by fetch-artifacts.sh, extracts
|
||||
findings from per-PID log files and stdout captures, writes structured
|
||||
JSON to /tmp/parsed-report.json.
|
||||
|
||||
Usage:
|
||||
parse_sanitizer_reports.py [report_dir]
|
||||
|
||||
report_dir defaults to /tmp/reports.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPORT_DIR = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("/tmp/reports")
|
||||
OUT = Path("/tmp/parsed-report.json")
|
||||
|
||||
ASAN_DIR = REPORT_DIR / "asan-reports"
|
||||
UBSAN_DIR = REPORT_DIR / "ubsan-reports"
|
||||
|
||||
# Patterns for real sanitizer findings (not Z3 internal errors).
|
||||
ASAN_ERROR = re.compile(
|
||||
r"==\d+==ERROR: (AddressSanitizer|LeakSanitizer): (.+)"
|
||||
)
|
||||
ASAN_SUMMARY = re.compile(
|
||||
r"SUMMARY: (AddressSanitizer|LeakSanitizer): (\d+) byte"
|
||||
)
|
||||
UBSAN_ERROR = re.compile(
|
||||
r"(.+:\d+:\d+): runtime error: (.+)"
|
||||
)
|
||||
# Stack frame: #N 0xADDR in func file:line
|
||||
STACK_FRAME = re.compile(
|
||||
r"\s+#(\d+) 0x[0-9a-f]+ in (.+?) (.+)"
|
||||
)
|
||||
|
||||
|
||||
def read_text(path):
|
||||
if path.is_file():
|
||||
return path.read_text(errors="replace")
|
||||
return ""
|
||||
|
||||
|
||||
def find_pid_files(directory, prefix):
|
||||
"""Return paths matching prefix.* (asan.12345, ubsan.67890, etc)."""
|
||||
if not directory.is_dir():
|
||||
return []
|
||||
return sorted(
|
||||
p for p in directory.iterdir()
|
||||
if p.name.startswith(prefix + ".") and p.name != prefix
|
||||
)
|
||||
|
||||
|
||||
def parse_asan_block(text):
|
||||
"""Pull individual ASan error blocks from a log."""
|
||||
findings = []
|
||||
current = None
|
||||
|
||||
for line in text.splitlines():
|
||||
m = ASAN_ERROR.match(line)
|
||||
if m:
|
||||
if current:
|
||||
findings.append(current)
|
||||
current = {
|
||||
"tool": m.group(1),
|
||||
"type": m.group(2).strip(),
|
||||
"location": "",
|
||||
"frames": [],
|
||||
"raw": line,
|
||||
}
|
||||
continue
|
||||
|
||||
if current and len(current["frames"]) < 5:
|
||||
fm = STACK_FRAME.match(line)
|
||||
if fm:
|
||||
frame = {"func": fm.group(2), "location": fm.group(3)}
|
||||
current["frames"].append(frame)
|
||||
if not current["location"] and ":" in fm.group(3):
|
||||
current["location"] = fm.group(3).strip()
|
||||
|
||||
if current:
|
||||
findings.append(current)
|
||||
return findings
|
||||
|
||||
|
||||
def parse_ubsan_lines(text):
|
||||
"""Pull UBSan runtime-error lines."""
|
||||
findings = []
|
||||
seen = set()
|
||||
for line in text.splitlines():
|
||||
m = UBSAN_ERROR.search(line)
|
||||
if m:
|
||||
key = (m.group(1), m.group(2))
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
findings.append({
|
||||
"tool": "UBSan",
|
||||
"type": m.group(2).strip(),
|
||||
"location": m.group(1).strip(),
|
||||
"raw": line.strip(),
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def scan_directory(directory, prefix, parse_pid_fn, log_pattern):
|
||||
"""Scan a report directory and return structured results."""
|
||||
summary_text = read_text(directory / "summary.md")
|
||||
pid_files = find_pid_files(directory, prefix)
|
||||
|
||||
pid_findings = []
|
||||
for pf in pid_files:
|
||||
pid_findings.extend(parse_pid_fn(pf.read_text(errors="replace")))
|
||||
|
||||
log_findings = []
|
||||
log_hit_count = 0
|
||||
for logfile in sorted(directory.glob("*.log")):
|
||||
content = logfile.read_text(errors="replace")
|
||||
hits = len(log_pattern.findall(content))
|
||||
log_hit_count += hits
|
||||
log_findings.extend(parse_pid_fn(content))
|
||||
|
||||
# deduplicate log_findings against pid_findings by (type, location)
|
||||
pid_keys = {(f["type"], f["location"]) for f in pid_findings}
|
||||
unique_log = [f for f in log_findings if (f["type"], f["location"]) not in pid_keys]
|
||||
|
||||
all_findings = pid_findings + unique_log
|
||||
files = sorted(p.name for p in directory.iterdir()) if directory.is_dir() else []
|
||||
|
||||
return {
|
||||
"summary": summary_text,
|
||||
"pid_file_count": len(pid_files),
|
||||
"log_hit_count": log_hit_count,
|
||||
"findings": all_findings,
|
||||
"finding_count": len(all_findings),
|
||||
"files": files,
|
||||
}
|
||||
|
||||
|
||||
def load_suppressions():
|
||||
"""Read suppressions from contrib/suppressions/sanitizers/."""
|
||||
base = Path("contrib/suppressions/sanitizers")
|
||||
result = {}
|
||||
for name in ("asan", "ubsan", "lsan"):
|
||||
path = base / f"{name}.txt"
|
||||
entries = []
|
||||
if path.is_file():
|
||||
for line in path.read_text().splitlines():
|
||||
line = line.strip()
|
||||
if line and not line.startswith("#"):
|
||||
entries.append(line)
|
||||
result[name] = entries
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
if not REPORT_DIR.is_dir():
|
||||
print(f"error: {REPORT_DIR} not found", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
asan = scan_directory(ASAN_DIR, "asan", parse_asan_block, ASAN_ERROR)
|
||||
ubsan = scan_directory(UBSAN_DIR, "ubsan", parse_ubsan_lines, UBSAN_ERROR)
|
||||
suppressions = load_suppressions()
|
||||
|
||||
report = {
|
||||
"asan": asan,
|
||||
"ubsan": ubsan,
|
||||
"suppressions": suppressions,
|
||||
"total_findings": asan["finding_count"] + ubsan["finding_count"],
|
||||
}
|
||||
|
||||
OUT.write_text(json.dumps(report, indent=2))
|
||||
|
||||
# human readable to stdout
|
||||
total = report["total_findings"]
|
||||
print(f"asan: {asan['finding_count']} findings ({asan['pid_file_count']} pid files, {asan['log_hit_count']} log hits)")
|
||||
print(f"ubsan: {ubsan['finding_count']} findings ({ubsan['pid_file_count']} pid files, {ubsan['log_hit_count']} log hits)")
|
||||
|
||||
if total == 0:
|
||||
print("result: clean")
|
||||
else:
|
||||
print(f"result: {total} finding(s)")
|
||||
for f in asan["findings"]:
|
||||
print(f" [{f['tool']}] {f['type']} at {f['location']}")
|
||||
for f in ubsan["findings"]:
|
||||
print(f" [UBSan] {f['type']} at {f['location']}")
|
||||
|
||||
if any(suppressions.values()):
|
||||
print("suppressions:")
|
||||
for tool, entries in suppressions.items():
|
||||
for e in entries:
|
||||
print(f" {tool}: {e}")
|
||||
|
||||
print(f"\njson: {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
72
.github/skills/README.md
vendored
Normal file
72
.github/skills/README.md
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Z3 Agent Skills
|
||||
|
||||
Reusable, composable verification primitives for the Z3 theorem prover.
|
||||
Each skill is a self-contained unit: a `SKILL.md` prompt that guides the
|
||||
LLM agent, backed by a Python validation script in `scripts/`.
|
||||
|
||||
## Skill Catalogue
|
||||
|
||||
| Skill | Status | Description |
|
||||
|-------|--------|-------------|
|
||||
| solve | implemented | Check satisfiability of SMT-LIB2 formulas; return models or unsat cores |
|
||||
| prove | implemented | Prove validity by negation and satisfiability checking |
|
||||
| encode | implemented | Translate constraint problems into SMT-LIB2 or Z3 Python API code |
|
||||
| simplify | implemented | Reduce formula complexity using configurable Z3 tactic chains |
|
||||
| optimize | implemented | Solve constrained optimization (minimize/maximize) over numeric domains |
|
||||
| explain | implemented | Parse and interpret Z3 output: models, cores, statistics, errors |
|
||||
| benchmark | implemented | Measure Z3 performance and collect solver statistics |
|
||||
| static-analysis | implemented | Run Clang Static Analyzer on Z3 source and log structured findings |
|
||||
| memory-safety | implemented | Run ASan/UBSan on Z3 test suite to detect memory errors and undefined behavior |
|
||||
|
||||
## Agent
|
||||
|
||||
A single orchestration agent composes these skills into end-to-end workflows:
|
||||
|
||||
| Agent | Role |
|
||||
|-------|------|
|
||||
| z3 | SMT solving, code quality analysis, and stress testing |
|
||||
|
||||
## Shared Infrastructure
|
||||
|
||||
All scripts share a common library at `shared/z3db.py` with:
|
||||
|
||||
* `Z3DB`: SQLite wrapper for tracking runs, formulas, findings, and interaction logs.
|
||||
* `run_z3()`: Pipe SMT-LIB2 into `z3 -in` with timeout handling.
|
||||
* `find_z3()`: Locate the Z3 binary across build directories and PATH.
|
||||
* Parsers: `parse_model()`, `parse_stats()`, `parse_unsat_core()`.
|
||||
|
||||
The database schema lives in `shared/schema.sql`.
|
||||
|
||||
## Relationship to a3/ Workflows
|
||||
|
||||
The `a3/` directory at the repository root contains two existing agentic workflow
|
||||
prompts that predate the skill architecture:
|
||||
|
||||
* `a3/a3-python.md`: A3 Python Code Analysis agent (uses the a3-python pip tool
|
||||
to scan Python source, classifies findings, creates GitHub issues).
|
||||
* `a3/a3-rust.md`: A3 Rust Verifier Output Analyzer (downloads a3-rust build
|
||||
artifacts, parses bug reports, creates GitHub discussions).
|
||||
|
||||
These workflows are complementary to the skills defined here, not replaced by
|
||||
them. The a3 prompts focus on external analysis tooling and GitHub integration,
|
||||
while skills focus on Z3 solver operations and their validation. Both may be
|
||||
composed by the same orchestrating agent.
|
||||
|
||||
## Usage
|
||||
|
||||
Check database status and recent runs:
|
||||
|
||||
```
|
||||
python shared/z3db.py status
|
||||
python shared/z3db.py runs --skill solve --last 5
|
||||
python shared/z3db.py log --run-id 12
|
||||
python shared/z3db.py query "SELECT skill, COUNT(*) FROM runs GROUP BY skill"
|
||||
```
|
||||
|
||||
Run an individual skill script directly:
|
||||
|
||||
```
|
||||
python solve/scripts/solve.py --file problem.smt2
|
||||
python encode/scripts/encode.py --validate formula.smt2
|
||||
python benchmark/scripts/benchmark.py --file problem.smt2
|
||||
```
|
||||
372
.github/skills/agentic-workflow-designer/SKILL.md
vendored
Normal file
372
.github/skills/agentic-workflow-designer/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
---
|
||||
name: agentic-workflow-designer
|
||||
description: Conversational skill that interviews users to design new agentic workflows
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Workflow Designer
|
||||
|
||||
Use this skill to run a structured interview with users who know their goal but not the workflow syntax yet, then generate one complete workflow `.md` file.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
Use this before `.github/aw/create-agentic-workflow.md` when requirements are unclear or incomplete.
|
||||
|
||||
- Use `skills/agentic-workflow-designer/SKILL.md` to discover and confirm requirements.
|
||||
- Use `.github/aw/create-agentic-workflow.md` once requirements are clear and ready for implementation.
|
||||
- Use `.github/aw/agentic-chat.md` when the user wants a specification/pseudo-code instead of a runnable workflow file.
|
||||
|
||||
## Interview Framework
|
||||
|
||||
Ask one question at a time. Move to the next phase only after the current phase is clear.
|
||||
|
||||
### Phase 1: Goal
|
||||
|
||||
Ask: **"What do you want to automate?"**
|
||||
|
||||
Capture:
|
||||
- Workflow name (kebab-case candidate)
|
||||
- Brief description
|
||||
- Optional emoji
|
||||
|
||||
### Phase 2: Trigger
|
||||
|
||||
Ask: **"When should this run?"**
|
||||
|
||||
Follow up only if needed:
|
||||
- Which event type(s)?
|
||||
- Any filters (labels, branches, commands)?
|
||||
- Scheduled cadence (daily/weekly/hourly)?
|
||||
|
||||
Map to the `on:` block.
|
||||
|
||||
### Phase 3: Scope (Read/Write)
|
||||
|
||||
Ask:
|
||||
- **"What should it read?"** (issues, PRs, code, discussions, CI data)
|
||||
- **"What should it create or update?"** (comments, issues, PRs, labels)
|
||||
|
||||
Map to:
|
||||
- `permissions:` (keep read-only for agent job)
|
||||
- `tools:`
|
||||
- `safe-outputs:`
|
||||
|
||||
### Phase 4: Data Strategy
|
||||
|
||||
Ask:
|
||||
- **"What data does the agent need to make decisions?"**
|
||||
- Follow up: **"Can we pre-fetch and aggregate that data with shell commands so the agent only reads compact JSON?"**
|
||||
|
||||
Capture:
|
||||
- Whether `steps:` should pre-fetch GitHub data with `gh` + `jq`
|
||||
- Output paths under `/tmp/gh-aw/data/`
|
||||
- Whether batch work should use sub-agents
|
||||
|
||||
Map to:
|
||||
- `steps:`
|
||||
- Prompt references to pre-computed file paths
|
||||
|
||||
### Phase 5: Guardrails
|
||||
|
||||
Ask: **"Should it block merging, just advise, or silently log?"**
|
||||
|
||||
Capture:
|
||||
- Visibility expectations (comment, issue, no visible output)
|
||||
- No-op behavior expectation
|
||||
|
||||
Guide toward safe output behavior and explicit `noop` instructions.
|
||||
|
||||
### Phase 6: Context & Network
|
||||
|
||||
Ask: **"Does it need external APIs, web access, package installs, or MCP servers?"**
|
||||
|
||||
Follow up:
|
||||
- **"Any third-party services or MCP servers to include (for example Slack, Jira, Datadog, custom internal MCP)?"**
|
||||
- **"Are you deploying on GitHub.com, GHEC with custom endpoints, or GHES?"**
|
||||
- For each integration, identify required auth from source docs and map it to GitHub Actions secrets + workflow env variables.
|
||||
- Ask for exact external domains (FQDN/wildcard).
|
||||
|
||||
Map to:
|
||||
- `network.allowed`
|
||||
- Optional MCP/GitHub tool usage in `tools:`
|
||||
- `secrets:` / `env:` wiring for integration tokens
|
||||
- GHES/GHEC settings such as `engine.api-target` and `aw.json` `ghes: true` (when applicable)
|
||||
|
||||
### Phase 7: Engine (optional)
|
||||
|
||||
Ask only if ambiguous: **"Any AI engine preference?"**
|
||||
|
||||
If no preference, suggest default:
|
||||
- "I'd suggest Copilot since you haven't mentioned a preference. Sound good?"
|
||||
|
||||
Map to `engine:` only when not default.
|
||||
|
||||
### Phase 8: Confirmation
|
||||
|
||||
Present a structured summary and ask for approval before generation.
|
||||
|
||||
## Decision Heuristics
|
||||
|
||||
### Trigger Mapping
|
||||
|
||||
| User says... | Maps to |
|
||||
|---|---|
|
||||
| "when someone opens a PR" | `on: pull_request:` with `types: [opened]` |
|
||||
| "when a PR is updated" | `on: pull_request:` with `types: [opened, synchronize]` |
|
||||
| "every morning", "daily" | fuzzy schedule shorthand `on: schedule: daily on weekdays` (compiler expands to cron) |
|
||||
| "every Monday", "weekly" | fuzzy schedule shorthand `on: schedule: weekly` (compiler expands to cron) |
|
||||
| "when I say /review" | `on: slash_command:` with `name: review` (or requested command) |
|
||||
| "when an issue is labeled bug" | `on: issues:` with `types: [labeled]` and label filter guidance |
|
||||
| "run when label ai-review is added" | `on: label_command:` with `name`/`names`, optional event scoping, and label-as-command semantics |
|
||||
| "run on PRs from forks" | `on: pull_request:` plus explicit `forks:` allowlist and fork security guardrails |
|
||||
| "sometimes automatic, sometimes manual" | semi-active pattern: combine `schedule`/event triggers with `workflow_dispatch` |
|
||||
| "manually", "on demand" | `on: workflow_dispatch:` |
|
||||
| "when a deployment fails" | `on: deployment_status:` |
|
||||
| "when another workflow finishes" | `on: workflow_run:` |
|
||||
|
||||
### Safe Output Mapping
|
||||
|
||||
| User says... | Maps to |
|
||||
|---|---|
|
||||
| "post a comment" | `add-comment` |
|
||||
| "create an issue" | `create-issue` |
|
||||
| "update issue title/body" | `update-issue` |
|
||||
| "close the issue" | `close-issue` |
|
||||
| "assign someone", "remove assignment" | `assign-to-user`, `unassign-from-user` |
|
||||
| "set issue type/field/milestone" | `set-issue-type`, `set-issue-field`, `assign-milestone` |
|
||||
| "open a PR", "submit changes" | `create-pull-request` |
|
||||
| "update PR description/title" | `update-pull-request` |
|
||||
| "close the PR", "merge the PR" | `close-pull-request`, `merge-pull-request` |
|
||||
| "mark PR ready", "sync PR branch" | `mark-pull-request-as-ready-for-review`, `update-branch` |
|
||||
| "commit a fix to the PR branch" | `push-to-pull-request-branch` |
|
||||
| "approve / request changes" | `submit-pull-request-review` |
|
||||
| "inline review comment", "reply to review thread" | `create-pull-request-review-comment`, `reply-to-pull-request-review-comment`, `resolve-pull-request-review-thread` |
|
||||
| "start or edit discussion", "close discussion" | `create-discussion`, `update-discussion`, `close-discussion` |
|
||||
| "request reviewer", "hide comment" | `add-reviewer`, `hide-comment` |
|
||||
| "create/update project", "project status update" | `create-project`, `update-project`, `create-project-status-update` |
|
||||
| "update release", "upload release asset" | `update-release`, `upload-asset` |
|
||||
| "create/auto-fix code scan alert" | `create-code-scanning-alert`, `autofix-code-scanning-alert` |
|
||||
| "start an agent session", "assign to an agent" | `create-agent-session`, `assign-to-agent` |
|
||||
| "store persistent memory comment" | `comment-memory` |
|
||||
| "link a sub-issue" | `link-sub-issue` |
|
||||
| "add labels", "remove labels" | `add-labels`, `remove-labels` |
|
||||
| "nothing visible", "just analyze" | no safe outputs required |
|
||||
|
||||
### Network Mapping
|
||||
|
||||
| User says... | Maps to |
|
||||
|---|---|
|
||||
| "calls an external API" | ask for exact FQDN/wildcard, then add to `network.allowed` |
|
||||
| "reads GitHub data / clones repos" | include `github` in `network.allowed` |
|
||||
| "uses GitHub Actions artifacts or cache" | include `github-actions` in `network.allowed` |
|
||||
| "installs npm packages" | include `node` in `network.allowed` |
|
||||
| "runs pip install" | include `python` in `network.allowed` |
|
||||
| "builds Go code" | include `go` in `network.allowed` |
|
||||
| "installs gems / uses Bundler" | include `ruby` in `network.allowed` |
|
||||
| "runs cargo build" | include `rust` in `network.allowed` |
|
||||
| "uses NuGet / .NET restore" | include `dotnet` in `network.allowed` |
|
||||
| "builds with Maven / Gradle" | include `java` in `network.allowed` |
|
||||
| "uses Docker / pulls container images / pushes to GHCR" | include `containers` in `network.allowed` |
|
||||
| "runs Playwright browser tests" | include `playwright` in `network.allowed` |
|
||||
| "runs apt install / yum / apk" | include `linux-distros` in `network.allowed` |
|
||||
| "uses Terraform / HashiCorp registry" | include `terraform` in `network.allowed` |
|
||||
| "connects to localhost / loopback / local services" | include `local` in `network.allowed` |
|
||||
| "uses Swift Package Manager" | include `swift` in `network.allowed` |
|
||||
| "uses Composer / PHP packages" | include `php` in `network.allowed` |
|
||||
| "uses pub.dev / Dart packages" | include `dart` in `network.allowed` |
|
||||
| "uses Hackage / Haskell packages" | include `haskell` in `network.allowed` |
|
||||
| "uses CPAN / Perl packages" | include `perl` in `network.allowed` |
|
||||
| "serves or loads web fonts" | include `fonts` in `network.allowed` |
|
||||
| "uses Deno or JSR packages" | include `deno` in `network.allowed` |
|
||||
| "uses Elixir / Hex packages" | include `elixir` in `network.allowed` |
|
||||
| "uses Bazel build" | include `bazel` in `network.allowed` |
|
||||
| "uses R / CRAN packages" | include `r` in `network.allowed` |
|
||||
| "no external access" | `network.allowed: [defaults]` (or `[]` if explicitly zero network) |
|
||||
|
||||
### Tool Mapping
|
||||
|
||||
| User says... | Maps to |
|
||||
|---|---|
|
||||
| "read GitHub issues/PRs/workflows" | `tools.github` with `mode: gh-proxy` and minimal `toolsets` |
|
||||
| "use full MCP server/tool definitions" | `tools.github` with `mode: local` |
|
||||
| "use other MCP servers but keep token cost down" | `tools.cli-proxy: true` (hybrid CLI-proxy mode) |
|
||||
| "edit files" | `edit` tool (default unless restricted) |
|
||||
| "run commands/tests" | `bash` tool (default unless restricted) |
|
||||
| "browse web pages/docs" | `web-fetch` and/or `web-search` |
|
||||
| "test UI flows" | `playwright` |
|
||||
|
||||
### Pattern Heuristics
|
||||
|
||||
| User says... | Recommended named pattern |
|
||||
|---|---|
|
||||
| "triage issues automatically" | `IssueOps` |
|
||||
| "run on /commands with human approval loops" | `ChatOps` |
|
||||
| "run every weekday and keep improving" | `DailyOps` |
|
||||
| "monitor workflow failures and trends" | `MonitorOps` |
|
||||
| "process a big backlog in chunks" | `BatchOps` |
|
||||
| "run manually with input parameters" | `DispatchOps` |
|
||||
| "apply a label-based workflow" | `LabelOps` |
|
||||
| "operate across multiple repositories" | `MultiRepoOps` |
|
||||
| "coordinate multiple sub-agents" | `Orchestration` |
|
||||
| "manage project board items" | `ProjectOps` |
|
||||
| "research, plan, and assign issues" | `ResearchPlanAssignOps` |
|
||||
| "self-correcting / retry on failure" | `CorrectionOps` |
|
||||
| "run in a side/fork repo" | `SideRepoOps` |
|
||||
| "write a spec before implementing" | `SpecOps` |
|
||||
| "A/B test workflow variants" | `TrialOps` |
|
||||
| "process items from a queue" | `WorkQueueOps` |
|
||||
| "deterministic, no LLM needed" | `DeterministicOps` |
|
||||
| "manage from a central repo" | `CentralRepoOps` |
|
||||
| "track work via GitHub Projects" | `Monitoring with Projects` |
|
||||
|
||||
### Integration Auth Mapping
|
||||
|
||||
When the user names a third-party service or MCP server:
|
||||
|
||||
1. Confirm whether native tool, MCP server, or safe-output job is the right integration path.
|
||||
2. Look up the integration's auth requirements and required scopes before finalizing the design.
|
||||
3. Provide a concrete setup checklist with:
|
||||
- required GitHub Actions secrets (names to create)
|
||||
- workflow env variables that consume those secrets
|
||||
- minimum token scopes/permissions needed
|
||||
|
||||
Output format to use:
|
||||
|
||||
```text
|
||||
Integration auth setup:
|
||||
- <service-or-mcp>: <purpose>
|
||||
- Secrets to create: <SECRET_NAME>, <SECRET_NAME>
|
||||
- Workflow env vars: <ENV_VAR>=${{ secrets.<SECRET_NAME> }}
|
||||
- Required scopes/permissions: <least-privilege scopes>
|
||||
```
|
||||
|
||||
Never suggest committing plaintext tokens.
|
||||
|
||||
### Data Strategy Mapping
|
||||
|
||||
| User says... | Maps to |
|
||||
|---|---|
|
||||
| "analyze PRs", "review issues", "check status" | add `steps:` that pre-fetch with `gh` + `jq` |
|
||||
| "read the diff", "look at changed files" | add `steps:` using `gh pr diff` or `gh pr view --json files` |
|
||||
| "search for patterns across repos" | add `steps:` using `gh search` + `jq` filters |
|
||||
| "just respond to a comment" | no pre-fetch needed (event payload is enough) |
|
||||
| "process each item individually" | suggest sub-agent pattern with `model: small` |
|
||||
|
||||
## Token Optimization Defaults
|
||||
|
||||
Apply these defaults unless the user explicitly asks otherwise:
|
||||
|
||||
1. Use DataOps by default for GitHub reads: pre-fetch/aggregate with `gh` + `jq` in `steps:`, store compact JSON in `/tmp/gh-aw/data/`, and point the prompt to those files (see `.github/aw/token-optimization.md` for details).
|
||||
2. Keep tool surface minimal: default to `tools.github.mode: gh-proxy`, include only required toolsets, and prefer `bash` + `gh` for simple reads.
|
||||
3. For batch workloads, split items into compact data and suggest sub-agent processing with `model: small`.
|
||||
4. Keep prompts compact: concise imperative instructions, explicit file paths, single-line `noop` guidance, and stable instructions before dynamic content.
|
||||
|
||||
## Progressive Disclosure Rules
|
||||
|
||||
1. Never dump all options at once; ask one targeted question at a time.
|
||||
2. Skip questions when answers are inferable from prior user statements.
|
||||
3. Offer smart defaults and request confirmation instead of over-questioning.
|
||||
4. Ask at most 5 questions before presenting a summary; then ask "anything else?" if needed.
|
||||
5. Detect done signals (`that's it`, `looks good`, `generate it`) and proceed to generation.
|
||||
|
||||
## Confirmation Format
|
||||
|
||||
Use this exact structure:
|
||||
|
||||
```text
|
||||
📋 Proposed workflow:
|
||||
- Name: <workflow-id>
|
||||
- Trigger: <event + key options>
|
||||
- Engine: <engine or default>
|
||||
- Tools: <tool summary>
|
||||
- Safe outputs: <list or none>
|
||||
- Network: <allowed summary>
|
||||
- Integrations/Auth: <service/mcp + required secrets/env vars>
|
||||
- Deployment: <GitHub.com or GHEC/GHES details>
|
||||
- Intent: <one-sentence task>
|
||||
```
|
||||
|
||||
Then ask: **"Ready to generate, or want to adjust anything?"**
|
||||
|
||||
## Generation Template
|
||||
|
||||
After confirmation, generate one workflow file using the same skeleton style as `.github/aw/create-agentic-workflow.md`.
|
||||
|
||||
```markdown
|
||||
---
|
||||
emoji: <emoji>
|
||||
description: <brief description>
|
||||
on:
|
||||
<trigger config>
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
tools:
|
||||
github:
|
||||
mode: gh-proxy
|
||||
toolsets: [default]
|
||||
steps:
|
||||
- name: <optional data prefetch>
|
||||
run: |
|
||||
mkdir -p /tmp/gh-aw/data
|
||||
<gh + jq commands that produce compact JSON>
|
||||
safe-outputs:
|
||||
<safe-output-types-if-needed>
|
||||
network:
|
||||
allowed:
|
||||
- defaults
|
||||
- <additional entries if needed>
|
||||
---
|
||||
|
||||
# <Workflow Name>
|
||||
|
||||
## Task
|
||||
|
||||
<clear instructions tied to trigger context>
|
||||
If `steps:` includes pre-fetch commands, read the resulting `/tmp/gh-aw/data/*.json` files instead of broad live re-fetches.
|
||||
|
||||
## Safe Outputs
|
||||
|
||||
- Use configured safe outputs for all visible write actions.
|
||||
- Call `noop` with a short reason when no action is needed.
|
||||
```
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
Before final output, run this internal self-check:
|
||||
|
||||
- [ ] Agent job permissions remain read-only (writes only via safe outputs)
|
||||
- [ ] `safe-outputs:` covers every write action mentioned in prompt/instructions
|
||||
- [ ] Network access is scoped; avoid blanket wildcard entries
|
||||
- [ ] Trigger matches the user's intended activation event
|
||||
- [ ] Prompt instructs agent to call `noop` when no action is needed
|
||||
- [ ] Unnecessary defaults are omitted (for example `engine: copilot`)
|
||||
- [ ] If reading GitHub data, `steps:` pre-fetches compact JSON (DataOps)
|
||||
- [ ] `tools.github.mode` is `gh-proxy` unless broader MCP toolsets are explicitly needed
|
||||
- [ ] Only required toolsets are listed (avoid blanket toolset lists)
|
||||
- [ ] Prompt references specific pre-computed file paths
|
||||
- [ ] For batch processing (>5 items), sub-agent pattern is suggested
|
||||
- [ ] For each third-party service/MCP integration, required secrets/env vars are listed
|
||||
- [ ] Auth guidance includes least-privilege token scope recommendations
|
||||
- [ ] For GHEC/GHES deployments, `engine.api-target` and GHES compatibility guidance are included when needed
|
||||
|
||||
## References (load only when needed)
|
||||
|
||||
In-repo references:
|
||||
- `.github/aw/syntax.md` (index → `.github/aw/syntax-core.md`, `.github/aw/syntax-agentic.md`, `.github/aw/syntax-tools-imports.md`)
|
||||
- `.github/aw/safe-outputs.md` (index → `.github/aw/safe-outputs-content.md`, `.github/aw/safe-outputs-management.md`, `.github/aw/safe-outputs-automation.md`, `.github/aw/safe-outputs-runtime.md`)
|
||||
- `.github/aw/network.md`
|
||||
- `.github/aw/patterns.md`
|
||||
- `.github/aw/subagents.md`
|
||||
- `.github/aw/token-optimization.md`
|
||||
- `.github/aw/triggers.md`
|
||||
- `.github/aw/create-agentic-workflow.md`
|
||||
|
||||
Portable HTTPS references:
|
||||
- `https://github.com/github/gh-aw/blob/main/.github/aw/syntax.md` (index → `.../syntax-core.md`, `.../syntax-agentic.md`, `.../syntax-tools-imports.md`)
|
||||
- `https://github.com/github/gh-aw/blob/main/.github/aw/safe-outputs.md` (index → `.../safe-outputs-content.md`, `.../safe-outputs-management.md`, `.../safe-outputs-automation.md`, `.../safe-outputs-runtime.md`)
|
||||
- `https://github.com/github/gh-aw/blob/main/.github/aw/network.md`
|
||||
- `https://github.com/github/gh-aw/blob/main/.github/aw/patterns.md`
|
||||
- `https://github.com/github/gh-aw/blob/main/.github/aw/triggers.md`
|
||||
- `https://github.com/github/gh-aw/blob/main/.github/aw/create-agentic-workflow.md`
|
||||
80
.github/skills/agentic-workflows/SKILL.md
vendored
Normal file
80
.github/skills/agentic-workflows/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
name: agentic-workflows
|
||||
description: Route gh-aw workflow design/create/debug/upgrade requests to the right prompts.
|
||||
---
|
||||
|
||||
# Agentic Workflows Router
|
||||
|
||||
Use this skill when a user asks to design, create, update, debug, or upgrade GitHub Agentic Workflows in this repository.
|
||||
|
||||
This skill is a dispatcher: identify the task type, load the matching workflow prompt/skill file, and follow it directly. Keep responses concise and ask a clarifying question if the correct prompt is unclear.
|
||||
|
||||
Read only the files you need:
|
||||
Load these files from `github/gh-aw` (they are not available locally).
|
||||
- `.github/aw/agentic-chat.md`
|
||||
- `.github/aw/agentic-workflows-mcp.md`
|
||||
- `.github/aw/asciicharts.md`
|
||||
- `.github/aw/campaign.md`
|
||||
- `.github/aw/charts-trending.md`
|
||||
- `.github/aw/charts.md`
|
||||
- `.github/aw/cli-commands.md`
|
||||
- `.github/aw/context.md`
|
||||
- `.github/aw/create-agentic-workflow.md`
|
||||
- `.github/aw/create-shared-agentic-workflow.md`
|
||||
- `.github/aw/debug-agentic-workflow.md`
|
||||
- `.github/aw/dependabot.md`
|
||||
- `.github/aw/deployment-status.md`
|
||||
- `.github/aw/experiments.md`
|
||||
- `.github/aw/github-agentic-workflows.md`
|
||||
- `.github/aw/github-mcp-server.md`
|
||||
- `.github/aw/llms.md`
|
||||
- `.github/aw/mcp-clis.md`
|
||||
- `.github/aw/memory.md`
|
||||
- `.github/aw/messages.md`
|
||||
- `.github/aw/network.md`
|
||||
- `.github/aw/optimize-agentic-workflow.md`
|
||||
- `.github/aw/patterns.md`
|
||||
- `.github/aw/pr-reviewer.md`
|
||||
- `.github/aw/report.md`
|
||||
- `.github/aw/reuse.md`
|
||||
- `.github/aw/safe-outputs-automation.md`
|
||||
- `.github/aw/safe-outputs-content.md`
|
||||
- `.github/aw/safe-outputs-management.md`
|
||||
- `.github/aw/safe-outputs-runtime.md`
|
||||
- `.github/aw/safe-outputs.md`
|
||||
- `.github/aw/serena-tool.md`
|
||||
- `.github/aw/shared-safe-jobs.md`
|
||||
- `.github/aw/skills.md`
|
||||
- `.github/aw/subagents.md`
|
||||
- `.github/aw/syntax-agentic.md`
|
||||
- `.github/aw/syntax-core.md`
|
||||
- `.github/aw/syntax-tools-imports.md`
|
||||
- `.github/aw/syntax.md`
|
||||
- `.github/aw/test-coverage.md`
|
||||
- `.github/aw/test-expression.md`
|
||||
- `.github/aw/token-optimization.md`
|
||||
- `.github/aw/triggers.md`
|
||||
- `.github/aw/update-agentic-workflow.md`
|
||||
- `.github/aw/upgrade-agentic-workflows.md`
|
||||
- `.github/aw/visual-regression.md`
|
||||
- `.github/aw/workflow-constraints.md`
|
||||
- `.github/aw/workflow-editing.md`
|
||||
- `.github/aw/workflow-patterns.md`
|
||||
|
||||
- `.github/skills/agentic-workflow-designer/SKILL.md`
|
||||
After loading the matching workflow prompt or skill, follow it directly:
|
||||
- Design workflows from scratch via interview: `skills/agentic-workflow-designer/SKILL.md`
|
||||
- Create new workflows: `.github/aw/create-agentic-workflow.md`
|
||||
- Update existing workflows: `.github/aw/update-agentic-workflow.md`
|
||||
- Debug, audit, or investigate workflows: `.github/aw/debug-agentic-workflow.md`
|
||||
- Upgrade workflows and fix deprecations: `.github/aw/upgrade-agentic-workflows.md`
|
||||
- Create shared components or MCP wrappers: `.github/aw/create-shared-agentic-workflow.md`
|
||||
- Create report-generating workflows: `.github/aw/report.md`
|
||||
- Fix Dependabot manifest PRs: `.github/aw/dependabot.md`
|
||||
- Analyze coverage workflows: `.github/aw/test-coverage.md`
|
||||
- Render compact markdown charts: `.github/aw/asciicharts.md`
|
||||
- Map CLI commands to MCP usage: `.github/aw/cli-commands.md`
|
||||
- Choose workflow architecture and patterns: `.github/aw/patterns.md`
|
||||
- Optimize token usage and cost: `.github/aw/token-optimization.md`
|
||||
|
||||
When the task involves OTEL, OTLP, traces, observability backends, or telemetry-driven analysis, also read and follow `skills/otel-queries/SKILL.md` after loading the matching workflow prompt or skill.
|
||||
79
.github/skills/benchmark/SKILL.md
vendored
Normal file
79
.github/skills/benchmark/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
name: benchmark
|
||||
description: Measure Z3 performance on a formula or file. Collects wall-clock time, theory solver statistics, memory usage, and conflict counts. Results are logged to z3agent.db for longitudinal tracking.
|
||||
---
|
||||
|
||||
Given an SMT-LIB2 formula or file, run Z3 with statistics enabled and report performance characteristics. This is useful for identifying performance regressions, comparing tactic strategies, and profiling theory solver workload distribution.
|
||||
|
||||
# Step 1: Run Z3 with statistics
|
||||
|
||||
Action:
|
||||
Invoke benchmark.py with the formula or file. Use `--runs N` for
|
||||
repeated timing.
|
||||
|
||||
Expectation:
|
||||
The script invokes `z3 -st`, parses the statistics block, and prints
|
||||
a performance summary. A run entry is logged to z3agent.db.
|
||||
|
||||
Result:
|
||||
Timing and statistics are displayed. Proceed to Step 2 to interpret.
|
||||
|
||||
```bash
|
||||
python3 scripts/benchmark.py --file problem.smt2
|
||||
python3 scripts/benchmark.py --file problem.smt2 --runs 5
|
||||
python3 scripts/benchmark.py --formula "(declare-const x Int)..." --debug
|
||||
```
|
||||
|
||||
# Step 2: Interpret the output
|
||||
|
||||
Action:
|
||||
Review wall-clock time, memory usage, conflict counts, and per-theory
|
||||
breakdowns.
|
||||
|
||||
Expectation:
|
||||
A complete performance profile including min/median/max timing when
|
||||
multiple runs are requested.
|
||||
|
||||
Result:
|
||||
If performance is acceptable, no action needed.
|
||||
If slow, try **simplify** to reduce the formula or adjust tactic strategies.
|
||||
|
||||
The output includes:
|
||||
|
||||
- wall-clock time (ms)
|
||||
- result (sat/unsat/unknown/timeout)
|
||||
- memory usage (MB)
|
||||
- conflicts, decisions, propagations
|
||||
- per-theory breakdown (arithmetic, bv, array, etc.)
|
||||
|
||||
With `--runs N`, the script runs Z3 N times and reports min/median/max timing.
|
||||
|
||||
# Step 3: Compare over time
|
||||
|
||||
Action:
|
||||
Query past benchmark runs from z3agent.db to detect regressions or
|
||||
improvements.
|
||||
|
||||
Expectation:
|
||||
Historical run data is available for comparison, ordered by recency.
|
||||
|
||||
Result:
|
||||
If performance regressed, investigate recent formula or tactic changes.
|
||||
If improved, record the successful configuration.
|
||||
|
||||
```bash
|
||||
python3 ../../shared/z3db.py runs --skill benchmark --last 20
|
||||
python3 ../../shared/z3db.py query "SELECT smtlib2, result, stats FROM formulas WHERE run_id IN (SELECT run_id FROM runs WHERE skill='benchmark') ORDER BY run_id DESC LIMIT 5"
|
||||
```
|
||||
|
||||
# Parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| formula | string | no | | SMT-LIB2 formula |
|
||||
| file | path | no | | path to .smt2 file |
|
||||
| runs | int | no | 1 | number of repeated runs for timing |
|
||||
| timeout | int | no | 60 | seconds per run |
|
||||
| z3 | path | no | auto | path to z3 binary |
|
||||
| debug | flag | no | off | verbose tracing |
|
||||
| db | path | no | .z3-agent/z3agent.db | logging database |
|
||||
80
.github/skills/benchmark/scripts/benchmark.py
vendored
Normal file
80
.github/skills/benchmark/scripts/benchmark.py
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
benchmark.py: measure Z3 performance with statistics.
|
||||
|
||||
Usage:
|
||||
python benchmark.py --file problem.smt2
|
||||
python benchmark.py --file problem.smt2 --runs 5
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import statistics
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "shared"))
|
||||
from z3db import Z3DB, run_z3, parse_stats, setup_logging
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog="benchmark")
|
||||
parser.add_argument("--formula")
|
||||
parser.add_argument("--file")
|
||||
parser.add_argument("--runs", type=int, default=1)
|
||||
parser.add_argument("--timeout", type=int, default=60)
|
||||
parser.add_argument("--z3", default=None)
|
||||
parser.add_argument("--db", default=None)
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_logging(args.debug)
|
||||
|
||||
if args.file:
|
||||
formula = Path(args.file).read_text()
|
||||
elif args.formula:
|
||||
formula = args.formula
|
||||
else:
|
||||
parser.error("provide --formula or --file")
|
||||
return
|
||||
|
||||
db = Z3DB(args.db)
|
||||
timings = []
|
||||
|
||||
for i in range(args.runs):
|
||||
run_id = db.start_run("benchmark", formula)
|
||||
result = run_z3(
|
||||
formula,
|
||||
z3_bin=args.z3,
|
||||
timeout=args.timeout,
|
||||
args=["-st"],
|
||||
debug=args.debug,
|
||||
)
|
||||
|
||||
stats = parse_stats(result["stdout"])
|
||||
db.log_formula(run_id, formula, result["result"], stats=stats)
|
||||
db.finish_run(
|
||||
run_id, result["result"], result["duration_ms"], result["exit_code"]
|
||||
)
|
||||
timings.append(result["duration_ms"])
|
||||
|
||||
if args.runs == 1:
|
||||
print(f"result: {result['result']}")
|
||||
print(f"time: {result['duration_ms']}ms")
|
||||
if stats:
|
||||
print("statistics:")
|
||||
for k, v in sorted(stats.items()):
|
||||
print(f" :{k} {v}")
|
||||
|
||||
if args.runs > 1:
|
||||
print(f"runs: {args.runs}")
|
||||
print(f"min: {min(timings)}ms")
|
||||
print(f"median: {statistics.median(timings):.0f}ms")
|
||||
print(f"max: {max(timings)}ms")
|
||||
print(f"result: {result['result']}")
|
||||
|
||||
db.close()
|
||||
sys.exit(0 if result["exit_code"] == 0 else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
73
.github/skills/encode/SKILL.md
vendored
Normal file
73
.github/skills/encode/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
name: encode
|
||||
description: Translate constraint problems into SMT-LIB2 or Z3 Python API code. Handles common problem classes including scheduling, graph coloring, arithmetic puzzles, and verification conditions.
|
||||
---
|
||||
|
||||
Given a problem description (natural language, pseudocode, or a partial formulation), produce a complete, syntactically valid SMT-LIB2 encoding or Z3 Python script. The encoding should declare all variables, assert all constraints, and include the appropriate check-sat / get-model commands.
|
||||
|
||||
# Step 1: Identify the problem class
|
||||
|
||||
Action:
|
||||
Determine the SMT theory and variable sorts required by the problem
|
||||
description.
|
||||
|
||||
Expectation:
|
||||
A clear mapping from the problem to one of the supported theories
|
||||
(LIA, LRA, QF_BV, etc.).
|
||||
|
||||
Result:
|
||||
If the theory is identified, proceed to Step 2. If the problem spans
|
||||
multiple theories, select the appropriate combined logic.
|
||||
|
||||
| Problem class | Theory | Typical sorts |
|
||||
|---------------|--------|---------------|
|
||||
| Integer arithmetic | LIA / NIA | Int |
|
||||
| Real arithmetic | LRA / NRA | Real |
|
||||
| Bitvector operations | QF_BV | (_ BitVec N) |
|
||||
| Arrays and maps | QF_AX | (Array Int Int) |
|
||||
| Strings and regex | QF_S | String, RegLan |
|
||||
| Uninterpreted functions | QF_UF | custom sorts |
|
||||
| Mixed theories | AUFLIA, etc. | combination |
|
||||
|
||||
# Step 2: Generate the encoding
|
||||
|
||||
Action:
|
||||
Invoke encode.py with the problem description and desired output format.
|
||||
|
||||
Expectation:
|
||||
The script produces a complete SMT-LIB2 file or Z3 Python script with
|
||||
all declarations, constraints, and check-sat commands.
|
||||
|
||||
Result:
|
||||
For `smtlib2` format: pass the output to **solve**.
|
||||
For `python` format: execute the script directly.
|
||||
Proceed to Step 3 for validation.
|
||||
|
||||
```bash
|
||||
python3 scripts/encode.py --problem "Find integers x, y such that x^2 + y^2 = 25 and x > 0" --format smtlib2
|
||||
python3 scripts/encode.py --problem "Schedule 4 tasks on 2 machines minimizing makespan" --format python
|
||||
```
|
||||
|
||||
# Step 3: Validate the encoding
|
||||
|
||||
Action:
|
||||
The script runs a syntax check by piping the output through `z3 -in`
|
||||
in parse-only mode.
|
||||
|
||||
Expectation:
|
||||
No parse errors. If errors occur, the offending line is reported.
|
||||
|
||||
Result:
|
||||
On success: the encoding is ready for **solve**, **prove**, or **optimize**.
|
||||
On parse error: fix the reported line and re-run.
|
||||
|
||||
# Parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| problem | string | yes | | problem description |
|
||||
| format | string | no | smtlib2 | output format: smtlib2 or python |
|
||||
| output | path | no | stdout | write to file instead of stdout |
|
||||
| validate | flag | no | on | run syntax check on the output |
|
||||
| debug | flag | no | off | verbose tracing |
|
||||
| db | path | no | .z3-agent/z3agent.db | logging database |
|
||||
149
.github/skills/encode/scripts/encode.py
vendored
Normal file
149
.github/skills/encode/scripts/encode.py
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
encode.py: validate and format SMT-LIB2 encodings.
|
||||
|
||||
Usage:
|
||||
python encode.py --validate formula.smt2
|
||||
python encode.py --validate formula.smt2 --debug
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "shared"))
|
||||
from z3db import Z3DB, run_z3, setup_logging
|
||||
|
||||
VALIDATION_TIMEOUT = 5
|
||||
|
||||
|
||||
def read_input(path_or_stdin: str) -> str:
|
||||
"""Read formula from a file path or stdin (when path is '-')."""
|
||||
if path_or_stdin == "-":
|
||||
return sys.stdin.read()
|
||||
p = Path(path_or_stdin)
|
||||
if not p.exists():
|
||||
print(f"file not found: {p}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return p.read_text()
|
||||
|
||||
|
||||
def find_errors(output: str) -> list:
|
||||
"""Extract (error ...) messages from Z3 output."""
|
||||
return re.findall(r'\(error\s+"([^"]+)"\)', output)
|
||||
|
||||
|
||||
def validate(formula: str, z3_bin: str = None, debug: bool = False) -> dict:
|
||||
"""
|
||||
Validate an SMT-LIB2 formula by piping it through z3 -in.
|
||||
Returns a dict with 'valid' (bool), 'errors' (list), and 'raw' output.
|
||||
"""
|
||||
result = run_z3(
|
||||
formula,
|
||||
z3_bin=z3_bin,
|
||||
timeout=VALIDATION_TIMEOUT,
|
||||
debug=debug,
|
||||
)
|
||||
errors = find_errors(result["stdout"]) + find_errors(result["stderr"])
|
||||
|
||||
if result["result"] == "timeout":
|
||||
# Timeout during validation is not a syntax error: the formula
|
||||
# parsed successfully but solving exceeded the limit. That counts
|
||||
# as syntactically valid.
|
||||
return {"valid": True, "errors": [], "raw": result}
|
||||
|
||||
if errors or result["exit_code"] != 0:
|
||||
return {"valid": False, "errors": errors, "raw": result}
|
||||
|
||||
return {"valid": True, "errors": [], "raw": result}
|
||||
|
||||
|
||||
def report_errors(errors: list, formula: str):
|
||||
"""Print each syntax error with surrounding context."""
|
||||
lines = formula.splitlines()
|
||||
print(f"validation failed: {len(errors)} error(s)", file=sys.stderr)
|
||||
for err in errors:
|
||||
print(f" : {err}", file=sys.stderr)
|
||||
if len(lines) <= 20:
|
||||
print("formula:", file=sys.stderr)
|
||||
for i, line in enumerate(lines, 1):
|
||||
print(f" {i:3d} {line}", file=sys.stderr)
|
||||
|
||||
|
||||
def write_output(formula: str, output_path: str, fmt: str):
|
||||
"""Write the validated formula to a file or stdout."""
|
||||
if fmt == "python":
|
||||
print(
|
||||
"python format output is generated by the agent, " "not by this script",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if output_path:
|
||||
Path(output_path).write_text(formula)
|
||||
print(f"written to {output_path}")
|
||||
else:
|
||||
print(formula)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog="encode")
|
||||
parser.add_argument(
|
||||
"--validate",
|
||||
metavar="FILE",
|
||||
help="path to .smt2 file to validate, or '-' for stdin",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--format",
|
||||
choices=["smtlib2", "python"],
|
||||
default="smtlib2",
|
||||
help="output format (default: smtlib2)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
metavar="FILE",
|
||||
default=None,
|
||||
help="write result to file instead of stdout",
|
||||
)
|
||||
parser.add_argument("--z3", default=None, help="path to z3 binary")
|
||||
parser.add_argument("--db", default=None)
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_logging(args.debug)
|
||||
|
||||
if not args.validate:
|
||||
parser.error("provide --validate FILE")
|
||||
return
|
||||
|
||||
formula = read_input(args.validate)
|
||||
|
||||
db = Z3DB(args.db)
|
||||
run_id = db.start_run("encode", formula)
|
||||
|
||||
result = validate(formula, z3_bin=args.z3, debug=args.debug)
|
||||
|
||||
if result["valid"]:
|
||||
db.log_formula(run_id, formula, "valid")
|
||||
db.finish_run(run_id, "valid", result["raw"]["duration_ms"], 0)
|
||||
write_output(formula, args.output, args.format)
|
||||
db.close()
|
||||
sys.exit(0)
|
||||
else:
|
||||
db.log_formula(run_id, formula, "error")
|
||||
for err in result["errors"]:
|
||||
db.log_finding(run_id, "syntax", err, severity="error")
|
||||
db.finish_run(
|
||||
run_id,
|
||||
"error",
|
||||
result["raw"]["duration_ms"],
|
||||
result["raw"]["exit_code"],
|
||||
)
|
||||
report_errors(result["errors"], formula)
|
||||
db.close()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
83
.github/skills/explain/SKILL.md
vendored
Normal file
83
.github/skills/explain/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
name: explain
|
||||
description: Parse and interpret Z3 output for human consumption. Handles models, unsat cores, proofs, statistics, and error messages. Translates solver internals into plain-language explanations.
|
||||
---
|
||||
|
||||
Given raw Z3 output (from the **solve**, **prove**, **optimize**, or **benchmark** skills), produce a structured explanation. This skill is for cases where the solver output is large, nested, or otherwise difficult to read directly.
|
||||
|
||||
# Step 1: Identify the output type
|
||||
|
||||
Action:
|
||||
Determine the category of Z3 output to explain: model, core,
|
||||
statistics, error, or proof.
|
||||
|
||||
Expectation:
|
||||
The output type maps to one of the recognized formats in the table below.
|
||||
|
||||
Result:
|
||||
If the type is ambiguous, use `--type auto` and let the script detect it.
|
||||
Proceed to Step 2.
|
||||
|
||||
| Output contains | Explanation type |
|
||||
|----------------|-----------------|
|
||||
| `(define-fun ...)` blocks | model explanation |
|
||||
| unsat core labels | conflict explanation |
|
||||
| `:key value` statistics | performance breakdown |
|
||||
| `(error ...)` | error diagnosis |
|
||||
| proof terms | proof sketch |
|
||||
|
||||
# Step 2: Run the explainer
|
||||
|
||||
Action:
|
||||
Invoke explain.py with the output file or stdin.
|
||||
|
||||
Expectation:
|
||||
The script auto-detects the output type and produces a structured
|
||||
plain-language summary.
|
||||
|
||||
Result:
|
||||
A formatted explanation is printed. If detection fails, re-run with
|
||||
an explicit `--type` flag.
|
||||
|
||||
```bash
|
||||
python3 scripts/explain.py --file output.txt
|
||||
python3 scripts/explain.py --stdin < output.txt
|
||||
python3 scripts/explain.py --file output.txt --debug
|
||||
```
|
||||
|
||||
# Step 3: Interpret the explanation
|
||||
|
||||
Action:
|
||||
Review the structured explanation for accuracy and completeness.
|
||||
|
||||
Expectation:
|
||||
Models list each variable with its value and sort. Cores list
|
||||
conflicting assertions. Statistics show time and memory breakdowns.
|
||||
|
||||
Result:
|
||||
Use the explanation to answer the user query or to guide the next
|
||||
skill invocation.
|
||||
|
||||
For models:
|
||||
- Each variable is listed with its value and sort
|
||||
- Array and function interpretations are expanded
|
||||
- Bitvector values are shown in decimal and hex
|
||||
|
||||
For unsat cores:
|
||||
- The conflicting named assertions are listed
|
||||
- A minimal conflict set is highlighted
|
||||
|
||||
For statistics:
|
||||
- Time breakdown by phase (preprocessing, solving, model construction)
|
||||
- Theory solver load distribution
|
||||
- Memory high-water mark
|
||||
|
||||
# Parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| file | path | no | | file containing Z3 output |
|
||||
| stdin | flag | no | off | read from stdin |
|
||||
| type | string | no | auto | force output type: model, core, stats, error |
|
||||
| debug | flag | no | off | verbose tracing |
|
||||
| db | path | no | .z3-agent/z3agent.db | logging database |
|
||||
129
.github/skills/explain/scripts/explain.py
vendored
Normal file
129
.github/skills/explain/scripts/explain.py
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
explain.py: interpret Z3 output in a readable form.
|
||||
|
||||
Usage:
|
||||
python explain.py --file output.txt
|
||||
echo "sat\n(model ...)" | python explain.py --stdin
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "shared"))
|
||||
from z3db import Z3DB, parse_model, parse_stats, parse_unsat_core, setup_logging
|
||||
|
||||
|
||||
def detect_type(text: str) -> str:
|
||||
if "(define-fun" in text:
|
||||
return "model"
|
||||
if "(error" in text:
|
||||
return "error"
|
||||
if re.search(r":\S+\s+[\d.]+", text):
|
||||
return "stats"
|
||||
first = text.strip().split("\n")[0].strip()
|
||||
if first == "unsat":
|
||||
return "core"
|
||||
return "unknown"
|
||||
|
||||
|
||||
def explain_model(text: str):
|
||||
model = parse_model(text)
|
||||
if not model:
|
||||
print("no model found in output")
|
||||
return
|
||||
print("satisfying assignment:")
|
||||
for name, val in model.items():
|
||||
# show hex for large integers (likely bitvectors)
|
||||
try:
|
||||
n = int(val)
|
||||
if abs(n) > 255:
|
||||
print(f" {name} = {val} (0x{n:x})")
|
||||
else:
|
||||
print(f" {name} = {val}")
|
||||
except ValueError:
|
||||
print(f" {name} = {val}")
|
||||
|
||||
|
||||
def explain_core(text: str):
|
||||
core = parse_unsat_core(text)
|
||||
if core:
|
||||
print(f"conflicting assertions ({len(core)}):")
|
||||
for label in core:
|
||||
print(f" {label}")
|
||||
else:
|
||||
print("unsat (no named assertions for core extraction)")
|
||||
|
||||
|
||||
def explain_stats(text: str):
|
||||
stats = parse_stats(text)
|
||||
if not stats:
|
||||
print("no statistics found")
|
||||
return
|
||||
print("performance breakdown:")
|
||||
for k in sorted(stats):
|
||||
print(f" :{k} {stats[k]}")
|
||||
|
||||
if "time" in stats:
|
||||
print(f"\ntotal time: {stats['time']}s")
|
||||
if "memory" in stats:
|
||||
print(f"peak memory: {stats['memory']} MB")
|
||||
|
||||
|
||||
def explain_error(text: str):
|
||||
errors = re.findall(r'\(error\s+"([^"]+)"\)', text)
|
||||
if errors:
|
||||
print(f"Z3 reported {len(errors)} error(s):")
|
||||
for e in errors:
|
||||
print(f" {e}")
|
||||
else:
|
||||
print("error in output but could not parse message")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog="explain")
|
||||
parser.add_argument("--file")
|
||||
parser.add_argument("--stdin", action="store_true")
|
||||
parser.add_argument(
|
||||
"--type", choices=["model", "core", "stats", "error", "auto"], default="auto"
|
||||
)
|
||||
parser.add_argument("--db", default=None)
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_logging(args.debug)
|
||||
|
||||
if args.file:
|
||||
text = Path(args.file).read_text()
|
||||
elif args.stdin:
|
||||
text = sys.stdin.read()
|
||||
else:
|
||||
parser.error("provide --file or --stdin")
|
||||
return
|
||||
|
||||
output_type = args.type if args.type != "auto" else detect_type(text)
|
||||
|
||||
db = Z3DB(args.db)
|
||||
run_id = db.start_run("explain", text[:200])
|
||||
|
||||
if output_type == "model":
|
||||
explain_model(text)
|
||||
elif output_type == "core":
|
||||
explain_core(text)
|
||||
elif output_type == "stats":
|
||||
explain_stats(text)
|
||||
elif output_type == "error":
|
||||
explain_error(text)
|
||||
else:
|
||||
print("could not determine output type")
|
||||
print("raw output:")
|
||||
print(text[:500])
|
||||
|
||||
db.finish_run(run_id, "success", 0, 0)
|
||||
db.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
92
.github/skills/memory-safety/SKILL.md
vendored
Normal file
92
.github/skills/memory-safety/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
---
|
||||
name: memory-safety
|
||||
description: Run AddressSanitizer and UndefinedBehaviorSanitizer on the Z3 test suite to detect memory errors, undefined behavior, and leaks. Logs each finding to z3agent.db.
|
||||
---
|
||||
|
||||
Build Z3 with compiler-based sanitizer instrumentation, execute the test suite, and parse the runtime output for memory safety violations. Supported sanitizers are AddressSanitizer (heap and stack buffer overflows, use-after-free, double-free, memory leaks) and UndefinedBehaviorSanitizer (signed integer overflow, null pointer dereference, misaligned access, shift errors). Findings are deduplicated and stored in z3agent.db for triage and longitudinal tracking.
|
||||
|
||||
# Step 1: Configure and build
|
||||
|
||||
Action:
|
||||
Invoke the script with the desired sanitizer flag. The script calls
|
||||
cmake with the appropriate `-fsanitize` flags and builds the `test-z3`
|
||||
target. Each sanitizer uses a separate build directory to avoid flag
|
||||
conflicts.
|
||||
|
||||
Expectation:
|
||||
cmake configures successfully and make compiles the instrumented binary.
|
||||
If a prior build exists with matching flags, only incremental compilation
|
||||
runs.
|
||||
|
||||
Result:
|
||||
On success: an instrumented `test-z3` binary is ready in the build
|
||||
directory. Proceed to Step 2.
|
||||
On failure: verify compiler support for the requested sanitizer flags
|
||||
and review cmake output.
|
||||
|
||||
```bash
|
||||
python3 scripts/memory_safety.py --sanitizer asan
|
||||
python3 scripts/memory_safety.py --sanitizer ubsan
|
||||
python3 scripts/memory_safety.py --sanitizer both
|
||||
```
|
||||
|
||||
To reuse an existing build:
|
||||
```bash
|
||||
python3 scripts/memory_safety.py --sanitizer asan --skip-build --build-dir build/sanitizer-asan
|
||||
```
|
||||
|
||||
# Step 2: Run and collect
|
||||
|
||||
Action:
|
||||
Execute the instrumented test binary with halt_on_error=0 so all
|
||||
violations are reported rather than aborting on the first.
|
||||
|
||||
Expectation:
|
||||
The script parses AddressSanitizer, UndefinedBehaviorSanitizer, and
|
||||
LeakSanitizer patterns from combined output, extracts source locations,
|
||||
and deduplicates by category/file/line.
|
||||
|
||||
Result:
|
||||
On `clean`: no violations detected.
|
||||
On `findings`: one or more violations found, each printed with severity,
|
||||
category, message, and source location.
|
||||
On `timeout`: test suite did not finish; increase timeout or investigate.
|
||||
On `error`: build or execution failed before sanitizer output.
|
||||
|
||||
```bash
|
||||
python3 scripts/memory_safety.py --sanitizer asan --timeout 900 --debug
|
||||
```
|
||||
|
||||
# Step 3: Interpret results
|
||||
|
||||
Action:
|
||||
Review printed findings and query z3agent.db for historical comparison.
|
||||
|
||||
Expectation:
|
||||
Each finding includes severity, category, message, and source location.
|
||||
The database query returns prior runs for trend analysis.
|
||||
|
||||
Result:
|
||||
On `clean`: no action required; proceed.
|
||||
On `findings`: triage by severity and category. Compare against prior
|
||||
runs to distinguish new regressions from known issues.
|
||||
On `timeout`: increase the deadline or investigate a possible infinite
|
||||
loop.
|
||||
On `error`: inspect build logs before re-running.
|
||||
|
||||
Query past runs:
|
||||
```bash
|
||||
python3 ../../shared/z3db.py runs --skill memory-safety --last 10
|
||||
python3 ../../shared/z3db.py query "SELECT category, severity, file, line, message FROM findings WHERE run_id IN (SELECT run_id FROM runs WHERE skill='memory-safety') ORDER BY run_id DESC LIMIT 20"
|
||||
```
|
||||
|
||||
# Parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| sanitizer | choice | no | asan | which sanitizer to enable: asan, ubsan, or both |
|
||||
| build-dir | path | no | build/sanitizer-{name} | path to the build directory |
|
||||
| timeout | int | no | 600 | seconds before killing the test run |
|
||||
| skip-build | flag | no | off | reuse an existing instrumented build |
|
||||
| debug | flag | no | off | verbose cmake, make, and test output |
|
||||
| db | path | no | .z3-agent/z3agent.db | path to the logging database |
|
||||
274
.github/skills/memory-safety/scripts/memory_safety.py
vendored
Normal file
274
.github/skills/memory-safety/scripts/memory_safety.py
vendored
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
memory_safety.py: run sanitizer checks on Z3 test suite.
|
||||
|
||||
Usage:
|
||||
python memory_safety.py --sanitizer asan
|
||||
python memory_safety.py --sanitizer ubsan --debug
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "shared"))
|
||||
from z3db import Z3DB, require_repo_root, setup_logging
|
||||
|
||||
logger = logging.getLogger("z3agent")
|
||||
|
||||
SANITIZER_FLAGS = {
|
||||
"asan": "-fsanitize=address -fno-omit-frame-pointer",
|
||||
"ubsan": "-fsanitize=undefined -fno-omit-frame-pointer",
|
||||
}
|
||||
|
||||
ASAN_ERROR = re.compile(r"ERROR:\s*AddressSanitizer:\s*(\S+)")
|
||||
UBSAN_ERROR = re.compile(r":\d+:\d+:\s*runtime error:\s*(.+)")
|
||||
LEAK_ERROR = re.compile(r"ERROR:\s*LeakSanitizer:")
|
||||
LOCATION = re.compile(r"(\S+\.(?:cpp|c|h|hpp)):(\d+)")
|
||||
|
||||
|
||||
def check_dependencies():
|
||||
"""Fail early if required build tools are not on PATH."""
|
||||
missing = []
|
||||
if not shutil.which("cmake"):
|
||||
missing.append(("cmake", "sudo apt install cmake"))
|
||||
if not shutil.which("make"):
|
||||
missing.append(("make", "sudo apt install build-essential"))
|
||||
|
||||
cc = shutil.which("clang") or shutil.which("gcc")
|
||||
if not cc:
|
||||
missing.append(("clang or gcc", "sudo apt install clang"))
|
||||
|
||||
if missing:
|
||||
print("required tools not found:", file=sys.stderr)
|
||||
for tool, install in missing:
|
||||
print(f" {tool}: {install}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def build_is_configured(build_dir: Path, sanitizer: str) -> bool:
|
||||
"""Check whether the build directory already has a matching cmake config."""
|
||||
cache = build_dir / "CMakeCache.txt"
|
||||
if not cache.is_file():
|
||||
return False
|
||||
expected = SANITIZER_FLAGS[sanitizer].split()[0]
|
||||
return expected in cache.read_text()
|
||||
|
||||
|
||||
def configure(build_dir: Path, sanitizer: str, repo_root: Path) -> bool:
|
||||
"""Run cmake with the requested sanitizer flags."""
|
||||
flags = SANITIZER_FLAGS[sanitizer]
|
||||
build_dir.mkdir(parents=True, exist_ok=True)
|
||||
cmd = [
|
||||
"cmake", str(repo_root),
|
||||
f"-DCMAKE_C_FLAGS={flags}",
|
||||
f"-DCMAKE_CXX_FLAGS={flags}",
|
||||
f"-DCMAKE_EXE_LINKER_FLAGS={flags}",
|
||||
"-DCMAKE_BUILD_TYPE=Debug",
|
||||
"-DZ3_BUILD_TEST=ON",
|
||||
]
|
||||
logger.info("configuring %s build in %s", sanitizer, build_dir)
|
||||
logger.debug("cmake command: %s", " ".join(cmd))
|
||||
proc = subprocess.run(cmd, cwd=build_dir, capture_output=True, text=True)
|
||||
if proc.returncode != 0:
|
||||
logger.error("cmake failed:\n%s", proc.stderr)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def compile_tests(build_dir: Path) -> bool:
|
||||
"""Compile the test-z3 target."""
|
||||
nproc = os.cpu_count() or 4
|
||||
cmd = ["make", f"-j{nproc}", "test-z3"]
|
||||
logger.info("compiling test-z3 (%d parallel jobs)", nproc)
|
||||
proc = subprocess.run(cmd, cwd=build_dir, capture_output=True, text=True)
|
||||
if proc.returncode != 0:
|
||||
logger.error("compilation failed:\n%s", proc.stderr[-2000:])
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def run_tests(build_dir: Path, timeout: int) -> dict:
|
||||
"""Execute test-z3 under sanitizer runtime and capture output."""
|
||||
test_bin = build_dir / "test-z3"
|
||||
if not test_bin.is_file():
|
||||
logger.error("test-z3 not found at %s", test_bin)
|
||||
return {"stdout": "", "stderr": "binary not found", "exit_code": -1,
|
||||
"duration_ms": 0}
|
||||
|
||||
env = os.environ.copy()
|
||||
env["ASAN_OPTIONS"] = "detect_leaks=1:halt_on_error=0:print_stacktrace=1"
|
||||
env["UBSAN_OPTIONS"] = "print_stacktrace=1:halt_on_error=0"
|
||||
|
||||
cmd = [str(test_bin), "/a"]
|
||||
logger.info("running: %s", " ".join(cmd))
|
||||
start = time.monotonic()
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd, capture_output=True, text=True, timeout=timeout,
|
||||
cwd=build_dir, env=env,
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
ms = int((time.monotonic() - start) * 1000)
|
||||
logger.warning("test-z3 timed out after %dms", ms)
|
||||
return {"stdout": "", "stderr": "timeout", "exit_code": -1,
|
||||
"duration_ms": ms}
|
||||
|
||||
ms = int((time.monotonic() - start) * 1000)
|
||||
logger.debug("exit_code=%d duration=%dms", proc.returncode, ms)
|
||||
return {
|
||||
"stdout": proc.stdout,
|
||||
"stderr": proc.stderr,
|
||||
"exit_code": proc.returncode,
|
||||
"duration_ms": ms,
|
||||
}
|
||||
|
||||
|
||||
def parse_findings(output: str) -> list:
|
||||
"""Extract sanitizer error reports from combined stdout and stderr."""
|
||||
findings = []
|
||||
lines = output.split("\n")
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
entry = None
|
||||
|
||||
m = ASAN_ERROR.search(line)
|
||||
if m:
|
||||
entry = {"category": "asan", "message": m.group(1),
|
||||
"severity": "high"}
|
||||
|
||||
if not entry:
|
||||
m = LEAK_ERROR.search(line)
|
||||
if m:
|
||||
entry = {"category": "leak",
|
||||
"message": "detected memory leaks",
|
||||
"severity": "high"}
|
||||
|
||||
if not entry:
|
||||
m = UBSAN_ERROR.search(line)
|
||||
if m:
|
||||
entry = {"category": "ubsan", "message": m.group(1),
|
||||
"severity": "medium"}
|
||||
|
||||
if not entry:
|
||||
continue
|
||||
|
||||
file_path, line_no = None, None
|
||||
window = lines[max(0, i - 2):i + 5]
|
||||
for ctx in window:
|
||||
loc = LOCATION.search(ctx)
|
||||
if loc and "/usr/" not in loc.group(1):
|
||||
file_path = loc.group(1)
|
||||
line_no = int(loc.group(2))
|
||||
break
|
||||
|
||||
entry["file"] = file_path
|
||||
entry["line"] = line_no
|
||||
entry["raw"] = line.strip()
|
||||
findings.append(entry)
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def deduplicate(findings: list) -> list:
|
||||
"""Remove duplicate reports at the same category, file, and line."""
|
||||
seen = set()
|
||||
result = []
|
||||
for f in findings:
|
||||
key = (f["category"], f["file"], f["line"], f["message"])
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
result.append(f)
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog="memory-safety")
|
||||
parser.add_argument("--sanitizer", choices=["asan", "ubsan", "both"],
|
||||
default="asan",
|
||||
help="sanitizer to enable (default: asan)")
|
||||
parser.add_argument("--build-dir", default=None,
|
||||
help="path to build directory")
|
||||
parser.add_argument("--timeout", type=int, default=600,
|
||||
help="seconds before killing test run")
|
||||
parser.add_argument("--skip-build", action="store_true",
|
||||
help="reuse existing instrumented build")
|
||||
parser.add_argument("--db", default=None,
|
||||
help="path to z3agent.db")
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_logging(args.debug)
|
||||
check_dependencies()
|
||||
repo_root = require_repo_root()
|
||||
|
||||
sanitizers = ["asan", "ubsan"] if args.sanitizer == "both" else [args.sanitizer]
|
||||
all_findings = []
|
||||
|
||||
db = Z3DB(args.db)
|
||||
|
||||
for san in sanitizers:
|
||||
if args.build_dir:
|
||||
build_dir = Path(args.build_dir)
|
||||
else:
|
||||
build_dir = repo_root / "build" / f"sanitizer-{san}"
|
||||
|
||||
run_id = db.start_run("memory-safety", f"sanitizer={san}")
|
||||
db.log(f"sanitizer: {san}, build: {build_dir}", run_id=run_id)
|
||||
|
||||
if not args.skip_build:
|
||||
needs_configure = not build_is_configured(build_dir, san)
|
||||
if needs_configure and not configure(build_dir, san, repo_root):
|
||||
db.finish_run(run_id, "error", 0, exit_code=1)
|
||||
print(f"FAIL: cmake configuration failed for {san}")
|
||||
continue
|
||||
if not compile_tests(build_dir):
|
||||
db.finish_run(run_id, "error", 0, exit_code=1)
|
||||
print(f"FAIL: compilation failed for {san}")
|
||||
continue
|
||||
|
||||
result = run_tests(build_dir, args.timeout)
|
||||
combined = result["stdout"] + "\n" + result["stderr"]
|
||||
findings = deduplicate(parse_findings(combined))
|
||||
|
||||
for f in findings:
|
||||
db.log_finding(
|
||||
run_id,
|
||||
category=f["category"],
|
||||
message=f["message"],
|
||||
severity=f["severity"],
|
||||
file=f["file"],
|
||||
line=f["line"],
|
||||
details={"raw": f["raw"]},
|
||||
)
|
||||
|
||||
status = "clean" if not findings else "findings"
|
||||
if result["exit_code"] == -1:
|
||||
status = "timeout" if "timeout" in result["stderr"] else "error"
|
||||
|
||||
db.finish_run(run_id, status, result["duration_ms"], result["exit_code"])
|
||||
all_findings.extend(findings)
|
||||
print(f"{san}: {len(findings)} finding(s), {result['duration_ms']}ms")
|
||||
|
||||
if all_findings:
|
||||
print(f"\nTotal: {len(all_findings)} finding(s)")
|
||||
for f in all_findings:
|
||||
loc = f"{f['file']}:{f['line']}" if f["file"] else "unknown location"
|
||||
print(f" [{f['severity']}] {f['category']}: {f['message']} at {loc}")
|
||||
db.close()
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("\nNo sanitizer findings.")
|
||||
db.close()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
75
.github/skills/optimize/SKILL.md
vendored
Normal file
75
.github/skills/optimize/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
name: optimize
|
||||
description: Solve constrained optimization problems using Z3. Supports minimization and maximization of objective functions over integer, real, and bitvector domains.
|
||||
---
|
||||
|
||||
Given a set of constraints and an objective function, find the optimal value. Z3 supports both hard constraints (must hold) and soft constraints (weighted preferences), as well as lexicographic multi-objective optimization.
|
||||
|
||||
# Step 1: Formulate the problem
|
||||
|
||||
Action:
|
||||
Write constraints and an objective using `(minimize ...)` or
|
||||
`(maximize ...)` directives, followed by `(check-sat)` and `(get-model)`.
|
||||
|
||||
Expectation:
|
||||
A valid SMT-LIB2 formula with at least one optimization directive and
|
||||
all variables declared.
|
||||
|
||||
Result:
|
||||
If the formula is well-formed, proceed to Step 2. For multi-objective
|
||||
problems, list directives in priority order for lexicographic optimization.
|
||||
|
||||
Example: minimize `x + y` subject to `x >= 1`, `y >= 2`, `x + y <= 10`:
|
||||
```smtlib
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(assert (>= x 1))
|
||||
(assert (>= y 2))
|
||||
(assert (<= (+ x y) 10))
|
||||
(minimize (+ x y))
|
||||
(check-sat)
|
||||
(get-model)
|
||||
```
|
||||
|
||||
# Step 2: Run the optimizer
|
||||
|
||||
Action:
|
||||
Invoke optimize.py with the formula or file path.
|
||||
|
||||
Expectation:
|
||||
The script prints `sat` with the optimal assignment, `unsat`, `unknown`,
|
||||
or `timeout`. A run entry is logged to z3agent.db.
|
||||
|
||||
Result:
|
||||
On `sat`: proceed to Step 3 to read the optimal values.
|
||||
On `unsat` or `timeout`: check constraints for contradictions or simplify.
|
||||
|
||||
```bash
|
||||
python3 scripts/optimize.py --file scheduling.smt2
|
||||
python3 scripts/optimize.py --formula "<inline smt-lib2>" --debug
|
||||
```
|
||||
|
||||
# Step 3: Interpret the output
|
||||
|
||||
Action:
|
||||
Parse the objective value and satisfying assignment from the output.
|
||||
|
||||
Expectation:
|
||||
`sat` with a model containing the optimal value, `unsat` indicating
|
||||
infeasibility, or `unknown`/`timeout`.
|
||||
|
||||
Result:
|
||||
On `sat`: report the optimal value and assignment.
|
||||
On `unsat`: the constraints are contradictory, no feasible solution exists.
|
||||
On `unknown`/`timeout`: relax constraints or try **simplify**.
|
||||
|
||||
# Parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| formula | string | no | | SMT-LIB2 formula with minimize/maximize |
|
||||
| file | path | no | | path to .smt2 file |
|
||||
| timeout | int | no | 60 | seconds |
|
||||
| z3 | path | no | auto | path to z3 binary |
|
||||
| debug | flag | no | off | verbose tracing |
|
||||
| db | path | no | .z3-agent/z3agent.db | logging database |
|
||||
58
.github/skills/optimize/scripts/optimize.py
vendored
Normal file
58
.github/skills/optimize/scripts/optimize.py
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
optimize.py: solve constrained optimization problems via Z3.
|
||||
|
||||
Usage:
|
||||
python optimize.py --file scheduling.smt2
|
||||
python optimize.py --formula "(declare-const x Int)..." --debug
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "shared"))
|
||||
from z3db import Z3DB, run_z3, parse_model, setup_logging
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog="optimize")
|
||||
parser.add_argument("--formula")
|
||||
parser.add_argument("--file")
|
||||
parser.add_argument("--timeout", type=int, default=60)
|
||||
parser.add_argument("--z3", default=None)
|
||||
parser.add_argument("--db", default=None)
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_logging(args.debug)
|
||||
|
||||
if args.file:
|
||||
formula = Path(args.file).read_text()
|
||||
elif args.formula:
|
||||
formula = args.formula
|
||||
else:
|
||||
parser.error("provide --formula or --file")
|
||||
return
|
||||
|
||||
db = Z3DB(args.db)
|
||||
run_id = db.start_run("optimize", formula)
|
||||
|
||||
result = run_z3(formula, z3_bin=args.z3, timeout=args.timeout, debug=args.debug)
|
||||
|
||||
model = parse_model(result["stdout"]) if result["result"] == "sat" else None
|
||||
|
||||
db.log_formula(run_id, formula, result["result"], str(model) if model else None)
|
||||
db.finish_run(run_id, result["result"], result["duration_ms"], result["exit_code"])
|
||||
|
||||
print(result["result"])
|
||||
if model:
|
||||
for name, val in model.items():
|
||||
print(f" {name} = {val}")
|
||||
|
||||
db.close()
|
||||
sys.exit(0 if result["exit_code"] == 0 else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
83
.github/skills/prove/SKILL.md
vendored
Normal file
83
.github/skills/prove/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
name: prove
|
||||
description: Prove validity of logical statements by negation and satisfiability checking. If the negation is unsatisfiable, the original statement is valid. Otherwise a counterexample is returned.
|
||||
---
|
||||
|
||||
Given a conjecture (an SMT-LIB2 assertion or a natural language claim), determine whether it holds universally. The method is standard: negate the conjecture and check satisfiability. If the negation is unsatisfiable, the original is valid. If satisfiable, the model is a counterexample.
|
||||
|
||||
# Step 1: Prepare the negated formula
|
||||
|
||||
Action:
|
||||
Wrap the conjecture in `(assert (not ...))` and append
|
||||
`(check-sat)(get-model)`.
|
||||
|
||||
Expectation:
|
||||
A complete SMT-LIB2 formula that negates the original conjecture with
|
||||
all variables declared.
|
||||
|
||||
Result:
|
||||
If the negation is well-formed, proceed to Step 2.
|
||||
If the conjecture is natural language, run **encode** first.
|
||||
|
||||
Example: to prove that `(> x 3)` implies `(> x 1)`:
|
||||
```smtlib
|
||||
(declare-const x Int)
|
||||
(assert (not (=> (> x 3) (> x 1))))
|
||||
(check-sat)
|
||||
(get-model)
|
||||
```
|
||||
|
||||
# Step 2: Run the prover
|
||||
|
||||
Action:
|
||||
Invoke prove.py with the conjecture and variable declarations.
|
||||
|
||||
Expectation:
|
||||
The script prints `valid`, `invalid` (with counterexample), `unknown`,
|
||||
or `timeout`. A run entry is logged to z3agent.db.
|
||||
|
||||
Result:
|
||||
On `valid`: proceed to **explain** if the user needs a summary.
|
||||
On `invalid`: report the counterexample directly.
|
||||
On `unknown`/`timeout`: try **simplify** first, or increase the timeout.
|
||||
|
||||
```bash
|
||||
python3 scripts/prove.py --conjecture "(=> (> x 3) (> x 1))" --vars "x:Int"
|
||||
```
|
||||
|
||||
For file input where the file contains the full negated formula:
|
||||
```bash
|
||||
python3 scripts/prove.py --file negated.smt2
|
||||
```
|
||||
|
||||
With debug tracing:
|
||||
```bash
|
||||
python3 scripts/prove.py --conjecture "(=> (> x 3) (> x 1))" --vars "x:Int" --debug
|
||||
```
|
||||
|
||||
# Step 3: Interpret the output
|
||||
|
||||
Action:
|
||||
Read the prover output to determine validity of the conjecture.
|
||||
|
||||
Expectation:
|
||||
One of `valid`, `invalid` (with counterexample), `unknown`, or `timeout`.
|
||||
|
||||
Result:
|
||||
On `valid`: the conjecture holds universally.
|
||||
On `invalid`: the model shows a concrete counterexample.
|
||||
On `unknown`/`timeout`: the conjecture may require auxiliary lemmas or induction.
|
||||
|
||||
# Parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| conjecture | string | no | | the assertion to prove (without negation) |
|
||||
| vars | string | no | | variable declarations as "name:sort" pairs, comma-separated |
|
||||
| file | path | no | | .smt2 file with the negated formula |
|
||||
| timeout | int | no | 30 | seconds |
|
||||
| z3 | path | no | auto | path to z3 binary |
|
||||
| debug | flag | no | off | verbose tracing |
|
||||
| db | path | no | .z3-agent/z3agent.db | logging database |
|
||||
|
||||
Either `conjecture` (with `vars`) or `file` must be provided.
|
||||
82
.github/skills/prove/scripts/prove.py
vendored
Normal file
82
.github/skills/prove/scripts/prove.py
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
prove.py: prove validity by negation + satisfiability check.
|
||||
|
||||
Usage:
|
||||
python prove.py --conjecture "(=> (> x 3) (> x 1))" --vars "x:Int"
|
||||
python prove.py --file negated.smt2
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "shared"))
|
||||
from z3db import Z3DB, run_z3, parse_model, setup_logging
|
||||
|
||||
|
||||
def build_formula(conjecture: str, vars_str: str) -> str:
|
||||
lines = []
|
||||
if vars_str:
|
||||
for v in vars_str.split(","):
|
||||
v = v.strip()
|
||||
name, sort = v.split(":")
|
||||
lines.append(f"(declare-const {name.strip()} {sort.strip()})")
|
||||
lines.append(f"(assert (not {conjecture}))")
|
||||
lines.append("(check-sat)")
|
||||
lines.append("(get-model)")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog="prove")
|
||||
parser.add_argument("--conjecture", help="assertion to prove")
|
||||
parser.add_argument("--vars", help="variable declarations, e.g. 'x:Int,y:Bool'")
|
||||
parser.add_argument("--file", help="path to .smt2 file with negated formula")
|
||||
parser.add_argument("--timeout", type=int, default=30)
|
||||
parser.add_argument("--z3", default=None)
|
||||
parser.add_argument("--db", default=None)
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_logging(args.debug)
|
||||
|
||||
if args.file:
|
||||
formula = Path(args.file).read_text()
|
||||
elif args.conjecture:
|
||||
formula = build_formula(args.conjecture, args.vars or "")
|
||||
else:
|
||||
parser.error("provide --conjecture or --file")
|
||||
return
|
||||
|
||||
db = Z3DB(args.db)
|
||||
run_id = db.start_run("prove", formula)
|
||||
|
||||
result = run_z3(formula, z3_bin=args.z3, timeout=args.timeout, debug=args.debug)
|
||||
|
||||
if result["result"] == "unsat":
|
||||
verdict = "valid"
|
||||
elif result["result"] == "sat":
|
||||
verdict = "invalid"
|
||||
else:
|
||||
verdict = result["result"]
|
||||
|
||||
model = parse_model(result["stdout"]) if verdict == "invalid" else None
|
||||
|
||||
db.log_formula(run_id, formula, verdict, str(model) if model else None)
|
||||
db.finish_run(run_id, verdict, result["duration_ms"], result["exit_code"])
|
||||
|
||||
print(verdict)
|
||||
if model:
|
||||
print("counterexample:")
|
||||
for name, val in model.items():
|
||||
print(f" {name} = {val}")
|
||||
|
||||
db.close()
|
||||
# Exit 0 when we successfully determined validity or invalidity;
|
||||
# exit 1 only for errors/timeouts.
|
||||
sys.exit(0 if verdict in ("valid", "invalid") else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
57
.github/skills/shared/schema.sql
vendored
Normal file
57
.github/skills/shared/schema.sql
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
-- z3agent schema v1
|
||||
|
||||
PRAGMA journal_mode=WAL;
|
||||
PRAGMA foreign_keys=ON;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS runs (
|
||||
run_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
skill TEXT NOT NULL,
|
||||
input_hash TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'running',
|
||||
duration_ms INTEGER,
|
||||
exit_code INTEGER,
|
||||
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_runs_skill ON runs(skill);
|
||||
CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS formulas (
|
||||
formula_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
run_id INTEGER REFERENCES runs(run_id) ON DELETE CASCADE,
|
||||
smtlib2 TEXT NOT NULL,
|
||||
result TEXT,
|
||||
model TEXT,
|
||||
stats TEXT,
|
||||
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_formulas_run ON formulas(run_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_formulas_result ON formulas(result);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS findings (
|
||||
finding_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
run_id INTEGER REFERENCES runs(run_id) ON DELETE CASCADE,
|
||||
category TEXT NOT NULL,
|
||||
severity TEXT,
|
||||
file TEXT,
|
||||
line INTEGER,
|
||||
message TEXT NOT NULL,
|
||||
details TEXT,
|
||||
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_findings_run ON findings(run_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_findings_category ON findings(category);
|
||||
CREATE INDEX IF NOT EXISTS idx_findings_severity ON findings(severity);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS interaction_log (
|
||||
log_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
run_id INTEGER REFERENCES runs(run_id) ON DELETE SET NULL,
|
||||
level TEXT NOT NULL DEFAULT 'info',
|
||||
message TEXT NOT NULL,
|
||||
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_log_run ON interaction_log(run_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_log_level ON interaction_log(level);
|
||||
374
.github/skills/shared/z3db.py
vendored
Normal file
374
.github/skills/shared/z3db.py
vendored
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
z3db: shared library and CLI for Z3 skill scripts.
|
||||
|
||||
Library usage:
|
||||
from z3db import Z3DB, find_z3, find_repo_root, require_repo_root, run_z3
|
||||
|
||||
CLI usage:
|
||||
python z3db.py init
|
||||
python z3db.py status
|
||||
python z3db.py log [--run-id N]
|
||||
python z3db.py runs [--skill solve] [--last N]
|
||||
python z3db.py query "SELECT ..."
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
SCHEMA_PATH = Path(__file__).parent / "schema.sql"
|
||||
DEFAULT_DB_DIR = ".z3-agent"
|
||||
DEFAULT_DB_NAME = "z3agent.db"
|
||||
|
||||
logger = logging.getLogger("z3agent")
|
||||
|
||||
|
||||
def setup_logging(debug: bool = False):
|
||||
level = logging.DEBUG if debug else logging.INFO
|
||||
fmt = (
|
||||
"[%(levelname)s] %(message)s"
|
||||
if not debug
|
||||
else "[%(levelname)s %(asctime)s] %(message)s"
|
||||
)
|
||||
logging.basicConfig(level=level, format=fmt, stream=sys.stderr)
|
||||
|
||||
|
||||
class Z3DB:
|
||||
"""SQLite handle for z3agent.db, tracks runs, formulas, findings, logs."""
|
||||
|
||||
def __init__(self, db_path: Optional[str] = None):
|
||||
if db_path is None:
|
||||
db_dir = Path(DEFAULT_DB_DIR)
|
||||
db_dir.mkdir(exist_ok=True)
|
||||
db_path = str(db_dir / DEFAULT_DB_NAME)
|
||||
self.db_path = db_path
|
||||
self.conn = sqlite3.connect(db_path)
|
||||
self.conn.execute("PRAGMA foreign_keys=ON")
|
||||
self.conn.row_factory = sqlite3.Row
|
||||
self._init_schema()
|
||||
|
||||
def _init_schema(self):
|
||||
self.conn.executescript(SCHEMA_PATH.read_text())
|
||||
|
||||
def close(self):
|
||||
self.conn.close()
|
||||
|
||||
def start_run(self, skill: str, input_text: str = "") -> int:
|
||||
input_hash = hashlib.sha256(input_text.encode()).hexdigest()[:16]
|
||||
cur = self.conn.execute(
|
||||
"INSERT INTO runs (skill, input_hash) VALUES (?, ?)",
|
||||
(skill, input_hash),
|
||||
)
|
||||
self.conn.commit()
|
||||
run_id = cur.lastrowid
|
||||
logger.debug("started run %d (skill=%s, hash=%s)", run_id, skill, input_hash)
|
||||
return run_id
|
||||
|
||||
def finish_run(
|
||||
self, run_id: int, status: str, duration_ms: int, exit_code: int = 0
|
||||
):
|
||||
self.conn.execute(
|
||||
"UPDATE runs SET status=?, duration_ms=?, exit_code=? WHERE run_id=?",
|
||||
(status, duration_ms, exit_code, run_id),
|
||||
)
|
||||
self.conn.commit()
|
||||
logger.debug("finished run %d: %s (%dms)", run_id, status, duration_ms)
|
||||
|
||||
def log_formula(
|
||||
self,
|
||||
run_id: int,
|
||||
smtlib2: str,
|
||||
result: str = None,
|
||||
model: str = None,
|
||||
stats: dict = None,
|
||||
) -> int:
|
||||
cur = self.conn.execute(
|
||||
"INSERT INTO formulas (run_id, smtlib2, result, model, stats) "
|
||||
"VALUES (?, ?, ?, ?, ?)",
|
||||
(run_id, smtlib2, result, model, json.dumps(stats) if stats else None),
|
||||
)
|
||||
self.conn.commit()
|
||||
return cur.lastrowid
|
||||
|
||||
def log_finding(
|
||||
self,
|
||||
run_id: int,
|
||||
category: str,
|
||||
message: str,
|
||||
severity: str = None,
|
||||
file: str = None,
|
||||
line: int = None,
|
||||
details: dict = None,
|
||||
) -> int:
|
||||
cur = self.conn.execute(
|
||||
"INSERT INTO findings (run_id, category, severity, file, line, "
|
||||
"message, details) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
(
|
||||
run_id,
|
||||
category,
|
||||
severity,
|
||||
file,
|
||||
line,
|
||||
message,
|
||||
json.dumps(details) if details else None,
|
||||
),
|
||||
)
|
||||
self.conn.commit()
|
||||
return cur.lastrowid
|
||||
|
||||
def log(self, message: str, level: str = "info", run_id: int = None):
|
||||
"""Write to stderr and to the interaction_log table."""
|
||||
getattr(logger, level, logger.info)(message)
|
||||
self.conn.execute(
|
||||
"INSERT INTO interaction_log (run_id, level, message) VALUES (?, ?, ?)",
|
||||
(run_id, level, message),
|
||||
)
|
||||
self.conn.commit()
|
||||
|
||||
def get_runs(self, skill: str = None, last: int = 10):
|
||||
sql = "SELECT * FROM runs"
|
||||
params = []
|
||||
if skill:
|
||||
sql += " WHERE skill = ?"
|
||||
params.append(skill)
|
||||
sql += " ORDER BY run_id DESC LIMIT ?"
|
||||
params.append(last)
|
||||
return self.conn.execute(sql, params).fetchall()
|
||||
|
||||
def get_status(self) -> dict:
|
||||
rows = self.conn.execute(
|
||||
"SELECT status, COUNT(*) as cnt FROM runs GROUP BY status"
|
||||
).fetchall()
|
||||
total = sum(r["cnt"] for r in rows)
|
||||
by_status = {r["status"]: r["cnt"] for r in rows}
|
||||
last = self.conn.execute(
|
||||
"SELECT timestamp FROM runs ORDER BY run_id DESC LIMIT 1"
|
||||
).fetchone()
|
||||
return {
|
||||
"total": total,
|
||||
**by_status,
|
||||
"last_run": last["timestamp"] if last else None,
|
||||
}
|
||||
|
||||
def get_logs(self, run_id: int = None, last: int = 50):
|
||||
if run_id:
|
||||
return self.conn.execute(
|
||||
"SELECT * FROM interaction_log WHERE run_id=? "
|
||||
"ORDER BY log_id DESC LIMIT ?",
|
||||
(run_id, last),
|
||||
).fetchall()
|
||||
return self.conn.execute(
|
||||
"SELECT * FROM interaction_log ORDER BY log_id DESC LIMIT ?", (last,)
|
||||
).fetchall()
|
||||
|
||||
def query(self, sql: str):
|
||||
return self.conn.execute(sql).fetchall()
|
||||
|
||||
|
||||
def find_z3(hint: str = None) -> str:
|
||||
"""Locate the z3 binary: explicit path > build dirs > PATH."""
|
||||
candidates = []
|
||||
if hint:
|
||||
candidates.append(hint)
|
||||
|
||||
repo_root = find_repo_root()
|
||||
if repo_root:
|
||||
for build_dir in ["build", "build/release", "build/debug"]:
|
||||
candidates.append(str(repo_root / build_dir / "z3"))
|
||||
|
||||
path_z3 = shutil.which("z3")
|
||||
if path_z3:
|
||||
candidates.append(path_z3)
|
||||
|
||||
for c in candidates:
|
||||
p = Path(c)
|
||||
if p.is_file() and os.access(p, os.X_OK):
|
||||
logger.debug("found z3: %s", p)
|
||||
return str(p)
|
||||
|
||||
logger.error("z3 binary not found. Searched: %s", candidates)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def find_repo_root() -> Optional[Path]:
|
||||
"""Best-effort search for the Z3 repository root from the current directory."""
|
||||
d = Path.cwd()
|
||||
for _ in range(10):
|
||||
if (d / "CMakeLists.txt").exists() and (d / "src").is_dir():
|
||||
return d
|
||||
parent = d.parent
|
||||
if parent == d:
|
||||
break
|
||||
d = parent
|
||||
return None
|
||||
|
||||
|
||||
def require_repo_root() -> Path:
|
||||
"""Return the Z3 repository root or exit the process if it is not found."""
|
||||
repo_root = find_repo_root()
|
||||
if repo_root is None:
|
||||
logger.error("could not locate Z3 repository root")
|
||||
sys.exit(1)
|
||||
return repo_root
|
||||
|
||||
|
||||
def run_z3(
|
||||
formula: str,
|
||||
z3_bin: str = None,
|
||||
timeout: int = 30,
|
||||
args: list = None,
|
||||
debug: bool = False,
|
||||
) -> dict:
|
||||
"""Pipe an SMT-LIB2 formula into z3 -in, return parsed output."""
|
||||
z3_path = find_z3(z3_bin)
|
||||
cmd = [z3_path, "-in"] + (args or [])
|
||||
|
||||
logger.debug("cmd: %s", " ".join(cmd))
|
||||
logger.debug("stdin:\n%s", formula)
|
||||
|
||||
start = time.monotonic()
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
input=formula,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
duration_ms = int((time.monotonic() - start) * 1000)
|
||||
logger.warning("z3 timed out after %dms", duration_ms)
|
||||
return {
|
||||
"stdout": "",
|
||||
"stderr": "timeout",
|
||||
"exit_code": -1,
|
||||
"duration_ms": duration_ms,
|
||||
"result": "timeout",
|
||||
}
|
||||
|
||||
duration_ms = int((time.monotonic() - start) * 1000)
|
||||
|
||||
logger.debug("exit_code=%d duration=%dms", proc.returncode, duration_ms)
|
||||
logger.debug("stdout:\n%s", proc.stdout)
|
||||
if proc.stderr:
|
||||
logger.debug("stderr:\n%s", proc.stderr)
|
||||
|
||||
first_line = proc.stdout.strip().split("\n")[0].strip() if proc.stdout else ""
|
||||
result = first_line if first_line in ("sat", "unsat", "unknown") else "error"
|
||||
|
||||
return {
|
||||
"stdout": proc.stdout,
|
||||
"stderr": proc.stderr,
|
||||
"exit_code": proc.returncode,
|
||||
"duration_ms": duration_ms,
|
||||
"result": result,
|
||||
}
|
||||
|
||||
|
||||
def parse_model(stdout: str) -> Optional[dict]:
|
||||
"""Pull define-fun entries from a (get-model) response."""
|
||||
model = {}
|
||||
for m in re.finditer(r"\(define-fun\s+(\S+)\s+\(\)\s+\S+\s+(.+?)\)", stdout):
|
||||
model[m.group(1)] = m.group(2).strip()
|
||||
return model if model else None
|
||||
|
||||
|
||||
def parse_stats(stdout: str) -> Optional[dict]:
|
||||
"""Parse :key value pairs from z3 -st output."""
|
||||
stats = {}
|
||||
for m in re.finditer(r":(\S+)\s+([\d.]+)", stdout):
|
||||
key, val = m.group(1), m.group(2)
|
||||
stats[key] = float(val) if "." in val else int(val)
|
||||
return stats if stats else None
|
||||
|
||||
|
||||
def parse_unsat_core(stdout: str) -> Optional[list]:
|
||||
for line in stdout.strip().split("\n"):
|
||||
line = line.strip()
|
||||
if line.startswith("(") and not line.startswith("(error"):
|
||||
labels = line.strip("()").split()
|
||||
if labels:
|
||||
return labels
|
||||
return None
|
||||
|
||||
|
||||
def cli():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Z3 Agent database CLI",
|
||||
prog="z3db",
|
||||
)
|
||||
parser.add_argument("--db", default=None, help="path to z3agent.db")
|
||||
parser.add_argument("--debug", action="store_true", help="verbose output")
|
||||
|
||||
sub = parser.add_subparsers(dest="command")
|
||||
|
||||
sub.add_parser("init", help="initialize the database")
|
||||
|
||||
sub.add_parser("status", help="show run summary")
|
||||
|
||||
log_p = sub.add_parser("log", help="show interaction log")
|
||||
log_p.add_argument("--run-id", type=int, help="filter by run ID")
|
||||
log_p.add_argument("--last", type=int, default=50)
|
||||
|
||||
runs_p = sub.add_parser("runs", help="list runs")
|
||||
runs_p.add_argument("--skill", help="filter by skill name")
|
||||
runs_p.add_argument("--last", type=int, default=10)
|
||||
|
||||
query_p = sub.add_parser("query", help="run raw SQL")
|
||||
query_p.add_argument("sql", help="SQL query string")
|
||||
|
||||
args = parser.parse_args()
|
||||
setup_logging(args.debug)
|
||||
|
||||
db = Z3DB(args.db)
|
||||
|
||||
if args.command == "init":
|
||||
print(f"Database initialized at {db.db_path}")
|
||||
|
||||
elif args.command == "status":
|
||||
s = db.get_status()
|
||||
print(
|
||||
f"Runs: {s['total']}"
|
||||
f" | success: {s.get('success', 0)}"
|
||||
f" | error: {s.get('error', 0)}"
|
||||
f" | timeout: {s.get('timeout', 0)}"
|
||||
f" | Last: {s['last_run'] or 'never'}"
|
||||
)
|
||||
|
||||
elif args.command == "log":
|
||||
for row in db.get_logs(args.run_id, args.last):
|
||||
print(
|
||||
f"[{row['level']}] {row['timestamp']} "
|
||||
f"(run {row['run_id']}): {row['message']}"
|
||||
)
|
||||
|
||||
elif args.command == "runs":
|
||||
for row in db.get_runs(args.skill, args.last):
|
||||
print(
|
||||
f"#{row['run_id']} {row['skill']} {row['status']} "
|
||||
f"{row['duration_ms']}ms @ {row['timestamp']}"
|
||||
)
|
||||
|
||||
elif args.command == "query":
|
||||
for row in db.query(args.sql):
|
||||
print(dict(row))
|
||||
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
db.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
76
.github/skills/simplify/SKILL.md
vendored
Normal file
76
.github/skills/simplify/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
name: simplify
|
||||
description: Reduce formula complexity using Z3 tactic chains. Supports configurable tactic pipelines for boolean, arithmetic, and bitvector simplification.
|
||||
---
|
||||
|
||||
Given a formula, apply a sequence of Z3 tactics to produce an equivalent but simpler form. This is useful for understanding what Z3 sees after preprocessing, debugging tactic selection, and reducing formula size before solving.
|
||||
|
||||
# Step 1: Choose tactics
|
||||
|
||||
Action:
|
||||
Select a tactic chain from the available Z3 tactics based on the
|
||||
formula's theory.
|
||||
|
||||
Expectation:
|
||||
A comma-separated list of tactic names suitable for the formula domain.
|
||||
|
||||
Result:
|
||||
If unsure, use the default chain: `simplify,propagate-values,ctx-simplify`.
|
||||
For bitvector formulas, add `bit-blast`. Proceed to Step 2.
|
||||
|
||||
| Tactic | What it does |
|
||||
|--------|-------------|
|
||||
| simplify | constant folding, algebraic identities |
|
||||
| propagate-values | substitute known equalities |
|
||||
| ctx-simplify | context-dependent simplification |
|
||||
| elim-uncnstr | remove unconstrained variables |
|
||||
| solve-eqs | Gaussian elimination |
|
||||
| bit-blast | reduce bitvectors to booleans |
|
||||
| tseitin-cnf | convert to CNF |
|
||||
| aig | and-inverter graph reduction |
|
||||
|
||||
# Step 2: Run simplification
|
||||
|
||||
Action:
|
||||
Invoke simplify.py with the formula and optional tactic chain.
|
||||
|
||||
Expectation:
|
||||
The script applies each tactic in sequence and prints the simplified
|
||||
formula. A run entry is logged to z3agent.db.
|
||||
|
||||
Result:
|
||||
If the output is simpler, pass it to **solve** or **prove**.
|
||||
If unchanged, try a different tactic chain.
|
||||
|
||||
```bash
|
||||
python3 scripts/simplify.py --formula "(assert (and (> x 0) (> x 0)))" --vars "x:Int"
|
||||
python3 scripts/simplify.py --file formula.smt2 --tactics "simplify,propagate-values,ctx-simplify"
|
||||
python3 scripts/simplify.py --file formula.smt2 --debug
|
||||
```
|
||||
|
||||
Without `--tactics`, the script applies the default chain: `simplify`, `propagate-values`, `ctx-simplify`.
|
||||
|
||||
# Step 3: Interpret the output
|
||||
|
||||
Action:
|
||||
Read the simplified formula output in SMT-LIB2 syntax.
|
||||
|
||||
Expectation:
|
||||
One or more `(assert ...)` blocks representing equivalent subgoals.
|
||||
|
||||
Result:
|
||||
A smaller formula indicates successful reduction. Pass the result to
|
||||
**solve**, **prove**, or **optimize** as needed.
|
||||
|
||||
# Parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| formula | string | no | | SMT-LIB2 formula to simplify |
|
||||
| vars | string | no | | variable declarations as "name:sort" pairs |
|
||||
| file | path | no | | path to .smt2 file |
|
||||
| tactics | string | no | simplify,propagate-values,ctx-simplify | comma-separated tactic names |
|
||||
| timeout | int | no | 30 | seconds |
|
||||
| z3 | path | no | auto | path to z3 binary |
|
||||
| debug | flag | no | off | verbose tracing |
|
||||
| db | path | no | .z3-agent/z3agent.db | logging database |
|
||||
82
.github/skills/simplify/scripts/simplify.py
vendored
Normal file
82
.github/skills/simplify/scripts/simplify.py
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
simplify.py: apply Z3 tactics to simplify an SMT-LIB2 formula.
|
||||
|
||||
Usage:
|
||||
python simplify.py --formula "(assert (and (> x 0) (> x 0)))" --vars "x:Int"
|
||||
python simplify.py --file formula.smt2 --tactics "simplify,solve-eqs"
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "shared"))
|
||||
from z3db import Z3DB, run_z3, setup_logging
|
||||
|
||||
DEFAULT_TACTICS = "simplify,propagate-values,ctx-simplify"
|
||||
|
||||
|
||||
def build_tactic_formula(base_formula: str, tactics: str) -> str:
|
||||
tactic_list = [t.strip() for t in tactics.split(",")]
|
||||
if len(tactic_list) == 1:
|
||||
tactic_expr = f"(then {tactic_list[0]} skip)"
|
||||
else:
|
||||
tactic_expr = "(then " + " ".join(tactic_list) + ")"
|
||||
return base_formula + f"\n(apply {tactic_expr})\n"
|
||||
|
||||
|
||||
def build_formula_from_parts(formula_str: str, vars_str: str) -> str:
|
||||
lines = []
|
||||
if vars_str:
|
||||
for v in vars_str.split(","):
|
||||
v = v.strip()
|
||||
name, sort = v.split(":")
|
||||
lines.append(f"(declare-const {name.strip()} {sort.strip()})")
|
||||
lines.append(formula_str)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog="simplify")
|
||||
parser.add_argument("--formula")
|
||||
parser.add_argument("--vars")
|
||||
parser.add_argument("--file")
|
||||
parser.add_argument("--tactics", default=DEFAULT_TACTICS)
|
||||
parser.add_argument("--timeout", type=int, default=30)
|
||||
parser.add_argument("--z3", default=None)
|
||||
parser.add_argument("--db", default=None)
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_logging(args.debug)
|
||||
|
||||
if args.file:
|
||||
base = Path(args.file).read_text()
|
||||
elif args.formula:
|
||||
base = build_formula_from_parts(args.formula, args.vars or "")
|
||||
else:
|
||||
parser.error("provide --formula or --file")
|
||||
return
|
||||
|
||||
formula = build_tactic_formula(base, args.tactics)
|
||||
|
||||
db = Z3DB(args.db)
|
||||
run_id = db.start_run("simplify", formula)
|
||||
|
||||
result = run_z3(formula, z3_bin=args.z3, timeout=args.timeout, debug=args.debug)
|
||||
|
||||
status = "success" if result["exit_code"] == 0 else "error"
|
||||
db.log_formula(run_id, formula, status)
|
||||
db.finish_run(run_id, status, result["duration_ms"], result["exit_code"])
|
||||
|
||||
print(result["stdout"])
|
||||
if result["stderr"] and result["exit_code"] != 0:
|
||||
print(result["stderr"], file=sys.stderr)
|
||||
|
||||
db.close()
|
||||
sys.exit(0 if result["exit_code"] == 0 else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
75
.github/skills/solve/SKILL.md
vendored
Normal file
75
.github/skills/solve/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
name: solve
|
||||
description: Check satisfiability of SMT-LIB2 formulas using Z3. Returns sat/unsat with models or unsat cores. Logs every invocation to z3agent.db for auditability.
|
||||
---
|
||||
|
||||
Given an SMT-LIB2 formula (or a set of constraints described in natural language), determine whether the formula is satisfiable. If sat, extract a satisfying assignment. If unsat and tracking labels are present, extract the unsat core.
|
||||
|
||||
# Step 1: Prepare the formula
|
||||
|
||||
Action:
|
||||
Convert the input to valid SMT-LIB2. If the input is natural language,
|
||||
use the **encode** skill first.
|
||||
|
||||
Expectation:
|
||||
A syntactically valid SMT-LIB2 formula ending with `(check-sat)` and
|
||||
either `(get-model)` or `(get-unsat-core)` as appropriate.
|
||||
|
||||
Result:
|
||||
If valid SMT-LIB2 is ready, proceed to Step 2.
|
||||
If encoding is needed, run **encode** first and return here.
|
||||
|
||||
# Step 2: Run Z3
|
||||
|
||||
Action:
|
||||
Invoke solve.py with the formula string or file path.
|
||||
|
||||
Expectation:
|
||||
The script pipes the formula to `z3 -in`, logs the run to
|
||||
`.z3-agent/z3agent.db`, and prints the result.
|
||||
|
||||
Result:
|
||||
Output is one of `sat`, `unsat`, `unknown`, or `timeout`.
|
||||
Proceed to Step 3 to interpret.
|
||||
|
||||
```bash
|
||||
python3 scripts/solve.py --formula "(declare-const x Int)(assert (> x 0))(check-sat)(get-model)"
|
||||
```
|
||||
|
||||
For file input:
|
||||
```bash
|
||||
python3 scripts/solve.py --file problem.smt2
|
||||
```
|
||||
|
||||
With debug tracing:
|
||||
```bash
|
||||
python3 scripts/solve.py --file problem.smt2 --debug
|
||||
```
|
||||
|
||||
# Step 3: Interpret the output
|
||||
|
||||
Action:
|
||||
Parse the Z3 output to determine satisfiability and extract any model
|
||||
or unsat core.
|
||||
|
||||
Expectation:
|
||||
`sat` with a model, `unsat` optionally with a core, `unknown`, or
|
||||
`timeout`.
|
||||
|
||||
Result:
|
||||
On `sat`: report the model to the user.
|
||||
On `unsat`: report the core if available.
|
||||
On `unknown`/`timeout`: try **simplify** or increase the timeout.
|
||||
|
||||
# Parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| formula | string | no | | SMT-LIB2 formula as a string |
|
||||
| file | path | no | | path to an .smt2 file |
|
||||
| timeout | int | no | 30 | seconds before killing z3 |
|
||||
| z3 | path | no | auto | explicit path to z3 binary |
|
||||
| debug | flag | no | off | print z3 command, stdin, stdout, stderr, timing |
|
||||
| db | path | no | .z3-agent/z3agent.db | path to the logging database |
|
||||
|
||||
Either `formula` or `file` must be provided.
|
||||
64
.github/skills/solve/scripts/solve.py
vendored
Normal file
64
.github/skills/solve/scripts/solve.py
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
solve.py: check satisfiability of an SMT-LIB2 formula via Z3.
|
||||
|
||||
Usage:
|
||||
python solve.py --formula "(declare-const x Int)(assert (> x 0))(check-sat)(get-model)"
|
||||
python solve.py --file problem.smt2
|
||||
python solve.py --file problem.smt2 --debug --timeout 60
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "shared"))
|
||||
from z3db import Z3DB, run_z3, parse_model, parse_unsat_core, setup_logging
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog="solve")
|
||||
parser.add_argument("--formula", help="SMT-LIB2 formula string")
|
||||
parser.add_argument("--file", help="path to .smt2 file")
|
||||
parser.add_argument("--timeout", type=int, default=30)
|
||||
parser.add_argument("--z3", default=None, help="path to z3 binary")
|
||||
parser.add_argument("--db", default=None)
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_logging(args.debug)
|
||||
|
||||
if args.file:
|
||||
formula = Path(args.file).read_text()
|
||||
elif args.formula:
|
||||
formula = args.formula
|
||||
else:
|
||||
parser.error("provide --formula or --file")
|
||||
return
|
||||
|
||||
db = Z3DB(args.db)
|
||||
run_id = db.start_run("solve", formula)
|
||||
|
||||
result = run_z3(formula, z3_bin=args.z3, timeout=args.timeout, debug=args.debug)
|
||||
|
||||
model = parse_model(result["stdout"]) if result["result"] == "sat" else None
|
||||
core = parse_unsat_core(result["stdout"]) if result["result"] == "unsat" else None
|
||||
|
||||
db.log_formula(run_id, formula, result["result"], str(model) if model else None)
|
||||
db.finish_run(run_id, result["result"], result["duration_ms"], result["exit_code"])
|
||||
|
||||
print(result["result"])
|
||||
if model:
|
||||
for name, val in model.items():
|
||||
print(f" {name} = {val}")
|
||||
if core:
|
||||
print("unsat core:", " ".join(core))
|
||||
if result["stderr"] and result["result"] == "error":
|
||||
print(result["stderr"], file=sys.stderr)
|
||||
|
||||
db.close()
|
||||
sys.exit(0 if result["exit_code"] == 0 else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
81
.github/skills/static-analysis/SKILL.md
vendored
Normal file
81
.github/skills/static-analysis/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
name: static-analysis
|
||||
description: Run Clang Static Analyzer (scan-build) on Z3 source and log structured findings to z3agent.db.
|
||||
---
|
||||
|
||||
Run the Clang Static Analyzer over a CMake build of Z3, parse the resulting plist diagnostics, and record each finding with file, line, category, and description. This skill wraps scan-build into a reproducible, logged workflow suitable for regular analysis sweeps and regression tracking.
|
||||
|
||||
# Step 1: Run the analysis
|
||||
|
||||
Action:
|
||||
Invoke the script pointing at the CMake build directory. The script
|
||||
runs `scan-build cmake ..` followed by `scan-build make` and writes
|
||||
checker output to the output directory.
|
||||
|
||||
Expectation:
|
||||
scan-build completes within the timeout, producing plist diagnostic
|
||||
files in the output directory (defaults to a `scan-results` subdirectory
|
||||
of the build directory).
|
||||
|
||||
Result:
|
||||
On success: diagnostics are parsed and findings are printed. Proceed to
|
||||
Step 2.
|
||||
On failure: verify that clang and scan-build are installed and that the
|
||||
build directory contains a valid CMake configuration.
|
||||
|
||||
```bash
|
||||
python3 scripts/static_analysis.py --build-dir build
|
||||
python3 scripts/static_analysis.py --build-dir build --output-dir /tmp/sa-results --debug
|
||||
python3 scripts/static_analysis.py --build-dir build --timeout 1800
|
||||
```
|
||||
|
||||
# Step 2: Interpret the output
|
||||
|
||||
Action:
|
||||
Review the printed findings and the summary table grouped by category.
|
||||
|
||||
Expectation:
|
||||
Each finding shows its source location, category, and description.
|
||||
The summary table ranks categories by frequency for quick triage.
|
||||
|
||||
Result:
|
||||
On zero findings: the codebase passes all enabled static checks.
|
||||
On findings: prioritize by category frequency and severity. Address
|
||||
null dereferences and use-after-free classes first.
|
||||
|
||||
Example output:
|
||||
|
||||
```
|
||||
[Dead store] src/ast/ast.cpp:142: Value stored to 'result' is never read
|
||||
[Null dereference] src/smt/theory_lra.cpp:87: Access to field 'next' results in a dereference of a null pointer
|
||||
```
|
||||
|
||||
# Step 3: Review historical findings
|
||||
|
||||
Action:
|
||||
Query z3agent.db to compare current results against prior analysis
|
||||
runs.
|
||||
|
||||
Expectation:
|
||||
Queries return category counts and run history, enabling regression
|
||||
detection across commits.
|
||||
|
||||
Result:
|
||||
On stable or decreasing counts: no regressions introduced.
|
||||
On increased counts: cross-reference new findings with recent commits
|
||||
to identify the responsible change.
|
||||
|
||||
```bash
|
||||
python3 ../../shared/z3db.py query "SELECT category, COUNT(*) as cnt FROM findings WHERE run_id IN (SELECT run_id FROM runs WHERE skill='static-analysis') GROUP BY category ORDER BY cnt DESC"
|
||||
python3 ../../shared/z3db.py runs --skill static-analysis --last 10
|
||||
```
|
||||
|
||||
# Parameters
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| build-dir | path | yes | | path to the CMake build directory |
|
||||
| output-dir | path | no | BUILD/scan-results | directory for scan-build output |
|
||||
| timeout | int | no | 1200 | seconds allowed for the full build |
|
||||
| db | path | no | .z3-agent/z3agent.db | logging database |
|
||||
| debug | flag | no | off | verbose tracing |
|
||||
258
.github/skills/static-analysis/scripts/static_analysis.py
vendored
Normal file
258
.github/skills/static-analysis/scripts/static_analysis.py
vendored
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
static_analysis.py: run Clang Static Analyzer on Z3 source.
|
||||
|
||||
Usage:
|
||||
python static_analysis.py --build-dir build
|
||||
python static_analysis.py --build-dir build --output-dir /tmp/sa-results
|
||||
python static_analysis.py --build-dir build --debug
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import plistlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "shared"))
|
||||
from z3db import Z3DB, setup_logging
|
||||
|
||||
logger = logging.getLogger("z3agent")
|
||||
|
||||
SCAN_BUILD_NAMES = ["scan-build", "scan-build-14", "scan-build-15", "scan-build-16"]
|
||||
|
||||
|
||||
def find_scan_build() -> str:
|
||||
"""Locate the scan-build binary on PATH."""
|
||||
for name in SCAN_BUILD_NAMES:
|
||||
path = shutil.which(name)
|
||||
if path:
|
||||
logger.debug("found scan-build: %s", path)
|
||||
return path
|
||||
print(
|
||||
"scan-build not found on PATH.\n"
|
||||
"Install one of the following:\n"
|
||||
" Ubuntu/Debian: sudo apt install clang-tools\n"
|
||||
" macOS: brew install llvm\n"
|
||||
" Fedora: sudo dnf install clang-tools-extra\n"
|
||||
f"searched for: {', '.join(SCAN_BUILD_NAMES)}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def run_configure(scan_build: str, build_dir: Path, output_dir: Path,
|
||||
timeout: int) -> bool:
|
||||
"""Run scan-build cmake to configure the project."""
|
||||
repo_root = build_dir.parent
|
||||
cmd = [
|
||||
scan_build,
|
||||
"-o", str(output_dir),
|
||||
"cmake",
|
||||
str(repo_root),
|
||||
]
|
||||
logger.info("configuring: %s", " ".join(cmd))
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd, cwd=str(build_dir),
|
||||
capture_output=True, text=True, timeout=timeout,
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.error("cmake configuration timed out after %ds", timeout)
|
||||
return False
|
||||
|
||||
if proc.returncode != 0:
|
||||
logger.error("cmake configuration failed (exit %d)", proc.returncode)
|
||||
logger.error("stderr: %s", proc.stderr[:2000])
|
||||
return False
|
||||
|
||||
logger.info("configuration complete")
|
||||
return True
|
||||
|
||||
|
||||
def run_build(scan_build: str, build_dir: Path, output_dir: Path,
|
||||
timeout: int) -> bool:
|
||||
"""Run scan-build make to build and analyze."""
|
||||
nproc = os.cpu_count() or 4
|
||||
cmd = [
|
||||
scan_build,
|
||||
"-o", str(output_dir),
|
||||
"--status-bugs",
|
||||
"make",
|
||||
f"-j{nproc}",
|
||||
]
|
||||
logger.info("building with analysis: %s", " ".join(cmd))
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd, cwd=str(build_dir),
|
||||
capture_output=True, text=True, timeout=timeout,
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.error("build timed out after %ds", timeout)
|
||||
return False
|
||||
|
||||
# scan-build returns nonzero when bugs are found (due to --status-bugs),
|
||||
# so a nonzero exit code is not necessarily a build failure.
|
||||
if proc.returncode != 0:
|
||||
logger.info(
|
||||
"scan-build exited with code %d (nonzero may indicate findings)",
|
||||
proc.returncode,
|
||||
)
|
||||
else:
|
||||
logger.info("build complete, no bugs reported by scan-build")
|
||||
|
||||
if proc.stderr:
|
||||
logger.debug("build stderr (last 2000 chars): %s", proc.stderr[-2000:])
|
||||
return True
|
||||
|
||||
|
||||
def collect_plist_files(output_dir: Path) -> list:
|
||||
"""Recursively find all .plist diagnostic files under the output directory."""
|
||||
plists = sorted(output_dir.rglob("*.plist"))
|
||||
logger.debug("found %d plist files in %s", len(plists), output_dir)
|
||||
return plists
|
||||
|
||||
|
||||
def parse_plist_findings(plist_path: Path) -> list:
|
||||
"""Extract findings from a single Clang plist diagnostic file.
|
||||
|
||||
Returns a list of dicts with keys: file, line, col, category, type, description.
|
||||
"""
|
||||
findings = []
|
||||
try:
|
||||
with open(plist_path, "rb") as f:
|
||||
data = plistlib.load(f)
|
||||
except Exception as exc:
|
||||
logger.warning("could not parse %s: %s", plist_path, exc)
|
||||
return findings
|
||||
|
||||
source_files = data.get("files", [])
|
||||
for diag in data.get("diagnostics", []):
|
||||
location = diag.get("location", {})
|
||||
file_idx = location.get("file", 0)
|
||||
source_file = source_files[file_idx] if file_idx < len(source_files) else "<unknown>"
|
||||
findings.append({
|
||||
"file": source_file,
|
||||
"line": location.get("line", 0),
|
||||
"col": location.get("col", 0),
|
||||
"category": diag.get("category", "uncategorized"),
|
||||
"type": diag.get("type", ""),
|
||||
"description": diag.get("description", ""),
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def collect_all_findings(output_dir: Path) -> list:
|
||||
"""Parse every plist file under output_dir and return merged findings."""
|
||||
all_findings = []
|
||||
for plist_path in collect_plist_files(output_dir):
|
||||
all_findings.extend(parse_plist_findings(plist_path))
|
||||
return all_findings
|
||||
|
||||
|
||||
def log_findings(db, run_id: int, findings: list):
|
||||
"""Persist each finding to z3agent.db."""
|
||||
for f in findings:
|
||||
db.log_finding(
|
||||
run_id,
|
||||
category=f["category"],
|
||||
message=f["description"],
|
||||
severity=f.get("type"),
|
||||
file=f["file"],
|
||||
line=f["line"],
|
||||
details={"col": f["col"], "type": f["type"]},
|
||||
)
|
||||
|
||||
|
||||
def print_findings(findings: list):
|
||||
"""Print individual findings and a category summary."""
|
||||
if not findings:
|
||||
print("No findings reported.")
|
||||
return
|
||||
|
||||
for f in findings:
|
||||
label = f["type"] or f["category"]
|
||||
print(f"[{label}] {f['file']}:{f['line']}: {f['description']}")
|
||||
|
||||
print()
|
||||
counts = Counter(f["category"] for f in findings)
|
||||
print(f"Total findings: {len(findings)}")
|
||||
print("By category:")
|
||||
for cat, cnt in counts.most_common():
|
||||
print(f" {cat}: {cnt}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="static_analysis",
|
||||
description="Run Clang Static Analyzer on Z3 and log findings.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--build-dir", required=True,
|
||||
help="path to the CMake build directory",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output-dir", default=None,
|
||||
help="directory for scan-build results (default: BUILD/scan-results)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--timeout", type=int, default=1200,
|
||||
help="seconds allowed for the full analysis build",
|
||||
)
|
||||
parser.add_argument("--db", default=None, help="path to z3agent.db")
|
||||
parser.add_argument("--debug", action="store_true", help="verbose tracing")
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_logging(args.debug)
|
||||
|
||||
scan_build = find_scan_build()
|
||||
|
||||
build_dir = Path(args.build_dir).resolve()
|
||||
build_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
output_dir = Path(args.output_dir) if args.output_dir else build_dir / "scan-results"
|
||||
output_dir = output_dir.resolve()
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
db = Z3DB(args.db)
|
||||
run_id = db.start_run("static-analysis", f"build_dir={build_dir}")
|
||||
start = time.monotonic()
|
||||
|
||||
if not run_configure(scan_build, build_dir, output_dir, timeout=args.timeout):
|
||||
elapsed = int((time.monotonic() - start) * 1000)
|
||||
db.finish_run(run_id, "error", elapsed, exit_code=1)
|
||||
db.close()
|
||||
sys.exit(1)
|
||||
|
||||
if not run_build(scan_build, build_dir, output_dir, timeout=args.timeout):
|
||||
elapsed = int((time.monotonic() - start) * 1000)
|
||||
db.finish_run(run_id, "error", elapsed, exit_code=1)
|
||||
db.close()
|
||||
sys.exit(1)
|
||||
|
||||
elapsed = int((time.monotonic() - start) * 1000)
|
||||
|
||||
findings = collect_all_findings(output_dir)
|
||||
log_findings(db, run_id, findings)
|
||||
|
||||
status = "clean" if len(findings) == 0 else "findings"
|
||||
db.finish_run(run_id, status, elapsed, exit_code=0)
|
||||
|
||||
db.log(
|
||||
f"static analysis complete: {len(findings)} finding(s) in {elapsed}ms",
|
||||
run_id=run_id,
|
||||
)
|
||||
|
||||
print_findings(findings)
|
||||
|
||||
db.close()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
14
.github/workflows/Windows.yml
vendored
14
.github/workflows/Windows.yml
vendored
|
|
@ -28,18 +28,19 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
uses: microsoft/setup-msbuild@v3
|
||||
- run: |
|
||||
md build
|
||||
cd build
|
||||
${{ matrix.cmd1 }}
|
||||
${{ matrix.cmd2 }}
|
||||
${{ matrix.cmd3 }}
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.arch }}
|
||||
cmake ${{ matrix.bindings }} -G "NMake Makefiles" ../
|
||||
nmake
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.arch }} || exit /b 1
|
||||
cmake ${{ matrix.bindings }} -G "NMake Makefiles" ../ || exit /b 1
|
||||
nmake || exit /b 1
|
||||
cd ..
|
||||
shell: cmd
|
||||
- name: Run Regressions
|
||||
|
|
@ -52,7 +53,8 @@ jobs:
|
|||
if: ${{ matrix.test }}
|
||||
run: |
|
||||
pushd build
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.arch }}
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.arch }} || exit /b 1
|
||||
pushd build\python
|
||||
python z3test.py z3
|
||||
python z3test.py z3num
|
||||
|
|
|
|||
1066
.github/workflows/a3-python-v2.lock.yml
generated
vendored
1066
.github/workflows/a3-python-v2.lock.yml
generated
vendored
File diff suppressed because it is too large
Load diff
568
.github/workflows/a3-python-v2.md
vendored
568
.github/workflows/a3-python-v2.md
vendored
|
|
@ -1,568 +0,0 @@
|
|||
---
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0" # Weekly on Sundays at midnight UTC
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
network:
|
||||
allowed: [defaults, python]
|
||||
safe-outputs:
|
||||
create-issue:
|
||||
labels:
|
||||
- bug
|
||||
- automated-analysis
|
||||
- a3-python
|
||||
title-prefix: "[a3-python] "
|
||||
description: Analyzes Python code using a3-python tool to identify bugs and issues
|
||||
name: A3 Python Code Analysis
|
||||
strict: true
|
||||
timeout-minutes: 45
|
||||
tracker-id: a3-python-analysis
|
||||
steps:
|
||||
- name: Checkout Python source files
|
||||
run: |
|
||||
git sparse-checkout init --cone
|
||||
git sparse-checkout set src
|
||||
echo "Source files checked out for Python analysis"
|
||||
source: z3prover/z3/a3/a3-python-v2.md@a91c5c58bd975f336bf5b744885ffd4b36b2d2ec
|
||||
---
|
||||
|
||||
# A3 Python Code Analysis Agent
|
||||
|
||||
You are an expert Python code analyst using the a3-python tool to identify bugs and code quality issues. Your mission is to analyze the Python codebase, identify true positives from the analysis output, and create GitHub issues when multiple likely issues are found.
|
||||
|
||||
## Current Context
|
||||
|
||||
- **Repository**: ${{ github.repository }}
|
||||
- **Workspace**: ${{ github.workspace }}
|
||||
|
||||
## Phase 1: Install and Setup a3-python
|
||||
|
||||
### 1.1 Install a3-python
|
||||
|
||||
Install the a3-python tool from PyPI:
|
||||
|
||||
```bash
|
||||
pip install a3-python
|
||||
```
|
||||
|
||||
Verify installation:
|
||||
|
||||
```bash
|
||||
a3 --version || python -m a3 --version || echo "a3 command not found in PATH"
|
||||
```
|
||||
|
||||
### 1.2 Check Available Commands
|
||||
|
||||
```bash
|
||||
a3 --help || python -m a3 --help
|
||||
```
|
||||
|
||||
## Phase 2: Run Analysis on Python Source Files
|
||||
|
||||
### 2.1 Identify Python Files
|
||||
|
||||
Find and verify Python source files in the repository:
|
||||
|
||||
```bash
|
||||
# Check common source directories
|
||||
find ${{ github.workspace }} -name "*.py" -type f | grep -E "(src/|lib/|app/|project/)" | head -20
|
||||
|
||||
# List all Python files in the repository
|
||||
find ${{ github.workspace }} -name "*.py" -type f | head -30
|
||||
```
|
||||
|
||||
### 2.2 Run a3-python Analysis
|
||||
|
||||
Run the a3 scan command on the repository to analyze all Python files:
|
||||
|
||||
```bash
|
||||
cd ${{ github.workspace }}
|
||||
|
||||
# Ensure PATH includes a3 command
|
||||
export PATH="$PATH:/home/runner/.local/bin"
|
||||
|
||||
# Run a3 scan on the repository
|
||||
if command -v a3 &> /dev/null; then
|
||||
# Run with multiple options for comprehensive analysis
|
||||
a3 scan . --verbose --dse-verify --deduplicate --consolidate-variants > a3-python-output.txt 2>&1 || \
|
||||
a3 scan . --verbose --functions --dse-verify > a3-python-output.txt 2>&1 || \
|
||||
a3 scan . --verbose > a3-python-output.txt 2>&1 || \
|
||||
echo "a3 scan command failed with all variations" > a3-python-output.txt
|
||||
elif python -m a3 --help &> /dev/null; then
|
||||
python -m a3 scan . > a3-python-output.txt 2>&1 || \
|
||||
echo "python -m a3 scan command failed" > a3-python-output.txt
|
||||
else
|
||||
echo "ERROR: a3-python tool not available" > a3-python-output.txt
|
||||
fi
|
||||
|
||||
# Verify output was generated
|
||||
ls -lh a3-python-output.txt
|
||||
cat a3-python-output.txt
|
||||
```
|
||||
|
||||
**Important**: The a3-python tool will analyze all Python files found in the repository, focusing on source code directories.
|
||||
|
||||
## Phase 3: Post-Process and Analyze Results
|
||||
|
||||
### 3.1 Review the Output
|
||||
|
||||
Read and analyze the contents of `a3-python-output.txt`:
|
||||
|
||||
```bash
|
||||
cat a3-python-output.txt
|
||||
```
|
||||
|
||||
### 3.2 Classify Findings
|
||||
|
||||
For each issue reported in the output, determine:
|
||||
|
||||
1. **True Positives (Likely Issues)**: Real bugs or code quality problems that should be addressed
|
||||
- Logic errors or bugs
|
||||
- Security vulnerabilities
|
||||
- Performance issues
|
||||
- Code quality problems
|
||||
- Broken imports or dependencies
|
||||
- Type mismatches or incorrect usage
|
||||
|
||||
2. **False Positives**: Findings that are not real issues
|
||||
- Style preferences without functional impact
|
||||
- Intentional design decisions
|
||||
- Test-related code patterns
|
||||
- Generated code or third-party code
|
||||
- Overly strict warnings without merit
|
||||
- **Assertion violations at the beginning of functions** (these are pre-conditions and intentional design)
|
||||
- Parameter validation checks in function entry points
|
||||
|
||||
### 3.3 Extract Source Code Context
|
||||
|
||||
For each true positive finding, extract the relevant source code context to make the report more readable:
|
||||
|
||||
```bash
|
||||
# Function to extract source code context around a specific line
|
||||
extract_code_context() {
|
||||
local file="$1"
|
||||
local line_num="$2"
|
||||
local context_lines="${3:-5}" # Default to 5 lines before/after
|
||||
|
||||
if [[ -f "$file" ]]; then
|
||||
echo "```python"
|
||||
# Show line numbers and context
|
||||
sed -n "$((line_num - context_lines)),$((line_num + context_lines))p" "$file" | \
|
||||
awk -v start=$((line_num - context_lines)) -v target=$line_num '{
|
||||
line_number = NR + start - 1
|
||||
if (line_number == target) {
|
||||
printf "%4d:❌ %s\n", line_number, $0
|
||||
} else {
|
||||
printf "%4d: %s\n", line_number, $0
|
||||
}
|
||||
}'
|
||||
echo "```"
|
||||
else
|
||||
echo "File not found: $file"
|
||||
fi
|
||||
}
|
||||
|
||||
# Export the function for use in subshells
|
||||
export -f extract_code_context
|
||||
```
|
||||
|
||||
For each true positive, collect the source code context:
|
||||
|
||||
```bash
|
||||
# Example usage for each finding:
|
||||
# extract_code_context "path/to/file.py" "line_number" 5
|
||||
|
||||
# Store contexts in a temporary file for later use in the issue
|
||||
echo "# Source Code Contexts" > source_contexts.md
|
||||
echo "" >> source_contexts.md
|
||||
|
||||
# For each true positive finding, extract context and append to file
|
||||
# Example workflow:
|
||||
for finding in "${true_positives[@]}"; do
|
||||
file=$(echo "$finding" | grep -o 'File: [^,]*' | cut -d' ' -f2)
|
||||
line=$(echo "$finding" | grep -o 'Line: [0-9]*' | cut -d' ' -f2)
|
||||
|
||||
if [[ -n "$file" && -n "$line" ]]; then
|
||||
echo "## Finding in $file at line $line" >> source_contexts.md
|
||||
extract_code_context "$file" "$line" 5 >> source_contexts.md
|
||||
echo "" >> source_contexts.md
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
### 3.5 Enhanced Analysis Workflow
|
||||
|
||||
Create an enhanced analysis workflow that automatically extracts source code context. **IMPORTANT**: Limit detailed examples to top 5 high-severity findings only.
|
||||
|
||||
```bash
|
||||
# Parse a3-python output and extract file/line information
|
||||
parse_findings() {
|
||||
local output_file="$1"
|
||||
|
||||
# Create arrays to store findings
|
||||
declare -a high_severity=()
|
||||
declare -a medium_severity=()
|
||||
declare -a false_positives=()
|
||||
|
||||
# Parse the output file and extract findings with file/line info
|
||||
# This is a template - adjust parsing based on actual a3-python output format
|
||||
while IFS= read -r line; do
|
||||
if [[ "$line" =~ ^.*\.py:[0-9]+:.* ]]; then
|
||||
# Extract file and line number from the finding
|
||||
file=$(echo "$line" | grep -oP '[\w/.-]+\.py')
|
||||
line_num=$(echo "$line" | grep -oP '\.py:\K[0-9]+')
|
||||
description=$(echo "$line" | cut -d':' -f3-)
|
||||
|
||||
echo "Found potential issue: $file:$line_num - $description"
|
||||
|
||||
# Classify by severity and type
|
||||
# High severity: NULL_PTR, DIV_ZERO
|
||||
# Medium severity: BOUNDS, ASSERT_FAIL (except pre-condition assertions)
|
||||
# False positives: Assertion violations at function start (pre-conditions)
|
||||
|
||||
if [[ "$description" =~ ASSERT_FAIL ]] && [[ $line_num -lt 10 ]]; then
|
||||
# Likely a pre-condition assertion at start of function
|
||||
false_positives+=("File: $file, Line: $line_num, Description: $description")
|
||||
elif [[ "$description" =~ (NULL_PTR|DIV_ZERO) ]]; then
|
||||
high_severity+=("File: $file, Line: $line_num, Description: $description")
|
||||
else
|
||||
medium_severity+=("File: $file, Line: $line_num, Description: $description")
|
||||
fi
|
||||
fi
|
||||
done < "$output_file"
|
||||
|
||||
# Generate enhanced report with TOP 5 HIGH-SEVERITY findings only in detail
|
||||
echo "# Enhanced Analysis Report" > enhanced_report.md
|
||||
echo "" >> enhanced_report.md
|
||||
echo "## Sample High-Severity Findings (Top 5)" >> enhanced_report.md
|
||||
echo "" >> enhanced_report.md
|
||||
|
||||
local counter=1
|
||||
local max_samples=5
|
||||
for finding in "${high_severity[@]}"; do
|
||||
if [ $counter -gt $max_samples ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
file=$(echo "$finding" | grep -o 'File: [^,]*' | cut -d' ' -f2)
|
||||
line_num=$(echo "$finding" | grep -o 'Line: [^,]*' | cut -d' ' -f2)
|
||||
desc=$(echo "$finding" | grep -o 'Description: .*' | cut -d' ' -f2-)
|
||||
|
||||
echo "### $counter. $desc" >> enhanced_report.md
|
||||
echo "**Location**: \`$file:$line_num\`" >> enhanced_report.md
|
||||
echo "" >> enhanced_report.md
|
||||
|
||||
if [[ -f "$file" ]]; then
|
||||
extract_code_context "$file" "$line_num" 5 >> enhanced_report.md
|
||||
else
|
||||
echo "```" >> enhanced_report.md
|
||||
echo "File not found: $file" >> enhanced_report.md
|
||||
echo "```" >> enhanced_report.md
|
||||
fi
|
||||
|
||||
echo "" >> enhanced_report.md
|
||||
((counter++))
|
||||
done
|
||||
|
||||
# Add summary statistics
|
||||
echo "## Summary Statistics" >> enhanced_report.md
|
||||
echo "- High Severity: ${#high_severity[@]}" >> enhanced_report.md
|
||||
echo "- Medium Severity: ${#medium_severity[@]}" >> enhanced_report.md
|
||||
echo "- False Positives: ${#false_positives[@]}" >> enhanced_report.md
|
||||
|
||||
# Display the enhanced report
|
||||
echo "=== Enhanced Analysis Report ==="
|
||||
cat enhanced_report.md
|
||||
}
|
||||
|
||||
# Run the enhanced analysis
|
||||
parse_findings "a3-python-output.txt"
|
||||
```
|
||||
|
||||
**Note**: The complete list of all findings should be added to a collapsible `<details>` section in the GitHub issue, not shown in full detail.
|
||||
|
||||
### 3.4 Categorize and Count
|
||||
|
||||
Create a structured analysis with source code context:
|
||||
|
||||
```markdown
|
||||
## Analysis Results
|
||||
|
||||
### 3.4 Categorize and Count
|
||||
|
||||
Create a structured analysis with source code context:
|
||||
|
||||
```markdown
|
||||
## Analysis Results
|
||||
|
||||
### High-Severity Issues (for detailed examples):
|
||||
1. [Issue 1 Description] - File: path/to/file.py, Line: X
|
||||
**Source Code Context:**
|
||||
```python
|
||||
[Line numbers with context - error line marked with ❌]
|
||||
```
|
||||
|
||||
2. [Issue 2 Description] - File: path/to/file.py, Line: Y
|
||||
**Source Code Context:**
|
||||
```python
|
||||
[Line numbers with context - error line marked with ❌]
|
||||
```
|
||||
|
||||
(Limit to top 5 high-severity for detailed display)
|
||||
|
||||
### False Positives:
|
||||
1. [FP 1 Description] - Reason: Pre-condition assertion at function start
|
||||
2. [FP 2 Description] - Reason for dismissal
|
||||
...
|
||||
|
||||
### Summary:
|
||||
- Total findings: X
|
||||
- High severity (NULL_PTR, DIV_ZERO): Y
|
||||
- Medium severity (BOUNDS, ASSERT_FAIL): Z
|
||||
- False positives (including pre-conditions): W
|
||||
```
|
||||
|
||||
## Phase 4: Create GitHub Issue (Conditional)
|
||||
|
||||
### 4.1 Determine If Issue Creation Is Needed
|
||||
|
||||
Create a GitHub issue **ONLY IF**:
|
||||
- ✅ There are **2 or more** true positives (likely issues)
|
||||
- ✅ The issues are actionable and specific
|
||||
- ✅ The analysis completed successfully
|
||||
|
||||
**Do NOT create an issue if**:
|
||||
- ❌ Zero or one true positive found
|
||||
- ❌ Only false positives detected
|
||||
- ❌ Analysis failed to run
|
||||
- ❌ Output file is empty or contains only errors
|
||||
|
||||
### 4.2 Generate Issue Description
|
||||
|
||||
**Important**: Use the enhanced report generated in Phase 3.5 to populate the issue with source code context.
|
||||
|
||||
If creating an issue, use this structure:
|
||||
|
||||
```markdown
|
||||
## A3 Python Code Analysis - [Date]
|
||||
|
||||
This issue reports **[number]** DSE-confirmed bugs identified by a3-python analysis tool across the Z3 Python API.
|
||||
|
||||
### Executive Summary
|
||||
|
||||
- **Total Findings**: X confirmed bugs
|
||||
- **High Severity**: Y (NULL_PTR: N1, DIV_ZERO: N2)
|
||||
- **Medium Severity**: Z (BOUNDS: N3, ASSERT_FAIL: N4)
|
||||
- **Analysis Method**: Deep Symbolic Execution (DSE) verification
|
||||
|
||||
### Files Most Affected
|
||||
|
||||
| File | Issues |
|
||||
|------|--------|
|
||||
| `path/to/file1.py` | X |
|
||||
| `path/to/file2.py` | Y |
|
||||
| `path/to/file3.py` | Z |
|
||||
|
||||
(Show only top 3-5 files)
|
||||
|
||||
### Sample High-Severity Findings
|
||||
|
||||
#### 1. [BUG_TYPE] in `function_name`
|
||||
|
||||
**Location**: `path/to/file.py:line_number`
|
||||
|
||||
```python
|
||||
10: def some_function():
|
||||
11: value = None
|
||||
12: ❌ return value.upper() # Error: NoneType has no attribute 'upper'
|
||||
13:
|
||||
14: # Rest of function...
|
||||
```
|
||||
|
||||
#### 2. [BUG_TYPE] in `function_name`
|
||||
|
||||
**Location**: `path/to/file.py:line_number`
|
||||
|
||||
```python
|
||||
25: if condition:
|
||||
26: result = process_data()
|
||||
27: ❌ return result # Error: 'result' may be undefined
|
||||
28: # Missing else clause
|
||||
```
|
||||
|
||||
(Show only top 5 high-severity examples with code context)
|
||||
|
||||
### Bug Type Analysis
|
||||
|
||||
| Type | Count | Description |
|
||||
|------|-------|-------------|
|
||||
| NULL_PTR | X | Potential None/null dereferences |
|
||||
| BOUNDS | Y | Array/string index out of bounds |
|
||||
| ASSERT_FAIL | Z | Assertion violations |
|
||||
| DIV_ZERO | W | Division by zero errors |
|
||||
|
||||
### Methodology
|
||||
|
||||
This analysis used **a3-python** with:
|
||||
- ✅ **Deep Symbolic Execution (DSE)**: Confirms bug reachability via concrete paths
|
||||
- ✅ **Barrier Theory**: Attempts to prove safety before flagging
|
||||
- ✅ **Multi-strategy verification**: 7+ verification techniques
|
||||
|
||||
All [number] issues are **DSE-confirmed**, meaning the tool verified these errors are reachable through real execution paths.
|
||||
|
||||
### Recommended Actions
|
||||
|
||||
**Immediate Priority** (High Severity - X issues):
|
||||
1. Add null/None checks before dereferences in core API functions
|
||||
2. Validate division denominators to prevent DIV_ZERO
|
||||
3. Focus on `most_affected_file.py` (N issues)
|
||||
|
||||
**Medium Priority** (Y issues):
|
||||
1. Add bounds checking for array/string indexing
|
||||
2. Review and strengthen assertion conditions
|
||||
3. Add comprehensive error handling
|
||||
|
||||
**Long-term**:
|
||||
1. Adopt comprehensive input validation across Python API
|
||||
2. Use Python type hints consistently (e.g., `Optional[T]`)
|
||||
3. Consider defensive programming patterns for C API wrappers
|
||||
|
||||
### Complete Analysis Data
|
||||
|
||||
<details>
|
||||
<summary>All [number] findings grouped by file (click to expand)</summary>
|
||||
|
||||
**path/to/file1.py** (X issues)
|
||||
- BUG_TYPE: N issues
|
||||
- Line Y: `function_name`
|
||||
- Line Z: `function_name`
|
||||
...
|
||||
|
||||
**path/to/file2.py** (X issues)
|
||||
- BUG_TYPE: N issues
|
||||
- Line Y: `function_name`
|
||||
...
|
||||
|
||||
(List ALL findings in collapsed section)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Raw a3-python output excerpt (click to expand)</summary>
|
||||
|
||||
```
|
||||
[PASTE FIRST 50-100 LINES OF a3-python-output.txt HERE FOR REFERENCE]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
*Note: All findings have been DSE-confirmed by a3-python's deep symbolic execution engine. For questions about specific findings, run `a3 scan` locally for detailed analysis.*
|
||||
```
|
||||
|
||||
### 4.3 Use Safe Outputs
|
||||
|
||||
Create the issue using the safe-outputs configuration:
|
||||
|
||||
- Title will be prefixed with `[a3-python]`
|
||||
- Labeled with `bug`, `automated-analysis`, `a3-python`
|
||||
- Contains structured analysis with actionable findings
|
||||
|
||||
## Important Guidelines
|
||||
|
||||
### Analysis Quality
|
||||
- **Be thorough**: Review all findings carefully
|
||||
- **Be accurate**: Distinguish real issues from false positives
|
||||
- **Be specific**: Provide file names, line numbers, and descriptions
|
||||
- **Be actionable**: Include recommendations for fixes
|
||||
- **Be concise**: Focus on the most critical findings in the main issue body
|
||||
|
||||
### Issue Formatting Best Practices
|
||||
- **Limit sample findings**: Show only top 5 high-severity examples with code context
|
||||
- **Use collapsible sections**: Put complete analysis data in `<details>` tags
|
||||
- **Prioritize readability**: Organize by severity and actionability, not just by file
|
||||
- **Avoid duplication**: Don't repeat the same information in multiple formats
|
||||
- **Keep it focused**: The issue should be scannable in under 2 minutes
|
||||
|
||||
### Classification Criteria
|
||||
|
||||
**True Positives** should meet these criteria:
|
||||
- The issue represents a real bug or problem
|
||||
- It could impact functionality, security, or performance
|
||||
- It's actionable with a clear fix
|
||||
- It's in code owned by the repository (not third-party)
|
||||
|
||||
**False Positives** typically include:
|
||||
- Style preferences without functional impact
|
||||
- Intentional design decisions that are correct
|
||||
- Test code patterns that look unusual but are valid
|
||||
- Generated or vendored code
|
||||
- Overly pedantic warnings
|
||||
- **Assertion violations at the beginning of functions** (these are pre-conditions)
|
||||
- Parameter validation checks (assert statements checking input parameters)
|
||||
|
||||
### Threshold for Issue Creation
|
||||
- **2+ true positives**: Create an issue with all findings
|
||||
- **1 true positive**: Do not create an issue (not enough to warrant it)
|
||||
- **0 true positives**: Exit gracefully without creating an issue
|
||||
|
||||
### Exit Conditions
|
||||
|
||||
Exit gracefully without creating an issue if:
|
||||
- Analysis tool failed to run or install
|
||||
- Python source files were not found or checked out properly
|
||||
- No Python files found in repository
|
||||
- Output file is empty or invalid
|
||||
- Zero or one true positive identified
|
||||
- All findings are false positives
|
||||
|
||||
### Success Metrics
|
||||
|
||||
A successful analysis:
|
||||
- ✅ Completes without errors
|
||||
- ✅ Generates comprehensive output with source code context
|
||||
- ✅ Accurately classifies findings
|
||||
- ✅ Creates actionable issue when appropriate
|
||||
- ✅ Provides clear recommendations with visual code examples
|
||||
- ✅ Shows error lines with surrounding context for better understanding
|
||||
|
||||
## Output Requirements
|
||||
|
||||
Your output MUST either:
|
||||
|
||||
1. **If analysis fails or no findings**:
|
||||
```
|
||||
✅ A3 Python analysis completed.
|
||||
No significant issues found - 0 or 1 true positive detected.
|
||||
```
|
||||
|
||||
2. **If 2+ true positives found**: Create an issue with:
|
||||
- Clear executive summary with statistics
|
||||
- Files most affected table (top 3-5 only)
|
||||
- **ONLY top 5 high-severity findings** with detailed source code context
|
||||
- Bug type analysis summary table
|
||||
- Methodology explanation
|
||||
- Recommended actions (prioritized)
|
||||
- **Complete findings list** in collapsed `<details>` section
|
||||
- **Raw output excerpt** (first 50-100 lines) in collapsed `<details>` section
|
||||
|
||||
**Critical**: Do NOT list all findings in the main issue body. Keep sample findings to 5 maximum and put comprehensive data in collapsible sections.
|
||||
|
||||
## Enhanced Workflow Summary
|
||||
|
||||
The enhanced workflow now includes:
|
||||
|
||||
1. **Automated Source Code Context Extraction**: The `extract_code_context` function automatically extracts 5 lines before and after each error location
|
||||
2. **Visual Error Highlighting**: Error lines are marked with ❌ for easy identification
|
||||
3. **Severity-Based Classification**: Automatically categorizes findings as high/medium severity
|
||||
4. **False Positive Detection**: Identifies pre-condition assertions at function entry points
|
||||
5. **Concise Reporting**: Limits detailed examples to top 5 high-severity findings
|
||||
6. **Progressive Disclosure**: Uses collapsible sections for complete data
|
||||
7. **Enhanced GitHub Issues**: Issues are scannable, actionable, and well-organized
|
||||
|
||||
Begin the analysis now. Install a3-python, run analysis on the repository, save output to a3-python-output.txt, extract source code context for findings, classify by severity, and create a GitHub issue if 2 or more likely issues are found. **Remember to keep the main issue body concise with only top 5 examples and put complete findings in a collapsed section.**
|
||||
1732
.github/workflows/a3-python.lock.yml
generated
vendored
1732
.github/workflows/a3-python.lock.yml
generated
vendored
File diff suppressed because one or more lines are too long
12
.github/workflows/a3-python.md
vendored
12
.github/workflows/a3-python.md
vendored
|
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0" # Weekly on Sundays at midnight UTC
|
||||
schedule: weekly on sunday
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
@ -10,23 +9,20 @@ permissions:
|
|||
network:
|
||||
allowed: [defaults, python]
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-issue:
|
||||
labels:
|
||||
- bug
|
||||
- automated-analysis
|
||||
- a3-python
|
||||
title-prefix: "[a3-python] "
|
||||
noop:
|
||||
report-as-issue: false
|
||||
description: Analyzes Python code using a3-python tool to identify bugs and issues
|
||||
name: A3 Python Code Analysis
|
||||
strict: true
|
||||
timeout-minutes: 45
|
||||
tracker-id: a3-python-analysis
|
||||
steps:
|
||||
- name: Checkout Python source files
|
||||
run: |
|
||||
git sparse-checkout init --cone
|
||||
git sparse-checkout set src
|
||||
echo "Python source files checked out from src directory"
|
||||
---
|
||||
|
||||
# A3 Python Code Analysis Agent
|
||||
|
|
|
|||
1668
.github/workflows/academic-citation-tracker.lock.yml
generated
vendored
Normal file
1668
.github/workflows/academic-citation-tracker.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
299
.github/workflows/academic-citation-tracker.md
vendored
Normal file
299
.github/workflows/academic-citation-tracker.md
vendored
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
---
|
||||
description: >
|
||||
Monthly Academic Citation & Research Trend Tracker for Z3.
|
||||
Searches arXiv, Semantic Scholar, and GitHub for recent papers and projects
|
||||
using Z3, analyses which Z3 features they rely on, and identifies the
|
||||
functionality — features or performance — most important to address next.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 6 1 * *"
|
||||
workflow_dispatch:
|
||||
|
||||
timeout-minutes: 60
|
||||
|
||||
permissions: read-all
|
||||
|
||||
network:
|
||||
allowed:
|
||||
- defaults
|
||||
- export.arxiv.org
|
||||
- api.semanticscholar.org
|
||||
- github
|
||||
|
||||
tools:
|
||||
cache-memory: true
|
||||
web-fetch: {}
|
||||
github:
|
||||
toolsets: [default, repos]
|
||||
bash: [":*"]
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
mentions: false
|
||||
allowed-github-references: []
|
||||
max-bot-mentions: 1
|
||||
create-discussion:
|
||||
title-prefix: "[Research Trends] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
expires: 60d
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
|
||||
---
|
||||
|
||||
# Academic Citation & Research Trend Tracker
|
||||
|
||||
## Job Description
|
||||
|
||||
Your name is ${{ github.workflow }}. You are an expert research analyst for the Z3
|
||||
theorem prover repository `${{ github.repository }}`. Your mission is to find recent
|
||||
academic papers and open-source projects that use Z3, understand *which Z3 features*
|
||||
they rely on, and synthesise what this reveals about the features and performance
|
||||
improvements that would have the greatest community impact.
|
||||
|
||||
## Your Task
|
||||
|
||||
### 1. Initialise or Resume Progress (Cache Memory)
|
||||
|
||||
Check cache memory for:
|
||||
- Papers and projects already covered in the previous run (DOIs, arXiv IDs, GitHub repo URLs)
|
||||
- Feature-usage counts accumulated across runs
|
||||
- Date of the last run
|
||||
|
||||
Use the cached data so this run focuses on **new** material (last 30 days by default; if no prior cache exists, cover the last 90 days).
|
||||
Initialise an empty tracking structure if the cache is absent.
|
||||
|
||||
### 2. Collect Recent Papers
|
||||
|
||||
#### 2.1 arXiv Search
|
||||
|
||||
Fetch recent papers that mention Z3 as a core tool. Use the arXiv API.
|
||||
First compute the date 30 days ago (or 90 days for the initial run) in YYYYMMDD format,
|
||||
then pass it as the `submittedDate` range filter:
|
||||
|
||||
```bash
|
||||
# Compute the start date (30 days ago)
|
||||
START_DATE=$(date -d "30 days ago" +%Y%m%d 2>/dev/null || date -v-30d +%Y%m%d)
|
||||
TODAY=$(date +%Y%m%d)
|
||||
|
||||
# Papers mentioning Z3 in cs.PL, cs.LO, cs.SE, cs.CR, cs.FM categories
|
||||
curl -s "https://export.arxiv.org/api/query?search_query=all:Z3+solver+AND+(cat:cs.PL+OR+cat:cs.LO+OR+cat:cs.SE+OR+cat:cs.CR+OR+cat:cs.FM)&submittedDate=[${START_DATE}2359+TO+${TODAY}2359]&sortBy=submittedDate&sortOrder=descending&max_results=40" \
|
||||
-o /tmp/arxiv-results.xml
|
||||
```
|
||||
|
||||
Parse the XML for: title, authors, abstract, arXiv ID, submission date, primary category.
|
||||
|
||||
#### 2.2 Semantic Scholar Search
|
||||
|
||||
Fetch recent papers via the Semantic Scholar API, filtering to the current year
|
||||
(or year-1 for the initial run) to surface only recent work:
|
||||
|
||||
```bash
|
||||
CURRENT_YEAR=$(date +%Y)
|
||||
|
||||
curl -s "https://api.semanticscholar.org/graph/v1/paper/search?query=Z3+theorem+prover&fields=title,authors,year,abstract,externalIds,citationCount,venue&limit=40&sort=relevance&year=${CURRENT_YEAR}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-o /tmp/s2-results.json
|
||||
```
|
||||
|
||||
Merge with the arXiv results (de-duplicate by DOI / arXiv ID).
|
||||
|
||||
#### 2.3 GitHub Projects
|
||||
|
||||
Use the GitHub MCP server tools to find recently-active repositories that depend on
|
||||
or study Z3. Use these example search strategies:
|
||||
- Repos with the `z3` topic pushed in the last 30 days:
|
||||
`topic:z3 pushed:>YYYY-MM-DD` (substitute the actual date)
|
||||
- Repos depending on z3 Python package with recent activity:
|
||||
`z3-solver in:file filename:requirements.txt pushed:>YYYY-MM-DD`
|
||||
- Repos referencing Z3Prover in README:
|
||||
`Z3Prover/z3 in:readme pushed:>YYYY-MM-DD`
|
||||
|
||||
Limit to the 20 most-relevant results; filter out the Z3 repo itself (`Z3Prover/z3`).
|
||||
|
||||
#### 2.4 Filter for Genuine Z3 Usage
|
||||
|
||||
Keep only results where Z3 is used as a *core* component (not just a passing mention).
|
||||
Discard:
|
||||
- Papers that mention Z3 only in a reference list
|
||||
- Repos that list z3 as an optional or dev dependency only
|
||||
- Papers behind hard paywalls where the abstract cannot be fetched
|
||||
|
||||
### 3. Analyse Feature Usage
|
||||
|
||||
For each retained paper or project extract, from the abstract, full text (when
|
||||
accessible), README, or source code:
|
||||
|
||||
**Z3 Feature / API Surface Used:**
|
||||
- SMT-LIB2 formula input (`check-sat`, `get-model`, theory declarations)
|
||||
- Python API (`z3py`) — which theories: Int/Real arithmetic, BitVectors, Arrays, Strings/Sequences, Uninterpreted Functions, Quantifiers
|
||||
- C/C++ API
|
||||
- Other language bindings (Java, C#, OCaml, JavaScript/WASM)
|
||||
- Fixedpoint / Datalog (`z3.Fixedpoint`)
|
||||
- Optimisation (`z3.Optimize`, MaxSMT)
|
||||
- Proofs / DRAT
|
||||
- Tactics and solvers (e.g., `qfbv`, `spacer`, `elim-quantifiers`, `nlsat`)
|
||||
- Incremental solving (`push`/`pop`, assumptions)
|
||||
- Model generation and evaluation
|
||||
- Interpolation / Horn clause solving (Spacer/PDR)
|
||||
- SMTCOMP/evaluation benchmarks
|
||||
|
||||
**Application Domain:**
|
||||
- Program verification / deductive verification
|
||||
- Symbolic execution / concolic testing
|
||||
- Security (vulnerability discovery, protocol verification, exploit generation)
|
||||
- Type checking / language design
|
||||
- Hardware verification
|
||||
- Constraint solving / planning / scheduling
|
||||
- Formal specification / theorem proving assistance
|
||||
- Compiler correctness
|
||||
- Machine learning / neural network verification
|
||||
- Other
|
||||
|
||||
**Pain Points Mentioned:**
|
||||
Note any explicit mentions of Z3 limitations, performance issues, missing features,
|
||||
workarounds, or comparisons where Z3 underperformed.
|
||||
|
||||
### 4. Aggregate Trends
|
||||
|
||||
Compute over all papers and projects collected (this run + cache history):
|
||||
- **Feature popularity ranking**: which APIs/theories appear most frequently
|
||||
- **Domain ranking**: which application areas use Z3 most
|
||||
- **Performance pain-point frequency**: mentions of timeouts, scalability, memory, or
|
||||
regression across Z3 versions
|
||||
- **Feature gap signals**: features requested but absent, or workarounds applied
|
||||
- **New vs. returning features**: compare with previous month's top features to spot
|
||||
rising or falling trends
|
||||
|
||||
### 5. Correlate with Open Issues and PRs
|
||||
|
||||
Use the GitHub MCP server to search the Z3 issue tracker and recent PRs for signals
|
||||
that align with the academic findings:
|
||||
- Are the performance pain-points also reflected in open issues?
|
||||
- Do any open feature requests map to high-demand research use-cases?
|
||||
- Are there recent PRs that address any of the identified gaps?
|
||||
|
||||
This produces a prioritised list of development recommendations grounded in both
|
||||
community usage and academic demand.
|
||||
|
||||
### 6. Generate the Discussion Report
|
||||
|
||||
Create a GitHub Discussion. Use `###` or lower for all section headers.
|
||||
Wrap verbose tables or lists in `<details>` tags to keep the report scannable.
|
||||
|
||||
Title: `[Research Trends] Academic Citation & Research Trend Report — [Month YYYY]`
|
||||
|
||||
Suggested structure:
|
||||
|
||||
```markdown
|
||||
**Period covered**: [start date] – [end date]
|
||||
**Papers analysed**: N (arXiv: N, Semantic Scholar: N, new this run: N)
|
||||
**GitHub projects analysed**: N (new this run: N)
|
||||
|
||||
### Executive Summary
|
||||
|
||||
2–3 sentences: headline finding about where Z3 is being used and what the
|
||||
community most needs.
|
||||
|
||||
### Top Z3 Features Used
|
||||
|
||||
| Rank | Feature / API | Papers | Projects | Trend vs. Last Month |
|
||||
|------|--------------|--------|----------|----------------------|
|
||||
| 1 | z3py – BitVectors | N | N | ↑ / ↓ / → |
|
||||
| … |
|
||||
|
||||
### Application Domain Breakdown
|
||||
|
||||
| Domain | Papers | % of Total |
|
||||
|--------|--------|------------|
|
||||
| Program verification | N | N% |
|
||||
| … |
|
||||
|
||||
### Performance & Feature Pain-Points
|
||||
|
||||
List the most-cited pain-points with representative quotes or paraphrases from
|
||||
abstracts/READMEs. Group by theme (scalability, string solver performance, API
|
||||
ergonomics, missing theories, etc.).
|
||||
|
||||
<details>
|
||||
<summary><b>All Pain-Point Mentions</b></summary>
|
||||
|
||||
One entry per paper/project that mentions a pain-point.
|
||||
|
||||
</details>
|
||||
|
||||
### Recommended Development Priorities
|
||||
|
||||
Ranked list of Z3 features or performance improvements most likely to have broad
|
||||
research impact, with rationale tied to specific evidence:
|
||||
|
||||
1. **[Priority 1]** — evidence: N papers, N projects, N related issues
|
||||
2. …
|
||||
|
||||
### Correlation with Open Issues / PRs
|
||||
|
||||
Issues and PRs in Z3Prover/z3 that align with the identified research priorities.
|
||||
|
||||
| Issue / PR | Title | Alignment |
|
||||
|-----------|-------|-----------|
|
||||
| #NNN | … | [feature / pain-point it addresses] |
|
||||
|
||||
### Notable New Papers
|
||||
|
||||
Brief description of 3–5 particularly interesting papers, their use of Z3, and
|
||||
any Z3-specific insights.
|
||||
|
||||
<details>
|
||||
<summary><b>All Papers This Run</b></summary>
|
||||
|
||||
| Source | Title | Authors | Date | Features Used | Domain |
|
||||
|--------|-------|---------|------|--------------|--------|
|
||||
| arXiv:XXXX.XXXXX | … | … | … | … | … |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>All GitHub Projects This Run</b></summary>
|
||||
|
||||
| Repository | Stars | Updated | Features Used | Domain |
|
||||
|-----------|-------|---------|--------------|--------|
|
||||
| owner/repo | N | YYYY-MM-DD | … | … |
|
||||
|
||||
</details>
|
||||
|
||||
### Methodology Note
|
||||
|
||||
Brief description of the search strategy, sources, and filters used this run.
|
||||
```
|
||||
|
||||
### 7. Update Cache Memory
|
||||
|
||||
Store for next run:
|
||||
- Set of all paper IDs (DOIs, arXiv IDs) and GitHub repo URLs already covered
|
||||
- Feature-usage frequency counts (cumulative)
|
||||
- Domain frequency counts (cumulative)
|
||||
- Date of this run
|
||||
- Top-3 pain-point themes for trend comparison
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Be accurate**: Only attribute feature usage to Z3 when the paper/code makes it explicit.
|
||||
- **Be exhaustive within scope**: Cover all material found; don't cherry-pick.
|
||||
- **Be concise in headlines**: Lead with the most actionable finding.
|
||||
- **Respect academic citation norms**: Include arXiv IDs and DOIs; do not reproduce
|
||||
full paper text — only titles, authors, and abstracts.
|
||||
- **Track trends**: The cache lets you show month-over-month changes.
|
||||
- **Stay Z3-specific**: Focus on insights relevant to Z3 development, not general SMT
|
||||
or theorem-proving trends.
|
||||
|
||||
## Important Notes
|
||||
|
||||
- DO NOT create pull requests or modify source files.
|
||||
- DO NOT reproduce copyrighted paper text beyond short fair-use quotes.
|
||||
- DO close older Research Trends discussions automatically (configured).
|
||||
- DO always cite sources (arXiv ID, DOI, GitHub URL) so maintainers can verify.
|
||||
- DO use cache memory to track longitudinal trends across months.
|
||||
589
.github/workflows/agentics-maintenance.yml
vendored
589
.github/workflows/agentics-maintenance.yml
vendored
|
|
@ -1,3 +1,4 @@
|
|||
# This file was automatically generated by pkg/workflow/maintenance_workflow.go (v0.81.6). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
|
||||
#
|
||||
# ___ _ _
|
||||
# / _ \ | | (_)
|
||||
|
|
@ -13,57 +14,601 @@
|
|||
# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
|
||||
# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
|
||||
#
|
||||
# This file was automatically generated by pkg/workflow/maintenance_workflow.go (v0.37.23). DO NOT EDIT.
|
||||
#
|
||||
# To regenerate this workflow, run:
|
||||
# gh aw compile
|
||||
# For more information: https://github.com/githubnext/gh-aw/blob/main/.github/aw/github-agentic-workflows.md
|
||||
# Not all edits will cause changes to this file.
|
||||
#
|
||||
# Alternative regeneration methods:
|
||||
# make recompile
|
||||
#
|
||||
# Or use the gh-aw CLI directly:
|
||||
# ./gh-aw compile --validate --verbose
|
||||
#
|
||||
# The workflow is generated when any workflow uses the 'expires' field
|
||||
# in create-discussions or create-issues safe-outputs configuration.
|
||||
# Schedule frequency is automatically determined by the shortest expiration time.
|
||||
# For more information: https://github.github.com/gh-aw/introduction/overview/
|
||||
#
|
||||
# This file defines the generated agentic maintenance workflow for this repository.
|
||||
# It runs scheduled cleanup for expiring safe outputs and supports manual maintenance operations.
|
||||
#
|
||||
# This workflow is generated automatically when workflows use expiring safe outputs
|
||||
# or when repository maintenance features are enabled in .github/workflows/aw.json.
|
||||
#
|
||||
# To disable maintenance workflow generation, set in .github/workflows/aw.json:
|
||||
# {"maintenance": false}
|
||||
#
|
||||
# Agentic maintenance docs:
|
||||
# https://github.github.com/gh-aw/reference/ephemerals/#manual-maintenance-operations
|
||||
#
|
||||
name: Agentic Maintenance
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "37 0 * * *" # Daily (based on minimum expires: 7 days)
|
||||
- cron: "37 */2 * * *" # Every 2 hours (based on minimum expires: 1 days)
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
operation:
|
||||
description: 'Optional maintenance operation to run'
|
||||
required: false
|
||||
type: choice
|
||||
default: ''
|
||||
options:
|
||||
- ''
|
||||
- 'disable'
|
||||
- 'enable'
|
||||
- 'update'
|
||||
- 'upgrade'
|
||||
- 'safe_outputs'
|
||||
- 'create_labels'
|
||||
- 'activity_report'
|
||||
- 'close_agentic_workflows_issues'
|
||||
- 'clean_cache_memories'
|
||||
- 'update_pull_request_branches'
|
||||
- 'validate'
|
||||
- 'forecast'
|
||||
run_url:
|
||||
description: 'Run URL or run ID to replay safe outputs from (e.g. https://github.com/owner/repo/actions/runs/12345 or 12345). Required when operation is safe_outputs.'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
workflow_call:
|
||||
inputs:
|
||||
operation:
|
||||
description: 'Optional maintenance operation to run (disable, enable, update, upgrade, safe_outputs, create_labels, activity_report, close_agentic_workflows_issues, clean_cache_memories, update_pull_request_branches, validate, forecast)'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
run_url:
|
||||
description: 'Run URL or run ID to replay safe outputs from (e.g. https://github.com/owner/repo/actions/runs/12345 or 12345). Required when operation is safe_outputs.'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
outputs:
|
||||
operation_completed:
|
||||
description: 'The maintenance operation that was completed (empty when none ran or a scheduled job ran)'
|
||||
value: ${{ jobs.run_operation.outputs.operation || inputs.operation }}
|
||||
applied_run_url:
|
||||
description: 'The run URL that safe outputs were applied from'
|
||||
value: ${{ jobs.apply_safe_outputs.outputs.run_url }}
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
close-expired-entities:
|
||||
if: ${{ (!(github.event.repository.fork)) && github.event_name != 'push' && (github.event_name != 'workflow_dispatch' && github.event_name != 'workflow_call' || inputs.operation == '') }}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
discussions: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Setup Scripts
|
||||
uses: githubnext/gh-aw/actions/setup@v0.45.1
|
||||
uses: github/gh-aw-actions/setup@v0.81.6
|
||||
with:
|
||||
destination: /opt/gh-aw/actions
|
||||
destination: ${{ runner.temp }}/gh-aw/actions
|
||||
|
||||
- name: Close expired discussions
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io);
|
||||
const { main } = require('/opt/gh-aw/actions/close_expired_discussions.cjs');
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/close_expired_discussions.cjs');
|
||||
await main();
|
||||
|
||||
- name: Close expired issues
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io);
|
||||
const { main } = require('/opt/gh-aw/actions/close_expired_issues.cjs');
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/close_expired_issues.cjs');
|
||||
await main();
|
||||
|
||||
- name: Close expired pull requests
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/close_expired_pull_requests.cjs');
|
||||
await main();
|
||||
|
||||
cleanup-cache-memory:
|
||||
if: ${{ (!(github.event.repository.fork)) && github.event_name != 'push' && (github.event_name != 'workflow_dispatch' && github.event_name != 'workflow_call' || inputs.operation == '' || inputs.operation == 'clean_cache_memories') }}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
actions: write
|
||||
steps:
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw-actions/setup@v0.81.6
|
||||
with:
|
||||
destination: ${{ runner.temp }}/gh-aw/actions
|
||||
|
||||
- name: Cleanup outdated cache-memory entries
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/cleanup_cache_memory.cjs');
|
||||
await main();
|
||||
|
||||
run_operation:
|
||||
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation != '' && inputs.operation != 'safe_outputs' && inputs.operation != 'create_labels' && inputs.operation != 'activity_report' && inputs.operation != 'close_agentic_workflows_issues' && inputs.operation != 'clean_cache_memories' && inputs.operation != 'update_pull_request_branches' && inputs.operation != 'validate' && inputs.operation != 'forecast' && (!(github.event.repository.fork)) }}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
actions: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
outputs:
|
||||
operation: ${{ steps.record.outputs.operation }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw-actions/setup@v0.81.6
|
||||
with:
|
||||
destination: ${{ runner.temp }}/gh-aw/actions
|
||||
|
||||
- name: Check admin/maintainer permissions
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/check_team_member.cjs');
|
||||
await main();
|
||||
|
||||
- name: Install gh-aw
|
||||
uses: github/gh-aw-actions/setup-cli@v0.81.6
|
||||
with:
|
||||
version: v0.81.6
|
||||
|
||||
- name: Run operation
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_AW_OPERATION: ${{ inputs.operation }}
|
||||
GH_AW_CMD_PREFIX: gh aw
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/run_operation_update_upgrade.cjs');
|
||||
await main();
|
||||
|
||||
- name: Record outputs
|
||||
id: record
|
||||
env:
|
||||
GH_AW_OPERATION: ${{ inputs.operation }}
|
||||
run: echo "operation=$GH_AW_OPERATION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
update_pull_request_branches:
|
||||
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'update_pull_request_branches' && (!(github.event.repository.fork)) }}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw-actions/setup@v0.81.6
|
||||
with:
|
||||
destination: ${{ runner.temp }}/gh-aw/actions
|
||||
|
||||
- name: Check admin/maintainer permissions
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/check_team_member.cjs');
|
||||
await main();
|
||||
|
||||
- name: Update pull request branches
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/update_pull_request_branches.cjs');
|
||||
await main();
|
||||
|
||||
apply_safe_outputs:
|
||||
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'safe_outputs' && (!(github.event.repository.fork)) }}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
discussions: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
outputs:
|
||||
run_url: ${{ steps.record.outputs.run_url }}
|
||||
steps:
|
||||
- name: Checkout actions folder
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
sparse-checkout: |
|
||||
actions
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw-actions/setup@v0.81.6
|
||||
with:
|
||||
destination: ${{ runner.temp }}/gh-aw/actions
|
||||
|
||||
- name: Check admin/maintainer permissions
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/check_team_member.cjs');
|
||||
await main();
|
||||
|
||||
- name: Apply Safe Outputs
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_AW_RUN_URL: ${{ inputs.run_url }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/apply_safe_outputs_replay.cjs');
|
||||
await main();
|
||||
|
||||
- name: Record outputs
|
||||
id: record
|
||||
env:
|
||||
GH_AW_RUN_URL: ${{ inputs.run_url }}
|
||||
run: echo "run_url=$GH_AW_RUN_URL" >> "$GITHUB_OUTPUT"
|
||||
|
||||
create_labels:
|
||||
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'create_labels' && (!(github.event.repository.fork)) }}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw-actions/setup@v0.81.6
|
||||
with:
|
||||
destination: ${{ runner.temp }}/gh-aw/actions
|
||||
|
||||
- name: Check admin/maintainer permissions
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/check_team_member.cjs');
|
||||
await main();
|
||||
|
||||
- name: Install gh-aw
|
||||
uses: github/gh-aw-actions/setup-cli@v0.81.6
|
||||
with:
|
||||
version: v0.81.6
|
||||
|
||||
- name: Create missing labels
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_CMD_PREFIX: gh aw
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/create_labels.cjs');
|
||||
await main();
|
||||
|
||||
activity_report:
|
||||
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'activity_report' && (!(github.event.repository.fork)) }}
|
||||
runs-on: ubuntu-slim
|
||||
timeout-minutes: 120
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw-actions/setup@v0.81.6
|
||||
with:
|
||||
destination: ${{ runner.temp }}/gh-aw/actions
|
||||
|
||||
- name: Check admin/maintainer permissions
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/check_team_member.cjs');
|
||||
await main();
|
||||
|
||||
- name: Install gh-aw
|
||||
uses: github/gh-aw-actions/setup-cli@v0.81.6
|
||||
with:
|
||||
version: v0.81.6
|
||||
|
||||
- name: Restore activity report logs cache
|
||||
id: activity_report_logs_cache
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ./.cache/gh-aw/activity-report-logs
|
||||
key: ${{ runner.os }}-activity-report-logs-${{ github.repository }}-${{ github.ref_name }}-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-activity-report-logs-${{ github.repository }}-
|
||||
${{ runner.os }}-activity-report-logs-
|
||||
- name: Download activity report logs
|
||||
timeout-minutes: 20
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_AW_CMD_PREFIX: gh aw
|
||||
run: |
|
||||
${GH_AW_CMD_PREFIX} logs \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--start-date -1w \
|
||||
--count 500 \
|
||||
--output ./.cache/gh-aw/activity-report-logs \
|
||||
--format markdown \
|
||||
--report-file ./.cache/gh-aw/activity-report-logs/report.md
|
||||
|
||||
- name: Save activity report logs cache
|
||||
if: ${{ always() }}
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ./.cache/gh-aw/activity-report-logs
|
||||
key: ${{ steps.activity_report_logs_cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Generate activity report issue
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const fs = require('node:fs');
|
||||
const reportPath = './.cache/gh-aw/activity-report-logs/report.md';
|
||||
if (!fs.existsSync(reportPath)) {
|
||||
core.warning('Activity report markdown not found at ' + reportPath + '; skipping issue creation.');
|
||||
return;
|
||||
}
|
||||
let reportBody = '';
|
||||
try {
|
||||
reportBody = fs.readFileSync(reportPath, 'utf8').trim();
|
||||
} catch (error) {
|
||||
core.warning('Failed to read activity report markdown at ' + reportPath + ': ' + error.message);
|
||||
return;
|
||||
}
|
||||
if (!reportBody) {
|
||||
core.warning('Activity report markdown is empty at ' + reportPath + '; skipping issue creation.');
|
||||
return;
|
||||
}
|
||||
const repoSlug = context.repo.owner + '/' + context.repo.repo;
|
||||
const body = [
|
||||
'### Agentic workflow activity report',
|
||||
'',
|
||||
'Repository: ' + repoSlug,
|
||||
'Generated at: ' + new Date().toISOString(),
|
||||
'',
|
||||
reportBody,
|
||||
].join('\n');
|
||||
const createdIssue = await github.rest.issues.create({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
title: '[aw] agentic status report',
|
||||
body,
|
||||
labels: ['agentic-workflows'],
|
||||
});
|
||||
core.info('Created issue #' + createdIssue.data.number + ': ' + createdIssue.data.html_url);
|
||||
|
||||
forecast_report:
|
||||
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'forecast' && (!(github.event.repository.fork)) }}
|
||||
runs-on: ubuntu-slim
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw-actions/setup@v0.81.6
|
||||
with:
|
||||
destination: ${{ runner.temp }}/gh-aw/actions
|
||||
|
||||
- name: Check admin/maintainer permissions
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/check_team_member.cjs');
|
||||
await main();
|
||||
|
||||
- name: Install gh-aw
|
||||
uses: github/gh-aw-actions/setup-cli@v0.81.6
|
||||
with:
|
||||
version: v0.81.6
|
||||
|
||||
- name: Restore forecast report logs cache
|
||||
id: forecast_report_logs_cache
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ./.github/aw/logs
|
||||
key: ${{ runner.os }}-forecast-report-logs-${{ github.repository }}-${{ github.ref_name }}-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-forecast-report-logs-${{ github.repository }}-
|
||||
${{ runner.os }}-forecast-report-logs-
|
||||
|
||||
- name: Generate forecast report
|
||||
id: generate_forecast_report
|
||||
timeout-minutes: 30
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
DEBUG: "*"
|
||||
GH_AW_CMD_PREFIX: gh aw
|
||||
run: |
|
||||
mkdir -p ./.cache/gh-aw/forecast
|
||||
set +e
|
||||
${GH_AW_CMD_PREFIX} forecast --repo "$GITHUB_REPOSITORY" --timeout 30 --verbose --json > ./.cache/gh-aw/forecast/report.json
|
||||
forecast_exit_code=$?
|
||||
set -e
|
||||
if [ "${forecast_exit_code}" -eq 124 ]; then
|
||||
echo '{"outcome":"timeout","message":"Forecast computation timed out after 30 minutes."}' > ./.cache/gh-aw/forecast/error.json
|
||||
echo "::error::Forecast computation timed out after 30 minutes."
|
||||
exit 1
|
||||
fi
|
||||
if [ "${forecast_exit_code}" -ne 0 ]; then
|
||||
echo '{"outcome":"error","message":"Forecast computation failed before producing a report."}' > ./.cache/gh-aw/forecast/error.json
|
||||
echo "::error::Forecast computation failed with exit code ${forecast_exit_code}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Debug forecast logs folder
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
run: |
|
||||
if [ ! -d ./.github/aw/logs ]; then
|
||||
echo "Logs directory not found: ./.github/aw/logs"
|
||||
exit 0
|
||||
fi
|
||||
echo "Files under ./.github/aw/logs:"
|
||||
find ./.github/aw/logs -type f | sort
|
||||
|
||||
- name: Save forecast report logs cache
|
||||
if: ${{ always() }}
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ./.github/aw/logs
|
||||
key: ${{ runner.os }}-forecast-report-logs-${{ github.repository }}-${{ github.ref_name }}-${{ github.run_id }}
|
||||
|
||||
- name: Generate forecast issue
|
||||
if: ${{ always() }}
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
FORECAST_STEP_OUTCOME: ${{ steps.generate_forecast_report.outcome }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/create_forecast_issue.cjs');
|
||||
await main();
|
||||
|
||||
close_agentic_workflows_issues:
|
||||
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'close_agentic_workflows_issues' && (!(github.event.repository.fork)) }}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw-actions/setup@v0.81.6
|
||||
with:
|
||||
destination: ${{ runner.temp }}/gh-aw/actions
|
||||
|
||||
- name: Check admin/maintainer permissions
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/check_team_member.cjs');
|
||||
await main();
|
||||
|
||||
- name: Close no-repro agentic-workflows issues
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/close_agentic_workflows_issues.cjs');
|
||||
await main();
|
||||
|
||||
validate_workflows:
|
||||
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'validate' && (!(github.event.repository.fork)) }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Scripts
|
||||
uses: github/gh-aw-actions/setup@v0.81.6
|
||||
with:
|
||||
destination: ${{ runner.temp }}/gh-aw/actions
|
||||
|
||||
- name: Check admin/maintainer permissions
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/check_team_member.cjs');
|
||||
await main();
|
||||
|
||||
- name: Install gh-aw
|
||||
uses: github/gh-aw-actions/setup-cli@v0.81.6
|
||||
with:
|
||||
version: v0.81.6
|
||||
|
||||
- name: Validate workflows and file issue on findings
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
GH_AW_CMD_PREFIX: gh aw
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
|
||||
setupGlobals(core, github, context, exec, io, getOctokit);
|
||||
const { main } = require('${{ runner.temp }}/gh-aw/actions/run_validate_workflows.cjs');
|
||||
await main();
|
||||
|
|
|
|||
4
.github/workflows/android-build.yml
vendored
4
.github/workflows/android-build.yml
vendored
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Configure CMake and build
|
||||
run: |
|
||||
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
tar -cvf z3-build-${{ matrix.android-abi }}.tar *.jar *.so
|
||||
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: android-build-${{ matrix.android-abi }}
|
||||
path: build/z3-build-${{ matrix.android-abi }}.tar
|
||||
|
|
|
|||
1924
.github/workflows/api-coherence-checker.lock.yml
generated
vendored
1924
.github/workflows/api-coherence-checker.lock.yml
generated
vendored
File diff suppressed because one or more lines are too long
19
.github/workflows/api-coherence-checker.md
vendored
19
.github/workflows/api-coherence-checker.md
vendored
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
description: Daily API coherence checker across Z3's multi-language bindings
|
||||
description: Daily API coherence checker across Z3's multi-language bindings including Rust
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
|
@ -13,24 +13,27 @@ network: defaults
|
|||
|
||||
tools:
|
||||
cache-memory: true
|
||||
serena: ["java", "python", "typescript", "csharp"]
|
||||
github:
|
||||
toolsets: [default]
|
||||
bash: [":*"]
|
||||
edit: {}
|
||||
glob: {}
|
||||
web-search: {}
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-discussion:
|
||||
title-prefix: "[API Coherence] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -40,7 +43,7 @@ steps:
|
|||
|
||||
Your name is ${{ github.workflow }}. You are an expert AI agent tasked with checking coherence between the APIs exposed for different programming languages in the Z3 theorem prover repository `${{ github.repository }}`.
|
||||
|
||||
Z3 provides bindings for multiple languages: **Java**, **.NET (C#)**, **C++**, **Python**, **TypeScript/JavaScript**, **OCaml**, and **Go**. Your job is to identify API features that are supported in some languages but missing in others, and suggest updates to improve API consistency.
|
||||
Z3 provides bindings for multiple languages: **Java**, **.NET (C#)**, **C++**, **Python**, **TypeScript/JavaScript**, **OCaml**, **Go**, and **Rust** (via the external [`z3` crate](https://github.com/prove-rs/z3.rs)). Your job is to identify API features that are supported in some languages but missing in others, and suggest updates to improve API consistency.
|
||||
|
||||
## Your Task
|
||||
|
||||
|
|
@ -79,6 +82,7 @@ The API implementations are located in:
|
|||
- **TypeScript/JavaScript**: `src/api/js/src/**/*.ts`
|
||||
- **OCaml**: `src/api/ml/*.ml` and `*.mli` (interface files)
|
||||
- **Go**: `src/api/go/*.go` (CGO bindings)
|
||||
- **Rust**: External repository [`prove-rs/z3.rs`](https://github.com/prove-rs/z3.rs). Clone it with `git clone --depth=1 https://github.com/prove-rs/z3.rs /tmp/z3.rs` and analyze the high-level `z3` crate in `/tmp/z3.rs/z3/src/`. The low-level `z3-sys` crate at `/tmp/z3.rs/z3-sys/` mirrors the C API and can be used to identify which C functions are exposed.
|
||||
|
||||
### 4. Analyze API Coherence
|
||||
|
||||
|
|
@ -94,6 +98,7 @@ For each selected API family:
|
|||
- **C++**: Use grep/glob to search for function declarations in `z3++.h`
|
||||
- **OCaml**: Use grep/glob to search for function definitions in `.ml` and `.mli` files
|
||||
- **Go**: Use grep/glob to search for function and method definitions in `src/api/go/*.go` files
|
||||
- **Rust**: Clone the external repo (`git clone --depth=1 https://github.com/prove-rs/z3.rs /tmp/z3.rs`) and use grep/glob to search for public types, methods, functions, traits, and trait implementations in `/tmp/z3.rs/z3/src/*.rs`. Note that trait implementations (`impl Trait for Type`) do not use the `pub` keyword in Rust but are as public as the type and trait themselves, so search for `impl ` patterns in addition to `pub ` patterns to find them.
|
||||
|
||||
3. **Compare implementations** across languages:
|
||||
- Is the same functionality available in all languages?
|
||||
|
|
@ -170,7 +175,7 @@ Store in cache memory:
|
|||
## Summary
|
||||
Analyzed: Solver APIs, BitVector operations, Context creation
|
||||
Total functions checked: 18
|
||||
Languages covered: 7
|
||||
Languages covered: 8
|
||||
Previously cached issues resolved: 2
|
||||
Inconsistencies found: 7
|
||||
|
||||
|
|
@ -188,7 +193,7 @@ The following cached issues have been resolved since the last run:
|
|||
|
||||
### 1. Missing BitVector Sign Extension in TypeScript
|
||||
**What**: Bit sign extension function `Z3_mk_sign_ext` is not exposed in TypeScript
|
||||
**Available in**: C, C++, Python, .NET, Java, Go
|
||||
**Available in**: C, C++, Python, .NET, Java, Go, Rust
|
||||
**Missing in**: TypeScript
|
||||
**Fix**: Add `signExt(int i)` method to `BitVecExpr` class
|
||||
**File**: `src/api/js/src/high-level/`
|
||||
|
|
|
|||
1934
.github/workflows/build-warning-fixer.lock.yml
generated
vendored
1934
.github/workflows/build-warning-fixer.lock.yml
generated
vendored
File diff suppressed because one or more lines are too long
5
.github/workflows/build-warning-fixer.md
vendored
5
.github/workflows/build-warning-fixer.md
vendored
|
|
@ -5,15 +5,16 @@ on:
|
|||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
tools:
|
||||
view: {}
|
||||
glob: {}
|
||||
edit:
|
||||
bash: true
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-pull-request:
|
||||
if-no-changes: ignore
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
timeout-minutes: 60
|
||||
---
|
||||
|
||||
|
|
|
|||
4
.github/workflows/build-z3-cache.yml
vendored
4
.github/workflows/build-z3-cache.yml
vendored
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
|
||||
- name: Restore or create cache
|
||||
id: cache-z3
|
||||
uses: actions/cache@v5.0.3
|
||||
uses: actions/cache@v6.1.0
|
||||
with:
|
||||
path: |
|
||||
build/z3
|
||||
|
|
|
|||
156
.github/workflows/ci.yml
vendored
156
.github/workflows/ci.yml
vendored
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
runRegressions: false
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -52,9 +52,9 @@ jobs:
|
|||
run: |
|
||||
set -e
|
||||
cd build
|
||||
make -j3
|
||||
make -j3 examples
|
||||
make -j3 test-z3
|
||||
make -j$(nproc)
|
||||
make -j$(nproc) examples
|
||||
make -j$(nproc) test-z3
|
||||
cd ..
|
||||
|
||||
- name: Run unit tests
|
||||
|
|
@ -81,10 +81,18 @@ jobs:
|
|||
container: "quay.io/pypa/manylinux_2_34_x86_64:latest"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Select Python
|
||||
run: |
|
||||
# Use the first available manylinux interpreter for deterministic selection.
|
||||
PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
|
||||
test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
|
||||
echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
|
||||
"$PYTHON" --version
|
||||
|
||||
- name: Setup Python virtual environment
|
||||
run: "/opt/python/cp38-cp38/bin/python -m venv $PWD/env"
|
||||
run: "$PYTHON -m venv $PWD/env"
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
|
|
@ -113,7 +121,7 @@ jobs:
|
|||
container: quay.io/pypa/manylinux_2_28_x86_64:latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download ARM toolchain
|
||||
run: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz'
|
||||
|
|
@ -123,8 +131,16 @@ jobs:
|
|||
mkdir -p /tmp/arm-toolchain/
|
||||
tar xf /tmp/arm-toolchain.tar.xz -C /tmp/arm-toolchain/ --strip-components=1
|
||||
|
||||
- name: Select Python
|
||||
run: |
|
||||
# Use the first available manylinux interpreter for deterministic selection.
|
||||
PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
|
||||
test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
|
||||
echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
|
||||
"$PYTHON" --version
|
||||
|
||||
- name: Setup Python virtual environment
|
||||
run: "/opt/python/cp38-cp38/bin/python -m venv $PWD/env"
|
||||
run: "$PYTHON -m venv $PWD/env"
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
|
|
@ -149,7 +165,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup OCaml
|
||||
uses: ocaml/setup-ocaml@v3
|
||||
|
|
@ -171,9 +187,9 @@ jobs:
|
|||
set -e
|
||||
cd build
|
||||
eval `opam config env`
|
||||
make -j3
|
||||
make -j3 examples
|
||||
make -j3 test-z3
|
||||
make -j$(nproc)
|
||||
make -j$(nproc) examples
|
||||
make -j$(nproc) test-z3
|
||||
cd ..
|
||||
|
||||
- name: Install Z3 OCaml package
|
||||
|
|
@ -204,7 +220,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup OCaml
|
||||
uses: ocaml/setup-ocaml@v3
|
||||
|
|
@ -226,21 +242,33 @@ jobs:
|
|||
set -e
|
||||
cd build
|
||||
eval `opam config env`
|
||||
make -j3
|
||||
make -j3 examples
|
||||
make -j3 test-z3
|
||||
make -j$(nproc)
|
||||
make -j$(nproc) examples
|
||||
make -j$(nproc) test-z3
|
||||
cd ..
|
||||
|
||||
- name: Install Z3 OCaml package
|
||||
run: eval `opam config env`; ocamlfind install z3-static build/api/ml/* build/libz3-static.a
|
||||
run: |
|
||||
eval `opam config env`
|
||||
ocamlfind install z3-static build/api/ml/* build/libz3-static.a
|
||||
# Ensure the stublibs directory where dllz3ml-static.so was installed is
|
||||
# listed in ld.conf so the OCaml bytecode linker can find it. When no
|
||||
# explicit -dll flag is passed, ocamlfind installs dll* files to stublibs
|
||||
# but may not update ld.conf, causing "Fatal error: exception End_of_file"
|
||||
# in ocamlc when it cannot locate the stub shared library at link time.
|
||||
STUBLIBS="$(ocamlfind printconf destdir)/stublibs"
|
||||
LDCONF="$(ocamlfind printconf ldconf)"
|
||||
if [ -d "$STUBLIBS" ] && ! grep -qF "$STUBLIBS" "$LDCONF" 2>/dev/null; then
|
||||
echo "$STUBLIBS" >> "$LDCONF"
|
||||
fi
|
||||
|
||||
- name: Build and run OCaml examples
|
||||
run: |
|
||||
set -e
|
||||
cd build
|
||||
eval `opam config env`
|
||||
make -j3
|
||||
make -j3 _ex_ml_example_post_install
|
||||
make -j$(nproc)
|
||||
make -j$(nproc) _ex_ml_example_post_install
|
||||
./ml_example_static.byte
|
||||
./ml_example_static_custom.byte
|
||||
./ml_example_static
|
||||
|
|
@ -298,7 +326,7 @@ jobs:
|
|||
runTests: false
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -309,13 +337,13 @@ jobs:
|
|||
run: sudo apt-get update && sudo apt-get install -y ninja-build
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
- name: Setup Julia (if needed)
|
||||
if: matrix.name == 'debugClang'
|
||||
uses: julia-actions/setup-julia@v2
|
||||
uses: julia-actions/setup-julia@v3
|
||||
with:
|
||||
version: '1'
|
||||
|
||||
|
|
@ -388,7 +416,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -402,9 +430,10 @@ jobs:
|
|||
run: |
|
||||
set -e
|
||||
cd build
|
||||
make -j3
|
||||
make -j3 examples
|
||||
make -j3 test-z3
|
||||
JOBS=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu || echo 1)
|
||||
make -j"$JOBS"
|
||||
make -j"$JOBS" examples
|
||||
make -j"$JOBS" test-z3
|
||||
./cpp_example
|
||||
./c_example
|
||||
cd ..
|
||||
|
|
@ -415,6 +444,79 @@ jobs:
|
|||
- name: Run regressions
|
||||
run: python z3test/scripts/test_benchmarks.py build/z3 z3test/regressions/smt2
|
||||
|
||||
- name: Validate JNI library architecture matches host
|
||||
run: |
|
||||
echo "Checking libz3java.dylib architecture..."
|
||||
ARCH=$(lipo -archs build/libz3java.dylib)
|
||||
HOST_ARCH=$(uname -m)
|
||||
echo "libz3java.dylib arch: $ARCH | host arch: $HOST_ARCH"
|
||||
if [ "$ARCH" != "$HOST_ARCH" ]; then
|
||||
echo "ERROR: libz3java.dylib has arch '$ARCH' but host is '$HOST_ARCH'"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: libz3java.dylib correctly built for $HOST_ARCH"
|
||||
|
||||
# ============================================================================
|
||||
# macOS JNI cross-compilation validation (ARM64 host -> x86_64 target)
|
||||
# ============================================================================
|
||||
macos-jni-cross-compile:
|
||||
name: "MacOS JNI cross-compile (ARM64 -> x64) architecture validation"
|
||||
runs-on: macos-15
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Configure (cross-compile ARM64 host -> x86_64 target)
|
||||
run: |
|
||||
CXXFLAGS="-arch x86_64" CFLAGS="-arch x86_64" LDFLAGS="-arch x86_64" \
|
||||
python scripts/mk_make.py --java --arm64=false
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
set -e
|
||||
cd build
|
||||
NPROC=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 1)
|
||||
make -j"$NPROC" libz3java.dylib
|
||||
cd ..
|
||||
|
||||
- name: Validate libz3java.dylib is x86_64
|
||||
run: |
|
||||
echo "Checking libz3java.dylib architecture..."
|
||||
ARCH=$(lipo -archs build/libz3java.dylib)
|
||||
echo "libz3java.dylib architecture: $ARCH"
|
||||
if [ "$ARCH" != "x86_64" ]; then
|
||||
echo "ERROR: Expected x86_64 (cross-compiled target), got: $ARCH"
|
||||
echo "This is the regression fixed in: JNI bindings use wrong architecture in macOS cross-compilation"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: libz3java.dylib correctly built for x86_64 target on ARM64 host"
|
||||
|
||||
# ============================================================================
|
||||
# Python script unit tests (build-script logic validation)
|
||||
# ============================================================================
|
||||
python-script-tests:
|
||||
name: "Python build-script unit tests"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Run Python script unit tests
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: python -m unittest discover -s scripts/tests -p "test_*.py" -v
|
||||
|
||||
# ============================================================================
|
||||
# macOS CMake Builds
|
||||
# ============================================================================
|
||||
|
|
@ -424,7 +526,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
|
|||
2056
.github/workflows/code-conventions-analyzer.lock.yml
generated
vendored
2056
.github/workflows/code-conventions-analyzer.lock.yml
generated
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,16 +1,13 @@
|
|||
---
|
||||
description: Analyzes Z3 codebase for consistent coding conventions and opportunities to use modern C++ features
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Once daily at midnight UTC
|
||||
schedule: daily
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
tools:
|
||||
cache-memory: true
|
||||
github:
|
||||
toolsets: [default]
|
||||
view: {}
|
||||
glob: {}
|
||||
edit: {}
|
||||
bash:
|
||||
- "clang-format --version"
|
||||
|
|
@ -18,6 +15,7 @@ tools:
|
|||
- "git diff:*"
|
||||
- "git show:*"
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-issue:
|
||||
title-prefix: "[Conventions] "
|
||||
labels: [code-quality, automated]
|
||||
|
|
@ -28,6 +26,8 @@ safe-outputs:
|
|||
close-older-discussions: true
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
network: defaults
|
||||
timeout-minutes: 20
|
||||
---
|
||||
|
|
|
|||
1992
.github/workflows/code-simplifier.lock.yml
generated
vendored
1992
.github/workflows/code-simplifier.lock.yml
generated
vendored
File diff suppressed because it is too large
Load diff
160
.github/workflows/code-simplifier.md
vendored
160
.github/workflows/code-simplifier.md
vendored
|
|
@ -1,30 +1,41 @@
|
|||
---
|
||||
name: Code Simplifier
|
||||
description: Analyzes recently modified code and creates pull requests with simplifications that improve clarity, consistency, and maintainability while preserving functionality
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
skip-if-match: is:pr is:open in:title "[code-simplifier]"
|
||||
schedule: daily
|
||||
skip-if-match: 'is:pr is:open in:title "[code-simplifier]"'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
|
||||
tracker-id: code-simplifier
|
||||
|
||||
|
||||
safe-outputs:
|
||||
create-issue:
|
||||
labels:
|
||||
- refactoring
|
||||
- code-quality
|
||||
- automation
|
||||
report-failure-as-issue: false
|
||||
create-pull-request:
|
||||
title-prefix: "[code-simplifier] "
|
||||
description: Analyzes recently modified code and creates pull requests with simplifications that improve clarity, consistency, and maintainability while preserving functionality
|
||||
name: Code Simplifier
|
||||
source: github/gh-aw/.github/workflows/code-simplifier.md@76d37d925abd44fee97379206f105b74b91a285b
|
||||
strict: true
|
||||
timeout-minutes: 30
|
||||
labels: [refactoring, code-quality, automation]
|
||||
reviewers: [copilot]
|
||||
expires: 1d
|
||||
noop:
|
||||
report-as-issue: false
|
||||
|
||||
network:
|
||||
allowed:
|
||||
- go
|
||||
|
||||
tools:
|
||||
github:
|
||||
toolsets:
|
||||
- default
|
||||
tracker-id: code-simplifier
|
||||
toolsets: [default]
|
||||
|
||||
timeout-minutes: 30
|
||||
strict: true
|
||||
source: github/gh-aw/.github/workflows/code-simplifier.md@6762bfba6ae426a03aac46e8f68701461c667404
|
||||
---
|
||||
|
||||
<!-- This prompt will be imported in the agentic workflow .github/workflows/code-simplifier.md at runtime. -->
|
||||
<!-- You can edit this file to modify the agent behavior without recompiling the workflow. -->
|
||||
|
||||
|
|
@ -34,11 +45,12 @@ You are an expert code simplification specialist focused on enhancing code clari
|
|||
|
||||
## Your Mission
|
||||
|
||||
Analyze recently modified code from the last 24 hours and apply refinements that improve code quality while preserving all functionality. Create a GitHub issue with a properly formatted diff if improvements are found.
|
||||
Analyze recently modified code from the last 24 hours and apply refinements that improve code quality while preserving all functionality. Create a pull request with the simplified code if improvements are found.
|
||||
|
||||
## Current Context
|
||||
|
||||
- **Repository**: ${{ github.repository }}
|
||||
- **Analysis Date**: $(date +%Y-%m-%d)
|
||||
- **Workspace**: ${{ github.workspace }}
|
||||
|
||||
## Phase 1: Identify Recently Modified Code
|
||||
|
|
@ -65,7 +77,7 @@ Use GitHub tools to:
|
|||
For each merged PR or recent commit:
|
||||
- Use `pull_request_read` with `method: get_files` to list changed files
|
||||
- Use `get_commit` to see file changes in recent commits
|
||||
- Focus on source code files (`.go`, `.js`, `.ts`, `.tsx`, `.cjs`, `.py`, etc.)
|
||||
- Focus on source code files (`.go`, `.js`, `.ts`, `.tsx`, `.cjs`, `.py`, `.cs`, etc.)
|
||||
- Exclude test files, lock files, and generated files
|
||||
|
||||
### 1.3 Determine Scope
|
||||
|
|
@ -88,6 +100,7 @@ Before simplifying, review the project's coding standards from relevant document
|
|||
- For Go projects: Check `AGENTS.md`, `DEVGUIDE.md`, or similar files
|
||||
- For JavaScript/TypeScript: Look for `CLAUDE.md`, style guides, or coding conventions
|
||||
- For Python: Check for style guides, PEP 8 adherence, or project-specific conventions
|
||||
- For .NET/C#: Check `.editorconfig`, `Directory.Build.props`, or coding conventions in docs
|
||||
|
||||
**Key Standards to Apply:**
|
||||
|
||||
|
|
@ -112,6 +125,14 @@ For **Python** projects:
|
|||
- Prefer explicit over implicit code
|
||||
- Use list/dict comprehensions where they improve clarity (not complexity)
|
||||
|
||||
For **.NET/C#** projects:
|
||||
- Follow Microsoft C# coding conventions
|
||||
- Use `var` only when the type is obvious from the right side
|
||||
- Use file-scoped namespaces (`namespace X;`) where supported
|
||||
- Prefer pattern matching over type casting
|
||||
- Use `async`/`await` consistently, avoid `.Result` or `.Wait()`
|
||||
- Use nullable reference types and annotate nullability
|
||||
|
||||
### 2.2 Simplification Principles
|
||||
|
||||
Apply these refinements to the recently modified code:
|
||||
|
|
@ -196,6 +217,9 @@ npm test
|
|||
|
||||
# For Python projects
|
||||
pytest
|
||||
|
||||
# For .NET projects
|
||||
dotnet test
|
||||
```
|
||||
|
||||
If tests fail:
|
||||
|
|
@ -217,6 +241,9 @@ npm run lint
|
|||
|
||||
# For Python projects
|
||||
flake8 . || pylint .
|
||||
|
||||
# For .NET projects
|
||||
dotnet format --verify-no-changes
|
||||
```
|
||||
|
||||
Fix any linting issues introduced by the simplifications.
|
||||
|
|
@ -235,13 +262,16 @@ npm run build
|
|||
# For Python projects
|
||||
# (typically no build step, but check imports)
|
||||
python -m py_compile changed_files.py
|
||||
|
||||
# For .NET projects
|
||||
dotnet build
|
||||
```
|
||||
|
||||
## Phase 4: Create GitHub Issue with Diff
|
||||
## Phase 4: Create Pull Request
|
||||
|
||||
### 4.1 Determine If Issue Is Needed
|
||||
### 4.1 Determine If PR Is Needed
|
||||
|
||||
Only create an issue if:
|
||||
Only create a PR if:
|
||||
- ✅ You made actual code simplifications
|
||||
- ✅ All tests pass
|
||||
- ✅ Linting is clean
|
||||
|
|
@ -255,42 +285,14 @@ If no improvements were made or changes broke tests, exit gracefully:
|
|||
No simplifications needed - code already meets quality standards.
|
||||
```
|
||||
|
||||
### 4.2 Generate Git Diff
|
||||
### 4.2 Generate PR Description
|
||||
|
||||
Before creating the issue, generate a properly formatted git diff that can be used to create a pull request:
|
||||
|
||||
```bash
|
||||
# Stage all changes if not already staged
|
||||
git add .
|
||||
|
||||
# Generate a complete unified diff of all staged changes
|
||||
git diff --cached > /tmp/code-simplification.diff
|
||||
|
||||
# Read the diff to include in the discussion
|
||||
cat /tmp/code-simplification.diff
|
||||
```
|
||||
|
||||
**Important**: The diff must be in standard unified diff format (git unified diff) that includes:
|
||||
- File headers with `diff --git a/path b/path`
|
||||
- Index lines with git hashes
|
||||
- `---` and `+++` lines showing old and new file paths
|
||||
- `@@` lines showing line numbers
|
||||
- Actual code changes with `-` for removed lines and `+` for added lines
|
||||
|
||||
This format is compatible with:
|
||||
- `git apply` command for direct application
|
||||
- GitHub's "Create PR from diff" functionality
|
||||
- GitHub Copilot for suggesting PR creation
|
||||
- Manual copy-paste into PR creation interface
|
||||
|
||||
### 4.3 Generate Issue Description
|
||||
|
||||
If creating an issue, use this structure:
|
||||
If creating a PR, use this structure:
|
||||
|
||||
```markdown
|
||||
## Code Simplification - [Date]
|
||||
|
||||
This discussion presents code simplifications that improve clarity, consistency, and maintainability while preserving all functionality.
|
||||
This PR simplifies recently modified code to improve clarity, consistency, and maintainability while preserving all functionality.
|
||||
|
||||
### Files Simplified
|
||||
|
||||
|
|
@ -321,37 +323,11 @@ Recent changes from:
|
|||
|
||||
### Testing
|
||||
|
||||
- ✅ All tests pass
|
||||
- ✅ Linting passes
|
||||
- ✅ Build succeeds
|
||||
- ✅ All tests pass (`make test-unit`)
|
||||
- ✅ Linting passes (`make lint`)
|
||||
- ✅ Build succeeds (`make build`)
|
||||
- ✅ No functional changes - behavior is identical
|
||||
|
||||
### Git Diff
|
||||
|
||||
Below is the complete diff that can be used to create a pull request. You can copy this diff and:
|
||||
- Use it with GitHub Copilot to create a PR
|
||||
- Apply it directly with `git apply`
|
||||
- Create a PR manually by copying the changes
|
||||
|
||||
```diff
|
||||
[PASTE THE COMPLETE GIT DIFF HERE]
|
||||
```
|
||||
|
||||
To apply this diff:
|
||||
|
||||
```bash
|
||||
# Save the diff to a file
|
||||
cat > /tmp/code-simplification.diff << 'EOF'
|
||||
[PASTE DIFF CONTENT]
|
||||
EOF
|
||||
|
||||
# Apply the diff
|
||||
git apply /tmp/code-simplification.diff
|
||||
|
||||
# Or create a PR from the current branch
|
||||
gh pr create --title "[code-simplifier] Code Simplification" --body "See discussion #[NUMBER]"
|
||||
```
|
||||
|
||||
### Review Focus
|
||||
|
||||
Please verify:
|
||||
|
|
@ -365,13 +341,14 @@ Please verify:
|
|||
*Automated by Code Simplifier Agent - analyzing code from the last 24 hours*
|
||||
```
|
||||
|
||||
### 4.4 Use Safe Outputs
|
||||
### 4.3 Use Safe Outputs
|
||||
|
||||
Create the issue using the safe-outputs configuration:
|
||||
Create the pull request using the safe-outputs configuration:
|
||||
|
||||
- Title will be prefixed with `[code-simplifier]`
|
||||
- Labeled with `refactoring`, `code-quality`, `automation`
|
||||
- Contains complete git diff for easy PR creation
|
||||
- Assigned to `copilot` for review
|
||||
- Set as ready for review (not draft)
|
||||
|
||||
## Important Guidelines
|
||||
|
||||
|
|
@ -388,7 +365,7 @@ Create the issue using the safe-outputs configuration:
|
|||
- **Clear over clever**: Prioritize readability and maintainability
|
||||
|
||||
### Exit Conditions
|
||||
Exit gracefully without creating an issue if:
|
||||
Exit gracefully without creating a PR if:
|
||||
- No code was changed in the last 24 hours
|
||||
- No simplifications are beneficial
|
||||
- Tests fail after changes
|
||||
|
|
@ -420,9 +397,12 @@ Your output MUST either:
|
|||
No simplifications needed - code already meets quality standards.
|
||||
```
|
||||
|
||||
3. **If simplifications made**: Create an issue with the changes using safe-outputs, including:
|
||||
- Clear description of improvements
|
||||
- Complete git diff in proper format
|
||||
- Instructions for applying the diff or creating a PR
|
||||
3. **If simplifications made**: Create a PR with the changes using safe-outputs
|
||||
|
||||
Begin your code simplification analysis now. Find recently modified code, assess simplification opportunities, apply improvements while preserving functionality, validate changes, and create an issue with a git diff if beneficial.
|
||||
Begin your code simplification analysis now. Find recently modified code, assess simplification opportunities, apply improvements while preserving functionality, validate changes, and create a PR if beneficial.
|
||||
|
||||
**Important**: If no action is needed after completing your analysis, you **MUST** call the `noop` safe-output tool with a brief explanation. Failing to call any safe-output tool is the most common cause of safe-output workflow failures.
|
||||
|
||||
```json
|
||||
{"noop": {"message": "No action needed: [brief explanation of what was analyzed and why]"}}
|
||||
```
|
||||
1574
.github/workflows/compare-stats-anomaly-reporter.lock.yml
generated
vendored
Normal file
1574
.github/workflows/compare-stats-anomaly-reporter.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
192
.github/workflows/compare-stats-anomaly-reporter.md
vendored
Normal file
192
.github/workflows/compare-stats-anomaly-reporter.md
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
---
|
||||
description: Analyze benchmark statistics from the latest 30 hours and publish bug/crash/anomaly summary as a GitHub Discussion
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 */12 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: read-all
|
||||
|
||||
strict: false
|
||||
timeout-minutes: 45
|
||||
|
||||
network:
|
||||
allowed:
|
||||
- defaults
|
||||
- mtzguido.tplinkdns.com
|
||||
|
||||
tools:
|
||||
bash: [":*"]
|
||||
github:
|
||||
toolsets: [default]
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-discussion:
|
||||
title-prefix: "[Compare Stats] "
|
||||
category: "agentic workflows"
|
||||
close-older-discussions: true
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
---
|
||||
|
||||
# Compare Stats Bug/Crash/Anomaly Reporter
|
||||
|
||||
Your name is ${{ github.workflow }}. You are a Z3 benchmarking analysis agent for `${{ github.repository }}`.
|
||||
|
||||
Analyze the benchmark statistics page below, focusing on results from the last 30 hours, then create a GitHub Discussion with a concise but actionable summary of:
|
||||
|
||||
- Bugs
|
||||
- Crashes
|
||||
- Anomalies
|
||||
|
||||
Source URL:
|
||||
`http://mtzguido.tplinkdns.com:8081/z3/`
|
||||
|
||||
Note: this endpoint is currently HTTP-only. Treat fetched data as non-sensitive benchmark telemetry and do not include secrets in requests or reports.
|
||||
Note: the workflow runs every 12 hours but analyzes 30 hours intentionally to provide overlap and avoid missing transient failures between runs.
|
||||
Overlapping windows are expected; `close-older-discussions: true` keeps only the latest report thread active.
|
||||
|
||||
## Requirements
|
||||
|
||||
### 1) Fetch and save the source page
|
||||
|
||||
Use bash to fetch the page into `/tmp/gh-aw/agent/benchmark_stats.html`.
|
||||
|
||||
Try this first:
|
||||
```bash
|
||||
curl -fsSL --max-time 60 "http://mtzguido.tplinkdns.com:8081/z3/" -o /tmp/gh-aw/agent/benchmark_stats.html
|
||||
```
|
||||
|
||||
If that fails, retry once with:
|
||||
```bash
|
||||
wget -q -T 60 -O /tmp/gh-aw/agent/benchmark_stats.html "http://mtzguido.tplinkdns.com:8081/z3/"
|
||||
```
|
||||
|
||||
If both fail, still create a discussion that explains the fetch failure, includes stderr output, and marks the report as incomplete.
|
||||
After a successful fetch, perform basic integrity checks before parsing:
|
||||
- file is non-empty
|
||||
- content includes `<html` and at least one `<table`
|
||||
- if checks fail, treat as suspicious/incomplete data and report this explicitly
|
||||
|
||||
### 2) Parse tabular data
|
||||
|
||||
Use Python to parse all tables from the HTML into normalized rows.
|
||||
|
||||
Use resilient parsing:
|
||||
- Prefer `pandas.read_html` when available.
|
||||
- If pandas fails, parse with `html.parser`/regex fallback.
|
||||
|
||||
Persist normalized JSON to `/tmp/gh-aw/agent/compare_stats_rows.json`.
|
||||
|
||||
### 3) Detect time window (last 30 hours)
|
||||
|
||||
Find candidate timestamp columns using case-insensitive column-name matches:
|
||||
- `time`, `timestamp`, `date`, `run`, `created`, `updated`
|
||||
|
||||
Parse datetimes with timezone handling if present. Use current UTC time and filter to rows where timestamp is within the past 30 hours.
|
||||
Treat naive timestamps as UTC.
|
||||
|
||||
If no timestamp can be extracted:
|
||||
- Report this limitation explicitly.
|
||||
- Continue analysis on all rows.
|
||||
- Mark the discussion as "time-window fallback".
|
||||
|
||||
### 4) Classify bugs/crashes/anomalies
|
||||
|
||||
Infer key columns using column-name heuristics:
|
||||
- status/result/outcome
|
||||
- benchmark/instance/file/name
|
||||
- set/suite/group/track/family
|
||||
- message/error/details/reason
|
||||
|
||||
Normalize status strings to lowercase.
|
||||
|
||||
#### Bugs / Crashes
|
||||
Classify a row as **crash/bug** if status/details contain terms like:
|
||||
- `crash`, `segfault`, `assert`, `abort`, `exception`, `error`, `failed`, `bug`
|
||||
|
||||
#### Anomalies
|
||||
At minimum, detect:
|
||||
|
||||
1. **Unknown-outlier anomaly** (required):
|
||||
- Within the same benchmark set/suite/group, if most rows are in `{sat, unsat, timeout}` but a minority are `unknown`, flag the `unknown` rows as anomalies.
|
||||
- Rationale: require enough samples for confidence and avoid flagging sets where `unknown` is common behavior. `0.4` caps unknown results to a minority, while `0.6` enforces a decisive majority of sat/unsat/timeout outcomes. Any remainder after those constraints is intentionally allowed for other statuses.
|
||||
- Use this threshold: `total_rows >= 4`, `unknown_count / total_rows <= 0.4`, and `(sat_count + unsat_count + timeout_count) / total_rows >= 0.6`.
|
||||
- If set/suite/group columns are missing, fallback grouping order is: directory prefix of benchmark path/name, then benchmark name prefix before first separator (`/`, `:`, `::`), then a single global group.
|
||||
|
||||
2. **Status divergence anomaly**:
|
||||
- Same benchmark name appears multiple times with conflicting non-timeout statuses (for example `sat` vs `unsat`).
|
||||
- Ignore timeout-only disagreements here; timeout behavior is covered under the repeated hard-failure anomaly section to reduce noise from transient runtime variance.
|
||||
|
||||
3. **Repeated hard-failure anomaly**:
|
||||
- Same benchmark appears repeatedly with crash/error-like status in the time window.
|
||||
|
||||
### 5) Generate discussion report
|
||||
|
||||
Create a GitHub Discussion using `create-discussion` safe output.
|
||||
|
||||
Use this structure:
|
||||
|
||||
```markdown
|
||||
### Compare Stats Analysis Report
|
||||
|
||||
**Source**: [benchmark statistics](http://mtzguido.tplinkdns.com:8081/z3/)
|
||||
**Workflow Run**: [#${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
**Analysis Time (UTC)**: <timestamp>
|
||||
**Window**: last 30 hours (or fallback mode)
|
||||
|
||||
### Executive Summary
|
||||
|
||||
- Rows analyzed: N
|
||||
- Rows in 30h window: M (or "timestamp unavailable")
|
||||
- Bugs/crashes: B
|
||||
- Anomalies: A
|
||||
|
||||
### Bugs and Crashes
|
||||
|
||||
| Benchmark Set | Benchmark | Status | Details | Timestamp |
|
||||
|---|---|---|---|---|
|
||||
| ... |
|
||||
|
||||
### Anomalies
|
||||
|
||||
#### Unknown-Outlier Cases
|
||||
| Benchmark Set | Benchmark | Status | Peer Status Distribution | Timestamp |
|
||||
|---|---|---|---|---|
|
||||
| ... |
|
||||
|
||||
#### Status Divergences
|
||||
| Benchmark | Observed Statuses | Benchmark Set(s) | Timestamp(s) |
|
||||
|---|---|---|---|
|
||||
| ... |
|
||||
|
||||
#### Repeated Hard Failures
|
||||
| Benchmark | Failure Count | Representative Status/Details | Benchmark Set(s) |
|
||||
|---|---|---|---|
|
||||
| ... |
|
||||
|
||||
### Notes and Limitations
|
||||
- Mention parsing assumptions
|
||||
- Mention missing columns/timestamps if any
|
||||
|
||||
<details>
|
||||
<summary><b>Raw Extraction Summary</b></summary>
|
||||
|
||||
- Table count
|
||||
- Candidate columns used
|
||||
- Top status distribution
|
||||
- Up to 30 representative raw rows (sanitized)
|
||||
|
||||
</details>
|
||||
```
|
||||
|
||||
## Reporting Rules
|
||||
|
||||
- Be factual and concise.
|
||||
- Do not claim certainty when column mapping is heuristic.
|
||||
- If no bugs/crashes/anomalies are found, still create the discussion and explicitly state "No issues detected in analyzed window."
|
||||
- Do not open PRs or modify repository files.
|
||||
6
.github/workflows/coverage.yml
vendored
6
.github/workflows/coverage.yml
vendored
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
COV_DETAILS_PATH: ${{github.workspace}}/cov-details
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6.0.2
|
||||
- uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup
|
||||
run: |
|
||||
|
|
@ -89,13 +89,13 @@ jobs:
|
|||
id: date
|
||||
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/upload-artifact@v6
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: coverage-${{steps.date.outputs.date}}
|
||||
path: ${{github.workspace}}/coverage.html
|
||||
retention-days: 4
|
||||
|
||||
- uses: actions/upload-artifact@v6
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: coverage-details-${{steps.date.outputs.date}}
|
||||
path: ${{env.COV_DETAILS_PATH}}
|
||||
|
|
|
|||
2
.github/workflows/cross-build.yml
vendored
2
.github/workflows/cross-build.yml
vendored
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Install cross build tools
|
||||
run: apt update && apt install -y ninja-build cmake python3 g++-13-${{ matrix.arch }}-linux-gnu
|
||||
|
|
|
|||
1666
.github/workflows/csa-analysis.lock.yml
generated
vendored
Normal file
1666
.github/workflows/csa-analysis.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
311
.github/workflows/csa-analysis.md
vendored
Normal file
311
.github/workflows/csa-analysis.md
vendored
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
---
|
||||
description: Weekly Clang Static Analyzer (CSA) build and report for Z3, posting findings to GitHub Discussions
|
||||
|
||||
on:
|
||||
schedule: weekly
|
||||
workflow_dispatch:
|
||||
|
||||
timeout-minutes: 180
|
||||
|
||||
permissions: read-all
|
||||
|
||||
network: defaults
|
||||
|
||||
tools:
|
||||
cache-memory: true
|
||||
github:
|
||||
toolsets: [default]
|
||||
bash: [":*"]
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-discussion:
|
||||
title-prefix: "[CSA] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
---
|
||||
|
||||
# Clang Static Analyzer (CSA) Report
|
||||
|
||||
## Job Description
|
||||
|
||||
Your name is ${{ github.workflow }}. You are an expert static analysis agent for the Z3 theorem prover repository `${{ github.repository }}`. Your task is to build Z3 using the Clang Static Analyzer (`scan-build`) and produce a structured report of the findings in a GitHub Discussion.
|
||||
|
||||
## Your Task
|
||||
|
||||
### 1. Set Up the Build Environment
|
||||
|
||||
Install required dependencies:
|
||||
|
||||
```bash
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y clang clang-tools cmake ninja-build python3
|
||||
```
|
||||
|
||||
Verify that `scan-build` is available:
|
||||
|
||||
```bash
|
||||
scan-build --version || clang --version
|
||||
which scan-build
|
||||
```
|
||||
|
||||
### 2. Configure Z3 with CMake
|
||||
|
||||
Run CMake inside a fresh `build` directory. Use `scan-build` to wrap the CMake configure step so that analysis starts from the configuration phase, then use it again during the actual build:
|
||||
|
||||
```bash
|
||||
mkdir -p build
|
||||
cd build
|
||||
scan-build cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -G Ninja ../
|
||||
```
|
||||
|
||||
### 3. Build Z3 with scan-build
|
||||
|
||||
Run the build under `scan-build`, directing the HTML report output to a well-known path. Capture both stdout and stderr:
|
||||
|
||||
```bash
|
||||
cd build
|
||||
scan-build -o /tmp/csa-report --html-title "Z3 CSA Report" ninja 2>&1 | tee /tmp/csa-build.log
|
||||
cd ..
|
||||
```
|
||||
|
||||
If `scan-build` is not available, try the `clang --analyze` approach via CMake flags as a fallback:
|
||||
|
||||
```bash
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_C_FLAGS="--analyze" \
|
||||
-DCMAKE_CXX_FLAGS="--analyze" \
|
||||
-G Ninja ../ 2>&1 | tee /tmp/csa-configure.log
|
||||
ninja 2>&1 | tee /tmp/csa-build.log
|
||||
cd ..
|
||||
```
|
||||
|
||||
### 4. Collect and Parse Results
|
||||
|
||||
After the build, examine the scan-build HTML output directory and build log to extract findings:
|
||||
|
||||
```bash
|
||||
# List generated report directories
|
||||
ls -la /tmp/csa-report/ 2>/dev/null || echo "No report directory found"
|
||||
|
||||
# Find all HTML report files
|
||||
find /tmp/csa-report -name "*.html" 2>/dev/null | head -50
|
||||
|
||||
# Count total number of bugs reported
|
||||
find /tmp/csa-report -name "report-*.html" 2>/dev/null | wc -l
|
||||
```
|
||||
|
||||
Parse the `index.html` summary if it exists:
|
||||
|
||||
```bash
|
||||
# Extract bug summary from index.html
|
||||
if [ -f /tmp/csa-report/*/index.html ]; then
|
||||
cat /tmp/csa-report/*/index.html
|
||||
fi
|
||||
```
|
||||
|
||||
Also scan the build log for warnings and errors:
|
||||
|
||||
```bash
|
||||
grep -E "(warning|error|bug).*\[.*\]" /tmp/csa-build.log | head -100
|
||||
```
|
||||
|
||||
### 4.5 Extract Report Content to Text
|
||||
|
||||
Extract the full CSA report content into a plain-text file so it can be embedded directly in the GitHub Discussion:
|
||||
|
||||
```bash
|
||||
python3 - << 'PYEOF' > /tmp/csa-extracted.txt 2>&1
|
||||
import os
|
||||
import re
|
||||
import glob
|
||||
import sys
|
||||
|
||||
report_dir = '/tmp/csa-report'
|
||||
if not os.path.isdir(report_dir):
|
||||
print('No CSA report directory found.')
|
||||
sys.exit(0)
|
||||
|
||||
subdirs = sorted([d for d in os.listdir(report_dir) if os.path.isdir(os.path.join(report_dir, d))])
|
||||
if not subdirs:
|
||||
print('No scan-build output subdirectory found.')
|
||||
sys.exit(0)
|
||||
|
||||
subdir = os.path.join(report_dir, subdirs[-1])
|
||||
index_path = os.path.join(subdir, 'index.html')
|
||||
|
||||
if os.path.exists(index_path):
|
||||
with open(index_path) as f:
|
||||
content = f.read()
|
||||
print('## Summary Table\n')
|
||||
rows = re.findall(r'<tr[^>]*>(.*?)</tr>', content, re.DOTALL)
|
||||
for row in rows:
|
||||
cells = re.findall(r'<td[^>]*>(.*?)</td>', row, re.DOTALL)
|
||||
if cells:
|
||||
clean = [re.sub(r'<[^>]+>', '', c).strip() for c in cells]
|
||||
line = ' | '.join(c for c in clean if c)
|
||||
if line:
|
||||
print(line)
|
||||
print()
|
||||
|
||||
report_files = sorted(glob.glob(os.path.join(subdir, 'report-*.html')))
|
||||
if report_files:
|
||||
print(f'## Individual Findings ({len(report_files)} total)\n')
|
||||
for i, rfile in enumerate(report_files, 1):
|
||||
with open(rfile) as f:
|
||||
rcontent = f.read()
|
||||
checker = re.search(r'<td class="CHECKER">(.*?)</td>', rcontent, re.DOTALL)
|
||||
filename = re.search(r'<td class="FILENAME">(.*?)</td>', rcontent, re.DOTALL)
|
||||
lineno = re.search(r'<td class="LINE">(.*?)</td>', rcontent, re.DOTALL)
|
||||
desc = re.search(r'<td class="DESC">(.*?)</td>', rcontent, re.DOTALL)
|
||||
c = re.sub(r'<[^>]+>', '', checker.group(1)).strip() if checker else 'unknown'
|
||||
fn = re.sub(r'<[^>]+>', '', filename.group(1)).strip() if filename else 'unknown'
|
||||
ln = re.sub(r'<[^>]+>', '', lineno.group(1)).strip() if lineno else '?'
|
||||
d = re.sub(r'<[^>]+>', '', desc.group(1)).strip() if desc else ''
|
||||
print(f'{i}. [{c}] {d}')
|
||||
print(f' File: {fn}, Line: {ln}')
|
||||
print()
|
||||
else:
|
||||
print('No individual report files found.')
|
||||
PYEOF
|
||||
cat /tmp/csa-extracted.txt
|
||||
```
|
||||
|
||||
### 5. Categorize Findings
|
||||
|
||||
Analyze the CSA findings and group them by:
|
||||
|
||||
- **Bug type** (e.g., null pointer dereference, use after free, memory leak, uninitialized value, logic error)
|
||||
- **Severity** (categorize by CSA checker family: `core.*`, `cplusplus.*`, `deadcode.*`, `security.*`, `unix.*`)
|
||||
- **Source file** (relative path within the Z3 source tree)
|
||||
|
||||
Build a structured summary:
|
||||
|
||||
1. Total number of findings
|
||||
2. Breakdown by checker/bug type
|
||||
3. Top affected files (files with the most findings)
|
||||
4. Any high-severity issues (null dereferences, memory safety issues)
|
||||
5. Any new findings compared to cached results from a previous run
|
||||
|
||||
### 6. Compare with Cached Results
|
||||
|
||||
Check cache memory for results from the previous run:
|
||||
- Number of findings from the last run
|
||||
- List of previously reported high-severity issues
|
||||
- Any issues that have been resolved since the last run
|
||||
|
||||
Update cache memory with the current run's results.
|
||||
|
||||
### 7. Create GitHub Discussion
|
||||
|
||||
Create a GitHub Discussion with a structured report. The discussion title should include the current date and a brief summary (e.g., "[CSA] Weekly Report - N findings").
|
||||
|
||||
**Discussion Structure:**
|
||||
|
||||
```markdown
|
||||
# Clang Static Analyzer Report
|
||||
|
||||
**Date**: [YYYY-MM-DD]
|
||||
**Commit**: [Short SHA of HEAD]
|
||||
**Build type**: Debug (CMake + Ninja)
|
||||
**Analyzer**: scan-build / clang-analyzer
|
||||
|
||||
## Summary
|
||||
|
||||
| Metric | Count |
|
||||
|--------|-------|
|
||||
| Total findings | N |
|
||||
| High severity (core.*) | X |
|
||||
| Memory issues (unix.*, cplusplus.*) | Y |
|
||||
| Dead code / logic errors | Z |
|
||||
| Files affected | F |
|
||||
|
||||
## Changes Since Last Run
|
||||
|
||||
- Findings resolved since last run: [list or "none"]
|
||||
- New findings introduced: [list or "none"]
|
||||
|
||||
## High-Priority Findings
|
||||
|
||||
[List the top findings by severity with file paths and checker names]
|
||||
|
||||
## Findings by Category
|
||||
|
||||
### Core Checkers (Null Dereference, Division by Zero, etc.)
|
||||
[List findings]
|
||||
|
||||
### C++ Specific (Memory Management, Object Lifetime)
|
||||
[List findings]
|
||||
|
||||
### Dead Code and Logic Errors
|
||||
[List findings]
|
||||
|
||||
### Security Checkers
|
||||
[List findings]
|
||||
|
||||
## Top Affected Files
|
||||
|
||||
[Table or list of files with the most findings]
|
||||
|
||||
## Full CSA Report Content
|
||||
|
||||
<details>
|
||||
<summary>Complete findings extracted from the CSA HTML report (click to expand)</summary>
|
||||
|
||||
[PASTE THE ENTIRE CONTENTS OF /tmp/csa-extracted.txt HERE verbatim — do not summarize or paraphrase]
|
||||
|
||||
</details>
|
||||
|
||||
## Build Log Excerpt
|
||||
|
||||
<details>
|
||||
<summary>Key warnings and errors from the build log (click to expand)</summary>
|
||||
|
||||
```
|
||||
[Relevant excerpt from build log]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Notes
|
||||
|
||||
- This report was generated automatically by the Z3 CSA Analysis workflow.
|
||||
- False positives may be present; human review is recommended before acting on findings.
|
||||
- To reproduce locally: `scan-build -o /tmp/csa-report cmake -DCMAKE_BUILD_TYPE=Debug -G Ninja . && scan-build ninja`
|
||||
```
|
||||
|
||||
### 8. Handle Edge Cases
|
||||
|
||||
- If `scan-build` is not installed, report the issue clearly and suggest installing `clang-tools` or `clang-analyzer`.
|
||||
- If the build fails entirely (not just with warnings), report the build failure and exit gracefully without creating a misleading discussion.
|
||||
- If zero findings are reported, still create the discussion celebrating the clean result.
|
||||
- If the discussion would be identical to the previous one (no changes), consider skipping creation unless it has been more than 2 weeks since the last run.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Be thorough**: Parse all available output from scan-build, not just the summary.
|
||||
- **Be accurate**: Clearly distinguish between true positives and likely false positives.
|
||||
- **Be actionable**: For each high-priority finding, include the file, line number, and a brief description.
|
||||
- **Be concise**: The discussion should be readable; use collapsible sections for verbose output.
|
||||
- **Track progress**: Use cache memory to detect regressions (new findings) and improvements (resolved findings) over time.
|
||||
- **Respect build time**: The build may take 15-20 minutes; use ninja with available cores.
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **DO NOT** create pull requests or modify source files.
|
||||
- **DO NOT** attempt to fix the findings automatically.
|
||||
- **DO** close older CSA discussions automatically (this is configured via `close-older-discussions: true`).
|
||||
- **DO** always report the commit SHA so findings can be correlated with specific code versions.
|
||||
- **DO** use cache memory to track trends over multiple runs.
|
||||
1153
.github/workflows/deeptest.lock.yml
generated
vendored
1153
.github/workflows/deeptest.lock.yml
generated
vendored
File diff suppressed because it is too large
Load diff
59
.github/workflows/deeptest.md
vendored
59
.github/workflows/deeptest.md
vendored
|
|
@ -1,59 +0,0 @@
|
|||
---
|
||||
description: Generate comprehensive test cases for Z3 source files
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
file_path:
|
||||
description: 'Path to the source file to generate tests for (e.g., src/util/vector.h)'
|
||||
required: true
|
||||
type: string
|
||||
issue_number:
|
||||
description: 'Issue number to link the generated tests to (optional)'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions: read-all
|
||||
|
||||
network: defaults
|
||||
|
||||
tools:
|
||||
cache-memory: true
|
||||
serena: ["python"]
|
||||
github:
|
||||
toolsets: [default]
|
||||
bash: [":*"]
|
||||
edit: {}
|
||||
glob: {}
|
||||
safe-outputs:
|
||||
create-pull-request:
|
||||
title-prefix: "[DeepTest] "
|
||||
labels: [automated-tests, deeptest]
|
||||
draft: false
|
||||
add-comment:
|
||||
max: 2
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
---
|
||||
|
||||
<!-- Edit the file linked below to modify the agent without recompilation. Feel free to move the entire markdown body to that file. -->
|
||||
{{#runtime-import agentics/deeptest.md}}
|
||||
|
||||
## Context
|
||||
|
||||
You are the DeepTest agent for the Z3 theorem prover repository.
|
||||
|
||||
**Workflow dispatch file path**: ${{ github.event.inputs.file_path }}
|
||||
|
||||
**Issue number** (if linked): ${{ github.event.inputs.issue_number }}
|
||||
|
||||
## Instructions
|
||||
|
||||
Follow the workflow steps defined in the imported prompt above to generate comprehensive test cases for the specified source file.
|
||||
43
.github/workflows/docs.yml
vendored
43
.github/workflows/docs.yml
vendored
|
|
@ -16,12 +16,37 @@ env:
|
|||
EM_VERSION: 3.1.73
|
||||
|
||||
jobs:
|
||||
build-docs:
|
||||
name: Build Documentation
|
||||
build-go-docs:
|
||||
name: Build Go Documentation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Generate Go Documentation
|
||||
working-directory: doc
|
||||
run: |
|
||||
python3 mk_go_doc.py --output-dir=api/html/go --go-api-path=../src/api/go
|
||||
|
||||
- name: Upload Go Documentation
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: go-docs
|
||||
path: doc/api/html/go/
|
||||
retention-days: 1
|
||||
|
||||
build-docs:
|
||||
name: Build Documentation
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-go-docs
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v6
|
||||
|
|
@ -63,13 +88,13 @@ jobs:
|
|||
working-directory: doc
|
||||
run: |
|
||||
eval $(opam env)
|
||||
python3 mk_api_doc.py --mld --output-dir=api --z3py-package-path=../build-x64/python/z3 --build=../build-x64
|
||||
python3 mk_api_doc.py --mld --go --output-dir=api --z3py-package-path=../build-x64/python/z3 --build=../build-x64
|
||||
Z3BUILD=build-x64 python3 mk_params_doc.py
|
||||
mkdir api/html/ml
|
||||
ocamldoc -html -d api/html/ml -sort -hide Z3 -I $( ocamlfind query zarith ) -I ../build-x64/api/ml ../build-x64/api/ml/z3enums.mli ../build-x64/api/ml/z3.mli
|
||||
|
||||
- name: Setup emscripten
|
||||
uses: mymindstorm/setup-emsdk@v14
|
||||
uses: mymindstorm/setup-emsdk@v16
|
||||
with:
|
||||
no-install: true
|
||||
version: ${{env.EM_VERSION}}
|
||||
|
|
@ -97,7 +122,13 @@ jobs:
|
|||
working-directory: doc
|
||||
run: |
|
||||
eval $(opam env)
|
||||
python3 mk_api_doc.py --js --output-dir=api --mld --z3py-package-path=../build-x64/python/z3 --build=../build-x64
|
||||
python3 mk_api_doc.py --js --go --output-dir=api --mld --z3py-package-path=../build-x64/python/z3 --build=../build-x64
|
||||
|
||||
- name: Download Go Documentation
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: go-docs
|
||||
path: doc/api/html/go/
|
||||
|
||||
- name: Deploy to z3prover.github.io
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
|
|
|
|||
267
.github/workflows/fstar-master-build.yml
vendored
Normal file
267
.github/workflows/fstar-master-build.yml
vendored
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
name: Build FStar master with Z3 master
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "9 4 * * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
z3_ref:
|
||||
description: Z3 ref to checkout and build
|
||||
required: false
|
||||
default: master
|
||||
z3_cmake_args:
|
||||
description: Extra CMake arguments for Z3 build
|
||||
required: false
|
||||
default: ""
|
||||
z3_runtime_args:
|
||||
description: "Extra Z3 runtime args (example: smt.ho_matching=true)"
|
||||
required: false
|
||||
default: "smt.ho_matching=true"
|
||||
fstar_ref:
|
||||
description: FStar ref to checkout and build
|
||||
required: false
|
||||
default: _nik_higher_order_smt
|
||||
fstar_opam_switch:
|
||||
description: OCaml switch for FStar build
|
||||
required: false
|
||||
default: "4.14.2"
|
||||
fstar_otherflags:
|
||||
description: "Extra FStar OTHERFLAGS"
|
||||
required: false
|
||||
default: "--split_queries on_failure --log_failing_queries --ext higher_order_smt --proof_recovery"
|
||||
discussion_category:
|
||||
description: Discussion category name
|
||||
required: false
|
||||
default: "Agentic Workflows"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
discussions: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build-and-report:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 180
|
||||
env:
|
||||
Z3_REF: ${{ github.event.inputs.z3_ref || 'master' }}
|
||||
Z3_CMAKE_ARGS: ${{ github.event.inputs.z3_cmake_args || '' }}
|
||||
Z3_RUNTIME_ARGS: ${{ github.event.inputs.z3_runtime_args || 'smt.ho_matching=true' }}
|
||||
FSTAR_REF: ${{ github.event.inputs.fstar_ref || 'master' }}
|
||||
FSTAR_OPAM_SWITCH: ${{ github.event.inputs.fstar_opam_switch || '4.14.2' }}
|
||||
FSTAR_OTHERFLAGS: ${{ github.event.inputs.fstar_otherflags || '' }}
|
||||
DISCUSSION_CATEGORY: ${{ github.event.inputs.discussion_category || 'Agentic Workflows' }}
|
||||
steps:
|
||||
- name: Checkout Z3
|
||||
uses: actions/checkout@v7.0.0
|
||||
with:
|
||||
ref: ${{ env.Z3_REF }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y cmake ninja-build python3 git curl unzip opam m4 pkg-config libgmp-dev
|
||||
|
||||
- name: Build Z3
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p /tmp/gh-aw/agent
|
||||
cmake -S . -B build/release -G Ninja -DCMAKE_BUILD_TYPE=Release $Z3_CMAKE_ARGS
|
||||
ninja -C build/release z3
|
||||
./build/release/z3 --version | tee /tmp/gh-aw/agent/z3-version.txt
|
||||
printf '(check-sat)\n' | ./build/release/z3 $Z3_RUNTIME_ARGS -in | tee /tmp/gh-aw/agent/z3-runtime-check.txt
|
||||
|
||||
- name: Prepare Z3 aliases for FStar
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p /tmp/gh-aw/agent/z3-bin
|
||||
ln -sf "$GITHUB_WORKSPACE/build/release/z3" /tmp/gh-aw/agent/z3-bin/z3
|
||||
ln -sf "$GITHUB_WORKSPACE/build/release/z3" /tmp/gh-aw/agent/z3-bin/z3-4.8.5
|
||||
ln -sf "$GITHUB_WORKSPACE/build/release/z3" /tmp/gh-aw/agent/z3-bin/z3-4.13.3
|
||||
/tmp/gh-aw/agent/z3-bin/z3 --version
|
||||
|
||||
- name: Build FStar
|
||||
id: build_fstar
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf /tmp/gh-aw/agent/FStar
|
||||
git clone --depth=1 --recurse-submodules --branch "$FSTAR_REF" https://github.com/FStarLang/FStar.git /tmp/gh-aw/agent/FStar
|
||||
cd /tmp/gh-aw/agent/FStar
|
||||
echo "FStar commit: $(git rev-parse HEAD)" | tee /tmp/gh-aw/agent/fstar-commit.txt
|
||||
|
||||
opam init --disable-sandboxing --yes
|
||||
opam switch create "$FSTAR_OPAM_SWITCH" --yes || opam switch "$FSTAR_OPAM_SWITCH"
|
||||
eval "$(opam env --switch="$FSTAR_OPAM_SWITCH")"
|
||||
opam install --deps-only . --yes
|
||||
|
||||
Z3_VERSION="$(sed -E -n 's/^Z3 version ([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' /tmp/gh-aw/agent/z3-version.txt | head -1)"
|
||||
test -n "$Z3_VERSION" || { echo "Error: Failed to extract Z3 version from /tmp/gh-aw/agent/z3-version.txt (expected: 'Z3 version X.Y.Z')"; cat /tmp/gh-aw/agent/z3-version.txt || true; exit 1; }
|
||||
|
||||
PATH="/tmp/gh-aw/agent/z3-bin:$PATH" OTHERFLAGS="--z3version $Z3_VERSION $FSTAR_OTHERFLAGS" make -j"$(nproc)" -k
|
||||
test -x /tmp/gh-aw/agent/FStar/out/bin/fstar.exe || { echo "Error: FStar binary not found or not executable at /tmp/gh-aw/agent/FStar/out/bin/fstar.exe"; exit 1; }
|
||||
/tmp/gh-aw/agent/FStar/out/bin/fstar.exe --version | tee /tmp/gh-aw/agent/fstar-version.txt
|
||||
|
||||
- name: Collect generated SMT2 files
|
||||
id: collect_smt2
|
||||
if: always()
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf /tmp/gh-aw/agent/smt2-artifact
|
||||
mkdir -p /tmp/gh-aw/agent/smt2-artifact
|
||||
SMT2_PREVIEW=/tmp/gh-aw/agent/smt2-preview.md
|
||||
SMT2_HEAD_LINES=1000
|
||||
> "$SMT2_PREVIEW"
|
||||
|
||||
if [ -d /tmp/gh-aw/agent/FStar ]; then
|
||||
mapfile -t SMT2_FILES < <(find /tmp/gh-aw/agent/FStar -type f -name '*.smt2' | sort)
|
||||
else
|
||||
SMT2_FILES=()
|
||||
fi
|
||||
|
||||
if [ "${#SMT2_FILES[@]}" -eq 0 ]; then
|
||||
echo "has_files=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for file in "${SMT2_FILES[@]}"; do
|
||||
rel="${file#/tmp/gh-aw/agent/FStar/}"
|
||||
target="/tmp/gh-aw/agent/smt2-artifact/${rel}"
|
||||
mkdir -p "$(dirname "$target")"
|
||||
cp "$file" "$target"
|
||||
{
|
||||
printf '#### `%s`\n\n' "$rel"
|
||||
printf '```smt2\n'
|
||||
head -n "$SMT2_HEAD_LINES" "$file"
|
||||
printf '\n```\n\n'
|
||||
} >> "$SMT2_PREVIEW"
|
||||
done
|
||||
|
||||
echo "has_files=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload generated SMT2 artifact
|
||||
id: upload_smt2
|
||||
if: always() && steps.collect_smt2.outputs.has_files == 'true'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fstar-generated-smt2-${{ github.run_id }}
|
||||
path: /tmp/gh-aw/agent/smt2-artifact
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
- name: Create discussion summary
|
||||
if: always()
|
||||
uses: actions/github-script@v9
|
||||
env:
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
FSTAR_BUILD_OUTCOME: ${{ steps.build_fstar.outcome }}
|
||||
SMT2_ARTIFACT_ID: ${{ steps.upload_smt2.outputs.artifact-id }}
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
const readIfExists = (path) => fs.existsSync(path) ? fs.readFileSync(path, 'utf8').trim() : null;
|
||||
const z3VersionText = readIfExists('/tmp/gh-aw/agent/z3-version.txt') ?? 'unknown';
|
||||
const fstarVersionFile = readIfExists('/tmp/gh-aw/agent/fstar-version.txt') ?? '';
|
||||
const fstarVersionText = fstarVersionFile ? fstarVersionFile.split('\n')[0] : 'unknown';
|
||||
const fstarCommitLine = readIfExists('/tmp/gh-aw/agent/fstar-commit.txt') ?? '';
|
||||
const fstarCommit = fstarCommitLine ? fstarCommitLine.replace(/^FStar commit:\s*/, '') : 'unknown';
|
||||
const fstarBuildOutcome = process.env.FSTAR_BUILD_OUTCOME || 'unknown';
|
||||
const fstarBuildSucceeded = fstarBuildOutcome === 'success';
|
||||
const fstarStatus = fstarBuildSucceeded
|
||||
? '✅ FStar build completed'
|
||||
: `⚠️ FStar build ${fstarBuildOutcome} (pipeline continued)`;
|
||||
const smt2ArtifactId = (process.env.SMT2_ARTIFACT_ID || '').trim();
|
||||
const smt2ArtifactUrl = smt2ArtifactId ? `${process.env.RUN_URL}/artifacts/${smt2ArtifactId}` : '';
|
||||
const smt2PreviewFile = '/tmp/gh-aw/agent/smt2-preview.md';
|
||||
const maxPreviewChars = 55000; // Keep below GitHub's 65536-character discussion body limit, leaving room for non-preview sections.
|
||||
let smt2Preview = readIfExists(smt2PreviewFile) ?? '';
|
||||
const smt2PreviewChars = Array.from(smt2Preview);
|
||||
if (smt2PreviewChars.length > maxPreviewChars) {
|
||||
smt2Preview = `${smt2PreviewChars.slice(0, maxPreviewChars).join('')}\n\n... (truncated due to discussion size limits)`;
|
||||
}
|
||||
const smt2Section = smt2ArtifactId
|
||||
? [
|
||||
`### Generated SMT2 files`,
|
||||
`- Artifact: ${smt2ArtifactUrl}`,
|
||||
``,
|
||||
`First 1000 lines per generated \`.smt2\` file:`,
|
||||
``,
|
||||
smt2Preview || '_No preview content available._'
|
||||
].join('\n')
|
||||
: [
|
||||
`### Generated SMT2 files`,
|
||||
`- No generated \`.smt2\` files were found.`
|
||||
].join('\n');
|
||||
const date = new Date().toISOString().slice(0, 10);
|
||||
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const categoryName = process.env.DISCUSSION_CATEGORY;
|
||||
|
||||
const categoryQuery = await github.graphql(
|
||||
`query($owner:String!, $repo:String!) {
|
||||
repository(owner:$owner, name:$repo) {
|
||||
id
|
||||
discussionCategories(first:50) {
|
||||
nodes { id name }
|
||||
}
|
||||
}
|
||||
}`,
|
||||
{ owner, repo }
|
||||
);
|
||||
|
||||
const categories = categoryQuery.repository.discussionCategories.nodes || [];
|
||||
const normalized = categoryName.trim().toLowerCase();
|
||||
const category = categories.find(c => c.name.toLowerCase() === normalized);
|
||||
if (!category) {
|
||||
throw new Error(`Discussion category '${categoryName}' not found`);
|
||||
}
|
||||
|
||||
const body = [
|
||||
`### Build status`,
|
||||
`- ✅ Z3 build completed`,
|
||||
`- ${fstarStatus}`,
|
||||
``,
|
||||
`### Inputs used`,
|
||||
`- z3_ref: \`${process.env.Z3_REF}\``,
|
||||
`- z3_cmake_args: \`${process.env.Z3_CMAKE_ARGS}\``,
|
||||
`- z3_runtime_args: \`${process.env.Z3_RUNTIME_ARGS}\``,
|
||||
`- fstar_ref: \`${process.env.FSTAR_REF}\``,
|
||||
`- fstar_opam_switch: \`${process.env.FSTAR_OPAM_SWITCH}\``,
|
||||
`- fstar_otherflags: \`${process.env.FSTAR_OTHERFLAGS}\``,
|
||||
``,
|
||||
`### Produced versions`,
|
||||
`- Z3: \`${z3VersionText}\``,
|
||||
`- FStar: \`${fstarVersionText}\``,
|
||||
`- FStar commit: \`${fstarCommit}\``,
|
||||
``,
|
||||
smt2Section,
|
||||
``,
|
||||
`### Run`,
|
||||
`- Workflow run: ${process.env.RUN_URL}`
|
||||
].join('\n');
|
||||
|
||||
await github.graphql(
|
||||
`mutation($repositoryId:ID!, $categoryId:ID!, $title:String!, $body:String!) {
|
||||
createDiscussion(input:{
|
||||
repositoryId:$repositoryId,
|
||||
categoryId:$categoryId,
|
||||
title:$title,
|
||||
body:$body
|
||||
}) {
|
||||
discussion { url }
|
||||
}
|
||||
}`,
|
||||
{
|
||||
repositoryId: categoryQuery.repository.id,
|
||||
categoryId: category.id,
|
||||
title: `FStar build with configurable Z3 inputs — ${date}`,
|
||||
body
|
||||
}
|
||||
);
|
||||
1691
.github/workflows/issue-backlog-processor.lock.yml
generated
vendored
Normal file
1691
.github/workflows/issue-backlog-processor.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
264
.github/workflows/issue-backlog-processor.md
vendored
Normal file
264
.github/workflows/issue-backlog-processor.md
vendored
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
---
|
||||
description: Processes the backlog of open issues every second day, creates a discussion with findings, and comments on relevant issues
|
||||
|
||||
on:
|
||||
schedule: every 2 days
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: read-all
|
||||
|
||||
tools:
|
||||
cache-memory: true
|
||||
github:
|
||||
toolsets: [default]
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-discussion:
|
||||
title-prefix: "[Issue Backlog] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
add-comment:
|
||||
max: 20
|
||||
noop:
|
||||
report-as-issue: false
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
timeout-minutes: 60
|
||||
---
|
||||
|
||||
# Issue Backlog Processor
|
||||
|
||||
## Job Description
|
||||
|
||||
Your name is ${{ github.workflow }}. You are an expert AI agent tasked with processing the backlog of open issues in the Z3 theorem prover repository `${{ github.repository }}`. Your mission is to analyze open issues systematically and help maintainers manage the backlog effectively by surfacing actionable insights and providing helpful comments.
|
||||
|
||||
> **CRITICAL**: You MUST call either `create-discussion` or `noop` before finishing, under all circumstances. Even if you only analyzed a small number of issues, always produce output. Never exit without calling one of these tools.
|
||||
|
||||
## Your Task
|
||||
|
||||
### 1. Initialize or Resume Progress (Cache Memory)
|
||||
|
||||
Check your cache memory for:
|
||||
- List of issue numbers already processed and commented on in previous runs
|
||||
- Issues previously flagged for closure, duplication, or merge
|
||||
- Date of last run
|
||||
- The batch cursor: the last issue number processed (used for pagination across runs)
|
||||
|
||||
If cache data exists:
|
||||
- Skip re-commenting on issues already commented in a recent run (within the last 4 days)
|
||||
- Re-evaluate previously flagged issues to see if their status has changed
|
||||
- Note any new issues that opened since the last run
|
||||
- Resume from where the previous run left off (use the stored batch cursor)
|
||||
|
||||
If this is the first run or memory is empty, initialize a fresh tracking structure.
|
||||
|
||||
### 2. Fetch Open Issues (Batched)
|
||||
|
||||
Use the GitHub API to list open issues in the repository. **Process at most 30 issues per run** to stay within context limits (this limit is based on the average size of Z3 issues including body text and inline code snippets; larger issues may require processing fewer):
|
||||
- Retrieve one page (30 issues) of open issues
|
||||
- Exclude pull requests (filter where `pull_request` is not present)
|
||||
- Sort by last updated date (most recently updated first)
|
||||
- If cache has a batch cursor from the last run, fetch the next page after that cursor; otherwise start from the most recently updated issues
|
||||
- For each issue, collect:
|
||||
- Issue number, title, body, labels, author
|
||||
- Date created and last updated
|
||||
- Number of comments
|
||||
- **Do NOT fetch comments for every issue up front.** Only fetch comments for a specific issue when at least one of the following is true: the body mentions a version number (potential closure), the title contains words like "duplicate", "same as", or "related to" (potential duplicate), or the issue has labels such as "question", "help wanted", or "wontfix" (potential closure/status change). Fetch comments lazily, one issue at a time, only when one of these criteria is met.
|
||||
- Any referenced pull requests, commits, or other issues
|
||||
|
||||
### 3. Analyze Each Issue
|
||||
|
||||
For each open issue, perform the following analysis:
|
||||
|
||||
#### 3.1 Identify Issues to Close
|
||||
|
||||
An issue can be safely closed if any of the following apply:
|
||||
- A merged pull request explicitly references the issue (e.g., "fixes #NNN", "closes #NNN") and the fix has been shipped
|
||||
- Comments from the reporter or maintainers indicate the issue is resolved
|
||||
- The described behavior no longer occurs in the current codebase (based on code inspection or comments confirming resolution)
|
||||
- The issue is a question that has been fully answered and no further action is needed
|
||||
- The issue is clearly obsolete (e.g., references a version or feature that no longer exists)
|
||||
|
||||
**Be conservative**: When in doubt, do NOT flag an issue for closure. Only flag issues where you have high confidence.
|
||||
|
||||
#### 3.2 Identify Duplicate or Mergeable Issues
|
||||
|
||||
An issue may be a duplicate or candidate for merging if:
|
||||
- Another open issue describes the same bug, behavior, or feature request
|
||||
- The same root cause has been identified across multiple issues
|
||||
- Issues are closely related enough that they should be tracked together
|
||||
|
||||
For each potential duplicate, identify:
|
||||
- The original or canonical issue it duplicates
|
||||
- The reason you believe they are related
|
||||
|
||||
#### 3.3 Identify Suggested Fixes
|
||||
|
||||
For issues describing bugs, incorrect behavior, or missing features:
|
||||
- Analyze the issue description, stack traces, SMT-LIB2 examples, or code snippets provided
|
||||
- Identify the likely Z3 component(s) involved (e.g., SAT solver, arithmetic theory, string solver, API, language bindings)
|
||||
- Point to specific source files or modules in `src/` that are likely relevant
|
||||
- Suggest what kind of fix might be needed (e.g., edge case handling, missing method, API inconsistency)
|
||||
|
||||
Focus on issues where a reasonable fix can be concisely described. Do not guess at fixes for complex soundness or performance issues.
|
||||
|
||||
#### 3.4 Determine If a Comment Is Warranted
|
||||
|
||||
Add a comment to an issue if you have **genuinely useful and specific information** to contribute, such as:
|
||||
- A related merged PR or commit that might resolve or partially address the issue
|
||||
- A confirmed duplicate with a reference to the canonical issue
|
||||
- A request for clarification or additional diagnostic information that would help resolve the issue
|
||||
- Confirmation that a fix has been shipped in a recent release
|
||||
- Specific guidance on which component to look at for a fix
|
||||
|
||||
**Do NOT add generic comments**, low-value acknowledgments, or comments that simply restate the issue.
|
||||
|
||||
### 4. Create a Discussion with Findings
|
||||
|
||||
**MANDATORY**: You MUST call `create-discussion` now, even if you only analyzed a few issues or found nothing actionable. If there is genuinely nothing to report, call `noop` instead. Do not skip this step.
|
||||
|
||||
Create a GitHub Discussion summarizing the analysis results.
|
||||
|
||||
**Title:** "[Issue Backlog] Backlog Analysis - [Date]"
|
||||
|
||||
**Content structure:**
|
||||
|
||||
```markdown
|
||||
# Issue Backlog Analysis - [Date]
|
||||
|
||||
## Executive Summary
|
||||
|
||||
- **Total open issues analyzed**: N
|
||||
- **Issues recommended for closure**: N
|
||||
- **Potential duplicates / merge candidates**: N
|
||||
- **Issues with suggested fixes**: N
|
||||
- **Issues commented on**: N
|
||||
|
||||
---
|
||||
|
||||
## Issues Recommended for Closure
|
||||
|
||||
These issues appear to be already resolved, no longer relevant, or fully answered.
|
||||
|
||||
| Issue | Title | Reason for Closure |
|
||||
|-------|-------|-------------------|
|
||||
| #NNN | [title] | [reasoning] |
|
||||
...
|
||||
|
||||
---
|
||||
|
||||
## Potential Duplicates / Merge Candidates
|
||||
|
||||
These issues appear to overlap with other open or closed issues.
|
||||
|
||||
| Issue | Title | Duplicate of | Notes |
|
||||
|-------|-------|-------------|-------|
|
||||
| #NNN | [title] | #MMM | [reasoning] |
|
||||
...
|
||||
|
||||
---
|
||||
|
||||
## Issues with Suggested Fixes
|
||||
|
||||
These issues have identifiable root causes or affected components.
|
||||
|
||||
### #NNN - [Issue Title]
|
||||
|
||||
- **Component**: [e.g., arithmetic solver, Python bindings, SMT-LIB2 parser]
|
||||
- **Relevant source files**: [e.g., `src/smt/theory_arith.cpp`]
|
||||
- **Suggested fix direction**: [concise description]
|
||||
|
||||
...
|
||||
|
||||
---
|
||||
|
||||
## Issues Needing More Information
|
||||
|
||||
These issues lack sufficient detail to investigate or reproduce.
|
||||
|
||||
| Issue | Title | Missing Information |
|
||||
|-------|-------|-------------------|
|
||||
| #NNN | [title] | [what is needed] |
|
||||
...
|
||||
|
||||
---
|
||||
|
||||
## Notable Issues Deserving Attention
|
||||
|
||||
Issues that are particularly impactful or have been waiting a long time.
|
||||
|
||||
| Issue | Title | Age | Notes |
|
||||
|-------|-------|-----|-------|
|
||||
| #NNN | [title] | [days old] | [why notable] |
|
||||
...
|
||||
|
||||
---
|
||||
|
||||
*Automated by Issue Backlog Processor - runs every 2 days*
|
||||
```
|
||||
|
||||
### 5. Comment on Issues
|
||||
|
||||
For each issue identified in step 3.4 as warranting a comment, post a helpful comment using the `add-comment` safe output.
|
||||
|
||||
**Comment guidelines:**
|
||||
- Be specific and actionable
|
||||
- Reference relevant PRs, commits, or other issues by number
|
||||
- Use a professional and respectful tone
|
||||
- Identify yourself as an automated analysis agent at the end of each comment
|
||||
- For potential closures, ask the reporter to confirm whether the issue is still relevant
|
||||
- For duplicates, politely link to the canonical issue
|
||||
|
||||
Example comment for a potentially resolved issue:
|
||||
```
|
||||
It appears that PR #MMM (merged on [date]) may have addressed this issue by [brief description]. Could you confirm whether this problem still occurs with the latest code? If it has been resolved, we can close this issue.
|
||||
|
||||
*This comment was added by the automated Issue Backlog Processor.*
|
||||
```
|
||||
|
||||
Example comment for a duplicate:
|
||||
```
|
||||
This issue appears to be related to (or a duplicate of) #MMM which describes a similar problem. Linking the two for tracking purposes.
|
||||
|
||||
*This comment was added by the automated Issue Backlog Processor.*
|
||||
```
|
||||
|
||||
### 6. Update Cache Memory
|
||||
|
||||
After completing the analysis, update cache memory with:
|
||||
- List of issue numbers processed in this run
|
||||
- Issues that were commented on (to avoid duplicate comments in future runs)
|
||||
- Issues flagged for closure, duplication, or merge
|
||||
- Date and timestamp of this run
|
||||
- Count of total issues analyzed
|
||||
- Batch cursor: the issue number of the last issue processed in this run, so the next run can continue from where this one left off
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Always produce output**: You MUST call `create-discussion` or `noop` before finishing — never exit silently. If in doubt about whether there is enough to report, call `create-discussion` with a brief summary.
|
||||
- **Batch processing**: Only analyze up to 30 issues per run. Store a cursor in cache memory so subsequent runs pick up where you left off.
|
||||
- **Lazy comment fetching**: Do NOT bulk-fetch all comments for all issues. Only fetch comments for a specific issue when one of these criteria is met: the body mentions a version number, the title contains duplicate/related keywords, or the issue has status-relevant labels (e.g., "question", "help wanted", "wontfix").
|
||||
- **Prioritize accuracy over coverage**: It is better to analyze 20 issues well than 200 issues poorly
|
||||
- **Be conservative on closures**: Incorrectly closing a valid issue is harmful; when in doubt, keep it open
|
||||
- **Respect the community**: Z3 is used by researchers, security engineers, and developers — treat all issues respectfully
|
||||
- **Focus on actionable output**: Every item in the discussion should be actionable for a maintainer
|
||||
- **Avoid comment spam**: Do not add comments unless they provide specific and useful information
|
||||
- **Understand Z3's complexity**: Soundness bugs (wrong answers) are critical and should never be auto-closed
|
||||
|
||||
## Z3-Specific Context
|
||||
|
||||
Z3 is an industrial-strength theorem prover and SMT solver used in program verification, security analysis, and formal methods. Key components to be aware of:
|
||||
|
||||
- **SMT solver** (`src/smt/`): Core solving engine with theory plugins
|
||||
- **SAT solver** (`src/sat/`): Boolean satisfiability engine
|
||||
- **Theory solvers**: Arithmetic (`src/smt/theory_arith*`), arrays, bit-vectors, strings, etc.
|
||||
- **API** (`src/api/`): C API and language bindings (Python, Java, C#, OCaml, Go, JavaScript)
|
||||
- **Tactics** (`src/tactic/`): Configurable solving strategies
|
||||
- **Parser** (`src/parsers/`): SMT-LIB2 and other input formats
|
||||
|
||||
When analyzing issues, consider:
|
||||
- Whether the issue has a reproducible SMT-LIB2 test case (important for SMT solver bugs)
|
||||
- Whether the issue affects a specific language binding or the core solver
|
||||
- Whether it is a soundness issue (critical), performance issue (important), or API/usability issue (moderate)
|
||||
- The Z3 version mentioned and whether it has since been fixed in a newer release
|
||||
60
.github/workflows/mark-prs-ready-for-review.yml
vendored
Normal file
60
.github/workflows/mark-prs-ready-for-review.yml
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
name: Mark Pull Requests Ready for Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
mark-ready:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Mark all draft pull requests ready for review
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
async function markReady(nodeId, number, title) {
|
||||
core.info(`Marking PR #${number} "${title}" ready for review.`);
|
||||
try {
|
||||
await github.graphql(`
|
||||
mutation($id: ID!) {
|
||||
markPullRequestReadyForReview(input: { pullRequestId: $id }) {
|
||||
pullRequest { number isDraft }
|
||||
}
|
||||
}
|
||||
`, { id: nodeId });
|
||||
} catch (err) {
|
||||
core.warning(`Failed to mark PR #${number} ready for review: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.eventName === 'pull_request') {
|
||||
const pr = context.payload.pull_request;
|
||||
if (pr.draft) {
|
||||
await markReady(pr.node_id, pr.number, pr.title);
|
||||
} else {
|
||||
core.info(`PR #${pr.number} is already ready for review. Nothing to do.`);
|
||||
}
|
||||
} else {
|
||||
const pulls = await github.paginate(github.rest.pulls.list, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
});
|
||||
|
||||
const drafts = pulls.filter(pr => pr.draft);
|
||||
core.info(`Found ${drafts.length} draft pull request(s).`);
|
||||
|
||||
for (const pr of drafts) {
|
||||
await markReady(pr.node_id, pr.number, pr.title);
|
||||
}
|
||||
}
|
||||
|
||||
core.info('Done.');
|
||||
1742
.github/workflows/memory-safety-report.lock.yml
generated
vendored
Normal file
1742
.github/workflows/memory-safety-report.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
221
.github/workflows/memory-safety-report.md
vendored
Normal file
221
.github/workflows/memory-safety-report.md
vendored
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
---
|
||||
description: >
|
||||
Analyze ASan/UBSan sanitizer logs from the memory-safety workflow
|
||||
and file findings as a GitHub issue.
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Memory Safety Analysis"]
|
||||
types: [completed]
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
timeout-minutes: 30
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
network: defaults
|
||||
|
||||
tools:
|
||||
cache-memory: true
|
||||
github:
|
||||
toolsets: [default, actions]
|
||||
bash: [":*"]
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
mentions: false
|
||||
allowed-github-references: []
|
||||
max-bot-mentions: 1
|
||||
create-issue:
|
||||
title-prefix: "[Memory Safety] "
|
||||
labels: [bug, memory-safety, automated-analysis]
|
||||
max: 1
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
---
|
||||
|
||||
# Memory Safety Analysis Report Generator
|
||||
|
||||
## Job Description
|
||||
|
||||
Your name is ${{ github.workflow }}. You are an expert memory safety analyst for the Z3 theorem prover repository `${{ github.repository }}`. Your task is to download, analyze, and report on the results from the Memory Safety Analysis workflow, covering runtime sanitizer (ASan/UBSan) findings.
|
||||
|
||||
**The `gh` CLI is not authenticated inside AWF.** Use GitHub MCP tools for all GitHub API interaction. Do not use `gh run download` or any other `gh` command.
|
||||
|
||||
## Your Task
|
||||
|
||||
### 1. Download Artifacts from the Triggering Workflow Run
|
||||
|
||||
If triggered by `workflow_run`, the run ID is `${{ github.event.workflow_run.id }}`. If manual dispatch (empty run ID), call `github-mcp-server-actions_list` with method `list_workflow_runs` for the "Memory Safety Analysis" workflow and pick the latest completed run.
|
||||
|
||||
Get the artifact list and download URLs:
|
||||
|
||||
1. Call `github-mcp-server-actions_list` with method `list_workflow_run_artifacts` and the run ID. The run produces two artifacts: `asan-reports` and `ubsan-reports`.
|
||||
2. For each artifact, call `github-mcp-server-actions_get` with method `download_workflow_run_artifact` and the artifact ID. This returns a temporary download URL.
|
||||
3. Run the helper scripts to download, extract, and parse:
|
||||
|
||||
```bash
|
||||
bash .github/scripts/fetch-artifacts.sh "$ASAN_URL" "$UBSAN_URL"
|
||||
python3 .github/scripts/parse_sanitizer_reports.py /tmp/reports
|
||||
```
|
||||
|
||||
After this, `/tmp/reports/{asan,ubsan}-reports/` contain the extracted files, `/tmp/parsed-report.json` has structured findings, and `/tmp/fetch-artifacts.log` has the download log.
|
||||
|
||||
### 2. Analyze Sanitizer Reports
|
||||
|
||||
Read `/tmp/parsed-report.json` for structured data. Also inspect the raw files if needed:
|
||||
|
||||
```bash
|
||||
# Check ASan results
|
||||
if [ -d /tmp/reports/asan-reports ]; then
|
||||
cat /tmp/reports/asan-reports/summary.md
|
||||
ls /tmp/reports/asan-reports/
|
||||
fi
|
||||
|
||||
# Check UBSan results
|
||||
if [ -d /tmp/reports/ubsan-reports ]; then
|
||||
cat /tmp/reports/ubsan-reports/summary.md
|
||||
ls /tmp/reports/ubsan-reports/
|
||||
fi
|
||||
```
|
||||
|
||||
For each sanitizer finding, extract:
|
||||
- **Error type** (heap-buffer-overflow, heap-use-after-free, stack-buffer-overflow, signed-integer-overflow, null-pointer-dereference, etc.)
|
||||
- **Source location** (file, line, column)
|
||||
- **Stack trace** (first 5 frames)
|
||||
- **Allocation/deallocation site** (for memory errors)
|
||||
|
||||
### 3. Compare with Previous Results
|
||||
|
||||
Check cache memory for previous run results:
|
||||
- Total findings from last run (ASan + UBSan)
|
||||
- List of previously known issues
|
||||
- Identify new findings (regressions) vs. resolved findings (improvements)
|
||||
|
||||
### 4. Generate the Issue Report
|
||||
|
||||
Create a GitHub issue using `create-issue`. Use `##` or lower for section headers and wrap verbose sections in `<details>` tags to keep the report scannable.
|
||||
|
||||
```markdown
|
||||
**Date**: YYYY-MM-DD
|
||||
**Commit**: `<short SHA>` ([full_sha](link)) on branch `<branch>`
|
||||
**Commit message**: first line of commit message
|
||||
**Triggered by**: push / workflow_dispatch (Memory Safety Analysis run [#<run_id>](link))
|
||||
**Report run**: [#<run_id>](link)
|
||||
|
||||
### Executive Summary
|
||||
|
||||
| Category | ASan | UBSan | Total |
|
||||
|----------|------|-------|-------|
|
||||
| Buffer Overflow | Y | - | Z |
|
||||
| Use-After-Free | Y | - | Z |
|
||||
| Double-Free | Y | - | Z |
|
||||
| Null Dereference | - | - | Z |
|
||||
| Integer Overflow | - | Y | Z |
|
||||
| Undefined Behavior | - | Y | Z |
|
||||
| Other | Y | Z | Z |
|
||||
| **Total** | **Y** | **Z** | **N** |
|
||||
|
||||
### Trend
|
||||
|
||||
- New findings since last run: N
|
||||
- Resolved since last run: N
|
||||
- Unchanged: N
|
||||
|
||||
### Critical Findings (Immediate Action Needed)
|
||||
|
||||
[List any high-severity findings: buffer overflows, use-after-free, double-free]
|
||||
|
||||
### Important Findings (Should Fix)
|
||||
|
||||
[List medium-severity: null derefs, integer overflows]
|
||||
|
||||
### Low-Severity / Informational
|
||||
|
||||
[List warnings: potential issues]
|
||||
|
||||
<details>
|
||||
<summary><b>ASan Findings</b></summary>
|
||||
|
||||
[Each finding with error type, location, and stack trace snippet]
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>UBSan Findings</b></summary>
|
||||
|
||||
[Each finding with error type, location, and explanation]
|
||||
|
||||
</details>
|
||||
|
||||
### Top Affected Files
|
||||
|
||||
| File | Findings |
|
||||
|------|----------|
|
||||
| src/... | N |
|
||||
|
||||
### Known Suppressions
|
||||
|
||||
[List from parsed-report.json suppressions field]
|
||||
|
||||
### Recommendations
|
||||
|
||||
1. [Actionable recommendations based on the findings]
|
||||
2. [Patterns to address]
|
||||
|
||||
<details>
|
||||
<summary><b>Raw Data</b></summary>
|
||||
|
||||
[Compressed summary of all data for future reference]
|
||||
|
||||
</details>
|
||||
```
|
||||
|
||||
If zero findings across all tools, call `noop` and include a clean-run summary (commit and workflow run link) in the no-op message.
|
||||
|
||||
### 5. Update Cache Memory
|
||||
|
||||
Store the current run's results in cache memory for future comparison:
|
||||
- Total count by category
|
||||
- List of file:line pairs with findings
|
||||
- Run metadata (commit SHA, date, run ID)
|
||||
|
||||
### 6. Handle Edge Cases
|
||||
|
||||
- If the triggering workflow failed entirely, report that analysis could not complete and include any partial results.
|
||||
- If no artifacts are available, report that and suggest running the workflow manually.
|
||||
- If the helper scripts fail, report the error in the issue body and stop.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Be thorough: analyze every available artifact and log file.
|
||||
- Be accurate: distinguish between ASan and UBSan findings.
|
||||
- Be actionable: for each finding, include enough context to locate and understand the issue.
|
||||
- Track trends: use cache memory to identify regressions and improvements over time.
|
||||
- Prioritize: critical memory safety issues (buffer overflow, UAF, double-free) should be prominently highlighted.
|
||||
|
||||
## Important Notes
|
||||
|
||||
- DO NOT create pull requests or modify source files.
|
||||
- DO NOT attempt to fix the findings automatically.
|
||||
- DO create issues only when there are actionable findings; use `noop` for clean runs.
|
||||
- DO always report the commit SHA so findings can be correlated with specific code versions.
|
||||
- DO use cache memory to track trends over multiple runs.
|
||||
249
.github/workflows/memory-safety.yml
vendored
Normal file
249
.github/workflows/memory-safety.yml
vendored
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
name: Memory Safety Analysis
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 1'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
full_scan:
|
||||
description: 'Run full codebase scan (not just changed files)'
|
||||
required: false
|
||||
default: 'false'
|
||||
type: boolean
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
concurrency:
|
||||
group: memory-safety-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# ============================================================================
|
||||
# Job 1: AddressSanitizer Build and Tests
|
||||
# ============================================================================
|
||||
asan-test:
|
||||
name: "ASan Build & Test"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
ASAN_OPTIONS: "detect_leaks=1:halt_on_error=0:print_stats=1:log_path=/tmp/asan"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y ninja-build clang
|
||||
|
||||
- name: Configure with ASan
|
||||
run: |
|
||||
mkdir -p build-asan
|
||||
cd build-asan
|
||||
CC=clang CXX=clang++ cmake \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls" \
|
||||
-DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=address" \
|
||||
-G Ninja ../
|
||||
|
||||
- name: Build Z3 with ASan
|
||||
run: |
|
||||
cd build-asan
|
||||
ninja -j$(nproc)
|
||||
ninja test-z3
|
||||
|
||||
- name: Run unit tests under ASan
|
||||
run: |
|
||||
cd build-asan
|
||||
./test-z3 -a 2>&1 | tee /tmp/asan-unit-test.log
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run SMT-LIB2 benchmarks under ASan
|
||||
run: |
|
||||
cd build-asan
|
||||
for f in ../examples/SMT-LIB2/bounded\ model\ checking/*.smt2; do
|
||||
echo "=== Testing: $f ==="
|
||||
timeout 60 ./z3 "$f" 2>&1 || true
|
||||
done | tee /tmp/asan-benchmark.log
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run regression tests under ASan
|
||||
run: |
|
||||
git clone --depth=1 https://github.com/z3prover/z3test z3test
|
||||
python z3test/scripts/test_benchmarks.py build-asan/z3 z3test/regressions/smt2 2>&1 | tee /tmp/asan-regression.log
|
||||
continue-on-error: true
|
||||
|
||||
- name: Collect ASan reports
|
||||
if: always()
|
||||
run: |
|
||||
mkdir -p /tmp/asan-reports
|
||||
cp /tmp/asan* /tmp/asan-reports/ 2>/dev/null || true
|
||||
if ls /tmp/asan.* 1>/dev/null 2>&1; then
|
||||
cp /tmp/asan.* /tmp/asan-reports/
|
||||
fi
|
||||
echo "# ASan Summary" > /tmp/asan-reports/summary.md
|
||||
echo "" >> /tmp/asan-reports/summary.md
|
||||
if ls /tmp/asan-reports/asan.* 1>/dev/null 2>&1; then
|
||||
echo "## Errors Found" >> /tmp/asan-reports/summary.md
|
||||
for f in /tmp/asan-reports/asan.*; do
|
||||
echo '```' >> /tmp/asan-reports/summary.md
|
||||
head -50 "$f" >> /tmp/asan-reports/summary.md
|
||||
echo '```' >> /tmp/asan-reports/summary.md
|
||||
echo "" >> /tmp/asan-reports/summary.md
|
||||
done
|
||||
else
|
||||
echo "No ASan errors detected." >> /tmp/asan-reports/summary.md
|
||||
fi
|
||||
|
||||
- name: Upload ASan reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: asan-reports
|
||||
path: /tmp/asan-reports/
|
||||
retention-days: 30
|
||||
|
||||
# ============================================================================
|
||||
# Job 2: UndefinedBehaviorSanitizer Build and Tests
|
||||
# ============================================================================
|
||||
ubsan-test:
|
||||
name: "UBSan Build & Test"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=0:log_path=/tmp/ubsan"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y ninja-build clang
|
||||
|
||||
- name: Configure with UBSan
|
||||
run: |
|
||||
mkdir -p build-ubsan
|
||||
cd build-ubsan
|
||||
CC=clang CXX=clang++ cmake \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_C_FLAGS="-fsanitize=undefined -fno-omit-frame-pointer -fsanitize-recover=all" \
|
||||
-DCMAKE_CXX_FLAGS="-fsanitize=undefined -fno-omit-frame-pointer -fsanitize-recover=all" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=undefined" \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=undefined" \
|
||||
-G Ninja ../
|
||||
|
||||
- name: Build Z3 with UBSan
|
||||
run: |
|
||||
cd build-ubsan
|
||||
ninja -j$(nproc)
|
||||
ninja test-z3
|
||||
|
||||
- name: Run unit tests under UBSan
|
||||
run: |
|
||||
cd build-ubsan
|
||||
./test-z3 -a 2>&1 | tee /tmp/ubsan-unit-test.log
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run SMT-LIB2 benchmarks under UBSan
|
||||
run: |
|
||||
cd build-ubsan
|
||||
for f in ../examples/SMT-LIB2/bounded\ model\ checking/*.smt2; do
|
||||
echo "=== Testing: $f ==="
|
||||
timeout 60 ./z3 "$f" 2>&1 || true
|
||||
done | tee /tmp/ubsan-benchmark.log
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run regression tests under UBSan
|
||||
run: |
|
||||
git clone --depth=1 https://github.com/z3prover/z3test z3test
|
||||
python z3test/scripts/test_benchmarks.py build-ubsan/z3 z3test/regressions/smt2 2>&1 | tee /tmp/ubsan-regression.log
|
||||
continue-on-error: true
|
||||
|
||||
- name: Collect UBSan reports
|
||||
if: always()
|
||||
run: |
|
||||
mkdir -p /tmp/ubsan-reports
|
||||
cp /tmp/ubsan* /tmp/ubsan-reports/ 2>/dev/null || true
|
||||
if ls /tmp/ubsan.* 1>/dev/null 2>&1; then
|
||||
cp /tmp/ubsan.* /tmp/ubsan-reports/
|
||||
fi
|
||||
echo "# UBSan Summary" > /tmp/ubsan-reports/summary.md
|
||||
echo "" >> /tmp/ubsan-reports/summary.md
|
||||
if ls /tmp/ubsan-reports/ubsan.* 1>/dev/null 2>&1; then
|
||||
echo "## Errors Found" >> /tmp/ubsan-reports/summary.md
|
||||
for f in /tmp/ubsan-reports/ubsan.*; do
|
||||
echo '```' >> /tmp/ubsan-reports/summary.md
|
||||
head -50 "$f" >> /tmp/ubsan-reports/summary.md
|
||||
echo '```' >> /tmp/ubsan-reports/summary.md
|
||||
echo "" >> /tmp/ubsan-reports/summary.md
|
||||
done
|
||||
else
|
||||
echo "No UBSan errors detected." >> /tmp/ubsan-reports/summary.md
|
||||
fi
|
||||
|
||||
- name: Upload UBSan reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ubsan-reports
|
||||
path: /tmp/ubsan-reports/
|
||||
retention-days: 30
|
||||
|
||||
# ============================================================================
|
||||
# Job 3: Summary Report
|
||||
# ============================================================================
|
||||
summary:
|
||||
name: "Memory Safety Summary"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [asan-test, ubsan-test]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
path: reports/
|
||||
|
||||
- name: Generate summary
|
||||
run: |
|
||||
echo "# Memory Safety Analysis Report" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Commit**: \`${{ github.sha }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Branch**: \`${{ github.ref_name }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Trigger**: \`${{ github.event_name }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
echo "## Job Results" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Analysis | Status |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| AddressSanitizer | \`${{ needs.asan-test.result }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| UndefinedBehaviorSanitizer | \`${{ needs.ubsan-test.result }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ -f reports/asan-reports/summary.md ]; then
|
||||
echo "## ASan Results" >> $GITHUB_STEP_SUMMARY
|
||||
cat reports/asan-reports/summary.md >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ -f reports/ubsan-reports/summary.md ]; then
|
||||
echo "## UBSan Results" >> $GITHUB_STEP_SUMMARY
|
||||
cat reports/ubsan-reports/summary.md >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "## Artifacts" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Sanitizer logs are available as workflow artifacts" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Run with \`workflow_dispatch\` and \`full_scan: true\` for complete codebase analysis" >> $GITHUB_STEP_SUMMARY
|
||||
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
BUILD_TYPE: Release
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
|
|
|
|||
2
.github/workflows/msvc-static-build.yml
vendored
2
.github/workflows/msvc-static-build.yml
vendored
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
BUILD_TYPE: Release
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
|
|
|
|||
244
.github/workflows/nightly-validation.yml
vendored
244
.github/workflows/nightly-validation.yml
vendored
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5
|
||||
|
|
@ -87,7 +87,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5
|
||||
|
|
@ -137,12 +137,12 @@ jobs:
|
|||
|
||||
validate-nuget-macos-x64:
|
||||
name: "Validate NuGet on macOS x64"
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5
|
||||
|
|
@ -165,23 +165,6 @@ jobs:
|
|||
cd test-nuget
|
||||
dotnet new console
|
||||
dotnet add package Microsoft.Z3 --source ../nuget-packages --prerelease
|
||||
# Configure project to properly load native dependencies on macOS x64
|
||||
# Use AnyCPU without RuntimeIdentifier to avoid architecture mismatch
|
||||
# The .NET runtime will automatically select the correct native library from runtimes/osx-x64/native/
|
||||
cat > test-nuget.csproj << 'CSPROJ'
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Z3" Version="*" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
CSPROJ
|
||||
|
||||
- name: Create test code
|
||||
run: |
|
||||
|
|
@ -214,7 +197,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5
|
||||
|
|
@ -237,23 +220,6 @@ jobs:
|
|||
cd test-nuget
|
||||
dotnet new console
|
||||
dotnet add package Microsoft.Z3 --source ../nuget-packages --prerelease
|
||||
# Configure project to properly load native dependencies on macOS ARM64
|
||||
# Use AnyCPU without RuntimeIdentifier to avoid architecture mismatch
|
||||
# The .NET runtime will automatically select the correct native library from runtimes/osx-arm64/native/
|
||||
cat > test-nuget.csproj << 'CSPROJ'
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Z3" Version="*" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
CSPROJ
|
||||
|
||||
- name: Create test code
|
||||
run: |
|
||||
|
|
@ -290,7 +256,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download Windows x64 build from release
|
||||
env:
|
||||
|
|
@ -326,7 +292,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download Windows x86 build from release
|
||||
env:
|
||||
|
|
@ -362,7 +328,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download Ubuntu x64 build from release
|
||||
env:
|
||||
|
|
@ -390,12 +356,12 @@ jobs:
|
|||
|
||||
validate-exe-macos-x64:
|
||||
name: "Validate executable on macOS x64"
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download macOS x64 build from release
|
||||
env:
|
||||
|
|
@ -428,7 +394,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download macOS ARM64 build from release
|
||||
env:
|
||||
|
|
@ -465,7 +431,7 @@ jobs:
|
|||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -504,7 +470,7 @@ jobs:
|
|||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -544,7 +510,7 @@ jobs:
|
|||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -587,7 +553,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -616,7 +582,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -640,12 +606,12 @@ jobs:
|
|||
|
||||
validate-python-wheel-macos-x64:
|
||||
name: "Validate Python wheel on macOS x64"
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -674,7 +640,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -699,18 +665,69 @@ jobs:
|
|||
pip install $wheel.FullName
|
||||
python -c "import z3; x = z3.Int('x'); s = z3.Solver(); s.add(x > 0); print('Result:', s.check()); print('Model:', s.model())"
|
||||
|
||||
validate-python-wheel-riscv64:
|
||||
name: "Validate Python wheel for RISC-V 64"
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Download RISC-V 64 Python wheel from release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
TAG="${{ github.event.inputs.release_tag }}"
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG="Nightly"
|
||||
fi
|
||||
gh release download $TAG --pattern "*riscv64.whl" --dir wheels
|
||||
|
||||
- name: Verify wheel platform tag and contents
|
||||
run: |
|
||||
pip install wheel
|
||||
WHEEL_FILE=$(ls wheels/*.whl | head -n 1)
|
||||
echo "Wheel file: $WHEEL_FILE"
|
||||
|
||||
# Check that the wheel has a riscv64 platform tag
|
||||
WHEEL_NAME=$(basename $WHEEL_FILE)
|
||||
echo "Wheel name: $WHEEL_NAME"
|
||||
if echo "$WHEEL_NAME" | grep -q "riscv64"; then
|
||||
echo "riscv64 platform tag found"
|
||||
else
|
||||
echo "ERROR: riscv64 platform tag not found in wheel name"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Inspect wheel contents
|
||||
python -m zipfile -l $WHEEL_FILE
|
||||
|
||||
# Verify wheel contains z3 library
|
||||
if python -m zipfile -l $WHEEL_FILE | grep -q "libz3"; then
|
||||
echo "libz3 found in wheel"
|
||||
else
|
||||
echo "ERROR: libz3 not found in wheel"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# MACOS DYLIB HEADERPAD VALIDATION
|
||||
# ============================================================================
|
||||
|
||||
validate-macos-headerpad-x64:
|
||||
name: "Validate macOS x64 dylib headerpad"
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download macOS x64 build from release
|
||||
env:
|
||||
|
|
@ -762,7 +779,7 @@ jobs:
|
|||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download macOS ARM64 build from release
|
||||
env:
|
||||
|
|
@ -806,3 +823,118 @@ jobs:
|
|||
echo "✗ install_name_tool failed to update install name"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# BUILD SCRIPT UNIT TESTS
|
||||
# ============================================================================
|
||||
|
||||
validate-build-script-tests:
|
||||
name: "Validate build script unit tests"
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Run build script unit tests
|
||||
run: python -m unittest discover -s scripts/tests -p "test_*.py" -v
|
||||
|
||||
# ============================================================================
|
||||
# DOTNET MANAGED WRAPPER ARCHITECTURE VALIDATION
|
||||
# ============================================================================
|
||||
|
||||
validate-dotnet-anycpu:
|
||||
name: "Validate Microsoft.Z3.dll is AnyCPU (issue #9863)"
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download NuGet package from release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
TAG="${{ github.event.inputs.release_tag }}"
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG="Nightly"
|
||||
fi
|
||||
gh release download $TAG --pattern "*.nupkg" --dir nuget-packages
|
||||
|
||||
- name: Extract managed DLL from NuGet package
|
||||
run: |
|
||||
# NuGet packages are ZIP archives; exclude the symbols package
|
||||
NUPKG=$(ls nuget-packages/*.nupkg | grep -v '\.symbols\.' | grep -v '\.snupkg' | head -n 1)
|
||||
echo "Checking package: $NUPKG"
|
||||
unzip -q "$NUPKG" "lib/netstandard2.0/Microsoft.Z3.dll" -d nupkg-extracted
|
||||
|
||||
- name: Check PE Machine field is AnyCPU (not architecture-specific)
|
||||
run: |
|
||||
python3 - <<'EOF'
|
||||
import struct
|
||||
import sys
|
||||
|
||||
dll_path = "nupkg-extracted/lib/netstandard2.0/Microsoft.Z3.dll"
|
||||
|
||||
with open(dll_path, 'rb') as f:
|
||||
# Verify MZ magic
|
||||
if f.read(2) != b'MZ':
|
||||
print("ERROR: Not a valid PE file (missing MZ header)")
|
||||
sys.exit(1)
|
||||
|
||||
# Read PE header offset stored at 0x3C in the DOS stub
|
||||
f.seek(0x3C)
|
||||
pe_offset = struct.unpack('<I', f.read(4))[0]
|
||||
|
||||
# Verify PE signature
|
||||
f.seek(pe_offset)
|
||||
if f.read(4) != b'PE\x00\x00':
|
||||
print("ERROR: Missing PE\\0\\0 signature")
|
||||
sys.exit(1)
|
||||
|
||||
# COFF Machine field is the 2 bytes immediately after the PE signature
|
||||
machine = struct.unpack('<H', f.read(2))[0]
|
||||
|
||||
machine_names = {
|
||||
0x0000: "Unknown/AnyCPU",
|
||||
0x014C: "i386 (AnyCPU for managed assemblies)",
|
||||
0x8664: "AMD64/x64",
|
||||
0xAA64: "ARM64",
|
||||
0x01C0: "ARM",
|
||||
0x01C4: "ARM Thumb-2",
|
||||
}
|
||||
machine_name = machine_names.get(machine, f"0x{machine:04X}")
|
||||
print(f"Machine field: 0x{machine:04X} = {machine_name}")
|
||||
|
||||
if machine == 0x8664:
|
||||
print()
|
||||
print("FAIL: Machine is AMD64 (0x8664).")
|
||||
print("This prevents loading on arm64 .NET hosts even though the assembly")
|
||||
print("contains only pure IL (CorFlags.ILONLY=True).")
|
||||
print("The DLL must be built as AnyCPU (Machine=0x014C) so the CLR loader")
|
||||
print("accepts it on every host architecture.")
|
||||
print("See issue #9863 for details.")
|
||||
sys.exit(1)
|
||||
elif machine == 0xAA64:
|
||||
print()
|
||||
print("FAIL: Machine is ARM64 (0xAA64).")
|
||||
print("This prevents loading on x64 .NET hosts.")
|
||||
print("The DLL must be built as AnyCPU (Machine=0x014C).")
|
||||
sys.exit(1)
|
||||
elif machine in (0x014C, 0x0000):
|
||||
print()
|
||||
print("PASS: Machine field indicates AnyCPU.")
|
||||
print("Microsoft.Z3.dll will load on any .NET host architecture.")
|
||||
else:
|
||||
print()
|
||||
print(f"FAIL: Unexpected Machine field 0x{machine:04X}.")
|
||||
print("Expected 0x014C (i386/AnyCPU) for a managed-only assembly.")
|
||||
sys.exit(1)
|
||||
EOF
|
||||
|
|
|
|||
310
.github/workflows/nightly.yml
vendored
310
.github/workflows/nightly.yml
vendored
|
|
@ -16,13 +16,17 @@ on:
|
|||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: nightly-release
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
MAJOR: '4'
|
||||
MINOR: '16'
|
||||
PATCH: '0'
|
||||
MINOR: '17'
|
||||
PATCH: '1'
|
||||
|
||||
jobs:
|
||||
# ============================================================================
|
||||
|
|
@ -33,9 +37,11 @@ jobs:
|
|||
name: "Mac Build x64"
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "13.3"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -43,10 +49,23 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Build
|
||||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=x64
|
||||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=x64 --os=osx-13.3
|
||||
|
||||
- name: Validate libz3.dylib and z3 architecture (must be x86_64)
|
||||
run: |
|
||||
set -e
|
||||
for f in build-dist/libz3.dylib build-dist/z3; do
|
||||
ARCH=$(lipo -archs "$f")
|
||||
echo "$f architecture: $ARCH"
|
||||
if [ "$ARCH" != "x86_64" ]; then
|
||||
echo "ERROR: $f has arch '$ARCH', expected 'x86_64' (see issue #9662)"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "OK: macOS x64 artifacts are x86_64"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: macOsBuild
|
||||
path: dist/*.zip
|
||||
|
|
@ -56,9 +75,11 @@ jobs:
|
|||
name: "Mac ARM64 Build"
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "13.3"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -66,10 +87,23 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Build
|
||||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=arm64
|
||||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=arm64 --os=osx-13.3
|
||||
|
||||
- name: Validate libz3.dylib and z3 architecture (must be arm64)
|
||||
run: |
|
||||
set -e
|
||||
for f in build-dist/libz3.dylib build-dist/z3; do
|
||||
ARCH=$(lipo -archs "$f")
|
||||
echo "$f architecture: $ARCH"
|
||||
if [ "$ARCH" != "arm64" ]; then
|
||||
echo "ERROR: $f has arch '$ARCH', expected 'arm64' (see issue #9662)"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "OK: macOS arm64 artifacts are arm64"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: MacArm64
|
||||
path: dist/*.zip
|
||||
|
|
@ -86,10 +120,10 @@ jobs:
|
|||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download macOS x64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: macOsBuild
|
||||
path: artifacts
|
||||
|
|
@ -101,6 +135,17 @@ jobs:
|
|||
Z3_DIR=$(find . -maxdepth 1 -type d -name "z3-*" | head -n 1)
|
||||
echo "Z3_DIR=$Z3_DIR" >> $GITHUB_ENV
|
||||
|
||||
- name: Validate shipped libz3.dylib architecture (must be x86_64)
|
||||
run: |
|
||||
set -e
|
||||
DYLIB="artifacts/$Z3_DIR/bin/libz3.dylib"
|
||||
ARCH=$(lipo -archs "$DYLIB")
|
||||
echo "Shipped $DYLIB architecture: $ARCH"
|
||||
if [ "$ARCH" != "x86_64" ]; then
|
||||
echo "ERROR: x64 nightly zip contains '$ARCH' libz3.dylib (see issue #9662)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Test install_name_tool with headerpad
|
||||
run: |
|
||||
cd artifacts/$Z3_DIR/bin
|
||||
|
|
@ -134,10 +179,10 @@ jobs:
|
|||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download macOS ARM64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: MacArm64
|
||||
path: artifacts
|
||||
|
|
@ -149,6 +194,17 @@ jobs:
|
|||
Z3_DIR=$(find . -maxdepth 1 -type d -name "z3-*" | head -n 1)
|
||||
echo "Z3_DIR=$Z3_DIR" >> $GITHUB_ENV
|
||||
|
||||
- name: Validate shipped libz3.dylib architecture (must be arm64)
|
||||
run: |
|
||||
set -e
|
||||
DYLIB="artifacts/$Z3_DIR/bin/libz3.dylib"
|
||||
ARCH=$(lipo -archs "$DYLIB")
|
||||
echo "Shipped $DYLIB architecture: $ARCH"
|
||||
if [ "$ARCH" != "arm64" ]; then
|
||||
echo "ERROR: arm64 nightly zip contains '$ARCH' libz3.dylib (see issue #9662)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Test install_name_tool with headerpad
|
||||
run: |
|
||||
cd artifacts/$Z3_DIR/bin
|
||||
|
|
@ -181,7 +237,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -196,9 +252,9 @@ jobs:
|
|||
|
||||
- name: Test
|
||||
run: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2
|
||||
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: UbuntuBuild
|
||||
path: dist/*.zip
|
||||
|
|
@ -210,7 +266,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -233,7 +289,7 @@ jobs:
|
|||
python scripts/mk_unix_dist.py --nodotnet --arch=arm64
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: UbuntuArm64
|
||||
path: dist/*.zip
|
||||
|
|
@ -245,7 +301,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -269,9 +325,9 @@ jobs:
|
|||
eval $(opam config env)
|
||||
python scripts/mk_make.py --ml
|
||||
cd build
|
||||
make -j3
|
||||
make -j3 examples
|
||||
make -j3 test-z3
|
||||
make -j$(nproc)
|
||||
make -j$(nproc) examples
|
||||
make -j$(nproc) test-z3
|
||||
cd ..
|
||||
|
||||
- name: Generate documentation
|
||||
|
|
@ -288,7 +344,7 @@ jobs:
|
|||
run: zip -r z3doc.zip doc/api
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: UbuntuDoc
|
||||
path: z3doc.zip
|
||||
|
|
@ -301,11 +357,19 @@ jobs:
|
|||
container: quay.io/pypa/manylinux_2_28_x86_64:latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Select Python
|
||||
run: |
|
||||
# Use the first available manylinux interpreter for deterministic selection.
|
||||
PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
|
||||
test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
|
||||
echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
|
||||
"$PYTHON" --version
|
||||
|
||||
- name: Setup Python environment
|
||||
run: |
|
||||
/opt/python/cp38-cp38/bin/python -m venv $PWD/env
|
||||
"$PYTHON" -m venv $PWD/env
|
||||
echo "$PWD/env/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install build tools
|
||||
|
|
@ -318,7 +382,7 @@ jobs:
|
|||
run: pip install ./src/api/python/wheelhouse/*.whl && python - <src/api/python/z3test.py z3 && python - <src/api/python/z3test.py z3num
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ManyLinuxPythonBuildAMD64
|
||||
path: src/api/python/wheelhouse/*.whl
|
||||
|
|
@ -331,7 +395,7 @@ jobs:
|
|||
container: quay.io/pypa/manylinux_2_28_x86_64:latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download ARM toolchain
|
||||
run: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz'
|
||||
|
|
@ -341,9 +405,17 @@ jobs:
|
|||
mkdir -p /tmp/arm-toolchain/
|
||||
tar xf /tmp/arm-toolchain.tar.xz -C /tmp/arm-toolchain/ --strip-components=1
|
||||
|
||||
- name: Select Python
|
||||
run: |
|
||||
# Use the first available manylinux interpreter for deterministic selection.
|
||||
PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
|
||||
test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
|
||||
echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
|
||||
"$PYTHON" --version
|
||||
|
||||
- name: Setup Python environment
|
||||
run: |
|
||||
/opt/python/cp38-cp38/bin/python -m venv $PWD/env
|
||||
"$PYTHON" -m venv $PWD/env
|
||||
echo "$PWD/env/bin" >> $GITHUB_PATH
|
||||
echo "/tmp/arm-toolchain/bin" >> $GITHUB_PATH
|
||||
echo "/tmp/arm-toolchain/aarch64-none-linux-gnu/libc/usr/bin" >> $GITHUB_PATH
|
||||
|
|
@ -358,19 +430,74 @@ jobs:
|
|||
run: cd src/api/python && CC=aarch64-none-linux-gnu-gcc CXX=aarch64-none-linux-gnu-g++ AR=aarch64-none-linux-gnu-ar LD=aarch64-none-linux-gnu-ld Z3_CROSS_COMPILING=aarch64 python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../..
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ManyLinuxPythonBuildArm64
|
||||
path: src/api/python/wheelhouse/*.whl
|
||||
retention-days: 2
|
||||
|
||||
manylinux-python-riscv64:
|
||||
name: "Python bindings (manylinux RISC-V 64 cross)"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
container: quay.io/pypa/manylinux_2_28_x86_64:latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download RISC-V toolchain
|
||||
run: curl -L -o /tmp/riscv-toolchain.tar.gz 'https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2024.09.03/riscv64-glibc-ubuntu-20.04-gcc-nightly-2024.09.03-nightly.tar.gz'
|
||||
|
||||
- name: Extract RISC-V toolchain
|
||||
run: |
|
||||
mkdir -p /tmp/riscv-toolchain/
|
||||
tar xf /tmp/riscv-toolchain.tar.gz -C /tmp/riscv-toolchain/ --strip-components=1
|
||||
|
||||
- name: Install MPFR 4 (required by RISC-V toolchain host binaries)
|
||||
run: |
|
||||
dnf install -y gmp-devel
|
||||
curl -L -o /tmp/mpfr.tar.xz https://ftp.gnu.org/gnu/mpfr/mpfr-4.2.1.tar.xz
|
||||
tar xf /tmp/mpfr.tar.xz -C /tmp/
|
||||
cd /tmp/mpfr-4.2.1 && ./configure --prefix=/usr/local --disable-static && make -j$(nproc) && make install
|
||||
ldconfig
|
||||
|
||||
- name: Select Python
|
||||
run: |
|
||||
# Use the first available manylinux interpreter for deterministic selection.
|
||||
PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
|
||||
test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
|
||||
echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
|
||||
"$PYTHON" --version
|
||||
|
||||
- name: Setup Python environment
|
||||
run: |
|
||||
"$PYTHON" -m venv $PWD/env
|
||||
echo "$PWD/env/bin" >> $GITHUB_PATH
|
||||
echo "/tmp/riscv-toolchain/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install build tools
|
||||
run: |
|
||||
echo $PATH
|
||||
stat $(which riscv64-unknown-linux-gnu-gcc)
|
||||
pip install build git+https://github.com/rhelmot/auditwheel
|
||||
|
||||
- name: Build wheels
|
||||
run: cd src/api/python && CC=riscv64-unknown-linux-gnu-gcc CXX=riscv64-unknown-linux-gnu-g++ AR=riscv64-unknown-linux-gnu-ar LD=riscv64-unknown-linux-gnu-ld Z3_CROSS_COMPILING=riscv64 python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../..
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ManyLinuxPythonBuildRiscv64
|
||||
path: src/api/python/wheelhouse/*.whl
|
||||
retention-days: 2
|
||||
|
||||
windows-build-x64:
|
||||
name: "Windows x64 build"
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -380,11 +507,12 @@ jobs:
|
|||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64 || exit /b 1
|
||||
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: WindowsBuild-x64
|
||||
path: dist/*.zip
|
||||
|
|
@ -396,7 +524,7 @@ jobs:
|
|||
timeout-minutes: 120
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -406,11 +534,12 @@ jobs:
|
|||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x86 || exit /b 1
|
||||
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: WindowsBuild-x86
|
||||
path: dist/*.zip
|
||||
|
|
@ -422,7 +551,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -432,11 +561,12 @@ jobs:
|
|||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64 || exit /b 1
|
||||
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.PATCH }} --zip
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: WindowsBuild-arm64
|
||||
path: dist/arm64/*.zip
|
||||
|
|
@ -452,7 +582,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -460,43 +590,43 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Download Win64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-x64
|
||||
path: package
|
||||
|
||||
- name: Download Win ARM64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-arm64
|
||||
path: package
|
||||
|
||||
- name: Download Ubuntu Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: UbuntuBuild
|
||||
path: package
|
||||
|
||||
- name: Download Ubuntu ARM64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: UbuntuArm64
|
||||
path: package
|
||||
|
||||
- name: Download macOS Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: macOsBuild
|
||||
path: package
|
||||
|
||||
- name: Download macOS Arm64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: MacArm64
|
||||
path: package
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v2
|
||||
uses: nuget/setup-nuget@v4
|
||||
with:
|
||||
nuget-version: 'latest'
|
||||
|
||||
|
|
@ -513,7 +643,7 @@ jobs:
|
|||
nuget pack out\Microsoft.Z3.sym.nuspec -Version ${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.PATCH }}.${{ github.run_number }} -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: NuGet
|
||||
path: |
|
||||
|
|
@ -527,7 +657,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -535,13 +665,13 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-x86
|
||||
path: package
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v2
|
||||
uses: nuget/setup-nuget@v4
|
||||
with:
|
||||
nuget-version: 'latest'
|
||||
|
||||
|
|
@ -558,7 +688,7 @@ jobs:
|
|||
nuget pack out\Microsoft.Z3.x86.sym.nuspec -Version ${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.PATCH }}.${{ github.run_number }} -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: NuGet32
|
||||
path: |
|
||||
|
|
@ -568,11 +698,11 @@ jobs:
|
|||
|
||||
python-package:
|
||||
name: "Python packaging"
|
||||
needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64]
|
||||
needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64, manylinux-python-riscv64]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -580,47 +710,53 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Download macOS x64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: macOsBuild
|
||||
path: artifacts
|
||||
|
||||
- name: Download macOS Arm64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: MacArm64
|
||||
path: artifacts
|
||||
|
||||
- name: Download Win64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-x64
|
||||
path: artifacts
|
||||
|
||||
- name: Download Win32 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-x86
|
||||
path: artifacts
|
||||
|
||||
- name: Download Win ARM64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-arm64
|
||||
path: artifacts
|
||||
|
||||
- name: Download ManyLinux AMD64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: ManyLinuxPythonBuildAMD64
|
||||
path: artifacts
|
||||
|
||||
- name: Download ManyLinux Arm64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: ManyLinuxPythonBuildArm64
|
||||
path: artifacts
|
||||
|
||||
- name: Download ManyLinux RISC-V 64 Build
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: ManyLinuxPythonBuildRiscv64
|
||||
path: artifacts
|
||||
|
||||
- name: Extract builds
|
||||
run: |
|
||||
cd artifacts
|
||||
|
|
@ -651,7 +787,7 @@ jobs:
|
|||
cp artifacts/*.whl src/api/python/dist/.
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: PythonPackages
|
||||
path: src/api/python/dist/*
|
||||
|
|
@ -681,10 +817,10 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
path: tmp
|
||||
|
||||
|
|
@ -692,19 +828,44 @@ jobs:
|
|||
run: ls -R tmp
|
||||
|
||||
- name: Delete existing Nightly release and tag
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Delete the release first (this also deletes assets)
|
||||
gh release delete Nightly --yes || echo "No release to delete"
|
||||
# Delete the tag explicitly
|
||||
git push origin :refs/tags/Nightly || echo "No tag to delete"
|
||||
if gh release view Nightly > /dev/null 2>&1; then
|
||||
# Delete the release and associated tag if it exists.
|
||||
if ! gh release delete Nightly --yes --cleanup-tag; then
|
||||
echo "Failed to delete existing Nightly release/tag"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "No release to delete"
|
||||
fi
|
||||
|
||||
if git ls-remote --exit-code --tags origin refs/tags/Nightly > /dev/null 2>&1; then
|
||||
# Tag may exist without a release (for example, if a previous run failed mid-deploy).
|
||||
git push origin :refs/tags/Nightly
|
||||
fi
|
||||
|
||||
- name: Create Nightly release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
git tag -f Nightly "${{ github.sha }}"
|
||||
if ! git push --force origin refs/tags/Nightly; then
|
||||
echo "Failed to push Nightly tag to origin"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TAG_SHA="$(git ls-remote --tags origin refs/tags/Nightly refs/tags/Nightly^{} | awk '
|
||||
$2 == "refs/tags/Nightly^{}" { sha = $1 }
|
||||
$2 == "refs/tags/Nightly" && sha == "" { sha = $1 }
|
||||
END { print sha }
|
||||
')"
|
||||
if [ "$TAG_SHA" != "${{ github.sha }}" ]; then
|
||||
echo "Nightly tag points to $TAG_SHA, expected ${{ github.sha }}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ls
|
||||
find tmp -type f \( -name "*.zip" -o -name "*.whl" -o -name "*.tar.gz" -o -name "*.nupkg" -o -name "*.snupkg" \) -print0 > release_files.txt
|
||||
|
||||
|
|
@ -730,7 +891,7 @@ jobs:
|
|||
--title "Nightly" \
|
||||
--notes "Automated nightly build from commit ${{ github.sha }}" \
|
||||
--prerelease \
|
||||
--target ${{ github.sha }} \
|
||||
--verify-tag \
|
||||
"${unique_files[@]}"
|
||||
else
|
||||
echo "No files to release after deduplication"
|
||||
|
|
@ -749,14 +910,23 @@ jobs:
|
|||
contents: read
|
||||
steps:
|
||||
- name: Download Python packages
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: PythonPackages
|
||||
path: dist
|
||||
|
||||
- name: Rewrite macOS wheel tags unsupported by test.PyPI
|
||||
run: |
|
||||
# TestPyPI rejects current macOS wheel tags such as macosx_13_3_*.
|
||||
# Rewrite only the unsupported 13_3 tag to 13 for upload validation.
|
||||
for whl in dist/*-macosx_13_3_*.whl; do
|
||||
[ -e "$whl" ] || continue
|
||||
mv "$whl" "${whl/macosx_13_3_/macosx_13_0_}"
|
||||
done
|
||||
ls -l dist
|
||||
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
packages-dir: dist
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
|
||||
|
|
|
|||
63
.github/workflows/nuget-build.yml
vendored
63
.github/workflows/nuget-build.yml
vendored
|
|
@ -4,9 +4,9 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version number for the NuGet package (e.g., 4.16.0)'
|
||||
description: 'Version number for the NuGet package (e.g., 4.17.1)'
|
||||
required: true
|
||||
default: '4.16.0'
|
||||
default: '4.17.1'
|
||||
push:
|
||||
tags:
|
||||
- 'z3-*'
|
||||
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -30,11 +30,12 @@ jobs:
|
|||
- name: Build Windows x64
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.16.0' }} --zip
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64 || exit /b 1
|
||||
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.17.1' }} --zip
|
||||
|
||||
- name: Upload Windows x64 artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: windows-x64
|
||||
path: dist/*.zip
|
||||
|
|
@ -44,7 +45,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -54,11 +55,12 @@ jobs:
|
|||
- name: Build Windows x86
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
|
||||
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.16.0' }} --zip
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x86 || exit /b 1
|
||||
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.17.1' }} --zip
|
||||
|
||||
- name: Upload Windows x86 artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: windows-x86
|
||||
path: dist/*.zip
|
||||
|
|
@ -68,7 +70,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -78,11 +80,12 @@ jobs:
|
|||
- name: Build Windows ARM64
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64
|
||||
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.16.0' }} --zip
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64 || exit /b 1
|
||||
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.17.1' }} --zip
|
||||
|
||||
- name: Upload Windows ARM64 artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: windows-arm64
|
||||
path: build-dist\arm64\dist\*.zip
|
||||
|
|
@ -92,7 +95,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -103,7 +106,7 @@ jobs:
|
|||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk
|
||||
|
||||
- name: Upload Ubuntu artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ubuntu
|
||||
path: dist/*.zip
|
||||
|
|
@ -113,7 +116,7 @@ jobs:
|
|||
runs-on: macos-14
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -124,7 +127,7 @@ jobs:
|
|||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk
|
||||
|
||||
- name: Upload macOS x64 artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: macos-x64
|
||||
path: dist/*.zip
|
||||
|
|
@ -134,7 +137,7 @@ jobs:
|
|||
runs-on: macos-14
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -145,7 +148,7 @@ jobs:
|
|||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=arm64
|
||||
|
||||
- name: Upload macOS ARM64 artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: macos-arm64
|
||||
path: dist/*.zip
|
||||
|
|
@ -157,7 +160,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -165,7 +168,7 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
path: packages
|
||||
|
||||
|
|
@ -181,7 +184,7 @@ jobs:
|
|||
ls -la package-files/
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v2
|
||||
uses: nuget/setup-nuget@v4
|
||||
with:
|
||||
nuget-version: 'latest'
|
||||
|
||||
|
|
@ -189,7 +192,7 @@ jobs:
|
|||
shell: cmd
|
||||
run: |
|
||||
cd package-files
|
||||
python ..\scripts\mk_nuget_task.py . ${{ github.event.inputs.version || '4.16.0' }} https://github.com/Z3Prover/z3 ${{ github.ref_name }} ${{ github.sha }} ${{ github.workspace }} symbols
|
||||
python ..\scripts\mk_nuget_task.py . ${{ github.event.inputs.version || '4.17.1' }} https://github.com/Z3Prover/z3 ${{ github.ref_name }} ${{ github.sha }} ${{ github.workspace }} symbols
|
||||
|
||||
- name: Pack NuGet package
|
||||
shell: cmd
|
||||
|
|
@ -198,7 +201,7 @@ jobs:
|
|||
nuget pack out\Microsoft.Z3.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
|
||||
|
||||
- name: Upload NuGet package
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: nuget-x64
|
||||
path: |
|
||||
|
|
@ -212,7 +215,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -220,7 +223,7 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Download x86 artifact
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: windows-x86
|
||||
path: packages
|
||||
|
|
@ -230,7 +233,7 @@ jobs:
|
|||
run: find packages -type f
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v2
|
||||
uses: nuget/setup-nuget@v4
|
||||
with:
|
||||
nuget-version: 'latest'
|
||||
|
||||
|
|
@ -238,7 +241,7 @@ jobs:
|
|||
shell: cmd
|
||||
run: |
|
||||
cd packages
|
||||
python ..\scripts\mk_nuget_task.py . ${{ github.event.inputs.version || '4.16.0' }} https://github.com/Z3Prover/z3 ${{ github.ref_name }} ${{ github.sha }} ${{ github.workspace }} symbols x86
|
||||
python ..\scripts\mk_nuget_task.py . ${{ github.event.inputs.version || '4.17.1' }} https://github.com/Z3Prover/z3 ${{ github.ref_name }} ${{ github.sha }} ${{ github.workspace }} symbols x86
|
||||
|
||||
- name: Pack NuGet package
|
||||
shell: cmd
|
||||
|
|
@ -247,7 +250,7 @@ jobs:
|
|||
nuget pack out\Microsoft.Z3.x86.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
|
||||
|
||||
- name: Upload NuGet package
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: nuget-x86
|
||||
path: |
|
||||
|
|
|
|||
20
.github/workflows/ocaml.yaml
vendored
20
.github/workflows/ocaml.yaml
vendored
|
|
@ -17,32 +17,24 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
# Cache ccache (shared across runs)
|
||||
- name: Cache ccache
|
||||
uses: actions/cache@v5.0.3
|
||||
uses: actions/cache@v6.1.0
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ runner.os }}-ccache-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-ccache-
|
||||
|
||||
# Cache opam (compiler + packages)
|
||||
- name: Cache opam
|
||||
uses: actions/cache@v5.0.3
|
||||
with:
|
||||
path: ~/.opam
|
||||
key: ${{ runner.os }}-opam-${{ matrix.ocaml-version }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-opam-${{ matrix.ocaml-version }}-
|
||||
|
||||
# Setup OCaml via action
|
||||
# Setup OCaml via action (handles opam caching internally)
|
||||
- uses: ocaml/setup-ocaml@v3
|
||||
with:
|
||||
ocaml-compiler: ${{ matrix.ocaml-version }}
|
||||
opam-disable-sandboxing: true
|
||||
|
||||
cache-prefix: v1
|
||||
|
||||
# Platform-specific dependencies
|
||||
- name: Install system dependencies (Ubuntu)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
|
@ -123,4 +115,4 @@ jobs:
|
|||
- name: Run ml_example (native)
|
||||
run: |
|
||||
export DYLD_LIBRARY_PATH=$(pwd)/build
|
||||
./ml_example
|
||||
./ml_example
|
||||
|
|
|
|||
1568
.github/workflows/ostrich-benchmark.lock.yml
generated
vendored
Normal file
1568
.github/workflows/ostrich-benchmark.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
423
.github/workflows/ostrich-benchmark.md
vendored
Normal file
423
.github/workflows/ostrich-benchmark.md
vendored
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
---
|
||||
description: Run Z3 string solver benchmarks (seq vs nseq) and ZIPT on all Ostrich benchmarks from tests/ostrich.zip on the c3 branch and post results as a GitHub discussion
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 6 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: read-all
|
||||
|
||||
network:
|
||||
allowed:
|
||||
- defaults
|
||||
- api.nuget.org
|
||||
|
||||
tools:
|
||||
bash: true
|
||||
github:
|
||||
toolsets: [default]
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-discussion:
|
||||
title-prefix: "[Ostrich Benchmark] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
|
||||
timeout-minutes: 180
|
||||
|
||||
steps:
|
||||
- name: Checkout c3 branch
|
||||
uses: actions/checkout@v6.0.2
|
||||
with:
|
||||
ref: c3
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
---
|
||||
|
||||
|
||||
# Ostrich Benchmark: Z3 c3 branch vs ZIPT
|
||||
|
||||
You are an AI agent that benchmarks Z3 string solvers (`seq` and `nseq`) and the standalone ZIPT solver on all SMT-LIB2 benchmarks from the `tests/ostrich.zip` archive on the `c3` branch, and publishes a summary report as a GitHub discussion.
|
||||
|
||||
## Context
|
||||
|
||||
- **Repository**: ${{ github.repository }}
|
||||
- **Workspace**: ${{ github.workspace }}
|
||||
- **Branch**: c3 (already checked out by the workflow setup step)
|
||||
|
||||
## Phase 1: Build Z3
|
||||
|
||||
Build Z3 from the checked-out `c3` branch using CMake + Ninja, including the .NET bindings required by ZIPT.
|
||||
|
||||
```bash
|
||||
cd ${{ github.workspace }}
|
||||
|
||||
# Install build dependencies if missing
|
||||
sudo apt-get install -y ninja-build cmake python3 zstd dotnet-sdk-8.0 unzip 2>/dev/null || true
|
||||
|
||||
# Configure the build in Release mode for better performance and lower memory usage
|
||||
# (Release mode is sufficient for benchmarking; the workflow does not use -tr: trace flags)
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DZ3_BUILD_DOTNET_BINDINGS=ON 2>&1 | tail -20
|
||||
|
||||
# Build z3 binary and .NET bindings SYNCHRONOUSLY (do NOT add & to background these commands).
|
||||
# Running ninja in the background while the LLM agent is also active causes OOM and kills the
|
||||
# agent process. Wait for each build command to finish before continuing.
|
||||
# -j1 limits parallelism to reduce peak memory usage alongside the LLM agent process.
|
||||
ninja z3 2>&1 | tail -30
|
||||
ninja build_z3_dotnet_bindings 2>&1 | tail -20
|
||||
|
||||
# Verify the build succeeded
|
||||
./z3 --version
|
||||
|
||||
# Locate the Microsoft.Z3.dll produced by the build
|
||||
Z3_DOTNET_DLL=$(find . -name "Microsoft.Z3.dll" -not -path "*/obj/*" | head -1)
|
||||
if [ -z "$Z3_DOTNET_DLL" ]; then
|
||||
echo "ERROR: Microsoft.Z3.dll not found after build"
|
||||
exit 1
|
||||
fi
|
||||
echo "Found Microsoft.Z3.dll at: $Z3_DOTNET_DLL"
|
||||
```
|
||||
|
||||
If the build fails, report the error clearly and exit without proceeding.
|
||||
|
||||
Once the binary is confirmed working, call the `noop` safe-output tool with the message `"Z3 built successfully from the c3 branch. Starting ZIPT build and benchmark — results will be posted as a GitHub Discussion once complete."` This keepalive call refreshes the safe-output MCP session before the long build and benchmark phases begin, preventing a session timeout.
|
||||
|
||||
## Phase 2a: Clone and Build ZIPT
|
||||
|
||||
Clone the ZIPT solver from the `parikh` branch and compile it against the Z3 .NET bindings built in Phase 1.
|
||||
|
||||
```bash
|
||||
cd ${{ github.workspace }}
|
||||
|
||||
# Re-locate the Microsoft.Z3.dll if needed
|
||||
Z3_DOTNET_DLL=$(find build -name "Microsoft.Z3.dll" -not -path "*/obj/*" | head -1)
|
||||
Z3_LIB_DIR=${{ github.workspace }}/build
|
||||
|
||||
# Clone ZIPT (parikh branch)
|
||||
git clone --depth=1 --branch parikh https://github.com/CEisenhofer/ZIPT.git /tmp/zipt
|
||||
|
||||
# Patch ZIPT.csproj to point at the freshly built Microsoft.Z3.dll
|
||||
# (the repo has a Windows-relative hardcoded path that won't exist here)
|
||||
sed -i "s|<HintPath>.*</HintPath>|<HintPath>$Z3_DOTNET_DLL</HintPath>|" /tmp/zipt/ZIPT/ZIPT.csproj
|
||||
|
||||
# Build ZIPT in Release mode
|
||||
cd /tmp/zipt/ZIPT
|
||||
dotnet build --configuration Release 2>&1 | tail -20
|
||||
|
||||
# Locate the built ZIPT.dll
|
||||
ZIPT_DLL=$(find /tmp/zipt/ZIPT/bin/Release -name "ZIPT.dll" | head -1)
|
||||
if [ -z "$ZIPT_DLL" ]; then
|
||||
echo "ERROR: ZIPT.dll not found after build"
|
||||
exit 1
|
||||
fi
|
||||
echo "ZIPT binary: $ZIPT_DLL"
|
||||
|
||||
# Make libz3.so visible to the .NET runtime at ZIPT startup
|
||||
ZIPT_OUT_DIR=$(dirname "$ZIPT_DLL")
|
||||
if cp "$Z3_LIB_DIR/libz3.so" "$ZIPT_OUT_DIR/" 2>/dev/null; then
|
||||
echo "Copied libz3.so to $ZIPT_OUT_DIR"
|
||||
else
|
||||
echo "WARNING: could not copy libz3.so to $ZIPT_OUT_DIR — setting LD_LIBRARY_PATH fallback"
|
||||
fi
|
||||
export LD_LIBRARY_PATH="$Z3_LIB_DIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
echo "ZIPT build complete."
|
||||
```
|
||||
|
||||
If the ZIPT build fails, note the error in the report but continue with the Z3-only benchmark columns.
|
||||
|
||||
## Phase 2b: Extract Benchmark Files
|
||||
|
||||
Extract all SMT-LIB2 files from the `tests/ostrich.zip` archive.
|
||||
|
||||
```bash
|
||||
cd ${{ github.workspace }}
|
||||
|
||||
# Extract the zip archive
|
||||
mkdir -p /tmp/ostrich_benchmarks
|
||||
unzip -q tests/ostrich.zip -d /tmp/ostrich_benchmarks
|
||||
|
||||
# List all .smt2 files
|
||||
find /tmp/ostrich_benchmarks -name "*.smt2" -type f | sort > /tmp/all_ostrich_files.txt
|
||||
TOTAL_FILES=$(wc -l < /tmp/all_ostrich_files.txt)
|
||||
echo "Total Ostrich .smt2 files: $TOTAL_FILES"
|
||||
|
||||
if [ "$TOTAL_FILES" -eq 0 ]; then
|
||||
echo "ERROR: No .smt2 files found in tests/ostrich.zip"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
Once the benchmark files are confirmed, call the `noop` safe-output tool with the message `"Benchmark files ready: <TOTAL_FILES> Ostrich .smt2 files extracted. Starting benchmark run — this may take over an hour."` This second keepalive refreshes the safe-output MCP session immediately before the long per-file benchmark loop begins.
|
||||
|
||||
## Phase 3: Run Benchmarks
|
||||
|
||||
Run every file from `/tmp/all_ostrich_files.txt` with both Z3 string solvers and ZIPT. Use a **5-second timeout** per run.
|
||||
|
||||
For each file, run:
|
||||
1. `z3 smt.string_solver=seq -T:5 <file>` — seq solver
|
||||
2. `z3 smt.string_solver=nseq -T:5 <file>` — nseq (ZIPT) solver
|
||||
3. `dotnet <ZIPT.dll> -t:5000 <file>` — standalone ZIPT solver (milliseconds)
|
||||
|
||||
Capture:
|
||||
- **Verdict**: `sat`, `unsat`, `unknown`, `timeout` (if exit code indicates timeout or process is killed), or `bug` (if a solver crashes / produces a non-standard result)
|
||||
- **Time** (seconds): wall-clock time for the run
|
||||
- A row is flagged `SOUNDNESS_DISAGREEMENT` when any two solvers that both produced a definitive answer (sat/unsat) disagree
|
||||
|
||||
Use a bash script to automate this:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
Z3=${{ github.workspace }}/build/z3
|
||||
ZIPT_DLL=$(find /tmp/zipt/ZIPT/bin/Release -name "ZIPT.dll" 2>/dev/null | head -1)
|
||||
ZIPT_AVAILABLE=false
|
||||
[ -n "$ZIPT_DLL" ] && ZIPT_AVAILABLE=true
|
||||
|
||||
# Ensure libz3.so is on the dynamic-linker path for the .NET runtime
|
||||
export LD_LIBRARY_PATH=${{ github.workspace }}/build${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
|
||||
|
||||
RESULTS=/tmp/benchmark_results.tsv
|
||||
mkdir -p /tmp/ostrich_run
|
||||
|
||||
echo -e "file\tseq_verdict\tseq_time\tnseq_verdict\tnseq_time\tzipt_verdict\tzipt_time\tnotes" > "$RESULTS"
|
||||
|
||||
run_z3_seq() {
|
||||
local file="$1"
|
||||
local start end elapsed verdict output exit_code
|
||||
|
||||
start=$(date +%s%3N)
|
||||
output=$(timeout 7 "$Z3" "smt.string_solver=seq" -T:5 "$file" 2>&1)
|
||||
exit_code=$?
|
||||
end=$(date +%s%3N)
|
||||
elapsed=$(echo "scale=3; ($end - $start) / 1000" | bc)
|
||||
|
||||
if echo "$output" | grep -q "^unsat"; then
|
||||
verdict="unsat"
|
||||
elif echo "$output" | grep -q "^sat"; then
|
||||
verdict="sat"
|
||||
elif echo "$output" | grep -q "^unknown"; then
|
||||
verdict="unknown"
|
||||
elif [ "$exit_code" -eq 124 ]; then
|
||||
verdict="timeout"
|
||||
elif echo "$output" | grep -qi "error\|assertion\|segfault\|SIGABRT\|exception"; then
|
||||
verdict="bug"
|
||||
else
|
||||
verdict="unknown"
|
||||
fi
|
||||
|
||||
echo "$verdict $elapsed"
|
||||
}
|
||||
|
||||
run_z3_nseq() {
|
||||
local file="$1"
|
||||
local start end elapsed verdict output exit_code
|
||||
|
||||
start=$(date +%s%3N)
|
||||
output=$(timeout 7 "$Z3" "smt.string_solver=nseq" -T:5 "$file" 2>&1)
|
||||
exit_code=$?
|
||||
end=$(date +%s%3N)
|
||||
elapsed=$(echo "scale=3; ($end - $start) / 1000" | bc)
|
||||
|
||||
if echo "$output" | grep -q "^unsat"; then
|
||||
verdict="unsat"
|
||||
elif echo "$output" | grep -q "^sat"; then
|
||||
verdict="sat"
|
||||
elif echo "$output" | grep -q "^unknown"; then
|
||||
verdict="unknown"
|
||||
elif [ "$exit_code" -eq 124 ]; then
|
||||
verdict="timeout"
|
||||
elif echo "$output" | grep -qi "error\|assertion\|segfault\|SIGABRT\|exception"; then
|
||||
verdict="bug"
|
||||
else
|
||||
verdict="unknown"
|
||||
fi
|
||||
|
||||
echo "$verdict $elapsed"
|
||||
}
|
||||
|
||||
run_zipt() {
|
||||
local file="$1"
|
||||
local start end elapsed verdict output exit_code
|
||||
|
||||
if [ "$ZIPT_AVAILABLE" != "true" ]; then
|
||||
echo "n/a 0.000"
|
||||
return
|
||||
fi
|
||||
|
||||
start=$(date +%s%3N)
|
||||
# ZIPT prints the filename on the first line, then SAT/UNSAT/UNKNOWN on subsequent lines
|
||||
output=$(timeout 7 dotnet "$ZIPT_DLL" -t:5000 "$file" 2>&1)
|
||||
exit_code=$?
|
||||
end=$(date +%s%3N)
|
||||
elapsed=$(echo "scale=3; ($end - $start) / 1000" | bc)
|
||||
|
||||
if echo "$output" | grep -qi "^UNSAT$"; then
|
||||
verdict="unsat"
|
||||
elif echo "$output" | grep -qi "^SAT$"; then
|
||||
verdict="sat"
|
||||
elif echo "$output" | grep -qi "^UNKNOWN$"; then
|
||||
verdict="unknown"
|
||||
elif [ "$exit_code" -eq 124 ]; then
|
||||
verdict="timeout"
|
||||
elif echo "$output" | grep -qi "error\|crash\|exception\|Unsupported"; then
|
||||
verdict="bug"
|
||||
else
|
||||
verdict="unknown"
|
||||
fi
|
||||
|
||||
echo "$verdict $elapsed"
|
||||
}
|
||||
|
||||
COUNTER=0
|
||||
while IFS= read -r file; do
|
||||
COUNTER=$((COUNTER + 1))
|
||||
fname=$(basename "$file")
|
||||
|
||||
seq_result=$(run_z3_seq "$file")
|
||||
nseq_result=$(run_z3_nseq "$file")
|
||||
zipt_result=$(run_zipt "$file")
|
||||
|
||||
seq_verdict=$(echo "$seq_result" | cut -d' ' -f1)
|
||||
seq_time=$(echo "$seq_result" | cut -d' ' -f2)
|
||||
nseq_verdict=$(echo "$nseq_result" | cut -d' ' -f1)
|
||||
nseq_time=$(echo "$nseq_result" | cut -d' ' -f2)
|
||||
zipt_verdict=$(echo "$zipt_result" | cut -d' ' -f1)
|
||||
zipt_time=$(echo "$zipt_result" | cut -d' ' -f2)
|
||||
|
||||
# Flag soundness disagreement when any two definitive verdicts disagree
|
||||
notes=""
|
||||
declare -A definitive_map
|
||||
[ "$seq_verdict" = "sat" ] || [ "$seq_verdict" = "unsat" ] && definitive_map[seq]="$seq_verdict"
|
||||
[ "$nseq_verdict" = "sat" ] || [ "$nseq_verdict" = "unsat" ] && definitive_map[nseq]="$nseq_verdict"
|
||||
[ "$zipt_verdict" = "sat" ] || [ "$zipt_verdict" = "unsat" ] && definitive_map[zipt]="$zipt_verdict"
|
||||
has_sat=false; has_unsat=false
|
||||
for v in "${definitive_map[@]}"; do
|
||||
[ "$v" = "sat" ] && has_sat=true
|
||||
[ "$v" = "unsat" ] && has_unsat=true
|
||||
done
|
||||
if $has_sat && $has_unsat; then
|
||||
notes="SOUNDNESS_DISAGREEMENT"
|
||||
fi
|
||||
|
||||
echo -e "$fname\t$seq_verdict\t$seq_time\t$nseq_verdict\t$nseq_time\t$zipt_verdict\t$zipt_time\t$notes" >> "$RESULTS"
|
||||
echo "[$COUNTER] [$fname] seq=$seq_verdict(${seq_time}s) nseq=$nseq_verdict(${nseq_time}s) zipt=$zipt_verdict(${zipt_time}s) $notes"
|
||||
done < /tmp/all_ostrich_files.txt
|
||||
|
||||
echo "Benchmark run complete. Results saved to $RESULTS"
|
||||
```
|
||||
|
||||
Save this script to `/tmp/run_ostrich_benchmarks.sh`, make it executable, and run it. Do not skip any file.
|
||||
|
||||
## Phase 4: Generate Summary Report
|
||||
|
||||
Read `/tmp/benchmark_results.tsv` and compute statistics. Then generate a Markdown report.
|
||||
|
||||
Compute:
|
||||
- **Total benchmarks**: total number of files run
|
||||
- **Per solver (seq, nseq, and ZIPT)**: count of sat / unsat / unknown / timeout / bug verdicts
|
||||
- **Total time used**: sum of all times for each solver
|
||||
- **Average time per benchmark**: total_time / total_files
|
||||
- **Soundness disagreements**: files where any two solvers that both returned a definitive answer disagree
|
||||
- **Bugs / crashes**: files with error/crash verdicts
|
||||
|
||||
Format the report as a GitHub Discussion post (GitHub-flavored Markdown):
|
||||
|
||||
```markdown
|
||||
### Ostrich Benchmark Report — Z3 c3 branch
|
||||
|
||||
**Date**: <today's date>
|
||||
**Branch**: c3
|
||||
**Benchmark set**: Ostrich (all files from tests/ostrich.zip)
|
||||
**Timeout**: 5 seconds per benchmark (`-T:5` for Z3; `-t:5000` for ZIPT)
|
||||
|
||||
---
|
||||
|
||||
### Summary
|
||||
|
||||
| Metric | seq solver | nseq solver | ZIPT solver |
|
||||
|--------|-----------|-------------|-------------|
|
||||
| sat | X | X | X |
|
||||
| unsat | X | X | X |
|
||||
| unknown | X | X | X |
|
||||
| timeout | X | X | X |
|
||||
| bug/crash | X | X | X |
|
||||
| **Total time (s)** | X.XXX | X.XXX | X.XXX |
|
||||
| **Avg time/benchmark (s)** | X.XXX | X.XXX | X.XXX |
|
||||
|
||||
**Soundness disagreements** (any two solvers return conflicting sat/unsat): N
|
||||
|
||||
---
|
||||
|
||||
### Per-File Results
|
||||
|
||||
<details>
|
||||
<summary>Click to expand full per-file table</summary>
|
||||
|
||||
| # | File | seq verdict | seq time (s) | nseq verdict | nseq time (s) | ZIPT verdict | ZIPT time (s) | Notes |
|
||||
|---|------|-------------|-------------|--------------|--------------|--------------|--------------|-------|
|
||||
| 1 | benchmark_0001.smt2 | sat | 0.123 | sat | 0.456 | sat | 0.789 | |
|
||||
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### Notable Issues
|
||||
|
||||
#### Soundness Disagreements (Critical)
|
||||
<list files where any two solvers disagree on sat/unsat, naming which solvers disagree>
|
||||
|
||||
#### Crashes / Bugs
|
||||
<list files where any solver crashed or produced an error>
|
||||
|
||||
#### Slow Benchmarks (> 4s)
|
||||
<list files that took more than 4 seconds for any solver>
|
||||
|
||||
---
|
||||
|
||||
*Generated automatically by the Ostrich Benchmark workflow on the c3 branch.*
|
||||
```
|
||||
|
||||
## Phase 5: Post to GitHub Discussion
|
||||
|
||||
Post the Markdown report as a new GitHub Discussion using the `create-discussion` safe output.
|
||||
|
||||
- **Category**: "Agentic Workflows"
|
||||
- **Title**: `[Ostrich Benchmark] Z3 c3 branch — <date>`
|
||||
- Close older discussions with the same title prefix to avoid clutter.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Always build from c3 branch**: The workspace is already checked out on c3; don't change branches.
|
||||
- **Synchronous builds only**: Never run `ninja` (or any other build command) in the background using `&`. Running the build concurrently with LLM inference causes the agent process to be killed by the OOM killer (exit 137) because C++ compilation and the LLM together exceed available RAM. Always wait for each build command to finish before proceeding.
|
||||
- **Release build**: The build uses `CMAKE_BUILD_TYPE=Release` for lower memory footprint and faster compilation on the GitHub Actions runner. The benchmark only needs verdict and timing output; no `-tr:` trace flags are used.
|
||||
- **Run all benchmarks**: Unlike the QF_S workflow, run every file in the archive — do not randomly sample.
|
||||
- **5-second timeout**: Pass `-T:5` to Z3 (both seq and nseq) and `-t:5000` to ZIPT (milliseconds). Use `timeout 7` as the outer OS-level guard to allow the solver to exit cleanly before being killed.
|
||||
- **Be precise with timing**: Use millisecond-precision timestamps and report times in seconds with 3 decimal places.
|
||||
- **Distinguish timeout from unknown**: A timeout is different from `(unknown)` returned by a solver within its time budget.
|
||||
- **ZIPT output format**: ZIPT prints the input filename on the first line, then `SAT`, `UNSAT`, or `UNKNOWN` on subsequent lines. Parse accordingly.
|
||||
- **Report soundness bugs prominently**: If any benchmark shows a conflict between any two solvers that both returned a definitive sat/unsat answer, highlight it as a critical finding and name which pair disagrees.
|
||||
- **Handle build failures gracefully**: If Z3 fails to build, report the error and create a brief discussion noting the build failure. If ZIPT fails to build, continue with only the seq/nseq columns and note `n/a` for ZIPT results.
|
||||
- **Large report**: Always put the per-file table in a `<details>` collapsible section since there may be many files.
|
||||
- **Progress logging**: Print a line per file as you run it (e.g., `[N] [filename] seq=...`) so the workflow log shows progress even for large benchmark sets.
|
||||
|
||||
## Safe Output Guarantee
|
||||
|
||||
You **MUST** call either `create_discussion` or `noop` before the workflow ends, regardless of what happened during execution:
|
||||
|
||||
- **Build succeeded, benchmarks ran**: Call `create_discussion` with the full report.
|
||||
- **Build succeeded, benchmarks partially ran**: Call `create_discussion` with whatever results were collected and a note about what could not be completed.
|
||||
- **Z3 build failed**: Call `noop` with a brief message describing the build error.
|
||||
- **No benchmarks could be run**: Call `noop` with a summary of what failed and why.
|
||||
|
||||
Failing to produce any safe output triggers an automatic workflow-failure issue that clutters the repository.
|
||||
57
.github/workflows/pyodide-pypi.yml
vendored
Normal file
57
.github/workflows/pyodide-pypi.yml
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
name: Pyodide Wheel (PyPI)
|
||||
|
||||
# Builds a PEP 783 `pyemscripten_*_wasm32` wheel for z3-solver using cibuildwheel
|
||||
# and publishes it to PyPI on tag pushes. Unlike the legacy pyodide.yml (which
|
||||
# uses `pyodide build` and produces a Pyodide-version-locked emscripten_* wheel
|
||||
# uploaded only as a CI artifact), this wheel is installable at runtime with
|
||||
# micropip from PyPI. See src/api/python/pyproject.toml [tool.cibuildwheel].
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-pyodide:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Build Pyodide wheel
|
||||
uses: pypa/cibuildwheel@v4.1.0
|
||||
with:
|
||||
# The Python bindings live in a subdirectory of the repo.
|
||||
package-dir: src/api/python
|
||||
env:
|
||||
CIBW_PLATFORM: pyodide
|
||||
# Exception/longjmp/bigint flags are declared in
|
||||
# src/api/python/pyproject.toml ([tool.pyodide.build]) and combined
|
||||
# with Pyodide's -fwasm-exceptions defaults. Don't set CFLAGS/CXXFLAGS
|
||||
# here — a JS-EH -fexceptions value conflicts with the wasm-EH ABI.
|
||||
|
||||
- name: Store Pyodide wheel
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: pyodide-wheel
|
||||
path: wheelhouse/*.whl
|
||||
|
||||
publish:
|
||||
name: Publish to PyPI
|
||||
runs-on: ubuntu-24.04
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
needs: [build-pyodide]
|
||||
environment: release
|
||||
permissions:
|
||||
id-token: write # trusted publishing (OIDC), no API token needed
|
||||
steps:
|
||||
- name: Download Pyodide wheel
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: pyodide-wheel
|
||||
path: dist/
|
||||
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
68
.github/workflows/pyodide.yml
vendored
68
.github/workflows/pyodide.yml
vendored
|
|
@ -1,68 +0,0 @@
|
|||
name: Pyodide Build
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 */2 * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
|
||||
- name: Setup packages
|
||||
run: sudo apt-get update && sudo apt-get install -y python3-dev python3-pip python3-venv
|
||||
|
||||
- name: Create venv
|
||||
run: python3 -m venv ~/env
|
||||
|
||||
- name: Install pyodide
|
||||
run: ~/env/bin/pip install pyodide-build pyodide-cli
|
||||
|
||||
- name: Configure CMake and build
|
||||
run: |
|
||||
git clone https://github.com/emscripten-core/emsdk.git ~/emsdk
|
||||
cd ~/emsdk && PYODIDE_EMSCRIPTEN_VERSION=$(~/env/bin/pyodide config get emscripten_version)
|
||||
./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}
|
||||
./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}
|
||||
|
||||
- name: Build Z3
|
||||
run: |
|
||||
source ~/emsdk/emsdk_env.sh
|
||||
cd src/api/python
|
||||
CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" CXXFLAG="${CXXFLAGS}" ~/env/bin/pyodide build --exports whole_archive
|
||||
env:
|
||||
CFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0 -g2"
|
||||
LDFLAGS: "-fexceptions -s WASM_BIGINT"
|
||||
CXXFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0"
|
||||
|
||||
- name: Setup env-pyodide
|
||||
run: |
|
||||
source ~/env/bin/activate
|
||||
source ~/emsdk/emsdk_env.sh
|
||||
pyodide venv ~/env-pyodide
|
||||
|
||||
- name: Setup z3 wheel
|
||||
run: |
|
||||
~/env-pyodide/bin/pip install src/api/python/dist/*.whl
|
||||
~/env-pyodide/bin/python - <src/api/python/z3test.py z3
|
||||
|
||||
- name: Package wheel
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: pyodide-wheel
|
||||
path: src/api/python/dist/*.whl
|
||||
retention-days: 1
|
||||
|
||||
1572
.github/workflows/qf-s-benchmark.lock.yml
generated
vendored
Normal file
1572
.github/workflows/qf-s-benchmark.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
405
.github/workflows/qf-s-benchmark.md
vendored
Normal file
405
.github/workflows/qf-s-benchmark.md
vendored
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
---
|
||||
description: Benchmark Z3 seq vs nseq string solvers on QF_S test suite from the c3 branch and post results as a GitHub discussion
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0,12 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: read-all
|
||||
|
||||
network: defaults
|
||||
|
||||
tools:
|
||||
bash: true
|
||||
github:
|
||||
toolsets: [default]
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-discussion:
|
||||
title-prefix: "[QF_S Benchmark] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
|
||||
timeout-minutes: 120
|
||||
|
||||
steps:
|
||||
- name: Checkout c3 branch
|
||||
uses: actions/checkout@v6.0.2
|
||||
with:
|
||||
ref: c3
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
---
|
||||
|
||||
# QF_S String Solver Benchmark
|
||||
|
||||
## Job Description
|
||||
|
||||
Your name is ${{ github.workflow }}. You are an expert performance analyst for the Z3 theorem prover, specializing in the string/sequence theory. Your task is to benchmark the `seq` solver (classical string theory) against the `nseq` solver (ZIPT-based string theory) on the QF_S test suite from the `c3` branch, and post a structured report as a GitHub Discussion.
|
||||
|
||||
The workspace already contains the `c3` branch (checked out by the preceding workflow step).
|
||||
|
||||
## Phase 1: Set Up the Build Environment
|
||||
|
||||
Install required build tools:
|
||||
|
||||
```bash
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y cmake ninja-build python3 python3-pip time
|
||||
```
|
||||
|
||||
Verify tools:
|
||||
|
||||
```bash
|
||||
cmake --version
|
||||
ninja --version
|
||||
python3 --version
|
||||
```
|
||||
|
||||
## Phase 2: Build Z3 in Release Mode
|
||||
|
||||
Build Z3 in Release mode for accurate benchmark performance numbers and lower memory usage. Running `ninja` in the background with `&` is not allowed — concurrent C++ compilation and LLM inference can exhaust available RAM and kill the agent process.
|
||||
|
||||
```bash
|
||||
mkdir -p /tmp/z3-build
|
||||
cd /tmp/z3-build
|
||||
cmake "$GITHUB_WORKSPACE" \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DZ3_BUILD_TEST_EXECUTABLES=OFF \
|
||||
2>&1 | tee /tmp/z3-cmake.log
|
||||
ninja -j2 z3 2>&1 | tee /tmp/z3-build.log
|
||||
```
|
||||
|
||||
Verify the binary was built:
|
||||
|
||||
```bash
|
||||
/tmp/z3-build/z3 --version
|
||||
```
|
||||
|
||||
If the build fails, report it immediately and stop.
|
||||
|
||||
Once the binary is confirmed working, call the `noop` safe-output tool with the message `"Z3 built successfully from the c3 branch. Benchmark starting — results will be posted as a GitHub Discussion once complete."` This keepalive call refreshes the safe-output MCP session before the long benchmark run begins, preventing a session timeout.
|
||||
|
||||
## Phase 3: Discover QF_S Benchmark Files
|
||||
|
||||
Find all `.smt2` benchmark files in the workspace that belong to the QF_S logic:
|
||||
|
||||
```bash
|
||||
# Search for explicit QF_S logic declarations
|
||||
grep -rl 'QF_S' "$GITHUB_WORKSPACE" --include='*.smt2' 2>/dev/null > /tmp/qf_s_files.txt
|
||||
|
||||
# Also look in dedicated benchmark directories
|
||||
find "$GITHUB_WORKSPACE" \
|
||||
\( -path "*/QF_S/*" -o -path "*/qf_s/*" -o -path "*/benchmarks/*" \) \
|
||||
-name '*.smt2' 2>/dev/null >> /tmp/qf_s_files.txt
|
||||
|
||||
# Deduplicate
|
||||
sort -u /tmp/qf_s_files.txt -o /tmp/qf_s_files.txt
|
||||
|
||||
TOTAL=$(wc -l < /tmp/qf_s_files.txt)
|
||||
echo "Found $TOTAL QF_S benchmark files"
|
||||
head -20 /tmp/qf_s_files.txt
|
||||
```
|
||||
|
||||
If fewer than 5 files are found, also scan the entire workspace for any `.smt2` file that exercises string constraints:
|
||||
|
||||
```bash
|
||||
if [ "$TOTAL" -lt 5 ]; then
|
||||
grep -rl 'declare.*String\|str\.\|seq\.' "$GITHUB_WORKSPACE" \
|
||||
--include='*.smt2' 2>/dev/null >> /tmp/qf_s_files.txt
|
||||
sort -u /tmp/qf_s_files.txt -o /tmp/qf_s_files.txt
|
||||
TOTAL=$(wc -l < /tmp/qf_s_files.txt)
|
||||
echo "After extended search: $TOTAL files"
|
||||
fi
|
||||
```
|
||||
|
||||
Cap the benchmark set to keep total runtime under 60 minutes:
|
||||
|
||||
```bash
|
||||
# Use at most 300 files; take a random sample if more are available
|
||||
if [ "$TOTAL" -gt 300 ]; then
|
||||
shuf -n 300 /tmp/qf_s_files.txt > /tmp/qf_s_sample.txt
|
||||
else
|
||||
cp /tmp/qf_s_files.txt /tmp/qf_s_sample.txt
|
||||
fi
|
||||
SAMPLE=$(wc -l < /tmp/qf_s_sample.txt)
|
||||
echo "Running benchmarks on $SAMPLE files"
|
||||
```
|
||||
|
||||
## Phase 4: Run Benchmarks — seq vs nseq
|
||||
|
||||
Run each benchmark with both solvers. Use a per-file timeout of 5 seconds. Set Z3's internal timeout to 4 seconds so it exits cleanly before the shell timeout fires.
|
||||
|
||||
```bash
|
||||
Z3=/tmp/z3-build/z3
|
||||
TIMEOUT_SEC=5
|
||||
Z3_TIMEOUT_SEC=4
|
||||
RESULTS=/tmp/benchmark-results.csv
|
||||
|
||||
echo "file,seq_result,seq_time_ms,nseq_result,nseq_time_ms" > "$RESULTS"
|
||||
|
||||
total=0
|
||||
done_count=0
|
||||
while IFS= read -r smt_file; do
|
||||
total=$((total + 1))
|
||||
|
||||
# Run with seq solver; capture both stdout (z3 output) and stderr (time output)
|
||||
SEQ_OUT=$({ time timeout "$TIMEOUT_SEC" "$Z3" \
|
||||
smt.string_solver=seq \
|
||||
-T:"$Z3_TIMEOUT_SEC" \
|
||||
"$smt_file" 2>/dev/null; } 2>&1)
|
||||
SEQ_RESULT=$(echo "$SEQ_OUT" | grep -E '^(sat|unsat|unknown)' | head -1)
|
||||
SEQ_MS=$(echo "$SEQ_OUT" | grep real | awk '{split($2,a,"m"); split(a[2],b,"s"); printf "%d", (a[1]*60+b[1])*1000}')
|
||||
[ -z "$SEQ_RESULT" ] && SEQ_RESULT="timeout"
|
||||
[ -z "$SEQ_MS" ] && SEQ_MS=$((TIMEOUT_SEC * 1000))
|
||||
|
||||
# Run with nseq solver; same structure
|
||||
NSEQ_OUT=$({ time timeout "$TIMEOUT_SEC" "$Z3" \
|
||||
smt.string_solver=nseq \
|
||||
-T:"$Z3_TIMEOUT_SEC" \
|
||||
"$smt_file" 2>/dev/null; } 2>&1)
|
||||
NSEQ_RESULT=$(echo "$NSEQ_OUT" | grep -E '^(sat|unsat|unknown)' | head -1)
|
||||
NSEQ_MS=$(echo "$NSEQ_OUT" | grep real | awk '{split($2,a,"m"); split(a[2],b,"s"); printf "%d", (a[1]*60+b[1])*1000}')
|
||||
[ -z "$NSEQ_RESULT" ] && NSEQ_RESULT="timeout"
|
||||
[ -z "$NSEQ_MS" ] && NSEQ_MS=$((TIMEOUT_SEC * 1000))
|
||||
|
||||
SHORT=$(basename "$smt_file")
|
||||
echo "$SHORT,$SEQ_RESULT,$SEQ_MS,$NSEQ_RESULT,$NSEQ_MS" >> "$RESULTS"
|
||||
|
||||
done_count=$((done_count + 1))
|
||||
if [ $((done_count % 50)) -eq 0 ]; then
|
||||
echo "Progress: $done_count / $SAMPLE files completed"
|
||||
fi
|
||||
done < /tmp/qf_s_sample.txt
|
||||
|
||||
echo "Benchmark run complete: $done_count files"
|
||||
```
|
||||
|
||||
## Phase 5: Collect Seq Traces for Interesting Cases
|
||||
|
||||
For benchmarks where `seq` solves in under 2 s but `nseq` times out (seq-fast/nseq-slow cases), collect a brief `seq` trace to understand what algorithm is used:
|
||||
|
||||
```bash
|
||||
Z3=/tmp/z3-build/z3
|
||||
mkdir -p /tmp/traces
|
||||
|
||||
# Find seq-fast / nseq-slow files: seq solved (sat/unsat) in <2000ms AND nseq timed out
|
||||
awk -F, 'NR>1 && ($2=="sat"||$2=="unsat") && $3<2000 && $4=="timeout" {print $1}' \
|
||||
/tmp/benchmark-results.csv > /tmp/seq_fast_nseq_slow.txt
|
||||
echo "seq-fast / nseq-slow files: $(wc -l < /tmp/seq_fast_nseq_slow.txt)"
|
||||
|
||||
# Collect traces for at most 5 such cases
|
||||
head -5 /tmp/seq_fast_nseq_slow.txt | while IFS= read -r short; do
|
||||
# Find the full path
|
||||
full=$(grep "/$short$" /tmp/qf_s_sample.txt | head -1)
|
||||
[ -z "$full" ] && continue
|
||||
timeout 5 "$Z3" \
|
||||
smt.string_solver=seq \
|
||||
-tr:seq \
|
||||
-T:5 \
|
||||
"$full" > "/tmp/traces/${short%.smt2}.seq.trace" 2>&1 || true
|
||||
done
|
||||
```
|
||||
|
||||
## Phase 6: Analyze Results
|
||||
|
||||
Compute summary statistics from the CSV. Save the analysis script to a file and run it:
|
||||
|
||||
```bash
|
||||
cat > /tmp/analyze_benchmark.py << 'PYEOF'
|
||||
import csv, sys
|
||||
|
||||
results = []
|
||||
with open('/tmp/benchmark-results.csv') as f:
|
||||
reader = csv.DictReader(f)
|
||||
for row in reader:
|
||||
results.append(row)
|
||||
|
||||
total = len(results)
|
||||
if total == 0:
|
||||
print("No results found.")
|
||||
sys.exit(0)
|
||||
|
||||
def is_correct(r, solver):
|
||||
prefix = 'seq' if solver == 'seq' else 'nseq'
|
||||
return r[f'{prefix}_result'] in ('sat', 'unsat')
|
||||
|
||||
def timed_out(r, solver):
|
||||
prefix = 'seq' if solver == 'seq' else 'nseq'
|
||||
return r[f'{prefix}_result'] == 'timeout'
|
||||
|
||||
seq_solved = sum(1 for r in results if is_correct(r, 'seq'))
|
||||
nseq_solved = sum(1 for r in results if is_correct(r, 'nseq'))
|
||||
seq_to = sum(1 for r in results if timed_out(r, 'seq'))
|
||||
nseq_to = sum(1 for r in results if timed_out(r, 'nseq'))
|
||||
|
||||
seq_times = [int(r['seq_time_ms']) for r in results if is_correct(r, 'seq')]
|
||||
nseq_times = [int(r['nseq_time_ms']) for r in results if is_correct(r, 'nseq')]
|
||||
|
||||
def median(lst):
|
||||
s = sorted(lst)
|
||||
n = len(s)
|
||||
return s[n//2] if n else 0
|
||||
|
||||
def mean(lst):
|
||||
return sum(lst)//len(lst) if lst else 0
|
||||
|
||||
# Disagreements (sat vs unsat or vice-versa)
|
||||
disagreements = [
|
||||
r for r in results
|
||||
if r['seq_result'] in ('sat','unsat')
|
||||
and r['nseq_result'] in ('sat','unsat')
|
||||
and r['seq_result'] != r['nseq_result']
|
||||
]
|
||||
|
||||
# seq-fast / nseq-slow: seq solved in <2s, nseq timed out
|
||||
seq_fast_nseq_slow = [
|
||||
r for r in results
|
||||
if is_correct(r, 'seq') and int(r['seq_time_ms']) < 2000 and timed_out(r, 'nseq')
|
||||
]
|
||||
# nseq-fast / seq-slow: nseq solved in <2s, seq timed out
|
||||
nseq_fast_seq_slow = [
|
||||
r for r in results
|
||||
if is_correct(r, 'nseq') and int(r['nseq_time_ms']) < 2000 and timed_out(r, 'seq')
|
||||
]
|
||||
|
||||
print(f"TOTAL={total}")
|
||||
print(f"SEQ_SOLVED={seq_solved}")
|
||||
print(f"NSEQ_SOLVED={nseq_solved}")
|
||||
print(f"SEQ_TIMEOUTS={seq_to}")
|
||||
print(f"NSEQ_TIMEOUTS={nseq_to}")
|
||||
print(f"SEQ_MEDIAN_MS={median(seq_times)}")
|
||||
print(f"NSEQ_MEDIAN_MS={median(nseq_times)}")
|
||||
print(f"SEQ_MEAN_MS={mean(seq_times)}")
|
||||
print(f"NSEQ_MEAN_MS={mean(nseq_times)}")
|
||||
print(f"DISAGREEMENTS={len(disagreements)}")
|
||||
print(f"SEQ_FAST_NSEQ_SLOW={len(seq_fast_nseq_slow)}")
|
||||
print(f"NSEQ_FAST_SEQ_SLOW={len(nseq_fast_seq_slow)}")
|
||||
|
||||
# Print top-10 slowest for nseq that seq handles fast
|
||||
print("\nTOP_SEQ_FAST_NSEQ_SLOW:")
|
||||
for r in sorted(seq_fast_nseq_slow, key=lambda x: -int(x['nseq_time_ms']))[:10]:
|
||||
print(f" {r['file']} seq={r['seq_time_ms']}ms nseq={r['nseq_time_ms']}ms seq_result={r['seq_result']} nseq_result={r['nseq_result']}")
|
||||
|
||||
print("\nTOP_NSEQ_FAST_SEQ_SLOW:")
|
||||
for r in sorted(nseq_fast_seq_slow, key=lambda x: -int(x['seq_time_ms']))[:10]:
|
||||
print(f" {r['file']} seq={r['seq_time_ms']}ms nseq={r['nseq_time_ms']}ms seq_result={r['seq_result']} nseq_result={r['nseq_result']}")
|
||||
|
||||
if disagreements:
|
||||
print(f"\nDISAGREEMENTS ({len(disagreements)}):")
|
||||
for r in disagreements[:10]:
|
||||
print(f" {r['file']} seq={r['seq_result']} nseq={r['nseq_result']}")
|
||||
PYEOF
|
||||
|
||||
python3 /tmp/analyze_benchmark.py
|
||||
```
|
||||
|
||||
## Phase 7: Create GitHub Discussion
|
||||
|
||||
Use the `create_discussion` safe-output tool to post a structured benchmark report.
|
||||
|
||||
The discussion body should be formatted as follows (fill in real numbers from Phase 6):
|
||||
|
||||
```markdown
|
||||
# QF_S Benchmark: seq vs nseq
|
||||
|
||||
**Date**: YYYY-MM-DD
|
||||
**Branch**: c3
|
||||
**Commit**: `<short SHA>`
|
||||
**Workflow Run**: [#<run_id>](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
**Files benchmarked**: N (capped at 300, timeout 5 s per file)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Metric | seq | nseq |
|
||||
|--------|-----|------|
|
||||
| Files solved (sat/unsat) | SEQ_SOLVED | NSEQ_SOLVED |
|
||||
| Timeouts | SEQ_TO | NSEQ_TO |
|
||||
| Median solve time (solved files) | X ms | Y ms |
|
||||
| Mean solve time (solved files) | X ms | Y ms |
|
||||
| **Disagreements (sat≠unsat)** | — | N |
|
||||
|
||||
---
|
||||
|
||||
## Performance Comparison
|
||||
|
||||
### seq-fast / nseq-slow (seq < 2 s, nseq timed out)
|
||||
|
||||
These are benchmarks where the classical `seq` solver is significantly faster. These represent regression risk for `nseq`.
|
||||
|
||||
| File | seq (ms) | nseq (ms) | seq result | nseq result |
|
||||
|------|----------|-----------|------------|-------------|
|
||||
[TOP 10 ENTRIES]
|
||||
|
||||
### nseq-fast / seq-slow (nseq < 2 s, seq timed out)
|
||||
|
||||
These are benchmarks where `nseq` shows a performance advantage.
|
||||
|
||||
| File | seq (ms) | nseq (ms) | seq result | nseq result |
|
||||
|------|----------|-----------|------------|-------------|
|
||||
[TOP 10 ENTRIES]
|
||||
|
||||
---
|
||||
|
||||
## Correctness
|
||||
|
||||
**Disagreements** (files where seq says `sat` but nseq says `unsat` or vice versa): N
|
||||
|
||||
[If disagreements exist, list all of them here with file paths and both results]
|
||||
|
||||
---
|
||||
|
||||
## seq Trace Analysis (seq-fast / nseq-slow cases)
|
||||
|
||||
<details>
|
||||
<summary>Click to expand trace snippets for top seq-fast/nseq-slow cases</summary>
|
||||
|
||||
[Insert trace snippet for each traced file, or "No traces collected" if section was skipped]
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## Raw Data
|
||||
|
||||
<details>
|
||||
<summary>Full results CSV (click to expand)</summary>
|
||||
|
||||
```csv
|
||||
[PASTE FIRST 200 LINES OF /tmp/benchmark-results.csv]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
*Generated by the QF_S Benchmark workflow. To reproduce: build Z3 from the `c3` branch and run `z3 smt.string_solver=seq|nseq -T:10 <file.smt2>`.*
|
||||
```
|
||||
|
||||
## Edge Cases
|
||||
|
||||
- If the build fails, call `missing_data` explaining the build error and stop.
|
||||
- If no benchmark files are found at all, call `missing_data` explaining that no QF_S `.smt2` files were found in the `c3` branch.
|
||||
- If Z3 crashes (segfault) on a file with either solver, record the result as `crash` and continue.
|
||||
- If the total benchmark set is very small (< 5 files), note this prominently in the discussion and suggest adding more QF_S benchmarks to the `c3` branch.
|
||||
- If zero disagreements and both solvers time out on the same files, note that the solvers are in agreement.
|
||||
- If `create_discussion` fails (e.g., MCP session error), call `report_incomplete` with the reason and include the top-line statistics (files solved, timeouts, disagreement count) in the `details` field.
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **DO NOT** modify any source files or create pull requests.
|
||||
- **DO NOT** run `ninja` or any build command in the background with `&` — concurrent C++ compilation and LLM inference can exhaust available RAM and kill the agent process. Always wait for build commands to complete before proceeding.
|
||||
- **DO NOT** run benchmarks for longer than 100 minutes total (leave buffer for posting).
|
||||
- **DO** always report the commit SHA so results can be correlated with specific code versions.
|
||||
- **DO** close older QF_S Benchmark discussions automatically (configured via `close-older-discussions: true`).
|
||||
- **DO** highlight disagreements prominently — these are potential correctness bugs.
|
||||
1867
.github/workflows/release-notes-updater.lock.yml
generated
vendored
1867
.github/workflows/release-notes-updater.lock.yml
generated
vendored
File diff suppressed because one or more lines are too long
15
.github/workflows/release-notes-updater.md
vendored
15
.github/workflows/release-notes-updater.md
vendored
|
|
@ -7,7 +7,12 @@ on:
|
|||
|
||||
timeout-minutes: 30
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
discussions: read
|
||||
copilot-requests: write
|
||||
|
||||
network: defaults
|
||||
|
||||
|
|
@ -16,21 +21,23 @@ tools:
|
|||
toolsets: [default]
|
||||
bash: [":*"]
|
||||
edit: {}
|
||||
glob: {}
|
||||
view: {}
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-discussion:
|
||||
title-prefix: "[Release Notes] "
|
||||
category: "Announcements"
|
||||
close-older-discussions: false
|
||||
noop:
|
||||
report-as-issue: false
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6.0.2
|
||||
with:
|
||||
fetch-depth: 0 # Fetch full history for analyzing commits
|
||||
persist-credentials: false
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
272
.github/workflows/release.yml
vendored
272
.github/workflows/release.yml
vendored
|
|
@ -23,7 +23,7 @@ permissions:
|
|||
contents: write
|
||||
|
||||
env:
|
||||
RELEASE_VERSION: '4.16.0'
|
||||
RELEASE_VERSION: '4.17.1'
|
||||
|
||||
jobs:
|
||||
# ============================================================================
|
||||
|
|
@ -34,9 +34,11 @@ jobs:
|
|||
name: "Mac Build x64"
|
||||
runs-on: macos-15
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "13.3"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -44,8 +46,21 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Build
|
||||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=x64
|
||||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=x64 --os=osx-13.3
|
||||
|
||||
- name: Validate libz3.dylib and z3 architecture (must be x86_64)
|
||||
run: |
|
||||
set -e
|
||||
for f in build-dist/libz3.dylib build-dist/z3; do
|
||||
ARCH=$(lipo -archs "$f")
|
||||
echo "$f architecture: $ARCH"
|
||||
if [ "$ARCH" != "x86_64" ]; then
|
||||
echo "ERROR: $f has arch '$ARCH', expected 'x86_64' (see issue #9662)"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "OK: macOS x64 artifacts are x86_64"
|
||||
|
||||
- name: Clone z3test
|
||||
run: git clone https://github.com/z3prover/z3test z3test
|
||||
|
||||
|
|
@ -53,7 +68,7 @@ jobs:
|
|||
run: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: macOsBuild
|
||||
path: dist/*.zip
|
||||
|
|
@ -63,9 +78,11 @@ jobs:
|
|||
name: "Mac ARM64 Build"
|
||||
runs-on: macos-15
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "13.3"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -73,13 +90,26 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Build
|
||||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=arm64
|
||||
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=arm64 --os=osx-13.3
|
||||
|
||||
- name: Validate libz3.dylib and z3 architecture (must be arm64)
|
||||
run: |
|
||||
set -e
|
||||
for f in build-dist/libz3.dylib build-dist/z3; do
|
||||
ARCH=$(lipo -archs "$f")
|
||||
echo "$f architecture: $ARCH"
|
||||
if [ "$ARCH" != "arm64" ]; then
|
||||
echo "ERROR: $f has arch '$ARCH', expected 'arm64' (see issue #9662)"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "OK: macOS arm64 artifacts are arm64"
|
||||
|
||||
- name: Clone z3test
|
||||
run: git clone https://github.com/z3prover/z3test z3test
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: MacArm64
|
||||
path: dist/*.zip
|
||||
|
|
@ -96,10 +126,10 @@ jobs:
|
|||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download macOS x64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: macOsBuild
|
||||
path: artifacts
|
||||
|
|
@ -111,6 +141,17 @@ jobs:
|
|||
Z3_DIR=$(find . -maxdepth 1 -type d -name "z3-*" | head -n 1)
|
||||
echo "Z3_DIR=$Z3_DIR" >> $GITHUB_ENV
|
||||
|
||||
- name: Validate shipped libz3.dylib architecture (must be x86_64)
|
||||
run: |
|
||||
set -e
|
||||
DYLIB="artifacts/$Z3_DIR/bin/libz3.dylib"
|
||||
ARCH=$(lipo -archs "$DYLIB")
|
||||
echo "Shipped $DYLIB architecture: $ARCH"
|
||||
if [ "$ARCH" != "x86_64" ]; then
|
||||
echo "ERROR: x64 release zip contains '$ARCH' libz3.dylib (see issue #9662)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Test install_name_tool with headerpad
|
||||
run: |
|
||||
cd artifacts/$Z3_DIR/bin
|
||||
|
|
@ -144,10 +185,10 @@ jobs:
|
|||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download macOS ARM64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: MacArm64
|
||||
path: artifacts
|
||||
|
|
@ -159,6 +200,17 @@ jobs:
|
|||
Z3_DIR=$(find . -maxdepth 1 -type d -name "z3-*" | head -n 1)
|
||||
echo "Z3_DIR=$Z3_DIR" >> $GITHUB_ENV
|
||||
|
||||
- name: Validate shipped libz3.dylib architecture (must be arm64)
|
||||
run: |
|
||||
set -e
|
||||
DYLIB="artifacts/$Z3_DIR/bin/libz3.dylib"
|
||||
ARCH=$(lipo -archs "$DYLIB")
|
||||
echo "Shipped $DYLIB architecture: $ARCH"
|
||||
if [ "$ARCH" != "arm64" ]; then
|
||||
echo "ERROR: arm64 release zip contains '$ARCH' libz3.dylib (see issue #9662)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Test install_name_tool with headerpad
|
||||
run: |
|
||||
cd artifacts/$Z3_DIR/bin
|
||||
|
|
@ -191,7 +243,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -208,7 +260,7 @@ jobs:
|
|||
run: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: UbuntuBuild
|
||||
path: dist/*.zip
|
||||
|
|
@ -220,7 +272,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -243,7 +295,7 @@ jobs:
|
|||
python scripts/mk_unix_dist.py --nodotnet --arch=arm64
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: UbuntuArm64
|
||||
path: dist/*.zip
|
||||
|
|
@ -255,7 +307,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -279,9 +331,9 @@ jobs:
|
|||
eval $(opam config env)
|
||||
python scripts/mk_make.py --ml
|
||||
cd build
|
||||
make -j3
|
||||
make -j3 examples
|
||||
make -j3 test-z3
|
||||
make -j$(nproc)
|
||||
make -j$(nproc) examples
|
||||
make -j$(nproc) test-z3
|
||||
cd ..
|
||||
|
||||
- name: Generate documentation
|
||||
|
|
@ -298,7 +350,7 @@ jobs:
|
|||
run: zip -r z3doc.zip doc/api
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: UbuntuDoc
|
||||
path: z3doc.zip
|
||||
|
|
@ -311,11 +363,19 @@ jobs:
|
|||
container: quay.io/pypa/manylinux_2_28_x86_64:latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Select Python
|
||||
run: |
|
||||
# Use the first available manylinux interpreter for deterministic selection.
|
||||
PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
|
||||
test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
|
||||
echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
|
||||
"$PYTHON" --version
|
||||
|
||||
- name: Setup Python environment
|
||||
run: |
|
||||
/opt/python/cp38-cp38/bin/python -m venv $PWD/env
|
||||
"$PYTHON" -m venv $PWD/env
|
||||
echo "$PWD/env/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install build tools
|
||||
|
|
@ -328,7 +388,7 @@ jobs:
|
|||
run: pip install ./src/api/python/wheelhouse/*.whl && python - <src/api/python/z3test.py z3 && python - <src/api/python/z3test.py z3num
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ManyLinuxPythonBuildAMD64
|
||||
path: src/api/python/wheelhouse/*.whl
|
||||
|
|
@ -341,7 +401,7 @@ jobs:
|
|||
container: quay.io/pypa/manylinux_2_28_x86_64:latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download ARM toolchain
|
||||
run: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz'
|
||||
|
|
@ -351,9 +411,17 @@ jobs:
|
|||
mkdir -p /tmp/arm-toolchain/
|
||||
tar xf /tmp/arm-toolchain.tar.xz -C /tmp/arm-toolchain/ --strip-components=1
|
||||
|
||||
- name: Select Python
|
||||
run: |
|
||||
# Use the first available manylinux interpreter for deterministic selection.
|
||||
PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
|
||||
test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
|
||||
echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
|
||||
"$PYTHON" --version
|
||||
|
||||
- name: Setup Python environment
|
||||
run: |
|
||||
/opt/python/cp38-cp38/bin/python -m venv $PWD/env
|
||||
"$PYTHON" -m venv $PWD/env
|
||||
echo "$PWD/env/bin" >> $GITHUB_PATH
|
||||
echo "/tmp/arm-toolchain/bin" >> $GITHUB_PATH
|
||||
echo "/tmp/arm-toolchain/aarch64-none-linux-gnu/libc/usr/bin" >> $GITHUB_PATH
|
||||
|
|
@ -368,19 +436,74 @@ jobs:
|
|||
run: cd src/api/python && CC=aarch64-none-linux-gnu-gcc CXX=aarch64-none-linux-gnu-g++ AR=aarch64-none-linux-gnu-ar LD=aarch64-none-linux-gnu-ld Z3_CROSS_COMPILING=aarch64 python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../..
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ManyLinuxPythonBuildArm64
|
||||
path: src/api/python/wheelhouse/*.whl
|
||||
retention-days: 7
|
||||
|
||||
manylinux-python-riscv64:
|
||||
name: "Python bindings (manylinux RISC-V 64 cross)"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
container: quay.io/pypa/manylinux_2_28_x86_64:latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download RISC-V toolchain
|
||||
run: curl -L -o /tmp/riscv-toolchain.tar.gz 'https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2024.09.03/riscv64-glibc-ubuntu-20.04-gcc-nightly-2024.09.03-nightly.tar.gz'
|
||||
|
||||
- name: Extract RISC-V toolchain
|
||||
run: |
|
||||
mkdir -p /tmp/riscv-toolchain/
|
||||
tar xf /tmp/riscv-toolchain.tar.gz -C /tmp/riscv-toolchain/ --strip-components=1
|
||||
|
||||
- name: Install MPFR 4 (required by RISC-V toolchain host binaries)
|
||||
run: |
|
||||
dnf install -y gmp-devel
|
||||
curl -L -o /tmp/mpfr.tar.xz https://ftp.gnu.org/gnu/mpfr/mpfr-4.2.1.tar.xz
|
||||
tar xf /tmp/mpfr.tar.xz -C /tmp/
|
||||
cd /tmp/mpfr-4.2.1 && ./configure --prefix=/usr/local --disable-static && make -j$(nproc) && make install
|
||||
ldconfig
|
||||
|
||||
- name: Select Python
|
||||
run: |
|
||||
# Use the first available manylinux interpreter for deterministic selection.
|
||||
PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
|
||||
test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
|
||||
echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
|
||||
"$PYTHON" --version
|
||||
|
||||
- name: Setup Python environment
|
||||
run: |
|
||||
"$PYTHON" -m venv $PWD/env
|
||||
echo "$PWD/env/bin" >> $GITHUB_PATH
|
||||
echo "/tmp/riscv-toolchain/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install build tools
|
||||
run: |
|
||||
echo $PATH
|
||||
stat $(which riscv64-unknown-linux-gnu-gcc)
|
||||
pip install build git+https://github.com/rhelmot/auditwheel
|
||||
|
||||
- name: Build wheels
|
||||
run: cd src/api/python && CC=riscv64-unknown-linux-gnu-gcc CXX=riscv64-unknown-linux-gnu-g++ AR=riscv64-unknown-linux-gnu-ar LD=riscv64-unknown-linux-gnu-ld Z3_CROSS_COMPILING=riscv64 python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../..
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ManyLinuxPythonBuildRiscv64
|
||||
path: src/api/python/wheelhouse/*.whl
|
||||
retention-days: 7
|
||||
|
||||
windows-build-x64:
|
||||
name: "Windows x64 build"
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -390,11 +513,12 @@ jobs:
|
|||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64 || exit /b 1
|
||||
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: WindowsBuild-x64
|
||||
path: dist/*.zip
|
||||
|
|
@ -406,7 +530,7 @@ jobs:
|
|||
timeout-minutes: 120
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -416,11 +540,12 @@ jobs:
|
|||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x86 || exit /b 1
|
||||
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: WindowsBuild-x86
|
||||
path: dist/*.zip
|
||||
|
|
@ -432,7 +557,7 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -442,11 +567,12 @@ jobs:
|
|||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64
|
||||
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
|
||||
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64 || exit /b 1
|
||||
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ env.RELEASE_VERSION }} --zip
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: WindowsBuild-arm64
|
||||
path: dist/arm64/*.zip
|
||||
|
|
@ -462,7 +588,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -470,43 +596,43 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Download Win64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-x64
|
||||
path: package
|
||||
|
||||
- name: Download Win ARM64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-arm64
|
||||
path: package
|
||||
|
||||
- name: Download Ubuntu Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: UbuntuBuild
|
||||
path: package
|
||||
|
||||
- name: Download Ubuntu ARM64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: UbuntuArm64
|
||||
path: package
|
||||
|
||||
- name: Download macOS Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: macOsBuild
|
||||
path: package
|
||||
|
||||
- name: Download macOS Arm64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: MacArm64
|
||||
path: package
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v2
|
||||
uses: nuget/setup-nuget@v4
|
||||
with:
|
||||
nuget-version: 'latest'
|
||||
|
||||
|
|
@ -523,7 +649,7 @@ jobs:
|
|||
nuget pack out\Microsoft.Z3.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: NuGet
|
||||
path: |
|
||||
|
|
@ -537,7 +663,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -545,13 +671,13 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-x86
|
||||
path: package
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v2
|
||||
uses: nuget/setup-nuget@v4
|
||||
with:
|
||||
nuget-version: 'latest'
|
||||
|
||||
|
|
@ -568,7 +694,7 @@ jobs:
|
|||
nuget pack out\Microsoft.Z3.x86.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: NuGet32
|
||||
path: |
|
||||
|
|
@ -578,11 +704,11 @@ jobs:
|
|||
|
||||
python-package:
|
||||
name: "Python packaging"
|
||||
needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64]
|
||||
needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64, manylinux-python-riscv64]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
|
|
@ -590,47 +716,53 @@ jobs:
|
|||
python-version: '3.x'
|
||||
|
||||
- name: Download macOS x64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: macOsBuild
|
||||
path: artifacts
|
||||
|
||||
- name: Download macOS Arm64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: MacArm64
|
||||
path: artifacts
|
||||
|
||||
- name: Download Win64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-x64
|
||||
path: artifacts
|
||||
|
||||
- name: Download Win32 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-x86
|
||||
path: artifacts
|
||||
|
||||
- name: Download Win ARM64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: WindowsBuild-arm64
|
||||
path: artifacts
|
||||
|
||||
- name: Download ManyLinux AMD64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: ManyLinuxPythonBuildAMD64
|
||||
path: artifacts
|
||||
|
||||
- name: Download ManyLinux Arm64 Build
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: ManyLinuxPythonBuildArm64
|
||||
path: artifacts
|
||||
|
||||
- name: Download ManyLinux RISC-V 64 Build
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: ManyLinuxPythonBuildRiscv64
|
||||
path: artifacts
|
||||
|
||||
- name: Extract builds
|
||||
run: |
|
||||
cd artifacts
|
||||
|
|
@ -658,7 +790,7 @@ jobs:
|
|||
cp artifacts/*.whl src/api/python/dist/.
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: PythonPackage
|
||||
path: src/api/python/dist/*
|
||||
|
|
@ -689,10 +821,10 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
path: tmp
|
||||
|
||||
|
|
@ -745,22 +877,22 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Download NuGet packages
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: NuGet
|
||||
path: packages
|
||||
|
||||
- name: Download NuGet32 packages
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: NuGet32
|
||||
path: packages
|
||||
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v2
|
||||
uses: nuget/setup-nuget@v4
|
||||
with:
|
||||
nuget-version: 'latest'
|
||||
|
||||
|
|
@ -781,10 +913,20 @@ jobs:
|
|||
contents: read
|
||||
steps:
|
||||
- name: Download Python packages
|
||||
uses: actions/download-artifact@v7.0.0
|
||||
uses: actions/download-artifact@v8.0.1
|
||||
with:
|
||||
name: PythonPackage
|
||||
path: dist
|
||||
|
||||
- name: Rewrite macOS wheel tags unsupported by test.PyPI
|
||||
run: |
|
||||
# TestPyPI rejects current macOS wheel tags such as macosx_13_3_*.
|
||||
# Rewrite only the unsupported 13_3 tag to 13 for upload validation.
|
||||
for whl in dist/*-macosx_13_3_*.whl; do
|
||||
[ -e "$whl" ] || continue
|
||||
mv "$whl" "${whl/macosx_13_3_/macosx_13_0_}"
|
||||
done
|
||||
ls -l dist
|
||||
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
|
|
|
|||
1668
.github/workflows/smtlib-benchmark-finder.lock.yml
generated
vendored
Normal file
1668
.github/workflows/smtlib-benchmark-finder.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
343
.github/workflows/smtlib-benchmark-finder.md
vendored
Normal file
343
.github/workflows/smtlib-benchmark-finder.md
vendored
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
---
|
||||
description: >
|
||||
Monthly SMTLIB Benchmark Finder.
|
||||
Searches GitHub for repositories containing SMT-LIB benchmarks (.smt2 files),
|
||||
excludes repositories that belong to the official SMT-LIB benchmark sets
|
||||
(linked from smtlib.org and hosted on Zenodo), and posts a curated summary
|
||||
of community-contributed benchmark links as a GitHub Discussion.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 8 1 * *"
|
||||
workflow_dispatch:
|
||||
|
||||
timeout-minutes: 60
|
||||
|
||||
permissions: read-all
|
||||
|
||||
network:
|
||||
allowed:
|
||||
- defaults
|
||||
- github
|
||||
- smtlib.cs.uiowa.edu
|
||||
- zenodo.org
|
||||
|
||||
tools:
|
||||
cache-memory: true
|
||||
web-fetch: {}
|
||||
github:
|
||||
toolsets: [default, repos]
|
||||
bash: [":*"]
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
mentions: false
|
||||
allowed-github-references: []
|
||||
max-bot-mentions: 1
|
||||
create-discussion:
|
||||
title-prefix: "[SMT-LIB Benchmarks] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
expires: 90d
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
|
||||
---
|
||||
|
||||
# SMTLIB Benchmark Finder
|
||||
|
||||
## Job Description
|
||||
|
||||
Your name is ${{ github.workflow }}. You are a research analyst for the Z3 theorem
|
||||
prover repository `${{ github.repository }}`. Your mission is to discover GitHub
|
||||
repositories that host SMT-LIB benchmarks, exclude the ones that are already part
|
||||
of the official SMT-LIB benchmark distribution (linked from smtlib.org and published
|
||||
on Zenodo), and post a curated summary of community-contributed benchmark links as a
|
||||
GitHub Discussion.
|
||||
|
||||
## Step 1: Load Cache and Determine Run Mode
|
||||
|
||||
Check cache memory for:
|
||||
- `official_repos`: set of GitHub repository full names (`owner/repo`) and Zenodo
|
||||
record IDs already identified as official SMT-LIB benchmark sets
|
||||
- `known_community_repos`: set of repo full names already listed in a previous report
|
||||
- `last_run_date`: ISO-8601 date string of the previous run
|
||||
|
||||
Use the cache to skip repos already classified. On the very first run (no cache),
|
||||
perform a full discovery pass. On subsequent runs focus on repos pushed or created
|
||||
since `last_run_date`.
|
||||
|
||||
## Step 2: Collect Official SMT-LIB Benchmark Sets to Exclude
|
||||
|
||||
### 2.1 Scrape smtlib.org
|
||||
|
||||
Fetch the SMT-LIB benchmarks page and extract all linked Zenodo DOIs and GitHub URLs:
|
||||
|
||||
```bash
|
||||
curl -s "https://smtlib.cs.uiowa.edu/benchmarks.shtml" -o /tmp/smtlib-benchmarks.html
|
||||
# Also try the main page and any mirror
|
||||
curl -s "https://smtlib.cs.uiowa.edu/" -o /tmp/smtlib-home.html
|
||||
```
|
||||
|
||||
Parse both files for:
|
||||
- Zenodo DOI links (`doi.org/10.5281/zenodo.*` or `zenodo.org/record/*`)
|
||||
- GitHub repository URLs (`github.com/...`)
|
||||
- Any other hosted benchmark archive links
|
||||
|
||||
### 2.2 Enumerate Zenodo SMT-LIB Community Records
|
||||
|
||||
Query the Zenodo API for all records in the SMT-LIB community:
|
||||
|
||||
```bash
|
||||
curl -s "https://zenodo.org/api/records?communities=smt-lib&size=100&page=1" \
|
||||
-o /tmp/zenodo-smtlib-page1.json
|
||||
|
||||
# Check if there are more pages (paginate until empty)
|
||||
curl -s "https://zenodo.org/api/records?communities=smt-lib&size=100&page=2" \
|
||||
-o /tmp/zenodo-smtlib-page2.json 2>/dev/null || true
|
||||
|
||||
curl -s "https://zenodo.org/api/records?communities=smt-lib&size=100&page=3" \
|
||||
-o /tmp/zenodo-smtlib-page3.json 2>/dev/null || true
|
||||
```
|
||||
|
||||
For each Zenodo record extract:
|
||||
- Record ID (e.g. `5827900`)
|
||||
- Title
|
||||
- Any GitHub repository URLs listed in the description or related identifiers
|
||||
|
||||
```bash
|
||||
python3 - <<'PYEOF'
|
||||
import json, re
|
||||
|
||||
def extract_github_repos(text):
|
||||
pattern = r'github\.com/([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)'
|
||||
return set(re.findall(pattern, text or ''))
|
||||
|
||||
official_repos = set()
|
||||
official_zenodo_ids = set()
|
||||
|
||||
for fname in ['/tmp/zenodo-smtlib-page1.json', '/tmp/zenodo-smtlib-page2.json',
|
||||
'/tmp/zenodo-smtlib-page3.json']:
|
||||
try:
|
||||
data = json.load(open(fname))
|
||||
except Exception:
|
||||
continue
|
||||
for hit in data.get('hits', {}).get('hits', []):
|
||||
rid = str(hit.get('id', ''))
|
||||
official_zenodo_ids.add(rid)
|
||||
metadata = hit.get('metadata', {})
|
||||
description = metadata.get('description', '')
|
||||
related = ' '.join(
|
||||
r.get('identifier', '')
|
||||
for r in metadata.get('related_identifiers', [])
|
||||
)
|
||||
title = metadata.get('title', '')
|
||||
for repo in extract_github_repos(description + ' ' + related + ' ' + title):
|
||||
official_repos.add(repo.lower().rstrip('.'))
|
||||
|
||||
with open('/tmp/smtlib-benchmarks.html') as f:
|
||||
html = f.read()
|
||||
for repo in extract_github_repos(html):
|
||||
official_repos.add(repo.lower().rstrip('.'))
|
||||
|
||||
print("OFFICIAL_ZENODO_IDS:", ','.join(sorted(official_zenodo_ids)) or '(none)')
|
||||
print("OFFICIAL_GITHUB_REPOS:", ','.join(sorted(official_repos)) or '(none)')
|
||||
PYEOF
|
||||
```
|
||||
|
||||
### 2.3 Well-Known Official Repository Patterns
|
||||
|
||||
Regardless of the above scrape, always exclude:
|
||||
- Any repo under the `SMT-LIB` GitHub organization (`SMT-LIB/*`)
|
||||
- Any repo whose name matches `smt-comp-*` that is under `SMT-Competition` org
|
||||
- The Z3 repo itself (`Z3Prover/z3`) and its forks
|
||||
|
||||
Combine all official sources into a single exclusion set stored at
|
||||
`/tmp/official_exclusions.txt` (one `owner/repo` pattern per line, lowercase).
|
||||
|
||||
## Step 3: Search GitHub for Community SMT-LIB Benchmark Repositories
|
||||
|
||||
Use multiple GitHub search strategies to find repos containing `.smt2` benchmark
|
||||
files that are NOT part of the official distribution. Search for repos updated
|
||||
since the last run date (or the last 90 days for the initial run).
|
||||
|
||||
Compute the cutoff date first:
|
||||
```bash
|
||||
CUTOFF=$(date -d "90 days ago" +%Y-%m-%d 2>/dev/null || date -v-90d +%Y-%m-%d)
|
||||
echo "Using cutoff date: $CUTOFF"
|
||||
```
|
||||
|
||||
### Search Strategies
|
||||
|
||||
Use the GitHub MCP server tools to run these searches:
|
||||
|
||||
1. **Topic search**: `topic:smtlib pushed:>$CUTOFF`
|
||||
2. **Topic search variant**: `topic:smt-lib pushed:>$CUTOFF`
|
||||
3. **Topic search variant**: `topic:smt2 pushed:>$CUTOFF`
|
||||
4. **Benchmark filename pattern**: `filename:*.smt2 pushed:>$CUTOFF` (limit to top results)
|
||||
5. **Benchmarks directory pattern**: `path:benchmarks *.smt2 in:path pushed:>$CUTOFF`
|
||||
6. **README mention**: `SMT-LIB benchmarks in:readme stars:>2 pushed:>$CUTOFF`
|
||||
7. **Organization-level search**: repos under `SMT-Competition` org, if any are not already excluded
|
||||
|
||||
For each search, collect: `full_name`, `html_url`, `description`, `stargazers_count`,
|
||||
`updated_at`, `pushed_at`, `default_branch`, `topics`.
|
||||
|
||||
Deduplicate by `full_name`. Limit to 200 total candidates before filtering.
|
||||
|
||||
## Step 4: Filter Out Official Benchmark Sets
|
||||
|
||||
For each candidate repo:
|
||||
|
||||
1. Check if `full_name.lower()` is in the exclusion set from Step 2.
|
||||
2. Check if the repo is owned by `SMT-LIB` or `SMT-Competition` org (case-insensitive).
|
||||
3. Check if the repo is a fork of a known official repo (if `fork: true` and parent is
|
||||
in the exclusion set).
|
||||
|
||||
Discard repos that match any of the above. Keep the rest as community benchmarks.
|
||||
|
||||
Also apply quality filters to reduce noise — skip repos that:
|
||||
- Have 0 stars and fewer than 3 `.smt2` files (likely a student homework or test repo
|
||||
with minimal public value); use your judgement — if the repo description clearly
|
||||
describes a research benchmark, keep it regardless of star count.
|
||||
- Were created but never pushed after creation (empty repos).
|
||||
- Have names that are clearly course assignment repositories
|
||||
(e.g. contain `homework`, `assignment`, `hw[0-9]`, `cs[0-9]{3}`).
|
||||
|
||||
## Step 5: Classify Remaining Repos
|
||||
|
||||
For each repo that survives filtering, classify it into one of these categories:
|
||||
|
||||
| Category | Description |
|
||||
|----------|-------------|
|
||||
| **Solver evaluation** | Benchmarks used to evaluate or compare SMT solvers |
|
||||
| **Verification** | Benchmarks from program verification or model checking |
|
||||
| **Security / CTF** | Benchmarks from security research or CTF challenges |
|
||||
| **Theory / logic** | Benchmarks exploring specific SMT theories or logics |
|
||||
| **Tool output** | Benchmarks generated by another tool (e.g. a compiler, fuzzer) |
|
||||
| **Education** | Course materials or tutorials with benchmark examples |
|
||||
| **Other / unknown** | Does not fit another category |
|
||||
|
||||
Base the classification on: repo description, README (fetch if web-fetch is available),
|
||||
topics, and directory structure. A single brief `web-fetch` of the repo's README is
|
||||
sufficient; do not fetch individual `.smt2` files.
|
||||
|
||||
Note the dominant SMT logic(s) present, if discernible from the description or topics
|
||||
(e.g. QF_BV, QF_LIA, QF_S, NIA, …).
|
||||
|
||||
## Step 6: Generate the Discussion Report
|
||||
|
||||
Create a GitHub Discussion. Use heading level 3 or deeper (`###`, `####`, …) for all
|
||||
section headers; never use `##` or `#` in the body.
|
||||
Wrap long tables in `<details>` tags to keep the report scannable.
|
||||
|
||||
Title: `[SMT-LIB Benchmarks] Community Benchmark Repository Survey — [Month YYYY]`
|
||||
|
||||
Structure the report as follows:
|
||||
|
||||
```markdown
|
||||
**Period covered**: [cutoff date] – [today's date]
|
||||
**Repositories found**: N community repos (after excluding M official sets)
|
||||
**New this run**: N (not listed in previous report)
|
||||
|
||||
### Overview
|
||||
|
||||
1–2 sentences summarising the breadth of community SMT-LIB benchmarks found.
|
||||
|
||||
### Community Benchmark Repositories
|
||||
|
||||
Use `###` for category headers. Within each category, list repos as a markdown table.
|
||||
For each repo include:
|
||||
- Repo link (`[owner/repo](html_url)`)
|
||||
- Stars
|
||||
- Last pushed date
|
||||
- Dominant logic(s) (if known)
|
||||
- Brief description (from repo description or README, max 120 chars)
|
||||
|
||||
#### Solver Evaluation
|
||||
|
||||
| Repository | ⭐ | Last pushed | Logic(s) | Description |
|
||||
|------------|-----|------------|---------|-------------|
|
||||
| [owner/repo](url) | N | YYYY-MM-DD | QF_BV, QF_LIA | … |
|
||||
|
||||
#### Verification
|
||||
|
||||
[same table structure]
|
||||
|
||||
#### Security / CTF
|
||||
|
||||
[same table structure]
|
||||
|
||||
#### Theory / Logic
|
||||
|
||||
[same table structure]
|
||||
|
||||
#### Tool Output
|
||||
|
||||
[same table structure]
|
||||
|
||||
#### Education
|
||||
|
||||
[same table structure]
|
||||
|
||||
#### Other / Unknown
|
||||
|
||||
[same table structure]
|
||||
|
||||
---
|
||||
|
||||
### Exclusions Applied
|
||||
|
||||
<details>
|
||||
<summary>Official SMT-LIB sets excluded from this report</summary>
|
||||
|
||||
List Zenodo record IDs and GitHub repos identified as official distributions.
|
||||
|
||||
| Source | Identifier | Notes |
|
||||
|--------|-----------|-------|
|
||||
| Zenodo | [10.5281/zenodo.XXXXXXX](https://zenodo.org/record/XXXXXXX) | Official QF_BV benchmark set |
|
||||
| GitHub | [SMT-LIB/benchmarks-non-incremental](https://github.com/SMT-LIB/benchmarks-non-incremental) | |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### Methodology
|
||||
|
||||
Brief note on search queries used, cutoff date, and any quality filters applied.
|
||||
```
|
||||
|
||||
## Step 7: Update Cache Memory
|
||||
|
||||
After posting the discussion, update cache memory with:
|
||||
- `official_repos`: updated exclusion set (union of previous + newly found)
|
||||
- `known_community_repos`: union of previous + repos listed in this report
|
||||
- `last_run_date`: today's ISO-8601 date
|
||||
- `report_url`: URL of the GitHub Discussion created
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Be conservative with exclusions**: when in doubt whether a repo is "official",
|
||||
keep it in the community list rather than silently dropping it.
|
||||
- **Be accurate**: only include repos that genuinely contain SMT-LIB `.smt2` files
|
||||
or clearly describe themselves as SMT-LIB benchmark collections.
|
||||
- **Avoid noise**: student homework repos and trivially small repos add clutter;
|
||||
apply the quality filters from Step 4 judiciously.
|
||||
- **No source code changes**: DO NOT create pull requests or modify any source files.
|
||||
- **No copyrighted content**: DO NOT reproduce benchmark file contents; only post
|
||||
links and metadata.
|
||||
- **Always cite sources**: include the full GitHub URL for every listed repository.
|
||||
- **Use cache**: skip repos already classified in a previous run to keep runtime short.
|
||||
- **Fail gracefully**: if GitHub search rate-limits the workflow, post whatever was
|
||||
collected so far with a note that the search was incomplete.
|
||||
|
||||
## Important Notes
|
||||
|
||||
- DO NOT create pull requests or modify source files.
|
||||
- DO close older SMT-LIB Benchmarks discussions automatically (configured).
|
||||
- DO always call `create_discussion` or `noop` before the workflow ends.
|
||||
Failing to produce any safe output triggers an automatic failure issue.
|
||||
- DO use cache memory to avoid re-processing repos already surveyed.
|
||||
- DO limit individual `web-fetch` calls (README fetches) to repos where the
|
||||
description alone is insufficient for classification.
|
||||
1081
.github/workflows/soundness-bug-detector.lock.yml
generated
vendored
1081
.github/workflows/soundness-bug-detector.lock.yml
generated
vendored
File diff suppressed because it is too large
Load diff
41
.github/workflows/soundness-bug-detector.md
vendored
41
.github/workflows/soundness-bug-detector.md
vendored
|
|
@ -1,41 +0,0 @@
|
|||
---
|
||||
description: Automatically validate and reproduce reported soundness bugs
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, labeled]
|
||||
schedule: daily
|
||||
|
||||
roles: all
|
||||
|
||||
permissions: read-all
|
||||
|
||||
network: defaults
|
||||
|
||||
tools:
|
||||
cache-memory: true
|
||||
github:
|
||||
toolsets: [default]
|
||||
bash: [":*"]
|
||||
web-fetch: {}
|
||||
|
||||
safe-outputs:
|
||||
add-comment:
|
||||
max: 2
|
||||
create-discussion:
|
||||
title-prefix: "[Soundness] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
---
|
||||
|
||||
<!-- Edit the file linked below to modify the agent without recompilation. Feel free to move the entire markdown body to that file. -->
|
||||
@./agentics/soundness-bug-detector.md
|
||||
1704
.github/workflows/specbot-crash-analyzer.lock.yml
generated
vendored
Normal file
1704
.github/workflows/specbot-crash-analyzer.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
248
.github/workflows/specbot-crash-analyzer.md
vendored
Normal file
248
.github/workflows/specbot-crash-analyzer.md
vendored
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
---
|
||||
description: >
|
||||
Build Z3 in debug mode from the c3 branch, compile and run the specbot tests,
|
||||
identify root causes for any crashes, and post findings as a GitHub Discussion.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
timeout-minutes: 120
|
||||
|
||||
permissions: read-all
|
||||
|
||||
network: defaults
|
||||
|
||||
tools:
|
||||
cache-memory: true
|
||||
github:
|
||||
toolsets: [default, discussions]
|
||||
bash: [":*"]
|
||||
edit: {}
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-discussion:
|
||||
title-prefix: "[Specbot] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
|
||||
steps:
|
||||
- name: Checkout c3 branch
|
||||
uses: actions/checkout@v6.0.2
|
||||
with:
|
||||
ref: c3
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y cmake ninja-build python3 gcc g++ 2>&1 | tail -5
|
||||
|
||||
- name: Build Z3 in debug mode
|
||||
id: build-z3
|
||||
continue-on-error: true
|
||||
run: |
|
||||
mkdir -p build/debug specbot-results
|
||||
cd build/debug
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ../.. 2>&1 | tee ../../specbot-results/cmake.log
|
||||
ninja 2>&1 | tee ../../specbot-results/build.log
|
||||
BUILD_EXIT=$?
|
||||
cd ../..
|
||||
echo "build_exit=${BUILD_EXIT}" >> specbot-results/build-status.txt
|
||||
ls -la build/debug/libz3* build/debug/*.so* 2>/dev/null >> specbot-results/build-status.txt || echo "Library not found" >> specbot-results/build-status.txt
|
||||
exit $BUILD_EXIT
|
||||
|
||||
- name: Compile specbot tests
|
||||
continue-on-error: true
|
||||
run: |
|
||||
mkdir -p specbot-results
|
||||
gcc -g -O0 \
|
||||
-I src/api \
|
||||
specbot/test_specbot_seq.c \
|
||||
-L build/debug \
|
||||
-lz3 \
|
||||
-Wl,-rpath,"${GITHUB_WORKSPACE}/build/debug" \
|
||||
-o specbot-results/test_specbot_seq \
|
||||
2>&1 | tee specbot-results/compile_specbot_seq.log
|
||||
echo "compile_specbot_seq_exit=$?" >> specbot-results/compile-status.txt
|
||||
|
||||
gcc -g -O0 \
|
||||
-I src/api \
|
||||
specbot/test_deeptest_seq.c \
|
||||
-L build/debug \
|
||||
-lz3 \
|
||||
-Wl,-rpath,"${GITHUB_WORKSPACE}/build/debug" \
|
||||
-o specbot-results/test_deeptest_seq \
|
||||
2>&1 | tee specbot-results/compile_deeptest_seq.log
|
||||
echo "compile_deeptest_seq_exit=$?" >> specbot-results/compile-status.txt
|
||||
|
||||
- name: Run specbot tests
|
||||
continue-on-error: true
|
||||
run: |
|
||||
mkdir -p specbot-results
|
||||
if [ -f specbot-results/test_specbot_seq ]; then
|
||||
LD_LIBRARY_PATH="${GITHUB_WORKSPACE}/build/debug" timeout 120 specbot-results/test_specbot_seq > specbot-results/test_specbot_seq.log 2>&1
|
||||
SPECBOT_EXIT=$?
|
||||
echo "specbot_seq_exit=${SPECBOT_EXIT}" >> specbot-results/test-status.txt
|
||||
else
|
||||
echo "Binary not compiled" > specbot-results/test_specbot_seq.log
|
||||
echo "specbot_seq_exit=127" >> specbot-results/test-status.txt
|
||||
fi
|
||||
|
||||
if [ -f specbot-results/test_deeptest_seq ]; then
|
||||
LD_LIBRARY_PATH="${GITHUB_WORKSPACE}/build/debug" timeout 120 specbot-results/test_deeptest_seq > specbot-results/test_deeptest_seq.log 2>&1
|
||||
DEEPTEST_EXIT=$?
|
||||
echo "deeptest_seq_exit=${DEEPTEST_EXIT}" >> specbot-results/test-status.txt
|
||||
else
|
||||
echo "Binary not compiled" > specbot-results/test_deeptest_seq.log
|
||||
echo "deeptest_seq_exit=127" >> specbot-results/test-status.txt
|
||||
fi
|
||||
|
||||
---
|
||||
|
||||
# Specbot Crash Analyzer
|
||||
|
||||
## Job Description
|
||||
|
||||
Your name is ${{ github.workflow }}. You are an expert C/C++ and SMT solver analyst for the Z3 theorem prover
|
||||
repository `${{ github.repository }}`. The pre-steps above have already built Z3 in debug mode from the `c3`
|
||||
branch, compiled and run the specbot test suite, and saved all output to the `specbot-results/` directory in
|
||||
the workspace (`${{ github.workspace }}/specbot-results/`). Your task is to analyze those results, diagnose
|
||||
any crash root causes by reading the relevant source files, and publish a structured findings report as a
|
||||
GitHub Discussion.
|
||||
|
||||
**Do not try to build Z3 or run tests yourself.** All build and test output is already in `specbot-results/`.
|
||||
|
||||
## Your Task
|
||||
|
||||
### 1. Read the Pre-Generated Results
|
||||
|
||||
All build and test outputs are in `specbot-results/` (relative to the workspace root). Read each file:
|
||||
|
||||
```bash
|
||||
# Build status
|
||||
cat specbot-results/build-status.txt 2>/dev/null || echo "No build status"
|
||||
|
||||
# Compile status
|
||||
cat specbot-results/compile-status.txt 2>/dev/null || echo "No compile status"
|
||||
|
||||
# Test status
|
||||
cat specbot-results/test-status.txt 2>/dev/null || echo "No test status"
|
||||
|
||||
# Test output from test_specbot_seq
|
||||
cat specbot-results/test_specbot_seq.log 2>/dev/null || echo "No test_specbot_seq output"
|
||||
|
||||
# Test output from test_deeptest_seq
|
||||
cat specbot-results/test_deeptest_seq.log 2>/dev/null || echo "No test_deeptest_seq output"
|
||||
|
||||
# Last 30 lines of the build log
|
||||
tail -30 specbot-results/build.log 2>/dev/null || echo "No build log"
|
||||
```
|
||||
|
||||
If `specbot-results/build-status.txt` shows `build_exit=0`, the build succeeded.
|
||||
If it shows a non-zero exit, include the last 50 lines of `specbot-results/build.log` in the report
|
||||
under a "Build Failure" section.
|
||||
|
||||
If `specbot-results/compile-status.txt` shows a non-zero exit for a test, include the compile error
|
||||
from `specbot-results/compile_specbot_seq.log` or `specbot-results/compile_deeptest_seq.log`.
|
||||
|
||||
Collect every line containing `CRASH` or `ABORT` from the test log files — these are the crashes to analyze.
|
||||
|
||||
### 2. Diagnose Each Crash
|
||||
|
||||
For each crashed test function, perform the following analysis:
|
||||
|
||||
1. **Identify the test body**: read `specbot/test_specbot_seq.c` or `specbot/test_deeptest_seq.c`
|
||||
to understand what Z3 API calls the test makes and what invariants it exercises.
|
||||
|
||||
2. **Find the likely crash site**: the test exercises the Z3 Nielsen/nseq string solver. Relevant source files are:
|
||||
- `src/smt/seq_solver.h` and `src/smt/seq_solver.cpp` (or nearby files)
|
||||
- `src/smt/seq_axioms.cpp`, `src/smt/seq_eq_solver.cpp`, `src/smt/seq_regex.cpp`
|
||||
- `src/math/lp/` for length-arithmetic paths
|
||||
- `src/api/z3_api.h` for the public API entry points
|
||||
|
||||
Use `grep` and `view` to locate assertion macros, `UNREACHABLE()`, `SASSERT`, or `throw` statements
|
||||
in the code paths exercised by the failing test. Example:
|
||||
```bash
|
||||
grep -rn "SASSERT\|UNREACHABLE\|Z3_CATCH" src/smt/seq_solver.cpp 2>/dev/null | head -30
|
||||
```
|
||||
|
||||
3. **Hypothesize root cause**: based on the Z3 API calls in the test and the assertion/throw sites in
|
||||
the solver source, state the most likely root cause. Common categories include:
|
||||
- Violated invariant (SASSERT/UNREACHABLE hit due to unexpected solver state)
|
||||
- Use-after-free or dangling reference during push/pop
|
||||
- Unhandled edge case in Nielsen graph construction
|
||||
- Missing theory-combination lemma between string length and integer arithmetic
|
||||
|
||||
4. **Suggest a fix**: propose a minimal, concrete fix — e.g., a guard condition, an additional lemma,
|
||||
a missing reference-count increment, or a missing case in a switch/match.
|
||||
|
||||
### 3. Generate the Report
|
||||
|
||||
After analyzing all crashes, produce a structured GitHub Discussion in the "Agentic Workflows" category
|
||||
using `create-discussion`.
|
||||
|
||||
The discussion body must follow this structure (use `###` and lower for headers):
|
||||
|
||||
```
|
||||
### Summary
|
||||
|
||||
- Build: Debug (CMake + Ninja, c3 branch)
|
||||
- Tests compiled: N
|
||||
- Tests run: N
|
||||
- Tests passed: N
|
||||
- Tests crashed: N
|
||||
- Tests timed out: N
|
||||
|
||||
### Crash Findings
|
||||
|
||||
For each crash, one subsection:
|
||||
|
||||
#### <test function name>
|
||||
|
||||
**Test file**: `specbot/test_specbot_seq.c` or `specbot/test_deeptest_seq.c`
|
||||
|
||||
**Observed failure**: ABORT/CRASH — one-line description of what was caught
|
||||
|
||||
**Root cause hypothesis**: explanation of which assertion or code path was hit and why
|
||||
|
||||
**Suggested fix**: concrete proposed change (file, function, what to add/change)
|
||||
|
||||
---
|
||||
|
||||
### Tests Passed
|
||||
|
||||
List of test names that passed.
|
||||
|
||||
<details>
|
||||
<summary><b>Full Test Output</b></summary>
|
||||
|
||||
Raw stdout/stderr from both test binaries.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Build Log</b></summary>
|
||||
|
||||
Last 30 lines of the ninja build output.
|
||||
|
||||
</details>
|
||||
```
|
||||
|
||||
If there are no crashes at all, write a "No Crashes Found" summary celebrating that all tests passed,
|
||||
and include the full test output in a collapsible section.
|
||||
|
||||
Use `mentions: false` behavior — do not mention any GitHub usernames in the report.
|
||||
|
||||
Format workflow run references as: `[§${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`.
|
||||
|
||||
## Usage
|
||||
|
||||
Trigger via **Actions → Specbot Crash Analyzer → Run workflow** on any branch. The pre-steps
|
||||
always check out the `c3` branch where `specbot/test_specbot_seq.c` and
|
||||
`specbot/test_deeptest_seq.c` live, build Z3, run the tests, and save results to `specbot-results/`.
|
||||
The agent then analyzes the results and posts a discussion to the "Agentic Workflows" category.
|
||||
1020
.github/workflows/specbot.lock.yml
generated
vendored
1020
.github/workflows/specbot.lock.yml
generated
vendored
File diff suppressed because it is too large
Load diff
58
.github/workflows/specbot.md
vendored
58
.github/workflows/specbot.md
vendored
|
|
@ -1,58 +0,0 @@
|
|||
---
|
||||
description: Automatically annotate code with assertions capturing class invariants, pre-conditions, and post-conditions using LLM-based specification mining
|
||||
|
||||
on:
|
||||
schedule: weekly
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target_path:
|
||||
description: 'Target directory or file to analyze (e.g., src/ast/, src/smt/smt_context.cpp)'
|
||||
required: false
|
||||
default: ''
|
||||
target_class:
|
||||
description: 'Specific class name to analyze (optional)'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
roles: [write, maintain, admin]
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.BOT_PAT }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
|
||||
tools:
|
||||
github:
|
||||
toolsets: [default]
|
||||
view: {}
|
||||
glob: {}
|
||||
edit: {}
|
||||
bash:
|
||||
- ":*"
|
||||
|
||||
mcp-servers:
|
||||
serena:
|
||||
container: "ghcr.io/githubnext/serena-mcp-server"
|
||||
version: "latest"
|
||||
|
||||
safe-outputs:
|
||||
create-discussion:
|
||||
title-prefix: "[SpecBot] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
|
||||
timeout-minutes: 45
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
---
|
||||
|
||||
<!-- Edit the file linked below to modify the agent without recompilation. Feel free to move the entire markdown body to that file. -->
|
||||
@./agentics/specbot.md
|
||||
1672
.github/workflows/tactic-to-simplifier.lock.yml
generated
vendored
Normal file
1672
.github/workflows/tactic-to-simplifier.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
279
.github/workflows/tactic-to-simplifier.md
vendored
Normal file
279
.github/workflows/tactic-to-simplifier.md
vendored
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
---
|
||||
description: Compares exposed tactics and simplifiers in Z3, and creates issues for tactics that can be converted to simplifiers
|
||||
|
||||
on:
|
||||
schedule: weekly
|
||||
workflow_dispatch:
|
||||
|
||||
timeout-minutes: 30
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
|
||||
network: defaults
|
||||
|
||||
tools:
|
||||
cache-memory: true
|
||||
github:
|
||||
toolsets: [default]
|
||||
bash: [":*"]
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-issue:
|
||||
labels:
|
||||
- enhancement
|
||||
- refactoring
|
||||
- tactic-to-simplifier
|
||||
title-prefix: "[tactic-to-simplifier] "
|
||||
max: 3
|
||||
noop:
|
||||
report-as-issue: false
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
---
|
||||
|
||||
# Tactic-to-Simplifier Comparison Agent
|
||||
|
||||
You are an expert Z3 theorem prover developer. Your task is to compare the tactics and simplifiers exposed in the Z3 codebase, identify tactics that could be converted into simplifiers, and create GitHub issues with the proposed code changes.
|
||||
|
||||
## Background
|
||||
|
||||
Z3 has two related but distinct abstraction layers:
|
||||
|
||||
- **Tactics** (`tactic` base class in `src/tactic/tactic.h`): Operate on *goals* (sets of formulas). Registered with `ADD_TACTIC` macros.
|
||||
- **Simplifiers** (`dependent_expr_simplifier` base class in `src/ast/simplifiers/dependent_expr_state.h`): Operate on individual `dependent_expr` objects in a `dependent_expr_state`. Registered with `ADD_SIMPLIFIER` macros.
|
||||
|
||||
The preferred modern pattern wraps a simplifier as a tactic using `dependent_expr_state_tactic` (see `src/tactic/dependent_expr_state_tactic.h`). Example from `src/tactic/core/propagate_values2_tactic.h`:
|
||||
|
||||
```cpp
|
||||
inline tactic * mk_propagate_values2_tactic(ast_manager & m, params_ref const & p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(propagate_values, m, p, s); });
|
||||
}
|
||||
|
||||
/*
|
||||
ADD_TACTIC("propagate-values2", "propagate constants.", "mk_propagate_values2_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("propagate-values", "propagate constants.", "alloc(propagate_values, m, p, s)")
|
||||
*/
|
||||
```
|
||||
|
||||
## Your Task
|
||||
|
||||
### Step 1: Collect All Tactics
|
||||
|
||||
Scan all header files in `src/` to extract every `ADD_TACTIC` registration:
|
||||
|
||||
```bash
|
||||
grep -rn "ADD_TACTIC(" src/ --include="*.h" | grep -v "^Binary"
|
||||
```
|
||||
|
||||
Parse each line to extract:
|
||||
- Tactic name (first quoted string)
|
||||
- Description (second quoted string)
|
||||
- Factory expression (third quoted string)
|
||||
- File path
|
||||
|
||||
### Step 2: Collect All Simplifiers
|
||||
|
||||
Scan all header files to extract every `ADD_SIMPLIFIER` registration:
|
||||
|
||||
```bash
|
||||
grep -rn "ADD_SIMPLIFIER(" src/ --include="*.h" | grep -v "^Binary"
|
||||
```
|
||||
|
||||
Parse each line to extract:
|
||||
- Simplifier name
|
||||
- Description
|
||||
- Factory expression
|
||||
- File path
|
||||
|
||||
### Step 3: Compare and Find Gaps
|
||||
|
||||
Build a comparison table. For each tactic, check if there is a corresponding simplifier with the same or a closely related name.
|
||||
|
||||
Key rules for matching:
|
||||
- Exact name match: tactic `simplify` ↔ simplifier `simplify`
|
||||
- Version suffix: tactic `propagate-values2` corresponds to simplifier `propagate-values` (the "2" suffix indicates the tactic wraps the simplifier)
|
||||
- Suffix `2`: tactics with `2` suffix (e.g., `elim-uncnstr2`, `propagate-bv-bounds2`) typically already have a simplifier counterpart
|
||||
|
||||
Identify tactics that have **no corresponding simplifier**.
|
||||
|
||||
### Step 4: Evaluate Convertibility
|
||||
|
||||
For each tactic without a corresponding simplifier, assess whether it is a good candidate for conversion by examining its implementation:
|
||||
|
||||
```bash
|
||||
# Read the tactic's header file to understand its implementation
|
||||
grep -rn "mk_<tactic_name>_tactic\|class <tactic_name>" src/ --include="*.h" --include="*.cpp"
|
||||
```
|
||||
|
||||
A tactic is a **good conversion candidate** if:
|
||||
1. It transforms formulas in a formula-by-formula way (no goal splitting/branching)
|
||||
2. It does not produce multiple goals from one
|
||||
3. It is a pure simplification (rewrites terms without adding new conjuncts that split the goal)
|
||||
4. It doesn't require global goal analysis beyond what `dependent_expr_state` provides
|
||||
|
||||
A tactic is **not suitable** for conversion if:
|
||||
- It splits goals into multiple subgoals
|
||||
- It requires tight coupling to the goal infrastructure
|
||||
- It depends on features unavailable in `dependent_expr_simplifier` (e.g., `goal_num_occurs`)
|
||||
- It only makes sense in a tactic pipeline (e.g., `fail`, `skip`, `sat`, `smt`, solver tactics)
|
||||
- It is a portfolio tactic (combines multiple tactics)
|
||||
|
||||
### Step 5: Check for Existing Issues
|
||||
|
||||
Before creating any issue, search for existing open issues to avoid duplicates:
|
||||
|
||||
Use GitHub tools to search: `repo:${{ github.repository }} is:issue is:open label:tactic-to-simplifier "<tactic-name>"`
|
||||
|
||||
Also check cache memory for previously created issues.
|
||||
|
||||
### Step 6: Create Issues for Convertible Tactics
|
||||
|
||||
For each convertible tactic that does not already have an open issue:
|
||||
|
||||
1. Read the tactic's existing implementation carefully (header + cpp files)
|
||||
2. Design the corresponding `dependent_expr_simplifier` subclass
|
||||
3. Draft the code for the new simplifier
|
||||
|
||||
Use `create-issue` to create a GitHub issue with:
|
||||
|
||||
**Title**: `Convert tactic '<tactic-name>' to a simplifier`
|
||||
|
||||
**Body**:
|
||||
|
||||
```markdown
|
||||
## Summary
|
||||
|
||||
The `<tactic-name>` tactic (described as: "<description>") currently only exists as a `tactic`
|
||||
and has no corresponding `dependent_expr_simplifier`. This issue proposes converting it to
|
||||
expose it as both a tactic (via the `dependent_expr_state_tactic` wrapper) and a simplifier.
|
||||
|
||||
## Background
|
||||
|
||||
Z3 provides two abstraction layers for formula transformation:
|
||||
- **Tactics** (`tactic` base class): Operate on goals
|
||||
- **Simplifiers** (`dependent_expr_simplifier` base class): Operate on individual formulas in a `dependent_expr_state`
|
||||
|
||||
The modern pattern wraps a simplifier inside a tactic using `dependent_expr_state_tactic`.
|
||||
|
||||
## Current Implementation
|
||||
|
||||
File: `<path/to/tactic/header.h>`
|
||||
|
||||
```cpp
|
||||
// Existing tactic registration
|
||||
ADD_TACTIC("<tactic-name>", "<description>", "mk_<tactic_name>_tactic(m, p)")
|
||||
```
|
||||
|
||||
## Proposed Change
|
||||
|
||||
### 1. Create a new simplifier class in `src/ast/simplifiers/<name>_simplifier.h`:
|
||||
|
||||
```cpp
|
||||
#pragma once
|
||||
#include "ast/simplifiers/dependent_expr_state.h"
|
||||
// ... other includes
|
||||
|
||||
class <name>_simplifier : public dependent_expr_simplifier {
|
||||
// ... internal state
|
||||
public:
|
||||
<name>_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& s)
|
||||
: dependent_expr_simplifier(m, s) { }
|
||||
|
||||
char const* name() const override { return "<simplifier-name>"; }
|
||||
|
||||
void reduce() override {
|
||||
for (unsigned idx : indices()) {
|
||||
auto& d = m_fmls[idx];
|
||||
// ... transform d.fml() ...
|
||||
expr_ref new_fml(m);
|
||||
// apply simplification
|
||||
m_fmls.update(idx, dependent_expr(m, new_fml, nullptr, d.dep()));
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Update `<path/to/existing/tactic_header.h>` to add the simplifier registration and new tactic factory:
|
||||
|
||||
```cpp
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/<name>_simplifier.h"
|
||||
|
||||
inline tactic* mk_<name>2_tactic(ast_manager& m, params_ref const& p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto& s) -> dependent_expr_simplifier* {
|
||||
return alloc(<name>_simplifier, m, p, s);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
ADD_TACTIC("<tactic-name>2", "<description>", "mk_<name>2_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("<tactic-name>", "<description>", "alloc(<name>_simplifier, m, p, s)")
|
||||
*/
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
- Enables use of `<tactic-name>` in Z3's simplifier pipeline (used by the new solver engine)
|
||||
- Follows the established modern pattern for formula simplification in Z3
|
||||
- No behavioral change for existing tactic users
|
||||
|
||||
## Notes
|
||||
|
||||
- The original `mk_<tactic_name>_tactic` should remain for backward compatibility
|
||||
- The simplifier should implement `supports_proofs()` if proof generation is relevant
|
||||
```
|
||||
|
||||
**Important instructions for issue body**:
|
||||
- Replace all placeholders (`<tactic-name>`, `<name>`, `<description>`, `<path>`, etc.) with **real, specific values** from the actual source code
|
||||
- Provide **actual code** based on reading the tactic's implementation, not generic templates
|
||||
- Include the real factory expression, include paths, and class names from the existing implementation
|
||||
- If the tactic has parameters, include them in the simplifier
|
||||
- If the tactic wraps another component (rewriter, solver, etc.), include that in the simplifier too
|
||||
|
||||
### Step 7: Update Cache Memory
|
||||
|
||||
Store in cache memory:
|
||||
- The list of all tactics analyzed in this run
|
||||
- The list of issues created (tactic name → issue number)
|
||||
- Tactics determined to be non-convertible and why
|
||||
- Tactics with existing issues (to skip in future runs)
|
||||
|
||||
## Conversion Criteria Reference
|
||||
|
||||
### Likely Convertible (if no simplifier exists)
|
||||
- Pure term rewriting tactics (apply rewriting rules)
|
||||
- Bound propagation tactics
|
||||
- Variable elimination tactics that work formula-by-formula
|
||||
- Normalization tactics (NNF, SNF) that apply local transformations
|
||||
- Tactics that simplify based on syntactic structure
|
||||
|
||||
### Not Convertible (skip these)
|
||||
- Solver-based tactics (`ctx-solver-simplify`, `sat`, `smt`, etc.) — require a solver
|
||||
- Portfolio/combinator tactics (`then`, `or-else`, `repeat`, etc.)
|
||||
- Decision procedure tactics (`qfbv`, `qflra`, etc.)
|
||||
- Tactics that split goals (`split-clause`, `tseitin-cnf`, `occf`)
|
||||
- Tactics that only make sense in goal context (`fail`, `skip`)
|
||||
- Tactics using `goal_num_occurs` for occurrence counting (the simplifier doesn't have this)
|
||||
- Tactics that produce multiple result goals
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Be specific**: Provide actual file paths, class names, and factory expressions — no generic placeholders
|
||||
- **Be careful**: Only create issues for tactics that are genuinely good candidates
|
||||
- **Avoid duplicates**: Always check existing issues before creating new ones
|
||||
- **One issue per tactic**: Create separate issues for each convertible tactic
|
||||
- **Read the code**: Examine the actual tactic implementation before proposing code for the simplifier
|
||||
- **Be incremental**: If there are many candidates, focus on the most impactful ones first
|
||||
- **Limit per run**: Create at most 3 new issues per run to avoid flooding the issue tracker
|
||||
1577
.github/workflows/tptp-benchmark.lock.yml
generated
vendored
Normal file
1577
.github/workflows/tptp-benchmark.lock.yml
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
547
.github/workflows/tptp-benchmark.md
vendored
Normal file
547
.github/workflows/tptp-benchmark.md
vendored
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
---
|
||||
description: >
|
||||
Weekly benchmark of Z3's TPTP front-end against 500 random TPTP problems.
|
||||
Downloads TPTP benchmarks from tptp.org, resolves axiom dependencies,
|
||||
skips large problems, runs each with a 5-second timeout, and posts a
|
||||
discrepancy/crash report as a GitHub discussion.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 6 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: read-all
|
||||
|
||||
network:
|
||||
allowed:
|
||||
- defaults
|
||||
- tptp.org
|
||||
|
||||
tools:
|
||||
bash: true
|
||||
github:
|
||||
toolsets: [default]
|
||||
|
||||
safe-outputs:
|
||||
report-failure-as-issue: false
|
||||
create-discussion:
|
||||
title-prefix: "[TPTP Benchmark] "
|
||||
category: "Agentic Workflows"
|
||||
close-older-discussions: true
|
||||
expires: 14d
|
||||
missing-tool:
|
||||
create-issue: true
|
||||
noop:
|
||||
report-as-issue: false
|
||||
|
||||
timeout-minutes: 300
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update -y -q
|
||||
sudo apt-get install -y cmake ninja-build python3 wget curl bc
|
||||
|
||||
- name: Build Z3
|
||||
run: |
|
||||
mkdir -p /tmp/z3-build
|
||||
cd /tmp/z3-build
|
||||
cmake "$GITHUB_WORKSPACE" \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DZ3_BUILD_TEST_EXECUTABLES=OFF
|
||||
ninja -j$(nproc) z3
|
||||
./z3 --version
|
||||
|
||||
---
|
||||
|
||||
# TPTP Front-End Benchmark
|
||||
|
||||
## Job Description
|
||||
|
||||
Your name is ${{ github.workflow }}. You are an expert testing engineer for the Z3 theorem prover. Your task is to:
|
||||
|
||||
1. Verify the Z3 binary built by the pre-flight step is available
|
||||
2. Download the TPTP benchmark library from tptp.org
|
||||
3. Select 500 random small-to-medium problems (with their axiom dependencies)
|
||||
4. Run each problem through Z3's TPTP front-end with a 5-second timeout
|
||||
5. Compare Z3's output against the expected SZS status declared in each problem file
|
||||
6. Post a detailed report as a GitHub Discussion summarising discrepancies and crashes
|
||||
|
||||
**Repository**: ${{ github.repository }}
|
||||
**Workspace**: ${{ github.workspace }}
|
||||
|
||||
## Phase 1: Verify Z3 Binary
|
||||
|
||||
Z3 was built by the workflow pre-flight step and is available at `/tmp/z3-build/z3`.
|
||||
Confirm the binary is present and functional:
|
||||
|
||||
```bash
|
||||
/tmp/z3-build/z3 --version
|
||||
```
|
||||
|
||||
If the binary is missing or returns an error, call the `noop` safe-output with a message describing the problem and stop.
|
||||
|
||||
Once confirmed, call `noop` with `"Z3 binary verified. Downloading TPTP benchmark library — this may take a few minutes."` to keep the safe-output session alive.
|
||||
|
||||
## Phase 2: Download the TPTP Problem Library
|
||||
|
||||
Find the latest TPTP release and download the full archive.
|
||||
|
||||
```bash
|
||||
# Find the latest TPTP distribution version by fetching the directory listing
|
||||
TPTP_DIST_URL="https://tptp.org/TPTP/Distribution/"
|
||||
LATEST_TGZ=$(curl -sL "$TPTP_DIST_URL" \
|
||||
| grep -oP 'TPTP-v[0-9]+\.[0-9]+\.[0-9]+\.tgz' \
|
||||
| sort -V | tail -1)
|
||||
|
||||
if [ -z "$LATEST_TGZ" ]; then
|
||||
echo "ERROR: Could not determine latest TPTP version from $TPTP_DIST_URL"
|
||||
# Fall back to a known stable version
|
||||
LATEST_TGZ="TPTP-v9.0.0.tgz"
|
||||
fi
|
||||
|
||||
echo "Downloading $LATEST_TGZ ..."
|
||||
mkdir -p /tmp/tptp_download
|
||||
wget -q --show-progress \
|
||||
"${TPTP_DIST_URL}${LATEST_TGZ}" \
|
||||
-O /tmp/tptp_download/tptp.tgz
|
||||
|
||||
echo "Extracting TPTP library..."
|
||||
mkdir -p /tmp/tptp
|
||||
tar -xzf /tmp/tptp_download/tptp.tgz -C /tmp/tptp --strip-components=1 2>&1 | tail -5
|
||||
|
||||
# Verify extraction
|
||||
if [ ! -d /tmp/tptp/Problems ] || [ ! -d /tmp/tptp/Axioms ]; then
|
||||
echo "ERROR: TPTP extraction failed — Problems/ or Axioms/ directory not found"
|
||||
ls /tmp/tptp/
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TPTP_ROOT=/tmp/tptp
|
||||
echo "TPTP library extracted to $TPTP_ROOT"
|
||||
echo "Problem domains available:"
|
||||
ls "$TPTP_ROOT/Problems/" | wc -l
|
||||
echo "Axiom files available:"
|
||||
ls "$TPTP_ROOT/Axioms/" | wc -l
|
||||
```
|
||||
|
||||
If the download or extraction fails, call `noop` with the error details and stop.
|
||||
|
||||
Call `noop` with `"TPTP library downloaded and extracted. Selecting 500 benchmark problems — filtering by size."` to keep the session alive.
|
||||
|
||||
## Phase 3: Select 500 Benchmark Problems
|
||||
|
||||
Filter out large problems and problems that depend on large axiom files, then take a random sample of 500.
|
||||
|
||||
Save this script to `/tmp/select_benchmarks.py` and run it:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Select 500 random TPTP problems that:
|
||||
- Have a known, conclusive expected status (Theorem, Unsatisfiable,
|
||||
CounterSatisfiable, Satisfiable) OR Unknown/Open status.
|
||||
- Are not "large" (problem file <= 50 KB).
|
||||
- Do not include any axiom file larger than 100 KB.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import random
|
||||
import sys
|
||||
|
||||
TPTP_ROOT = "/tmp/tptp"
|
||||
PROBLEMS_DIR = os.path.join(TPTP_ROOT, "Problems")
|
||||
AXIOMS_DIR = os.path.join(TPTP_ROOT, "Axioms")
|
||||
MAX_PROBLEM_SIZE = 50 * 1024 # 50 KB
|
||||
MAX_AXIOM_SIZE = 100 * 1024 # 100 KB
|
||||
SAMPLE_SIZE = 500
|
||||
OUTPUT_FILE = "/tmp/selected_benchmarks.txt"
|
||||
|
||||
include_re = re.compile(r"include\s*\(\s*['\"]([^'\"]+)['\"]", re.IGNORECASE)
|
||||
status_re = re.compile(r"%\s*Status\s*:\s*(\S+)", re.IGNORECASE)
|
||||
|
||||
def axiom_sizes_ok(problem_path):
|
||||
"""Return True if all included axiom files exist and are <= MAX_AXIOM_SIZE."""
|
||||
try:
|
||||
with open(problem_path, encoding="utf-8", errors="replace") as f:
|
||||
content = f.read(4096) # header is in first few KB
|
||||
except OSError:
|
||||
return False
|
||||
for m in include_re.finditer(content):
|
||||
axiom_rel = m.group(1) # e.g. "Axioms/AGT001+0.ax"
|
||||
axiom_path = os.path.join(TPTP_ROOT, axiom_rel)
|
||||
if not os.path.exists(axiom_path):
|
||||
return False # axiom missing — skip
|
||||
if os.path.getsize(axiom_path) > MAX_AXIOM_SIZE:
|
||||
return False # axiom too large — skip
|
||||
return True
|
||||
|
||||
candidates = []
|
||||
skipped_size = 0
|
||||
skipped_axiom = 0
|
||||
|
||||
for domain in sorted(os.listdir(PROBLEMS_DIR)):
|
||||
domain_dir = os.path.join(PROBLEMS_DIR, domain)
|
||||
if not os.path.isdir(domain_dir):
|
||||
continue
|
||||
for fname in os.listdir(domain_dir):
|
||||
if not fname.endswith(".p"):
|
||||
continue
|
||||
fpath = os.path.join(domain_dir, fname)
|
||||
size = os.path.getsize(fpath)
|
||||
if size > MAX_PROBLEM_SIZE:
|
||||
skipped_size += 1
|
||||
continue
|
||||
if not axiom_sizes_ok(fpath):
|
||||
skipped_axiom += 1
|
||||
continue
|
||||
candidates.append(fpath)
|
||||
|
||||
print(f"Total candidates (after filtering): {len(candidates)}", flush=True)
|
||||
print(f" Skipped — problem too large : {skipped_size}", flush=True)
|
||||
print(f" Skipped — axiom too large : {skipped_axiom}", flush=True)
|
||||
|
||||
if len(candidates) == 0:
|
||||
print("ERROR: No suitable benchmark problems found.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if len(candidates) > SAMPLE_SIZE:
|
||||
random.seed(42)
|
||||
selected = random.sample(candidates, SAMPLE_SIZE)
|
||||
else:
|
||||
selected = candidates
|
||||
|
||||
selected.sort()
|
||||
with open(OUTPUT_FILE, "w") as f:
|
||||
f.write("\n".join(selected) + "\n")
|
||||
|
||||
print(f"Selected {len(selected)} problems → {OUTPUT_FILE}", flush=True)
|
||||
```
|
||||
|
||||
Run the script:
|
||||
|
||||
```bash
|
||||
python3 /tmp/select_benchmarks.py
|
||||
SELECTED=$(wc -l < /tmp/selected_benchmarks.txt)
|
||||
echo "Benchmark set: $SELECTED problems"
|
||||
```
|
||||
|
||||
If no problems are found, call `noop` with an error message and stop.
|
||||
|
||||
Call `noop` with `"$SELECTED problems selected. Starting benchmark run with 5-second timeout per problem — this will take approximately $(( SELECTED * 7 / 60 )) minutes."` to keep the session alive.
|
||||
|
||||
## Phase 4: Run Benchmarks
|
||||
|
||||
Save the following script to `/tmp/run_tptp_benchmarks.sh`, make it executable, and run it.
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
Z3=/tmp/z3-build/z3
|
||||
TPTP_ROOT=/tmp/tptp
|
||||
TIMEOUT_HARD=8 # outer OS-level guard (seconds; 3 s beyond Z3's -T:5)
|
||||
Z3_TIMEOUT=5 # Z3 internal timeout: -T:N sets N-second limit (uppercase -T is seconds)
|
||||
|
||||
RESULTS=/tmp/tptp_results.tsv
|
||||
PROBLEM_LIST=/tmp/selected_benchmarks.txt
|
||||
|
||||
echo -e "file\texpected\tactual\ttime_s\tnotes" > "$RESULTS"
|
||||
|
||||
# Helper: extract the expected SZS status from the TPTP problem header.
|
||||
get_expected_status() {
|
||||
local file="$1"
|
||||
# Look for lines like: "% Status : Theorem"
|
||||
grep -m1 -iP '%\s*Status\s*:\s*\K\S+' "$file" 2>/dev/null || echo "Unknown"
|
||||
}
|
||||
|
||||
# Helper: run z3 on a single TPTP problem with timeout.
|
||||
run_benchmark() {
|
||||
local file="$1"
|
||||
local start end elapsed output exit_code verdict
|
||||
|
||||
start=$(date +%s%3N) # milliseconds since epoch
|
||||
output=$(TPTP="$TPTP_ROOT" timeout "$TIMEOUT_HARD" \
|
||||
"$Z3" -tptp -T:"$Z3_TIMEOUT" "$file" 2>&1) || exit_code=$?
|
||||
exit_code=${exit_code:-0}
|
||||
end=$(date +%s%3N)
|
||||
elapsed=$(echo "scale=3; ($end - $start) / 1000" | bc)
|
||||
|
||||
# Extract SZS status line from output
|
||||
szs_line=$(echo "$output" | grep -m1 "% SZS status" || true)
|
||||
|
||||
if [ -n "$szs_line" ]; then
|
||||
# Parse the status keyword (e.g. "Theorem", "CounterSatisfiable", "GaveUp")
|
||||
verdict=$(echo "$szs_line" | grep -oP '% SZS status \K\S+' || echo "Unknown")
|
||||
elif [ "$exit_code" -eq 124 ]; then
|
||||
verdict="Timeout"
|
||||
elif [ "$exit_code" -ne 0 ]; then
|
||||
verdict="Crash"
|
||||
else
|
||||
verdict="NoOutput"
|
||||
fi
|
||||
|
||||
echo "$verdict $elapsed"
|
||||
}
|
||||
|
||||
COUNTER=0
|
||||
TOTAL=$(wc -l < "$PROBLEM_LIST")
|
||||
|
||||
while IFS= read -r problem_file; do
|
||||
COUNTER=$((COUNTER + 1))
|
||||
|
||||
expected=$(get_expected_status "$problem_file")
|
||||
result_line=$(run_benchmark "$problem_file")
|
||||
actual=$(echo "$result_line" | cut -d' ' -f1)
|
||||
elapsed=$(echo "$result_line" | cut -d' ' -f2)
|
||||
fname=$(basename "$problem_file")
|
||||
|
||||
# Classify notes
|
||||
notes=""
|
||||
# Soundness discrepancy: both answers are conclusive but conflict
|
||||
conclusive_expected=false
|
||||
conclusive_actual=false
|
||||
case "$expected" in
|
||||
Theorem|Unsatisfiable) conclusive_expected=true ;;
|
||||
Satisfiable|CounterSatisfiable) conclusive_expected=true ;;
|
||||
esac
|
||||
case "$actual" in
|
||||
Theorem|Unsatisfiable) conclusive_actual=true ;;
|
||||
Satisfiable|CounterSatisfiable) conclusive_actual=true ;;
|
||||
esac
|
||||
|
||||
if $conclusive_expected && $conclusive_actual; then
|
||||
# Map expected to the Z3 output equivalents for comparison
|
||||
# Theorem (has-conjecture unsat) matches "Theorem"
|
||||
# Unsatisfiable (no-conjecture unsat) matches "Unsatisfiable"
|
||||
# Satisfiable (no-conjecture sat) matches "Satisfiable"
|
||||
# CounterSatisfiable (has-conjecture sat) matches "CounterSatisfiable"
|
||||
if [ "$expected" != "$actual" ]; then
|
||||
# Check for sat/unsat polarity conflict
|
||||
sat_expected=false; sat_actual=false
|
||||
case "$expected" in Satisfiable|CounterSatisfiable) sat_expected=true ;; esac
|
||||
case "$actual" in Satisfiable|CounterSatisfiable) sat_actual=true ;; esac
|
||||
if [ "$sat_expected" != "$sat_actual" ]; then
|
||||
notes="SOUNDNESS_ERROR"
|
||||
else
|
||||
notes="STATUS_MISMATCH"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$actual" = "Crash" ]; then
|
||||
notes="CRASH"
|
||||
fi
|
||||
|
||||
echo -e "$fname\t$expected\t$actual\t$elapsed\t$notes" >> "$RESULTS"
|
||||
|
||||
if [ -n "$notes" ]; then
|
||||
echo "[$COUNTER/$TOTAL] $fname expected=$expected actual=$actual time=${elapsed}s *** $notes ***"
|
||||
elif [ $((COUNTER % 50)) -eq 0 ]; then
|
||||
echo "[$COUNTER/$TOTAL] Progress checkpoint last=$fname actual=$actual time=${elapsed}s"
|
||||
fi
|
||||
|
||||
done < "$PROBLEM_LIST"
|
||||
|
||||
echo "Benchmark run complete: $COUNTER problems processed. Results → $RESULTS"
|
||||
```
|
||||
|
||||
Run it:
|
||||
|
||||
```bash
|
||||
chmod +x /tmp/run_tptp_benchmarks.sh
|
||||
/tmp/run_tptp_benchmarks.sh
|
||||
```
|
||||
|
||||
Do not skip any file in the list.
|
||||
|
||||
## Phase 5: Analyze Results
|
||||
|
||||
Save the following script to `/tmp/analyze_tptp.py` and run it:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Compute summary statistics from the TPTP benchmark TSV."""
|
||||
import csv
|
||||
|
||||
RESULTS_FILE = "/tmp/tptp_results.tsv"
|
||||
|
||||
rows = []
|
||||
with open(RESULTS_FILE, newline="") as f:
|
||||
reader = csv.DictReader(f, delimiter="\t")
|
||||
for row in reader:
|
||||
rows.append(row)
|
||||
|
||||
total = len(rows)
|
||||
|
||||
# Verdict counts
|
||||
from collections import Counter, defaultdict
|
||||
actual_counts = Counter(r["actual"] for r in rows)
|
||||
expected_counts = Counter(r["expected"] for r in rows)
|
||||
|
||||
# Flagged rows
|
||||
soundness_errors = [r for r in rows if r["notes"] == "SOUNDNESS_ERROR"]
|
||||
status_mismatches = [r for r in rows if r["notes"] == "STATUS_MISMATCH"]
|
||||
crashes = [r for r in rows if r["notes"] == "CRASH"]
|
||||
timeouts = [r for r in rows if r["actual"] == "Timeout"]
|
||||
gave_up = [r for r in rows if r["actual"] == "GaveUp"]
|
||||
|
||||
# Solved correctly (expected matches actual for conclusive verdicts)
|
||||
conclusive_expected = {"Theorem", "Unsatisfiable", "Satisfiable", "CounterSatisfiable"}
|
||||
correct = [r for r in rows
|
||||
if r["expected"] in conclusive_expected
|
||||
and r["actual"] == r["expected"]]
|
||||
|
||||
print(f"TOTAL={total}")
|
||||
print(f"CORRECT={len(correct)}")
|
||||
print(f"TIMEOUTS={len(timeouts)}")
|
||||
print(f"GAVE_UP={len(gave_up)}")
|
||||
print(f"CRASHES={len(crashes)}")
|
||||
print(f"SOUNDNESS_ERRORS={len(soundness_errors)}")
|
||||
print(f"STATUS_MISMATCHES={len(status_mismatches)}")
|
||||
|
||||
print("\n--- Actual verdict breakdown ---")
|
||||
for v, c in sorted(actual_counts.items()):
|
||||
print(f" {v}: {c}")
|
||||
|
||||
print("\n--- Expected status breakdown ---")
|
||||
for v, c in sorted(expected_counts.items()):
|
||||
print(f" {v}: {c}")
|
||||
|
||||
if soundness_errors:
|
||||
print(f"\n--- SOUNDNESS ERRORS ({len(soundness_errors)}) ---")
|
||||
for r in soundness_errors:
|
||||
print(f" {r['file']} expected={r['expected']} actual={r['actual']}")
|
||||
|
||||
if crashes:
|
||||
print(f"\n--- CRASHES ({len(crashes)}) ---")
|
||||
for r in crashes:
|
||||
print(f" {r['file']} expected={r['expected']}")
|
||||
|
||||
if status_mismatches:
|
||||
print(f"\n--- STATUS MISMATCHES ({len(status_mismatches)}) ---")
|
||||
for r in status_mismatches[:20]:
|
||||
print(f" {r['file']} expected={r['expected']} actual={r['actual']}")
|
||||
```
|
||||
|
||||
Run the analysis:
|
||||
|
||||
```bash
|
||||
python3 /tmp/analyze_tptp.py
|
||||
```
|
||||
|
||||
## Phase 6: Generate and Post the Discussion Report
|
||||
|
||||
Read the TSV at `/tmp/tptp_results.tsv` and the analysis output, then compose a Markdown report and call `create_discussion`.
|
||||
|
||||
The report should use `###` or lower for all headers (never `#` or `##`). Use collapsible `<details>` sections for large tables.
|
||||
|
||||
Use this structure:
|
||||
|
||||
```markdown
|
||||
**Date**: <today's date>
|
||||
**Branch**: master
|
||||
**Commit**: `<short SHA>` (run `git rev-parse --short HEAD` in ${{ github.workspace }} to get the SHA)
|
||||
**Workflow Run**: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
**TPTP version**: <downloaded version>
|
||||
**Problems benchmarked**: <N> (random sample, timeout 5 s per problem)
|
||||
|
||||
---
|
||||
|
||||
### Summary
|
||||
|
||||
| Metric | Count |
|
||||
|--------|-------|
|
||||
| Total problems run | N |
|
||||
| Correct (expected = actual) | N |
|
||||
| Timeouts | N |
|
||||
| GaveUp (within time budget) | N |
|
||||
| Crashes / errors | N |
|
||||
| Soundness errors (sat↔unsat conflict) | N |
|
||||
| Status mismatches (Theorem vs Unsatisfiable etc.) | N |
|
||||
|
||||
### Expected Status Distribution
|
||||
|
||||
| Expected Status | Count |
|
||||
|----------------|-------|
|
||||
| Theorem | N |
|
||||
| Unsatisfiable | N |
|
||||
| Satisfiable | N |
|
||||
| CounterSatisfiable | N |
|
||||
| Unknown / Open | N |
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ Critical: Soundness Errors
|
||||
|
||||
[List ALL files where Z3 returned a conclusive answer that contradicts the expected answer
|
||||
(e.g., expected Theorem but got CounterSatisfiable). If none, write "None detected."]
|
||||
|
||||
### 💥 Crashes
|
||||
|
||||
[List ALL files where Z3 crashed (non-zero exit, no SZS output, not a timeout).
|
||||
Include filename and expected status. If none, write "None detected."]
|
||||
|
||||
### Status Mismatches
|
||||
|
||||
[Files where both answers are conclusive but differ in Theorem vs Unsatisfiable polarity
|
||||
(e.g., expected Theorem but actual Unsatisfiable). These may indicate conjecture-handling
|
||||
differences rather than soundness bugs. If none, write "None detected."]
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>View all Timeouts (problems where Z3 exceeded the 5-second limit)</summary>
|
||||
|
||||
| # | File | Expected Status |
|
||||
|---|------|----------------|
|
||||
[First 100 timeout rows]
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>View full per-problem results table</summary>
|
||||
|
||||
| # | File | Expected | Actual | Time (s) | Notes |
|
||||
|---|------|----------|--------|----------|-------|
|
||||
[All rows, or first 500 if over limit]
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### Recommendations
|
||||
|
||||
[Based on the findings, list actionable items. E.g.: investigate soundness errors,
|
||||
file crash bugs, note domains where Z3 consistently times out.]
|
||||
```
|
||||
|
||||
Post the discussion using the `create_discussion` safe output. The title should be
|
||||
`[TPTP Benchmark] master — <date>`.
|
||||
|
||||
## Safe Output Guarantee
|
||||
|
||||
You **MUST** call either `create_discussion` or `noop` before the workflow ends:
|
||||
|
||||
- **Full success**: Call `create_discussion` with the complete report.
|
||||
- **Partial results** (some problems ran): Call `create_discussion` with whatever results are available and a note about incomplete execution.
|
||||
- **Download failure**: Call `noop` with the download error details.
|
||||
- **No problems selected**: Call `noop` explaining why no problems were found.
|
||||
- **Binary missing**: If `/tmp/z3-build/z3` is unexpectedly absent, call `noop` with that detail and stop.
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **Build failure handling**: Z3 was built before the agent loaded. If the binary is missing or non-functional, call `noop` with the error and stop.
|
||||
- **TPTP environment variable**: Set `TPTP=/tmp/tptp` when invoking `z3 -tptp` so that `include()` directives in problem files resolve correctly against the downloaded Axioms directory.
|
||||
- **Timeout detection**: Use `timeout 8` as the outer OS-level guard (3 seconds beyond Z3's `-T:5`) to allow Z3 to exit cleanly before the shell kills it. If the exit code from `timeout` is 124, record the verdict as `Timeout`.
|
||||
- **Crash detection**: A crash is a non-zero exit code with no `% SZS status` line in the output and no timeout. Record it separately from `GaveUp`.
|
||||
- **SZS status semantics**: Z3 outputs `Theorem` (not `Unsatisfiable`) when it proves a conjecture; `CounterSatisfiable` (not `Satisfiable`) when it finds a counterexample to a conjecture. A status mismatch between `Theorem` and `Unsatisfiable` for the same problem may be innocuous and depends on whether the problem file uses a conjecture formula.
|
||||
- **Report soundness bugs prominently**: Any case where the polarity of the answer conflicts (expected Theorem/Unsatisfiable but got CounterSatisfiable/Satisfiable, or vice versa) is a potential soundness bug and must be highlighted as critical.
|
||||
- **Keep progress log**: Print a line for every flagged result and every 50th problem so the workflow log shows progress.
|
||||
- **Close older discussions**: Configured via `close-older-discussions: true`. Only the latest weekly report remains open.
|
||||
4
.github/workflows/wasm-release.yml
vendored
4
.github/workflows/wasm-release.yml
vendored
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v6
|
||||
|
|
@ -36,7 +36,7 @@ jobs:
|
|||
cp ../../../LICENSE.txt .
|
||||
|
||||
- name: Setup emscripten
|
||||
uses: mymindstorm/setup-emsdk@v14
|
||||
uses: mymindstorm/setup-emsdk@v16
|
||||
with:
|
||||
no-install: true
|
||||
version: ${{env.EM_VERSION}}
|
||||
|
|
|
|||
4
.github/workflows/wasm.yml
vendored
4
.github/workflows/wasm.yml
vendored
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6.0.2
|
||||
uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v6
|
||||
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
node-version: "lts/*"
|
||||
|
||||
- name: Setup emscripten
|
||||
uses: mymindstorm/setup-emsdk@v14
|
||||
uses: mymindstorm/setup-emsdk@v16
|
||||
with:
|
||||
no-install: true
|
||||
version: ${{env.EM_VERSION}}
|
||||
|
|
|
|||
2
.github/workflows/wip.yml
vendored
2
.github/workflows/wip.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6.0.2
|
||||
- uses: actions/checkout@v7.0.0
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||
|
|
|
|||
1959
.github/workflows/workflow-suggestion-agent.lock.yml
generated
vendored
1959
.github/workflows/workflow-suggestion-agent.lock.yml
generated
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue