foottrace 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -2
  3. package/dist/esm/index.d.ts +27 -0
  4. package/dist/esm/index.js +12 -0
  5. package/dist/esm/lib/capture/freeze.d.ts +91 -0
  6. package/dist/esm/lib/capture/freeze.js +313 -0
  7. package/dist/esm/lib/capture/ownData.d.ts +18 -0
  8. package/dist/esm/lib/capture/ownData.js +35 -0
  9. package/dist/esm/lib/capture/valueKinds.d.ts +57 -0
  10. package/dist/esm/lib/capture/valueKinds.js +141 -0
  11. package/dist/esm/lib/ids/runtimeStageId.d.ts +173 -0
  12. package/dist/esm/lib/ids/runtimeStageId.js +193 -0
  13. package/dist/esm/lib/memory/EventLog.d.ts +105 -0
  14. package/dist/esm/lib/memory/EventLog.js +147 -0
  15. package/dist/esm/lib/memory/RecordFrame.d.ts +232 -0
  16. package/dist/esm/lib/memory/RecordFrame.js +295 -0
  17. package/dist/esm/lib/memory/SharedMemory.d.ts +46 -0
  18. package/dist/esm/lib/memory/SharedMemory.js +87 -0
  19. package/dist/esm/lib/memory/TransactionBuffer.d.ts +521 -0
  20. package/dist/esm/lib/memory/TransactionBuffer.js +946 -0
  21. package/dist/esm/lib/memory/admission.d.ts +81 -0
  22. package/dist/esm/lib/memory/admission.js +152 -0
  23. package/dist/esm/lib/memory/backtrack.d.ts +286 -0
  24. package/dist/esm/lib/memory/backtrack.js +456 -0
  25. package/dist/esm/lib/memory/commitLogUtils.d.ts +217 -0
  26. package/dist/esm/lib/memory/commitLogUtils.js +383 -0
  27. package/dist/esm/lib/memory/deltaEncoding.d.ts +66 -0
  28. package/dist/esm/lib/memory/deltaEncoding.js +121 -0
  29. package/dist/esm/lib/memory/equality.d.ts +93 -0
  30. package/dist/esm/lib/memory/equality.js +261 -0
  31. package/dist/esm/lib/memory/eventPosition.d.ts +18 -0
  32. package/dist/esm/lib/memory/eventPosition.js +1 -0
  33. package/dist/esm/lib/memory/honesty.d.ts +93 -0
  34. package/dist/esm/lib/memory/honesty.js +91 -0
  35. package/dist/esm/lib/memory/keyPaths.d.ts +125 -0
  36. package/dist/esm/lib/memory/keyPaths.js +226 -0
  37. package/dist/esm/lib/memory/logModel.d.ts +76 -0
  38. package/dist/esm/lib/memory/logModel.js +363 -0
  39. package/dist/esm/lib/memory/merge.d.ts +26 -0
  40. package/dist/esm/lib/memory/merge.js +51 -0
  41. package/dist/esm/lib/memory/pathOps.d.ts +118 -0
  42. package/dist/esm/lib/memory/pathOps.js +292 -0
  43. package/dist/esm/lib/memory/paths.d.ts +56 -0
  44. package/dist/esm/lib/memory/paths.js +60 -0
  45. package/dist/esm/lib/memory/placeholders.d.ts +39 -0
  46. package/dist/esm/lib/memory/placeholders.js +39 -0
  47. package/dist/esm/lib/memory/recordCommit.d.ts +45 -0
  48. package/dist/esm/lib/memory/recordCommit.js +60 -0
  49. package/dist/esm/lib/memory/scrub.d.ts +52 -0
  50. package/dist/esm/lib/memory/scrub.js +72 -0
  51. package/dist/esm/lib/memory/types.d.ts +209 -0
  52. package/dist/esm/lib/memory/types.js +9 -0
  53. package/dist/esm/lib/memory/utils.d.ts +73 -0
  54. package/dist/esm/lib/memory/utils.js +150 -0
  55. package/dist/esm/lib/memory/verbs.d.ts +333 -0
  56. package/dist/esm/lib/memory/verbs.js +426 -0
  57. package/dist/esm/lib/recorder/CommitRangeIndex.d.ts +147 -0
  58. package/dist/esm/lib/recorder/CommitRangeIndex.js +206 -0
  59. package/dist/esm/lib/slice/elementProvenance.d.ts +81 -0
  60. package/dist/esm/lib/slice/elementProvenance.js +189 -0
  61. package/dist/esm/lib/slice/forwardSliceForKey.d.ts +88 -0
  62. package/dist/esm/lib/slice/forwardSliceForKey.js +242 -0
  63. package/dist/esm/lib/slice/index.d.ts +26 -0
  64. package/dist/esm/lib/slice/index.js +25 -0
  65. package/dist/esm/lib/slice/keyIndex.d.ts +137 -0
  66. package/dist/esm/lib/slice/keyIndex.js +292 -0
  67. package/dist/esm/lib/slice/keyTimeline.d.ts +49 -0
  68. package/dist/esm/lib/slice/keyTimeline.js +112 -0
  69. package/dist/esm/lib/slice/keysReadSources.d.ts +51 -0
  70. package/dist/esm/lib/slice/keysReadSources.js +94 -0
  71. package/dist/esm/lib/slice/serialize.d.ts +78 -0
  72. package/dist/esm/lib/slice/serialize.js +277 -0
  73. package/dist/esm/lib/slice/sliceForKey.d.ts +40 -0
  74. package/dist/esm/lib/slice/sliceForKey.js +78 -0
  75. package/dist/esm/lib/slice/types.d.ts +567 -0
  76. package/dist/esm/lib/slice/types.js +24 -0
  77. package/dist/esm/lib/time-travel/axis.d.ts +119 -0
  78. package/dist/esm/lib/time-travel/axis.js +178 -0
  79. package/dist/esm/lib/time-travel/bundles.d.ts +77 -0
  80. package/dist/esm/lib/time-travel/bundles.js +135 -0
  81. package/dist/esm/lib/time-travel/chain.d.ts +93 -0
  82. package/dist/esm/lib/time-travel/chain.js +187 -0
  83. package/dist/esm/lib/time-travel/commitStops.d.ts +55 -0
  84. package/dist/esm/lib/time-travel/commitStops.js +201 -0
  85. package/dist/esm/lib/time-travel/index.d.ts +22 -0
  86. package/dist/esm/lib/time-travel/index.js +20 -0
  87. package/dist/esm/lib/time-travel/stateAt.d.ts +102 -0
  88. package/dist/esm/lib/time-travel/stateAt.js +152 -0
  89. package/dist/esm/lib/time-travel/tagStops.d.ts +72 -0
  90. package/dist/esm/lib/time-travel/tagStops.js +101 -0
  91. package/dist/esm/lib/time-travel/timeTravel.d.ts +70 -0
  92. package/dist/esm/lib/time-travel/timeTravel.js +359 -0
  93. package/dist/esm/lib/time-travel/types.d.ts +470 -0
  94. package/dist/esm/lib/time-travel/types.js +22 -0
  95. package/dist/esm/package.json +1 -0
  96. package/dist/esm/paths.d.ts +4 -0
  97. package/dist/esm/paths.js +4 -0
  98. package/dist/esm/write.d.ts +49 -0
  99. package/dist/esm/write.js +47 -0
  100. package/dist/index.js +67 -0
  101. package/dist/lib/capture/freeze.js +319 -0
  102. package/dist/lib/capture/ownData.js +40 -0
  103. package/dist/lib/capture/valueKinds.js +145 -0
  104. package/dist/lib/ids/runtimeStageId.js +209 -0
  105. package/dist/lib/memory/EventLog.js +151 -0
  106. package/dist/lib/memory/RecordFrame.js +299 -0
  107. package/dist/lib/memory/SharedMemory.js +91 -0
  108. package/dist/lib/memory/TransactionBuffer.js +950 -0
  109. package/dist/lib/memory/admission.js +158 -0
  110. package/dist/lib/memory/backtrack.js +462 -0
  111. package/dist/lib/memory/commitLogUtils.js +398 -0
  112. package/dist/lib/memory/deltaEncoding.js +127 -0
  113. package/dist/lib/memory/equality.js +265 -0
  114. package/dist/lib/memory/eventPosition.js +2 -0
  115. package/dist/lib/memory/honesty.js +94 -0
  116. package/dist/lib/memory/keyPaths.js +242 -0
  117. package/dist/lib/memory/logModel.js +371 -0
  118. package/dist/lib/memory/merge.js +55 -0
  119. package/dist/lib/memory/pathOps.js +308 -0
  120. package/dist/lib/memory/paths.js +66 -0
  121. package/dist/lib/memory/placeholders.js +42 -0
  122. package/dist/lib/memory/recordCommit.js +64 -0
  123. package/dist/lib/memory/scrub.js +77 -0
  124. package/dist/lib/memory/types.js +10 -0
  125. package/dist/lib/memory/utils.js +169 -0
  126. package/dist/lib/memory/verbs.js +443 -0
  127. package/dist/lib/recorder/CommitRangeIndex.js +210 -0
  128. package/dist/lib/slice/elementProvenance.js +194 -0
  129. package/dist/lib/slice/forwardSliceForKey.js +246 -0
  130. package/dist/lib/slice/index.js +42 -0
  131. package/dist/lib/slice/keyIndex.js +312 -0
  132. package/dist/lib/slice/keyTimeline.js +116 -0
  133. package/dist/lib/slice/keysReadSources.js +100 -0
  134. package/dist/lib/slice/serialize.js +285 -0
  135. package/dist/lib/slice/sliceForKey.js +82 -0
  136. package/dist/lib/slice/types.js +25 -0
  137. package/dist/lib/time-travel/axis.js +183 -0
  138. package/dist/lib/time-travel/bundles.js +143 -0
  139. package/dist/lib/time-travel/chain.js +192 -0
  140. package/dist/lib/time-travel/commitStops.js +205 -0
  141. package/dist/lib/time-travel/index.js +31 -0
  142. package/dist/lib/time-travel/stateAt.js +159 -0
  143. package/dist/lib/time-travel/tagStops.js +105 -0
  144. package/dist/lib/time-travel/timeTravel.js +363 -0
  145. package/dist/lib/time-travel/types.js +23 -0
  146. package/dist/paths.js +14 -0
  147. package/dist/types/index.d.ts +27 -0
  148. package/dist/types/lib/capture/freeze.d.ts +91 -0
  149. package/dist/types/lib/capture/ownData.d.ts +18 -0
  150. package/dist/types/lib/capture/valueKinds.d.ts +57 -0
  151. package/dist/types/lib/ids/runtimeStageId.d.ts +173 -0
  152. package/dist/types/lib/memory/EventLog.d.ts +105 -0
  153. package/dist/types/lib/memory/RecordFrame.d.ts +232 -0
  154. package/dist/types/lib/memory/SharedMemory.d.ts +46 -0
  155. package/dist/types/lib/memory/TransactionBuffer.d.ts +521 -0
  156. package/dist/types/lib/memory/admission.d.ts +81 -0
  157. package/dist/types/lib/memory/backtrack.d.ts +286 -0
  158. package/dist/types/lib/memory/commitLogUtils.d.ts +217 -0
  159. package/dist/types/lib/memory/deltaEncoding.d.ts +66 -0
  160. package/dist/types/lib/memory/equality.d.ts +93 -0
  161. package/dist/types/lib/memory/eventPosition.d.ts +18 -0
  162. package/dist/types/lib/memory/honesty.d.ts +93 -0
  163. package/dist/types/lib/memory/keyPaths.d.ts +125 -0
  164. package/dist/types/lib/memory/logModel.d.ts +76 -0
  165. package/dist/types/lib/memory/merge.d.ts +26 -0
  166. package/dist/types/lib/memory/pathOps.d.ts +118 -0
  167. package/dist/types/lib/memory/paths.d.ts +56 -0
  168. package/dist/types/lib/memory/placeholders.d.ts +39 -0
  169. package/dist/types/lib/memory/recordCommit.d.ts +45 -0
  170. package/dist/types/lib/memory/scrub.d.ts +52 -0
  171. package/dist/types/lib/memory/types.d.ts +209 -0
  172. package/dist/types/lib/memory/utils.d.ts +73 -0
  173. package/dist/types/lib/memory/verbs.d.ts +333 -0
  174. package/dist/types/lib/recorder/CommitRangeIndex.d.ts +147 -0
  175. package/dist/types/lib/slice/elementProvenance.d.ts +81 -0
  176. package/dist/types/lib/slice/forwardSliceForKey.d.ts +88 -0
  177. package/dist/types/lib/slice/index.d.ts +26 -0
  178. package/dist/types/lib/slice/keyIndex.d.ts +137 -0
  179. package/dist/types/lib/slice/keyTimeline.d.ts +49 -0
  180. package/dist/types/lib/slice/keysReadSources.d.ts +51 -0
  181. package/dist/types/lib/slice/serialize.d.ts +78 -0
  182. package/dist/types/lib/slice/sliceForKey.d.ts +40 -0
  183. package/dist/types/lib/slice/types.d.ts +567 -0
  184. package/dist/types/lib/time-travel/axis.d.ts +119 -0
  185. package/dist/types/lib/time-travel/bundles.d.ts +77 -0
  186. package/dist/types/lib/time-travel/chain.d.ts +93 -0
  187. package/dist/types/lib/time-travel/commitStops.d.ts +55 -0
  188. package/dist/types/lib/time-travel/index.d.ts +22 -0
  189. package/dist/types/lib/time-travel/stateAt.d.ts +102 -0
  190. package/dist/types/lib/time-travel/tagStops.d.ts +72 -0
  191. package/dist/types/lib/time-travel/timeTravel.d.ts +70 -0
  192. package/dist/types/lib/time-travel/types.d.ts +470 -0
  193. package/dist/types/paths.d.ts +4 -0
  194. package/dist/types/write.d.ts +49 -0
  195. package/dist/write.js +56 -0
  196. package/package.json +48 -4
