Skip to content

03 — Data and Content Catalog

What feeds the system. Sources, characteristics, access. The substrate Chemigram builds on.

This document inventories what the system works with — every external data source, content type, and substrate that Chemigram depends on. For each: what it provides, how Chemigram accesses it, its characteristics, and what creative or functional potential it has.

The discipline: Chemigram orchestrates; everything in this catalog is used, not built. Per 04's "darktable does the photography" and "BYOA" principles, every capability described here comes from outside the engine.

Substrate sources

darktable

Property Detail
What it provides Complete image-processing pipeline: demosaic, color science, all parametric modules (exposure, color calibration, tone equalizer, filmic, color balance rgb, masks, etc.), rendering to JPEG/TIFF/PNG
Access mechanism darktable-cli invocation against an isolated --configdir. Headless. No GUI required.
Format XMP sidecars (RDF/XML) hold edit state. .dtstyle XML files capture single-module styles.
Version pinning darktable 5.x stable. Module modversion numbers are version-specific; vocabulary requires re-capture when darktable's modversions change.
Characteristics Mature (15+ years), active development, scene-referred pipeline, Apple Silicon native since 4.4.2, OpenCL via macOS Metal/MPS
Creative potential Genuinely competitive with Lightroom and ahead in some areas (parametric masks, contrast equalizer, diffuse-or-sharpen). Local-adjustment surface is broader than LR's.
Constraints XMP op_params and blendop_params are hex-encoded C structs — not human-editable. We treat them as opaque blobs and copy them between .dtstyle and XMP. See 04/XMP composition.

Lensfun (via darktable)

Property Detail
What it provides Lens correction profiles (distortion, vignetting, TCA) for thousands of camera+lens combinations
Access Bundled with darktable; no separate installation
Characteristics Excellent F-mount (Nikon) coverage. Patchy for newer Sony G Master and similar mirrorless lenses.
Used by L1 vocabulary entries that enable lens correction

darktable noise profiles

Property Detail
What it provides Per-camera-body profiled denoise data (noiseprofiles.json)
Access Bundled with darktable
Coverage Hundreds of camera bodies, including all major Sony/Nikon/Canon/Fuji models from the last decade
Used by L1 vocabulary entries that enable profiled denoise

Embedded lens correction metadata (camera-side)

Property Detail
What it provides Lens distortion polynomial coefficients embedded in raw files by some manufacturers
Format TIFF tags / maker notes; darktable reads via --apply-method=embedded
Coverage Sony ARW, Fuji RAF (APS-C), Olympus ORF — the cameras that bake corrections into raws
Significance For Sony A1 + GM glass, embedded metadata is better than Lensfun (which lacks profiles for newer lenses). darktable defaults to embedded when present.

Photographer-authored content

Vocabulary primitives (.dtstyle files)

The agent's action space. The most important content type in the project.

Property Detail
What it is Single-module darktable styles, each capturing one named move (expo_+0.5, colorcal_underwater_recover_blue, etc.)
Format XML per darktable's .dtstyle schema. Each <plugin> entry has <operation>, hex <op_params>, gzip+base64 <blendop_params>, modversion, iop_order
Authoring In darktable GUI: set one module to desired value, save as named style, export as .dtstyle
Layer attribution L1 (technical correction), L2 (look establishment), L3 (taste/agent vocabulary) — see 04/Layer model
Mask kinds none (global), parametric (luminance/hue/chroma masks in blendop_params), drawn (geometric primitives — gradient/ellipse/rectangle/path — encoded into masks_history and bound via patched blendop_params). Per ADR-076 the earlier raster-PNG path was retired in v1.5.0.
Lifecycle Authored once, used many times. Versioned alongside darktable releases.
Distribution Bundled starter (this monorepo, MIT). Community packs (this monorepo, attributed). Personal vocabularies (separate private repos). See docs/LICENSING.md.

Vocabulary manifest

{
  "name": "gradient_top_dampen_highlights",
  "layer": "L3",
  "subtype": "exposure",
  "path": "layers/L3/masked/gradient_top_dampen_highlights.dtstyle",
  "touches": ["exposure"],
  "tags": ["mask", "gradient", "highlights", "dampen"],
  "description": "Dampen top-half highlights via -0.5 EV through a top-bright gradient.",
  "mask_spec": {
    "dt_form": "gradient",
    "dt_params": {"anchor_x": 0.5, "anchor_y": 0.5, "rotation": 0.0, "compression": 0.5}
  },
  "modversions": {"exposure": 7},
  "darktable_version": "5.4",
  "source": "expressive-baseline",
  "license": "MIT"
}

