diff --git a/.changeset/30168-unshipped-locale-write-open-before-pair.md b/.changeset/30168-unshipped-locale-write-open-before-pair.md
new file mode 100644
index 00000000000..da78dc7c046
--- /dev/null
+++ b/.changeset/30168-unshipped-locale-write-open-before-pair.md
@@ -0,0 +1,11 @@
+---
+'@objectstack/metadata-protocol': minor
+---
+
+fix(metadata-protocol): an email template at a locale the base does not ship keeps its package-less write on a fresh install; the named-base refusal names `os migrate packageless-overlays`
+
+Clause-②: no
+Metadata-protocol: semantic
+
+- **An added locale is writable before the pair exists.** Both overlay-write closes (the package-less write, and the write naming the read-only base with `?package=`) stay open where the fix they would name cannot be taken: an `email_template` member at a locale the base does not ship is refused as a declared override, so its write stays open (ADR-0146 D11). The close asked the override write gate about the pair; on a fresh install the pair is not ensured yet, the gate's closure read answers nothing for a package that does not exist, and the close read that "not concerned" as "admitted". So `PUT /api/v1/meta/email_template/` with a zh-CN body beside a base that ships en-US only answered `403 NOT_OVERRIDABLE`, naming a pair write that was refused too (`declared_package_ships_no_such_item`). The close now judges a pair that is not installed with the closure the ensure door gives it (the base and what the base reaches), so the write is accepted, as it is once the pair exists. A member the base ships is closed exactly as before.
+- **The named-base refusal names the ceremony.** `os migrate packageless-overlays` moves a row stored bound to the read-only base into the pair with its declaration. The sentence for a save, publish or rollback naming the base now says such a row "is served until moved by `os migrate packageless-overlays`, overridden by the pair, or deleted naming the base". One spelling, every door; the code and status (`403 NOT_OVERRIDABLE`) are unchanged.
diff --git a/packages/metadata-protocol/src/protocol-packaged-base-refusal.ts b/packages/metadata-protocol/src/protocol-packaged-base-refusal.ts
index 7c958aa1395..6d461885e9e 100644
--- a/packages/metadata-protocol/src/protocol-packaged-base-refusal.ts
+++ b/packages/metadata-protocol/src/protocol-packaged-base-refusal.ts
@@ -46,6 +46,7 @@ import {
import { SysMetadataRepository } from './sys-metadata-repository.js';
import { OBJECT_EXTENSION_TYPE } from './protocol-object-extension-item.js';
import { customizationPairEnsureRefusal, type PairRegistry } from './protocol-customization-package.js';
+import { resolvePackageClosure } from '@objectstack/lint/runtime';
import { overlayMemberField, overlayMemberOfBody, overlayMemberValue } from './migrations/overlay-index.js';
import {
customizationBasePackageId,
@@ -254,12 +255,12 @@ export function packagelessOverlayWriteRefusal(
closed: { base: string; pair: string },
operation: PackagelessWriteOperation,
): Error {
- const { item, shipped, pair, declared, writeInto } = closedOverlayWriteClauses(request, closed);
- const rule = 'A write naming no package is closed where `os migrate packageless-overlays` can move its row '
+ const { item, shipped, pair, declared, writeInto, ceremony } = closedOverlayWriteClauses(request, closed);
+ const rule = `A write naming no package is closed where ${ceremony} can move its row `
+ `(ADR-0146 D11); a package-less row stored before it is served until that command moves it into '${closed.pair}'.`;
const message = operation === 'rollback'
? `${item} ${shipped}, and a rollback of its package-less row is closed (ADR-0146 D11): run `
- + `\`os migrate packageless-overlays\` to move the row into ${pair}; to restore a version from before `
+ + `${ceremony} to move the row into ${pair}; to restore a version from before `
+ `the move, save that version's body there, declaring ${declared}.`
: operation === 'delete'
? `${item} ${shipped}: remove its customization from ${pair}. ${rule}`
@@ -274,6 +275,13 @@ export function packagelessOverlayWriteRefusal(
* the declaration, and the fix — write it into the pair, declaring the
* override. ONE spelling: a writer refused at either address reads the same
* fix, and only the rule clause that follows it says which address is closed.
+ *
+ * Two more clauses, one spelling each: `ceremony`, the command that moves a
+ * stored row into the pair (both sentences name it), and `boundRowServed`,
+ * what serves a row bound to the base stored before the close until one of
+ * the three open doors acts on it — [#29392] that command, which moves it
+ * into the pair with its declaration, the pair's declared override, or a
+ * delete naming the base.
*/
function closedOverlayWriteClauses(
request: { type: string; name: string },
@@ -286,7 +294,9 @@ function closedOverlayWriteClauses(
const declared = `overrides: { package: '${closed.base}', basedOn }`;
const writeInto = (operation: 'save' | 'publish') =>
`${item} ${shipped}: ${operation === 'publish' ? 'publish' : 'save'} it into ${pair}, declaring ${declared}.`;
- return { item, shipped, pair, declared, writeInto };
+ const ceremony = '`os migrate packageless-overlays`';
+ const boundRowServed = `is served until moved by ${ceremony}, overridden by the pair, or deleted naming the base.`;
+ return { item, shipped, pair, declared, writeInto, ceremony, boundRowServed };
}
/** `403 NOT_OVERRIDABLE`, the code and status the sealed write already answers, so neither close gains a code. */
@@ -306,11 +316,11 @@ function closedOverlayWriteError(message: string): Error {
* a row bound to the base. `rollback` is every restore of a stored version:
* the per-item rollback and [#29390] a commit revert's restore limb (and the
* package rollback through it), one sentence for both. ⛔ Not `delete`:
- * removing a row bound to the base writes nothing, and it is the one way to
- * clear such a row stored before the close — `os migrate packageless-overlays`
- * reads package-less rows only, so it never moves one. The removal serves the
- * base's own document again, or the pair's declared override where one exists
- * (it is served over the row).
+ * removing a row bound to the base writes nothing, and it is one way to clear
+ * such a row stored before the close; [#29392] `os migrate packageless-overlays`
+ * is the other, moving it into the pair with its declaration. The removal
+ * serves the base's own document again, or the pair's declared override where
+ * one exists (it is served over the row).
*/
export type NamedBaseWriteOperation = Exclude;
@@ -322,12 +332,14 @@ export type NamedBaseWriteOperation = Exclude`) onto an item of one of the five overlay
* types that package ships stored the customization as a row bound to the
* base, with no declaration: served at the base's address in place of its
- * document, outside the pair D1 puts every customization in, and not seen by
- * `os migrate packageless-overlays`. It is refused with the fix the
- * package-less close names ({@link closedOverlayWriteClauses}): write it into
- * the pair, declaring the override. A row stored that way before the close is
- * still served until the pair declares the override (the declared override is
- * served over it) or a delete naming the base removes it — both open.
+ * document, outside the pair D1 puts every customization in. It is refused
+ * with the fix the package-less close names ({@link closedOverlayWriteClauses}):
+ * write it into the pair, declaring the override. A row stored that way before
+ * the close is still served until one of three open doors acts on it, all
+ * named in the sentence ({@link closedOverlayWriteClauses}' `boundRowServed`):
+ * [#29392] `os migrate packageless-overlays` moves it into the pair with its
+ * declaration, the pair declares the override (served over it), or a delete
+ * naming the base removes it.
*
* Fix first, under the REST door's 500-character bound (`CLIENT_MESSAGE_MAX`,
* `@objectstack/types`), as the package-less sentence.
@@ -337,13 +349,12 @@ export function namedBaseOverlayWriteRefusal(
closed: { base: string; pair: string },
operation: NamedBaseWriteOperation,
): Error {
- const { item, shipped, pair, declared, writeInto } = closedOverlayWriteClauses(request, closed);
- const served = 'is served until the pair declares the override, or a delete naming the base removes it.';
+ const { item, shipped, pair, declared, writeInto, boundRowServed } = closedOverlayWriteClauses(request, closed);
const message = operation === 'rollback'
? `${item} ${shipped}, and a rollback of its row bound to the read-only base is closed (ADR-0146 D1, D11): `
- + `save the version's body into ${pair}, declaring ${declared}. The row ${served}`
+ + `save the version's body into ${pair}, declaring ${declared}. It ${boundRowServed}`
: `${writeInto(operation)} The read-only base itself takes no customization (ADR-0146 D1, D11): `
- + `a row bound to it from before ${served}`;
+ + `a row bound to it ${boundRowServed}`;
return closedOverlayWriteError(message);
}
@@ -356,8 +367,31 @@ export interface OverlayPairFix {
readonly base: string;
readonly pair: string;
readonly basedOn: string;
+ /**
+ * The closure the pair WILL have, present only while the pair is not
+ * installed: the ensure door declares the pair's one dependency, the base
+ * (`manifest.dependencies: { [base]: range }`), so its closure is the base
+ * and what the base reaches. The gate's own read
+ * (`resolveWritePackageScope`) answers nothing for a package the registry
+ * cannot produce, and its `not_concerned` on a declared body is no verdict
+ * about the write the fix takes.
+ */
+ readonly pendingPairScope?: WrittenPackageScope;
+}
+
+/** A written package and the closure of its declared dependencies, as `resolveWritePackageScope` answers it. */
+export interface WrittenPackageScope {
+ readonly packageId: string;
+ readonly dependencies: readonly string[];
}
+/**
+ * The override write gate as a close asks it: {@link overrideWriteVerdict},
+ * with the written package's closure when the caller holds it (a pair the
+ * ensure step would create, {@link OverlayPairFix.pendingPairScope}).
+ */
+export type OverrideWriteVerdictAsk = (request: OverrideWriteSubject, writtenScope?: WrittenPackageScope) => OverrideWriteVerdict;
+
/**
* [ADR-0146 D1, D11] The registry half of whether the fix both overlay-write
* closes name — write it into `base`'s pair, declaring the override — can be
@@ -390,7 +424,11 @@ export function overlayPairFixOf(
if (customizationPairEnsureRefusal(host.engine, registry, base, pair) !== undefined) return null; // the ensure door would refuse
const basedOn = registry.getPackage(base)?.manifest?.version;
if (typeof basedOn !== 'string' || basedOn === '') return null; // no `basedOn` to declare
- return { base, pair, basedOn };
+ if (registry.getPackage(pair)) return { base, pair, basedOn };
+ // The pair the ensure door would create: one dependency, the base.
+ const baseClosure = resolvePackageClosure(base, (id) => registry.getPackage(id) as { manifest?: unknown } | undefined);
+ const dependencies = [base, ...(baseClosure?.dependencies ?? []).filter((id) => id !== pair)];
+ return { base, pair, basedOn, pendingPairScope: { packageId: pair, dependencies } };
}
/**
@@ -400,9 +438,14 @@ export function overlayPairFixOf(
* identity at `member`, declaring `overrides: { package: base, basedOn }`. An
* `email_template` member at a locale the base does not ship is refused
* there, so its write stays open.
+ *
+ * The gate is asked about the pair the fix writes into, installed or not: a
+ * pair not yet ensured (a fresh install) is judged with the closure the
+ * ensure door gives it ({@link OverlayPairFix.pendingPairScope}), so the
+ * answer is the one the write meets once the pair exists.
*/
export function overlayPairFixAdmits(
- host: { readonly overrideWriteVerdict: (request: OverrideWriteSubject) => OverrideWriteVerdict },
+ host: { readonly overrideWriteVerdict: OverrideWriteVerdictAsk },
fix: OverlayPairFix,
subject: {
/** The singular type. */
@@ -419,7 +462,7 @@ export function overlayPairFixAdmits(
...(body ?? { name, ...(disc && member !== null ? { [disc]: member } : {}) }),
overrides: { package: fix.base, basedOn: fix.basedOn },
};
- const verdict = host.overrideWriteVerdict({ type, name, packageId: fix.pair, item: declaredBody });
+ const verdict = host.overrideWriteVerdict({ type, name, packageId: fix.pair, item: declaredBody }, fix.pendingPairScope);
return verdict.verdict !== 'refuse';
}
@@ -429,7 +472,7 @@ export interface NamedBaseCloseHost {
/** The read-only code packages that ship a name of an overlay type ([] for any other type). */
readonly overlayBaseShippers: (type: string, name: string) => string[];
/** The override write gate, as the declared write into the pair meets it at the save door. */
- readonly overrideWriteVerdict: (request: OverrideWriteSubject) => OverrideWriteVerdict;
+ readonly overrideWriteVerdict: OverrideWriteVerdictAsk;
}
/** The write naming a package that {@link namedBaseCloseSubject} judges. */
@@ -953,17 +996,25 @@ export function overrideWriteRefusalError(
* member ({@link overrideWriteMemberOf}), and an artifact of another member is
* no shipper: a registry that ignores `member` can answer one, as
* `mergeArtifactProtection` guards on the read side.
+ *
+ * `writtenScope`, when given for the written package, is its closure in
+ * place of the registry's read: a close asking about a pair the ensure step
+ * would create ({@link OverlayPairFix.pendingPairScope}). Every door's own
+ * write passes none.
*/
export function overrideWriteVerdict(
this: Pick,
request: OverrideWriteSubject,
+ writtenScope?: WrittenPackageScope,
): OverrideWriteVerdict {
const packageId = typeof request.packageId === 'string' && request.packageId.length > 0
? request.packageId
: undefined;
const declaration = declarationOf(request.item);
if (packageId === undefined && !declaration.present) return NOT_CONCERNED;
- const scope = packageId === undefined ? undefined : this.resolveWritePackageScope(packageId);
+ const scope = packageId === undefined
+ ? undefined
+ : writtenScope?.packageId === packageId ? writtenScope : this.resolveWritePackageScope(packageId);
// A named package the registry cannot produce is not this gate's to
// judge: both doors ask `uninstalledNamedBaseRefusal`, which refuses a
// write naming an uninstalled base (`WRITABLE_PACKAGE_REQUIRED`) — the
diff --git a/packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts b/packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts
index 8f4617e19f4..bc822f865da 100644
--- a/packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts
+++ b/packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts
@@ -137,8 +137,7 @@ import {
overlayPairFixAdmits,
overlayPairFixOf,
overrideWriteMemberOf,
- type OverrideWriteSubject,
- type OverrideWriteVerdict,
+ type OverrideWriteVerdictAsk,
} from './protocol-packaged-base-refusal.js';
import { readOnlyShippingPackages, shippingPackageIds } from './protocol-environment-rows.js';
import { isServedOverrideRowOf } from './served-override.js';
@@ -689,8 +688,12 @@ export interface PackagelessCloseHost {
readonly rethrowUnlessMetadataStoreUnprovisioned: ObjectStackProtocolImplementation['rethrowUnlessMetadataStoreUnprovisioned'];
/** The read-only code packages that ship a name of an overlay type ([] for any other type). */
readonly overlayBaseShippers: (type: string, name: string) => string[];
- /** The override write gate, as the ceremony's step 2 meets it through the save door. */
- readonly overrideWriteVerdict: (request: OverrideWriteSubject) => OverrideWriteVerdict;
+ /**
+ * The override write gate, as the ceremony's step 2 meets it through the
+ * save door: on a pair step 1 has not ensured yet, with the closure the
+ * ensure door gives it (`OverlayPairFix.pendingPairScope`).
+ */
+ readonly overrideWriteVerdict: OverrideWriteVerdictAsk;
}
/** The package-less write {@link packagelessCloseSubject} judges, and the row it would write. */
diff --git a/packages/metadata-protocol/src/protocol.named-base-overlay-write.test.ts b/packages/metadata-protocol/src/protocol.named-base-overlay-write.test.ts
index a3adc31ca08..c8cd3a596ae 100644
--- a/packages/metadata-protocol/src/protocol.named-base-overlay-write.test.ts
+++ b/packages/metadata-protocol/src/protocol.named-base-overlay-write.test.ts
@@ -25,7 +25,8 @@
* draft stays a draft and no active row is written;
* - the rollback of a row bound to the base: refused, the row unchanged;
* - CONTROLS, open as on `main`: a delete naming the base removes such a row
- * (the one way to clear it: the ceremony moves package-less rows only), a
+ * (one way to clear it; [#29392] `os migrate packageless-overlays` moves it
+ * into the pair, and the sentence names both), a
* draft discard naming it, the pair's declared save (served over a row
* bound to the base), a server-stated rewrite (`source` set), a writable
* package's same name, a name the base does not ship (ADR-0070 D1's own
@@ -105,7 +106,12 @@ function matches(row: Row, where: Record = {}): boolean {
});
}
-function makeProtocol() {
+/**
+ * `pairInstalled: false` boots the base with no pair yet: a fresh install,
+ * before anything ensured `.custom` (the ensure door, or the ceremony's
+ * step 1, creates it on the first write that needs it).
+ */
+function makeProtocol({ pairInstalled = true }: { pairInstalled?: boolean } = {}) {
const tables = new Map();
const table = (n: string) => {
if (!tables.has(n)) tables.set(n, []);
@@ -116,7 +122,8 @@ function makeProtocol() {
const hit = SHIPPED.find((i) => i.type === type && i.name === name);
return hit ? { ...ITEMS.find((i) => i.type === type && i.name === name)?.body(PACKAGED), name, label: PACKAGED, _packageId: hit.pkg, _provenance: 'package' } : undefined;
};
- const packageRecord = (id: string) => (MANIFESTS[id] ? { manifest: { id, ...MANIFESTS[id] } } : undefined);
+ const installed = (id: string) => MANIFESTS[id] !== undefined && (pairInstalled || id !== PAIR);
+ const packageRecord = (id: string) => (installed(id) ? { manifest: { id, ...MANIFESTS[id] } } : undefined);
const engine: any = {
// A booted code package is read-only (`isWritablePackage` reads `manifests` first).
manifests: new Map([[BASE, {}], [NO_PAIR_BASE, {}], [NO_PAIR_ID_BASE, {}], [NO_VERSION_BASE, {}]]),
@@ -161,7 +168,7 @@ function makeProtocol() {
getItem: (type: string, name: string) => artifact(type, name),
getObject: () => undefined,
getPackage: packageRecord,
- getAllPackages: () => Object.keys(MANIFESTS).map(packageRecord),
+ getAllPackages: () => Object.keys(MANIFESTS).filter(installed).map(packageRecord),
isPackageDisabled: () => false,
getArtifactItem: artifact,
applyNavContributions: (a: unknown) => a,
@@ -205,6 +212,10 @@ function expectClosedSentence(message: string, type: string, name: string, verb:
expect(message).toContain('ADR-0146 D1, D11');
if (verb === 'rollback') expect(message).toContain('a rollback of its row bound to the read-only base is closed');
else expect(message).toContain(`${verb} it into '${PAIR}'`);
+ // [#29392] The ceremony moves a row bound to the base into the pair: the
+ // sentence points an operator holding one to it, beside the other two doors.
+ expect(message).toContain('is served until moved by `os migrate packageless-overlays`, overridden by the pair, '
+ + 'or deleted naming the base.');
// The REST door cuts a refusal's text at 500 characters
// (`CLIENT_MESSAGE_MAX`, `@objectstack/types`): the whole sentence fits.
expect(message.length).toBeLessThan(500);
@@ -433,6 +444,45 @@ describe('CONTROLS: every step the fix takes, failing, leaves the write naming t
});
});
+describe('a fresh install, the pair not yet ensured: an email template at a locale the base does not ship stays open (ADR-0146 D11)', () => {
+ // The close asks the override write gate about the declared body written
+ // into the pair. Before the pair exists, that question is the one the
+ // ensured pair would meet (its closure: the base and what the base
+ // reaches), never "no such package, not concerned" read as "admitted".
+ const email = ITEMS.find((i) => i.type === 'email_template')!;
+ const unshipped = (label: string) => ({ ...email.body(label), locale: 'fr-FR' });
+
+ it('the package-less save is accepted and served as the environment\'s own member', async () => {
+ const { protocol, rows } = makeProtocol({ pairInstalled: false });
+
+ const saved = await protocol.saveMetaItem({ type: email.type, name: email.name, item: unshipped('Bienvenue') });
+
+ expect(saved).toMatchObject({ success: true });
+ expect(rows(email.type, email.name)).toEqual([{ package_id: null, state: 'active', label: 'Bienvenue' }]);
+ });
+
+ it('the save naming the base is accepted, as it is with the pair installed', async () => {
+ const { protocol, rows } = makeProtocol({ pairInstalled: false });
+
+ const saved = await protocol.saveMetaItem({ type: email.type, name: email.name, item: unshipped('Bienvenue'), packageId: BASE });
+
+ expect(saved).toMatchObject({ success: true });
+ expect(rows(email.type, email.name)).toEqual([{ package_id: BASE, state: 'active', label: 'Bienvenue' }]);
+ });
+
+ it('CONTROL: a member the base ships is still closed onto the pair, naming it, with the pair not yet ensured', async () => {
+ const { protocol, rows } = makeProtocol({ pairInstalled: false });
+
+ const packageless = await thrown(() => protocol.saveMetaItem({ type: email.type, name: email.name, item: email.body('Mine') }));
+ const namedBase = await thrown(() => protocol.saveMetaItem({ type: email.type, name: email.name, item: email.body('Mine'), packageId: BASE }));
+
+ expect(resolveThrownHttpError(packageless)).toMatchObject({ status: 403, code: 'NOT_OVERRIDABLE' });
+ expect(String(packageless.message)).toContain(`'${PAIR}' (?package=${PAIR})`);
+ expectClosed(namedBase, email.type, email.name, 'save');
+ expect(rows(email.type, email.name)).toEqual([]);
+ });
+});
+
describe('the commit doors: restoring a row bound to the base is closed, removing one stays open (#29390)', () => {
/** Each commit a second apart, so the package rollback's "strictly newer" reads an order. */
let clock = Date.parse('2026-11-02T00:00:00.000Z');
diff --git a/packages/metadata-protocol/src/protocol.ts b/packages/metadata-protocol/src/protocol.ts
index e24e9696ab0..f2d2da016e1 100644
--- a/packages/metadata-protocol/src/protocol.ts
+++ b/packages/metadata-protocol/src/protocol.ts
@@ -338,6 +338,7 @@ import type {
NamedBaseWriteSubject,
OverrideWriteSubject,
OverrideWriteVerdict,
+ WrittenPackageScope,
PackagelessWriteOperation,
} from './protocol-packaged-base-refusal.js';
// [ADR-0146 D10, #28564] The `__c` extension-field reservation's one judge.
@@ -8270,8 +8271,8 @@ export class ObjectStackProtocolImplementation implements
* {@link promoteDraftForPublish}) or a rollback that would land a row bound
* to the base. D1 puts a read-only package's customizations in its paired
* `.custom`; such a row is served at the base's address with no
- * declaration, outside the pair, and `os migrate packageless-overlays`
- * never sees it (its census reads package-less rows only). Refused with
+ * declaration, outside the pair, until [#29392] `os migrate
+ * packageless-overlays` moves it into the pair. Refused with
* the fix the package-less close names ({@link packagelessOverlayWriteRefusal},
* one spelling): write it into the pair, declaring the override.
*
@@ -8285,9 +8286,10 @@ export class ObjectStackProtocolImplementation implements
* that could not be taken.
*
* ⛔ Not a delete: removing a row bound to the base writes nothing, and it
- * is the only way to clear such a row stored before the close (the
- * ceremony moves package-less rows only). A row stored before it stays
- * served until the pair declares the override, which is served over it.
+ * is one way to clear such a row stored before the close; the ceremony is
+ * the other, moving it into the pair with its declaration. A row stored
+ * before it stays served until one of those, or the pair's declared
+ * override, which is served over it; the sentence names all three.
*
* Authoring writes only: the server-stated rewrites (`source` set, the
* `package-duplicate` write face) name the row they rewrite. Registry
@@ -8319,15 +8321,17 @@ export class ObjectStackProtocolImplementation implements
*
* Asked by the save door ahead of {@link refusePackagedBaseOverride}, and
* by the promotion ({@link promoteDraftForPublish}, so both
- * `publishMetaItem` and `publishPackageDrafts`) on the draft's body.
+ * `publishMetaItem` and `publishPackageDrafts`) on the draft's body. The
+ * two overlay-write closes ask it about the pair their fix writes into,
+ * with the closure a pair not yet ensured will have (`writtenScope`).
*/
- private overrideWriteVerdict(request: OverrideWriteSubject): OverrideWriteVerdict {
+ private overrideWriteVerdict(request: OverrideWriteSubject, writtenScope?: WrittenPackageScope): OverrideWriteVerdict {
const host: ThisParameterType = {
isWritablePackage: (...a) => this.isWritablePackage(...a),
resolveWritePackageScope: (...a) => this.resolveWritePackageScope(...a),
shippedArtifactsOf: (...a) => this.shippedArtifactsOf(...a),
};
- return overrideWriteVerdict.call(host, request);
+ return overrideWriteVerdict.call(host, request, writtenScope);
}
/** Docblock and body: {@link ProtocolWriteGatesFacet.refusePackagedBaseRemoval} in `protocol-write-gates.ts` [#29985 codemod]. */