From 0996be67f1c7ff50edf986c65307129e921dd910 Mon Sep 17 00:00:00 2001 From: ritchie <4462072+repentsinner@users.noreply.github.com> Date: Wed, 30 Sep 2026 13:39:33 -0700 Subject: [PATCH] docs: name the umbrella project by role, and drop citations into it The specification, roadmap, docstrings, comments and fixture notes named a private umbrella project, linked to it, and cited section and workstream slugs that resolve only there. They now name it by role and carry no link or foreign slug. Protocol and schema identifiers are data-format strings and stay unchanged. --- ROADMAP.md | 5 +---- SPEC.md | 39 +++++++++++++++++--------------------- display_report/analysis.py | 5 ++--- display_report/gamut.py | 10 ++++------ display_report/transfer.py | 9 ++++----- display_report/volume.py | 4 ++-- tests/fixtures/README.md | 7 +++---- tests/test_gamut.py | 7 +++---- tests/test_transfer.py | 4 ++-- tests/test_volume.py | 2 +- 10 files changed, 39 insertions(+), 53 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index d9f56ef..28b2701 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,10 +1,7 @@ # display-report — Roadmap This layer's roadmap. Cross-repo coordination and the pipeline-wide -sequencing live in -[color-wrangler](https://github.com/Fuse-Technical-Group/color-wrangler); -workstreams there are addressed by slug in backticks and resolve in -that repository's ROADMAP.md. +sequencing live in the umbrella project's roadmap. ## Contract-driven analysis §road:contract-analysis-impl diff --git a/SPEC.md b/SPEC.md index 2ee83da..9ab3d73 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,10 +1,7 @@ # display-report — Specification This layer's specification. The pipeline-wide architecture and the -artifact chain live in -[color-wrangler](https://github.com/Fuse-Technical-Group/color-wrangler); -sections there are addressed by slug in backticks and resolve in that -repository's SPEC.md. +artifact chain live in the umbrella project's specification. ## Scope §spec:scope @@ -15,18 +12,16 @@ serial port and no DeckLink, imports no device driver, and needs no rig: a machine that reports needs a file and nothing else. One tool touches instruments and signal hardware, and it is -display-measure (`§spec:session-ownership`). This repository consumes -what that tool emits. +display-measure. This repository consumes what that tool emits. **Why the validator owns no measure path.** display-report validates either this pipeline or a display-side calibration, and its value is that it has no stake in the correction under test. A second measure path living inside it would be an ungated one — the session gates -(`§spec:signal-contract`) live in display-measure, and a path that -skips them measures a display in an undeclared state and renders a -well-formed report of it. That is the failure the gates exist to -prevent, reintroduced in the repository whose independence is the -reason they matter. +live in display-measure, and a path that skips them measures a display +in an undeclared state and renders a well-formed report of it. That is +the failure the gates exist to prevent, reintroduced in the repository +whose independence is the reason they matter. **Reproducibility is a property of the seam, not of a bundled loop.** A third party reproduces a report by writing the seam file from @@ -37,16 +32,16 @@ second device path inside the validator was never what delivered it. Not owned here: instrument and signal-generator access (display-measure), OCIO semantics and config generation (ocio-display-gen), the show manifest and the promotion decision -(color-wrangler). +(the umbrella project). ## Report input §spec:report-input *Status: complete* -The report's input is one file: the measurement seam file -(`§spec:measurement-seam`), carrying the measurements, the spectra -behind them, the protocol that produced them, the declared signal -contract, the attested panel state, and the hash chain. +The report's input is one file: the measurement seam file, carrying +the measurements, the spectra behind them, the protocol that produced +them, the declared signal contract, the attested panel state, and the +hash chain. Analysis is a pure function of that file. Two runs over one file produce one report. @@ -61,7 +56,7 @@ catches it. **Rows without a measured spectrum are legible as such.** A disciplined session reads its dark end with a colorimeter, and those -rows carry a reconstructed spectrum or none (`§spec:spectral-retention`). +rows carry a reconstructed spectrum or none. An analysis needing a measured spectrum reports which rows it excluded and why, rather than treating a scaled estimate as a measurement. @@ -113,8 +108,8 @@ judges from. Plot bounds derived from a hardcoded PQ inverse place the measured points wrongly on a gamma session's page — the chart renders, and it is wrong. -The page's content — what figures it carries and what they -discriminate — is specified in `§spec:report-metrics`. +The umbrella project's specification defines the page's content: +what figures it carries and what they discriminate. ## Programmatic surface §spec:report-api @@ -126,9 +121,9 @@ rendered report as bytes. The command-line entry point is a caller like any other and holds no logic of its own. **Why an importable surface.** The operator's surface is a browser -served from the session host (`§spec:web-ui`), and it generates the -report from a loaded artifact without the operator leaving the page or -learning a second tool. A caller reduced to shelling out to a CLI and +served from the session host, and it generates the report from a +loaded artifact without the operator leaving the page or learning a +second tool. A caller reduced to shelling out to a CLI and scraping a path cannot report a failure precisely. display-report remains the only thing that decides what a report says; which surface invokes it is a separate question. diff --git a/display_report/analysis.py b/display_report/analysis.py index db636ba..28e98cb 100644 --- a/display_report/analysis.py +++ b/display_report/analysis.py @@ -296,9 +296,8 @@ def black(self) -> dict: tmp = self._black = {} tmp["measurements"] = measurements = self._data.measurements[mask] - # A disciplined session reads its dark end with a colorimeter - # (§spec:spectral-retention), so the black rows are exactly the ones - # most likely to carry no spectrum. Black's tristimulus is measured + # A disciplined session reads its dark end with a colorimeter, so + # the black rows are exactly the ones most likely to carry no spectrum. Black's tristimulus is measured # either way; the spectral fields are only available when a black # row carried a spectrum, and are None rather than zero when not -- # a zero here would read as a perfectly black display. diff --git a/display_report/gamut.py b/display_report/gamut.py index 12d4ef5..8d08697 100644 --- a/display_report/gamut.py +++ b/display_report/gamut.py @@ -1,15 +1,14 @@ -"""Gamut arithmetic for the situational-awareness view (§spec:gamut-visualization). +"""Gamut arithmetic for the situational-awareness view. Everything here works in CIE 1976 u'v'. The 1931 xy diagram exaggerates green distances and crushes blue ones, which misleads exactly where -narrow-band LED primaries land; artifacts record xy and views convert -(§spec:gamut-visualization). +narrow-band LED primaries land; artifacts record xy and views convert. **Coverage, never area ratio.** The fraction of a target a display can actually reproduce is bounded above by 1.0. The ratio of triangle areas is not, and it flatters a display that is large in a direction no content uses: the bench display measures 131.6% of Rec.709 by area and -cannot reach Rec.709 blue (§spec:report-metrics). +cannot reach Rec.709 blue. **A scalar hides where the shortfall is.** Coverage weights by chromaticity area, so a thin slice of unreachable colour reads as a @@ -165,8 +164,7 @@ def primary_deficits(display: list[Point], standard: str) -> dict[str, float]: Read against the display's own native primaries rather than a standard's wherever the question is what a *configuration* costs: no display reaches Rec.709 blue, so that shortfall is a property of - the emitter and discriminates nothing between calibrations - (§spec:report-metrics). + the emitter and discriminates nothing between calibrations. """ target = [uv_from_xy(*p) for p in STANDARD_GAMUTS[standard]] deficits = {} diff --git a/display_report/transfer.py b/display_report/transfer.py index e02d5a9..14fc787 100644 --- a/display_report/transfer.py +++ b/display_report/transfer.py @@ -1,4 +1,4 @@ -"""Transfer fidelity and quantization headroom (§spec:gamut-visualization). +"""Transfer fidelity and quantization headroom. Two questions about the same ramp. Does the display follow the transfer function its contract declares, and is the encoding precise enough that @@ -17,7 +17,7 @@ here asks the same question of any encoding — a gamma contract at 12 bits, a PQ contract at 10 — rather than asking whether a display tracks one particular curve. That generality is the point: it is the metric -that survives changing the contract (§road:lut-transfer-probe). +that survives changing the contract. Its threshold is not a hard line. Barten's model takes viewing distance, field size and spatial frequency, and the value used here is the peak of @@ -46,7 +46,7 @@ Ramp = list[tuple[int, float]] | tuple[tuple[int, float], ...] # Readings at or under this are instrument noise rather than the display's -# response; fitting through them fits the noise (§road:instrument-floors). +# response; fitting through them fits the noise. FLOOR = 0.001 # The spatial frequency Barten's sensitivity peaks near, cycles per @@ -182,8 +182,7 @@ def quantization_headroom( measured. The ramp is driven at 12-bit codes, so a step at 10 bits is four of them: this is how one contract's quantization gets compared against another's on the same measured display, which is what - §road:lut-transfer-probe needs to settle whether a PQ-like LUT - contract beats the 12-bit gamma one. + settles whether a PQ-like LUT contract beats the 12-bit gamma one. """ codes_per_step = 2.0 ** (MEASURED_BIT_DEPTH - bit_depth) rows: list[HeadroomRow] = [] diff --git a/display_report/volume.py b/display_report/volume.py index deb5735..6858f54 100644 --- a/display_report/volume.py +++ b/display_report/volume.py @@ -1,4 +1,4 @@ -"""The reproducible set as a solid (§spec:gamut-visualization). +"""The reproducible set as a solid. A chromaticity triangle is one slice of a three-dimensional set, and it hides what decides whether a colour is usable: the luminance it is @@ -15,7 +15,7 @@ rather than drawing a capability nobody measured. This display sums to within 0.75% of its measured white, which is what earns the picture. -**Why CIELAB.** §spec:report-metrics asks for a luminance-inclusive +**Why CIELAB.** The report's metrics call for a luminance-inclusive volume in a perceptually uniform space, so that equal distances in the picture mean roughly equal perceived differences and the solid's shape carries information rather than the space's distortion. L* is relative diff --git a/tests/fixtures/README.md b/tests/fixtures/README.md index 35941ae..97afbc6 100644 --- a/tests/fixtures/README.md +++ b/tests/fixtures/README.md @@ -12,10 +12,9 @@ display-measure characterize --out hybrid_session.csmf --instrument doubles-hybr 72 rows from the `color-wrangler/characterize/3` protocol against display-measure's deterministic display double, under a GAMMA 2.4 contract. 71 rows are spectral; the black row is colorimetric and carries no spectrum -at all, because a disciplined session reads its dark end with a colorimeter -(§spec:spectral-retention). That row is the point of the fixture — it is the -shape the analysis has to tolerate, and no hand-built file would have -produced it. +at all, because a disciplined session reads its dark end with a colorimeter. +That row is the point of the fixture — it is the shape the analysis has to +tolerate, and no hand-built file would have produced it. Regenerate it from display-measure when the protocol or the seam format changes. Do not hand-edit it. diff --git a/tests/test_gamut.py b/tests/test_gamut.py index 4a86ac3..e6e0218 100644 --- a/tests/test_gamut.py +++ b/tests/test_gamut.py @@ -1,8 +1,7 @@ -"""Gamut arithmetic for the situational-awareness view (§spec:gamut-visualization). +"""Gamut arithmetic for the situational-awareness view. The numbers here are the bench display's, measured under three different -panel configurations, because the arithmetic's job is to tell them apart -(§spec:report-metrics). +panel configurations, because the arithmetic's job is to tell them apart. """ import pytest @@ -42,7 +41,7 @@ def test_a_gamut_covers_itself_entirely(self) -> None: def test_coverage_never_exceeds_one(self) -> None: """The bench display's triangle is larger than Rec.709's by area and still cannot reproduce all of it — the distinction area ratios - lose (§spec:report-metrics).""" + lose.""" for primaries in (AUG12, AUG29): for name in STANDARD_GAMUTS: target = [uv_from_xy(*p) for p in STANDARD_GAMUTS[name]] diff --git a/tests/test_transfer.py b/tests/test_transfer.py index 38aacb7..f817c29 100644 --- a/tests/test_transfer.py +++ b/tests/test_transfer.py @@ -1,4 +1,4 @@ -"""Transfer fidelity and quantization headroom (§spec:gamut-visualization). +"""Transfer fidelity and quantization headroom. The bench display's own gray ramp, dark-room capture. Its declared contract is a 2.35 power law and it does not follow one: the measured response @@ -108,7 +108,7 @@ def test_the_threshold_flag_tracks_the_ratio(self) -> None: class TestBitDepthComparison: """The same measured display, judged against a different encoding — the - comparison §road:lut-transfer-probe needs.""" + comparison that settles which LUT contract to run.""" def test_fewer_bits_make_every_step_coarser(self) -> None: twelve = { diff --git a/tests/test_volume.py b/tests/test_volume.py index 6e88c6e..8b39bcb 100644 --- a/tests/test_volume.py +++ b/tests/test_volume.py @@ -1,4 +1,4 @@ -"""The reproducible set as a solid (§spec:gamut-visualization). +"""The reproducible set as a solid. A chromaticity triangle is one slice of a three-dimensional set, and it hides the thing that decides whether a colour is usable: the luminance