Spot-read console: one patch Lab/XYZ from the live instrument #148
Open
opened 2026-09-12 18:34:19 +01:00 by gronod
·
1 comment
No Branch/Tag Specified
main
milestone/m10-studio
feat/148-spot-read
feat/146-media-library
chore/m10-tracking
develop
feat/119-debug-disable-lib-validation
feat/117-static-iccerycore
feat/115-assertisolated-swift57
feat/113-printtask-defer-swift57
feat/111-stage4-viewbuilder-limit
feat/109-ci-xcodegen-pin
feat/m9-slice4-state-and-views
feat/m9-slice3-xctest-async-stateful
feat/m9-slice2-xctest-core-pure
feat/m9-slice1-config-and-core-syntax
milestone/m8-consolidation
feat/80-process-run-notice-consolidation
feat/79-runner-loop-contract-tests
feat/86-args-builder-completion
feat/84-process-manager-edge-contracts
feat/83-calibration-profile-contracts
feat/82-preset-mapping-contracts
feat/81-json-store-contracts
v2.0.0-monterey
v2.0.0-pre2-grok
v2.0.0-pre1
Labels
Clear labels
Bug/Architecture
Bug/Backend
Bug/DevOps
Bug/UI
Compat/Breaking
Feature/Architecture
Feature/Backend
Feature/DevOps
Feature/UI
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Project/ICCery-v2
Core / wizard / process infra defect
Argyll orchestration, file/data defect
Build, packaging, CI defect
Views, controls, dialogs defect
Breaking change that won't be backward compatible
Core / wizard / process infra
Argyll orchestration, file/data engines
Build, packaging, CI
Views, controls, dialogs
Something is not working
Documentation changes
Improve existing functionality
New functionality
This is security issue
Issue or pull request related to testing
Priority
Critical
1
The priority is critical
Priority
High
2
The priority is high
Priority
Low
4
The priority is low
Priority
Medium
3
The priority is medium
ICCery v2 Mac rewrite
Reviewed
Confirmed
1
Issue has been confirmed
Reviewed
Duplicate
2
This issue or pull request already exists
Reviewed
Invalid
3
Invalid issue
Reviewed
Won't Fix
3
This issue won't be fixed
Status
Abandoned
3
Somebody has started to work on this but abandoned work
Status
Blocked
1
Something is blocking this issue or pull request
Status
Need More Info
2
Feedback is required to reproduce issue or to continue work
Milestone
No items
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: gronod/iccery-v2-mac#148
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
AppSettings.defaultInstrumentis stored and never applied to argv (AppSettings.swift, docs/04 §0.1). Stage 3 ownschartread. Add a console that runs bundledspotreadso the user can trust the instrument before a long chart, and sodefaultInstrumentfinally has a consumer that is notprinttarg -i.Spec refs
docs/15-stage3-chartread.md(classifier, stdin table,-cport rules)docs/04-argyll-binaries.md/docs/05-argyll-fork.mdinstlistJSONInstrumentDevice/InstrumentSelection(port 1 / Auto omit-c, #111)q\n, ~500 ms, then kill (#147)enable_i1pro2_leds→-Y lonly (letter L, #204)ARGYLL_NOT_INTERACTIVE=1on every child$PATH(BinaryResolver)Scope
In:
SpotReadArgs/SpotReadConfig/SpotReadSampleinICCeryCoreArgyllRunner.runSpotread→AsyncStreamof samples + promptsSpotReadViewModel+ sheet from sidebar / Stage 3selectCsvSavePathdefaultInstrumentseeds the picker wheninstlistcontains that type; still never written intoprinttarg -iortargenOut:
chartreads/udispwin/dispread, #90)$PATHorchartreadfallback whenspotreadis missingImplementation notes
Argv
Prefer bundled
spotreadifBinaryResolver.resolve("spotread")exists (Gronod 3.5.0 fork ships it next toinstlist). Goldens:-v.-e= paper / reflective (no display-d).-comitted for Auto and port 1 (#111). Never pass array index.-Y liffsettings.enableI1Pro2Leds(default false).spotread -uunless fork docs for this binary explicitly say JSON-on-stdout. v2.0-upolicy isprinttarg+chartread+profcheckonly (AGENTS.md/docs/25).If
spotreadis absent, do not search$PATH. Show “spotread sidecar missing — run fetch-argyll” and stop. Do not silently reuse process idchartread_{basename}.Process id:
spotread(single lease). Duplicate Detect/Read while live → exclusive-id error (#116), same asinstlist.I/O
Streaming
ProcessManagerpath (notrunCaptured): prompts come on stdout. ReuseChartreadClassifiermatchers where the C strings are identical (Calibrate / Place / Trigger). New matchers go inSpotReadClassifierafter reading realspotreadprompt strings from the fork — do not guess.stdin:
" \n"or"\n"(matchChartreadInput)"q\n"then ~500 ms then kill if XY; handheld may kill afterq\nParse a sample from a line containing Lab / XYZ. Store:
ΔE₀₀ between last two samples using existing
ColorDifference. Traffic lights use settingsdeltaEGoodMax/deltaEWarningMax(swatch bands, not Stage 5 profcheck bands).defaultInstrument
On sheet open:
detectInstruments()(existing)settings.defaultInstrument != nil, select the first device whose type orprinttarg -icode matches (i1,CM,p3,SS,20/22/41/51).autoand a one-line note “Saved default instrument not present”Applying the picker does not write
printtarginstrument. A checkbox “Also set as default instrument” updatesAppSettingsonly.UI
This is a sheet, not a sixth wizard stage and not a Stage 3 tab. Stage 3 keeps
chartread. Clone the look ofStage3View.instrumentSection(Theme.panel, 16 pt padding) but do not reuse Stage 3 identifiers.Milestone4/ measurement UI tests querybtnDetectInstruments,chartreadInstrumentSelect,btnCalibrate,btnTrigger,btnCancel,chartreadPrompt. A second control with those ids will makewaitForflaky.Entry
Add one sidebar button under View Gamut, above the stepper divider:
btnSpotRead.controlSize(.large), same full-widthLabelstyle asbtnCalibratePrinter/btnViewGamutWizardState.cwdis non-nil (#59). Does not require.ti2/ Stage 3 unlocked. Disabled + help “Set a working folder in Stage 1 first.” when cwd is emptyMeasurementWorkflowViewModel.isChartreadRunningis true. Help: “Stop the Stage 3 chart read first.” Opening the sheet must notkilla livechartread_{basename}childbtnSpotReadon Stage 3. One entry point. Tests usewaitFor("btnSpotRead")helpOverlayon the button, wired to$showingAllHelp. Overlay must not change sidebar heightPresent from
RootView, sibling of the preset / gamut / media sheets:Do not present from
SidebarView(270 pt column clips). On dismiss (onDisappear): sendq\n, wait ~500 ms, kill process idspotreadif still live. Clearing the sheet flag is not enough.Sheet size: 560×640,
Theme.background, titleText("Spot Read"). Close buttonbtnCloseSpotReadbottom trailing (not a competing Cancel that could be confused with Stage 3btnCancel).Missing sidecar
If
BinaryResolver.resolve("spotread")is nil, the sheet still opens but the body is only:Text("spotread sidecar missing — run fetch-argyll")idspotSidecarMissingNo Detect, no picker, no Start. Do not fall back to
chartread.Layout (top → bottom)
Instrument card
Same structure as
Stage3View.instrumentSection, own ids:btnSpotDetectInstrumentsdetectInstruments()/instlist. Disabled while aspotreadchild is live or whileinstlistis already leased (#116)spotDetectErrorspotInstrumentSelectText("Auto (first available port)")tag""+Text(device.displayName)per device..pickerStyle(.menu)spotDefaultMissingsettings.defaultInstrumentis set and no detected device matchesspotSetDefaultToggle("Also set as default instrument"). On apply, writesAppSettings.defaultInstrumentonly. Never writesprinttarg -ispotXYHintisXY:Text("XY tables use Stage 3. Spot Read is a handheld / reflective probe.")Spot Read still runs-ereflective; do not show Stage 3’sxyTablePanel/ Place-Align-Scan-Remove chipsOn sheet appear:
settings.defaultInstrumentagainst type /printtarg -icodes (i1,CM,p3,SS,20/22/41/51).auto+spotDefaultMissingcopyText("Saved default instrument not present")Update the Settings caption under Default instrument (today: “Display-only — Stage 2's instrument select is used for actual runs.”) to
Text("Seeds Spot Read and Stage 3 when the instrument is plugged in. printtarg -i is still chosen on Stage 2."). That is a one-line Settings change in this ticket, not a new Settings tab.LED toggle is not duplicated here.
enable_i1pro2_ledsstays in Settings and flows into argv only.Prompt line
Id
spotPrompt. Idle copy:Text("Press Start to open the instrument.")Then reuse classifier states as text only (do not reuse
chartreadPrompt):Errors go to
spotLastError(caption,Themeerror colour) and aWizardViewModelnotice if the sheet was just opened onto a missing sidecar. Never renderrawLine/ log HTML.A compact
ProcessLogViewis allowed, idsspotLogContainer/spotLog— optional, collapsed by default. Do not copychartreadLogids.Transport
One
HStack, 12 pt spacing, same pattern aschartreadControlsSectionbut own buttons:btnSpotStartspotreadwith current selectionbtnSpotCalibratebtnSpotTrigger" \n"/"\n")btnSpotStopq\n+ ~500 ms + kill idspotreadRules:
isChartreadRunningspotreadis already live → exclusive-id error inspotLastError, do not spawn a second childs/ubuttonbtnCancelorbtnCalibrateWhile a child is live, the instrument picker and Detect are disabled (changing port under a running
spotreadis undefined).Last sample
Card id
spotLastSample. Empty state:Text("No readings yet.")idspotLastEmpty.After a sample:
spotLabLspotLabAspotLabBspotXYZspotSwatchspotDeltaEspotLastInstrumentSwatch colour from the same Lab→sRGB display convert
SwatchPatchViewalready uses. ΔE usesColorDifferenceand settingsdeltaEGoodMax/deltaEWarningMax. Render asTextplus a 8×8 traffic-light circle (good / warning / fail). First sample hidesspotDeltaE(no previous).L* outside 0…100 still displays; add caption
Text("Implausible L*")idspotLabImplausible— do not clamp.History
TableorListidspotHistoryTable, max 50 rows, newest first. Row idspotHistoryRow-{uuid}.Columns: time (HH:mm:ss), L*, a*, b*, ΔE vs previous (blank on first), instrument short name.
Text("No history.")idspotHistoryEmptyrawLinein the tableFooter buttons:
Copy LabbtnSpotCopyLab— putsL* a* b*of the displayed last sample on the pasteboard as plain text (50.0 1.2 -3.4). Disabled when emptyExport CSV…btnSpotExportCsv—FileDialogService.selectCsvSavePath(same helper as verification history). RFC-4180 headertimestamp,L,a,b,dE00,instrument,port. Disabled when empty. Cancel of the panel is a no-opselectDatasetFileConcurrency the UI has to surface
showingSpotRead = falsemid-read runs the same Stop path asbtnSpotStopkillAllalready covers idspotread; the sheet should just disappearchartread_{basename}into this UIUI tests (
Milestone10SpotReadUITests)Mock sidecar / no hardware:
testSpotReadButtonDisabledWithoutCwdtestSpotReadButtonDisabledDuringChartread(start a mock chartread fixture, assertbtnSpotReaddisabled)testSheetHasOwnInstrumentIds—spotInstrumentSelectexists;chartreadInstrumentSelectdoes not appear insidespotReadViewtestMissingSidecarShowsMessage— fixture withoutspotreadbinary showsspotSidecarMissing, hidesbtnSpotStarttestHistoryCopyDisabledWhenEmptyHardware gate stays in the Test section below (one calibrate + one read).
Identifiers (complete)
Concurrency
View model
@MainActor. Do not hop to main per stdout line. Batch classifier + parse off the main actor; publish samples in groups.killAllon terminate still covers idspotread(#147/#149).Rewrite invariants
Never PATH-resolve. Never
-Y lunless the LED setting is on. Never reuse process idchartread_{basename}for this sheet. Nos/ukeys.Dependencies
Blocks-on: #2 ProcessManager, #3 resolver, #5 settings, #18 instlist, #19 classifier strings as reference.
Unblocks: media-library “trust instrument before capture” UX (soft).
Test
SpotReadArgsgoldens (auto omits-c, port 2 includes-c 2, LED flag)ColorDifferencedefaultInstrumentmatching / not-present noteMilestone10SpotReadUITests): button gating, own instrument ids, missing-sidecar copy, empty history disables Copy/Export.Acceptance criteria
spotreadonlydefaultInstrumentseeds picker and does not touchprinttarg -iselectCsvSavePathchartreadsession remains exclusive of this sheetbtnCalibrate,btnTrigger,btnCancel,chartreadInstrumentSelect,btnDetectInstruments)feat/<this>-spot-read→milestone/m10-studio→developM10 implementation order (see milestone): 2 of 4. Code-independent of #146; merge after #146 so capture-from-session can use a live Lab check. Do not share a PR with Stage 3 chartread work.