Skip to content

Commit 483b3ec

Browse files
committed
test(objectql): duplicatePackage refuses the copied view whose object copy was refused (#24563 patch round 2)
Seat answer A (6277565064): the case 'a name already claimed is refused up front' now pins two refusals, the ticket object and then view/ticket_views with list-view-object-outside-closure at views[0].object; the copy keeps the source's ticket, outside the copy package's closure. The changeset names duplicatePackage among the doors. Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK Co-authored-by: Claude <noreply@anthropic.com>
1 parent a0c24ab commit 483b3ec

2 files changed

Lines changed: 9 additions & 4 deletions

File tree

‎.changeset/24563-list-view-out-of-closure.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ Clause-②: yes (narrowing)
1212

1313
**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).
1414

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.
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`), a package draft publish (`publishPackageDrafts`), and a package copy (`duplicatePackage`), which writes its copies through the same door.
16+
- TO: `saveMetaItem` in publish mode, the draft promotion and `publishPackageDrafts` answer `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. In `duplicatePackage`, a copied list view whose bound object stays outside the copy package's closure is refused with the same rule and listed in its `failed[]`. One way that happens is when that object's own copy was refused.
1717
- 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.
1818

1919
**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.

‎packages/objectql/src/protocol-duplicate-package-unprefixed-object.test.ts‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,15 +284,20 @@ describe('ADR-0070 D4 — duplicatePackage of an object whose name lacks the sou
284284
const res = await protocol.duplicatePackage({ sourcePackageId: P, targetPackageId: Q });
285285

286286
expect(res.success).toBe(false);
287-
expect(res.failed).toHaveLength(1);
288-
const [refusal] = res.failed;
287+
expect(res.failed).toHaveLength(2);
288+
const [refusal, viewRefusal] = res.failed;
289289
expect(refusal).toMatchObject({ type: 'object', name: 'ticket' });
290290
// Located: the object, the name it would take, who holds that name,
291291
// and the source rename that clears it.
292292
expect(refusal.error).toContain("Object 'ticket'");
293293
expect(refusal.error).toContain("'dupdst_ticket'");
294294
expect(refusal.error).toContain("'dupsrc_ticket'");
295295
expect(refusal.error).toContain("Rename 'ticket' on the source");
296+
// [#24563] The `ticket` copy was refused, so the copy of `ticket_views`
297+
// keeps the source's `ticket` — an object outside the copy package's
298+
// closure — and the save door refuses it with the same rule.
299+
expect(viewRefusal).toMatchObject({ type: 'view', name: 'ticket_views' });
300+
expect(viewRefusal.error).toContain('views[0].object [list-view-object-outside-closure]');
296301

297302
// NEGATIVE CONTROL: the prefixed sibling took the name, with its own
298303
// body, and nothing of `ticket` was written under it.

0 commit comments

Comments
 (0)