Repository navigation
Commit faf8dce
fix(runtime,service-datasource): an import over a code-defined datasource is held to its package's ADR-0028 namespace (#21906)
Fixes #21889
Clause-②: no
An import over a code-defined datasource is now held to the ADR-0028
namespace of the package that declares the datasource, and the draft
door answers the prefixed name. This follows triage's direction A
(`5999047022`, which amends `5998041661`). There are two halves.
## What changed
- **Writer (`packages/runtime/src/app-plugin.ts`).** `AppPlugin`
registers each code-defined datasource through `applyProtection`
(`@objectstack/spec/shared`), the stamping helper the other load paths
use. The datasource is stamped with the id and version of the package
body that declares it. The owner is read the way the metadata plugin's
artifact door reads it (ADR-0130 D4):
- `packages` absent: every datasource takes the manifest's id, the key
`registerApp` installs the package under. The list read is the same one
as before (D7).
- `packages` present: each body's datasources take that body's id
(`artifactPackageId`, in `resolveArtifactPackageOrder` order). The
artifact's top-level manifest id is never stamped onto every entry (the
#14599 misattribution class), and no name-to-package index is built over
the flattened list.
- A top-level datasource that no body declares keeps its registration
under the artifact's own id, as the door's residual sweep does, and a
warning names it.
- **Reader (`packages/services/service-datasource/src/plugin.ts`,
`getNamespace` and its docblock only).** The package record is read from
the engine registry (`registry.getPackage` on the `objectql` service),
the store the publish gate reads for the same check
(`publishPackageDrafts`, `metadata-protocol`). It used to be asked of
the `metadata` service, which holds no package records in any
composition. The id comes only from the stamped `_packageId`: no guess,
no fallback store, no second resolution path. The engine is resolved
when it is used, like `metadata()` (the read-at-use posture).
Outside `getNamespace`, `plugin.ts` changes two docblock words, so that
neither docblock names package reads: the `metadata()` docblock ("every
datasource read below") and the `MetadataServiceLike` docblock
("datasource definitions"). Nothing under `packages/spec`, no
metadata-door change, and no new error code.
## The ruling's pins, at the real doors
Measured at `8e5ff7aa` on the showcase's `showcase_external`, under
`objectstack dev` (`pnpm dev -- --fresh -p 38931`) and `objectstack
start` (`--compile -p 38933`, fresh database). Both gave the same
readings:
| door | answer |
|---|---|
| `POST …/external/tables/orders/import` `{"name":"probe_orders_21889"}`
| `400 EXTERNAL_IMPORT_ERROR` "Object 'probe_orders_21889' is missing
the package namespace prefix. Rename it to 'showcase_probe_orders_21889'
(namespace = 'showcase')." `GET /meta/object/probe_orders_21889` answers
`404`. |
| `POST …/external/tables/orders/draft` | `200`, `name:
'showcase_orders'`, no `TODO(namespace)` in the source |
| import `{"name":"showcase_probe_orders_21889"}` (control) | `201`, and
`GET /data/showcase_probe_orders_21889` answers `200` with 4 rows |
| import with no `name` override (the carry) | `201`, saved as
`showcase_customers` |
| `PATCH` / `DELETE` / `PUT /api/v1/datasources/showcase_external`
(control) | `400 DATASOURCE_ADMIN_ERROR` / `400 DATASOURCE_ADMIN_ERROR`
/ `405 METHOD_NOT_ALLOWED`, unchanged |
| `POST …/external/validate` (control) | `200`, `ok: true`, over
`showcase_ext_customer` and `showcase_ext_order` |
| `GET /data/showcase_ext_order` (control) | `200`, 4 rows |
## Clause-② readings
- `GET /api/v1/meta/datasource`, `showcase_external`, after the change
(dev and start alike): `_diagnostics, _packageId, _packageVersion,
_provenance, active, autoConnect, config, driver, external, label, name,
origin, schemaMode`. The values are `com.example.showcase`, `0.1.0` and
`package`. Before the change the item carried none of the three (the
writer ablation below, on the harness). All three are declared on
`DatasourceSchema` (`...MetadataProtectionFields`). On an item with no
`protection` block, `applyProtection` writes exactly those three
(`shared/protection.zod.ts`), so no `_lock*` key and no further key is
added.
- The host `default` item is unchanged: `_diagnostics, config, driver,
label, name, origin`, with no `_packageId`.
- The admin list (`GET /api/v1/datasources`) is unchanged on dev and
start for both items: `active, driver, label, name, origin, schemaMode,
status`.
- No published entry gains an export. `codeDefinedDatasourceOwners` is a
private method, and `artifact-collections.ts` is not touched.
## Tests
- `packages/runtime/src/app-plugin.datasource-provenance.test.ts` (new,
5 cases): the single-package bundle in both datasource spellings, the
two-body ADDITIVE `packages[]` artifact and the option-B artifact (each
datasource carries its own body's id and is registered once), and the
residual top-level datasource (the artifact's own id, plus the warning).
No in-repo example declares a datasource in a multi-package artifact, so
the two-body cases are pinned here.
-
`packages/services/service-datasource/src/__tests__/external-namespace-reads-engine-registry.test.ts`
(new, 11 cases): the namespace pins now sit on the store the reader
reads.
- The draft is prefixed and carries no TODO.
- An unprefixed import name is refused with `code` + `status` and the
shared validator's own ADR-0028 message, and nothing is saved.
- A prefixed import name is saved, and an import with no override saves
the derived prefixed name.
- The engine is read at each use, and the start and dev orderings give
the same answer.
- Nothing else resolves a namespace: a `package` item in the metadata
service (seeded with a different namespace) is never read, a datasource
with no `_packageId` or with `sys_metadata` resolves none, a package
with no namespace resolves none, and with no engine the documented
fallback holds.
- `external-metadata-read-at-use.test.ts`: the `package` map the
metadata fake seeded (a store no composition fills) is removed. Its two
namespace cases and the dev ordering's draft line moved to the file
above.
-
`packages/qa/dogfood/test/external-import-code-datasource-namespace.dogfood.test.ts`
(new, 7 cases, a real boot of the showcase):
- Premise: the provenance on `GET /meta/datasource/showcase_external`.
- The two ruling pins.
- Controls: the prefixed import with its rows, the `default` datasource
with no package, the admin service's refusals and its list (the harness
mounts no admin routes, so the door's status and code are the dev/start
readings above), and validate with the federated read.
- The carry, as triage accepted it:
- `external-import-saves-like-meta.dogfood.test.ts` moves to
`showcase_dogfood_ext_cust_21788`. Its control used to import `orders`
under the remote table's own name, a shape the namespace now refuses. It
now imports with no `name` override and asserts the saved name
`showcase_orders` across a cold boot.
- `external-import-destructive-remedy.dogfood.test.ts` moves to
`showcase_dogfood_ext_cust_21841` and `…_v2`.
- `external-validate-sees-runtime-save.dogfood.test.ts` (landed on
`main` by #21875 after this PR branched) moves its imported object to
`showcase_dogfood_ext_ord_21842`. Nothing else in it changes: `SAVED`
goes through `PUT /meta/object`, which runs no namespace check, and the
later assertions pass on the renamed object.
- A repo-wide grep for other pins of an unprefixed import, or of
`TODO(namespace)`, on a datasource whose package resolves found none.
The remaining draft tests drive `ExternalDatasourceService` with an
injected `getNamespace`, and the REST tests mock the service.
Runs at `3ec0e9f6`, the current head (it carries merges of `origin/main`
at `d2ed5e04` and `374ca5cb`). Each ran through the shared verify lock,
`VERDICT command-exit 0`:
- `pnpm --filter @objectstack/runtime test`: 328 files passed, 4644
passed, 19 skipped.
- `pnpm --filter @objectstack/service-datasource test`: 40 files, 741
passed.
- `typecheck` for `runtime`, `service-datasource` and `dogfood`: exit 0.
- Dogfood, 6 files (the three import files,
`external-validate-sees-runtime-save`,
`external-validate-start-ordering` and `showcase-external-autoconnect`):
23 passed.
## Ablations (one-time; restored by blob hash and an empty `git diff
HEAD`, then rebuilt)
Every leg went through `scripts/ablation-replace.mjs` (anchor hit and
landed) and `scripts/ablation-dist-preflight.mjs` (the mutation reached
`dist/`, and the restore removed it).
- **W, writer stamp removed** (`applyProtection(…)` replaced by an
unstamped copy, runtime rebuilt):
- The runtime unit file went red, 5 of 5.
- Dogfood went red, 3 of 7: the premise, the refusal (it answered `201`
and saved `dogfood_ext_order_21889`, the defect itself), and the draft
(`'orders'`). The 4 controls stayed green.
- The rebuild's DTS step exited 1 on TS6133 (the now-unused
`applyProtection` and `owner`). The JS was emitted, and the preflight
read the stamp absent from `dist/index.js`.
- The first attempt was a no-op that the tool refused (the replacement
text already occurred inside the anchor). It is void, and the reading
above is the second attempt.
- **R, reader reverted to asking the metadata service**
(service-datasource rebuilt, build exit 0):
- The new service-datasource unit file went red, 8 of 11.
- Dogfood went red, 2 of 7: the refusal (`201`) and the draft
(`'orders'`). The premise and the controls stayed green.
- **A, every body stamped with the top-level manifest's coordinates**
(source-level suite, no build): the runtime unit file went red, 3 of 5
(additive, option-B and residual). The two single-package cases stayed
green.
## Gates
All at `3ec0e9f6`.
- `node scripts/pm/dispatch-gates.mjs --ran`: 69 derived, 69 run, 0
NOT-MEASURED, 0 UNRUN.
- 73 commands exit 0: those 69, the full `pnpm lint` (`eslint .
--no-inline-config`), and the three PR-context checks run against this
PR (`check-closing-target-claim`, `check-partof-closing-keyword`,
`check-single-claim-paths`).
- The dispatch's whole list (134 commands) last ran at `f7d3aac1`, and
every one exits 0. Two of them (`check:dual-build-cjs-loads`,
`check:published-readme-exports`) exit 0 only after a workspace build
cleared their prerequisite.
## Acceptance notes
- **Wider than the title, by construction.** Any datasource that carries
`_packageId` now resolves its package's namespace, including one the
metadata plugin's artifact door registers. That was the reader's
documented intent all along. It is measured here only on
`showcase_external`.
- **Serial:** #21899 remains open and owns the metadata door's refusal
for `origin: 'code'` datasources. Until it lands, a meta-door save can
replace the stamped item and drop `_packageId`.
- **#21875 landed first.** This PR carries both docblock words its
reader change made false (the `metadata()` docblock and
`MetadataServiceLike`), and moves #21875's runtime-save validate pin to
the prefixed import name.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent f243a29 commit faf8dce
10 files changed
Lines changed: 771 additions & 57 deletions
File tree
- .changeset
- packages
- qa/dogfood/test
- runtime/src
- services/service-datasource/src
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
Lines changed: 177 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
Lines changed: 6 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
33 | 37 | | |
34 | 38 | | |
35 | 39 | | |
| |||
43 | 47 | | |
44 | 48 | | |
45 | 49 | | |
46 | | - | |
| 50 | + | |
47 | 51 | | |
48 | | - | |
| 52 | + | |
49 | 53 | | |
50 | 54 | | |
51 | 55 | | |
| |||
Lines changed: 17 additions & 9 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
34 | 41 | | |
35 | 42 | | |
36 | 43 | | |
| |||
43 | 50 | | |
44 | 51 | | |
45 | 52 | | |
46 | | - | |
47 | | - | |
48 | | - | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
49 | 56 | | |
50 | 57 | | |
51 | 58 | | |
| |||
101 | 108 | | |
102 | 109 | | |
103 | 110 | | |
104 | | - | |
105 | | - | |
| 111 | + | |
| 112 | + | |
106 | 113 | | |
| 114 | + | |
107 | 115 | | |
108 | | - | |
| 116 | + | |
109 | 117 | | |
110 | 118 | | |
111 | 119 | | |
| |||
120 | 128 | | |
121 | 129 | | |
122 | 130 | | |
123 | | - | |
124 | | - | |
125 | | - | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
126 | 134 | | |
127 | 135 | | |
Lines changed: 2 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
52 | | - | |
| 52 | + | |
| 53 | + | |
53 | 54 | | |
54 | 55 | | |
55 | 56 | | |
| |||
0 commit comments