@@ -0,0 +1,26 @@
1
+ /**
2
+ * slice/ — variable-first slicing, both directions (the triage query layer).
3
+ *
4
+ * One variable, one contract, every surface — asked identically by UI panels
5
+ * (explainable-ui / lens), LLM triage tools (trace toolpacks), and offline
6
+ * autopsy agents:
7
+ *
8
+ * - BACKWARD — "why is this VARIABLE what it is?" (`sliceForKey`,
9
+ * `arrayProvenance` / `elementProvenance`).
10
+ * - FORWARD — "who READ it, and what did it FEED?" (`forwardSliceForKey`),
11
+ * plus the flat chronological view (`keyTimeline`).
12
+ *
13
+ * See README.md for the algorithms (thin-slice composition, append-fold
14
+ * provenance, live-range walking), the honesty model, serialization rules,
15
+ * and the evolution path.
16
+ *
17
+ * DAG position: memory ← slice (leaf-adjacent; imports memory/ only).
18
+ */
19
+ export { arrayProvenance, elementProvenance } from './elementProvenance.js';
20
+ export { type ForwardSliceForKeyOptions, forwardSliceForKey } from './forwardSliceForKey.js';
21
+ export { keysReadFromExecutionTree, keysReadFromMap, resolveKeysReadSource } from './keysReadSources.js';
22
+ export { type KeyTimelineOptions, keyTimeline } from './keyTimeline.js';
23
+ export { formatForwardSlice, formatSlice, formatTimeline, forwardSliceToJSON, sliceToJSON } from './serialize.js';
24
+ export { type SliceForKeyOptions, sliceForKey } from './sliceForKey.js';
25
+ export { normaliseStateKey } from '../memory/paths.js';
26
+ export type { ArrayProvenance, AttributionBasis, ElementBirth, FedBasis, ForwardEdge, ForwardNode, ForwardRead, ForwardSlice, ForwardSliceJSON, HonestyNote, HonestyNoteCode, KeyMoment, KeysReadSource, KeyTimeline, MissingProvenanceReason, MissingSliceReason, ReadsCoverage, SliceJSON, StateKey, VariableSlice, } from './types.js';
@@ -0,0 +1,137 @@
1
+ /**
2
+ * slice/keyIndex.ts — the one pass over the log both FORWARD queries share.
3
+ *
4
+ * WHY it exists: a backward slice only ever asks "who wrote key k before
5
+ * idx i" — `findLastWriter` answers that with a backward scan. A forward
6
+ * query asks the opposite, and the opposite is not symmetric: reads are NOT
7
+ * in the commit log, and `KeysReadSource` is a LOOKUP (runtimeStageId →
8
+ * keys), not an enumerable collection. So "who read k" cannot be looked up —
9
+ * it has to be inverted.
10
+ *
11
+ * THE INVERSION (and the law it rests on): the engine commits exactly ONE
12
+ * bundle per executed stage, so the commit log IS the list of execution
13
+ * steps. Calling the reads lookup once per bundle turns the strategy into a
14
+ * key → [commit indices that read it] index — and gives every read moment a
15
+ * commit position, the join key the whole library is addressed by.
16
+ *
17
+ * Consequence worth stating (documented, not silently absorbed): a reads
18
+ * provider naming a step that has no commit in THIS log contributes nothing.
19
+ * That is the same log↔reads pairing law the backward side has — a subflow
20
+ * runs in an isolated runtime, so its log and its tree must come from the
21
+ * same scope (see README.md § Subflow boundaries).
22
+ *
23
+ * Cost: one lookup call per bundle, once per query — O(N + total read keys).
24
+ * Both forward queries then run off sorted index arrays.
25
+ *
26
+ * DAG position: internal to slice/ (not exported from the barrel).
27
+ */
28
+ import type { KeysReadLookup } from '../memory/backtrack.js';
29
+ import { type WriterIndex } from '../memory/keyPaths.js';
30
+ import type { CommitBundle, TraceEntry } from '../memory/types.js';
31
+ import type { HonestyNote } from './types.js';
32
+ /** How many known keys an unknown-key refusal lists before it says "+N more". */
33
+ export declare const KNOWN_KEYS_LISTED = 10;
34
+ /**
35
+ * The inverted view of one commit log + its reads provider.
36
+ *
37
+ * WRITES follow the writer rule every key query shares (`memory/keyPaths.ts`, 9.33.0): a commit
38
+ * writes a key with a row ON it, INSIDE it, or AROUND it when that changed it. READS follow the
39
+ * same path relation: a stage that read the key, a path inside it (part of its value) or a
40
+ * container around it (the whole of its value, and more) is a reader of the key. A read key is
41
+ * matched as the reads provider spelled it, as a DELIM path — the engine's own nested reads are
42
+ * spelled with dots (`StageContext · getValue`), and a dot is a legal character of a top-level key,
43
+ * so `'cfg.a'` stays the literal key it may be.
44
+ */
45
+ export interface KeyIndex {
46
+ /** Where every path was written — the writer rule's index (`keyPaths · buildWriterIndex`). */
47
+ readonly writers: WriterIndex;
48
+ /** key → commit ARRAY positions that WROTE it under the writer rule, ascending. Memoised per key. */
49
+ writesOf(key: string): number[];
50
+ /**
51
+ * The writes that END a value's life: every write except one that reached the key only through paths
52
+ * inside it — that one changed part of the value, and a reader after it still read the rest. Memoised.
53
+ */
54
+ closingWritesOf(key: string): number[];
55
+ /** Read key, as the reads provider named it → commit ARRAY positions whose stage READ it, ascending. */
56
+ readonly readsByKey: Map<string, number[]>;
57
+ /** key → commit ARRAY positions whose stage read it, a path inside it or a container around it, ascending. Memoised. */
58
+ readsOf(key: string): number[];
59
+ /** Every key this log knows BY NAME: written paths ∪ recorded read keys — the list an unknown-key note shows. */
60
+ readonly knownKeys: Set<string>;
61
+ /** Does the log know `key` — a write of it (the writer rule), or a read on, inside or around it? */
62
+ isKnown(key: string): boolean;
63
+ }
64
+ /**
65
+ * Build the index. Indices go in ascending order by construction (we iterate
66
+ * the log forwards), which is what lets every range query below be a pair of
67
+ * binary searches instead of a scan.
68
+ */
69
+ export declare function buildKeyIndex(commitLog: CommitBundle[], lookup: KeysReadLookup): KeyIndex;
70
+ /**
71
+ * LAW — an unknown key is NAMED as unknown; it never renders as "no history".
72
+ *
73
+ * The failure this prevents: `forwardSliceForKey(log, 'recipId')` (typo) must
74
+ * not come back looking like a real variable that nothing happened to. The
75
+ * answer says so out loud AND lists the keys the log does know — bounded, so
76
+ * a 400-key agent state is a hint, not a wall.
77
+ *
78
+ * Why a NOTE and not a throw: the module's absence convention is a RESULT,
79
+ * not an exception (`VariableSlice.missing`) — one absence vocabulary across
80
+ * both doors. The loudness comes from the note + `missing: 'never-written'`,
81
+ * not from a stack trace a triage tool would have to catch.
82
+ */
83
+ export declare function unknownKeyNote(index: Pick<KeyIndex, 'knownKeys'>, key: string): HonestyNote;
84
+ /**
85
+ * Does this log carry ANY recorded read? The provider-agnostic version of
86
+ * `ReadsCoverage.stepsWithReads > 0` (map / custom-fn strategies carry no
87
+ * coverage), and the switch that decides whether "nobody read it" is a
88
+ * FINDING or a BLIND SPOT.
89
+ */
90
+ export declare function hasRecordedReads(index: KeyIndex): boolean;
91
+ /**
92
+ * Ascending indices in the INCLUSIVE range [from, to]. `to === undefined`
93
+ * means "to the end of the log".
94
+ */
95
+ export declare function indicesInRange(sorted: number[] | undefined, from: number, to?: number): number[];
96
+ /** The largest index in the ascending array that is < `before`, or undefined. */
97
+ export declare function lastIndexBefore(sorted: number[] | undefined, before?: number): number | undefined;
98
+ /** The smallest index in the ascending array that is > `after`, or undefined. */
99
+ export declare function firstIndexAfter(sorted: number[] | undefined, after: number): number | undefined;
100
+ /**
101
+ * The LAST trace entry for `path` in a bundle — the one that decides both the
102
+ * committed verb and (under the writeProvenance dial) the widest read prefix.
103
+ * Full mode can record several ops on one path in one commit; read prefixes
104
+ * only grow during a stage, so the last entry is the honest one to attribute
105
+ * with.
106
+ */
107
+ export declare function lastTraceEntry(bundle: CommitBundle, path: string): TraceEntry | undefined;
108
+ /**
109
+ * The trace entry that decides a WRITE of `key` in `bundle` — its verb, and the read prefix a
110
+ * fed edge is judged by: the LAST entry on the exact path when there is one (as before 9.33.0),
111
+ * otherwise the last entry inside or around the key (the commit wrote it through a nested row).
112
+ */
113
+ export declare function writeEntry(bundle: CommitBundle, key: string): TraceEntry | undefined;
114
+ /**
115
+ * The commits in `writes` that reached `key` ONLY through rows inside it — no row on the key and
116
+ * none around it — so each changed part of its value, not the whole. What a `'nested-rows'` note
117
+ * names.
118
+ */
119
+ export declare function nestedOnlyWrites(commitLog: readonly CommitBundle[], key: string, writes: readonly number[]): number[];
120
+ /** How many commit positions a `'nested-rows'` note lists before it says "+N more". */
121
+ export declare const NESTED_WRITES_LISTED = 5;
122
+ /** A write the answer rests on reached the key only through paths inside it. */
123
+ export declare function nestedRowsNote(key: string, commits: readonly number[]): HonestyNote;
124
+ /** The value a backward slice explains holds, or lost the key under, the log's redaction placeholder. */
125
+ export declare function redactedValueNote(key: string): HonestyNote;
126
+ /** No set or delete of the key, or of a container around it, is in range: the value rests on the pre-run state. */
127
+ export declare function fromInitialStateNote(key: string): HonestyNote;
128
+ /** The `readTracking: 'off'` signature — see the HonestyNoteCode docs. */
129
+ export declare function readsNotRecordedNote(): HonestyNote;
130
+ /** The value predates every write this log can see. */
131
+ export declare function preRunOriginNote(key: string): HonestyNote;
132
+ /** At least one fed edge rests on stage-level co-occurrence only. */
133
+ export declare function conservativeEdgesNote(key: string): HonestyNote;
134
+ /** A budget cut the walk — stated, never silent. */
135
+ export declare function truncatedNote(byDepth: boolean, byNodes: boolean): HonestyNote;
136
+ /** Distinct written paths of a bundle, in first-touch order. */
137
+ export declare function writtenPaths(bundle: CommitBundle): string[];
@@ -0,0 +1,49 @@
1
+ /**
2
+ * slice/keyTimeline.ts — the whole life of ONE key, in commit order.
3
+ *
4
+ * The flat companion to {@link forwardSliceForKey}: no graph, no budgets,
5
+ * no anchor — every write of the key and every recorded read of it, as one
6
+ * chronological list. This is what a UI timeline row and an LLM's "walk me
7
+ * through what happened to `total`" both want, and what makes a forward
8
+ * slice checkable: the same facts, unfolded instead of walked.
9
+ *
10
+ * Every moment carries BOTH universal join keys — `runtimeStageId` (to
11
+ * recorders, execution trees, UIs) and `commitIdx` (to the commit log).
12
+ *
13
+ * ### LAW — read attribution is the walk's, exactly
14
+ *
15
+ * A read moment's `fromWriteIdx` is the write whose live range it sits in:
16
+ * the last write at a STRICTLY earlier commit index. That is the same
17
+ * `(writeIdx, nextWriteIdx]` rule `forwardSliceForKey` walks (reads fire
18
+ * PRE-commit), so the two doors can never disagree about which value a read
19
+ * saw — pinned by a cross-door property test. A read before ANY write has no
20
+ * `fromWriteIdx` and raises the `'pre-run-origin'` note: it saw initial
21
+ * state, frozen run `input`, or a closure.
22
+ *
23
+ * DAG position: memory ← slice. Imports memory/ only (see README.md).
24
+ */
25
+ import type { KeysReadLookup } from '../memory/backtrack.js';
26
+ import type { CommitBundle } from '../memory/types.js';
27
+ import type { KeysReadSource, KeyTimeline, StateKey } from './types.js';
28
+ /** Options for {@link keyTimeline}. */
29
+ export interface KeyTimelineOptions {
30
+ /**
31
+ * Exclusive commit-array-index upper bound: only moments BEFORE this idx.
32
+ * Same word and semantics as `sliceForKey`'s `before` (note the sibling
33
+ * `arrayProvenance` uses an INCLUSIVE `atIdx` — this family follows the
34
+ * slice doors).
35
+ */
36
+ before?: number;
37
+ }
38
+ /**
39
+ * The life of one key as a chronological list of writes and reads.
40
+ *
41
+ * @param commitLog Ordered commit bundles — `getSnapshot().commitLog`.
42
+ * @param key A {@link StateKey} — normalised through the same
43
+ * `normaliseStateKey` both slice doors use.
44
+ * @param keysRead A {@link KeysReadSource} or bare lookup (reads are not in
45
+ * the commit log).
46
+ * @returns Always a {@link KeyTimeline}; absence carries its reason, and an
47
+ * unknown key is NAMED as unknown with a bounded list of known keys.
48
+ */
49
+ export declare function keyTimeline(commitLog: CommitBundle[], key: StateKey, keysRead: KeysReadSource | KeysReadLookup, options?: KeyTimelineOptions): KeyTimeline;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * slice/keysReadSources.ts — the shipped {@link KeysReadSource} strategies.
3
+ *
4
+ * The canonical strategy list and rationale live on the KeysReadSource type
5
+ * (types.ts) — the doc consumers hover. This file holds the implementations.
6
+ *
7
+ * Deliberately NOT here: a QualityRecorder adapter. Importing recorder/ would
8
+ * drag this leaf library up the dependency DAG. The adapter is a one-liner at
9
+ * the call site instead: `(id) => recorder.getByKey(id)?.keysRead ?? []`.
10
+ */
11
+ import type { KeysReadLookup } from '../memory/backtrack.js';
12
+ import type { ExecutionTree } from '../memory/types.js';
13
+ import type { KeysReadSource } from './types.js';
14
+ /**
15
+ * Post-hoc reads from a finished run's execution tree — ZERO setup.
16
+ *
17
+ * WHY this works: a stage's `stageReads` records the keys it
18
+ * tracked-read whenever the `readTracking` dial ≠ 'off' ('full' is the
19
+ * engine default; 'summary' replaces VALUES with markers but keeps the
20
+ * KEYS — and keys are all a slice needs). Under 'off' this source returns
21
+ * empty read-sets — detectable via `coverage.stepsWithReads === 0`, which
22
+ * `sliceForKey` copies onto the slice so tools can say "reads were not
23
+ * recorded" instead of the lie "no dependencies".
24
+ *
25
+ * SUBFLOW PAIRING (important): a subflow runs in an ISOLATED runtime — its
26
+ * commits live in `snapshot.subflowResults[sfId].commitLog`, its reads in
27
+ * `snapshot.subflowResults[sfId].executionTree`. Trees and logs must be
28
+ * paired from the SAME scope: to slice a key inside a subflow, re-anchor
29
+ * there —
30
+ *
31
+ * ```ts
32
+ * const sf = snapshot.subflowResults['sf-tools'];
33
+ * sliceForKey(sf.commitLog, key, keysReadFromExecutionTree(sf.executionTree));
34
+ * ```
35
+ *
36
+ * Passing multiple trees widens READ resolution only (useful when one log
37
+ * genuinely spans them); it does not let a root-log slice cross a subflow
38
+ * mount — see README.md § Subflow boundaries.
39
+ */
40
+ export declare function keysReadFromExecutionTree(tree: ExecutionTree | readonly ExecutionTree[]): KeysReadSource;
41
+ /**
42
+ * Reads from a prebuilt map — e.g. collected live from
43
+ * `ScopeRecorder.onRead` events, or deserialized from a stored trace.
44
+ */
45
+ export declare function keysReadFromMap(map: ReadonlyMap<string, readonly string[]> | Readonly<Record<string, readonly string[]>>): KeysReadSource;
46
+ /**
47
+ * Normalize the ergonomic union: callers may pass a full strategy object or
48
+ * a bare lookup function (wrapped as kind 'custom-fn' so the honesty
49
+ * breadcrumb still says where reads came from).
50
+ */
51
+ export declare function resolveKeysReadSource(src: KeysReadSource | KeysReadLookup): KeysReadSource;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * slice/serialize.ts — JSON-safe and LLM-safe projections of a slice.
3
+ *
4
+ * WHY (the failure this prevents): `VariableSlice.root` is an in-memory DAG.
5
+ * Nodes are SHARED — a diamond ancestor is one object reached through many
6
+ * paths, and each node appears in both `parents` and `parentEdges[].parent`.
7
+ * `JSON.stringify` knows nothing about sharing: it re-serializes every shared
8
+ * subtree per path, which explodes combinatorially on diamond-heavy slices.
9
+ * The two consumers that would naively stringify are exactly the ones this
10
+ * library exists for — wire transfer (persist / send a slice) and LLM tools
11
+ * (bounded context). Each gets a purpose-built projection:
12
+ *
13
+ * - {@link sliceToJSON} — flat, id-referenced, LINEAR in node count.
14
+ * - {@link formatSlice} — one bounded human/LLM-readable string that also
15
+ * renders the honesty envelope (missing reason, reads coverage, truncation)
16
+ * a raw `formatCausalChain` doesn't know about.
17
+ *
18
+ * The FORWARD half has exactly the same hazard and the same two projections:
19
+ * {@link forwardSliceToJSON} / {@link formatForwardSlice}. A forward DAG
20
+ * shares nodes just as readily — two values read by one stage feed the same
21
+ * child write — so a forward root must never be stringified either.
22
+ *
23
+ * {@link KeyTimeline} is the deliberate exception, stated so nobody adds a
24
+ * pointless twin: it is a FLAT list of plain fields holding no live
25
+ * references and no sharing, so `JSON.stringify(timeline)` is already
26
+ * correct and linear. What it still needs is the bounded LLM projection —
27
+ * {@link formatTimeline} — because "the whole life of a key" is unbounded in
28
+ * a long run.
29
+ */
30
+ import type { ForwardSlice, ForwardSliceJSON, KeyTimeline, SliceJSON, VariableSlice } from './types.js';
31
+ /**
32
+ * Flat, JSON-safe projection: every DAG node exactly once (keyed by
33
+ * runtimeStageId), edges as id references. Linear in node count — safe to
34
+ * persist, send, or feed to structured consumers. Lossless for everything
35
+ * except the in-memory object graph itself (rebuild adjacency from `edges`).
36
+ */
37
+ export declare function sliceToJSON(slice: VariableSlice): SliceJSON;
38
+ /**
39
+ * One bounded string for LLM triage tools (the `traceToolpack` consumption
40
+ * pattern: tools return plain strings, never recursive objects). Wraps
41
+ * `formatCausalChain` (which is budget-bounded by causalChain's
42
+ * maxDepth/maxNodes and renders shared nodes once as `↳ … (see above)`),
43
+ * and adds the honesty envelope the raw chain doesn't carry:
44
+ *
45
+ * - missing slices render their reason ("value came from initial state /
46
+ * frozen args / a closure — the commit log cannot see those"),
47
+ * - a reads-less provider (`readTracking: 'off'` signature) renders an
48
+ * explicit "⚠ reads were not recorded" instead of silently showing an
49
+ * anchor with no dependencies,
50
+ * - truncation footers pass through from formatCausalChain.
51
+ */
52
+ export declare function formatSlice(slice: VariableSlice): string;
53
+ /**
54
+ * Flat, JSON-safe projection of a {@link ForwardSlice} — the forward twin of
55
+ * {@link sliceToJSON}, linear in node count.
56
+ *
57
+ * Node ids are OPAQUE (`n0`, `n1`, … in BFS order) rather than
58
+ * `runtimeStageId` as in `SliceJSON`, for a structural reason: a forward
59
+ * node is a (key, write) pair, so ONE stage that wrote two keys owns two
60
+ * nodes and a runtimeStageId cannot key the map. Never parse the ids — join
61
+ * on `runtimeStageId` / `commitIdx` / `key`, which every node carries.
62
+ */
63
+ export declare function forwardSliceToJSON(slice: ForwardSlice): ForwardSliceJSON;
64
+ /**
65
+ * One bounded string for LLM triage tools — the forward twin of
66
+ * {@link formatSlice}. Shared nodes render once (`↳ … (see above)`, the same
67
+ * convention `formatCausalChain` uses), read lists are capped, and every
68
+ * honesty note renders as a `⚠` line — including the one that says a `fed`
69
+ * edge is conservative, which is why edges print `[exact]` / `[conservative]`
70
+ * rather than looking alike.
71
+ */
72
+ export declare function formatForwardSlice(slice: ForwardSlice): string;
73
+ /**
74
+ * One bounded string for a {@link KeyTimeline} — the LLM/human projection.
75
+ * (The timeline's JSON needs no helper: it is already flat and JSON-safe —
76
+ * see the file header.) Truncation is stated, never silent.
77
+ */
78
+ export declare function formatTimeline(timeline: KeyTimeline): string;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * slice/sliceForKey.ts — the variable-first backward slice query.
3
+ *
4
+ * "Why is `key` what it is?" — the ONE question every triage surface asks:
5
+ * a human clicking a key in explainable-ui, an LLM calling a `backtrack`
6
+ * tool mid-conversation, an offline autopsy agent over a stored run.
7
+ *
8
+ * WHY this is deliberately a composition and not a new algorithm:
9
+ * `findLastWriter` already answers "who made the value what it is" and
10
+ * `causalChain` already answers "what influenced that step" (thin backward
11
+ * slicing, Weiser 1984). What was missing is the shared CONTRACT — without
12
+ * it, every surface hand-rolls the anchor step differently and their answers
13
+ * disagree. The value of this function is that they can't.
14
+ */
15
+ import { type CausalChainOptions, type KeysReadLookup } from '../memory/backtrack.js';
16
+ import type { CommitBundle } from '../memory/types.js';
17
+ import type { KeysReadSource, StateKey, VariableSlice } from './types.js';
18
+ /** Options for {@link sliceForKey} — causalChain's options plus anchoring. */
19
+ export interface SliceForKeyOptions extends CausalChainOptions {
20
+ /**
21
+ * Exclusive commit-array-index upper bound: slice the value as it stood
22
+ * BEFORE this idx (time-travel triage — "why was it X at step 12?").
23
+ * Same contract as `findLastWriter`'s `beforeIdx`. Omit for the current
24
+ * value. Chained-triage idiom: to slice at an {@link ElementBirth}, pass
25
+ * `before: birth.commitIdx + 1` (birth idx is inclusive, this bound is
26
+ * exclusive — the +1 makes the birth commit itself the anchor).
27
+ */
28
+ before?: number;
29
+ }
30
+ /**
31
+ * Backward slice for one state key: anchor at the key's last writer, then
32
+ * walk read→write (and optional control) dependencies transitively.
33
+ *
34
+ * Honest-absence contract: a missing slice is a RESULT, not an error —
35
+ * `missing: 'never-written'` tells a triage tool (or its LLM caller) that
36
+ * the value came from initial state / frozen args / a closure, which is
37
+ * itself diagnostic information. See {@link VariableSlice} — including its
38
+ * SERIALIZATION WARNING before you stringify the result.
39
+ */
40
+ export declare function sliceForKey(commitLog: CommitBundle[], key: StateKey, keysRead: KeysReadSource | KeysReadLookup, options?: SliceForKeyOptions): VariableSlice;