From 8bbcd7f94fb75d96f0bf69712809ae193ad6d508 Mon Sep 17 00:00:00 2001 From: Forge Dev Date: Fri, 10 Jul 2026 18:44:26 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20package=20skeleton=20=E2=80=94=2013=20?= =?UTF-8?q?targets,=20NIO+ArgumentParser=20deps,=20sqlite=20shim,=20placeh?= =?UTF-8?q?older=20tests=20green?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + Package.resolved | 51 ++++++ Package.swift | 68 +++++++ Sources/CSQLite/module.modulemap | 5 + Sources/CSQLite/shim.h | 1 + Sources/ForgeCamera/ForgeCamera.swift | 1 + Sources/ForgeCameraARRI/ForgeCameraARRI.swift | 1 + Sources/ForgeCameraRED/ForgeCameraRED.swift | 1 + Sources/ForgeCameraSony/ForgeCameraSony.swift | 1 + Sources/ForgeColor/ForgeColor.swift | 1 + Sources/ForgeExport/ForgeExport.swift | 1 + Sources/ForgeGrade/ForgeGrade.swift | 1 + Sources/ForgeLibrary/ForgeLibrary.swift | 1 + Sources/ForgeLogger/ForgeLogger.swift | 1 + Sources/ForgePanel/ForgePanel.swift | 1 + Sources/ForgeSim/ForgeSim.swift | 1 + Sources/ForgeVideo/ForgeVideo.swift | 1 + Sources/forge-cli/Main.swift | 1 + .../PlaceholderTests.swift | 5 + .../PlaceholderTests.swift | 5 + .../PlaceholderTests.swift | 5 + Tests/ForgeCameraTests/PlaceholderTests.swift | 5 + Tests/ForgeColorTests/PlaceholderTests.swift | 5 + Tests/ForgeExportTests/PlaceholderTests.swift | 5 + Tests/ForgeGradeTests/PlaceholderTests.swift | 5 + .../ForgeLibraryTests/PlaceholderTests.swift | 5 + Tests/ForgeLoggerTests/PlaceholderTests.swift | 5 + Tests/ForgePanelTests/PlaceholderTests.swift | 5 + Tests/ForgeSimTests/PlaceholderTests.swift | 5 + Tests/ForgeVideoTests/PlaceholderTests.swift | 5 + docs/plans/001-implementation-plan.md | 171 ++++++++++++++++++ 31 files changed, 372 insertions(+) create mode 100644 .gitignore create mode 100644 Package.resolved create mode 100644 Package.swift create mode 100644 Sources/CSQLite/module.modulemap create mode 100644 Sources/CSQLite/shim.h create mode 100644 Sources/ForgeCamera/ForgeCamera.swift create mode 100644 Sources/ForgeCameraARRI/ForgeCameraARRI.swift create mode 100644 Sources/ForgeCameraRED/ForgeCameraRED.swift create mode 100644 Sources/ForgeCameraSony/ForgeCameraSony.swift create mode 100644 Sources/ForgeColor/ForgeColor.swift create mode 100644 Sources/ForgeExport/ForgeExport.swift create mode 100644 Sources/ForgeGrade/ForgeGrade.swift create mode 100644 Sources/ForgeLibrary/ForgeLibrary.swift create mode 100644 Sources/ForgeLogger/ForgeLogger.swift create mode 100644 Sources/ForgePanel/ForgePanel.swift create mode 100644 Sources/ForgeSim/ForgeSim.swift create mode 100644 Sources/ForgeVideo/ForgeVideo.swift create mode 100644 Sources/forge-cli/Main.swift create mode 100644 Tests/ForgeCameraARRITests/PlaceholderTests.swift create mode 100644 Tests/ForgeCameraREDTests/PlaceholderTests.swift create mode 100644 Tests/ForgeCameraSonyTests/PlaceholderTests.swift create mode 100644 Tests/ForgeCameraTests/PlaceholderTests.swift create mode 100644 Tests/ForgeColorTests/PlaceholderTests.swift create mode 100644 Tests/ForgeExportTests/PlaceholderTests.swift create mode 100644 Tests/ForgeGradeTests/PlaceholderTests.swift create mode 100644 Tests/ForgeLibraryTests/PlaceholderTests.swift create mode 100644 Tests/ForgeLoggerTests/PlaceholderTests.swift create mode 100644 Tests/ForgePanelTests/PlaceholderTests.swift create mode 100644 Tests/ForgeSimTests/PlaceholderTests.swift create mode 100644 Tests/ForgeVideoTests/PlaceholderTests.swift create mode 100644 docs/plans/001-implementation-plan.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f44143d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.build/ +*.xcodeproj +.DS_Store diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..d05e122 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,51 @@ +{ + "originHash" : "57b6115003c2c969ebbe73715dd931651604f0cecc62dafe84130999976d57b3", + "pins" : [ + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "6a52f3251125d74daf04fcbd5e6f08a75d074382", + "version" : "1.8.2" + } + }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "0442cb5a3f98ab802acb777929fdb446bda11a34", + "version" : "1.3.1" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "a0cb0954ecb21e4e31b0070e6ed5674e8556685a", + "version" : "1.6.0" + } + }, + { + "identity" : "swift-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio.git", + "state" : { + "revision" : "558f24a4647193b5a0e2104031b71c55d31ff83a", + "version" : "2.97.1" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system.git", + "state" : { + "revision" : "669763cfd5806a67e21972d7e5e2d6b80b1ea985", + "version" : "1.6.5" + } + } + ], + "version" : 3 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..b1c3bb3 --- /dev/null +++ b/Package.swift @@ -0,0 +1,68 @@ +// swift-tools-version:6.0 +import PackageDescription + +let package = Package( + name: "forge", + platforms: [.macOS(.v14)], + dependencies: [ + .package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"), + .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), + ], + targets: [ + // Pure color math + .target(name: "ForgeColor"), + // Node stack, flattening, serialization + .target(name: "ForgeGrade", dependencies: ["ForgeColor"]), + // Camera seam + .target(name: "ForgeCamera", dependencies: ["ForgeGrade"]), + // Simulated cameras + .target(name: "ForgeSim", dependencies: [ + "ForgeCamera", + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + // Vendor drivers + .target(name: "ForgeCameraARRI", dependencies: [ + "ForgeCamera", + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .target(name: "ForgeCameraRED", dependencies: [ + "ForgeCamera", + .product(name: "NIO", package: "swift-nio"), + ]), + .target(name: "ForgeCameraSony", dependencies: [ + "ForgeCamera", + .product(name: "NIO", package: "swift-nio"), + ]), + // Shot logging + storage + exports + .target(name: "ForgeLibrary", dependencies: ["ForgeGrade", "CSQLite"]), + .systemLibrary(name: "CSQLite", path: "Sources/CSQLite"), + .target(name: "ForgeLogger", dependencies: ["ForgeCamera", "ForgeLibrary"]), + .target(name: "ForgeExport", dependencies: ["ForgeLibrary", "ForgeGrade"]), + // Panel + video seams + .target(name: "ForgePanel", dependencies: [ + "ForgeGrade", + .product(name: "NIO", package: "swift-nio"), + ]), + .target(name: "ForgeVideo", dependencies: ["ForgeGrade"]), + // CLI + .executableTarget(name: "forge-cli", dependencies: [ + "ForgeCamera", "ForgeCameraARRI", "ForgeCameraRED", "ForgeCameraSony", + "ForgeSim", "ForgeLogger", "ForgeExport", "ForgePanel", "ForgeVideo", + .product(name: "ArgumentParser", package: "swift-argument-parser"), + ]), + // Tests + .testTarget(name: "ForgeColorTests", dependencies: ["ForgeColor"]), + .testTarget(name: "ForgeGradeTests", dependencies: ["ForgeGrade"]), + .testTarget(name: "ForgeCameraTests", dependencies: ["ForgeCamera", "ForgeSim"]), + .testTarget(name: "ForgeCameraARRITests", dependencies: ["ForgeCameraARRI", "ForgeSim"]), + .testTarget(name: "ForgeCameraREDTests", dependencies: ["ForgeCameraRED", "ForgeSim"]), + .testTarget(name: "ForgeCameraSonyTests", dependencies: ["ForgeCameraSony", "ForgeSim"]), + .testTarget(name: "ForgeLibraryTests", dependencies: ["ForgeLibrary"]), + .testTarget(name: "ForgeLoggerTests", dependencies: ["ForgeLogger", "ForgeSim"]), + .testTarget(name: "ForgeExportTests", dependencies: ["ForgeExport"]), + .testTarget(name: "ForgePanelTests", dependencies: ["ForgePanel"]), + .testTarget(name: "ForgeVideoTests", dependencies: ["ForgeVideo"]), + ] +) diff --git a/Sources/CSQLite/module.modulemap b/Sources/CSQLite/module.modulemap new file mode 100644 index 0000000..0a291b5 --- /dev/null +++ b/Sources/CSQLite/module.modulemap @@ -0,0 +1,5 @@ +module CSQLite [system] { + header "shim.h" + link "sqlite3" + export * +} diff --git a/Sources/CSQLite/shim.h b/Sources/CSQLite/shim.h new file mode 100644 index 0000000..f52e1f0 --- /dev/null +++ b/Sources/CSQLite/shim.h @@ -0,0 +1 @@ +#include diff --git a/Sources/ForgeCamera/ForgeCamera.swift b/Sources/ForgeCamera/ForgeCamera.swift new file mode 100644 index 0000000..6fa1b1e --- /dev/null +++ b/Sources/ForgeCamera/ForgeCamera.swift @@ -0,0 +1 @@ +// ForgeCamera diff --git a/Sources/ForgeCameraARRI/ForgeCameraARRI.swift b/Sources/ForgeCameraARRI/ForgeCameraARRI.swift new file mode 100644 index 0000000..1df3842 --- /dev/null +++ b/Sources/ForgeCameraARRI/ForgeCameraARRI.swift @@ -0,0 +1 @@ +// ForgeCameraARRI diff --git a/Sources/ForgeCameraRED/ForgeCameraRED.swift b/Sources/ForgeCameraRED/ForgeCameraRED.swift new file mode 100644 index 0000000..7ca92e5 --- /dev/null +++ b/Sources/ForgeCameraRED/ForgeCameraRED.swift @@ -0,0 +1 @@ +// ForgeCameraRED diff --git a/Sources/ForgeCameraSony/ForgeCameraSony.swift b/Sources/ForgeCameraSony/ForgeCameraSony.swift new file mode 100644 index 0000000..056f1ba --- /dev/null +++ b/Sources/ForgeCameraSony/ForgeCameraSony.swift @@ -0,0 +1 @@ +// ForgeCameraSony diff --git a/Sources/ForgeColor/ForgeColor.swift b/Sources/ForgeColor/ForgeColor.swift new file mode 100644 index 0000000..d93edd4 --- /dev/null +++ b/Sources/ForgeColor/ForgeColor.swift @@ -0,0 +1 @@ +// ForgeColor diff --git a/Sources/ForgeExport/ForgeExport.swift b/Sources/ForgeExport/ForgeExport.swift new file mode 100644 index 0000000..d9de51d --- /dev/null +++ b/Sources/ForgeExport/ForgeExport.swift @@ -0,0 +1 @@ +// ForgeExport diff --git a/Sources/ForgeGrade/ForgeGrade.swift b/Sources/ForgeGrade/ForgeGrade.swift new file mode 100644 index 0000000..47eb76b --- /dev/null +++ b/Sources/ForgeGrade/ForgeGrade.swift @@ -0,0 +1 @@ +// ForgeGrade diff --git a/Sources/ForgeLibrary/ForgeLibrary.swift b/Sources/ForgeLibrary/ForgeLibrary.swift new file mode 100644 index 0000000..91cd49c --- /dev/null +++ b/Sources/ForgeLibrary/ForgeLibrary.swift @@ -0,0 +1 @@ +// ForgeLibrary diff --git a/Sources/ForgeLogger/ForgeLogger.swift b/Sources/ForgeLogger/ForgeLogger.swift new file mode 100644 index 0000000..bd1b97f --- /dev/null +++ b/Sources/ForgeLogger/ForgeLogger.swift @@ -0,0 +1 @@ +// ForgeLogger diff --git a/Sources/ForgePanel/ForgePanel.swift b/Sources/ForgePanel/ForgePanel.swift new file mode 100644 index 0000000..b510258 --- /dev/null +++ b/Sources/ForgePanel/ForgePanel.swift @@ -0,0 +1 @@ +// ForgePanel diff --git a/Sources/ForgeSim/ForgeSim.swift b/Sources/ForgeSim/ForgeSim.swift new file mode 100644 index 0000000..72544f5 --- /dev/null +++ b/Sources/ForgeSim/ForgeSim.swift @@ -0,0 +1 @@ +// ForgeSim diff --git a/Sources/ForgeVideo/ForgeVideo.swift b/Sources/ForgeVideo/ForgeVideo.swift new file mode 100644 index 0000000..1b1e587 --- /dev/null +++ b/Sources/ForgeVideo/ForgeVideo.swift @@ -0,0 +1 @@ +// ForgeVideo diff --git a/Sources/forge-cli/Main.swift b/Sources/forge-cli/Main.swift new file mode 100644 index 0000000..ecf35ad --- /dev/null +++ b/Sources/forge-cli/Main.swift @@ -0,0 +1 @@ +@main struct ForgeCLI { static func main() { print("forge") } } diff --git a/Tests/ForgeCameraARRITests/PlaceholderTests.swift b/Tests/ForgeCameraARRITests/PlaceholderTests.swift new file mode 100644 index 0000000..ec80674 --- /dev/null +++ b/Tests/ForgeCameraARRITests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeCameraARRIPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgeCameraREDTests/PlaceholderTests.swift b/Tests/ForgeCameraREDTests/PlaceholderTests.swift new file mode 100644 index 0000000..229d844 --- /dev/null +++ b/Tests/ForgeCameraREDTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeCameraREDPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgeCameraSonyTests/PlaceholderTests.swift b/Tests/ForgeCameraSonyTests/PlaceholderTests.swift new file mode 100644 index 0000000..a9151a7 --- /dev/null +++ b/Tests/ForgeCameraSonyTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeCameraSonyPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgeCameraTests/PlaceholderTests.swift b/Tests/ForgeCameraTests/PlaceholderTests.swift new file mode 100644 index 0000000..d7f4e67 --- /dev/null +++ b/Tests/ForgeCameraTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeCameraPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgeColorTests/PlaceholderTests.swift b/Tests/ForgeColorTests/PlaceholderTests.swift new file mode 100644 index 0000000..160225e --- /dev/null +++ b/Tests/ForgeColorTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeColorPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgeExportTests/PlaceholderTests.swift b/Tests/ForgeExportTests/PlaceholderTests.swift new file mode 100644 index 0000000..42fceb0 --- /dev/null +++ b/Tests/ForgeExportTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeExportPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgeGradeTests/PlaceholderTests.swift b/Tests/ForgeGradeTests/PlaceholderTests.swift new file mode 100644 index 0000000..2baca9e --- /dev/null +++ b/Tests/ForgeGradeTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeGradePlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgeLibraryTests/PlaceholderTests.swift b/Tests/ForgeLibraryTests/PlaceholderTests.swift new file mode 100644 index 0000000..7b17a2a --- /dev/null +++ b/Tests/ForgeLibraryTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeLibraryPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgeLoggerTests/PlaceholderTests.swift b/Tests/ForgeLoggerTests/PlaceholderTests.swift new file mode 100644 index 0000000..14b4e2b --- /dev/null +++ b/Tests/ForgeLoggerTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeLoggerPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgePanelTests/PlaceholderTests.swift b/Tests/ForgePanelTests/PlaceholderTests.swift new file mode 100644 index 0000000..5688b13 --- /dev/null +++ b/Tests/ForgePanelTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgePanelPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgeSimTests/PlaceholderTests.swift b/Tests/ForgeSimTests/PlaceholderTests.swift new file mode 100644 index 0000000..d4879bf --- /dev/null +++ b/Tests/ForgeSimTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeSimPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/Tests/ForgeVideoTests/PlaceholderTests.swift b/Tests/ForgeVideoTests/PlaceholderTests.swift new file mode 100644 index 0000000..de3d311 --- /dev/null +++ b/Tests/ForgeVideoTests/PlaceholderTests.swift @@ -0,0 +1,5 @@ +import XCTest + +final class ForgeVideoPlaceholderTests: XCTestCase { + func testPlaceholder() { XCTAssertTrue(true) } +} diff --git a/docs/plans/001-implementation-plan.md b/docs/plans/001-implementation-plan.md new file mode 100644 index 0000000..df54fdf --- /dev/null +++ b/docs/plans/001-implementation-plan.md @@ -0,0 +1,171 @@ +# Forge — Implementation Plan + +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 + ForgeGrade/ // node stack, grade documents, serialization, gang/link + ForgeCamera/ // CameraDriver protocol, connection supervisor, discovery + ForgeCameraARRI/ // Alexa 35 REST driver + ForgeCameraRED/ // DSMC3 RCP2 driver + ForgeCameraSony/ // Venice 2 CNA driver + ForgeSim/ // simulated cameras (in-process HTTP/TCP servers) for tests + demo + ForgeLogger/ // rec-state watcher, shot record creation + ForgeLibrary/ // SQLite project/day/shot store + ForgeExport/ // CDL/CCC/CUBE/ALE/CSV/EDL writers + ForgePanel/ // panel abstraction (TIPC protocol layer, mappable controls) + ForgeVideo/ // video I/O abstraction (frame source/sink protocols) + forge-cli/ // headless CLI: run engine, connect sim/real cameras, script grades + Tests/ (mirror per target) + 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. +- Node stack: ordered `[GradeNode]`, kinds: `.inputTransform`, `.cdl`, `.saturation`, `.curves` (4x cubic spline master+RGB), `.lut3d`, `.outputTransform`. Enable/bypass per node. +- 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 { get } // connection, rec state, clip name, metadata + func push(look: FlattenedLook) async throws // CDL and/or LUT per capabilities +} +``` + +`ConnectionSupervisor` owns retry/backoff/health per slot. `ForgeSim` implements vendor-faithful fake endpoints (ARRI-style REST JSON, RCP2-style TCP framing, CNA-style) so drivers are tested against wire formats, not hand-waved mocks. + +--- + +## Phase 1 — Core color + grade engine (all-Linux, pure math) + +### Task 1.1: Repo + package skeleton +- `swift package init`, targets above (empty), CI-style `swift test` green. +- Commit: `chore: package skeleton`. + +### Task 1.2: ForgeColor — CDL +- Tests: identity CDL passes pixels through; known slope/offset/power vectors (hand-computed); sat=0 gives luma gray; negative-input power clamp behavior per ASC (clamp to 0 pre-power). +- Impl: `struct CDL { slope, offset, power: SIMD3; sat: Float }`, `apply(_ rgb: SIMD3) -> SIMD3`. + +### Task 1.3: ForgeColor — transfer functions +- Tests: round-trip lin→log→lin < 1e-4 for LogC4, Log3G10, S-Log3; spot-check published anchor values (e.g. LogC4 18% gray encodes to documented code value). +- Impl: encode/decode pairs + gamut matrices (RWG→Rec709 etc. as 3x3s). + +### Task 1.4: ForgeColor — 3D LUT +- Tests: identity lattice is no-op; trilinear interp matches direct eval within tolerance; CUBE parse/write round-trip; malformed CUBE rejected. +- Impl: `Lut3D` (size, flat float array), trilinear sample, `.cube` reader/writer. + +### Task 1.5: ForgeColor — curves +- Tests: identity spline no-op; monotonic control points produce monotonic output; endpoint pinning. +- Impl: Catmull-Rom/cubic hermite spline, master + per-channel. + +### Task 1.6: ForgeGrade — node stack eval +- Tests: empty stack = identity; ordering matters (CDL→LUT ≠ LUT→CDL); bypass flag skips node; full stack on known pixel vectors. +- Impl: `GradeStack.evaluate(_:)` composing ForgeColor prims. + +### Task 1.7: ForgeGrade — flattening +- 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. +- Impl: `Flattener.flatten(stack:latticeSize:splitLeadingCDL:) -> FlattenedLook`. + +### Task 1.8: ForgeGrade — serialization + snapshots +- Tests: JSON round-trip stack equality; sorted-key determinism (byte-equal re-encode); unknown-node-kind decode fails loudly. +- Impl: Codable with stable encoder config. + +## Phase 2 — Camera seam + ARRI driver + sim + +### Task 2.1: ForgeCamera — protocol, capabilities, supervisor +- 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. +- Tests: sim serves documented-shape JSON; look upload stored + retrievable. + +### Task 2.3: ForgeCameraARRI — driver against sim +- Tests (all against sim): connect + capability report; metadata poll populates state stream; rec start/stop events emitted with clip name; `push(look:)` uploads flattened LUT + CDL, sim receives correct payload; reconnect after sim restart. +- Impl: REST client, poll loop (200ms metadata, 100ms rec state), ALF4-equivalent payload assembly. NOTE: exact ARRI REST paths verified against public API docs when macOS/hardware phase starts; sim encodes best-documented shape and driver keeps paths in one constants file for easy correction. + +### Task 2.4: forge-cli MVP +- `forge sim start`, `forge connect --driver arri`, `forge look load `, `forge look push`, `forge status`. Wire supervisor + driver + grade engine. +- Tests: CLI integration test — start sim, connect, push look, assert sim received it. + +## Phase 3 — Clip logger + library + exports + +### Task 3.1: ForgeLibrary — SQLite store +- Tests: create project/day/shot CRUD; grade snapshot blob round-trip; migration table v1; concurrent writer safety (serialized queue). +- 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. + +### Task 3.5: CLI: `forge export --day --format ale|edl|cdl|cube|csv` +- Integration test: sim session → 3 auto-logged shots → export all formats → golden compare. + +## Phase 4 — RED + Sony drivers + +### Task 4.1: ForgeSim RCP2-style TCP sim + ForgeCameraRED +- RCP2 is length-prefixed JSON-ish over TCP. Sim implements handshake, param get/set/notify for rec state, clip name, IPP2 CDL slots, LUT upload. Driver tests mirror 2.3 set. Native CDL capability = true → exercises CDL-split flatten path end-to-end. + +### Task 4.2: ForgeSim CNA-style sim + ForgeCameraSony +- 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. +- Impl: NIO TCP client, control-map model (trackballs→SOP, rings→master, knobs→sat/contrast/node, buttons→slot/bypass/still/new-shot). + +### Task 5.2: ForgeVideo — frame source/sink seam +- 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. + +## Phase 6 — macOS (requires Mac hardware; out of scope on this box, scaffolded only) + +- SwiftUI app shell (3-zone layout per design), Metal preview renderer (same GradeStack→shader codegen or LUT texture sampling), DeckLink SDK C++ bridge implementing FrameSource/Sink, Tangent Hub local connection, real-camera path verification (ARRI paths const file, RED SDK agreement, Venice protocol docs). +- Everything above compiles as dependencies; app layer is thin. + +## Definition of done per task +1. Tests written first, seen failing. +2. Impl minimal to pass. +3. `swift test` fully green. +4. Commit. + +## Risks carried +- ARRI/RED/Sony exact wire details need doc/hardware verification — isolated in per-driver constants files + sims, cheap to correct. +- Venice grade path may stay LUT-only. Capability system absorbs this. +- Metal/DeckLink/Tangent-USB untestable here — protocol/TIPC layer testable, seams proven with fakes.