Repository navigation
Commit 1fb274e
fix(rest,runtime): the published door serves a code-defined datasource's code definition over a stored row (declinesStoredRow made public) (#22001)
Fixes #21986
Clause-②: yes (widening)
## Summary
`GET /api/v1/meta/datasource/:name/published` served a stored
`sys_metadata` row under a code-defined datasource name, while `GET
/api/v1/meta/datasource/:name`, the `/meta/datasource` list and
`/layers` all served the code definition. The protocol makes that
decision with one predicate for both name classes, `declinesStoredRow`,
but it was `private`, so both published doors asked `isShippedFlowName`
alone.
Triage direction A (`6016753988`), as claimed in `6017296526`:
- `@objectstack/metadata-protocol`:
`ObjectStackProtocolImplementation.declinesStoredRow(type, name)` is now
public, under the same name. Its doc comment says who may ask it: a door
that serves a stored row out of the layered read.
`isDeclaredCodeDatasource` and `isStoredEntryOfDeclinedName` stay
private.
- `@objectstack/rest` (`rest-server.ts`, the `publishedOverlay` branch)
and `@objectstack/runtime` (`domains/meta.ts`, the `Pick` and the
published branch) ask `declinesStoredRow` in place of
`isShippedFlowName`, in the same duck-typed `typeof … === 'function'`
shape. No door restates `isDeclaredCodeDatasource` or the host's
code-datasource set.
- A protocol without `declinesStoredRow` gets the stored row, as before.
The door does not fall back to `isShippedFlowName`, and the existing
no-predicate controls now hide `declinesStoredRow` (with
`isShippedFlowName` still visible) to pin that.
- `isShippedFlowName` stays public and unchanged in behaviour.
## Reproduction, before and after
**Door level, at base `aa09db58c9`.** The new pin in each door's test
file runs the real `ObjectStackProtocolImplementation` and the real
`MetadataManager`, with a package that declares `showcase_external` and
a stored row labelled `Shadow 21986`. Both doors failed the same way:
```
AssertionError: datasource: expected { name: 'showcase_external', …(4) } to match object { name: 'showcase_external', …(2) }
- "label": "External Analytics (SQLite)",
+ "label": "Shadow 21986",
- "origin": "code",
+ "origin": "runtime",
```
The pin's own precondition passed on the base: `getMetaItemLayered`
answered `overlay` = the row and `effective` = the code definition.
**Real showcase composition.** This used a throwaway `bootStack` probe
that was never committed. It stored a row under `showcase_external`
through the `/meta` repository (label `PROBE SHADOW`, `origin: runtime`,
its own file), restarted, and then read:
| read | base `aa09db58c9` | this branch |
|:--|:--|:--|
| `/meta/datasource/showcase_external/published` | 200, `PROBE SHADOW`,
`origin: runtime`, `probe-shadow.db` | 200, `External Analytics
(SQLite)`, `origin: code`, `.objectstack/data/showcase_external.db` |
| `/meta/datasource/showcase_external` | the code definition | the code
definition |
| `/layers` `effective` | the code definition | the code definition |
| `/layers` `overlay` | the row, `overlayScope: env` | the row,
`overlayScope: env` |
The order's mechanism check was what `layered.effective` IS for a
code-defined datasource with a stored row. It is the code definition,
measured in both harnesses above. So the door now serves the same body
as the by-name read.
A serve-shaped composition was also probed on this branch, with
`MetadataPlugin` composed as `objectstack serve` does. There
`/published` answers the same code definition before the row exists and
after the row plus a restart. The lean harness answers `501
NOT_IMPLEMENTED` on `/published` before any row exists. That is the
harness's own recorded degradation: it has no `getPublished`-capable
metadata service, as `meta-published-and-state-routes.dogfood.test.ts`
states. It is not in this card's scope.
## Pins
- `packages/rest/src/meta-published-overlay.test.ts`, new block
`[#21986]`:
- a stored row under a code-defined datasource name: the door answers
the code definition, which is the layered `effective` layer, for both
`datasource` and `datasources`, and the row stays at rest;
- control: a runtime datasource's stored row is still what the door
serves (`toEqual(layered.overlay)`).
- `packages/runtime/src/domains/meta-published-runtime-publish.test.ts`:
the same two cases on the dispatcher twin.
-
`packages/metadata-protocol/src/protocol.declines-stored-row-published.test.ts`
(new) pins that the published predicate answers both name classes (a
shipped flow; a package-declared datasource and the host's `default`, in
both spellings) and nothing else (an unshipped flow, a runtime
datasource, the same names under another type, and a missing or empty
name). It calls the method through the class's declared type, so the
package's `tsc --noEmit`, which includes this file (`--listFiles`: 1
hit), fails if the member stops being public.
- The shipped-flow pins stay green through the switch. The only edit to
them is the no-predicate control in each door file, which now hides the
predicate the door asks.
- The datasource reads need `manage_platform_settings`
(`META_TYPE_READ_CAPABILITIES`), so the new door cases read as a caller
that holds it.
- No dogfood pin is added. Each door's answer is fully determined by the
real protocol's layered read plus the predicate, and the unit pins
exercise both unmocked. The composition reading above was taken once and
is recorded here. `datasource-restore-code-wins.dogfood.test.ts` already
pins the by-name read and the list over the real composition after a
restart.
## Ablation (each door's pin goes red with the switch reverted)
Run at `69706663`, through `scripts/ablation-replace.mjs` in wrap mode.
The merge of `origin/main` after it did not touch either door file. The
door subjects are imported from `src` (`./rest-server.js`, and
`../http-dispatcher.js` into `domains/meta.ts`), so no rebuild sits on
the path.
- REST: anchor `decliner.declinesStoredRow(layered.type, layered.name)`
replaced by `(decliner as any).isShippedFlowName(layered.type,
layered.name)`. Anchor 1 to 0, blob `bca14816` to `8b422cf0`. Result:
`Tests 1 failed | 15 passed (16)`. The failing case was the `[#21986]`
main case (received `Shadow 21986` / `origin: runtime`). Every
shipped-flow pin stayed green. Restored: blob equals HEAD (`bca14816`),
and `git diff HEAD` is empty.
- Runtime: anchor `protocol.declinesStoredRow(layered.type,
layered.name)` replaced the same way. Anchor 1 to 0, blob `65882c0e` to
`f78f1e7f`. Result: `Tests 1 failed | 11 passed (12)`, on the same case.
Restored: blob equals HEAD (`65882c0e`), and `git diff HEAD` is empty.
- Cross-package type, reverse leg: the runtime `Pick` key was replaced
by `'isDeclaredCodeDatasource'` (still private). `tsc --noEmit` went red
with `TS2344: Type '"isDeclaredCodeDatasource"' does not satisfy the
constraint 'keyof ObjectStackProtocolImplementation'`, while
`'declinesStoredRow'` in the same position typechecks green. So the
typecheck reads the rebuilt `.d.ts`. Restored: blob equals HEAD.
## Public surface for the contract review: the built `.d.ts`
`ObjectStackProtocolImplementation` members were read with the
TypeScript parser from the BUILT
`packages/metadata-protocol/dist/index.d.ts` and `index.d.cts`, before
(base) and after (this branch). Full declarations, whitespace collapsed,
sorted, and split public / non-public:
- public members: 65 before, 66 after. The `.d.ts` and `.d.cts` lists
are byte-identical in both runs, and the after list is unchanged when
rebuilt at `5e185d57`, after the merge.
- the whole set difference, order-insensitive:
- added public: `declinesStoredRow(type: string, name: unknown):
boolean;`
- removed non-public: `private declinesStoredRow;`
- nothing else moves.
## `isShippedFlowName` census after the switch (source, not tests)
- Calls: only inside the class.
`packages/metadata-protocol/src/protocol.ts:16769`
(`isStoredFlowEntryOfShippedName`, private) and `:16820`
(`declinesStoredRow`).
- Doc links in the same file: `:8976`, `:10119`, `:10904`, `:16760` and
`:16785`. Also the invariant text of
`scripts/adr-anchors/packages__metadata-protocol__src__protocol.ts.json`,
which is not a reader.
- `@objectstack/rest` and `@objectstack/runtime`: 0 readers.
- `../objectui` at `9dfaca654`: 0 hits. The control `getMetaItemLayered`
hits there, so the grep runs.
- Readers outside the class are tests only:
`protocol.flow-by-name-shipped-name.test.ts`,
`protocol.flow-layered-shipped-name.test.ts`,
`protocol.declines-stored-row-published.test.ts`,
`meta-published-overlay.test.ts`,
`meta-published-runtime-publish.test.ts` and
`flow-shipped-name-published-door.dogfood.test.ts`.
- So nothing in these two repositories needs it public any more. It is
not retired here, as ruled. The `cloud` repository was not read.
## Tests and gates
All of these were run at HEAD `5e185d57`, which is this branch after
merging `origin/main` `6befe19c`. That merge added one
`metadata-protocol` commit, which does not touch `protocol.ts`. The
suites below were also green at `69706663` before the merge.
- `pnpm --filter @objectstack/metadata-protocol test`: `Test Files 219
passed | 3 skipped (222)`, `Tests 28045 passed | 19 skipped (28064)`.
- `pnpm --filter @objectstack/rest test`: `Test Files 260 passed (260)`,
`Tests 4914 passed | 326 skipped (5240)`. Its `test:repo` project: `5
passed (5)`, `177 passed | 1 skipped`.
- `pnpm --filter @objectstack/runtime test`: `Test Files 331 passed
(331)`, `Tests 4670 passed | 19 skipped (4689)`. Its `test:repo`
project: `3 passed (3)`, `751 passed`.
- Typecheck of all three packages: exit 0. For rest and runtime this
includes `check:test-typecheck` (rest: 0 held files; runtime: the
existing ledger, unchanged).
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` derived 66 families over the 9 changed paths, the same list
the order carried. All 66 were run at `5e185d57` with their exit codes
recorded: 66 exit 0. The tool reconciled them: `66 derived, 66 run, 0
NOT-MEASURED, 0 UNRUN`.
- Before the merge, at `69706663`, `check:dual-build-cjs-loads` first
answered `PREREQUISITE NOT MET`, because eight unrelated packages had no
`dist/`. Once those were built it passed, and at `5e185d57` it passed on
the first run: `106 published require entry point(s) across 66
package(s) load`.
- `pnpm lint` (`eslint . --no-inline-config`, the whole repository, not
narrowed): exit 0 at `5e185d57`.
- `check-changeset-no-major --base origin/main`: `This diff introduces
no major bump`. The level axis was also driven offline, with `--event`
naming a payload that carries this body: `LEVEL AXIS: this PR declares
clause-② yes (widening), and it grades a package … at minor or above`
(`@objectstack/metadata-protocol: minor`).
## Acceptance notes
- **Deviation from the claim's file surface, declared here (commit
`77c8e7aa`, droppable on its own).** The claim says no other line of
`protocol.ts` moves. But `isShippedFlowName`'s doc comment ships in the
built `.d.ts`, and it stated: "The published doors ask this predicate
alone, so for such a name they still serve the stored row … That door is
not moved here." This PR makes that false. So that one paragraph now
reads "[#21986] The published doors ask declinesStoredRow in its place,
so for such a name they serve the code definition too." The diff is 3
lines added and 4 removed, comment only. It is a separate commit so the
`domain:engine` seat can drop it if it rules otherwise.
- **Changesets and the lockstep group.**
`@objectstack/metadata-protocol` is `minor` (the public method,
`Clause-②: yes (widening)`). `@objectstack/rest` and
`@objectstack/runtime` are each `patch` (`Clause-②: no`): each published
door stops serving such a row. All three packages sit in the one `fixed`
group in `.changeset/config.json`, so the release versions rest and
runtime at the group's minor anyway. The patch files carry their own
changelog text.
- The pending
`.changeset/21922-metadata-protocol-meta-read-declines-code-datasource-row.md`
still says "Not moved: `GET /api/v1/meta/datasource/:name/published`
still serves the stored row". That was true of its own change. This PR's
rest changeset states the door's new answer, and that other PR's
changeset is left untouched.
- Observation, not filed: the `GetPublishedMetaItemResponseSchema` JSDoc
in `packages/spec/src/api/protocol.zod.ts` describes the route's
producers as "the `state:'active'` overlay row via `getMetaItemLayered`,
else … `getPublished`". Since the shipped-flow change, and now for
code-defined datasources too, the layered producer can hand back the
`effective` layer instead of the row. This is comment-only drift on a
`packages/spec` path, which is outside this lane.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 803764a commit 1fb274e
9 files changed
Lines changed: 387 additions & 39 deletions
File tree
- .changeset
- packages
- metadata-protocol/src
- rest/src
- runtime/src/domains
Lines changed: 12 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
Lines changed: 10 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
Lines changed: 86 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16746 | 16746 | | |
16747 | 16747 | | |
16748 | 16748 | | |
16749 | | - | |
16750 | | - | |
16751 | | - | |
16752 | | - | |
| 16749 | + | |
| 16750 | + | |
| 16751 | + | |
16753 | 16752 | | |
16754 | 16753 | | |
16755 | 16754 | | |
| |||
16806 | 16805 | | |
16807 | 16806 | | |
16808 | 16807 | | |
| 16808 | + | |
| 16809 | + | |
| 16810 | + | |
| 16811 | + | |
| 16812 | + | |
| 16813 | + | |
| 16814 | + | |
| 16815 | + | |
| 16816 | + | |
| 16817 | + | |
16809 | 16818 | | |
16810 | | - | |
| 16819 | + | |
16811 | 16820 | | |
16812 | 16821 | | |
16813 | 16822 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
586 | 586 | | |
587 | 587 | | |
588 | 588 | | |
589 | | - | |
590 | | - | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
591 | 594 | | |
592 | 595 | | |
593 | | - | |
| 596 | + | |
594 | 597 | | |
595 | 598 | | |
596 | 599 | | |
| |||
602 | 605 | | |
603 | 606 | | |
604 | 607 | | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8621 | 8621 | | |
8622 | 8622 | | |
8623 | 8623 | | |
8624 | | - | |
8625 | | - | |
8626 | | - | |
8627 | | - | |
8628 | | - | |
8629 | | - | |
8630 | | - | |
8631 | | - | |
8632 | | - | |
| 8624 | + | |
| 8625 | + | |
| 8626 | + | |
| 8627 | + | |
| 8628 | + | |
| 8629 | + | |
| 8630 | + | |
| 8631 | + | |
| 8632 | + | |
| 8633 | + | |
| 8634 | + | |
| 8635 | + | |
| 8636 | + | |
| 8637 | + | |
| 8638 | + | |
8633 | 8639 | | |
8634 | 8640 | | |
8635 | 8641 | | |
8636 | 8642 | | |
8637 | 8643 | | |
8638 | 8644 | | |
8639 | | - | |
8640 | | - | |
8641 | | - | |
| 8645 | + | |
| 8646 | + | |
| 8647 | + | |
8642 | 8648 | | |
8643 | 8649 | | |
8644 | 8650 | | |
| |||
0 commit comments