Download PDF

Whitepaper  ·  July 2026

The build was green.
The system was wrong.

The COBOL Confidence Trap. How generative AI constructs a logically self-consistent but factually incorrect modernization — tests pass, demos run, CI is green — and why deterministic transpilation is the only foundation that makes those green results mean anything.

Prepared by Ionate, Inc. · Field Analysis · Public — July 2026
01 — Executive Summary

The most dangerous modernization is the one that looks complete.

When a team asks a generative AI system to modernize a COBOL program without access to the transpiled source output, the AI does exactly what it was designed to do: it produces coherent, plausible code. It infers a database schema, writes controllers and repositories, generates an end-to-end test suite. Every build passes. Every test shows green. The demo runs from start to finish without error.

None of it reflects the behavior the COBOL programs actually implement.

This is not theoretical. During a COBOL modernization program at a major Brazilian Municipal Authority — a public-sector mission-critical tax management system — a manual review of actual COBOL source files revealed that the AI's inferred implementation was wrong in every data-critical dimension: wrong column names that would silently return null in production, missing validity filters that would surface decades of retired records as active, absent cross-table joins that would strip tax classification data from every screen, and no referential guard against deleting pricing records tied to tens of thousands of active tax bills.

The AI's end-to-end test suite reported 43 tests passing. It was correct. All 43 tests passed. And all 43 tests were validating a system that bore no resemblance to the COBOL's actual business rules.

This document explains the mechanism, presents the documented evidence, models the production consequences, and establishes what an organization must demand before any AI-generated COBOL modernization is accepted as correct.

"The AI did not hallucinate randomly. It built a logically consistent, self-validating implementation of a system it had invented. By every visible metric — build, tests, demo — it was indistinguishable from a correct modernization. The only way to see the difference was to read the COBOL."

5Logic failures missed by 43 passing tests
43/43Tests passing while testing the wrong system
4Production failure scenarios with regulatory consequences
28yrOf soft-deleted records the AI's schema couldn't model
ZeroLogic errors in the deterministic transpiler pilot scope
02 — The Closed Loop: How AI Validates Itself

A system that writes its own tests cannot fail them.

In a correctly executed modernization, every artifact in the delivery chain derives its correctness from the COBOL source. The chain is:

COBOL source → Transpiled Java → Repository SQL → Controller → BFF → Angular → Tests

A test that fails in this chain means the transpiled behavior diverged from what the COBOL encoded. The test is meaningful because its reference point is the source of truth — the COBOL program — not the modernized code itself.

When generative AI is asked to modernize COBOL programs without access to transpiler output, it produces a different chain entirely:

AI inference → Inferred schema → Inferred controller → Inferred tests → PASS ✓

From the outside, this chain is indistinguishable from the correct one. The CI build is green. The smoke test dashboard shows all rows passing. The Cypress report confirms 43/43. But the chain has no COBOL in it. It is a self-consistent model of a system the AI invented — internally coherent, externally wrong.

This is the COBOL Confidence Trap: the combination of a plausible-looking implementation, a passing test suite, and the organizational assumption that passing tests imply correct business logic. Each element is defensible in isolation. Together they produce a system that will fail the moment it encounters real production data.

A test suite generated by the same agent that generated the implementation cannot test whether the implementation is correct. It can only test whether the implementation is internally consistent. Internal consistency is not business correctness.

03 — Five Documented Failures

Not theoretical risks. Documented differences.

The following five failures are not hypothetical. They are documented differences between the AI's inferred implementation and what the COBOL EXEC SQL statements actually show, discovered only after reading the original source files. Each was invisible to the test suite. Each would have surfaced in production.

Failure 1: Wrong Column Name in the Primary Business Table

The AI inferred a description column for the service type master table and named it based on what seemed logical given the table's purpose. The actual COBOL program, however, uses a distinct column name defined decades ago when the system was built:

What AI inferredWhat COBOL showsProduction impact
svc_description_text VARCHAR(60) SVC_DESCRIPTION CHAR(?) Different name, different type — any field written through the AI's controller is stored in a non-existent column

The COBOL inquiry program declares:

SELECT SERVICE_TYPE_ID, FUND_CODE, TAX_CODE_ID, ..., SVC_DESCRIPTION, ...
FROM SERVICE_TYPE_MASTER T1
WHERE SERVICE_TYPE_ID = :param AND DEACTIVATION_DATE IS NULL

The column is SVC_DESCRIPTION. The AI named it svc_description_text. Every service description written through the AI's controller would be stored in a column that does not exist in the real DB2 system. On the first connection to production data, the description field would return null for every record. Operators would see blank description fields. Root cause: six weeks of investigation to discover the column name was wrong from the start.

Failure 2: Missing Validity Date Logic — Active vs. Retired Records

The AI's query for the service type inquiry was:

-- AI's implementation:
SELECT * FROM service_type_master WHERE service_type_id = ?

What the COBOL program actually executes:

-- COBOL's actual logic:
SELECT ... FROM SERVICE_TYPE_MASTER T1
WHERE SERVICE_TYPE_ID = :param AND DEACTIVATION_DATE IS NULL

The COBOL system uses DEACTIVATION_DATE IS NULL to distinguish active service types from those retired from use. Records are never physically deleted from the table — they are soft-closed by setting an end date when the service type is legally retired. This is the standard pattern for systems that must maintain an audit trail of historical tax classification.

The AI's implementation, having no visibility into this business rule, simply returns all records regardless of status. The table had accumulated over 28 years of operational history. A search that should return a few hundred active service types would instead return thousands of results — including types that had been legally retired years or decades earlier.

Production impact: Tax officers create new bills against service types that have been legally retired. Tax collection errors. Potential regulatory non-compliance on audited transactions.

Failure 3: Missing Entire Table — Tax Code Integration

The AI's schema for the service type table had no tax code column. The COBOL programs, however, join to a tax code registry table on every inquiry and alteration operation:

SELECT TAX_CODE_NAME FROM TAX_CODE_REGISTRY T1
WHERE TAX_CODE_ID = :cod_tax_param

Every service type in the real system is linked to a tax classification code from a master registry. The COBOL inquiry screen shows the classification name alongside the service type, and operators confirm it before making any changes. It is a visual audit control built into the workflow.

The AI's implementation has no TAX_CODE_ID column in the model, no join to the tax code registry, and no classification name in the API response. The Angular screen never shows the tax classification field.

Production impact: Operators alter service types without confirming which tax classification applies. Misclassified revenue is discovered in the next municipal audit. The error may affect multiple fiscal periods before detection.

Failure 4: Missing Referential Guard Against Issued Bills

The AI's value record deletion (the operation that removes a service pricing entry) executes the delete directly:

// AI's implementation — no referential check:
db.update("DELETE FROM service_value_table WHERE ...", params);

The COBOL program, before executing any deletion, checks whether any issued bills reference the pricing entry being deleted:

SELECT COUNT(*) FROM ISSUED_BILLS T1
WHERE SERVICE_TYPE_ID = :param AND BILL_ISSUE_DATE >= :cutoff_date

If the count is greater than zero, the COBOL returns an error: DELETION NOT PERMITTED — ACTIVE BILLS EXIST. This guard exists because the municipal bill emission system calculates bill amounts by referencing the active pricing entry. A deleted pricing record leaves those bills pointing to a non-existent entry.

Production impact: A fiscal administrator deletes a pricing record that has tens of thousands of active bills citing it. Those bills can no longer calculate their amounts. The tax collection process stalls for that service category. The audit trail is broken. Potential legal liability for the municipality.

Failure 5: Schema Column Count Discrepancy

The AI's inferred schema for the service type master table contained 17 columns — the ones it could derive from the table name, likely usage, and the few visible field references in the interface specifications. The actual COBOL programs reveal a minimum of 19 columns, including fields the AI never knew to look for:

TAX_CODE_ID, FINE_EXEMPT_IND, VALUE_EXEMPT_IND, CONSENSUS_VALUE_IND, CALC_BASIS_CODE, ACTIVATION_DATE, DEACTIVATION_DATE, LEGAL_ACCRUAL_IND, DATE_TREATMENT_CODE, SUPPLEMENTAL_INFO_IND, SUPPLEMENTAL_INFO_TYPE, DECLARATION_TYPE_CODE, TAX_RECEIVABLE_IND

Any INSERT via the AI's controller into the real DB2 database would fail on column constraints. Any SELECT from the real DB2 would silently return null for columns the AI never defined. The AI's schema is not just incomplete — it is structurally incompatible with the production database it was supposed to target.

04 — Why Every Test Passed

The question that matters: how did 43 tests pass?

This is the critical question. The end-to-end test suite ran 43 tests and all passed. The CI build was green. The development team had every reasonable signal that the system was working. How?

The Closed Validation Circle

AI wrote the schema
  ↓
AI wrote the controllers (using that schema)
  ↓
AI wrote the Cypress tests (asserting what the controllers return)
  ↓
Tests pass: controllers return exactly what the schema contains

The tests never verify:

  • Whether svc_description_text is the right column name in the production DB2 system
  • Whether service types with a non-null deactivation date should be hidden from operators
  • Whether the tax code registry should be joined for display
  • Whether the issued bills table should be checked before deleting a pricing record

The tests only verify that a POST to the service endpoint returns 201, that a GET returns 200 with the expected message code, that a delete operation returns 409 when a known foreign key constraint is violated. All of this is true in the AI's world. None of it reflects whether the business rules match the COBOL.

The Assert-What-You-Control Problem

// TC-S04: INQUIRY — reads existing service type
cy.get('[data-cy="service-type-id"]').type('1001');
cy.get('[data-cy="btn-inquire"]').click();
cy.get('.p-toast-message-success').should('contain', 'MSG-S004A');

This test asserts that a success toast appears with code MSG-S004A. The AI controls what that code maps to in the message catalog, when the controller sends it (always on a successful GET), and what the toast component renders. The test does not assert that the correct column was returned, that deactivated service types were filtered, that the tax classification was populated, or that the description length matches the real COBOL's character type definition.

A test that says "the inquiry succeeded" is not a test of what was inquired. It is a test that the inquiry HTTP flow completed without throwing an exception.

Seed Data Designed to Pass

The AI wrote the seed data. The tests used that seed data. A record inserted with svc_description_text = 'MUNICIPAL PROPERTY LEASE' is retrieved with svc_description_text = 'MUNICIPAL PROPERTY LEASE'. Consistent. Wrong. Passing.

The seed data contained exactly the fields the AI's schema defined. When the test reads record 1001, it reads back exactly what was seeded — through the AI's incorrect schema. The test passes because the AI controlled both the data and the assertion. The real DB2 table was never involved.

You cannot test for what you don't know you're missing. The AI that builds the schema, the controller, the seed data, and the assertions has no mechanism for discovering its own blind spots. Each component confirms the others. The system is self-consistent and wrong.

05 — Four Production Failure Scenarios

What happens when the system meets reality.

The failures in Section 3 are latent. They exist in the code but have not yet caused visible damage. The following scenarios model what happens at each stage of deployment — pre-production, user acceptance, and production — when the AI-inferred system encounters real data.

Scenario A: First Connection to Real Production Data

The modernized system is connected to the real DB2 database for the first time in the pre-production environment.

The system's controller tries to map the DB2 response to its inferred model. The real database returns SVC_DESCRIPTION; the model reads svc_description_text. Result: null. The real database includes TAX_CODE_ID; the model has no such field. Result: silently ignored.

The API returns a response with null description. The Angular form renders blank service type descriptions on every record. Operators report: "The description field is always empty." Engineering begins a root-cause investigation. Six weeks later: the column name has been wrong since the first day of development.

What the test suite reported: 43/43 PASSING.

