Skip to content

Commit 76b0cb7

Browse files
chipiclaude
andcommitted
docs: refresh narrative docs for drawn-mask-only architecture (v1.5.0)
Doc-only follow-up to the ADR-076 cleanup. The previous commit landed code, tests, and ADRs; this one updates the narrative tier (concept/PRD/RFC/getting-started/api/index) so users reading the docs see the same architecture the code now reflects. - RFC-003/004/009: status markers updated to "superseded by ADR-076". - PRD-004: reframed as partially-shipped — drawn-form geometric masks today, content-aware masking deferred to Phase 4 via a sibling project producing darktable drawn-form geometry. Original aspirational lede preserved. - concept/04-architecture.md: section 6 (Local adjustments) rewritten around mask_spec + drawn-form serialization; section 7.4 (Mask integration) collapses the dead PNG-storage option discussion into a one-paragraph "masks ride inside the XMP" note. - concept/03-data-catalog.md: vocabulary-manifest example uses gradient_top_dampen_highlights; Generated-masks section becomes Mask-geometry-in-the-XMP; Masking-providers row reframed around v1.5 + Phase 4. - concept/00-introduction.md: glossary updates — drops Raster mask / Mask registry / Symbolic mask reference / Masking provider entries; adds mask_spec and Content-aware masking (Phase 4). - getting-started.md: session walkthrough rewritten around shipped drawn-form entries (gradient_top_dampen_highlights, gradient_bottom_lift_shadows) instead of the old generate_mask / mask_override flow; troubleshooting MASKING_ERROR re-aimed at malformed mask_spec. - api/index.md, index.md: tool count corrected (27 → 22), masking subsystem bullet rewritten, masks/ removed from workspace tree. - phase-3-preview.md: substantially rewritten — Phase 3 is now expanding the masked vocabulary across drawn-form + parametric flavors, both authorable today; content-aware framed as Phase 4. No code or test changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 155784b commit 76b0cb7

11 files changed

Lines changed: 223 additions & 282 deletions

docs/api/index.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ Auto-generated from the docstrings in `src/chemigram/core/`. Calibrated to **Sli
1313
| [`chemigram.core.exif`](exif.md) | EXIF read for L1 vocabulary binding | `read_exif`, `ExifData`, `ExifReadError` |
1414
| [`chemigram.core.binding`](binding.md) | Exact-match L1 vocabulary lookup | `bind_l1`, `VocabularyIndex` |
1515

16-
## Coming in later slices
16+
## Subsequent slices (shipped)
1717

18-
- **Slice 2:** `chemigram.core.versioning` (content-addressed XMP DAG), `chemigram.core.masks`
19-
- **Slice 3:** `chemigram.mcp.server` (MCP tool surface), `chemigram.mcp.prompts` (versioned templates)
20-
- **Slice 4:** `chemigram.core.masking` (`MaskingProvider` Protocol, `CoarseAgentProvider`)
21-
- **Slice 5:** `chemigram.core.context`, `chemigram.core.sessions`
18+
- **Slice 2:** `chemigram.core.versioning` content-addressed XMP DAG (`canonical`, `repo`, `ops`).
19+
- **Slice 3:** `chemigram.mcp.server` (MCP tool surface), `chemigram.mcp.prompts` (versioned templates).
20+
- **Slice 4 (revised in v1.5.0):** `chemigram.core.masking.dt_serialize` — drawn-form encoders for darktable's `masks_history`. (The earlier `MaskingProvider` Protocol + `CoarseAgentProvider` + PNG mask registry were retired in v1.5.0 per ADR-076 — darktable doesn't read external PNGs for raster masks.)
21+
- **Slice 5:** `chemigram.core.context`, `chemigram.core.session`.
2222

2323
See the [implementation plan](../IMPLEMENTATION.md) for the full slicing.
2424

docs/concept/00-introduction.md

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ The vocabulary used across the package. When in doubt about what a term means, f
9393

9494
**Chemigram** — the project itself. Named after the cameraless photographic process where an image emerges from chemical reaction on light-sensitive paper, guided but not fully controlled. The name fits because each edit emerges from a loop between photographer's intent, agent's moves, and tool's response.
9595

