Skip to content

fix(package-apps): take D1 and run-record begin off the warm fetch path - #1410

Merged
kentcdodds merged 2 commits into
mainfrom
cursor/package-app-hot-path-a287
Aug 12, 2026
Merged

kentcdodds merged 2 commits into
mainfrom
cursor/package-app-hot-path-a287

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Aug 12, 2026 •

Copy link
Copy Markdown
Owner

Intent

Package apps felt slow. A hello-world fetch that returns ok confirmed that: ~113ms warm platform overhead vs ~0ms for keyless packages.invoke of a trivial export. This PR removes the two host costs that do not belong on the response path.

Probe

Hidden private package perf-app-tiny-a287 (package_id 6143a543-ef4c-482d-a1c4-a6e236feaa91): default export { ok: true }, app handler return new Response('ok'). Timed from execute against production:

Surface Cold Warm avg (n=7)
packages.invoke ping 184ms ~0ms
package_app_fetch ok 2230ms 113ms (103–124ms)

User code is not the bottleneck. Two host costs were on every warm hit:

  1. Uncached D1 for the saved-package row and entity-source row (invoke already has a 15s freshness cache; apps skipped it).
  2. Awaited WorkerEntrypoint RPC for packageRuntimeRunStart before user fetch. beginRunRecord is synchronous and fire-and-forgets the running insert; finish already upserts the terminal row. The isolate hop was the cost.

Summary

  • Package-app serve uses resolveSavedPackage + loadInvokeManifestBySourceId (same freshness/commit caches as keyless invoke). Warm serve now does zero D1/KV loads before dispatch.
  • Generated app worker kicks off run-record begin without await, runs user fetch/onRealtimeEvent, and waitUntils begin-then-finish. Start RPC failures no longer fail the HTTP response.
  • package_app_fetch shares the saved-package freshness cache and validates the path before any D1 lookup.
  • On published artifact miss, rebuild re-derives kody.app.entry from the loaded files and persists against a freshly loaded source row (same as invoke), so a stale freshness-cached manifest cannot poison the previous commit's bundle identity.
  • Guardrails docs now treat package-app HTTP as an invocation hot path.

Remaining warm cost after this (not in this PR): APP_LOADER.get per request (stubs are request-bound; worker options are already cached) plus isolate import() of the user module.

Testing

  • vitest node-unit for package-app serve/worker, handler, package_app_fetch, and invoke-contract-cache
  • Pre-push: 2036 node+workers unit tests, 7 Playwright e2e
  • Production probe numbers above (before this change; re-measure perf-app-tiny-a287 after deploy)
System recap — extends existing primitives (medium risk)

Mode: recap · Base: main @ 4e2f43a9 · Head: 189e5312

Classification: extends — package-app HTTP now shares the invoke freshness cache and no longer awaits run-record begin on the response path.

Primitives touched

Primitive Group Impact
package-apps assistant extends — generated worker defers begin RPC into waitUntil; artifact rebuild persists against the fresh source
package-runtime runtime extends — serve path uses invoke freshness/commit caches
saved-packages assistant extends — package_app_fetch shares the cache; path validation before D1
app-ui surfaces composes — handler tests only

System map

Warm package-app HTTP now resolves the saved package and manifest through the invoke freshness cache, then runs user fetch without waiting for the run-record begin RPC. Artifact misses rebuild against the current published source, not the cached row.

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
	savedPackages["saved-packages<br/>Saved packages"]:::extended
	packageApps["package-apps<br/>Package apps"]:::extended
	packageRuntime["package-runtime<br/>Package runtime"]:::extended
	appUi["app-ui<br/>Browser app"]:::touched
	savedPackages -->|"15s freshness cache on serve + package_app_fetch"| packageRuntime
	packageRuntime -->|"loadInvokeManifestBySourceId"| packageApps
	packageApps -->|"begin RPC waitUntil-chained before finish"| packageRuntime
	packageApps -->|"rebuild persist uses fresh source row"| packageRuntime
	appUi -->|"handlePackageAppRequest still calls serve"| packageRuntime
	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

Change flow

sequenceDiagram
	participant Browser
	participant Serve as servePackageAppRequest
	participant Cache as invoke freshness cache
	participant Isolate as PackageAppWorker
	participant Bridge as packageRuntimeRunStart
	Browser->>Serve: GET /packages/{kodyId}/
	Serve->>Cache: resolveSavedPackage + loadInvokeManifest
	Note over Cache: warm: zero D1/KV
	Serve->>Isolate: entrypoint.fetch
	Isolate-->>Bridge: startRuntimeRun (not awaited)
	Isolate->>Isolate: user fetch()
	Isolate-->>Browser: Response
	Isolate->>Bridge: waitUntil begin then finish
Loading

Before / after

Warm hello-world app_fetch (production probe, before this PR): ~113ms platform overhead vs ~0ms keyless invoke.

Warm serve D1/KV (this PR's node test): zero saved-package, source-row, and manifest loads before dispatch.

Artifact miss after republish: rebuild entry and persist identity come from the freshly loaded source, matching invoke.

Invariants

Cache keys still start with the owning userId. Publish/rebuild keep using uncached loadPackageManifestBySourceId. A dropped running insert remains harmless because finish upserts the terminal row. Artifact hits may still serve the stale commit for up to the freshness TTL.

Open in Web Open in Cursor 

Summary by CodeRabbit

  • Performance

    • Improved package app response times by serving warm requests without additional database or cache reads.
    • Added caching for saved packages and invocation manifests.
  • Reliability

    • Package app requests and realtime interactions now continue even if run-record initialization fails.
    • Run completion is handled asynchronously to avoid delaying responses.
  • Tests

    • Added coverage for warm-cache behavior, non-blocking run tracking, and deferred run completion.
  • Documentation

    • Updated architecture guidance for package app performance, caching, and run-record handling.

A hello-world package app that returns `ok` was ~113ms warm vs ~0ms for
keyless packages.invoke of a trivial export. Serve now uses the invoke
freshness cache, and the generated worker no longer awaits the run-record
begin RPC before user fetch.
@kentcdodds
kentcdodds marked this pull request as ready for review August 12, 2026 21:56
@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Package-app serving now uses cached package and invocation-manifest loaders. Warm requests avoid package, source, and manifest storage loads. Fetch and realtime handlers start run recording asynchronously and finish through waitUntil.

Changes

Package-app runtime and serving

Layer / File(s) Summary
Non-blocking runtime run records
packages/worker/src/package-runtime/package-app.ts, packages/worker/src/package-runtime/package-app.node.test.ts, docs/contributing/architecture/run-records.md
Fetch and realtime handlers no longer await run initialization. Completion waits for initialization inside waitUntil, and failed starts return a null handle. Tests cover the sequencing contract.
Cached package-app serving
packages/worker/src/mcp/capabilities/packages/package-app-fetch.ts, packages/worker/src/package-runtime/package-app-serve.ts, packages/worker/src/package-runtime/package-app-serve.node.test.ts, docs/contributing/architecture/invocation-overhead-guardrails.md, packages/worker/src/package-invocations/invoke-contract-cache.ts, packages/worker/src/package-invocations/module-artifacts.ts
Saved packages and invocation manifests use cached loaders. Path validation occurs before cache warming. Tests verify cold loads and warm requests with zero additional package, source, or manifest storage loads.
Invocation loader test migration
packages/worker/src/app/handlers/package-app.node.test.ts
Handler tests mock the package-invocation resolution and manifest-loader APIs, including failure, forwarding, and username-mismatch cases.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Mergeability Score: 🔵 Low · up to b1e86

The change improves package-app latency, but the lookup cache needs to distinguish id-based and kody_id-based requests to avoid returning the wrong package record. The PR is otherwise mergeable with explicit owner follow-up on this bounded correctness issue.

Sequence Diagram(s)

sequenceDiagram
  participant PackageAppHandler
  participant packageRuntimeRunStart
  participant UserCode
  participant waitUntil
  participant packageRuntimeRunFinish
  PackageAppHandler->>packageRuntimeRunStart: start run without awaiting
  PackageAppHandler->>UserCode: process fetch or realtime request
  PackageAppHandler->>waitUntil: schedule run completion
  waitUntil->>packageRuntimeRunStart: await start promise
  waitUntil->>packageRuntimeRunFinish: finish resolved run
Loading

Possibly related PRs

Suggested reviewers: kody-bot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed The title clearly summarizes the main changes: removing D1 loads and awaited run-record initialization from the warm package-app fetch path.
Description check ✅ Passed The description includes Intent, Summary, Testing, and System changes sections with specific scope, rationale, risks, and verification details.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/package-app-hot-path-a287

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.

@github-actions

github-actions Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

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

Worker: kody-pr-1410
Runtime worker: kody-pr-1410-runtime (https://kody-pr-1410-runtime.kody-a99.workers.dev)
D1: kody-pr-1410-db
KV: kody-pr-1410-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: 1

🤖 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 `@packages/worker/src/mcp/capabilities/packages/package-app-fetch.ts`:
- Around line 262-277: The cache key passed to
resolveSavedPackageWithFreshnessCache must include the lookup namespace: prefix
packageIdOrKodyId with “package-id” when input.packageId is provided and
“kody-id” when resolving by input.kodyId, while preserving the existing lookup
query behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 16db9155-2150-44f1-b600-5444d7aac69b

📥 Commits

Reviewing files that changed from the base of the PR and between 4e2f43a and b1e8685.

📒 Files selected for processing (10)
  • docs/contributing/architecture/invocation-overhead-guardrails.md
  • docs/contributing/architecture/run-records.md
  • packages/worker/src/app/handlers/package-app.node.test.ts
  • packages/worker/src/mcp/capabilities/packages/package-app-fetch.ts
  • packages/worker/src/package-invocations/invoke-contract-cache.ts
  • packages/worker/src/package-invocations/module-artifacts.ts
  • packages/worker/src/package-runtime/package-app-serve.node.test.ts
  • packages/worker/src/package-runtime/package-app-serve.ts
  • packages/worker/src/package-runtime/package-app.node.test.ts
  • packages/worker/src/package-runtime/package-app.ts

Comment on lines +262 to +277
const packageIdOrKodyId = input.packageId ?? input.kodyId ?? ''
return await resolveSavedPackageWithFreshnessCache({
userId: input.userId,
kodyId: input.kodyId ?? '',
packageIdOrKodyId,
load: async () => {
if (input.packageId !== undefined) {
return await getSavedPackageById(input.db, {
userId: input.userId,
packageId: input.packageId,
})
}
return await getSavedPackageByKodyId(input.db, {
userId: input.userId,
kodyId: input.kodyId ?? '',
})
},

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect schema and write paths for constraints that prevent a package ID from
# matching a different package Kody ID for the same user.
rg -n -C 4 'saved_packages|kody_id|package_id|CREATE TABLE' .

Repository: kentcdodds/kody

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- cache helper and call sites ---'
rg -n -C 8 'resolveSavedPackageWithFreshnessCache|packageIdOrKodyId' packages/worker/src

printf '%s\n' '--- saved package repository definitions ---'
rg -n -C 10 'getSavedPackageBy(Id|KodyId)|saved_packages' packages/worker/src packages/*/migrations packages/*/src 2>/dev/null | head -n 1200

printf '%s\n' '--- schema constraints and package identifiers ---'
rg -n -C 6 'CREATE TABLE[^;]*(saved_packages|packages)|UNIQUE[^;]*(kody_id|user_id)|kody_id.*UNIQUE|package.*id' packages --glob '*.sql' --glob '*.ts' --glob '*.tsx' | head -n 1600

Repository: kentcdodds/kody

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- exact helper locations ---'
rg -l 'resolveSavedPackageWithFreshnessCache' packages/worker/src
rg -l 'function getSavedPackageBy(Id|KodyId)|export async function getSavedPackageBy(Id|KodyId)' packages/worker/src

printf '%s\n' '--- relevant implementation excerpts ---'
for f in $(rg -l 'resolveSavedPackageWithFreshnessCache' packages/worker/src); do
  echo "FILE: $f"
  rg -n -C 18 'resolveSavedPackageWithFreshnessCache|packageIdOrKodyId' "$f"
done

for f in $(rg -l 'function getSavedPackageBy(Id|KodyId)|export async function getSavedPackageBy(Id|KodyId)' packages/worker/src); do
  echo "FILE: $f"
  rg -n -C 16 'getSavedPackageBy(Id|KodyId)' "$f"
done

printf '%s\n' '--- production schema/migrations containing saved_packages ---'
git ls-files | rg '(^|/)(migrations?|schema|.*test-schema).*|\.sql$' | while read -r f; do
  if rg -q 'saved_packages' "$f"; then
    echo "FILE: $f"
    rg -n -C 8 'saved_packages|idx_saved_packages_user_kody_id' "$f"
  fi
done

Repository: kentcdodds/kody

Length of output: 18047


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- saved_packages write paths and ID generation ---'
rg -n -C 12 'INSERT INTO saved_packages|UPDATE saved_packages SET|id:\s*.*package|packageId.*random|randomUUID|crypto\.randomUUID|create.*package' packages/worker/src packages/worker/migrations --glob '*.ts' --glob '*.sql' | head -n 1800

printf '%s\n' '--- Kody ID validation and normalization ---'
rg -n -C 10 'kodyId|kody_id|KodyId|kody id' packages/worker/src/package-registry packages/worker/src/community packages/universal packages/shared --glob '*.ts' 2>/dev/null | rg -v 'node_modules' | head -n 1800

printf '%s\n' '--- cache tests and package-app-fetch tests ---'
rg -n -C 14 'saved-package|cache|package_id|kody_id|resolveSavedPackage' packages/worker/src/package-invocations packages/worker/src/mcp/capabilities/packages --glob '*test.ts' --glob '*.ts' | head -n 2200

Repository: kentcdodds/kody

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -u

printf '%s\n' '--- insertSavedPackage definition and callers ---'
rg -n -C 20 'export (async )?function insertSavedPackage|function insertSavedPackage|insertSavedPackage\(' packages/worker/src --glob '*.ts' --glob '!*.test.ts' | head -n 1000

printf '%s\n' '--- package ID assignments in production code ---'
rg -n -C 8 'const (packageId|id) =|packageId\s*[:=].*crypto|id\s*[:=].*crypto|crypto\.randomUUID\(\)' packages/worker/src --glob '*.ts' --glob '!*.test.ts' | rg -C 4 'package|saved|randomUUID|id' | head -n 1200

printf '%s\n' '--- Kody ID schemas and validators ---'
rg -n -C 14 'kody[_-]?id|kodyId' packages/worker/src/package-registry packages/worker/src/mcp/capabilities/packages packages/worker/src/community --glob '*.ts' --glob '!*.test.ts' | head -n 1600

Repository: kentcdodds/kody

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- manifest Kody ID validation ---'
rg -n -C 18 'kody:\s*z|kodyIdSchema|kody.*id|id:\s*z\.(string|literal)|Kody.*Schema' packages/worker/src packages --glob '*.ts' --glob '!*.test.ts' | head -n 1600

printf '%s\n' '--- package ID creation in save flows ---'
rg -n -C 10 'const packageId|let packageId|packageId =' packages/worker/src/mcp/capabilities/packages/save-package.ts packages/worker/src/mcp/capabilities/packages/create-stub-package.ts packages/worker/src/mcp/capabilities/repo/repo-promote-to-package.ts packages/worker/src/package-registry/service.ts

printf '%s\n' '--- relevant cache tests ---'
rg -n -C 20 'resolveSavedPackageWithFreshnessCache|savedPackageCacheKey|clearInvokeContractCachesForTests|packageIdOrKodyId' packages/worker/src --glob '*test.ts' | head -n 1800

Repository: kentcdodds/kody

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- authored package schema definition ---'
rg -n -C 25 'authoredPackageJsonSchema|AuthoredPackageJson' packages/worker/src packages --glob '*.ts' --glob '!*.test.ts' | head -n 1400

printf '%s\n' '--- package ID creation around save handlers ---'
sed -n '220,325p' packages/worker/src/mcp/capabilities/packages/save-package.ts
sed -n '60,118p' packages/worker/src/mcp/capabilities/packages/create-stub-package.ts
sed -n '140,165p' packages/worker/src/mcp/capabilities/repo/repo-promote-to-package.ts

Repository: kentcdodds/kody

Length of output: 1919


Include the lookup kind in the cache key. id and kody_id use different lookup queries, but the schema does not make their namespaces disjoint. Prefix the key with package-id or kody-id to prevent one lookup from returning another package’s cached record.

🤖 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/packages/package-app-fetch.ts` around
lines 262 - 277, The cache key passed to resolveSavedPackageWithFreshnessCache
must include the lookup namespace: prefix packageIdOrKodyId with “package-id”
when input.packageId is provided and “kody-id” when resolving by input.kodyId,
while preserving the existing lookup query behavior.

@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.

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 b1e8685. Configure here.

Comment thread packages/worker/src/package-runtime/package-app-serve.ts
…urce

On artifact miss, re-derive kody.app.entry from loaded files and look up
the current source row so a freshness-cached manifest cannot poison the
previous commit's bundle identity.
@kentcdodds
kentcdodds merged commit 64118f5 into main Aug 12, 2026
10 checks passed
@kentcdodds
kentcdodds deleted the cursor/package-app-hot-path-a287 branch August 12, 2026 22:45
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.

2 participants