@namzu/sdk 20.2.0 → 20.4.0

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.
Files changed (82) hide show
  1. package/CHANGELOG.md +185 -0
  2. package/dist/manager/run/persistence.d.ts +2 -2
  3. package/dist/manager/run/persistence.d.ts.map +1 -1
  4. package/dist/manager/run/persistence.js +14 -5
  5. package/dist/manager/run/persistence.js.map +1 -1
  6. package/dist/public-runtime.d.ts +2 -1
  7. package/dist/public-runtime.d.ts.map +1 -1
  8. package/dist/public-runtime.js +6 -1
  9. package/dist/public-runtime.js.map +1 -1
  10. package/dist/runtime/query/checkpoint.d.ts +27 -1
  11. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  12. package/dist/runtime/query/checkpoint.js +34 -4
  13. package/dist/runtime/query/checkpoint.js.map +1 -1
  14. package/dist/runtime/query/context.d.ts +2 -0
  15. package/dist/runtime/query/context.d.ts.map +1 -1
  16. package/dist/runtime/query/context.js +1 -0
  17. package/dist/runtime/query/context.js.map +1 -1
  18. package/dist/runtime/query/index.d.ts +32 -1
  19. package/dist/runtime/query/index.d.ts.map +1 -1
  20. package/dist/runtime/query/index.js +5 -0
  21. package/dist/runtime/query/index.js.map +1 -1
  22. package/dist/runtime/query/resume-run.d.ts +9 -1
  23. package/dist/runtime/query/resume-run.d.ts.map +1 -1
  24. package/dist/runtime/query/resume-run.js +2 -1
  25. package/dist/runtime/query/resume-run.js.map +1 -1
  26. package/dist/store/index.d.ts +2 -1
  27. package/dist/store/index.d.ts.map +1 -1
  28. package/dist/store/index.js +2 -1
  29. package/dist/store/index.js.map +1 -1
  30. package/dist/store/run/checkpoint-disk.d.ts +18 -2
  31. package/dist/store/run/checkpoint-disk.d.ts.map +1 -1
  32. package/dist/store/run/checkpoint-disk.js +50 -5
  33. package/dist/store/run/checkpoint-disk.js.map +1 -1
  34. package/dist/store/run/checkpoint-memory.d.ts +22 -2
  35. package/dist/store/run/checkpoint-memory.d.ts.map +1 -1
  36. package/dist/store/run/checkpoint-memory.js +99 -4
  37. package/dist/store/run/checkpoint-memory.js.map +1 -1
  38. package/dist/store/run/claim-disk.d.ts +130 -0
  39. package/dist/store/run/claim-disk.d.ts.map +1 -0
  40. package/dist/store/run/claim-disk.js +550 -0
  41. package/dist/store/run/claim-disk.js.map +1 -0
  42. package/dist/store/run/disk.d.ts +10 -8
  43. package/dist/store/run/disk.d.ts.map +1 -1
  44. package/dist/store/run/disk.js.map +1 -1
  45. package/dist/store/run/listing.d.ts +44 -1
  46. package/dist/store/run/listing.d.ts.map +1 -1
  47. package/dist/store/run/listing.js +92 -1
  48. package/dist/store/run/listing.js.map +1 -1
  49. package/dist/store/run/memory.d.ts +46 -0
  50. package/dist/store/run/memory.d.ts.map +1 -0
  51. package/dist/store/run/memory.js +104 -0
  52. package/dist/store/run/memory.js.map +1 -0
  53. package/dist/types/run/checkpoint-store.d.ts +178 -2
  54. package/dist/types/run/checkpoint-store.d.ts.map +1 -1
  55. package/dist/types/run/config.d.ts +12 -0
  56. package/dist/types/run/config.d.ts.map +1 -1
  57. package/dist/types/run/index.d.ts +1 -0
  58. package/dist/types/run/index.d.ts.map +1 -1
  59. package/dist/types/run/index.js +1 -0
  60. package/dist/types/run/index.js.map +1 -1
  61. package/dist/types/run/store.d.ts +103 -0
  62. package/dist/types/run/store.d.ts.map +1 -0
  63. package/dist/types/run/store.js +30 -0
  64. package/dist/types/run/store.js.map +1 -0
  65. package/package.json +1 -1
  66. package/src/manager/run/persistence.ts +17 -7
  67. package/src/public-runtime.ts +6 -0
  68. package/src/runtime/query/checkpoint.ts +42 -5
  69. package/src/runtime/query/context.ts +3 -0
  70. package/src/runtime/query/index.ts +39 -1
  71. package/src/runtime/query/resume-run.ts +12 -2
  72. package/src/store/index.ts +5 -0
  73. package/src/store/run/checkpoint-disk.ts +70 -5
  74. package/src/store/run/checkpoint-memory.ts +118 -3
  75. package/src/store/run/claim-disk.ts +593 -0
  76. package/src/store/run/disk.ts +10 -8
  77. package/src/store/run/listing.ts +116 -1
  78. package/src/store/run/memory.ts +121 -0
  79. package/src/types/run/checkpoint-store.ts +189 -2
  80. package/src/types/run/config.ts +13 -0
  81. package/src/types/run/index.ts +1 -0
  82. package/src/types/run/store.ts +112 -0
