ProcessManager: spawn / stdin / kill / captured / event bus #2

Closed
opened 2026-09-08 17:38:39 +01:00 by gronod · 0 comments
Owner

Summary

ProcessManager actor plus runCaptured. This is the AGPL boundary and the #84/#116/#147 contract.

Spec refs

docs/03-ipc-and-process-manager.md, 04-argyll-binaries.md §0.3–0.5, §12.

Scope

In: streaming spawn, captured run, event stream, killAll hooks.
Out: argv builders, JSON parsers for instlist/printtarg/profcheck.

Implementation notes

Streaming spawn(id, binary, args, cwd):

  • Reject duplicate live ids with a loud error (never replace occupant).
  • Pipe stdin/stdout/stderr. Take all three handles at spawn.
  • Env ARGYLL_NOT_INTERACTIVE=1 on every spawn.
  • Line-split stdout. Lines starting ROW_COLORS_JSON: (17 chars, space after colon) → event jsonRow with prefix stripped. Those lines must NEVER also appear as stdout.
  • sendStdin(id, Data) writes exact bytes + flush. Caller includes \n.
  • kill(id): drop/close stdin first, then terminate.
  • killAll(): close all stdins, signal all waits, return count.
  • Reap → exit(id, code): natural status.unwrap_or(0), killed unwrap_or(1).
  • Events: AsyncStream of stdout | stderr | jsonRow | exit | error. Do not hop to @MainActor per line.
  • killAll on NSApplication.willTerminate and last window close.
  • Log argv with $HOME rewritten to ~.

Captured runCaptured(binary, args, cwd) -> (code, stdout, stderr):

  • Same env, same resolveBinary, covered by killAll if still running.
  • Used ONLY by printcal / applycal (issues 29 / 24).
  • No event bus.

Process ids (protocol — UI filters on these strings):
targen_{basename}, printtarg_{basename}, chartread_{basename}, average_{output}, colprof_{basename}, profcheck_{ti3_path}, iccgamut_{stem}, instlist.

Foundation Process is fine. Do not use a shell string.

Rewrite invariants

#84 stdin independent of wait. #116 exclusive ids. #134 env var. #147/#149 killAll on quit. #56 spawn id is a protocol.

Dependencies

Blocks-on: 1 (app target). 3 may be stubbed for unit tests (inject absolute paths).
Unblocks: every runner.

Test

  • CI/mock: duplicate-id rejection; stdin survives while child waits; json-row isolation; kill emits exit 1; killAll drains N children; runCaptured returns full stdout; home sanitisation.
  • Hardware: N/A.

Acceptance criteria

  • All unit tests above pass.
  • Body of this ticket documents both spawn APIs.
## Summary `ProcessManager` actor plus `runCaptured`. This is the AGPL boundary and the #84/#116/#147 contract. ## Spec refs docs/03-ipc-and-process-manager.md, 04-argyll-binaries.md §0.3–0.5, §12. ## Scope In: streaming spawn, captured run, event stream, killAll hooks. Out: argv builders, JSON parsers for instlist/printtarg/profcheck. ## Implementation notes Streaming `spawn(id, binary, args, cwd)`: - Reject duplicate live ids with a loud error (never replace occupant). - Pipe stdin/stdout/stderr. Take all three handles at spawn. - Env `ARGYLL_NOT_INTERACTIVE=1` on every spawn. - Line-split stdout. Lines starting `ROW_COLORS_JSON: ` (17 chars, space after colon) → event `jsonRow` with prefix stripped. Those lines must NEVER also appear as `stdout`. - `sendStdin(id, Data)` writes exact bytes + flush. Caller includes `\n`. - `kill(id)`: drop/close stdin first, then terminate. - `killAll()`: close all stdins, signal all waits, return count. - Reap → `exit(id, code)`: natural `status.unwrap_or(0)`, killed `unwrap_or(1)`. - Events: `AsyncStream` of `stdout | stderr | jsonRow | exit | error`. Do not hop to `@MainActor` per line. - `killAll` on `NSApplication.willTerminate` and last window close. - Log argv with `$HOME` rewritten to `~`. Captured `runCaptured(binary, args, cwd) -> (code, stdout, stderr)`: - Same env, same `resolveBinary`, covered by `killAll` if still running. - Used ONLY by printcal / applycal (issues 29 / 24). - No event bus. Process ids (protocol — UI filters on these strings): `targen_{basename}`, `printtarg_{basename}`, `chartread_{basename}`, `average_{output}`, `colprof_{basename}`, `profcheck_{ti3_path}`, `iccgamut_{stem}`, `instlist`. Foundation `Process` is fine. Do not use a shell string. ## Rewrite invariants #84 stdin independent of wait. #116 exclusive ids. #134 env var. #147/#149 killAll on quit. #56 spawn id is a protocol. ## Dependencies Blocks-on: 1 (app target). 3 may be stubbed for unit tests (inject absolute paths). Unblocks: every runner. ## Test - CI/mock: duplicate-id rejection; stdin survives while child waits; json-row isolation; kill emits exit 1; killAll drains N children; runCaptured returns full stdout; home sanitisation. - Hardware: N/A. ## Acceptance criteria - [ ] All unit tests above pass. - [ ] Body of this ticket documents both spawn APIs.
gronod added this to the M1 — Foundation & process core milestone 2026-09-08 17:38:39 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gronod/iccery-v2-mac#2