Scenario B: The Retired Record Flood

The production database contains a complete history of service types: active records and retired ones, accumulated over more than two decades of operations. The COBOL system has always filtered retired records with DEACTIVATION_DATE IS NULL. Operators have never seen retired records in the inquiry screen because the COBOL never showed them.

The AI's system has no deactivation date column in its schema and no filter in its query. When the full production table is loaded, the inquiry screen returns 2,847 results where there should be 312 active ones. The interface is unusable. Tax officers, unable to find the correct active service type, begin creating bills against types that were legally retired years earlier.

What the test suite reported: INQUIRY WORKING, MSG-S004A RETURNED.

Scenario C: Bill Deletion Destroys the Audit Trail

A fiscal administrator decides to update a pricing record for a service type. The correct workflow — per the COBOL — is to delete the old pricing entry and insert a new one. The COBOL program, before executing the deletion, checks whether any issued bills reference that pricing entry. If they do, it blocks the deletion and returns: DELETION NOT PERMITTED — ACTIVE BILLS EXIST.

The AI's implementation has no such check. The delete executes. Tens of thousands of active bills now reference a pricing entry that no longer exists. The municipal bill emission system, which calculates bill amounts by referencing the active pricing entry, cannot complete its calculations for that service category. Bills cannot be issued. The municipality cannot collect taxes for this category until the data is manually reconstructed.

What the test suite reported: INCLUSION PASSING, INQUIRY PASSING.

Scenario D: Silent Tax Misclassification

The municipal tax system uses the tax code field to route each service type to the correct accounting ledger. The COBOL inquiry screen shows the tax classification name alongside each service type entry, and operators confirm it visually before making any changes. It is a deliberate workflow control that has prevented misclassification for decades.

The AI's Angular screen has no tax classification field. The API response contains no tax code. An operator who would normally see the classification — and who would stop to confirm it before altering a service type — sees nothing. They alter the service type, assuming it falls into the default category. It does not. The misclassified revenue is discovered in the next municipal audit. The affected fiscal period may already be closed.

What the test suite reported: SERVICE ALTER WORKING, MSG-S002A RETURNED.

06 — What AI Can and Cannot Infer About COBOL

The boundary between inference and ground truth.

Understanding where generative AI fails in COBOL modernization requires understanding what COBOL actually encodes — and how much of that encoding is structurally inaccessible to inference.

What AI Can Infer Correctly

  • Structural relationships — foreign key chains, primary key composition, table names from DDL or interface specifications
  • Standard CRUD patterns — INSERT, SELECT, UPDATE, DELETE scaffolding for common operations
  • Screen field counts — from CICS map analysis or screen layout documents
  • Standard audit columns — creation timestamp, last-modified user, last-modified timestamp
  • Angular component scaffolding, BFF routing, and test structure — the above-the-data-layer artifacts that do not depend on exact business rule knowledge

What AI Cannot Infer Without the COBOL

  • Exact column names — COBOL programs use specific names that were defined by the original system architects, often decades ago. These names frequently differ from what seems logical based on the table's purpose or the interface field label.
  • Validity and lifecycle patterns — soft-delete conventions, activation date semantics, and status filtering are business decisions encoded in COBOL conditionals. There is no structural indicator that tells AI which tables use physical deletion vs. soft-close vs. status flags.
  • Cross-table lookups for display — which subsidiary tables are joined for display-only fields is entirely a function of what each EXEC SQL statement joins. Without reading the EXEC SQL, there is no basis for knowing which joins exist.
  • Referential guards against operational tables — a deletion guard against an issued-bills table is a business rule, not a database constraint. It exists in COBOL conditional logic and is invisible to schema inference.
  • Business rules encoded in COBOL conditionals — a COBOL program may have dozens of lines of conditional logic between a SELECT and the response it sends to the screen. Every branch represents a business decision. AI inference produces none of those branches.
  • Character type semantics — the distinction between CHAR(N) and VARCHAR(N) in DB2 COBOL systems affects storage, comparison, and padding behavior. Inferring the wrong type is a silent correctness error.

