ironwork cobolwork GitHub

ironwork · Roadmap

Features by target release

In a 3,000-program sample, 96.8% of the IBM-valid programs compile, against about 83% at 0.1.1.

0.3.0 closed the constructs the census found refused, 0.4.0 added the semantics library and the LIR, and 0.4.1 exit codes that say who ended a run. Next come a VM with the interpreter kept beside it, jobs, the witnesses cobolwork’s labels wait on, and SQL and the census’s last refusals. 0.9.0 is the release candidate: it freezes the formats and publishes a conformance report, and 1.0 follows it. Targets give the order of the work, not dates, and a feature stays on its target card with a pill naming the release that shipped it. An additional pill marks a feature added after the first roadmap, of 30 September 2026.

0.2.0released

Every feature on this card shipped first in 0.1.2

  • EXEC SQL at run time: programs with embedded SQL run, against PostgreSQL or a recording, with TLS in a separate build.in 0.1.2
  • SORT and MERGE with FASTSRT, Report Writer, object-oriented COBOL, Language Environment services and collating sequences: batch programs that sort, print reports, define classes or call Language Environment run.in 0.1.2
  • ENTRY, ALTER, GO TO DEPENDING ON: older control flow runs, with PERFORM VARYING with AFTER, and DISPLAY WITH NO ADVANCING.in 0.1.2
  • PERFORM ranges as IBM runs them: control that leaves a range and comes back runs as on z/OS, including a GO TO out of a range and back into it.in 0.1.2additional
  • DECLARATIVES: error, debugging and report handlers run. USE AFTER ERROR or EXCEPTION, USE FOR DEBUGGING and USE BEFORE REPORTING.in 0.1.2
  • LINAGE: page-formatted files run, with LINAGE-COUNTER and WRITE AT END-OF-PAGE.in 0.1.2
  • DECIMAL-POINT IS COMMA, SYNCHRONIZED slack bytes, PICTURE P, RENAMES and 88-level FALSE: the data descriptions these clauses write compile and run.in 0.1.2additional
  • Diagnostics with IBM’s severities: a build reads ironwork’s result as it reads IBM’s. Warnings and informational messages sit beside errors, with return codes 0, 4, 8, 12 and 16.in 0.1.2
  • Programs with E-level messages run: a program IBM would run despite errors runs here, and NOCOMPILE(W|E|S) on a CBL card is honoured.in 0.1.2additional
  • compare and compile provenance: a change can be shown to leave every output unchanged. Sealed run evidence, --serve with a CSD-defined region, ironwork ddl, and IBM’s reserved words refused as names shipped with it.in 0.1.2
  • EXEC DLI refused by name: an IMS program is caught when compiled, rather than ending the run.0.2.0 reads and checks EXEC DLI instead; see 0.3.0.in 0.1.2
  • EXIT PERFORM outside an inline PERFORM: refused, as IBM refuses it.in 0.1.2additional

0.3.0released

