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.
- package/LICENSE +21 -0
- package/README.md +73 -2
- package/dist/esm/index.d.ts +27 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/lib/capture/freeze.d.ts +91 -0
- package/dist/esm/lib/capture/freeze.js +313 -0
- package/dist/esm/lib/capture/ownData.d.ts +18 -0
- package/dist/esm/lib/capture/ownData.js +35 -0
- package/dist/esm/lib/capture/valueKinds.d.ts +57 -0
- package/dist/esm/lib/capture/valueKinds.js +141 -0
- package/dist/esm/lib/ids/runtimeStageId.d.ts +173 -0
- package/dist/esm/lib/ids/runtimeStageId.js +193 -0
- package/dist/esm/lib/memory/EventLog.d.ts +105 -0
- package/dist/esm/lib/memory/EventLog.js +147 -0
- package/dist/esm/lib/memory/RecordFrame.d.ts +232 -0
- package/dist/esm/lib/memory/RecordFrame.js +295 -0
- package/dist/esm/lib/memory/SharedMemory.d.ts +46 -0
- package/dist/esm/lib/memory/SharedMemory.js +87 -0
- package/dist/esm/lib/memory/TransactionBuffer.d.ts +521 -0
- package/dist/esm/lib/memory/TransactionBuffer.js +946 -0
- package/dist/esm/lib/memory/admission.d.ts +81 -0
- package/dist/esm/lib/memory/admission.js +152 -0
- package/dist/esm/lib/memory/backtrack.d.ts +286 -0
- package/dist/esm/lib/memory/backtrack.js +456 -0
- package/dist/esm/lib/memory/commitLogUtils.d.ts +217 -0
- package/dist/esm/lib/memory/commitLogUtils.js +383 -0
- package/dist/esm/lib/memory/deltaEncoding.d.ts +66 -0
- package/dist/esm/lib/memory/deltaEncoding.js +121 -0
- package/dist/esm/lib/memory/equality.d.ts +93 -0
- package/dist/esm/lib/memory/equality.js +261 -0
- package/dist/esm/lib/memory/eventPosition.d.ts +18 -0
- package/dist/esm/lib/memory/eventPosition.js +1 -0
- package/dist/esm/lib/memory/honesty.d.ts +93 -0
- package/dist/esm/lib/memory/honesty.js +91 -0
- package/dist/esm/lib/memory/keyPaths.d.ts +125 -0
- package/dist/esm/lib/memory/keyPaths.js +226 -0
- package/dist/esm/lib/memory/logModel.d.ts +76 -0
- package/dist/esm/lib/memory/logModel.js +363 -0
- package/dist/esm/lib/memory/merge.d.ts +26 -0
- package/dist/esm/lib/memory/merge.js +51 -0
- package/dist/esm/lib/memory/pathOps.d.ts +118 -0
- package/dist/esm/lib/memory/pathOps.js +292 -0
- package/dist/esm/lib/memory/paths.d.ts +56 -0
- package/dist/esm/lib/memory/paths.js +60 -0
- package/dist/esm/lib/memory/placeholders.d.ts +39 -0
- package/dist/esm/lib/memory/placeholders.js +39 -0
- package/dist/esm/lib/memory/recordCommit.d.ts +45 -0
- package/dist/esm/lib/memory/recordCommit.js +60 -0
- package/dist/esm/lib/memory/scrub.d.ts +52 -0
- package/dist/esm/lib/memory/scrub.js +72 -0
- package/dist/esm/lib/memory/types.d.ts +209 -0
- package/dist/esm/lib/memory/types.js +9 -0
- package/dist/esm/lib/memory/utils.d.ts +73 -0
- package/dist/esm/lib/memory/utils.js +150 -0
- package/dist/esm/lib/memory/verbs.d.ts +333 -0
- package/dist/esm/lib/memory/verbs.js +426 -0
- package/dist/esm/lib/recorder/CommitRangeIndex.d.ts +147 -0
- package/dist/esm/lib/recorder/CommitRangeIndex.js +206 -0
- package/dist/esm/lib/slice/elementProvenance.d.ts +81 -0
- package/dist/esm/lib/slice/elementProvenance.js +189 -0
- package/dist/esm/lib/slice/forwardSliceForKey.d.ts +88 -0
- package/dist/esm/lib/slice/forwardSliceForKey.js +242 -0
- package/dist/esm/lib/slice/index.d.ts +26 -0
- package/dist/esm/lib/slice/index.js +25 -0
- package/dist/esm/lib/slice/keyIndex.d.ts +137 -0
- package/dist/esm/lib/slice/keyIndex.js +292 -0
- package/dist/esm/lib/slice/keyTimeline.d.ts +49 -0
- package/dist/esm/lib/slice/keyTimeline.js +112 -0
- package/dist/esm/lib/slice/keysReadSources.d.ts +51 -0
- package/dist/esm/lib/slice/keysReadSources.js +94 -0
- package/dist/esm/lib/slice/serialize.d.ts +78 -0
- package/dist/esm/lib/slice/serialize.js +277 -0
- package/dist/esm/lib/slice/sliceForKey.d.ts +40 -0
- package/dist/esm/lib/slice/sliceForKey.js +78 -0
- package/dist/esm/lib/slice/types.d.ts +567 -0
- package/dist/esm/lib/slice/types.js +24 -0
- package/dist/esm/lib/time-travel/axis.d.ts +119 -0
- package/dist/esm/lib/time-travel/axis.js +178 -0
- package/dist/esm/lib/time-travel/bundles.d.ts +77 -0
- package/dist/esm/lib/time-travel/bundles.js +135 -0
- package/dist/esm/lib/time-travel/chain.d.ts +93 -0
- package/dist/esm/lib/time-travel/chain.js +187 -0
- package/dist/esm/lib/time-travel/commitStops.d.ts +55 -0
- package/dist/esm/lib/time-travel/commitStops.js +201 -0
- package/dist/esm/lib/time-travel/index.d.ts +22 -0
- package/dist/esm/lib/time-travel/index.js +20 -0
- package/dist/esm/lib/time-travel/stateAt.d.ts +102 -0
- package/dist/esm/lib/time-travel/stateAt.js +152 -0
- package/dist/esm/lib/time-travel/tagStops.d.ts +72 -0
- package/dist/esm/lib/time-travel/tagStops.js +101 -0
- package/dist/esm/lib/time-travel/timeTravel.d.ts +70 -0
- package/dist/esm/lib/time-travel/timeTravel.js +359 -0
- package/dist/esm/lib/time-travel/types.d.ts +470 -0
- package/dist/esm/lib/time-travel/types.js +22 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/paths.d.ts +4 -0
- package/dist/esm/paths.js +4 -0
- package/dist/esm/write.d.ts +49 -0
- package/dist/esm/write.js +47 -0
- package/dist/index.js +67 -0
- package/dist/lib/capture/freeze.js +319 -0
- package/dist/lib/capture/ownData.js +40 -0
- package/dist/lib/capture/valueKinds.js +145 -0
- package/dist/lib/ids/runtimeStageId.js +209 -0
- package/dist/lib/memory/EventLog.js +151 -0
- package/dist/lib/memory/RecordFrame.js +299 -0
- package/dist/lib/memory/SharedMemory.js +91 -0
- package/dist/lib/memory/TransactionBuffer.js +950 -0
- package/dist/lib/memory/admission.js +158 -0
- package/dist/lib/memory/backtrack.js +462 -0
- package/dist/lib/memory/commitLogUtils.js +398 -0
- package/dist/lib/memory/deltaEncoding.js +127 -0
- package/dist/lib/memory/equality.js +265 -0
- package/dist/lib/memory/eventPosition.js +2 -0
- package/dist/lib/memory/honesty.js +94 -0
- package/dist/lib/memory/keyPaths.js +242 -0
- package/dist/lib/memory/logModel.js +371 -0
- package/dist/lib/memory/merge.js +55 -0
- package/dist/lib/memory/pathOps.js +308 -0
- package/dist/lib/memory/paths.js +66 -0
- package/dist/lib/memory/placeholders.js +42 -0
- package/dist/lib/memory/recordCommit.js +64 -0
- package/dist/lib/memory/scrub.js +77 -0
- package/dist/lib/memory/types.js +10 -0
- package/dist/lib/memory/utils.js +169 -0
- package/dist/lib/memory/verbs.js +443 -0
- package/dist/lib/recorder/CommitRangeIndex.js +210 -0
- package/dist/lib/slice/elementProvenance.js +194 -0
- package/dist/lib/slice/forwardSliceForKey.js +246 -0
- package/dist/lib/slice/index.js +42 -0
- package/dist/lib/slice/keyIndex.js +312 -0
- package/dist/lib/slice/keyTimeline.js +116 -0
- package/dist/lib/slice/keysReadSources.js +100 -0
- package/dist/lib/slice/serialize.js +285 -0
- package/dist/lib/slice/sliceForKey.js +82 -0
- package/dist/lib/slice/types.js +25 -0
- package/dist/lib/time-travel/axis.js +183 -0
- package/dist/lib/time-travel/bundles.js +143 -0
- package/dist/lib/time-travel/chain.js +192 -0
- package/dist/lib/time-travel/commitStops.js +205 -0
- package/dist/lib/time-travel/index.js +31 -0
- package/dist/lib/time-travel/stateAt.js +159 -0
- package/dist/lib/time-travel/tagStops.js +105 -0
- package/dist/lib/time-travel/timeTravel.js +363 -0
- package/dist/lib/time-travel/types.js +23 -0
- package/dist/paths.js +14 -0
- package/dist/types/index.d.ts +27 -0
- package/dist/types/lib/capture/freeze.d.ts +91 -0
- package/dist/types/lib/capture/ownData.d.ts +18 -0
- package/dist/types/lib/capture/valueKinds.d.ts +57 -0
- package/dist/types/lib/ids/runtimeStageId.d.ts +173 -0
- package/dist/types/lib/memory/EventLog.d.ts +105 -0
- package/dist/types/lib/memory/RecordFrame.d.ts +232 -0
- package/dist/types/lib/memory/SharedMemory.d.ts +46 -0
- package/dist/types/lib/memory/TransactionBuffer.d.ts +521 -0
- package/dist/types/lib/memory/admission.d.ts +81 -0
- package/dist/types/lib/memory/backtrack.d.ts +286 -0
- package/dist/types/lib/memory/commitLogUtils.d.ts +217 -0
- package/dist/types/lib/memory/deltaEncoding.d.ts +66 -0
- package/dist/types/lib/memory/equality.d.ts +93 -0
- package/dist/types/lib/memory/eventPosition.d.ts +18 -0
- package/dist/types/lib/memory/honesty.d.ts +93 -0
- package/dist/types/lib/memory/keyPaths.d.ts +125 -0
- package/dist/types/lib/memory/logModel.d.ts +76 -0
- package/dist/types/lib/memory/merge.d.ts +26 -0
- package/dist/types/lib/memory/pathOps.d.ts +118 -0
- package/dist/types/lib/memory/paths.d.ts +56 -0
- package/dist/types/lib/memory/placeholders.d.ts +39 -0
- package/dist/types/lib/memory/recordCommit.d.ts +45 -0
- package/dist/types/lib/memory/scrub.d.ts +52 -0
- package/dist/types/lib/memory/types.d.ts +209 -0
- package/dist/types/lib/memory/utils.d.ts +73 -0
- package/dist/types/lib/memory/verbs.d.ts +333 -0
- package/dist/types/lib/recorder/CommitRangeIndex.d.ts +147 -0
- package/dist/types/lib/slice/elementProvenance.d.ts +81 -0
- package/dist/types/lib/slice/forwardSliceForKey.d.ts +88 -0
- package/dist/types/lib/slice/index.d.ts +26 -0
- package/dist/types/lib/slice/keyIndex.d.ts +137 -0
- package/dist/types/lib/slice/keyTimeline.d.ts +49 -0
- package/dist/types/lib/slice/keysReadSources.d.ts +51 -0
- package/dist/types/lib/slice/serialize.d.ts +78 -0
- package/dist/types/lib/slice/sliceForKey.d.ts +40 -0
- package/dist/types/lib/slice/types.d.ts +567 -0
- package/dist/types/lib/time-travel/axis.d.ts +119 -0
- package/dist/types/lib/time-travel/bundles.d.ts +77 -0
- package/dist/types/lib/time-travel/chain.d.ts +93 -0
- package/dist/types/lib/time-travel/commitStops.d.ts +55 -0
- package/dist/types/lib/time-travel/index.d.ts +22 -0
- package/dist/types/lib/time-travel/stateAt.d.ts +102 -0
- package/dist/types/lib/time-travel/tagStops.d.ts +72 -0
- package/dist/types/lib/time-travel/timeTravel.d.ts +70 -0
- package/dist/types/lib/time-travel/types.d.ts +470 -0
- package/dist/types/paths.d.ts +4 -0
- package/dist/types/write.d.ts +49 -0
- package/dist/write.js +56 -0
- 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;
|