Skip to content

Move run-derived state into per-user RunLog - #1114

Merged
kody-bot merged 11 commits into
mainfrom
cursor/runlog-consolidation-1b40
Aug 1, 2026
Merged

kody-bot merged 11 commits into
mainfrom
cursor/runlog-consolidation-1b40

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Jul 31, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • move workflow projections, job run observability, and package activation state into dedicated per-user RunLog tables outside history caps
  • preserve legacy D1 state through bounded batch read-through and retry-safe one-time seeding; keep scheduling/compatibility fields with no drops
  • atomically reserve workflow slots and commit terminal run/ledger/derived-state transitions; recover stale reservations and retain terminal projections for 90 days
  • make projection updates monotonic/terminal-sticky and clear frozen D1 job error/duration fallbacks only when a new run advances the retention anchor
  • export and purge every new state surface and document the storage boundaries

Validation

  • CI=1 npm run validate — passed locally on 2fdca129 (1,706 unit/Workers tests plus E2E/MCP/static/typecheck/build/docs/migrations)
  • PR CI — Node, Workers, E2E, MCP, and Static passed
  • CodeRabbit completed successfully; all valid CodeRabbit/Bugbot findings were addressed
System recap — extends existing primitives (medium risk)

Mode: recap · Base: main @ 915db38a · Head: 2fdca129

Classification: extends — RunLog gains authoritative dedicated-state contracts and workflow/job/activation readers are rewired with expand-phase continuity.

Primitives touched

Primitive Group Impact
run-records storage extends — dedicated state, transactions, retention, batch import/export
workflows assistant extends — projection, legacy read-through, atomic reservation
jobs assistant extends — seeded outcome/counters; D1 scheduling retained
usage-metering storage extends — activation moves off per-run D1 writes
account-export assistant extends — exports all dedicated RunLog state
capability-registry assistant composes — exact durable idempotency reads

System map

Workflow/job terminal events flow into the per-user RunLog; bounded legacy reads seed pre-deploy D1 state before authoritative decisions.

Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).

flowchart LR
	workflows["workflows<br/>Workflows"]:::extended
	jobs["jobs<br/>Scheduled jobs"]:::extended
	usage["usage-metering<br/>Usage metering"]:::extended
	runs["run-records<br/>Run records"]:::extended
	registry["capability-registry<br/>Capability registry"]:::touched
	export["account-export<br/>Account data export"]:::extended
	workflows -->|"bounded batch import + reservation"| runs
	jobs -->|"retry-safe seeded outcomes"| runs
	usage -->|"transactional success + milestones"| runs
	registry -->|"exact idempotency lookup"| runs
	export -->|"paged dedicated-state phases"| runs
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Invariants

  • Every RPC resolves RUN_LOG.idFromName(userId); dedicated tables omit user_id because the Durable Object is user-scoped.
  • Dedicated state is outside the 30-day / 2,000-run history cap; terminal workflows retain the legacy 90-day idempotency window.
  • D1 scheduling remains and no table/column is dropped; projection stores reject terminal-to-active regressions.
  • Seed failures remain retryable and later merges cannot overwrite newer DO state.
  • clearAll and account export cover every dedicated table.

Conductor report

STATUS: done

What shipped: additive per-user RunLog workflow projections, job observability, package success counters, and activation milestones; bounded legacy-state cutover; atomic entitlement reservations and terminal transitions; export/deletion/retention guardrails; updated architecture docs.

Risk self-assessment: high — concurrent_workflows enforcement semantics now atomically reserve against RunLog, so the PR is green and ready-for-review but intentionally not self-merged.

Merged/deployed: no / no. PR: #1114 · CI: https://github.com/kentcdodds/kody/actions/runs/30675733325

Scope spill: meter-do should move shared concurrent_workflows usage readers in entitlements/service.ts to the exposed RunLog count; reporting-off-d1 must replace legacy workflow/job/activation aggregates in app/admin-insights-data.ts before compatibility stores retire.

Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features
    • Account run-record exports now include workflow activity, job observability, package successes, and activation milestones with continuous pagination.
    • Job views and “run now” results display current run status, timing, errors, and counters.
    • Workflow activity tracking is more reliable across creation, cancellation, retries, and terminal states.
  • Bug Fixes
    • Improved job retention decisions and prevented stale run metrics from overwriting scheduling data.
    • Account deletion now removes associated run history and observability data.
  • Documentation
    • Updated architecture and data-storage documentation for expanded exports, retention, and cleanup.

