Skip to content

Commit 0719d47

Browse files
committed
wip(metadata-protocol): a draft save naming no package is judged by D9.9 at its draft address
Claude-Session: https://claude.ai/code/session_01VXa74mKHXdwRyYmB3TCuo1 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2906cd8 commit 0719d47

1 file changed

Lines changed: 56 additions & 2 deletions

File tree

‎packages/metadata-protocol/src/protocol.ts‎

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2158,6 +2158,14 @@ interface UnboundOwnerRefusalState {
21582158
* definition, and one naming `P` finds no `P` draft (`NO_DRAFT`).
21592159
*/
21602160
adoptedDraft?: boolean;
2161+
/**
2162+
* [#23712] A DRAFT save naming no package (with `namedNoPackage`): the
2163+
* packages whose drafts of the object are stored with no package-unbound
2164+
* draft beside them. Once the rows are deleted, the draft address is the
2165+
* one package's draft (`packagelessDraftAddress`), so each is discarded
2166+
* by naming its package before the save writes the package-less draft.
2167+
*/
2168+
strandedDrafts?: readonly string[];
21612169
/**
21622170
* Where the refusal is raised: the save door (default), a draft promotion
21632171
* (the per-item publish and `publish-drafts`), or a registration of a
@@ -19725,6 +19733,7 @@ export class ObjectStackProtocolImplementation implements
1972519733
* | a DRAFT save naming `Q`, no package's row stored | the same delete, then a PUBLISH-mode save naming `Q` (it serves the object at once), then draft as before. A draft save registers nothing, so a draft sent right after the delete leaves the object unserved until it is published |
1972619734
* | rows bound to packages (`Q` or any other) are already stored beside the package-less one (the rows the save door wrote before this refusal existed) | delete each of them by naming its package, then the package-less definition, then a publish-mode save naming `Q`. Each row is named because a delete naming no package resolves to the one binding present, or is refused `409` over two ({@link packagelessAddress}), never to the package-less definition |
1972719735
* | one such row, a publish save naming NO package | it is addressed to that row ({@link packagelessAddress}); delete the row, then the same save edits the package-less definition. Over two bindings the save is refused `409` before this refusal |
19736+
* | [#23712] a DRAFT save naming NO package, whose draft address ({@link packagelessDraftAddress}) is a package `Q`'s: one row bound to `Q`, or no such row and a lone draft bound to `Q` | delete the row by naming its package, and with no package-unbound draft stored, discard each package's draft by naming its package (`?state=draft&package=`), then the same save writes the package-less draft. Deleting the row alone leaves `Q`'s draft, which the address then still names |
1972819737
* | a promotion of `Q`'s draft (the per-item publish, `publish-drafts`), a draft stored before the package-less row or by a draft save the door addressed to `Q` | the deletes above for the state, then the same publish: the draft stays stored, and its promotion registers it |
1972919738
* | a publish naming no package of a draft bound to NO package, which the address adopts into the one stored binding `P` | no publish moves that draft into `P` (measured: with `P`'s row gone it promotes into the package-less definition; one naming `P` answers `NO_DRAFT`). Delete `P`'s row, publish again (it updates the package-less definition), then the package-less DELETE and a publish-mode save naming `P` move the object |
1973019739
*
@@ -19769,6 +19778,25 @@ export class ObjectStackProtocolImplementation implements
1976919778
+ `addressed into '${q}', whose row is never served. DELETE ${path}?package=${q}, then publish again: `
1977019779
+ `the draft lands in the package-less definition. To move it into '${q}' then: the same DELETE with no `
1977119780
+ `package and no dropStorage (records stay), then save it naming '${q}' in publish mode.` + tail;
19781+
} else if (state.mode === 'draft' && state.namedNoPackage && state.at !== 'promote') {
19782+
// [#23712] A draft save naming no package writes the package's
19783+
// draft the address names. Each step names its package: a delete
19784+
// or discard naming none resolves by the rows the earlier steps
19785+
// leave. With the rows and the package drafts gone, the address is
19786+
// the package-unbound draft (`packagelessDraftAddress`).
19787+
const drafts = [...new Set(state.strandedDrafts ?? [])].sort();
19788+
const list = (pkgs: readonly string[]) => pkgs.map((pkg) => `'${pkg}'`).join(', ');
19789+
const what = [
19790+
...(rows.length > 0 ? [`delete its row${rows.length > 1 ? 's' : ''} bound to ${list(rows)}`] : []),
19791+
...(drafts.length > 0 ? [`discard its draft${drafts.length > 1 ? 's' : ''} bound to ${list(drafts)}`] : []),
19792+
].join(' and ');
19793+
const steps = [
19794+
...rows.map((pkg) => `DELETE ${path}?package=${pkg}`),
19795+
...drafts.map((pkg) => `DELETE ${path}?state=draft&package=${pkg}`),
19796+
].join(', then ');
19797+
message = `A draft save naming no package of object '${name}' writes a draft bound to '${q}', which no `
19798+
+ 'publish can serve: the object is served by its definition bound to no package. First '
19799+
+ `${what} (REST: ${steps}), then send this save again: it then writes the package-less draft.` + tail;
1977219800
} else if (rows.length > 0 && state.namedNoPackage) {
1977319801
// A save naming no package is addressed to the one binding present.
1977419802
message = `A save naming no package of object '${name}' is addressed to its stored definition bound to `
@@ -22579,8 +22607,28 @@ export class ObjectStackProtocolImplementation implements
2257922607
// owns (the `'sys_metadata'` sentinel): a save naming a package beside
2258022608
// it is refused here, with the move-into-a-package prescription
2258122609
// ({@link unboundOwnerPackageRefusal}).
22610+
//
22611+
// [#23712] …asked of the package the write lands in. A draft save
22612+
// naming no package keeps `packageId` empty (the repository resolves
22613+
// its binding, #11087), but it writes the draft
22614+
// {@link packagelessDraftAddress} names, which over a row bound to
22615+
// `Q`, or a lone draft bound to `Q`, is `Q`'s: judged with the
22616+
// request's empty package, the sentinel owner read `own` and the save
22617+
// answered `200` for a draft no publish can serve. An ambiguous
22618+
// address is left to the repository's `409`, as before.
2258222619
if (singularType === 'object') {
22583-
const layer = this.classifyObjectContribution(request.name, request.packageId);
22620+
const draftAddress = mode === 'draft'
22621+
&& request.packageId == null
22622+
&& request.source === undefined
22623+
&& request.writeFace !== 'package-duplicate'
22624+
? await this.packagelessDraftAddress(
22625+
singularType, request.name, itemDiscriminator(singularType, request.item),
22626+
)
22627+
: undefined;
22628+
const writtenPackage = draftAddress && draftAddress.kind !== 'ambiguous'
22629+
? draftAddress.into
22630+
: request.packageId;
22631+
const layer = this.classifyObjectContribution(request.name, writtenPackage);
2258422632
if (layer.kind === 'mismatch') {
2258522633
// [#23514] For a sentinel owner the way out depends on the
2258622634
// state: the mode, whether the caller named a package, and
@@ -22589,9 +22637,15 @@ export class ObjectStackProtocolImplementation implements
2258922637
const storedPackages = layer.ownerPackageId === 'sys_metadata'
2259022638
? (await this.packagelessAddress(singularType, request.name)).candidates
2259122639
: [];
22640+
// [#23712] With no package-unbound draft stored, the address
22641+
// is a package's draft once the rows are gone (`409` over
22642+
// two), so each such draft is discarded by its package too.
22643+
const strandedDrafts = draftAddress && draftAddress.kind !== 'ambiguous' && !draftAddress.drafts.unbound
22644+
? draftAddress.drafts.candidates
22645+
: [];
2259222646
throw ObjectStackProtocolImplementation.overlayPackageMismatchError(
2259322647
request.name, layer.packageId, layer.ownerPackageId,
22594-
{ mode, storedPackages, namedNoPackage },
22648+
{ mode, storedPackages, namedNoPackage, strandedDrafts },
2259522649
);
2259622650
}
2259722651
}

0 commit comments

Comments
 (0)