@saasontools/strauss-kb 0.1.6 → 0.1.7

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/dist/index.d.cts CHANGED
@@ -225,12 +225,11 @@ declare function resolveHeads(from: KbRecord, byId: Map<string, KbRecord>): {
225
225
  };
226
226
 
227
227
  /**
228
- * Edges a trace may follow.
229
- *
230
- * Two more are conceivable and absent: `strauss_answered` carries no target id,
231
- * so a question's resolution lives in its own body rather than in another
232
- * record; and following OKF's body markdown links would need a markdown AST
233
- * pass this package does not yet do.
228
+ * Edges a trace may follow — the shared kb-edges.ts definitions, minus
229
+ * `body-link`: body links can reach most of a bundle from anywhere, which
230
+ * suits a bounded pack but floods a timeline. Also absent by design:
231
+ * `strauss_answered` carries no target id, so a question's resolution lives
232
+ * in its own body rather than in another record.
234
233
  */
235
234
  declare const TRACE_EDGES: readonly ["supersession", "anchor", "source"];
236
235
  type KbTraceEdge = (typeof TRACE_EDGES)[number];
@@ -260,6 +259,58 @@ type KbTraceOptions = {
260
259
  */
261
260
  declare function trace(seedId: string, bundle: KbRecord[], options?: KbTraceOptions): KbTraceStep[];
262
261
 
262
+ declare const DEFAULT_PACK_HOPS = 2;
263
+ declare const DEFAULT_PACK_MAX_NODES = 20;
264
+ type KbPackOptions = {
265
+ /** How far from the root the walk may reach. */
266
+ hops?: number;
267
+ /** How many records the pack may hold, root included. */
268
+ maxNodes?: number;
269
+ /** Approximate token ceiling over what is actually emitted. */
270
+ budgetTokens?: number;
271
+ };
272
+ /** One record as the pack emits it — the same mapping `kb_load` hands back. */
273
+ type KbPackedRecord = {
274
+ conceptId: string;
275
+ title: string | null;
276
+ standing: KbStanding;
277
+ supersededBy: string[];
278
+ warnings: KbWarning[];
279
+ anchors: KbAnchor[];
280
+ body: string;
281
+ };
282
+ /**
283
+ * Deliberately timestamp-free: the same bundle and root must produce the same
284
+ * bytes on every run, so a caller can diff two packs and trust that a changed
285
+ * byte means changed knowledge.
286
+ */
287
+ type KbPackResult = {
288
+ root: string;
289
+ records: KbPackedRecord[];
290
+ /** Named only, exactly as `load` stubs them. Bodies reachable via trace. */
291
+ superseded: KbSupersededStub[];
292
+ /** Every reachable record the hop and node limits cut, never summarized. */
293
+ excluded: string[];
294
+ recordCount: number;
295
+ tokensLoaded: number;
296
+ budgetTokens: number;
297
+ };
298
+ /**
299
+ * A bounded, progressively disclosed neighbourhood around one record.
300
+ *
301
+ * Where `load` hands over a whole base and `trace` a timeline, a pack is the
302
+ * subgraph a reader needs to act near one record: everything within `hops`
303
+ * of the root, cut to `maxNodes` by ranking, with every cut id listed —
304
+ * a named gap is knowable, a silent one is not.
305
+ *
306
+ * Adjudication runs against the whole bundle, not the reached slice, so a
307
+ * record's standing cannot depend on whether its replacement happened to be
308
+ * within reach. Superseded records become the same stubs `load` emits, costed
309
+ * as stubs, and the budget is a refusal rather than a truncation: a partial
310
+ * pack looks complete to its reader.
311
+ */
312
+ declare function pack(bundle: KbRecord[], rootId: string, options?: KbPackOptions): KbPackResult;
313
+
263
314
  declare const LOG_FILE = "log.jsonl";
264
315
  declare const kbLogEntrySchema: z.ZodObject<{
265
316
  at: z.ZodString;
@@ -310,6 +361,8 @@ type KbLogger = {
310
361
  info?(entry: Record<string, unknown>): void;
311
362
  warn?(entry: Record<string, unknown>): void;
312
363
  };
364
+ /** Roughly an eighth of a large context window — generous, and overridable. */
365
+ declare const DEFAULT_LOAD_BUDGET = 25000;
313
366
  /**
314
367
  * A superseded record, named but not spelled out.
315
368
  *
@@ -467,6 +520,8 @@ declare class KbStore {
467
520
  }): Promise<KbLoadResult>;
468
521
  /** How a position was arrived at, as a timeline. See `trace.ts`. */
469
522
  trace(bundlePath: string, seedId: string, options?: KbTraceOptions): Promise<KbTraceStep[]>;
523
+ /** A bounded neighbourhood around one record. See `pack.ts`. */
524
+ pack(bundlePath: string, rootId: string, options?: KbPackOptions): Promise<KbPackResult>;
470
525
  /**
471
526
  * The stored index, rebuilt if it disagrees with the records.
472
527
  *
@@ -545,6 +600,7 @@ declare enum Fault {
545
600
  declare enum ErrorTypes {
546
601
  KbRecordAlreadyExists = "KbRecordAlreadyExists",
547
602
  KbInvalidConceptId = "KbInvalidConceptId",
603
+ KbPackBudgetExceeded = "KbPackBudgetExceeded",
548
604
  KbRecordNotFound = "KbRecordNotFound",
549
605
  KbSelfVerification = "KbSelfVerification",
550
606
  KbWriteConflict = "KbWriteConflict"
@@ -596,6 +652,20 @@ declare class KbSelfVerificationError extends BaseError {
596
652
  readonly generatedBy: string;
597
653
  constructor(conceptId: string, actor: string, generatedBy: string);
598
654
  }
655
+ /**
656
+ * A pack that will not fit its token budget. Refusal, not truncation: a
657
+ * partial pack is indistinguishable from a complete one, so the caller gets
658
+ * the full picture — how many records, how many tokens, and every id the
659
+ * walk's own limits already cut — and decides whether to raise the budget or
660
+ * tighten the walk.
661
+ */
662
+ declare class KbPackBudgetExceededError extends BaseError {
663
+ readonly recordCount: number;
664
+ readonly approxTokens: number;
665
+ readonly budgetTokens: number;
666
+ readonly excluded: string[];
667
+ constructor(recordCount: number, approxTokens: number, budgetTokens: number, excluded: string[]);
668
+ }
599
669
  declare class KbInvalidConceptIdError extends BaseError {
600
670
  constructor(message: string, details: Record<string, string>);
601
671
  }
@@ -1018,6 +1088,33 @@ type MatchOptions = {
1018
1088
  };
1019
1089
  declare function matchToDiff(files: DiffFile[], records: KbRecord[], options?: MatchOptions): DiffMatch[];
1020
1090
 
1091
+ /**
1092
+ * The edges between records in one bundle, defined once.
1093
+ *
1094
+ * Both walks — `trace` and `pack` — consume this module, so they cannot drift
1095
+ * into disagreeing about what makes two records neighbours, and a diagnostic
1096
+ * pass over the graph can reuse the same definition.
1097
+ *
1098
+ * There is no separate `related` kind: compose.ts renders `relatedConceptIds`
1099
+ * as body links (`Relates to [id](id.md).`), so in stored form a related edge
1100
+ * IS a body link, and a distinct kind would count the same markdown twice.
1101
+ */
1102
+ declare const KB_EDGE_KINDS: readonly ["body-link", "supersession", "anchor", "source"];
1103
+ type KbEdgeKind = (typeof KB_EDGE_KINDS)[number];
1104
+ type KbNeighbour = {
1105
+ record: KbRecord;
1106
+ /** Every edge kind that connects it to the record asked about. */
1107
+ via: KbEdgeKind[];
1108
+ };
1109
+ /**
1110
+ * Every record `from` touches, each carrying the full set of edge kinds that
1111
+ * connect the pair. Order is deterministic: bundle order per kind, kinds in
1112
+ * the order given.
1113
+ */
1114
+ declare function neighbours(from: KbRecord, bundle: KbRecord[], kinds?: readonly KbEdgeKind[]): KbNeighbour[];
1115
+ /** The records one edge kind connects `from` to, in bundle order. */
1116
+ declare function edgeNeighbours(from: KbRecord, bundle: KbRecord[], kind: KbEdgeKind): KbRecord[];
1117
+
1021
1118
  type KbValidationProblem = {
1022
1119
  check: string;
1023
1120
  conceptId: string;
@@ -1297,4 +1394,4 @@ declare function parseMarkdownWithFrontmatter<S extends z.ZodType>(text: string,
1297
1394
  frontmatter: ReturnType<S["safeParse"]>;
1298
1395
  };
1299
1396
 
1300
- export { BaseError, CONTEXT_BEGIN, CONTEXT_END, CONTEXT_PROFILES, type ComposeInput, type ComposedRecord, DECISION_TYPE, type DecisionInput, type DiffFile, type DiffHunk, type DiffMatch, type ErrorDetails, type ErrorProps, ErrorTypes, Fault, INDEX_FILE, KB_COMMANDS, KB_COMMANDS_BY_NAME, KB_CONCEPT_ID_PATTERN, KB_CONFIDENCES, KB_DIR, KB_MATERIALITIES, KB_RECORD_STATUSES, KB_RECORD_TYPES, KB_SLUG_PATTERN, type KbActorStamp, type KbAdjudicated, type KbAnchor, KbBaseFrozenError, type KbCommand, type KbCommandContext, type KbContextBudgets, type KbContextOptions, type KbContextResult, KbInvalidConceptIdError, type KbLoadResult, type KbLogEntry, type KbLogReadResult, type KbLogger, type KbMergedPin, type KbMergedPins, type KbPin, type KbPinLayer, type KbPinOptions, type KbPinResult, type KbPinStatus, KbPinsMalformedError, type KbPinsManifest, type KbRecord, KbRecordAlreadyExistsError, type KbRecordFrontmatter, KbRecordNotFoundError, type KbRecordStatus, type KbRecordType, type KbRecordTypeSpec, type KbSearchLogger, KbSelfVerificationError, type KbSource, type KbStanding, KbStore, type KbSupersededStub, type KbSyncResult, type KbTraceEdge, type KbTraceOptions, type KbTraceStep, type KbValidationProblem, type KbVerifiedEvent, type KbWarning, KbWriteConflictError, type KbWriteInput, LOG_FILE, type MatchOptions, NO_DECISION_SLUG, PINS_FILE, PINS_LOCAL_FILE, PIN_LAYERS, type QmdModule, RECORD_TYPES, SEARCH_INDEX_FILE, type SearchHit, type SearchOptions, type SymbolRange, TRACE_EDGES, adjudicate, assertBaseNotFrozen, buildContext, composeDecisionRecord, composeInputSchema, composeNoDecisionRecord, composeRecord, contextProfileBudgets, createKbMcpServer, decisionInputSchema, indexIsStale, isKbRecordType, isNoDecisionRecord, kbActorStampSchema, kbAnchorSchema, kbConceptIdSchema, kbJsonSchemas, kbLogEntrySchema, kbRecordFrontmatterSchema, kbSourceSchema, kbVerifiedEventSchema, listPins, loadQmd, matchToDiff, mergedContextBudgets, parseLog, parseMarkdownWithFrontmatter, pinBase, readMergedPins, readPinsLayer, renderIndex, renderIndexLine, renderLogEntry, resolveHeads, resolveHits, resolvePinPath, runKbCli, runKbMcpServer, searchBase, selectDecisions, splitMarkdownFrontmatter, stringifyMarkdownWithFrontmatter, syncInstructions, toHookJson, trace, unpinBase, validateBundle };
1397
+ export { BaseError, CONTEXT_BEGIN, CONTEXT_END, CONTEXT_PROFILES, type ComposeInput, type ComposedRecord, DECISION_TYPE, DEFAULT_LOAD_BUDGET, DEFAULT_PACK_HOPS, DEFAULT_PACK_MAX_NODES, type DecisionInput, type DiffFile, type DiffHunk, type DiffMatch, type ErrorDetails, type ErrorProps, ErrorTypes, Fault, INDEX_FILE, KB_COMMANDS, KB_COMMANDS_BY_NAME, KB_CONCEPT_ID_PATTERN, KB_CONFIDENCES, KB_DIR, KB_EDGE_KINDS, KB_MATERIALITIES, KB_RECORD_STATUSES, KB_RECORD_TYPES, KB_SLUG_PATTERN, type KbActorStamp, type KbAdjudicated, type KbAnchor, KbBaseFrozenError, type KbCommand, type KbCommandContext, type KbContextBudgets, type KbContextOptions, type KbContextResult, type KbEdgeKind, KbInvalidConceptIdError, type KbLoadResult, type KbLogEntry, type KbLogReadResult, type KbLogger, type KbMergedPin, type KbMergedPins, type KbNeighbour, KbPackBudgetExceededError, type KbPackOptions, type KbPackResult, type KbPackedRecord, type KbPin, type KbPinLayer, type KbPinOptions, type KbPinResult, type KbPinStatus, KbPinsMalformedError, type KbPinsManifest, type KbRecord, KbRecordAlreadyExistsError, type KbRecordFrontmatter, KbRecordNotFoundError, type KbRecordStatus, type KbRecordType, type KbRecordTypeSpec, type KbSearchLogger, KbSelfVerificationError, type KbSource, type KbStanding, KbStore, type KbSupersededStub, type KbSyncResult, type KbTraceEdge, type KbTraceOptions, type KbTraceStep, type KbValidationProblem, type KbVerifiedEvent, type KbWarning, KbWriteConflictError, type KbWriteInput, LOG_FILE, type MatchOptions, NO_DECISION_SLUG, PINS_FILE, PINS_LOCAL_FILE, PIN_LAYERS, type QmdModule, RECORD_TYPES, SEARCH_INDEX_FILE, type SearchHit, type SearchOptions, type SymbolRange, TRACE_EDGES, adjudicate, assertBaseNotFrozen, buildContext, composeDecisionRecord, composeInputSchema, composeNoDecisionRecord, composeRecord, contextProfileBudgets, createKbMcpServer, decisionInputSchema, edgeNeighbours, indexIsStale, isKbRecordType, isNoDecisionRecord, kbActorStampSchema, kbAnchorSchema, kbConceptIdSchema, kbJsonSchemas, kbLogEntrySchema, kbRecordFrontmatterSchema, kbSourceSchema, kbVerifiedEventSchema, listPins, loadQmd, matchToDiff, mergedContextBudgets, neighbours, pack, parseLog, parseMarkdownWithFrontmatter, pinBase, readMergedPins, readPinsLayer, renderIndex, renderIndexLine, renderLogEntry, resolveHeads, resolveHits, resolvePinPath, runKbCli, runKbMcpServer, searchBase, selectDecisions, splitMarkdownFrontmatter, stringifyMarkdownWithFrontmatter, syncInstructions, toHookJson, trace, unpinBase, validateBundle };
package/dist/index.d.ts CHANGED
@@ -225,12 +225,11 @@ declare function resolveHeads(from: KbRecord, byId: Map<string, KbRecord>): {
225
225
  };
226
226
 
227
227
  /**
228
- * Edges a trace may follow.
229
- *
230
- * Two more are conceivable and absent: `strauss_answered` carries no target id,
231
- * so a question's resolution lives in its own body rather than in another
232
- * record; and following OKF's body markdown links would need a markdown AST
233
- * pass this package does not yet do.
228
+ * Edges a trace may follow — the shared kb-edges.ts definitions, minus
229
+ * `body-link`: body links can reach most of a bundle from anywhere, which
230
+ * suits a bounded pack but floods a timeline. Also absent by design:
231
+ * `strauss_answered` carries no target id, so a question's resolution lives
232
+ * in its own body rather than in another record.
234
233
  */
235
234
  declare const TRACE_EDGES: readonly ["supersession", "anchor", "source"];
236
235
  type KbTraceEdge = (typeof TRACE_EDGES)[number];
@@ -260,6 +259,58 @@ type KbTraceOptions = {
260
259
  */
261
260
  declare function trace(seedId: string, bundle: KbRecord[], options?: KbTraceOptions): KbTraceStep[];
262
261
 
262
+ declare const DEFAULT_PACK_HOPS = 2;
263
+ declare const DEFAULT_PACK_MAX_NODES = 20;
264
+ type KbPackOptions = {
265
+ /** How far from the root the walk may reach. */
266
+ hops?: number;
267
+ /** How many records the pack may hold, root included. */
268
+ maxNodes?: number;
269
+ /** Approximate token ceiling over what is actually emitted. */
270
+ budgetTokens?: number;
271
+ };
272
+ /** One record as the pack emits it — the same mapping `kb_load` hands back. */
273
+ type KbPackedRecord = {
274
+ conceptId: string;
275
+ title: string | null;
276
+ standing: KbStanding;
277
+ supersededBy: string[];
278
+ warnings: KbWarning[];
279
+ anchors: KbAnchor[];
280
+ body: string;
281
+ };
282
+ /**
283
+ * Deliberately timestamp-free: the same bundle and root must produce the same
284
+ * bytes on every run, so a caller can diff two packs and trust that a changed
285
+ * byte means changed knowledge.
286
+ */
287
+ type KbPackResult = {
288
+ root: string;
289
+ records: KbPackedRecord[];
290
+ /** Named only, exactly as `load` stubs them. Bodies reachable via trace. */
291
+ superseded: KbSupersededStub[];
292
+ /** Every reachable record the hop and node limits cut, never summarized. */
293
+ excluded: string[];
294
+ recordCount: number;
295
+ tokensLoaded: number;
296
+ budgetTokens: number;
297
+ };
298
+ /**
299
+ * A bounded, progressively disclosed neighbourhood around one record.
300
+ *
301
+ * Where `load` hands over a whole base and `trace` a timeline, a pack is the
302
+ * subgraph a reader needs to act near one record: everything within `hops`
303
+ * of the root, cut to `maxNodes` by ranking, with every cut id listed —
304
+ * a named gap is knowable, a silent one is not.
305
+ *
306
+ * Adjudication runs against the whole bundle, not the reached slice, so a
307
+ * record's standing cannot depend on whether its replacement happened to be
308
+ * within reach. Superseded records become the same stubs `load` emits, costed
309
+ * as stubs, and the budget is a refusal rather than a truncation: a partial
310
+ * pack looks complete to its reader.
311
+ */
312
+ declare function pack(bundle: KbRecord[], rootId: string, options?: KbPackOptions): KbPackResult;
313
+
263
314
  declare const LOG_FILE = "log.jsonl";
264
315
  declare const kbLogEntrySchema: z.ZodObject<{
265
316
  at: z.ZodString;
@@ -310,6 +361,8 @@ type KbLogger = {
310
361
  info?(entry: Record<string, unknown>): void;
311
362
  warn?(entry: Record<string, unknown>): void;
312
363
  };
364
+ /** Roughly an eighth of a large context window — generous, and overridable. */
365
+ declare const DEFAULT_LOAD_BUDGET = 25000;
313
366
  /**
314
367
  * A superseded record, named but not spelled out.
315
368
  *
@@ -467,6 +520,8 @@ declare class KbStore {
467
520
  }): Promise<KbLoadResult>;
468
521
  /** How a position was arrived at, as a timeline. See `trace.ts`. */
469
522
  trace(bundlePath: string, seedId: string, options?: KbTraceOptions): Promise<KbTraceStep[]>;
523
+ /** A bounded neighbourhood around one record. See `pack.ts`. */
524
+ pack(bundlePath: string, rootId: string, options?: KbPackOptions): Promise<KbPackResult>;
470
525
  /**
471
526
  * The stored index, rebuilt if it disagrees with the records.
472
527
  *
@@ -545,6 +600,7 @@ declare enum Fault {
545
600
  declare enum ErrorTypes {
546
601
  KbRecordAlreadyExists = "KbRecordAlreadyExists",
547
602
  KbInvalidConceptId = "KbInvalidConceptId",
603
+ KbPackBudgetExceeded = "KbPackBudgetExceeded",
548
604
  KbRecordNotFound = "KbRecordNotFound",
549
605
  KbSelfVerification = "KbSelfVerification",
550
606
  KbWriteConflict = "KbWriteConflict"
@@ -596,6 +652,20 @@ declare class KbSelfVerificationError extends BaseError {
596
652
  readonly generatedBy: string;
597
653
  constructor(conceptId: string, actor: string, generatedBy: string);
598
654
  }
655
+ /**
656
+ * A pack that will not fit its token budget. Refusal, not truncation: a
657
+ * partial pack is indistinguishable from a complete one, so the caller gets
658
+ * the full picture — how many records, how many tokens, and every id the
659
+ * walk's own limits already cut — and decides whether to raise the budget or
660
+ * tighten the walk.
661
+ */
662
+ declare class KbPackBudgetExceededError extends BaseError {
663
+ readonly recordCount: number;
664
+ readonly approxTokens: number;
665
+ readonly budgetTokens: number;
666
+ readonly excluded: string[];
667
+ constructor(recordCount: number, approxTokens: number, budgetTokens: number, excluded: string[]);
668
+ }
599
669
  declare class KbInvalidConceptIdError extends BaseError {
600
670
  constructor(message: string, details: Record<string, string>);
601
671
  }
@@ -1018,6 +1088,33 @@ type MatchOptions = {
1018
1088
  };
1019
1089
  declare function matchToDiff(files: DiffFile[], records: KbRecord[], options?: MatchOptions): DiffMatch[];
1020
1090
 
1091
+ /**
1092
+ * The edges between records in one bundle, defined once.
1093
+ *
1094
+ * Both walks — `trace` and `pack` — consume this module, so they cannot drift
1095
+ * into disagreeing about what makes two records neighbours, and a diagnostic
1096
+ * pass over the graph can reuse the same definition.
1097
+ *
1098
+ * There is no separate `related` kind: compose.ts renders `relatedConceptIds`
1099
+ * as body links (`Relates to [id](id.md).`), so in stored form a related edge
1100
+ * IS a body link, and a distinct kind would count the same markdown twice.
1101
+ */
1102
+ declare const KB_EDGE_KINDS: readonly ["body-link", "supersession", "anchor", "source"];
1103
+ type KbEdgeKind = (typeof KB_EDGE_KINDS)[number];
1104
+ type KbNeighbour = {
1105
+ record: KbRecord;
1106
+ /** Every edge kind that connects it to the record asked about. */
1107
+ via: KbEdgeKind[];
1108
+ };
1109
+ /**
1110
+ * Every record `from` touches, each carrying the full set of edge kinds that
1111
+ * connect the pair. Order is deterministic: bundle order per kind, kinds in
1112
+ * the order given.
1113
+ */
1114
+ declare function neighbours(from: KbRecord, bundle: KbRecord[], kinds?: readonly KbEdgeKind[]): KbNeighbour[];
1115
+ /** The records one edge kind connects `from` to, in bundle order. */
1116
+ declare function edgeNeighbours(from: KbRecord, bundle: KbRecord[], kind: KbEdgeKind): KbRecord[];
1117
+
1021
1118
  type KbValidationProblem = {
1022
1119
  check: string;
1023
1120
  conceptId: string;
@@ -1297,4 +1394,4 @@ declare function parseMarkdownWithFrontmatter<S extends z.ZodType>(text: string,
1297
1394
  frontmatter: ReturnType<S["safeParse"]>;
1298
1395
  };
1299
1396
 
1300
- export { BaseError, CONTEXT_BEGIN, CONTEXT_END, CONTEXT_PROFILES, type ComposeInput, type ComposedRecord, DECISION_TYPE, type DecisionInput, type DiffFile, type DiffHunk, type DiffMatch, type ErrorDetails, type ErrorProps, ErrorTypes, Fault, INDEX_FILE, KB_COMMANDS, KB_COMMANDS_BY_NAME, KB_CONCEPT_ID_PATTERN, KB_CONFIDENCES, KB_DIR, KB_MATERIALITIES, KB_RECORD_STATUSES, KB_RECORD_TYPES, KB_SLUG_PATTERN, type KbActorStamp, type KbAdjudicated, type KbAnchor, KbBaseFrozenError, type KbCommand, type KbCommandContext, type KbContextBudgets, type KbContextOptions, type KbContextResult, KbInvalidConceptIdError, type KbLoadResult, type KbLogEntry, type KbLogReadResult, type KbLogger, type KbMergedPin, type KbMergedPins, type KbPin, type KbPinLayer, type KbPinOptions, type KbPinResult, type KbPinStatus, KbPinsMalformedError, type KbPinsManifest, type KbRecord, KbRecordAlreadyExistsError, type KbRecordFrontmatter, KbRecordNotFoundError, type KbRecordStatus, type KbRecordType, type KbRecordTypeSpec, type KbSearchLogger, KbSelfVerificationError, type KbSource, type KbStanding, KbStore, type KbSupersededStub, type KbSyncResult, type KbTraceEdge, type KbTraceOptions, type KbTraceStep, type KbValidationProblem, type KbVerifiedEvent, type KbWarning, KbWriteConflictError, type KbWriteInput, LOG_FILE, type MatchOptions, NO_DECISION_SLUG, PINS_FILE, PINS_LOCAL_FILE, PIN_LAYERS, type QmdModule, RECORD_TYPES, SEARCH_INDEX_FILE, type SearchHit, type SearchOptions, type SymbolRange, TRACE_EDGES, adjudicate, assertBaseNotFrozen, buildContext, composeDecisionRecord, composeInputSchema, composeNoDecisionRecord, composeRecord, contextProfileBudgets, createKbMcpServer, decisionInputSchema, indexIsStale, isKbRecordType, isNoDecisionRecord, kbActorStampSchema, kbAnchorSchema, kbConceptIdSchema, kbJsonSchemas, kbLogEntrySchema, kbRecordFrontmatterSchema, kbSourceSchema, kbVerifiedEventSchema, listPins, loadQmd, matchToDiff, mergedContextBudgets, parseLog, parseMarkdownWithFrontmatter, pinBase, readMergedPins, readPinsLayer, renderIndex, renderIndexLine, renderLogEntry, resolveHeads, resolveHits, resolvePinPath, runKbCli, runKbMcpServer, searchBase, selectDecisions, splitMarkdownFrontmatter, stringifyMarkdownWithFrontmatter, syncInstructions, toHookJson, trace, unpinBase, validateBundle };
1397
+ export { BaseError, CONTEXT_BEGIN, CONTEXT_END, CONTEXT_PROFILES, type ComposeInput, type ComposedRecord, DECISION_TYPE, DEFAULT_LOAD_BUDGET, DEFAULT_PACK_HOPS, DEFAULT_PACK_MAX_NODES, type DecisionInput, type DiffFile, type DiffHunk, type DiffMatch, type ErrorDetails, type ErrorProps, ErrorTypes, Fault, INDEX_FILE, KB_COMMANDS, KB_COMMANDS_BY_NAME, KB_CONCEPT_ID_PATTERN, KB_CONFIDENCES, KB_DIR, KB_EDGE_KINDS, KB_MATERIALITIES, KB_RECORD_STATUSES, KB_RECORD_TYPES, KB_SLUG_PATTERN, type KbActorStamp, type KbAdjudicated, type KbAnchor, KbBaseFrozenError, type KbCommand, type KbCommandContext, type KbContextBudgets, type KbContextOptions, type KbContextResult, type KbEdgeKind, KbInvalidConceptIdError, type KbLoadResult, type KbLogEntry, type KbLogReadResult, type KbLogger, type KbMergedPin, type KbMergedPins, type KbNeighbour, KbPackBudgetExceededError, type KbPackOptions, type KbPackResult, type KbPackedRecord, type KbPin, type KbPinLayer, type KbPinOptions, type KbPinResult, type KbPinStatus, KbPinsMalformedError, type KbPinsManifest, type KbRecord, KbRecordAlreadyExistsError, type KbRecordFrontmatter, KbRecordNotFoundError, type KbRecordStatus, type KbRecordType, type KbRecordTypeSpec, type KbSearchLogger, KbSelfVerificationError, type KbSource, type KbStanding, KbStore, type KbSupersededStub, type KbSyncResult, type KbTraceEdge, type KbTraceOptions, type KbTraceStep, type KbValidationProblem, type KbVerifiedEvent, type KbWarning, KbWriteConflictError, type KbWriteInput, LOG_FILE, type MatchOptions, NO_DECISION_SLUG, PINS_FILE, PINS_LOCAL_FILE, PIN_LAYERS, type QmdModule, RECORD_TYPES, SEARCH_INDEX_FILE, type SearchHit, type SearchOptions, type SymbolRange, TRACE_EDGES, adjudicate, assertBaseNotFrozen, buildContext, composeDecisionRecord, composeInputSchema, composeNoDecisionRecord, composeRecord, contextProfileBudgets, createKbMcpServer, decisionInputSchema, edgeNeighbours, indexIsStale, isKbRecordType, isNoDecisionRecord, kbActorStampSchema, kbAnchorSchema, kbConceptIdSchema, kbJsonSchemas, kbLogEntrySchema, kbRecordFrontmatterSchema, kbSourceSchema, kbVerifiedEventSchema, listPins, loadQmd, matchToDiff, mergedContextBudgets, neighbours, pack, parseLog, parseMarkdownWithFrontmatter, pinBase, readMergedPins, readPinsLayer, renderIndex, renderIndexLine, renderLogEntry, resolveHeads, resolveHits, resolvePinPath, runKbCli, runKbMcpServer, searchBase, selectDecisions, splitMarkdownFrontmatter, stringifyMarkdownWithFrontmatter, syncInstructions, toHookJson, trace, unpinBase, validateBundle };
package/dist/index.js CHANGED
@@ -1,16 +1,19 @@
1
1
  import {
2
2
  runKbCli
3
- } from "./chunk-V7TRZ2ER.js";
3
+ } from "./chunk-GKCG4P3L.js";
4
4
  import {
5
5
  createKbMcpServer,
6
6
  runKbMcpServer
7
- } from "./chunk-BVF7X5VO.js";
7
+ } from "./chunk-LCQKARFK.js";
8
8
  import {
9
9
  BaseError,
10
10
  CONTEXT_BEGIN,
11
11
  CONTEXT_END,
12
12
  CONTEXT_PROFILES,
13
13
  DECISION_TYPE,
14
+ DEFAULT_LOAD_BUDGET,
15
+ DEFAULT_PACK_HOPS,
16
+ DEFAULT_PACK_MAX_NODES,
14
17
  ErrorTypes,
15
18
  Fault,
16
19
  INDEX_FILE,
@@ -19,12 +22,14 @@ import {
19
22
  KB_CONCEPT_ID_PATTERN,
20
23
  KB_CONFIDENCES,
21
24
  KB_DIR,
25
+ KB_EDGE_KINDS,
22
26
  KB_MATERIALITIES,
23
27
  KB_RECORD_STATUSES,
24
28
  KB_RECORD_TYPES,
25
29
  KB_SLUG_PATTERN,
26
30
  KbBaseFrozenError,
27
31
  KbInvalidConceptIdError,
32
+ KbPackBudgetExceededError,
28
33
  KbPinsMalformedError,
29
34
  KbRecordAlreadyExistsError,
30
35
  KbRecordNotFoundError,
@@ -48,6 +53,7 @@ import {
48
53
  composeRecord,
49
54
  contextProfileBudgets,
50
55
  decisionInputSchema,
56
+ edgeNeighbours,
51
57
  indexIsStale,
52
58
  isKbRecordType,
53
59
  isNoDecisionRecord,
@@ -62,6 +68,8 @@ import {
62
68
  listPins,
63
69
  loadQmd,
64
70
  mergedContextBudgets,
71
+ neighbours,
72
+ pack,
65
73
  parseLog,
66
74
  parseMarkdownWithFrontmatter,
67
75
  pinBase,
@@ -82,7 +90,7 @@ import {
82
90
  trace,
83
91
  unpinBase,
84
92
  validateBundle
85
- } from "./chunk-PNSRTKYN.js";
93
+ } from "./chunk-GKUQOJEK.js";
86
94
 
87
95
  // src/match-diff.ts
88
96
  function matchToDiff(files, records, options = {}) {
@@ -169,6 +177,9 @@ export {
169
177
  CONTEXT_END,
170
178
  CONTEXT_PROFILES,
171
179
  DECISION_TYPE,
180
+ DEFAULT_LOAD_BUDGET,
181
+ DEFAULT_PACK_HOPS,
182
+ DEFAULT_PACK_MAX_NODES,
172
183
  ErrorTypes,
173
184
  Fault,
174
185
  INDEX_FILE,
@@ -177,12 +188,14 @@ export {
177
188
  KB_CONCEPT_ID_PATTERN,
178
189
  KB_CONFIDENCES,
179
190
  KB_DIR,
191
+ KB_EDGE_KINDS,
180
192
  KB_MATERIALITIES,
181
193
  KB_RECORD_STATUSES,
182
194
  KB_RECORD_TYPES,
183
195
  KB_SLUG_PATTERN,
184
196
  KbBaseFrozenError,
185
197
  KbInvalidConceptIdError,
198
+ KbPackBudgetExceededError,
186
199
  KbPinsMalformedError,
187
200
  KbRecordAlreadyExistsError,
188
201
  KbRecordNotFoundError,
@@ -207,6 +220,7 @@ export {
207
220
  contextProfileBudgets,
208
221
  createKbMcpServer,
209
222
  decisionInputSchema,
223
+ edgeNeighbours,
210
224
  indexIsStale,
211
225
  isKbRecordType,
212
226
  isNoDecisionRecord,
@@ -222,6 +236,8 @@ export {
222
236
  loadQmd,
223
237
  matchToDiff,
224
238
  mergedContextBudgets,
239
+ neighbours,
240
+ pack,
225
241
  parseLog,
226
242
  parseMarkdownWithFrontmatter,
227
243
  pinBase,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/match-diff.ts"],"sourcesContent":["import { adjudicate, type KbAdjudicated } from \"./adjudicate.js\";\nimport type { KbAnchor, KbRecord } from \"./kb-record.schema.js\";\n\n/**\n * Which records apply to which part of a change.\n *\n * Takes a structural description of a diff rather than a patch, so this package\n * carries no diff parser: callers already have one, and a knowledge base has no\n * business preferring a particular flavour of unified diff.\n *\n * Deterministic on purpose. Every step here is mechanical — the one judgment,\n * whether a matched record is worth showing a reviewer, is deliberately absent.\n * A model placed here would sit between the reviewer and their diff on every\n * review, to answer a question nobody has yet shown needs asking.\n *\n * Distinct from `load()`, which hands a reader the whole base. That answers\n * \"does anything address this question\"; this answers \"what is attached to this\n * code\", and an anchor is the author's own statement rather than an inference\n * from one. A reader guessing which record relates to a hunk would be guessing\n * at something already written down — and a diff has dozens of hunks, which is\n * dozens of reader calls against microseconds of matching. Where they compose:\n * this narrows a hunk to a few records, and a reader asked to explain them gets\n * those, not the base.\n */\nexport type DiffHunk = {\n /** 1-based, inclusive, in the file's post-change line numbering. */\n startLine: number;\n endLine: number;\n};\n\nexport type DiffFile = {\n /** Repo-relative, matching how anchors are written. */\n filePath: string;\n hunks: DiffHunk[];\n};\n\n/**\n * A symbol resolved to lines. Supplied by whatever the caller uses to index\n * symbols; absence is tolerated — see `place()`.\n */\nexport type SymbolRange = {\n file: string;\n symbol: string;\n startLine: number;\n endLine: number;\n};\n\nexport type DiffMatch = {\n filePath: string;\n hunk: DiffHunk;\n /** Current records first — what still holds should be read before what does not. */\n records: KbAdjudicated[];\n /**\n * `symbol` when every record here was placed by a resolved symbol range,\n * `file` when at least one fell back to the whole file. Reported rather than\n * hidden: a caller showing a file-level match as though it were pinned to\n * these lines is claiming a precision it does not have.\n */\n precision: \"symbol\" | \"file\";\n};\n\nexport type MatchOptions = {\n /** Without these, symbol anchors degrade to file level rather than vanishing. */\n symbolRanges?: SymbolRange[];\n now?: Date;\n};\n\nexport function matchToDiff(\n files: DiffFile[],\n records: KbRecord[],\n options: MatchOptions = {},\n): DiffMatch[] {\n const ranges = indexRanges(options.symbolRanges ?? []);\n const anchored = records.filter(\n (record) => (record.frontmatter.strauss_anchors ?? []).length > 0,\n );\n const matches: DiffMatch[] = [];\n\n for (const file of files) {\n const candidates = anchored\n .map((record) => ({\n record,\n anchors: (record.frontmatter.strauss_anchors ?? []).filter(\n (anchor) => normalize(anchor.file) === normalize(file.filePath),\n ),\n }))\n .filter(({ anchors }) => anchors.length > 0);\n if (!candidates.length) continue;\n\n for (const hunk of file.hunks) {\n const hits: KbRecord[] = [];\n let precision: DiffMatch[\"precision\"] = \"symbol\";\n\n for (const { record, anchors } of candidates) {\n const placement = place(anchors, file.filePath, hunk, ranges);\n if (placement === \"miss\") continue;\n if (placement === \"file\") precision = \"file\";\n hits.push(record);\n }\n\n if (!hits.length) continue;\n matches.push({\n filePath: file.filePath,\n hunk,\n records: order(adjudicate(hits, records, options.now)),\n precision,\n });\n }\n }\n\n return matches;\n}\n\n/**\n * Whether any of a record's anchors puts it on this hunk.\n *\n * An anchor naming only a file is about the whole file, so it lands on every\n * hunk in it. One naming a symbol lands only where that symbol's lines overlap\n * — unless nothing resolved the symbol, in which case it falls back to the file\n * rather than disappearing. A record silently absent because a resolver was\n * unavailable is worse than one shown imprecisely and labelled as such.\n */\nfunction place(\n anchors: KbAnchor[],\n filePath: string,\n hunk: DiffHunk,\n ranges: Map<string, SymbolRange[]>,\n): \"symbol\" | \"file\" | \"miss\" {\n let fallback: \"file\" | \"miss\" = \"miss\";\n\n for (const anchor of anchors) {\n if (!anchor.symbol) return \"file\";\n\n const resolved = ranges.get(key(filePath, anchor.symbol));\n if (!resolved?.length) {\n fallback = \"file\";\n continue;\n }\n if (resolved.some((range) => overlaps(range, hunk))) return \"symbol\";\n }\n\n return fallback;\n}\n\nfunction overlaps(range: SymbolRange, hunk: DiffHunk): boolean {\n return range.startLine <= hunk.endLine && hunk.startLine <= range.endLine;\n}\n\n/** Current before superseded, then oldest first, so an arc reads in order. */\nfunction order(records: KbAdjudicated[]): KbAdjudicated[] {\n const rank: Record<string, number> = {\n current: 0,\n unsettled: 1,\n open: 2,\n superseded: 3,\n rejected: 4,\n };\n return [...records].sort(\n (left, right) =>\n (rank[left.standing] ?? 9) - (rank[right.standing] ?? 9) ||\n (left.record.frontmatter.generated?.at ?? \"\").localeCompare(\n right.record.frontmatter.generated?.at ?? \"\",\n ),\n );\n}\n\nfunction indexRanges(ranges: SymbolRange[]): Map<string, SymbolRange[]> {\n const byKey = new Map<string, SymbolRange[]>();\n for (const range of ranges) {\n const id = key(range.file, range.symbol);\n byKey.set(id, [...(byKey.get(id) ?? []), range]);\n }\n return byKey;\n}\n\nfunction key(file: string, symbol: string): string {\n return `${normalize(file)}#${symbol}`;\n}\n\n/** Anchors are written by hand often enough that `./` shows up. */\nfunction normalize(path: string): string {\n return path.replace(/^\\.\\//, \"\");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEO,SAAS,YACd,OACA,SACA,UAAwB,CAAC,GACZ;AACb,QAAM,SAAS,YAAY,QAAQ,gBAAgB,CAAC,CAAC;AACrD,QAAM,WAAW,QAAQ;AAAA,IACvB,CAAC,YAAY,OAAO,YAAY,mBAAmB,CAAC,GAAG,SAAS;AAAA,EAClE;AACA,QAAM,UAAuB,CAAC;AAE9B,aAAW,QAAQ,OAAO;AACxB,UAAM,aAAa,SAChB,IAAI,CAAC,YAAY;AAAA,MAChB;AAAA,MACA,UAAU,OAAO,YAAY,mBAAmB,CAAC,GAAG;AAAA,QAClD,CAAC,WAAW,UAAU,OAAO,IAAI,MAAM,UAAU,KAAK,QAAQ;AAAA,MAChE;AAAA,IACF,EAAE,EACD,OAAO,CAAC,EAAE,QAAQ,MAAM,QAAQ,SAAS,CAAC;AAC7C,QAAI,CAAC,WAAW,OAAQ;AAExB,eAAW,QAAQ,KAAK,OAAO;AAC7B,YAAM,OAAmB,CAAC;AAC1B,UAAI,YAAoC;AAExC,iBAAW,EAAE,QAAQ,QAAQ,KAAK,YAAY;AAC5C,cAAM,YAAY,MAAM,SAAS,KAAK,UAAU,MAAM,MAAM;AAC5D,YAAI,cAAc,OAAQ;AAC1B,YAAI,cAAc,OAAQ,aAAY;AACtC,aAAK,KAAK,MAAM;AAAA,MAClB;AAEA,UAAI,CAAC,KAAK,OAAQ;AAClB,cAAQ,KAAK;AAAA,QACX,UAAU,KAAK;AAAA,QACf;AAAA,QACA,SAAS,MAAM,WAAW,MAAM,SAAS,QAAQ,GAAG,CAAC;AAAA,QACrD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAWA,SAAS,MACP,SACA,UACA,MACA,QAC4B;AAC5B,MAAI,WAA4B;AAEhC,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,OAAO,OAAQ,QAAO;AAE3B,UAAM,WAAW,OAAO,IAAI,IAAI,UAAU,OAAO,MAAM,CAAC;AACxD,QAAI,CAAC,UAAU,QAAQ;AACrB,iBAAW;AACX;AAAA,IACF;AACA,QAAI,SAAS,KAAK,CAAC,UAAU,SAAS,OAAO,IAAI,CAAC,EAAG,QAAO;AAAA,EAC9D;AAEA,SAAO;AACT;AAEA,SAAS,SAAS,OAAoB,MAAyB;AAC7D,SAAO,MAAM,aAAa,KAAK,WAAW,KAAK,aAAa,MAAM;AACpE;AAGA,SAAS,MAAM,SAA2C;AACxD,QAAM,OAA+B;AAAA,IACnC,SAAS;AAAA,IACT,WAAW;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ;AACA,SAAO,CAAC,GAAG,OAAO,EAAE;AAAA,IAClB,CAAC,MAAM,WACJ,KAAK,KAAK,QAAQ,KAAK,MAAM,KAAK,MAAM,QAAQ,KAAK,OACrD,KAAK,OAAO,YAAY,WAAW,MAAM,IAAI;AAAA,MAC5C,MAAM,OAAO,YAAY,WAAW,MAAM;AAAA,IAC5C;AAAA,EACJ;AACF;AAEA,SAAS,YAAY,QAAmD;AACtE,QAAM,QAAQ,oBAAI,IAA2B;AAC7C,aAAW,SAAS,QAAQ;AAC1B,UAAM,KAAK,IAAI,MAAM,MAAM,MAAM,MAAM;AACvC,UAAM,IAAI,IAAI,CAAC,GAAI,MAAM,IAAI,EAAE,KAAK,CAAC,GAAI,KAAK,CAAC;AAAA,EACjD;AACA,SAAO;AACT;AAEA,SAAS,IAAI,MAAc,QAAwB;AACjD,SAAO,GAAG,UAAU,IAAI,CAAC,IAAI,MAAM;AACrC;AAGA,SAAS,UAAU,MAAsB;AACvC,SAAO,KAAK,QAAQ,SAAS,EAAE;AACjC;","names":[]}
1
+ {"version":3,"sources":["../src/match-diff.ts"],"sourcesContent":["import { adjudicate, type KbAdjudicated } from \"./adjudicate.js\";\nimport type { KbAnchor, KbRecord } from \"./kb-record.schema.js\";\n\n/**\n * Which records apply to which part of a change.\n *\n * Takes a structural description of a diff rather than a patch, so this package\n * carries no diff parser: callers already have one, and a knowledge base has no\n * business preferring a particular flavour of unified diff.\n *\n * Deterministic on purpose. Every step here is mechanical — the one judgment,\n * whether a matched record is worth showing a reviewer, is deliberately absent.\n * A model placed here would sit between the reviewer and their diff on every\n * review, to answer a question nobody has yet shown needs asking.\n *\n * Distinct from `load()`, which hands a reader the whole base. That answers\n * \"does anything address this question\"; this answers \"what is attached to this\n * code\", and an anchor is the author's own statement rather than an inference\n * from one. A reader guessing which record relates to a hunk would be guessing\n * at something already written down — and a diff has dozens of hunks, which is\n * dozens of reader calls against microseconds of matching. Where they compose:\n * this narrows a hunk to a few records, and a reader asked to explain them gets\n * those, not the base.\n */\nexport type DiffHunk = {\n /** 1-based, inclusive, in the file's post-change line numbering. */\n startLine: number;\n endLine: number;\n};\n\nexport type DiffFile = {\n /** Repo-relative, matching how anchors are written. */\n filePath: string;\n hunks: DiffHunk[];\n};\n\n/**\n * A symbol resolved to lines. Supplied by whatever the caller uses to index\n * symbols; absence is tolerated — see `place()`.\n */\nexport type SymbolRange = {\n file: string;\n symbol: string;\n startLine: number;\n endLine: number;\n};\n\nexport type DiffMatch = {\n filePath: string;\n hunk: DiffHunk;\n /** Current records first — what still holds should be read before what does not. */\n records: KbAdjudicated[];\n /**\n * `symbol` when every record here was placed by a resolved symbol range,\n * `file` when at least one fell back to the whole file. Reported rather than\n * hidden: a caller showing a file-level match as though it were pinned to\n * these lines is claiming a precision it does not have.\n */\n precision: \"symbol\" | \"file\";\n};\n\nexport type MatchOptions = {\n /** Without these, symbol anchors degrade to file level rather than vanishing. */\n symbolRanges?: SymbolRange[];\n now?: Date;\n};\n\nexport function matchToDiff(\n files: DiffFile[],\n records: KbRecord[],\n options: MatchOptions = {},\n): DiffMatch[] {\n const ranges = indexRanges(options.symbolRanges ?? []);\n const anchored = records.filter(\n (record) => (record.frontmatter.strauss_anchors ?? []).length > 0,\n );\n const matches: DiffMatch[] = [];\n\n for (const file of files) {\n const candidates = anchored\n .map((record) => ({\n record,\n anchors: (record.frontmatter.strauss_anchors ?? []).filter(\n (anchor) => normalize(anchor.file) === normalize(file.filePath),\n ),\n }))\n .filter(({ anchors }) => anchors.length > 0);\n if (!candidates.length) continue;\n\n for (const hunk of file.hunks) {\n const hits: KbRecord[] = [];\n let precision: DiffMatch[\"precision\"] = \"symbol\";\n\n for (const { record, anchors } of candidates) {\n const placement = place(anchors, file.filePath, hunk, ranges);\n if (placement === \"miss\") continue;\n if (placement === \"file\") precision = \"file\";\n hits.push(record);\n }\n\n if (!hits.length) continue;\n matches.push({\n filePath: file.filePath,\n hunk,\n records: order(adjudicate(hits, records, options.now)),\n precision,\n });\n }\n }\n\n return matches;\n}\n\n/**\n * Whether any of a record's anchors puts it on this hunk.\n *\n * An anchor naming only a file is about the whole file, so it lands on every\n * hunk in it. One naming a symbol lands only where that symbol's lines overlap\n * — unless nothing resolved the symbol, in which case it falls back to the file\n * rather than disappearing. A record silently absent because a resolver was\n * unavailable is worse than one shown imprecisely and labelled as such.\n */\nfunction place(\n anchors: KbAnchor[],\n filePath: string,\n hunk: DiffHunk,\n ranges: Map<string, SymbolRange[]>,\n): \"symbol\" | \"file\" | \"miss\" {\n let fallback: \"file\" | \"miss\" = \"miss\";\n\n for (const anchor of anchors) {\n if (!anchor.symbol) return \"file\";\n\n const resolved = ranges.get(key(filePath, anchor.symbol));\n if (!resolved?.length) {\n fallback = \"file\";\n continue;\n }\n if (resolved.some((range) => overlaps(range, hunk))) return \"symbol\";\n }\n\n return fallback;\n}\n\nfunction overlaps(range: SymbolRange, hunk: DiffHunk): boolean {\n return range.startLine <= hunk.endLine && hunk.startLine <= range.endLine;\n}\n\n/** Current before superseded, then oldest first, so an arc reads in order. */\nfunction order(records: KbAdjudicated[]): KbAdjudicated[] {\n const rank: Record<string, number> = {\n current: 0,\n unsettled: 1,\n open: 2,\n superseded: 3,\n rejected: 4,\n };\n return [...records].sort(\n (left, right) =>\n (rank[left.standing] ?? 9) - (rank[right.standing] ?? 9) ||\n (left.record.frontmatter.generated?.at ?? \"\").localeCompare(\n right.record.frontmatter.generated?.at ?? \"\",\n ),\n );\n}\n\nfunction indexRanges(ranges: SymbolRange[]): Map<string, SymbolRange[]> {\n const byKey = new Map<string, SymbolRange[]>();\n for (const range of ranges) {\n const id = key(range.file, range.symbol);\n byKey.set(id, [...(byKey.get(id) ?? []), range]);\n }\n return byKey;\n}\n\nfunction key(file: string, symbol: string): string {\n return `${normalize(file)}#${symbol}`;\n}\n\n/** Anchors are written by hand often enough that `./` shows up. */\nfunction normalize(path: string): string {\n return path.replace(/^\\.\\//, \"\");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEO,SAAS,YACd,OACA,SACA,UAAwB,CAAC,GACZ;AACb,QAAM,SAAS,YAAY,QAAQ,gBAAgB,CAAC,CAAC;AACrD,QAAM,WAAW,QAAQ;AAAA,IACvB,CAAC,YAAY,OAAO,YAAY,mBAAmB,CAAC,GAAG,SAAS;AAAA,EAClE;AACA,QAAM,UAAuB,CAAC;AAE9B,aAAW,QAAQ,OAAO;AACxB,UAAM,aAAa,SAChB,IAAI,CAAC,YAAY;AAAA,MAChB;AAAA,MACA,UAAU,OAAO,YAAY,mBAAmB,CAAC,GAAG;AAAA,QAClD,CAAC,WAAW,UAAU,OAAO,IAAI,MAAM,UAAU,KAAK,QAAQ;AAAA,MAChE;AAAA,IACF,EAAE,EACD,OAAO,CAAC,EAAE,QAAQ,MAAM,QAAQ,SAAS,CAAC;AAC7C,QAAI,CAAC,WAAW,OAAQ;AAExB,eAAW,QAAQ,KAAK,OAAO;AAC7B,YAAM,OAAmB,CAAC;AAC1B,UAAI,YAAoC;AAExC,iBAAW,EAAE,QAAQ,QAAQ,KAAK,YAAY;AAC5C,cAAM,YAAY,MAAM,SAAS,KAAK,UAAU,MAAM,MAAM;AAC5D,YAAI,cAAc,OAAQ;AAC1B,YAAI,cAAc,OAAQ,aAAY;AACtC,aAAK,KAAK,MAAM;AAAA,MAClB;AAEA,UAAI,CAAC,KAAK,OAAQ;AAClB,cAAQ,KAAK;AAAA,QACX,UAAU,KAAK;AAAA,QACf;AAAA,QACA,SAAS,MAAM,WAAW,MAAM,SAAS,QAAQ,GAAG,CAAC;AAAA,QACrD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAWA,SAAS,MACP,SACA,UACA,MACA,QAC4B;AAC5B,MAAI,WAA4B;AAEhC,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,OAAO,OAAQ,QAAO;AAE3B,UAAM,WAAW,OAAO,IAAI,IAAI,UAAU,OAAO,MAAM,CAAC;AACxD,QAAI,CAAC,UAAU,QAAQ;AACrB,iBAAW;AACX;AAAA,IACF;AACA,QAAI,SAAS,KAAK,CAAC,UAAU,SAAS,OAAO,IAAI,CAAC,EAAG,QAAO;AAAA,EAC9D;AAEA,SAAO;AACT;AAEA,SAAS,SAAS,OAAoB,MAAyB;AAC7D,SAAO,MAAM,aAAa,KAAK,WAAW,KAAK,aAAa,MAAM;AACpE;AAGA,SAAS,MAAM,SAA2C;AACxD,QAAM,OAA+B;AAAA,IACnC,SAAS;AAAA,IACT,WAAW;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ;AACA,SAAO,CAAC,GAAG,OAAO,EAAE;AAAA,IAClB,CAAC,MAAM,WACJ,KAAK,KAAK,QAAQ,KAAK,MAAM,KAAK,MAAM,QAAQ,KAAK,OACrD,KAAK,OAAO,YAAY,WAAW,MAAM,IAAI;AAAA,MAC5C,MAAM,OAAO,YAAY,WAAW,MAAM;AAAA,IAC5C;AAAA,EACJ;AACF;AAEA,SAAS,YAAY,QAAmD;AACtE,QAAM,QAAQ,oBAAI,IAA2B;AAC7C,aAAW,SAAS,QAAQ;AAC1B,UAAM,KAAK,IAAI,MAAM,MAAM,MAAM,MAAM;AACvC,UAAM,IAAI,IAAI,CAAC,GAAI,MAAM,IAAI,EAAE,KAAK,CAAC,GAAI,KAAK,CAAC;AAAA,EACjD;AACA,SAAO;AACT;AAEA,SAAS,IAAI,MAAc,QAAwB;AACjD,SAAO,GAAG,UAAU,IAAI,CAAC,IAAI,MAAM;AACrC;AAGA,SAAS,UAAU,MAAsB;AACvC,SAAO,KAAK,QAAQ,SAAS,EAAE;AACjC;","names":[]}