96-
**Engine** — the Python code that does the orchestration. Includes XMP composition, vocabulary loading, render pipeline, versioning, mask registry, and MCP server. See `04`/2.
96+
**Engine** — the Python code that does the orchestration. Includes XMP composition, vocabulary loading, render pipeline, versioning, drawn-mask serialization, and MCP server. See `04`/2.
9797

9898
**Agent** — the AI capability that drives Mode A or Mode B sessions. Per the BYOA principle, the agent is photographer-configured (Claude, GPT, etc.), not bundled with Chemigram.
9999

@@ -117,7 +117,7 @@ The vocabulary used across the package. When in doubt about what a term means, f
117117

118118
**`.dtstyle`** — the file format of a vocabulary primitive. XML, captures one module's parameters and blend operation. Authored by photographer (or community) in darktable's GUI; loaded by Chemigram at session start.
119119

120-
**Manifest** — the JSON metadata accompanying vocabulary entries. Contains layer assignment, modules touched, tags, description, mask kind, and other engine-relevant metadata.
120+
**Manifest** — the JSON metadata accompanying vocabulary entries. Contains layer assignment, modules touched, tags, description, optional `mask_spec` (drawn-form geometry for mask-bound entries), and other engine-relevant metadata.
121121

122122
**Vocabulary gap** — when the agent needs a primitive that doesn't exist. Worked around by composing existing primitives, then logged to `vocabulary_gaps.jsonl` for later authoring. Gaps are content, not failure.
123123

@@ -155,19 +155,15 @@ The vocabulary used across the package. When in doubt about what a term means, f
155155

156156
### Masks
157157