Every IBM-valid program compiles, and the witnesses cobolwork measures with

  • The intrinsic functions of Enterprise COBOL 6.4 beyond the first 21: a program calling any of them compiles. 61 more compile, SQRT, WHEN-COMPILED, PRESENT-VALUE, the trigonometric and U functions, COMBINED-DATETIME and CONTENT-OF among them, with table arguments given ALL subscripts.in 0.2.0
  • The census re-run at every release: progress towards compiling every IBM-valid program is measured, and each remaining refusal of an IBM-valid construct named.Last run 2026-10-04, at v0.7.0: 1,166 of the 3,000 compile, 65 fewer than at v0.6.0 because 0.7.0 refuses what Enterprise COBOL refuses: a MOVE whose sender is a numeric function, in 64 programs, and an ASSIGN USING.in 0.3.0additional
  • The gaps that census named: the IBM-valid programs it found refused compile.
    • ASSIGN with names after the first, PASSWORD in a SELECT, IS before an abbreviated relation’s operator, FUNCTION ALL INTRINSIC, EXTERNAL and GLOBAL data, INITIALIZE’s phrases, REPLACE, items after an OCCURS DEPENDING ON table, NX and floating-point literals, ALPHABETIC-UPPER and LENGTH OF a table item.
    • Each program the census still refuses was judged again against IBM’s Enterprise COBOL 6.4 documentation: none is valid Enterprise COBOL 6.4, except three uncertain ones with a doubled separator period.
    • The six FUNCTION-ID programs it named are not valid Enterprise COBOL 6.4: each also uses EXIT FUNCTION, a level-02 RETURNING item, a pointer to a user-defined function, or TYPEDEF.
    Limit: no IBM compiler has checked these verdicts.in 0.3.0additional
  • MOVE, ADD and SUBTRACT CORRESPONDING: group-to-group moves and sums compile.in 0.2.0additional
  • Condition-names on a FILLER or a repeated name: each reaches its conditional variable by position.in 0.2.0additional
  • An unqualified paragraph-name: resolved within its own section first, as IBM resolves it.in 0.2.0additional
  • EXEC DLI read and checked when compiled: IMS programs compile, with the DL/I interface block built in as the IMS translator supplies it.Limit: a run still ends at a DL/I command.in 0.2.0additional
  • XML PARSE, XML GENERATE, JSON GENERATE and JSON PARSE: programs that read or write XML and JSON run.Limit: XML PARSE VALIDATING is not run.in 0.2.0
  • IS EXTERNAL and IS GLOBAL data, and USE GLOBAL declaratives: data and handlers shared between programs compile.in 0.3.0additional
  • INITIALIZE … REPLACING and WITH FILLER.in 0.3.0additional
  • PERFORM a subscripted item TIMES, TEST BEFORE without WITH, and procedure-names of digits alone.in 0.2.0additional
  • Abbreviated combined relations, such as AND <= .202 and = ('02' OR '03'): compile with an inherited NOT kept.in 0.2.0additional
  • The forms NIST’s test suite writes and IBM accepts: SELECT clauses with optional words left out, CLOSE REEL, UNIT, NO REWIND and LOCK as IBM’s table gives them, CALL USING without BY, STOP with a literal, and an EVALUATE subject that is a condition-name.In 0.3.0, tools/nist.py runs NIST’s CCVS85 audit routines. At v0.7.0, of the 403 programs run on their own, 376 pass every test, 8 report a failed test, 19 are refused and none abends; 24 subprograms run when called, 27 flagging tests are compiled and not run, and 4 flagging tests give a compile error.in 0.2.0additional
  • A PICTURE ending in an insertion comma or period, as IBM allows.in 0.2.0additional
  • INSPECT … TALLYING over a function’s value, such as FUNCTION REVERSE.in 0.2.0
  • Messages instead of refusals: these compile with a message. A non-COBOL character is accepted with an error, a program with no STOP RUN, GOBACK or EXIT PROGRAM gets a warning, and a declarative section with no paragraph after its USE statement an informational message.in 0.2.0additional
  • EJECT, SKIP and TITLE between data entries, and the REPLACE statement.EJECT, SKIP1 to SKIP3 and TITLE are in 0.2.0.in 0.3.0additional
  • CURRENCY SIGN other than $, SET TO ENTRY, and EXEC CICS HANDLE ABEND PROGRAM.CURRENCY SIGN with a character literal, with or without PICTURE SYMBOL, is in 0.2.0.in 0.3.0additional
  • RECORD DELIMITER, variable-length items after an OCCURS DEPENDING ON table, CLOSE … UNIT, NX literals, floating-point VALUE literals, IS ALPHABETIC-UPPER, and LENGTH OF a table item.RECORD DELIMITER and CLOSE … UNIT are in 0.2.0.in 0.3.0additional
  • DIVIDE … ROUNDED: a rounded quotient matches IBM’s, carried to IBM’s intermediate precision: a ROUNDED receiver counts one more decimal place in the intermediate.in 0.2.0additional
  • An input trace: cobolwork can confirm a finding by a run. --trace-marker records, at each sink, whether a marker put in at a source reached its operand.--trace-statements, which records each start of the statements a file lists, is in 0.3.0.in 0.2.0
  • Coverage reports and a fuzzing harness: a run reports the code it reached, and a fuzzed abend comes back with its input. An SSRANGE failure ends a run with U4038 and IBM’s message id, so a fuzzed overrun is told apart from a construct ironwork does not run.run --coverage and the U4038 ending are in 0.2.0, and ironwork fuzz in 0.3.0. Limit: it fuzzes batch programs only, and keeps coverage only for the runs that abend.in 0.3.0
  • A virtual printer: a program that prints through CALL 'SYSTEM' runs, and a change that stops it printing diverges in compare. Given DD PRINTER, an lp or lpr command appends the files it names, each a DD, to that DD and returns 0, and no command runs on the host.in 0.3.0additional

