Add synthetic requests for package self-testing - #1315
Conversation
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
|
Warning Review limit reached
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 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (16)
📝 WalkthroughWalkthroughAdds ChangesSynthetic package 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
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
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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>
|
🔎 Preview deployed: https://kody-pr-1315.kody-a99.workers.dev Worker: Mocks:
|
There was a problem hiding this comment.
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 winSanitization now runs before shape validation, so the non-object payload guard is dead.
stripUntrustedSubscriptionEnvelopeFieldsreturns{ ...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.dispatchaccepts a non-object payload and forwards a mangled object to schema validation and to subscribers. Before this change the caller receivedevents.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 winThe synthetic header literals are duplicated in generated worker source and pinned by a source-text test.
package-app-synthetic.tsownspackageAppSyntheticHeaderNameandpackageAppSyntheticHeaderValue, 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: interpolatepackageAppSyntheticHeaderNameandpackageAppSyntheticHeaderValueinto the generatedisSyntheticPackageAppRequesthelper instead of hard-coding'Kody-Synthetic'and'true'.packages/worker/src/package-runtime/package-app-synthetic.node.test.ts#L54-L63: replace thereadFileSyncsubstring assertions with a behavioral check of theapp_fetchrun 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 winThis test re-implements the production logic instead of exercising it.
Lines 46-50 duplicate the body of
applyTrustedPackageAppDispatchinpackage-app-serve.ts. The test therefore proves only thatisPackageAppSyntheticRequestreads a header that the test itself set. IfservePackageAppRequeststopped callingapplyTrustedPackageAppDispatch, or applied it beforecreatePackageCodeRequest, this test would still pass.Export
applyTrustedPackageAppDispatchfrompackage-app-serve.tsand 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 winThe output does not tell the caller whether
bodyis text or base64.
encodePackageAppFetchBodyreturns either decoded text or base64, butoutputSchemaexposes onlystatus,headers,body, andtruncated. The caller must guess the encoding fromContent-Type, and that guess fails in two cases:
- A binary response with a textual
Content-Typethat fails strict UTF-8 decoding returns base64.- Truncation at line 417 can split a multi-byte UTF-8 sequence, so
decodeUtf8Bodyreturnsnulland atext/htmlresponse 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 winThe mocked
parsePackageAppPathhides the real path-resolution behavior.The mock uses a permissive regex. The real implementation in
package-app-serve.tsalso runsgetUsernameFormatValidationErroranddecodeURIComponent. No test in this file exercises the real parser against apathvalue thatnew URL()normalizes, so the..retargeting case reported onpackage-app-fetch.tslines 89-93 is not covered.Add a case that passes a
pathcontaining..and asserts the resultingpackagePath.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 winAdd 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.tsLines 237-241). No test exercises that branch.Add a test that mocks a message with
classification: 'quarantined'and requestsemail.message.received. Assert the handler throws and thatinvokePackageSubscriptionis 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 winBoth new test suites omit a mock reset between tests. Each suite declares hoisted mocks and never clears them, so recorded calls accumulate.
toHaveBeenCalledWithmatches 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 abeforeEachafter the dynamic import that callsmocks.invokeSavedPackageModule.mockClear().packages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.node.test.ts#L5-L16: add abeforeEachafter the dynamic import that clears all mocks and re-applies the defaultinvokePackageSubscriptionresolved 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 winImport the receipt topic from
#worker/email/package-subscriptions.ts.
inboundEmailReceiptTopicis defined inpackages/worker/src/email/package-subscriptions.ts, andexpectedTopicuses this value during replay validation. Import the shared constant instead of redeclaringemail.message.receivedlocally 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
📒 Files selected for processing (35)
docs/contributing/decisions/0013-synthetic-package-requests.mddocs/contributing/decisions/index.mddocs/guides/package-authoring.mddocs/guides/package-subscriptions.mddocs/use/index.mddocs/use/package-app-fetch.mddocs/use/synthetic-event-dispatch.mdpackages/worker/src/app/handlers/package-app.tspackages/worker/src/app/package-app-origin.tspackages/worker/src/app/package-app-origin.workers.test.tspackages/worker/src/email/package-subscriptions.tspackages/worker/src/mcp/capabilities/packages/domain.tspackages/worker/src/mcp/capabilities/packages/package-app-fetch.node.test.tspackages/worker/src/mcp/capabilities/packages/package-app-fetch.tspackages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.node.test.tspackages/worker/src/mcp/capabilities/packages/package-subscription-dispatch.tspackages/worker/src/mcp/capabilities/packages/publish-external-push.node.test.tspackages/worker/src/mcp/capabilities/packages/publish-external-push.tspackages/worker/src/mcp/tools/search-entity-plugins/package.tspackages/worker/src/mcp/tools/search-format.node.test.tspackages/worker/src/package-invocations/common.tspackages/worker/src/package-invocations/idempotent-module-invocation.tspackages/worker/src/package-invocations/module-artifacts.tspackages/worker/src/package-invocations/module-execution.tspackages/worker/src/package-invocations/service.tspackages/worker/src/package-invocations/subscription-dispatch.node.test.tspackages/worker/src/package-invocations/subscription-dispatch.tspackages/worker/src/package-invocations/subscription-envelope.node.test.tspackages/worker/src/package-invocations/subscription-envelope.tspackages/worker/src/package-registry/package-test-hints.node.test.tspackages/worker/src/package-registry/package-test-hints.tspackages/worker/src/package-runtime/package-app-serve.tspackages/worker/src/package-runtime/package-app-synthetic.node.test.tspackages/worker/src/package-runtime/package-app-synthetic.tspackages/worker/src/package-runtime/package-app.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ 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 } : {}), |
There was a problem hiding this comment.
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)
Reviewed by Cursor Bugbot for commit 28806bd. Configure here.
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>


Intent
Let package authors verify real app and subscription surfaces after publishing with platform-marked synthetic requests whose side effects remain real.
Summary
package_app_fetchandpackage_subscription_dispatchcapabilities with platform-only markers, package-runtime recursion guards, direct real-surface dispatch, stored-email replay, unique synthetic idempotency keys, and marker-aware run records.Testing
npm run validate— green (569 test files / 1,952 tests, 4 MCP tests, 7 Playwright tests, plus format, lint, typecheck, build, docs and boundary checks).System changes
System recap — extends existing primitives (medium risk)
Mode: recap · Base:
main@4451ccb· Head:b5ade3cClassification: extends — existing package app, subscription, package runtime, and MCP capability contracts gain platform-marked test invocations; no primitive is added.
Primitives touched
saved-packagespackage-appspackage-runtimepackage-events-dispatch-queueemailmcp-serverapp-uiSystem 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).
Invariants
packages.invoke.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 validategreen, PR CI green, production healthcheck/smoke/reindex green; remains: none.Summary by CodeRabbit
New Features
package_app_fetchto test published package apps with realistic requests and response details.package_subscription_dispatchto simulate declared subscription events, including stored email replay.Documentation