diff --git a/TODO.complete/01-scaffolding.md b/TODO.complete/01-scaffolding.md index 58002dc..3ca91c6 100644 --- a/TODO.complete/01-scaffolding.md +++ b/TODO.complete/01-scaffolding.md @@ -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). diff --git a/TODO.complete/04-custom-types.md b/TODO.complete/04-custom-types.md index 2e1e026..c06a734 100644 --- a/TODO.complete/04-custom-types.md +++ b/TODO.complete/04-custom-types.md @@ -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. diff --git a/TODO.complete/05-bundle-schemas-and-fixtures.md b/TODO.complete/05-bundle-schemas-and-fixtures.md index 97b66e8..f0e13d8 100644 --- a/TODO.complete/05-bundle-schemas-and-fixtures.md +++ b/TODO.complete/05-bundle-schemas-and-fixtures.md @@ -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. @@ -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"`. diff --git a/TODO.complete/06-errors.md b/TODO.complete/06-errors.md index 88dc622..0ccfebb 100644 --- a/TODO.complete/06-errors.md +++ b/TODO.complete/06-errors.md @@ -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`. diff --git a/TODO.complete/07-dcc-base-modules.md b/TODO.complete/07-dcc-base-modules.md index 5493b94..f18c235 100644 --- a/TODO.complete/07-dcc-base-modules.md +++ b/TODO.complete/07-dcc-base-modules.md @@ -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`. @@ -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: diff --git a/TODO.complete/08-dcc-v2-wrappers.md b/TODO.complete/08-dcc-v2-wrappers.md index a611100..68b7d96 100644 --- a/TODO.complete/08-dcc-v2-wrappers.md +++ b/TODO.complete/08-dcc-v2-wrappers.md @@ -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.) diff --git a/TODO.complete/09-dcc-v3-wrappers.md b/TODO.complete/09-dcc-v3-wrappers.md index c8e765f..8d5876b 100644 --- a/TODO.complete/09-dcc-v3-wrappers.md +++ b/TODO.complete/09-dcc-v3-wrappers.md @@ -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`. @@ -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). diff --git a/TODO.complete/10-d-si-model.md b/TODO.complete/10-d-si-model.md index fc1d0e3..87c1988 100644 --- a/TODO.complete/10-d-si-model.md +++ b/TODO.complete/10-d-si-model.md @@ -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` diff --git a/TODO.complete/11-wire-dcc-dsi.md b/TODO.complete/11-wire-dcc-dsi.md index 281b738..5dd1558 100644 --- a/TODO.complete/11-wire-dcc-dsi.md +++ b/TODO.complete/11-wire-dcc-dsi.md @@ -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. diff --git a/TODO.complete/12-root-parser-api.md b/TODO.complete/12-root-parser-api.md index 9ff4cba..01aa8f7 100644 --- a/TODO.complete/12-root-parser-api.md +++ b/TODO.complete/12-root-parser-api.md @@ -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 diff --git a/TODO.complete/13-round-trip-specs.md b/TODO.complete/13-round-trip-specs.md index 65b8dea..1c3403a 100644 --- a/TODO.complete/13-round-trip-specs.md +++ b/TODO.complete/13-round-trip-specs.md @@ -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. diff --git a/TODO.complete/14-xsd-validation.md b/TODO.complete/14-xsd-validation.md index f32da18..fd71200 100644 --- a/TODO.complete/14-xsd-validation.md +++ b/TODO.complete/14-xsd-validation.md @@ -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) diff --git a/TODO.complete/15-schematron.md b/TODO.complete/15-schematron.md index a1324e1..03c4c1d 100644 --- a/TODO.complete/15-schematron.md +++ b/TODO.complete/15-schematron.md @@ -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) @@ -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 diff --git a/TODO.complete/16-business-rules.md b/TODO.complete/16-business-rules.md index 665c505..530f7b4 100644 --- a/TODO.complete/16-business-rules.md +++ b/TODO.complete/16-business-rules.md @@ -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. @@ -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. diff --git a/TODO.complete/17-json-converter.md b/TODO.complete/17-json-converter.md index 03109d5..2ef2219 100644 --- a/TODO.complete/17-json-converter.md +++ b/TODO.complete/17-json-converter.md @@ -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). diff --git a/TODO.complete/18-file-extraction.md b/TODO.complete/18-file-extraction.md index 6b806cf..220f034 100644 --- a/TODO.complete/18-file-extraction.md +++ b/TODO.complete/18-file-extraction.md @@ -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). diff --git a/TODO.complete/19-formula-extraction.md b/TODO.complete/19-formula-extraction.md index db4074d..5c7a980 100644 --- a/TODO.complete/19-formula-extraction.md +++ b/TODO.complete/19-formula-extraction.md @@ -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 `` formula trees from `dcc:formula`, build a Ruby AST, and evaluate numerically with BigDecimal precision. diff --git a/TODO.complete/20-signature.md b/TODO.complete/20-signature.md index 2bf9166..518af79 100644 --- a/TODO.complete/20-signature.md +++ b/TODO.complete/20-signature.md @@ -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 diff --git a/TODO.complete/21-xslt-transform.md b/TODO.complete/21-xslt-transform.md index 747580a..1a8c26d 100644 --- a/TODO.complete/21-xslt-transform.md +++ b/TODO.complete/21-xslt-transform.md @@ -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. @@ -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). diff --git a/TODO.complete/22-inspect-report.md b/TODO.complete/22-inspect-report.md index 1e70002..0e45c7c 100644 --- a/TODO.complete/22-inspect-report.md +++ b/TODO.complete/22-inspect-report.md @@ -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. diff --git a/TODO.complete/23-output-models.md b/TODO.complete/23-output-models.md index 5838e12..e37cc69 100644 --- a/TODO.complete/23-output-models.md +++ b/TODO.complete/23-output-models.md @@ -1,6 +1,11 @@ # 23 — Output models with format transforms (P2) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- `lib/dcc/output.rb` does not exist; the shared concern was never written. +- Five result models define `to_json`/`to_yaml` by hand. `Dcc::Extract::File` + defines `to_s` only, so the verification below is false for it. ## Goal Establish the pattern: every command/service returns a model object that responds to `to_s` (human-readable), `to_json` (machine), `to_yaml` (Ruby). The CLI picks the formatter based on `--format`. diff --git a/TODO.complete/24-cli-scaffold.md b/TODO.complete/24-cli-scaffold.md index 78645c4..b64efab 100644 --- a/TODO.complete/24-cli-scaffold.md +++ b/TODO.complete/24-cli-scaffold.md @@ -1,6 +1,15 @@ # 24 — CLI scaffold (P2) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- Three command groups are missing: `dcc signature sign|verify`, + `dcc transform xslt`, and `dcc issue`. +- `dcc extract` handles `files` only; `formulae`, `quantities` and `table` + abort with "Unknown extract target". +- The commands live inline in `lib/dcc/cli/cli.rb`; `lib/dcc/cli/commands/` + was never created. +- An undocumented `dcc serve` command exists. ## Goal Thor-based CLI dispatching to all Dcc services with human-readable default output and `--format json|yaml` switches. diff --git a/TODO.complete/25-yaml-csv-html-converters.md b/TODO.complete/25-yaml-csv-html-converters.md index 595d75e..ed9bf18 100644 --- a/TODO.complete/25-yaml-csv-html-converters.md +++ b/TODO.complete/25-yaml-csv-html-converters.md @@ -1,6 +1,12 @@ # 25 — YAML/CSV/HTML converters (P2) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- The HTML report is a flat table. + `lib/dcc/convert/templates/report.liquid` has no collapsible tree — no + `
`, no toggle script — and no language selector; languages appear + as static cells at lines 117-118. ## Goal Additional output formats beyond JSON: YAML (Ruby-friendly), CSV (tabular quantity export), HTML (human-readable report). @@ -12,7 +18,8 @@ Additional output formats beyond JSON: YAML (Ruby-friendly), CSV (tabular quanti ## Design notes - Each converter returns a `Dcc::Convert::Result` model — never a raw String. -- HTML converter uses ERB templates from `lib/dcc/convert/templates/`. +- HTML converter uses Liquid templates from `lib/dcc/convert/templates/` + (`report.liquid`), with helpers in `lib/dcc/convert/liquid_filters.rb`. ## Dependencies - 17. diff --git a/TODO.complete/26-builder-dsl.md b/TODO.complete/26-builder-dsl.md index 65bb92c..c82308d 100644 --- a/TODO.complete/26-builder-dsl.md +++ b/TODO.complete/26-builder-dsl.md @@ -1,6 +1,14 @@ # 26 — Builder DSL (P2) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- `lib/dcc/builder/dsl.rb` is an empty placeholder; the DSL lives on + `Session`. +- `Session` has no `result`, `quantity` or `data` methods, so a built DCC + cannot contain measured values. +- `spec/dcc/builder/builder_spec.rb` has three examples, not coverage of + every DSL method. ## Goal Programmatic DCC creation: `Dcc.build(version: 3) { administrative_data { ... } }` returns a `Dcc::V3::DigitalCalibrationCertificate` instance ready to serialize. @@ -9,7 +17,7 @@ Programmatic DCC creation: `Dcc.build(version: 3) { administrative_data { ... } - `lib/dcc/builder.rb` — autoloads. - `lib/dcc/builder/session.rb` — builder session tracking the current container. - `lib/dcc/builder/dsl.rb` — DSL methods (`administrative_data`, `core_data`, `items`, `item`, `measurement_results`, `result`, `quantity`, etc.). -- `lib/dcc/builder/error.rb` — uses `Dcc::Error::BuilderError`. +- No `lib/dcc/builder/error.rb` was created; the builder raises `Dcc::BuilderError` from `lib/dcc/error.rb`. ## Design notes - DSL is evaluated in `Session#instance_eval` so blocks feel natural. @@ -21,4 +29,5 @@ Programmatic DCC creation: `Dcc.build(version: 3) { administrative_data { ... } ## Verification - Build a minimal DCC, serialize, re-parse, assert key fields match. -- Spec covers every DSL method. +- (Not covered: `spec/dcc/builder/builder_spec.rb` has three examples and does not + reach every DSL method. See the gaps above.) diff --git a/TODO.complete/27-diff-tool.md b/TODO.complete/27-diff-tool.md index d3a1705..c246442 100644 --- a/TODO.complete/27-diff-tool.md +++ b/TODO.complete/27-diff-tool.md @@ -9,11 +9,11 @@ Structural diff of two DCCs — what items/quantities/results were added, remove - `lib/dcc/diff.rb` — autoloads. - `lib/dcc/diff/result.rb` — `Dcc::Diff::Result` model with `changes` (Array), `to_s` (unified-diff style), `to_json`, `to_yaml`. - `lib/dcc/diff/change.rb` — `Change` value object (`path`, `kind` ∈ {add, remove, change}, `before`, `after`). -- `lib/dcc/diff/comparator.rb` — recursive tree comparator with sensible equality rules (BigDecimal vs Float, language-tagged text). +- No `lib/dcc/diff/comparator.rb` was created; the recursive comparator is `Dcc::Diff.collect_differences` in `lib/dcc/diff.rb`. ## Design notes -- Identity keys: `uniqueIdentifier` for certificates, `id`/`refId` for elements, `name`+`refType` for quantities. -- Numeric tolerance configurable (default 1e-9). +- Neither identity keys nor a configurable numeric tolerance is implemented. + The comparator walks attributes recursively and compares collections positionally. ## Dependencies - 12. diff --git a/TODO.complete/28-migration.md b/TODO.complete/28-migration.md index 976535e..99229b1 100644 --- a/TODO.complete/28-migration.md +++ b/TODO.complete/28-migration.md @@ -1,6 +1,13 @@ # 28 — Version migration (P2) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- `lib/dcc/migrate/` does not exist: `route.rb`, `v2_to_v3.rb` and + `v3_to_v3_4.rb` are all absent. +- `lib/dcc/migrate.rb` is a 41-line shim that serializes, re-parses under the + target context and rewrites `schemaVersion`. There are no per-field + transformations and no D-SI v1 → v2 transition. ## Goal `Dcc.migrate(dcc, from:, to:)` upgrades or downgrades a DCC between schema versions, applying the necessary field renames and D-SI version transitions. diff --git a/TODO.complete/29-uncertainty-propagation.md b/TODO.complete/29-uncertainty-propagation.md index 333860d..275a476 100644 --- a/TODO.complete/29-uncertainty-propagation.md +++ b/TODO.complete/29-uncertainty-propagation.md @@ -1,6 +1,10 @@ # 29 — Uncertainty propagation (P2) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- `lib/dcc/quantity_math/complex.rb` and `matrix.rb` do not exist, so complex + arithmetic and covariance-aware multivariate operations are unavailable. ## Goal BigDecimal-based arithmetic on D-SI quantities that propagates uncertainty via root-sum-square (RSS) for add/sub and fractional for mul/div. diff --git a/TODO.complete/30-quantity-format.md b/TODO.complete/30-quantity-format.md index 2459805..60037cb 100644 --- a/TODO.complete/30-quantity-format.md +++ b/TODO.complete/30-quantity-format.md @@ -1,13 +1,25 @@ # 30 — Quantity pretty-printing (P2) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- The documented output is wrong. For `value: 42.0, uncertainty: 0.05, + unit: "\kelvin"` the formatter returns `42.0(05) kelvin` (short), + `42.0 ± 0.05 kelvin` (long) and `\qty{42.0 +- 0.05}{\kelvin}` (latex) — + not `42.00(5) K`. The value is not padded to the uncertainty's precision, + the compact uncertainty keeps its leading zero, and the unit is not + rendered as a symbol. +- `lib/dcc/quantity_format/formatter.rb` never requires `bigdecimal`, so + `Formatter.new` raises `NoMethodError` in isolation. The suite passes only + because `lib/dcc/validate/schematron/profile.rb` requires it first. ## Goal SmartCom-style human-readable formatting of D-SI quantities with proper significant digits and unit composition. ## Files - `lib/dcc/quantity_format.rb` — autoloads. -- `lib/dcc/quantity_format/formatter.rb` — `Dcc::QuantityFormat::Formatter.call(quantity, format: :short|:long|:latex)`. +- `lib/dcc/quantity_format/formatter.rb` — `Dcc::QuantityFormat::Formatter.new(value:, uncertainty:, unit:)` + with `#to_short`, `#to_long` and `#to_latex`. - Templates for each format style. ## Design notes diff --git a/TODO.complete/31-rest-api.md b/TODO.complete/31-rest-api.md index 406cc5a..8dcfc23 100644 --- a/TODO.complete/31-rest-api.md +++ b/TODO.complete/31-rest-api.md @@ -1,6 +1,11 @@ # 31 — REST API server (P3, optional) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- `lib/dcc/server/app.rb` serves three routes: `GET /`, `GET /demo.xml` and + `POST /validate`. None of the PTB endpoints listed above exist. +- Storage ships `Memory` only; the `Disk` and `Redis` backends do not exist. ## Goal Rack/Sinatra REST API matching PTB's endpoints: `/dccs`, `/dccs/:id/validate/{xsd,schematron}`, `/dccs/:id/convert/json`, `/dccs/:id/extract/{files,formulae}`, `/dccs/:id/signature`, `/dccs/:id/transform/xslt`. diff --git a/TODO.complete/32-i18n-helpers.md b/TODO.complete/32-i18n-helpers.md index fec3fdd..9b0eaac 100644 --- a/TODO.complete/32-i18n-helpers.md +++ b/TODO.complete/32-i18n-helpers.md @@ -1,6 +1,13 @@ # 32 — i18n / multilingual navigation helpers (P3) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- `TextLookup.call` never raises. Missing mandatory-language coverage falls + through to the first content element regardless of language, which is the + failure this helper was meant to catch. +- The signature is `call(text_obj, dcc:, lang:)`; there is no `fallback:` + parameter. ## Goal Pick the best language from `dcc:textType`, validate language coverage, and provide fallback rules for multilingual content. @@ -17,4 +24,7 @@ Pick the best language from `dcc:textType`, validate language coverage, and prov - 12. ## Verification -- Spec covers happy path + missing-language fallback + mandatory-coverage failure. +- Spec covers the happy path: a requested language, `lang: nil` falling back to the + declared ones, and nil input. +- (Not covered: `spec/dcc/i18n/text_lookup_spec.rb` has three examples and reaches + neither missing-language fallback nor any mandatory-coverage failure.) diff --git a/TODO.complete/33-streaming-parser.md b/TODO.complete/33-streaming-parser.md index ca89c40..dbeea3e 100644 --- a/TODO.complete/33-streaming-parser.md +++ b/TODO.complete/33-streaming-parser.md @@ -1,6 +1,11 @@ # 33 — Streaming parser for large documents (P3) -**Status:** COMPLETED +**Status:** PENDING + +## Gaps +- Neither `lib/dcc/streaming.rb` nor `lib/dcc/streaming/reader.rb` exists. +- Nothing in `lib/` uses `Nokogiri::XML::Reader`. +- `spec/dcc/streaming/reader_spec.rb` was removed — see phase 38. ## Goal Lazy enumeration of items / results / quantities for multi-MB DCC documents using Nokogiri::XML::Reader. diff --git a/TODO.complete/34-plugin-system.md b/TODO.complete/34-plugin-system.md index 5190785..769dc09 100644 --- a/TODO.complete/34-plugin-system.md +++ b/TODO.complete/34-plugin-system.md @@ -1,6 +1,15 @@ # 34 — Plugin system (P3) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- `lib/dcc/plugin/base.rb` does not exist, and there is no + `Dcc.load_plugins` or gem-prefix auto-discovery. +- Nothing consumes the registry. `Dcc::Validate`, `Dcc::Convert` and + `Dcc::Cli` never read `Dcc::Plugin.all`, so a registered plugin has no + effect on validation, conversion or the CLI. +- `spec/dcc/plugin_spec.rb` asserts only that the registry stores and returns + objects. ## Goal Registry-based plugin system for custom validators, converters, and CLI commands. diff --git a/TODO.complete/35-documentation.md b/TODO.complete/35-documentation.md index 050b8d5..a1a82ef 100644 --- a/TODO.complete/35-documentation.md +++ b/TODO.complete/35-documentation.md @@ -1,6 +1,16 @@ # 35 — Documentation (P3) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- `docs/api/` does not exist, and `yard` is not in the Gemfile, so + `bundle exec yard doc` fails. +- Five example scripts are absent: `sign_with_xmldsig.rb`, + `migrate_v2_to_v3.rb`, `diff_two_dccs.rb`, + `extract_and_evaluate_formula.rb`, `streaming_processor.rb`. Two more + shipped under different names — `validate_batch.rb` as `validate_full.rb`, + `convert_to_json.rb` as `convert_all_formats.rb`. +- No RSpec doctest wraps the examples. ## Goal Polished documentation for users and contributors: README, examples, YARD docs, getting-started guide. diff --git a/TODO.complete/36-remove-require-relative.md b/TODO.complete/36-remove-require-relative.md index 79720c4..f082d0c 100644 --- a/TODO.complete/36-remove-require-relative.md +++ b/TODO.complete/36-remove-require-relative.md @@ -21,6 +21,7 @@ The base `Dcc::Validate::Schematron::Rule` is autoloaded from parent `lib/dcc/validate/schematron/rules.rb` aggregator. ## Verification -- `grep -rln require_relative lib/` returns nothing +- `grep -rn "^\s*require_relative" lib/` returns nothing (an unanchored grep + still matches a comment in `rules.rb:6`) - `bundle exec rspec` still passes - Schematron rules load on first use (lazy) diff --git a/TODO.complete/37-implement-stub-schematron-rules.md b/TODO.complete/37-implement-stub-schematron-rules.md index 7796f58..79d1517 100644 --- a/TODO.complete/37-implement-stub-schematron-rules.md +++ b/TODO.complete/37-implement-stub-schematron-rules.md @@ -1,6 +1,13 @@ # 37 — Implement stub Schematron rules (P1) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- `AdministrativeDataCompleteness` is not in the Schematron profile, so it + never runs during validation — see phase 15. +- The specs do not exercise violations. All four examples in + `spec/dcc/validate/schematron_stubs_spec.rb` parse `valid.xml` and assert + only that the result is an Array. ## Goal Four Schematron rules currently return `[]` unconditionally. Implement diff --git a/TODO.complete/38-convert-pending-specs.md b/TODO.complete/38-convert-pending-specs.md index 5c98bb9..86cbb5a 100644 --- a/TODO.complete/38-convert-pending-specs.md +++ b/TODO.complete/38-convert-pending-specs.md @@ -2,6 +2,12 @@ **Status:** COMPLETED +## Outcome +Two of the specs listed below were removed rather than fixed, because the +features they cover do not exist: `spec/dcc/extract/formula_spec.rb` +(phase 19) and `spec/dcc/streaming/reader_spec.rb` (phase 33). That is +option 3 in the approach below. The remaining three were made to pass. + ## Goal 5 specs use `pending`/`skip`. Convert each to a real, passing spec or remove it if the feature truly isn't implemented. diff --git a/TODO.complete/40-wire-dcc-v2-quantity-to-dsi-v1.md b/TODO.complete/40-wire-dcc-v2-quantity-to-dsi-v1.md index b552624..f0ce0ab 100644 --- a/TODO.complete/40-wire-dcc-v2-quantity-to-dsi-v1.md +++ b/TODO.complete/40-wire-dcc-v2-quantity-to-dsi-v1.md @@ -13,4 +13,5 @@ for `Dcc::V2::Quantity` against D-SI v1. ## Verification - Parse a v2 fixture with embedded D-SI quantities -- Spec covers V2 quantity access +- (Not covered: the only spec naming `Dcc::V2` is `spec/dcc_spec.rb`, asserting + `parser_for(2)` returns the module. Nothing reaches a v2 quantity.) diff --git a/TODO.complete/41-yard-documentation.md b/TODO.complete/41-yard-documentation.md index 8d78c4d..68ace47 100644 --- a/TODO.complete/41-yard-documentation.md +++ b/TODO.complete/41-yard-documentation.md @@ -1,6 +1,12 @@ # 41 — Add YARD documentation throughout (P2) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- Five of the files listed below carry no annotations at all: + `lib/dcc/convert/json.rb`, `convert/yaml.rb`, `convert/html.rb`, + `lib/dcc/quantity_math.rb`, `lib/dcc/quantity_format.rb`. +- `yard` is not in the Gemfile, so neither verification step can run. ## Goal Add YARD-style `# @!method` and `# @param`/`# @return` annotations to diff --git a/TODO.complete/42-changelog-contributing.md b/TODO.complete/42-changelog-contributing.md index 1acfb99..c0edb38 100644 --- a/TODO.complete/42-changelog-contributing.md +++ b/TODO.complete/42-changelog-contributing.md @@ -1,6 +1,11 @@ # 42 — Add CHANGELOG + CONTRIBUTING + LICENSE note (P2) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- `CHANGELOG.adoc`'s `[Unreleased]` section is inaccurate. It claims YARD + documentation throughout the public API (phase 41 is incomplete) and + performance memoization for hot paths (phase 43 is not started). ## Goal Project-level docs for users and contributors. diff --git a/TODO.complete/43-performance-memoization.md b/TODO.complete/43-performance-memoization.md index d12f3fa..35ce43a 100644 --- a/TODO.complete/43-performance-memoization.md +++ b/TODO.complete/43-performance-memoization.md @@ -1,6 +1,14 @@ # 43 — Performance: memoize schema lookups (P2) -**Status:** COMPLETED +**Status:** PENDING + +## Gaps +- `Dcc::Schema.path` (`lib/dcc/schema.rb:19-21`) is a bare `File.join` with + no cache. +- `detect_version` is not memoized. +- `spec/perf_spec.rb` does not exist; the benchmark was never run. +- Only the pre-existing `Dcc::Validate::Xsd` cache + (`lib/dcc/validate/xsd.rb:44`) is in place. ## Goal `Dcc::Validate::Xsd` already memoizes the loaded schema, but other diff --git a/TODO.complete/45-eliminate-respond-to.md b/TODO.complete/45-eliminate-respond-to.md index 0979846..a5fc403 100644 --- a/TODO.complete/45-eliminate-respond-to.md +++ b/TODO.complete/45-eliminate-respond-to.md @@ -1,6 +1,12 @@ # 45 — Eliminate all respond_to? calls from lib/ (P0) -**Status:** COMPLETED +**Status:** PARTIAL + +## Gaps +- Six `respond_to?` calls remain in `lib/`, none of them the exempted CLI + `$stdout.tty?` case: + `validate/schematron/rules/unit_format_check.rb:34`, `:47`, `:50`; + `non_si_declaration.rb:49`; `id_refid_linking.rb:41`, `:44`. ## Goal 67 `respond_to?` calls remain in lib/ despite the project rule: diff --git a/TODO.complete/README.md b/TODO.complete/README.md index a50b0b3..379b338 100644 --- a/TODO.complete/README.md +++ b/TODO.complete/README.md @@ -1,23 +1,37 @@ # TODO.complete — dcc gem work breakdown This directory holds the actionable work breakdown for the dcc gem. Each -file is one self-contained phase. See the master plan at -`/Users/mulgogi/.claude/plans/investigate-src-ptb-dcclib-src-ptb-dccex-fluffy-pike.md` -for the full architecture and design rationale. +file is one self-contained phase. The original master plan is not tracked +in this repository. + +Statuses describe work merged into the default branch. See the +repository's open pull requests for work in progress. + +Statuses were last audited against commit `9fbbb4a`. They are a snapshot — +re-verify against the current tree before relying on them. ## Priority levels -- **P0** — foundation; blocks all subsequent work. Phases 01-06. -- **P1** — core feature parity with PTB Python dcclib. Phases 07-19. -- **P2** — features that exceed PTB ("the fantastic extras"). Phases 20-30. -- **P3** — optional / nice-to-have. Phases 31-35. +- **P0** — foundation; blocks all subsequent work. +- **P1** — core feature parity with PTB Python dcclib. +- **P2** — features that exceed PTB ("the fantastic extras"). +- **P3** — optional / nice-to-have. + +Each phase's own priority is in its file header. Phases 36-45 are +follow-up work added after the original 01-35 breakdown and mix all four +levels. ## Status legend - `COMPLETED` — phase is done and verified. -- `IN PROGRESS` — actively being worked. +- `PARTIAL` — the core goal works, but a named deliverable is missing, + is not wired into anything, or behaves differently than this file + describes. Each such file has a `## Gaps` section with the specifics. - `PENDING` — not yet started. +There is no `IN PROGRESS`. These statuses describe the default branch, and work in +flight is not on it yet. Check the open pull requests for that. + ## Phase index | # | Pri | Title | Status | Blocked by | @@ -25,46 +39,93 @@ for the full architecture and design rationale. | 01 | P0 | Scaffolding | COMPLETED | — | | 02 | P0 | Namespace classes | COMPLETED | 01 | | 03 | P0 | ContextConfiguration + VersionedParser | COMPLETED | 01 | -| 04 | P0 | Custom types | COMPLETED | 02 | +| 04 | P0 | Custom types | PARTIAL | 02 | | 05 | P0 | Bundle XSD/Schematron resources + fixtures | COMPLETED | 01 | | 06 | P0 | Error hierarchy | COMPLETED | 01 | -| 07 | P1 | DCC base mapping modules | COMPLETED | 02, 03, 04 | +| 07 | P1 | DCC base mapping modules | PARTIAL | 02, 03, 04 | | 08 | P1 | Dcc::V2 wrappers | COMPLETED | 07 | -| 09 | P1 | Dcc::V3 wrappers | COMPLETED | 07 | -| 10 | P1 | D-SI model | COMPLETED | 04 | +| 09 | P1 | Dcc::V3 wrappers | PARTIAL | 07 | +| 10 | P1 | D-SI model | PARTIAL | 04 | | 11 | P1 | Wire DCC ↔ D-SI | COMPLETED | 09, 10 | | 12 | P1 | Root parser API | COMPLETED | 08, 09, 11 | -| 13 | P1 | Round-trip fidelity specs | COMPLETED | 12 | +| 13 | P1 | Round-trip fidelity specs | PARTIAL | 12 | | 14 | P1 | XSD validation | COMPLETED | 05, 12 | -| 15 | P1 | Pure-Ruby Schematron | COMPLETED | 12 | -| 16 | P1 | Business rule validators | COMPLETED | 12 | -| 17 | P1 | JSON converter | COMPLETED | 12 | -| 18 | P1 | File extraction | COMPLETED | 12 | +| 15 | P1 | Pure-Ruby Schematron | PARTIAL | 12 | +| 16 | P1 | Business rule validators | PARTIAL | 12 | +| 17 | P1 | JSON converter | PARTIAL | 12 | +| 18 | P1 | File extraction | PARTIAL | 12 | | 19 | P1 | Formula extraction | PENDING | 12 | | 20 | P2 | XMLDSig signature | COMPLETED | 12 | | 21 | P2 | XSLT transform | COMPLETED | 01 | -| 22 | P2 | Inspection report | COMPLETED | 12 | -| 23 | P2 | Output models with format transforms | COMPLETED | 14-19 | -| 24 | P2 | CLI scaffold | COMPLETED | 14-22 | -| 25 | P2 | YAML/CSV/HTML converters | COMPLETED | 17 | -| 26 | P2 | Builder DSL | COMPLETED | 09, 11 | +| 22 | P2 | Inspection report | PARTIAL | 12 | +| 23 | P2 | Output models with format transforms | PARTIAL | 14-19 | +| 24 | P2 | CLI scaffold | PARTIAL | 14-22 | +| 25 | P2 | YAML/CSV/HTML converters | PARTIAL | 17 | +| 26 | P2 | Builder DSL | PARTIAL | 09, 11 | | 27 | P2 | DCC diff tool | COMPLETED | 12 | -| 28 | P2 | Version migration | COMPLETED | 08, 09 | -| 29 | P2 | Uncertainty propagation | COMPLETED | 10 | -| 30 | P2 | Quantity pretty-printing | COMPLETED | 10 | -| 31 | P3 | REST API server | PENDING | 24 | -| 32 | P3 | i18n navigation helpers | PENDING | 12 | +| 28 | P2 | Version migration | PARTIAL | 08, 09 | +| 29 | P2 | Uncertainty propagation | PARTIAL | 10 | +| 30 | P2 | Quantity pretty-printing | PARTIAL | 10 | +| 31 | P3 | REST API server | PARTIAL | 24 | +| 32 | P3 | i18n navigation helpers | PARTIAL | 12 | | 33 | P3 | Streaming parser | PENDING | 12 | -| 34 | P3 | Plugin system | PENDING | 14-19 | -| 35 | P3 | Documentation | PENDING | all | +| 34 | P3 | Plugin system | PARTIAL | 14-19 | +| 35 | P3 | Documentation | PARTIAL | all | +| 36 | P0 | Remove require_relative in Schematron rules | COMPLETED | — | +| 37 | P1 | Implement stub Schematron rules | PARTIAL | 15 | +| 38 | P1 | Convert pending specs to passing specs | COMPLETED | — | +| 39 | P1 | Complete Dcc::Si::V1 wrappers | COMPLETED | 10 | +| 40 | P2 | Wire Dcc::V2::Quantity to D-SI v1 | COMPLETED | 10, 39 | +| 41 | P2 | YARD documentation | PARTIAL | — | +| 42 | P2 | CHANGELOG + CONTRIBUTING | PARTIAL | — | +| 43 | P2 | Performance: memoize schema lookups | PENDING | — | +| 44 | P3 | Final code-quality pass + rubocop | COMPLETED | — | +| 45 | P0 | Eliminate respond_to? calls from lib/ | PARTIAL | — | + +Totals: 17 COMPLETED, 25 PARTIAL, 3 PENDING. ## Critical path 01 → 02, 03, 04, 05, 06 (parallel) → 07 → 08, 09, 10 (parallel) → 11 → 12. -After 12 (root parser), feature phases 14-30 parallelize and are now all done. +After 12 (root parser), the feature phases 13-35 parallelize. Most of +them landed, but several shipped without a named deliverable — see the +PARTIAL rows above. ## Remaining -P1 phase 19 (Formula extraction) and P3 phases 31-35 remain as -follow-up work. Each is documented in its TODO file with concrete next -steps. +**PENDING — not started.** + +- **19** Formula extraction. None of the five files exist, and + `lib/dcc/extract.rb:7` autoloads `:Formula`, so referencing + `Dcc::Extract::Formula` raises `LoadError`. +- **33** Streaming parser. Neither file exists; nothing in `lib/` uses + `Nokogiri::XML::Reader`. +- **43** Performance memoization. Only the pre-existing + `Dcc::Validate::Xsd` cache is in place; `Schema.path` and + `detect_version` are uncached and `spec/perf_spec.rb` does not exist. + +**PARTIAL — usable, with a documented gap.** Each file's `## Gaps` +section has the detail. + +- Model coverage: **07** (six v3.4.0-rc.2 modules), **09** (`:dcc_v3_4` + context never registered), **10** (D-SI multivariate uncertainty), + **04** (`Base64Binary` returns text, `SchemaVersion` lacks + minor/patch). +- Validation: **15** and **37** (`AdministrativeDataCompleteness` is + written but absent from the profile, so it never runs), **16** (two of + the four business rules). +- Output and conversion: **17** (no `style:` selector), **22** (missing + report sections; `has_signature` reads the wrong element), **23** (no + shared `Dcc::Output` concern), **25** (HTML report has no collapsible + tree or language selector), **30** (documented example does not match + the output). +- Features: **18** (only two of four rings detected), **24** (three CLI + command groups missing), **26** (builder cannot express measured + values), **28** (migration is a re-parse shim), **29** (no complex or + matrix arithmetic), **31** (none of the PTB endpoints), **32** (missing + language coverage never raises), **34** (nothing consumes the plugin + registry). +- Housekeeping: **13** (round-trip specs assert a substring, not + canonical equivalence), **35** and **41** (`yard` is not in the + Gemfile), **42** (CHANGELOG overstates 41 and 43), **45** (six + `respond_to?` calls remain).