Implementation¶
The slice-by-slice implementation guide for Chemigram. Names slices, gates, RFC closures. Source of truth for "what gets built when." Last updated · 2026-05-10 (v1.10.0 release pass)
This document describes the implementation phases for building Chemigram, decomposed where possible into concrete slices with acceptance gates. Each slice's gate is the moment when a set of RFCs gets closed into ADRs — closure is not a continuous process, it's a checkpoint.
Visual one-pager:
docs/diagrams/phase-1-timeline.mdrenders the release sequence from Phase 0 → v1.10.0 as a Mermaid timeline. Update it whenever a new release row is added below.
This document supersedes earlier phase descriptions in docs/briefs/architecture.md (now historical) and consolidates references previously scattered across concept/, TODO.md, and ADRs.
Status snapshot¶
| Phase | Description | Status |
|---|---|---|
| Phase 0 | Validation — manual XMP composition end-to-end | ✅ Closed green (8 findings logged) |
| Phase 1 | Minimum viable loop — Python engine, MCP server, starter vocabulary | ✅ Closed (v1.0.0) — Slices 1–6 shipped. Issues #1–#29 closed; 13 RFCs closed → ADR-050..061. |
| Phase 1.1 | Comprehensive validation — capability matrix + real-darktable e2e suite | ✅ Closed (v1.1.0) — 519 tests; 3 engine bugs root-caused and fixed (ADR-062 + branch/checkout consistency + provider-exception escape). Issues #30–#38 closed. |
| Phase 1.2 | Engine unblock + reference-image validation infrastructure | ✅ Closed (v1.2.0) — slice-and-gate. RFC-018 v0.2 closed the synthesizer Path B gap → ADR-063 + ADR-064. RFC-019 v0.2 shipped the CI-safe synthetic reference fixture + assertion library → ADR-066 + ADR-067 + ADR-068. Mode A prompt v3 landed. |
| Phase 1.3 | Command-line interface | ✅ Closed (v1.3.0) — RFC-020 + PRD-005 → ADR-069/070/071/072. 22 verbs in chemigram.cli mirroring MCP verb-for-verb; output human default + NDJSON via --json; thin-wrapper discipline lint-enforced. |
| Phase 1.4 | expressive-baseline vocabulary authoring |
✅ Closed (v1.4.0) — 31+4 entries shipped (programmatic + drawn-mask). #63 channelmixerrgb B&W still wants a hand-authored darktable seed; #62 tone_lifted_shadows_subject was retired in v1.5.0 cleanup. |
| Phase 1.5 | Mask architecture cleanup | ✅ Closed (v1.5.0) — drawn-mask only (ADR-076). Removed the PNG-mask path + provider system + 5 MCP tools + the CLI masks sub-app; supersedes ADR-021/022/055/057/058/074. |
| Phase 1.6 | Parameterized vocabulary (Path C as default) | ✅ Closed (v1.6.0) — RFC-021 → ADR-077..080. 18 parameterized entries across 11 modules. Path C decoders for exposure / bilat / colorbalancergb / colorequal / sigmoid / vignette / sharpen / temperature / hazeremoval / etc. |
| Phase 1.7 | Tier 2 expansion + Lightroom-parity Bucket A (A.1–A.7) | ✅ Closed (v1.7.0) — RFC-022 → ADR-081 tiering policy. Buckets A.2–A.7 shipped (dehaze, WB tint, midtones grade, Color Grading 9 axes, Texture, ashift Transform, WB Kelvin UX). |
| Phase 1.8 | HSL Color Mixer + denoise + lens + filmic v6 | ✅ Closed (v1.8.0) — RFC-023 → ADR-083 (HSL via colorequal mv4, 24 axes). Plus #95–#97 closures for noise/lens/filmic. Lightroom daily-use parity: 51/52 (98%). |
| Phase 1.9 | Mask + retouch architecture trilogy | ✅ Closed (v1.9.0) — RFC-029 / ADR-084 (compositional spatial masks), RFC-024 / ADR-085 (parametric range filters), RFC-026 / ADR-086 (LLM-vision-as-provider), RFC-025 / ADR-087 (spot heal/clone via apply_spot MCP tool). 5 compositional-mask L2 looks added; CLI verbs vocab validate and cache added. RFC-030 deferred (deployed sibling-provider precision tier). 83 vocabulary entries shipped. |
| Phase 1.10 | Photographer-survey vocabulary expansion + workflow primitives | ✅ Closed (v1.10.0) — 6-genre photographer-workflow survey shipped; 29 new L2 looks (5 B&W / 9 landscape / 5 portrait / 5 wildlife / 4 food / 1 product) + bw_convert v2 (colorequal-based 8-axis Adams-school B&W). Three workflow primitives: RFC-035 / ADR-088 (parametric L2 strength via Path B), RFC-036 / ADR-089 (mixed-op apply_per_region), RFC-037 / ADR-090 (propagate_state LR-Sync analog). Vocab-load dtstyle-modversion drift check (Gap A from RFC-035/036/037 retro). 112 expressive-baseline entries shipped. ADRs in Draft until darkroom-session validation flips them to Accepted. |
| Phase 2 | Vocabulary maturation — grow vocab from session evidence | In progress — use-driven; intermittent; grows the personal pack on top of starter + expressive-baseline. Not slice-and-gate. |
| Phase 3 | Additional drawn-form geometries in vocabulary | ✅ Effectively closed via v1.9.0 — path geometry shipped (RFC-026 substrate / N-vertex polygons). Brush geometry remains conditional per Phase 2 evidence. |
| Phase 4 | Content-aware masking | ✅ Phase 1 closed via RFC-026 / ADR-086 (LLM-vision-as-provider, conversation-native, zero deployment). Phase 2 (RFC-030, drafted, deferred) holds the deployed sibling-provider scaffolding for the precision tier (SAM-class subject masks, depth, AI auto-spot detection). |
| ✅ Retired by ADR-081 (2026-05-07) — Path C is the default for Tier 1+2 modules per the parameterization tiering policy; further extension rides per-module Tier 2 expansion or Tier 3 promotion ADRs, not a distinct "Phase 5". |
Phases 3–4 are now substantially closed. Phase 3 (additional drawn geometries) shipped path forms via RFC-026's substrate; brush remains conditional. Phase 4 (content-aware masking) is delivered in two tiers: Phase 1 (LLM-vision, RFC-026 / ADR-086) ships now via the chat-client's vision capability; Phase 2 (deployed sibling providers, RFC-030 drafted/deferred) lands when LLM-vision precision becomes the bottleneck. Phase 5 was retired by ADR-081.
A note on "Phase" naming collisions¶
Two unrelated senses of "Phase N" exist in this project. To avoid confusion:
- Implementation phases (0–5) — the build sequence described in this document. The dominant use of "Phase" going forward.
- Doc methodology phases (Phase 1 = concept package, Phase 2 = definition documents) — a methodological layer used to produce
docs/concept/,docs/prd/,docs/rfc/,docs/adr/. The doc methodology was followed; that work is complete; the term is no longer in active use.
If "Phase 2" appears unqualified in this project from now on, it means the implementation phase (vocabulary maturation), not the doc methodology phase.
Closure-as-gate¶
A core principle for this project: RFCs don't close on judgment, they close at slice-gate events.
Every RFC names which slice's gate produces the evidence that closes it. When a slice's gate is met, the team writes the closing ADRs as part of the slice's wrap-up. This protects against two failure modes:
- Premature closure — committing to a schema before any code has exercised it
- Drift — RFCs lingering in Draft state because nobody can tell when the moment to close has arrived
Phase 1 has six slices (described below). Each names which RFCs close at its gate.
Phase 0 — Validation [CLOSED GREEN]¶
Goal: prove the vocabulary-composition approach works end-to-end before writing any Python.
Method: Author 4 single-module styles by hand in darktable, manually compose them into an XMP, render with darktable-cli, confirm the output matches expectations.
Outcome: Validated. Lab notebook at examples/phase-0-notebook.md. Eight findings closed into ADRs:
- ADR-002 — SET semantics: replace by
(operation, multi_priority) - ADR-009 — Path A vs Path B for synthesis
- ADR-010 — Vocabulary parser identifies user entries by empty
<multi_name> - ADR-011 — Reject
darktable-cli --stylefor vocabulary application - ADR-012 —
--apply-custom-presets falsealways - ADR-024 — Authoring discipline: uncheck non-target modules in dialog
- ADR-025 — WB and color calibration coupling
- ADR-026 — Vocabulary modversion-pinned to darktable version
Evidence: Render time ~2s for 1024px on Apple Silicon (darktable 5.4.1, M-series). Composition works as expected. Same-module collisions handled by SET-replace semantics in our synthesis layer.
One unexpected result: Phase 0 testing demonstrated that hex op_params manipulation is feasible for exposure (one float at predictable byte offset). This is direct evidence for Path C (Phase 5) — strengthening the case to build it earlier than originally planned. See TODO.md.
Phase 1 — Minimum viable loop¶
Goal: the simplest working Chemigram. One real session, one real image, the loop converges.
Reference docs: PRD-001 (Mode A), PRD-003 (Vocabulary as voice), PRD-004 (Local adjustments), TA (full component map).
Definition of done: A photographer opens an image, has a 10-turn session, branches once, snapshots three states, applies at least two mask-bound primitives, exports a final JPEG. The session transcript reads cleanly. Context is loaded at start, taste update proposed and confirmed at end, vocabulary gaps logged.
Phase 1 is decomposed into six slices. Slices roughly follow dependency order — earlier slices unblock later ones — but slices 4 and 5 can run in parallel after slice 3 completes.
Slice 1 — Synthesizer + render¶
Scope:
chemigram.core.dtstyleparser — read.dtstyleXML into Python objectschemigram.core.xmpsynthesizer — compose XMPs from baseline + vocabulary entries (Path A and Path B per ADR-009)chemigram.core.pipelinewithDarktableCliStage— single-stage render pipeline invokingdarktable-cli- A handful of hand-rolled
.dtstylevocabulary entries (5–10) for testing - A CLI script (not MCP yet) that takes
image + primitive_name, applies it, renders a preview - EXIF auto-binding stub (camera + lens detection from EXIF, no user-prompting yet)
Gate: Apply 5 different vocabulary primitives to a real raw, get correct rendered output. Same-module collisions handled correctly (replace, not append). Render times ≤3s for 1024px on Apple Silicon. EXIF auto-bind correctly identifies camera + lens for at least 3 different test images.
RFCs that close at this gate:
- ✅ RFC-001 (XMP synthesizer architecture) — closed via Issue #3; closes into ADR-050 (parser API + error contract). Open follow-up: Path B / iop_order origin.
- ✅ RFC-005 (pipeline stage protocol) — closed via Issue #4; closes into ADR-052 (PipelineStage Protocol + v1 single-stage DarktableCliStage)
- ✅ RFC-006 (same-module collision behavior) — closed via Issue #3; closes into ADR-051 (SET-replace, last-writer-wins, Path B deferred)
- ✅ RFC-015 (EXIF auto-binding rules) — closed via Issue #5; closes into ADR-053 (exact-match on
(make, model, lens_model))
Sketch of what comes out:
- ADRs locking parser API, error shapes, synthesizer composition algorithm
- A working
chemigram.corepackage that's useful in isolation (without MCP) - ~600–900 lines of Python
Slice 2 — Versioning + mask registry¶
Scope:
chemigram.core.versioning— content-addressed DAG of XMP snapshots (per-image)- Per-image repo creation:
~/Pictures/Chemigram/<image_id>/withobjects/,refs/heads/,refs/tags/,HEAD,log.jsonl - Snapshot, checkout, branch, log, diff, tag operations
chemigram.core.masks— mask registry per image with symbolic refs (current_subject_mask, etc.)- Mask storage in
objects/(raster PNGs, content-addressed)
Gate: Apply primitives, snapshot, branch, checkout — get the same rendered state every time. Mask registry survives across sessions. Branching three from one parent and checking out each produces three correct previews.
RFCs that close at this gate:
- ✅ RFC-002 (canonical XMP serialization for stable hashing) — closed via Issue #6; closes into ADR-054 (canonical XMP serialization). Snapshot tests pin the v3 reference and minimal fixture hashes against literal expected values.
- ✅ RFC-003 (mask storage in versioning) — closed via Issue #9; closes into ADR-055 (raster masks share
objects/;masks/registry.jsonmaps symbolic names to hashes plus provenance).
Sketch of what came out:
- ADR-054 (canonical XMP serialization), ADR-055 (mask storage)
chemigram.core.versioningpackage:canonical.py,repo.py,ops.py,masks.py- ~1,500 lines of Python (well above the 400–600 estimate — the surface was richer than projected)
Slice 3 — MCP server + tool surface ✅ shipped (v0.3.0)¶
Scope (shipped):
chemigram.core.vocab(VocabularyIndex+load_starter) — manifest-driven vocabulary loader (#10).chemigram.mcp.prompts(PromptStore+MANIFEST.toml+mode_a/system_v1.j2migrated fromdocs/agent-prompt.md) — closes RFC-016 (#11).chemigram.mcp.serverframework — stdio transport, tool registry, error-contract types, in-memory test harness, re-addedchemigram-mcpconsole script (#12).- 27 MCP tools across vocab/edit + context-stubs (#13), versioning + rendering (#14), and ingest + workspace + masks (#15).
chemigram.core.workspace.Workspaceorchestrator +ingest_workspaceflow.- End-to-end Mode A gate test (
tests/integration/mcp/test_full_session.py).
Gate: an MCP client drives ingest → bind_layers → list_vocabulary → apply_primitive → snapshot → branch/checkout → diff → tag → log → log_vocabulary_gap end-to-end through the in-memory harness; render-dependent tools surface a clean darktable_error against placeholder rawb-bytes (real-render gate runs in Slice 6 when a real raw fixture is available).
RFCs closed at this gate:
- RFC-010 (MCP tool surface) → ADR-056. Surface evidence: 27 tools, structured
ToolResultenvelope, closedErrorCodeenum,state_aftershape canonicalized. - RFC-016 (versioned prompt system) → already closed in #11; index updates landed alongside the gate.
Out of scope (deferred):
- Real masking provider — Slice 4 (
generate_mask/regenerate_maskare stubs returningNOT_IMPLEMENTEDwithslice=4). - Real context layer — Slice 5 (context-read tools are stubs with
slice=5). - Real darktable-cli baseline generation — currently uses a bundled
_baseline_v1.xmpstand-in so vocabulary primitives have a real history to SET-replace against (Path A).
Slice 4 — Coarse masking + local adjustments ✅ shipped (v0.4.0); architecturally superseded (v1.5.0)¶
Status note (2026-05-03): the substance of this slice — the
MaskingProviderProtocol,CoarseAgentProvider, the PNG mask registry, the five mask MCP tools, themask_kind/mask_refschema, themask_overrideapply argument — was all retired in v1.5.0 (ADR-076) when we discovered darktable doesn't read external PNG raster masks. The drawn-mask path (path 4a, v1.4.0) replaced it, validated end-to-end against real darktable. The historical scope below is preserved as build-record; for current architecture see ADR-076 andconcept/04-architecture.md§ 6.
Scope (shipped in v0.4.0; later superseded):
chemigram.core.masking—MaskingProviderProtocol +MaskResult(ADR-057, #17).chemigram.core.masking.coarse_agent.CoarseAgentProvider— sampling-based bundled default per ADR-058. Pillow rasterizer for bbox + polygon descriptors (#17).- Real
generate_mask/regenerate_maskMCP tool implementations replacing v0.3.0 slice=4 stubs. Auto-renders preview, calls provider, registers PNG (#18). - Mask-bound L3 vocabulary entries:
tone_lifted_shadows_subject(mask_kind: "raster",mask_ref: "current_subject_mask") added to test pack.apply_primitive(mask_override=...)real path materializes registered PNG to<workspace>/masks/<name>.pngfor darktable consumption (#19). - End-to-end gate test (
tests/integration/mcp/test_full_session_with_masks.py) drives ingest → generate_mask → list_masks → apply_primitive(mask_override) → regenerate_mask → log through the harness with a fake sampling-based masker (#20).
Gate met: mask-bound primitive applied successfully end-to-end; mask refinement loop works; mechanism proven. Mask quality target (≥70% accept-on-first-generation) is a Slice 6 manual-evidence concern; not blocking.
RFCs closed:
- RFC-004 → ADR-058 (default masking provider =
CoarseAgentProvider;chemigram-masker-samremains the recommended production upgrade). - RFC-009 → ADR-057 (Protocol shape, error categories, sampling-based pattern).
Out of scope (deferred):
- Real
chemigram-masker-samSAM-backed provider — Phase 4 sibling project. - Composite Layer 3 mask operations (ADR-021) — Phase 2+ once session evidence shows utility.
- Async
MaskingProvider.generate_async— reserved for a follow-up RFC. - Mode A prompt v2 with mask refinement specifics — deferred to Slice 6.
Slice 5 — Context layer + sessions ✅ shipped (v0.5.0)¶
Scope (shipped):
chemigram.core.context— multi-scopeTastesloader (per ADR-048:_default.md+ brief-declared genres),Briefparser,Noteswith line-truncation summarization (10 + 30 + ellision per RFC-011),RecentLog,RecentGaps(handles both v0.3.0 minimal and post-#24 RFC-013 records). Tolerant of missing files (#21).chemigram.core.session.SessionTranscript— JSONL writer per ADR-029.start_session()factory;build_server(transcript=...)plumbs into the MCP server's tool dispatch so every tool call auto-logstool_call+tool_resultentries (#22).- Real
read_context+ propose/confirm tools (propose_taste_update,confirm_taste_update,propose_notes_update,confirm_notes_update) replacing the v0.3.0 slice=5 stubs. Proposals live in-memory inToolContext.proposals; confirmation appends to the target file (~/.chemigram/tastes/<file>.mdor<workspace>/notes.md) and clears the proposal (#23). - Vocabulary gap schema upgrade to RFC-013 shape:
session_id(auto fromctx.transcript),snapshot_hash(auto from HEAD),intent,intent_category,missing_capability,operations_involved,vocabulary_used,satisfaction,notes. Backwards-compat reader handles both shapes (#24). - End-to-end gate test (
tests/integration/mcp/test_full_session_with_context.py) drives read_context → apply ×2 → propose/confirm taste → log_gap → propose/confirm notes → read_context (sees gap + log entries) (#25).
Gate met: full Mode A flow exercised end-to-end through the in-memory MCP harness with a real transcript writer attached. Tastes file + notes file gain confirmed lines; transcript JSONL contains header + tool_call/tool_result/proposal/confirmation + footer in order.
RFCs closed:
- RFC-011 → ADR-059 (loading order tastes→brief→notes→recent_log→recent_gaps; structured-top + prose-body shape; line-truncation summarization).
- RFC-013 → ADR-060 (full JSONL schema;
session_id+snapshot_hashauto-population; backwards-compat reader). - RFC-014 → ADR-061 (end-of-session is agent-orchestrated; no engine
end_sessiontool).
Out of scope (deferred):
- LLM-based notes summarization — Phase 2 if line-truncation proves inadequate.
- Auto-classification of
intent_category— Phase 2; defaults to"uncategorized". - Cross-session reflection ("across the last 3 sessions you've reached for X") — future tool.
- Mode A prompt v2 with end-of-session refinements — Slice 6 evidence-driven.
Slice 6 — Starter vocabulary + Phase 1 close ✅ shipped (v1.0.0)¶
Scope (shipped):
- Minimal starter vocabulary pack populated at
vocabulary/starter/— five entries (expo_+0.5,expo_-0.5,wb_warm_subtle,look_neutral,tone_lifted_shadows_subject) (#26). Deliberately small per the project's "starter is small; Phase 2 grows from session evidence" framing. scripts/verify-vocab.shCI check (#27) catches manifest drift fast.- Mode A prompt v2 (#28) refined for the now-real masking + context flows; v1 stays loadable for eval reproducibility.
- Phase 1 closeout: pyproject
0.5.0→1.0.0; classifierPre-Alpha→Beta; doc surfaces synced; tag v1.0.0 + milestone close (#29).
Gate met: framework-complete release per the project decision (RFC-014's manual photographer-session evidence is Phase 2 use-driven, not a 1.0.0 blocker).
Out of scope (deferred to Phase 2):
- Authoring 30–50 vocabulary entries from real session evidence — Phase 2 is the use-phase that grows the pack.
- The
iguana-galapagos.mdreal-session walkthrough — Phase 2 evidence-collection. - RFC-007 (modversion drift) — stays open until a darktable update forces the question.
RFCs that don't close in Phase 1¶
| RFC | Why |
|---|---|
| RFC-008 (vocabulary discovery at scale) | Speculative — only relevant after vocabulary grows past ~100 entries (Phase 2 mid-stage) |
| RFC-012 (programmatic generation Path C) | Deferred — closes when discrete vocabulary granularity becomes a bottleneck (Phase 5 trigger) |
| RFC-007 (modversion drift) | May or may not close in Phase 1 depending on whether a darktable update lands during the phase |
Phase 1 risks¶
Same-module collision edge cases. Phase 0 validated the common case; Phase 1 may surface edge cases (different multi_priority, masked vs unmasked variants). Cheap to handle when they appear; addressed in Slice 1's gate.
Coarse masking quality. PRD-004's sharpest threat applies — if the default masker isn't usable, Phase 1's local-adjustment story is weak. Slice 4's gate is where this risk gets validated; if the gate doesn't pass, the response is to document SAM (Phase 4) more aggressively as the production path, not to delay Phase 1.
Performance on first real session. ~2s renders are fine in isolation; sustained over a session with branching, the loop has to feel responsive. Worth profiling in Slice 6.
Slice ⅘ parallelism. They're designed to run independently after Slice 3, but if Slice 4 surfaces a need for context (e.g., the masker reading the photographer's taste), the parallelism breaks. Watch for this.
Phase 2 — Vocabulary maturation¶
Goal: Grow the vocabulary from what real sessions surface, not from what we imagined upfront.
This is a use-phase, not a build-phase. The starter vocabulary is deliberately small and generic. Real sessions will surface gaps — moves the photographer wanted that the vocabulary didn't have. Each gap, captured properly (vocabulary gap log + later authoring), grows the personal vocabulary.
Phase 2 doesn't decompose into slices the way Phase 1 does, because the work is intermittent and use-driven, not a build sequence with gates.
Activity:
- Run real sessions in the Phase 1 engine
- Log gaps as they surface (the agent flags them automatically per Slice 5's tooling)
- Periodically (a vocabulary-authoring evening per month, roughly) open darktable, capture missing primitives as
.dtstylefiles, drop them into personal vocabulary - Watch which primitives get used heavily, which never get used at all
- Refine, retire, and rename as understanding deepens
Reference docs:
- PRD-003 — Vocabulary as voice (the central thesis being tested)
- ADR-023 — Vocabulary primitives are
.dtstyle+ manifest entries - ADR-024 — Authoring discipline
- CONTRIBUTING.md, vocabulary contributions section
Markers of maturation:
- After 3 months: ~30–60 personal entries beyond the starter pack
- After 6 months: ~80–120 personal entries; starter pack additions submitted upstream
- After 1 year: ~150–200 personal entries; vocabulary feels like it captures the photographer's craft
Phase 2 ends when: the photographer hits a class of need the vocabulary architecture itself can't satisfy. That's the trigger for Phase 3, 4, or 5 (whichever applies).
Phase 3 — Parametric masks in vocabulary [conditional]¶
Trigger: Phase 2 sessions repeatedly surface needs for masked vocabulary entries that pre-baked parametric masks (luminance / chroma / hue ranges) can satisfy.
Goal: Add a tier of vocabulary entries with darktable-native parametric masks captured in the GUI as part of the style.
Examples: expo_+0.5_subject_only, wb_warm_water_only, tone_lift_shadows_subject — each authored by setting up the parametric mask in darktable's GUI, then capturing the style.
Engine work: None new. Parametric masks are part of blendop_params, which the synthesizer already treats as opaque. The contribution is purely vocabulary.
Reference docs:
- PRD-004 — Local adjustments (Layer 1 in the three-layer mask pattern)
- ADR-021 — Three-layer mask pattern
Phase 3 may be implicit. It's possible Phase 2 vocabulary growth already includes parametric-masked entries from day one (since darktable supports them natively). In that case Phase 3 dissolves into Phase 2 with no formal transition.
Phase 4 — AI masks via external raster module [conditional]¶
Trigger: Phase 2 sessions repeatedly surface needs for subject-aware local adjustments that parametric masks can't cleanly express. Specifically: needing to mask "the manta" or "the bird's eye" without the limits of luminance/hue thresholds.
Goal: Ship at least one production-quality AI-masking provider. The masking abstraction itself is built in Phase 1 Slice 4; Phase 4 is when the SAM-based provider lands.
Scope:
chemigram-masker-samsibling project (separate repo) wrapping Segment Anything- Distribution path: pip-installable, model-weight-download script
- Configuration in user's MCP setup
- Documentation update: shift the "production path" prose from coarse-agent to SAM
Reference docs:
- PRD-004 — Local adjustments through AI masking
- RFC-004, RFC-009 — closed in Phase 1 Slice 4
Note on dependency: chemigram-masker-sam lives in its own repo (per ADR-032). Building it is independent of the engine's roadmap. It can ship anytime after Phase 1 Slice 4 has locked the MaskingProvider protocol.
Phase 5 — Continuous control via hex encoders (Path C) [retired by ADR-081, 2026-05-07]¶
Status: Retired. The question this phase asked — should the project commit to extending Path C beyond high-value modules? — was answered by the parameterization work between v1.6.0 and the RFC-022 closure:
- RFC-021 (Decided) shipped the apply-time Path C architecture and 8 magnitude-ladder modules (Tier 1 in ADR-081 terms).
- RFC-022 (Decided) shipped the Tier 2 baseline expansion (4 brand-new modules: sharpen, crop, colorbalancergb additional axes, toneequalizer) and codified the four-tier parameterization policy in ADR-081.
Per ADR-081, Path C is the default for Tier 1+2 modules; ADR-008's opacity continues for Tier 3. Further parameterized-vocabulary work happens as Tier 2 expansions (during the build-baseline phase) or Tier 3 promotion ADRs (post-multi-photographer-review). There is no distinct "Phase 5" implementation effort separate from those cadences.
What's preserved as historical record: Phase 0's evidence that exposure's hex op_params was reverse-engineerable directly informed the v1.6.0+ parameterization architecture. Phase 5 was the placeholder for that work; ADR-077..080 + ADR-081 are its conclusion.
Recalibrations from the original plan¶
The original plan in docs/briefs/architecture.md had six phases with rough time estimates ("a few hours," "a weekend," "a week, intermittent"). Some have been validated; some need adjustment.
| Brief estimate | Now we think |
|---|---|
| Phase 0: a few hours | ✅ Took ~6 hours including unexpected findings — accurate |
| Phase 1: a weekend | Likely 2–3 weekends — the brief underestimated. Six slices, ~2,000–3,000 lines total. |
| Phase 2: a week, intermittent | This was always going to be open-ended. "A week" was misleading — vocabulary maturation runs indefinitely. |
| Phase 3: when needed | Likely already happens implicitly inside Phase 2. May not exist as a distinct phase. |
| Phase 4: if needed | Probably needed within 6 months of real Phase 1 use. The marine-photography use case demands AI masks. |
| Phase 5: if needed | Phase 0 evidence suggests starting on exposure earlier. May fragment into 5a (exposure now) + 5b (other modules later). |
These are estimates, not commitments. The phasing is conditional on what use surfaces.
Maintenance¶
This document is updated when:
- A slice's gate is met and RFCs close (update the slice's status, the closed RFCs' status in
rfc/index.md, andadr/TA.mdmap) - A phase status changes (started, blocked, closed)
- The phasing itself revises (a slice splits, merges, or is deleted; a phase changes scope)
- A trigger condition gets met for a conditional phase
- An RFC closes into an ADR that affects phasing assumptions
Status changes should ripple to: docs/concept/00-introduction.md (the "Project status" section near the bottom), CLAUDE.md (the "Phase awareness" section), and docs/TODO.md (if relevant items are unblocked).
Phase descriptions themselves are stable — they describe intent, not progress. The status snapshot at the top is what changes most often.
IMPLEMENTATION · v0.1 · The slice-by-slice plan. Closure-as-gate.