Files
settled-reach/tooling/domains/jobs/__init__.py
T
jpmschweitzerandClaude Opus 5 6f08cc9156 feat(config): T-1278 — the jobs domain, and typer.Exit is not a SystemExit
reach jobs list / status / log --follow / wait. A domain rather than core/,
because these verbs carry logic and state: they reconcile recorded status
against process liveness, tail a file from an offset, and relay an exit code.

Found a latent bug in already-committed code before building on it. typer.Exit
is a RuntimeError, not a SystemExit, so @handle_errors caught it like any other
unexpected exception: `raise typer.Exit(3)` inside a decorated command printed
"unexpected Exit: 3" and exited 1, silently discarding the requested code.
Nothing hit it because the check router had been converted to ReachError — but
jobs wait needs exactly this and it is what anyone would naturally write. Added
core/errors.ReachExit as the sanctioned control-flow exit, passed straight
through with no verdict. ReachError would have been wrong twice: a failure
verdict for a command that worked, and a demand for a fix= where there is no
remedy.

Reconciliation proved out on a real corpse rather than a simulated one — the
job stranded by the T-1277 bug, status "running" with its process long gone,
now reports as died. DIED is derived, never recorded, because a process killed
outright cannot write its own ending. It relays 137, never 0: a died job has no
exit code of its own and borrowing success points the exit-0 trap straight at
whatever gated on the run.

Second UTC bug of the same family as T-1276's: jobs list reported a job started
minutes earlier as running for 133m, because _parse used mktime on a UTC stamp
and silently added the offset to every duration.

console.render() is public now, so jobs log replays stored events through the
same path a live run prints them — a second renderer would drift, and the
divergence would surface exactly when someone is reading a log to find out what
went wrong.

test_jobs.py closes the gap T-1257 named: D-263 claims services are callable
without a CLI round trip, and nothing had ever demonstrated it, which left the
layering as unverified decoration. Every test here calls the service directly.

Not yet exercised, and said plainly: log --follow against a genuinely
long-running job. Nothing in reach runs long enough to tail yet. The offset
mechanics underneath are tested; the live loop waits for a slow domain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 17:02:36 +02:00

13 lines
644 B
Python

"""The `jobs` domain — detached runs, their logs, and their outcomes.
A domain rather than part of `core/`, and this is the first real test of that
bound (D-263). The *primitives* — spawn, detach, record — are substrate and live
in `core/process.py`. These verbs have logic and state of their own: they
reconcile recorded status against process liveness, tail a file from an offset,
and relay an exit code. A job store in `core/` would be exactly the drift the
record warns about.
Also the first domain written from scratch under the full contract rather than
ported, which makes it the worked example for the reach skill (T-1254).
"""