diff --git a/packages/queue/src/store.mjs b/packages/queue/src/store.mjs index a28a60e87..c8983201d 100644 --- a/packages/queue/src/store.mjs +++ b/packages/queue/src/store.mjs @@ -501,6 +501,9 @@ function mutateAs(ctx, { verb, op, args, by, yes = false, derived = false }) { const cmp = witnessStep(ctx, loc, cur, { verb, op, args: cargs }); for (const e of cmp.confirmed) res.err.push(`durable now, never acknowledged: ${e.op} by ${e.by} at ${e.at}`); // Step 3: a recorded op answers with its receipt, before any view check. + if (verb === "add" && PRIVILEGED.has(actor) && cargs.gateOwner === null) { + throw refuse(`${actor} must pass --gate-owner SEAT on add: a privileged add has no default gate owner, so a row never gets a gate nobody meant`); + } const prior = cur.doc.log.find((e) => e.op === op); if (prior) { if (prior.verb !== verb || !sameJson(prior.args, cargs)) { @@ -528,12 +531,10 @@ function mutateAs(ctx, { verb, op, args, by, yes = false, derived = false }) { if (view.state === "unknown") throw refuse(unknownMessage(view)); if (view.state === "stale" && verb !== "accept-history") throw refuse(staleMessage(view, cur.doc.log)); // A privileged add names its gate owner. There is no default for it: the - // Jason default put a gate nobody meant on row 100. This check lives in the + // Jason default put a gate nobody meant on a row. This check lives in the // write path, not in applyEntry, so replay of old entries is unchanged and an - // older reader still loads the log. - if (verb === "add" && PRIVILEGED.has(actor) && cargs.gateOwner === null) { - throw refuse(`${actor} must pass --gate-owner SEAT on add: a privileged add has no default gate owner, so a row never gets a gate nobody meant (row 100 got jason by default)`); - } + // older reader still loads the log. It stays below step 3: a retry of an add + // recorded before this check answers with its receipt. // Step 5. const resolved = resolveFor(ctx, loc, cur, verb, cargs, cmp); const rev = cur.doc.revision + 1;