Skip to content

fix(protocol): persist versionless package installs; drop sys_packages on delete - #2540

Merged
os-zhuang merged 5 commits into
mainfrom
fix/durable-package-install
Jul 3, 2026
Merged

fix(protocol): persist versionless package installs; drop sys_packages on delete#2540
os-zhuang merged 5 commits into
mainfrom
fix/durable-package-install

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#2532 — runtime-created base packages vanished from GET /packages after a server restart (their metadata and tables survived; only the package registration was lost).

Root cause

protocol.installPackage writes both package stores (in-memory registry + durable sys_packages via the package service), but the durable half was guarded by:

if(pkgSvc?.publish&&(manifestasany)?.version){}

Runtime-created bases arrive versionless ({id, name} from the builder / Setup) — so persistence was silently skipped for exactly those packages. service-package hydrates sys_packages back into the registry at boot, so no row = no package after restart.

Fix

  • Default the version (0.1.0) instead of skipping persistence — the registry and the durable row must agree.
  • A failed persist now logs loudly (persist FAILED … will not survive a restart) instead of silently succeeding in-memory (kept non-fatal by design).
  • deletePackage drops the sys_packages record — otherwise, once installs persist, an uninstalled package would RESURRECT at the next boot via hydration.

Verified

  • Unit: durable-package.test.ts — versionless persists with defaulted version; explicit version untouched; persist failure non-fatal + loud; delete drops the durable row. metadata-protocol suite 8/8.
  • E2E (three boots, one db): install versionless → restart → still listed · DELETE (200) → restart → stays gone (no resurrection).

Found while browser-demoing the login→build journey; workaround (re-POST same id) no longer needed.

🤖 Generated with Claude Code

…s on delete (#2532)
installPackage's durable half was guarded by `pkgSvc?.publish &&
manifest.version`, silently skipping every versionless runtime-created base
({id,name} from the builder/Setup) — in-memory only, gone after restart, while
metadata/tables survived. Default the version (0.1.0) instead of skipping, log
persist failures loudly, and make deletePackage drop the sys_packages row so
uninstalled packages don't resurrect at boot via service-package hydration.
Verified: unit tests (8/8) + three-boot E2E on one db — install → restart →
still listed; DELETE → restart → stays gone.
Fixes#2532
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented Jul 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 3, 2026 6:54pm

Request Review

@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation tests tooling labels Jul 3, 2026
@github-actions

github-actionsBot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/metadata-protocol, @objectstack/objectql, @objectstack/runtime.

25 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/cloud-artifact-api.mdx(via packages/runtime)
  • content/docs/concepts/core/services.mdx(via @objectstack/objectql)
  • content/docs/concepts/implementation-status.mdx(via @objectstack/objectql, @objectstack/runtime)
  • content/docs/concepts/metadata-lifecycle.mdx(via @objectstack/objectql)
  • content/docs/concepts/north-star.mdx(via packages/runtime)
  • content/docs/concepts/packages.mdx(via @objectstack/objectql, @objectstack/runtime)
  • content/docs/guides/api-reference.mdx(via @objectstack/runtime)
  • content/docs/guides/authentication.mdx(via @objectstack/objectql, @objectstack/runtime)
  • content/docs/guides/cloud-deployment.mdx(via @objectstack/runtime)
  • content/docs/guides/deployment-vercel.mdx(via @objectstack/objectql, @objectstack/runtime)
  • content/docs/guides/driver-configuration.mdx(via @objectstack/runtime)
  • content/docs/guides/formula.mdx(via packages/objectql)
  • content/docs/guides/hook-bodies.mdx(via @objectstack/runtime)
  • content/docs/guides/kernel-services.mdx(via @objectstack/objectql)
  • content/docs/guides/objectql-migration.mdx(via @objectstack/objectql)
  • content/docs/guides/packages.mdx(via @objectstack/objectql, @objectstack/runtime)
  • content/docs/guides/plugin-chatbot-integration.mdx(via @objectstack/runtime)
  • content/docs/guides/plugins.mdx(via @objectstack/objectql)
  • content/docs/guides/production-readiness.mdx(via @objectstack/runtime)
  • content/docs/guides/single-project-mode.mdx(via @objectstack/runtime)
  • content/docs/protocol/objectos/http-protocol.mdx(via @objectstack/runtime)
  • content/docs/protocol/objectos/index.mdx(via @objectstack/objectql, @objectstack/runtime)
  • content/docs/protocol/objectos/lifecycle.mdx(via @objectstack/runtime)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/objectql)
  • content/docs/releases/v9.mdx(via @objectstack/objectql)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…es persist too
The duplicate path did a bare registry.installPackage — the copied base would
vanish from GET /packages on restart, same #2532 mechanics. Route through
installPackage to get the durable sys_packages write (and version default).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…re scope-less
Two more holes found browser-verifying the duplicate journey:
1. dispatcher-plugin never MOUNTED the newer handlePackages branches — duplicate
(ADR-0070 D4), adopt-orphans (D5), discard-drafts, commits / commit-revert /
rollback (ADR-0067) all 404'd at the hono layer on every serve stack despite
working handler code. Mount them all; routing only.
2. duplicatePackage copied the SOURCE manifest verbatim — including scope
(e.g. 'project'), branding the writable copy as read-only in every
writability heuristic. The duplicate's manifest is now scope-less.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…istence
Two objectql protocol tests asserted the pre-#2540 behavior:
- installPackage used to SKIP sys_packages persistence for versionless
manifests (the {id,name} shape the builder/Setup create), so base
packages vanished on restart (#2532). It now defaults version to
'0.1.0' and persists. Test updated to expect persistence with the
defaulted version.
- duplicatePackage used to do a bare registry.installPackage write and
keep the source scope. It now routes through installPackage (so the
copy also lands in sys_packages) and strips scope (a duplicate must be
a writable base, not inherit the source's read-only scope). Test
updated to assert the routed call + scope stripped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@os-zhuang
os-zhuang merged commit b5be479 into mainJul 3, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the fix/durable-package-install branch July 3, 2026 19:16
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DB-created base packages vanish from GET /packages after server restart

1 participant

@os-zhuang