Skip to content

Sync: promote the answer, collapse the evidence - #47

Merged
guarzo merged 3 commits into
mainfrom
fix/sync-status-strip
Aug 3, 2026
Merged

Sync: promote the answer, collapse the evidence#47
guarzo merged 3 commits into
mainfrom
fix/sync-status-strip

Conversation

@guarzo

@guarzo guarzo commented Aug 3, 2026

Copy link
Copy Markdown
Owner

/admin/sync spent five screenfuls of five structurally identical tables to communicate one bit: nothing is wrong. The answer was already on the page — MEMBERSHIP OK 18m ago — as the smallest text on it.

What changed

A status strip replaces the five section headers. Job, health, last run, cadence — one row each, the whole page's answer above the fold.

Cadence, not next run. The column shows every 30m / hourly :05 / daily 03:00 UTC, read from src/core/schedules.ts — the same map src/worker/queues.ts registers its pg-boss schedules from, so the page cannot drift from what the worker runs. It is deliberately not a next-run timestamp: computing one needs a cron evaluator (cron-parser is only a transitive pg-boss dep) and the worker's clock, and an approximated next-run is worse than none. formatCadence falls back to the raw cron for any shape it does not model rather than paraphrasing confidently. If nothing on the page is scheduled the column is dropped entirely rather than filled with dashes.

History collapses under each strip entry, closed by default. A partial or failed latest run opens on its own. A run still in flight does not — it resolves within seconds, and opening on it would make the page flap through every sweep. Built on <details> so it still works with no JS and browsers can still find text inside a collapsed section; the React state mirror exists only to put an explicit aria-expanded on the summary.

Counts become real columns — but only the ones that moved. A counter that is zero on every run in the window gets no column; an all-zero result collapses to no change. Full JSON stays behind a disclosure. This is the same rule that removed the error column, applied to the count keys.

One timestamp and a duration replace STARTED + FINISHED expressing a one-second delta twice.

ERROR is gone as a column — it was an em-dash on every row. The error string rides the status cell and renders only when populated.

Both enqueue buttons show a pending label, and the queued redirect renders a role="status" notice (needed because a server-action redirect re-renders without a document load). No confirmation dialog: the action is non-destructive and DESIGN.md/PRODUCT.md push against modals.

Boundaries held

The enqueue-don't-execute boundary is untouched — the buttons still enqueue into the outbox, the worker still executes. Nothing moved into the request path. src/core/ stays pure (formatDuration takes both timestamps rather than reading a clock). queues.ts changed only to read its cron from the shared map, and now throws at startup if a JOB_QUEUES entry has no schedule, rather than ticking never; tests/schedules.test.ts cross-checks the two tables so they cannot drift.

Verification

$ npm run typecheck
> tsc --noEmit          (clean)

$ npm run lint
✖ 4 problems (0 errors, 4 warnings)
  — all four pre-existing @next/next/no-img-element warnings

$ npx playwright test
13 passed (24.4s)

$ npm test
Test Files  47 passed (47)
     Tests  335 passed (335)

Screenshot check with wanderer seeded failed: the red FAILED token is the only non-green item in the health column and the only expanded section — it is what the eye lands on first. Verified again at 390px.

Where to look

src/core/schedules.ts (is the cadence honest?), src/core/run-summary.ts:62 (the column-earns-its-width rule), and src/app/admin/sync/page.tsx:216 (the three ways counts can be absent, which read differently).

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 5 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 856f872d-e713-4949-b22c-37b8921c110b

📥 Commits

Reviewing files that changed from the base of the PR and between c84694f and 9564073.

📒 Files selected for processing (10)
  • e2e/sync.spec.ts
  • src/app/_components/disclosure.tsx
  • src/app/_components/submit.tsx
  • src/app/admin/sync/page.tsx
  • src/app/globals.css
  • src/core/run-summary.ts
  • src/core/schedules.ts
  • src/worker/queues.ts
  • tests/run-summary.test.ts
  • tests/schedules.test.ts

Comment @coderabbitai help to get the list of available commands.

guarzo added 3 commits August 3, 2026 18:13
/admin/sync spent five screenfuls of five structurally identical tables
to say one thing: nothing is wrong. The answer was already on the page,
as the smallest text on it.

- The five section headers become one status strip: job, health, last
  run, cadence. Cadence comes from src/core/schedules.ts, the same map
  the worker registers its pg-boss schedules from, so it is a fact
  rather than a second copy that rots. It is a cadence, not a next-run
  timestamp: that would need a cron evaluator and the worker's clock,
  and an approximated one is worse than none.
- Each job's history collapses under its strip entry, closed by
  default. A partial or failed latest run opens on its own. A run still
  in flight does not — it resolves within seconds, and opening on it
  would make the page flap through every sweep.
- Counts become real columns, but only for counters that actually moved
  somewhere in the window; an all-zero result collapses to "no change".
  Full JSON stays behind a disclosure.
- STARTED plus FINISHED expressed a one-second delta in two full
  timestamps. Now one timestamp and a duration.
- ERROR was an em-dash on every row. It rides the status cell and
  renders only when populated.
- Both enqueue buttons show a pending label on submit, and the queued
  redirect renders a role="status" notice. No dialog: the action is
  non-destructive and DESIGN.md pushes against modals.

The enqueue-don't-execute boundary is unchanged — the buttons still
enqueue and the worker still executes. queues.ts changed only to read
its cron from the shared map, and throws at startup if a queue has no
schedule rather than ticking never.
… width

Two defects the polish pass turned up in the new code:

- formatDuration rounded each unit separately, so 5m 59.6s rendered as
  "5m 60s". It now rounds to whole seconds once and decomposes that.
- A run whose counts were a recorded-but-empty object took the per-key
  branch with an empty column set and emitted no cells at all, leaving
  a row one td short of its header. Empty column sets now take the
  spanning branch, where an all-zero result still reads "no change" and
  an empty one reads as nothing recorded.
… grid

main's audit-log work gave Json a `summary` prop that renders a caller
supplied one-liner in place of the truncated peek — the same job the
`label` prop added here was doing. Dropped `label` and its CSS; the sync
page passes summary="json".

The .log--runs rule was still `table-layout: fixed; min-width: 60rem`,
written so the five per-job sections shared one column grid. They no
longer do — each job shows only the counters it moves — so that rule was
forcing a 60rem scroll under five-column tables. Replaced with a floor
set by legibility instead of alignment, plus a minimum on the status
column: it is the one cell carrying prose now that the error folds into
it, and auto layout was free to squeeze it to one word per line.
@guarzo
guarzo force-pushed the fix/sync-status-strip branch from 7dcfc3d to 9564073 Compare August 3, 2026 22:19
@guarzo
guarzo merged commit d4fb44b into main Aug 3, 2026
6 checks passed
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