Skip to content

feat(tuner): trustworthy job statuses end-to-end — Ray ground truth in the API, finish-aware stop/delete in the UI - #130

Open
Fidasek009 wants to merge 9 commits into
masterfrom
refine-ray-job-status-handling
Open

feat(tuner): trustworthy job statuses end-to-end — Ray ground truth in the API, finish-aware stop/delete in the UI#130
Fidasek009 wants to merge 9 commits into
masterfrom
refine-ray-job-status-handling

Conversation

@Fidasek009

@Fidasek009 Fidasek009 commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements the approved spec (docs/superpowers/specs/2026-08-10-tuner-job-status-design.md) from the tuner quality review, plus follow-ups surfaced by review and live e2e runs. Trial and job RUNNING status now reflect Ray task ground truth instead of optimistic guesses; a start watchdog bounds the one non-terminating code path (indefinite queue wait); early pruning uses two axes (speed AND cost-per-step) so both the fastest and the most cost-efficient config are always kept; and finished jobs are persisted dashboard-side instead of being polled forever.

Changes

  • Honest trial status: _submit_trials no longer writes RUNNING at submission. Trials stay PENDING until the read path renders them RUNNING — only while Ray's State API reports the task executing. Queued trials correctly show PENDING.
  • Derived job status: PENDING until the first trial actually executes, RUNNING monotonically after, FINISHED at grid exhaustion (unchanged), terminal DB statuses always win.
  • Start watchdog: if ray.wait returns nothing for TRIAL_START_TIMEOUT_SECONDS (2h, hardcoded), one filtered list_tasks State API query decides: trials executing (or an HTTP outage — never a false kill) extends the wait; zero RUNNING on a successful query means the batch never became schedulable — futures cancelled, job fails with "cluster busy or unavailable - retry later". Terminal jobs never leave dangling PENDING trials: _error_pending_trials sweeps them to ERROR on every failure path.
  • Ray client-mode hardening (found via live e2e logs): Ray SDK address resolution overrides explicit addresses with RAY_ADDRESS, and resolving a ray:// URL ran a full client connect/disconnect that raced job threads' ray.init. Fixed by pinning RAY_API_SERVER_ADDRESS to the dashboard HTTP URL (State API stays pure-HTTP) and serializing client init under a lock.
  • Dual-axis pruning: a trial early-stops only when it is BOTH slower than EARLY_STOP_THRESHOLD of the fastest champion AND costlier per step than EARLY_STOP_COST_RATIO × the cheapest champion (cost_per_step = footprint.hourly_cost() / steps_per_sec). The two champions are tracked independently, so a slow-but-cheap CPU config keeps exploring while a slow-and-expensive one stops early. Speed threshold hardened 65% → 75% since both axes must now fire.
  • UI (TunerView/AmberTunerView + tables, unchanged enum/type surface):
    • Stop only while PENDING/RUNNING; FINISHED offers Delete instead.
    • Pruned trials (performance: null) hidden once the job finishes — consistent with the stopped-job view; dedicated empty state when nothing was measured.
    • A selection whose trial is filtered away on completion is cleared, so Start can't launch a hidden trial.
  • Dashboard API: a tuner poll returning FINISHED persists the job as stopped (preserving measured trials, deleting it from the tuner) — no infinite re-polling of completed jobs.

Testing

  • TDD throughout (RED → GREEN) for state plumbing, derivation, watchdog (stall/extend/outage paths), pending-sweep, dual-axis _should_early_stop boundaries, champion tracking, and dashboard FINISHED persistence.
  • make fix, make type-check, make test all green (tuner 151, dashboard API 103, remaining harness 62).
  • ruff format --diff / ruff check clean (the exact CI lint-py commands).
  • Live e2e on dev (make -C tuner e2e ENV=dev): 3/3 passed (~13 min) against the pushed dev image — real GMX+AMBER jobs through submission, dual-axis pruning paths, polling, and deletion, with zero client-mode errors in the pod logs.

