Skip to content

ULC v1.3.0

Choose a tag to compare

@github-actions github-actions released this 09 Aug 06:17
· 6 commits to main since this release
854f2ed

ulc CLI v1.3.0

Download a single-file binary for your platform below. See CHANGELOG.md
at the tag for what landed in this release.

An integrity release. Default ulc validate output now includes hash-verification findings for the family cutsheet (product_family.cutsheet) and the emergency photometry reference (emergency.photometry_reference): on records whose files are absent locally this adds INFO findings, and a locally present file whose hash does not match its declared value is an ERROR that fails validation, where previously only source_files entries were checked. ulc validate --json now escapes <, >, and & as \u003c, \u003e, and \u0026, so its bytes change for records containing those characters in echoed strings, aligning it with ulc scope; JSON semantics after parsing are identical. A new opt-in flag, ulc validate --verify-evidence, byte-verifies attestation evidence documents. ulc from-sheet now bounds workbook archives (entry count, per-part and total inflated size, compression ratio) and rejects a file exceeding one of them with an error naming the limit, while the CSV bundle path is unchanged. from-sheet now resolves symbolic links before hashing a referenced file and rejects any reference that resolves outside the assets root. Text output now renders control characters in record-supplied text as visible escapes, so a crafted filename cannot forge report lines or alter terminal state; JSON output, which already escapes the control characters below U+0020, is unchanged. Schema validation now asserts the schema's declared string formats: the eight format: date fields must be ISO 8601 dates (YYYY-MM-DD) and the two format: uri fields must be absolute URIs. These declarations were previously annotations that no check enforced, so a malformed date (for example a spreadsheet date serial where an ISO date belongs) or a relative URL validated cleanly; it now fails validation, in ulc validate and in ulc from-sheet, which validates every record before writing it. There is no schema or taxonomy change and no computed value change: grades and completeness findings are byte-identical, every golden file is byte-identical, and no example record changed a byte.

For consumers

If you pin exit codes, hash verification adds one new case: a record whose cutsheet or emergency photometry file sits beside it locally and does not match its declared hash now exits 1 instead of 0. That is the intended correction, and on the eight shipped examples nothing moves, because no source file is distributed alongside them.

If you parse --json, expect one additional finding object per record whose cutsheet is not reachable locally, and summary.infos one higher. The escaping change moves bytes only for records that carry <, >, or & in an echoed string such as a filename or a record id; no shipped example does, so all eight are byte-identical apart from the added finding.

A source-file/not-found-locally INFO at a record's source_files entry is expected and documented when a manufacturer publishes an evidence reference while withholding the document itself. It is not a defect, the record still validates, and the theme still reads documented. See "Withholding the evidence document" in docs/authoring-patterns.md.

--verify-evidence is opt-in and absent from default runs. Its real coverage is evidence documents that a record references but does not also list among its source files; where a record follows the shipped guidance and dual-writes the document into source_files, the default walk already covers those bytes and the flag adds a second pointer rather than a second check.

Schema validation now enforces declared string formats, which adds one exit-code class: a record carrying a malformed date (a spreadsheet date serial such as 46082, a 3/1/26, a 2026-13-45) in any of the eight format: date fields, or a relative or otherwise non-absolute URL in either of the two format: uri fields, exits 1 where it previously exited 0. Well-formed records do not move: all eight shipped examples are byte-identical under the assertion. The check catches malformed values, not missing ones; every date field is optional, and an absent date behaves exactly as before.

Validator

  • File-reference hash verification now runs at every site the schema defines a reference: source_files[].reference, product_family.cutsheet, and emergency.photometry_reference on every run, plus attestations[].source_document_ref and product_family.shared_attestations[].source_document_ref under --verify-evidence. The sites are named by a declared registry, and a schema-derived test fails the build if a future schema mounts a reference the registry does not name, so a new reference field cannot ship unverified by oversight. Findings reuse the existing source-file/* codes with the site's own JSON Pointer as the discriminator; no code or message template changed.
  • New flag ulc validate --verify-evidence. A locally absent evidence document stays INFO, so a record that withholds the document still validates; a document whose SHA-256 does not match is an ERROR and fails validation. Absent from default runs, so default output and the goldens are untouched by it.
  • ulc from-sheet bounds .xlsx archives: 1024 entries, 16 MiB inflated per part, 32 MiB inflated across every part the reader opens, and a 100:1 compression ratio above a 1 MiB floor. A workbook exceeding one is rejected with an error naming the limit. The byte limits are derived from the measured amplification of worksheet XML into the workbook model. Parts the reader never opens are not charged, and the CSV bundle path parses no archive and is unchanged.
  • ulc from-sheet resolves symbolic links before hashing and rejects a reference that resolves outside the assets root. A reference to a file that is not present behaves as before: an error unless --allow-missing-files is set, which stamps the zero sentinel and marks the record a draft.
  • ulc validate --json escapes <, >, and &, matching ulc scope. ulc build-index --stdout still does not, because its output must preserve the record's own byte shape.
  • Text output sanitizes record-supplied strings at the render boundary, covering control characters (C0, DEL, and C1) and the bidirectional formatting characters that reorder rendered text without any control character present, so a filename cannot display as a different name from the one whose bytes were hashed. Escapes are \xNN below U+0100 and \uNNNN above it. Message text is unchanged; consumers needing exact bytes use --json.
  • ulc from-sheet assembles a shared string from its formatting runs in one pass. The previous accumulation reallocated per run, so a workbook inside every size limit could still hold the reader at full CPU for minutes; nothing bounds how many runs a single string may carry.
  • Schema validation asserts declared formats. The eight format: date fields (record_status_as_of, revision_date on file references, introduced_at on product families, original_issue_date and expiration_date on sustainability declarations, and issued_date, valid_until, and list_date on attestations) must parse as ISO 8601 YYYY-MM-DD dates, and the two format: uri fields (url on a file reference, end_of_life_program_url) must be absolute URIs. A malformed value fails validation with a schema/violation finding at its own JSON Pointer, so a record can no longer pass while --expiry silently skips an unparseable date, and ulc from-sheet refuses to write a record whose workbook carried one.

Docs

  • README.md: the source-inputs section names the primary source documents and points at the full SourceFileType vocabulary, which covers twelve document types rather than the three the section used to list.
  • docs/how-it-works.md: Path 2 describes emitting from the systems that hold the data rather than from a PIM alone, and the CLI bullet mentions --verify-evidence.
  • docs/authoring-patterns.md: new guidance on withholding an evidence document while still documenting a theme, and a new source_files primitive subsection covering what the array asserts, including document-grade commercial references.
  • docs/methodology.md: the trust-boundary section states what a withheld evidence document does and does not change.
  • mappings/README.md and mappings/pim/README.md: product attributes and compliance evidence commonly live in different systems; a new "Where the evidence lives" section describes joining them at emit time.
  • tools/validator/README.md: records the widened verification, the new flag, the archive limits, the assets-root containment rule, and the escaping alignment.
  • templates/workbook/README.md: the notes for .xlsx authors cover formatting date and identifier columns as Text before entry, why a spreadsheet silently re-types an ISO date into a serial number and an identifier such as 1200.40 into 1200.4, and that schema validation rejects a date that is not YYYY-MM-DD.
  • mappings/pim/salsify.md and mappings/pim/akeneo.md: the asset mapping takes the date portion of updated_at for reference.revision_date, which is one of the eight format: date fields.
  • ROADMAP.md: the active-version heading now names the current line, the minors paragraph records that this release is the first to move default output, and the attestation-document byte-verification and JSON escaping-consistency items move out of deferred work because this release ships them.