Remove the ambient storage binding from package invocation contexts - #828
Conversation
|
Warning Review limit reached
Next review available in: 11 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. 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 (10)
✨ Finishing Touches🧪 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 |
|
🔎 Preview deployed: https://kody-pr-828.kody-a99.workers.dev Worker: Mocks:
|
Problem
Final stage of removing legacy ambient-storage-in-package-code. The staged
plan so far: #816 shipped
packageStorage()(package-bucket storage thatsurvives static imports), #817 prescribed it as the one storage rule for
saved-package code and added the advisory check nudge, and #820 escalated
that nudge to a failing repo check so new publishes cannot introduce the
pattern. The removal itself was gated on an audit of what is actually
published.
The gate is verified. An ad hoc artifact audit across ALL production
users — 26 users, 74 published packages, every current published bundle
artifact scanned from
BUNDLE_ARTIFACTS_KV— found ZERO remainingambient-storage dereferences in invocation-context artifacts. Every flagged
package has been migrated and republished.
Approach
Remove the
storageToolswiring that bound ambientstorageto the packagebucket in package-invocation contexts. Exactly two sites bound it (the ones
catalogued in #820's PR body):
packages/worker/src/package-invocations/service.ts— therunBundledModuleWithRegistryoptions for package export AND subscriptionhandler runs (one shared call site) no longer pass
storageTools: { storageId: buildPackageInvocationStorageId(...), writable: true }.packages/worker/src/package-retrievers/service.ts— retriever runs nolonger pass the read-only equivalent.
After removal, ambient
storagein package-invocation context is simplyabsent: the storage helper prelude is omitted, so
storagereaches thesandbox as
undefined, guarded access (if (storage) { ... }) keepsworking, and guard-less access gets the #812/#814
runtime_helper_unboundstructured error whose nextStep already leads with
packageStorage()(from#817). No new error machinery was needed — omitting the binding activates the
existing path.
buildPackageInvocationStorageId/buildPackageRetrieverStorageIdremain:they still feed
callerContext.storageContext(package-secret scoping andapproval URLs) and runtime-debug metadata, neither of which is the
kody:runtimeambient binding. Comments at both call sites say so.Explicitly unchanged
storageId— the prescribed ambientuse (
mcp/tools/execute.ts,mcp/capabilities/meta/execute.ts).(
jobs/service.ts,package-runtime/package-service.ts) — run-scopedscratch space (
job:<id>,service:<pkg>:<name>), a distinct feature, notthe legacy package-bucket binding.
packageStorage()— now the only way package code reaches its bucket.Judgment calls
writable: falsebindingconstrained only the ambient helper:
packageStorage()has been writablein retriever context since Add
packageStorage(): package-bucket storage that survives static imports #816 (retrievers passpackageContext, whichgrants the package id;
createPackageStorageKodyToolsalways buildswritable tools). So the read-only ambient binding was not a real safety
boundary, and nothing replaces it — retrievers staying read-mostly is a
convention, now stated in
docs/contributing/packages-and-manifests.mdandat the call site.
packageStorage()in retriever context is covered by theexisting skills-retriever-style workers tests and the reworked two-package
test.
pattern — confirmed, not removed. Apps run in their own worker; their
storagecomes fromcreateRuntimeinpackage-runtime/package-app.ts(
createStorageProxy(runtimeBridge, packageId)) over host-pinned bridgeRPCs — not from the per-run
storageTools/helper-prelude binding this PRremoves. Note for a possible follow-up: the app runtime does not provide
packageStorage()(__kodyPackageStorageis absent from the app runtimeobject), so app code's storage access stays on the app bridge's ambient
shape; aligning apps with the prescription would mean adding
packageStorageto the app runtime bridge first.Docs
docs/use/packages.md— the legacy section is now "Ambientstorageinpackage code (removed)": the binding no longer exists in invocation contexts,
with the
runtime_helper_unbound/packageStorage()remedy pointer, and thepackageStorage()bullets no longer describe ambient storage as bound in thepackage's own runtime.
docs/use/execute.mdand the execute tool's sandboxtext (
mcp/tools/execute.ts) state that saved-package invocation runs bind noambient storage.
docs/contributing/packages-and-manifests.mdupdates theprescription bullet (removal shipped, audit cited) and the retriever
read-only line.
docs/use/email-primitives.mdand the service-pattern guideanchor follow.
Tests
package-storage.workers.test.ts(realbuildKodyModuleBundlebundles,end-to-end):
post-removal — no
storageTools— and assertstypeof storage === 'undefined'in the host package while both packages'packageStorage()buckets still resolve correctly.
legacy package code that dereferences ambient
storageguard-lesslyyields the structured
runtime_helper_unboundhint withhelperName: 'storage'and a nextStep that mentionspackageStorage()before the
storageIdalternative.package-invocations/service.node.test.ts— the subscription invocationtest now asserts
storageToolsis NOT passed torunBundledModuleWithRegistry(was asserting the package-bucket binding).TypeError stays bare" is unchanged: its bound case models ad hoc execute
with a caller
storageId, which this PR does not touch.Local gate:
npm run typecheck,npm run lint(0 errors, 21 pre-existingwarnings),
npm run format:check, andnpm run test(319 files / 998 tests)all pass on
main@3f4a43b1. The Playwright/MCP E2E halves ofnpm run validatewere not run locally; CI covers them.System recap — removes a runtime binding (medium risk, audit-gated)
Mode: recap · Base:
main@3f4a43b1· Head:18afe9afClassification: removes — package-invocation runs (exports, subscription
handlers, retrievers) stop binding ambient
storage; the change isdeliberately audit-gated: zero currently-published invocation-context
artifacts dereference the binding (26 users / 74 packages scanned).
Primitives touched
package-invocationsstorageToolspackage-retrieversstorageToolspackage-storagepackageStorage()is now the only package-bucket pathmcp-serverSystem map
Package code reaches its bucket through
packageStorage()only; the removedambient binding falls back to the existing unbound-helper error machinery, so
hypothetical stragglers get an actionable structured hint instead of a bare
TypeError.
Legend: green = composes · amber = extended by this PR · red = removed
binding · gray = context.
Before / after
Legacy package code doing
import { storage } from 'kody:runtime'thenstorage.get(...), invoked as a package export:storagewas bound to the package bucket(
package:<packageId>), identical topackageStorage().storageisundefined; the run fails with the structuredruntime_helper_unbounderror whose remedy leads withpackageStorage()(identical bucket, so the fix is a rename).
if (storage) { ... }guardsobserve
undefinedand keep working. Per the completed audit, no publishedartifact takes this path today.
Invariants
Bucket naming, provenance grants, entitlement enforcement, and per-user
isolation are untouched — the diff removes two
storageToolsinputs, updatestests and docs, and changes no storage-runner or grant code.
callerContext.storageContext(secret scoping) still carries the packagestorage id. Job/service scratch buckets and caller-bound execute storage
behave exactly as before. Repo checks (#820) prevent the removed pattern from
being republished.