On-set live grading. Pushes CDL/LUT looks over IP directly to Alexa 35 / RED DSMC3 / Venice 2. Node-stack grade engine, Tangent Wave panel, auto clip logging from camera metadata, DeckLink SDI I/O, shot library + post deliverables.
## Ground rules
- Swift 6.0.3, SwiftPM monorepo at `forge/`. Toolchain: `/opt/swift/usr/bin` (add to PATH).
- TDD: test first, watch fail, implement, watch pass. Every task.
- Commit after every green task. Message style: `core: add CDL node math`, `driver-arri: parse rec state`.
- Dev box is Linux; product targets macOS. All hardware (cameras, DeckLink, Tangent, Metal) sits behind Swift protocols. Linux gets mock/sim implementations — these ARE the test doubles. macOS real implementations land later behind `#if os(macOS)` and are the only untestable-here code.
- YAGNI: no speculative config, no plugin systems, no premature abstraction beyond the hardware seams listed.
## Package layout (SPM targets, one repo)
```
forge/
Package.swift
Sources/
ForgeColor/ // color math: CDL, curves, LUT, color spaces, flattening
App/ (macOS-only Xcode project, later phase, not buildable on Linux)
```
Dependencies kept minimal: swift-nio (TCP/HTTP servers+clients for drivers and sims), sqlite3 via system module (no GRDB on Linux — thin hand-rolled DAO), swift-argument-parser (CLI). Nothing else.
## Color/data model decisions (fixed now)
- Working space: linear float RGB internally. Input transforms: LogC4, Log3G10+RWG, S-Log3+SGamut3.Cine → linear; output transform for preview: Rec709 (2.4 gamma). Published coefficient sets from vendor whitepapers, encoded as tested constants.
- CDL math per ASC spec: `out = (in * slope + offset)^power`, sat per Rec709 luma weights (0.2126/0.7152/0.0722) — matches ASC CDL 1.2.
- Flattening: evaluate stack over 33³ (default) or 65³ lattice in camera-native log space → single 3D LUT. If node[0] is CDL and target camera supports native CDL, split: CDL stays live-tweakable, remainder baked.
- Grade snapshot: full stack serialized JSON, stored per shot. Deterministic encoding (sorted keys) so snapshots diff cleanly.
## CameraDriver seam (fixed now)
```swift
public protocol CameraDriver: Actor {
var capabilities: CameraCapabilities { get } // nativeCDL, lut3dSizes, metadataFields...
func connect() async throws
func disconnect() async
var state: AsyncStream<CameraState> { get } // connection, rec state, clip name, metadata
- Tests: flattened 33³ LUT of a stack matches direct eval within 1/1024 max err on random samples in-gamut; CDL-split mode: node[0] CDL excluded from bake, returned separately; deterministic output.
- Tests: supervisor retries with backoff against a driver that fails N times then connects; state stream publishes disconnect on driver error; slot registry add/remove.
- Impl: as seam above + `CameraSlot`, `SlotRegistry`.
### Task 2.2: ForgeSim — ARRI-style simulator
- In-process NIO HTTP server mimicking Alexa 35 REST shape: auth-less JSON endpoints for system info, rec state, clip name, EI/WB/tint/TC, look upload accept + store. Scriptable: test can flip rec state, change clip name.
- Impl: system sqlite3 module, thin DAO, WAL mode.
### Task 3.2: ForgeLogger — auto shot records
- Tests: rec-start event from driver → shot row with clip name, TC-in, metadata snapshot, current grade snapshot; rec-stop → TC-out/duration; offline camera → manual shot API; duplicate rec-start debounce.
- Impl: subscribes slot state streams, writes via ForgeLibrary.
### Task 3.3: ForgeExport — CDL/CCC/CUBE
- Tests: golden-file comparisons for .cdl XML, .ccc multi-clip, .cube from snapshot; re-import parses equal.
### Task 3.4: ForgeExport — ALE/CSV/EDL
- Tests: ALE header/column golden file with camera metadata fields; CMX3600 EDL with CDL comments (`* ASC_SOP`/`* ASC_SAT` lines) parse-checked; CSV escaping.
- Venice 2 capability-narrow: LUT push + metadata read; nativeCDL=false. Tests mirror 2.3. Capability flags verified: pushing CDL-split look to Sony driver auto-falls-back to full bake (test this explicitly).
### Task 4.3: Multicam gang
- Tests: gang A+B → edit on A pushes to both, each flattened per own camera capabilities/log space; ungang stops propagation; per-slot override survives gang edits.
## Phase 5 — Panel + video seams
### Task 5.1: ForgePanel — TIPC protocol layer
- Tangent Hub speaks TIPC over TCP (port 64246) — protocol layer fully testable on Linux against a scripted fake Hub. Tests: handshake/registration XML map, encoder delta → CDL param change with sensitivity curve, button → command dispatch, display text writeback (panel shows param values), focus-follows-slot.
- Protocols: `FrameSource` / `FrameSink` (pixel buffer + timecode). Linux impl: file-based test source (reads DPX/PNG frames), null sink. Tests: grade-stack applied to test frame matches golden graded frame; still-grab writes correct file, attaches to shot.
- DeckLink real impl deferred to macOS phase; seam proven here.
### Task 5.3: Debounced push pipeline
- Tests: rapid param changes (simulated panel spins) → preview eval every change, camera push coalesced to ≤1 per 150ms, final state always pushed (trailing edge); per-camera independent debounce.