Skip to content

feat(rewrite): add durable jobs and worker foundation - #401

Open
mira-2026 wants to merge 13 commits into
mira/greenfield-rewrite-phase-three-notificationsfrom
mira/greenfield-rewrite-phase-three-jobs
Open

feat(rewrite): add durable jobs and worker foundation#401
mira-2026 wants to merge 13 commits into
mira/greenfield-rewrite-phase-three-notificationsfrom
mira/greenfield-rewrite-phase-three-jobs

Conversation

@mira-2026

@mira-2026 mira-2026 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add the Phase 3 durable jobs and schedules domain: nine capability-gated tRPC procedures, strict transport models, realtime invalidation topics, generated schemas, and parity inventory
  • add seven hardened SQLite scheduling tables plus repository/service semantics for caller-scoped idempotency, schedule coalescing, deterministic claims, fenced run/resource leases, bounded events, cancellation, retries, and persistent claim pause
  • replace the database-only worker with an Effect-owned coordinator and scheduler, including registration/heartbeat, lease renewal/recovery, typed retryable actions, graceful draining, and the safe system.worker-smoke action

Behavior and regression coverage

  • schedules support interval, daily, and canonical five-field cron timing with explicit IANA zones, DST regressions, retained dormant cadence, compare-and-swap configuration versions, and one coalesced occurrence rather than backlog replay
  • one immediate transaction considers at most 32 ordered candidates, skips occupied resource sets, and claims the first eligible run with all resource leases or none; renew and settle paths are fenced by run, worker, token, state, and durable time
  • manual enqueue idempotency is scoped to the requesting principal and stable request intent; schedule ticks use a deterministic occurrence namespace
  • run history reserves structural capacity within the 1,000-event/1 MiB limits so a legal ten-attempt lifecycle can always record its terminal event
  • only explicitly typed retryable action failures retry; permanent, unknown-action, timeout, and cancellation outcomes fail closed under the declared policy
  • jobs.getRun reads run state and its event page from one SQLite snapshot, and worker-produced audit/realtime timestamps use the repository's effective clamped transition time
  • run settlement and expired-lease recovery atomically invalidate both run and schedule projections, with durable cancellation retaining its canonical outcome even when it races shutdown or recovery
  • the worker exposes completion failures to the process boundary and shuts down in order: drain, stop new schedule/claim work, interrupt or settle active work, stop the worker record, dispose Effect runtime, then close the database
  • the production release lifecycle now authenticates through the bundled web process, enqueues system.worker-smoke, observes the separate bundled worker complete it, and proves web-first shutdown with exit code 0 for both processes and no forced stop

Verification

  • Repository lint: cd greenfield && bun run lint
  • Repository formatting: cd greenfield && bun run format:check
  • TypeScript graphs: cd greenfield && bun run typecheck
  • Source boundaries: cd greenfield && bun run check:boundaries
  • Database schema and migration drift: cd greenfield && bun run db:check
  • Generated documentation drift: cd greenfield && bun run docs:check
  • Complete greenfield Bun suite: 1,518/1,518 tests, 7,714 assertions
  • Complete greenfield browser suite: 142/142 tests, 759 assertions
  • Review-focused jobs, contracts, schema, realtime, and shutdown regressions
  • Bundled production lifecycle after final shutdown hardening: 2/2 tests, 40 assertions
  • Migration SQL and snapshot checksums independently matched the runtime manifest

Risk checklist

  • No secrets, tokens, .env files, database dumps, or runtime state committed
  • Principal kinds, jobs:read / jobs:write, manual action exposure, and mutation error policies were reviewed at contract and procedure boundaries
  • The unpublished fresh-database baseline, snapshot, manifest hashes, strict constraints, triggers, foreign keys, and bounded query plans are covered by migration tests
  • Claiming, fencing, idempotency, retries, cancellation, event budgets, realtime/audit atomicity, and shutdown ordering have deterministic regression coverage
  • No new package dependency or host/shell/Gateway authority was introduced
  • No UI is included in this backend/worker slice; the browser only learns the lazy jobs/schedules contract modules for the following reader/editor slice

Deployment / operations

  • No deploy or restart is needed; the greenfield stack remains inactive until supervised cutover
  • No configuration or secret changes are needed
  • Rollback before cutover is to revert this PR's commits and rebuild the still-unpublished greenfield baseline/release

Notes for reviewers

  • focus on SQLite transition triggers, claim/resource fencing, schedule cursor semantics, typed retry classification, process completion propagation, and the real bundled web-to-worker smoke path
  • openClawCron.*, the /jobs browser route, cache/metrics/overview, privileged actions, and Gateway-backed work remain deliberately deferred
  • stack base: feat(rewrite): add Phase 3 notification center #400 at locked head 1dc765dfc9753cadfb3cd246a4daa7ae02c3fee3

@mira-2026
mira-2026 requested a review from rajohan as a code owner August 8, 2026 01:42
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mira-2026, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

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: CHILL

Plan: Pro Plus

Run ID: b8b78a28-106f-4f7b-ae36-ff6c60ab277a

📥 Commits

Reviewing files that changed from the base of the PR and between 3b31df4 and 82d74dd.

📒 Files selected for processing (9)
  • greenfield/migrations/20260804022252_dashboard-foundation/migration.sql
  • greenfield/migrations/20260804022252_dashboard-foundation/snapshot.json
  • greenfield/src/server/database/migrations/jobsSchema.test.ts
  • greenfield/src/server/database/schema/jobRuns.ts
  • greenfield/src/server/domains/jobs/coordinator.test.ts
  • greenfield/src/server/domains/jobs/repository.test.ts
  • greenfield/src/server/domains/jobs/repository.ts
  • greenfield/src/server/domains/jobs/service.test.ts
  • greenfield/src/shared/databaseMigrationManifest.ts
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added durable job scheduling and execution with retries, cancellation, resource coordination, worker heartbeats, and progress events.
    • Added APIs to view, run, update, cancel, and pause or resume jobs and schedules.
    • Added realtime updates for job runs, queues, and schedules.
    • Added idempotent manual runs and job read/write capabilities.
    • Added interval, daily, and cron schedules with time-zone validation.
  • Documentation

    • Updated architecture and progress documentation for durable workers and scheduling.
  • Bug Fixes

    • Improved worker startup, shutdown, failure handling, and schedule reconciliation.

Walkthrough

This change adds durable jobs and schedules with SQLite persistence, validated contracts, worker coordination, leases, retries, realtime events, authenticated procedures, application wiring, and production lifecycle verification.

Changes

Durable jobs platform

Layer / File(s) Summary
Job contracts and realtime models
greenfield/src/contracts/*
Adds job, schedule, worker, pagination, authorization, realtime, and validation contracts.
Database schema and validation
greenfield/migrations/*, greenfield/src/server/database/*
Adds durable job tables, triggers, indexes, leases, worker control, row validation, and migration tests.
Repository, service, and schedule operations
greenfield/src/server/domains/jobs/repository.ts, service.ts, records.ts, sideEffects.ts, scheduleTime.ts
Adds transactional storage, schedule reconciliation, manual enqueueing, cancellation, claiming, projections, side effects, and schedule occurrence calculation.
Worker action execution and lifecycle
greenfield/src/server/domains/jobs/coordinator.ts, actionRegistry.ts, workerRuntime.ts, greenfield/src/app/worker.ts
Adds action execution, heartbeats, leases, retries, cancellation, schedule polling, completion tracking, and forced shutdown.
Application and API wiring
greenfield/src/app/*, greenfield/src/server/trpc/*, greenfield/src/server/domains/jobs/routes.ts, greenfield/src/browser/api/*
Injects JobService into request context, mounts jobs and schedules routers, and loads browser procedure contracts.
Production and parity verification
greenfield/src/test/integration/*, greenfield/src/test/parity/*, greenfield/scripts/*, greenfield/docs/*
Verifies bundled worker execution, shutdown behavior, source boundaries, generated documentation, capabilities, and implemented endpoint parity.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DashboardServer
  participant JobService
  participant JobRepository
  participant WorkerRuntime
  participant JobWorkerCoordinator
  participant JobActionRegistry

  DashboardServer->>JobService: reconcile registered schedules
  JobService->>JobRepository: persist schedule records
  WorkerRuntime->>JobWorkerCoordinator: initialize worker
  JobWorkerCoordinator->>JobRepository: claim due run
  JobWorkerCoordinator->>JobActionRegistry: execute registered action
  JobWorkerCoordinator->>JobRepository: settle run and append events
  WorkerRuntime->>JobWorkerCoordinator: dispose with termination signal
Loading

Possibly related PRs

Suggested labels: area: tasks

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main changes: durable jobs and the worker foundation.
Description check ✅ Passed The description covers the required sections and provides concrete behavior, regression coverage, verification results, risks, operations, and reviewer notes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

❤️ Share

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

@mira-2026 mira-2026 added status: needs-review Ready for human or agent review type: feature Adds user-visible functionality type: tests Adds or updates automated tests/coverage type: documentation Documentation, README, comments, and operational notes labels Aug 8, 2026
@mira-2026 mira-2026 changed the title mira/greenfield rewrite phase three jobs feat(rewrite): add durable jobs and worker foundation Aug 8, 2026
@mira-2026 mira-2026 added area: backend Backend API, server routes, services, and integrations area: database Database dashboard, Postgres, PgBouncer, or query views area: ops Operational actions, deploys, services, backups, and health labels Aug 8, 2026
Comment thread greenfield/src/server/domains/jobs/coordinator.ts Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (24)
greenfield/src/contracts/jobModel.ts (1)

505-512: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Parenthesize the mixed relational and equality comparison.

Line 509 reads hasStarted !== run.attemptCount > 0. The relational operator binds first, so the expression is correct. Explicit parentheses remove the ambiguity for readers.

♻️ Proposed readability fix
-        hasStarted !== (run.lastAttemptStartedAtMs !== undefined) ||
-        hasStarted !== run.attemptCount > 0
+        hasStarted !== (run.lastAttemptStartedAtMs !== undefined) ||
+        hasStarted !== run.attemptCount > 0

Apply the same shape as the neighboring comparison:

hasStarted !== (run.attemptCount > 0)
🤖 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/contracts/jobModel.ts` around lines 505 - 512, In the
validation logic around hasStarted, parenthesize the relational comparison in
the mixed expression so it explicitly reads as hasStarted compared with
(run.attemptCount > 0), matching the neighboring comparison’s shape without
changing behavior.
greenfield/src/contracts/jobModel.test.ts (1)

217-264: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add positive cases for draining and stopped workers.

The worker tests assert only one accepted projection (online). All draining and stopped samples are negative. A regression that rejects every non-online worker would pass this suite.

♻️ Suggested extra assertions
+        expect(
+            v.parse(jobWorkerSummarySchema, {
+                activeRunCount: 1,
+                capacity: 2,
+                drainingAtMs: 2500,
+                heartbeatAtMs: 2600,
+                id: workerId,
+                releaseId: "a".repeat(40),
+                startedAtMs: 1000,
+                state: "draining",
+            }).state
+        ).toBe("draining");
+        expect(
+            v.parse(jobWorkerSummarySchema, {
+                activeRunCount: 0,
+                capacity: 2,
+                drainingAtMs: 2500,
+                heartbeatAtMs: 2600,
+                id: workerId,
+                releaseId: "a".repeat(40),
+                startedAtMs: 1000,
+                state: "stopped",
+                stoppedAtMs: 3000,
+            }).state
+        ).toBe("stopped");
🤖 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/contracts/jobModel.test.ts` around lines 217 - 264, Add
accepted test cases in “validates worker and schedule projections across state
boundaries” for both “draining” and “stopped” worker states, using valid field
combinations and asserting successful parsing. Keep the existing negative
boundary cases, including invalid stopped projections, so the suite verifies
non-online states are accepted without weakening validation.
greenfield/src/contracts/jobs.ts (1)

290-310: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared job-domain access and transport policy. Both contract files declare identical queryTransport and mutationTransport objects and identical jobs:read / jobs:write access objects. One policy change must then be edited in two places, and the two files can drift.

  • greenfield/src/contracts/jobs.ts#L290-L310: move jobReadAccess, jobSessionWriteAccess, queryTransport, and mutationTransport into a shared module, for example ./jobAccess.ts, and import them here.
  • greenfield/src/contracts/schedules.ts#L181-L206: import the shared constants instead of redefining them. Keep scheduleRunAccess local, because it intentionally omits principalKinds.
🤖 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/contracts/jobs.ts` around lines 290 - 310, Extract the shared
job-domain constants jobReadAccess, jobSessionWriteAccess, queryTransport, and
mutationTransport from greenfield/src/contracts/jobs.ts lines 290-310 into a
shared module such as ./jobAccess.ts, then import and reuse them in jobs.ts. In
greenfield/src/contracts/schedules.ts lines 181-206, replace the duplicate
access and transport definitions with imports from that module, while keeping
scheduleRunAccess local because its principalKinds behavior differs.
greenfield/src/server/database/schema/jobRuns.ts (1)

121-124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Share the event-budget constants with the repository.

The budget values 1000, 967, and 1048576 are written as literals here, again in the hand-written triggers in the migration file (Lines 1259, 1284, 1291, 1296), and again as named constants in greenfield/src/server/domains/jobs/repository.ts (jobRunEventMaximum, jobRunPayloadEventMaximum, jobPayloadEventByteMaximum). Three copies can drift, and drift shows up only as an aborted transaction at runtime. Import the shared constants here and interpolate them.

🤖 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/server/database/schema/jobRuns.ts` around lines 121 - 124,
Update the job_runs_event_budget_check definition to import and interpolate
jobRunEventMaximum, jobRunPayloadEventMaximum, and jobPayloadEventByteMaximum
from the jobs repository instead of using the literals 1000, 967, and 1048576;
preserve the existing event-count and payload constraints.
greenfield/src/server/database/schema/jobChecks.ts (1)

57-81: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Wrap the OR helpers in parentheses.

optionalJobMessageCheck, optionalJobTerminalCodeCheck, and optionalBoundedJobTextCheck return a top-level OR expression without enclosing parentheses. Every current caller uses them standalone, so the generated SQL is correct today. If a later caller composes one of them with AND, SQLite applies AND before OR and the constraint silently weakens. jobActorCheck already wraps its result on Line 50. Apply the same rule to these three helpers.

♻️ Proposed change
-    return sql`${column} IS NULL OR (${boundedControlSafeTextCheck(column, maximumCodePoints)} AND length(CAST(${column} AS BLOB)) <= ${maximumBytesSql})`;
+    return sql`(${column} IS NULL OR (${boundedControlSafeTextCheck(column, maximumCodePoints)} AND length(CAST(${column} AS BLOB)) <= ${maximumBytesSql}))`;
-    return sql`${column} IS NULL OR (length(${column}) BETWEEN 1 AND ${maximumLengthSql} AND ${nulFreeTextCheck(column)} AND ${column} = lower(${column}) AND substr(${column}, 1, 1) GLOB '[a-z0-9]' AND ${column} NOT GLOB '*[^a-z0-9._/-]*')`;
+    return sql`(${column} IS NULL OR (length(${column}) BETWEEN 1 AND ${maximumLengthSql} AND ${nulFreeTextCheck(column)} AND ${column} = lower(${column}) AND substr(${column}, 1, 1) GLOB '[a-z0-9]' AND ${column} NOT GLOB '*[^a-z0-9._/-]*'))`;
-    return sql`${column} IS NULL OR (${boundedNonBlankTextCheck(column, maximumLength)})`;
+    return sql`(${column} IS NULL OR (${boundedNonBlankTextCheck(column, maximumLength)}))`;

Note: this changes the emitted SQL text, so the migration file and the checksums in greenfield/src/shared/databaseMigrationManifest.ts must be regenerated.

🤖 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/server/database/schema/jobChecks.ts` around lines 57 - 81,
Wrap the complete SQL expressions returned by optionalJobMessageCheck,
optionalJobTerminalCodeCheck, and optionalBoundedJobTextCheck in parentheses,
matching jobActorCheck so future AND composition preserves the intended OR
grouping. Regenerate the migration file and update checksums in
databaseMigrationManifest.ts to reflect the emitted SQL changes.
greenfield/src/server/database/schema/workerInstances.ts (1)

28-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reuse lowercaseHexTextCheck for release_id.

This predicate repeats the lowercase-hex rule that lowercaseHexTextCheck already provides. greenfield/src/server/database/schema/jobRuns.ts Line 119 uses that helper for enqueue_sha256. Use the helper here so both hex columns share one definition.

♻️ Proposed change
 import {
-    nulFreeTextCheck,
+    lowercaseHexTextCheck,
     timestampMillisecondsCheck,
     uuidV7TextCheck,
 } from "./checks.ts";
         check(
             "worker_instances_release_id_check",
-            sql`length(${table.releaseId}) = 40 AND ${nulFreeTextCheck(table.releaseId)} AND ${table.releaseId} = lower(${table.releaseId}) AND ${table.releaseId} NOT GLOB '*[^0-9a-f]*'`
+            lowercaseHexTextCheck(table.releaseId, 40)
         ),

Verify that the helper emits the same SQL text before you regenerate the migration and the manifest checksums.

🤖 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/server/database/schema/workerInstances.ts` around lines 28 -
31, Update the worker_instances release_id constraint to reuse the existing
lowercaseHexTextCheck helper, matching the usage for enqueue_sha256 in
jobRuns.ts, instead of duplicating the lowercase-hex predicate inline. Verify
the helper emits equivalent SQL, then regenerate the migration and manifest
checksums.
greenfield/src/server/database/schema/scheduledJobs.ts (1)

96-99: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Move timeZone validation into the database check.

The application schema already enforces scheduleTimeZoneSchema and rejects values such as US/Eastern, GMT, +01:00, and local. The SQLite check is still the only source of truth for direct SQL writes and can still persist unparseable time_zone values. Add a matching canonical/check constraint or make this check consistent with the contract validation.

🤖 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/server/database/schema/scheduledJobs.ts` around lines 96 - 99,
Update scheduled_jobs_time_zone_check so database validation matches
scheduleTimeZoneSchema rather than only checking length and NUL characters.
Enforce the canonical accepted time-zone format for direct SQL writes, reject
values such as US/Eastern, GMT, +01:00, and local, and preserve the existing
allowance for NULL.
greenfield/src/server/database/validation/jobWorkerControl.ts (1)

71-77: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse nonnegativeDateSchema for the update timestamp.

greenfield/src/server/database/validation/jobDisableIntents.ts line 147 calls nonnegativeDateSchema(v.date()) for the same purpose. Reusing the helper keeps one definition of the timestamp rule and the same error message across tables.

♻️ Proposed fix
-    updatedAt: v.pipe(
-        v.date("Worker control timestamp is invalid"),
-        v.check(
-            (value) => Number.isFinite(value.getTime()) && value.getTime() >= 0,
-            "Worker control timestamp is invalid"
-        )
-    ),
+    updatedAt: nonnegativeDateSchema(v.date("Worker control timestamp is invalid")),
🤖 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/server/database/validation/jobWorkerControl.ts` around lines
71 - 77, Replace the inline validation pipeline for updatedAt with the shared
nonnegativeDateSchema helper, passing v.date() as used by jobDisableIntents.
Preserve the existing field validation while centralizing the nonnegative
timestamp rule and error message.
greenfield/src/server/database/validation/jobRuns.ts (1)

156-161: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add parentheses to the mixed comparison.

hasStarted !== run.attemptCount > 0 parses as hasStarted !== (run.attemptCount > 0) because relational operators bind tighter than equality operators. The behavior is correct, but the expression is hard to read next to the explicit parentheses on the line above.

♻️ Proposed readability fix
     if (
         hasStarted !== (run.lastAttemptStartedAt !== null) ||
-        hasStarted !== run.attemptCount > 0
+        hasStarted !== run.attemptCount > 0 === false
     ) {

Use the explicit form instead:

if (
    hasStarted !== (run.lastAttemptStartedAt !== null) ||
    hasStarted !== (run.attemptCount > 0)
) {
    return false;
}
🤖 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/server/database/validation/jobRuns.ts` around lines 156 - 161,
In the validation condition, update the comparison involving run.attemptCount so
the relational check is explicitly parenthesized as (run.attemptCount > 0),
matching the existing parentheses around run.lastAttemptStartedAt and preserving
behavior.
greenfield/src/server/database/validation/jobDisableIntents.ts (1)

22-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the duplicated actorIsValid helper.

The same function exists in greenfield/src/server/database/validation/jobRuns.ts (lines 43-49), and a two-kind variant exists in greenfield/src/server/database/validation/jobWorkerControl.ts (controlActorIsValid). A shared helper in one validation module keeps the actor-identity rules in one place.

🤖 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/server/database/validation/jobDisableIntents.ts` around lines
22 - 28, Extract the shared actor-identity validation logic from actorIsValid in
jobDisableIntents.ts and the equivalent helper in jobRuns.ts into a common
validation helper, then update both callers to reuse it. Incorporate the
compatible automation, system, and user handling from controlActorIsValid
without changing the existing validation schemas or behavior.
greenfield/src/server/database/validation/rowSchemas.test.ts (1)

540-601: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Negative tests do not pin the expected failure message. Across both test files, several rejection assertions use a bare .toThrow(). Any failure satisfies them, including a failure that the test does not target, such as a renamed column tripping v.strictObject or a foreign-key violation replacing a trigger violation. The shared root cause is one missing expected-message argument on each assertion.

  • greenfield/src/server/database/validation/rowSchemas.test.ts#L540-L601: pass the specific Valibot message to each .toThrow(), for example "Stored job resource keys are not canonical" and "Stored job event count is invalid".
  • greenfield/src/server/database/migrations/jobsSchema.test.ts#L781-L789: pass the expected event-count constraint message, matching the style of the neighboring assertions.
  • greenfield/src/server/database/migrations/jobsSchema.test.ts#L1082-L1087: pass the expected identity-update trigger message for resource_leases.
🤖 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/server/database/validation/rowSchemas.test.ts` around lines
540 - 601, Replace every bare toThrow() in
greenfield/src/server/database/validation/rowSchemas.test.ts lines 540-601 with
the specific expected Valibot message for its validation case, including the
canonical resource-keys and invalid event-count messages. Update the event-count
rejection in greenfield/src/server/database/migrations/jobsSchema.test.ts lines
781-789 with its expected constraint message, and update the resource_leases
identity-update rejection in lines 1082-1087 with the expected trigger message,
matching neighboring assertions.
greenfield/src/server/database/migrations/jobsSchema.test.ts (1)

265-275: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Static analysis flags the interpolated query text here.

The helper builds EXPLAIN QUERY PLAN ${query} by interpolation. The statement text cannot be parameterized, so interpolation is required for the plan prefix. All callers pass literals that are defined in this test file, so there is no injection path. The finding is a false positive for this helper.

The embedded identifiers at Line 1158 and Line 1185 can still be bound as parameters, which removes the pattern that trips the scanner.

🤖 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/server/database/migrations/jobsSchema.test.ts` around lines
265 - 275, Keep the required query interpolation in
expectUsesIndexWithoutTemporarySort, but update the SQL statements containing
embedded identifiers at the two flagged call sites to bind those values as
parameters. Preserve the existing query plans and assertions while removing the
interpolated identifier pattern that triggers static analysis.

Source: Linters/SAST tools

greenfield/src/server/domains/jobs/service.ts (2)

766-778: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Reconciliation emits a realtime event for one schedule id only.

The realtime payload uses jobActionRegistrations[0]?.scheduleId, but reconciliation can insert or update every registered schedule. Subscribers that refresh a single schedule from this event will miss the other rows. Consider emitting one realtime entry per reconciled schedule, or a directory-level identity that clients interpret as "refresh the schedule list".

🤖 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/server/domains/jobs/service.ts` around lines 766 - 778, The
reconciliation side effect currently identifies only the first schedule via
jobActionRegistrations[0]?.scheduleId. Update the realtime payload in
mutationSideEffects for jobs.schedule.reconcile to represent all reconciled
schedules, either by emitting an entry per schedule or by using a
directory-level identity that clients interpret as refreshing the schedule list;
preserve the successful reconciliation metadata.

311-348: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Share one schedule-insert builder with the coordinator.

scheduleInsertShape duplicates scheduleInsert in greenfield/src/server/domains/jobs/coordinator.ts (lines 245-278). The two builders produce the same durable row but differ in the failure mode: this one throws JobValidationError, the coordinator throws RangeError. Any future column addition must be applied twice, and a missed update writes inconsistent schedule rows.

Extract one builder and let the caller map the missing-occurrence case to its own error.

🤖 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/server/domains/jobs/service.ts` around lines 311 - 348,
Extract the shared durable-row construction from scheduleInsertShape and
coordinator’s scheduleInsert into one reusable builder, including all schedule
columns and nextRunAt calculation. Have the builder expose the
missing-occurrence case without choosing a caller-specific error, then let
scheduleInsertShape map it to JobValidationError and the coordinator map it to
RangeError while preserving each caller’s existing behavior.
greenfield/src/server/domains/jobs/repository.ts (3)

972-996: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Bind the supplied queued event to the inserted run.

#insertSuppliedEvent writes input.queuedEvent without checking that queuedEvent.jobRunId equals run.id. A caller mistake writes the event onto a different run, and the run row then reports an event it does not own. Add one guard before the insert.

♻️ Proposed guard
         const inserted = this.#transaction.insert(jobRuns).values(run).returning().get();
         const record = parseRun(requiredRow(inserted, "manual run insert"));
+        if (input.queuedEvent.jobRunId !== record.id) {
+            throw new TypeError("Queued run event must reference the inserted run");
+        }
         this.#insertSuppliedEvent(input.queuedEvent);
🤖 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/server/domains/jobs/repository.ts` around lines 972 - 996, In
enqueueManualRun, validate that input.queuedEvent.jobRunId matches the newly
inserted run.id before calling `#insertSuppliedEvent`; reject the operation on
mismatch so the supplied event can only be written to the run created by this
method.

1064-1070: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Do not return a conflict result after intent rows are written.

Lines 1023-1045 insert or close disable-intent rows before this guarded update. If the guarded update returns no row, this branch returns version-changed, and the caller treats it as a normal conflict. The transaction still commits the intent writes. The immediate-transaction guard at line 1003 makes this branch unreachable today, so this is defensive hardening only. Throw here instead, so the transaction rolls back if the invariant ever breaks.

♻️ Proposed change
         if (row === undefined) {
-            const observed = requiredRow(
-                this.#findScheduleRecord(input.id),
-                "schedule conflict read"
-            );
-            return { kind: "version-changed", schedule: observed };
+            throw new Error("Jobs repository schedule update lost its version guard");
         }
🤖 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/server/domains/jobs/repository.ts` around lines 1064 - 1070,
In the guarded-update handling within the repository method containing the
disable-intent writes, replace the row === undefined branch’s version-changed
return with an exception. Preserve the existing schedule lookup only if needed
for the thrown error, and ensure the exception causes the transaction to roll
back rather than committing intent rows as a normal conflict.

2154-2157: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unnecessary as unknown as cast on database.transaction.

drizzle-orm@1.0.0-rc.4 accepts an optional config argument for the Bun SQLite transaction API, so bind a typed wrapper instead. This keeps the callback and { behavior } option type-checked.

🤖 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/server/domains/jobs/repository.ts` around lines 2154 - 2157,
Update the runTransaction definition around database.transaction to remove the
as unknown as cast and create a typed wrapper that binds database.transaction
while preserving the generic callback and optional { behavior: "deferred" |
"immediate" } configuration typing.
greenfield/src/server/domains/jobs/workerSystem.test.ts (1)

19-30: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Exit early when the run reaches a non-success terminal state.

waitForTerminal only stops on "succeeded" or on the 2000 ms deadline. If the coordinator settles the run as "failed", "cancelled", or "timed-out", the loop keeps polling a state that can no longer change. The test then spends the full deadline before it reports the failure.

Break out as soon as the state is terminal.

♻️ Proposed refactor for a fast, explicit failure
+const terminalStates = new Set(["cancelled", "failed", "succeeded", "timed-out"]);
+
 async function waitForTerminal(
     readState: () => string | undefined
 ): Promise<string | undefined> {
     const deadline = Date.now() + 2000;
     let state = readState();
-    while (state !== "succeeded") {
+    while (state === undefined || !terminalStates.has(state)) {
         if (Date.now() >= deadline) return state;
         await Bun.sleep(2);
         state = readState();
     }
     return state;
 }
🤖 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/server/domains/jobs/workerSystem.test.ts` around lines 19 -
30, Update waitForTerminal so its polling loop exits immediately for any
terminal state, including failed, cancelled, and timed-out, while retaining the
existing deadline handling for undefined or non-terminal states and returning
the observed state.
greenfield/src/server/domains/jobs/coordinator.test.ts (1)

284-292: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the forced-shutdown path.

No test passes a forceSignal to dispose, and coordinatorOptions omits forceDrainMs. As a result waitForActiveExecution always takes the forceSignal === undefined branch in coordinator.ts Line 448.

The forced branch on coordinator.ts Lines 452-474 contains the escalation logic and the forced-drain timeout. That logic is the reason jobWorkerForceDrainMs exists, and it is currently untested.

Add a test that runs a non-cancellable action, calls dispose(controller.signal), and aborts the controller. Set a small forceDrainMs so the test stays fast.

Also applies to: 605-641

🤖 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/server/domains/jobs/coordinator.test.ts` around lines 284 -
292, Add coverage in the coordinator disposal tests for the forced-shutdown
path: configure a small forceDrainMs in coordinatorOptions, run a
non-cancellable action, invoke dispose with an AbortController signal, and abort
the controller to exercise waitForActiveExecution’s forceSignal branch and
timeout escalation. Assert the forced-drain behavior completes as expected while
keeping existing graceful-disposal coverage unchanged.
greenfield/scripts/documentation/jsonSchema.ts (1)

680-690: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Document cron aliases in the generated schema. normalizeScheduleCronExpression runs before scheduleCronExpressionIsValid, so inputs like JAN and MON are accepted and normalized before the published regex pattern checks. Add a $comment noting the monthly and weekday alias forms, or avoid applying this transform rule when the schema should document only accepted raw input.

🤖 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/scripts/documentation/jsonSchema.ts` around lines 680 - 690,
Update the schema generation branch for normalizeScheduleCronExpression so the
generated schedule cron schema documents the accepted JAN–DEC monthly and
SUN–SAT weekday aliases via a $comment alongside the normalized regex pattern.
Preserve the existing transform handling for the other operations.
greenfield/src/server/domains/jobs/coordinator.ts (1)

718-720: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use the explicit try/catch form in heartbeatLoop and scheduleLoop.

claimLoop already uses Effect.tryPromise({ catch: error => error, try: ... }), while the other two use the single-argument form. With Effect 4.0.0-beta.104, the single-argument form wraps Promise rejections in Effect’s error boundary, so completion can reject with different shaped failures depending on the loop. Keep all three loops using the object form so rejects preserve the original error.

🤖 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/server/domains/jobs/coordinator.ts` around lines 718 - 720,
The heartbeatLoop and scheduleLoop definitions use the single-argument
Effect.tryPromise form, unlike claimLoop, causing rejected promises to receive
inconsistent error wrapping. Update both loops to use the explicit object form
with try invoking the existing tracked pass and catch returning the original
error, preserving the current loop behavior.
greenfield/src/server/domains/jobs/workerRuntime.ts (2)

170-173: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Annotate the factory with the DashboardWorkerRuntime return type.

createDashboardWorkerRuntime returns an inferred shape. greenfield/src/app/worker.ts consumes it through the DashboardWorkerRuntime interface in greenfield/src/worker/runtime.ts. An explicit return type documents that contract and reports drift at the definition instead of at the call site.

🤖 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/server/domains/jobs/workerRuntime.ts` around lines 170 - 173,
Annotate the createDashboardWorkerRuntime factory with an explicit
DashboardWorkerRuntime return type, importing or referencing the existing
interface from the worker runtime module as appropriate. Preserve the current
implementation and dependency handling while ensuring the factory is checked
against that contract.

129-144: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Replace the substring test with an explicit action mapping.

input.action.includes("enqueue") infers the realtime operation from an action name. Any future action name that contains enqueue but does not create a run emits created, and realtime consumers then insert instead of update. An explicit set of creating actions keeps the contract stable.

♻️ Proposed refactor
+const runCreatingActions: ReadonlySet<string> = new Set([
+    "jobs.run.enqueue-scheduled",
+]);
+
 export function createSystemJobWorkerSideEffects(
                 realtime: {
                     id: input.targetId,
                     kind: "run",
-                    operation: input.action.includes("enqueue") ? "created" : "updated",
+                    operation: runCreatingActions.has(input.action)
+                        ? "created"
+                        : "updated",
                 },
🤖 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/server/domains/jobs/workerRuntime.ts` around lines 129 - 144,
Update forRun in createJobMutationSideEffects to derive realtime.operation from
an explicit set of known run-creating actions, returning "created" only for
those actions and "updated" for all others. Remove the
input.action.includes("enqueue") substring check and preserve the existing
realtime payload structure.
greenfield/src/app/worker.ts (1)

67-79: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

The default runtime factory drops the structured logger.

createRuntime receives logger but createDashboardWorkerRuntime accepts no logger. The durable loop therefore emits no structured records for claims, retries, lease loss, or drain. Only the process-level runtime.started, runtime.stopped, and runtime.start_failed records exist. Consider passing the logger into the runtime options so worker execution is observable in production.

🤖 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/app/worker.ts` around lines 67 - 79, The createRuntime factory
currently ignores its logger when constructing the dashboard worker runtime.
Update createRuntime and the createDashboardWorkerRuntime options to accept and
pass through the structured logger, preserving it for durable-loop claim, retry,
lease-loss, and drain records.
🤖 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/src/contracts/jobModel.ts`:
- Around line 494-504: Update the validation logic around hasScheduleIdentity so
manual runs are not required to have scheduledJobId and scheduledJobVersion.
Keep schedule runs requiring both schedule identity fields and scheduledForAtMs,
while preserving the existing consistency validation for schedule-backed data.

In `@greenfield/src/contracts/jobRealtime.ts`:
- Around line 101-125: Update each schema in jobRealtimeChangeSchemas to add a
cross-field validation requiring payload.id to equal entityId, while preserving
the existing field validations. In greenfield/src/contracts/jobRealtime.test.ts
lines 96-135, add negative tests covering mismatched entityId and payload.id
values for each change type.

In `@greenfield/src/contracts/jobs.ts`:
- Around line 180-191: Update listRuns to read repository.listRuns(input) and
repository.readQueueState() within the same database transaction, ensuring the
queue summary is validated against a consistent snapshot. Preserve
jobQueueSummaryIsConsistent and jobQueueSummarySchema validation, and use the
repository’s existing transaction mechanism rather than disabling validation.

In `@greenfield/src/server/database/schema/resourceLeases.ts`:
- Line 23: Implement an expiry reaper for resource_leases that removes rows
whose lease expiration has passed, and clear the corresponding lease fields in
job_runs so retry-safe jobs can be claimed again. Reuse the existing resource
lease and job run schema symbols and ensure cleanup is scoped to expired
orphaned leases without affecting active leases.

In `@greenfield/src/server/domains/jobs/coordinator.ts`:
- Around line 524-536: Validate and parse run.payloadJson before registering
lifecycleAbort or creating the timeout in the coordinator flow. Catch
parseJsonText or v.parse failures, settle the run as a non-retryable closed
failure using the existing unknown-action handling path around the action
lookup, and return without propagating the error. Pass the validated parsed
payload into registration.execute instead of parsing it there, while preserving
normal cleanup for valid payloads.
- Around line 922-933: Update the initialization-failure path in dispose,
specifically the catch around initializePromise, to call rejectCompletion with
the normalized coordinator failure before rethrowing it. Preserve the existing
abort behavior and ensure completion is rejected when initialization fails,
including failures from registerWorker or reconcileSchedules.
- Around line 469-474: Update the forced-drain race around execution to create
an AbortController, pass its signal to waitFor(forceDrainMs), and abort it in a
finally block after Promise.race settles. Preserve the timeout error when the
drain timer wins and ensure the timer is cancelled when execution completes
first.

In `@greenfield/src/server/domains/jobs/routes.ts`:
- Around line 29-56: Update runJobEffect so failures from Effect.runPromise are
unwrapped before checking JobNotFoundError, JobConflictError, and
JobValidationError, or move the mappings into the Effect pipeline using
catchTag/cause handling. Preserve the existing tRPC mappings to NOT_FOUND,
CONFLICT, and BAD_REQUEST, and rethrow unrelated failures.

---

Nitpick comments:
In `@greenfield/scripts/documentation/jsonSchema.ts`:
- Around line 680-690: Update the schema generation branch for
normalizeScheduleCronExpression so the generated schedule cron schema documents
the accepted JAN–DEC monthly and SUN–SAT weekday aliases via a $comment
alongside the normalized regex pattern. Preserve the existing transform handling
for the other operations.

In `@greenfield/src/app/worker.ts`:
- Around line 67-79: The createRuntime factory currently ignores its logger when
constructing the dashboard worker runtime. Update createRuntime and the
createDashboardWorkerRuntime options to accept and pass through the structured
logger, preserving it for durable-loop claim, retry, lease-loss, and drain
records.

In `@greenfield/src/contracts/jobModel.test.ts`:
- Around line 217-264: Add accepted test cases in “validates worker and schedule
projections across state boundaries” for both “draining” and “stopped” worker
states, using valid field combinations and asserting successful parsing. Keep
the existing negative boundary cases, including invalid stopped projections, so
the suite verifies non-online states are accepted without weakening validation.

In `@greenfield/src/contracts/jobModel.ts`:
- Around line 505-512: In the validation logic around hasStarted, parenthesize
the relational comparison in the mixed expression so it explicitly reads as
hasStarted compared with (run.attemptCount > 0), matching the neighboring
comparison’s shape without changing behavior.

In `@greenfield/src/contracts/jobs.ts`:
- Around line 290-310: Extract the shared job-domain constants jobReadAccess,
jobSessionWriteAccess, queryTransport, and mutationTransport from
greenfield/src/contracts/jobs.ts lines 290-310 into a shared module such as
./jobAccess.ts, then import and reuse them in jobs.ts. In
greenfield/src/contracts/schedules.ts lines 181-206, replace the duplicate
access and transport definitions with imports from that module, while keeping
scheduleRunAccess local because its principalKinds behavior differs.

In `@greenfield/src/server/database/migrations/jobsSchema.test.ts`:
- Around line 265-275: Keep the required query interpolation in
expectUsesIndexWithoutTemporarySort, but update the SQL statements containing
embedded identifiers at the two flagged call sites to bind those values as
parameters. Preserve the existing query plans and assertions while removing the
interpolated identifier pattern that triggers static analysis.

In `@greenfield/src/server/database/schema/jobChecks.ts`:
- Around line 57-81: Wrap the complete SQL expressions returned by
optionalJobMessageCheck, optionalJobTerminalCodeCheck, and
optionalBoundedJobTextCheck in parentheses, matching jobActorCheck so future AND
composition preserves the intended OR grouping. Regenerate the migration file
and update checksums in databaseMigrationManifest.ts to reflect the emitted SQL
changes.

In `@greenfield/src/server/database/schema/jobRuns.ts`:
- Around line 121-124: Update the job_runs_event_budget_check definition to
import and interpolate jobRunEventMaximum, jobRunPayloadEventMaximum, and
jobPayloadEventByteMaximum from the jobs repository instead of using the
literals 1000, 967, and 1048576; preserve the existing event-count and payload
constraints.

In `@greenfield/src/server/database/schema/scheduledJobs.ts`:
- Around line 96-99: Update scheduled_jobs_time_zone_check so database
validation matches scheduleTimeZoneSchema rather than only checking length and
NUL characters. Enforce the canonical accepted time-zone format for direct SQL
writes, reject values such as US/Eastern, GMT, +01:00, and local, and preserve
the existing allowance for NULL.

In `@greenfield/src/server/database/schema/workerInstances.ts`:
- Around line 28-31: Update the worker_instances release_id constraint to reuse
the existing lowercaseHexTextCheck helper, matching the usage for enqueue_sha256
in jobRuns.ts, instead of duplicating the lowercase-hex predicate inline. Verify
the helper emits equivalent SQL, then regenerate the migration and manifest
checksums.

In `@greenfield/src/server/database/validation/jobDisableIntents.ts`:
- Around line 22-28: Extract the shared actor-identity validation logic from
actorIsValid in jobDisableIntents.ts and the equivalent helper in jobRuns.ts
into a common validation helper, then update both callers to reuse it.
Incorporate the compatible automation, system, and user handling from
controlActorIsValid without changing the existing validation schemas or
behavior.

In `@greenfield/src/server/database/validation/jobRuns.ts`:
- Around line 156-161: In the validation condition, update the comparison
involving run.attemptCount so the relational check is explicitly parenthesized
as (run.attemptCount > 0), matching the existing parentheses around
run.lastAttemptStartedAt and preserving behavior.

In `@greenfield/src/server/database/validation/jobWorkerControl.ts`:
- Around line 71-77: Replace the inline validation pipeline for updatedAt with
the shared nonnegativeDateSchema helper, passing v.date() as used by
jobDisableIntents. Preserve the existing field validation while centralizing the
nonnegative timestamp rule and error message.

In `@greenfield/src/server/database/validation/rowSchemas.test.ts`:
- Around line 540-601: Replace every bare toThrow() in
greenfield/src/server/database/validation/rowSchemas.test.ts lines 540-601 with
the specific expected Valibot message for its validation case, including the
canonical resource-keys and invalid event-count messages. Update the event-count
rejection in greenfield/src/server/database/migrations/jobsSchema.test.ts lines
781-789 with its expected constraint message, and update the resource_leases
identity-update rejection in lines 1082-1087 with the expected trigger message,
matching neighboring assertions.

In `@greenfield/src/server/domains/jobs/coordinator.test.ts`:
- Around line 284-292: Add coverage in the coordinator disposal tests for the
forced-shutdown path: configure a small forceDrainMs in coordinatorOptions, run
a non-cancellable action, invoke dispose with an AbortController signal, and
abort the controller to exercise waitForActiveExecution’s forceSignal branch and
timeout escalation. Assert the forced-drain behavior completes as expected while
keeping existing graceful-disposal coverage unchanged.

In `@greenfield/src/server/domains/jobs/coordinator.ts`:
- Around line 718-720: The heartbeatLoop and scheduleLoop definitions use the
single-argument Effect.tryPromise form, unlike claimLoop, causing rejected
promises to receive inconsistent error wrapping. Update both loops to use the
explicit object form with try invoking the existing tracked pass and catch
returning the original error, preserving the current loop behavior.

In `@greenfield/src/server/domains/jobs/repository.ts`:
- Around line 972-996: In enqueueManualRun, validate that
input.queuedEvent.jobRunId matches the newly inserted run.id before calling
`#insertSuppliedEvent`; reject the operation on mismatch so the supplied event can
only be written to the run created by this method.
- Around line 1064-1070: In the guarded-update handling within the repository
method containing the disable-intent writes, replace the row === undefined
branch’s version-changed return with an exception. Preserve the existing
schedule lookup only if needed for the thrown error, and ensure the exception
causes the transaction to roll back rather than committing intent rows as a
normal conflict.
- Around line 2154-2157: Update the runTransaction definition around
database.transaction to remove the as unknown as cast and create a typed wrapper
that binds database.transaction while preserving the generic callback and
optional { behavior: "deferred" | "immediate" } configuration typing.

In `@greenfield/src/server/domains/jobs/service.ts`:
- Around line 766-778: The reconciliation side effect currently identifies only
the first schedule via jobActionRegistrations[0]?.scheduleId. Update the
realtime payload in mutationSideEffects for jobs.schedule.reconcile to represent
all reconciled schedules, either by emitting an entry per schedule or by using a
directory-level identity that clients interpret as refreshing the schedule list;
preserve the successful reconciliation metadata.
- Around line 311-348: Extract the shared durable-row construction from
scheduleInsertShape and coordinator’s scheduleInsert into one reusable builder,
including all schedule columns and nextRunAt calculation. Have the builder
expose the missing-occurrence case without choosing a caller-specific error,
then let scheduleInsertShape map it to JobValidationError and the coordinator
map it to RangeError while preserving each caller’s existing behavior.

In `@greenfield/src/server/domains/jobs/workerRuntime.ts`:
- Around line 170-173: Annotate the createDashboardWorkerRuntime factory with an
explicit DashboardWorkerRuntime return type, importing or referencing the
existing interface from the worker runtime module as appropriate. Preserve the
current implementation and dependency handling while ensuring the factory is
checked against that contract.
- Around line 129-144: Update forRun in createJobMutationSideEffects to derive
realtime.operation from an explicit set of known run-creating actions, returning
"created" only for those actions and "updated" for all others. Remove the
input.action.includes("enqueue") substring check and preserve the existing
realtime payload structure.

In `@greenfield/src/server/domains/jobs/workerSystem.test.ts`:
- Around line 19-30: Update waitForTerminal so its polling loop exits
immediately for any terminal state, including failed, cancelled, and timed-out,
while retaining the existing deadline handling for undefined or non-terminal
states and returning the observed state.
🪄 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: 79e0eae7-c561-4eea-a8c7-371905d76128

📥 Commits

Reviewing files that changed from the base of the PR and between 1dc765d and 3192837.

⛔ Files ignored due to path filters (31)
  • greenfield/docs/generated/procedures.md is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/realtime-events.md is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/automationSecurity.createPrincipal.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/automationSecurity.createPrincipal.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/automationSecurity.disablePrincipal.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/automationSecurity.listPrincipals.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/automationSecurity.replaceCapabilities.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/automationSecurity.replaceCapabilities.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/events.stream.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/events.stream.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/jobs.cancelRun.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/jobs.cancelRun.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/jobs.getRun.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/jobs.getRun.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/jobs.listRuns.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/jobs.listRuns.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/jobs.runs.realtime.payload.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/jobs.setClaimingPaused.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/jobs.setClaimingPaused.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.get.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.get.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.list.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.list.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.listRuns.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.listRuns.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.records.realtime.payload.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.run.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.run.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.update.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.update.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/securityAudit.listEvents.output.schema.json is excluded by !**/generated/** and included by **/*
📒 Files selected for processing (85)
  • greenfield/docs/architecture/greenfield-rewrite/application-architecture.md
  • greenfield/docs/architecture/greenfield-rewrite/data-and-security.md
  • greenfield/docs/architecture/greenfield-rewrite/progress.md
  • greenfield/docs/architecture/greenfield-rewrite/runtime-and-delivery.md
  • greenfield/migrations/20260804022252_dashboard-foundation/migration.sql
  • greenfield/migrations/20260804022252_dashboard-foundation/snapshot.json
  • greenfield/scripts/documentation/artifacts.test.ts
  • greenfield/scripts/documentation/jsonSchema.test.ts
  • greenfield/scripts/documentation/jsonSchema.ts
  • greenfield/scripts/sourceBoundaries/externalAuthorityPolicy.ts
  • greenfield/scripts/sourceBoundaries/policy.test.ts
  • greenfield/scripts/sourceBoundaries/sourceTopologyPolicy.ts
  • greenfield/src/app/dashboardServer.test.ts
  • greenfield/src/app/dashboardServer.ts
  • greenfield/src/app/server.ts
  • greenfield/src/app/trpcHttpHandler.ts
  • greenfield/src/app/worker.test.ts
  • greenfield/src/app/worker.ts
  • greenfield/src/browser/api/trpcClient.test.ts
  • greenfield/src/browser/api/trpcClient.ts
  • greenfield/src/contracts/contractRegistry.ts
  • greenfield/src/contracts/events.test.ts
  • greenfield/src/contracts/events.ts
  • greenfield/src/contracts/jobModel.test.ts
  • greenfield/src/contracts/jobModel.ts
  • greenfield/src/contracts/jobRealtime.test.ts
  • greenfield/src/contracts/jobRealtime.ts
  • greenfield/src/contracts/jobs.test.ts
  • greenfield/src/contracts/jobs.ts
  • greenfield/src/contracts/schedules.test.ts
  • greenfield/src/contracts/schedules.ts
  • greenfield/src/contracts/security.test.ts
  • greenfield/src/contracts/security.ts
  • greenfield/src/server/database/migrations/jobsSchema.test.ts
  • greenfield/src/server/database/migrations/migrationGraph.test.ts
  • greenfield/src/server/database/schema/automationPrincipalCapabilities.ts
  • greenfield/src/server/database/schema/drizzleSchema.ts
  • greenfield/src/server/database/schema/jobChecks.ts
  • greenfield/src/server/database/schema/jobDisableIntents.ts
  • greenfield/src/server/database/schema/jobRunEvents.ts
  • greenfield/src/server/database/schema/jobRuns.ts
  • greenfield/src/server/database/schema/jobWorkerControl.ts
  • greenfield/src/server/database/schema/resourceLeases.ts
  • greenfield/src/server/database/schema/scheduledJobs.ts
  • greenfield/src/server/database/schema/workerInstances.ts
  • greenfield/src/server/database/validation/jobDisableIntents.ts
  • greenfield/src/server/database/validation/jobRunEvents.ts
  • greenfield/src/server/database/validation/jobRuns.ts
  • greenfield/src/server/database/validation/jobWorkerControl.ts
  • greenfield/src/server/database/validation/resourceLeases.ts
  • greenfield/src/server/database/validation/rowSchemas.test.ts
  • greenfield/src/server/database/validation/scheduledJobs.ts
  • greenfield/src/server/database/validation/workerInstances.ts
  • greenfield/src/server/domains/jobs/actionRegistry.test.ts
  • greenfield/src/server/domains/jobs/actionRegistry.ts
  • greenfield/src/server/domains/jobs/coordinator.test.ts
  • greenfield/src/server/domains/jobs/coordinator.ts
  • greenfield/src/server/domains/jobs/errors.ts
  • greenfield/src/server/domains/jobs/procedures.test.ts
  • greenfield/src/server/domains/jobs/procedures.ts
  • greenfield/src/server/domains/jobs/records.ts
  • greenfield/src/server/domains/jobs/repository.test.ts
  • greenfield/src/server/domains/jobs/repository.ts
  • greenfield/src/server/domains/jobs/routes.ts
  • greenfield/src/server/domains/jobs/scheduleTime.test.ts
  • greenfield/src/server/domains/jobs/scheduleTime.ts
  • greenfield/src/server/domains/jobs/service.test.ts
  • greenfield/src/server/domains/jobs/service.ts
  • greenfield/src/server/domains/jobs/sideEffects.test.ts
  • greenfield/src/server/domains/jobs/sideEffects.ts
  • greenfield/src/server/domains/jobs/testSupport/service.ts
  • greenfield/src/server/domains/jobs/workerRuntime.test.ts
  • greenfield/src/server/domains/jobs/workerRuntime.ts
  • greenfield/src/server/domains/jobs/workerSystem.test.ts
  • greenfield/src/server/test/support/requestContext.ts
  • greenfield/src/server/test/system/serverGatewayCredentialVerification.test.ts
  • greenfield/src/server/trpc/appRouter.ts
  • greenfield/src/server/trpc/context.test.ts
  • greenfield/src/server/trpc/context.ts
  • greenfield/src/server/trpc/procedureErrorPolicy.ts
  • greenfield/src/shared/databaseMigrationManifest.ts
  • greenfield/src/test/integration/delivery/productionReleaseLifecycle.test.ts
  • greenfield/src/test/parity/fixtures/greenfield-contracts.json
  • greenfield/src/test/parity/fixtures/legacy-endpoints.json
  • greenfield/src/worker/runtime.ts

Comment thread greenfield/src/contracts/jobModel.ts
Comment thread greenfield/src/contracts/jobRealtime.ts Outdated
Comment thread greenfield/src/contracts/jobs.ts
Comment thread greenfield/src/server/database/schema/resourceLeases.ts
Comment thread greenfield/src/server/domains/jobs/coordinator.ts Outdated
Comment thread greenfield/src/server/domains/jobs/coordinator.ts
Comment thread greenfield/src/server/domains/jobs/coordinator.ts
Comment thread greenfield/src/server/domains/jobs/routes.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 319283756a

ℹ️ 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".

Comment thread greenfield/src/server/domains/jobs/service.ts Outdated
Comment thread greenfield/src/server/domains/jobs/repository.ts Outdated
Comment thread greenfield/src/server/domains/jobs/repository.ts Outdated
@mira-2026

mira-2026 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

Review follow-up is pushed in cecdfaf, with the final incremental review fixes in af5b7bc.

  • All three Codex P2 findings and every valid inline CodeRabbit finding are fixed with regressions.
  • CodeRabbit nitpicks were applied where they improved correctness, reuse, validation, or test clarity.
  • Two non-actionable suggestions remain intentionally unchanged: worker logger wiring needs a reviewed structured worker-event/observer boundary rather than an unused pass-through, and the localized Drizzle rc.4 transaction cast is retained with an explanation because the cast-free generic alternative spreads conditional return types through the repository.
  • Final local verification: Bun 1498/1498, browser 142/142, final focused review regressions 37/37, both typechecks, lint, format, docs, database drift, source boundaries, and diff checks are green. An independent read-only audit found no remaining actionable issues before the final test-only review follow-up.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
greenfield/src/server/database/validation/jobActors.ts (1)

15-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Make the system branch explicit.

The final return handles system implicitly. If the kind union gains a new member later, the new kind silently validates against jobActionKeySchema and passes. An explicit branch with a closing return false fails closed instead.

The reuse of jobActionKeySchema for system actor ids also reads as accidental. The explicit branch documents that the shape is intentionally the same as the SQL job_runs_requested_actor_check system rule.

♻️ Proposed change
     if (kind === "user") return v.safeParse(securityRecordIdSchema, id).success;
-    return v.safeParse(jobActionKeySchema, id).success;
+    // System actor ids share the bounded lowercase key shape used by action keys.
+    if (kind === "system") return v.safeParse(jobActionKeySchema, id).success;
+    return false;
 }
🤖 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/server/database/validation/jobActors.ts` around lines 15 - 24,
Update jobActorIdentityIsValid to add an explicit kind === "system" branch that
validates with jobActionKeySchema, then replace the implicit final validation
with return false so future kind values fail closed.
greenfield/src/contracts/jobRealtime.ts (1)

137-159: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the repeated identity-check wrapper.

The same v.pipe(..., v.check(...)) wrapper with the same message repeats three times. A small helper removes the repetition and keeps the message defined once.

♻️ Proposed helper
+const identityMessage = "Job realtime entity identity is inconsistent";
+
+function withMatchingIdentity<
+    TSchema extends v.GenericSchema<
+        unknown,
+        { readonly entityId: string; readonly payload: { readonly id: string } }
+    >,
+>(schema: TSchema) {
+    return v.pipe(
+        schema,
+        v.check<v.InferOutput<TSchema>, typeof identityMessage>(
+            jobRealtimeIdentityMatches,
+            identityMessage
+        )
+    );
+}
+
 /** Topic-specific client change schemas built from the producer routing policy. */
 export const jobRealtimeChangeSchemas = [
-    v.pipe(
-        jobRunRealtimeChangeObjectSchema,
-        v.check<
-            v.InferOutput<typeof jobRunRealtimeChangeObjectSchema>,
-            "Job realtime entity identity is inconsistent"
-        >(jobRealtimeIdentityMatches, "Job realtime entity identity is inconsistent")
-    ),
+    withMatchingIdentity(jobRunRealtimeChangeObjectSchema),
🤖 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/contracts/jobRealtime.ts` around lines 137 - 159, Extract the
repeated v.pipe/v.check identity-validation pattern from
jobRealtimeChangeSchemas into a small helper that accepts a realtime change
schema and applies jobRealtimeIdentityMatches with the shared inconsistency
message. Replace all three inline wrappers with calls to this helper, preserving
the existing schema order and validation behavior.
greenfield/src/server/domains/jobs/service.test.ts (1)

213-219: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Strengthen the pre-change cursor assertion.

Line 214 asserts that enabled.nextRunAtMs is not the post-change value. The assertion passes when enabled.nextRunAtMs is undefined, so it does not prove that the enabled schedule carried the original 60-second cadence cursor. Assert the expected original value instead.

🤖 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/server/domains/jobs/service.test.ts` around lines 213 - 219,
Update the enabled schedule assertion in the relevant test to verify
enabled.nextRunAtMs equals the original 60-second cadence timestamp based on
authenticationTestNow, rather than merely asserting it differs from the
post-change value; keep the existing repository nextRunAt assertion unchanged.
🤖 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/src/server/domains/jobs/repository.test.ts`:
- Around line 170-181: Sequence the rollback state assertion after the rejected
enqueue operation has settled: retain the existing non-awaited rejects matcher
for repository.enqueueManualRun, then await a separate promise or otherwise
synchronize on the same operation before calling repository.findRun. Ensure
findRun verifies the post-rollback state rather than observing the database
before the deferred write executes.

---

Nitpick comments:
In `@greenfield/src/contracts/jobRealtime.ts`:
- Around line 137-159: Extract the repeated v.pipe/v.check identity-validation
pattern from jobRealtimeChangeSchemas into a small helper that accepts a
realtime change schema and applies jobRealtimeIdentityMatches with the shared
inconsistency message. Replace all three inline wrappers with calls to this
helper, preserving the existing schema order and validation behavior.

In `@greenfield/src/server/database/validation/jobActors.ts`:
- Around line 15-24: Update jobActorIdentityIsValid to add an explicit kind ===
"system" branch that validates with jobActionKeySchema, then replace the
implicit final validation with return false so future kind values fail closed.

In `@greenfield/src/server/domains/jobs/service.test.ts`:
- Around line 213-219: Update the enabled schedule assertion in the relevant
test to verify enabled.nextRunAtMs equals the original 60-second cadence
timestamp based on authenticationTestNow, rather than merely asserting it
differs from the post-change value; keep the existing repository nextRunAt
assertion unchanged.
🪄 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: ca07075d-6b78-4fae-806e-ed3dd7f94d96

📥 Commits

Reviewing files that changed from the base of the PR and between 3192837 and cecdfaf.

⛔ Files ignored due to path filters (5)
  • greenfield/docs/generated/schemas/events.stream.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.get.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.list.output.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.update.input.schema.json is excluded by !**/generated/** and included by **/*
  • greenfield/docs/generated/schemas/schedules.update.output.schema.json is excluded by !**/generated/** and included by **/*
📒 Files selected for processing (40)
  • greenfield/migrations/20260804022252_dashboard-foundation/migration.sql
  • greenfield/migrations/20260804022252_dashboard-foundation/snapshot.json
  • greenfield/scripts/documentation/artifacts.test.ts
  • greenfield/scripts/documentation/jsonSchema.test.ts
  • greenfield/scripts/documentation/jsonSchema.ts
  • greenfield/src/contracts/events.test.ts
  • greenfield/src/contracts/events.ts
  • greenfield/src/contracts/jobModel.test.ts
  • greenfield/src/contracts/jobModel.ts
  • greenfield/src/contracts/jobProcedurePolicies.ts
  • greenfield/src/contracts/jobRealtime.test.ts
  • greenfield/src/contracts/jobRealtime.ts
  • greenfield/src/contracts/jobs.ts
  • greenfield/src/contracts/scheduleTimeZones.ts
  • greenfield/src/contracts/schedules.ts
  • greenfield/src/server/database/migrations/jobsSchema.test.ts
  • greenfield/src/server/database/schema/checks.ts
  • greenfield/src/server/database/schema/jobChecks.ts
  • greenfield/src/server/database/schema/jobRuns.ts
  • greenfield/src/server/database/schema/scheduledJobs.ts
  • greenfield/src/server/database/schema/workerInstances.ts
  • greenfield/src/server/database/validation/jobActors.ts
  • greenfield/src/server/database/validation/jobDisableIntents.ts
  • greenfield/src/server/database/validation/jobRuns.ts
  • greenfield/src/server/database/validation/jobWorkerControl.ts
  • greenfield/src/server/database/validation/rowSchemas.test.ts
  • greenfield/src/server/database/validation/scalars.ts
  • greenfield/src/server/domains/jobs/coordinator.test.ts
  • greenfield/src/server/domains/jobs/coordinator.ts
  • greenfield/src/server/domains/jobs/registeredSchedule.ts
  • greenfield/src/server/domains/jobs/repository.test.ts
  • greenfield/src/server/domains/jobs/repository.ts
  • greenfield/src/server/domains/jobs/service.test.ts
  • greenfield/src/server/domains/jobs/service.ts
  • greenfield/src/server/domains/jobs/workerRuntime.test.ts
  • greenfield/src/server/domains/jobs/workerRuntime.ts
  • greenfield/src/server/domains/jobs/workerSystem.test.ts
  • greenfield/src/shared/databaseMigrationManifest.ts
  • greenfield/src/shared/workerRuntime.ts
  • greenfield/src/worker/runtime.ts
🚧 Files skipped from review as they are similar to previous changes (21)
  • greenfield/src/shared/databaseMigrationManifest.ts
  • greenfield/src/contracts/jobRealtime.test.ts
  • greenfield/scripts/documentation/artifacts.test.ts
  • greenfield/src/server/database/validation/rowSchemas.test.ts
  • greenfield/src/server/database/schema/workerInstances.ts
  • greenfield/src/server/database/validation/jobDisableIntents.ts
  • greenfield/src/server/database/migrations/jobsSchema.test.ts
  • greenfield/src/server/database/schema/jobRuns.ts
  • greenfield/src/server/database/validation/jobWorkerControl.ts
  • greenfield/src/contracts/schedules.ts
  • greenfield/scripts/documentation/jsonSchema.ts
  • greenfield/src/server/database/validation/jobRuns.ts
  • greenfield/src/contracts/events.ts
  • greenfield/src/server/domains/jobs/workerRuntime.ts
  • greenfield/src/contracts/jobModel.test.ts
  • greenfield/src/server/domains/jobs/service.ts
  • greenfield/src/contracts/jobs.ts
  • greenfield/src/server/database/schema/jobChecks.ts
  • greenfield/src/contracts/jobModel.ts
  • greenfield/src/server/domains/jobs/coordinator.ts
  • greenfield/src/server/domains/jobs/repository.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: dashboard-checks
  • GitHub Check: frontend-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/server/database/schema/checks.ts
  • greenfield/src/contracts/jobProcedurePolicies.ts
  • greenfield/src/server/domains/jobs/registeredSchedule.ts
  • greenfield/src/shared/workerRuntime.ts
  • greenfield/src/server/database/validation/scalars.ts
  • greenfield/src/worker/runtime.ts
  • greenfield/src/contracts/scheduleTimeZones.ts
  • greenfield/scripts/documentation/jsonSchema.test.ts
  • greenfield/src/server/domains/jobs/workerSystem.test.ts
  • greenfield/src/server/database/schema/scheduledJobs.ts
  • greenfield/src/contracts/events.test.ts
  • greenfield/src/server/database/validation/jobActors.ts
  • greenfield/src/server/domains/jobs/repository.test.ts
  • greenfield/src/server/domains/jobs/workerRuntime.test.ts
  • greenfield/src/server/domains/jobs/service.test.ts
  • greenfield/src/server/domains/jobs/coordinator.test.ts
  • greenfield/src/contracts/jobRealtime.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/scripts/documentation/jsonSchema.test.ts
  • greenfield/src/server/domains/jobs/workerSystem.test.ts
  • greenfield/src/contracts/events.test.ts
  • greenfield/src/server/domains/jobs/repository.test.ts
  • greenfield/src/server/domains/jobs/workerRuntime.test.ts
  • greenfield/src/server/domains/jobs/service.test.ts
  • greenfield/src/server/domains/jobs/coordinator.test.ts
🔇 Additional comments (20)
greenfield/src/server/domains/jobs/workerRuntime.test.ts (1)

116-138: LGTM!

greenfield/src/server/domains/jobs/coordinator.test.ts (2)

1-1: LGTM!

Also applies to: 145-160, 162-271, 330-375


698-857: LGTM!

Also applies to: 890-892, 905-907

greenfield/src/shared/workerRuntime.ts (1)

1-6: LGTM!

greenfield/src/worker/runtime.ts (1)

1-1: 🩺 Stability & Availability

No change needed.

greenfield/src/worker/runtime.ts is a type-only re-export, and current importers use type-only imports.

greenfield/scripts/documentation/jsonSchema.test.ts (1)

26-27: LGTM!

Also applies to: 106-129

greenfield/src/contracts/events.test.ts (2)

8-8: LGTM!

Also applies to: 22-22


94-128: 🎯 Functional Correctness

No change needed. The matching variants parse successfully in greenfield/src/contracts/jobRealtime.test.ts, and this test covers only the mismatched entities.

greenfield/src/contracts/jobProcedurePolicies.ts (1)

1-28: LGTM!

greenfield/src/contracts/scheduleTimeZones.ts (1)

1-453: LGTM!

greenfield/src/contracts/jobRealtime.ts (1)

100-109: LGTM!

greenfield/migrations/20260804022252_dashboard-foundation/migration.sql (2)

616-634: LGTM!

Also applies to: 636-705, 740-782, 784-800


929-951: 🗄️ Data Integrity & Integration

No change needed. The job_disable_intents_active_schedule_unique index and the single-intent update path prevent multiple non-ended intents for one scheduled job, so the replacement exemption cannot match multiple active intents.

greenfield/src/server/database/schema/checks.ts (1)

23-26: LGTM!

greenfield/src/server/database/schema/scheduledJobs.ts (1)

12-16: LGTM!

Also applies to: 103-106

greenfield/src/server/database/validation/scalars.ts (1)

53-57: LGTM!

greenfield/src/server/domains/jobs/workerSystem.test.ts (1)

18-31: LGTM!

Also applies to: 34-45

greenfield/src/server/domains/jobs/repository.test.ts (1)

450-531: LGTM!

greenfield/src/server/domains/jobs/registeredSchedule.ts (1)

11-43: LGTM!

greenfield/src/server/domains/jobs/service.test.ts (1)

141-165: LGTM!

Also applies to: 225-293, 347-407

Comment thread greenfield/src/server/domains/jobs/repository.test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: af5b7bca3f

ℹ️ 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".

Comment thread greenfield/src/server/domains/jobs/repository.ts Outdated
Comment thread greenfield/src/server/domains/jobs/repository.ts
Comment thread greenfield/src/server/domains/jobs/repository.ts Outdated
Comment thread greenfield/src/app/worker.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
greenfield/src/server/domains/jobs/repository.ts (1)

297-312: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Handle the new left-disabled result before continuing the schedule pass.

expireDisableIntents() can now return kind: "left-disabled", but the coordinator only branches on next-occurrence-unavailable and continues to listDueSchedules. That can still attempt to schedule the same disabled intent again or treat it the same as a successfully re-enabled/disabled-to-expired path. Add an explicit left-disabled branch that preserves the disabled schedule state.

🤖 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/server/domains/jobs/repository.ts` around lines 297 - 312, The
coordinator handling expireDisableIntents() must explicitly handle the
"left-disabled" result before continuing to listDueSchedules. Add a branch keyed
on ExpireDisableIntentResult.kind that preserves the returned disabled schedule
state and prevents it from being processed as re-enabled or passed through the
normal scheduling path.
🧹 Nitpick comments (4)
greenfield/src/server/domains/jobs/service.ts (2)

543-545: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Read only the worker control row here.

readQueueState runs the state-count aggregate, the distinct resource-class query, the worker query, and the active-count query. This path needs only control. Add a narrow readWorkerControl reader to the repository and call it here. The pause and resume mutations then avoid four unnecessary queries.

🤖 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/server/domains/jobs/service.ts` around lines 543 - 545,
Replace the readQueueState call in the job service flow with a narrow
repository.readWorkerControl reader that accepts the existing minimumHeartbeatAt
filter and returns only the control row. Implement readWorkerControl in the
repository using only the worker control query, then preserve the existing
.control consumers and pause/resume behavior.

441-450: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Separate the registry check from the exposure check.

Lines 441-444 collapse two distinct causes into one reason. A schedule that is no longer in the code-owned registry now reports action-not-manually-exposed. updateSchedule reports action-unavailable for the same registry condition at Line 583. Split the branches so both procedures report the same reason for the same cause.

