Skip to content

Fix a dirty file

Available in: Python · Node · CLI · Browser

Mechanically repair a non-conforming AGS4 file — non-destructively — into a fresh handle.

# what this shows: .fix() mechanically repairs a dirty AGS4 file, non-destructively, into a NEW handle.
from laterite import read

# A dirty file: the data row is SHORT — fewer fields than the HEADING row (Rule 4).
# (AGS4 lines are CRLF-terminated; keep them so the only defect is the short row.)
dirty_text = (
    '"GROUP","LOCA"\r\n'
    '"HEADING","LOCA_ID","LOCA_TYPE","LOCA_NATE"\r\n'
    '"UNIT","","",""\r\n'
    '"TYPE","ID","PA","2DP"\r\n'
    '"DATA","BH01","BH"\r\n'  # <- only 3 fields, HEADING declares 4
)

dirty = read(text=dirty_text)
fixed = dirty.fix()  # returns a NEW Ags4File; the original is untouched

kinds = [a["kind"] for a in fixed.fix_report.applied]
print(fixed.fix_report.applied[0]["kind"])

assert fixed is not dirty  # non-destructive: a fresh handle
assert fixed.fix_report.applied[0]["kind"] == "pad_short_row"
assert "pad_short_row" in kinds  # the short row was padded to width
pad_short_row

.fix() runs the safe-repair pass over a file you've already read and returns a new Ags4File — the original handle is left untouched (fixed is not dirty). What it changed rides on fixed.fix_report.applied, a list of {"kind": …} records: here the lone DATA row had only 3 fields where the HEADING declared 4, so the fixer padded it to width and recorded kind == "pad_short_row".

Read the kinds off the report to see every repair that was applied:

kinds = [a["kind"] for a in fixed.fix_report.applied]

Because it returns the file, you chain straight on — re-.validate() the fixed handle, or emit it.

// what this shows: .fix() mechanically repairs a dirty AGS4 file, non-destructively, into a NEW handle.
import { read } from "laterite";
import assert from "node:assert/strict";

// A dirty file: the data row is SHORT — fewer fields than the HEADING row (Rule 4).
// (AGS4 lines are CRLF-terminated; keep them so the only defect is the short row.)
const dirtyText =
  '"GROUP","LOCA"\r\n' +
  '"HEADING","LOCA_ID","LOCA_TYPE","LOCA_NATE"\r\n' +
  '"UNIT","","",""\r\n' +
  '"TYPE","ID","PA","2DP"\r\n' +
  '"DATA","BH01","BH"\r\n'; // <- only 3 fields, HEADING declares 4

const dirty = read(undefined, { text: dirtyText });
const fixed = dirty.fix(); // returns a NEW Ags4File; the original is untouched

console.log(fixed.fixReport.applied[0].kind);

assert.notEqual(fixed, dirty); // non-destructive: a fresh handle
assert.equal(fixed.fixReport.applied[0].kind, "pad_short_row");
pad_short_row

Same behaviour, camelCase: .fix() returns a new Ags4File and the record of what changed rides on .fixReport.applied ({kind, label, rule, line?, risk} per fix), with what couldn't be mechanically repaired left in .fixReport.findings. Write the repaired bytes out with fixed.save(path), or keep chaining. The free-function form fix(path) returns the FixResult directly when you don't need the handle. Pass { risky: true } to add the intent-guessing repairs on top of the safe pass.

lat fix delivery.ags --fix-out repaired.ags
applied 1 fix(es) [pad_short_row] → repaired.ags
repaired.ags: 5 finding(s) remain (not mechanically fixable)

fix applies the same safe-repair pass and writes the repaired file where --fix-out points (omit it for a sibling <file>.fixed.ags, or use --in-place to overwrite the source). The summary names each applied kind and counts what remains for a human — here the padded row was fixed, while the missing TRAN/UNIT/TYPE groups can't be invented, so the exit code stays 1. Add --risky for the intent-guessing tier.

Open the web app and drop your file on the Fix pane. It computes the available repairs, shows a before/after diff so you can review each one, and hands back the repaired file as a download — the same safe-fix engine as every other surface, running entirely in your browser.

These are safe repairs — width/whitespace/structural defects that have one unambiguous correction. Anything judgemental (a wrong value, a missing KEY) is left for you; fix will not invent data.

See also: CLI reference · Validate a delivery