feat(rewrite): add Phase 3 jobs browser - #402
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe PR adds an authenticated ChangesJobs dashboard
Test orchestration
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant JobsRoute
participant jobQueries
participant jobMutations
participant RealtimeTransport
Browser->>JobsRoute: open /jobs
JobsRoute->>jobQueries: load runs, schedules, queue, and history
Browser->>jobMutations: submit cancellation, schedule, or worker action
jobMutations->>jobQueries: patch and refresh cached projections
RealtimeTransport->>jobQueries: invalidate affected query roots
jobQueries-->>JobsRoute: render updated dashboard state
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
greenfield/src/browser/jobs/jobRouteSearch.test.ts (1)
21-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd non-string input cases.
The tests cover string inputs only. Route search values can arrive as numbers, arrays,
null, or nested objects after URL parsing. Add cases such asparseJobsRouteSearch({ runId: 5, scheduleId: null })andparseJobsRouteSearch(null)to lock the discard behavior for those shapes.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@greenfield/src/browser/jobs/jobRouteSearch.test.ts` around lines 21 - 32, Add non-string route-search input cases to the “drops malformed selections independently” test, covering numeric, array, null, and nested-object values for runId and scheduleId, plus a null top-level input. Assert malformed values are discarded independently and invalid top-level input returns an empty object.greenfield/src/browser/jobs/useJobRealtimeInvalidation.test.tsx (1)
87-97: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReplace the fixed sleep with a condition wait.
The test waits
jobRealtimeRefreshDelayMs + 20ms on real timers. A loaded CI machine can exceed that margin and make the test flake. UsewaitForon the invalidation state instead, or use fake timers as the second test does.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@greenfield/src/browser/jobs/useJobRealtimeInvalidation.test.tsx` around lines 87 - 97, Update the realtime invalidation test around the act block and queryClient state assertions to replace the fixed setTimeout sleep with waitFor that waits until runKey and scheduleKey are invalidated, while preserving the unrelatedKey non-invalidation assertion; alternatively, use the fake-timer approach established by the second test.greenfield/src/browser/jobs/ScheduleTable.tsx (1)
106-115: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueConsider memoizing the table data array.
datais a new array with new row objects on every render. The table recomputes its row model each render, and the virtualized path can hold many rows. Wrap the mapping inuseMemokeyed onschedules,selectedId, andonSelectto keep row identity stable.Note that
onSelectmust also be stable at the call site for the memo to help.ScheduleBrowsercurrently recreatesselectScheduleon each render.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@greenfield/src/browser/jobs/ScheduleTable.tsx` around lines 106 - 115, Memoize the mapped table data used by useTable in ScheduleTable, with useMemo dependencies on schedules, selectedId, and onSelect so row identity remains stable. Also update ScheduleBrowser to memoize selectSchedule, ensuring the onSelect dependency itself is stable and the optimization is effective.greenfield/src/browser/jobs/JobRunBrowser.tsx (1)
498-510: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDerive
maxLengthfrom the schedule id contract.The schedule id schema uses
scheduleIdMaximumLength, but this input hardcodes80. If the contract bound changes, valid schedule ids can be truncated and then rejected. Import and usescheduleIdMaximumLengthinstead.♻️ Proposed change
import { jobResourceClasses, type JobResourceClass, type JobRunState, jobRunStates, type JobTriggerType, jobTriggerTypes, + scheduleIdMaximumLength, scheduleIdSchema, } from "../../contracts/jobModel.ts";<Input className="mt-2 font-mono" - maxLength={80} + maxLength={scheduleIdMaximumLength}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@greenfield/src/browser/jobs/JobRunBrowser.tsx` around lines 498 - 510, Replace the hardcoded maxLength value on the schedule filter Input in JobRunBrowser with the imported scheduleIdMaximumLength contract constant, ensuring the field stays aligned with the schedule ID schema.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@greenfield/scripts/runCoverage.ts`:
- Around line 137-139: Update mergeCoverageReports to pass a valid glob pattern
that matches the generated coverage reports, or explicitly merge the entries in
reportPaths individually before summarization; do not pass an empty pattern
alongside filePaths, and preserve the existing Promise<string> merged-report
behavior.
In `@greenfield/src/browser/jobs/scheduleEditorForm.ts`:
- Around line 28-40: Update intervalMilliseconds to avoid rejecting valid
three-decimal inputs caused by floating-point multiplication: round the
seconds-to-milliseconds product before the Number.isSafeInteger check, while
preserving the existing format and minimum/maximum validation.
In `@greenfield/src/browser/test/setup.test.ts`:
- Around line 21-29: Update the nested zero-delay scheduling test around “leaves
arbitrary nested zero-delay work for teardown to drain” to record completion
from the innermost callback, then add an assertion at the start of the following
test that the completion marker is set. Preserve the existing three-level
scheduling structure while verifying teardown drained the terminal callback.
---
Nitpick comments:
In `@greenfield/src/browser/jobs/jobRouteSearch.test.ts`:
- Around line 21-32: Add non-string route-search input cases to the “drops
malformed selections independently” test, covering numeric, array, null, and
nested-object values for runId and scheduleId, plus a null top-level input.
Assert malformed values are discarded independently and invalid top-level input
returns an empty object.
In `@greenfield/src/browser/jobs/JobRunBrowser.tsx`:
- Around line 498-510: Replace the hardcoded maxLength value on the schedule
filter Input in JobRunBrowser with the imported scheduleIdMaximumLength contract
constant, ensuring the field stays aligned with the schedule ID schema.
In `@greenfield/src/browser/jobs/ScheduleTable.tsx`:
- Around line 106-115: Memoize the mapped table data used by useTable in
ScheduleTable, with useMemo dependencies on schedules, selectedId, and onSelect
so row identity remains stable. Also update ScheduleBrowser to memoize
selectSchedule, ensuring the onSelect dependency itself is stable and the
optimization is effective.
In `@greenfield/src/browser/jobs/useJobRealtimeInvalidation.test.tsx`:
- Around line 87-97: Update the realtime invalidation test around the act block
and queryClient state assertions to replace the fixed setTimeout sleep with
waitFor that waits until runKey and scheduleKey are invalidated, while
preserving the unrelatedKey non-invalidation assertion; alternatively, use the
fake-timer approach established by the second test.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 4d3824f5-2c6b-4b77-a75a-d9c947263440
📒 Files selected for processing (47)
greenfield/docs/architecture/greenfield-rewrite/progress.mdgreenfield/package.jsongreenfield/scripts/runBrowserTests.test.tsgreenfield/scripts/runBrowserTests.tsgreenfield/scripts/runCoverage.test.tsgreenfield/scripts/runCoverage.tsgreenfield/src/browser/jobs/JobQueuePanel.test.tsxgreenfield/src/browser/jobs/JobQueuePanel.tsxgreenfield/src/browser/jobs/JobRunBrowser.test.tsxgreenfield/src/browser/jobs/JobRunBrowser.tsxgreenfield/src/browser/jobs/JobRunDetail.test.tsxgreenfield/src/browser/jobs/JobRunDetail.tsxgreenfield/src/browser/jobs/JobRunTable.test.tsxgreenfield/src/browser/jobs/JobRunTable.tsxgreenfield/src/browser/jobs/JobsRoute.test.tsxgreenfield/src/browser/jobs/JobsRoute.tsxgreenfield/src/browser/jobs/ScheduleBrowser.tsxgreenfield/src/browser/jobs/ScheduleDetail.tsxgreenfield/src/browser/jobs/ScheduleDetailForm.test.tsxgreenfield/src/browser/jobs/ScheduleDetailState.test.tsxgreenfield/src/browser/jobs/ScheduleEditor.test.tsxgreenfield/src/browser/jobs/ScheduleEditor.tsxgreenfield/src/browser/jobs/ScheduleTable.test.tsxgreenfield/src/browser/jobs/ScheduleTable.tsxgreenfield/src/browser/jobs/jobBrowserFailure.tsgreenfield/src/browser/jobs/jobMutations.test.tsxgreenfield/src/browser/jobs/jobMutations.tsgreenfield/src/browser/jobs/jobQueries.test.tsgreenfield/src/browser/jobs/jobQueries.tsgreenfield/src/browser/jobs/jobRouteSearch.test.tsgreenfield/src/browser/jobs/jobRouteSearch.tsgreenfield/src/browser/jobs/jobRunPresentation.tsgreenfield/src/browser/jobs/scheduleEditorForm.test.tsgreenfield/src/browser/jobs/scheduleEditorForm.tsgreenfield/src/browser/jobs/schedulePresentation.tsgreenfield/src/browser/jobs/testSupport/ScheduleDetail.tsxgreenfield/src/browser/jobs/useJobRealtimeInvalidation.test.tsxgreenfield/src/browser/jobs/useJobRealtimeInvalidation.tsgreenfield/src/browser/layout/DashboardShell.tsxgreenfield/src/browser/lib/dashboardRoutes.tsgreenfield/src/browser/router.tsxgreenfield/src/browser/routes/jobs.lazy.tsxgreenfield/src/browser/test/setup.test.tsgreenfield/src/browser/test/setup.tsgreenfield/src/browser/ui/DataTable.test.tsxgreenfield/src/browser/ui/DataTable.tsxgreenfield/src/contracts/schedules.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
- GitHub Check: dashboard-checks
- GitHub Check: Analyze JavaScript and TypeScript
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-08-07T18:47:49.639Z
Learnt from: mira-2026
Repo: rajohan/Mira-Dashboard PR: 398
File: greenfield/src/server/domains/monitoring/catalogErrors.ts:3-3
Timestamp: 2026-08-07T18:47:49.639Z
Learning: In the greenfield TypeScript application, use the pinned Effect version 4.0.0-beta.104 API. Preserve `Schema.Literals` for readonly literal tuples and arrays, and do not replace it with variadic `Schema.Literal(...)` unless the replacement has been validated against the pinned Effect version.
Applied to files:
greenfield/src/browser/lib/dashboardRoutes.tsgreenfield/src/browser/jobs/jobRouteSearch.test.tsgreenfield/src/browser/jobs/schedulePresentation.tsgreenfield/src/browser/test/setup.tsgreenfield/src/browser/jobs/useJobRealtimeInvalidation.tsgreenfield/src/browser/jobs/scheduleEditorForm.test.tsgreenfield/src/browser/jobs/jobBrowserFailure.tsgreenfield/src/browser/jobs/jobRunPresentation.tsgreenfield/scripts/runBrowserTests.test.tsgreenfield/src/contracts/schedules.tsgreenfield/scripts/runBrowserTests.tsgreenfield/src/browser/jobs/jobRouteSearch.tsgreenfield/src/browser/test/setup.test.tsgreenfield/scripts/runCoverage.test.tsgreenfield/src/browser/jobs/jobQueries.test.tsgreenfield/scripts/runCoverage.tsgreenfield/src/browser/jobs/jobQueries.tsgreenfield/src/browser/jobs/scheduleEditorForm.tsgreenfield/src/browser/jobs/jobMutations.ts
📚 Learning: 2026-08-07T17:05:36.638Z
Learnt from: mira-2026
Repo: rajohan/Mira-Dashboard PR: 397
File: greenfield/src/server/domains/agents/service.test.ts:225-239
Timestamp: 2026-08-07T17:05:36.638Z
Learning: In Bun test files, write rejection assertions as `expect(promise).rejects...` without `await`. The repository's installed matcher types return `void`, and ESLint's `typescript(await-thenable)` rule rejects awaiting these matcher assertions.
Applied to files:
greenfield/src/browser/jobs/jobRouteSearch.test.tsgreenfield/src/browser/jobs/scheduleEditorForm.test.tsgreenfield/scripts/runBrowserTests.test.tsgreenfield/src/browser/test/setup.test.tsgreenfield/scripts/runCoverage.test.tsgreenfield/src/browser/jobs/jobQueries.test.ts
🪛 ast-grep (0.45.0)
greenfield/src/browser/jobs/jobQueries.ts
[error] 136-144: Avoid SQL injection
Context: client.query(
"jobs.listRuns",
{
...(pageParam === undefined ? {} : { cursor: pageParam }),
...(filters === undefined ? {} : { filters }),
limit: 100,
},
{ signal }
)
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-typescript)
[error] 307-315: Avoid SQL injection
Context: client.query(
"schedules.list",
{
...(pageParam === undefined ? {} : { cursor: pageParam }),
enabled,
limit: 100,
},
{ signal }
)
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-typescript)
[error] 337-345: Avoid SQL injection
Context: client.query(
"schedules.listRuns",
{
...(pageParam === undefined ? {} : { cursor: pageParam }),
id,
limit: 100,
},
{ signal }
)
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
(sql-injection-typescript)
🪛 OpenGrep (1.26.0)
greenfield/src/browser/jobs/ScheduleDetail.tsx
[ERROR] 76-78: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
🔇 Additional comments (60)
greenfield/src/browser/test/setup.ts (2)
4-35: LGTM!Also applies to: 37-45, 83-96
36-36: 🩺 Stability & AvailabilityNo change needed for negative timeouts.
setTimeoutcannot receive negative delays from the guarded test paths, and the tracked zero-delay queue handlesundefined/0delays as used.> Likely an incorrect or invalid review comment.greenfield/src/browser/test/setup.test.ts (1)
6-19: LGTM!Also applies to: 31-40
greenfield/src/browser/ui/DataTable.test.tsx (1)
118-122: LGTM!greenfield/src/browser/ui/DataTable.tsx (1)
78-88: LGTM!Also applies to: 143-143
greenfield/package.json (1)
26-26: LGTM!greenfield/scripts/runBrowserTests.ts (3)
55-99: LGTM!
139-152: LGTM!
118-131: 🗄️ Data Integrity & IntegrationNo change needed for the ignore pattern.
--path-ignore-patterns=src/browser/jobs/ScheduleDetail{Form,State}.test.tsxis valid; Bun supports brace expansion for test path ignore globs.greenfield/scripts/runBrowserTests.test.ts (1)
15-114: LGTM!greenfield/scripts/runCoverage.ts (3)
6-79: LGTM!
98-108: LGTM!
122-130: LGTM!Also applies to: 146-151, 169-195
greenfield/scripts/runCoverage.test.ts (1)
3-8: LGTM!Also applies to: 30-95, 97-180
greenfield/src/browser/jobs/jobQueries.ts (2)
26-127: LGTM!Also applies to: 129-212, 300-377
234-298: LGTM!greenfield/src/browser/jobs/jobQueries.test.ts (1)
116-143: LGTM!Also applies to: 145-191, 439-520, 522-643
greenfield/src/browser/jobs/useJobRealtimeInvalidation.ts (1)
1-25: LGTM!greenfield/src/browser/jobs/JobsRoute.tsx (1)
9-33: LGTM!greenfield/src/browser/routes/jobs.lazy.tsx (1)
1-14: LGTM!greenfield/src/contracts/schedules.ts (1)
104-112: 📐 Maintainability & Code QualityNo issue:
ScheduleDetail.tsximportsscheduleDisableReasonSchemafromgreenfield/src/contracts/schedules.ts.greenfield/src/browser/jobs/jobMutations.ts (1)
91-132: LGTM!Also applies to: 162-209, 211-253, 322-361, 364-419, 630-675
greenfield/src/browser/jobs/jobMutations.test.tsx (1)
233-372: LGTM!Also applies to: 374-545, 547-636, 638-726, 728-801, 803-855, 857-887, 889-988
greenfield/src/browser/jobs/ScheduleTable.tsx (1)
33-96: LGTM!Also applies to: 127-137
greenfield/src/browser/jobs/JobRunDetail.test.tsx (1)
67-112: LGTM!Also applies to: 114-156, 158-193, 195-230, 232-273
greenfield/src/browser/jobs/ScheduleBrowser.tsx (1)
46-141: LGTM!Also applies to: 143-177, 184-254, 256-311
greenfield/src/browser/jobs/JobsRoute.test.tsx (1)
209-504: LGTM!Also applies to: 514-553, 556-633, 635-780, 782-838, 840-933, 935-1013, 1015-1057, 1059-1137, 1139-1201, 1203-1298, 1300-1348, 1350-1403, 1405-1426
greenfield/src/browser/router.tsx (1)
8-8: LGTM!Also applies to: 37-41, 58-58
greenfield/src/browser/layout/DashboardShell.tsx (1)
5-5: LGTM!Also applies to: 35-35
greenfield/src/browser/lib/dashboardRoutes.ts (1)
7-7: LGTM!greenfield/docs/architecture/greenfield-rewrite/progress.md (1)
10-18: LGTM!Also applies to: 949-970
greenfield/src/browser/jobs/jobRouteSearch.ts (1)
1-34: LGTM!greenfield/src/browser/jobs/jobRunPresentation.ts (1)
1-30: LGTM!greenfield/src/browser/jobs/jobBrowserFailure.ts (1)
1-22: LGTM!greenfield/src/browser/jobs/JobQueuePanel.tsx (1)
1-236: LGTM!greenfield/src/browser/jobs/JobQueuePanel.test.tsx (1)
1-89: LGTM!greenfield/src/browser/jobs/JobRunDetail.tsx (1)
1-463: LGTM!greenfield/src/browser/jobs/JobRunTable.tsx (1)
1-173: LGTM!greenfield/src/browser/jobs/JobRunTable.test.tsx (1)
1-122: LGTM!greenfield/src/browser/jobs/JobRunBrowser.tsx (1)
85-198: LGTM!Also applies to: 315-496, 511-550
greenfield/src/browser/jobs/JobRunBrowser.test.tsx (1)
1-531: LGTM!greenfield/src/browser/jobs/scheduleEditorForm.ts (3)
42-83: LGTM!
85-102: LGTM!
104-130: LGTM!greenfield/src/browser/jobs/scheduleEditorForm.test.ts (1)
12-56: LGTM!Also applies to: 58-101
greenfield/src/browser/jobs/ScheduleEditor.tsx (3)
25-61: LGTM!
63-98: LGTM!
99-273: LGTM!greenfield/src/browser/jobs/ScheduleEditor.test.tsx (1)
11-31: LGTM!Also applies to: 33-92
greenfield/src/browser/jobs/ScheduleDetail.tsx (6)
56-72: LGTM!
74-123: LGTM!
125-130: LGTM!Also applies to: 148-175
177-237: LGTM!
239-379: LGTM!
438-444: 🎯 Functional CorrectnessNo action needed.
Buttondefaults totype="button"inside forms, so Cancel does not triggersubmitDisableIntent.greenfield/src/browser/jobs/ScheduleDetailForm.test.tsx (1)
15-122: LGTM!greenfield/src/browser/jobs/ScheduleDetailState.test.tsx (1)
20-172: LGTM!greenfield/src/browser/jobs/testSupport/ScheduleDetail.tsx (1)
1-110: LGTM!greenfield/src/browser/jobs/schedulePresentation.ts (1)
1-16: LGTM!greenfield/src/browser/jobs/ScheduleTable.test.tsx (1)
9-83: LGTM!
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b4628bf7b5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Summary
/jobsoperator UI for queue state, live workers, claim pause/resume, global run history, exact run detail, bounded events, and cancellationVerification
bun run test:bun— 1541/1541bun run test:browser— 218/218 (146 core + 62 jobs + 5 schedule form + 5 schedule state across 8 browser processes)bun run test:coverage— 92.05% (48,950/53,180 lines; required 85%)bun run typecheckbun run lintbun run format:checkbun run check:boundariesbun run docs:checkbun run db:checkbun run build:browsergit diff --checkStack and scope
82d74ddb7ca477d6a911f6faaa76c9eb6b0fb5f6/jobsfrontend parity remains planned until the OpenClaw cron half landsopenClawCron.*remain explicitly deferred