Skip to content

Add synthetic requests for package self-testing - #1315

Merged
kody-bot merged 9 commits into
mainfrom
cursor/kody-synthetic-requests-9ee8
Aug 8, 2026
Merged

kody-bot merged 9 commits into
mainfrom
cursor/kody-synthetic-requests-9ee8

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Aug 8, 2026 •

Copy link
Copy Markdown
Owner

Intent

Let package authors verify real app and subscription surfaces after publishing with platform-marked synthetic requests whose side effects remain real.

Summary

  • Adds owner-scoped package_app_fetch and package_subscription_dispatch capabilities with platform-only markers, package-runtime recursion guards, direct real-surface dispatch, stored-email replay, unique synthetic idempotency keys, and marker-aware run records.
  • Strips forged markers from public app ingress and all real subscription dispatch, and adds conditional publish/package-detail test hints.
  • Adds ADR 0013, authoring/subscription guidance, end-user docs, diagnostics, and focused coverage.

Testing

System changes

System recap — extends existing primitives (medium risk)

Mode: recap · Base: main @ 4451ccb · Head: b5ade3c

Classification: extends — existing package app, subscription, package runtime, and MCP capability contracts gain platform-marked test invocations; no primitive is added.

Primitives touched

Primitive Group Impact
saved-packages assistant extends — two test capabilities and conditional publish hints
package-apps assistant extends — trusted synthetic request marker and run metadata
package-runtime runtime extends — shared real app-serving path and recursion guards
package-events-dispatch-queue storage extends — real-dispatch marker stripping and trusted direct synthetic dispatch
email assistant composes — shared production envelope builder for replay
mcp-server surfaces extends — package detail Test section and discovery keywords
app-ui surfaces composes — hosted-app ingress strips forged markers and improves diagnostics

System map

Interactive MCP probes flow through the same app/subscription handlers as production while platform-only markers distinguish their run records.

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
	mcpServer["mcp-server<br/>MCP endpoint (/mcp)"]:::extended
	savedPackages["saved-packages<br/>Saved packages"]:::extended
	packageApps["package-apps<br/>Package apps"]:::extended
	packageRuntime["package-runtime<br/>Package runtime"]:::extended
	packageEvents["package-events-dispatch-queue<br/>Package events dispatch queue"]:::extended
	email["email<br/>Email"]:::touched
	mcpServer -->|"package_app_fetch / package_subscription_dispatch"| savedPackages
	savedPackages -->|"trusted Kody-Synthetic request"| packageApps
	packageApps -->|"real fetch handler + synthetic run metadata"| packageRuntime
	savedPackages -->|"trusted synthetic envelope, direct target"| packageEvents
	email -->|"shared stored-message envelope builder"| packageEvents
	packageEvents -->|"real subscription handler + marker-aware run"| 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

Invariants

  • Per-user and delegated package ownership resolution matches packages.invoke.
  • Synthetic markers are reserved: public/real paths strip forged values; only trusted platform dispatch can set them.
  • Run metadata agrees with the request/envelope seen by package code.

Conductor report

Synthetic requests report: DONE; PR #1315; merged commit c6d9536; deploy result success https://github.com/kentcdodds/kody/actions/runs/31263341667; evidence: npm run validate green, PR CI green, production healthcheck/smoke/reindex green; remains: none.

Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Added package_app_fetch to test published package apps with realistic requests and response details.
    • Added package_subscription_dispatch to simulate declared subscription events, including stored email replay.
    • Publish results and package details now include ready-to-use verification hints for apps and subscriptions.
    • Added safeguards for request validation, credential handling, idempotency, response limits, and unsupported WebSocket requests.
  • Documentation

    • Added guides covering synthetic app and subscription verification, publishing readiness checks, usage examples, and safety requirements.
    • Added ADR documentation describing the verification approach and supported boundaries.

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

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@cursor[bot], you've reached your PR review limit, so we couldn't start this review.

Next review available in: 5 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5de10728-f33d-4024-b516-5643b2ec835b

📥 Commits

Reviewing files that changed from the base of the PR and between 0a4934a and b5ade3c.

📒 Files selected for processing (16)
  • docs/guides/package-subscriptions.md
  • docs/use/package-app-fetch.md
  • docs/use/synthetic-event-dispatch.md
  • packages/worker/src/email/package-subscriptions.ts
  • packages/worker/src/mcp/capabilities/packages/package-app-fetch.node.test.ts
  • packages/worker/src/mcp/capabilities/packages/package-app-fetch.ts
  • packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.node.test.ts
  • packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.ts
  • packages/worker/src/package-invocations/module-artifacts.ts
  • packages/worker/src/package-invocations/service.node.test.ts
  • packages/worker/src/package-invocations/subscription-dispatch.ts
  • packages/worker/src/package-invocations/subscription-envelope.node.test.ts
  • packages/worker/src/package-runtime/package-app-serve.ts
  • packages/worker/src/package-runtime/package-app-synthetic.node.test.ts
  • packages/worker/src/package-runtime/package-app.node.test.ts
  • packages/worker/src/package-runtime/package-app.ts
📝 Walkthrough

Walkthrough

Adds package_app_fetch and package_subscription_dispatch MCP capabilities. They execute published package handlers with synthetic markers, production-equivalent context, sanitization, bounded responses, idempotency, activity metadata, and publish-time test hints.

Changes

Synthetic package verification

Layer / File(s) Summary
Shared package-app runtime
packages/worker/src/package-runtime/*, packages/worker/src/app/*
Extracts shared package-app serving logic, strips credentials and internal headers, supports trusted synthetic requests, and standardizes diagnostics.
Package app fetch capability
packages/worker/src/mcp/capabilities/packages/package-app-fetch.ts, packages/worker/src/mcp/capabilities/packages/domain.ts, packages/worker/src/mcp/capabilities/packages/package-app-fetch.node.test.ts
Adds package_app_fetch with package selection, caller validation, websocket rejection, request limits, sanitized forwarding, and bounded text or base64 responses.
Trusted subscription invocation
packages/worker/src/package-invocations/*, packages/worker/src/email/package-subscriptions.ts
Adds trusted synthetic envelope handling, marker sanitization, fresh idempotency keys, subscription run metadata, and the exported email receipt envelope builder.
Subscription dispatch capability
packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.ts, packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.node.test.ts
Adds package_subscription_dispatch for custom parameters or stored email replay, declared-topic validation, delegated package scope, trusted invocation, and structured results.
Publish hints and documentation
packages/worker/src/package-registry/package-test-hints.ts, packages/worker/src/mcp/capabilities/packages/publish-external-push.ts, packages/worker/src/mcp/tools/search-entity-plugins/package.ts, docs/*
Generates app and subscription test hints in publish and package-detail responses, and documents post-publish synthetic verification.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MCPCaller
  participant packageAppFetchCapability
  participant servePackageAppRequest
  participant PackageAppWorker
  MCPCaller->>packageAppFetchCapability: Submit synthetic HTTP request
  packageAppFetchCapability->>servePackageAppRequest: Forward sanitized request and owner
  servePackageAppRequest->>PackageAppWorker: Invoke package app entrypoint
  PackageAppWorker-->>servePackageAppRequest: Return response
  servePackageAppRequest-->>packageAppFetchCapability: Return bounded response data
Loading
sequenceDiagram
  participant MCPCaller
  participant packageSubscriptionDispatchCapability
  participant invokePackageSubscription
  participant SubscriptionHandler
  MCPCaller->>packageSubscriptionDispatchCapability: Submit parameters or email replay
  packageSubscriptionDispatchCapability->>invokePackageSubscription: Send trusted synthetic envelope
  invokePackageSubscription->>SubscriptionHandler: Invoke declared subscription
  SubscriptionHandler-->>invokePackageSubscription: Return handler result and side effects
  invokePackageSubscription-->>packageSubscriptionDispatchCapability: Return status and metadata
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.81% 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
Title check ✅ Passed The title clearly and concisely describes the PR's main change: adding synthetic requests for package self-testing.
Description check ✅ Passed The description includes all required sections and provides clear intent, changes, testing evidence, and system impact.
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/kody-synthetic-requests-9ee8

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 August 8, 2026 13:52
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 August 8, 2026 14:21
@github-actions

github-actions Bot commented Aug 8, 2026 •

Copy link
Copy Markdown
Contributor

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

Worker: kody-pr-1315
D1: kody-pr-1315-db
KV: kody-pr-1315-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: 8

Caution

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

⚠️ Outside diff range comments (1)
packages/worker/src/package-invocations/subscription-dispatch.ts (1)

66-73: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Sanitization now runs before shape validation, so the non-object payload guard is dead.

stripUntrustedSubscriptionEnvelopeFields returns { ...params }. For an array payload it returns an object with numeric keys. For a string payload it returns an index-keyed object. For a number or boolean it returns {}.

The check at Line 69 runs on that spread result, so it can never fail. events.dispatch accepts a non-object payload and forwards a mangled object to schema validation and to subscribers. Before this change the caller received events.dispatch payload must be a JSON object when provided.

Validate the raw payload first, then strip.

🐛 Proposed fix
-	const payload = stripUntrustedSubscriptionEnvelopeFields(
-		(input.payload ?? {}) as Record<string, unknown>,
-	)
-	if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
-		throw new Error(
-			'events.dispatch payload must be a JSON object when provided.',
-		)
-	}
+	const rawPayload = input.payload ?? {}
+	if (
+		!rawPayload ||
+		typeof rawPayload !== 'object' ||
+		Array.isArray(rawPayload)
+	) {
+		throw new Error(
+			'events.dispatch payload must be a JSON object when provided.',
+		)
+	}
+	const payload = stripUntrustedSubscriptionEnvelopeFields(
+		rawPayload as Record<string, unknown>,
+	)
🤖 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/subscription-dispatch.ts` around
lines 66 - 73, Validate the raw input.payload shape before calling
stripUntrustedSubscriptionEnvelopeFields: reject provided arrays and non-object
JSON values with the existing error, while allowing null/omitted payload
according to current defaults. Then sanitize only the validated object and
preserve the existing payload flow for valid object inputs.
🧹 Nitpick comments (7)
packages/worker/src/package-runtime/package-app.ts (1)

605-607: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

The synthetic header literals are duplicated in generated worker source and pinned by a source-text test. package-app-synthetic.ts owns packageAppSyntheticHeaderName and packageAppSyntheticHeaderValue, but the generated worker hard-codes copies, and the test asserts on that exact source text. The two changes must land together.

  • packages/worker/src/package-runtime/package-app.ts#L605-L607: interpolate packageAppSyntheticHeaderName and packageAppSyntheticHeaderValue into the generated isSyntheticPackageAppRequest helper instead of hard-coding 'Kody-Synthetic' and 'true'.
  • packages/worker/src/package-runtime/package-app-synthetic.node.test.ts#L54-L63: replace the readFileSync substring assertions with a behavioral check of the app_fetch run metadata, or match the interpolated constants instead of fixed source lines.
🤖 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-app.ts` around lines 605 - 607,
Update the generated isSyntheticPackageAppRequest helper in
packages/worker/src/package-runtime/package-app.ts:605-607 to interpolate
packageAppSyntheticHeaderName and packageAppSyntheticHeaderValue from
package-app-synthetic.ts instead of hard-coded literals. In
packages/worker/src/package-runtime/package-app-synthetic.node.test.ts:54-63,
replace the brittle readFileSync source-text assertions with a behavioral
app_fetch run-metadata check, or assertions that use the interpolated constants.
packages/worker/src/package-runtime/package-app-synthetic.node.test.ts (1)

30-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

This test re-implements the production logic instead of exercising it.

Lines 46-50 duplicate the body of applyTrustedPackageAppDispatch in package-app-serve.ts. The test therefore proves only that isPackageAppSyntheticRequest reads a header that the test itself set. If servePackageAppRequest stopped calling applyTrustedPackageAppDispatch, or applied it before createPackageCodeRequest, this test would still pass.

Export applyTrustedPackageAppDispatch from package-app-serve.ts and assert the real strip-then-mark order.

♻️ Proposed change to cover the real dispatch path
 	const stripped = createPackageCodeRequest(
 		inbound,
 		'https://apps.example.com/probe',
 	)
 	expect(isPackageAppSyntheticRequest(stripped)).toBe(false)
 
-	const trusted = new Request(stripped)
-	trusted.headers.set(
-		packageAppSyntheticHeaderName,
-		packageAppSyntheticHeaderValue,
-	)
+	const trusted = applyTrustedPackageAppDispatch(stripped, { synthetic: true })
 	expect(isPackageAppSyntheticRequest(trusted)).toBe(true)
+	expect(
+		isPackageAppSyntheticRequest(
+			applyTrustedPackageAppDispatch(stripped, undefined),
+		),
+	).toBe(false)
 })

Add the export in packages/worker/src/package-runtime/package-app-serve.ts:

export function applyTrustedPackageAppDispatch(
	request: Request,
	dispatch: PackageAppTrustedDispatch | undefined,
) {
	// unchanged body
}
🤖 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-app-synthetic.node.test.ts`
around lines 30 - 52, Export applyTrustedPackageAppDispatch from
package-app-serve.ts and update the test to invoke that production helper
instead of manually copying its header-setting logic. Assert the real trusted
dispatch flow strips the synthetic header via createPackageCodeRequest first,
then reapplies it through applyTrustedPackageAppDispatch, preserving the
expected false-before-dispatch and true-after-dispatch results.
packages/worker/src/mcp/capabilities/packages/package-app-fetch.ts (1)

162-172: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

The output does not tell the caller whether body is text or base64.

encodePackageAppFetchBody returns either decoded text or base64, but outputSchema exposes only status, headers, body, and truncated. The caller must guess the encoding from Content-Type, and that guess fails in two cases:

  • A binary response with a textual Content-Type that fails strict UTF-8 decoding returns base64.
  • Truncation at line 417 can split a multi-byte UTF-8 sequence, so decodeUtf8Body returns null and a text/html response is returned as base64.

Add an explicit discriminator while the schema is still new.

♻️ Proposed change
 function encodePackageAppFetchBody(response: Response, bytes: Uint8Array) {
 	const text = decodeUtf8Body(bytes)
 	if (
 		isTextualResponse(response) === true ||
 		(!response.headers.has('Content-Type') && text !== null)
 	) {
-		if (text === null) return bytesToBase64(bytes)
-		return text
+		if (text === null) {
+			return { body: bytesToBase64(bytes), encoding: 'base64' as const }
+		}
+		return { body: text, encoding: 'text' as const }
 	}
-	return bytesToBase64(bytes)
+	return { body: bytesToBase64(bytes), encoding: 'base64' as const }
 }
 		outputSchema: z.object({
 			status: z.number().int(),
 			headers: z.record(z.string(), z.string()),
 			body: z.string(),
+			encoding: z.enum(['text', 'base64']),
 			truncated: z.boolean(),
 		}),

Also applies to: 315-320

🤖 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 162 - 172, Update encodePackageAppFetchBody and the corresponding
outputSchema to include an explicit body encoding discriminator, such as whether
body is text or base64. Ensure both strict UTF-8 failure cases—binary
textual-content responses and truncated multi-byte sequences—return the base64
discriminator, while successfully decoded responses return the text
discriminator; update all affected output construction paths, including the
additional location noted in the comment.
packages/worker/src/mcp/capabilities/packages/package-app-fetch.node.test.ts (1)

39-51: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The mocked parsePackageAppPath hides the real path-resolution behavior.

The mock uses a permissive regex. The real implementation in package-app-serve.ts also runs getUsernameFormatValidationError and decodeURIComponent. No test in this file exercises the real parser against a path value that new URL() normalizes, so the .. retargeting case reported on package-app-fetch.ts lines 89-93 is not covered.

Add a case that passes a path containing .. and asserts the resulting packagePath.kodyId.

🤖 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.node.test.ts`
around lines 39 - 51, Add a test in the package-app fetch suite that supplies a
path containing “..” through the fetch flow and asserts the resulting
packagePath.kodyId matches the URL-normalized target. Ensure the case exercises
the real parsePackageAppPath behavior rather than relying on the permissive
mock, while preserving existing test setup and assertions.
packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.node.test.ts (1)

195-258: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the replay topic-mismatch branch.

The handler rejects a replay when the requested topic does not match the classification-derived topic (packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.ts Lines 237-241). No test exercises that branch.

Add a test that mocks a message with classification: 'quarantined' and requests email.message.received. Assert the handler throws and that invokePackageSubscription is not called.

🤖 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-subscription-dispatch.node.test.ts`
around lines 195 - 258, Add a test alongside the replay test for the
topic-mismatch path in packageSubscriptionDispatchCapability.handler: mock the
stored message with classification "quarantined", request
email.message.received, and assert the handler rejects. Also verify
mocks.invokePackageSubscription was not called.
packages/worker/src/package-invocations/subscription-dispatch.node.test.ts (1)

1-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Both new test suites omit a mock reset between tests. Each suite declares hoisted mocks and never clears them, so recorded calls accumulate. toHaveBeenCalledWith matches any recorded call, which makes both suites order-dependent and can hide a regression once assertions overlap.

  • packages/worker/src/package-invocations/subscription-dispatch.node.test.ts#L1-L16: add a beforeEach after the dynamic import that calls mocks.invokeSavedPackageModule.mockClear().
  • packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.node.test.ts#L5-L16: add a beforeEach after the dynamic import that clears all mocks and re-applies the default invokePackageSubscription resolved value.
🤖 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/subscription-dispatch.node.test.ts`
around lines 1 - 16, Reset hoisted mocks before each test to prevent call
history from leaking between cases. In
packages/worker/src/package-invocations/subscription-dispatch.node.test.ts
(lines 1-16), add a beforeEach after the dynamic import that clears
mocks.invokeSavedPackageModule. In
packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.node.test.ts
(lines 5-16), add a beforeEach after the dynamic import that clears all mocks
and reapplies the default resolved value for invokePackageSubscription.
packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.ts (1)

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

Import the receipt topic from #worker/email/package-subscriptions.ts.

inboundEmailReceiptTopic is defined in packages/worker/src/email/package-subscriptions.ts, and expectedTopic uses this value during replay validation. Import the shared constant instead of redeclaring email.message.received locally to avoid duplicate topic strings.

🤖 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-subscription-dispatch.ts`
at line 35, Update package-subscription dispatch to import and reuse
inboundEmailReceiptTopic from the shared email package-subscriptions module,
removing the local duplicate declaration while preserving expectedTopic replay
validation.
🤖 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/guides/package-subscriptions.md`:
- Around line 99-100: Update the package_publish_external_push documentation to
qualify that test_hints.subscriptions[] is available only for published and
already_published final results. Clarify that dispatched results must be polled
until completion before callers use the hints, and do not imply the field exists
for not_fast_forward or checks_failed responses.

In `@docs/use/package-app-fetch.md`:
- Around line 31-63: Document the optional package_scope field in
docs/use/package-app-fetch.md (lines 31-63), explaining that callers must
preserve the signed-in user’s owner scope when it appears in test_hints.app; add
package_scope to the fixture and replay examples in
docs/guides/package-subscriptions.md (lines 78-94); and add it to the fields
table in docs/use/synthetic-event-dispatch.md (lines 32-56), including its
owner-scoped behavior.

In `@docs/use/synthetic-event-dispatch.md`:
- Around line 53-54: Update the synthetic event dispatch documentation to
distinguish top-level request idempotency from event-envelope idempotency: state
that callers cannot provide a top-level idempotency_key because the platform
generates it, while clarifying that package-emitted fixtures may still require
the nested params.idempotency_key field.

In
`@packages/worker/src/mcp/capabilities/packages/package-app-fetch.node.test.ts`:
- Around line 321-333: Update the large-response test around
packageAppFetchCapability.handler to verify that capped.body decodes
successfully from base64 and matches the corresponding byte prefix of
largeBinary, rather than asserting only its encoded length. Preserve the
truncated expectation and ensure the assertion reflects a decodable response
capped at the configured byte limit.

In `@packages/worker/src/mcp/capabilities/packages/package-app-fetch.ts`:
- Around line 89-93: Reject paths containing traversal segments in
normalizePackageAppFetchPath before constructing the hosted URL, while
preserving existing normalization for safe paths. In
packages/worker/src/mcp/capabilities/packages/package-app-fetch.node.test.ts
lines 39-51, add coverage passing a path containing .. and assert that the
capability rejects it rather than relying on the permissive parser mock.

In
`@packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.ts`:
- Around line 108-110: Update the capability metadata in the package
subscription dispatch definition to set destructive to true. Keep readOnly and
idempotent unchanged so callers require confirmation for this real
side-effecting operation.
- Around line 286-301: Update the synthetic response construction around the
succeeded branch to apply the established response-size bounding logic to
body['result'] before assigning it to result. Ensure the MCP tool output never
returns an unbounded structured result, while preserving the existing error path
and response metadata.

In `@packages/worker/src/package-invocations/subscription-envelope.ts`:
- Around line 46-51: Update isMissingPackageModuleError to recognize the new
“does not declare subscription” wording emitted by
resolvePackageModuleResolution, while preserving the existing
missing-subscription detection behavior so these failures continue mapping to
404 subscription_not_found instead of the generic 500 path.

---

Outside diff comments:
In `@packages/worker/src/package-invocations/subscription-dispatch.ts`:
- Around line 66-73: Validate the raw input.payload shape before calling
stripUntrustedSubscriptionEnvelopeFields: reject provided arrays and non-object
JSON values with the existing error, while allowing null/omitted payload
according to current defaults. Then sanitize only the validated object and
preserve the existing payload flow for valid object inputs.

---

Nitpick comments:
In
`@packages/worker/src/mcp/capabilities/packages/package-app-fetch.node.test.ts`:
- Around line 39-51: Add a test in the package-app fetch suite that supplies a
path containing “..” through the fetch flow and asserts the resulting
packagePath.kodyId matches the URL-normalized target. Ensure the case exercises
the real parsePackageAppPath behavior rather than relying on the permissive
mock, while preserving existing test setup and assertions.

In `@packages/worker/src/mcp/capabilities/packages/package-app-fetch.ts`:
- Around line 162-172: Update encodePackageAppFetchBody and the corresponding
outputSchema to include an explicit body encoding discriminator, such as whether
body is text or base64. Ensure both strict UTF-8 failure cases—binary
textual-content responses and truncated multi-byte sequences—return the base64
discriminator, while successfully decoded responses return the text
discriminator; update all affected output construction paths, including the
additional location noted in the comment.

In
`@packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.node.test.ts`:
- Around line 195-258: Add a test alongside the replay test for the
topic-mismatch path in packageSubscriptionDispatchCapability.handler: mock the
stored message with classification "quarantined", request
email.message.received, and assert the handler rejects. Also verify
mocks.invokePackageSubscription was not called.

In
`@packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.ts`:
- Line 35: Update package-subscription dispatch to import and reuse
inboundEmailReceiptTopic from the shared email package-subscriptions module,
removing the local duplicate declaration while preserving expectedTopic replay
validation.

In `@packages/worker/src/package-invocations/subscription-dispatch.node.test.ts`:
- Around line 1-16: Reset hoisted mocks before each test to prevent call history
from leaking between cases. In
packages/worker/src/package-invocations/subscription-dispatch.node.test.ts
(lines 1-16), add a beforeEach after the dynamic import that clears
mocks.invokeSavedPackageModule. In
packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.node.test.ts
(lines 5-16), add a beforeEach after the dynamic import that clears all mocks
and reapplies the default resolved value for invokePackageSubscription.

In `@packages/worker/src/package-runtime/package-app-synthetic.node.test.ts`:
- Around line 30-52: Export applyTrustedPackageAppDispatch from
package-app-serve.ts and update the test to invoke that production helper
instead of manually copying its header-setting logic. Assert the real trusted
dispatch flow strips the synthetic header via createPackageCodeRequest first,
then reapplies it through applyTrustedPackageAppDispatch, preserving the
expected false-before-dispatch and true-after-dispatch results.

In `@packages/worker/src/package-runtime/package-app.ts`:
- Around line 605-607: Update the generated isSyntheticPackageAppRequest helper
in packages/worker/src/package-runtime/package-app.ts:605-607 to interpolate
packageAppSyntheticHeaderName and packageAppSyntheticHeaderValue from
package-app-synthetic.ts instead of hard-coded literals. In
packages/worker/src/package-runtime/package-app-synthetic.node.test.ts:54-63,
replace the brittle readFileSync source-text assertions with a behavioral
app_fetch run-metadata check, or assertions that use the interpolated constants.
🪄 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: 5a308125-3b8e-43cb-a6b3-7ed621005cc0

📥 Commits

Reviewing files that changed from the base of the PR and between 4451ccb and 0a4934a.

📒 Files selected for processing (35)
  • docs/contributing/decisions/0013-synthetic-package-requests.md
  • docs/contributing/decisions/index.md
  • docs/guides/package-authoring.md
  • docs/guides/package-subscriptions.md
  • docs/use/index.md
  • docs/use/package-app-fetch.md
  • docs/use/synthetic-event-dispatch.md
  • packages/worker/src/app/handlers/package-app.ts
  • packages/worker/src/app/package-app-origin.ts
  • packages/worker/src/app/package-app-origin.workers.test.ts
  • packages/worker/src/email/package-subscriptions.ts
  • packages/worker/src/mcp/capabilities/packages/domain.ts
  • packages/worker/src/mcp/capabilities/packages/package-app-fetch.node.test.ts
  • packages/worker/src/mcp/capabilities/packages/package-app-fetch.ts
  • packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.node.test.ts
  • packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.ts
  • packages/worker/src/mcp/capabilities/packages/publish-external-push.node.test.ts
  • packages/worker/src/mcp/capabilities/packages/publish-external-push.ts
  • packages/worker/src/mcp/tools/search-entity-plugins/package.ts
  • packages/worker/src/mcp/tools/search-format.node.test.ts
  • packages/worker/src/package-invocations/common.ts
  • packages/worker/src/package-invocations/idempotent-module-invocation.ts
  • packages/worker/src/package-invocations/module-artifacts.ts
  • packages/worker/src/package-invocations/module-execution.ts
  • packages/worker/src/package-invocations/service.ts
  • packages/worker/src/package-invocations/subscription-dispatch.node.test.ts
  • packages/worker/src/package-invocations/subscription-dispatch.ts
  • packages/worker/src/package-invocations/subscription-envelope.node.test.ts
  • packages/worker/src/package-invocations/subscription-envelope.ts
  • packages/worker/src/package-registry/package-test-hints.node.test.ts
  • packages/worker/src/package-registry/package-test-hints.ts
  • packages/worker/src/package-runtime/package-app-serve.ts
  • packages/worker/src/package-runtime/package-app-synthetic.node.test.ts
  • packages/worker/src/package-runtime/package-app-synthetic.ts
  • packages/worker/src/package-runtime/package-app.ts

Comment thread docs/guides/package-subscriptions.md Outdated
Comment thread docs/use/package-app-fetch.md
Comment thread docs/use/synthetic-event-dispatch.md Outdated
Comment thread packages/worker/src/mcp/capabilities/packages/package-app-fetch.ts
Comment thread packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.ts Outdated
Comment thread packages/worker/src/package-invocations/subscription-envelope.ts
cursoragent and others added 2 commits August 8, 2026 14:37
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
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.

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 28806bd. Configure here.

kodyId: input.kodyId,
hasApp: input.hasApp,
}),
...(testHints ? { test_hints: testHints } : {}),

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.

Already published URL hints mismatch

Medium Severity

For already_published, test_hints come from the published manifest while hosted_app_url still keys off the saved row’s hasApp. Fresh published responses use manifest-derived hints for both. The same publish payload can omit hosted_app_url but still advertise package_app_fetch, or the reverse.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 28806bd. Configure here.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@kody-bot
kody-bot merged commit c6d9536 into main Aug 8, 2026
10 checks passed
@kody-bot
kody-bot deleted the cursor/kody-synthetic-requests-9ee8 branch August 8, 2026 14:53
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