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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,34 @@ Releases are automated. To ship a release:

For emergency manual releases (bypassing the PR flow), trigger the `Release on merge` workflow manually via `workflow_dispatch`, providing the version input.

## 1.18.0 (2026-10-17)

This release adds published color states and emitter channels beside the tested baseline for tunable-white, dim-to-warm and color-mixing luminaires. The shared resolver returns the baseline first and then the other published states, preserving each state's own colorimetry, spectrum, alpha-opic metrics and flux. Builder 0.11.0 projects the two numeric white-range endpoints and applies the new endpoint grading rows.

### For consumers

The existing top-level measurement blocks remain the tested baseline. `color_tuning.baseline_role` identifies its role; `states` lists only other states. The nominal CCT bin takes precedence over measured CCT, with `configuration.tested_conditions.nominal_cct_at_test` first for the baseline. The generated `index.cct_range_low_k` and `index.cct_range_high_k` appear together only when both endpoints resolve. A tunable range filter also checks `color_tunability` for `tunable_white` or `dim_to_warm`; a dark-sky filter uses the larger of the high endpoint and the numeric tested CCT.

Stored tunable-white or dim-to-warm records without both endpoints fall below Standard after rebuilding the index. Both endpoint CCTs are required for Standard; endpoint CRI Ra and melanopic DER are required for Full. Existing grading rows continue to read the baseline only. Settings, dim-level points, channels and endpoint spectra never add a gate. Static-white records and records without a color-tunability capability cannot carry a `color_tuning` block. Every stored index should be rebuilt for builder 0.11.0.

### Grading

The public gating table gains two pointer rows: Standard `/color_tuning` (`datasheet_pdf`, `identity`) and Full `/color_tuning/states` (`datasheet_pdf`, `CIE 13.3`). The scope manifest remains `scope_version` 2.0.0, with 71 gating rows, 73 enrichment rows, two observation rows and 61 gating pointer rows. The two new enrichment rows ask for channels on color-mixing records and endpoint spectra when a color-tuning block is present. The eight existing examples keep their grades and change only `index.builder_version`; the two color-mixing validate golden pairs gain the channel nudge.

### Validator and workbook

`color-tuning/inconsistent` reports duplicate state or channel identifiers, a lone endpoint, a reversed range, a state outside a tunable-white or dim-to-warm range, repeated CCT/dim-level/case-folded-label identities, and shared CCT/dim-level states missing a label. The schema prevents repeating the baseline endpoint and permits at most one listed end of each role. Dim-level roles require a percent in (0, 100]; setting roles forbid a percent.

The workbook adds `color_states` and `emission_channels`, the three `color_tuning_baseline_*` records columns, and `state_id`/`channel_id` on the spectrum sheet. Blank spectrum identifiers still target the baseline. Each new provenanced measurement column has provenance companions and defaults to rated, datasheet_pdf, transcribed. Dim-level percentages are scalar metadata without provenance companions. The converter refuses malformed or ambiguous state/channel rows with sheet, row and record context. Use a 1.18.0 converter or newer for the new sheets; older converters ignore them. Newly converted records default to 1.18.0.

### Examples

A ninth example records one public Lumenpulse Lumencore Linear Graze LCVA configuration: 48 in, 13 W/ft, 10x10, DMX/RDM, NA/NH. The 4000 K IES baseline matches the sheet's explicit 4000 K table and unscaled file. Its 1800 K and 6500 K endpoints carry the measured CRI and TM-30 values printed in the supplied reports, plus the original 401-sample spectra. One claimed LM-79 attestation anchors measured values under the existing evidence-family policy. The sheet, IES, endpoint reports and spectral files are individually hashed source documents; none is committed. Endpoint flux, melanopic DER and through-optics status remain absent because the files do not state them. The example is schema-valid at the incomplete floor, with its ordinary missing-data roadmap.

### Documentation

The methodology defines color-state identity, the single CCT rule, baseline ownership and endpoint grading. The workbook README and converter design document describe the two authoring sheets and per-state spectra. The README, roadmap and PIM guide examples advance to 1.18.0.

## 1.17.0 (2026-10-14)

This release adds a workbook authoring path for the TM-30 hue-bin local fidelity values a record carries at `colorimetry.tm_30.rf_h_per_bin`, so the Full row that asks for them can be met from a workbook. The schema structure and taxonomy do not change; the array's description is corrected to name the one value its entries carry. No required set, conformance grade, achievement state, or generated index member changes. The builder remains 0.10.0, and all eight published examples remain byte-identical.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ To check the current state of manufacturer adoption (which manufacturers have pu

## Project status

The current release is `1.17.0`. ULC computes two orthogonal axes rather than declaring them: the reference builder grades each record against three completeness grades (`core`, `standard`, `full`) above an `incomplete` floor, and computes the Product Achievements axis (per theme, `none`, `claimed`, or `documented`) beside it, stamping both into the generated index. The toolchain ships the split schema and taxonomy, the drift-guard tooling, the reference command-line validator and compiler (`ulc`) with schema validation, builder parity, and source-file hash verification, the deterministic `from-sheet` workbook-to-record converter, the fill-in workbook template under `templates/workbook/`, and the PIM platform mapping guides under `mappings/pim/`. See `CHANGELOG.md` for the full release history.
The current release is `1.18.0`. ULC computes two orthogonal axes rather than declaring them: the reference builder grades each record against three completeness grades (`core`, `standard`, `full`) above an `incomplete` floor, and computes the Product Achievements axis (per theme, `none`, `claimed`, or `documented`) beside it, stamping both into the generated index. The toolchain ships the split schema and taxonomy, the drift-guard tooling, the reference command-line validator and compiler (`ulc`) with schema validation, builder parity, and source-file hash verification, the deterministic `from-sheet` workbook-to-record converter, the fill-in workbook template under `templates/workbook/`, and the PIM platform mapping guides under `mappings/pim/`. See `CHANGELOG.md` for the full release history.

The specification will continue to evolve based on real-world use, industry feedback, and alignment with adjacent standards. See `CHANGELOG.md` for release notes.

Expand Down
4 changes: 3 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ for the next major. Pre-1.0 releases generally aimed for additive changes;
compatibility-tightening changes occurred only when documented in the changelog
(as with the v0.3.0 `cri_tier` enum tightening).

## Active version: v1.17.x
## Active version: v1.18.x

The current line, and ULC's first formal backward-compatibility commitment.
v1.0.0 adds **Product Achievements**, a second computed axis alongside data
Expand Down Expand Up @@ -164,6 +164,8 @@ v1.16.0 moves the LM-79 attestation from Standard to Full and zonal lumens from

v1.17.0 gives the TM-30 hue-bin local fidelity values a workbook authoring path: one `tm_30_hue_bins` sheet whose sixteen rows convert to `colorimetry.tm_30.rf_h_per_bin`, so the Full row that asks for them can be met from a workbook. The converter, template and one schema description change; the schema structure, taxonomy, required sets, grades, achievements, generated index, and eight published examples do not. Builder version 0.10.0 remains current.

v1.18.0 adds a referenced tested baseline, published luminaire color states and emitter channels. One resolver owns state reading and CCT selection; builder 0.11.0 projects both white-range endpoints. Tunable-white and dim-to-warm records need both endpoints for Standard and CRI Ra plus melanopic DER at each end for Full. Channels and endpoint spectra are enrichment. The workbook adds two sheets and state/channel spectrum routing. The eight existing examples keep their grades; a ninth public tunable-white example demonstrates the range, endpoint spectra and reported color-quality values. Scope contract 2.0.0 remains current.

The 1.0 milestone is defined by the two computed axes and the compatibility
commitment, justified by the additive-only release history and a validator
hardened against real cutsheets. Two items that earlier drafts framed as 1.0
Expand Down
13 changes: 12 additions & 1 deletion docs/methodology.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ Conditional rows (each names the predicate that switches it on):
| Per-length normalized | `photometry.per_length_normalized` | linear fixture |
| Declared-by-length | `photometry.declared_by_length` | linear fixture |
| CRI tier | `configuration.tested_axes.cri_tier` | primarily white light |
| Published white range | `color_tuning` (both ends resolve a CCT, counting the baseline) | tunable_white or dim_to_warm |
| SDCM step (MacAdam) | `colorimetry.sdcm_step` | primarily white light |
| Dimming method | `electrical.dimming_method` | analog or phase-cut driver |
| Dimming range | `electrical.dimming_range_percent` | analog or phase-cut driver |
Expand All @@ -207,6 +208,7 @@ The dimming method and dimming range gates apply only to analog and phase-cut dr
| Method-backed lumen maintenance | positive TM-21 projection hours or a positive TM-28 projection | `test_report` |
| TM-30 fidelity Rf (conditional: primarily white light) | `colorimetry.tm_30.rf` | `test_report` |
| TM-30 per-hue-bin detail (conditional: primarily white light) | `colorimetry.tm_30.rf_h_per_bin` | `test_report` |
| Endpoint CRI Ra and melanopic DER (conditional: tunable_white or dim_to_warm) | `color_tuning/states`, with the baseline endpoint read from the top-level blocks | `datasheet_pdf` |
| Measured spectrum as data (conditional: primarily white light) | colorimetry.spectral_power_distribution with source_kind laboratory_table or exchange_file, measured_through_optics true, value_type measured or rated | test_report |

`zonal_lumens` sits at standard with the rest of the photometric file: it is computed from the candela grid of that file and needs no test report. The LM-79 attestation sits at full, with the report content it stands for.
Expand All @@ -215,7 +217,7 @@ The dimming method and dimming range gates apply only to analog and phase-cut dr

Beyond the achieved level, the grader emits a roadmap in two parts. The first is the **tier roadmap**: for each grade the record has not yet reached, the specific hard fields it must add to get there, emitted as `conformance/gap` findings that name the source document and governing standard. The second is the **enrichment roadmap**: optional dimensions a record could disclose to deepen the datasheet even though they never change the grade, emitted as `conformance/enrichment` findings. Both are surfaced once a record reaches core.

The enrichment roadmap covers the depth a fuller datasheet carries but the tiers deliberately do not gate: power factor, warranty term, luminous opening shape, emission face, Duv, chromaticity x and y, ambient operating range, compatible accessories, thermal derating, flicker, alpha-opic and circadian metrics, chromaticity-shift projection, field angle, cutoff angle, spacing criterion, UGR, LCS zonal lumens, IK rating, EPA, installed orientation, optical radiation band, adaptive lighting modes, photometric file format, the TM-30 design-intent code, the measured spectrum as data (named again below Full, so a record at core or above sees it without a gate), and the finer sub-fields of the photometry, test-condition, operating-point, lumen-maintenance, colorimetry, and electrical blocks. Each sub-field suggestion fires only when its parent block is genuinely populated and the field itself absent, so an absent block draws a single block-level suggestion rather than sub-field spam.
The enrichment roadmap also names emitter channels on color-mixing records and spectra at both range ends when a color-tuning block is present. It covers the depth a fuller datasheet carries but the tiers deliberately do not gate: power factor, warranty term, luminous opening shape, emission face, Duv, chromaticity x and y, ambient operating range, compatible accessories, thermal derating, flicker, alpha-opic and circadian metrics, chromaticity-shift projection, field angle, cutoff angle, spacing criterion, UGR, LCS zonal lumens, IK rating, EPA, installed orientation, optical radiation band, adaptive lighting modes, photometric file format, the TM-30 design-intent code, the measured spectrum as data (named again below Full, so a record at core or above sees it without a gate), and the finer sub-fields of the photometry, test-condition, operating-point, lumen-maintenance, colorimetry, and electrical blocks. Each sub-field suggestion fires only when its parent block is genuinely populated and the field itself absent, so an absent block draws a single block-level suggestion rather than sub-field spam.

A small residual set stays as plain `conformance/observation` notes rather than joining the enrichment roadmap. There are four sources. Two fire when the field is absent, in the same "not disclosed" idiom as the enrichment rows, but are deliberately kept off the roadmap: a sustainability declaration (a tracked program attestation such as an EPD or Declare label, not the datasheet depth a specifier asks a manufacturer to add) and the deprecated legacy-cutoff classification (superseded by the BUG rating and LCS zonal lumens, retained only for lossless ingestion of legacy datasheets and a drop candidate for a future major version). The other two are a note when a headline photometric value (flux, power, or maximum intensity) carries a `value_type` other than `measured` (a rated figure or an optical simulation), and the attestation-coverage summary listing the compliance programs tracked on the record.

Expand All @@ -231,6 +233,7 @@ This layer replaced an earlier handful of inline conditionals, and three correct
| --- | --- | --- | --- |
| `hasWhitePoint` | `configuration.tested_axes.color_tunability` | in {static_white, tunable_white, dim_to_warm, rgbw, rgbww} | nominal CCT (core); Duv and chromaticity x/y (enrichment) |
| `isWhiteLightPrimary` | `color_tunability` | in {static_white, tunable_white, dim_to_warm} only | CRI Ra (core); CRI tier and SDCM step (standard); TM-30 Rf and hue bins and the measured spectrum as data (full); the TM-30 PVF code and the spectrum nudge (enrichment) |
| `tunesWhite` | `color_tunability` | in {tunable_white, dim_to_warm} | both published endpoints resolve a CCT (standard); both endpoints carry CRI Ra and melanopic DER (full) |
| `directional` | `product_family.primary_category` | downlight, tracklight, cylinder, wall_washer, grazer, facade_projector, in_ground_uplight, sports_flood | beam angle (standard); field angle (enrichment) |
| `outdoorSite` | `primary_category` | flood_area_site, roadway_street, walkway, bulkhead_wall_pack, sports_flood | joined with `northAmericanMarket` for outdoor distribution type, longitudinal range, and BUG (standard); LCS zonal (enrichment); legacy cutoff (observation) |
| `northAmericanMarket` | `product_family.markets` | absent, empty, or contains `north_america` | joined with `outdoorSite` for the three North American outdoor-classification rows |
Expand Down Expand Up @@ -261,6 +264,14 @@ The first is the outdoor-site boundary. An `in_ground_uplight` and a `facade_pro

The second is the white-point versus white-light-primary split. A CCT is meaningful for any fixture with a white point, including the white channel of an RGBW or RGBWW fixture, so `hasWhitePoint` gates nominal CCT broadly. CRI, SDCM, and TM-30 are white-light-quality metrics that a color-mixing fixture does not characterize: an RGBW fixture is specified by its mixing gamut, not a single CRI figure measured against a reference illuminant. The measured spectrum is waived for color-mixing fixtures for the same reason: no single spectrum characterizes them. So `isWhiteLightPrimary` excludes every color-mixing mode (RGBW included), and those quality metrics are waived for color-mixing fixtures rather than counted against them.

### Color states

The existing colorimetry, alpha-opic and photometry blocks remain the tested baseline. `color_tuning` names its role, label when needed, and dim level; `states` lists only other published states. Every reader uses the shared resolver, which returns the baseline first with its own measurement blocks, then the listed states. Each spectrum lives within its state's colorimetry. The nominal CCT bin takes precedence over the measured value; the baseline uses `configuration.tested_conditions.nominal_cct_at_test` first, then `colorimetry.nominal_cct_k`, then `measured_cct_k.value`. An off-bin published endpoint such as 1850 K uses `measured_cct_k` with value type `rated`, following the existing policy for rated instrument values in measured fields.

A tunable-white or dim-to-warm record needs both endpoints for Standard and CRI Ra plus melanopic DER at both endpoints for Full. When the tested baseline is an endpoint, its top-level values satisfy that endpoint. The existing grading rows continue to read only the baseline. Presets, dim-level points, channels and endpoint spectra never add a gate. Dim-to-warm uses the high end at full output and the low end at the dimmest published level, with output percentages when published. A stored tunable record without endpoints falls below Standard after rebuilding its index. Static-white records carry no color-tuning block.

The validator refuses duplicate state or channel identifiers, a lone endpoint, a reversed range, a state outside a tunable-white or dim-to-warm range, and repeated state identities. One identity key applies to every capability: CCT, dim level (absent means 100), and case-folded label (absent means empty). States sharing CCT and dim level must each have a label. Labeled white presets at one CCT are distinct states; an RGBW record may carry channels only and an unnamed tested baseline. A multichannel preset may fall outside its published white range. The index projects both numeric range ends only when both resolve; a range filter reads those ends together with the capability, and a dark-sky filter reads the larger of the range high end and the numeric baseline CCT.

### Product-class profiles: exit signs and emergency luminaires

Two product classes grade against their own dataset rather than the architectural-photometry profile above, so a maker of exit-sign-only or emergency-only products is graded on the evidence its cutsheets actually carry instead of being stranded at incomplete for lacking an LM-79 report it never produces. The class is derived from `product_family.primary_category`, an existing core field: `exit_sign` selects the sign profile, `emergency_luminaire` selects the dedicated-emergency profile, and every other category keeps the normal profile. Keying on a core field means every profile-selecting predicate reads a core field, so the core-fields-only invariant holds by construction and a record cannot select two conflicting profiles.
Expand Down
Loading
Loading