cursoragent and others added 3 commits July 31, 2026 23:05
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

RunLog now stores workflow projections, job observability, package success records, and activation milestones. Workflow operations use RunLog with D1 compatibility mirroring. Job views hydrate observability data, and account exports page all RunLog state.

Changes

RunLog state and exports

Layer / File(s) Summary
Dedicated RunLog persistence and export
packages/worker/src/run-records/*, packages/worker/src/account/*, docs/contributing/architecture/*
RunLog adds dedicated tables, RPCs, migration seeding, retention rules, transactional terminal updates, cleanup, and cursor-paged exports for workflow projections, job observability, package successes, and activation milestones.

Workflow projection migration

Layer / File(s) Summary
RunLog-backed workflow lifecycle
packages/worker/src/package-runtime/package-workflows.ts, packages/worker/src/mcp/capabilities/durable-escalation.ts, related tests
Workflow creation, lookup, listing, cancellation, idempotency, reservations, and escalation use RunLog projections with bounded D1 import and asynchronous compatibility mirroring. Terminal states remain monotonic.

Job observability ownership

Layer / File(s) Summary
Job state ownership and hydration
packages/worker/src/jobs/*
D1 job updates no longer write observability counters, errors, or duration. Retention uses lastRunAt, while job inspection and immediate execution hydrate views from RunLog.

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

Sequence Diagram(s)

sequenceDiagram
  participant JobService
  participant RunLog
  participant JobView
  JobService->>RunLog: persist terminal job observability
  JobService->>RunLog: hydrate job view
  RunLog-->>JobView: return status, errors, duration, and counters
Loading

Possibly related PRs

  • kentcdodds/kody#969: Migrates run-history and observability data from D1 into per-user RunLog Durable Objects.
  • kentcdodds/kody#973: Changes job lifecycle and retention behavior around RunLog-owned observability.
  • kentcdodds/kody#976: Updates durable escalation lookup and idempotency behavior for RunLog-backed workflow projections.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: moving run-derived state into per-user RunLog storage.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/runlog-consolidation-1b40

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.

cursoragent and others added 5 commits July 31, 2026 23:11
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@kody-bot
kody-bot marked this pull request as ready for review July 31, 2026 23:59
@github-actions

github-actions Bot commented Aug 1, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-1114.kody-a99.workers.dev

Worker: kody-pr-1114
D1: kody-pr-1114-db
KV: kody-pr-1114-oauth-kv

Mocks:

@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: 5

Caution

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

⚠️ Outside diff range comments (2)
packages/worker/src/jobs/service.ts (1)

1337-1399: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Move the RunLog read out of the per-user write lease.

hydrateJobViewFromRunLog at Line 1388 runs inside withAccountWriteLease's write() callback, after updateJobRow has already committed. This call exists only to enrich the returned job view; it does not affect the write's correctness. Holding the lease during this extra RPC round trip serializes other operations for the same user behind it unnecessarily.

Return the updated record from write() and hydrate after the lease is released.

🔧 Proposed fix to hydrate after the lease is released
 export async function runJobNow(input: {
 	env: Env
 	userId: string
 	jobId: string
 	callerContext?: McpCallerContext | null
 	repoCheckPolicyOverride?: JobRepoCheckPolicy | null
 	waitUntil?: (promise: Promise<unknown>) => void
 }) {
-	return await withAccountWriteLease({
+	const result = await withAccountWriteLease({
 		db: input.env.APP_DB,
 		stableUserId: input.userId,
 		async write() {
 			// ... unchanged up to updateJobRow ...
-			const job = await hydrateJobViewFromRunLog({
-				env: input.env,
-				userId: input.userId,
-				job: toJobView(updated),
-			})
 			return {
-				job,
+				job: toJobView(updated),
 				execution: outcome.execution,
 				deletedAfterRun,
 			}
 		},
 	})
+	const job = await hydrateJobViewFromRunLog({
+		env: input.env,
+		userId: input.userId,
+		job: result.job,
+	})
+	return { ...result, job }
 }
🤖 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 `@packages/worker/src/jobs/service.ts` around lines 1337 - 1399, Move the
hydrateJobViewFromRunLog call out of the withAccountWriteLease write callback.
Have write() return the updated job record and execution result, then invoke
hydrateJobViewFromRunLog after withAccountWriteLease resolves so the RunLog read
occurs after the lease is released while preserving the existing response fields
and deletion status.
packages/worker/src/run-records/service.node.test.ts (1)

26-33: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Verify the activation seed path through this stub.

With surface: 'job', status: 'success', and packageId: 'pkg-a', prepareTerminalRunSideEffectSeeds reaches ensureActivationStateSeeded, which calls rpc.isActivationInitialized() and rpc.importActivationState(). Add those RunLog RPC mocks and assert the seed-call expectation if the test should exercise seeding; otherwise silence the skipped seed warning when RUN_LOG.get() only supports run lifecycle methods.

🤖 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 `@packages/worker/src/run-records/service.node.test.ts` around lines 26 - 33,
Update the RUN_LOG.get stub used by the terminal-run test to include
isActivationInitialized and importActivationState RPC mocks. Configure the test
inputs for the job success seed path and assert the expected activation seed
calls from prepareTerminalRunSideEffectSeeds through
ensureActivationStateSeeded, rather than allowing the stub to omit or warn about
these methods.
🧹 Nitpick comments (8)
packages/worker/src/account/export.node.test.ts (1)

1653-1669: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: keep the run_log notes assertions in one test file.

packages/worker/src/account/user-owned-surfaces.node.test.ts lines 101-113 assert the same run_log surface metadata and note substrings. Keep the surface-metadata assertions in the owning test file and keep the export test focused on export behavior.

🤖 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 `@packages/worker/src/account/export.node.test.ts` around lines 1653 - 1669,
Remove the duplicated run_log metadata and notes assertions from the test
containing “run_log surface notes document clearAll purging every RunLog table,”
leaving those checks in user-owned-surfaces.node.test.ts. Keep this export test
focused solely on export behavior.
packages/worker/src/run-records/service.ts (1)

570-594: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Optional: skip the seed probe after the first successful seed.

Every terminal finish with a jobId calls getJobRunObservability before finishRun, and every qualifying success calls isActivationInitialized. Both remain one extra awaited DO RPC per terminal run after seeding has completed. Consider folding the seed check into the terminal finishRun RPC, or caching the seeded state per isolate, to remove the extra round trip from the hot path.

🤖 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 `@packages/worker/src/run-records/service.ts` around lines 570 - 594, Optimize
prepareTerminalRunSideEffectSeeds by avoiding repeated observability and
activation seed probes after their first successful initialization. Reuse a
per-isolate cache or fold the checks into the terminal finishRun flow, while
preserving seeding for each previously uninitialized job or activation state.
packages/worker/src/package-invocations/service.node.test.ts (1)

252-303: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The fake activation logic diverges from the Durable Object implementation.

RunLogBase.applySuccessfulPackageActivationIncrement returns early when the global package_activated milestone already exists, so it stops incrementing package_run_successes for every package. This fake always increments and only guards the milestone inserts. A test that reaches activation and then finishes more invocations will observe counts that the real Durable Object never produces.

Align the fake with the Durable Object, or add a comment that states the fake models only the pre-activation path.

♻️ Proposed alignment
 				if (!alreadyTerminal && runStatus === 'success') {
+					// Mirrors the DO: once `package_activated` exists, counting stops.
+					if (activationMilestones.has('package_activated')) return {
+						ledgerUpdated,
+						record: ledgerUpdated ? null : row ? clone(row) : null,
+					}
 					const packageId =
🤖 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 `@packages/worker/src/package-invocations/service.node.test.ts` around lines
252 - 303, Update the fake activation logic in the test’s run-row persistence
handler to match RunLogBase.applySuccessfulPackageActivationIncrement: return or
skip all package_run_successes and milestone updates once the global
package_activated milestone exists. Preserve the existing terminal-write and
surface/package filtering behavior for pre-activation runs.
packages/worker/src/run-records/continuity-and-retention.workers.test.ts (1)

28-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Both test files copy one console.warn mock that discards every message. The mock returns for expected substrings and also returns for all other messages, so no unexpected warning reaches the console or fails a test.

  • packages/worker/src/run-records/continuity-and-retention.workers.test.ts#L28-L34: forward non-matching messages to the previous mock implementation, then move the helper into shared test support.
  • packages/worker/src/run-records/dedicated-state.workers.test.ts#L40-L46: delete the duplicate and import the shared helper.
🤖 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 `@packages/worker/src/run-records/continuity-and-retention.workers.test.ts`
around lines 28 - 34, Update
packages/worker/src/run-records/continuity-and-retention.workers.test.ts lines
28-34 by moving silenceExpectedConsoleWarns into shared test support and
forwarding non-matching warnings to the previous console.warn mock
implementation. Update
packages/worker/src/run-records/dedicated-state.workers.test.ts lines 40-46 by
deleting its duplicate helper and importing the shared one.
packages/worker/src/run-records/run-log-do.ts (1)

655-665: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Prefer schema introspection to suppress the duplicate column error.

For a fresh Durable Object, installedVersion is null, so this path runs ALTER TABLE job_run_observability ADD COLUMN legacy_seeded ... even though CREATE TABLE already defines that column. Use PRAGMA table_info(job_run_observability) or sqlite_master to check whether the column exists before running the alter, instead of relying on SQL duplicate-column errors as expected control flow.

🤖 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 `@packages/worker/src/run-records/run-log-do.ts` around lines 655 - 665, Update
the schema migration block in the run-log Durable Object to introspect
job_run_observability with PRAGMA table_info or sqlite_master before adding
legacy_seeded. Only execute the ALTER TABLE statement when the column is absent,
and remove the try/catch that uses duplicate-column errors as expected control
flow; preserve the existing installedVersion threshold.
packages/worker/src/package-runtime/package-workflows.node.test.ts (1)

9-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use an inline type specifier for the service.ts import.

The static check reports consistent-type-specifier-style for this top-level type-only import.

♻️ Proposed fix
-import type {
-	WorkflowProjectionRecord,
-	WorkflowProjectionUpsertInput,
-} from '`#worker/run-records/service.ts`'
+import {
+	type WorkflowProjectionRecord,
+	type WorkflowProjectionUpsertInput,
+} from '`#worker/run-records/service.ts`'

The same warning applies to packages/worker/src/package-runtime/package-workflows-cancel.node.test.ts Lines 4-7 and packages/worker/src/mcp/capabilities/durable-escalation.node.test.ts Lines 10-13.

🤖 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 `@packages/worker/src/package-runtime/package-workflows.node.test.ts` around
lines 9 - 12, Update the type-only imports from run-records/service.ts in
package-workflows.node.test.ts, package-workflows-cancel.node.test.ts, and
durable-escalation.node.test.ts to use inline type specifiers for each imported
symbol, preserving the existing imported types and import source.

Source: Linters/SAST tools

packages/worker/src/mcp/capabilities/durable-escalation.ts (1)

96-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the existing D1-to-projection mapping instead of duplicating it.

This block re-implements the row mapping that mapD1WorkflowRunRow and projectionUpsertFromInspection already perform in packages/worker/src/package-runtime/package-workflows.ts (Lines 649-705). The two copies also disagree on one rule: mapD1WorkflowRunRow throws for an unknown source_type, while Line 118 here coerces any unknown value to 'inline'.

Export a narrow importer from package-workflows.ts (for example importCreatingWorkflowRunFromD1) and call it here. That keeps one mapping and one validation rule for the legacy workflow_runs shape.

🤖 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 `@packages/worker/src/mcp/capabilities/durable-escalation.ts` around lines 96 -
138, Replace the duplicated row-to-projection mapping in
importCreatingD1RowIfPresent with a narrow exported importer from
package-workflows.ts, such as importCreatingWorkflowRunFromD1, and invoke it
after fetching the D1 row. Reuse
mapD1WorkflowRunRow/projectionUpsertFromInspection so unknown source_type values
are validated consistently instead of coerced to inline.
packages/worker/src/package-runtime/package-workflows-cancel.node.test.ts (1)

17-322: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Extract the shared RunLog projection fake into test support.

This hoisted store, the vi.mock('#worker/run-records/service.ts') forwarder, and createWaitUntilFlusher are near-identical copies of Lines 37-366 in packages/worker/src/package-runtime/package-workflows.node.test.ts. Two more partial copies exist in packages/worker/src/mcp/capabilities/durable-escalation.node.test.ts (Lines 19-166) and packages/worker/src/mcp/run-kody-registry.node.test.ts (Lines 37-245).

The copies already differ. The run-kody-registry.node.test.ts copy omits the terminal-status stickiness rule that this copy applies at Lines 90-101. A fake that models RunLog write ordering is correctness-critical, so drift between copies weakens every test that depends on it.

Move the store and the forwarder into a shared helper under packages/worker/src/test-support/, and let each test file import 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 `@packages/worker/src/package-runtime/package-workflows-cancel.node.test.ts`
around lines 17 - 322, Extract the shared RunLog projection fake currently
defined by runRecordMocks, the vi.mock('`#worker/run-records/service.ts`')
forwarder, and createWaitUntilFlusher into a helper under test-support. Preserve
the complete behavior here, including updatedAt ordering and terminal-status
stickiness, then update package-workflows.node.test.ts,
durable-escalation.node.test.ts, and run-kody-registry.node.test.ts to import
and reuse that helper instead of maintaining local copies.
🤖 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 `@docs/contributing/architecture/data-storage.md`:
- Around line 251-261: The storage documentation still attributes run counters
and terminal outcomes to D1 jobs. In
docs/contributing/architecture/data-storage.md lines 251-261, revise the Storage
split list to limit D1 jobs to schedule fields, last_run_at, source pointers,
and storage_id; in docs/contributing/architecture/run-records.md lines 243-247,
update the Entity state row and paragraph so last-run outcomes and counters are
attributed to RunLog job_run_observability.

In `@packages/worker/src/jobs/job-run-observability-hydrate.ts`:
- Around line 13-28: Distinguish a missing observability record from a RunLog
lookup failure in getJobRunObservability and getJobRunObservabilityBatch, rather
than representing both as null or an empty result. Propagate that availability
state to applyJobRunObservabilityToJobView so a RunLog outage produces an
explicit “status unknown” outcome instead of retaining or implying stale success
data, while preserving normal job values when no record exists.

In `@packages/worker/src/package-runtime/package-workflows.ts`:
- Around line 861-887: Update importActiveD1WorkflowRuns and
importRecentD1WorkflowRuns to start all importWorkflowInspectionIntoRunLog calls
without awaiting each iteration, then await their combined completion so per-row
RPCs overlap while preserving completion before either function returns.

In `@packages/worker/src/run-records/run-log-do.ts`:
- Around line 1406-1412: The production method
applySuccessfulPackageActivationIncrement currently skips package_run_successes
after the global package_activated milestone; document this counter contract in
its doc comment, or move the milestone check after incrementing if counters are
intended as per-package totals. In
packages/worker/src/package-invocations/service.node.test.ts lines 252-303, make
the fake apply the same short-circuit behavior or explicitly document that it
models only the pre-activation path.

In `@packages/worker/src/run-records/service.ts`:
- Around line 438-442: Update isMissingD1RelationError to recognize D1 “no such
column” errors in addition to “no such table,” so readJobRunObservabilityFromD1
treats schemas lacking legacy observability columns as a successful empty
snapshot, sets legacySeeded, and avoids retrying the query on subsequent
terminal job finishes.

---

Outside diff comments:
In `@packages/worker/src/jobs/service.ts`:
- Around line 1337-1399: Move the hydrateJobViewFromRunLog call out of the
withAccountWriteLease write callback. Have write() return the updated job record
and execution result, then invoke hydrateJobViewFromRunLog after
withAccountWriteLease resolves so the RunLog read occurs after the lease is
released while preserving the existing response fields and deletion status.

In `@packages/worker/src/run-records/service.node.test.ts`:
- Around line 26-33: Update the RUN_LOG.get stub used by the terminal-run test
to include isActivationInitialized and importActivationState RPC mocks.
Configure the test inputs for the job success seed path and assert the expected
activation seed calls from prepareTerminalRunSideEffectSeeds through
ensureActivationStateSeeded, rather than allowing the stub to omit or warn about
these methods.

---

Nitpick comments:
In `@packages/worker/src/account/export.node.test.ts`:
- Around line 1653-1669: Remove the duplicated run_log metadata and notes
assertions from the test containing “run_log surface notes document clearAll
purging every RunLog table,” leaving those checks in
user-owned-surfaces.node.test.ts. Keep this export test focused solely on export
behavior.

In `@packages/worker/src/mcp/capabilities/durable-escalation.ts`:
- Around line 96-138: Replace the duplicated row-to-projection mapping in
importCreatingD1RowIfPresent with a narrow exported importer from
package-workflows.ts, such as importCreatingWorkflowRunFromD1, and invoke it
after fetching the D1 row. Reuse
mapD1WorkflowRunRow/projectionUpsertFromInspection so unknown source_type values
are validated consistently instead of coerced to inline.

In `@packages/worker/src/package-invocations/service.node.test.ts`:
- Around line 252-303: Update the fake activation logic in the test’s run-row
persistence handler to match
RunLogBase.applySuccessfulPackageActivationIncrement: return or skip all
package_run_successes and milestone updates once the global package_activated
milestone exists. Preserve the existing terminal-write and surface/package
filtering behavior for pre-activation runs.

In `@packages/worker/src/package-runtime/package-workflows-cancel.node.test.ts`:
- Around line 17-322: Extract the shared RunLog projection fake currently
defined by runRecordMocks, the vi.mock('`#worker/run-records/service.ts`')
forwarder, and createWaitUntilFlusher into a helper under test-support. Preserve
the complete behavior here, including updatedAt ordering and terminal-status
stickiness, then update package-workflows.node.test.ts,
durable-escalation.node.test.ts, and run-kody-registry.node.test.ts to import
and reuse that helper instead of maintaining local copies.

In `@packages/worker/src/package-runtime/package-workflows.node.test.ts`:
- Around line 9-12: Update the type-only imports from run-records/service.ts in
package-workflows.node.test.ts, package-workflows-cancel.node.test.ts, and
durable-escalation.node.test.ts to use inline type specifiers for each imported
symbol, preserving the existing imported types and import source.

In `@packages/worker/src/run-records/continuity-and-retention.workers.test.ts`:
- Around line 28-34: Update
packages/worker/src/run-records/continuity-and-retention.workers.test.ts lines
28-34 by moving silenceExpectedConsoleWarns into shared test support and
forwarding non-matching warnings to the previous console.warn mock
implementation. Update
packages/worker/src/run-records/dedicated-state.workers.test.ts lines 40-46 by
deleting its duplicate helper and importing the shared one.

In `@packages/worker/src/run-records/run-log-do.ts`:
- Around line 655-665: Update the schema migration block in the run-log Durable
Object to introspect job_run_observability with PRAGMA table_info or
sqlite_master before adding legacy_seeded. Only execute the ALTER TABLE
statement when the column is absent, and remove the try/catch that uses
duplicate-column errors as expected control flow; preserve the existing
installedVersion threshold.

In `@packages/worker/src/run-records/service.ts`:
- Around line 570-594: Optimize prepareTerminalRunSideEffectSeeds by avoiding
repeated observability and activation seed probes after their first successful
initialization. Reuse a per-isolate cache or fold the checks into the terminal
finishRun flow, while preserving seeding for each previously uninitialized job
or activation state.
🪄 Autofix (Beta)

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

Review profile: CHILL

Plan: Pro Plus

Run ID: b0787500-5537-4118-9648-d320ece1f9db

📥 Commits

Reviewing files that changed from the base of the PR and between 64b4117 and 29d825b.

📒 Files selected for processing (41)
  • docs/contributing/architecture/data-storage.md
  • docs/contributing/architecture/run-records.md
  • packages/worker/src/account/export.node.test.ts
  • packages/worker/src/account/export.ts
  • packages/worker/src/account/user-owned-surfaces.node.test.ts
  • packages/worker/src/account/user-owned-surfaces.ts
  • packages/worker/src/jobs/execution-safety.node.test.ts
  • packages/worker/src/jobs/inspect.node.test.ts
  • packages/worker/src/jobs/inspect.ts
  • packages/worker/src/jobs/job-retention.node.test.ts
  • packages/worker/src/jobs/job-retention.ts
  • packages/worker/src/jobs/job-run-observability-hydrate.node.test.ts
  • packages/worker/src/jobs/job-run-observability-hydrate.ts
  • packages/worker/src/jobs/job-schedule-watchdog.node.test.ts
  • packages/worker/src/jobs/job-schedule-watchdog.ts
  • packages/worker/src/jobs/process-due-jobs.node.test.ts
  • packages/worker/src/jobs/process-due-jobs.ts
  • packages/worker/src/jobs/repo.ts
  • packages/worker/src/jobs/repo.workers.test.ts
  • packages/worker/src/jobs/run-due-jobs-claim-fence.node.test.ts
  • packages/worker/src/jobs/service.node.test.ts
  • packages/worker/src/jobs/service.ts
  • packages/worker/src/mcp/capabilities/durable-escalation.node.test.ts
  • packages/worker/src/mcp/capabilities/durable-escalation.ts
  • packages/worker/src/mcp/run-kody-registry.node.test.ts
  • packages/worker/src/package-invocations/service.node.test.ts
  • packages/worker/src/package-runtime/package-workflows-cancel.node.test.ts
  • packages/worker/src/package-runtime/package-workflows.node.test.ts
  • packages/worker/src/package-runtime/package-workflows.ts
  • packages/worker/src/run-records/continuity-and-retention.workers.test.ts
  • packages/worker/src/run-records/dedicated-state.workers.test.ts
  • packages/worker/src/run-records/job-run-observability.ts
  • packages/worker/src/run-records/package-activation-state.ts
  • packages/worker/src/run-records/run-log-do.ts
  • packages/worker/src/run-records/run-records.workers.test.ts
  • packages/worker/src/run-records/service.node.test.ts
  • packages/worker/src/run-records/service.ts
  • packages/worker/src/run-records/types.ts
  • packages/worker/src/run-records/workflow-projection.ts
  • packages/worker/src/usage/activation.node.test.ts
  • packages/worker/src/usage/activation.ts

Comment thread docs/contributing/architecture/data-storage.md
Comment on lines +13 to +28
export function applyJobRunObservabilityToJobView(
job: JobView,
observability: JobRunObservabilityRecord | null | undefined,
): JobView {
if (!observability) return job
return {
...job,
lastRunAt: observability.lastRunAt ?? job.lastRunAt,
lastRunStatus: observability.lastRunStatus ?? job.lastRunStatus,
lastRunError: observability.lastRunError ?? undefined,
lastDurationMs: observability.lastDurationMs ?? undefined,
runCount: observability.runCount,
successCount: observability.successCount,
errorCount: observability.errorCount,
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

RunLog outage silently blanks job error/duration/count data.

applyJobRunObservabilityToJobView returns job unchanged whenever observability is null or undefined. getJobRunObservability and getJobRunObservabilityBatch return null or an empty array both when no record exists and when the RunLog RPC call fails or the binding is unavailable. Since D1 no longer stores a fresh copy of lastRunError, lastDurationMs, or the run counters (this cohort freezes them at their prior D1 values), a RunLog outage during a job that actually failed results in a job view with no error message and stale counters, and the caller has no way to distinguish "no error occurred" from "RunLog was unreachable".

Consider surfacing availability distinctly, so consumers can show a "status unknown" state instead of implying success.

🤖 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 `@packages/worker/src/jobs/job-run-observability-hydrate.ts` around lines 13 -
28, Distinguish a missing observability record from a RunLog lookup failure in
getJobRunObservability and getJobRunObservabilityBatch, rather than representing
both as null or an empty result. Propagate that availability state to
applyJobRunObservabilityToJobView so a RunLog outage produces an explicit
“status unknown” outcome instead of retaining or implying stale success data,
while preserving normal job values when no record exists.

Comment thread packages/worker/src/package-runtime/package-workflows.ts
Comment thread packages/worker/src/run-records/run-log-do.ts
Comment thread packages/worker/src/run-records/service.ts Outdated
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/jobs/job-run-observability-hydrate.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
? serializeCallerContext(activeCallerContext)
: row.callerContextJson,
})
return {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Update job skips RunLog overlay

Medium Severity

updateJob still returns a toJobView built only from the D1 scheduling row. After this change, terminal counters, last-run error, and duration live in RunLog job_run_observability, and applyExecutionOutcome no longer updates those fields on D1. job_update (and any caller using the mutation return value) can show zero counts and missing errors even when runs succeeded, while job_list, job_get, and runJobNow already hydrate from RunLog.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 86f066b. Configure here.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2fdca12. Configure here.

env: input.env,
userId: input.userId,
job: toJobView(updated),
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Run now stale observability

Medium Severity

After a manual run, runJobNow hydrates the returned JobView from RunLog while JobManager.runNow still passes waitUntil, so finishRunRecord can persist terminal job_run_observability asynchronously. D1 no longer updates counters or error/duration in applyExecutionOutcome, so the response can show the previous run’s counts, error, duration, or status until the deferred finish completes.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2fdca12. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants