Extract ArgyllRunner streaming loop (runStreamingTool) and collapse tool-failed errors
#79
Notifications
Due Date
No due date set.
Blocks
#83 Single `CalibrationIdentity` and one `.icc`/`.icm` resolver
gronod/iccery-v2-mac
#86 Dead UI, duplicated colour types, shared argv / save-panel helpers
gronod/iccery-v2-mac
Reference: gronod/iccery-v2-mac#79
Reference in New Issue
Block a user
Summary
ArgyllRunner(Packages/ICCeryCore/Sources/ICCeryCore/Argyll/ArgyllRunner.swift, 879 lines) copies the same subscribe-before-spawn loop for every streaming tool. Extract one internal helper and stop inventing a new*Failederror case per binary.This is the AGPL-isolation call site. Behaviour must not change:
ARGYLL_NOT_INTERACTIVE=1stays insideProcessManager; runners still subscribe beforerunStreaming; exit 0 is necessary but not sufficient — the artefact must exist.Spec refs
docs/03-ipc-and-process-manager.md(subscribe-before-spawn, no main-actor hop per line)docs/04-argyll-binaries.md(per-tool argv stays in*Argsenums — do not merge builders)AGENTS.mdAGPL boundarydevelop@2e8b07cScope
In
ArgyllRunner, roughly:requireArtefact(_ url: URL) throws -> URL(throws.missingArtefact).runTargen→ requirebasename.ti1runPrinttarg→ helper, then existing manifest +TiffPreviewpost-passrunAverage→ requirebasename.ti3runColprof→ helper withflushPartialLines: true, thenArtefactProbe.resolveProfilerunIccgamut→ requirestem.gamrunProfcheck→ helper, thenProfcheckParserrunCalibrationTargen→ requireCAL_*.ti1runApplycal,runPrintcal) stay captured. Optionally shareensureNotRunning+ log-split of captured stdout; do not route them through the streaming helper.runChartreadstays its ownAsyncStream(prompt/row protocol). After the helper exists, reuseensureNotRunning+ batch-flush constants only.ArgyllRunnerError:.missingArtefact,.malformedManifest,.instrumentDetectionFailed,.profcheckUnparseable.processFailed/.chartreadFailed/.averageFailed/.colprofFailed/.printcalFailed/.applycalFailed/.iccgamutFailed/.profcheckFailedwith:User-facing
errorDescriptionstays specific ("Profile creation failed"vs"Averaging failed") via thetoolstring — do not regress banner copy in Stage 3/4/0.Out
TargenArgs/PrinttargArgs/ColprofArgs/ etc.ProcessManagerpipe/EOF contract (that is the sibling ProcessManager ticket).Full solution
collect(...)+ensureNotRunning+ spawn block intorunStreamingTool.runXbecomes: sanitize/resolve cwd →*Args.build→ProcessID.*→binaryResolver.resolve→runStreamingTool→ interpretCollectedRun..icm-wins-over-.iccviaArtefactProbe.resolveProfile(#69). Do not reimplement extension swap here (sibling artefact ticket may later feed a URL-based helper; call the existing basename API for now).runCalibrationTargenmust still accept a non-prefixed stem and writeCAL_{stem}.ti1. Do not invent a second prefix policy in this ticket if the CalibrationIdentity ticket has not landed — keep the currenthasPrefix("CAL_")ternary and leave a// TODO(M7-cal-identity)only if you touch that line.Rewrite invariants
processManager.events()beforerunStreaming.$PATH; resolver only.@MainActorinside the runner;onLogBatchstays coalesced (20 lines or ~100 ms).flushPartialLinesremains colprof-only unless a test proves another tool needs it.runTargen,runPrinttarg, …) — UI tests and view models call these.Dependencies
Blocks-on: none (M1–M6 closed on
develop).Unblocks: CalibrationIdentity ticket (moves
CAL_prefix out of the runner), cleanup/ArgsBuilder ticket (safer once this file shrinks).Test
Update / add under
Tests/ICCeryCoreTests/:ArgyllRunnerColprofTests.swift,ArgyllRunnerCalibrationTests.swift— keep existing success/failure fixtures; they must still compile after the error-enum collapse. MapXCTAssertThrowsError/catchpatterns from.colprofFailedto.toolFailed(tool: "colprof", ...).ArgyllRunnerStreamingLoopTests.swift(or extend an existing runner test):.toolFailedand still returns accumulated logs..missingArtefactwhen exit is 0 but the expected file is absent (use a fixture binary that exits 0 and writes nothing).flushPartialLines: trueeventually delivers a partial-line batch (colprof-dot fixture).PrinttargTests.swift/ measurement tests that pattern-matchArgyllRunnerError— update exhaustiveness.Tests/ICCeryUITests/Milestone2UITests.swiftetc.) should not need edits if public APIs and banner strings stay stable. If a banner string changes because of the error collapse, update the accessibility assertion and record the old/new copy in the PR.Acceptance criteria
runXsignature change.ArgyllRunnerErrorno longer has per-tool*Failedduplicates.xcodebuild test -scheme ICCery -destination 'platform=macOS'(or package test target) green on the milestone branch.Reopening #79. PR #87 auto-closed #79 when merging the stacked consolidation commit
d4261ba, but the explicit streaming loop contract tests and exact toolFailed error assertions required by the ticket acceptance criteria remain absent. Issue will remain open until feat/79-runner-loop-contract-tests lands in M8 Phase 6 and the full gate passes.Implementation originally landed in stacked commit
d4261bavia PR #87.Completion/verification landed in PR #102 at
78ffeff.Acceptance evidence:
[x] Shared ArgyllRunner streaming loop contracts verified (ArgyllRunnerStreamingLoopTests).
[x] Exact toolFailed(tool:code:logs:) error assertions and missingArtefact cases verified.
[x] Colprof partial progress flushing before exit verified.
[x] Removed per-tool failure error token duplicates (processFailed, printcalFailed, etc.).
[x] Public runX signatures unchanged.
Verification at milestone/m8-consolidation
891a504ee7:targeted suites: passed (20 tests)
full universal ICCeryCoreTests: 339 passed, 0 failed
full ICCeryUITests: 29 passed, 0 failed
Closing manually after code and tests are present on the milestone branch.