v0.23.1
·
24 commits
to main
since this release
Patch Changes
- Chat writes can no longer resurrect a deleted chat, the delete cascade is bounded, and cookie-only server-action calls get a 401 instead of a 500.
updateChatandappendMessagenow re-check the chat throughgetChatand returnfalseinstead of writing when it is gone (records.update/records.createare upserts, so an unguarded post-stream write recreated a deleted chat as a title-less ghost). Newrecords.deleteWheretool on the room tools API —{ collection, where, limit }→{ deleted }, bounded per call, same delete permission check asrecords.deleteand refusing the whole page if any match is denied — letsdeleteChatCascadespend one subrequest per page instead of one per message. The scaffoldedPOST /api/actions/:namereads the bearer token defensively: an authenticated caller with noAuthorizationheader is now refused 401 rather than throwing, and the file carries the trust-model note that action tools execute as the caller with RBAC off. - CLI honesty pass:
deepspace test run's default suite now names every spec file it skipped (prose line +skippedSpecsin--json);pushrefuses a GitHub-sourced app in its preflight, naming the repository, instead of reconstructing a repository-less sentence from git's discarded 422;push'sno_commitsrefusal andapp init --new-idboth name their next step; and a scaffold whose identity registered no longer tells you toauth login. deploysettles two refusals before it builds and uploads: a collaborator whose app has no liveAPP_OWNER_JWTis refused with the platform's own sentence and a newowner_jwt_missingcode (the deploy worker's commit-time 409 now carries that code too), and a changed wranglernameis confirmed or refused (rename_required) from the registered host instead of after the upload.- Verification-pass fixes:
records.deleteWhererefuses awherewhose keys name no field (an unknown key used to be silently dropped, deleting an unfiltered page), refuses schemaless collections and non-numeric limits, and pages after the RBAC read filter; a users schema with an explicitread: falsekeeps an empty roster for that role, and a role change now refreshes every connected roster; the scaffold's chat route stops (rather than continuing) when a mid-stream delete makesappendMessagereturnfalse, andPATCH /api/ai/chats/:idanswers 404 for a write that never landed; the build plugin's.dev.varssweep clears every worker dir before reporting an unsafe one; a missingwrangler.tomlisnot_in_app_repowith a remedy instead ofinvalid_config, anddetectAppNameshares the one reader;push'sno_commitsnames the__APP_ID__case only when the placeholder is present (with theapp initaction);app initships agit commit … wrangler.tomlaction when it did not commit; secrets refusals keep the server'scodein--json;app listshows a renamed app's reserved old name. - AX-pass fixes:
secretsreads on an unregistered app id refuseapp_not_registeredinstead of answering an empty list; the scaffold registers its AI chat routes only when the copilot schemas are present (they could only 500 without them) and names the valid model ids (unknown_model) on a badmodelId;create-deepspacesays which plane it registered the app on and which id; deploy/push/pull/update/workspace share onenot_in_app_reposentence;lintSchemaswarns when a role uses the'team'level with noteam_memberscollection; an implausible command guess is no longer handed back as an executableactionandlogoutcounts as destructive;transfer acceptnames collaborators inherited from the previous owner;secrets set/upload/delete --jsoncarryappliesAtDeploy: true;rollback --helpstates secrets are kept;app updateleaves afile:/link:deepspace spec alone. - Review follow-ups:
executeQuery'slimitnow counts the records the caller receives — when a per-row read filter applies it scans in bounded batches (a 5,000-row scan budget per call — worst case one batch ofmax(limit, 200)rows past it, and alimitabove the budget is honored as asked — reported to callers that ask, solimitstays a bound on work) instead of letting a SQLLIMITend the result early, andread: 'own'/read: truepush into SQL like'team'already did (only when the declared field resolves to a real column — a misdeclared one falls back to the per-row check instead of throwing) (sorecords.deleteWherepages boundedly under RBAC, and a partially-failed page is refused rather than read as "no more matches"); a newroster: 'read-policy'users-schema option scopes theuser.listroster to the caller's read policy for tenant/team-partitioned apps; ranked command guesses are never handed back as executable actions (only an exact quoted path is);classifyPushTransportFailurekeeps a last-resort 422 →source_managed_by_githubfallback;transfer acceptreportsinheritedCollaboratorson replays too;app initoffers itsgit commitaction only when wrangler.toml is actually uncommitted; one anonymous-identity helper (isAnonymousUserId). - The users roster (
useUsers()) now updates live: a room pushesuser.listto every other connected socket when a user registers for the first time or changes name, avatar, or role — the client only asked once per connection, so a peer who joined after a tab connected rendered as "Unknown" there until reload. app listnow surfaces incoming ownership offers and an undeployed app's reserved name,transfer acceptreports a replayed acceptance as a replay instead of a fresh handshake, andtransfer status/offername the offerer and say that acceptance ends the offerer's access- Fix
useUsers()returning an empty roster (chat authors rendered as "Unknown") in rooms without an appusersschema: the users list now returns every registered user's public identity to non-admin callers instead of filtering by the schema's row policy, which still guards full-row reads. - One wrangler.toml reader behind the CLI and
deepspace/build— one error shape (WranglerConfigError; a missing file isnot_in_app_repo, a broken oneinvalid_config). New refusal,duplicate_app_id: a wrangler.toml that sets the sameDEEPSPACE_APP_IDunder more than one section ([vars]and[env.<name>.vars]) is refused by every CLI command and byvite build— each environment is its own app; rundeepspace app init --env <name>to mint one for it.test accounts recoverrestores the display name the platform now returns.