Why Sophisticated AI Models Make This Worse, Not Better

A common response to this analysis is that more capable AI models will solve the problem — that sufficiently large training sets and longer context windows will eventually allow AI to infer COBOL business rules correctly. This optimism misunderstands the nature of the failure.

The failures documented in Section 3 are not failures of capability. They are failures of information. No model — regardless of capability — can correctly infer the name of a column that was defined in 1994 by a systems architect whose decision was never documented outside the COBOL program itself. No model can infer that a particular table uses soft-close semantics without reading the EXEC SQL statements that implement those semantics. More capable inference produces more plausible wrong answers, not correct ones.

A more capable model produces a more convincing wrong answer. The COBOL Confidence Trap is at its most dangerous when the AI is sophisticated enough that its inferences look authoritative. The plausibility of the output is inversely correlated with the team's incentive to verify it.

07 — Ground Truth First: The Transpiler as Foundation

The pilot that worked — and why it worked.

The same modernization program that produced the five failures in Section 3 also produced a pilot scope that worked correctly. The difference was not team capability, tool choice, or test coverage. It was the presence of transpiler output.

Why the Pilot Was Correct

The pilot scope — a set of accounting fund inquiry and management programs — was executed with Ionate transpiler output available from the start:

  1. The Ionate transpiler ran on the real COBOL source programs
  2. The transpiler extracted the exact SQL from every EXEC SQL statement in the source
  3. The resulting repository classes contain verbatim SQL — not inferred SQL, not approximated SQL, but the exact text the COBOL would have sent to DB2
  4. The controllers use those repositories — they execute exactly what the COBOL would execute
  5. The end-to-end tests validate the behavior of that exact SQL

The pilot tests pass. And they are correct. The new scope tests also pass. And they are wrong. The distinction is the chain of derivation.

What the Transpiler Provides That Inference Cannot

// From SvcFundRepository — verbatim SQL extracted from pilot program SAF-02:
@Query(value =
    "SELECT FUND_CODE, ACCOUNTING_NAME FROM ACCOUNTING_FUND_MASTER T1 "
  + "WHERE FUND_CODE = :cod_fund_param",
    nativeQuery = true)
List<Map<String, Object>> saf02_fund_inquiry_query(
    @Param("cod_fund_param") BigDecimal fundCode);

This SQL was not written by a human or by an AI. It was mechanically extracted from the COBOL's embedded SQL by the transpiler. It is the ground truth. Any test that passes against this SQL is testing real COBOL behavior.

Compare to the AI's inferred version for the service type scope:

// AI's ServiceController — inferred, wrong:
db.queryForList(
    "SELECT * FROM service_type_master WHERE service_type_id = ?",
    serviceTypeId);
// Missing: DEACTIVATION_DATE IS NULL
// Wrong column accessed: svc_description_text (does not exist in real DB2)
// Missing: TAX_CODE_ID for TAX_CODE_REGISTRY join
// Missing: COUNT(*) from ISSUED_BILLS before value record deletion

The Required Gate: Transpilation Before Testing

COBOL sources
    ↓
Ionate Transpiler
    ↓
Repository classes (exact SQL — verbatim from COBOL EXEC SQL)
    ↓  ◀ GATE: build must pass before E2E proceeds
Transpiled program classes (full COBOL logic)
    ↓
Controller using verified repositories
    ↓
BFF and Angular (can be AI-generated against correct interfaces)
    ↓
E2E Tests (can be AI-generated against correct behavior)
    ↓
43/43 GREEN means something real

Without the transpiler gate, the chain above is replaced by the closed loop described in Section 2 — where 43/43 green means only that the AI's implementation is self-consistent.

The Division of Labor That Works

Generative AI is not useless in this workflow. It is mispositioned. AI is genuinely useful for generating Angular component scaffolding, BFF routing boilerplate, Cypress test structure, and API documentation — the above-the-data-layer artifacts that do not require exact knowledge of COBOL business rules. AI that is given transpiler-correct repositories and controllers can generate correct tests, because the interfaces are now correct.

