You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit c866c5a
Browse filesBrowse the repository at this point in the historyBrowse files
fix(cloud-connection): an install-local uninstall runs the protocol's registered uninstall cleanups, so the package's permission sets and their grants go with it
7
+
8
+
Clause-②: yes
9
+
10
+
`DELETE /api/v1/marketplace/install-local/:manifestId` removed the package's ledger entry and nothing else. After a restart the package's objects were gone, but its `managed_by: package` rows in `sys_permission_set`, and every grant of them, survived the uninstall. That broke ADR-0090's "No ghost grants" promise on this door.
11
+
12
+
The door now runs the uninstall cleanups that domain plugins register with the protocol (`registerUninstallCleanup`) once the ledger entry is gone. It uses the same registry and the same runner as the protocol's own uninstall, so `plugin-security`'s `security.package-permissions` cleanup removes the package's sets with their position and user bindings, and any cleanup registered later fires here too. The cleanups run with the package's manifest id and no organization, because an install-local package is installed for the whole runtime.
13
+
14
+
The response carries each outcome as `data.cleanups`, the way the protocol's uninstall reports them. A failed cleanup is reported there and named in the operator log with its remedy (install the package again, then uninstall it again). When the protocol cannot run the cleanups, the response says so as one failed `protocol.runUninstallCleanups` outcome. An uninstall that does not happen (a refused caller, an id this door never installed, a ledger write that fails) revokes nothing.
15
+
16
+
`@objectstack/metadata-protocol`: `ObjectStackProtocolImplementation` gains `runUninstallCleanups({ packageId, organizationId?, actor? })`, the one runner of the uninstall-cleanup registry. It runs every registered cleanup for the package and answers one `UninstallCleanupOutcome` per cleanup. It never throws: a failed cleanup is an outcome, and a thrown fault's driver text goes to the operator log, not into the outcome. `deletePackage` now calls it as its last step in place of its own loop, and its `cleanups` are unchanged. The only visible difference there is the log tag of a failed cleanup's warning, now `[protocol.runUninstallCleanups]` instead of `[protocol.deletePackage]`.
17
+
18
+
`@objectstack/cloud-connection` now declares its dependency on `@objectstack/metadata-protocol`, which it already received through `@objectstack/runtime`, for the cleanup outcome types.
`os migrate account-issuer`, `os migrate audit-metadata-bodies`, `os migrate meta --stored`, `os secret orphans`, `os secret rewrap` and `os storage orphans` answer a project whose database does not exist yet with empty work and exit 0, instead of exiting 1 on a refused read (#21552)
6
+
7
+
Clause-②: no
8
+
9
+
Each of these commands boots read-only by default: the schema sync is held back, and a missing SQLite file is opened as an empty in-memory stand-in. That boot already measures which tables the database lacks, because the held-back sync lists each one as a table to create. Each command then read the very tables it had just found missing, and the database refused the read. On a never-booted database (or a `--database-url` that points at one) every default run exited 1:
Each command now reads only the tables its boot found present. A table that does not exist holds nothing, so:
18
+
19
+
-`os migrate account-issuer` reports no account and no collision (`ok: true`), exit 0;
20
+
-`os migrate audit-metadata-bodies` reports nothing to rewrite, with `failures: 0`, exit 0;
21
+
-`os migrate meta --stored` reports no stored metadata to examine (`scanned: 0`, `clean: true`), exit 0;
22
+
-`os secret orphans` and `os secret rewrap` report no secret to act on, with every holder family enumerated rather than a gap, exit 0;
23
+
-`os storage orphans` reports no stranded file, exit 0.
24
+
25
+
Each names the tables it did not read: on stdout in human mode, on stderr under `--json`, where stdout stays one document. `os migrate account-issuer` is the one that recognises the refusal instead of asking the boot: its boot composes no auth plugin, so `sys_account` is never listed as a table to create. It recognises only the missing-table refusal for `sys_account`, with the shared `isMissingTableError` predicate.
26
+
27
+
A table that exists but lacks a column, and any other read that is refused, is still read and still refuses with exit 1. The write modes (`--apply`, `--delete`) are unchanged: they boot with the schema sync, so their tables exist before they read.
Nine migration-step rationale passages state their decisions in words instead of tracker numbers, and two registry comments cite the commit that decided them
6
+
7
+
Clause-②: no
8
+
9
+
The protocol 17 and protocol 18 step rationales are what `os migrate meta` shows per hop
10
+
and what the protocol upgrade guide prints. Nine of their passages named GitHub issues
11
+
that no longer exist, so an upgrading author met a number with nothing behind it. Each of
12
+
those passages now carries no number at all and says what was decided: why `mongo` and
13
+
`mongodb` are both accepted, why the form-view option `default` and `connector.errorMapping`
14
+
were retired, which earlier cleanup the import mapping `lookup` params finish, what the
15
+
memory driver's placeholder refusal extends, how the plugin manifest's `contributes`
16
+
members and `routes` were retired, and why the stack `themes` carrier and the
17
+
component-translation `submitLabel` key went. Two source comments of the migration
18
+
registry now cite the commit behind them. Text only: no migration step, entry, retired key
19
+
or def, conversion, schema, export or runtime behaviour changes.
Copy file name to clipboardExpand all lines: docs/protocol-upgrade-guide.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -65,7 +65,7 @@ Closing the same audit on the data side, `datasource.readReplicas` is removed (#
65
65
66
66
The datasource close-out also graduates the four legacy `datasource.config` spellings the shared driver factory still tolerated via undeclared read-side `??` fallbacks (#4456, the #4410 follow-up): sqlite `file`/`database` (use `filename`), postgres/mysql `connectionString` (use `url`) and `user` (use `username`), and mongo `uri` (use `url`) and `user` (use `username`). #4410 made the authoring gate reject each with a rename hint, but a runtime datasource persisted in `sys_metadata` before the gate kept working only because the factory read leniently — and deleting that tolerance without a conversion would have silently moved data (a stored sqlite `file:` row falls back to `:memory:`). The `datasource-config-driver-key-aliases` conversion rewrites the stored shape to the canonical keys at every rehydration seam, the factory now reads exactly one spelling per key, and the four `??` chains are deleted. Driver-aware by construction: `database` renames only under sqlite, where it aliased the file path — for every other driver it is a canonical key and is untouched. Retired from the load path not for lying but because the authoring gate already rejects the spellings loudly; the chain and the stored-row replay are the seams that accept them.
67
67
68
-
Finishing the same datasource surface, the canonical driver id `mongo` is renamed to `mongodb` (#6345). The two spellings have both been accepted since #4410 and both still are, so no boot breaks and no data moves — what changed is which one is CANONICAL, and that string is published as `DRIVER_CATALOG.id` and is what the Studio connection form writes into `datasource.driver`. Every row written before the rename therefore carries `mongo` while the form now emits `mongodb`, leaving one deployment with two spellings of one driver and any reader that matches a stored driver against the published catalog id silently missing the older rows. The `datasource-driver-mongo-to-mongodb` conversion converges the stored value at every rehydration seam; it stays on the LIVE load path (unlike the config-key aliases beside it) precisely because `mongo` is still legal — there is no loud rejection for it to pre-empt, and nothing to lose by converging early. The rename is what let the driver-selection id and the config-contract id become one string: `packages/spec`'s driver vocabulary is now a single table both boot hosts read, which closed the last fork where `OS_DATABASE_DRIVER=pg` booted under `os start` and was refused by `os migrate`. `turso`/libSQL joins the same table with a real config contract, so a libSQL `config` is validated instead of waved through.
68
+
Finishing the same datasource surface, the canonical driver id `mongo` is renamed to `mongodb`. The two spellings have both been accepted since `datasource.config` was first parsed against its driver's own contract, and both still are, so no boot breaks and no data moves — what changed is which one is CANONICAL, and that string is published as `DRIVER_CATALOG.id` and is what the Studio connection form writes into `datasource.driver`. Every row written before the rename therefore carries `mongo` while the form now emits `mongodb`, leaving one deployment with two spellings of one driver and any reader that matches a stored driver against the published catalog id silently missing the older rows. The `datasource-driver-mongo-to-mongodb` conversion converges the stored value at every rehydration seam; it stays on the LIVE load path (unlike the config-key aliases beside it) precisely because `mongo` is still legal — there is no loud rejection for it to pre-empt, and nothing to lose by converging early. The rename is what let the driver-selection id and the config-contract id become one string: `packages/spec`'s driver vocabulary is now a single table both boot hosts read, which closed the last fork where `OS_DATABASE_DRIVER=pg` booted under `os start` and was refused by `os migrate`. `turso`/libSQL joins the same table with a real config contract, so a libSQL `config` is validated instead of waved through.
69
69
70
70
The `script` flow node converges on its one real path (#4343). It had four ways to name what it ran and only one of them ran anything: `config.actionType: 'email' | 'slack'` were logger-backed stubs that wrote a line, reported success and delivered nothing under any configuration — with `config.template` / `.recipients` / `.variables` feeding a message no channel ever sent; inline `config.script` was recognized and never executed (the built-in runtime has no server-side JS sandbox), so the node warned and no-op'd; and every other `actionType` value was shorthand for a registered-function name, a second spelling of `config.function`. All five keys are retired and `function` becomes required, which is also what finally made the contract PARSEABLE: while the legal key set depended on `actionType`, a flat parse would either reject valid shapes or wave everything through, so `script` (with `subflow`) now runs through the same execute-time contract parse #4277 gave the flat builtins. A shorthand `actionType` CONVERTS into `function` — that is what it meant — unless `function` is already set, in which case it was dead metadata the executor never reached. The other four are dropped outright: nothing read them, so there is no value to preserve, and rebuilding the intent is an authoring decision the tombstones prescribe per branch (a `notify` node for mail — it delivers through the messaging service, the in-app inbox by default and real email once `@objectstack/plugin-email` is installed; a `connector_action` with the Slack connector, or an `http` node posting to a webhook, for Slack; a registered function for an inline body). Retired from the load path for the same reason as the rest: absorbing `actionType: 'email'` silently would let an author keep believing the flow sends mail.
0 commit comments