You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit a0c24ab
Browse filesBrowse the repository at this point in the historyBrowse files
fix(lint)!: refuse a package's list view bound outside its closure at the save door (#24563 patch round 1)
list-view-object-outside-closure is an error: a publish-mode write of a
list view into a package whose bound object is outside the package's
closure (or nowhere) is refused 422 INVALID_METADATA at the binding, with
the declare-the-dependency hint. Both pin layers flip to the refusal; the
in-closure, declared-dependency, platform-object, overlay and flow
controls are unchanged. Changeset: minor, !, Clause-② yes (narrowing),
FROM/TO/Fix, ADR-0087 not-required (no-migration-prescription).
Fixtures that bound a package's view outside its closure are re-trued to
an object the package may reach, assertions unchanged: the objectql
publish-drafts conformance view (a platform object), the objectql
duplicatePackage names-in-step source package (declares the third
package it references), and the dogfood cross-package default container
(the Studio-created package declares the showcase).
Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: .changeset/24563-list-view-out-of-closure.md
+17-8Lines changed: 17 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,26 +2,35 @@
2
2
'@objectstack/lint': minor
3
3
---
4
4
5
-
fix(lint): a list view written into a package and bound outside that package's closure is reported at the save door, not skipped (#24563)
5
+
fix(lint)!: the save door refuses a list view written into a package and bound outside that package's closure (#24563)
6
6
7
-
Clause-②: yes
7
+
Clause-②: yes (narrowing)
8
8
9
-
**Why.** The runtime save door judges a write into a package against that package's closure (ruling #9612): the package's own objects, the objects of the packages its manifest declares in `dependencies`, platform/system objects, and provenance-less overlay rows. Every other object is dropped from the snapshot before any rule runs. The list-view field rule read a binding missing from that snapshot as "an object this stack does not define" and skipped the whole list view. So a list view written into a package and bound to an object of a package it never declared (or to an object that exists nowhere) was stored with every field reference unjudged: `200`, no advisory, while the same body as an overlay write is refused `422 list-view-field-unknown` for an unknown `columns` field. No other rule judged the binding on a `view` write.
9
+
**Why.** The runtime save door judges a write into a package against that package's closure (ruling #9612). The closure is the package's own objects, the objects of the packages its manifest declares in `dependencies`, platform/system objects, and provenance-less overlay rows; every other object is dropped from the snapshot before any rule runs. The list-view field rule read a binding missing from that snapshot as "an object this stack does not define" and skipped the whole list view. So a list view written into a package and bound to an object of a package it never declared, or to an object that exists nowhere, was stored with every field reference unjudged: `200`, no advisory. The same body as an overlay write is refused `422 list-view-field-unknown` for an unknown `columns` field. No other rule judged the binding on a `view` write, and such a view then fails at render ("This view's query was rejected").
10
10
11
-
**What changes.** At the runtime save door (Studio, REST `/meta`, MCP), a list view written into a package whose bound object is outside the package's closure now draws one `list-view-object-outside-closure` warning, located at the key the binding is read from (`views[0].data.object` for a standalone list view). It rides the save response's `advisories` and the `[Protocol] authoring advisory` log line. This covers a publish-mode save (`PUT /api/v1/meta/view/:name?package=…`, `saveMetaItem`), the promotion of a draft (`publishMetaItem`), and a package draft publish (`publishPackageDrafts`, on the published item's `advisories`). An object that exists nowhere draws the same warning. It is the verdict a flow's out-of-closure trigger object already gets at the same door (`flow-trigger-unknown-object`, `warning`): reported, not refused. The write is still stored, and the list view's field references stay unjudged until the binding is inside the closure. An object written into a package whose built-in `listViews` entry retargets `data.object` outside the closure draws the same warning.
11
+
**What is refused.** A list view written into a package whose bound object is outside the package's closure is refused with one `list-view-object-outside-closure` issue. The issue is located at the key the binding is read from: `views[0].data.object` for a standalone list view, otherwise the container's `objectName` / `object`. The hint names the remedy. An object that exists nowhere is refused the same way. This is the verdict the dataset door already gives an out-of-closure base object under the same closure (`object-reference-unknown`). An object written into a package whose built-in `listViews` entry retargets `data.object` outside the closure is refused the same way. The list view's field references stay unjudged until the binding is inside the closure.
12
12
13
-
- Fix: declare a dependency on the package that defines the bound object in the `dependencies` of the writing package's manifest, or bind the view to an object inside the closure. Once the object is inside, its fields are judged: an unknown `columns` field is refused `422 list-view-field-unknown`, as at the overlay door.
13
+
**BREAKING — what moves for consumers.** This is an accept-set narrowing on the metadata save door, shipped as `minor` under the launch-window convention for accept-set narrowings (`.changeset/pre.json` is open in `next` pre mode).
14
+
15
+
- FROM: a publish-mode write of a list view into a package, bound outside the package's closure, answered `200` and was stored, with no issue. This covers `PUT /api/v1/meta/view/:name?package=…` (and `saveMetaItem` in publish mode), the promotion of a draft (`POST /api/v1/meta/view/:name/publish`, `publishMetaItem`), and a package draft publish (`publishPackageDrafts`).
16
+
- TO: it answers `422 INVALID_METADATA`, with the `list-view-object-outside-closure` issue at the binding, and nothing is stored. A package draft publish fails the item, and the batch with it.
17
+
- Fix: declare a dependency on the package that defines the bound object in the `dependencies` of the writing package's manifest, or bind the view to an object the package may reach (its own, a declared dependency's, or a platform object). Then save again. Once the object is inside the closure its fields are judged: an unknown `columns` field is refused `422 list-view-field-unknown`, as at the overlay door. Saving the view as a draft (`mode: 'draft'`) is still allowed, because drafts are never gated; publishing that draft is judged.
18
+
19
+
**The next save of a stored list view bound outside its package's closure is refused with the fix; stored views keep serving until then.** The door judges writes only. A stored row is not re-judged at boot, and it loads and is served as before.
14
20
15
21
**Unchanged.**
16
22
17
23
- A list view bound to an object inside the closure (the package's own, a declared dependency's, a platform/system object, an overlay row): same field verdicts, same refusals.
18
-
- A write that names no package (an overlay write): judged against the whole collection, exactly as before. An object that exists nowhere stays silent there.
24
+
- A write that names no package (an overlay write) is judged against the whole collection, exactly as before. An object that exists nowhere stays unjudged there.
25
+
- The flow door: `flow-trigger-unknown-object` stays an advisory.
19
26
-`os validate`, `os build` and `os lint`: no closure is stated, so the rule skips an unresolvable binding as before.
20
-
-Drafts are not gated.
21
-
- Measured before the change: the 11 views shipped in this repository (`examples/app-crm`, `app-todo`, `app-showcase`, `app-multi-package`, the `create-objectstack` blank template, `qa/downstream-contract`) carry 39 list-view bindings. None is outside its package's closure. The door's `view` rule set finds nothing in any of them, with the package's closure or without it, before or after this change. None of the 97 platform built-in list views retargets `data.object`. Deployed rows were not measured.
27
+
-`OS_ALLOW_UNLINTED_METADATA_WRITES=1` still turns a refusal into a logged write.
28
+
- Measured before crossing: the 11 views shipped in this repository carry 39 list-view bindings, in `examples/app-crm`, `app-todo`, `app-showcase` and `app-multi-package`, the `create-objectstack` blank template, and `qa/downstream-contract`. None is outside its package's closure, and the door's `view` rule set refuses none of them, with the package's closure or without it. None of the 97 platform built-in list views retargets `data.object`. Deployed rows were not measured.
22
29
23
30
**API.**
24
31
25
32
-`AuthoringRuleContext` gains an optional `packageScope` (`{ packageId, dependencies }`). Only the runtime publish gate sets it, and only when it narrowed the snapshot's objects to a package closure. `runAuthoringRules` never sets it.
26
33
-`validateListViewFieldRefs(stack, options?)` accepts an optional `{ packageScope }` (`ListViewFieldRefOptions`, exported).
27
34
-`LIST_VIEW_OBJECT_OUTSIDE_CLOSURE` (`'list-view-object-outside-closure'`) is exported.
35
+
36
+
<!-- adr-0087: not-required (no-migration-prescription) a refusal at the metadata save door of a list view written into a package and bound to an object outside that package's dependency closure: no authorable key, spelling, export or stored shape moves, and no stored row is read, rewritten or converted. A stored list view with such a binding keeps loading until it is next saved, and the repair is the author's choice of which package to declare as a dependency (or which object to bind), which no ledger entry can derive. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id covers this door (not already-registered); and the change is a door verdict, not a declaration or an export (not runtime-interface-only, type-surface-only or runtime-export-retired). -->
0 commit comments