Not in scope

  • "Explored x of N" trial counters: already possible client-side (all grid trials are created PENDING up-front and are all present in the status payload), no tuner change needed.
  • Per-trial wedge detection for a RUNNING trial that never completes (accepted gap documented in the spec).

Fidasek009 and others added 4 commits August 10, 2026 16:27
Co-Authored-By: kimi-k3 <noreply@kiloc.ai>
Trials stay PENDING at submission; RUNNING is rendered at read time from
Ray task state (State API) instead of an optimistic write, so queued
trials no longer masquerade as running. Job status derives from effective
trial statuses, and a 2h start watchdog fails batches whose trials never
became schedulable instead of hanging forever.

Spec: docs/superpowers/specs/2026-08-10-tuner-job-status-design.md

Co-Authored-By: kimi-k3 <noreply@kiloc.ai>
With tuner-side statuses now trustworthy, a FINISHED job shows Delete
instead of an inert Stop. Pruned (unmeasured) trials are hidden once the
job completes, matching the stopped-job view.

Co-Authored-By: kimi-k3 <noreply@kiloc.ai>
@Fidasek009 Fidasek009 changed the title feat(tuner): derive job and trial statuses from Ray ground truth feat(tuner): trustworthy job statuses end-to-end — Ray ground truth in the API, finish-aware stop/delete in the UI Aug 10, 2026
@Fidasek009 Fidasek009 self-assigned this Aug 10, 2026

@Fidasek009 Fidasek009 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Ray-ground-truth statuses + watchdog + finish-aware UI. See inline comments.

Comment thread tuner/api/rayworker/tuner.py
Comment thread dashboard/ui/src/components/Wizard/TuneStep/TunerTable.tsx
Comment thread dashboard/ui/src/components/Wizard/TuneStep/AmberTunerTable.tsx
Comment thread tuner/api/routers/_shared.py
Comment thread tuner/api/rayworker/tuner.py Outdated
Comment thread tuner/api/rayworker/tuner.py
Fidasek009 and others added 5 commits August 10, 2026 19:47
…y, stale-PENDING sweep

- One filtered list_tasks query replaces per-trial get_task probes; query
  failure is distinguishable from 'nothing running', so a State API outage
  extends the wait instead of false-killing a healthy batch.
- Terminal jobs sweep dangling PENDING trials to ERROR (stall, dead thread,
  and crash paths alike).
- UI clears a selection whose trial is filtered out on job completion.
- Probe failures log one warning line, no traceback.

Co-Authored-By: kimi-k3 <noreply@kiloc.ai>
Ray SDK address resolution overrides any explicit address with the
RAY_ADDRESS env var, and a ray:// address triggers a full client
connect/disconnect cycle (ray_client_address_to_api_server_url). Those
cycles raced the job thread's ray.init in production: concurrent first
inits corrupt ClientContext ('NoneType' object has no attribute
'connection_info') and a second init hits 'Ray Client is already
connected'.

- Pin RAY_API_SERVER_ADDRESS to the dashboard HTTP URL so State API
  queries stay pure-HTTP.
- Serialize ray.init under a lock and drop the unreliable
  ray.is_initialized() fast-path in client mode.
- Document client-mode gotchas and e2e validation in tuner/AGENTS.md;
  add the conventional CLAUDE.md symlink.

Co-Authored-By: kimi-k3 <noreply@kiloc.ai>
Co-Authored-By: kimi-k3 <noreply@kiloc.ai>
…sive

A trial prunes only when it is both below EARLY_STOP_THRESHOLD of the
fastest champion AND above EARLY_STOP_COST_RATIO x the cheapest in
cost-per-step (footprint hourly rate / steps/sec), so the fastest and
the most cost-efficient configs are always kept. Speed threshold
hardened from 65% to 75% now that two axes must both fire.

Dashboard: a tuner job reporting FINISHED is persisted as stopped
(preserving its measured trials) and the tuner job is deleted, so
dashboard never re-polls completed jobs.

Co-Authored-By: kimi-k3 <noreply@kiloc.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant