@frockbot/computer-core 0.3.17 → 0.3.18

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frockbot/computer-core",
3
- "version": "0.3.17",
3
+ "version": "0.3.18",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -12,7 +12,7 @@
12
12
  "typecheck": "tsc --noEmit -p tsconfig.json"
13
13
  },
14
14
  "dependencies": {
15
- "@frockbot/kernel-contracts": "0.3.17",
15
+ "@frockbot/kernel-contracts": "0.3.18",
16
16
  "cordis": "4.0.0-rc.8"
17
17
  },
18
18
  "devDependencies": {
package/src/core.ts CHANGED
@@ -895,6 +895,29 @@ function guardedHandle(
895
895
  guardedOperation(assertCurrent, () =>
896
896
  sync.reconcile(reason, options),
897
897
  ),
898
+ // Forwarded when the provider offers it. Dropping it here is how a
899
+ // publish's required `dist/` pull was "refused: this Computer cannot
900
+ // reconcile a single durable root" on production (Bob, 2026-09-04)
901
+ // while the provider underneath could — and once ordinary sync
902
+ // stopped carrying `dist/`, that refusal was the whole failure.
903
+ ...(sync.reconcileRoot
904
+ ? {
905
+ reconcileRoot: (
906
+ root: Parameters<
907
+ NonNullable<ComputerSyncV1["reconcileRoot"]>
908
+ >[0],
909
+ reason: Parameters<
910
+ NonNullable<ComputerSyncV1["reconcileRoot"]>
911
+ >[1],
912
+ options?: Parameters<
913
+ NonNullable<ComputerSyncV1["reconcileRoot"]>
914
+ >[2],
915
+ ) =>
916
+ guardedOperation(assertCurrent, () =>
917
+ sync.reconcileRoot!(root, reason, options),
918
+ ),
919
+ }
920
+ : {}),
898
921
  signal: (options) =>
899
922
  guardedOperation(assertCurrent, () => sync.signal(options)),
900
923
  }
package/src/index.test.ts CHANGED
@@ -230,6 +230,71 @@ describe("ComputerRegistry", () => {
230
230
  await root.fiber.dispose();
231
231
  });
232
232
 
233
+ test("keeps a provider's single-root reconciliation on the guarded handle", async () => {
234
+ // Bob on production (2026-09-04): the provider could reconcile one root,
235
+ // the guarded handle dropped that method, and the publish that needed
236
+ // `dist/` pulled explicitly was refused as "cannot reconcile a single
237
+ // durable root" while ordinary sync no longer carried build output.
238
+ const root = new Context();
239
+ await root.plugin(ComputerRegistry);
240
+ const calls: string[] = [];
241
+ const syncing: ComputerProvider = {
242
+ id: "sprites",
243
+ open: async (identity, tenant, assignment) => ({
244
+ assignment,
245
+ identity,
246
+ tenant,
247
+ sync: {
248
+ reconcile: async (reason) => {
249
+ calls.push(`reconcile:${reason}`);
250
+ return { status: "ok" } as never;
251
+ },
252
+ reconcileRoot: async (declared, reason, options) => {
253
+ calls.push(
254
+ `root:${reason}:${declared.kind}:${(options?.requiredPaths ?? []).join(",")}`,
255
+ );
256
+ return { status: "ok" } as never;
257
+ },
258
+ signal: async () => undefined,
259
+ },
260
+ close: () => Promise.resolve(),
261
+ }),
262
+ };
263
+ root.computers.register(syncing);
264
+ const identity = { userId: "user-1" };
265
+ root.computers.assign(identity, "sprites");
266
+ const computer = await root.computers.open(identity, { botId: "bot-1" });
267
+
268
+ expect(computer.sync?.reconcileRoot).toBeDefined();
269
+ await computer.sync?.reconcileRoot?.(
270
+ {
271
+ kind: "package-declared",
272
+ userId: "user-1",
273
+ packageId: "applets",
274
+ rootId: "source",
275
+ },
276
+ "publish",
277
+ { requiredPaths: ["a.b/dist/server.js"] },
278
+ );
279
+ expect(calls).toEqual(["root:publish:package-declared:a.b/dist/server.js"]);
280
+
281
+ // Still guarded: a stale handle refuses it like every other operation.
282
+ root.computers.register({ ...syncing, id: "local" });
283
+ root.computers.assign(identity, "local");
284
+ await expect(
285
+ computer.sync?.reconcileRoot?.(
286
+ {
287
+ kind: "package-declared",
288
+ userId: "user-1",
289
+ packageId: "applets",
290
+ rootId: "source",
291
+ },
292
+ "publish",
293
+ ),
294
+ ).rejects.toMatchObject({ code: "stale-assignment" });
295
+ await root.fiber.dispose();
296
+ });
297
+
233
298
  test("fails clearly when a User has no Computer assignment", async () => {
234
299
  const root = new Context();
235
300
  await root.plugin(ComputerRegistry);