@@ -1,5 +1,5 @@
1
1
  import type { CheckpointId, IterationCheckpoint } from '../../types/hitl/index.js';
2
- import type { CheckpointListingScope, CheckpointRunScope, CheckpointStore, DurableRunPage, ListDurableRunsOptions } from '../../types/run/checkpoint-store.js';
2
+ import type { CheckpointListingScope, CheckpointRunScope, CheckpointStore, ClaimFence, ClaimRunOptions, DurableRunPage, ListDurableRunsOptions, RunClaim } from '../../types/run/checkpoint-store.js';
3
3
  /**
4
4
  * Process-local {@link CheckpointStore}, keyed by the full five-layer scope.
5
5
  *
@@ -22,7 +22,27 @@ export declare class InMemoryCheckpointStore implements CheckpointStore {
22
22
  /** Same key → the run's scope, so a listing can rebuild an addressable entry. */
23
23
  private readonly scopes;
24
24
  private key;
25
- writeCheckpoint(scope: CheckpointRunScope, checkpoint: IterationCheckpoint): Promise<void>;
25
+ /** `tenant/project/session/run` → the run's current holding, if any. */
26
+ private readonly claims;
27
+ /**
28
+ * The highest fence ever issued per run, kept separately from the claim.
29
+ *
30
+ * The claim is removed on release; this is not. That separation is the
31
+ * whole point: the first version deleted the claim and then computed the
32
+ * next fence from it, so releasing rewound the counter to 1 and a worker
33
+ * stalled at fence 1 could write beside a new holder also at fence 1 —
34
+ * and the documented `finally { releaseRun() }` did it on every pass.
35
+ *
36
+ * The disk store gets this property from file names that persist. In
37
+ * memory the equivalent is a high-water mark nothing clears, and the two
38
+ * must agree, because this class is what a host reads when writing a
39
+ * backend of its own.
40
+ */
41
+ private readonly highWater;
42
+ claimRun(scope: CheckpointRunScope, options: ClaimRunOptions): Promise<RunClaim | null>;
43
+ releaseRun(scope: CheckpointRunScope, fence: ClaimFence): Promise<void>;
44
+ writeCheckpoint(scope: CheckpointRunScope, checkpoint: IterationCheckpoint, fence?: ClaimFence): Promise<void>;
45
+ private writeUnchecked;
26
46
  readCheckpoint(scope: CheckpointRunScope, checkpointId: CheckpointId): Promise<IterationCheckpoint | null>;
27
47
  listCheckpoints(scope: CheckpointRunScope): Promise<IterationCheckpoint[]>;
