Neuro-Symbolic Formal Verification Engine for COBOL to Java Migration
verify-cbl detects penny drift (rounding discrepancies) between COBOL and Java financial calculations using the Z3 SMT solver with Monte Carlo fuzzing fallback. A self-correcting LLM agent translates COBOL to mathematically proven Java BigDecimal code.
Problem Statement: Standard LLM translation produces semantically incorrect Java code due to differences in rounding semantics (COBOL ROUNDED uses HALF_UP; Java double truncates). This causes cumulative financial drift at scale.
Solution: Formal verification via satisfiability modulo theories (SMT) with automatic counter-example generation and LLM self-correction loop.
- System Architecture
- Quick Start
- Installation
- CLI Reference
- Python API
- Verification Pipeline
- Supported COBOL Features
- LLM Providers
- Testing
- Benchmarks
- Docker Deployment
- License
flowchart LR
A[COBOL Source] --> B[Parser]
B --> C[IR Expression Tree]
C --> D[LLM Translation]
D --> E[Java BigDecimal]
E --> F{Z3 Solver}
F -->|equivalent| G[✓ Verified Output]
F -->|drift| H[Error Diagnosis]
H --> D
F -->|timeout| I[Fuzzing 100k]
I --> G
Pipeline: COBOL → IR → LLM → Java → Z3 verification → Output (with self-correction loop on errors)
# Install
uv pip install -e ".[all]"
# Verify single statement
verify-cbl check \
--cobol "COMPUTE WS-TAX ROUNDED = WS-AMOUNT * 0.22." \
--cobol-vars "WS-AMOUNT:9(7)V99,WS-TAX:9(6)V99" \
--java "double tax = amount * 0.22;"
# Translate COBOL file
verify-cbl translate -s payroll.cbl -o Payroll.java --llm ollama
# Run Streamlit demo
uv run streamlit run demo/streamlit_app.py| Requirement | Version | Purpose |
|---|---|---|
| Python | 3.12+ | Runtime |
| uv | latest | Package manager (recommended) |
| Ollama | optional | Local LLM inference |
git clone https://github.com/yourusername/verify-cbl.git
cd verify-cbl
# Core (Z3 verification, CLI)
uv pip install -e .
# With LLM agent
uv pip install -e ".[agent]"
# With REST API
uv pip install -e ".[api]"
# With Streamlit demo
uv pip install -e ".[demo]"
# All features
uv pip install -e ".[all]"
# Development
uv pip install -e ".[all,dev]"| Package | Version | Purpose |
|---|---|---|
z3-solver |
>=4.12.0 | SMT solver for formal verification |
pydantic |
>=2.0.0 | Data validation |
typer |
>=0.12.0 | CLI framework |
rich |
>=13.0.0 | Terminal output |
litellm |
>=1.30.0 | LLM integration (optional) |
fastapi |
>=0.109.0 | REST API (optional) |
streamlit |
>=1.32.0 | Web demo (optional) |
| Command | Description |
|---|---|
verify-cbl check |
Verify single COBOL/Java statement pair |
verify-cbl translate |
Translate COBOL file to verified Java |
verify-cbl compare |
Compare existing COBOL and Java files |
verify-cbl batch |
Batch verify multiple statements |
verify-cbl scan |
Extract variables from COBOL file |
verify-cbl serve |
Start REST API server |
verify-cbl demo |
Run CLI demonstration |
verify-cbl demo-streamlit |
Start Streamlit web demo |
verify-cbl info |
Show engine capabilities |
# Translate with Ollama (local LLM)
verify-cbl translate \
-s tests/industrial_cases/case1_payroll_complex.cbl \
-o Payroll.java \
--llm ollama \
--model deepseek-coder-v2
# Batch verify with JSON report
verify-cbl batch payroll.cbl -o report.json --transactions 5000000
# Compare files for drift
verify-cbl compare --cobol legacy.cbl --java Modern.java -f jsonfrom decimal import Decimal
from verify_cbl import (
SymbolicEngine, Variable, NumericType, RangeConstraint,
CobolComputeParser, SymbolTable, RoundingMode, RoundingOp
)
# Define symbol table
symbols = SymbolTable()
symbols.define("WS-AMOUNT", "9(7)V99")
symbols.define("WS-TAX", "9(6)V99")
# Parse COBOL
parser = CobolComputeParser(symbols)
cobol_expr = parser.parse("COMPUTE WS-TAX ROUNDED = WS-AMOUNT * 0.22.").expression
# Create Java equivalent with truncation
java_expr = RoundingOp(
operand=cobol_expr.operand,
scale=2,
mode=RoundingMode.DOWN # Java truncation
)
# Define constraints
variables = [
Variable(
name="WS-AMOUNT",
type_=NumericType.DECIMAL,
constraints=[RangeConstraint(
min_value=Decimal("0.01"),
max_value=Decimal("99999.99"),
decimal_places=2
)]
)
]
# Verify
engine = SymbolicEngine(timeout_ms=2000)
result = engine.verify_equivalence(cobol_expr, java_expr, variables)
if not result.is_equivalent:
ce = result.counter_example
print(f"Drift at WS-AMOUNT={ce.inputs['WS-AMOUNT']}")
print(f"COBOL: {ce.output_a}, Java: {ce.output_b}")
print(f"Difference: {ce.difference}")from verify_cbl import MigrationAgent, MockLLM
from verify_cbl.agent import OllamaLLM
# Demo mode
agent = MigrationAgent(llm=MockLLM(), max_attempts=5)
# Production mode with local LLM
agent = MigrationAgent(
llm=OllamaLLM(model="deepseek-coder-v2"),
max_attempts=5
)
result = agent.translate(
"COMPUTE WS-TAX ROUNDED = WS-AMOUNT * 0.22.",
"WS-TAX:9(5)V99, WS-AMOUNT:9(7)V99"
)
print(f"Java: {result.java_code}")
print(f"Verified: {result.success}")
print(f"Method: {result.final_method}") # FORMAL or FUZZINGfrom verify_cbl import FileTranslator
from verify_cbl.agent import OllamaLLM
translator = FileTranslator(
llm=OllamaLLM(model="deepseek-coder-v2"),
max_math_attempts=5,
)
result = translator.translate_file(
"legacy.cbl",
"Modern.java",
audit_path="audit.json"
)
print(f"Total: {result.statements_total}")
print(f"Verified: {result.statements_verified}")
print(f"LLM-only: {result.statements_llm}")flowchart LR
A[PRE-VALIDATION] --> B[COBOL PARSING]
B --> C[JAVA GENERATION]
C --> D[SYNTAX VALIDATION]
D --> E[FORMAL VERIFICATION]
E --> F[ERROR DIAGNOSIS]
F -->|retry| C
| Phase | Description |
|---|---|
| PRE-VALIDATION | Check COBOL statement and variable definitions |
| COBOL PARSING | Parse to IR expression tree |
| JAVA GENERATION | LLM generates BigDecimal code |
| SYNTAX VALIDATION | Verify Java structure before Z3 |
| FORMAL VERIFICATION | Z3/Fuzzer equivalence check |
| ERROR DIAGNOSIS | Categorize error for targeted fix |
| Category | Description | Fix Strategy |
|---|---|---|
SYNTAX_ERROR |
Java doesn't parse | Regenerate |
WRONG_ROUNDING |
Wrong RoundingMode | Specify HALF_UP/DOWN |
WRONG_SCALE |
Wrong decimal scale | Use PIC clause scale |
MISSING_SETSCALE |
No setScale() call | Add final rounding |
MISSING_OPERAND |
Variable omitted | Include all COBOL vars |
WRONG_VARIABLE |
Unknown variable used | Use only defined vars |
CHAINED_CALCULATION |
Intermediate rounding | Accept if drift < 0.05 |
| Statement | Support | Translation |
|---|---|---|
COMPUTE |
Full | Direct BigDecimal expression |
ADD |
Full | Normalized to COMPUTE |
SUBTRACT |
Full | Normalized to COMPUTE |
MULTIPLY |
Full | Normalized to COMPUTE |
DIVIDE |
Full | Normalized to COMPUTE |
MOVE |
Full | Assignment with scale |
IF/ELSE |
Full | Nested conditionals |
PERFORM VARYING |
Full | For-loop translation |
| Function | Template | Java Translation |
|---|---|---|
NUMVAL |
Yes | new BigDecimal(x.trim()) |
NUMVAL-C |
Yes | Currency string parsing |
ABS |
Yes | x.abs() |
INTEGER |
Yes | x.setScale(0, RoundingMode.DOWN) |
ANNUITY |
Yes | Complex annuity formula |
PRESENT-VALUE |
Yes | NPV calculation |
| PIC | Interpretation |
|---|---|
9(5)V99 |
5 integer, 2 decimal |
S9(7)V9(4) |
Signed, 7 integer, 4 decimal |
9(2) |
Integer only (scale=0) |
| COBOL | Java | RoundingMode |
|---|---|---|
ROUNDED |
setScale(n, HALF_UP) |
0.5 rounds up |
| (none) | setScale(n, DOWN) |
Truncation |
| Provider | Model | Configuration |
|---|---|---|
| Mock | Demo | No setup |
| Ollama | deepseek-coder-v2, qwen2.5-coder | Local: ollama serve |
| OpenAI | gpt-4-turbo, gpt-4o | OPENAI_API_KEY |
| Anthropic | claude-3-5-sonnet | ANTHROPIC_API_KEY |
# Ollama (recommended for local)
ollama pull deepseek-coder-v2
verify-cbl translate -s legacy.cbl -o Modern.java --llm ollama
# OpenAI
export OPENAI_API_KEY=sk-...
verify-cbl translate -s legacy.cbl -o Modern.java --llm openai
# Anthropic
export ANTHROPIC_API_KEY=sk-ant-...
verify-cbl translate -s legacy.cbl -o Modern.java --llm anthropic# All tests (421+)
uv run pytest tests/ -v
# With coverage
uv run pytest --cov=src/verify_cbl --cov-report=html
# Specific module
uv run pytest tests/test_translator.py -v| Case | Scenario | Complexity |
|---|---|---|
| case1 | Payroll Complex | Taxes, overtime, bonuses |
| case2 | Insurance Premium | Risk factors, compound interest |
| case3 | Loan Amortization | French amortization, APR |
| case4 | Chained Rounding | Sequential rounding operations |
| case5 | Forex Settlement | Multi-currency spreads |
| case6 | Interest Calculation | ACT/365 daily interest |
| case7 | Tax Withholding | Nested IF-ELSE with thresholds |
| case8 | Multi-Currency FX | Bid/ask spread + commission |
42 benchmark cases covering realistic COBOL calculations.
| Category | Cases | Description |
|---|---|---|
| Simple Arithmetic | 6 | ADD, SUB, chain operations |
| Multiplication | 8 | VAT, interest, discount, commission |
| Division | 6 | Division by 3, 7, 11 (repeating decimals) |
| Complex | 10 | Payroll, insurance, amortization |
| Edge Cases | 12 | Rounding boundaries, precision limits |
uv run python -m benchmarks.runner --quiet
# Expected: 42/42 PASS, 0 FAIL| Metric | Value |
|---|---|
| Test Cases | 42 |
| Accuracy | 100% |
| Recall | 100% |
| Z3 Timeouts | 0 (with HybridEngine) |
# CLI image
docker build -t verify-cbl .
# API server image
docker build --target api -t verify-cbl-api .# API server
docker run -p 8000:8000 verify-cbl-api
# Health check
curl http://localhost:8000/health# API server
docker-compose up api
# With Ollama
docker-compose --profile ollama up api-with-ollamaverify-cbl/
├── src/verify_cbl/
│ ├── agent/ # MigrationAgent, LLM providers, diagnostics
│ ├── engine/ # Z3 solver, fuzzing, hybrid engine
│ ├── ir/ # Expression nodes, constraints, types
│ ├── parsers/ # COBOL/Java parsers, BigDecimal simplifier
│ ├── translator/ # FileTranslator, code generator
│ ├── scanners/ # DATA DIVISION scanner
│ ├── prompts/ # LLM prompt templates (YAML)
│ ├── compare/ # COBOL vs Java comparison
│ ├── cli.py # Typer CLI (15 commands)
│ └── api.py # FastAPI REST server
├── tests/ # 421+ tests
│ ├── industrial_cases/ # 8 real-world scenarios
│ └── cobol_samples/ # 10 COBOL programs
├── benchmarks/ # 42 benchmark cases
├── demo/ # Streamlit web demo
├── Dockerfile # Multi-stage build
└── docker-compose.yml # API + CLI containers
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| POST | /check |
Verify COBOL/Java pair |
| POST | /translate |
Translate COBOL to Java |
| POST | /compare |
Compare files |
| POST | /batch |
Batch verification |
curl -X POST http://localhost:8000/check \
-H "Content-Type: application/json" \
-d '{
"cobol": "COMPUTE WS-TAX ROUNDED = WS-AMOUNT * 0.22.",
"java": "BigDecimal tax = amount.multiply(new BigDecimal(\"0.22\")).setScale(2, RoundingMode.HALF_UP);",
"cobol_vars": "WS-AMOUNT:9(7)V99,WS-TAX:9(6)V99"
}'# config.yaml
engine:
z3_timeout_ms: 2000
fuzzing_iterations: 100000
agent:
max_attempts: 5
chained_drift_tolerance: 0.05from verify_cbl.config import MigrationConfig
config = MigrationConfig.from_yaml("config.yaml")MIT License. See LICENSE.
Alessandro Potenza (ap.alessandro.potenza@gmail.com)