# CLI Render Contract The desktop app (Story: "Launch a text-only render from the desktop app") integrates against this contract, not against `EnvelopeRenderer.Cli`'s internals. Treat it as the stable interface between the two; changing it is a cross-cutting decision, not a local one. Implementation: [`src/EnvelopeRenderer.Cli`](src/EnvelopeRenderer.Cli). ## Arguments All three are required. There is no short form and no `=` form in this MVP — keep the surface intentionally small per the story's conversation notes. | Argument | Value | Notes | |---|---|---| | `--template ` | Local or UNC path to the XML template | Must exist and be readable. | | `--csv ` | Local or UNC path to the CSV data file | Must exist and be readable. | | `--output ` | Local or UNC path to write the rendered PDF to | Parent directory must already exist; the CLI does not create directories. | | `-h`, `--help` | — | Prints usage to stdout and exits `0`. Overrides everything else on the command line. | Unknown arguments, a flag given without a value, a flag given twice, or an empty value are all usage errors (exit `2`). ## stdout / stderr rules - **stdout** is reserved for machine-readable output: today just `--help` text (human-readable by exception, since it's explicitly for a human at a terminal); starting with the "Emit machine-readable progress during render" story, line-oriented progress events land here too. The desktop app should be able to treat stdout as parseable and never need to filter noise out of it. - **stderr** carries every error, one per line, each prefixed `ERROR: `. Multiple problems (e.g. two missing arguments) are each reported as their own line rather than stopping at the first. ## Exit codes | Code | Meaning | |---|---| | `0` | Success — PDF written to `--output`. | | `1` | Template, CSV, or render error: malformed/invalid template XML, a template `column` not present in the CSV header row, an empty CSV, a missing/unresolvable font, or a Debenu render/save failure (including a missing or invalid license key — see below). Read stderr for which. | | `2` | Invalid usage — missing, unknown, duplicate, or empty-valued argument. | | `3` | Input not found — `--template` or `--csv` path does not exist. | | `4` | Output path invalid — output directory does not exist, or the output path is itself an existing directory. | Validation runs in this order and stops at the first failing stage: usage → input paths → output path → render. A caller can rely on, e.g., never seeing an output-path error (`4`) while an input path is still missing. ## Rendering The text-only template format (`--template`) is documented separately in [`TEMPLATE_FORMAT.md`](TEMPLATE_FORMAT.md). ## Debenu license key Rendering (not argument validation) requires a Debenu Quick PDF Library 10.13 license key at runtime, read from the `DEBENU_LICENSE_KEY` environment variable. Without it, every render fails at the save step with exit `1` — confirmed directly against the vendor DLL: page creation, font embedding, and text drawing all succeed, but `SaveToFile`/`SaveToString` both return error code 999 regardless of content. A working key for local development is kept at the project root in `key.txt` (untracked — never commit it or hardcode it into source). ## UNC paths Local and UNC paths are both accepted and validated identically — `System.IO.File.Exists` / `Directory.Exists` handle UNC paths natively, so there's no special-case code. What is **not** yet defined is timeout/retry behavior against a slow or unreachable UNC share; that's an open impediment (`logs/impediment_log.md`, logged 2026-09-04) carried as an explicit risk into the Debenu render story, not solved here. ## Smoke examples Run from `code/`: ``` $ dotnet run --project src/EnvelopeRenderer.Cli -- --help (usage text) exit=0 $ dotnet run --project src/EnvelopeRenderer.Cli -- ERROR: Missing required argument '--template'. ERROR: Missing required argument '--csv'. ERROR: Missing required argument '--output'. (usage text on stderr) exit=2 $ dotnet run --project src/EnvelopeRenderer.Cli -- --template nope.xml --csv sample-data/wilson.csv --output out.pdf ERROR: Template not found: 'nope.xml'. exit=3 $ dotnet run --project src/EnvelopeRenderer.Cli -- --template envelope.xml --csv sample-data/wilson.csv --output nosuchdir/out.pdf ERROR: Output directory does not exist: 'nosuchdir'. exit=4 $ dotnet run --project src/EnvelopeRenderer.Cli -- --template sample-data/sample-envelope-template.xml --csv sample-data/wilson.csv --output out.pdf ERROR: Failed to save PDF to 'out.pdf' (error code 999). exit=1 # (DEBENU_LICENSE_KEY not set — see "Debenu license key" above) $ DEBENU_LICENSE_KEY="$(cat ../key.txt)" dotnet run --project src/EnvelopeRenderer.Cli -- --template sample-data/sample-envelope-template.xml --csv "sample-data/87700 - 999999 - Wilson Township.csv" --output out.pdf exit=0 ```