Compat report schema reference
This page is generated from the machine schema, property by property, so it cannot drift from it. For the narrative version — how the verdict is derived, what the warning-code ranges mean, jq recipes for gating CI — read the compatibility report guide.
Identity
Section titled “Identity”| Field | Value |
|---|---|
| Dialect | https://json-schema.org/draft/2020-12/schema |
$id | https://rendlio.com/schemas/compat-report/v1.json |
| Served at | /schemas/compat-report/v1.json — byte-identical to the vendored file |
| Title | Rendlio Compatibility Report |
| Root type | object, additionalProperties: false |
| Top-level properties | 13, of which 13 required |
| Vendored pin | engine commit 8f2013b, sha256 18e09a291424… |
Family scope
Section titled “Family scope”Family scope: the envelope core (schemaVersion, engine, input, options, mode, result, warnings, timings) is shared by every Rendlio engine; the remaining sections are grid-engine payload. This schema is never extended to carry another format.
That is a normative $comment in the schema itself, not a note added here: compat-report.v1 never mutates. A future engine that needs a different envelope gets a new $id and a sibling schema, so a validator pinned to v1 keeps working.
Top-level properties
Section titled “Top-level properties”| Property | Type | Required | Description |
|---|---|---|---|
schemaVersion | string | yes | Version of this report schema, semver-like 1.x. Additive optional fields bump the minor; anything else bumps the major and gets a new schema URL. (pattern ^1\.[0-9]+$) |
engine | object | yes | Identity of the build that produced the report. |
input | object | yes | Identity of the bytes that were read, so a report can be tied back to an exact input. |
options | object | yes | Canonicalized effective options — an exact echo of the CLI flag values including defaults, plus licenseMode ("licensed:<tier>" or "evaluation"). No other licence data ever appears in a report. Deliberately unconstrained so a new flag does not need a schema change. |
mode | "convert" | "diagnose" | yes | Which mode produced the report. diagnose never writes documents and additionally records rendered entries. |
result | object | yes | The run’s verdict and the process exit code, always in agreement. |
pages | object | yes | Output pagination. |
sheets | array of object | yes | One entry per sheet in the workbook, in workbook order. |
features | array of object | yes | The heart of the report: one entry per (feature, status) pair, enforcing the engine-wide rule that nothing is ever silently approximated. In convert mode only deviations are listed, so an empty array means nothing deviated; diagnose additionally lists rendered entries. |
fonts | object | yes | Font resolution outcome. Substitutions are called out because they shift column widths and line breaks. |
formulas | object | yes | Formula-cell accounting. |
warnings | array of object | yes | Coded warnings. Codes are owned by engine modules in fixed ranges, listed in the guide. |
timings | object | yes | Wall-clock milliseconds per phase. Reported for observability; not a determinism guarantee. |
schemaVersion
Section titled “schemaVersion”Version of this report schema, semver-like 1.x. Additive optional fields bump the minor; anything else bumps the major and gets a new schema URL.
Type string · required · pattern ^1\.[0-9]+$.
engine
Section titled “engine”Identity of the build that produced the report.
Type object · required · additionalProperties: false.
| Field | Type | Required | Description |
|---|---|---|---|
name | "Rendlio" (const) | yes | Producing engine. Always the same value — a report is never emitted by anything else. |
version | string | yes | Engine semver, as printed by rendlio version. |
releaseDate | string | yes | ISO date of the build. Licence maintenance windows are compared against this, not against the clock. (format date) |
Identity of the bytes that were read, so a report can be tied back to an exact input.
Type object · required · additionalProperties: false.
| Field | Type | Required | Description |
|---|---|---|---|
sha256 | string | yes | SHA-256 of the input bytes, lower-case hex. (pattern ^[0-9a-f]{64}$) |
sizeBytes | integer | yes | Length of the input in bytes. (minimum 0) |
fileName | string | no | Original file name. Omitted when the input arrived on stdin. |
options
Section titled “options”Canonicalized effective options — an exact echo of the CLI flag values including defaults, plus licenseMode ("licensed:<tier>" or "evaluation"). No other licence data ever appears in a report. Deliberately unconstrained so a new flag does not need a schema change.
Type object · required · free-form — no constrained properties.
Which mode produced the report. diagnose never writes documents and additionally records rendered entries.
Type "convert" | "diagnose" · required.
mode values — all 2, exhaustive:
| Value | Meaning |
|---|---|
"convert" | A conversion run. Writes documents; the report lists deviations only. |
"diagnose" | Analysis only. Writes no documents and additionally records rendered entries. |
result
Section titled “result”The run’s verdict and the process exit code, always in agreement.
Type object · required · additionalProperties: false.
| Field | Type | Required | Description |
|---|---|---|---|
status | "success" | "warnings" | "unsupported-content" | "invalid-input" | "resource-limit" | "error" | yes | Verdict, derived by the ladder documented in the compatibility report guide. |
exitCode | integer | yes | Process exit code, matching status: 0 success, 1 warnings, 2 unsupported content, 3 invalid input, 4 resource limit, 5 internal error. (minimum 0, maximum 5) |
result.status values — all 6, exhaustive:
| Value | Meaning |
|---|---|
"success" | Faithful throughout — no warnings, nothing approximated. |
"warnings" | Converted, with at least one warning or one approximated feature. |
"unsupported-content" | Converted, but at least one feature was unsupported. |
"invalid-input" | Not a workbook this engine can read — wrong format, or encrypted. |
"resource-limit" | A limit fired (timeout, memory, size). Partial outputs are deleted. |
"error" | Internal fault. Report the input and this report. |
Output pagination.
Type object · required · additionalProperties: false.
| Field | Type | Required | Description |
|---|---|---|---|
count | integer | yes | Rendered page count. 0 for diagnose and for failures. (minimum 0) |
sheets
Section titled “sheets”One entry per sheet in the workbook, in workbook order.
Type array of object · required · additionalProperties: false.
| Item field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Sheet name as stored in the workbook. |
index | integer | yes | Zero-based position of the sheet in the workbook. (minimum 0) |
printed | boolean | yes | Whether the sheet contributed pages. false = hidden, or entirely outside the print area. |
features
Section titled “features”The heart of the report: one entry per (feature, status) pair, enforcing the engine-wide rule that nothing is ever silently approximated. In convert mode only deviations are listed, so an empty array means nothing deviated; diagnose additionally lists rendered entries.
Type array of object · required · additionalProperties: false.
| Item field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Stable feature identifier F-<AREA>-<slug>, where <AREA> is one of PARSE, STY, NUM, LAY, CF, CHT, IMG, TXT, PDF, PNG, REC, LIC, SEC. Identifiers are never repurposed. (pattern ^F-(PARSE|STY|NUM|LAY|CF|CHT|IMG|TXT|PDF|PNG|REC|LIC|SEC)-[a-z0-9-]+$) |
status | "rendered" | "approximated" | "unsupported" | "skipped" | yes | What happened to this feature — see the value table below. |
count | integer | yes | Occurrences of this (feature, status) pair. Always the real total, even when locations is truncated. (minimum 1) |
locations | array of location | no | Where the occurrences are. Capped; count keeps the real total. (at most 20 items) |
locationsTruncated | boolean | no | true when more occurrences existed than locations could list. |
detail | string | no | Plain-language statement of the rule that applied. Static template text plus counts and locations only — never cell contents, so a diagnose report is safe to share. |
features[].status values — all 4, exhaustive:
| Value | Meaning |
|---|---|
"rendered" | Faithful per the specification rule for this feature. Recorded in diagnose mode only. |
"approximated" | Rendered, but a documented deviation applies — detail says which. |
"unsupported" | Present in the file, not rendered; the space it would occupy follows the rule for that feature. |
"skipped" | Present but intentionally outside conversion semantics — not a fidelity gap. |
Font resolution outcome. Substitutions are called out because they shift column widths and line breaks.
Type object · required · additionalProperties: false.
| Field | Type | Required | Description |
|---|---|---|---|
substitutions | array of object | yes | Every family that was requested but not used, with what replaced it. |
missing | array of string | yes | Requested families with no substitute rule at all. |
fonts.substitutions[]
Section titled “fonts.substitutions[]”Type array of object · additionalProperties: false.
| Item field | Type | Required | Description |
|---|---|---|---|
requested | string | yes | Family name asked for by the workbook. |
used | string | yes | Family actually rendered with. |
reason | "metric-substitute" | "fallback" | "missing-default" | yes | Why the substitution happened — see the value table below. |
fonts.substitutions[].reason values — all 3, exhaustive:
| Value | Meaning |
|---|---|
"metric-substitute" | Replaced by a metrically compatible family; layout is preserved. |
"fallback" | Replaced by a generic fallback; glyph shapes and metrics differ. |
"missing-default" | The workbook’s default family was unavailable — expect column widths and line breaks to shift. |
formulas
Section titled “formulas”Formula-cell accounting.
Type object · required · additionalProperties: false.
| Field | Type | Required | Description |
|---|---|---|---|
cellsTotal | integer | yes | Number of formula cells in the workbook. (minimum 0) |
cachedMissing | integer | yes | Formula cells carrying no cached value. In off recalc mode these render empty. (minimum 0) |
recalcMode | "off" | "strict-v1" | yes | Recalculation mode in force — see the value table below. |
closureSize | integer | null | no | Size of the evaluation closure when strict-v1 evaluated or refused; null otherwise. (minimum 0) |
formulas.recalcMode values — all 2, exhaustive:
| Value | Meaning |
|---|---|
"off" | Cached formula values are rendered as stored. Cells with no cached value render empty. |
"strict-v1" | The engine evaluated the formula closure itself, or refused and said so. |
warnings
Section titled “warnings”Coded warnings. Codes are owned by engine modules in fixed ranges, listed in the guide.
Type array of object · required · additionalProperties: false.
| Item field | Type | Required | Description |
|---|---|---|---|
code | string | yes | Warning code, W followed by four digits. (pattern ^W[0-9]{4}$) |
severity | "info" | "warn" | "error" | yes | How much the warning matters — see the value table below. |
message | string | yes | Static template text plus counts and locations only — never cell contents. |
location | location object | no | Where the warning applies. Omitted, or all-null, for a document-scope warning. |
warnings[].severity values — all 3, exhaustive:
| Value | Meaning |
|---|---|
"info" | Worth knowing; nothing to act on. |
"warn" | Output may differ from the workbook — check the named location. |
"error" | Something did not work; read result.status for the verdict. |
timings
Section titled “timings”Wall-clock milliseconds per phase. Reported for observability; not a determinism guarantee.
Type object · required · additionalProperties: false.
| Field | Type | Required | Description |
|---|---|---|---|
parseMs | number | yes | Time spent reading and parsing the workbook. (minimum 0) |
layoutMs | number | yes | Time spent paginating and laying out. (minimum 0) |
renderMs | number | yes | Time spent producing output bytes. (minimum 0) |
totalMs | number | yes | Total wall-clock time for the run. (minimum 0) |
location
Section titled “location”A place in the workbook. At least one property must be present; each may be null when it does not apply.
Type object · at least 1 property present · additionalProperties: false.
Referenced by features[].locations[], warnings[].location.
| Field | Type | Required | Description |
|---|---|---|---|
sheet | string | null | no | Sheet name, or null for a workbook-scope location. |
ref | string | null | no | A1 cell or range — "B7", "A1:C30" — or null. |
objectId | string | null | no | Drawing/chart part-relative id, or null. |
Every enum in the schema
Section titled “Every enum in the schema”All 7 closed value sets in v1, read straight out of the schema. Nothing outside these lists is valid.
| Path | Kind | Values |
|---|---|---|
engine.name | const | "Rendlio" |
mode | enum | "convert" | "diagnose" |
result.status | enum | "success" | "warnings" | "unsupported-content" | "invalid-input" | "resource-limit" | "error" |
features[].status | enum | "rendered" | "approximated" | "unsupported" | "skipped" |
fonts.substitutions[].reason | enum | "metric-substitute" | "fallback" | "missing-default" |
formulas.recalcMode | enum | "off" | "strict-v1" |
warnings[].severity | enum | "info" | "warn" | "error" |
Annotated example
Section titled “Annotated example”A complete report — every top-level property, and every optional field the schema allows. The generator validates it against the schema with ajv before writing this page, so it is a report the schema accepts. Real reports are plain JSON; the // comments are annotation only.
{ "schemaVersion": "1.0", "engine": { "name": "Rendlio", // always "Rendlio" "version": "0.3.1", "releaseDate": "2026-10-02" }, "input": { "sha256": "fd646a748c6397ce85167b4763e8f5ee037caac1c33318c48d6955ae01837993", // of the input bytes, not of the output "sizeBytes": 183422, "fileName": "quarterly-close.xlsx" // optional }, "options": { // abridged; the real echo carries every effective flag "report": "-", "recalc": "off", "sheets": "all", "licenseMode": "evaluation" // the only licence data a report ever carries }, "mode": "diagnose", // "convert" | "diagnose" "result": { "status": "unsupported-content", // one of 6 — see the `result.status` values table "exitCode": 2 // always agrees with status }, "pages": { "count": 0 // 0 for diagnose and for failures }, "sheets": [ { "name": "Summary", "index": 0, "printed": true // false = hidden or outside the print area }, { "name": "Data", "index": 1, "printed": true }, { "name": "Archive", "index": 2, "printed": false } ], "features": [ // convert mode lists deviations only; [] = nothing deviated { "id": "F-NUM-number-format", "status": "rendered", // one of 4 — see the `features[].status` values table "count": 41, // real total, even when locations is truncated "detail": "Excel display strings reproduced with the locale pinned to the workbook setting." // optional }, { "id": "F-STY-cell-fill", "status": "rendered", "count": 218, "detail": "Solid, pattern, and gradient fills painted from the workbook theme." }, { "id": "F-LAY-merged-cell", "status": "rendered", "count": 12, "detail": "Merged ranges drawn as a single cell with the anchor cell's alignment." }, { "id": "F-CF-time-period", "status": "approximated", "count": 3, "locations": [ // optional { "sheet": "Summary", "ref": "B4:B31" }, { "sheet": "Data", "ref": "H2:H480" } ], "detail": "timePeriod rules are evaluated against the conversion date, not the workbook's last-open date." }, { "id": "F-CHT-3d-surface", "status": "unsupported", "count": 2, "locations": [ { "sheet": "Summary", "objectId": "drawing1:chart2" }, { "sheet": "Data", "objectId": "drawing2:chart1" } ], "detail": "3-D surface charts are outside the supported chart set; the plot area is rendered empty." }, { "id": "F-IMG-vml-extrusion", "status": "unsupported", "count": 1, "locations": [ { "sheet": "Archive", "objectId": "vmlDrawing1:shape7" } ], "detail": "Legacy VML 3-D extrusion is outside the supported drawing set; the space it occupies is rendered empty." }, { "id": "F-SEC-vba-project", "status": "skipped", "count": 1, "detail": "VBA projects are never executed and never rendered; this is not a fidelity gap." }, { "id": "F-REC-data-validation", "status": "skipped", "count": 27, "locations": [ { "sheet": "Data", "ref": "C2" }, { "sheet": "Data", "ref": "C3" }, { "sheet": "Data", "ref": "C4" }, { "sheet": "Data", "ref": "C5" }, { "sheet": "Data", "ref": "C6" }, { "sheet": "Data", "ref": "C7" }, { "sheet": "Data", "ref": "C8" }, { "sheet": "Data", "ref": "C9" }, { "sheet": "Data", "ref": "C10" }, { "sheet": "Data", "ref": "C11" }, { "sheet": "Data", "ref": "C12" }, { "sheet": "Data", "ref": "C13" }, { "sheet": "Data", "ref": "C14" }, { "sheet": "Data", "ref": "C15" }, { "sheet": "Data", "ref": "C16" }, { "sheet": "Data", "ref": "C17" }, { "sheet": "Data", "ref": "C18" }, { "sheet": "Data", "ref": "C19" }, { "sheet": "Data", "ref": "C20" }, { "sheet": "Data", "ref": "C21" } ], "locationsTruncated": true, // optional "detail": "Data-validation rules have no static representation and are intentionally outside conversion semantics." } ], "fonts": { "substitutions": [ { "requested": "Calibri", "used": "Carlito", "reason": "metric-substitute" // "metric-substitute" | "fallback" | "missing-default" }, { "requested": "Wingdings", "used": "DejaVu Sans", "reason": "fallback" }, { "requested": "Aptos Narrow", "used": "DejaVu Sans", "reason": "missing-default" } ], "missing": [ "Marlett" ] }, "formulas": { "cellsTotal": 4183, "cachedMissing": 0, // render empty while recalcMode is "off" "recalcMode": "off", // "off" | "strict-v1" "closureSize": null // integer once strict-v1 evaluates or refuses · optional }, "warnings": [ { "code": "W1011", "severity": "warn", // "info" | "warn" | "error" "message": "External workbook link rendered from its cached value; no network refresh was attempted.", "location": { // all-null = document scope · optional "sheet": "Summary", "ref": "E12" } }, { "code": "W2104", "severity": "info", "message": "Custom number format approximated to the nearest supported pattern." }, { "code": "W5104", "severity": "warn", "message": "Requested font is not installed; a metric-compatible substitute was used." }, { "code": "W7002", "severity": "error", "message": "A page exceeded the PDF media box and was clipped to the print area.", "location": { "sheet": "Data" } }, { "code": "W9001", "severity": "info", "message": "No licence found; the evaluation watermark was applied to every page." } ], "timings": { // wall-clock, not a determinism guarantee "parseMs": 118.4, "layoutMs": 96.2, "renderMs": 0, "totalMs": 231.7 }}Validating a report yourself
Section titled “Validating a report yourself”curl -sO https://rendlio.com/schemas/compat-report/v1.jsonajv validate --spec=draft2020 -s v1.json -d report.jsonThe schema is served at its own $id, so that URL is fetchable rather than merely canonical. Within v1 only optional fields are ever added, so a validator pinned to v1 keeps working across engine updates.