Runtime Probes & Reconciliation¶
vocabulary
¶
The closed vocabularies every probe layer speaks — states, verdicts, handles, results.
The lowest layer of this package, and it exists because all three layers above it must agree on
exactly these words. runtimes produces them, reconcile fuses them into verdicts, and cancel
acts on those verdicts; putting any of this in one of those modules would make the other two
import it and cycle.
Two vocabularies live here and they are deliberately distinct even where they share a spelling.
The NATIVE_* set is what a runtime says about a job right now. The registry literals
(_REGISTRY_RUNNING, _CANCELLED, _TERMINAL) are what a run_jobs row last wrote. Both
spell "RUNNING", and conflating them is precisely the bug this package exists to catch: a stale
registry row that still says RUNNING for a job whose runtime is long gone.
ProbeHandle is the coordinate blob that makes reconciliation possible at all — built at launch,
serialized under run_jobs.job_telemetry.$.probe_handle, parsed back out by a reader. It stays
a pure dataclass with dict (de)serialization only, no GCP imports, so importing this module never
pulls a cloud extra.
CancelResult
dataclass
¶
One runtime's answer to a cancel request for a job — the write-boundary evidence.
stopped is True when the runtime accepted the stop (the job was live and is now being
torn down). already_gone is True when there was nothing to stop — the batch/job/cluster
was already NotFound (a Ray cluster GC'd on completion is the common case), which the caller
treats as an effective cancel (the job is not running). Either flag True means the registry
row may be finalized to CANCELLED; when both are False the stop genuinely failed
(permission denied, timeout) and the caller leaves the registry as-is and reports honestly.
detail is a short human reason (a permission hint, a NotFound note, or a
runtimes._short_detail of the swallowed exception). Like check, cancel is advisory: it never
raises.
ProbeHandle
dataclass
¶
ProbeHandle(
runtime,
native_id,
region,
id_kind="exact",
spark_mode=None,
resource_name=None,
created_at=None,
)
The runtime coordinates for one family's job — the write-blob and read-parse shared type.
native_id is the platform-native id (a Serverless batch_id, a Ray submission_id, a
Dataproc cluster job's server-assigned id, or a BigQuery job-id prefix); it is "" when the
real id isn't known yet (a cluster job pre-stamp-back). region is where the job runs.
id_kind is "prefix" for BigQuery (whose jobs share a deterministic id prefix) and
"exact" otherwise; it is carried on the persisted blob so a reader (and the P5 cancel path)
can tell a prefix match from an exact id without re-deriving it per runtime. spark_mode
("serverless" | "cluster") is set for Spark only; resource_name (the Ray persistent-
resource path) for Ray only. created_at is not a persisted coordinate — it is hydrated
from the job row at read time (from_job_row) purely to lower-bound the BigQuery history scan;
it is excluded from to_blob and is None for non-BQ handles and pre-feature rows.
from_job_row
classmethod
¶
Parse the handle out of a v_run_jobs row, or None when there isn't a usable one.
row["probe_handle"] is the JSON_QUERY string the view projects (or an already-parsed
dict); None (a pre-feature run) or a malformed/incomplete blob returns None so a
reader degrades to registry-only rather than raising.
Source code in src/scale_forecasting/probes/vocabulary.py
to_blob
¶
The compact dict stored under run_jobs.job_telemetry.$.probe_handle.
Omits the runtime-specific fields (spark_mode/resource_name) when unset so a handle
carries only the coordinates its runtime actually has.
Source code in src/scale_forecasting/probes/vocabulary.py
ProbeResult
dataclass
¶
One runtime's live answer for a job: the normalized state + whether it still exists.
native_state is one of the closed NATIVE_* set. exists is False only for
NATIVE_NOT_FOUND (the job / cluster is gone) — a reader uses it to tell "torn down" apart
from "failed". detail is a short human reason (a status message, or — when a probe
degrades — a one-line Type: message summary of the swallowed exception, via
runtimes._short_detail). telemetry carries any extra fields the
probe cheaply gathered (Dataproc batch usage, statement counts); it is always a plain JSON-able
dict, empty when the probe has nothing to add.
RuntimeProbe
¶
Bases: Protocol
Read (and, on request, stop) one family's runtime job from its ProbeHandle.
name is the runtime key. check maps the platform-native job state into the closed
NATIVE_* set and never raises: a probe is advisory, so any error (auth, timeout, a torn-down
cluster the native call chokes on) degrades to ProbeResult(NATIVE_UNKNOWN, exists=True, ...)
rather than propagating. cancel issues the runtime's stop and returns a CancelResult; it
likewise never raises (a failed stop is reported, not thrown).
reconcile
¶
Is this job actually still running? — fuse registry, artifacts and live runtime into verdicts.
The question a reader asks when a progress bar stops moving, and the answer is not any one
source's alone. A run_jobs row records a last-written status that a crashed driver never got
to update; the landed-cell counts say what work survived; the runtime says whether the job still
exists. One FamilyVerdict per family is what you get from fusing all three.
Split pure from I/O, as the rest of this codebase is: _verdict_for_family,
_assemble_probe_report and _narrow_to_job are pure and unit-tested per matrix row, and
read_and_probe is the one function that reads BigQuery and calls the probes before handing the
assembled inputs over.
Escalation is the cost control. A terminal family is never probed — the registry is
authoritative once the work is done — so a routine poll of a finished run makes zero native calls.
Only non-terminal jobs are escalated, and _is_stale decides whether a vanished one is
genuinely LOST or merely still-starting. That startup grace is why a normal launch window does not
cry wolf.
FamilyVerdict
dataclass
¶
FamilyVerdict(
family,
runtime,
registry_status,
native_state,
exists,
verdict,
disagreement,
n_done,
n_expected,
detail,
telemetry=dict(),
)
The reconciled truth for one family: what the registry says vs. what the runtime says.
registry_status is the family's last-written job status; native_state / exists are
the live vocabulary.ProbeResult reading (None for a family that wasn't escalated —
terminal or never launched). verdict is one of the VERDICT_* set and disagreement is
True only when the runtime contradicts the registry (a reader's "look here" flag).
n_done / n_expected are the family's landed-vs-expected cell counts (the artifact
evidence that splits a vanished job into LIKELY_COMPLETED vs. LOST). detail is a
short human reason; telemetry carries anything the probe cheaply gathered.
ProbeReport
dataclass
¶
A run's reconciled snapshot: header status + one FamilyVerdict per family.
escalated is True when at least one family was probed against its runtime (False for
a routine poll of an all-terminal run — the terminal short-circuit that keeps polling native-
call-free). disagreement is the run-wide roll-up: True when any family's runtime
contradicts its registry status.
probe_run
¶
Reconcile a run's registry state against live runtime state → a ProbeReport.
Reads the run's header + config + job rows + landed-cell counts (registry.reads,
registry.jobs), assembles the registry-side progress (review._assemble_progress), then
escalates only the non-terminal
jobs to their runtime — a routine poll of an already-terminal run touches no runtime (empty
to_probe ⇒ escalated=False). job narrows both the escalation and the report to one
family (the per-family drill-down; an unknown name raises ConfigError listing the valid ones);
settings is the GCP identity (from the SF_* env when None); stale_after_s
overrides the startup-grace floor (_DEFAULT_STALE_S) that decides whether a vanished young job
reads LOST or still-starting, and abandoned_after_s the far longer window
(_DEFAULT_ABANDONED_WAIT_S) past which a capacity wait reads as a walk nobody is walking. A
family whose handle can't be parsed (a pre-feature or malformed row) degrades to registry-only
via no_handle rather than raising.
Source code in src/scale_forecasting/probes/reconcile.py
read_and_probe
¶
Read the registry, escalate the incomplete jobs, reconcile → (progress, report, rows).
The shared read+probe body behind three callers: probe_run (which keeps just the report),
cancel.cancel_run (which also needs the v_run_jobs rows for their job_id +
probe_handle to finalize), and review.monitor_run with probe=True (which keeps the
progress and attaches the report) — so a monitor that escalates pays for the native calls, not
for a second set of registry queries. rows is the run's job rows filtered to job (all
families when None); progress and the reconciled report cover the same set.
abandoned_after_s overrides the capacity-wait window (_DEFAULT_ABANDONED_WAIT_S).
Source code in src/scale_forecasting/probes/reconcile.py
settle
¶
Write back the verdict the probe already computed — the fourth registry-repair verb.
--probe reads a run, reconciles it against its runtimes, decides that the deep-learning family
whose row still says RUNNING actually finished forty minutes ago — and then throws that answer
away. Nothing in the system could act on it: close-runs refuses a run with a non-terminal job
row (correctly — only a probe can tell a live job from a stale row), --cancel --force stamps
CANCELLED over work that succeeded, and drop-run destroys real predictions to fix a status
field. settle_run is the missing move: --probe that writes, at the job tier, taking the
probe's own per-family verdict rather than a roll-up.
Three properties hold it together, and they are the same three that hold cancel together because
they are the properties a repair verb needs:
Preview by default. Without yes, settle_run probes, builds the SettlePlan, and writes
nothing. yes= rather than confirm= is deliberate: confirm= gates stopping running work
in the cloud, yes= gates repairing a status field in our own registry — the same gate
registry.ops.close_runs and registry.ops.sweep_orphans use, and settle belongs with them.
Refusal is the feature. Only five (verdict, native-state) combinations settle. UNKNOWN — the
probe degraded, no handle was recorded, the runtime says SUCCEEDED but the cells have not landed —
writes nothing, ever. A verb that guessed would be worse than no verb, because its guesses would
be indistinguishable from measurements afterwards.
Settle never deletes, and never invents time. One UPDATE per row: the status, a
failure_reason when there is a token for it, and an audit blob under job_telemetry.$.settle
recording who settled it, from what, on what evidence. It does not stamp ended_at or
runtime_seconds — a row settled three days after the fact would report three days of runtime
for a ten-minute job, and every consumer already tolerates those being NULL. It does not touch the
run header: registry.ops.close_runs owns that tier, and settling the job rows is what unblocks
it (the report prints the hint).
The pure pieces — the decision table, the plan, the audit blob, and the plan→rows join — are unit
tested per row; settle_run is the I/O orchestrator that sequences them.
SettleDecision
dataclass
¶
What one family's row will be set to, and the one-line reason it can be (pure).
failure_reason is the short token stamped alongside a FAILED status (vocabulary's
RUNTIME_FAILED / RUNTIME_LOST); None for a COMPLETED settle, which needs no excuse.
SettleItem
dataclass
¶
SettleItem(
family,
runtime,
registry_status,
verdict,
native_state,
n_done,
n_expected,
decision,
note,
)
One family's line in the settle preview: what it says now, what it would say, and why.
decision is None for a family that will be left alone; note is the human one-liner
shown in the preview either way, because why a row is being refused is the part an operator
most needs — "UNKNOWN: the probe degraded" means go look, and burying it under a count of what
was settled is how the one row that mattered gets missed.
SettleOutcome
dataclass
¶
What actually happened to one family's row, read back rather than assumed.
final_status is the row's status re-read after the write, not the status we asked for.
A settle UPDATE carries a status guard (_SETTLE_GUARD), and a guarded skip is silent — the
statement succeeds and touches nothing — so the only honest way to report the outcome is to
look. settled is True when final_status is what the decision asked for.
SettlePlan
dataclass
¶
Every family of a run with its settle verdict — one SettleItem each, in report order.
SettleReport
dataclass
¶
The result of a settle call — a preview (executed=False) or the executed outcome.
plan is always present (it is what a no-yes call returns). header_hint is the
follow-up line when the run's header is still non-terminal after the job rows were settled:
settle deliberately does not close headers, so it says who does.
format_settle_plan
¶
Render a SettlePlan as the operator-facing preview — the exact text a dry run prints.
Source code in src/scale_forecasting/probes/settle.py
settle_run
¶
settle_run(
run_id,
*,
job=None,
yes=False,
reason="",
actor=None,
settings=None,
stale_after_s=None,
abandoned_after_s=None,
)
Repair a run's stale job rows from the probe's own verdicts — preview unless yes.
Probes the run (reconcile.read_and_probe), builds the SettlePlan, and — only when
yes — writes each settleable family's row: the decided status, the failure_reason
token when there is one, and the audit blob merged under job_telemetry.$.settle. Without
yes it returns the plan and writes nothing. job narrows to one family; reason and
actor (default: resolved from ADC) are recorded in the audit. stale_after_s and
abandoned_after_s pass through to the probe, where they set the two clocks that decide the
LOST and ABANDONED_WAIT verdicts respectively.
Every write carries a status guard against _SETTLE_GUARD, so a job that reached a terminal
status of its own between the probe read and the UPDATE keeps it — the process that ran the
job outranks a repair verb. That skip is silent, so the rows are re-read once afterwards and
each outcome reports the status it actually ended up with.
Source code in src/scale_forecasting/probes/settle.py
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 | |
cancel
¶
Stop a run's jobs — the blast-radius plan, the confirmation gate, and the audit trail.
The only destructive, outward-facing path in this package, so it is built around honesty about what stops and what survives (§7 of the design). Three properties hold it together:
Preview by default. Without confirm, cancel_run probes, builds the CancelPlan, and
touches no runtime and no registry. You see the blast radius before anything happens to it.
Cancel never deletes. Partial results that already landed are retained; they are labelled by the CANCELLED status and the cell counts, never removed. A cancelled run stays readable.
Every stop is auditable. Each finalized row carries who cancelled it, when, why, and what the
runtime state was at the moment of the stop, merged into job_telemetry.$.cancel alongside the
handle that addressed the job — so a cancelled attempt stays reconcilable afterwards.
The pure pieces — plan, audit blob, header roll-up, and the plan→rows join (_cancel_steps) that
decides which families a confirmed cancel actually addresses — are unit-tested per row; cancel_run
is the I/O orchestrator that sequences them.
CancelOutcome
dataclass
¶
What actually happened to one family's job on a confirmed cancel.
requested is True when a stop was issued to the runtime (False when we couldn't even
address the job — no handle). cancelled is True when the registry row was finalized to
CANCELLED (the runtime confirmed the stop, or the job was already gone). stopped /
already_gone are the raw vocabulary.CancelResult flags; detail is the short reason.
CancelPlan
dataclass
¶
The full blast radius of a cancel: one CancelPlanItem per family + the ensemble impact.
ensemble_suppressed is True when cancelling a base family will cause the run's ensemble
node to be skipped (§7.4 — a cancelled base family suppresses the ensemble rather than producing
a lopsided one). n_cancellable is how many jobs a confirmed cancel would actually stop.
CancelPlanItem
dataclass
¶
CancelPlanItem(
family,
runtime,
registry_status,
native_state,
cancellable,
n_done,
n_expected,
note,
)
One family's line in the blast-radius preview: what will happen and what data is retained.
cancellable is keyed on the registry status (non-terminal ⇒ will cancel) so the plan is
robust even when the live probe degraded; native_state enriches the note when we have it.
n_done/n_expected are the landed-vs-expected cells — the partial results a cancel
retains (never deletes). note is the human one-liner shown in the preview.
CancelReport
dataclass
¶
The result of a cancel call — a preview (executed=False) or the executed outcome.
plan is the blast radius (always present — it is what a no-confirm call returns).
outcomes is empty for a preview and one CancelOutcome per attempted family otherwise.
header_status is the run's rolled-up status after the cancel (None when the header was
not changed — e.g. a preview, or a cancel whose stops all failed). actor / reason are
the audit identity recorded on the cancelled rows.
cancel_run
¶
cancel_run(
run_id,
*,
job=None,
confirm=False,
reason="",
actor=None,
settings=None,
stale_after_s=None,
)
Cancel a run (or one family) — preview unless confirm; finalize registry to CANCELLED.
Probes the run first (reconcile.read_and_probe) to reconcile live state, builds the blast-
radius CancelPlan, and — only when confirm — stops each cancellable family's runtime
job and finalizes its row to CANCELLED with an audit blob (_finalize_cancelled), then
rolls the run header up (_roll_header_after_cancel). Without confirm it returns the plan,
touches no runtime and no registry (the CLI prints the preview and exits). job narrows to
one family; reason and actor (default: resolved from ADC) are recorded for audit. Cancel
never deletes — landed partial results are retained and labelled by the CANCELLED status +
counts.
Source code in src/scale_forecasting/probes/cancel.py
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | |