@byollm/relay 0.1.0-alpha.23 → 0.1.0-alpha.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -143,12 +143,24 @@ interface ClaimInput {
143
143
  readonly runnerId: string;
144
144
  readonly owner: string;
145
145
  readonly device: PublicIdentity;
146
- /** The site this relay routes for. Multi-tenancy widens this to a set. */
147
- readonly siteId: string;
148
- /** Job kinds this device can actually run. */
146
+ /**
147
+ * The (site, owner) pairs this device may run work for — cloud_009 §3.
148
+ *
149
+ * **One set of pairs, not a set of sites and a set of owners.** Consent
150
+ * binds a user to a site, so the two cannot travel separately: a device
151
+ * whose owner consented to site A, serving a roster member who consented
152
+ * to site B, would have every element of both sets and no consented route
153
+ * between them. Two sets multiply; consent does not.
154
+ *
155
+ * Built by {@link Projection.routesFor} and matched with {@link routeKey},
156
+ * so the relay and a store in another repository agree on the encoding by
157
+ * calling the same function rather than by both spelling it out.
158
+ *
159
+ * This is the collapse that lets a claim stay one operation: a predicate
160
+ * cannot travel to a store over a network, and a set can.
161
+ */
162
+ readonly routes: ReadonlySet<string>;
149
163
  readonly kinds: ReadonlySet<string>;
150
- /** Whose work it may run — the projection, already collapsed to data. */
151
- readonly owners: ReadonlySet<string>;
152
164
  readonly max: number;
153
165
  readonly leaseMs: number;
154
166
  }
@@ -181,15 +193,6 @@ interface RelayStateOptions {
181
193
  }
182
194
  /** Why a lease-scoped operation was refused, in the caller's vocabulary. */
183
195
  type HolderRefusal = "not-found" | "not-holder" | "stale-lease" | "not-ready";
184
- /**
185
- * In-memory routing state.
186
- *
187
- * Deliberately not durable. The skeleton proves the protocol, and the
188
- * production hub replaces this with the closed multi-tenant router behind the
189
- * same shape (cloud_004 §9). Anything a restart loses here is a job that
190
- * returns to its site's queue — which is the behaviour a lapsed lease already
191
- * has to produce, so nothing new needs to be true for this to be safe.
192
- */
193
196
  declare class RelayState implements RoutingStore {
194
197
  #private;
195
198
  constructor(options?: RelayStateOptions);
@@ -218,7 +221,7 @@ declare class RelayState implements RoutingStore {
218
221
  siteId: string;
219
222
  stub: JobStub;
220
223
  }): Promise<RoutedJob>;
221
- job(jobId: string): Promise<RoutedJob | undefined>;
224
+ job(siteId: string, jobId: string): Promise<RoutedJob | undefined>;
222
225
  jobs(): Promise<RoutedJob[]>;
223
226
  /** Jobs a site must seal for, right now. */
224
227
  awaiting(siteId: string): Promise<RoutedJob[]>;
