Skip to content

feat(tui): hide auto-design-router noise from queue by default#707

Open
cpcloud wants to merge 4 commits intoroborev-dev:mainfrom
cpcloud:tui/hide-classify-jobs
Open

feat(tui): hide auto-design-router noise from queue by default#707
cpcloud wants to merge 4 commits intoroborev-dev:mainfrom
cpcloud:tui/hide-classify-jobs

Conversation

@cpcloud
Copy link
Copy Markdown
Contributor

@cpcloud cpcloud commented May 9, 2026

Summary

  • Adds show_classify_jobs config (off by default, global + per-repo *bool override) that hides auto-design-router byproducts from the TUI queue: rows with job_type='classify' and rows with status='skipped'. Both predicates are scoped to source='auto_design' so a future pipeline that adopts the same job_type or status for unrelated reasons isn't silently swallowed.
  • Server-side filtering via new /api/jobs?hide_classify_jobs=true query param backed by storage.WithHideClassifyJobs. SQL uses COALESCE on j.source so rows with source IS NULL aren't dropped by NULL three-valued logic.
  • Per-repo override applies only when the queue is filtered to a single repo, otherwise the global value applies, avoiding one repo's preference leaking into a multi-repo view.
  • Session-only s hotkey toggles visibility; the queue footer label and the ? help view both reflect the current state.
  • Hitting l on an auto-design classify or terminal skipped row renders the classifier verdict, skip_reason, and any internal error in a header band above the (typically empty) streamed log. Reasoning lines are width-truncated and have embedded newlines/tabs folded to spaces so each occupies exactly one terminal row, keeping logVisibleLines accurate. Verdict text branches on job.Status so failed/canceled classifiers don't claim "in progress".
  • Effective show-classify value is cached on the model and recomputed only on filter or session-toggle changes, keeping render and fetch paths free of LoadRepoConfig disk I/O.

cpcloud added 2 commits May 9, 2026 07:22
Adds a show_classify_jobs config (off by default) that hides
auto-design-router byproducts from the TUI queue: rows with
job_type='classify' (the routing decision in flight) and rows with
status='skipped' (the terminal state for skipped design reviews).
Both predicates are scoped to source='auto_design' so a future
pipeline that adopts the same job_type or status for unrelated
reasons isn't silently swallowed.

Configuration:
- Global Config.ShowClassifyJobs (bool, default false).
- RepoConfig.ShowClassifyJobs (*bool override).
- Per-repo override applies when the queue is filtered to a single
  repo, otherwise the global value is used. This avoids one repo's
  preference leaking into a multi-repo view.
- ResolveShowClassifyJobs mirrors ResolveAutoClosePassingReviews.

Server-side filtering:
- New /api/jobs?hide_classify_jobs=true query param.
- storage.WithHideClassifyJobs adds a single SQL clause that hides
  rows where source='auto_design' AND (job_type='classify' OR
  status='skipped'), with COALESCE on j.source so rows with
  source IS NULL aren't dropped by three-valued-logic NULL.

TUI:
- Session-only 's' hotkey toggles visibility on the fly. Footer
  label tracks state ("show classify" vs "hide classify"), and the
  '?' help view lists the shortcut under Filtering.
- Hitting 'l' on an auto-design classify row or terminal skipped
  row shows the classifier's verdict, skip_reason, and any internal
  error in a header band above the (typically empty) streamed log.
  Reasoning lines are width-truncated and have embedded
  newlines/tabs folded to spaces so each occupies exactly one
  terminal row, keeping logVisibleLines accurate for long
  classifier error blobs. Both classify and skipped triggers
  require source='auto_design'.
- Effective show-classify value is cached on the model and
  recomputed only on filter or session-toggle changes, keeping
  render and fetch paths free of LoadRepoConfig disk I/O.

Tests cover resolver precedence, the SQL filter (with auto_design,
empty source, NULL source, and non-auto_design classify cases), the
/api/jobs endpoint, classify reasoning header rendering, width
truncation, and newline folding.
Caught by standard review 2217: classifyReasoningLines treated every
classify row whose status wasn't 'skipped' as 'in progress', so a
failed or canceled classifier showed an in-progress verdict alongside
the error detail. That misleads operators looking at why a classifier
didn't run.

Branch on the row's status: queued/running stays 'in progress', while
failed and canceled get explicit verdict text. Anything unexpected
falls through to a verbatim '...: <status>' so a future status value
can't silently regress to a misleading default.

Tests cover queued, running, failed, and canceled classify rows; the
failed and canceled cases also assert the header does NOT contain
'in progress'.
@roborev-ci
Copy link
Copy Markdown

roborev-ci Bot commented May 9, 2026

roborev: Combined Review (2a5fce6)

Medium issue found: the TUI log header will not show auto-design classifier verdict/reason data because list job records do not populate the required fields.

Medium

  • cmd/roborev/tui/render_log.go:137
    The new log header path depends on job.Source == "auto_design", but log jobs come from m.jobs, populated via ListJobs. That query does not select/scan j.source or j.skip_reason, so real classify/skipped rows have empty Source, and the new verdict/reason lines never render.
    Fix: Include j.source and j.skip_reason in ListJobs’ SELECT/scan path, or load full job details before rendering classify log headers.

Synthesized from 3 reviews (agents: codex, gemini | types: default, security)

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 9, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 66.25000% with 54 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/daemon_client/client.gen.go 0.00% 21 Missing ⚠️
cmd/roborev/tui/handlers_queue.go 0.00% 10 Missing ⚠️
cmd/roborev/tui/render_log.go 83.60% 8 Missing and 2 partials ⚠️
cmd/roborev/tui/handlers_msg.go 33.33% 3 Missing and 1 partial ⚠️
cmd/roborev/tui/render_queue.go 50.00% 1 Missing and 2 partials ⚠️
cmd/roborev/tui/fetch.go 71.42% 2 Missing ⚠️
cmd/roborev/tui/handlers.go 0.00% 2 Missing ⚠️
cmd/roborev/tui/tui.go 90.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Two bugs that hid the new classify reasoning header from view, found
by running the TUI against seeded auto-design rows:

1. ListJobs didn't select j.skip_reason or j.source — the columns
   were added by the auto-design router PR (roborev-dev#661) but the list query
   was not updated. Result: every job loaded by the TUI's queue or
   /api/jobs caller had Source="" and SkipReason="". The
   classifyReasoningLines helper requires Source='auto_design', so
   it always returned nil for list-fetched jobs and the reasoning
   header never rendered. GetJobByID already selects both columns;
   ListJobs is now in sync.

2. The TUI's log view bounced back to the queue with a "No log
   available for this job" flash whenever the on-disk log file was
   missing. That fires for every classify/skipped row because the
   classifier runs as a one-shot SchemaAgent.Decide call and never
   writes to the streaming log. So even with the data fixed, the
   user couldn't see the reasoning header — the view exited before
   it rendered. Now: when the job has classifyReasoningLines output,
   stay in the log view with an empty body so renderLogView's
   header band shows the verdict, skip reason, and any classifier
   error.

Verified manually with an isolated daemon + seeded auto_design rows;
captures show the verdict + reason + detail rendering correctly for
both terminal skipped and failed classify states.
@roborev-ci
Copy link
Copy Markdown

roborev-ci Bot commented May 9, 2026

roborev: Combined Review (feda678)

Review verdict: one medium issue should be addressed before merge.

Medium

  • cmd/roborev/tui/render_log.go:148 - Skipped auto-design rows always render as Auto-design verdict: no design review needed, but classifier execution failures are also converted into skipped rows with job.Error populated. Those rows did not produce a clean “no design review needed” verdict, so the log view can mislead operators about a classifier failure. Distinguish job.Error != "" in the skipped auto-design branch and render a classifier failure or degraded-skip message instead.

Synthesized from 3 reviews (agents: codex, gemini | types: default, security)

- render_log.go: distinguish degraded auto-design skips from clean
  ones. completeClassifyAsSkip writes status=skipped with job.Error
  populated when the classifier execution fails, but the previous
  branch labeled both kinds of skipped rows as "no design review
  needed" — misleading operators about a classifier failure. Now
  branches on job.Error: empty → "no design review needed", non-empty
  → "Auto-design classifier failed (skipped)".
- render_log_classify_test.go: rename the failure-path case to
  assert it now contains "classifier failed" and does NOT contain
  "no design review needed"; add notWant to the clean-skip case
  to lock in that it does NOT contain "failed".
@roborev-ci
Copy link
Copy Markdown

roborev-ci Bot commented May 9, 2026

roborev: Combined Review (3a86be2)

High: TUI queue can break when listing standard review jobs due to NULL string scans.

High

  • internal/storage/jobs.go around line 846: ListJobs selects nullable j.skip_reason and j.source directly, then scans them into string fields. For standard review jobs where these columns can be NULL, this can fail with sql: Scan error... converting NULL to string is unsupported, breaking the TUI queue. Use COALESCE(j.skip_reason, '') and COALESCE(j.source, '') in the query.

Synthesized from 3 reviews (agents: codex, gemini | types: default, security)

@cpcloud
Copy link
Copy Markdown
Contributor Author

cpcloud commented May 9, 2026

False positive on the latest roborev-ci finding. The review claims ListJobs scans nullable j.skip_reason and j.source directly into string fields, which would fail on NULL.

In fact the scan targets are sql.NullString:

// internal/storage/hydration.go
type reviewJobScanFields struct {
    ...
    SkipReason        sql.NullString
    Source            sql.NullString
}

The call site at internal/storage/jobs.go:880-887 scans into &fields.SkipReason, &fields.Sourcesql.NullString handles NULL natively. The hydration step then unwraps via .Valid:

if fields.SkipReason.Valid {
    job.SkipReason = fields.SkipReason.String
}
if fields.Source.Valid {
    job.Source = fields.Source.String
}

TestListJobsWithHideClassifyJobs already exercises this with a row whose source IS NULL (the test asserts that row remains visible after the hide filter); it passes. No code change.

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.

3 participants