Repository navigation
Commit f9ebb7d
Fixes #22800
Clause-②: no (narrowing)
This PR carries census row 9 of #22661 (seat 1 ticks the parent's
checklist and closes the parent; this PR does not). The identity import
handed `runImport` a run protocol without `getMetaItem`, so the
reference resolution matched its lookup cells without asking the lookup
target its declared exposure. The import already held that reader and
gave it to the parser a few lines earlier. Classes, positions and
functions only, per the card.
## What changed
- **One reader, two halves.** `runAdminImportUsers`
(`packages/plugins/plugin-auth/src/admin-import-users.ts`) binds the
declaration reader it already holds (`IdentityImportDeps.getMetaItem`)
once, as `metaReader`. It hands `metaReader` to `prepareImportRequest`,
which reads `sys_user`'s fields, so lookup cells become references at
all. It also spreads `metaReader` into the run protocol (`const
protocol: ImportProtocolLike`). With one binding the two cannot
disagree: whenever a cell is a reference, its target is judged.
- **The decision is the family's, unchanged.** `servesReferenceTarget`
(`packages/core/src/utils/import-runner.ts`, landed by #22739) now asks
this door's targets for `list`, the same way it asks for every other
`runImport` caller. There is no second rule, and no code in
`@objectstack/core` or `@objectstack/rest` changes; the one
`@objectstack/core` edit is a TSDoc sentence (see Acceptance notes). The
import's reads run under the system identity. That gives no exemption,
because the decision takes no caller.
- **The answer for a refusing target** is the precedent's answer. Every
cell gets `reference_not_found`: one naming a record, one naming none,
and a pasted id. Nothing of the target is read. A target with no
`enable` block, or with a whitelist that grants `list`, resolves exactly
as before. No new error code, no status change.
- **Signature measured, not adapted.** `deps.getMetaItem` takes `{ type:
string; name: string }`. `GetMetaItemRequest` requires exactly those two
members, and the rest are optional. So the dep is assignable to
`ImportProtocolLike.getMetaItem` by parameter contravariance, and its
promise of `any` is assignable to a promise of `unknown`. `tsc` is green
and no adapter was written. The producer is `auth-plugin.ts`, which
forwards the protocol service's `getMetaItem`. That method answers the
`{ type, name, item }` envelope `servesReferenceTarget` reads
(`item.enable`), the same envelope the parser already reads (`item`).
## Measured first: which targets `sys_user`'s reference fields name
Read on `origin/main` `eed637c09f`, from
`packages/platform-objects/src/identity/sys-user.object.ts`:
| field | target | target's declaration | serves `list` |
|---|---|---|---|
| `manager_id` | `sys_user` | `apiEnabled: true`, `apiMethods: ['get',
'list', 'update', 'bulk']` | yes |
| `primary_business_unit_id` | `sys_business_unit` | `apiEnabled: true`,
no `apiMethods` (default-open) | yes |
- **The null.** No current target refuses `list`, so no shipped import
answer changes. The wiring is pinned anyway, as the card directs, so a
future refusing declaration is judged.
- **The registry injects nothing into `sys_user`.** It is `managedBy:
'better-auth'`, and `resolveInjectedSystemColumns` returns no columns
for that row. So these two lookups are the whole reference population.
- **Grep control.** The same pattern found the two `Field.lookup` calls
plus one inline action param (`type: 'lookup'`, a dialog param and not a
field). No in-repo object extension targets `sys_user`.
- **The manager column never reaches `runImport`.** The import strips
`manager_id` before the run, and its own second pass links managers. In
practice, `primary_business_unit_id` is the cell this door matches.
- **Not measured:** deployed or cloud-held extensions of `sys_user`.
## Pins
All in the `[#22800]` block of
`packages/plugins/plugin-auth/src/admin-import-users.test.ts` (6 cases).
They use fixture objects only and reuse the file's already-pinned engine
double, with only `find` overridden. The block adds no new engine double
and leaves the double ledger untouched.
- Two refusing declarations, `apiEnabled: false` and a whitelist
granting `get` but not `list`. A match, a miss and an id all answer
`reference_not_found`. The target is never read, and no user is created.
- Two serving controls, no `enable` block and a whitelist granting
`list`. A match and an id resolve, and a miss is reported, as before.
- The target is asked through the import's reader, once per import, by
its own name.
- A control without a declaration reader: no cell is a reference, so the
target is neither asked nor read.
The decision's own matrix (every declaration, fail-closed) stays pinned
once, in
`packages/core/src/utils/import-runner-reference-exposure.test.ts`.
**Census pin, row 9.**
`packages/core/src/security/second-object-read-exposure.pin.test.ts` had
no row for this door to move. The identity import never calls
`referenceTargetOf`. It takes its targets from the
`import-field-meta.ts` map, which is already classified `decided`. So
the door is named in the docblock, in the shape row 8 used for the MCP
stdio bridge: it reaches a classified read without calling the arbiter,
and it is held by its own package's pin. No `CALLERS` row is added. A
row there would turn the pin's "classified files still call the arbiter"
leg red.
## Ablation
Run on committed `49e162847a` through `scripts/ablation-replace.mjs`.
The anchor was the protocol's `...metaReader,` spread (deleted), hit 1 →
0. The blob went `7be1a3b399e7` → `c5bf2e31b008`. The subject is
imported by relative path from source, so no `dist/` hop is involved.
- With the wiring removed, 3 cases go red and 3 stay green. The red ones
are both refusing legs (the match and id cells come back `ok: true`, the
pre-fix behaviour) and the "asked through the reader" leg (only
`sys_user` was asked). The green ones are the two serving controls and
the no-reader control.
- Restore is proven: the blob equals HEAD (`7be1a3b399e7`), and `git
diff HEAD` is empty.
- The same ablation on the first head `574f795aee`, with the pins in a
separate file, gave the same 3 / 3.
## Verification
Each reading names its commit. The final head is `8c40d537f4`. That
commit changes the changeset and one TSDoc sentence in
`import-runner.ts`; `plugin-auth`'s source is unchanged since
`49e162847a` (blob `7be1a3b399e7`), so the `plugin-auth` readings and
the ablation below stand.
- **Build.** `turbo run build --filter='@objectstack/plugin-auth^...'`
ran 27 tasks, all successful, at `574f795aee`. The sources of those 27
are unchanged since. `@objectstack/plugin-auth` was built at
`4405bc2cb0`, and `admin-import-users.ts` is unchanged since (blob
`7be1a3b399e7`).
- **`@objectstack/plugin-auth` at `49e162847a`.** `vitest run`: 136
files, 2786 passed, 10 skipped. `typecheck` exit 0, including
`check:test-typecheck`. The `[#22800]` block adds no test-layer error:
the file's 11 ledgered errors all sit above it.
- **`@objectstack/core` at `8c40d537f4`.** Rebuilt; `dist/index.d.ts`
carries the reworded sentence. The census pin passes 4 / 4 (`--project
repo`), the 12 `import-runner*.test.ts` files pass 138 / 138, and
`typecheck` exits 0.
- **Identity import files at `49e162847a`.**
`admin-import-users.test.ts` and
`admin-import-users-manager-pass.test.ts` together: 56 / 56.
- **Lint, narrowed and proven (`8c40d537f4`).** `eslint
--no-inline-config --format json` on the 4 changed `.ts` files gives 4
files, 0 errors, 0 warnings. Each file is in the linted population:
`isPathIgnored` is false and a config resolves for each.
`eslint.config.mjs` enables no type-aware linting (no
`parserOptions.project`), so this diff cannot move the verdict on any
untouched file. Repo-wide `pnpm lint` is CI's.
- **Gates at `8c40d537f4`.** `dispatch-gates --commands` derived 68
commands: the earlier 67 plus `check:error-code-casing`, now that
`import-runner.ts` is in the diff. All 68 exited 0.
`check:dual-build-cjs-loads` exited 3 (PREREQUISITE NOT MET) on its
first run in the battery and 0 when re-run once the workspace was built.
`--ran` with exit codes: 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN.
The roster families keyed to these paths (`check-changeset-fixed`,
`check:auth-mount-ledger`, `check:authz-resolver`,
`check:filter-alias-parity`) all exited 0. `check-adr-0087-registration`
accepts the changeset as `[BREAKING+bang+clause-②-narrowing]
not-required (no-migration-prescription)`. `check-changeset-no-major`,
given a `pull_request` payload carrying `Clause-②: no (narrowing)`,
exits 0.
- **Not merged with `origin/main`.** Main has moved 9 commits (to
`8532783f65`) with no overlap with this PR's paths. Two gate inputs
changed there: `scripts/engine-double-contract.pinned.json` dropped two
rows for a removed `plugin-security` test and none of this file's rows,
and `scripts/cross-package-test-inputs.mjs` declared two inputs for a
`cli` test. CI's merge ref validates the joint tree.
## Acceptance notes
- **One TSDoc sentence in `@objectstack/core`, reworded here.** The
`ImportProtocolLike.getMetaItem` docblock
(`packages/core/src/utils/import-runner.ts`, `:214`) gave "plugin-auth's
identity import" as the example of a protocol without the member. That
sentence now states the rule without the example and says the identity
import carries the member (#22800). It ships in core's
`dist/index.d.ts`. No other line of the file is touched. The `:204`
mention concerns `validateData`, which the identity import still omits,
so it stays true. A comment changes no declaration, so
`@objectstack/core` takes no changeset entry.
- **The pending #22739 changeset says the opposite of this one.**
`.changeset/22739-import-reference-target-exposure.md`, in its Bindings
bullet, says the identity import "is not judged". Neither changeset has
been released yet, so the next CHANGELOG would carry both sentences.
This PR's changeset states the change as FROM → TO, so a reader sees the
order. Carrier: whoever compiles the release notes; no seat is named.
- **Clause-② is `no (narrowing)`**, per the seat's answer on this card.
The family spelled this narrowing with the arm both times (#22739, the
MCP bridge row), and a narrowing at zero measured producers is still a
narrowing. The changeset is BREAKING (`fix(plugin-auth)!:`), `minor`,
and carries an ADR-0087 `not-required (no-migration-prescription)`
disposition.
- The other residual, a future `runImport` caller without the member,
stays with the parent's close-out, as the card says.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6d5191e commit f9ebb7d
5 files changed
Lines changed: 171 additions & 6 deletions
File tree
- .changeset
- packages
- core/src
- security
- utils
- plugins/plugin-auth/src
| 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 | + | |
Lines changed: 8 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
51 | | - | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
52 | 59 | | |
53 | 60 | | |
54 | 61 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
211 | 211 | | |
212 | 212 | | |
213 | 213 | | |
214 | | - | |
215 | | - | |
216 | | - | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
217 | 217 | | |
218 | 218 | | |
219 | 219 | | |
| |||
Lines changed: 114 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
660 | 660 | | |
661 | 661 | | |
662 | 662 | | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
159 | 159 | | |
160 | 160 | | |
161 | 161 | | |
162 | | - | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
163 | 168 | | |
164 | 169 | | |
165 | 170 | | |
| |||
441 | 446 | | |
442 | 447 | | |
443 | 448 | | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
444 | 460 | | |
445 | 461 | | |
446 | 462 | | |
| |||
453 | 469 | | |
454 | 470 | | |
455 | 471 | | |
456 | | - | |
| 472 | + | |
457 | 473 | | |
458 | 474 | | |
459 | 475 | | |
| |||
507 | 523 | | |
508 | 524 | | |
509 | 525 | | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
510 | 533 | | |
511 | 534 | | |
512 | 535 | | |
| |||
0 commit comments