Skip to content

ADR-001 — Vocabulary approach (Architecture B)

Status · Accepted Date · 2026-04-27 TA anchor ·/components/synthesizer ·/constraints/opaque-hex-blobs Related RFC · None (foundational decision from concept package)

Context

darktable's edit state is encoded in XMP sidecars. Module parameters are hex-encoded C structs (op_params) and gzip+base64 blend operations (blendop_params) — neither is human-editable, both are modversion-pinned to specific darktable releases. The agent needs a way to express edits over this opaque substrate.

Three approaches were considered during the concept-package work: hex param manipulation (decode/encode each module's binary struct), pre-authored vocabulary (.dtstyle files captured in darktable's GUI), and a Lua bridge to a running darktable instance.

Decision

Chemigram uses the vocabulary approach: pre-authored single-module styles captured in darktable's GUI, exported as .dtstyle XML files, composed into XMPs by the synthesizer. The agent's action space is a finite, named, photographer-curated set of moves rather than continuous parameter values.

Rationale

  • The agent's action space becomes meaningful and finite (closer to how a human apprentice learns moves, not slider-tweaking).
  • Authoring vocabulary is a craft act — it forces the photographer to articulate the moves they reach for, which is part of the project's research thesis.
  • Vocabulary is portable, inspectable, versionable, distributable as packs.
  • Implementation cost is small: parse .dtstyle, copy hex blobs into synthesized XMPs, no struct decoding.
  • Phase 0 testing validated end-to-end that this works on darktable 5.4.1.

Alternatives considered

  • Architecture A — hex param manipulation: decode/encode each module's binary params struct. Rejected for v1: per-module engineering, modversion drift, complex modules (filmic, color balance rgb) have dozens of fields. Reserved as Path C for high-value modules where continuous control matters (see RFC-012).
  • Architecture C — Lua bridge to running darktable: the path darktable-mcp (w1ne) takes. Rejected: recreates the Lightroom-SDK fragility we picked darktable to escape. App must stay open and focused.
  • Architecture D — write a custom raw processor: rejected as obviously absurd; darktable is the substrate, not something we'd reimplement.

Consequences

Positive: - The vocabulary becomes the project's voice — bad vocabulary makes the agent stupid, good vocabulary makes it expressive - Implementation cost in Phase 1 is bounded (~300 lines for synthesizer + parser) - Vocabulary itself is a research artifact (a portrait of how a photographer edits)

Negative: - Coarse rather than continuous parameter control (mitigated by Path C for high-value modules) - Vocabulary requires authoring work (mitigated by gap-surfacing tooling) - Photographer must understand darktable's GUI well enough to author primitives correctly

Implementation notes

src/chemigram_core/dtstyle.py parses .dtstyle files. src/chemigram_core/xmp.py synthesizes XMPs by composing parsed entries. See RFC-001 for the synthesizer architecture and ADR-002 for SET semantics.