@sema-agent/core
Version:
Stateless, task-oriented AI agent core
428 lines (427 loc) • 25.2 kB
JavaScript
import { strict as assert } from "node:assert";
import { MAILBOX_INVALID_PEER_META_CODE, MAILBOX_TOMBSTONED_RECIPIENT_CODE } from "../mailbox-store.js";
import { beginContract } from "./contract-harness.js";
export const MAILBOX_CONTRACT_SCOPE = "default";
const msg = (content, sentAt = 1000) => ({ content, sentAt });
async function withStores(mk, fn) {
const live = [];
const make = () => {
const s = mk();
live.push(s);
return s;
};
try {
await fn(make);
}
finally {
for (const s of live.splice(0))
s.close?.();
}
}
export async function mailboxStoreContract(mk, runAssertion) {
const { run: runRaw, settle } = beginContract(runAssertion);
const run = (name, fn) => runRaw(name, () => withStores(mk, fn));
const S = MAILBOX_CONTRACT_SCOPE;
run("append 铸单调 seq;claimLease 取全部可见消息(不删);ack 才删;活租约=单消费者", async (make) => {
const s = make();
assert.equal(await s.append(S, "a1", msg("m1")), 1);
assert.equal(await s.append(S, "a1", msg("m2")), 2);
assert.equal(await s.peekCount(S, "a1"), 2);
const lease = await s.claimLease(S, "a1", "w1", 60_000, 10_000);
assert.deepEqual(lease.messages.map((m) => m.content), ["m1", "m2"]);
assert.equal(lease.maxSeq, 2);
assert.equal(await s.claimLease(S, "a1", "w2", 60_000, 20_000), null);
assert.equal(await s.peekCount(S, "a1"), 2);
await s.ack(S, "a1", "w1", 2);
assert.equal(await s.peekCount(S, "a1"), 0);
});
run("X-1 无损:lease 过期/release 后消息按原 seq 回可见;seq 在箱的一生中永不复用", async (make) => {
const s = make();
await s.append(S, "a1", msg("m1"));
const l1 = await s.claimLease(S, "a1", "w1", 1_000, 10_000);
assert.equal(l1.maxSeq, 1);
const l2 = await s.claimLease(S, "a1", "w2", 1_000, 20_000);
assert.deepEqual(l2.messages.map((m) => m.seq), [1]);
await s.releaseLease(S, "a1", "w2");
const l3 = await s.claimLease(S, "a1", "w3", 1_000, 20_500);
assert.deepEqual(l3.messages.map((m) => m.seq), [1]);
await s.ack(S, "a1", "w3", 1);
assert.equal(await s.append(S, "a1", msg("m2")), 2);
});
run("lease 窗内 append:后到消息在下次 claim 时可见(lease 只覆盖 claim 时刻快照)", async (make) => {
const s = make();
await s.append(S, "a1", msg("m1"));
const l = await s.claimLease(S, "a1", "w1", 60_000, 10_000);
assert.equal(l.maxSeq, 1);
await s.append(S, "a1", msg("m2"));
await s.ack(S, "a1", "w1", l.maxSeq);
assert.equal(await s.peekCount(S, "a1"), 1);
const next = await s.claimLease(S, "a1", "w2", 60_000, 20_000);
assert.deepEqual(next.messages.map((m) => m.content), ["m2"]);
});
run("drop 清整箱;reap 按最新消息龄整箱老化", async (make) => {
const s = make();
await s.append(S, "a1", msg("old", 1_000));
await s.append(S, "a2", msg("fresh", 90_000));
assert.equal(await s.reap(S, 100_000, { maxAgeMs: 50_000 }), 1);
assert.equal(await s.peekCount(S, "a1"), 0);
assert.equal(await s.peekCount(S, "a2"), 1);
await s.drop(S, "a2");
assert.equal(await s.peekCount(S, "a2"), 0);
});
run("RB-86 空箱 ⇒ null:没消息就不发租约(既不假装投递,也不 fence 掉后来真正的消费者)", async (make) => {
const s = make();
assert.equal(await s.claimLease(S, "never-written", "o1", 60_000, 1_000), null, "从未写过的箱");
await s.append(S, "a1", msg("m1"));
const first = await s.claimLease(S, "a1", "o1", 60_000, 1_000);
await s.ack(S, "a1", "o1", first.maxSeq);
assert.equal(await s.peekCount(S, "a1"), 0);
assert.equal(await s.claimLease(S, "a1", "o1", 60_000, 2_000), null, "全部 ack 后箱内零可见消息");
assert.equal(await s.claimLease(S, "a2", "ghost", 60_000, 3_000), null);
await s.append(S, "a2", msg("real", 4_000));
const real = await s.claimLease(S, "a2", "o2", 60_000, 4_000);
assert.deepEqual(real?.messages.map((m) => m.content), ["real"], "空取不该留下 fence 后来者的幽灵租约");
});
run("RB-261 守恒律:一条消息在被 ack 消费之前必须至少投递过一次(maxSeq 不得覆盖未投递项)", async (make) => {
const s = make();
const appended = ["m1", "m2", "m3"];
for (const [i, c] of appended.entries())
await s.append(S, "a1", msg(c, 1_000 + i));
const delivered = new Set();
let round = 0;
while ((await s.peekCount(S, "a1")) > 0) {
round += 1;
assert.ok(round < 10, "要么有限轮内收敛,要么就是投递不动了");
const owner = `w${round}`;
const lease = await s.claimLease(S, "a1", owner, 60_000, 10_000 + round);
assert.notEqual(lease, null, "还有可见消息时 claimLease 不得返回 null");
for (const m of lease.messages)
delivered.add(m.content);
await s.ack(S, "a1", owner, lease.maxSeq);
}
assert.deepEqual([...delivered].sort(), [...appended].sort(), "每条被消费掉的消息都必须先被投递过");
});
run("releaseLease owner-fenced:非持有者的 release 是 no-op(活租约的单消费者窗口不被外人撬开)", async (make) => {
const s = make();
await s.append(S, "a1", msg("m1"));
const held = await s.claimLease(S, "a1", "owner-A", 60_000, 10_000);
assert.equal(held.messages.length, 1);
await s.releaseLease(S, "a1", "not-the-owner");
assert.equal(await s.claimLease(S, "a1", "rival", 60_000, 11_000), null, "A 的活租约必须仍然 fence 住 rival");
await s.releaseLease(S, "a1", "owner-A");
const after = await s.claimLease(S, "a1", "rival", 60_000, 12_000);
assert.deepEqual(after?.messages.map((m) => m.seq), [1], "归还后同一条按原 seq 回可见");
});
run("peerMeta 回环:典型 typed 记录逐字节回读;缺席保持缺席(pre-385/foreign 记录语义);空对象也是「在场」", async (make) => {
const s = make();
const full = {
fromSession: "sess_1",
fromMode: "prompting",
senderKey: "k-1",
kind: "peer_message",
fromScope: "team/a b",
gateReceiptId: "gr-9",
};
await s.append(S, "a1", { content: "typed", sentAt: 1_000, peerMeta: full });
await s.append(S, "a1", { content: "plain", sentAt: 1_001 });
await s.append(S, "a1", { content: "empty-meta", sentAt: 1_002, peerMeta: {} });
const lease = await s.claimLease(S, "a1", "w1", 60_000, 10_000);
assert.deepEqual(lease.messages.map((m) => m.content), ["typed", "plain", "empty-meta"]);
assert.deepEqual(lease.messages[0].peerMeta, full, "六字段逐字节回读");
assert.equal("peerMeta" in lease.messages[1], false, "未带 peerMeta 的记录回读时该键必须缺席(不是 undefined 值)");
assert.deepEqual(lease.messages[2].peerMeta, {}, "空对象=在场但无字段,与缺席区分");
});
run("peerMeta 坏值响亮:garbage 以具名码拒 append,且零副作用(不入箱、不耗 seq、不动租约)", async (make) => {
const s = make();
const seqBefore = await s.append(S, "a1", { content: "anchor", sentAt: 1_000 });
const held = await s.claimLease(S, "a1", "holder", 60_000, 10_000);
assert.equal(held?.messages.length, 1);
const codeOf = (e) => e?.code;
class Meta {
fromSession = "s";
}
const hiddenKey = { fromSession: "s" };
Object.defineProperty(hiddenKey, "bogus", { value: 1, enumerable: false });
for (const [label, bad] of [
["fromMode 集外值", { fromMode: "root" }],
["kind 集外值", { kind: "gossip" }],
["未知键", { fromSession: "s", bogus: 1 }],
["空字符串", { senderKey: "" }],
["非字符串", { gateReceiptId: 7 }],
["数组", ["fromSession"]],
["null", null],
["字符串", "fromSession=x"],
["Date", new Date(0)],
["Map", new Map()],
["class 实例", new Meta()],
["symbol 键", { fromSession: "s", [Symbol("hidden")]: 1 }],
["不可枚举的未知键", hiddenKey],
]) {
let thrown;
try {
await s.append(S, "a1", { content: `bad:${label}`, sentAt: 2_000, peerMeta: bad });
}
catch (e) {
thrown = e;
}
assert.notEqual(thrown, undefined, `${label}:必须拒`);
assert.equal(codeOf(thrown), MAILBOX_INVALID_PEER_META_CODE, `${label}:拒必须带码`);
}
assert.equal(await s.peekCount(S, "a1"), 1, "被拒的 append 不得入箱");
assert.equal(await s.claimLease(S, "a1", "rival", 60_000, 11_000), null, "被拒的 append 不得动活租约");
await s.ack(S, "a1", "holder", held.maxSeq);
assert.equal(await s.append(S, "a1", { content: "next", sentAt: 3_000 }), seqBefore + 1, "被拒的 append 不得消耗 seq");
});
run("peerMeta 不串引用:消费者改写租约批里的 peerMeta,不得改到下次投递的记录", async (make) => {
const s = make();
await s.append(S, "a1", { content: "m1", sentAt: 1_000, peerMeta: { fromMode: "bypass", fromSession: "sess_1" } });
const first = await s.claimLease(S, "a1", "w1", 1_000, 10_000);
first.messages[0].peerMeta.fromSession = "forged";
await s.releaseLease(S, "a1", "w1");
const again = await s.claimLease(S, "a1", "w2", 1_000, 20_000);
assert.deepEqual(again.messages[0].peerMeta, { fromMode: "bypass", fromSession: "sess_1" }, "重投的记录必须是原样");
});
await settle();
}
export async function mailboxAckOwnershipContract(mk, runAssertion) {
const { run: runRaw, settle } = beginContract(runAssertion);
const run = (name, fn) => runRaw(name, () => withStores(mk, fn));
const S = MAILBOX_CONTRACT_SCOPE;
run("a non-holder's ack does not delete messages held under another owner's LIVE lease", async (make) => {
const s = make();
await s.append(S, "a1", { content: "m1", sentAt: 1 });
await s.append(S, "a1", { content: "m2", sentAt: 2 });
const lease = await s.claimLease(S, "a1", "consumer-A", 60_000, 10_000);
assert.equal(lease?.messages.length, 2);
await s.ack(S, "a1", "stale-actor", lease.maxSeq);
assert.equal(await s.peekCount(S, "a1"), 2, "the messages must survive — this caller never held the lease");
});
run("angle 4 — internal consistency: releaseLease and ack now apply the SAME owner fence (was the asymmetry that proved this was an oversight)", async (make) => {
const s = make();
await s.append(S, "a1", { content: "m1", sentAt: 1 });
await s.claimLease(S, "a1", "real-owner", 60_000, 10_000);
await s.releaseLease(S, "a1", "wrong-owner");
await s.ack(S, "a1", "wrong-owner", 1);
assert.equal(await s.peekCount(S, "a1"), 1, "neither operation should have touched anything");
});
run("angle 5 — ack with no lease EVER claimed is a no-op, not a silent wipe", async (make) => {
const s = make();
await s.append(S, "a1", { content: "m1", sentAt: 1 });
await s.ack(S, "a1", "nobody", Number.MAX_SAFE_INTEGER);
assert.equal(await s.peekCount(S, "a1"), 1);
});
run("the legitimate holder's own ack still works (regression check — the fix must not break the happy path)", async (make) => {
const s = make();
await s.append(S, "a1", { content: "m1", sentAt: 1 });
await s.append(S, "a1", { content: "m2", sentAt: 2 });
const lease = await s.claimLease(S, "a1", "consumer-A", 60_000, 10_000);
await s.ack(S, "a1", "consumer-A", lease.maxSeq);
assert.equal(await s.peekCount(S, "a1"), 0);
});
run("angle 3 in detail — the real loss sequence (stale owner outlives its own expired lease, a NEW owner has since taken over): the new owner's custody survives", async (make) => {
const s = make();
await s.append(S, "a1", { content: "ship-the-release", sentAt: 1 });
await s.append(S, "a1", { content: "notify-oncall", sentAt: 2 });
const leaseA = await s.claimLease(S, "a1", "consumer-A", 1_000, 1_000);
assert.equal(leaseA?.messages.length, 2);
const leaseB = await s.claimLease(S, "a1", "consumer-B", 60_000, 5_000);
assert.equal(leaseB?.messages.length, 2);
await s.ack(S, "a1", "consumer-A", leaseA.maxSeq);
assert.equal(await s.peekCount(S, "a1"), 2, "B's live custody must survive A's stale ack");
await s.ack(S, "a1", "consumer-B", leaseB.maxSeq);
assert.equal(await s.peekCount(S, "a1"), 0);
});
await settle();
}
export async function mailboxBundledOnlyContract(mk, runAssertion) {
const { run: runRaw, settle } = beginContract(runAssertion);
const run = (name, fn) => runRaw(name, () => withStores(mk, fn));
run("RB-251:handle 含 NUL 被两个后端一致拒绝,且不得跨 (scope,handle) 串箱", async (make) => {
const s = make();
const NUL = String.fromCharCode(0);
await assert.rejects(s.append("s", `a${NUL}b`, msg("secret")));
assert.equal(await s.peekCount(`s${NUL}a`, "b"), 0, "歧义键下不得出现串箱可读");
await s.append("s", "plain", msg("ok"));
assert.equal(await s.peekCount("s", "plain"), 1);
});
run("多租户:同名 handle 在不同 scope 下是两个互不可见的箱(各自独立铸 seq)", async (make) => {
const s = make();
assert.equal(await s.append("tenant-a", "shared-handle", msg("for-a")), 1);
assert.equal(await s.append("tenant-b", "shared-handle", msg("for-b")), 1, "另一个 scope 的箱独立铸号");
assert.equal(await s.peekCount("tenant-a", "shared-handle"), 1);
const a = await s.claimLease("tenant-a", "shared-handle", "w", 60_000, 10_000);
assert.deepEqual(a.messages.map((m) => m.content), ["for-a"]);
const b = await s.claimLease("tenant-b", "shared-handle", "w", 60_000, 10_000);
assert.deepEqual(b.messages.map((m) => m.content), ["for-b"]);
await s.ack("tenant-a", "shared-handle", "w", a.maxSeq);
assert.equal(await s.peekCount("tenant-a", "shared-handle"), 0);
assert.equal(await s.peekCount("tenant-b", "shared-handle"), 1, "跨 scope 的 ack 不得越界");
await s.drop("tenant-b", "shared-handle");
assert.equal(await s.peekCount("tenant-b", "shared-handle"), 0);
});
await settle();
}
export async function mailboxCrossProcessContract(hooks) {
const { run: runRaw, settle } = beginContract(hooks.runAssertion);
const run = (name, fn) => runRaw(name, async () => {
const pair = await hooks.openPair();
try {
await fn(pair.local, pair.peer);
}
finally {
await pair.dispose();
}
});
const S = MAILBOX_CONTRACT_SCOPE;
const H = "session.shared";
const range = (n) => Array.from({ length: n }, (_, i) => i + 1);
run("跨进程 seq 单铸:两个进程交错 append N+N ⇒ seq 恰为 1..2N(无重复无空洞),每条消息在任一方视角都可见", async (local, peer) => {
const N = 40;
const seqs = [];
for (let i = 0; i < N; i++) {
const [a, b] = await Promise.all([local.append(S, H, msg(`local-${i}`, 1_000 + i)), peer.append(S, H, msg(`peer-${i}`, 2_000 + i))]);
seqs.push(a, b);
}
assert.deepEqual([...seqs].sort((x, y) => x - y), range(2 * N), "两方拿到的回执合起来必须恰是 1..2N");
assert.equal(await peer.peekCount(S, H), 2 * N, "对方进程的观察面必须看到本方的 append");
assert.equal(await local.peekCount(S, H), 2 * N);
const lease = await local.claimLease(S, H, "cycle-1", 60_000, 10_000);
assert.deepEqual(lease.messages.map((m) => m.seq), range(2 * N), "本方 claim 必须按 seq 序取到全部 2N 条");
assert.equal(lease.maxSeq, 2 * N);
const contents = new Set(lease.messages.map((m) => m.content));
for (let i = 0; i < N; i++) {
assert.ok(contents.has(`local-${i}`), `local-${i} 不得丢`);
assert.ok(contents.has(`peer-${i}`), `peer-${i} 不得丢`);
}
});
run("lease/ack 不跨进程交叉:一方的活租约 fence 住另一方的 claim;非持有方的 ack 是 no-op;持有方的 ack 对另一方立即可见;高水位跨对方的 ack 存活", async (local, peer) => {
await local.append(S, H, msg("m1", 1));
await peer.append(S, H, msg("m2", 2));
const held = await peer.claimLease(S, H, "cycle-peer", 60_000, 10_000);
assert.deepEqual(held.messages.map((m) => m.seq), [1, 2]);
assert.equal(await local.claimLease(S, H, "cycle-local", 60_000, 11_000), null, "对方进程的活租约必须 fence 住本方");
await local.ack(S, H, "cycle-local", held.maxSeq);
assert.equal(await peer.peekCount(S, H), 2, "非持有方的 ack 不得动持有方手上的批");
await peer.ack(S, H, "cycle-peer", held.maxSeq);
assert.equal(await local.peekCount(S, H), 0, "持有方的 ack 必须被本方看见");
assert.equal(await local.claimLease(S, H, "cycle-local", 60_000, 12_000), null);
assert.equal(await local.append(S, H, msg("m3", 3)), 3, "seq 高水位必须跨对方进程的 ack 存活(永不复用)");
await peer.append(S, H, msg("m4", 4));
const held2 = await peer.claimLease(S, H, "cycle-peer-2", 1_000, 20_000);
assert.deepEqual(held2.messages.map((m) => m.seq), [3, 4]);
const takeover = await local.claimLease(S, H, "cycle-local-2", 60_000, 30_000);
assert.deepEqual(takeover.messages.map((m) => m.seq), [3, 4], "过期租约的接管跨进程成立,seq 原样");
});
run("一方的 housekeeping 不丢另一方的 append:两进程各自反复压缩/重写期间交错写入 120 条,任一方视角都完整", async (local, peer) => {
const N = 60;
for (let i = 0; i < N; i++) {
await Promise.all([local.append(S, H, msg(`L${i}`, 1_000 + i)), peer.append(S, H, msg(`P${i}`, 2_000 + i))]);
}
const fromPeer = await peer.claimLease(S, H, "cycle-peer", 60_000, 10_000);
assert.deepEqual(fromPeer.messages.map((m) => m.seq), range(2 * N), "对方视角:seq 1..2N 全在");
await peer.releaseLease(S, H, "cycle-peer");
const fromLocal = await local.claimLease(S, H, "cycle-local", 60_000, 11_000);
assert.deepEqual(fromLocal.messages.map((m) => m.seq), range(2 * N), "本方视角:seq 1..2N 全在");
const contents = fromLocal.messages.map((m) => m.content).sort();
assert.deepEqual(contents, [...range(N).map((i) => `L${i - 1}`), ...range(N).map((i) => `P${i - 1}`)].sort());
await local.ack(S, H, "cycle-local", fromLocal.maxSeq);
assert.equal(await peer.peekCount(S, H), 0, "本方的 ack 对方可见");
});
run("reap 看得见对方进程的旧消息,哪怕本方缓存里这个箱是空的(已加载的空缓存不得替对方的旧消息说「没有」)", async (local, peer) => {
await local.append(S, H, msg("mine", 1_000));
const held = await local.claimLease(S, H, "cycle-local", 60_000, 1_000);
await local.ack(S, H, "cycle-local", held.maxSeq);
await peer.append(S, H, msg("old-from-peer", 2_000));
assert.equal(await local.reap(S, 100_000, { maxAgeMs: 50_000 }), 1, "对方写的过期消息必须被本方 reap 看见并老化");
assert.equal(await peer.peekCount(S, H), 0);
assert.equal(await peer.append(S, H, msg("after", 200_000)), 3, "高水位跨 reap 存活(对方视角)");
});
run("reap 与 drop 的 seq 语义跨进程:一方 reap 后另一方继续高水位;一方 drop 后另一方从 1 重铸", async (local, peer) => {
await peer.append(S, H, msg("old", 1_000));
assert.equal(await local.reap(S, 100_000, { maxAgeMs: 50_000 }), 1, "本方 reap 必须看见对方写的旧消息");
assert.equal(await peer.peekCount(S, H), 0);
assert.equal(await peer.append(S, H, msg("after-reap", 200_000)), 2, "reap 清箱但保高水位(RB-90 跨进程)");
await local.drop(S, H);
assert.equal(await peer.peekCount(S, H), 0, "本方 drop 对方可见");
assert.equal(await peer.append(S, H, msg("after-drop", 300_000)), 1, "drop 结束箱的一生:对方从 1 重铸");
assert.equal(await local.peekCount(S, H), 1);
});
await settle();
}
export async function mailboxTombstonedRecipientContract(mk, hooks) {
const { run: runRaw, settle } = beginContract(hooks.runAssertion);
const run = (name, fn) => runRaw(name, () => withStores(mk, fn));
const S = MAILBOX_CONTRACT_SCOPE;
const codeOf = (e) => e?.code;
run("预删除态的收件人:append 响亮拒且带码(既不静默收下级联即将删掉的消息,也不裸抛)", async (make) => {
const s = make();
await hooks.tombstone(s, S, "a1");
let thrown;
try {
await s.append(S, "a1", msg("m1"));
}
catch (e) {
thrown = e;
}
assert.notEqual(thrown, undefined, "静默收下 = 给发件人一张级联马上撕掉的回执");
assert.equal(codeOf(thrown), MAILBOX_TOMBSTONED_RECIPIENT_CODE, "裸抛与拒同形:消费方要能按码分诊");
let again;
try {
await s.append(S, "a1", msg("m2"));
}
catch (e) {
again = e;
}
assert.equal(codeOf(again), MAILBOX_TOMBSTONED_RECIPIENT_CODE);
});
run("拒的粒度是收件人,不是开关:同 scope 下另一个活收件人照常收信", async (make) => {
const s = make();
await hooks.tombstone(s, S, "a1");
assert.equal(await s.append(S, "a2", msg("still-live")), 1, "活收件人不受邻居的删除级联牵连");
assert.equal(await s.peekCount(S, "a2"), 1);
});
run("收件人身份是 (scope, handle) 整体:A 租户的删除级联不得拒掉 B 租户的同名收件人", async (make) => {
const s = make();
await hooks.tombstone(s, "tenant-a", "a1");
assert.equal(await s.append("tenant-b", "a1", msg("other-tenant")), 1, "同名 handle 在另一个租户下是另一个收件人");
assert.equal(await s.peekCount("tenant-b", "a1"), 1);
});
run("两件套还得是单射的:把 (scope, handle) 拼成一个字符串记态,不得让两个不同收件人撞成一个", async (make) => {
const s = make();
await hooks.tombstone(s, "tenant a", "b");
assert.equal(await s.append("tenant", "a b", msg("distinct-recipient")), 1, "拼键歧义不得把另一个收件人一起拒掉");
assert.equal(await s.peekCount("tenant", "a b"), 1);
});
run("副作用轴 · 活租约:被拒的 append 不得动别人手上的租约(单消费者围栏不因删除窗口松开)", async (make) => {
const s = make();
await s.append(S, "a1", msg("in-flight"));
const held = await s.claimLease(S, "a1", "consumer-A", 60_000, 10_000);
assert.equal(held?.messages.length, 1);
await hooks.tombstone(s, S, "a1");
await assert.rejects(s.append(S, "a1", msg("after-tombstone", 2_000)), "预删除态下的 append 必须拒");
assert.equal(await s.claimLease(S, "a1", "rival", 60_000, 11_000), null, "A 的活租约必须仍然 fence 住别人");
await s.ack(S, "a1", "consumer-A", held.maxSeq);
assert.equal(await s.peekCount(S, "a1"), 0);
});
run("拒的是入口,不是清仓:被拒的 append 零副作用,已入箱的消息按原 seq/内容原封不动", async (make) => {
const s = make();
const seq = await s.append(S, "a1", msg("parked-before"));
await hooks.tombstone(s, S, "a1");
let thrown;
let refused = false;
try {
await s.append(S, "a1", msg("after-tombstone", 2_000));
}
catch (e) {
refused = true;
thrown = e;
}
assert.equal(refused, true, "预删除态下的 append 必须拒");
assert.equal(codeOf(thrown), MAILBOX_TOMBSTONED_RECIPIENT_CODE, "箱非空时的拒同样要带码");
assert.equal(await s.peekCount(S, "a1"), 1, "被拒的 append 不得入箱(幽灵投递),也不得清箱");
const lease = await s.claimLease(S, "a1", "w1", 60_000, 10_000);
assert.deepEqual(lease?.messages.map((m) => m.seq), [seq]);
assert.deepEqual(lease?.messages.map((m) => m.content), ["parked-before"], "取到的仍是拒之前那一条");
});
await settle();
}