Skip to content

feat: show bounded monitors in the dashboard - #5176

Closed
kyleseaman wants to merge 1 commit into
token-monitors-toolsfrom
codex/token-monitors-dashboard
Closed

feat: show bounded monitors in the dashboard#5176
kyleseaman wants to merge 1 commit into
token-monitors-toolsfrom
codex/token-monitors-dashboard

Conversation

@kyleseaman

@kyleseaman kyleseaman commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Stack: #5171#5172#5173#5174#5175#5176#5177

Position: 6 of 7. Base: #5175. Next: #5177.

Adds one normalized automation state model and bounded create, inspect, update, stop, restart, terminal, and sidebar UX.

Verification: TypeScript, production build, 454 focused UI tests, i18n, and pseudolocale rendering.

@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 81b2e1e2298051a8e4e63e220f692b0035bb5ec2 and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 81b2e1e

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 81b2e1e2298051a8e4e63e220f692b0035bb5ec2: <one-sentence reason>

@kyleseaman kyleseaman closed this Aug 23, 2026
@kyleseaman
kyleseaman deleted the codex/token-monitors-dashboard branch August 23, 2026 05:18
@github-actions github-actions Bot removed the readiness: checking Automated validation is still running label Aug 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — 🟡 CONCERNS

UX-level review of 81b2e1e2298051a8e4e63e220f692b0035bb5ec2 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Reviewed the full diff: the new SessionAutomationPopover, MonitorRadar, sidebar status rows, the automation normalizer, and all new i18n strings. No screenshots in this PR. Findings below.

UX-Verdict: CONCERNS

Solid bounded-monitor surface, but the seven-field create form lives in a dismiss-on-outside-click popover that silently discards everything typed.

Watch

  • Draft loss on accidental dismiss — the create/edit form (7 fields incl. a 1,000-char "Instructions for an actionable wake" textarea) sits in a Radix popover; the close handler resets sourceId: null and reopen replaces the draft with incoming (SessionAutomationPopover.tsx editor effect), so one stray click outside or Escape wipes all input, every time. Moderate frequency × real work lost × persistent. Smallest fix: keep the draft keyed by sourceId across close/reopen until saved or explicitly cancelled.
  • Blocked-record dead end — an invalid/newer-schema record normalizes to terminal: {reason: 'invalid_monitor_record'}, actionable: false; the form is hidden when terminal and "Restart monitor" renders disabled={!monitor.actionable} with no explanation, so the popover offers only Close — the user can neither restart, dismiss the record, nor create a fresh monitor for that slot. Rare × feature-dead-for-slot × permanent. Add a helper line explaining why restart is unavailable and an escape (clear record / start new).

Suggestions

  • The legacy form's warning banner reuses the button string use_legacy_costly ("Use legacy goal loop (costly)") — as a role="note" it reads as an instruction to do what the user already did; give the notice its own declarative string ("This legacy goal loop invokes the agent every cycle and can be costly.").
  • "Terminal reason" renders raw codes (invalid_monitor_record, target_unavailable) in mono as the answer to "why did it stop"; map the known outcome enum to one-line sentences, keeping the code as secondary detail.
  • The composer trigger shows a bare mono number beside the radar icon (fmtNumber(monitor.usage.probes)) with only an aria-label; add a title (e.g. "Probes: 27") so sighted users can learn what it counts without opening the popover.

[UX-REVIEWED] 81b2e1e

@github-actions

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — 🟡 CONCERNS

Design-level review of 81b2e1e2298051a8e4e63e220f692b0035bb5ec2 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Design-Verdict: CONCERNS

Sound consolidation, but the SPA compiles the backend's numeric bounds into record validity, so a benign server-side limit change bricks live monitors' controls.

Watch

  • Bounds as validity, duplicated owner. STRUCTURED_MONITOR_LIMITS in automation.ts hardcodes the ceilings owned by monitoring/models.py (MAX_MONITOR_CADENCE_SECS, MAX_MONITOR_TOKENS, …), and the normalizer's supported check uses isBoundedInteger(...) as a validity criterion — not just a form constraint. Chain: backend raises a ceiling (a config-constant edit, not a schema change, so no version bump) → a legitimately served record fails requiredLimitsValidstructuredFallback marks it terminal blocked / invalid_monitor_record and non-actionable → the dashboard shows a live, budget-consuming monitor as stopped and withdraws its Stop control (Restart is the sole terminal action). Range checks belong on the create/update form; for inbound records, type-check and render. The exact-key-set strictness on the observation is fine — that one is version-gated and protects a real boundary — but the numeric bounds are not version-gated and have two owners with no shared pin.

Suggestions

  • Restrict the frontend limit constants to form validation and display; accept any type-valid served value in the normalizer, or have /api/monitors advertise its limits so the form reads them instead of restating them.

[DESIGN-REVIEWED] 81b2e1e

@kyleseaman

Copy link
Copy Markdown
Collaborator Author

Replaced by #5185 after renaming the branch to remove the prefix.

@github-actions

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Premise-level review of 81b2e1e2298051a8e4e63e220f692b0035bb5ec2 — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Evidence gathered; composing the review.

First-Principles-Verdict: CONCERNS

Every capability earns its place, but a hand-rolled reduced-motion hook duplicates framer-motion's useReducedMotion, and two UX changes ride along undeclared.

What this change ships

Intent: let a user create, watch, and control a bounded PR monitor from the dashboard — an ADDITION (RFC-backed, PR 6 of 7).

  1. Monitor detail/inspect shows typed latest classification, reason, summary (3 new persisted fields) — justified
  2. Public monitor payload rebuilt from a strict fact allowlist; persistence-only keys never reach browser/agent — justified (agent/external boundary)
  3. Composer chip opens a popover to create/edit/stop/restart a bounded monitor with backend-matched limits — justified
  4. Legacy goal-loop form demoted behind "Use legacy (costly)" button + warn notice — undeclared relabel
  5. Sidebar rows gain monitor status lanes (pulse above work, passive below unread) — justified
  6. Sidebar "Thinking…" lane now keyed to s.running, not the widened set — undeclared, rides along (needed)
  7. One Redux automations collection replaces goalLoops + the window CustomEvent dual path — justified deletion
  8. Reconnect seed reads both lists with per-slot fencing/tombstones — justified (documented race)
  9. New MonitorRadar glyph with a hand-rolled reduced-motion hook — duplicate of framer-motion's useReducedMotion
  10. 12-locale catalog, context notes, spec/doc updates — mandated by documented invariants

Watch

  • MonitorRadar.tsx re-implements useReducedMotionPreference via matchMedia; grep useReducedMotion from framer-motion: 17 existing consumer files (ui.tsx, SegmentedControl.tsx, …). Second spelling of an existing hook in a component already importing framer-motion.
  • The canonical-observation schema is now spelled twice — _GITHUB_OBSERVATION_FIELDS in monitoring/models.py and the exact-key list in automation.ts:isCanonicalGitHubObservation (2 copies). Divergence fails closed (record goes inert), which is the designed behavior, but a v1 field addition must land in both or every monitor blanks.
  • Items 4 and 6 change shipped behavior the description ("bounded create, inspect, update, stop, restart, terminal, and sidebar UX") never names; both look correct, a human should confirm the lane reordering intent.

Subtractions

  • Delete useReducedMotionPreference in MonitorRadar.tsx; use useReducedMotion from framer-motion (17 existing consumers).
  • Drop the legacyNotice?: string prop on AutoNudgePopover — one consumer (SessionAutomationPopover.tsx:186) always passes the same catalog string; render the notice inside the legacy popover unconditionally.
  • Shrink StructuredMonitor: monitorKind, objective, version, and latest.observedAt have zero production consumers (grep outside monitoring/automation.ts and tests: 0; the popover renders a fixed review_ready label).

[FIRST-PRINCIPLES-REVIEWED] 81b2e1e

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