Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion TODO.complete/01-scaffolding.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Stand up the gem skeleton: gemspec, dependencies, build tooling, lib entry point.

## Files created
- `dcc.gemspec` — modelled on reqif.gemspec; deps: `lutaml-model ~> 0.8`, `nokogiri`, `thor`.
- `dcc.gemspec` — modelled on reqif.gemspec; deps: `lutaml-model ~> 0.8`, `moxml`, `mml`, `liquid`, `thor`. Note: `tty-table` is required at load by `lib/dcc/cli/cli.rb` but is only a Gemfile dev dependency.
- `Gemfile` — dev deps + GitHub lutaml-model + optional group (`sinatra`, `tty-prompt`, `xmldsig`).
- `Rakefile` — `bundler/gem_tasks`, rspec, rubocop; default = spec + rubocop.
- `.rspec`, `.rubocop.yml`, `.rubocop_todo.yml`, `.gitignore`, `LICENSE` (MIT).
Expand Down
10 changes: 9 additions & 1 deletion TODO.complete/04-custom-types.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# 04 — Custom types (P0)

**Status:** COMPLETED
**Status:** PARTIAL

## Gaps
- `Base64Binary.cast` returns the base64 *text* unchanged rather than the
binary ASCII-8BIT String promised above; decoding is only reachable through
the separate `.decode` helper.
- `SchemaVersion` defines `major` (and `self.extract_major`) only — there are
no `minor` or `patch` accessors, and no `serialize`, so it is not a
`cast`/`serialize` inverse pair.

## Goal
Six `Lutaml::Model::Type::Value` subclasses that encode DCC/D-SI-specific XML constraints.
Expand Down
6 changes: 3 additions & 3 deletions TODO.complete/05-bundle-schemas-and-fixtures.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ Ship the 12 DCC XSDs, 6 D-SI XSDs, QUDT, Schematron, and xmldsig-core schemas wi
- `lib/dcc/schema.rb` — `Dcc::Schema.path`, `.exists?`, `.read` API + autoload of `Version`.
- `lib/dcc/schema/version.rb` — `DCC_ALL`, `DCC_LATEST`, `DSI_ALL`, `DSI_LATEST`, `resolve_dcc(version, xml:)`, `resolve_dsi`, `detect_from_xml`, `major(v)`.
- `lib/dcc/schema/resources/dcc/v{2.1.0,...,3.3.0}/dcc.xsd` — 12 DCC XSDs.
- `lib/dcc/schema/resources/dsi/v{1.0.1,...,2.2.1}/SI_Format.xsd` — 6 D-SI XSDs.
- `lib/dcc/schema/resources/dcc/dsi/v{1.0.1,...,2.2.1}.xsd` — 6 D-SI XSDs.
- `lib/dcc/schema/resources/qudt/qudt.xsd`.
- `lib/dcc/schema/resources/schematron/dcc.sch`, `dcc.svrl`.
- `lib/dcc/schema/resources/xmldsig/xmldsig-core-schema.xsd`.
- `spec/fixtures/ptb/` — PTB dcclib test resources (valid.xml, invalid_*.xml, custom.*, certs/).
- `spec/fixtures/dcclib/` — PTB dcclib test resources (valid.xml, invalid_*.xml, custom.*, certs/).
- `spec/fixtures/dcc_examples/` — DCC XSD repo examples (example.xml, siliziumkugel.xml, excel_viewer_example.xml).
- `spec/fixtures/dsi_examples/{BRONZE,SILVER,GOLD,PLATINUM,IMPROVABLE,NOT_PASSED}/` — D-SI examples by tier.

Expand All @@ -24,4 +24,4 @@ Ship the 12 DCC XSDs, 6 D-SI XSDs, QUDT, Schematron, and xmldsig-core schemas wi

## Verification
- `Dcc::Schema.exists?("dcc/v3.3.0/dcc.xsd")` → true.
- `Dcc::Schema::Version.resolve_dcc("auto", xml: File.read("spec/fixtures/ptb/valid.xml"))` → `"3.3.0"`.
- `Dcc::Schema::Version.resolve_dcc("auto", xml: File.read("spec/fixtures/dcclib/valid.xml"))` → `"3.3.0"`.
2 changes: 1 addition & 1 deletion TODO.complete/06-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Structured error classes for every failure mode the gem can encounter.

