pactium 0.2.0 → 0.2.1

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/docs/API.md ADDED
@@ -0,0 +1,698 @@
1
+ # Pactium API Reference
2
+
3
+ This document covers the complete public API surface of the `pactium` package.
4
+
5
+ ## Entry Points
6
+
7
+ | Export path | Description |
8
+ | --- | --- |
9
+ | `pactium` | Core proof-first protocol API |
10
+ | `pactium/licolite` | LicoLite integration aspect |
11
+
12
+ ---
13
+
14
+ ## Core API (`pactium`)
15
+
16
+ ### `createPactium(options?)`
17
+
18
+ Creates a Pactium instance with full protocol capabilities.
19
+
20
+ ```js
21
+ import { createPactium } from "pactium";
22
+
23
+ const pactium = createPactium({
24
+ dataDir: "./.pactium", // Data directory path (default: ~/.pactium)
25
+ inMemory: false, // Use in-memory storage (for testing)
26
+ storage: null // Provide a pre-configured StoragePort
27
+ });
28
+ ```
29
+
30
+ **Returns:** `PactiumCore`
31
+
32
+ The returned instance exposes:
33
+
34
+ | Property | Type | Description |
35
+ | --- | --- | --- |
36
+ | `protocol` | `string` | Protocol identifier (`pactium.v0.2`) |
37
+ | `schema` | `string` | Schema version |
38
+ | `dataDir` | `string` | Resolved data directory path |
39
+ | `storage` | `PactiumStoragePort` | Underlying storage port |
40
+ | `ledger` | `PactiumLedger` | Operation Ledger instance |
41
+ | `indexEngine` | `PactiumIndexEngine` | Verifiable Index Engine instance |
42
+
43
+ ---
44
+
45
+ ### Operation Lifecycle
46
+
47
+ #### `pactium.recordOperation(input)`
48
+
49
+ Records a complete operation (intent + outcome) in a single call. This is the high-level convenience API.
50
+
51
+ ```js
52
+ const envelope = await pactium.recordOperation({
53
+ operationId: "workspace.file.write",
54
+ workspaceId: "workspace-a",
55
+ idempotencyKey: "intent-unique-key",
56
+ outcomeIdempotencyKey: "outcome-unique-key",
57
+ input: { path: "file.txt" },
58
+ outcome: "success",
59
+ stateMutations: [
60
+ { key: "file.txt", value: { content: "data" } }
61
+ ]
62
+ });
63
+ ```
64
+
65
+ **Returns:** `PactiumProofEnvelope`
66
+
67
+ #### `pactium.beginOperationIntent(input)`
68
+
69
+ Records an Operation Intent fact. Use this for two-phase operation lifecycle.
70
+
71
+ ```js
72
+ const intentEnvelope = await pactium.beginOperationIntent({
73
+ operationId: "workspace.file.write",
74
+ workspaceId: "workspace-a",
75
+ idempotencyKey: "intent-key",
76
+ input: { path: "file.txt" }
77
+ });
78
+ ```
79
+
80
+ **Returns:** `PactiumProofEnvelope` -- The envelope's `factRef` references the intent.
81
+
82
+ #### `pactium.appendOperationOutcome(input)`
83
+
84
+ Records an Operation Outcome fact that closes an existing intent.
85
+
86
+ ```js
87
+ const outcomeEnvelope = await pactium.appendOperationOutcome({
88
+ intentId: intentEnvelope.factId,
89
+ workspaceId: "workspace-a",
90
+ idempotencyKey: "outcome-key",
91
+ outcome: "success",
92
+ stateMutations: [
93
+ { key: "file.txt", value: { content: "data" } }
94
+ ]
95
+ });
96
+ ```
97
+
98
+ **Returns:** `PactiumProofEnvelope`
99
+
100
+ #### `pactium.lookupOpenIntent(intentId)`
101
+
102
+ Looks up an Operation Intent that does not yet have a corresponding outcome.
103
+
104
+ ```js
105
+ const openIntent = await pactium.lookupOpenIntent("intent-id");
106
+ // Returns the intent record or null
107
+ ```
108
+
109
+ #### `pactium.lookupOutcome(intentId)`
110
+
111
+ Looks up the Operation Outcome for a given intent.
112
+
113
+ ```js
114
+ const outcome = await pactium.lookupOutcome("intent-id");
115
+ // Returns the outcome record or null
116
+ ```
117
+
118
+ ---
119
+
120
+ ### Workspace Projection
121
+
122
+ #### `pactium.getWorkspaceProjection(workspaceId)`
123
+
124
+ Returns the current workspace projection state for a given workspace.
125
+
126
+ ```js
127
+ const projection = await pactium.getWorkspaceProjection("workspace-a");
128
+ // { workspaceId, nextOrdinal, orderRoot, membershipRoot, order, membership }
129
+ ```
130
+
131
+ #### `pactium.proveWorkspaceMembership(input)`
132
+
133
+ Generates a verifiable proof that a ledger event belongs (or does not belong) to a workspace.
134
+
135
+ ```js
136
+ const proof = await pactium.proveWorkspaceMembership({
137
+ workspaceId: "workspace-a",
138
+ ledgerEventId: "ledger-event-id"
139
+ });
140
+ // { member: true/false, proof: { ... } }
141
+ ```
142
+
143
+ ---
144
+
145
+ ### Proof Verification
146
+
147
+ #### `pactium.verifyEnvelope(envelope, options?)`
148
+
149
+ Verifies a Proof Envelope against local proof material.
150
+
151
+ ```js
152
+ const result = await pactium.verifyEnvelope(envelope);
153
+ // { ok: true/false, failures: [...], checked: [...] }
154
+ ```
155
+
156
+ **Returns:** `PactiumVerificationResult`
157
+
158
+ #### `verifyProofEnvelope(envelope, options?)`
159
+
160
+ Standalone envelope verification function (does not require a Pactium instance).
161
+
162
+ ```js
163
+ import { verifyProofEnvelope } from "pactium";
164
+
165
+ const result = await verifyProofEnvelope(envelope, {
166
+ storage: storagePort,
167
+ verifierRegistry: customRegistry
168
+ });
169
+ ```
170
+
171
+ #### `verifyProofBundle(bundle, options?)`
172
+
173
+ Verifies a portable Proof Bundle without access to local storage.
174
+
175
+ ```js
176
+ import { verifyProofBundle } from "pactium";
177
+
178
+ const result = await verifyProofBundle(bundle, {
179
+ verifyAllBlocks: true // Verify all content-addressed blocks
180
+ });
181
+ ```
182
+
183
+ **Returns:** `PactiumVerificationResult & { bundleHash?: string }`
184
+
185
+ #### `pactium.exportProofBundle(envelopeOrId, options?)`
186
+
187
+ Exports an envelope as a portable Proof Bundle.
188
+
189
+ ```js
190
+ const bundle = await pactium.exportProofBundle(envelope, {
191
+ format: "indexed"
192
+ });
193
+ ```
194
+
195
+ **Returns:** `PactiumProofBundle`
196
+
197
+ ---
198
+
199
+ ### Verification Failures
200
+
201
+ #### `createVerificationFailure(input)`
202
+
203
+ Creates a structured verification failure object.
204
+
205
+ ```js
206
+ import { createVerificationFailure } from "pactium";
207
+
208
+ const failure = createVerificationFailure({
209
+ layer: "ledger",
210
+ code: "consistency_violation",
211
+ severity: "critical",
212
+ message: "Ledger history diverged",
213
+ repairable: false
214
+ });
215
+ ```
216
+
217
+ **Returns:** `PactiumVerificationFailure`
218
+
219
+ | Field | Type | Description |
220
+ | --- | --- | --- |
221
+ | `protocol` | `string` | Protocol identifier |
222
+ | `layer` | `string` | Verification layer (ledger, index, envelope, extension) |
223
+ | `code` | `string` | Machine-readable failure code |
224
+ | `severity` | `string` | Severity level (critical, warning, info) |
225
+ | `message` | `string?` | Human-readable description |
226
+ | `evidenceRef` | `string?` | Reference to failure evidence |
227
+ | `repairable` | `boolean?` | Whether automated repair is possible |
228
+ | `details` | `object?` | Additional structured details |
229
+
230
+ ---
231
+
232
+ ### Ledger Transparency Log
233
+
234
+ #### `createLedgerTransparencyLog(options?)`
235
+
236
+ Creates a standalone Ledger Transparency Log instance.
237
+
238
+ ```js
239
+ import { createLedgerTransparencyLog } from "pactium";
240
+
241
+ const ledger = createLedgerTransparencyLog({ storage: storagePort });
242
+ ```
243
+
244
+ **Returns:** `PactiumLedger`
245
+
246
+ | Method | Description |
247
+ | --- | --- |
248
+ | `ledger.append(entry)` | Append an entry and return `{ head, leafIndex, inclusionProof }` |
249
+ | `ledger.head()` | Return the current `PactiumLedgerHead` |
250
+ | `ledger.entries()` | Return all ledger entries |
251
+ | `ledger.pageEntries({ start, limit })` | Return a paginated slice of entries |
252
+
253
+ #### Proof helpers
254
+
255
+ ```js
256
+ import {
257
+ createLedgerInclusionProof,
258
+ verifyLedgerInclusionProof,
259
+ createLedgerConsistencyProof,
260
+ verifyLedgerConsistencyProof,
261
+ createCompactRange,
262
+ ledgerLeafHash,
263
+ ledgerNodeHash,
264
+ emptyTreeHash
265
+ } from "pactium";
266
+
267
+ // Verify that a leaf is included in the ledger at a given size
268
+ const inclusion = createLedgerInclusionProof({ leafIndex, treeSize, hashes });
269
+ const valid = verifyLedgerInclusionProof({ leafHash, treeSize, rootHash, proof: inclusion });
270
+
271
+ // Verify that a smaller ledger is a prefix of a larger one
272
+ const consistency = createLedgerConsistencyProof({ oldSize, newSize, hashes });
273
+ const valid = verifyLedgerConsistencyProof({ oldSize, newSize, oldRoot, newRoot, proof: consistency });
274
+ ```
275
+
276
+ ---
277
+
278
+ ### Signed Ledger Heads
279
+
280
+ ```js
281
+ import {
282
+ signLedgerHead,
283
+ verifyLedgerHeadSignature,
284
+ advanceTrustedHead,
285
+ createVerifierManifest,
286
+ ledgerHeadSigningPayload
287
+ } from "pactium";
288
+
289
+ // Create a verifier manifest describing the signing authority
290
+ const manifest = createVerifierManifest({
291
+ signerId: "authority-1",
292
+ algorithm: "ed25519",
293
+ publicKey: publicKeyHex
294
+ });
295
+
296
+ // Sign a ledger head
297
+ const signedHead = signLedgerHead(head, { signer, manifest });
298
+
299
+ // Verify signature
300
+ const result = verifyLedgerHeadSignature(signedHead, manifest);
301
+ // { ok: true/false, accepted: signatureCount }
302
+
303
+ // Advance a local trust anchor
304
+ const advanced = advanceTrustedHead({
305
+ lastTrusted: previousHead,
306
+ candidate: newHead,
307
+ consistencyProof: proof
308
+ });
309
+ ```
310
+
311
+ ---
312
+
313
+ ### Verifiable Index Engine
314
+
315
+ #### `createVerifiableIndexEngine(options?)`
316
+
317
+ Creates a Verifiable Index Engine instance (Canonical Prolly Tree).
318
+
319
+ ```js
320
+ import { createVerifiableIndexEngine } from "pactium";
321
+
322
+ const engine = createVerifiableIndexEngine({
323
+ storage: storagePort,
324
+ domain: "state" // Domain adapter identifier
325
+ });
326
+ ```
327
+
328
+ **Returns:** `PactiumIndexEngine`
329
+
330
+ | Method | Description |
331
+ | --- | --- |
332
+ | `engine.createIndex(entries?, options?)` | Create an index from initial entries |
333
+ | `engine.put(root, key, value, options?)` | Insert/update a key |
334
+ | `engine.delete(root, key, options?)` | Delete a key |
335
+ | `engine.get(root, key)` | Retrieve a value by key |
336
+ | `engine.prove(root, key)` | Generate a membership/non-membership proof |
337
+ | `engine.verifyProof(proof)` | Verify an index proof |
338
+ | `engine.scan(root, options?)` | Scan keys in range |
339
+ | `engine.prefix(root, keyPrefix?, options?)` | Scan keys by prefix |
340
+ | `engine.diff(leftRoot, rightRoot)` | Compute differences between two roots |
341
+ | `engine.readSnapshot(root)` | Read full index snapshot |
342
+ | `engine.readIndexRoot(root)` | Read index root metadata |
343
+ | `engine.readNode(root)` | Read a single index node |
344
+
345
+ #### `verifyIndexProof(proof)`
346
+
347
+ Standalone index proof verification.
348
+
349
+ ```js
350
+ import { verifyIndexProof } from "pactium";
351
+
352
+ const valid = verifyIndexProof(proof);
353
+ ```
354
+
355
+ ---
356
+
357
+ ### Canonical Value Encoding
358
+
359
+ ```js
360
+ import {
361
+ canonicalEncode,
362
+ canonicalDecode,
363
+ canonicalString,
364
+ normalizeCanonicalValue
365
+ } from "pactium";
366
+
367
+ // Encode a value to canonical bytes (deterministic)
368
+ const bytes = canonicalEncode({ key: "value", nested: [1, 2, 3] });
369
+
370
+ // Decode canonical bytes back to a value
371
+ const value = canonicalDecode(bytes);
372
+
373
+ // Get the canonical JSON string (sorted keys)
374
+ const str = canonicalString({ b: 2, a: 1 }); // '{"a":1,"b":2}'
375
+
376
+ // Normalize a value to PactiumCanonicalValue
377
+ const normalized = normalizeCanonicalValue(input);
378
+ ```
379
+
380
+ The canonical value model is a restricted IPLD/DAG-CBOR-style data model supporting: `null`, `boolean`, `number`, `string`, arrays, and plain objects with string keys.
381
+
382
+ ---
383
+
384
+ ### Protocol Hashing
385
+
386
+ ```js
387
+ import {
388
+ protocolHash,
389
+ protocolHashHex,
390
+ cidForBytes,
391
+ cidForCanonical,
392
+ HASH_DOMAINS
393
+ } from "pactium";
394
+
395
+ // Hash with domain separation
396
+ const hash = protocolHash("ledger.leaf", value);
397
+ const hex = protocolHashHex("ledger.node", value);
398
+
399
+ // Content-addressed identifiers
400
+ const cid = cidForBytes(bytes); // "cid:sha256:<hex>"
401
+ const cid = cidForCanonical(value); // Canonical encode then hash
402
+ ```
403
+
404
+ ---
405
+
406
+ ### Storage Port
407
+
408
+ ```js
409
+ import { createStoragePort, resolveDataDir, resolveWithin } from "pactium";
410
+
411
+ const storage = createStoragePort({
412
+ dataDir: "./.pactium",
413
+ inMemory: false
414
+ });
415
+
416
+ await storage.initialize();
417
+
418
+ // Content-addressed block storage
419
+ const record = await storage.putBlock(value);
420
+ const block = await storage.getBlock(cid);
421
+ const exists = await storage.hasBlock(cid);
422
+
423
+ // Protocol object storage (scoped key-value)
424
+ await storage.putProtocolObject("ledger", "head", headValue);
425
+ const head = await storage.getProtocolObject("ledger", "head");
426
+ ```
427
+
428
+ ---
429
+
430
+ ### Tracking Cursors and Append Conditions
431
+
432
+ ```js
433
+ import {
434
+ createTrackingCursor,
435
+ advanceTo,
436
+ covers,
437
+ samePositionAs,
438
+ verifyTrackingCursor,
439
+ createAppendCondition
440
+ } from "pactium";
441
+
442
+ // Create a cursor tracking ledger position
443
+ const cursor = createTrackingCursor({ position: 0, headHash: rootHash });
444
+
445
+ // Advance to a new position
446
+ const advanced = advanceTo(cursor, 5, { headHash: newRootHash });
447
+
448
+ // Check coverage
449
+ covers(cursor, 3); // true if cursor position >= 3
450
+
451
+ // Append condition for optimistic concurrency
452
+ const condition = createAppendCondition({
453
+ expectedHead: currentHead,
454
+ expectedSize: currentSize
455
+ });
456
+ ```
457
+
458
+ ---
459
+
460
+ ### Maintenance and Repair
461
+
462
+ ```js
463
+ import { createRepairPlanner, createMaintenanceTaskEngine } from "pactium";
464
+
465
+ // Repair planning from verification failures
466
+ const planner = createRepairPlanner();
467
+ const plan = planner.planRepair(failures);
468
+ // Returns deterministic repair tasks for host execution
469
+
470
+ // Maintenance task engine
471
+ const engine = createMaintenanceTaskEngine({ pactium });
472
+ const result = await engine.run("doctor");
473
+ // { ok: true/false, dataDir, checks: [...] }
474
+ ```
475
+
476
+ ---
477
+
478
+ ### Protocol Constants
479
+
480
+ ```js
481
+ import {
482
+ PACTIUM_PROTOCOL, // "pactium.v0.2"
483
+ PACTIUM_SCHEMA_VERSION, // "pactium.v0.2.schema.latest"
484
+ PACTIUM_PACKAGE_VERSION, // "0.2.1"
485
+ PACTIUM_INDEX_ENGINE, // "pactium.verifiable-index-engine"
486
+ PACTIUM_INDEX_SPLITTER, // "pactium-cdc-boundary"
487
+ PACTIUM_PROOF_BUNDLE_TYPE, // "pactium.proof-bundle.indexed"
488
+ PACTIUM_BUNDLE_ENCODING, // "pactium.bundle.indexed-record-stream"
489
+ PACTIUM_PROOF_TYPES, // { ledgerInclusion, ledgerConsistency, indexMembership, indexNonMembership }
490
+ PACTIUM_PROTOCOL_PROFILE, // Full protocol parameter matrix
491
+ HASH_DOMAINS // Domain separation constants
492
+ } from "pactium";
493
+ ```
494
+
495
+ ---
496
+
497
+ ## LicoLite Aspect API (`pactium/licolite`)
498
+
499
+ ### `createLicoLiteAspect(options?)`
500
+
501
+ Creates a LicoLite Aspect instance with workspace projection and signing defaults.
502
+
503
+ ```js
504
+ import { createLicoLiteAspect, createLicoLiteSigner } from "pactium/licolite";
505
+
506
+ const licolite = createLicoLiteAspect({
507
+ pactium: pactiumInstance, // Optional: provide existing instance
508
+ dataDir: "./.pactium", // Or create new instance from dataDir
509
+ evidencePolicy: "production", // "production" | "opportunistic"
510
+ signer: createLicoLiteSigner({
511
+ signerId: "host-signer",
512
+ secret: signingSecret
513
+ })
514
+ });
515
+ ```
516
+
517
+ **Returns:** `LicoLiteAspect`
518
+
519
+ | Property | Type | Description |
520
+ | --- | --- | --- |
521
+ | `protocol` | `string` | LicoLite aspect protocol identifier |
522
+ | `core` | `PactiumCore` | Underlying Pactium instance |
523
+ | `evidencePolicy` | `string` | Evidence policy mode |
524
+ | `workspaceProjectionDefault` | `true` | Workspace projection always enabled |
525
+ | `criticalExtensions` | `string[]` | Required critical extensions |
526
+ | `signer` | `LicoLiteSigner \| null` | Signing authority |
527
+
528
+ ---
529
+
530
+ ### `createLicoLiteSigner(options)`
531
+
532
+ Creates a signing authority for LicoLite proof envelopes.
533
+
534
+ ```js
535
+ import { createLicoLiteSigner } from "pactium/licolite";
536
+
537
+ const signer = createLicoLiteSigner({
538
+ signerId: "my-signer",
539
+ secret: "base64-encoded-secret",
540
+ algorithm: "ed25519" // Default
541
+ });
542
+ ```
543
+
544
+ **Returns:** `LicoLiteSigner`
545
+
546
+ | Method | Description |
547
+ | --- | --- |
548
+ | `signer.sign(message)` | Sign a message string |
549
+ | `signer.verify(message, signature)` | Verify a signature |
550
+
551
+ ---
552
+
553
+ ### `licolite.recordWorkspaceOperation(input)`
554
+
555
+ Records a workspace operation with LicoLite policy and workspace effect evidence.
556
+
557
+ ```js
558
+ const envelope = await licolite.recordWorkspaceOperation({
559
+ operationId: "workspace.file.write",
560
+ workspaceId: "workspace-a",
561
+ idempotencyKey: "intent-key",
562
+ outcomeIdempotencyKey: "outcome-key",
563
+ input: { path: "file.txt" },
564
+ outcome: "success",
565
+ policyEvidence: { decision: "allow", rule: "write-permitted" },
566
+ workspaceEffectEvidence: { durableRef: "host:asset:file-001" },
567
+ stateMutations: [
568
+ { key: "file.txt", value: { content: "data" } }
569
+ ]
570
+ });
571
+ ```
572
+
573
+ The returned envelope includes critical LicoLite extensions for policy and workspace effect evidence.
574
+
575
+ ---
576
+
577
+ ### `licolite.verifyEnvelope(envelope, options?)`
578
+
579
+ LicoLite-level verification that checks core proofs plus:
580
+ - Signature validity
581
+ - Critical extension support
582
+ - Policy extension binding
583
+ - Workspace effect extension binding
584
+ - Workspace projection proof
585
+
586
+ ```js
587
+ const result = await licolite.verifyEnvelope(envelope);
588
+ // { ok: true/false, failures: [...] }
589
+ ```
590
+
591
+ ---
592
+
593
+ ### `licolite.verifyBundle(bundle, options?)`
594
+
595
+ Verifies a Proof Bundle with LicoLite-level checks.
596
+
597
+ ```js
598
+ const result = await licolite.verifyBundle(bundle);
599
+ ```
600
+
601
+ ---
602
+
603
+ ### `licolite.planRepair(failures?)`
604
+
605
+ Translates verification failures into deterministic repair tasks.
606
+
607
+ ```js
608
+ const plan = licolite.planRepair(result.failures);
609
+ // { tasks: [...], repairable: true/false }
610
+ ```
611
+
612
+ ---
613
+
614
+ ### LicoLite Constants
615
+
616
+ ```js
617
+ import {
618
+ LICOLITE_ASPECT_PROTOCOL,
619
+ LICOLITE_POLICY_EXTENSION, // "licolite.policy"
620
+ LICOLITE_WORKSPACE_EFFECT_EXTENSION, // "licolite.workspaceEffect"
621
+ LICOLITE_SIGNATURE_EXTENSION, // "licolite.signature"
622
+ LICOLITE_CRITICAL_EXTENSIONS,
623
+ LICOLITE_SUPPORTED_CRITICAL_EXTENSIONS
624
+ } from "pactium/licolite";
625
+ ```
626
+
627
+ ### Evidence Helpers
628
+
629
+ ```js
630
+ import {
631
+ licoLitePolicyExtensionValue,
632
+ licoLiteWorkspaceEffectExtensionValue
633
+ } from "pactium/licolite";
634
+
635
+ // Generate policy extension value
636
+ const policyExt = licoLitePolicyExtensionValue({
637
+ decision: "allow",
638
+ rule: "upload-permitted"
639
+ });
640
+
641
+ // Generate workspace effect extension value
642
+ const effectExt = licoLiteWorkspaceEffectExtensionValue({
643
+ durableRef: "host:asset:001",
644
+ effectType: "file-write"
645
+ });
646
+ ```
647
+
648
+ ---
649
+
650
+ ## TypeScript Types
651
+
652
+ All public types are exported from the package entry points:
653
+
654
+ ```ts
655
+ import type {
656
+ PactiumCanonicalValue,
657
+ PactiumRecord,
658
+ PactiumDataDirOptions,
659
+ PactiumStoragePort,
660
+ PactiumLedgerHead,
661
+ PactiumProofEnvelope,
662
+ PactiumVerificationFailure,
663
+ PactiumVerificationResult,
664
+ PactiumProofBundle,
665
+ PactiumIndexScanOptions,
666
+ PactiumIndexEngine,
667
+ PactiumLedgerPageOptions,
668
+ PactiumLedgerPage,
669
+ PactiumLedger,
670
+ PactiumCore
671
+ } from "pactium";
672
+
673
+ import type {
674
+ LicoLiteSigner,
675
+ LicoLiteAspect
676
+ } from "pactium/licolite";
677
+ ```
678
+
679
+ ---
680
+
681
+ ## Error Handling
682
+
683
+ Pactium does not throw exceptions for verification failures. Instead, verification methods return structured results:
684
+
685
+ ```js
686
+ const result = await pactium.verifyEnvelope(envelope);
687
+
688
+ if (!result.ok) {
689
+ for (const failure of result.failures) {
690
+ console.error(`[${failure.layer}] ${failure.code}: ${failure.message}`);
691
+ if (failure.repairable) {
692
+ // Can be addressed by repair planner
693
+ }
694
+ }
695
+ }
696
+ ```
697
+
698
+ Protocol errors (invalid arguments, storage failures) throw standard JavaScript `Error` instances.