Skip to content

Commit 4df813f

Browse files
committed
fix(spec): register the drivers-name refusal; regenerate references; changeset
ADR-0087 semantic entry datasource-name-route-shadowed-refused, regenerated api-surface / export-origins / datasource reference, and the drivers.mdx sentence now names the schema refusal. Claude-Session: https://claude.ai/code/session_01ADHkmS1mEJA9L4pTrajNhU Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3f0dd1d commit 4df813f

6 files changed

Lines changed: 66 additions & 4 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/service-datasource': patch
4+
---
5+
6+
fix(spec)!: a datasource named `drivers` is refused at every door that parses a datasource, not only at the admin create door
7+
8+
Clause-②: no (narrowing)
9+
Metadata-protocol: narrowed
10+
Skills-docs: none in `skills/**` — `skills/objectstack-data/rules/datasources.md` teaches `default` as host-reserved and no file under `skills/**` declares or shows a datasource named `drivers`; every taught use stays true. Docs: `content/docs/data-modeling/drivers.mdx` said `drivers` was reserved at the admin API only; it now names the schema refusal and every door.
11+
12+
**BREAKING** for a datasource named `drivers`.
13+
14+
- **Before.** The datasource admin API mounts the driver catalog at `GET /api/v1/datasources/drivers`, ahead of the by-name read `GET /api/v1/datasources/:name`, so a datasource named `drivers` can never be read by name (its Setup edit form never loads). The admin create door (`POST /api/v1/datasources`) refused the name, but `DatasourceSchema.name` accepted it, so the same unreachable datasource was still declared in code (`defineDatasource`, a stack's `datasources`) and saved through the metadata save door (`PUT /api/v1/meta/datasource/drivers` answered 200).
15+
- **Now.** `DatasourceSchema.name` refuses every name in the new export `ROUTE_SHADOWED_DATASOURCE_NAMES` (`@objectstack/spec/data`, today `['drivers']`) with one `invalid_format` issue at `name`: `Datasource name 'drivers' is reserved: the datasource admin API serves its own route at /api/v1/datasources/drivers, ahead of the by-name read /api/v1/datasources/:name, so a datasource of that name could never be read by name. Pick another name.` So `defineStack`, `os validate`, `os build` and the metadata save door (422 `INVALID_METADATA`, nothing stored) all refuse it. The published JSON Schema carries the refusal as a second `pattern` on `name`.
16+
- **One list.** `@objectstack/service-datasource` now reads the spec's list: the admin registrar mounts the driver catalog from a segment typed as a member of it, and the create door refuses each name in it, as before.
17+
- **Not changed.** `default` (the host's primary datasource row still parses; a stack still may not declare it), `test`, and names that only contain the word (`driver`, `drivers_archive`, `my_drivers`).
18+
- **Stored rows.** A stored datasource row named `drivers` still loads, with `_diagnostics` reading `valid: false` and carrying the refusal, and its next metadata save is refused. Through the admin door it stays listed and removable, and a `label`/`active`-only edit still saves. Nothing rewrites it: the new name, and every object that routes to it, are the author's call. The upgrade guide entry is `datasource-name-route-shadowed-refused`.
19+
- **The remedy.** Rename the datasource (for example to `driver_registry`) and point every object `datasource` / `datasourceMapping` rule that names it at the new name.
20+
21+
<!-- adr-0087: registered datasource-name-route-shadowed-refused -->

‎content/docs/data-modeling/drivers.mdx‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,12 @@ through the same datasource connection path — one failure verdict, one escape
278278
hatch, and a real `status` for the primary DB in **Setup → Datasources**. The
279279
name `default` is host-reserved: an app bundle declaring a datasource with that
280280
name is rejected at load.
281-
The name `drivers` is reserved at the datasource admin API: creating a runtime
282-
datasource under it is refused, because `GET /api/v1/datasources/drivers` is the
283-
driver catalog, so a datasource of that name could never be opened by name.
281+
The name `drivers` is reserved too, because `GET /api/v1/datasources/drivers`
282+
is the driver catalog, so a datasource of that name could never be opened by
283+
name. `DatasourceSchema.name` refuses every name in `ROUTE_SHADOWED_DATASOURCE_NAMES`
284+
(`@objectstack/spec/data`), so `defineDatasource`, `defineStack`, `os validate`
285+
and the metadata save door refuse it, and so does the datasource admin API's
286+
create door.
284287

285288
<Callout type="warn">
286289
`OS_ALLOW_DRIVER_CONNECT_FAILURE=1` boots anyway, in an explicitly degraded

‎content/docs/references/data/datasource.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const result = DatasourceSchema.parse(data);
2828

2929
| Property | Type | Required | Description |
3030
| :--- | :--- | :--- | :--- |
31-
| **name** | `string` | ✅ | Unique datasource identifier |
31+
| **name** | `string` | ✅ | Unique datasource identifier (snake_case). Reserved: 'drivers', a path the datasource admin API serves itself, so a datasource of that name could never be read by name. |
3232
| **label** | `string` | optional | Display label |
3333
| **driver** | `string` | ✅ | Underlying driver type |
3434
| **config** | `Record<string, any>` | ✅ | Driver specific configuration |

‎packages/spec/api-surface/data.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@
627627
"RETIRED_FILTER_OPERATORS (const)",
628628
"RETIRED_SUB_DAY_INTERVALS (const)",
629629
"RLS_DENY_FILTER (const)",
630+
"ROUTE_SHADOWED_DATASOURCE_NAMES (const)",
630631
"RPC_QUERY_ALIAS_SLOTS (const)",
631632
"RUNTIME_OWNED_FIELD_TYPES (const)",
632633
"RangeOperatorSchema (const)",

‎packages/spec/export-origins/data.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,7 @@
616616
"RETIRED_FILTER_OPERATORS": "src/data/filter.zod.ts#RETIRED_FILTER_OPERATORS (const)",
617617
"RETIRED_SUB_DAY_INTERVALS": "src/data/analytics.zod.ts#RETIRED_SUB_DAY_INTERVALS (const)",
618618
"RLS_DENY_FILTER": "src/data/readable-ids.ts#RLS_DENY_FILTER (const)",
619+
"ROUTE_SHADOWED_DATASOURCE_NAMES": "src/data/datasource.zod.ts#ROUTE_SHADOWED_DATASOURCE_NAMES (const)",
619620
"RPC_QUERY_ALIAS_SLOTS": "src/data/data-engine.zod.ts#RPC_QUERY_ALIAS_SLOTS (const)",
620621
"RUNTIME_OWNED_FIELD_TYPES": "src/data/field.zod.ts#RUNTIME_OWNED_FIELD_TYPES (const)",
621622
"RangeOperatorSchema": "src/data/filter.zod.ts#RangeOperatorSchema (const)",
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import type { SemanticMigration } from '../../types.js';
4+
5+
// #29308. An accept-set narrowing of `DatasourceSchema.name` with no
6+
// conversion: a datasource named after a literal the datasource admin API
7+
// mounts beside its by-name read (`drivers`, the driver catalog) is refused.
8+
// The new name, and every object `datasource` / `datasourceMapping` rule that
9+
// routes to it, only the author can pick. Same class as
10+
// stack-datasource-host-reserved-name-refused.
11+
export const entry: SemanticMigration = {
12+
id: 'datasource-name-route-shadowed-refused',
13+
surface: 'a datasource whose `name` is one of `ROUTE_SHADOWED_DATASOURCE_NAMES` (today `drivers`), '
14+
+ 'e.g. defineDatasource({ name: \'drivers\', driver: \'postgres\', config: { … } }), in '
15+
+ '`defineDatasource`, a stack\'s `datasources`, `os validate`, `os build` and the metadata save door '
16+
+ '(`PUT /api/v1/meta/datasource/drivers`)',
17+
replacement: 'The same datasource under another snake_case name (e.g. `driver_registry`), with every '
18+
+ 'object that names it (`datasource`, or a `datasourceMapping` rule) pointed at the new name. ⛔ NOT '
19+
+ 'mechanically convertible: the new name is the author\'s call.',
20+
reason:
21+
'The datasource admin API mounts the driver catalog at `GET /api/v1/datasources/drivers`, ahead of '
22+
+ 'the by-name read `GET /api/v1/datasources/:name`, so a datasource named `drivers` was stored and '
23+
+ 'listed while its detail read (the load of its Setup edit form) always answered the catalog. The '
24+
+ 'admin create door already refused the name; `DatasourceSchema.name` accepted it, so the same '
25+
+ 'unreachable datasource was still minted from code and through the metadata save door. The schema '
26+
+ 'now refuses every name in `ROUTE_SHADOWED_DATASOURCE_NAMES`, the one list the admin routes mount '
27+
+ 'from, with an `invalid_format` issue at `name` naming the name and the route; the published JSON '
28+
+ 'Schema carries the refusal as a second `pattern`. Measured producers: none in this repository.',
29+
acceptanceCriteria:
30+
'No datasource is named `drivers`; `defineStack`, `os validate` and `os build` report no issue at '
31+
+ '`name` (or `datasources.N.name`) reading "Datasource name \'drivers\' is reserved", and the '
32+
+ 'metadata save door answers 200 for the renamed datasource. A stored `sys_metadata` datasource row '
33+
+ 'named `drivers` still loads, with `_diagnostics` reading `valid: false` and carrying this refusal, '
34+
+ 'and its next metadata save is refused; nothing rewrites it. Through the admin door that row stays '
35+
+ 'listed and removable, and a `label`/`active`-only edit still saves.',
36+
};

0 commit comments

Comments
 (0)