Rules hide in the code
Overdraft limits, rounding modes, status codes that downstream jobs depend on. Decades of decisions live in the code and nowhere else.
Pre-launch · Accepting design partners
agoralb is building an AI agent that reads legacy COBOL, Java and .NET systems, surfaces the business rules buried in them, and produces modern code with tests that show it still behaves the same. Your engineers review and approve every change.
The problem
Overdraft limits, rounding modes, status codes that downstream jobs depend on. Decades of decisions live in the code and nowhere else.
Manual migrations are slow and costly. Line-by-line translation tools produce code that compiles but that nobody wants to maintain.
Most migrations find behavior differences in production. The cutover is where the risk lives, so that is the part we focus on.
How it works
An agent loop rather than a single prompt: it reads code, runs tools, executes tests, and revises its work based on what fails.
Parse the repository into programs, copybooks, call graphs and data flows, so the agent works from structure instead of raw text.
Write every business rule in plain language, linked to the exact source lines. Domain experts confirm or correct each rule before translation starts.
Produce idiomatic code in the target stack that keeps data semantics exact: fixed-point stays fixed-point, and padded codes stay padded at the boundaries.
Generate characterization tests and run them against both the legacy and the modern code. Any difference is flagged for review and never silently "fixed".
Example
A hand-prepared example of the output we are building toward. This is a static illustration, not a live run of the product.
IDENTIFICATION DIVISION.
PROGRAM-ID. POST-DEBIT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-BALANCE PIC S9(9)V99 COMP-3.
01 WS-AMOUNT PIC S9(9)V99 COMP-3.
01 WS-OVERDRAFT PIC S9(9)V99 COMP-3 VALUE 500.00.
01 WS-STATUS PIC X(3).
PROCEDURE DIVISION.
POST-DEBIT-PARA.
IF WS-AMOUNT > WS-BALANCE + WS-OVERDRAFT
MOVE 'NSF' TO WS-STATUS
ELSE
SUBTRACT WS-AMOUNT FROM WS-BALANCE
MOVE 'OK ' TO WS-STATUS
END-IF.
GOBACK.
NSF when the amount exceeds the balance plus a fixed overdraft of 500.00. Source: lines 7, 11.
Review: the limit is hard-coded. Should it be configurable per account?S9(9)V99, max 999,999,999.99). They must not become floating-point. Source: lines 5–7.WS-STATUS is 3 characters: 'NSF' or 'OK ' (with a trailing space). Downstream consumers may compare the padded value. Source: lines 8, 12, 15.
Review: confirm the downstream consumers before changing the format.package ledger
import "errors"
// Cents is signed fixed-point money with 2 implied decimals,
// matching PIC S9(9)V99 in POST-DEBIT. (R2)
type Cents int64
// OverdraftLimit mirrors WS-OVERDRAFT. Flagged for review:
// candidate for per-account configuration. (R1)
const OverdraftLimit Cents = 500_00
// ErrNSF maps to legacy status 'NSF'. The API adapter emits
// the padded 3-char codes 'NSF' / 'OK '. (R3)
var ErrNSF = errors.New("NSF")
// PostDebit mirrors POST-DEBIT-PARA. (R1, R2)
func PostDebit(balance, amount Cents) (Cents, error) {
if amount > balance+OverdraftLimit {
return balance, ErrNSF
}
return balance - amount, nil
}
// Characterization cases: each one was run against the legacy
// program and its observed output is recorded here as the oracle.
func TestPostDebit_MatchesLegacy(t *testing.T) {
cases := []struct {
name string
balance, amount Cents
want Cents
wantErr error
}{
{"within balance", 1000_00, 250_00, 750_00, nil},
{"exactly at limit", 100_00, 600_00, -500_00, nil},
{"one cent over limit", 100_00, 600_01, 100_00, ErrNSF},
}
for _, tc := range cases {
got, err := PostDebit(tc.balance, tc.amount)
if got != tc.want || !errors.Is(err, tc.wantErr) {
t.Errorf("%s: got (%d, %v), want (%d, %v)",
tc.name, got, err, tc.want, tc.wantErr)
}
}
}
Principles
Every behavior difference between the legacy and the modern code is flagged for review. The agent never "fixes" a bug on its own. Sometimes the bug is the contract.
Every rule and every generated function links back to the source lines it came from, so reviewers can audit the reasoning instead of trusting it.
We use Anthropic's commercial API, which does not train models on customer inputs by default. Retention terms are agreed with each partner in writing.
Output arrives as reviewable pull requests in your repository. Nothing merges or deploys without your team's approval.
Why Claude
We chose Claude for the three things this problem depends on most.
Design partner program
We are working with a small number of teams that have real legacy systems and a real reason to move them.
FAQ
Not yet. We are pre-launch and building with design partners. The example on this page is hand-prepared to show the output we are aiming for.
No. The model is one component. Parsing, rule tracking, test generation, and running the legacy and modern code side by side are deterministic tooling that the agent drives and checks itself against.
COBOL to Java or Go, and older Java and .NET Framework to their modern versions. Partner demand decides what comes next.
We are designing for deployment inside your cloud account. Fully air-gapped environments are on the roadmap. Tell us your constraints in the form.
Design partners pay nothing during the program. Launch pricing will be based on scope, and partners get preferential terms.
That is exactly the one we want to see.