## Files created
- `lib/dcc/error.rb` — base `Dcc::Error < StandardError` plus `Dcc::Error::*` subclasses:
- `lib/dcc/error.rb` — base `Dcc::Error < StandardError` plus sibling classes declared directly under `Dcc::` (not nested inside `Dcc::Error`):
- `ParseError` — malformed XML / encoding issues.
- `ValidationError` (base, carries `issues:`) + `SchemaError` subclass.
- `SignatureError`, `ConversionError`, `ExtractionError`, `TransformError`.
Expand Down
20 changes: 14 additions & 6 deletions TODO.complete/07-dcc-base-modules.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
# 07 — DCC base mapping modules (P1)

**Status:** COMPLETED
**Status:** PARTIAL

## Gaps
- The six v3.4.0-rc.2 modules are absent: `measurand.rb`,
`relative_uncertainty.rb`, `position_coordinates.rb`,
`performance_location.rb`, `report_amended_substituted.rb`,
`primitive_quantity.rb`. Those constants appear nowhere in `lib/`.
- The DCC v3.3 element set is complete: 44 modules ship under `lib/dcc/base/`.

## Goal
Create `lib/dcc/base/*.rb` — one shared XML mapping module per DCC element type. Each module uses `self.included(klass)` to inject `attribute` declarations and an `xml do ... end` mapping block. The version wrappers (`Dcc::V2::*`, `Dcc::V3::*`) include these modules and add version-specific extras.

## Files to create
One file per DCC v3.3.0 complexType (≈40 files). Representative subset:
One file per DCC v3.3.0 complexType. 44 files ship under `lib/dcc/base/`. Representative subset:

- `lib/dcc/base/digital_calibration_certificate.rb` — root, `schemaVersion` attribute, `administrativeData` + `measurementResults` + optional `comment`/`document`/`ds:Signature`.
- `lib/dcc/base/administrative_data.rb` — `xs:all` container of `dccSoftware`/`coreData`/`items`/`calibrationLaboratory`/`respPersons`/`customer`/`statements`.
Expand All @@ -22,15 +29,16 @@ One file per DCC v3.3.0 complexType (≈40 files). Representative subset:
- `lib/dcc/base/contact.rb` (strict and not-strict variants).
- `lib/dcc/base/software.rb` (with v3 `type` enum).
- `lib/dcc/base/identifications.rb`.
- `lib/dcc/base/used_methods.rb`, `lib/dcc/base/used_software.rb`.
- `lib/dcc/base/influence_conditions.rb`.
- `lib/dcc/base/used_method.rb` + `used_method_list.rb` (drafted above as `used_methods.rb`).
- `lib/dcc/base/software.rb` + `software_list.rb` (drafted above as `used_software.rb`).
- `lib/dcc/base/influence_condition.rb` + `influence_condition_list.rb`.
- `lib/dcc/base/measuring_equipment.rb`.
- `lib/dcc/base/resp_person.rb`.
- `lib/dcc/base/calibration_laboratory.rb`.
- `lib/dcc/base/customer.rb`.
- Customer is modelled by `lib/dcc/base/contact.rb`; there is no `customer.rb`.
- `lib/dcc/base/equipment_class.rb`.
- `lib/dcc/base/location.rb`.
- `lib/dcc/base/hash.rb` (previousReport hash reference).
- `lib/dcc/base/hash_type.rb` (previousReport hash reference).
- Plus v3.4.0-rc.2 modules: `lib/dcc/base/measurand.rb`, `rich_content.rb`, `ref_type_definition.rb`, `relative_uncertainty.rb`, `position_coordinates.rb`, `performance_location.rb`, `report_amended_substituted.rb`, `primitive_quantity.rb`.

Each module follows this template:
Expand Down
3 changes: 2 additions & 1 deletion TODO.complete/08-dcc-v2-wrappers.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ end

## Verification
- `Dcc::V2.parse(fixture_xml_v2_3_0)` returns a `Dcc::V2::DigitalCalibrationCertificate`.
- Spec covers every wrapper class: parse + serialize + assert class identity.
- (Not covered: no spec names a single one of the 47 `Dcc::V2::` wrapper classes,
so the per-class parse/serialize/identity claim never ran.)
12 changes: 10 additions & 2 deletions TODO.complete/09-dcc-v3-wrappers.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# 09 — Dcc::V3 wrappers (P1)

