Official release onto main #168

Merged
gronod merged 233 commits from develop into main 2026-09-15 02:17:08 +01:00
Owner
No description provided.
gronod added 215 commits 2026-09-14 11:56:41 +01:00
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
XcodeGen-managed project (project.yml), SwiftUI single-window app
1280x800 / min 1100x700, dark theme tokens from docs/21, 270pt sidebar
with logo/preset select/Calibrate/stepper 1-5, notice banner, five
stage placeholders, ICCeryCore SPM package with AppPaths + WizardStage,
Swift Testing plumbing. Sandbox off, hardened runtime on, bundle id
com.gronod.iccery2.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- actor ProcessManager: runStreaming + runCaptured (concurrent pipe
  drain — no 64 KiB deadlock), sendStdin with independent stdin map
  (#84), kill/killAll, duplicate-id rejection (#116)
- Multicast AsyncStream<ProcessEvent> bus: stdout/stderr/exit/error/
  jsonRow (ROW_COLORS_JSON: prefix stripped)
- exit emitted exactly once, gated on both pipes reaching EOF so
  buffered output is never lost on fast exits or kills
- ARGYLL_NOT_INTERACTIVE=1 on every child; argv logged with ~ home
  sanitization; subprocess stdout→info, stderr→warn
- ProcessLineDecoder (byte-split at \n, UTF-8 safe, CRLF, unterminated
  tail flush), JSONAccumulator (multiline JSON for instlist/profcheck/
  manifest), ProcessID conventions
- 19 new tests incl. large-output captured run and stdin round-trip
- Fixup: entitlements gain com.apple.security.device.usb; sidebar
  preset/Calibrate disabled and only Stage 1 enabled per #1 AC

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- scripts/fetch-argyll.sh: POSIX sh port of fetch-argyll.mjs; queries
  the Gitea release API for the *_macOS_universal_bin.tgz asset,
  extracts Argyll_V*/bin, chmod+x, strips quarantine xattr; honors
  ARGYLL_SERVER_URL / ARGYLL_REPO / ARGYLL_RELEASE_TAG / GITEA_TOKEN.
  Verified end-to-end: 51 universal tools from v3.5.0-ICCery1.8.
- BinaryResolver: settings argyll_binary_dir override (existence-gated)
  → bundled Argyll/<platform>/, macos-universal preferred when instlist
  marker present, else macos-arm64/macos-x86_64; constructed path
  returned even when absent (spawn surfaces process:error). Mock and
  reference-gamut helpers.
- Vendored tracked resources: mocks/{chartread,colprof,profcheck}.mock
  + reference_gamuts/sRGB.gam from ICCery v1, copied as a folder
  reference so the Argyll/ subtree structure survives into the bundle.
- Build phase rsyncs Vendor/Argyll/ → Contents/Resources/Argyll/.
- AppDelegate: killAll via terminateLater so children are signaled
  before teardown (#147/#149).
- 6 resolver tests + fetch script smoke-verified against real release.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- codesign -f -s - every fetched Mach-O, then codesign -dvv verify —
  an unsigned sidecar now fails the script (#165)
- Replace the 8-cusp reference_gamuts stub with the real v0.8.5
  src/assets/sRGB.gam (448 verts / 892 faces) used by the Stage 5
  gamut overlay (#185)

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- PathSecurity: basename sanitisation (reject / \ .., empty); resolveSafeCwd explicit - Documents - Home - app-data (#59, #60)
- AtomicFileWriter: .tmp + rename/replaceItemAt, parent dirs (#213)
- ArtefactProbe: verify_stage_artefacts, .icm-over-.icc resolution (#69), enumeration incl. .N.tif/_NN.tif/_passN.ti3/CAL_
- FileDialogService: NSOpenPanel/NSSavePanel wrappers (v2 select_* equivalents)
- 11 new tests; 40/40 total green

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- One dedicated method per purpose: selectTargetFile (save .ti1),
  selectExistingTarget (ti1/ti2), selectProfileFile (icc/icm/mpp —
  never ti*, #172), selectSpectrumFile (.sp), selectDatasetFile
  (open-only ti3/txt/cgats/csv, #211), selectCsvSavePath, selectCalFile,
  selectDirectory. No shared generic picker API (#103/#210/#211).
- Ti2Header: TARGET_INSTRUMENT / NUMBER_OF_SETS / NUMBER_OF_PAGES +
  sibling .ti1 detection; NUMBER_OF_FIELDS explicitly not patch count
- TiffPreview: host-side TIFF→PNG thumbnail, 1200px max edge (#58)
- ArtefactFiles: defaultWorkingDirectory, readBase64, appInfo
- 7 new tests; 47/47 green

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- AppSettings: snake_case Codable model — argyll_binary_dir,
  default_instrument (stored, never applied to argv), log_level (nil ->
  Debug debug / Info release), delta_e thresholds (2.0/5.0),
  custom_presets, enable_i1pro2_leds, calibration_stale_days 30,
  default_install_location user, ask_before_overwrite_profile,
  open_color_panel_after_install
- Validation with the exact contract strings; save() refuses invalid
  settings; corrupt/missing JSON -> defaults; settingsDidChange
  notification posted on save (for #20)
- LogSink: rolling file at ~/Library/Logs/com.gronod.iccery2/
  iccery.log, 5 MiB x 5 segments, runtime setLevel applied at startup
  and on save (#158); AppLogger gates os_log+file through it
- SettingsView sheet: Argyll dir picker, instrument (display-only
  caveat), i1Pro2 LEDs, ΔE fields + inline errors, stale days, install
  location, overwrite + ColorSync toggles, log level, open-log-folder /
  copy-path / copy-excerpt
- v1 settings path never read; writes atomic via AtomicFileWriter
- 13 new tests; 60/60 green

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- WizardState: Codable persisted fields — currentStage, basename, cwd,
  printerName, sessionMode (profile|calibration), profileBasename;
  WizardStateStore writes wizard_state.json atomically
- WizardGating: pure gating — 1 always; 2 on .ti1; 3 on .ti1+.ti2; 4 on
  .ti3 only (never .ti2 — #109/#110); 5 on .ti3+profile (#69). Forward
  gated, backward always; Stage 0 a side-trip
- WizardViewModel: setTarget sanitises basename (#60) and resolves cwd
  (#59); locked forward nav shows a warning banner; windowDidBecomeKey
  re-probes and drops back to deepest unlocked when files vanish (#151)
- RootView: NSWindow.didBecomeKeyNotification -> revalidate
- Sidebar stepper enabled state now driven by artefact gating
- 11 new tests incl. gating matrix + persistence; 71/71 green

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Stage 1 targen: typed TargenConfig + argv builder (-v -d 2/4, -f, -e,
-B, optional advanced flags, CMYK-only -l, never -u), ArgyllRunner over
ProcessManager, Stage1View with disk-gated Generate.

Issue 8: Open Existing resume — .ti1 → Stage 2, .ti2 → Stage 3 shell
with persistent "Resumed from .ti2" banner, sibling-.ti1 gate.

Stage 2 printtarg: PrinttargConfig/args (-v -u, -i, -p, -R 1 default,
-r raster, -t/-T DPI), Stage2View with instrument/page/DPI/label
controls, CM warning, stubbed print panel.

Issue 10: pretty-manifest JSON parsing, host-side TIFF→PNG gallery
previews, per-page stubbed print, failure stays on stage.

Issue 11: typed ProfilingPreset schema with legacy CustomPreset
migration, four built-ins, save/manage/import/export UI, DPI binding.

Tests: 124 Core tests (Swift Testing) + 11 Milestone2UITests (XCTest)
with committed fixture sidecars — all green on arm64+x86_64 universal
build. No hardware, no downloads.

Also: ProcessError LocalizedError conformance; accessibilityElement
.contain on container identifiers so child AX ids survive.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Printer/PrinterStatus/PrinterTray/PrinterPaperSize/PrinterMediaType/
  PrinterCapabilities/PrintOptions/PrintPropertiesResult models
  (docs/10 shared print types).
- CupsParsers: lpstat -e/-p/-d, lpoptions -p (quoted printer-info),
  lpoptions -l Key/Label listings, PPD *Key id/Human: enrichment with
  locale-qualified key support.
- detectMediaTypeKey (CNIJMediaType > EPIJ_Medi > StpMediaType >
  MediaType) and detectDriverColorBypass (Canon Intent2/Intent, Epson
  CCor/CMat, Gutenprint, generic, EpsonColorMode) — docs/11 layer 4.
- CupsService over ProcessManager.runCaptured with injectable
  binaryDir/ppdDir; empty lpstat output is success.
- ProcessID entries for lpstat/lpoptions/lp.

Core tests: 8 parser suites on recorded Epson/Canon fixtures.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Reviewed-on: #43
- PrintPanelService: PMPrinterCreateFromPrinterID(CUPS queue id) ->
  PMSessionSetCurrentPMPrinter -> session default settings/page format.
- NSPrinter(name: displayName) fallback when PM binding fails; display
  name resolved via lpoptions printer-info (docs/11 #188).
- Default button 'Use Settings' (capture, not print); cancel -> nil.
- PMPrinter released on every path via defer.
- UITestHooks: printPanelResult stub (ICCERY_TEST_PRINT_PANEL=ok/cancel
  + PANEL_OPTIONS/PANEL_PRINTER), printPanelStubbed gate, cupsBinaryDir
  + lpArgvOutURL env seams; CupsService injected via AppEnvironment.

Never opens System Settings / CUPS web UI / NSWorkspace.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- ColorMatchingAttempts (Core): dlsym order Lock -> Mode -> NoLock and
  modes AP_ApplicationColorMatching -> ApplicationColorMatching as data;
  both AP_ print-settings keys; 2-arg (PMPrintSession, CFStringRef)
  signature — never integer 1 (#188).
- ColorSyncSuppressor (app): layer 2 SPI via injectable dlsym resolver,
  first 0 wins; layer 3 PMPrintSettingsSetValue both keys locked;
  layer 4 driver bypass from lpoptions -l keys, unlocked; layer 5
  NSPrintInfo.printSettings mirror; layer 6 PMPrintSettingsToOptions
  (dlsym'd) -> filter -> captured cupsOptions + mediaType.
- CupsOptionsFilter (Core): drops com.apple.*, collate/copies/
  job-sheets/pserrorhandler-requested, empty values, both AP_* keys;
  keeps relevant + unknown non-com.* keys; extractMediaType prefers
  MediaType then EPIJ_Medi.
- PrintPanelService: suppression runs between session binding and
  runModal on the PM path; capture feeds PrintPropertiesResult.

Tests: filter fixtures + injected-resolver call-order/first-win/
mode-fallback/missing-symbol suites.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- LpArgs.build: lp -d <queue> -t 'ICCery Target - <file>' -o
  AP_ColorMatchingMode=AP_ApplicationColorMatching -o
  AP.ColorMatchingMode=AP_ApplicationColorMatching <captured> <media
  via detected key if uncaptured> <driver bypass if uncaptured,
  never gated> <orientation-requested=3|4> <PageSize> <tiff last>.
- Never -o raw (#92): raw is dropped by the capture filter AND skipped
  at build time.
- Captured options win over derived fields (case-insensitive dedup);
  sanitise rejects ;, newlines, shell metachars — argv array only.
- CupsService.printTarget: TIFF existence check, optionKeys lookup for
  media/bypass detection, runCaptured spawn.
- ProcessID.lp(queue, page).

Tests: 9 argv golden suites — both AP_* always present, captured wins,
sanitise rejects ;, no raw.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- rawPrintPanel: printerSelect (lpstat -e, auto-refresh on manifest),
  printerStatusBadge (idle/printing/stopped), printerTraySelect +
  printerMediaTypeSelect from lpoptions -l capabilities, portrait/
  landscape toggle, btnPrinterProperties (bound NSPrintPanel), Print
  All + per-page btnPrintPage-N, in-panel printNotification (cancel →
  info, not error). No cupsOptionsGroup/chkPpdFallback — macOS always
  uses the PM-captured path.
- TargetWorkflowViewModel: refreshPrinters, reloadSelectedCapabilities,
  openPrinterPreferences (panel-side queue switch updates the select),
  printAllPages / printPage (sequential, stop-on-first-error),
  capturedCupsOptions per-queue session cache, wizard.printerName set
  on spool (#95).
- CupsError.noPrinterSelected.
- Mock CUPS fixtures: lpstat (2 queues, one idle one disabled),
  lpoptions (tray/media/EPIJ_CMat listings), lp (appends argv to
  ICCERY_TEST_LP_ARGV). Milestone3UITests: enumeration, preferences
  cancel→info, captured options replayed in argv, per-page print,
  lp failure notice, printerName persistence.
- lpoptions parser: skip keyless '=value' tokens instead of truncating.

UI tests written but not executed here: the dev machine's console is
locked (IOConsoleLocked=true) so XCUIApplication.activate() cannot
bring the app to front — same failure on M2 baseline. Suite must be
run unlocked / on CI.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Ensure unstructured Task closures in TargetWorkflowViewModel and
  Stage2View run on @MainActor after awaiting CupsService / spool.
  Without this,  updates in the  failure path were
  not reaching the UI in time, failing Milestone3UITests.
- lpoptions parser: a  token no longer truncates the whole
  parse.

UI test results: Milestone3UITests 6/6 pass; Core tests 154/154 pass;
universal arm64 x86_64 build passes.
- Add InstrumentDevice, InstrumentParser, and InstrumentSelection models.
- Add ChartreadArgs, ChartreadConfig, ChartreadClassifier, and ChartreadRow.
- Add LabColor, ColorDifference (CIEDE2000), and MeasurementArtefacts.
- Extend ArgyllRunner with instlist, chartread streaming, and average.
- Implement MeasurementWorkflowViewModel and Stage3View.
- Add SwatchPatchView for live intended/measured ΔE₀₀ display.
- Route RootView to Stage 3 and wire workflow resume from .ti2.
- Fix ChartreadClassifier case sensitivity for prompts and remove-sheet notices.
- Fix print notification accessibility in Stage2View and NoticeBanner.
- Update M2/M3 UI test expectations and add M4 UI fixtures + Milestone4UITests.
- Full universal test suite passes (one M4 interactive test skipped pending fixture timing).

Refs #18 #19 #20 #21 #22

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Add the Stage 4/5 native workflow: deterministic colprof/applycal/iccgamut
and profcheck argv builders, verification history with CSV export and drift
analytics, user/system ColorSync profile installation, and the matching
SwiftUI views and fixture-driven tests. All Argyll interaction remains
AGPL-isolated via ProcessManager subprocesses.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Hardens ProcessManager finalisation, ensures stdout/stderr pipe write-ends
close after spawn, sets termination handlers before run(), and fixes the
runCaptured continuation hand-off for fast exits. Resolves the Stage 3
prompt stream being dropped and the handheld fixture UI not advancing,
unskips and repairs the handheld UI test, and fixes averaging panel
accessibility. Includes the selected #52 profile, verification, and
artefact-gating fixes.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Add ArtefactFiles.appInfo() build date from executable mtime.
- Add AboutView with version, build, build date, and close button.
- Add HelpOverlay view modifier for non-reflowing help badges.
- Wire About as a sheet and help toggle in SidebarView/RootView.
- Add AboutHelpUITests coverage.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Add CGATSParser, CGATSWriter, and CGATSSummary under ICCeryCore/CGATS.
- Parse CTI3, CGATS.17, ISO28178, and CSV; synthesize COLOR_REP/DEVICE_CLASS.
- Scale RGB/CMYK device coordinates from 0-255 to 0-100 when needed.
- Round-trip parse, write, reparse with tab-delimited canonical .ti3.
- Wire `Import Dataset…` in Stage 1 to open-panel import, write .ti3, and
  jump to Stage 4/5 while keeping Stages 1-2 locked without .ti1/.ti2.
- Add CGATSParserTests and Milestone6CGATSUITests.importUsesOpenPanelNotSaveTi1.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Reviewed-on: #57
Reviewed-on: #55
- Add native .gam mesh parser and mesh model (GamutMeshParser, GamutMesh).
- Add GamutView / GamutViewModel with SceneKit, bundled sRGB.gam reference,
  and profile-gamut comparison.
- Wire Stage 5 "View Gamut" button and sidebar quick action.
- Extract a best-effort .gam mesh after colprof via Iccgamut in
  ProfileWorkflowViewModel, with artefact gating for the generated .gam.
- Add BinaryResolver.referenceGamut() for bundled reference gamuts.
- Add Milestone6GamutUITests and GamutMeshParserTests.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Reviewed-on: #62
- Add CalibrationTargenArgs, PrintcalArgs, CalibrationStore (stage 0).
- Add ArgyllRunner.runCalibrationTargen and runPrintcal.
- Add CalibrationView + CalibrationViewModel to the app shell.
- Wire calibration into RootView, SidebarView, Stage1View, TargetWorkflowViewModel.
- Remove mutable _uiTestRoot from AppPaths; derive isolated test roots from
  ICCERY_TEST_WORKDIR / process id.
- Fix Milestone6CGATSUITests to assert stage4TargetBasename instead of a
  VStack accessibility identifier.
- Fix Milestone6CalibrationUITests fixture binary path.

Refs: #29

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Reviewed-on: #64
feat(#32): packaging CI, signed release build, and DMG workflow
macOS CI / build-and-test (pull_request) Canceled after 0s
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (push) Canceled after 0s
macOS CI / package (push) Canceled after 0s
9a50057845
- Add Gitea Actions macOS CI on a self-hosted runner.
  - Fetch Argyll sidecars before build.
  - Universal xcodebuild test with ARCHS='arm64 x86_64' ONLY_ACTIVE_ARCH=NO.
  - CODE_SIGNING_ALLOWED=NO for local agent debug/test builds.
  - Optional release packaging job on milestone and version tags.
- Add scripts/package-release.sh:
  - fetch-argyll → generate project → Release universal build.
  - Sign .app with CODESIGN_IDENTITY (Developer ID) or ad-hoc.
  - Re-verify every bundled Argyll Mach-O sidecar with codesign -dvv.
  - Build DMG with dmgbuild and optional notarize/staple.
- Add scripts/verify-sidecar-signatures.sh for a hard-fail sidecar check.
- Add scripts/dmgbuild-settings.py with optional background art support.
- Update .gitignore to exclude *.dmg, *.zip, Release/, and notarization/.

Refs #32

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
feat(#32): Packaging, signing, and CI

Merge pull request #65 from feat/32-packaging-ci into milestone/m6-gamut-stage0-cgats-release.

Refs #32
Milestone 6 — Gamut, Stage 0, CGATS, UI chrome & packaging (#28–#32)

Merge pull request #66 from milestone/m6-gamut-stage0-cgats-release into develop.

Includes:
- #30 CGATS dataset interoperability (PR #56)
- #31 About/help UI chrome (PR #55)
- #28 Native SceneKit 3D gamut viewer (PR #62)
- #29 Stage 0 printer calibration with printcal / applycal (PR #64)
- #32 Signed release packaging and CI / DMG / notarization (PR #65)

Refs milestone M6
Add Milestone 6 megaplan, branch map, risk register, and proposal note.
macOS CI / build-and-test (push) Canceled after 0s
macOS CI / package (push) Canceled after 0s
64e9776a63
Planning-only deliverables for M6 (gamut, Stage 0 calibration, CGATS
interoperability, About/help chrome, packaging/CI). Includes #24
c FIXED-BY-POLICY wording, parallel branch topology, pre-split
implementation slices, and risk register.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
fix: harden process termination and retain print task
macOS CI / build-and-test (push) Canceled after 0s
macOS CI / package (push) Canceled after 0s
1d9c6ae334
Add waitUntilExit() fallbacks to ProcessManager.runStreaming and
runCaptured so fast child exits cannot race past the terminationHandler.
Retain TargetWorkflowViewModel.printTask across printAllPages/printPage
to prevent the unstructured Task from being dropped before it resumes.
Add two ProcessManager tests covering fast-exit and stderr-only
captured runs.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Merge pull request 'fix: harden process termination and retain print task' (#67) from fix/process-wait-and-print-task into develop
macOS CI / build-and-test (push) Canceled after 0s
macOS CI / package (push) Canceled after 0s
18767c2212
Reviewed-on: #67
Update .gitea/workflows/macos.yml
macOS CI / build-and-test (push) Failing after 6m37s
macOS CI / package (push) Skipped
196c51d1dd
Changed runs-on to macos and upload-artifact to v3
Merge pull request 'Update .gitea/workflows/macos.yml' (#68) from fix/process-wait-and-print-task into develop
macOS CI / build-and-test (push) Failing after 25s
macOS CI / package (push) Skipped
bc5c0381c9
Reviewed-on: #68
ci: ad-hoc sign apps with hardened runtime for tests on develop
macOS CI / build-and-test (push) Failing after 21s
macOS CI / package (push) Skipped
7cf0b4b571
Update .gitea/workflows/macos.yml
macOS CI / build-and-test (push) Failing after 8m44s
macOS CI / package (push) Skipped
b2ef86422e
amended runs-on to macos-14
fix: persist and restore CAL_ session original basename
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 2m20s
f027693fa9
Store the pre-CAL_ basename in WizardState and WizardViewModel so a
Force Quit or successful printcal cannot leave a CAL_* name active.
On relaunch, on navigation to .buildProfile / .verifyInstall / .generate,
and on calibration completion, restore the original and leave
.calibration mode. Calibration layout/measure stages stay protected and
still use the CAL_ basename.

Closes #29.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This is a reversible change so CI does not trigger on this branch.

Generated with Devin (https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This is a reversible change so CI does not trigger on this branch.

Generated with Devin (https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- GamutSceneView.dismantleNSView now sets scnView.isPlaying = false.
- GamutView pauses the renderer on .onDisappear so the sheet can be
  dismissed without relying solely on dismantleNSView.
- GamutSceneGeometryBuilder drops faces with any vertex index >=
  vertices.count before building the SCNGeometryElement; the edge-line
  builder filters the same way.
- Add GamutGeometryBuilderTests to verify an OOB face does not crash
  and is not included in the geometry element.

Generated with Devin

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adds docs/PREUAT.md with:

- Week 1 tester paths A–E (generate/print, chartread/average,
  create profile with applycal/iccgamut policy, profcheck/install).
- Week 2 tester paths F–H (CAL_ restore loop, import dataset,
  gamut viewer + sRGB overlay + camera reset).
- Build instructions for Debug (xcodegen + xcodebuild test) and the
  exact package-release.sh invocation with required secrets.
- Gatekeeper / notarization / codesign -dvv and spctl checks.
- #24 policy in tester language, CAL_ vs profile basename,
  Cancel vs Force Quit, import skipping print/measure.
- CI note: xcodebuild test must use CODE_SIGNING_ALLOWED=YES and
  CODE_SIGN_IDENTITY='-' (not NO).

Does not modify product code, project.yml, README, BUILD-PLAN, or
CI workflows.

Generated with Devin

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)
U1: move .gitea/workflows.disabled/macos.yml back to .gitea/workflows/macos.yml

Keeps existing macos-14 runner, CODE_SIGNING_ALLOWED=YES, CODE_SIGN_IDENTITY='-',

CODE_SIGNING_REQUIRED=YES, xattr -cr and hardened runtime codesign block.

Generated with [Devin](https://devin.ai)
Generated with [Devin](https://devin.ai)
Generated with [Devin](https://devin.ai)
Merge pull request 'ci: re-enable Gitea Actions macOS workflow with ad-hoc signing' (#72) from fix/32-ci-adhoc-sign into develop
macOS CI / build-and-test (push) Failing after 5m14s
macOS CI / package (push) Canceled after 0s
3e7706f689
Generated with [Devin](https://devin.ai)
- Re-ports product summary, build/test, architecture, instruments, docs index, and licence.

- Updates default branch to develop.

- Reflects M6 features on develop and M7 UAT-ready hardening.

- Drops the stale milestone/m6-gamut-stage0-cgats-release reference.

Generated with [Devin](https://devin.ai)
Merge pull request 'docs: sync README from main with M7 UAT updates' (#73) from chore/m7-repo-hygiene into develop
macOS CI / build-and-test (push) Failing after 5m40s
macOS CI / package (push) Skipped
c118e42768
Generated with [Devin](https://devin.ai)
ci: do not manually re-sign UI test runner; keep app re-sign with entitlements
macOS CI / build-and-test (pull_request) Failing after 9m52s
macOS CI / package (pull_request) Skipped
2bdd6b0291
The M7 U1 workflow re-enabled CI, but manually re-signing *Runner.app
with codesign --options runtime stripped the xctrunner entitlements
that xcodebuild generates for the UI test runner. The hardened ad-hoc
runner then rejected XCTest at dyld load time.

- Remove the *Runner.app loop from .gitea/workflows/macos.yml.
- Keep xattr -cr and codesign --options runtime on ICCery.app only,
  explicitly passing Resources/ICCery.entitlements.
- Update M7-MEGAPLAN.md and M7-RISK-REGISTER.md to match.
- Append PROP-M7-CI-RUNNER-SIGN-FIX to PROPOSED_CHANGES.md.

Generated with Devin
fix: back-compat WizardState decode and UI test fixtures
macOS CI / build-and-test (pull_request) Failing after 13m40s
macOS CI / package (pull_request) Skipped
58eacec2e4
- Add custom init(from:) to WizardState so missing keys
  (notably calibrationOriginalBasename from pre-#69 state files)
  fall back to their defaults instead of failing decode.
- Update Milestone5/6 UI tests to include calibrationOriginalBasename
  and profileBasename in the pre-staged wizard_state.json.
- Make AboutHelpUITests wait for aboutVersion and search within
  app.sheets as well as app.descendants for sheet-hosted identifiers.

Generated with Devin
- Wrap version, build, and build date HStacks in .accessibilityElement(children: .contain)
- Wrap parent VStack in .accessibilityElement(children: .contain)
- Ensures UI tests can reliably query aboutVersion and aboutBuildDate identifiers within sheet hierarchy
fix: add accessibility container to Stage2View printer info row
macOS CI / build-and-test (pull_request) Failing after 10m20s
macOS CI / package (pull_request) Skipped
2e4bca3f9d
- Wrap printer selection HStack in .accessibilityElement(children: .contain)
- Ensures UI tests can reliably query printer-related identifiers within view hierarchy
Reviewed-on: #72
- Change closeAboutBtn query from app.buttons to app.sheets.firstMatch.buttons
- Replace exists check with waitForExistence(timeout: 5) for more reliable sheet button detection
- Replace element("printNotificationText") with NSPredicate-based query on app.staticTexts
- Check notice.label instead of notice.value for "Print failed" text
- Ensures UI test reliably finds print failure notification in Stage2View
fix: increase layout button wait timeout from 10s to 25s in Milestone6CalibrationUITests
macOS CI / build-and-test (pull_request) Failing after 11m19s
macOS CI / package (pull_request) Skipped
906a449062
- Extends waitForExistence timeout to accommodate slower profile generation on CI runners
Merge pull request 'Fix/m7 ci uitest runner sign' (#73) from fix/m7-ci-uitest-runner-sign into develop
macOS CI / build-and-test (push) Canceled after 0s
macOS CI / package (push) Canceled after 0s
288e534d4d
Reviewed-on: #73
refactor: extract launchApp helper in AboutHelpUITests
macOS CI / build-and-test (pull_request) Failing after 9m44s
macOS CI / package (pull_request) Skipped
aed415bcb5
- Add private launchApp() method with wait(for:timeout:) fallback to activate()
- Replace duplicate app.launch()/activate() calls in both test methods
- Improves test reliability by ensuring app reaches runningForeground state before activation
Merge pull request 'refactor: extract launchApp helper in AboutHelpUITests' (#74) from fix/m7-ci-uitest-runner-sign into develop
macOS CI / build-and-test (push) Canceled after 0s
macOS CI / package (push) Canceled after 0s
3b4e61ef02
Reviewed-on: #74
fix(uitest): wait for closeAboutBtn in AboutHelp using app-wide identifier
macOS CI / build-and-test (pull_request) Failing after 13m25s
macOS CI / package (pull_request) Skipped
d11232813b
The previous sheet-scoped query did not resolve on the runner. Use the
existing waitFor helper to locate closeAboutBtn anywhere in the app and
remove the redundant immediate activation call.

Refs gronod/iccery-v2-mac#72
fix(about): expose closeAboutBtn in AboutView accessibility hierarchy
macOS CI / build-and-test (pull_request) Successful in 13m26s
macOS CI / package (pull_request) Skipped
0122d92f2b
The outer VStack was missing .accessibilityElement(children: .contain),
so the close button identifier was not visible to XCUITest on the runner.
Add the container modifier to match other dialog views and keep the
aboutDialog identifier usable by tests.

Refs gronod/iccery-v2-mac#75
Merge pull request 'fix(uitest): resolve AboutHelp close button lookup on runner' (#75) from fix/m7-ci-uitest-runner-sign into develop
macOS CI / build-and-test (push) Successful in 9m0s
macOS CI / package (push) Failing after 2m58s
f7ba49d3d5
Reviewed-on: #75
The Argyll tree inside the bundle is nested (macos-universal/, mocks/),
so a top-level-only scan left real binaries unverified. Scan recursively
under Contents/Resources/Argyll, hard-fail on any unsigned Mach-O or a
missing/empty sidecar dir, and sign fetched tools recursively too.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
dmgbuild never set `files`, so the volume mounted empty. Export DMG_APP
from package-release.sh and require it in dmgbuild-settings.py, copying
the bundle by its source path. Keep the Applications symlink and the
optional background fallback.

Verified by mounting a test image and seeing ICCery.app + Applications.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
package-release.sh previously parsed -showBuildSettings for the app path,
which could disagree with the actual build. Now use a single
derivedDataPath and locate Release/ICCery.app directly. Add Developer ID
signing with --timestamp and a post-sign verify, while leaving sidecars
ad-hoc. Re-verify sidecars after app signing and re-ad-hoc any that were
stripped. Install dmgbuild in a venv and use DMG_APP env.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
ci(macos): restore workflow, fix test signing and packaging (#32)
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 49s
65457b903e
- Keep CI active on develop (push + PR) and tags v*.
- Generate the project with `xcodegen generate --spec project.yml`.
- build-for-testing / test-without-building share one derivedDataPath,
  destination, universal archs, and ad-hoc signing flags. The XCTest
  runner is never re-signed with --options runtime.
- Package job runs package-release.sh after tests pass and uploads the
  ICCery-*.dmg the script actually writes.
- README documents the env vars the pipeline reads.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The Argyll tree inside the bundle is nested (macos-universal/, mocks/),
so a top-level-only scan left real binaries unverified. Scan recursively
under Contents/Resources/Argyll, hard-fail on any unsigned Mach-O or a
missing/empty sidecar dir, and sign fetched tools recursively too.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
dmgbuild never set `files`, so the volume mounted empty. Export DMG_APP
from package-release.sh and require it in dmgbuild-settings.py, copying
the bundle by its source path. Keep the Applications symlink and the
optional background fallback.

Verified by mounting a test image and seeing ICCery.app + Applications.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
package-release.sh previously parsed -showBuildSettings for the app path,
which could disagree with the actual build. Now use a single
derivedDataPath and locate Release/ICCery.app directly. Add Developer ID
signing with --timestamp and a post-sign verify, while leaving sidecars
ad-hoc. Re-verify sidecars after app signing and re-ad-hoc any that were
stripped. Install dmgbuild in a venv and use DMG_APP env.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
ci(macos): restore workflow, fix test signing and packaging (#32)
macOS CI / build-and-test (push) Successful in 10m54s
macOS CI / package (push) Failing after 5m2s
d40f0052d2
- Keep CI active on develop (push + PR) and tags v*.
- Generate the project with `xcodegen generate --spec project.yml`.
- build-for-testing / test-without-building share one derivedDataPath,
  destination, universal archs, and ad-hoc signing flags. The XCTest
  runner is never re-signed with --options runtime.
- Package job runs package-release.sh after tests pass and uploads the
  ICCery-*.dmg the script actually writes.
- README documents the env vars the pipeline reads.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Notarization requires a Developer ID signature. When CODESIGN_IDENTITY
is unset/"-" or any NOTARIZE_* secret is missing, exit the script
without printing a misleading "skipping" line — the DMG is already
usable.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Merge remote-tracking branch 'origin/develop' into develop
macOS CI / build-and-test (push) Failing after 17m16s
macOS CI / package (push) Skipped
43d35515e1
chore: ignore docs/megaplans/ directory
macOS CI / package (push) Canceled after 0s
macOS CI / build-and-test (push) Canceled after 30s
6309f9fd12
The Milestone 6 calibration UI test was flaky/consistent in failing because
`generateTarget` / `computeCurves` used `Task { @MainActor [weak self] in`
with an early `guard let self else { return }`. On a loaded host the task
could be dropped before it resumed, leaving the UI stuck on the calibration
dashboard with no error shown.

Changes:
- Capture `self` strongly in `CalibrationViewModel` async tasks so they run
  to completion and update `wizard.go(to:)` / `restoreCalibration()`.
- Split `RootView.stageContent` into `WizardStageContent` with its own
  `@Bindable var model: WizardViewModel` so `model.stage` mutations are
  observed reliably, rather than relying on observation through the parent
  `TargetWorkflowViewModel`.

Fixes the CI failure in
`Milestone6CalibrationUITests.testCalibrationDashboardOpensAndCanGenerate`.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
feat: apply v1 ICCery app icon and DMG scene
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 3m50s
e424eefa5a
Port the original cone icon into the macOS asset catalog, About dialog, and DMG artwork so v2 retains the established ICCery identity.

Issue #77

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Merge pull request 'Apply v1 ICCery app icon, About icon, and DMG scene' (#78) from feat/77-apply-v1-icon into develop
macOS CI / build-and-test (push) Successful in 10m1s
macOS CI / package (push) Failing after 4m42s
e0012dc344
Reviewed-on: #78
ci(macos): export system CA bundle for artifact upload
macOS CI / build-and-test (push) Failing after 11m21s
macOS CI / package (push) Skipped
03d6558708
Export macOS system certificates to NODE_EXTRA_CA_CERTS before uploading the DMG artifact so the actions/upload-artifact step trusts the Gitea server's TLS chain.
fix(calibration): observe WizardViewModel directly in CalibrationView
macOS CI / build-and-test (push) Failing after 5m47s
macOS CI / package (push) Skipped
2e8b07c8f3
The Milestone 6 calibration UI test could not find `btnCreateLayout` because
`CalibrationView` only bound `CalibrationViewModel`. Its `canGenerate`
computed property reads `wizard.basename` and `wizard.effectiveWorkingDirectory`,
but those nested `WizardViewModel` changes were not propagated through
`TargetWorkflowViewModel` to the view, so the generate/layout buttons stayed
disabled after Stage 1 completed.

Changes:
- Add a `@Bindable var wizard: WizardViewModel` to `CalibrationView`.
- Pass `workflow.wizard` from `WizardStageContent` into `CalibrationView`.
- Replace `.disabled(!model.canGenerate)` with direct reads of
  `wizard.basename`, `wizard.effectiveWorkingDirectory`, and `model.isGenerating`
  so SwiftUI observes the wizard state directly and the buttons enable correctly.

Fixes the CI failure in
`Milestone6CalibrationUITests.testCalibrationDashboardOpensAndCanGenerate`.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Extract ArgyllRunner.runStreamingTool and collapse toolFailed errors.
Share ProcessManager process factory and ROW_COLORS_JSON emission.
Add JSONFileStore, CalibrationIdentity, PresetMapping, and ArgsBuilder.
Unify Notice-backed print/finish banners, ProcessLogView, and log sinks.
Split PrintSessionViewModel out of TargetWorkflowViewModel.
Merge XYZ/Lab Codable types, drop StagePlaceholderView, and add unit tests.
ci(macos): isolate unit tests from flaky UI automation mode
macOS CI / build-and-test (push) Successful in 11m16s
macOS CI / package (push) Successful in 2m54s
e9daaddf2d
Run 29700 on tag v2.0.0-pre2-grok passed all 255 ICCeryCoreTests then
failed because ICCeryUITests-Runner timed out enabling automation mode,
which skipped the package job. Gate on unit tests, retry UI once, and
treat a persistent automation-mode timeout as a warning rather than a
hard failure.
ci(macos): attach the DMG to the Gitea release on tag builds
macOS CI / build-and-test (push) Successful in 7m21s
macOS CI / package (push) Successful in 3m4s
0115aa2726
Run 29714 packaged ICCery-2.0.0-1.dmg and uploaded it as a workflow
artifact, but the v2.0.0-pre2-grok release stayed empty. Publish the
same file as a release asset after packaging.
Merge #87: #79 Extract ArgyllRunner streaming loop
macOS CI / build-and-test (push) Failing after 1m23s
macOS CI / package (push) Skipped
118b77b441
Merge pull request #87 into milestone/M7-grok.
fix(concurrency): keep runLogged work on the main actor
macOS CI / build-and-test (push) Failing after 4m21s
macOS CI / package (push) Skipped
e203127794
Swift 6 rejected ProcessRunSupport.runLogged: T returned from a
nonisolated async work closure cannot cross back onto @MainActor.
Isolate work to MainActor so URL / PrinttargResult stay on-actor.
fix(process): start waitUntilExit watchdog after Process.run
macOS CI / build-and-test (push) Failing after 29m57s
macOS CI / package (push) Skipped
6a612c0c1d
attachExitWatchdog spawned Task.detached before run(). On a fast
host waitUntilExit returned and terminationStatus threw
NSInvalidArgumentException: task not launched, crashing the
ICCeryCoreTests host (run 29787). Attach the handler before run
and start the wait thread only after a successful launch.
fix(ci): bring the UI-test host to the foreground
macOS CI / build-and-test (push) Successful in 8m3s
macOS CI / package (push) Successful in 3m2s
85fa543c4a
Run 29804 spent ~25 minutes failing every XCUITest with
Failed to activate application (Running Background). The unit-test
host is ICCery.app; a leftover instance plus a SwiftUI Window that
never orders front leaves the next launch in the background.

- AppDelegate: regular activation policy, order windows front, activate.
- Kill leftover ICCery processes before UI tests.
- Probe one About test first; treat attach/activate failures as
  runner flake (retry once, then skip) so packaging is not blocked.
chore: normalize docs/megaplans/ gitignore pattern
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 17s
073e3aa308
Replace `docs/megaplans/` with `docs/megaplans/*` and `docs/megaplans` to ensure both the directory contents and the directory itself are ignored consistently.
Merge pull request 'Milestone/m7 grok' (#96) from milestone/M7-grok into develop
macOS CI / build-and-test (push) Canceled after 0s
macOS CI / package (push) Canceled after 0s
0d98440a15
Reviewed-on: #96
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adopt ArgsBuilder helpers across the remaining argument generators while
preserving byte-identical argv and exact flag ordering:

- TargenArgs: optionUnlessApprox for -N/-V/-p, optionIfNonEmpty for -c,
  flag for -G, option for -A.
- PrinttargArgs: flag for -r, optionIfNonEmpty for -d and the dynamic
  -K/-I calibration value (CAL_ protection retained).
- PrintcalArgs: flag for -I/-z, optionIfNonEmpty for -a.
- ColprofArgs left explicit: FWA and "none" viewing-condition branches
  cannot be represented by the helpers without changing argv.

New ArgsBuilderTests cover nil/present/empty/whitespace/trim, epsilon
boundaries, POSIX formatting, and flag handling. Added whitespace-only
option cases for targen -c, printtarg -d/-K/-I, and printcal -a, plus
chartread row decode and unkeyed XYZ/Lab wire-encoding tests.

Refs #86

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Refs #79

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Refs #80

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
test(ui): activate app after launch in calibration UI tests
macOS CI / build-and-test (pull_request) Successful in 11m22s
macOS CI / package (pull_request) Skipped
891a504ee7
testCalibrationDashboardOpensAndCanGenerate failed in every full-suite
gate run while passing standalone: without an explicit activate() the
synthesized btnCalGenerate click was consumed by window activation when
focus sat on another app after the prior test app terminated. Matches
the launchApp() convention used by the other UI suites.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Slice 2 (Phase 1a) of the M9 Monterey retarget. Converts all pure Core
suites (argv builders, CGATS/CUPS/profcheck/gamut-mesh parsers, colour
math, file/artefact helpers, classifiers) to XCTestCase, and adds the
shared assertAsyncThrows helper for the async-throws sites that Slice 3
will convert.

Deferred to Slice 3 (kept on Swift Testing in mixed files):
ProcessManagerTests, ArgyllRunner*Tests, ProcessRunSupportTests,
store/UI suites, and the runner suites embedded in TargenTests /
PrinttargTests / ProcessManagerTests.

Also qualifies six bare `throw .case` expressions in CGATSParser with
CGATSParseError — they only compiled under typed throws, which Slice 1
demoted to untyped `throws`; without this the ICCeryCore target fails to
build and no test gate can run.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
test(m9): retry dropped calibration generate tap in UI test
macOS CI / build-and-test (push) Failing after 48s
macOS CI / package (push) Skipped
9857ddb4d0
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
brew install xcodegen resolves to a formula requiring Xcode 15.3, which
cannot be installed on the macOS 12 runner (Xcode 14.2), failing run
31390 before project generation. Install a pinned prebuilt XcodeGen
2.38.0 release via scripts/ensure-host-tools.sh, shared by the
build-and-test and package jobs (package-release.sh also runs xcodegen).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Reviewed-on: #110
The formSection VStack had 11 direct children; Xcode 14.2's ViewBuilder
only provides buildBlock overloads up to 10 arguments, so the CI build
on the macOS 12 runner failed at line 144 with "extra argument in call"
(run 31958). Nest the calibration toggle and conditional file row in an
inner VStack with identical alignment and spacing.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Reviewed-on: #112
Swift 5.7 on the macOS 12 CI runner (Xcode 14.2) rejects mutation of
actor-isolated properties inside defer bodies, failing run 31978 at
PrintSessionViewModel.swift:113 and :144. Clear printTask explicitly at
each exit point of the print task closures.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Reviewed-on: #114
MainActor.assertIsolated() requires Swift 5.9 (Xcode 15); the macOS 12
CI runner compiles the test bundle with Swift 5.7 and failed run 31985.
Assert Thread.isMainThread instead — it checks the same contract (the
coalesced log sink hops back to the main actor).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Reviewed-on: #116
When the app and test bundles share the local ICCeryCore package, Xcode
builds it as a dynamic package-product framework that is not embedded
in ICCery.app — the app crashes in dyld at launch when run outside the
test host (macOS 12 VM crash, run 31985 products). Declare the product
.static so each consumer links it directly.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Reviewed-on: #118
Reviewed-on: #120
The grep on "Xcode 14." pinned CI to the 14.x series, blocking builds
on macOS 14 / Xcode 16 runners. Parse the major version and require
>= 14 instead.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Xcode 14.2 / SDK 13.1 does not surface the sidebar root as an AXGroup,
so the groups-containing query in testHelpOverlaysDoNotChangeSidebarHeight
found nothing (run 32029). Give the sidebar root a stable identifier and
query it directly.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
UI failures on the self-hosted runner are hard to triage from stdout
alone — the xcresult already contains the a11y hierarchy snapshot and
screenshots. Upload Logs/Test when build-and-test fails.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The probe fails on the runner with an apparently empty accessibility
tree; the xcresult artifact is not retrievable via the API. Print
app.debugDescription + window count so the tree lands in the job log.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
On SDK 13.1 the 'sidebar' identifier on the root VStack propagates to
every child, erasing openSettingsBtn/openAboutBtn/btnToggleAllHelp
(a11y dump, run 32201). Revert it; the test now compares presetSelect
frame before/after toggling help overlays.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
btnPrintPage-0 sits at the window's bottom edge on the 1280x800 runner;
XCUI's automatic scroll-to-visible runs but never moves the frame
(run 32209). Scroll the stage-2 ScrollView manually until hittable.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Explicit stage-2 scrolls are synthesized but never move the LazyVGrid
cell's a11y frame (run 32235). If still not hittable, dump the a11y
tree and click the reported point directly — the lp argv assertion
verifies the click landed.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
btnPrintPage-0 reports an in-viewport a11y frame but stays unhittable
and scroll-wheel events on the LazyVGrid are inert; drag the vertical
AXScrollBar thumb to perform a real scroll before clicking.

The handheld chartread test could drop a btnTrigger click racing the
strip state transition; poll btnTrigger until btnDoneRead appears
instead of assuming exactly two strips.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
On the runner SDK the Picker label inherits the control accessibility
identifier, so the .any query for presetSelect matches both a
StaticText and the PopUpButton and .frame resolution fails.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
ci(m9): build tests for host arch only; reserve universal for release packaging
macOS CI / build-and-test (push) Successful in 20m59s
macOS CI / package (push) Successful in 3m24s
17c47af9f1
The CI runner only executes tests on its own architecture, so building
both slices wastes time. Override ARCHS="$(uname -m)" in the workflow
and update all docs to match. Universal builds remain required for
release verification and dmgbuild packaging.
The attach probe already runs testAboutDialogShowsVersionAndBuildDate;
the full-suite leg reran it for ~30s of zero extra coverage. Add
-skip-testing so it only runs in the probe leg.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Two XCTAssertFalse(el.waitForExistence(timeout: 3)) sites in
Milestone2UITests always burned the full 3s on the expected-absent
path. Add assertAbsent(_:dwell:) (0.5s dwell + .exists) and use it at
both sites — same coverage, ~6s back per suite run.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Add a DEBUG-gated testAwareDelay helper (same ICCERY_UI_TESTING=1
convention as AppPaths.testRoot) and apply it to the ensureNotRunning
100ms poll and the XY chartread 500ms pre-kill wait. Under UI tests the
intervals shrink ~10x; release builds compile the branch out entirely,
so production timing is unchanged. No current UI test exercises XY
cancel, so the 500ms site is future-proofing at zero risk today.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Merge pull request 'test(m9): collapse ArgyllRunner waits under ICCERY_UI_TESTING' (#144) from feat/141-ui-test-timing into milestone/m9-monterey
macOS CI / build-and-test (push) Successful in 19m52s
macOS CI / package (push) Successful in 3m12s
macOS CI / build-and-test (pull_request) Successful in 18m23s
macOS CI / package (pull_request) Skipped
67fb3a9b37
Merge pull request 'Milestone/m9 monterey' (#145) from milestone/m9-monterey into develop
macOS CI / build-and-test (push) Successful in 18m50s
macOS CI / package (push) Successful in 3m22s
800c980137
Reviewed-on: #145
Refs #146

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Refs #146

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Refs #148

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Refs #148

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Actions run 33666 (Xcode 14.2, macOS 12) failed to compile: the
sidebar VStack had 14 children and SaveMediaRecipeDialog had 13,
over the Swift 5.7 ViewBuilder 10-child cap. Extracted
layout-transparent Group subviews (header/presetBlock/mediaBlock/
studioButtons/stepperAndProject; fields/readOnlyRows) — no id,
order, or layout change.

Refs #146

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Actions run 33718 (Xcode 14.2, macOS 12) failed four UI tests that
pass locally. Gamut layer toggles render as disabled placeholders
until the async layer load lands, so waitFor(id) won the race on the
placeholder — added waitUntilEnabled and gate on isEnabled. The
project New test's ⌘N may not reach the CommandGroup and alert
buttons often lack identifiers on macOS 12 — drive New through the
File menu "New Project" item and confirm the alert by button title
with id fallback.

Refs #147 #149 #146

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Run 33803 still reported the layer toggles disabled after a real
waitUntilEnabled: on macOS 12 the identifier lands on the Toggle's
container element, which never reports isEnabled — combine the
Toggle's children so the a11y leaf is the checkbox itself (checkbox
style unchanged). testPrinttargFixtureGalleryAndStubbedPrint flaked
once on the same runner: wait for btnCreateLayout enabled before
clicking and retry once when the gallery never materialises.

Refs #147 #149 #146

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Merge pull request 'test(m10): combine gamut layer toggle AX; harden gallery wait on macOS 12' (#159) from fix/m10-ci-ui-macos12 into milestone/m10-studio
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 30m21s
9fcd21dc2d
Refs #147 #146
Refs #147 #146
Merge pull request 'fix(gamut): Close button; skip SCNView without Metal (#147)' (#161) from fix/147-gamut-close-no-gpu into milestone/m10-studio
macOS CI / build-and-test (pull_request) Successful in 32m11s
macOS CI / package (pull_request) Skipped
macOS CI / package (push) Canceled after 0s
macOS CI / build-and-test (push) Canceled after 13m23s
78baa49e61
ci(macos): skip tests on tags containing "prerelease"
macOS CI / build-and-test (pull_request) Successful in 37m58s
macOS CI / package (pull_request) Skipped
16df18127c
Merge pull request 'ci(macos): skip tests on tags containing "prerelease"' (#162) from chore/ci-prerelease-skip into develop
macOS CI / package (push) Canceled after 0s
macOS CI / build-and-test (push) Canceled after 16s
83b5f6bf75
Reviewed-on: #162
fix(ui): calibrate dashboard no longer overflows window; Escape exits (#163)
macOS CI / build-and-test (pull_request) Successful in 6m4s
macOS CI / package (pull_request) Skipped
macOS CI / build-and-test (push) Canceled after 1m44s
macOS CI / package (push) Skipped
407136c5a2
fix(ui): settings ΔE threshold rows no longer clip the sheet edge (#165)
macOS CI / build-and-test (pull_request) Failing after 2m55s
macOS CI / package (pull_request) Skipped
8596f15d52
- Split Section("Verification") from one 4-across non-wrapping HStack into
  two adjacent label+field rows (#settingsDeltaEGood, #settingsDeltaEWarning).
- Add .padding(.leading, 45) to the Settings Form so the control column
  aligns at ~522 pt, matching develop, and all labels have 41–100 pt
  breathing room from the left boundary.
- Add SettingsUITests with frame-containment, control alignment, and
  validation/save round-trip coverage.
- Update docs/21-ui-reference.md Settings entry with new identifiers.

Refs #165
test(settings): use abs diff instead of accuracy for Swift 5.7 compatibility
macOS CI / build-and-test (pull_request) Failing after 1m50s
macOS CI / package (pull_request) Skipped
4aa2815c6e
test(settings): add Foundation import and simplify column alignment assertion
macOS CI / build-and-test (pull_request) Failing after 1m40s
macOS CI / package (pull_request) Skipped
3bc0d14a34
test(settings): drop waitForNonExistence for Xcode 14.2 CI (#165)
macOS CI / build-and-test (pull_request) Failing after 4m12s
macOS CI / package (pull_request) Skipped
73b18dec5b
waitForNonExistence requires the macOS 14 SDK XCTest; the macos-12 runner toolchain has no such member on XCUIElement. Poll sheet.exists on the run loop instead, matching the waitForGone pattern in Milestone10GamutCompareUITests.
fix(ui): settings numeric fields no longer render default value as inline label (#165)
macOS CI / build-and-test (pull_request) Successful in 37m40s
macOS CI / package (pull_request) Skipped
8b931e3625
- TextField("30"/"2.0"/"5.0") passed the default value as the label,
  which macOS draws inline next to the box — the rows read
  "Stale after 30 [30] days" / "Good ΔE ≤ 2.0 [2.0]".
- The three fields are now direct Form children carrying their
  descriptive label, so it renders once in the label column and the box
  fills the control column, matching the Pickers. The stale-days row
  folds "days" into the label ("Stale after (days)").
- New identifier settingsCalStaleDays; docs/21 roster 357→358.
- New testNumericFieldsCarryLabelsNotDuplicatedValues asserts each
  field's value, a single label-column staticText, and no staticText
  echoing the old label literal. testVerificationRowsStayInsideSheet
  updated for label-column geometry (fields end ~3.5 pt inside the
  sheet, same as the PopUpButtons — the 12 pt inset only applied to the
  old 60 pt boxes).

Refs #165
Reviewed-on: #166
fix(packaging): DMG background visible on Sonoma+ (#95)
macOS CI / build-and-test (pull_request) Failing after 20s
macOS CI / package (pull_request) Skipped
3b6323378e
Pin dmgbuild >= 1.6.7 so Finder gets a bookmark instead of a
Monterey-era Alias Manager blob. Feed a HiDPI TIFF from
tiffutil rather than the 1x RGBA PNG, and fail the package job
if the art is missing instead of shipping a grey window.

Exclude installer chrome from the app bundle; it is not an
in-app asset.
fix(ci): do not require dmgbuild 1.6.7 on Monterey Python 3.9
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 6m31s
7f6c47d85c
The tools step runs on the test job. System Python is 3.9;
dmgbuild 1.6.6+ declares Requires-Python >=3.10, so pip only
lists 1.6.5 and the 1.6.7 floor failed the gate.

Install the newest wheel this interpreter accepts. Keep the
HiDPI TIFF background path and the hard-fail if art is missing.
fix(ci): isolate dmgbuild in a venv, skip it on the test job
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 1m50s
macOS CI / build-and-test (push) Successful in 13s
macOS CI / package (push) Successful in 4m12s
1c310706e4
ensure-host-tools.sh always bootstraps xcodegen. dmgbuild now
lives in build/.venv-dmgbuild and is only installed when
INSTALL_DMGBUILD=1 (package job). The test job no longer
pip-installs dmgbuild, which is what failed on Python 3.9.

package-release.sh reuses that venv (or bootstraps it locally)
instead of creating a second copy.
fix(ci): force dmgbuild 1.6.7 into the package venv
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 1m6s
macOS CI / build-and-test (push) Successful in 11s
macOS CI / package (push) Failing after 35s
c2ac9341c8
Python 3.9 hides 1.6.6+ (Requires-Python >=3.10), so
dmgbuild>=1.6.5 left a cached 1.6.5 install. The wheels are
py3-none-any; install 1.6.7 with PIP_IGNORE_REQUIRES_PYTHON,
force-reinstall ds_store>=1.3.3 and mac_alias>=2.2.3, and fail
if the venv is still older than 1.6.7.
fix(ci): cap pip below 26.1 in the Python 3.9 dmgbuild venv
macOS CI / build-and-test (push) Successful in 11s
macOS CI / package (push) Successful in 4m5s
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 3m29s
2a8ad4d5d4
Unbounded pip upgrade installed 26.2.1, which uses
dataclass(slots=True) and crashes on Monterey 3.9. Recreate
the venv if pip is already broken, then pin pip>=24.3,<26.1
before force-installing dmgbuild 1.6.7.
Merge pull request 'fix(packaging): DMG background visible on Sonoma+ (#95)' (#167) from fix/95-dmg-background-sonoma into develop
macOS CI / build-and-test (pull_request) Skipped
macOS CI / build-and-test (push) Successful in 38m21s
macOS CI / package (push) Successful in 4m14s
fa02e774ca
Reviewed-on: #167
gronod added 1 commit 2026-09-14 13:23:40 +01:00
Merge origin/main into develop (keep README)
macOS CI / package (push) Canceled after 0s
macOS CI / build-and-test (push) Canceled after 16m10s
7e5e20cb69
gronod added 1 commit 2026-09-14 13:37:55 +01:00
docs: sync README with Monterey floor, M10, wiki, and packaging
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 51s
macOS CI / build-and-test (push) Failing after 43m55s
macOS CI / package (push) Skipped
ccaad2d940
Requirements were still Xcode 15.4 / Swift 6. Point the product
at the user wiki, record host-arch tests, the dmgbuild venv, and
the current develop-first git workflow.
gronod scheduled this pull request to auto merge when all checks succeed 2026-09-14 14:12:59 +01:00
gronod added 2 commits 2026-09-14 14:37:17 +01:00
fix(ui): show media Apply failure inside the manage sheet (#170)
macOS CI / build-and-test (push) Skipped
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 2m31s
890e7281eb
Failed Apply kept the sheet open while the window banner sat
behind it, so Monterey XCTest never saw noticeText. Mirror the
failure string in the dialog (manageMediaNotice) and point the
UI test at that identifier.

Fixes #170
fix(ui): media Apply missing-printer notice visible in manage sheet (#170)

Fixes #170
gronod added 2 commits 2026-09-14 16:16:38 +01:00
test(m4): wait for Start Read to be enabled before clicking
macOS CI / build-and-test (push) Skipped
macOS CI / build-and-test (pull_request) Canceled after 0s
macOS CI / package (pull_request) Canceled after 0s
9dfa79ebd0
Runs 35251 and 35293 left Stage 3 idle because XCTest clicked a
disabled Start Read. driveOnePass already waits for isEnabled;
the handheld average test now uses it.
test(m4): wait for Start Read enabled before chartread

Runs 35251 and 35293 clicked a disabled Start Read.
gronod canceled auto merging this pull request when all checks succeed 2026-09-14 16:28:04 +01:00
gronod scheduled this pull request to auto merge when all checks succeed 2026-09-14 16:28:27 +01:00
gronod canceled auto merging this pull request when all checks succeed 2026-09-14 16:28:41 +01:00
gronod added 3 commits 2026-09-14 16:42:04 +01:00
ci: add GitHub Actions twin of the Gitea macOS workflow
macOS CI / build-and-test (push) Skipped
macOS CI / build-and-test (pull_request) Failing after 42m38s
macOS CI / package (pull_request) Skipped
288b08e3d9
Copy .gitea/workflows/macos.yml to .github with the github.com deltas:
macos-14 (macos-12 is gone), upload-artifact@v4, no private-CA bundle,
and gh release upload instead of the Gitea asset script.
Update .github/workflows/macos.yml
macOS CI / build-and-test (push) Skipped
macOS CI / build-and-test (pull_request) Failing after 3m40s
macOS CI / package (pull_request) Skipped
cba9476af7
Merge pull request 'ci: GitHub Actions twin of the Gitea macOS workflow' (#174) from ci/github-workflows into develop
macOS CI / build-and-test (push) Successful in 40m30s
macOS CI / package (push) Successful in 3m27s
83ed53c7fc
Reviewed-on: #174
gronod added 1 commit 2026-09-14 16:44:56 +01:00
Update .github/workflows/macos.yml
macOS CI / package (push) Canceled after 0s
macOS CI / build-and-test (push) Canceled after 27m49s
67cf452e59
gronod added 2 commits 2026-09-14 17:18:17 +01:00
fix(ci): pin GitHub Actions to macos-14 and keep the window on-screen
macOS CI / build-and-test (push) Skipped
macOS CI / build-and-test (pull_request) Canceled after 0s
macOS CI / package (pull_request) Canceled after 0s
93acd32b51
macos-latest is macos-26-arm64 (run 34864198118). A 1280x800 window
centered on that display hangs off the left edge, so XCTest sees
sidebar controls at x ≈ -116 and refuses the click. Pin the GitHub
workflow to macos-14 and clamp the main window to visibleFrame.
Reviewed-on: #175
gronod added 2 commits 2026-09-14 17:35:44 +01:00
test(m2): mock CUPS so Print All enables on GitHub macos-14
macOS CI / build-and-test (push) Skipped
macOS CI / build-and-test (pull_request) Canceled after 0s
macOS CI / package (pull_request) Canceled after 0s
4af2d96294
testPrinttargFixtureGalleryAndStubbedPrint asserted btnPrintAll
immediately after the gallery appeared, without ICCERY_CUPS_BIN_DIR.
Gitea Monterey has system queues; GitHub macos-14-arm64 does not, so
selectedPrinter stays empty and the button stays disabled (run
34867767434). Point M2 at Fixtures/bin like M3, and wait for enable.
Reviewed-on: #176
gronod added 1 commit 2026-09-14 19:41:48 +01:00
test(m4): retry Start Read until the chartread session is running
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 1m52s
macOS CI / build-and-test (push) Successful in 37m49s
macOS CI / package (push) Successful in 4m39s
c1c37bb7d7
testTwoPassAverageFailurePromotesFirstPass died on Gitea run 35443
waiting for btnCalibrate after a single Start click. Handheld in the
same job succeeded: AX reported enabled and synthesized the click, but
SwiftUI startRead() was still a no-op (same class as 35251).

Re-click until btnCancel/Calibrate/Trigger exist — Cancel is in the
tree for the whole session — then wait for Calibrate.
gronod added 2 commits 2026-09-14 23:52:44 +01:00
feat: add license window to About dialog (issue #31)
macOS CI / build-and-test (push) Skipped
8cb79e8ffa
- Add LicenseWindowView.swift with ICCery license, ArgyllCMS AGPLv3 license (from bundled License.txt), and attribution links to upstream and Gronod fork
- Add "View Licenses" button to AboutView.swift that opens license window as sheet
- Update fetch-argyll.sh to copy License.txt to Vendor/Argyll/ root
- Add UI tests for license window content, links, and AGPL isolation note

The license window includes:
- ICCery license text (embedded from LICENCE.md)
- ArgyllCMS AGPLv3 license (dynamically loaded from bundled Resources/Argyll/License.txt)
- Attribution to Graeme Gill with link to argyllcms.com
- Attribution to Gronod fork with link to git.i3omb.com/gronod/argyllcms
- AGPL subprocess isolation note
fix: LicenseWindowView compatibility with macOS 12.0 (remove NavigationStack)
macOS CI / build-and-test (pull_request) Canceled after 0s
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (push) Failing after 41m0s
macOS CI / package (push) Skipped
90d034baab
gronod added 1 commit 2026-09-15 01:23:34 +01:00
fix: UI test helpers for nested license window sheet
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 21s
macOS CI / build-and-test (push) Successful in 37m17s
macOS CI / package (push) Successful in 3m29s
2fefe98434
- Update element() helper in AboutHelpUITests to search all sheets (nested sheets)
- Add .accessibilityElement(children: .contain) to LicenseWindowView root and attribution section
- All 6 AboutHelpUITests now pass (2 existing + 4 new license window tests)
gronod merged commit 2fefe98434 into main 2026-09-15 02:17:08 +01:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gronod/iccery-v2-mac#168