28
48
  deleteCheckpoint(scope: CheckpointRunScope, checkpointId: CheckpointId): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"checkpoint-memory.d.ts","sourceRoot":"","sources":["../../../src/store/run/checkpoint-memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AAClF,OAAO,KAAK,EACX,sBAAsB,EACtB,kBAAkB,EAClB,eAAe,EAEf,cAAc,EACd,sBAAsB,EACtB,MAAM,qCAAqC,CAAA;AAG5C;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,uBAAwB,YAAW,eAAe;IAC9D,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4D;IACjF,iFAAiF;IACjF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAE/D,OAAO,CAAC,GAAG;IAIL,eAAe,CAAC,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB1F,cAAc,CACnB,KAAK,EAAE,kBAAkB,EACzB,YAAY,EAAE,YAAY,GACxB,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAIhC,eAAe,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAM1E,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAItF,eAAe,CACpB,KAAK,EAAE,sBAAsB,EAC7B,OAAO,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,cAAc,CAAC;CAkB1B"}
1
+ {"version":3,"file":"checkpoint-memory.d.ts","sourceRoot":"","sources":["../../../src/store/run/checkpoint-memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AAClF,OAAO,KAAK,EACX,sBAAsB,EACtB,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,eAAe,EAEf,cAAc,EACd,sBAAsB,EACtB,QAAQ,EACR,MAAM,qCAAqC,CAAA;AAS5C;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,uBAAwB,YAAW,eAAe;IAC9D,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4D;IACjF,iFAAiF;IACjF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAE/D,OAAO,CAAC,GAAG;IAIX,wEAAwE;IACxE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IAErD;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;IAEpD,QAAQ,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAuBvF,UAAU,CAAC,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBvE,eAAe,CACpB,KAAK,EAAE,kBAAkB,EACzB,UAAU,EAAE,mBAAmB,EAC/B,KAAK,CAAC,EAAE,UAAU,GAChB,OAAO,CAAC,IAAI,CAAC;YAgCF,cAAc;IA4BtB,cAAc,CACnB,KAAK,EAAE,kBAAkB,EACzB,YAAY,EAAE,YAAY,GACxB,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAIhC,eAAe,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAM1E,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAItF,eAAe,CACpB,KAAK,EAAE,sBAAsB,EAC7B,OAAO,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,cAAc,CAAC;CAoB1B"}
@@ -1,4 +1,4 @@
1
- import { assertContiguousListingScope, paginateDurableRuns, toDurableRunEntry } from './listing.js';
1
+ import { assertContiguousListingScope, fencedOut, paginateDurableRuns, toClaimSummary, toDurableRunEntry, } from './listing.js';
2
2
  /**
3
3
  * Process-local {@link CheckpointStore}, keyed by the full five-layer scope.
4
4
  *
@@ -23,7 +23,100 @@ export class InMemoryCheckpointStore {
23
23
  key(scope) {
24
24
  return [scope.tenantId, scope.projectId, scope.sessionId, scope.runId].join('/');
25
25
  }
26
- async writeCheckpoint(scope, checkpoint) {
26
+ /** `tenant/project/session/run` → the run's current holding, if any. */
27
+ claims = new Map();
28
+ /**
29
+ * The highest fence ever issued per run, kept separately from the claim.
30
+ *
31
+ * The claim is removed on release; this is not. That separation is the
32
+ * whole point: the first version deleted the claim and then computed the
33
+ * next fence from it, so releasing rewound the counter to 1 and a worker
34
+ * stalled at fence 1 could write beside a new holder also at fence 1 —
35
+ * and the documented `finally { releaseRun() }` did it on every pass.
36
+ *
37
+ * The disk store gets this property from file names that persist. In
38
+ * memory the equivalent is a high-water mark nothing clears, and the two
39
+ * must agree, because this class is what a host reads when writing a
40
+ * backend of its own.
41
+ */
42
+ highWater = new Map();
43
+ async claimRun(scope, options) {
44
+ const key = this.key(scope);
45
+ const now = options.now ?? Date.now();
46
+ const held = this.claims.get(key);
47
+ // Held by somebody else and still live. Not an error: two readers on
48
+ // one queue is the ordinary case.
49
+ if (held && now < held.expiresAt && held.holder !== options.holder)
50
+ return null;
51
+ // A reclaim of an expired holding and a renewal by the current holder
52
+ // are the same write. The fence advances either way, so a previous
53
+ // holder that wakes up is fenced out in both cases — a renewal that
54
+ // kept the fence would leave a stalled twin able to write.
55
+ // Counted from the high-water mark, never from the live claim. A
56
+ // released run has no claim, and computing from that absence is what
57
+ // rewound the counter to 1 on every release.
58
+ const fence = (this.highWater.get(key) ?? 0) + 1;
59
+ const claim = { holder: options.holder, fence, expiresAt: now + options.ttlMs };
60
+ this.highWater.set(key, fence);
61
+ this.claims.set(key, claim);
62
+ return claim;
63
+ }
64
+ async releaseRun(scope, fence) {
65
+ const key = this.key(scope);
66
+ const held = this.claims.get(key);
67
+ // A stale fence releases nothing. A worker that stalled past its lease
68
+ // must not be able to hand away a run somebody else now holds.
69
+ //
70
+ // The high-water mark deliberately survives this. Dropping the claim
71
+ // returns the run to the queue; forgetting the number it reached would
72
+ // re-issue a fence a stalled worker still believes it holds.
73
+ if (!held || held.fence !== fence)
74
+ return;
75
+ this.claims.delete(key);
76
+ // And the counter steps past the released holding, because the disk
77
+ // store's release does: it appends a TOMBSTONE at `fence + 1`, which
78
+ // raises the maximum name and therefore the fence every later write is
79
+ // checked against. Without this step a holder that released could still
80
+ // write with the fence it just gave up — refused on disk, accepted here.
81
+ //
82
+ // The consequence is visible in the numbering: a released run's next
83
+ // claim is `fence + 2` in both stores, because the tombstone consumed
84
+ // one. That is parity, not an off-by-one.
85
+ this.highWater.set(key, fence + 1);
86
+ }
87
+ async writeCheckpoint(scope, checkpoint, fence) {
88
+ const key = this.key(scope);
89
+ // An unfenced write is allowed even on a claimed run: a host adopting
90
+ // claims on one worker must not break the workers that have not
91
+ // adopted them. A fenced write is checked, and that check is what
92
+ // makes the lease real.
93
+ if (fence !== undefined) {
94
+ // Against the HIGH-WATER MARK, not the live claim, and the difference
95
+ // is a silent loss rather than a duplicate.
96
+ //
97
+ // `releaseRun` deletes the claim. Reading `claims` here meant that
98
+ // between a release and the next take there was no holding to
99
+ // compare against, so `held` was `undefined` and every fence was
100
+ // accepted however stale. w1 stalls at fence 1; w2 reclaims at 2,
101
+ // finishes the work, releases cleanly; w1 wakes and writes with
102
+ // fence 1 — accepted — and its checkpoint carries a fresh
103
+ // `createdAt`, so it sorts newest and the next resume restores w1's
104
+ // stale history. w2's completed work is gone, with no error
105
+ // anywhere.
106
+ //
107
+ // The disk store refuses that write, because `currentFence` reads
108
+ // file names and the release tombstone raised the maximum. The
109
+ // minting side here already counted from the high-water mark; only
110
+ // the enforcement side was left reading the claim, so the two
111
+ // shipped stores disagreed at the one point that decides whether a
112
+ // lease is real.
113
+ const current = this.highWater.get(key) ?? 0;
114
+ if (fence < current)
115
+ throw fencedOut(scope, fence, current);
116
+ }
117
+ return this.writeUnchecked(scope, checkpoint);
118
+ }
119
+ async writeUnchecked(scope, checkpoint) {
27
120
  const key = this.key(scope);
28
121
  let run = this.runs.get(key);
29
122
  if (!run) {
@@ -74,8 +167,10 @@ export class InMemoryCheckpointStore {
74
167
  if (scope.sessionId !== undefined && runScope.sessionId !== scope.sessionId)
75
168
  continue;
76
169
  const entry = toDurableRunEntry(runScope, [...checkpoints.values()], now);
77
- if (entry)
78
- entries.push(entry);
170
+ if (!entry)
171
+ continue;
172
+ const claim = this.claims.get(key);
173
+ entries.push(claim ? { ...entry, claim: toClaimSummary(claim, now) } : entry);
79
174
  }
80
175
  return paginateDurableRuns(entries, options);
81
176
  }
@@ -1 +1 @@
1
- {"version":3,"file":"checkpoint-memory.js","sourceRoot":"","sources":["../../../src/store/run/checkpoint-memory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"checkpoint-memory.js","sourceRoot":"","sources":["../../../src/store/run/checkpoint-memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * A run claim on a filesystem, correct across PROCESSES.
3
+ *
4
+ * ## The fence is the filename
5
+ *
6
+ * A run's claims live at `{runDir}/claims/{fence}.json`. Taking the run means
7
+ * exclusively creating the next number; the kernel makes exactly one of any
8
+ * number of simultaneous callers the creator, and every other gets `EEXIST`.
9
+ * The current holding is the highest-numbered file.
10
+ *
11
+ * That single decision is the whole mechanism, and it is what the first
12
+ * version of this file got wrong. That version kept ONE mutable `claim.json`
13
+ * and protected it with a lock, which needed a stale-lock breaker, which was
14
+ * `unlink` followed by an exclusive create — two operations. An adversarial
15
+ * pass reproduced the consequence from separate processes: two workers both
16
+ * judge a stale guard breakable, the second unlinks the FIRST one's fresh
17
+ * guard, both end up inside the section believing they hold it, neither has
18
+ * written yet so neither can be the loser of a re-read, and both write the
19
+ * same fence. Twenty-six of three hundred runs went to two workers at an
20
+ * identical fence — and an identical fence fences nobody out, because the
21
+ * comparison is `<`.
22
+ *
23
+ * Numbering the files instead removes every one of those steps. There is no
24
+ * lock to go stale, so no breaker, so no window. Concurrency is decided by
25
+ * one `O_CREAT | O_EXCL`.
26
+ *
27
+ * ## Four properties this layout gives for free
28
+ *
29
+ * **Monotonic across release and deletion.** Fences are file names that stay,
30
+ * so releasing cannot rewind the counter. Deleting `claim.json` used to send
31
+ * the next caller down a fresh-claim path that minted fence 1 again — so a
32
+ * worker stalled at fence 1 could write alongside the new holder, and the
33
+ * documented `finally { releaseRun() }` did it on every pass. Releasing here
34
+ * appends a tombstone rather than removing anything.
35
+ *
36
+ * **Unreadable content cannot wedge the run.** The fence is in the name, so a
37
+ * damaged or half-written body never hides the ordering. A caller reads the
38
+ * highest number and takes the next one; the previous holder, alive or not,
39
+ * is fenced out by arithmetic. Refusing to take an unparseable claim was safe
40
+ * against double-writing and left the run permanently unclaimable, which is
41
+ * the failure a lease exists to prevent.
42
+ *
43
+ * **The write-time fence check needs no parsing at all.** It compares a
44
+ * number against a directory listing, so a corrupt body cannot make the check
45
+ * skip itself.
46
+ *
47
+ * **No `rename` and no `unlink` on the contended path.** Renaming over a path
48
+ * another process merely holds open for reading fails on non-POSIX
49
+ * filesystems — measured at 84% under two concurrent readers — and a listing
50
+ * sweep reads exactly these files. Creating a new name never collides with a
51
+ * reader.
52
+ *
53
+ * ## The name appears already complete, because `link` publishes it
54
+ *
55
+ * An exclusive create decides the winner, but `wx` is open-THEN-write: for an
56
+ * instant the winning name exists and is empty. A reader landing in it parses
57
+ * nothing, reports the holding expired, and a second worker takes the next
58
+ * fence. Their fences differ so the loser's first checkpoint is refused — and
59
+ * both have already restored the run and executed its tools by then, and tool
60
+ * side effects are fenced by nothing.
61
+ *
62
+ * So the body is written to a temporary name first and the fence name is
63
+ * created by `link`ing to it. `link` fails `EEXIST` when the destination
64
+ * exists, so it arbitrates exactly as `wx` did, and the destination it creates
65
+ * is a second name for a file that was already whole. There is no instant at
66
+ * which the fence name exists and its body does not.
67
+ *
68
+ * Measured from separate OS processes on both platform families:
69
+ *
70
+ * - `link` refused an existing destination 20,000/20,000 times. Six writers
71
+ * over 6,000 fences produced no fence with two winners and none with none.
72
+ * - Paired identical fixtures: `wx` showed an empty destination in 15,985 of
73
+ * 16,000 first observations, `link` in 0 of 16,000. All 156,000 frontier
74
+ * observations were `ENOENT` or a complete parseable body — none empty, none
75
+ * torn.
76
+ * - `rename` is disqualified, and not for the reason first assumed. It never
77
+ * reports `EEXIST`; it silently REPLACES, 20,000/20,000. It cannot arbitrate
78
+ * a race at all — two workers publishing one fence would both succeed and
79
+ * the second would erase the first. (Its `EPERM`-under-readers failure is
80
+ * real too, at 93.7% on the non-POSIX family, but the exclusivity failure
81
+ * disqualifies it first.)
82
+ *
83
+ * Cost: three syscalls rather than one, +1.0 ms per acquisition on the
84
+ * non-POSIX family and +0.04 ms on POSIX, for an operation that runs once per
85
+ * run plus renewals.
86
+ *
87
+ * ## What it still does not do
88
+ *
89
+ * It does not detect liveness. Nothing can from here: a stalled holder, a
90
+ * suspended container and a partitioned network are indistinguishable, and
91
+ * they are indistinguishable from the holder's own side too, which is why it
92
+ * keeps writing. The fence is the answer — the write is checked, not the
93
+ * writer.
94
+ */
95
+ import type { ClaimFence, ClaimRunOptions, RunClaim } from '../../types/run/checkpoint-store.js';
96
+ /**
97
+ * The highest fence ever issued for this run, or 0 when it has never been
98
+ * claimed.
99
+ *
100
+ * Reads names only. This is deliberately the one question the contended path
101
+ * asks, because a name cannot be half-written: a file either exists or does
102
+ * not, where a body can be observed mid-write.
103
+ */
104
+ export declare function currentFence(runDir: string): Promise<ClaimFence>;
105
+ /**
106
+ * The run's current holding, or `null` when it has never been claimed.
107
+ *
108
+ * Returns `null` for an unreadable body too, and that is safe HERE in a way
109
+ * it was not in the previous design: the fence is known from the name
110
+ * regardless, so an unreadable body means "somebody took this number and its
111
+ * details are unavailable", and the caller's response is to take the NEXT
112
+ * number rather than to give up. Nothing is inferred from the absence.
113
+ */
114
+ export declare function readClaim(runDir: string): Promise<RunClaim | null>;
115
+ /** Take or extend the run's claim. `null` when somebody else holds it. */
116
+ export declare function acquireClaim(runDir: string, options: ClaimRunOptions): Promise<RunClaim | null>;
117
+ /**
118
+ * Give up a holding early, so the run returns to the queue without waiting
119
+ * out its lease.
120
+ *
121
+ * Appends a tombstone at the next fence rather than deleting anything. The
122
+ * counter must never rewind: a worker stalled at an old fence has to stay
123
+ * fenced out forever, and removing the record would let a later claimer be
124
+ * issued a number that stalled worker already believes it holds.
125
+ *
126
+ * A stale fence releases nothing — a worker that stalled past its lease must
127
+ * not be able to hand away a run somebody else now holds.
128
+ */
129
+ export declare function releaseClaim(runDir: string, fence: ClaimFence): Promise<void>;
130
+ //# sourceMappingURL=claim-disk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim-disk.d.ts","sourceRoot":"","sources":["../../../src/store/run/claim-disk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6FG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAA;AAoRhG;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAGtE;AA8DD;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA6DxE;AAED,0EAA0E;AAC1E,wBAAsB,YAAY,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,GACtB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAoC1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBnF"}