Repository navigation
fix(objectql): a package registered again lists its navigation contributions once, not once more per registration - #30656
Conversation
…n contributions instead of appending them An artifact reload re-registers every package of the artifact through registerApp, whose manifest step pushed each navigationContributions entry once more per registration, so every module's entries were listed twice in the app until a restart. The step now replaces the package's whole set in place (fresh-boot order kept), and an uninstall drops it. Claude-Session: https://claude.ai/code/session_01Utp6CZUUc37kySmxP9uGue Co-authored-by: Claude <noreply@anthropic.com>
… for the nav contribution replace Claude-Session: https://claude.ai/code/session_01Utp6CZUUc37kySmxP9uGue Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CZUUc37kySmxP9uGue Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 18 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 21 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 82a2684f1590da420c39d34fc667b32c21bb3a02 && git checkout 82a2684f1590da420c39d34fc667b32c21bb3a02
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b8929f486de2aaa535dbb9db6f510e944f77be15 0be4e9a0b7f79b7fbedf84b70bc8cf22a01755a3 && git checkout -B drift-repro b8929f486de2aaa535dbb9db6f510e944f77be15 && git merge --no-ff 0be4e9a0b7f79b7fbedf84b70bc8cf22a01755a3
node scripts/docs-audit/affected-docs.mjs --json b8929f486de2aaa535dbb9db6f510e944f77be15
|
|
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL 维护者速读
席位核对 — ACCEPT · domain:server#2 · head
|
Requested by Bill · project thread
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL
维护者速读
navigationContributions)改为按包整体替换,不再每注册一次就追加一份;卸载包时一并清掉。os dev重编产物触发的重载会把产物里每个包都重注册一次),每次重载多一份,正好是「一个模块操作,所有模块都重复」。registerAppNavContribution不变。回滚即还原本 PR。packages/objectql/src/registry.ts的setPackageNavContributions;packages/objectql/src/engine.ts导航折叠那段(约 L2402);registry-nav-contributions.test.ts末尾新增的 package enable: after a module package is disabled and enabled again, every module's navigationContributions entries appear twice in the console sidebar until the server restarts #30581 describe。pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/registry-nav-contributions.test.ts,预期 13 个全绿,其中 package enable: after a module package is disabled and enabled again, every module's navigationContributions entries appear twice in the console sidebar until the server restarts #30581 下 7 个。Fixes #30581
Clause-②: no — the served app navigation returns to what a fresh boot serves; nothing a caller sends is refused
Metadata-protocol: none
What changes
The card's mechanism, measured and corrected. On the real kernel (
MetadataPlugin+ObjectQLPlugin+AppPlugin,PATCH /packages/:id/disable|enablethroughHttpDispatcher,origin/main6701c08; the 18.3.0 enable door is the same code), an app plus two modules (ordersintog_sales,serviceintog_service):GET /meta/appnavigationg_sales[accounts,orders] g_service[cases]orders, twicemetadata:reloadedwith the artifactg_sales[accounts,orders,orders] g_service[cases,cases]manifestdoorSo disable and enable do not re-fold anything. What doubles every module's entries is a re-registration of the package bodies:
ObjectQLPlugin.refreshReloadedPackagesre-registers every package of the artifact onmetadata:reloaded(fired by theos devartifact watcher and the HMR reload POST), and the manifest step ofObjectQL.registerAppcalledregisterAppNavContribution, which only appends. A restart starts from one copy again. HotCRM's measurement most likely saw a reload land between the disable and the enable.The fix: derive, don't append.
SchemaRegistry.setPackageNavContributions(packageId, contributions)(new) makes the given list the package's whole set of contributions, in every app. It replaces in place: an app's list keeps the package's entries where the first registration put them, so ties in the fold (equal priority, the common case at the default 200) resolve in fresh-boot order. An app the package newly targets gets them at the end; an app it no longer names loses them.ObjectQL.registerAppcalls it for every body with an id, with an empty list when the body declares none, so a body that dropped its contributions clears them. A body with no id keeps the old additive call.uninstallPackageclears the package's contributions. Before, an uninstalled package's entries stayed in the app: with no package row left, the fold's disabled check could not withhold them.registerAppNavContributionis unchanged (additive, one contribution at a time).registry-module-mock.ts) answers the new member.The fold itself (
applyNavContributions) is untouched, and so areenablePackage/disablePackageand theonPackageEnabledannouncement (#30377): no dedupe by label or path after the fact.Tests
New top-level describes in the existing harness files.
packages/objectql/src/registry-nav-contributions.test.ts, throughObjectQL.registerApp(app + two contributing modules + one module with none, equal priorities), each compared with a fresh registration's snapshot taken in the same test:onPackageEnabledlistener hears the module exactly once per enable;packages/runtime/src/domains/packages-disable-app-module.test.ts, through the real doors: two cycles ofPATCH …/disable,metadata:reloadedwith the artifact,PATCH …/enable, then the suite registered again through themanifestservice; the servedGET /meta/app/qs_appgroup equals the fresh boot's after each step.Ablations (one per negative pin, objectql rebuilt each leg, marker proved in dist/)
Each through
scripts/ablation-replace.mjs,node scripts/ablation-dist-preflight.mjs objectql ABLATION_30581green before the run, restored to the HEAD blob afterwards.engine.ts: the body path back to the additive call. objectql: 4 failed (two reloads, one module re-registered, disable/enable with reload, changed body); runtime: the package enable: after a module package is disabled and enabled again, every module's navigationContributions entries appear twice in the console sidebar until the server restarts #30581 case failed,enabled again #1: expected [ 'nav_accounts', 'nav_orders', …(1) ] to deeply equal [ 'nav_accounts', 'nav_orders' ].registry.ts: replace at the end instead of in place. objectql: 2 failed (one module re-registered, changed body); runtime stays green (one contributor there).registry.ts: the uninstall clear removed. objectql: 1 failed (uninstall).Restore leg: objectql rebuilt,
ablation-dist-preflight.mjs objectql ABLATION_30581 --absentgreen, working tree clean against HEAD.Verification
All on the final head
0be4e9a0b(branch merged withorigin/main14c67d4), dogfood closure rebuilt first.pnpm --filter @objectstack/objectql test: Test Files 555 passed (555), Tests 11057 passed (11057);typecheckexit 0.pnpm --filter @objectstack/runtime test: Test Files 544 passed (544), Tests 8702 passed, 19 skipped (8721);typecheckexit 0.pnpm --filter @objectstack/dogfood test): VERDICT command-exit 0, Test Files 420 passed, 3 skipped (423), Tests 3504 passed, 36 skipped (3540).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 63 families, each run with its exit code recorded before any pipe, all 63 exit 0;--ranreconciliation: 63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN.eslint --no-inline-configon the five changed source and test files: exit 0 (the repo-widepnpm lintis left to CI'sLint & Repo Gates).check-nul-bytes: exit 0.Acceptance notes
registerAppNavContributionkeeps appending. Today only tests call it; a plugin that calls it from code twice still gets two copies, as before.