**Status:** COMPLETED
**Status:** PARTIAL

## Gaps
- The `:dcc_v3_4` substitution context is never registered.
`lib/dcc/v3/configuration.rb` defines `CONTEXT_ID = :dcc_v3` and nothing
else, so `Dcc.parse(xml, context: :dcc_v3_4)` cannot work.
- No v3.4 fixture exists under `spec/fixtures/`, so the last verification
step below has never run.
- The v3.4.0-rc.2 attributes themselves are not modelled — see phase 07.

## Goal
Thin wrapper classes for every DCC element under `Dcc::V3::*`, including v3 extras (`refId`, `refType`, v3.4.0-rc.2 features as optional attributes), registering in `:dcc_v3`.
Expand All @@ -15,7 +23,7 @@ Thin wrapper classes for every DCC element under `Dcc::V3::*`, including v3 extr
## Design notes
- V3-specific modules (`V3Extras`) add the `refId`/`refType` attributes shared by most types.
- V3.4.0-rc.2 features (`measurands`, `richContentType`, `reportAmendedSubstituted`, `relativeUncertainty`, `positionCoordinates`, `performanceLocation`) live in `Dcc::Base::*` modules as optional attributes — V3 wrappers can ignore them.
- `ds:Signature` element on root (new in 3.4.0) is an optional collection attribute on `DigitalCalibrationCertificate`.
- `ds:Signature` element on root is an optional collection attribute on `DigitalCalibrationCertificate`. It is not new in 3.4.0 — the bundled v3.3.0 XSD already declares it (`dcc/v3.3.0/dcc.xsd:66`).

## Dependencies
- 07 (DCC base modules).
Expand Down
16 changes: 11 additions & 5 deletions TODO.complete/10-d-si-model.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
# 10 — D-SI model (P1)

**Status:** COMPLETED
**Status:** PARTIAL

## Gaps
- `lib/dcc/si/base/` ships 12 modules, not the ~30 listed below.
- D-SI multivariate uncertainty is unmodelled: covariance matrix,
covariance, column, ellipsoidal region and rectangular region appear
nowhere in `lib/`.

## Goal
Build `Dcc::Si::V1::*` and `Dcc::Si::V2::*` covering all D-SI quantity types, uncertainty types, and supporting types (column, covariance, region). Same registry pattern as DCC.

## Files to create
- `lib/dcc/si/si.rb` — `Dcc::Si` top-level: `parse(input, version: 2)`, `SUPPORTED_VERSIONS = [1, 2]`, `Dcc::Si::UNSPECIFIED_CONTEXT`.
- `lib/dcc/si/versioned_parser.rb` — mirrors `Dcc::VersionedParser` for D-SI.
- `lib/dcc/si/context_configuration.rb` — re-uses `Dcc::ContextConfiguration`.
- `lib/dcc/si.rb` — `Dcc::Si` top-level: `parse(input, version: 2)`, `SUPPORTED_VERSIONS = [1, 2]`, `Dcc::Si::UNSPECIFIED_CONTEXT`.
- D-SI reuses `Dcc::VersionedParser`; no separate `si/versioned_parser.rb` was needed.
- D-SI reuses `Dcc::ContextConfiguration` directly; no separate file was needed.
- `lib/dcc/si/namespace.rb` — `Dcc::Si::Namespace = Dcc::Namespace::Si`.
- `lib/dcc/si/common_elements.rb` — shared base for D-SI elements.
- `lib/dcc/si/base/` — one module per D-SI complexType (≈30 files):
- `quantity.rb` (abstract head)
- `real.rb`, `complex.rb`, `constant.rb`
- `real_list.rb`, `complex_list.rb`, `real_list_xml_list.rb`, `complex_list_xml_list.rb`
- `hybrid.rb`, `list.rb` (recursive)
- `hybrid.rb`, `si_list.rb` (recursive; drafted as `list.rb`)
- `standard_mu.rb`, `expanded_mu.rb`, `coverage_interval_mu.rb`
- `ellipsoidal_region.rb`, `rectangular_region.rb`
- `covariance_matrix.rb`, `covariance.rb`, `column.rb`
Expand Down
7 changes: 6 additions & 1 deletion TODO.complete/11-wire-dcc-dsi.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ Connect `dcc:quantityType` to D-SI quantity classes and finalize the `dcc:dataTy

## Verification
- PTB `valid.xml` parses and round-trips with full D-SI quantity tree intact.
- Spec: parse `dcc:quantity` containing `si:hybrid`, navigate `dcc.measurement_results.first.results.first.data.quantities.first.real.first.value` returns a BigDecimal.
- Spec: parse `dcc:quantity` containing `si:hybrid` and navigate the D-SI subtree.
The real path is `dcc.measurement_results.measurement_result.first.results.result.first.data.first.quantity` —
`measurement_results` and `results` are list wrappers, `data` is a collection, and the
attribute is `quantity`, not `quantities`.
- `si:real` values are typed `:string`, not BigDecimal. That is a deliberate
modelling decision, not a gap in this phase.
7 changes: 5 additions & 2 deletions TODO.complete/12-root-parser-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ Finalize the public parse entry points: `Dcc.parse`, `Dcc::V2.parse`, `Dcc::V3.p

