Skip to content

Commit 7610e85

Browse files
committed
docs(changeset): the field set does not widen; the residual narrowing and its producer census (#22739)
Claude-Session: https://claude.ai/code/session_01JfJfBUC3cQ6hhgm9MQK76T Co-authored-by: Claude <noreply@anthropic.com>
1 parent f06912e commit 7610e85

1 file changed

Lines changed: 10 additions & 4 deletions

File tree

‎.changeset/22739-import-reference-target-exposure.md‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,27 @@ fix(core)!: an import's reference resolution asks the lookup TARGET its declared
66

77
Clause-②: no (narrowing)
88

9-
<!-- adr-0087: not-required (no-migration-prescription) No metadata moves: no spec key, authorable spelling, export or stored shape is removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite. What narrows is one runtime read: the import's reference resolution stops matching cells against a lookup's target object whose existing `enable` declaration already refuses that read on every data route, and the remedy is the declaration the author already wrote. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); and no exported declaration is removed or narrowed — `ImportProtocolLike` gains one optional member (not runtime-interface-only or type-surface-only). -->
9+
<!-- adr-0087: not-required (no-migration-prescription) No metadata moves: no spec key, authorable spelling, export or stored shape is removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite. What narrows is one runtime read, in two places: the import's reference resolution stops matching cells against a lookup's target object whose existing `enable` declaration already refuses that read on every data route (the remedy is the declaration the author already wrote), and a field of the legacy, schema-refused type spelling `'reference'` no longer takes a target from its raw `reference` key, since the spec's arbiter gives it none (no shipped producer declares one). The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); and no exported declaration is removed or narrowed — `ImportProtocolLike` gains one optional member (not runtime-interface-only or type-surface-only). -->
1010

1111
**BREAKING** (an accept-set narrowing), shipped as `minor` under the launch-window convention for breaking changes.
1212

1313
`POST /api/v1/data/:object/import` and the async `POST /api/v1/data/:object/import/jobs` judge the ADDRESSED object's `enable` block. A lookup cell is resolved by matching its text against the lookup's TARGET object — an object nobody addressed.
1414

15-
**FROM.** For an administrator and a member alike, a cell was matched against a target whose declaration refuses the read on every data route (`enable.apiEnabled: false`, the deny-all `apiMethods: []`, or a whitelist without `list`): a cell naming an existing record stored its id, and a cell naming none answered `reference_not_found` — so the row report told the two apart.
15+
**FROM.**
1616

17-
**TO.** The runner asks the spec's one decision (`canServeApiOperation`) of the TARGET's `enable` block, for `list` — matching a cell is a predicate read over the target, the runtime `find` the spec maps to `list`. A target the decision does not serve answers every cell, naming a record or not (a pasted id included), `reference_not_found` — the answer a target the caller cannot read (no read permission, or a record its row scope hides) already gets. Nothing of the target is read. A target with no `enable` block, or a whitelist that grants `list`, resolves exactly as before. A declaration that cannot be read withholds the target too (fail-closed).
17+
- For an administrator and a member alike, a cell was matched against a target whose declaration refuses the read on every data route (`enable.apiEnabled: false`, the deny-all `apiMethods: []`, or a whitelist without `list`): a cell naming an existing record stored its id, and a cell naming none answered `reference_not_found` — so the row report told the two apart.
18+
- A field of the legacy type spelling `'reference'` (not a `FieldType`, so `ObjectSchema` refuses it, but still listed in the import, export and import-template doors' own type tables) took its target from the raw `reference` key: import matched its cells against that object and export expanded it.
19+
20+
**TO.**
21+
22+
- The runner asks the spec's one decision (`canServeApiOperation`) of the TARGET's `enable` block, for `list` — matching a cell is a predicate read over the target, the runtime `find` the spec maps to `list`. A target the decision does not serve answers every cell, naming a record or not (a pasted id included), `reference_not_found` — the answer a target the caller cannot read (no read permission, or a record its row scope hides) already gets. Nothing of the target is read. A target with no `enable` block, or a whitelist that grants `list`, resolves exactly as before. A declaration that cannot be read withholds the target too (fail-closed).
23+
- A field is a reference on those doors exactly when it declares a `reference` string, as before, and its target is read through the spec's arbiter `referenceTargetOf`, which names none for a type outside `lookup` / `master_detail` / `user` / `tree`: such a `'reference'`-typed field's cell is stored as written, and export leaves its stored id. Every other non-reference type was never resolved by these doors and is unchanged. Measured over every git-tracked non-test source under `packages/` and `examples/` (`.ts`, `.js`, `.json`; fixtures excluded) at `0d326bfb12`: no shipped field declares `reference` on a non-reference type (0 object literals, 0 `Field.*` helper calls; the one literal pairing `type` with `reference` is a key-classification table, not a field), against a control of 30 reference-typed literals and 80 `Field.lookup` / `masterDetail` / `user` / `tree` calls; a planted literal and a planted helper call were both found.
1824

1925
**Bindings.**
2026

2127
- **Every caller of `runImport` whose protocol reads object declarations.** The decision takes no user, so an administrator, a member and a system context (the connector pull) are answered the same. The target's declaration is read through the protocol's `getMetaItem`, a new optional member of `ImportProtocolLike`; a protocol without it (plugin-auth's identity import, whose reads run under the system identity) is not judged.
2228
- **No new error code, no status change.** The refusal is the per-row `reference_not_found` the resolver already answers.
23-
- **The target is read through `referenceTargetOf`.** `buildFieldMetaMap` now takes a reference field's target from the spec's arbiter instead of the raw `reference` key, so a `user` field written without `reference` (fully specified: the type fixes the target) targets `sys_user` on the import, export and import-template doors, as it already did on the data door's `$expand`. Measured on the import door: a cell holding a user's email used to fail `reference_not_found` (the raw text reached the write as an id); it now resolves to that user. A non-string `reference` carrier, which `ObjectSchema` refuses, now throws there instead of reading as "no target".
29+
- **Which fields count as references does not widen.** A `user` field written without `reference` still has no target on these doors (its cell reaches the write as written), although the arbiter gives it `sys_user`; adopting that target would widen what the import accepts and is not this change.
2430

2531
**Fix, if an import relied on the old answer:** the target's own declaration decides — grant `list` in its `enable.apiMethods`, or drop `enable.apiEnabled: false`, if the object is meant to be matched by import.
2632

0 commit comments

Comments
 (0)