@amalgm/shell 0.1.135 → 0.1.136

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.
@@ -2,18 +2,28 @@
2
2
  * Direct-to-staging content placement for Cold Add.
3
3
  *
4
4
  * During Cold Add each ordinary file's bytes are written once, directly into
5
- * their hidden staged destination: every chunk is verified before its
6
- * positioned write, the whole-file hash is proven from that same stream, the
7
- * complete file is fsynced, and the workspace is revealed later by one atomic
8
- * rename. A second cache copy is not part of the critical path.
5
+ * their hidden staged destination. The cold Add's mass — single-chunk files —
6
+ * pays one handle lifecycle per file; a multi-chunk file pays one bounded
7
+ * lifecycle per chunk rather than retaining descriptors across a whole wave.
8
+ * The directory skeleton exists before the first byte lands, the open that
9
+ * writes a cold file is the open that creates it, every chunk is verified
10
+ * before its positioned write, the whole-file hash is proven from that same
11
+ * stream, and the chunk that completes the stream makes the proven bytes
12
+ * durable and user-visible through the handle that wrote them before it
13
+ * closes. Nothing waits for a seal pass: seal asserts the proof the stream
14
+ * already produced and places duplicates. A second cache copy is not part of
15
+ * the critical path, and the workspace is revealed later by one atomic rename.
9
16
  *
10
17
  * SQLite chunk evidence makes an interrupted staging resumable. Retained
11
18
  * staged bytes are never trusted: every retained chunk is re-read and
12
- * re-hashed against its manifest before it may satisfy the download, and the
13
- * whole-file digest is proven again from that same stream.
19
+ * re-hashed against its manifest before it may satisfy the download, a
20
+ * retained file is cut to its manifest length so no stale tail survives under
21
+ * a proven prefix, and the whole-file digest is proven again from that same
22
+ * stream.
14
23
  */
15
24
  import type { ContentManifest, TransferArtifact } from "@amalgm/live";
16
25
  import type { SealedContentFile } from "./content-cache-host.js";
26
+ import { ConcurrentActivityClock } from "./transfer-instrumentation.js";
17
27
  /** The shape every download-set target satisfies, whether it stages bytes in
18
28
  * the machine cache or directly inside a hidden Add staging root. */