158-
**Mask** — a spatial selection that restricts an effect to part of the frame. Three kinds in Chemigram: parametric, drawn, raster.
158+
**Mask** — a spatial selection that restricts an effect to part of the frame. Two kinds in Chemigram (per ADR-076): parametric and drawn. (The earlier raster-PNG path was retired in v1.5.0 — darktable doesn't read external PNGs for raster masks.)
159159

160160
**Parametric mask** — a mask defined by pixel-value conditions (luminance range, hue range, etc.) in `blendop_params`. Content-agnostic.
161161

162-
**Drawn mask** — a mask defined by geometric primitives (gradient, circle, ellipse, path) in `blendop_params`. Pre-authored by photographer in GUI.
162+
**Drawn mask** — a mask defined by geometric primitives (gradient, ellipse, rectangle, path) encoded into `<darktable:masks_history>` and bound to plugins via `blendop_params.mask_id`. Either pre-authored by the photographer in darktable's GUI, or declared in a vocabulary entry's `mask_spec` and serialized at apply time.
163163

164-
**Raster mask** — a PNG mask file referenced by darktable. The path for AI-generated subject masks. Resolved symbolically at XMP synthesis time.
164+
**`mask_spec`** — a vocabulary-entry field declaring drawn-form geometry. Shape: `{"dt_form": "gradient" | "ellipse" | "rectangle", "dt_params": {...}}`. Presence triggers the drawn-mask apply path. See `04`/6.2.
165165

166-
**Mask registry** — Chemigram's per-image record of generated masks. Tracks names, generators, prompts, freshness. See `04`/6.3.
167-
168-
**Symbolic mask reference** — a placeholder name like `current_subject_mask` that vocabulary entries use. The engine resolves the symbol to an actual PNG path at synthesis time. Lets multiple primitives reuse the same mask.
169-
170-
**Masking provider** — a pluggable implementation that generates masks. v1 ships a coarse agentic default; production-quality masking via `chemigram-masker-sam` sibling project. Configurable per target type. See `04`/6.4.
166+
**Content-aware masking (Phase 4)** — pixel-precise organic masks (e.g., subject silhouettes) come from a future sibling project that produces darktable drawn-form geometry. Not available in v1.5.0; the `apply_with_drawn_mask` seam is ready when it ships. See `04`/6.4.
171167

172168
### Context files
173169

@@ -177,15 +173,15 @@ The vocabulary used across the package. When in doubt about what a term means, f
177173

178174
**`notes.md`** — what we've learned about a specific image. Lives at `<image_id>/notes.md`. Accumulates across sessions on the same image.
179175

180-
**`config.toml`** — user configuration. Vocabulary sources, masking providers, L1/L2 binding rules, storage paths.
176+
**`config.toml`** — user configuration. Vocabulary sources, L1/L2 binding rules, storage paths.
181177

182178
### Disciplines
183179

184180
**Agent is the only writer** — the photographer reads previews and gives feedback; the agent is the sole mutator of edit state. See `04`/1.1.
185181

186182
**darktable does the photography, Chemigram does the loop** — every image-processing capability comes from darktable. Chemigram contributes orchestration, vocabulary, agent loop, versioning, session capture. See `04`/1.2.
187183

188-
**BYOA (Bring Your Own AI)** — Chemigram doesn't ship AI capabilities; it integrates them via MCP. Maskers, evaluators, the photo agent itself are all photographer-configured. See `04`/1.3.
184+
**BYOA (Bring Your Own AI)** — Chemigram doesn't ship AI capabilities; it integrates them via MCP. The photo agent itself is photographer-configured (Claude, GPT, etc.); future evaluators and content-aware maskers are sibling projects, not bundled in core. See `04`/1.3.
189185

190186
### Engineering terms
191187

docs/concept/03-data-catalog.md

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -59,27 +59,28 @@ The agent's action space. The most important content type in the project.
5959
| Format | XML per darktable's `.dtstyle` schema. Each `<plugin>` entry has `<operation>`, hex `<op_params>`, gzip+base64 `<blendop_params>`, modversion, iop_order |
6060
| Authoring | In darktable GUI: set one module to desired value, save as named style, export as `.dtstyle` |
6161
| Layer attribution | L1 (technical correction), L2 (look establishment), L3 (taste/agent vocabulary) — see `04`/Layer model |
62-
| Mask kinds | `none` (global), `parametric` (luminance/hue/chroma masks in `blendop_params`), `drawn` (geometric primitives in `blendop_params`), `raster` (external PNG via mask reference) |
62+
| 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. |
6363
| Lifecycle | Authored once, used many times. Versioned alongside darktable releases. |
6464
| Distribution | Bundled starter (this monorepo, MIT). Community packs (this monorepo, attributed). Personal vocabularies (separate private repos). See `docs/LICENSING.md`. |
6565

6666
### Vocabulary manifest
6767

6868
```json
6969
{
70-
"name": "tone_lifted_shadows_subject",
70+
"name": "gradient_top_dampen_highlights",
7171
"layer": "L3",
72-
"subtype": "look",
73-
"path": "layers/L3/local/tone_lifted_shadows_subject.dtstyle",
74-
"touches": ["toneequalizer"],
75-
"tags": ["tone", "shadows", "local", "subject"],
76-
"description": "Lift shadow zones, restricted to the subject mask.",
77-
"mask_kind": "raster",
78-
"mask_ref": "current_subject_mask",
79-
"global_variant": "tone_lifted_shadows",
80-
"modversions": {"toneequalizer": 4},
81-
"darktable_version": "5.0",
82-
"source": "starter",
72+
"subtype": "exposure",
73+
"path": "layers/L3/masked/gradient_top_dampen_highlights.dtstyle",
74+
"touches": ["exposure"],
75+
"tags": ["mask", "gradient", "highlights", "dampen"],
76+
"description": "Dampen top-half highlights via -0.5 EV through a top-bright gradient.",
77+
"mask_spec": {
78+
"dt_form": "gradient",
79+
"dt_params": {"anchor_x": 0.5, "anchor_y": 0.5, "rotation": 0.0, "compression": 0.5}
80+
},
81+
"modversions": {"exposure": 7},
82+
"darktable_version": "5.4",
83+
"source": "expressive-baseline",
8384
"license": "MIT"
8485
}
8586
```
@@ -102,7 +103,7 @@ The photographer's taste, externalized as durable prose. Read at every session s
102103

103104
### `~/.chemigram/config.toml`
104105

105-
The user's configuration. Vocabulary sources, masking provider preferences, L1/L2 binding rules.
106+
The user's configuration. Vocabulary sources and L1/L2 binding rules.
106107

107108
```toml
108109
[vocabulary]
@@ -112,9 +113,6 @@ sources = [
112113
"~/private/chemigram-vocabulary-marko",
113114
]
114115

115-
[masking]
116-
default_provider = "sam-mcp"
117-
118116
[[layers.L1.bindings]]
119117
camera = "NIKON D850"
120118
lens = "AF-S Nikkor 24-70mm f/2.8E ED VR"
@@ -145,7 +143,6 @@ Each image is its own project (per `04`/Project structure):
145143
sessions/ # transcripts
146144
previews/ # render cache
147145
exports/ # final outputs
148-
masks/ # registered masks
149146
vocabulary_gaps.jsonl # gaps surfaced this image
150147
```
151148

@@ -187,9 +184,9 @@ Content-addressed by SHA-256 over canonical XMP serialization. Each snapshot is
187184

188185
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.
189186

190-
### Generated masks (`masks/`)
187+
### Mask geometry (inside the XMP)
191188

192-
PNG raster masks generated by configured masking providers. Registered with symbolic names (`current_subject_mask`, `current_sky_mask`, custom-named persistent masks). Vocabulary entries reference these symbolically; engine resolves to actual paths at synthesis time.
189+
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.
193190

194191
## External AI capability (BYOA — Bring Your Own AI)
195192

@@ -204,15 +201,13 @@ Per `04`'s BYOA principle, AI capabilities are not bundled with Chemigram. They'
204201
| 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) |
205202
| Not required | Specific model family. The system prompt is portable. |
206203

