@net-mesh/sdk 0.19.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.
@@ -0,0 +1,574 @@
1
+ /**
2
+ * Capability-System Enhancements — TypeScript surface.
3
+ *
4
+ * This module mirrors the substrate's enhancement-track types:
5
+ *
6
+ * - Typed taxonomy ({@link Tag}, {@link TagKey}, {@link TaxonomyAxis})
7
+ * with reserved-prefix enforcement at construction time.
8
+ * - Fluent {@link Predicate} builder (`p.*`) producing the canonical
9
+ * wire IR (`PredicateWire`) consumed by `net-where` headers.
10
+ * - {@link diffCapabilities} returning the same `CapabilitySetDiff`
11
+ * shape the cross-binding fixtures pin.
12
+ * - {@link requireTag} / {@link requireAxisValue} chain helpers
13
+ * producing `{ tags, metadata }` directly.
14
+ * - {@link StandardPlacement} configuration object + the
15
+ * {@link placementFilterFromFn} callback shape.
16
+ *
17
+ * All wire-format types match the JSON shape the substrate emits via
18
+ * `serde_json` — the cross-binding tests in
19
+ * `tests/cross_lang_capability/` pin the canonical bytes.
20
+ *
21
+ * @packageDocumentation
22
+ */
23
+ /**
24
+ * Canonical capability axis. Mirrors `TaxonomyAxis` in the substrate
25
+ * (`adapter::net::behavior::tag::TaxonomyAxis`). The wire form is the
26
+ * lowercase string.
27
+ */
28
+ export type TaxonomyAxis = 'hardware' | 'software' | 'devices' | 'dataforts';
29
+ /** Every axis the substrate knows about. */
30
+ export declare const TAXONOMY_AXES: readonly TaxonomyAxis[];
31
+ /**
32
+ * Reserved cross-axis prefixes. Substrate-privileged paths
33
+ * (`announceChain`, fork-coordination, scope helpers) emit these;
34
+ * user code calling {@link tagFromUserString} is rejected.
35
+ */
36
+ export declare const RESERVED_PREFIXES: readonly string[];
37
+ /**
38
+ * `<axis>.<key>` identifier — the addressing pair for axis-prefixed
39
+ * tags and axis-keyed predicates.
40
+ */
41
+ export interface TagKey {
42
+ axis: TaxonomyAxis;
43
+ /** Key within the axis namespace (e.g. `gpu`, `runtime.python`). */
44
+ key: string;
45
+ }
46
+ /**
47
+ * Build a {@link TagKey}. Throws on empty key — matches the substrate's
48
+ * `TagKey::new` contract (the constructor is fallible-by-debug-assert
49
+ * there; we surface as a thrown error here).
50
+ */
51
+ export declare function tagKey(axis: TaxonomyAxis, key: string): TagKey;
52
+ /** Separator between an axis-tag's key and its value. */
53
+ export type AxisSeparator = ':' | '=';
54
+ /**
55
+ * Typed tag. Mirrors `Tag` in the substrate. The wire form is the
56
+ * canonical Display string ({@link tagToString}).
57
+ *
58
+ * - `axisPresent` — `<axis>.<key>` (no value).
59
+ * - `axisValue` — `<axis>.<key>=<value>` or `<axis>.<key>:<value>`.
60
+ * - `reserved` — one of the {@link RESERVED_PREFIXES} cross-axis tags.
61
+ * - `legacy` — arbitrary string outside the typed taxonomy. The
62
+ * substrate keeps these as-is for forward compatibility.
63
+ */
64
+ export type Tag = {
65
+ kind: 'axisPresent';
66
+ axis: TaxonomyAxis;
67
+ key: string;
68
+ } | {
69
+ kind: 'axisValue';
70
+ axis: TaxonomyAxis;
71
+ key: string;
72
+ value: string;
73
+ separator: AxisSeparator;
74
+ } | {
75
+ kind: 'reserved';
76
+ prefix: string;
77
+ body: string;
78
+ } | {
79
+ kind: 'legacy';
80
+ raw: string;
81
+ };
82
+ /** True iff `s` starts with one of the {@link RESERVED_PREFIXES}. */
83
+ export declare function startsWithReservedPrefix(s: string): string | undefined;
84
+ /**
85
+ * Render a {@link Tag} to its canonical wire string. Matches the
86
+ * substrate's `Display` impl for `Tag`.
87
+ */
88
+ export declare function tagToString(tag: Tag): string;
89
+ /**
90
+ * Parse a wire string into a {@link Tag}. Privileged path — does
91
+ * NOT reject reserved prefixes (substrate code that legitimately
92
+ * needs to round-trip e.g. `scope:tenant:foo` calls this).
93
+ *
94
+ * User code should prefer {@link tagFromUserString}, which rejects
95
+ * reserved prefixes.
96
+ */
97
+ export declare function tagFromString(s: string): Tag;
98
+ /**
99
+ * Parse a wire string from user code. Rejects the reserved
100
+ * cross-axis prefixes ({@link RESERVED_PREFIXES}) — application code
101
+ * cannot emit those by design. Mirrors the substrate's
102
+ * `Tag::parse_user`.
103
+ */
104
+ export declare function tagFromUserString(s: string): Tag;
105
+ /** Numeric kinds — match the substrate's wire `kind` strings exactly. */
106
+ export type PredicateNode = {
107
+ kind: 'exists';
108
+ key: TagKey;
109
+ } | {
110
+ kind: 'equals';
111
+ key: TagKey;
112
+ value: string;
113
+ } | {
114
+ kind: 'numeric_at_least';
115
+ key: TagKey;
116
+ threshold: number;
117
+ } | {
118
+ kind: 'numeric_at_most';
119
+ key: TagKey;
120
+ threshold: number;
121
+ } | {
122
+ kind: 'numeric_in_range';
123
+ key: TagKey;
124
+ min: number;
125
+ max: number;
126
+ } | {
127
+ kind: 'semver_at_least';
128
+ key: TagKey;
129
+ version: string;
130
+ } | {
131
+ kind: 'semver_at_most';
132
+ key: TagKey;
133
+ version: string;
134
+ } | {
135
+ kind: 'semver_compatible';
136
+ key: TagKey;
137
+ version: string;
138
+ } | {
139
+ kind: 'string_prefix';
140
+ key: TagKey;
141
+ prefix: string;
142
+ } | {
143
+ kind: 'string_matches';
144
+ key: TagKey;
145
+ pattern: string;
146
+ } | {
147
+ kind: 'metadata_exists';
148
+ key: string;
149
+ } | {
150
+ kind: 'metadata_equals';
151
+ key: string;
152
+ value: string;
153
+ } | {
154
+ kind: 'metadata_matches';
155
+ key: string;
156
+ pattern: string;
157
+ } | {
158
+ kind: 'metadata_numeric_at_least';
159
+ key: string;
160
+ threshold: number;
161
+ } | {
162
+ kind: 'and';
163
+ children: number[];
164
+ } | {
165
+ kind: 'or';
166
+ children: number[];
167
+ } | {
168
+ kind: 'not';
169
+ child: number;
170
+ };
171
+ /**
172
+ * Wire-format predicate. The exact JSON shape the substrate's
173
+ * `net-where` request header carries; pinned by the
174
+ * `predicate_nrpc_envelope.json` cross-binding fixture.
175
+ */
176
+ export interface PredicateWire {
177
+ nodes: PredicateNode[];
178
+ root_idx: number;
179
+ }
180
+ /**
181
+ * In-memory predicate AST. Sugar over {@link PredicateWire} — the
182
+ * fluent {@link p} builder constructs these and {@link predicateToWire}
183
+ * flattens them.
184
+ */
185
+ export type Predicate = {
186
+ type: 'exists';
187
+ key: TagKey;
188
+ } | {
189
+ type: 'equals';
190
+ key: TagKey;
191
+ value: string;
192
+ } | {
193
+ type: 'numericAtLeast';
194
+ key: TagKey;
195
+ threshold: number;
196
+ } | {
197
+ type: 'numericAtMost';
198
+ key: TagKey;
199
+ threshold: number;
200
+ } | {
201
+ type: 'numericInRange';
202
+ key: TagKey;
203
+ min: number;
204
+ max: number;
205
+ } | {
206
+ type: 'semverAtLeast';
207
+ key: TagKey;
208
+ version: string;
209
+ } | {
210
+ type: 'semverAtMost';
211
+ key: TagKey;
212
+ version: string;
213
+ } | {
214
+ type: 'semverCompatible';
215
+ key: TagKey;
216
+ version: string;
217
+ } | {
218
+ type: 'stringPrefix';
219
+ key: TagKey;
220
+ prefix: string;
221
+ } | {
222
+ type: 'stringMatches';
223
+ key: TagKey;
224
+ pattern: string;
225
+ } | {
226
+ type: 'metadataExists';
227
+ key: string;
228
+ } | {
229
+ type: 'metadataEquals';
230
+ key: string;
231
+ value: string;
232
+ } | {
233
+ type: 'metadataMatches';
234
+ key: string;
235
+ pattern: string;
236
+ } | {
237
+ type: 'metadataNumericAtLeast';
238
+ key: string;
239
+ threshold: number;
240
+ } | {
241
+ type: 'and';
242
+ children: Predicate[];
243
+ } | {
244
+ type: 'or';
245
+ children: Predicate[];
246
+ } | {
247
+ type: 'not';
248
+ child: Predicate;
249
+ };
250
+ /**
251
+ * Fluent predicate constructors. Match the substrate's `Predicate::*`
252
+ * factory methods one-to-one.
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * import { p, predicateToWire } from '@net-mesh/sdk';
257
+ *
258
+ * const pred = p.and(
259
+ * p.exists({ axis: 'hardware', key: 'gpu' }),
260
+ * p.numericAtLeast({ axis: 'hardware', key: 'memory_gb' }, 64),
261
+ * p.metadataEquals('intent', 'ml-training'),
262
+ * );
263
+ * const wire = predicateToWire(pred);
264
+ * ```
265
+ */
266
+ export declare const p: {
267
+ readonly exists: (key: TagKey) => Predicate;
268
+ readonly equals: (key: TagKey, value: string) => Predicate;
269
+ readonly numericAtLeast: (key: TagKey, threshold: number) => Predicate;
270
+ readonly numericAtMost: (key: TagKey, threshold: number) => Predicate;
271
+ readonly numericInRange: (key: TagKey, min: number, max: number) => Predicate;
272
+ readonly semverAtLeast: (key: TagKey, version: string) => Predicate;
273
+ readonly semverAtMost: (key: TagKey, version: string) => Predicate;
274
+ readonly semverCompatible: (key: TagKey, version: string) => Predicate;
275
+ readonly stringPrefix: (key: TagKey, prefix: string) => Predicate;
276
+ readonly stringMatches: (key: TagKey, pattern: string) => Predicate;
277
+ readonly metadataExists: (key: string) => Predicate;
278
+ readonly metadataEquals: (key: string, value: string) => Predicate;
279
+ readonly metadataMatches: (key: string, pattern: string) => Predicate;
280
+ readonly metadataNumericAtLeast: (key: string, threshold: number) => Predicate;
281
+ readonly and: (...children: Predicate[]) => Predicate;
282
+ readonly or: (...children: Predicate[]) => Predicate;
283
+ readonly not: (child: Predicate) => Predicate;
284
+ };
285
+ /** Flatten an AST into the wire IR. Children always sit at lower
286
+ * indices than their parents (post-order). */
287
+ export declare function predicateToWire(pred: Predicate): PredicateWire;
288
+ /** Inverse of {@link predicateToWire}. Rebuilds the AST from a wire
289
+ * IR. Throws on out-of-range indices or malformed nodes. */
290
+ export declare function predicateFromWire(wire: PredicateWire): Predicate;
291
+ /** Header the substrate uses to carry a predicate over nRPC. */
292
+ export declare const RPC_WHERE_HEADER = "net-where";
293
+ /** Encode a predicate into the request-header value. The substrate
294
+ * pins this as the canonical JSON-encoded {@link PredicateWire}. */
295
+ export declare function predicateToRpcHeader(pred: Predicate): string;
296
+ /** Decode a `net-where` header value back into an AST. Throws
297
+ * on malformed JSON or out-of-range indices. */
298
+ export declare function predicateFromRpcHeader(value: string): Predicate;
299
+ /**
300
+ * Build the `net-where:` request-header entry for a
301
+ * Phase 9b predicate-pushdown call. Drops straight into a
302
+ * `MeshRpc.call` `CallOptions.requestHeaders` array.
303
+ *
304
+ * @example
305
+ * ```ts
306
+ * import { p, tagKey, whereHeader } from '@net-mesh/sdk';
307
+ * const pred = p.exists(tagKey('hardware', 'gpu'));
308
+ * await rpc.call(targetNodeId, 'filter-svc', body, {
309
+ * requestHeaders: [whereHeader(pred)],
310
+ * });
311
+ * ```
312
+ *
313
+ * The header value is the canonical JSON-encoded `PredicateWire`
314
+ * pinned by `predicate_nrpc_envelope.json`.
315
+ */
316
+ export declare function whereHeader(pred: Predicate): {
317
+ name: string;
318
+ value: Buffer;
319
+ };
320
+ /** Wire-format capability shape — string tags + string→string metadata. */
321
+ export interface CapabilitySetWire {
322
+ tags: string[];
323
+ metadata: Record<string, string>;
324
+ }
325
+ /** Per-key metadata change. Mirrors `MetadataChange` in the substrate. */
326
+ export type MetadataChange = {
327
+ kind: 'added';
328
+ key: string;
329
+ value: string;
330
+ } | {
331
+ kind: 'removed';
332
+ key: string;
333
+ prev_value: string;
334
+ } | {
335
+ kind: 'updated';
336
+ key: string;
337
+ prev_value: string;
338
+ new_value: string;
339
+ };
340
+ /** Diff between two capability snapshots. Mirrors
341
+ * `CapabilitySetDiff` in the substrate. */
342
+ export interface CapabilitySetDiff {
343
+ added_tags: string[];
344
+ removed_tags: string[];
345
+ metadata_changes: MetadataChange[];
346
+ }
347
+ /**
348
+ * Compute `curr.diff(prev)`. Pinned by the
349
+ * `capability_set_diff.json` cross-binding fixture.
350
+ *
351
+ * - Tag arrays are sorted by wire string.
352
+ * - Metadata changes are sorted by key (BTreeMap semantics in the
353
+ * substrate).
354
+ * - A key rename surfaces as Removed + Added (NOT Updated). Only a
355
+ * value change for the same key is Updated.
356
+ */
357
+ export declare function diffCapabilities(prev: CapabilitySetWire, curr: CapabilitySetWire): CapabilitySetDiff;
358
+ /**
359
+ * Add an axis-tag (no value) to the wire shape. Idempotent; no-op
360
+ * if the tag is already present.
361
+ */
362
+ export declare function requireTag(caps: CapabilitySetWire, axis: TaxonomyAxis, key: string): CapabilitySetWire;
363
+ /**
364
+ * Add an axis-value tag (`<axis>.<key>=<value>` by default) to the
365
+ * wire shape. Idempotent for the exact (axis, key, value, separator)
366
+ * triple.
367
+ */
368
+ export declare function requireAxisValue(caps: CapabilitySetWire, axis: TaxonomyAxis, key: string, value: string, separator?: AxisSeparator): CapabilitySetWire;
369
+ /** Set / overwrite a metadata entry. */
370
+ export declare function withMetadata(caps: CapabilitySetWire, key: string, value: string): CapabilitySetWire;
371
+ /** Empty wire-format capability set. */
372
+ export declare function emptyCapabilities(): CapabilitySetWire;
373
+ /**
374
+ * Configuration for the substrate's `StandardPlacement` filter. All
375
+ * fields are optional — an empty config matches every node.
376
+ *
377
+ * The wire shape is JSON-friendly (`predicate` carries a
378
+ * {@link PredicateWire}, not the AST). Bindings encode this object
379
+ * before handing it to the runtime.
380
+ */
381
+ export interface StandardPlacement {
382
+ /**
383
+ * Required tags — every listed wire-string must be present. Use
384
+ * {@link tagToString} to turn typed {@link Tag} values into
385
+ * wire form.
386
+ */
387
+ requireTags?: string[];
388
+ /**
389
+ * Forbidden tags — none of these may be present. Same wire form
390
+ * as {@link requireTags}.
391
+ */
392
+ forbidTags?: string[];
393
+ /** Required metadata key/value equalities. */
394
+ requireMetadata?: Record<string, string>;
395
+ /**
396
+ * Free-form predicate evaluated against each candidate's tag set
397
+ * + metadata. Combined with the tag/metadata constraints via AND.
398
+ */
399
+ predicate?: PredicateWire;
400
+ /**
401
+ * Maximum candidates to return. The runtime picks deterministically
402
+ * by node id when more match.
403
+ */
404
+ limit?: number;
405
+ /**
406
+ * Custom placement filter — a string id resolved by the runtime
407
+ * to a registered callback (see {@link placementFilterFromFn}).
408
+ */
409
+ customFilterId?: string;
410
+ }
411
+ /**
412
+ * Builder for {@link StandardPlacement}. Returns a frozen config
413
+ * object suitable for handing to the runtime.
414
+ */
415
+ export declare class StandardPlacementBuilder {
416
+ private cfg;
417
+ requireTag(axis: TaxonomyAxis, key: string): this;
418
+ requireAxisValue(axis: TaxonomyAxis, key: string, value: string, separator?: AxisSeparator): this;
419
+ forbidTag(axis: TaxonomyAxis, key: string): this;
420
+ requireMetadata(key: string, value: string): this;
421
+ withPredicate(pred: Predicate | PredicateWire): this;
422
+ withLimit(n: number): this;
423
+ withCustomFilterId(id: string): this;
424
+ build(): StandardPlacement;
425
+ }
426
+ /** Convenience constructor for a {@link StandardPlacementBuilder}. */
427
+ export declare function standardPlacement(): StandardPlacementBuilder;
428
+ /**
429
+ * Candidate handed to a custom placement-filter callback. The
430
+ * runtime materializes one of these per candidate before evaluating
431
+ * the user predicate.
432
+ */
433
+ export interface PlacementCandidate {
434
+ nodeId: bigint;
435
+ tags: string[];
436
+ metadata: Record<string, string>;
437
+ }
438
+ /**
439
+ * Synchronous predicate: `true` to keep, `false` to drop.
440
+ *
441
+ * Custom filters run under the placement hot path — keep them tight
442
+ * and avoid I/O. The runtime registers them by id; the daemon's
443
+ * {@link StandardPlacement} references that id via
444
+ * {@link StandardPlacement.customFilterId}.
445
+ */
446
+ export type PlacementFilterFn = (candidate: PlacementCandidate) => boolean;
447
+ /**
448
+ * Wrap a user-supplied predicate as a placement filter. Returns a
449
+ * `{ id, fn }` pair the binding can register with the runtime —
450
+ * future `StandardPlacement.customFilterId = id` uses route through
451
+ * the wrapped function.
452
+ *
453
+ * The default id generator uses a counter; callers can supply an
454
+ * explicit id when they want stable identity (e.g. for hot-reload).
455
+ */
456
+ export interface RegisteredPlacementFilter {
457
+ id: string;
458
+ fn: PlacementFilterFn;
459
+ }
460
+ export declare function placementFilterFromFn(fn: PlacementFilterFn, explicitId?: string): RegisteredPlacementFilter;
461
+ /**
462
+ * Evaluate a {@link Predicate} against a wire-format `(tags, metadata)`
463
+ * context. Mirrors the substrate's `Predicate::evaluate_unplanned`
464
+ * — children of `and` / `or` evaluate in declaration order with
465
+ * standard short-circuit semantics.
466
+ *
467
+ * Pinned across bindings by `predicate_eval.json`. Use this for local
468
+ * pre-filtering of result sets before sending an nRPC `where:`
469
+ * predicate over the wire, or for client-side validation of a
470
+ * predicate against a known capability set.
471
+ */
472
+ export declare function evaluatePredicate(pred: Predicate, tags: readonly string[], metadata: Readonly<Record<string, string>>): boolean;
473
+ /**
474
+ * Per-clause trace entry. Mirrors the substrate's `ClauseTrace`:
475
+ * each leaf carries a one-line `label` + the boolean `result`;
476
+ * composites carry the planner-ordered subset of children that
477
+ * actually ran.
478
+ */
479
+ export interface ClauseTrace {
480
+ label: string;
481
+ result: boolean;
482
+ children: ClauseTrace[];
483
+ }
484
+ /**
485
+ * Evaluate a predicate against `(tags, metadata)` and produce a
486
+ * trace tree.
487
+ *
488
+ * Mirrors the substrate's `Predicate::evaluate_with_trace`:
489
+ * - `And` / `Or` children evaluated in cost-ascending order.
490
+ * - Short-circuited siblings DON'T appear in the trace — operators
491
+ * see "the metadata clause failed; we never got to the GPU
492
+ * check."
493
+ * - `Not`'s child carries the pre-negation result; `Not`'s own node
494
+ * carries the post-negation result.
495
+ *
496
+ * Pinned across bindings by `predicate_trace.json`. Useful for
497
+ * client-side debugging of why a candidate did / didn't match
498
+ * before hitting the wire.
499
+ */
500
+ export declare function evaluatePredicateWithTrace(pred: Predicate, tags: readonly string[], metadata: Readonly<Record<string, string>>): {
501
+ result: boolean;
502
+ trace: ClauseTrace;
503
+ };
504
+ /**
505
+ * Per-clause aggregated stats. Mirrors the substrate's `ClauseStats`.
506
+ */
507
+ export interface ClauseStats {
508
+ /** Clause label — same string as `ClauseTrace.label`. */
509
+ label: string;
510
+ /** Number of candidates that reached this clause (not short-circuited). */
511
+ evaluated: number;
512
+ /** Number of those evaluations that returned `true`. */
513
+ matched: number;
514
+ }
515
+ /**
516
+ * Wire-format debug report. The `clause_stats` array is sorted by
517
+ * label (BTreeMap semantics in the substrate); bindings produce that
518
+ * canonical order.
519
+ */
520
+ export interface PredicateDebugReport {
521
+ total_candidates: number;
522
+ matched: number;
523
+ clause_stats: ClauseStats[];
524
+ }
525
+ /** Wire-format evaluation context — what `evaluate*` consumes. */
526
+ export interface EvalContextWire {
527
+ tags: string[];
528
+ metadata: Record<string, string>;
529
+ }
530
+ /**
531
+ * Run `pred` against each context in `contexts`, accumulating
532
+ * per-clause hit / miss stats. Mirrors the substrate's
533
+ * `PredicateDebugReport::from_evaluations`.
534
+ *
535
+ * The returned report's `clause_stats` is sorted by label
536
+ * (BTreeMap semantics) so bindings produce byte-identical output
537
+ * for the same input corpus.
538
+ */
539
+ export declare function predicateDebugReport(pred: Predicate, contexts: readonly EvalContextWire[]): PredicateDebugReport;
540
+ /**
541
+ * Redact metadata-clause values in a {@link PredicateDebugReport}.
542
+ *
543
+ * Walks the report's `clause_stats` and rewrites any label whose
544
+ * metadata key is in the supplied `keys` list:
545
+ *
546
+ * - `MetadataEquals(<key>=<value>)` → `MetadataEquals(<key>=<redacted>)`
547
+ * - `MetadataMatches(<key> contains "<pattern>")` → `MetadataMatches(<key> contains "<redacted>")`
548
+ * - `MetadataNumericAtLeast(<key> >= <threshold>)` → `MetadataNumericAtLeast(<key> >= <redacted>)`
549
+ * - `MetadataExists(<key>)` — unchanged (no value to redact)
550
+ * - All non-metadata labels (Exists, Equals, Numeric*, Semver*,
551
+ * String*, And, Or, Not on tags) unchanged.
552
+ *
553
+ * After rewriting, stats with the same redacted label are merged
554
+ * (`evaluated` and `matched` summed). Output is sorted by label.
555
+ *
556
+ * Use this before persisting a debug report to disk or sharing with
557
+ * a teammate when the predicate's authored metadata values are
558
+ * sensitive (PII, secrets, internal classifications).
559
+ *
560
+ * Pinned across bindings by `predicate_debug_report_redacted.json`.
561
+ */
562
+ export declare function redactMetadataKeys(report: PredicateDebugReport, keys: readonly string[]): PredicateDebugReport;
563
+ /**
564
+ * Reconstruct a {@link PredicateDebugReport} from its wire JSON form
565
+ * (the shape produced by JSON-stringifying the report). Validates
566
+ * required fields; on malformed input throws a descriptive error.
567
+ *
568
+ * Symmetric inverse of `JSON.stringify(report)` — call
569
+ * `predicateDebugReportFromWire(JSON.parse(text))` to round-trip a
570
+ * report through disk.
571
+ */
572
+ export declare function predicateDebugReportFromWire(wire: unknown): PredicateDebugReport;
573
+ /** Render a one-line-per-clause text summary suitable for CLI output. */
574
+ export declare function renderDebugReport(report: PredicateDebugReport): string;