@@ -380,13 +383,39 @@ declare class RelayState implements RoutingStore {
380
383
  interface RoutingStore {
381
384
  /** The one clock every deadline in this store is stamped from. */
382
385
  now(): Promise<number>;
383
- /** Take a stub for routing. Idempotent by id. */
386
+ /**
387
+ * Take a stub for routing. Idempotent by id, **within a site**.
388
+ *
389
+ * A republished id belonging to the same site returns what is already
390
+ * routing, unchanged: a site that restarts and republishes its queue must
391
+ * not disturb work in flight (byollm_009 §4.2's replay argument rests on
392
+ * it).
393
+ *
394
+ * A republished id belonging to a **different** site is a different job —
395
+ * cloud_009 §3. Keys are `(site, id)`, so two sites choosing one id collide
396
+ * nowhere and there is nothing to refuse.
397
+ *
398
+ * cloud_008 finding 58 closed this by refusing the collision, which was as
399
+ * far as a single-site relay could go and left a cross-tenant existence
400
+ * oracle: a site knows its own stub is well-formed, so any answer it can
401
+ * tell apart from success confirms that somebody else holds that id. The
402
+ * refusal is gone with the collision.
403
+ */
384
404
  enqueue(input: {
385
405
  id: string;
386
406
  siteId: string;
387
407
  stub: JobStub;
388
408
  }): Promise<RoutedJob>;
389
- job(jobId: string): Promise<RoutedJob | undefined>;
409
+ /**
410
+ * One job, named by the site that published it — cloud_009 §3.
411
+ *
412
+ * The site id is a parameter rather than a scan, and the difference is
413
+ * finding eleven wearing a different hat: a reader that finds a job by id
414
+ * alone is a reader that can see every tenant's state through one door,
415
+ * which is exactly the anonymous read the debug page had and lost. The
416
+ * debug page is per-site or it is nothing.
417
+ */
418
+ job(siteId: string, jobId: string): Promise<RoutedJob | undefined>;
390
419
  jobs(): Promise<RoutedJob[]>;
391
420
  /** Jobs a site must seal for, right now. */
392
421
  awaiting(siteId: string): Promise<RoutedJob[]>;
@@ -1,4 +1,4 @@
1
- import { R as RoutingStore } from './store-C_NtPAvT.js';
1
+ import { R as RoutingStore } from './store-Dh5ukn3Y.js';
2
2
  import '@byollm/protocol';
3
3
 
4
4
  /**
@@ -1,3 +1,7 @@
1
+ import {
2
+ routeKey
3
+ } from "./chunk-ZH2GB6FY.js";
4
+
1
5
  // src/store-contract.ts
2
6
  import { generateKeys, publicIdentityOf } from "@byollm/protocol";
3
7
  import { describe, expect, it } from "vitest";
@@ -21,17 +25,21 @@ var ENVELOPE = {
21
25
  direction: "payload",
22
26
  deadlineAt: 41024448e5
23
27
  };
24
- var claimArgs = (over = {}) => ({
25
- runnerId: "runner_1",
26
- owner: "alice",
27
- device: publicIdentityOf(generateKeys(Date.now())),
28
- siteId: SITE,
29
- kinds: /* @__PURE__ */ new Set(["llm.generate"]),
30
- owners: /* @__PURE__ */ new Set(["alice"]),
31
- max: 10,
32
- leaseMs: 6e4,
33
- ...over
34
- });
28
+ var claimArgs = (over = {}) => {
29
+ const { owners = ["alice"], sites = [SITE], ...rest } = over;
30
+ return {
31
+ runnerId: "runner_1",
32
+ owner: "alice",
33
+ device: publicIdentityOf(generateKeys(Date.now())),
34
+ kinds: /* @__PURE__ */ new Set(["llm.generate"]),
35
+ routes: new Set(
36
+ sites.flatMap((site) => owners.map((owner) => routeKey(site, owner)))
37
+ ),
38
+ max: 10,
39
+ leaseMs: 6e4,
40
+ ...rest
41
+ };
42
+ };
35
43
  function describeStoreContract(name, options) {
36
44
  const { make, serialising, writeRawStub } = options;
37
45
  describe(`the routing store \u2014 ${name}`, () => {
@@ -40,7 +48,28 @@ function describeStoreContract(name, options) {
40
48
  await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
41
49
  await store.claim(claimArgs());
42
50
  await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
43
- expect((await store.job("a"))?.state).toBe("awaiting-payload");
51
+ expect((await store.job(SITE, "a"))?.state).toBe("awaiting-payload");
52
+ await done();
53
+ });
54
+ it("gives two sites the same id without either seeing the other", async () => {
55
+ const { store, done } = await make();
56
+ await store.enqueue({ id: "shared", siteId: SITE, stub: stub("shared") });
57
+ await store.enqueue({
58
+ id: "shared",
59
+ siteId: "site_other",
60
+ stub: stub("shared", "bob")
61
+ });
62
+ expect((await store.job(SITE, "shared"))?.stub.owner).toBe("alice");
63
+ expect((await store.job("site_other", "shared"))?.stub.owner).toBe("bob");
64
+ const granted = await store.claim(claimArgs());
65
+ expect(granted.map((job) => job.id)).toEqual(["shared"]);
66
+ expect(granted[0]?.owner).toBe("alice");
67
+ const replay = await store.enqueue({
68
+ id: "shared",
69
+ siteId: SITE,
70
+ stub: stub("shared")
71
+ });
72
+ expect(replay).toEqual(await store.job(SITE, "shared"));
44
73
  await done();
45
74
  });
46
75
  it("grants only what the owners set allows", async () => {
@@ -90,7 +119,7 @@ function describeStoreContract(name, options) {
90
119
  expect(renewed.renewed[0].expiresAt).toBeGreaterThan(
91
120
  granted.lease.expiresAt
92
121
  );
93
- expect((await store.job("a"))?.claimedBy?.leaseExpiresAt).toBe(
122
+ expect((await store.job(SITE, "a"))?.claimedBy?.leaseExpiresAt).toBe(
94
123
  renewed.renewed[0].expiresAt
95
124
  );
96
125
  await done();
@@ -99,7 +128,7 @@ function describeStoreContract(name, options) {
99
128
  const { store, done } = await make();
100
129
  await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
101
130
  const [granted] = await store.claim(claimArgs({ leaseMs: 1e3 }));
102
- const before = (await store.job("a"))?.claimedBy?.leaseExpiresAt;
131
+ const before = (await store.job(SITE, "a"))?.claimedBy?.leaseExpiresAt;
103
132
  const renewed = await store.renewLeases({
104
133
  runnerId: "runner_1",
105
134
  leases: [{ jobId: "a", leaseId: "a-grant-that-ended" }],
@@ -107,8 +136,10 @@ function describeStoreContract(name, options) {
107
136
  });
108
137
  expect(renewed.renewed).toEqual([]);
109
138
  expect(renewed.lost).toEqual(["a"]);
110
- expect((await store.job("a"))?.claimedBy?.leaseExpiresAt).toBe(before);
111
- expect((await store.job("a"))?.claimedBy?.leaseId).toBe(
139
+ expect((await store.job(SITE, "a"))?.claimedBy?.leaseExpiresAt).toBe(
140
+ before
141
+ );
142
+ expect((await store.job(SITE, "a"))?.claimedBy?.leaseId).toBe(
112
143
  granted.lease.id
113
144
  );
114
145
  await done();
@@ -126,7 +157,7 @@ function describeStoreContract(name, options) {
126
157
  await writeRawStub(store, "old", JSON.stringify(legacy));
127
158
  const granted = await store.claim(claimArgs({ max: 10 }));
128
159
  expect(granted.map((job) => job.id)).toEqual(["good"]);
129
- expect((await store.job("old"))?.state).toBe("queued");
160
+ expect((await store.job(SITE, "old"))?.state).toBe("queued");
130
161
  await done();
131
162
  });
132
163
  it("honours a self job's audience", async () => {
@@ -137,10 +168,10 @@ function describeStoreContract(name, options) {
137
168
  stub: { ...stub("private", "alice"), audience: "self" }
138
169
  });
139
170
  const granted = await store.claim(
140
- claimArgs({ owner: "owner", owners: /* @__PURE__ */ new Set(["owner", "alice"]) })
171
+ claimArgs({ owner: "owner", owners: ["owner", "alice"] })
141
172
  );
142
173
  expect(granted).toEqual([]);
143
- expect((await store.job("private"))?.state).toBe("queued");
174
+ expect((await store.job(SITE, "private"))?.state).toBe("queued");
144
175
  await done();
145
176
  });
146
177
  it("still offers that job to its owner's own machine", async () => {
@@ -151,7 +182,7 @@ function describeStoreContract(name, options) {
151
182
  stub: { ...stub("private", "alice"), audience: "self" }
152
183
  });
153
184
  const granted = await store.claim(
154
- claimArgs({ owner: "alice", owners: /* @__PURE__ */ new Set(["alice"]) })
185
+ claimArgs({ owner: "alice", owners: ["alice"] })
155
186
  );
156
187
  expect(granted.map((job) => job.id)).toEqual(["private"]);
157
188
  await done();
@@ -166,7 +197,7 @@ function describeStoreContract(name, options) {
166
197
  reason: "refused"
167
198
  });
168
199
  expect(await store.claim(claimArgs())).toEqual([]);
169
- expect((await store.job("a"))?.state).toBe("queued");
200
+ expect((await store.job(SITE, "a"))?.state).toBe("queued");
170
201
  expect(
171
202
  (await store.claim(claimArgs({ runnerId: "runner_2" }))).map(
172
203
  (j) => j.id
@@ -194,7 +225,7 @@ function describeStoreContract(name, options) {
194
225
  stub: { ...stub("late"), deadlineAt: Date.now() - 1e3 }
195
226
  });
196
227
  expect(await store.claim(claimArgs())).toEqual([]);
197
- expect(await store.job("late")).toBeUndefined();
228
+ expect(await store.job(SITE, "late")).toBeUndefined();
198
229
  await done();
199
230
  });
200
231
  it("names a cancelled job to the device holding it, and offers it to nobody", async () => {
@@ -232,7 +263,7 @@ function describeStoreContract(name, options) {
232
263
  duplicate: true,
233
264
  state: "done"
234
265
  });
235
- expect((await store.job("a"))?.disposition).toBe("ok");
266
+ expect((await store.job(SITE, "a"))?.disposition).toBe("ok");
236
267
  await done();
237
268
  });
238
269
  it("answers the device that finished it with `duplicate`", async () => {
@@ -267,7 +298,7 @@ function describeStoreContract(name, options) {
267
298
  disposition: "ok"
268
299
  });
269
300
  expect(stranger).toEqual({ refused: "not-holder" });
270
- expect((await store.job("a"))?.disposition).toBe("ok");
301
+ expect((await store.job(SITE, "a"))?.disposition).toBe("ok");
271
302
  await done();
272
303
  });
273
304
  it("refuses a result produced under a grant that ended", async () => {
@@ -282,8 +313,8 @@ function describeStoreContract(name, options) {
282
313
  disposition: "ok"
283
314
  });
284
315
  expect(late).toEqual({ refused: "stale-lease" });
285
- expect((await store.job("a"))?.state).toBe("awaiting-payload");
286
- expect((await store.job("a"))?.result).toBeUndefined();
316
+ expect((await store.job(SITE, "a"))?.state).toBe("awaiting-payload");
317
+ expect((await store.job(SITE, "a"))?.result).toBeUndefined();
287
318
  await done();
288
319
  });
289
320
  it("stamps deadlines from its own clock", async () => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/store-contract.ts"],"sourcesContent":["import { generateKeys, publicIdentityOf, type JobStub } from \"@byollm/protocol\";\nimport { describe, expect, it } from \"vitest\";\nimport type { RoutingStore } from \"./store.js\";\n\n/**\n * The routing store's behaviour, written once and run against every\n * implementation — cloud_008 finding 54.\n *\n * There were two copies. This package tested `RelayState`; `byollm-cloud`\n * tested `ValkeyRoutingStore` with a file that opened by explaining it was\n * \"written once and parameterised… a second copy of the scenario is a second\n * place for two implementations to quietly diverge\" — and was itself the\n * second copy. It had drifted to sixteen cases against the eighteen in the\n * ledger, which is exactly the divergence the sentence predicted, happening\n * to the sentence.\n *\n * So the contract lives here, in the package that declares `RoutingStore`,\n * and both repositories import it. A store that cannot pass this is not a\n * routing store, whatever it implements.\n *\n * ## Why this ships in the published package\n *\n * Because the implementation that matters most is in another repository. A\n * contract only the author can run is a description; one a third party runs\n * against their own implementation is a contract. `@byollm/relay/store-contract`\n * is a subpath export so nothing that imports the relay itself pulls in a\n * test framework.\n *\n * ## What this cannot prove\n *\n * `CLAIM_ATOMIC` is a MUST and every case here would pass against a store\n * that read, decided and wrote in three steps — in one process there is\n * nothing to interleave. Concurrency belongs with the implementation that\n * has a network in it, and `byollm-cloud`'s suite runs it against Valkey with\n * many connections. Named here so the omission is a decision rather than an\n * oversight.\n */\n\n/** The control-plane site id these cases route under. */\nexport const CONTRACT_SITE = \"site_store\";\n\n/** A site's identity key id — byollm_009 Amendment A §A.3. */\nconst SITE_KEY_ID = \"BYOLLM-TEST-SITE-KEY-ID\";\n\nconst SITE = CONTRACT_SITE;\n\nconst stub = (id: string, owner = \"alice\"): JobStub => ({\n id,\n kind: \"llm.generate\",\n owner,\n site: SITE_KEY_ID,\n audience: \"self\",\n sizeClass: \"small\",\n streaming: false,\n deadlineAt: 4_102_444_800_000,\n});\n\nconst ENVELOPE = {\n ciphertext: \"AAAA\",\n recipientKeyId: \"r\",\n senderKeyId: \"s\",\n direction: \"payload\" as const,\n deadlineAt: 4_102_444_800_000,\n};\n\nconst claimArgs = (over: Record<string, unknown> = {}) => ({\n runnerId: \"runner_1\",\n owner: \"alice\",\n device: publicIdentityOf(generateKeys(Date.now())),\n siteId: SITE,\n kinds: new Set([\"llm.generate\"]),\n owners: new Set([\"alice\"]),\n max: 10,\n leaseMs: 60_000,\n ...over,\n});\n\nexport interface StoreContractOptions {\n /**\n * A fresh, empty store, and how to dispose of it.\n *\n * Arrow-typed rather than a method, because the caller passes these\n * around: a method signature lets `this` travel with the call, and a\n * factory read off an options object is exactly where that goes wrong.\n */\n readonly make: () => Promise<{\n store: RoutingStore;\n done: () => Promise<void>;\n }>;\n /**\n * Whether this store writes stubs as bytes and reads them back.\n *\n * An in-process store holds typed objects and cannot hold a stub it cannot\n * parse; a serialising one can, because what it wrote may have been written\n * by a previous version. The case that covers it is skipped rather than\n * hidden — a reader should know which half of this contract each store is\n * proving (cloud_008 §2.1a).\n */\n readonly serialising?: boolean;\n /** Write a stub's raw bytes, bypassing serialisation. */\n readonly writeRawStub?: (\n store: RoutingStore,\n id: string,\n raw: string,\n ) => Promise<void>;\n}\n\n/** Run the contract. Call inside a suite; it declares its own `describe`. */\nexport function describeStoreContract(\n name: string,\n options: StoreContractOptions,\n): void {\n const { make, serialising, writeRawStub } = options;\n describe(`the routing store — ${name}`, () => {\n it(\"is idempotent by job id\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n // A republish must not disturb work in flight — byollm_009 §4.2's replay\n // argument rests on it.\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n expect((await store.job(\"a\"))?.state).toBe(\"awaiting-payload\");\n await done();\n });\n\n it(\"grants only what the owners set allows\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"mine\", siteId: SITE, stub: stub(\"mine\") });\n await store.enqueue({\n id: \"theirs\",\n siteId: SITE,\n stub: stub(\"theirs\", \"mallory\"),\n });\n const granted = await store.claim(claimArgs());\n expect(granted.map((job) => job.id)).toEqual([\"mine\"]);\n await done();\n });\n\n it(\"refuses a stale lease and honours the current one\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n await store.seal({ jobId: \"a\", siteId: SITE, envelope: ENVELOPE });\n\n expect(\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: \"an-older-grant\",\n }),\n ).toEqual({ refused: \"stale-lease\" });\n\n expect(\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n }),\n ).toEqual({ envelope: ENVELOPE });\n await done();\n });\n\n it(\"renews a grant it still holds, and extends the sweep with it\", async () => {\n // cloud_008 §0.6, at the contract level: renewal has to be one operation,\n // because between a read and a write another replica can sweep the lease\n // — and the renewal would then resurrect a grant on a job already back in\n // the queue. In Valkey that is a script; in memory it is a method; the\n // guarantee is the same one, so it is tested here rather than twice.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs({ leaseMs: 1_000 }));\n\n const renewed = await store.renewLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n leaseMs: 60_000,\n });\n\n expect(renewed.lost).toEqual([]);\n expect(renewed.renewed.map((r) => r.jobId)).toEqual([\"a\"]);\n // The store's own clock did the arithmetic — the caller passed a duration,\n // never an instant, which is what keeps two replicas from measuring one\n // lease against two clocks (cloud_006 §3.4).\n expect(renewed.renewed[0]!.expiresAt).toBeGreaterThan(\n granted!.lease.expiresAt,\n );\n\n // And it is the *stored* grant that moved, not just the number returned.\n // Reporting a renewal without performing one is the mutation that\n // survived the first version of the relay's own test for this.\n expect((await store.job(\"a\"))?.claimedBy?.leaseExpiresAt).toBe(\n renewed.renewed[0]!.expiresAt,\n );\n await done();\n });\n\n it(\"reports a lease the runner no longer holds as lost, and renews nothing\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs({ leaseMs: 1_000 }));\n const before = (await store.job(\"a\"))?.claimedBy?.leaseExpiresAt;\n\n const renewed = await store.renewLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: \"a-grant-that-ended\" }],\n leaseMs: 60_000,\n });\n\n expect(renewed.renewed).toEqual([]);\n expect(renewed.lost).toEqual([\"a\"]);\n // A refusal must not advance anything — the current holder's grant is\n // untouched by somebody else's stale renewal.\n expect((await store.job(\"a\"))?.claimedBy?.leaseExpiresAt).toBe(before);\n expect((await store.job(\"a\"))?.claimedBy?.leaseId).toBe(\n granted!.lease.id,\n );\n await done();\n });\n\n it(\"does not hand out a stub it cannot parse\", async () => {\n // cloud_008 §2.1a, and the case is not hypothetical: two jobs enqueued\n // before alpha.15 had no `site`, came back in a claim, and the daemon's\n // `.strict()` parse rejected the whole response. Two dead rows denied\n // claims to every device on the hub, and the queue could not drain past\n // them.\n //\n // The store had `JSON.parse(stub) as JobStub` — trusting what it wrote,\n // when what it wrote was written by a previous version. A routing store\n // outlives the deployment that filled it, so a wire change to `JobStub`\n // is a data migration whether or not anyone plans one.\n //\n // Only meaningful against a store that serialises: `RelayState` holds\n // typed objects and cannot reach this state, so it passes by\n // construction — which is worth saying rather than hiding, because a\n // reader should know which half of this contract each store is proving.\n const { store, done } = await make();\n if (!serialising || writeRawStub === undefined) {\n await done();\n return;\n }\n\n await store.enqueue({ id: \"good\", siteId: SITE, stub: stub(\"good\") });\n await store.enqueue({ id: \"old\", siteId: SITE, stub: stub(\"old\") });\n // Exactly what alpha.14 wrote: a stub with no `site`.\n const legacy = { ...stub(\"old\") } as Record<string, unknown>;\n delete legacy[\"site\"];\n await writeRawStub(store, \"old\", JSON.stringify(legacy));\n\n const granted = await store.claim(claimArgs({ max: 10 }));\n\n // The good one is still handed out — \"return nothing\" would pass a test\n // that only checked the bad one was absent, and would stop all routing.\n expect(granted.map((job) => job.id)).toEqual([\"good\"]);\n // And the bad one is back in the queue rather than stuck holding a lease\n // nobody received.\n expect((await store.job(\"old\"))?.state).toBe(\"queued\");\n await done();\n });\n\n it(\"honours a self job's audience\", async () => {\n // cloud_008 §2.1 at the contract level. The owners set is everyone this\n // device may run for, which for a Team owner's machine is the whole\n // roster — right for public and named, wrong for the one audience a user\n // picks because they want their own machine.\n const { store, done } = await make();\n await store.enqueue({\n id: \"private\",\n siteId: SITE,\n stub: { ...stub(\"private\", \"alice\"), audience: \"self\" },\n });\n\n // A roster owner's machine: may run alice's work in general.\n const granted = await store.claim(\n claimArgs({ owner: \"owner\", owners: new Set([\"owner\", \"alice\"]) }),\n );\n\n expect(granted).toEqual([]);\n expect((await store.job(\"private\"))?.state).toBe(\"queued\");\n await done();\n });\n\n it(\"still offers that job to its owner's own machine\", async () => {\n const { store, done } = await make();\n await store.enqueue({\n id: \"private\",\n siteId: SITE,\n stub: { ...stub(\"private\", \"alice\"), audience: \"self\" },\n });\n const granted = await store.claim(\n claimArgs({ owner: \"alice\", owners: new Set([\"alice\"]) }),\n );\n expect(granted.map((job) => job.id)).toEqual([\"private\"]);\n await done();\n });\n\n it(\"does not offer a job back to the runner that refused it\", async () => {\n // `REFUSAL_NOT_REOFFERED`. `releaseLeases` gained an **optional** reason,\n // which means a store can ignore it and still typecheck — this class has\n // now diverged from the interface that way twice, so the contract is the\n // only thing that catches it.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n reason: \"refused\",\n });\n\n expect(await store.claim(claimArgs())).toEqual([]);\n expect((await store.job(\"a\"))?.state).toBe(\"queued\");\n // And somebody else may still run it.\n expect(\n (await store.claim(claimArgs({ runnerId: \"runner_2\" }))).map(\n (j) => j.id,\n ),\n ).toEqual([\"a\"]);\n await done();\n });\n\n it(\"keeps a job claimable by a runner that only went away\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n reason: \"shutdown\",\n });\n\n expect((await store.claim(claimArgs())).map((j) => j.id)).toEqual([\"a\"]);\n await done();\n });\n\n it(\"stops offering a job past its deadline, and drops the ciphertext\", async () => {\n // cloud_008 §2.2. `deadlineAt` travelled on every stub and nothing read\n // it, so expired jobs were offered forever and their sealed payloads kept\n // for as long as the store lived.\n const { store, done } = await make();\n await store.enqueue({\n id: \"late\",\n siteId: SITE,\n stub: { ...stub(\"late\"), deadlineAt: Date.now() - 1_000 },\n });\n\n expect(await store.claim(claimArgs())).toEqual([]);\n expect(await store.job(\"late\")).toBeUndefined();\n await done();\n });\n\n it(\"names a cancelled job to the device holding it, and offers it to nobody\", async () => {\n // cloud_008 §2.2.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n\n expect(await store.cancel({ jobId: \"a\", siteId: SITE })).toBe(true);\n expect(await store.cancelRequests(\"runner_1\")).toEqual([\"a\"]);\n\n // Scoped to the caller's site: a site must not reach another's work by\n // guessing an id.\n expect(await store.cancel({ jobId: \"a\", siteId: \"someone_else\" })).toBe(\n false,\n );\n await done();\n });\n\n it(\"records a result once\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n const first = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n const replay = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: { ...ENVELOPE, ciphertext: \"different\" },\n disposition: \"error\",\n });\n\n expect(first).toEqual({ accepted: true, state: \"done\" });\n // `duplicate` — cloud_008 §3.6. Case 18 above is the one that says why.\n expect(replay).toEqual({\n accepted: false,\n duplicate: true,\n state: \"done\",\n });\n // The property, not the boolean: the second result did not overwrite.\n expect((await store.job(\"a\"))?.disposition).toBe(\"ok\");\n await done();\n });\n\n it(\"answers the device that finished it with `duplicate`\", async () => {\n // Contract case 18 — cloud_008 §3.6, and the eighteenth exists because\n // the compiler cannot see any of this: the return type's `duplicate` is\n // optional, so a store that never sets it typechecks perfectly while\n // enforcing `RESULT_IDEMPOTENT` by accident or not at all.\n //\n // A daemon whose acknowledgment was lost hears that its answer is already\n // recorded, rather than that its lease is stale — the second message\n // invents a worry about a result safely on disk.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n const first = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n expect(first).toEqual({ accepted: true, state: \"done\" });\n\n const replay = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: { ...ENVELOPE, ciphertext: \"a-different-result\" },\n disposition: \"error\",\n });\n expect(replay).toEqual({\n accepted: false,\n duplicate: true,\n state: \"done\",\n });\n\n // A *different* device, on a job that is terminal, gets the refusal it\n // would get for one that is not — otherwise the two answers differ and a\n // job id becomes a terminality probe.\n const stranger = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_2\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n expect(stranger).toEqual({ refused: \"not-holder\" });\n\n // And the first answer is what survived, which is the property rather\n // than any of the booleans above.\n expect((await store.job(\"a\"))?.disposition).toBe(\"ok\");\n await done();\n });\n\n it(\"refuses a result produced under a grant that ended\", async () => {\n // cloud_008 §1.4a at the contract level, and it belongs here more than\n // anywhere: `RoutingStore.complete` gained `leaseId` and this class kept\n // typechecking without it, because a parameter object with fewer\n // properties is assignable to one with more. The relay would have\n // enforced LEASE_HONORED on the memory store and not on the store that\n // runs in production, with nothing red on the way.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n\n const late = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: \"a-grant-that-ended\",\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n\n expect(late).toEqual({ refused: \"stale-lease\" });\n // Untouched: the current holder can still finish it.\n expect((await store.job(\"a\"))?.state).toBe(\"awaiting-payload\");\n expect((await store.job(\"a\"))?.result).toBeUndefined();\n await done();\n });\n\n it(\"stamps deadlines from its own clock\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const before = await store.now();\n const [granted] = await store.claim(claimArgs({ leaseMs: 45_000 }));\n const after = await store.now();\n\n // Bracketed rather than exact: a real store's clock moves between the two\n // reads, which is the whole reason it is the store's clock and not ours.\n expect(granted!.lease.expiresAt).toBeGreaterThanOrEqual(before + 45_000);\n expect(granted!.lease.expiresAt).toBeLessThanOrEqual(after + 45_000);\n await done();\n });\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/store-contract.ts"],"sourcesContent":["import { generateKeys, publicIdentityOf, type JobStub } from \"@byollm/protocol\";\nimport { describe, expect, it } from \"vitest\";\nimport { routeKey } from \"./state.js\";\nimport type { RoutingStore } from \"./store.js\";\n\n/**\n * The routing store's behaviour, written once and run against every\n * implementation — cloud_008 finding 54.\n *\n * There were two copies. This package tested `RelayState`; `byollm-cloud`\n * tested `ValkeyRoutingStore` with a file that opened by explaining it was\n * \"written once and parameterised… a second copy of the scenario is a second\n * place for two implementations to quietly diverge\" — and was itself the\n * second copy. It had drifted to sixteen cases against the eighteen in the\n * ledger, which is exactly the divergence the sentence predicted, happening\n * to the sentence.\n *\n * So the contract lives here, in the package that declares `RoutingStore`,\n * and both repositories import it. A store that cannot pass this is not a\n * routing store, whatever it implements.\n *\n * ## Why this ships in the published package\n *\n * Because the implementation that matters most is in another repository. A\n * contract only the author can run is a description; one a third party runs\n * against their own implementation is a contract. `@byollm/relay/store-contract`\n * is a subpath export so nothing that imports the relay itself pulls in a\n * test framework.\n *\n * ## What this cannot prove\n *\n * `CLAIM_ATOMIC` is a MUST and every case here would pass against a store\n * that read, decided and wrote in three steps — in one process there is\n * nothing to interleave. Concurrency belongs with the implementation that\n * has a network in it, and `byollm-cloud`'s suite runs it against Valkey with\n * many connections. Named here so the omission is a decision rather than an\n * oversight.\n */\n\n/** The control-plane site id these cases route under. */\nexport const CONTRACT_SITE = \"site_store\";\n\n/** A site's identity key id — byollm_009 Amendment A §A.3. */\nconst SITE_KEY_ID = \"BYOLLM-TEST-SITE-KEY-ID\";\n\nconst SITE = CONTRACT_SITE;\n\nconst stub = (id: string, owner = \"alice\"): JobStub => ({\n id,\n kind: \"llm.generate\",\n owner,\n site: SITE_KEY_ID,\n audience: \"self\",\n sizeClass: \"small\",\n streaming: false,\n deadlineAt: 4_102_444_800_000,\n});\n\nconst ENVELOPE = {\n ciphertext: \"AAAA\",\n recipientKeyId: \"r\",\n senderKeyId: \"s\",\n direction: \"payload\" as const,\n deadlineAt: 4_102_444_800_000,\n};\n\n/**\n * A claim, with its routes written out — cloud_009 §3.\n *\n * `routes` replaced `siteId` + `owners`, and the cases that pass `owners`\n * below now pass the pairs those owners are reachable through. That rewrite\n * is the contract's own statement of the rule: there is no way to express\n * \"this owner, any site\" here, because that is not a thing consent says.\n */\nconst claimArgs = (\n over: { owners?: string[]; sites?: string[] } & Record<string, unknown> = {},\n) => {\n const { owners = [\"alice\"], sites = [SITE], ...rest } = over;\n return {\n runnerId: \"runner_1\",\n owner: \"alice\",\n device: publicIdentityOf(generateKeys(Date.now())),\n kinds: new Set([\"llm.generate\"]),\n routes: new Set(\n sites.flatMap((site) => owners.map((owner) => routeKey(site, owner))),\n ),\n max: 10,\n leaseMs: 60_000,\n ...rest,\n };\n};\n\nexport interface StoreContractOptions {\n /**\n * A fresh, empty store, and how to dispose of it.\n *\n * Arrow-typed rather than a method, because the caller passes these\n * around: a method signature lets `this` travel with the call, and a\n * factory read off an options object is exactly where that goes wrong.\n */\n readonly make: () => Promise<{\n store: RoutingStore;\n done: () => Promise<void>;\n }>;\n /**\n * Whether this store writes stubs as bytes and reads them back.\n *\n * An in-process store holds typed objects and cannot hold a stub it cannot\n * parse; a serialising one can, because what it wrote may have been written\n * by a previous version. The case that covers it is skipped rather than\n * hidden — a reader should know which half of this contract each store is\n * proving (cloud_008 §2.1a).\n */\n readonly serialising?: boolean;\n /** Write a stub's raw bytes, bypassing serialisation. */\n readonly writeRawStub?: (\n store: RoutingStore,\n id: string,\n raw: string,\n ) => Promise<void>;\n}\n\n/** Run the contract. Call inside a suite; it declares its own `describe`. */\nexport function describeStoreContract(\n name: string,\n options: StoreContractOptions,\n): void {\n const { make, serialising, writeRawStub } = options;\n describe(`the routing store — ${name}`, () => {\n it(\"is idempotent by job id\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n // A republish must not disturb work in flight — byollm_009 §4.2's replay\n // argument rests on it.\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"awaiting-payload\");\n await done();\n });\n\n it(\"gives two sites the same id without either seeing the other\", async () => {\n // cloud_009 §3. Keyed by the bare id, the second site's enqueue returned\n // the *first* site's job (cloud_008 finding 58), and the refusal that\n // fixed it was a cross-tenant existence oracle: a site knows its own stub\n // is well-formed, so any answer it can tell apart from success confirms\n // somebody else holds that id. Keyed by (site, id), the collision does\n // not exist and there is nothing to answer.\n const { store, done } = await make();\n await store.enqueue({ id: \"shared\", siteId: SITE, stub: stub(\"shared\") });\n await store.enqueue({\n id: \"shared\",\n siteId: \"site_other\",\n stub: stub(\"shared\", \"bob\"),\n });\n\n // Two jobs, each its own site's.\n expect((await store.job(SITE, \"shared\"))?.stub.owner).toBe(\"alice\");\n expect((await store.job(\"site_other\", \"shared\"))?.stub.owner).toBe(\"bob\");\n\n // And a claim for one site does not see the other's, which is the\n // property the key exists for rather than a restatement of it.\n const granted = await store.claim(claimArgs());\n expect(granted.map((job) => job.id)).toEqual([\"shared\"]);\n expect(granted[0]?.owner).toBe(\"alice\");\n\n // The same site republishing is still absorbed — byollm_009 §4.2's\n // replay argument, which is what idempotence is for.\n const replay = await store.enqueue({\n id: \"shared\",\n siteId: SITE,\n stub: stub(\"shared\"),\n });\n expect(replay).toEqual(await store.job(SITE, \"shared\"));\n await done();\n });\n\n it(\"grants only what the owners set allows\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"mine\", siteId: SITE, stub: stub(\"mine\") });\n await store.enqueue({\n id: \"theirs\",\n siteId: SITE,\n stub: stub(\"theirs\", \"mallory\"),\n });\n const granted = await store.claim(claimArgs());\n expect(granted.map((job) => job.id)).toEqual([\"mine\"]);\n await done();\n });\n\n it(\"refuses a stale lease and honours the current one\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n await store.seal({ jobId: \"a\", siteId: SITE, envelope: ENVELOPE });\n\n expect(\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: \"an-older-grant\",\n }),\n ).toEqual({ refused: \"stale-lease\" });\n\n expect(\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n }),\n ).toEqual({ envelope: ENVELOPE });\n await done();\n });\n\n it(\"renews a grant it still holds, and extends the sweep with it\", async () => {\n // cloud_008 §0.6, at the contract level: renewal has to be one operation,\n // because between a read and a write another replica can sweep the lease\n // — and the renewal would then resurrect a grant on a job already back in\n // the queue. In Valkey that is a script; in memory it is a method; the\n // guarantee is the same one, so it is tested here rather than twice.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs({ leaseMs: 1_000 }));\n\n const renewed = await store.renewLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n leaseMs: 60_000,\n });\n\n expect(renewed.lost).toEqual([]);\n expect(renewed.renewed.map((r) => r.jobId)).toEqual([\"a\"]);\n // The store's own clock did the arithmetic — the caller passed a duration,\n // never an instant, which is what keeps two replicas from measuring one\n // lease against two clocks (cloud_006 §3.4).\n expect(renewed.renewed[0]!.expiresAt).toBeGreaterThan(\n granted!.lease.expiresAt,\n );\n\n // And it is the *stored* grant that moved, not just the number returned.\n // Reporting a renewal without performing one is the mutation that\n // survived the first version of the relay's own test for this.\n expect((await store.job(SITE, \"a\"))?.claimedBy?.leaseExpiresAt).toBe(\n renewed.renewed[0]!.expiresAt,\n );\n await done();\n });\n\n it(\"reports a lease the runner no longer holds as lost, and renews nothing\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs({ leaseMs: 1_000 }));\n const before = (await store.job(SITE, \"a\"))?.claimedBy?.leaseExpiresAt;\n\n const renewed = await store.renewLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: \"a-grant-that-ended\" }],\n leaseMs: 60_000,\n });\n\n expect(renewed.renewed).toEqual([]);\n expect(renewed.lost).toEqual([\"a\"]);\n // A refusal must not advance anything — the current holder's grant is\n // untouched by somebody else's stale renewal.\n expect((await store.job(SITE, \"a\"))?.claimedBy?.leaseExpiresAt).toBe(\n before,\n );\n expect((await store.job(SITE, \"a\"))?.claimedBy?.leaseId).toBe(\n granted!.lease.id,\n );\n await done();\n });\n\n it(\"does not hand out a stub it cannot parse\", async () => {\n // cloud_008 §2.1a, and the case is not hypothetical: two jobs enqueued\n // before alpha.15 had no `site`, came back in a claim, and the daemon's\n // `.strict()` parse rejected the whole response. Two dead rows denied\n // claims to every device on the hub, and the queue could not drain past\n // them.\n //\n // The store had `JSON.parse(stub) as JobStub` — trusting what it wrote,\n // when what it wrote was written by a previous version. A routing store\n // outlives the deployment that filled it, so a wire change to `JobStub`\n // is a data migration whether or not anyone plans one.\n //\n // Only meaningful against a store that serialises: `RelayState` holds\n // typed objects and cannot reach this state, so it passes by\n // construction — which is worth saying rather than hiding, because a\n // reader should know which half of this contract each store is proving.\n const { store, done } = await make();\n if (!serialising || writeRawStub === undefined) {\n await done();\n return;\n }\n\n await store.enqueue({ id: \"good\", siteId: SITE, stub: stub(\"good\") });\n await store.enqueue({ id: \"old\", siteId: SITE, stub: stub(\"old\") });\n // Exactly what alpha.14 wrote: a stub with no `site`.\n const legacy = { ...stub(\"old\") } as Record<string, unknown>;\n delete legacy[\"site\"];\n await writeRawStub(store, \"old\", JSON.stringify(legacy));\n\n const granted = await store.claim(claimArgs({ max: 10 }));\n\n // The good one is still handed out — \"return nothing\" would pass a test\n // that only checked the bad one was absent, and would stop all routing.\n expect(granted.map((job) => job.id)).toEqual([\"good\"]);\n // And the bad one is back in the queue rather than stuck holding a lease\n // nobody received.\n expect((await store.job(SITE, \"old\"))?.state).toBe(\"queued\");\n await done();\n });\n\n it(\"honours a self job's audience\", async () => {\n // cloud_008 §2.1 at the contract level. The owners set is everyone this\n // device may run for, which for a Team owner's machine is the whole\n // roster — right for public and named, wrong for the one audience a user\n // picks because they want their own machine.\n const { store, done } = await make();\n await store.enqueue({\n id: \"private\",\n siteId: SITE,\n stub: { ...stub(\"private\", \"alice\"), audience: \"self\" },\n });\n\n // A roster owner's machine: may run alice's work in general.\n const granted = await store.claim(\n claimArgs({ owner: \"owner\", owners: [\"owner\", \"alice\"] }),\n );\n\n expect(granted).toEqual([]);\n expect((await store.job(SITE, \"private\"))?.state).toBe(\"queued\");\n await done();\n });\n\n it(\"still offers that job to its owner's own machine\", async () => {\n const { store, done } = await make();\n await store.enqueue({\n id: \"private\",\n siteId: SITE,\n stub: { ...stub(\"private\", \"alice\"), audience: \"self\" },\n });\n const granted = await store.claim(\n claimArgs({ owner: \"alice\", owners: [\"alice\"] }),\n );\n expect(granted.map((job) => job.id)).toEqual([\"private\"]);\n await done();\n });\n\n it(\"does not offer a job back to the runner that refused it\", async () => {\n // `REFUSAL_NOT_REOFFERED`. `releaseLeases` gained an **optional** reason,\n // which means a store can ignore it and still typecheck — this class has\n // now diverged from the interface that way twice, so the contract is the\n // only thing that catches it.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n reason: \"refused\",\n });\n\n expect(await store.claim(claimArgs())).toEqual([]);\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"queued\");\n // And somebody else may still run it.\n expect(\n (await store.claim(claimArgs({ runnerId: \"runner_2\" }))).map(\n (j) => j.id,\n ),\n ).toEqual([\"a\"]);\n await done();\n });\n\n it(\"keeps a job claimable by a runner that only went away\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n reason: \"shutdown\",\n });\n\n expect((await store.claim(claimArgs())).map((j) => j.id)).toEqual([\"a\"]);\n await done();\n });\n\n it(\"stops offering a job past its deadline, and drops the ciphertext\", async () => {\n // cloud_008 §2.2. `deadlineAt` travelled on every stub and nothing read\n // it, so expired jobs were offered forever and their sealed payloads kept\n // for as long as the store lived.\n const { store, done } = await make();\n await store.enqueue({\n id: \"late\",\n siteId: SITE,\n stub: { ...stub(\"late\"), deadlineAt: Date.now() - 1_000 },\n });\n\n expect(await store.claim(claimArgs())).toEqual([]);\n expect(await store.job(SITE, \"late\")).toBeUndefined();\n await done();\n });\n\n it(\"names a cancelled job to the device holding it, and offers it to nobody\", async () => {\n // cloud_008 §2.2.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n\n expect(await store.cancel({ jobId: \"a\", siteId: SITE })).toBe(true);\n expect(await store.cancelRequests(\"runner_1\")).toEqual([\"a\"]);\n\n // Scoped to the caller's site: a site must not reach another's work by\n // guessing an id.\n expect(await store.cancel({ jobId: \"a\", siteId: \"someone_else\" })).toBe(\n false,\n );\n await done();\n });\n\n it(\"records a result once\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n const first = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n const replay = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: { ...ENVELOPE, ciphertext: \"different\" },\n disposition: \"error\",\n });\n\n expect(first).toEqual({ accepted: true, state: \"done\" });\n // `duplicate` — cloud_008 §3.6. Case 18 above is the one that says why.\n expect(replay).toEqual({\n accepted: false,\n duplicate: true,\n state: \"done\",\n });\n // The property, not the boolean: the second result did not overwrite.\n expect((await store.job(SITE, \"a\"))?.disposition).toBe(\"ok\");\n await done();\n });\n\n it(\"answers the device that finished it with `duplicate`\", async () => {\n // Contract case 18 — cloud_008 §3.6, and the eighteenth exists because\n // the compiler cannot see any of this: the return type's `duplicate` is\n // optional, so a store that never sets it typechecks perfectly while\n // enforcing `RESULT_IDEMPOTENT` by accident or not at all.\n //\n // A daemon whose acknowledgment was lost hears that its answer is already\n // recorded, rather than that its lease is stale — the second message\n // invents a worry about a result safely on disk.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n const first = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n expect(first).toEqual({ accepted: true, state: \"done\" });\n\n const replay = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: { ...ENVELOPE, ciphertext: \"a-different-result\" },\n disposition: \"error\",\n });\n expect(replay).toEqual({\n accepted: false,\n duplicate: true,\n state: \"done\",\n });\n\n // A *different* device, on a job that is terminal, gets the refusal it\n // would get for one that is not — otherwise the two answers differ and a\n // job id becomes a terminality probe.\n const stranger = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_2\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n expect(stranger).toEqual({ refused: \"not-holder\" });\n\n // And the first answer is what survived, which is the property rather\n // than any of the booleans above.\n expect((await store.job(SITE, \"a\"))?.disposition).toBe(\"ok\");\n await done();\n });\n\n it(\"refuses a result produced under a grant that ended\", async () => {\n // cloud_008 §1.4a at the contract level, and it belongs here more than\n // anywhere: `RoutingStore.complete` gained `leaseId` and this class kept\n // typechecking without it, because a parameter object with fewer\n // properties is assignable to one with more. The relay would have\n // enforced LEASE_HONORED on the memory store and not on the store that\n // runs in production, with nothing red on the way.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n\n const late = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: \"a-grant-that-ended\",\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n\n expect(late).toEqual({ refused: \"stale-lease\" });\n // Untouched: the current holder can still finish it.\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"awaiting-payload\");\n expect((await store.job(SITE, \"a\"))?.result).toBeUndefined();\n await done();\n });\n\n it(\"stamps deadlines from its own clock\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const before = await store.now();\n const [granted] = await store.claim(claimArgs({ leaseMs: 45_000 }));\n const after = await store.now();\n\n // Bracketed rather than exact: a real store's clock moves between the two\n // reads, which is the whole reason it is the store's clock and not ours.\n expect(granted!.lease.expiresAt).toBeGreaterThanOrEqual(before + 45_000);\n expect(granted!.lease.expiresAt).toBeLessThanOrEqual(after + 45_000);\n await done();\n });\n });\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byollm/relay",
3
- "version": "0.1.0-alpha.23",
3
+ "version": "0.1.0-alpha.25",
4
4
  "type": "module",
5
5
  "description": "The reference relay: routes sealed byollm jobs between sites and daemons, holding no decryption keys by construction.",
6
6
  "license": "MIT",
@@ -31,7 +31,7 @@
31
31
  ],
32
32
  "dependencies": {
33
33
  "zod": "^4.1.13",
34
- "@byollm/protocol": "0.1.0-alpha.23"
34
+ "@byollm/protocol": "0.1.0-alpha.25"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
@@ -39,9 +39,9 @@
39
39
  "devDependencies": {
40
40
  "@supabase/supabase-js": "^2.112.2",
41
41
  "vitest": "^4.1.10",
42
- "byollm": "0.1.0-alpha.23",
43
- "@byollm/server": "0.1.0-alpha.23",
44
- "@byollm/conformance": "0.1.0-alpha.23"
42
+ "byollm": "0.1.0-alpha.25",
43
+ "@byollm/server": "0.1.0-alpha.25",
44
+ "@byollm/conformance": "0.1.0-alpha.25"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "vitest": ">=3"