207-
### Masking providers
204+
### Mask provision
208205

209206
| Property | Detail |
210207
|-|-|
211-
| Role | Generate PNG raster masks from preview images and target descriptions |
212-
| Access | `MaskingProvider` protocol; default implementations call out via MCP |
213-
| v1 default | Coarse agentic provider — uses photo agent's vision capability for bbox/gradient/color-region masks. No PyTorch dependency. Sufficient for many cases, especially Layer 1 vocabulary. |
214-
| Production option | `chemigram-masker-sam` sibling project — wraps SAM/MobileSAM/SAM 2 as MCP server. Local install, GPU-accelerated. Pixel-precise subject segmentation. |
215-
| Future options | Hosted services (Replicate, Modal), prompted segmentation (GroundingDINO + SAM), photographer-trained specialists |
208+
| v1.5.0 | Drawn-form geometry baked into vocabulary entries' `mask_spec`. Three forms supported: gradient, ellipse, rectangle. Encoded directly into the XMP's `masks_history` at apply time; no AI involved. See ADR-076. |
209+
| Phase 4 | Sibling project (working name `chemigram-masker-sam`) producing darktable-compatible drawn-form geometry from natural-language prompts. Wire format is the same darktable schema the geometric primitives use today — not PNG bytes. The apply path stays unchanged when content-aware masking lands. |
210+
| 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. |
216211

217212
### Mode B evaluators (future)
218213

@@ -244,7 +239,7 @@ Vocabulary entries can reference these via relative path. Engine copies path int
244239

245240
## Session data is local-only
246241

247-
A key principle: session data — transcripts, taste evolution, preference history, generated masks, vocabulary gaps — never leaves the photographer's machine automatically. No telemetry. No phone-home. No cloud dependency.
242+
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.
248243

249244
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.
250245

@@ -264,9 +259,8 @@ This is part of the agent-is-the-only-writer discipline — not just an architec
264259
| Per-image briefs/notes/metadata | Image context | Photographer (agent-assisted) | Per-image |
265260
| XMP snapshots | Edit state | Agent (writes), photographer (reads) | Per-image, versioned |
266261
| Session transcripts | Conversation logs | Both, captured automatically | Per-session, append-only |
267-
| Generated masks | Spatial selections | Masking provider | Per-image, registered |
262+
| Mask geometry | Spatial selections | Authored into vocabulary entries' `mask_spec` (or, in Phase 4, produced by a sibling project) | Inside the XMP |
268263
| Photo agent | AI capability | BYOA — photographer configures | External |
269-
| Masking providers | AI capability | BYOA — photographer configures | External |
270264

271265
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.
272266

0 commit comments

Comments
 (0)