0.4.0released

The semantics library, the LIR, and the rest of IBM’s options

  • The semantics library in rt: every executor gives the same result, because each behaviour is decided in one place. It covers storage access, MOVE, comparison, editing, arithmetic stores, abends, and every service a program meets.In 0.4.0, storage, MOVE, comparison, editing, STRING, UNSTRING, INSPECT, SET, ACCEPT, DISPLAY, the intrinsic functions, arithmetic, the file verbs, CICS, SQL, Language Environment services, object-oriented COBOL, SORT, MERGE, Report Writer, CALL and the run unit live in rt, and both executors call them.in 0.4.0
  • The LIR: the form compiled code is generated from. It holds resolved places, arithmetic plans, basic blocks with explicit PERFORM exits and typed service calls, lowered from everything the interpreter runs.In 0.4.0, every test program ironwork compiles lowers, 1,482 of 1,482, with items after an OCCURS DEPENDING ON table, INITIALIZE’s new phrases, SET TO ENTRY, EXTERNAL and GLOBAL data and files, FUNCTION-ID and EXEC CICS HANDLE ABEND among them. Limit: generated code is checked against the interpreter only for the programs the VM runs to the end.in 0.4.0
  • The rest of IBM’s options that change results: a program computes under ironwork what it computes under these options. ZONEDATA, INVDATA, ZWB, APOST and QUOTE, CURRENCY, NSYMBOL, DISPSIGN, INTDATE, QUALIFY, INITIAL and VLR.In 0.4.0, VSAMOPENFS takes effect: a VSAM data set left open for output, by a run that ended under TRAP(OFF), opens next time with status 97, or 00 under VSAMOPENFS(SUCC). In 0.5.0, NSYMBOL(DBCS) compiles and runs DBCS items and literals.in 0.2.0additional
  • NUMCHECK, ZONECHECK, PARMCHECK and INITCHECK: invalid data and uninitialised items are caught where IBM’s checks catch them. NUMCHECK and ZONECHECK test each sender a statement reads, PARMCHECK checks a buffer after WORKING-STORAGE around each CALL, and INITCHECK warns at compile time of items that may be used before a value is set. ZON(LAX) tolerates the redefinitions IBM’s examples show, and invalid data no statement changes is reported at compile time.In 0.4.0, the VM runs the checks too.in 0.3.0additional
  • CICS programs as CICS runs them: a CICS program that LINKs, XCTLs, CALLs or handles an abend behaves as it does in a region. HANDLE ABEND PROGRAM, LABEL, CANCEL and RESET act at each logical level across CALL, LINK and XCTL; RETURN, XCTL and STOP RUN in a CALLed or LINKed program end their logical level; and each LINK, XCTL or HANDLE ABEND PROGRAM exit starts a run unit with its own programs, EXTERNAL data and heap.In 0.4.1, LINK and XCTL raise LENGERR, and FUNCTION RANDOM’s sequence and RETURN-CODE are kept per run unit. Limit: an INVOKE in a CICS task abends, since object-oriented COBOL cannot run under CICS.in 0.4.0additional

0.5.0released

The VM and load modules

  • A VM over the LIR: a program runs from its generated code with the interpreter’s results, and run --vm and cics --vm write the same evidence journal and coverage report as the interpreter, which cobolwork’s verifier accepts.In 0.3.0, ironwork run --vm runs a program’s core statements on the VM, and the interpreter stays the default. In 0.4.0, the VM also runs file statements, SORT and MERGE, the Report Writer, JSON and XML, EXEC SQL and EXEC CICS, Language Environment services and the virtual printer, object-oriented COBOL, and the NUMCHECK and PARMCHECK checks, and runs 680 of the 682 lowered test programs with no difference from the interpreter, taint included. In 0.5.0, it runs every statement the interpreter runs, and on cobolwork’s 438 test fixtures, the site’s programs and CardDemo’s 31 programs the two executors give the same journals, coverage, output and files. Limit: the VM is opt-in until 0.9.0: run and cics use the interpreter unless given --vm, and --vm with --serve is refused. It stops a run, naming what it reached, at FUNCTION UUID4 and at FUNCTION RANDOM in a subscript, whose values change on every run.in 0.5.0
  • Both executors in CI: the VM is held to the interpreter. Every test and oracle case runs in each, and both are fuzzed differentially.In 0.3.0, the test harness runs every test program ironwork generates code for on both executors and compares their results. In 0.5.0, CI runs the 384 NIST CCVS85 programs on the VM too and compares them with the interpreter, and ironwork fuzz --differential keeps a smaller input for each way the executors differ.in 0.5.0
  • Load modules: a compiled program is a reproducible binary that static and dynamic CALL load.Writing and reading a module shipped in 0.1.2, and ironwork compile and ironwork dump in 0.2.0. In 0.4.0, ironwork run x.iwm runs a module on the VM, and a CALL finds its callee in NAME.iwm. In 0.5.0, ironwork cics x.iwm runs a module as a task’s first program, a module run writes the journal and coverage report its source run gives, ironwork dump prints the generated code as text with data names and source lines, and a module stays readable across 0.x releases where its format allows. Limit: a module from 0.4.1 or earlier must be compiled again, and a module runs even when its source is newer; the loader does not compare file times.in 0.5.0
  • Times against the interpreter and cobc -O2: speed is measured against targets set before the timing run, on the benchmarks measured before the work began. The VM meets each: table search runs in 0.187 of the interpreter’s time and call-heavy code in 0.184 (target 0.20), packed decimal in 0.294 (target 0.33), and sequential I/O in 0.66 of cobc -O2’s time (target 1.25).Limit: measured on one Apple M5 Pro, with thin margins for table search and call-heavy code.in 0.5.0
  • No silent difference between executors: a program the generated code cannot represent is refused when it is compiled, never run to a different result.In 0.4.1, lowering fails when it misses a range and refuses a FUNCTION argument holding a floating-point expression in an EXEC CICS option, and SEARCH ALL keys, floating-point EXEC CICS options and unsubscripted SQL indicator arrays give the interpreter’s results on the VM.in 0.4.1additional
  • Exit codes that say who set them: a CI step can tell a program’s RETURN-CODE from ironwork refusing or stopping a run.In 0.4.1, run, cics and job pass a RETURN-CODE of 0 to 238 through and keep 239 and above for ironwork: 240 an abend, 241 no program to run, 242 a code-generation refusal, 243 a VM stop, 244 a construct ironwork does not run, 245 an unreadable source, JCL or module, 246 usage and 255 a panic. --exit-code gives cobolwork’s verdict codes instead.in 0.4.1additional
  • GnuCOBOL’s results on request: a migration to GnuCOBOL is checked against the results it will give, with --dialect gnucobol, which gives cobc’s results where ironwork’s assumptions chose otherwise: intermediate arithmetic, DISPLAY of signed and binary items and of numeric literals, ACCEPT at the end of SYSIN, dynamic CALL of an ENTRY name, a shorter EXTERNAL record, and NOINVDATA comparisons. --dialect ibm is the default.in 0.5.0additional
  • Extensions found in real programs: six extensions other compilers accept compile, each with a warning that names it, under --compliance extended: free-form source, level-78 and CONSTANT entries, <>, & between literals, BINARY-SHORT, -LONG and -DOUBLE, and a PROGRAM-ID with no IDENTIFICATION DIVISION. --compliance strict, the default, refuses what Enterprise COBOL refuses.On main, extended also reads GnuCOBOL’s PROCEDURE DIVISION RETURNING OMITTED, with IWX0009-W, and both levels refuse an ACCEPT … FROM a name Enterprise COBOL does not read, such as GnuCOBOL’s COMMAND-LINE, where it was read as SYSIN.in 0.5.0additional
  • Results that departed from IBM’s documented rules: twelve results now follow Enterprise COBOL’s manuals, among them a quotient’s decimal places, FUNCTION MOD’s sign, a LINKAGE item that redefines another, floating-point exponentiation and DISPLAY of national data, each with the page it follows, and seven more NIST CCVS85 programs run clean.in 0.5.0additional

0.6.0released

Jobs

  • A JCL runner: a job runs off the mainframe, step by step, with its data sets. It covers job steps, DD allocation, COND and IF, in-stream data, and the utilities for which cobolwork documents data movement.
    • ironwork job runs steps with DD dispositions, COND and IF/THEN/ELSE.
    • In-stream and cataloged procedures, with symbolics and overrides, and INCLUDE.
    • IEFBR14, IEBGENER, IDCAMS, and SORT, MERGE and COPY as utilities.
    • Generation data groups and backward references read, and DISP=MOD data sets written after what they hold.
    • With --evidence, each step recorded in a hash-chained journal.
    In 0.3.0, a COBOL program gets its step’s PARM as Language Environment passes it, DFSORT’s INCLUDE, OMIT, INREC, OUTREC and OUTFIL run in DFSORT’s order, and the reader takes what real jobs carry, such as &SYSUID and SYSIN data with no DD before it. Of the 1,561 JCL files in two 500-repository corpora, ironwork job refuses 853, against 1,549 at 89ef754, mostly files that are not jobs, cataloged procedures the run was not given, and programs ironwork does not run. In 0.5.0, DFSORT’s IFTHEN, edit masks, TO= conversions and SYMNAMES run, and IDCAMS runs LISTCAT, PRINT, DEFINE ALTERNATEINDEX and PATH, and BLDINDEX. A DD naming a path reads the base cluster in alternate-key order. Limit: a program’s ALTERNATE RECORD KEY reads use the base cluster as it stands; DFSORT’s FINDREP, PARSE, arithmetic, dates and SEQNUM, and IBM programs beyond those listed, are refused by name.in 0.5.0
  • Migration equivalence for a job: a migrated job’s results are checked against production’s. It runs against recorded SQL and files, and its data sets and step outcomes are compared with production’s.in 0.1.2
  • Fuzzing jobs, CICS tasks and PARM: abends are found in jobs and CICS transactions, not only in batch programs, with a hang and an input-chosen program load (S806) kept as findings under strict conditions.In 0.5.0, ironwork fuzz --interface runs a subprogram as a caller would, run --argument gives a program its USING items, and a hang is kept only for a loop the empty input does not run. On main, an interface run gives the subprogram’s files data sets as the main fuzz does, and a CALL finds a program in the -L libraries by its PROGRAM-ID as well as by its file name.in 0.4.0additional

0.7.0released

What cobolwork’s labels wait on

  • Input followed byte by byte: a run says whether input could be in each operation’s operand, even after arithmetic changes it. cobolwork refutes a finding only on this evidence (--trace-input).Limit: SORT and MERGE, the Report Writer, XML and JSON, object-oriented COBOL, calls through pointers and Language Environment services are not followed yet; after the first of these, a sink cannot be cleared.in 0.4.0additional
  • Files named at run time: a file assigned from a data item is run and traced. Under --compliance extended, ASSIGN TO an item’s name, ASSIGN DYNAMIC and ASSIGN USING take the DD name from the item’s value at each OPEN, and the input trace records a dynamic-file-path sink at the SELECT.Limit: the value names a DD, never a host path: a path, or a name the run was not given, fails the OPEN with status 35, and --compliance strict refuses the form, as Enterprise COBOL does.in 0.7.0additional
  • Connection targets: a database location taken from a data item is recorded. EXEC SQL CONNECT TO and SET CONNECTION naming a host variable give the input trace a connection-target sink with the location’s value, at the statement’s line.Limit: the statement is still refused by name once traced, and an MQ queue manager is not traced, since ironwork does not run MQ.in 0.5.0additional
  • Coverage for every fuzz run: each fuzz run keeps the statements it reached, not only the runs that abend. fuzz, fuzz --job, fuzz --cics and fuzz --interface write coverage/runs.json, with how many runs reached each statement and paragraph.in 0.5.0additional
  • Numeric functions at IBM’s precision: an expression holding a numeric function gives Enterprise COBOL’s digits. MAX, MIN, RANGE, REM and SUM keep their arguments’ decimal places and count them in an expression’s intermediate precision, and INTEGER, INTEGER-PART and MOD have IBM’s digits.In 0.7.0, a MOVE whose sender is a numeric function is refused under --compliance strict, as Enterprise COBOL refuses it, and moved at IBM’s precision with a warning under extended.in 0.7.0additional

