An earlier commit claimed to move `film_sim` from `[tone, colour]` to `[effect]` and did not. It edited `ops/film_sim.yaml`, where `attributes:` is read, validated against the vocabulary, and then dropped: a `rust:` node publishes its own descriptor, and the type still said tone and colour. The stock went on appearing in the Light group beside exposure and again in Colour beside white balance, exactly as before, and every test passed. Nothing caught it because nothing could. The declaration parsed, the parity tests compare ids rather than attributes, and an operation filed under the wrong groups renders perfectly. It surfaced only on screen, as a missing Effects tab — which is indistinguishable from a category that genuinely has nothing in it, and is precisely how `Optics` looked for as long as it was empty. So three changes rather than one: `FilmSim`'s descriptor declares `Attribute::Effect`, which is the move the earlier commit described. `attributes:` joins the keys a `rust:` node may not carry, beside `params`, `uniforms`, `wgsl`, `helpers`, `define` and `label`. The rule was already written — "its descriptor comes from the type" — and attributes were the one field that slipped past it. A key that is silently ignored is worse than one that is rejected, because it reads as though it worked; the eight hand-written declarations lose a line that never did anything. And a test asserts that every attribute the chain carries reaches the tab strip. That is the property that was actually broken, and its failure mode is invisible from every direction: the controls exist, they are in the shader, and there is no way to filter to them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
26 lines
1.2 KiB
YAML
26 lines
1.2 KiB
YAML
id: film_sim
|
|
order: 25
|
|
# What this node is *about* is not written here, and cannot be: a `rust:` node
|
|
# publishes its own descriptor, so `attributes:` in this file would be read,
|
|
# validated and then ignored. See `Attribute::Effect` on `FilmSim`'s descriptor
|
|
# in `../src/ops/film_sim.rs`.
|
|
rust: FilmSim
|
|
|
|
why_rust: |
|
|
It carries a stock's measured tables — an exposure matrix, three
|
|
characteristic curves and a density lookup — which are not parameters and
|
|
which no `uniforms:` expression could produce. Its neutral is "no stock
|
|
loaded" rather than a set of values, and it is the one node that declares
|
|
`Operation::renders`, so the composer omits the camera profile's base curve
|
|
and the conversion out of camera space on its behalf.
|
|
|
|
placement: |
|
|
After white balance and exposure, and before everything else.
|
|
|
|
Those two are what the camera did — interpreting the sensor, and correcting
|
|
the amount of light that reached it — and they are only meaningful on
|
|
scene-linear values, which is what a film has to be handed. Everything below
|
|
is a decision about the picture, and a decision about the picture belongs
|
|
after the film has rendered it, exactly as it does when you scan a frame and
|
|
then work on the scan.
|