@frockbot/workspace-store 0.3.6 → 0.3.8

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/workspace-store",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -15,7 +15,7 @@
15
15
  "typecheck": "tsc --noEmit -p tsconfig.json"
16
16
  },
17
17
  "dependencies": {
18
- "@frockbot/kernel-contracts": "0.3.6"
18
+ "@frockbot/kernel-contracts": "0.3.8"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/bun": "1.4.0",
package/src/store.test.ts CHANGED
@@ -680,11 +680,14 @@ describe("an unrecorded file is still overwritable by the writer that read it",
680
680
  // The `record` that follows a `put` fails once: the bytes land, and the
681
681
  // only place their generation exists is beside them in the object store.
682
682
  const record = generations.record;
683
- let failed = false;
683
+ let attempts = 0;
684
684
  generations.record = (entry) => {
685
- if (failed) return record(entry);
686
- failed = true;
687
- return Promise.reject(new Error("the ledger is unreachable"));
685
+ attempts += 1;
686
+ // Both attempts fail: the retry is not what is under test here.
687
+ if (attempts <= 2) {
688
+ return Promise.reject(new Error("the ledger is unreachable"));
689
+ }
690
+ return record(entry);
688
691
  };
689
692
 
690
693
  const first = await files.write({
@@ -693,7 +696,12 @@ describe("an unrecorded file is still overwritable by the writer that read it",
693
696
  writer: user,
694
697
  expectedGenerationId: null,
695
698
  });
696
- expect(first.status).toBe("unavailable");
699
+ // The bytes are durable, so the write is `ok`. Reporting a failure here
700
+ // told the model nothing was written while object storage held it, and
701
+ // the write then turned up in the next injection anyway.
702
+ expect(first).toMatchObject({ status: "ok", ledgerPending: true });
703
+ // It is tried twice before it is called pending.
704
+ expect(attempts).toBe(2);
697
705
  expect(await generations.current(INSTRUCTIONS, "notes.md")).toBeUndefined();
698
706
 
699
707
  // The file reads back with the generation its writer minted…
package/src/store.ts CHANGED
@@ -44,6 +44,7 @@
44
44
  // and `gcTombstoneMarkersV1` collects markers old enough that no create can
45
45
  // still be conditioned on one.
46
46
  import {
47
+ retryOnceV1,
47
48
  WORKSPACE_MAX_FILE_BYTES,
48
49
  WORKSPACE_MAX_LIST_ENTRIES,
49
50
  isWorkspaceComputerReadOnlyRootV1,
@@ -613,19 +614,25 @@ class ObjectWorkspaceFiles implements WorkspaceFilesV1 {
613
614
  );
614
615
  }
615
616
  if (written) {
617
+ // The bytes are durable. The ledger is the index of that fact, not
618
+ // the fact: reporting a failure here told the model nothing was
619
+ // written while object storage held it, and the write then turned up
620
+ // in the next injection anyway — exactly the intent/outcome
621
+ // divergence the record exists to prevent. So the ledger call is
622
+ // retried, and a still-failing ledger is `ok` with `ledgerPending`:
623
+ // the metadata beside the bytes lets `reconcile` repair the entry.
616
624
  try {
617
- await this.generations.record({
618
- schemaVersion: 1,
619
- root,
620
- path: relative,
621
- generation,
622
- etag: written.etag,
623
- });
624
- } catch (error) {
625
- return failure(
626
- "unavailable",
627
- error instanceof Error ? error.message : String(error),
625
+ await retryOnceV1(() =>
626
+ this.generations.record({
627
+ schemaVersion: 1,
628
+ root,
629
+ path: relative,
630
+ generation,
631
+ etag: written.etag,
632
+ }),
628
633
  );
634
+ } catch {
635
+ return { status: "ok", generation, ledgerPending: true };
629
636
  }
630
637
  return { status: "ok", generation };
631
638
  }