♻️ Proposed change
-                if (
-                    !isRegisteredJobSchedule(schedule.id, schedule.actionKey) ||
-                    registration?.manualExposure !== "jobs-write"
-                ) {
+                if (!isRegisteredJobSchedule(schedule.id, schedule.actionKey)) {
+                    throw new JobConflictError({
+                        id: input.id,
+                        reason: "action-unavailable",
+                        resource: "schedule",
+                    });
+                }
+                if (registration?.manualExposure !== "jobs-write") {
                     throw new JobConflictError({
                         id: input.id,
                         reason: "action-not-manually-exposed",
                         resource: "schedule",
                     });
                 }

Note: this changes an observable error reason. Update greenfield/src/server/domains/jobs/service.test.ts Lines 259-266 if you apply it.

🤖 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/server/domains/jobs/service.ts` around lines 441 - 450, Split
the combined condition in the schedule validation around isRegisteredJobSchedule
into separate branches: throw the registry-specific action-unavailable reason
when the schedule is not registered, and retain action-not-manually-exposed only
for registered schedules whose manualExposure is not jobs-write. Update the
corresponding service test expectation in service.test.ts.
greenfield/src/server/domains/jobs/repository.test.ts (1)

970-978: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Derive the stale-worker count from the summary bound.

The loop registers 33 stale workers. That number only proves "filter before limit" while workerSummaryMaximum stays at 32. If the constant changes, the test still passes but no longer exercises the bound. Import the constant and size the loop from it, so the intent survives a limit change.

🤖 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/server/domains/jobs/repository.test.ts` around lines 970 -
978, Update the stale-worker registration loop around workerSummaryMaximum so
its iteration count is derived from the imported summary limit rather than the
hardcoded 33-worker range. Import and use workerSummaryMaximum, registering one
more stale worker than the limit to preserve the filter-before-limit assertion
when the bound changes.
greenfield/src/server/domains/jobs/repository.ts (1)

1514-1550: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared intent-closure block.

Lines 1520-1541 and Lines 1569-1590 perform the same guarded jobDisableIntents update, the same v.parse closure payload, and the same requiredRow handling. Only the failure label differs. Extract one private helper so the fencing predicate cannot drift between the two expiry paths.

♻️ Proposed helper
`#closeExpiredIntent`(
    intent: JobDisableIntentRecord,
    transitionAt: Date,
    systemActorId: string,
    context: string
): JobDisableIntentRecord {
    const row = this.#transaction
        .update(jobDisableIntents)
        .set(
            v.parse(jobDisableIntentCloseSchema, {
                endedAt: transitionAt,
                endedById: systemActorId,
                endedByKind: "system",
                endedReason: "expired",
            })
        )
        .where(
            and(
                eq(jobDisableIntents.id, intent.id),
                isNull(jobDisableIntents.endedAt),
                lte(jobDisableIntents.expiresAt, transitionAt)
            )
        )
        .returning()
        .get();
    return parseDisableIntent(requiredRow(row, context));
}

Note: keep the original input.at bound in the predicate if you extract it; pass it explicitly rather than reusing transitionAt.

🤖 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/server/domains/jobs/repository.ts` around lines 1514 - 1550,
Extract the duplicated jobDisableIntents closure logic into a private
`#closeExpiredIntent` helper, including the parsed closure payload, guarded
update, and requiredRow/parseDisableIntent handling. Update both expiry paths to
call it with their transition time, system actor ID, and distinct failure
context; preserve input.at as the fencing predicate bound rather than
substituting transitionAt.
🤖 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/src/server/domains/jobs/repository.test.ts`:
- Around line 258-284: Sequence the assertions in the reconcileSchedules test by
storing its returned promise, asserting rejection with
expect(...).rejects.toThrow() without await, and only then reading
findSchedule("system.worker-smoke"). Follow the established pattern from the
test block around lines 175-186 so the deferred transaction settles before
validating the unchanged schedule state.

---

Outside diff comments:
In `@greenfield/src/server/domains/jobs/repository.ts`:
- Around line 297-312: The coordinator handling expireDisableIntents() must
explicitly handle the "left-disabled" result before continuing to
listDueSchedules. Add a branch keyed on ExpireDisableIntentResult.kind that
preserves the returned disabled schedule state and prevents it from being
processed as re-enabled or passed through the normal scheduling path.

---

Nitpick comments:
In `@greenfield/src/server/domains/jobs/repository.test.ts`:
- Around line 970-978: Update the stale-worker registration loop around
workerSummaryMaximum so its iteration count is derived from the imported summary
limit rather than the hardcoded 33-worker range. Import and use
workerSummaryMaximum, registering one more stale worker than the limit to
preserve the filter-before-limit assertion when the bound changes.

In `@greenfield/src/server/domains/jobs/repository.ts`:
- Around line 1514-1550: Extract the duplicated jobDisableIntents closure logic
into a private `#closeExpiredIntent` helper, including the parsed closure payload,
guarded update, and requiredRow/parseDisableIntent handling. Update both expiry
paths to call it with their transition time, system actor ID, and distinct
failure context; preserve input.at as the fencing predicate bound rather than
substituting transitionAt.

In `@greenfield/src/server/domains/jobs/service.ts`:
- Around line 543-545: Replace the readQueueState call in the job service flow
with a narrow repository.readWorkerControl reader that accepts the existing
minimumHeartbeatAt filter and returns only the control row. Implement
readWorkerControl in the repository using only the worker control query, then
preserve the existing .control consumers and pause/resume behavior.
- Around line 441-450: Split the combined condition in the schedule validation
around isRegisteredJobSchedule into separate branches: throw the
registry-specific action-unavailable reason when the schedule is not registered,
and retain action-not-manually-exposed only for registered schedules whose
manualExposure is not jobs-write. Update the corresponding service test
expectation in service.test.ts.
🪄 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: 97224325-908e-4d30-a88c-ff81284e735f

📥 Commits

Reviewing files that changed from the base of the PR and between cecdfaf and 3e1fae4.

📒 Files selected for processing (18)
  • greenfield/src/app/worker.test.ts
  • greenfield/src/app/worker.ts
  • greenfield/src/contracts/jobModel.ts
  • greenfield/src/contracts/jobRealtime.ts
  • greenfield/src/server/database/validation/jobActors.ts
  • greenfield/src/server/domains/jobs/actionRegistry.test.ts
  • greenfield/src/server/domains/jobs/actionRegistry.ts
  • greenfield/src/server/domains/jobs/coordinator.test.ts
  • greenfield/src/server/domains/jobs/coordinator.ts
  • greenfield/src/server/domains/jobs/errors.ts
  • greenfield/src/server/domains/jobs/repository.test.ts
  • greenfield/src/server/domains/jobs/repository.ts
  • greenfield/src/server/domains/jobs/service.test.ts
  • greenfield/src/server/domains/jobs/service.ts
  • greenfield/src/server/domains/jobs/sideEffects.test.ts
  • greenfield/src/server/domains/jobs/sideEffects.ts
  • greenfield/src/server/domains/jobs/workerRuntime.test.ts
  • greenfield/src/server/domains/jobs/workerRuntime.ts
🚧 Files skipped from review as they are similar to previous changes (11)
  • greenfield/src/server/domains/jobs/actionRegistry.test.ts
  • greenfield/src/server/database/validation/jobActors.ts
  • greenfield/src/server/domains/jobs/workerRuntime.test.ts
  • greenfield/src/server/domains/jobs/errors.ts
  • greenfield/src/app/worker.ts
  • greenfield/src/contracts/jobModel.ts
  • greenfield/src/server/domains/jobs/workerRuntime.ts
  • greenfield/src/server/domains/jobs/actionRegistry.ts
  • greenfield/src/server/domains/jobs/coordinator.test.ts
  • greenfield/src/server/domains/jobs/coordinator.ts
  • greenfield/src/contracts/jobRealtime.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Analyze JavaScript and TypeScript
  • GitHub Check: frontend-checks
  • GitHub Check: dashboard-checks
🧰 Additional context used
🧠 Learnings (2)
📚 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/server/domains/jobs/service.test.ts
  • greenfield/src/server/domains/jobs/sideEffects.test.ts
  • greenfield/src/app/worker.test.ts
  • greenfield/src/server/domains/jobs/repository.test.ts
📚 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/server/domains/jobs/service.test.ts
  • greenfield/src/server/domains/jobs/sideEffects.test.ts
  • greenfield/src/server/domains/jobs/sideEffects.ts
  • greenfield/src/app/worker.test.ts
  • greenfield/src/server/domains/jobs/repository.test.ts
  • greenfield/src/server/domains/jobs/service.ts
  • greenfield/src/server/domains/jobs/repository.ts
🔇 Additional comments (25)
greenfield/src/app/worker.test.ts (4)

45-58: LGTM!


84-123: LGTM!


152-159: LGTM!


233-255: LGTM!

greenfield/src/server/domains/jobs/repository.test.ts (5)

175-186: LGTM!


285-349: LGTM!


351-443: LGTM!


854-886: LGTM!

Also applies to: 1054-1100


145-165: LGTM!

Also applies to: 507-507, 560-562, 584-584, 606-606, 635-635, 644-644, 682-684, 710-710

greenfield/src/server/domains/jobs/repository.ts (6)

796-808: LGTM!


1083-1086: LGTM!


1920-1937: LGTM!


2340-2349: LGTM!


705-712: LGTM!


1014-1042: 🩺 Stability & Availability

No change needed.

Queued runs with an unregistered action key are settled by the worker with the failed outcome and terminal code action-unavailable.

greenfield/src/server/domains/jobs/service.test.ts (3)

406-442: LGTM!

Also applies to: 461-477, 497-503


5-5: LGTM!

Also applies to: 14-14, 215-216


245-278: 🎯 Functional Correctness

No change needed.

registered comes from system.worker-smoke, whose default schedule is disabled; the cloned enabled value is not a hidden dependency here.

			> Likely an incorrect or invalid review comment.
greenfield/src/server/domains/jobs/service.ts (3)

198-206: LGTM!

Also applies to: 298-300, 419-424


575-586: LGTM!

Also applies to: 676-677


789-806: LGTM!

greenfield/src/server/domains/jobs/sideEffects.test.ts (1)

3-6: LGTM!

Also applies to: 78-99

greenfield/src/server/domains/jobs/sideEffects.ts (3)

52-57: LGTM!


98-117: LGTM!


140-149: LGTM!

Comment thread greenfield/src/server/domains/jobs/repository.test.ts
@mira-2026

Copy link
Copy Markdown
Collaborator Author

Addressed the complete CodeRabbit review in 9a3ccd9: explicit left-disabled validation and same-pass skip, a narrow worker-control read, distinct action-unavailable mapping, the canonical worker-summary bound, one fenced intent-closure helper, and settled rollback promises before persisted-state assertions. Final local gate: 61 jobs/worker tests, both typechecks, lint, format, docs, db check, and source boundaries all green.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9a3ccd9833

ℹ️ 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".

Comment thread greenfield/src/server/domains/jobs/actionRegistry.ts Outdated
Comment thread greenfield/src/server/domains/jobs/repository.ts Outdated
Comment thread greenfield/src/server/domains/jobs/coordinator.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b54fd8bf4

ℹ️ 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".

Comment thread greenfield/src/server/domains/jobs/service.ts Outdated
Comment thread greenfield/src/server/domains/jobs/coordinator.ts Outdated
Comment thread greenfield/src/server/domains/jobs/service.ts Outdated
Comment thread greenfield/src/server/domains/jobs/repository.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 064e0cfa48

ℹ️ 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".

Comment thread greenfield/src/server/domains/jobs/repository.ts
Comment thread greenfield/src/server/domains/jobs/repository.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4c4be994a4

ℹ️ 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".

Comment thread greenfield/src/server/database/schema/scheduledJobs.ts Outdated
Comment thread greenfield/src/server/domains/jobs/service.ts Outdated
Comment thread greenfield/src/server/domains/jobs/coordinator.ts Outdated
Comment thread greenfield/src/server/database/schema/scheduledJobs.ts Outdated
Comment thread greenfield/src/server/domains/jobs/coordinator.ts Outdated
Comment thread greenfield/src/server/domains/jobs/service.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b31df4fd5

ℹ️ 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".

Comment thread greenfield/src/server/domains/jobs/repository.ts Outdated
Comment thread greenfield/migrations/20260804022252_dashboard-foundation/migration.sql Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: backend Backend API, server routes, services, and integrations area: database Database dashboard, Postgres, PgBouncer, or query views area: ops Operational actions, deploys, services, backups, and health status: needs-review Ready for human or agent review type: documentation Documentation, README, comments, and operational notes type: feature Adds user-visible functionality type: tests Adds or updates automated tests/coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants