> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metaphi.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Run a batch job

> Give a job input data sets, sort and total them, check the output, and run several jobs as a chain.

This guide builds a two-step job: SORT orders an input data set, then a COBOL program totals it. Needs `harwell:runs:write`.

## 1. The program

```text theme={"dark"}
payroll/
  Progs/TOTAL.cbl
  Procs/PAYJOB.jcl
  CATALOG/PAY.INPUT
  CATALOG/_geometry.json
  run.json
```

`Progs/TOTAL.cbl`:

```cobol theme={"dark"}
       IDENTIFICATION DIVISION.
       PROGRAM-ID. TOTAL.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT PAY-IN  ASSIGN TO PAYIN.
           SELECT PAY-OUT ASSIGN TO PAYOUT.
       DATA DIVISION.
       FILE SECTION.
       FD  PAY-IN RECORDING MODE F.
       01  IN-REC.
           05 IN-NAME   PIC X(10).
           05 IN-AMOUNT PIC 9(5).
       FD  PAY-OUT RECORDING MODE F.
       01  OUT-REC      PIC X(20).
       WORKING-STORAGE SECTION.
       01  WS-EOF       PIC X VALUE 'N'.
       01  WS-TOTAL     PIC 9(7) VALUE 0.
       PROCEDURE DIVISION.
           OPEN INPUT PAY-IN OUTPUT PAY-OUT.
           PERFORM UNTIL WS-EOF = 'Y'
               READ PAY-IN
                   AT END MOVE 'Y' TO WS-EOF
                   NOT AT END ADD IN-AMOUNT TO WS-TOTAL
               END-READ
           END-PERFORM.
           MOVE SPACES TO OUT-REC.
           STRING 'TOTAL ' WS-TOTAL DELIMITED BY SIZE INTO OUT-REC.
           WRITE OUT-REC.
           DISPLAY 'TOTAL IS ' WS-TOTAL.
           CLOSE PAY-IN PAY-OUT.
           STOP RUN.
```

`ASSIGN TO PAYIN` names a DD. The job binds each DD to a data set.

## 2. The job

`Procs/PAYJOB.jcl`:

```text theme={"dark"}
//PAYJOB   JOB
//SORT     EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTIN   DD DSN=PAY.INPUT,DISP=SHR
//SORTOUT  DD DSN=PAY.SORTED,DISP=(NEW,CATLG),
//            DCB=(RECFM=FB,LRECL=15)
//SYSIN    DD *
  SORT FIELDS=(1,10,CH,A)
/*
//TOTAL    EXEC PGM=TOTAL
//PAYIN    DD DSN=PAY.SORTED,DISP=SHR
//PAYOUT   DD DSN=PAY.TOTAL,DISP=(NEW,CATLG),
//            DCB=(RECFM=FB,LRECL=20)
//SYSOUT   DD SYSOUT=*
```

## 3. The input data set

Each file in `CATALOG/` is a data set, named by its data set name. A fixed-format data set holds fixed-length records with no newlines between them.

```sh theme={"dark"}
printf 'SMITH     00100JONES     00250ADAMS     00050' > CATALOG/PAY.INPUT
echo '{"PAY.INPUT": {"lrecl": 15, "recfm": "FB"}}' > CATALOG/_geometry.json
echo '{"submit": ["PAYJOB"]}' > run.json
```

`_geometry.json` declares each data set's record length and format, as the job that created it would have. A file that is exactly N lines of LRECL bytes is read as N records with the newlines dropped. The result's `seed:` line says how each data set was read.

## 4. Run it

```json theme={"dark"}
run_unit {"unit_zip_b64": "<payroll, zipped and base64-encoded>"}
```

```text theme={"dark"}
PAYJOB ok · ...
JOB PAYJOB  [ok]
  SORT    SORT    utility  ran  RC=0  SORT 3 recs on 1 key(s) [lrecl=15]
  TOTAL   TOTAL   cobol    ran  RC=0  ran; 1 out DD(s)
--- PAYJOB TOTAL TOTAL · DISPLAY ---
TOTAL IS 0000400
```

`PAY.TOTAL` is written. The resource links carry each step's `SYSOUT` and the job log.

## 5. Check the output

An `ORACLE/` directory states the expected end state, and the result then carries a verdict.

```sh theme={"dark"}
mkdir -p ORACLE/datasets
printf 'TOTAL 0000400       ' > ORACLE/datasets/PAY.TOTAL
```

Run it again. The last line of the text reads `oracle: passed`. Change `ADAMS     00050` to `00051` in `CATALOG/PAY.INPUT` and run it again: the oracle names `PAY.TOTAL` and the record and byte where it first differs, and the call is a tool error.

`ORACLE/` can also hold `jobs.json` (`{"PAYJOB": "ok"}`), expected Db2 tables, data sets that must not exist, and expected screens. See [Unit format](https://docs.metaphi.ai/harwell/unit-format.md#oracle).

## 6. Run several jobs

A chain runs many jobs under one clock, each after the jobs it names. It replaces `submit` in `run.json`.

`CHAIN/chain.json`:

```json theme={"dark"}
{
  "clock": "20261103220000",
  "jobs": [
    {"name": "EODEXTR"},
    {"name": "EODPOST", "after": ["EODEXTR"]},
    {"name": "EODRPT",  "after": ["EODPOST"], "tolerate": [4]}
  ]
}
```

Each `name` is a deck in `Procs/`. A job whose predecessor failed is `blocked` and does not run. `tolerate` lists return codes that still count as `ok`. Rerun a chain from the job that failed with `resume: true` and the `work_id` of the failed run.

## 7. Set the clock

Programs read the date and time from the run clock, never the host's. Set it in `run.json`:

```json theme={"dark"}
{"submit": ["PAYJOB"], "clock": "20261103120000"}
```

The same unit run with the same clock gives the same result.

## Next

* [Query Db2 and data sets](https://docs.metaphi.ai/harwell/guides/db2-and-data-sets.md): keep the volume and read what the job wrote.
* [Unit format](https://docs.metaphi.ai/harwell/unit-format.md): every `run.json` key.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.