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 && (manifest as any)?.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

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 3, 2026 6:54pm

Request Review

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

github-actions Bot 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 main Jul 3, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the fix/durable-package-install branch July 3, 2026 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

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