Skip to content

ADR-025 — WB and color calibration coupling

Status · Accepted Date · 2026-04-27 TA anchor ·/components/synthesizer ·/contracts/dtstyle-schema Related RFC · None (Phase 0 finding 3)

Context

Phase 0 testing surfaced that darktable's modern scene-referred pipeline couples white balance (temperature module) with color calibration (channelmixerrgb module). Adjusting WB while color calibration is enabled auto-updates color calibration. This is darktable's intentional behavior — color calibration uses WB as input.

For Chemigram vocabulary, this means a "WB warm subtle" primitive authored with color calibration enabled actually touches both temperature AND channelmixerrgb. The photographer's intent ("warm the white balance") gets implemented as a coupled two-module move.

Decision

Vocabulary primitives that touch WB can take either of two forms; both are valid:

Decoupled WB primitive — authored with color calibration disabled before adjusting WB. The resulting .dtstyle touches only temperature. Manifest declares touches: ["temperature"].

Coupled WB primitive — authored with color calibration enabled (darktable's default in scene-referred). The resulting .dtstyle touches both temperature AND channelmixerrgb. Manifest declares touches: ["temperature", "channelmixerrgb"].

The vocabulary entry's manifest is authoritative — the parser uses touches to filter .dtstyle content; both single-module and multi-module entries work correctly through the synthesizer.

Rationale

  • Both forms are legitimate. Some workflows want decoupled control (precise WB without affecting color cal); some want coupled (the modern darktable way, where WB feeds color cal). Forcing one would break the other.
  • The synthesizer handles multi-module entries fine. dtstyle.parse_dtstyle() returns a list of plugin records; SET semantics (ADR-002) applies independently per module.
  • Honest documentation. Photographers must understand the coupling when authoring; CONTRIBUTING.md documents both forms and lets the contributor choose.

Alternatives considered

  • Force decoupled (always disable color cal before WB authoring): rejected — excludes legitimate "modern darktable workflow" vocabulary and forces an extra step that doesn't match how some photographers actually work.
  • Force coupled (always include both): rejected — excludes legitimate "I want only WB control" vocabulary.
  • Detect coupling automatically and split into two primitives: rejected — adds parser complexity for a discipline that the photographer can decide directly.

Consequences

Positive: - Both common WB workflows are supported - The manifest's touches declaration is the single source of truth - The synthesizer is simpler — it doesn't need WB-specific logic

Negative: - Photographers must understand the coupling when authoring (mitigated: documented in CONTRIBUTING.md/Authoring procedure step 3) - Two photographers' "wb_warm_subtle" entries may not be byte-identical (one decoupled, one coupled), even though both are valid (mitigated: vocabulary names should clarify; e.g., wb_warm_subtle_decoupled vs wb_warm_subtle_modern)

Implementation notes

docs/CONTRIBUTING.md/Authoring procedure step 3 explains both forms. The vocabulary's starter pack documents which form each WB entry uses. Manifest-schema validation in CI checks that touches declarations match the <operation> tags actually present in the .dtstyle.