19
29
  export interface ContentDownloadTarget {
@@ -29,15 +39,32 @@ export interface AddStagingRouting {
29
39
  readonly evidenceDir: string;
30
40
  readonly destinations: ReadonlyMap<string, readonly string[]>;
31
41
  }
42
+ type StoreActivity = "file" | "evidence" | "hash";
32
43
  /** Where one download set's staged placement time went, accumulated across
33
- * every staged file: positioned chunk writes, whole-file hash drain work
34
- * (including the bytes an out-of-order arrival forces back off the disk),
35
- * durability fsyncs, and duplicate-destination copies. */
44
+ * every staged file. Legs are the wall time of the named work; the activity
45
+ * clock beside them partitions the same wall time by what was physically in
46
+ * flight. */
36
47
  interface StagedPlacementMetrics {
48
+ /** Creating the directory skeleton, once per set. */
49
+ skeletonMs: number;
50
+ directories: number;
51
+ /** Creating or reclaiming a file. */
52
+ openMs: number;
53
+ /** Positioned chunk writes. */
37
54
  writeMs: number;
55
+ /** Chunk evidence rows. */
56
+ evidenceMs: number;
57
+ /** Waiting for, then running, the ordered whole-file proof. */
58
+ hashTurnMs: number;
59
+ /** The proof's own work, including bytes an out-of-order arrival forces
60
+ * back off the disk. */
38
61
  drainMs: number;
39
62
  preadBytes: number;
40
- fsyncMs: number;
63
+ /** Making a proven file user-visible and durable. */
64
+ finishMs: number;
65
+ /** Releasing handles. */
66
+ closeMs: number;
67
+ /** Duplicate destinations. */
41
68
  copyMs: number;
42
69
  }
43
70
  /** The device-scoped home of one workspace Add's staging resume evidence.
@@ -60,16 +87,20 @@ declare class AddStagingEvidence {
60
87
  }
61
88
  /** One ordinary file downloading directly into its staged destination.
62
89
  *
63
- * Chunks may arrive in any order; each lands with a positioned write, and a
64
- * serialized drain feeds the whole-file hasher strictly in chunk order — from
65
- * the arriving bytes when the next expected chunk is at hand, otherwise by
66
- * reading an already-written later chunk when its turn arrives. This keeps no
67
- * unaccounted buffers and cannot deadlock failure settlement. Resume rereads
68
- * every retained chunk by necessity so local staging can never stand in for
69
- * content proof. */
90
+ * Chunks may arrive in any order; each lands with a positioned write through
91
+ * its own handle, and a serialized proof turn feeds the whole-file hasher
92
+ * strictly in chunk order — from the arriving bytes when the next expected
93
+ * chunk is at hand, otherwise by reading an already-written later chunk back
94
+ * through that same handle. The turn that completes the stream proves the
95
+ * digest and finishes the file before the handle closes, so a single-chunk
96
+ * file — the cold Add's mass — is created, written, proven, made durable, and
97
+ * released inside one putChunk, and open handles never outnumber chunks in
98
+ * flight. Resume rereads every retained chunk by necessity so local staging
99
+ * can never stand in for content proof. */
70
100
  export declare class StagedContentFile implements ContentDownloadTarget {
71
101
  private readonly evidence;
72
102
  private readonly metrics;
103
+ private readonly storeActivity;
73
104
  readonly artifact: TransferArtifact;
74
105
  readonly manifest: ContentManifest;
75
106
  private readonly destinations;
@@ -79,36 +110,69 @@ export declare class StagedContentFile implements ContentDownloadTarget {
79
110
  private readonly ready;
80
111
  private nextChunk;
81
112
  private hashTurn;
82
- private prepared;
113
+ private failure;
114
+ private placed;
115
+ private finished;
83
116
  private sealed;
84
- constructor(evidence: AddStagingEvidence, metrics: StagedPlacementMetrics, artifact: TransferArtifact, manifest: ContentManifest, destinations: readonly string[]);
85
- private prepare;
86
- /** Feed the whole-file hasher every chunk that is now in order. Turns are
87
- * serialized: parallel chunk arrivals only mark readiness. */
88
- private queueHash;
117
+ constructor(evidence: AddStagingEvidence, metrics: StagedPlacementMetrics, storeActivity: ConcurrentActivityClock<StoreActivity>, artifact: TransferArtifact, manifest: ContentManifest, destinations: readonly string[]);
118
+ /** Time one leg of placement. An activity names what is physically in
119
+ * flight; a leg without one is a wait. */
120
+ private leg;
121
+ private legSync;
122
+ /** One handle on the staged file. A cold Add's first open creates the file
123
+ * outright — never a probing open that is guaranteed to fail. The first
124
+ * open of a retained file proves it is a regular file and cuts it to its
125
+ * manifest length. Every later chunk open still re-proves that its exact,
126
+ * no-follow target is a regular file; retained staging never becomes trust. */
127
+ private openHandle;
128
+ /** Feed the whole-file hasher every chunk now in order — the arriving bytes
129
+ * on the arrival's own turn, otherwise an already-written chunk read back
130
+ * through this handle. True when this turn completed the stream. */
131
+ private drain;
132
+ /** The stream is complete in order: prove its digest, then make the proven
133
+ * bytes user-visible and durable through the handle that wrote them. */
134
+ private finish;
135
+ /** Queue one serialized proof turn. A failed turn rejects its own caller
136
+ * and is remembered for seal; the chain stays usable so later chunks fail
137
+ * on their own evidence, not on a poisoned chain. */
138
+ private turn;
139
+ private prove;
89
140
  /** Re-prove one retained staged chunk before it may satisfy the download.
90
- * Evidence rows only say what should be there; the bytes decide. */
141
+ * Evidence rows only say what should be there; the bytes decide. A retained
142
+ * chunk that verifies joins the ordered proof exactly like an arrival. */
91
143
  hasChunk(index: number): Promise<boolean>;
92
144
  /** Persist one block already verified by @amalgm/live with a positioned
93
- * write, then advance the streaming whole-file proof. */
145
+ * write through this chunk's own handle, then advance the ordered proof.
146
+ * The chunk that completes the proof finishes the file before its handle
147
+ * closes; any other chunk simply releases its handle. */
94
148
  putChunk(index: number, bytes: Uint8Array): Promise<void>;
95
- /** Prove the streamed whole-file digest, fsync, and place every duplicate
96
- * destination. The staged file needs no rename of its own: the whole
97
- * staging root is revealed by one atomic rename after rows commit. */
149
+ /** Assert the proof this file's own stream already produced, and place
150
+ * every duplicate destination from the proven primary. An artifact with no
151
+ * chunks has no stream: its proof is the empty digest, made here. */
98
152
  seal(): Promise<SealedContentFile>;
99
- /** Keep staged bytes and evidence for the next attempt. */
153
+ /** Keep staged bytes and evidence for the next attempt. Handles live only
154
+ * inside the chunk operations that opened them, so an interrupted file
155
+ * holds nothing here; an in-flight proof turn is merely allowed to settle. */
100
156
  close(): Promise<void>;
101
157
  }
102
- /** One evidence database shared by every staged file in a download set. */
158
+ /** One evidence database and one directory skeleton shared by every staged
159
+ * file in a download set. */
103
160
  export declare class StagedContentSet {
104
161
  private readonly evidence;
162
+ private readonly storeActivity;
105
163
  private readonly metrics;
106
164
  private closed;
107
- constructor(evidenceDir: string);
165
+ private constructor();
166
+ /** Open the set for these destinations. Every directory that will hold a
167
+ * staged file — primary or duplicate — exists before the first byte lands,
168
+ * so no chunk's store path ever includes a mkdir. */
169
+ static open(evidenceDir: string, destinations: ReadonlyMap<string, readonly string[]>): Promise<StagedContentSet>;
108
170
  target(artifact: TransferArtifact, manifest: ContentManifest, destinations: readonly string[]): StagedContentFile;
109
- /** The staged rail's internal placement split, keyed for the download
110
- * stage ledger. `stagedWriteMs` and `stagedDrainMs` decompose the outer
111
- * write leg; `stagedFsyncMs` and `stagedCopyMs` decompose the seal leg. */
171
+ /** The staged rail's placement split, keyed for the download stage ledger:
172
+ * the one-time skeleton, then the per-file legs — open, write, evidence,
173
+ * the ordered proof (turn wait and drain work), finish, close — and
174
+ * duplicate copies. The `stagedStore*` states partition the same wall time
175
+ * by which of file, evidence, and hash work was physically in flight. */
112
176
  report(): Record<string, number>;
113
177
  close(): void;
114
178
  }