The failure is not in using AI. The failure is in using AI to generate the one component — the data access layer — that only the COBOL can correctly define.

08 — What Organizations Must Demand

The minimum standard for any COBOL modernization.

The COBOL Confidence Trap is avoidable. It requires no organizational heroics, no special process, and no unusual technical capability. It requires one disciplined structural rule: no system's data access layer is accepted as correct unless it was derived from the COBOL source, not inferred from the system's interfaces.

For procurement and vendor evaluation

Demand This

Transpiler-Derived SQL

Require that all repository-layer SQL be demonstrably extracted from COBOL EXEC SQL statements, not authored by AI inference or human approximation. Ask vendors to show you a diff between the COBOL source SQL and the generated repository SQL. If they cannot produce that diff, the derivation is not traceable.

Demand This

Independent Test Corpus

Require that the test corpus used to validate the modernized system was derived from the legacy source semantics — not generated by the same tool that generated the implementation. A test corpus that was created by the modernization tool is a closed-loop validator. It cannot detect the tool's own blind spots.

Demand This

Transpilation Gate Before E2E

Require that no end-to-end test run is accepted as evidence of correctness unless the transpiled repository classes are present and passing. A passing E2E suite against AI-inferred repositories is evidence of internal consistency, not business correctness. These are not the same thing.

Demand This

Inferred vs. Extracted Labeling

Require that every generated artifact be explicitly labeled as either "inferred" (AI-derived without COBOL source verification) or "extracted" (mechanically derived from COBOL source). The two categories carry fundamentally different levels of trust and different requirements for validation before production promotion.

For program governance

Organizations running AI-assisted COBOL modernization programs should treat the transpiler gate as a non-negotiable release criterion, not a nice-to-have. Specifically:

  1. Do not promote any scope to shared environments until transpiled repository classes are available and integrated. AI-scaffolded repositories are development scaffolding, not production candidates.
  2. Treat a passing CI suite without transpiler integration as "the scaffold compiles and is self-consistent" — not as "the system implements correct business logic." The distinction must be explicit in program status reporting.
  3. When transpiled repositories become available, perform an explicit diff against any AI-inferred repositories. Every difference in that diff is a latent defect the AI introduced. Document it, test it, resolve it before the scope is considered closed.
  4. Re-run E2E test suites with transpiled repositories connected to a production-mirror database before any user acceptance testing. The tests that fail in this re-run are the ones that were silently wrong all along.

The single pattern to prohibit

There is one pattern that concentrates all of the risk described in this paper into a single failure mode:

AI generates schema + implementation + tests → tests pass → team believes system is correct → system is promoted to production → production has wrong business rules running silently for months, until a manual audit, a regulator's inquiry, or a reconciliation discrepancy surfaces the damage.

This pattern is not hypothetical. It happened. The only reason the failures in Section 3 were caught before production was that COBOL sources were available for manual review. In programs where source files are not fully accessible — binary-only deployments, missing copybooks, undocumented EXEC SQL — the same pattern produces errors that are undetectable until production reveals them.

The right question for every scope

For every program scope entering modernization, ask one question before accepting any test result as evidence of correctness:

Was the data access layer derived from the COBOL source — or did someone infer it?

If the answer is "inferred," the passing test suite proves internal consistency. Nothing more. The scope is not testably correct until the derivation chain runs from COBOL to repository to test — without inference filling any of the gaps.

Scenario Data layer source What passing tests prove
Transpiler output available Extracted from COBOL EXEC SQL Business logic correctness
AI-inferred, COBOL reviewed Inferred, then verified against source Correctness of verified portions
AI-inferred, no COBOL review Inferred only Internal consistency only

How much of your modernization program is inferred?

Ionate's field architects will review your current AI-assisted modernization scope and identify which components are transpiler-derived versus inferred — at no cost and with no commitment. The assessment is yours regardless of next steps.