## Files
- `lib/dcc.rb` already defines `Dcc.parse(input, version:, context:, register:)`, `parser_for(version)`, `detect_version(input)`.
- Add integration spec: `spec/dcc/parse_spec.rb` covering:
- No `spec/dcc/parse_spec.rb` was created. `spec/dcc_spec.rb` covers
`parser_for`, `detect_version` and input handling; the `Dcc.parse`
examples live in `spec/ptb_fixtures_spec.rb` and `spec/round_trip_spec.rb`.
Between them they cover:
- Auto-detect from `schemaVersion="3.3.0"` → V3.
- Auto-detect from `schemaVersion="2.3.0"` → V2.
- Explicit `version: 2` / `version: 3`.
- Custom context via `context: :dcc_v3_4`.
- (Not covered: `context: :dcc_v3_4` — that context does not exist. See phase 09.)
- Error on unknown version.

## Design notes
Expand Down
10 changes: 9 additions & 1 deletion TODO.complete/13-round-trip-specs.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# 13 — Round-trip fidelity specs (P1)

**Status:** COMPLETED
**Status:** PARTIAL

## Gaps
- `spec/round_trip_spec.rb:47-48` asserts only that the output is a String
containing `digitalCalibrationCertificate`. There is no canonical
comparison, despite the goal above.
- The `fixture_globs` list omits `dsi_examples/**/*.xml`, so no D-SI example
is round-tripped — even though the file comment at line 10 says otherwise.
- No per-class `spec/dcc/model/{element}_spec.rb` files exist.

## Goal
Lossless round-trip on every PTB fixture: parse → serialize → canonically compare with the original.
Expand Down
4 changes: 3 additions & 1 deletion TODO.complete/14-xsd-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
## Files
- `lib/dcc/validate.rb` — autoloads.
- `lib/dcc/validate/result.rb` — `Dcc::Validate::Result` (model with `ok?`, `issues`, `to_s/to_json/to_yaml`), `Dcc::Validate::Issue` (severity, line, column, message, path), `Dcc::Validate::Severity` enum.
- `lib/dcc/validate/error.rb` — `Dcc::Error::SchemaError` already exists; add helpers here.
- No `lib/dcc/validate/error.rb` was created; `Dcc::SchemaError` lives in `lib/dcc/error.rb`.
`Issue` and `Severity` are their own files (`validate/issue.rb`, `validate/severity.rb`),
not part of `result.rb`.
- `lib/dcc/validate/xsd.rb` — service class:
```ruby
def self.call(xml, version: :auto)
Expand Down
12 changes: 9 additions & 3 deletions TODO.complete/15-schematron.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
# 15 — Pure-Ruby Schematron (P1)

**Status:** COMPLETED
**Status:** PARTIAL

## Gaps
- The profile runs 13 rules, not 14.
`lib/dcc/validate/schematron/profile.rb` omits
`Rules::AdministrativeDataCompleteness`, so that rule never runs during
validation even though the file exists and is autoloaded.

## Goal
Reimplement PTB's 14 Schematron patterns as `Lutaml::Model::Validation::Rule` subclasses operating on the parsed object tree. No Saxon-HE, no JVM.

## Files
- `lib/dcc/validate/schematron.rb` — autoloads + entry point.
- `lib/dcc/validate/schematron/profile.rb` — `Dcc::Validate::Schematron::Profile < Lutaml::Model::Validation::Profile` aggregating all 14 rules.
- `lib/dcc/validate/schematron/profile.rb` — `Dcc::Validate::Schematron::Profile`, a plain class with no superclass, aggregating 13 of the 14 rules (see Gaps).
- `lib/dcc/validate/schematron/rules/` — 14 rule files, one per PTB pattern:
- `used_methods_placement.rb` (error if no usedMethods anywhere)
- `used_software_placement.rb` (warning)
Expand All @@ -24,7 +30,7 @@ Reimplement PTB's 14 Schematron patterns as `Lutaml::Model::Validation::Rule` su
- `xml_list_spacing.rb` (single-space separator, no leading/trailing)
- `administrative_data_completeness.rb` (mandatory sections present)

Each rule subclasses `Lutaml::Model::Validation::Rule`:
Each rule subclasses `Dcc::Validate::Schematron::Rules::Base` (the sketch below predates that base class):

```ruby
module Dcc::Validate::Schematron::Rules
Expand Down
11 changes: 9 additions & 2 deletions TODO.complete/16-business-rules.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# 16 — Business rule validators (P1)

**Status:** COMPLETED
**Status:** PARTIAL

## Gaps
- Only two rules ship: `MainSignerSingle`
(`lib/dcc/validate/business_rules.rb:71`, the renamed
`RespPersonsMainSigner`) and `UniqueIdentifierPresent` (line 94).
- `MandatoryLanguageCoverage`, `StatementRefType` and `PreviousReportHash`
do not exist.

## Goal
Ruby-side semantic checks beyond what Schematron covers — cross-section consistency, signature presence, template coherence.
Expand All @@ -11,7 +18,7 @@ Ruby-side semantic checks beyond what Schematron covers — cross-section consis

## Design notes
- Examples:
- `RespPersonsMainSigner` — exactly one `respPerson` has `mainSigner=true`.
- `RespPersonsMainSigner` — exactly one `respPerson` has `mainSigner=true`. Shipped as `MainSignerSingle`.
- `MandatoryLanguageCoverage` — every `dcc:content` element has the mandatory language code.
- `StatementRefType` — every `statement` with `refType="conformity"` has a `declaration` child.
- `PreviousReportHash` — if `previousReport` present, all hash fields populated.
Expand Down
9 changes: 8 additions & 1 deletion TODO.complete/17-json-converter.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# 17 — JSON converter (P1)

**Status:** COMPLETED
**Status:** PARTIAL

## Gaps
- There is no `style:` parameter. `lib/dcc/convert/json.rb:11` is
`def call(dcc, pretty: true)`, so neither `:ptb_compatible` nor `:lutaml`
can be selected.
- Conversion re-serializes to XML and walks the tree (`xml_to_hash`) rather
than using lutaml-model's JSON mapping DSL.