The manifest is the vocabulary's API. The .dtstyle files are the payload.

Photographer's context (per-photographer)

~/.chemigram/taste.md

The photographer's taste, externalized as durable prose. Read at every session start. Curated over months.

Property Detail
Format Markdown. Free-form within sections.
Structure Working preferences, recurring patterns, vocabulary affinities, brief language, camera notes, session preferences
Authoring Initial draft by photographer (1 hour, imperfect, useful from day 1). Subsequent additions: agent proposes, photographer confirms.
Lifecycle Living document. Periodic synthesis (every 10-20 sessions) consolidates redundant entries.
Persistence User's machine, version-controllable. Never uploaded automatically.

~/.chemigram/config.toml

The user's configuration. Vocabulary sources and L1/L2 binding rules.

[vocabulary]
sources = [
    "$CHEMIGRAM_INSTALL/vocabulary/starter",
    "$CHEMIGRAM_INSTALL/vocabulary/packs/fuji-sims",
    "~/private/chemigram-vocabulary-marko",
]

[[layers.L1.bindings]]
camera = "NIKON D850"
lens = "AF-S Nikkor 24-70mm f/2.8E ED VR"
template = "lens_correct_full + denoise_auto"

[[layers.L1.bindings]]
camera = "NIKON D850"
lens = "AF-S Fisheye Nikkor 8-15mm f/3.5-4.5E ED"
template = "denoise_auto"   # NO lens correction, preserve fisheye

Photographer-edited TOML. No GUI for configuration. Auto-resolution from EXIF chooses the right binding per image.

Per-image content (per photo project)

Each image is its own project (per 04/Project structure):

~/Pictures/Chemigram/<image_id>/
  raw/                                   # symlink to original raw
  brief.md                               # what this image is for
  notes.md                               # what we've learned
  metadata.json                          # EXIF cache, layer bindings
  current.xmp                            # synthesized from current snapshot
  objects/                               # snapshot store (versioning)
  refs/                                  # branches, tags, HEAD
  log.jsonl                              # operation log
  sessions/                              # transcripts
  previews/                              # render cache
  exports/                               # final outputs
  vocabulary_gaps.jsonl                  # gaps surfaced this image

brief.md

Photographer's intent for this image. Written at session start, sometimes updated mid-session if the goal shifts. Read at every session on this image.

notes.md

What we've learned about this image across sessions. Subject identification, lighting facts, decisions made, branches explored, open questions. Both photographer and agent contribute (agent proposes, photographer confirms).

metadata.json

EXIF cache + layer binding decisions:

{
  "exif": {
    "camera": "X-Pro2",
    "lens": "XF 35mm f/2 R WR",
    "iso": 200,
    "fuji_sim": "Acros"
  },
  "auto_binding": {
    "l1": "fuji_xpro2_default",
    "l2_suggested": "fuji_acros",
    "l2_applied": "fuji_acros"
  }
}

The agent reads this to know what baseline it's working from.

XMP snapshots (objects/)

Content-addressed by SHA-256 over canonical XMP serialization. Each snapshot is a complete edit state. See 04/Versioning.

Session transcripts (sessions/)

Full conversation logs from Mode A sessions. Per-session: goal, brief at start, full transcript, snapshots produced, vocabulary used, gaps surfaced, outcome. JSONL format, one entry per turn.

Mask geometry (inside the XMP)

Drawn-form masks ride inside the XMP they belong to — <darktable:masks_history> carries the geometry, plugins' blendop_params reference it via mask_id. There is no separate mask directory or registry; the snapshot's content hash captures both the dtstyle and its mask binding atomically. Per ADR-076 (the v1.5.0 cleanup), the previous PNG-based masks/ directory was retired — darktable never read those files, so the path was a silent no-op.

External AI capability (BYOA — Bring Your Own AI)

Per 04's BYOA principle, AI capabilities are not bundled with Chemigram. They're configured by the photographer.

The photo agent (Mode A driver)