0.8.0started

SQL and the remaining IBM-valid constructs

  • Dynamic SQL: programs that prepare SQL at run time run, with PREPARE, EXECUTE, EXECUTE IMMEDIATE, DESCRIBE and the SQLDA, against PostgreSQL or a recording.In 0.7.0, PREPARE, EXECUTE, EXECUTE IMMEDIATE and cursors declared for a prepared statement run under Db2 13’s rules and SQLCODEs. On main, DESCRIBE, PREPARE … INTO and USING DESCRIPTOR fill and read the SQLDA as Db2 13 for z/OS lays it out.on main
  • Multi-row FETCH and INSERT: rowset cursors run, and a stored procedure’s CALL replays from a recording.
  • DBCS data under NSYMBOL(DBCS): programs with DBCS items and literals compile and run. PICTURE G and N, USAGE DISPLAY-1 and GRAPHIC host variables run under the eleven mixed CCSIDs of the Programming Guide’s Table 47, on both executors.in 0.5.0additional
  • The census’s last refusals: the IBM-valid programs it still refuses compile,In 0.5.0, a LINKAGE item that redefines another and floating-point exponentiation run as IBM documents them. On main, UPSI switches are tested and set as Enterprise COBOL does, and COPY REPLACING takes an identifier with its qualifiers and subscripts.startedadditional
  • XML PARSE VALIDATING refused by name: a program that validates XML is told why it does not run, It is refused as IWR0001-S at VALIDATING, naming its operand, until IBM’s Optimized Schema Representation can be read. An XML-SCHEMA clause alone still runs.on mainadditional
  • One assumption switched at a time: a difference from a GnuCOBOL build is traced to the one assumption that gives it, with --assume ID=VALUE: C101, C14, C95, C15, C51, C180 and C262 each take ibm or gnucobol, and C101 also off. It is repeatable and wins over --dialect.on mainadditional

0.9.0started

The release candidate: a conformance statement and frozen formats

  • A conformance report with each release: how closely ironwork follows IBM is published with the evidence for each part, from NIST’s CCVS85 suite, the census, Hercules, Db2 for Linux and the differential between executors, with every behaviour ironwork chose rather than observed listed.Limit: no IBM compiler has witnessed it, and the report says so.additional
  • IBM’s own output as evidence: compile listings IBM’s compiler produced, found in public repositories, check ironwork’s messages, options and storage layouts.additional
  • A message id on every diagnostic: a build reads ironwork’s results by id, and in JSON with --diagnostics json.On main, a message takes its id from one catalogue, which holds ten so far, IWX0001 to IWX0009 and IWR0001, and --diagnostics json on check, run, cics and compile writes each message as a JSON object a line.startedadditional
  • Formats frozen: the run journal, SQL recordings, fuzz manifests and load modules carry a version, and change only at a major release.additional
  • The VM as default: programs run on the VM, with --interpret kept, measured against a performance target set before the VM was timed.
  • Untrusted programs contained: a program under test touches only the files its DDs name and starts no shell, within stated step, time and memory limits.additional
  • Tested where it ships: every release target is tested in CI on Linux, macOS and Windows, and each release file is attested and rebuilt to the same bytes.additional

1.0.0planned

Stable

  • No silent differences: the interpreter and the VM agree on every fuzzed program, over a stated number of CPU-hours.additional
  • A stated scope: what conformance means, what is refused by name, and what waits on IBM’s compiler is written down.additional

Unscheduleddecision

After 1.0, or waiting on a decision or the goldens

  • Enterprise COBOL goldens: the model’s predictions scored against IBM’s compiler. A route to IBM’s compiler is chosen, and the first goldens wait on its licence and a host.Limit: until they exist, the oracle’s predictions are unscored, and how PERFORM ranges and SORT procedures exit is unsettled.
  • A verifier for high-assurance builds: each compilation checked against its source by a separate tool, after the LIR and the goldens (verifier.md).
  • XML PARSE VALIDATING, which waits on reading IBM’s Optimized Schema Representation, or on the goldens.additional
  • LOBs and DRDA, CICS Tier 4, and MQ.
  • An IMS runtime, decided together with cobolwork reading IMS.
  • Native code from the LIR, only if the VM misses its performance target.