## Goal
Convert a parsed DCC into JSON. Two styles: `:ptb_compatible` (matches PTB `JSONConverter` output) and `:lutaml` (uses lutaml-model's JSON mapping DSL directly).
Expand Down
11 changes: 10 additions & 1 deletion TODO.complete/18-file-extraction.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# 18 — File extraction (P1)

**Status:** COMPLETED
**Status:** PARTIAL

## Gaps
- Ring detection covers two of the four rings. The walker at
`lib/dcc/extract/file.rb:52-58` assigns only `ADMINISTRATIVE_DATA` and
`MEASUREMENT_RESULTS`, so `in_ring(dcc, ring: :comment)` and
`ring: :document` always return empty.
- `build_file` (line 126) falls back to `ring || Ring::ADMINISTRATIVE_DATA`,
so a `byteData` under the root `document` element is reported as
administrative data.

## Goal
Walk a parsed DCC and enumerate every embedded file (`dcc:byteDataType` element) with its ring (administrativeData / measurementResults / comment / document).
Expand Down
9 changes: 8 additions & 1 deletion TODO.complete/19-formula-extraction.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# 19 — Formula extraction and evaluation (P1)

**Status:** COMPLETED
**Status:** PENDING

## Gaps
- None of the five files exist. `lib/dcc/extract/` contains only `file.rb`
and `ring.rb`.
- `lib/dcc/extract.rb:7` still autoloads `:Formula`, so referencing
`Dcc::Extract::Formula` raises `LoadError`.
- `spec/dcc/extract/formula_spec.rb` was removed — see phase 38.

## Goal
Extract MathML `<ml:apply>` formula trees from `dcc:formula`, build a Ruby AST, and evaluate numerically with BigDecimal precision.
Expand Down
4 changes: 2 additions & 2 deletions TODO.complete/20-signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ Sign a DCC with XMLDSig (enveloped RSA-SHA-256) and verify signed DCCs against a
- `lib/dcc/signature/result.rb` — result model with `to_s`, `to_json`, `to_yaml`.

## Design notes
- Soft-dependency on `xmldsig` gem. If not installed, raise `Dcc::Error::MissingDependencyError` with friendly message.
- Fallback: shell out to `xmlsec1` CLI if `xmldsig` lacks an algorithm.
- Implemented with `Moxml::Signature`, plus OpenSSL for keys and certificates.
Neither the `xmldsig` gem nor an `xmlsec1` fallback is used.
- Extract the signed subtree via `Verifier` so callers know what's actually trustworthy (per PTB warning).

## Dependencies
Expand Down
10 changes: 6 additions & 4 deletions TODO.complete/21-xslt-transform.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
**Status:** COMPLETED

## Goal
Apply XSLT stylesheets to DCC XML. Default engine: Nokogiri (XSLT 1.0). Optional `:saxon` engine shells out to Saxon-HE for XSLT 2.0/3.0.
Apply XSLT stylesheets to DCC XML. Default engine: `:xsltproc`, which shells out to the `xsltproc` binary (XSLT 1.0). Optional `:saxon` engine shells out to Saxon-HE for XSLT 2.0/3.0.

## Files
- `lib/dcc/transform.rb` — autoloads.
- `lib/dcc/transform/xslt.rb` — `Dcc::Transform::Xslt.call(xml, stylesheet, engine: :nokogiri)` returns the transformed String.
- `lib/dcc/transform/xslt.rb` — `Dcc::Transform::Xslt.call(xml, stylesheet, engine: :xsltproc)` returns the transformed String.
- `lib/dcc/transform/result.rb` — result model.

## Design notes
- Nokogiri::XSLT is XSLT 1.0 only — document prominently.
- `xsltproc` is XSLT 1.0 only — document prominently. `Nokogiri::XSLT` is not used.
- Saxon bridge: detect `ENV["SAXON_JAR"]` or `which java`, run `java -cp "$SAXON_JAR" net.sf.saxon.Transform -s:- -xsl:stylesheet.xsl`.
- PTB ships XSLT 3.0 stylesheets for some operations; the Saxon bridge is opt-in.

Expand All @@ -20,4 +20,6 @@ Apply XSLT stylesheets to DCC XML. Default engine: Nokogiri (XSLT 1.0). Optional

## Verification
- Apply identity XSLT to `valid.xml` → output structurally equivalent.
- Spec covers Nokogiri engine; Saxon engine is opt-in (skip if jar absent).
- (Not covered: no spec exercises either engine. Nothing under `spec/` references
`Dcc::Transform::Xslt` or `xsltproc`, so the engine claim below never ran.)
- Saxon engine is opt-in (skip if jar absent).
12 changes: 11 additions & 1 deletion TODO.complete/22-inspect-report.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# 22 — Inspection report (P2)

**Status:** COMPLETED
**Status:** PARTIAL

## Gaps
- `Report` declares 11 attributes and its `to_s` prints the Identity and
Measurement sections only.
- Absent: calibration laboratory, customer and dates from the Administrative
section; the signature `valid?` flag; and the entire Validity section.
- `signature_present?` (`lib/dcc/inspect/report.rb:170`) tests
`dcc.document`, which is the `dcc:document` element rather than a
`ds:Signature`. `has_signature` is therefore wrong for both signed and
unsigned documents.

## Goal
Produce a human-readable summary of a DCC: counts, signature status, validity, languages, key identifiers.
Expand Down
Loading
Loading