Property Detail
Role Reads photographer's context, drives the editing loop, surfaces uncertainty, catches composition tensions, proposes context updates
Access MCP — Chemigram's MCP server is called by whatever agent the photographer configures (Claude, GPT, Gemini, local model, etc.)
Required capabilities Vision (to look at preview JPEGs), tool use (to call Chemigram's MCP tools), reasoning over reasonably-long context (~50-100K tokens for a typical session)
Not required Specific model family. The system prompt is portable.

Mask provision

Property Detail
v1.5.0 Drawn-form geometry baked into vocabulary entries' mask_spec. Three forms initially: gradient, ellipse, rectangle. Encoded into the XMP's masks_history at apply time. See ADR-076.
v1.9.0 — drawn shapes dt_form: "path" added (N-vertex closed polygons) per RFC-026 substrate; agent-facing build-by-words workflow per RFC-029 / ADR-084 (mask-shapes-from-words.md).
v1.9.0 — parametric range filters mask_spec.range_filter field with kind ∈ {luminance, color_h, color_s, color_l} per RFC-024 / ADR-085. Encoded into blendif byte fields in blendop_params. Composes with drawn masks via AND-intersection.
v1.9.0 — retouch apply_spot MCP tool (sister to apply_primitive) for heal/clone at a coordinate. Form-array op_params encoding per RFC-025 / ADR-087.
v1.9.0 — LLM-vision provider The chat-client's vision-capable LLM (Claude.ai / ChatGPT / Claude Code) sees render_preview JPEGs and constructs mask_spec from spatial reasoning. Zero deployment cost. RFC-026 / ADR-086.
RFC-030 (deferred) Deployed sibling-provider scaffolding for the precision tier — SAM-class subject masks, MiDaS-class depth, content-aware spot detectors. Unfreezes when LLM-vision precision becomes the bottleneck.
Out of scope PNG-based mask interchange. The earlier MaskingProvider Protocol (returning PNG bytes) was retired in v1.5.0 — verified that darktable never reads external PNGs for raster masks.

Mode B evaluators (future)

Not in v1. Planned as EvaluatorProvider protocol — reference-based perceptual similarity, vision-model self-eval, learned critic from accumulated session data. See 04/Modes and docs/TODO.md.

Borrowed vocabulary (community packs)

Property Detail
What Pre-existing vocabulary collections from the darktable community, redistributed with attribution
Examples Fuji film simulations from bastibe/Darktable-Film-Simulation-Panel and t3mujinpack, Nikon picture-control emulations
Format Same .dtstyle files as native vocabulary. Wrapped in pack directory with manifest.json and ATTRIBUTION.md
License Per-upstream (typically MIT or CC variants). Preserved in pack distribution.
Calibration note Fuji sims are calibrated to Fuji X-Trans color science. Applying to Sony/Nikon/Canon raws produces "spirit" matches, not pixel-identical. Documented in vocabulary metadata.

Custom color science assets (extensibility hook)

A hook for users who want to bring their own color science:

chemigram-vocabulary/
  profiles/
    *.icc                    # custom ICC input profiles
    *.cube                   # 3D LUTs
    *.basecurve              # custom basecurve presets

Vocabulary entries can reference these via relative path. Engine copies path into module config; darktable reads asset at render time. Empty in v1; documented for future use including potential per-sensor color-science fitting (see docs/TODO.md).

Session data is local-only

A key principle: session data — transcripts, taste evolution, preference history, vocabulary gaps — never leaves the photographer's machine automatically. No telemetry. No phone-home. No cloud dependency.

If a photographer chooses to publish anonymized insights, that's their choice. The project provides no infrastructure for publishing session data and no encouragement to do so by default.

This is part of the agent-is-the-only-writer discipline — not just an architectural property of edit operations, but a privacy property of everything Chemigram observes.

Catalog summary

Source Type Authored by Lifetime
darktable Substrate Upstream project External; we pin versions
Lensfun, noise profiles Bundled with darktable Upstream External
Embedded lens metadata Camera-baked Hardware Per-image
Vocabulary primitives Authored content Photographer or community Per-vocabulary
Vocabulary manifest Structured metadata Generated from above Per-vocabulary
taste.md Photographer's taste Photographer (agent-assisted) Persistent across sessions
config.toml User configuration Photographer Persistent
Per-image briefs/notes/metadata Image context Photographer (agent-assisted) Per-image
XMP snapshots Edit state Agent (writes), photographer (reads) Per-image, versioned
Session transcripts Conversation logs Both, captured automatically Per-session, append-only
Mask geometry Spatial selections Authored into vocabulary entries' mask_spec (or, in Phase 4, produced by a sibling project) Inside the XMP
Photo agent AI capability BYOA — photographer configures External

What feeds Chemigram comes from four directions: darktable provides processing, photographer provides taste, configured agents provide AI capability, community provides vocabulary packs. Chemigram itself produces orchestration and accumulated state.


03 · Data and Content Catalog · v1.0