ui: split /hosts/{id} into host page + /runs/{runID} run page
CI / Lint + build + test (push) Successful in 1m35s
Release / release (push) Successful in 23m47s

Host page owns host metadata, full runs table with per-row stage strip,
in-flight banner, and empty-state CTA. Run page owns pipeline, active
step, logs, sub-steps, spec diffs, and hold banner with a breadcrumb
back to the host. Dashboard tile reverts to host-only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-18 20:37:57 -04:00
parent 5c6bfa5ffa
commit 19608bef1b
23 changed files with 3173 additions and 2827 deletions
+10 -3
View File
@@ -155,9 +155,8 @@ func (r *Runs) LatestForHost(ctx context.Context, hostID int64) (*model.Run, err
}
// ListForHost returns the most recent `limit` runs for a host, newest
// first. Caller uses this to drive the host-detail runs sidebar (last 20
// by default, Phase 2). Zero/negative limit falls back to a safe cap so
// a mistaken call can't scan the whole history into memory.
// first. Zero/negative limit falls back to a safe cap so a mistaken call
// can't scan the whole history into memory.
func (r *Runs) ListForHost(ctx context.Context, hostID int64, limit int) ([]model.Run, error) {
if limit <= 0 {
limit = 20
@@ -193,6 +192,14 @@ func (r *Runs) ListForHost(ctx context.Context, hostID int64, limit int) ([]mode
return out, rows.Err()
}
// ListForHostAll returns every run for a host, newest first. Caps at a
// defensive 1000 rows so a runaway host that somehow accumulated tens
// of thousands of runs doesn't blow up the page load; typical hosts
// finish with < 50.
func (r *Runs) ListForHostAll(ctx context.Context, hostID int64) ([]model.Run, error) {
return r.ListForHost(ctx, hostID, 1000)
}
// Active returns all runs in non-terminal states.
func (r *Runs) Active(ctx context.Context) ([]model.Run, error) {
rows, err := r.DB.QueryContext(ctx, `