footprintjs 6.1.2 → 7.1.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/CLAUDE.md +31 -26
- package/README.md +17 -1
- package/dist/advanced.js +2 -6
- package/dist/esm/advanced.js +1 -3
- package/dist/esm/lib/builder/structure/StructureRecorder.js +2 -2
- package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +59 -24
- package/dist/esm/lib/memory/TransactionBuffer.js +92 -9
- package/dist/esm/lib/memory/utils.js +62 -1
- package/dist/esm/lib/recorder/InOutRecorder.js +31 -15
- package/dist/esm/lib/recorder/QualityRecorder.js +40 -13
- package/dist/esm/lib/recorder/RecorderOperation.js +3 -3
- package/dist/esm/lib/recorder/index.js +1 -8
- package/dist/esm/lib/recorder/qualityTrace.js +1 -1
- package/dist/esm/lib/runner/ExecutionRuntime.js +1 -1
- package/dist/esm/lib/scope/recorders/MetricRecorder.js +37 -9
- package/dist/esm/trace.js +1 -12
- package/dist/lib/builder/structure/StructureRecorder.js +2 -2
- package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +59 -24
- package/dist/lib/memory/TransactionBuffer.js +91 -8
- package/dist/lib/memory/utils.js +64 -2
- package/dist/lib/recorder/InOutRecorder.js +31 -15
- package/dist/lib/recorder/QualityRecorder.js +40 -13
- package/dist/lib/recorder/RecorderOperation.js +3 -3
- package/dist/lib/recorder/index.js +2 -12
- package/dist/lib/recorder/qualityTrace.js +1 -1
- package/dist/lib/runner/ExecutionRuntime.js +1 -1
- package/dist/lib/scope/recorders/MetricRecorder.js +37 -9
- package/dist/trace.js +2 -16
- package/dist/types/advanced.d.ts +0 -2
- package/dist/types/lib/builder/structure/StructureRecorder.d.ts +1 -1
- package/dist/types/lib/engine/narrative/CombinedNarrativeRecorder.d.ts +26 -5
- package/dist/types/lib/memory/TransactionBuffer.d.ts +67 -1
- package/dist/types/lib/memory/utils.d.ts +29 -0
- package/dist/types/lib/recorder/InOutRecorder.d.ts +17 -6
- package/dist/types/lib/recorder/QualityRecorder.d.ts +17 -3
- package/dist/types/lib/recorder/RecorderOperation.d.ts +2 -2
- package/dist/types/lib/recorder/index.d.ts +0 -3
- package/dist/types/lib/recorder/qualityTrace.d.ts +5 -2
- package/dist/types/lib/runner/ExecutionRuntime.d.ts +1 -1
- package/dist/types/lib/scope/recorders/MetricRecorder.d.ts +18 -3
- package/dist/types/trace.d.ts +0 -3
- package/package.json +1 -1
- package/dist/esm/lib/recorder/BoundaryStateTracker.js +0 -260
- package/dist/esm/lib/recorder/KeyedRecorder.js +0 -94
- package/dist/esm/lib/recorder/SequenceRecorder.js +0 -193
- package/dist/lib/recorder/BoundaryStateTracker.js +0 -264
- package/dist/lib/recorder/KeyedRecorder.js +0 -98
- package/dist/lib/recorder/SequenceRecorder.js +0 -197
- package/dist/types/lib/recorder/BoundaryStateTracker.d.ts +0 -212
- package/dist/types/lib/recorder/KeyedRecorder.d.ts +0 -56
- package/dist/types/lib/recorder/SequenceRecorder.d.ts +0 -112
package/dist/types/advanced.d.ts
CHANGED
|
@@ -51,5 +51,3 @@ export { applyOutputMapping, ChildrenExecutor, computeNodeType, ContinuationReso
|
|
|
51
51
|
export type { ExecutionCounter } from './lib/engine/runtimeStageId.js';
|
|
52
52
|
export { buildRuntimeStageId, createExecutionCounter, parseRuntimeStageId } from './lib/engine/runtimeStageId.js';
|
|
53
53
|
export { findCommit, findCommits, findLastWriter } from './lib/memory/commitLogUtils.js';
|
|
54
|
-
export { BoundaryStateTracker } from './lib/recorder/BoundaryStateTracker.js';
|
|
55
|
-
export { KeyedRecorder } from './lib/recorder/KeyedRecorder.js';
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* - Runtime events carry `runtimeStageId` (`[subflowPath/]stageId#N`),
|
|
16
16
|
* `iteration`, `traversalContext` — load-bearing for time-travel
|
|
17
|
-
* UIs, commit-log indices, and `
|
|
17
|
+
* UIs, commit-log indices, and `SequenceStore<T>` consumers.
|
|
18
18
|
* NONE of these exist at build time.
|
|
19
19
|
*
|
|
20
20
|
* - Build events carry only `stageId` + structural fields. No
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CombinedNarrativeRecorder — Inline narrative builder that merges flow + data during traversal.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* + O(1) per-step lookup by runtimeStageId)
|
|
6
|
-
* first-class abstraction for observers that span
|
|
4
|
+
* Composes a SequenceStore<CombinedNarrativeEntry> for dual-indexed storage (ordered sequence
|
|
5
|
+
* + O(1) per-step lookup by runtimeStageId) — Convention 1, one purpose per recorder.
|
|
6
|
+
* Implements `CombinedRecorder` — the library's first-class abstraction for observers that span
|
|
7
|
+
* both data-flow and control-flow streams.
|
|
7
8
|
*
|
|
8
9
|
* Event ordering guarantees this works:
|
|
9
10
|
* 1. Scope events (onRead, onWrite) fire DURING stage execution
|
|
@@ -15,7 +16,6 @@
|
|
|
15
16
|
*/
|
|
16
17
|
import type { CombinedRecorder } from '../../recorder/CombinedRecorder.js';
|
|
17
18
|
import type { EmitEvent } from '../../recorder/EmitRecorder.js';
|
|
18
|
-
import { SequenceRecorder } from '../../recorder/SequenceRecorder.js';
|
|
19
19
|
import type { ErrorEvent, PauseEvent, ReadEvent, ResumeEvent, WriteEvent } from '../../scope/types.js';
|
|
20
20
|
import type { CombinedNarrativeEntry, NarrativeRenderer } from './narrativeTypes.js';
|
|
21
21
|
import type { FlowBreakEvent, FlowDecisionEvent, FlowErrorEvent, FlowForkEvent, FlowLoopEvent, FlowPauseEvent, FlowResumeEvent, FlowSelectedEvent, FlowStageEvent, FlowSubflowEvent } from './types.js';
|
|
@@ -42,8 +42,10 @@ export interface CombinedNarrativeRecorderOptions {
|
|
|
42
42
|
* Scope variants of these events are deliberately ignored here — the
|
|
43
43
|
* narrative only surfaces control-flow lifecycle events.
|
|
44
44
|
*/
|
|
45
|
-
export declare class CombinedNarrativeRecorder
|
|
45
|
+
export declare class CombinedNarrativeRecorder implements CombinedRecorder {
|
|
46
46
|
readonly id: string;
|
|
47
|
+
/** Dual-indexed ordered storage (Convention 1 — composed, not inherited). */
|
|
48
|
+
private readonly store;
|
|
47
49
|
/**
|
|
48
50
|
* Pending scope ops keyed by runtimeStageId. Flushed in onStageExecuted/onDecision.
|
|
49
51
|
*
|
|
@@ -100,6 +102,25 @@ export declare class CombinedNarrativeRecorder extends SequenceRecorder<Combined
|
|
|
100
102
|
* Root-level entries have subflowId = undefined.
|
|
101
103
|
*/
|
|
102
104
|
getEntriesBySubflow(): Record<string, CombinedNarrativeEntry[]>;
|
|
105
|
+
/** All narrative entries in execution order. */
|
|
106
|
+
getEntries(): CombinedNarrativeEntry[];
|
|
107
|
+
/** Total number of narrative entries. */
|
|
108
|
+
get entryCount(): number;
|
|
109
|
+
/** O(1) lookup: all narrative entries for one execution step. */
|
|
110
|
+
getEntriesForStep(runtimeStageId: string): CombinedNarrativeEntry[];
|
|
111
|
+
/** Number of distinct execution steps that produced entries. */
|
|
112
|
+
get stepCount(): number;
|
|
113
|
+
/** Pre-built per-step range index — O(1) lookups for time-travel scrubbing. */
|
|
114
|
+
getEntryRanges(): ReadonlyMap<string, {
|
|
115
|
+
readonly firstIdx: number;
|
|
116
|
+
readonly endIdx: number;
|
|
117
|
+
}>;
|
|
118
|
+
/** Reduce ALL entries to a single value. */
|
|
119
|
+
aggregate<R>(fn: (acc: R, entry: CombinedNarrativeEntry) => R, initial: R): R;
|
|
120
|
+
/** Reduce entries, optionally filtered to a set of visible runtimeStageIds. */
|
|
121
|
+
accumulate<R>(fn: (acc: R, entry: CombinedNarrativeEntry) => R, initial: R, keys?: ReadonlySet<string>): R;
|
|
122
|
+
/** Progressive reveal: entries whose runtimeStageId is in the visible set. */
|
|
123
|
+
getEntriesUpTo(visibleIds: ReadonlySet<string>): CombinedNarrativeEntry[];
|
|
103
124
|
/** Clears all state. Called automatically before each run. */
|
|
104
125
|
clear(): void;
|
|
105
126
|
private incrementStageCounter;
|
|
@@ -23,7 +23,60 @@ export declare class TransactionBuffer {
|
|
|
23
23
|
/** Read current value at path (includes uncommitted changes). */
|
|
24
24
|
get(path: (string | number)[], defaultValue?: any): any;
|
|
25
25
|
/**
|
|
26
|
-
* Flush all staged mutations and return the commit bundle
|
|
26
|
+
* Flush all staged mutations and return the commit bundle — recording the
|
|
27
|
+
* stage's NET CHANGE, not its raw write log.
|
|
28
|
+
*
|
|
29
|
+
* ── WHY (the defect this fixes) ─────────────────────────────────────────
|
|
30
|
+
* Previously every `set`/`merge` was recorded verbatim, so the commit bundle
|
|
31
|
+
* was a log of *operations* rather than *changes*. Two operations produce no
|
|
32
|
+
* net change yet were still committed as "mutations":
|
|
33
|
+
*
|
|
34
|
+
* 1. No-op write — writing a key the value it already holds (e.g. an
|
|
35
|
+
* agent context slot re-emitting identical content every
|
|
36
|
+
* turn). base K=1, stage writes K=1.
|
|
37
|
+
* 2. Write-revert — changing then restoring a key within one stage.
|
|
38
|
+
* base K=1, stage writes K=2 then K=1.
|
|
39
|
+
*
|
|
40
|
+
* Recording these as mutations (a) bloated causal slicing / backtracking with
|
|
41
|
+
* spurious dependencies on intermediate values that never reach final state,
|
|
42
|
+
* and (b) made downstream "what changed here?" consumers light up stages that
|
|
43
|
+
* changed nothing — most visibly the lens highlight flagging every slot.
|
|
44
|
+
*
|
|
45
|
+
* ── HOW ─────────────────────────────────────────────────────────────────
|
|
46
|
+
* At commit we hold BOTH `baseSnapshot` (state when the stage began) and
|
|
47
|
+
* `workingCopy` (state after all its writes). For each path the stage touched
|
|
48
|
+
* we keep it in the bundle ONLY if its final value differs from the base
|
|
49
|
+
* value ({@link deepEqual}). No-op AND write-revert paths drop out, because
|
|
50
|
+
* both compare equal to base. This is a single net-delta diff at commit time
|
|
51
|
+
* — one deep compare per touched path, O(changed state), paid once per stage
|
|
52
|
+
* (NOT per write). A naive per-write deep-equal skip would be more expensive
|
|
53
|
+
* and would still miss write-revert (the intermediate write differs from the
|
|
54
|
+
* value present at the moment of writing).
|
|
55
|
+
*
|
|
56
|
+
* ── TWO HONEST TIERS (by design — do not "unify" them) ──────────────────
|
|
57
|
+
* • commit (here) = CHANGE-level — truthful net delta. Feeds the commit
|
|
58
|
+
* log, causal chain, narrative, and the lens highlight.
|
|
59
|
+
* • `onWrite` event = OP-level — fires on EVERY write attempt regardless of
|
|
60
|
+
* net change. Feeds metrics / behavioural observability
|
|
61
|
+
* (a debugger wants to see "wrote 2, then reverted").
|
|
62
|
+
* `onWrite` is unchanged by this method; only the COMMIT becomes change-only.
|
|
63
|
+
*
|
|
64
|
+
* ── EMPTY COMMITS ARE INTENTIONAL ───────────────────────────────────────
|
|
65
|
+
* A stage that nets no change commits an EMPTY patch — NOT nothing.
|
|
66
|
+
* {@link StageContext.commit} still records the bundle unconditionally, so
|
|
67
|
+
* every executed stage remains a time-travel cursor stop (its `runtimeStageId`
|
|
68
|
+
* marker is preserved); only its PATCH is empty. This is what keeps the
|
|
69
|
+
* commit-indexed slider stable while making the highlight truthful.
|
|
70
|
+
*
|
|
71
|
+
* ── KNOWN LIMITATIONS / FUTURE ──────────────────────────────────────────
|
|
72
|
+
* • Explicit key DELETION is still unrepresentable in MemoryPatch (a removed
|
|
73
|
+
* key cannot be expressed). Setting a key to `undefined` is treated as a
|
|
74
|
+
* change (value differs from base), not a deletion. Tracked for a future
|
|
75
|
+
* `delete` verb.
|
|
76
|
+
* • Array-merge dedup in {@link deepSmartMerge} still uses reference equality
|
|
77
|
+
* (`new Set`), so deep-equal *objects* in a merged array are not deduped.
|
|
78
|
+
* Orthogonal to this change; tracked separately.
|
|
79
|
+
*
|
|
27
80
|
* Resets the buffer to empty state after commit.
|
|
28
81
|
*/
|
|
29
82
|
commit(): {
|
|
@@ -35,4 +88,17 @@ export declare class TransactionBuffer {
|
|
|
35
88
|
verb: 'set' | 'merge';
|
|
36
89
|
}[];
|
|
37
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* Rebuild overwrite / updates / trace keeping ONLY paths whose final value
|
|
93
|
+
* differs from the base value — i.e. the stage's net change. See
|
|
94
|
+
* {@link TransactionBuffer.commit} for the rationale.
|
|
95
|
+
*
|
|
96
|
+
* Paths are compared at the exact granularity they were written (each trace
|
|
97
|
+
* entry's path), against `workingCopy` (final) vs `baseSnapshot` (start).
|
|
98
|
+
* Surviving `set` paths copy their final value from `overwritePatch`;
|
|
99
|
+
* surviving `merge` paths copy their accumulated delta from `updatePatch` —
|
|
100
|
+
* preserving the set-vs-merge verb so replay ({@link applySmartMerge}) is
|
|
101
|
+
* byte-for-byte identical to recording only the real changes.
|
|
102
|
+
*/
|
|
103
|
+
private toChangeOnlyPayload;
|
|
38
104
|
}
|
|
@@ -57,6 +57,35 @@ export declare function redactPatch(patch: MemoryPatch, redactedSet: Set<string>
|
|
|
57
57
|
* Normalises an array path into a stable string key using DELIM.
|
|
58
58
|
*/
|
|
59
59
|
export declare function normalisePath(path: (string | number)[]): string;
|
|
60
|
+
/**
|
|
61
|
+
* Structural deep equality for committed-state values.
|
|
62
|
+
*
|
|
63
|
+
* Used by {@link TransactionBuffer} to decide whether a stage actually CHANGED
|
|
64
|
+
* a path or merely re-wrote / reverted it to the value it already held (a
|
|
65
|
+
* "no-op write"). Committed state is JSON-shaped — it must survive
|
|
66
|
+
* `structuredClone` — so this only needs to handle the shapes that can reach a
|
|
67
|
+
* commit: primitives, arrays, and plain objects.
|
|
68
|
+
*
|
|
69
|
+
* Semantics:
|
|
70
|
+
* - reference / identical-primitive short-circuits first (cheap fast path)
|
|
71
|
+
* - `NaN` equals `NaN` (primitive compare falls back to `Object.is`)
|
|
72
|
+
* - arrays: equal length AND deep-equal element-wise (order-sensitive)
|
|
73
|
+
* - objects: identical own-key set AND deep-equal per key
|
|
74
|
+
* - mismatched kinds (array vs object, object vs null) → not equal
|
|
75
|
+
*
|
|
76
|
+
* Cost & safety:
|
|
77
|
+
* - Allocates NOTHING but transient `Object.keys` arrays — no clones. It is
|
|
78
|
+
* strictly cheaper than the `structuredClone` the commit already performs.
|
|
79
|
+
* - Primitive comparisons (the bulk of state) are O(1) via the `===` /
|
|
80
|
+
* `Object.is` fast paths; only nested objects/arrays incur a walk, bounded
|
|
81
|
+
* by the value's own size.
|
|
82
|
+
* - Assumes ACYCLIC, JSON-shaped values — the same contract the memory layer
|
|
83
|
+
* already relies on (committed state is `structuredClone`d and must be
|
|
84
|
+
* JSON-serialisable for checkpoints). A cyclic value is out of contract
|
|
85
|
+
* here exactly as it is for checkpointing; dev mode flags cycles at write
|
|
86
|
+
* time via `ScopeFacade.setValue`.
|
|
87
|
+
*/
|
|
88
|
+
export declare function deepEqual(a: any, b: any): boolean;
|
|
60
89
|
/**
|
|
61
90
|
* Deep union merge helper.
|
|
62
91
|
* - Arrays (non-empty): union without duplicates (encounter order preserved)
|
|
@@ -61,7 +61,6 @@
|
|
|
61
61
|
* ```
|
|
62
62
|
*/
|
|
63
63
|
import type { FlowRecorder, FlowRunEvent, FlowSubflowEvent } from '../engine/narrative/types.js';
|
|
64
|
-
import { SequenceRecorder } from './SequenceRecorder.js';
|
|
65
64
|
export type InOutPhase = 'entry' | 'exit';
|
|
66
65
|
/** Synthetic id for the top-level run's boundary pair. */
|
|
67
66
|
export declare const ROOT_SUBFLOW_ID = "__root__";
|
|
@@ -120,21 +119,22 @@ export interface InOutRecorderOptions {
|
|
|
120
119
|
export declare function inOutRecorder(options?: InOutRecorderOptions): InOutRecorder;
|
|
121
120
|
/**
|
|
122
121
|
* Stateful accumulator that watches `FlowRecorder` chart-boundary events
|
|
123
|
-
* (run start/end + subflow entry/exit) and
|
|
124
|
-
* its
|
|
122
|
+
* (run start/end + subflow entry/exit) and pushes `InOutEntry` records to
|
|
123
|
+
* its composed `SequenceStore` (Convention 1 — one purpose per recorder).
|
|
125
124
|
*
|
|
126
125
|
* Attach via `executor.attachCombinedRecorder(recorder)` — footprintjs
|
|
127
126
|
* detects the `FlowRecorder` method shape and routes events.
|
|
128
127
|
*/
|
|
129
|
-
export declare class InOutRecorder
|
|
128
|
+
export declare class InOutRecorder implements FlowRecorder {
|
|
130
129
|
readonly id: string;
|
|
130
|
+
/** 1:N ordered storage with per-step index (Convention 1 — composed). */
|
|
131
|
+
private readonly store;
|
|
131
132
|
constructor(options?: InOutRecorderOptions);
|
|
132
133
|
onRunStart(event: FlowRunEvent): void;
|
|
133
134
|
onRunEnd(event: FlowRunEvent): void;
|
|
134
135
|
onSubflowEntry(event: FlowSubflowEvent): void;
|
|
135
136
|
onSubflowExit(event: FlowSubflowEvent): void;
|
|
136
|
-
/** All entries in execution order (entry+exit interleaved).
|
|
137
|
-
* Alias for `getEntries()` with domain-clearer naming. */
|
|
137
|
+
/** All entries in execution order (entry+exit interleaved). */
|
|
138
138
|
getBoundaries(): InOutEntry[];
|
|
139
139
|
/** Entry/exit pair for one chart execution.
|
|
140
140
|
* Returns `{ entry, exit }` — `exit` is `undefined` for in-progress / paused
|
|
@@ -148,6 +148,17 @@ export declare class InOutRecorder extends SequenceRecorder<InOutEntry> implemen
|
|
|
148
148
|
* for slider / scrubbing UIs. The top-level run's entry is the first
|
|
149
149
|
* step (depth 0). */
|
|
150
150
|
getSteps(): InOutEntry[];
|
|
151
|
+
/** O(1) lookup: all boundary entries (both phases) for one runtimeStageId. */
|
|
152
|
+
getEntriesForStep(runtimeStageId: string): InOutEntry[];
|
|
153
|
+
/** Pre-built per-step range index — O(1) lookups for time-travel scrubbing. */
|
|
154
|
+
getEntryRanges(): ReadonlyMap<string, {
|
|
155
|
+
readonly firstIdx: number;
|
|
156
|
+
readonly endIdx: number;
|
|
157
|
+
}>;
|
|
158
|
+
/** Progressive reveal: boundaries whose runtimeStageId is in the visible set. */
|
|
159
|
+
getEntriesUpTo(visibleIds: ReadonlySet<string>): InOutEntry[];
|
|
160
|
+
/** Clear all stored boundaries — called by the executor before each run(). */
|
|
161
|
+
clear(): void;
|
|
151
162
|
/** The root run's entry/exit pair, if the run has started.
|
|
152
163
|
* Convenience for consumers that want to bracket the timeline by the
|
|
153
164
|
* outermost in/out. */
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Collects quality scores during traversal (accumulate pattern).
|
|
5
5
|
* After execution, use qualityTrace() to backtrack from any low-scoring step.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Composes a `KeyedStore<QualityEntry>` for O(1) lookup and standard operations
|
|
8
|
+
* (Convention 1 — one purpose per recorder):
|
|
8
9
|
* - **Translate**: `getByKey('call-llm#5')` — quality at this step
|
|
9
10
|
* - **Accumulate**: progressive quality up to slider position
|
|
10
11
|
* - **Aggregate**: overall pipeline quality score
|
|
@@ -30,7 +31,6 @@
|
|
|
30
31
|
* ```
|
|
31
32
|
*/
|
|
32
33
|
import type { ReadEvent, ScopeRecorder, StageEvent, WriteEvent } from '../scope/types.js';
|
|
33
|
-
import { KeyedRecorder } from './KeyedRecorder.js';
|
|
34
34
|
import type { RecorderOperation } from './RecorderOperation.js';
|
|
35
35
|
/** Per-step quality data stored by QualityRecorder. */
|
|
36
36
|
export interface QualityEntry {
|
|
@@ -69,10 +69,12 @@ export interface QualityRecorderOptions {
|
|
|
69
69
|
/** Preferred UI operation. Defaults to 'accumulate' (progressive quality). */
|
|
70
70
|
preferredOperation?: RecorderOperation;
|
|
71
71
|
}
|
|
72
|
-
export declare class QualityRecorder
|
|
72
|
+
export declare class QualityRecorder implements ScopeRecorder {
|
|
73
73
|
private static _counter;
|
|
74
74
|
readonly id: string;
|
|
75
75
|
readonly preferredOperation: RecorderOperation;
|
|
76
|
+
/** 1:1 per-step storage (Convention 1 — composed, not inherited). */
|
|
77
|
+
private readonly store;
|
|
76
78
|
private readonly scoringFn;
|
|
77
79
|
private currentRuntimeStageId;
|
|
78
80
|
private currentStageId;
|
|
@@ -84,6 +86,18 @@ export declare class QualityRecorder extends KeyedRecorder<QualityEntry> impleme
|
|
|
84
86
|
onRead(event: ReadEvent): void;
|
|
85
87
|
onWrite(event: WriteEvent): void;
|
|
86
88
|
onStageEnd(event: StageEvent): void;
|
|
89
|
+
/** Translate: quality entry for a specific runtimeStageId. */
|
|
90
|
+
getByKey(runtimeStageId: string): QualityEntry | undefined;
|
|
91
|
+
/** All per-step quality entries as a read-only Map (insertion-ordered). */
|
|
92
|
+
getMap(): ReadonlyMap<string, QualityEntry>;
|
|
93
|
+
/** All per-step quality entries (insertion-ordered). */
|
|
94
|
+
values(): QualityEntry[];
|
|
95
|
+
/** Number of scored steps. */
|
|
96
|
+
get size(): number;
|
|
97
|
+
/** Aggregate: reduce ALL scored steps to a single value. */
|
|
98
|
+
aggregate<R>(fn: (acc: R, entry: QualityEntry, key: string) => R, initial: R): R;
|
|
99
|
+
/** Accumulate: reduce scored steps up to a slider position. */
|
|
100
|
+
accumulate<R>(fn: (acc: R, entry: QualityEntry, key: string) => R, initial: R, keys?: ReadonlySet<string>): R;
|
|
87
101
|
/** Overall quality score — average of all step scores. */
|
|
88
102
|
getOverallScore(): number;
|
|
89
103
|
/** Find the lowest-scoring step. */
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Data is collected during the single DFS traversal. The consumer chooses the
|
|
5
5
|
* operation at READ time:
|
|
6
6
|
*
|
|
7
|
-
* | Operation |
|
|
7
|
+
* | Operation | KeyedStore method | SequenceStore method | Use case |
|
|
8
8
|
* |-------------|--------------------------|--------------------------------|-----------------------------|
|
|
9
|
-
* | Translate | `
|
|
9
|
+
* | Translate | `get(id)` | `getByKey(id)` | Per-step detail |
|
|
10
10
|
* | Accumulate | `accumulate(fn, init, k)` | `accumulate(fn, init, k)` | Running total up to slider |
|
|
11
11
|
* | Aggregate | `aggregate(fn, init)` | `aggregate(fn, init)` | Grand total for dashboards |
|
|
12
12
|
*
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
export { BoundaryStateStore } from './BoundaryStateStore.js';
|
|
2
2
|
export { KeyedStore } from './KeyedStore.js';
|
|
3
3
|
export { SequenceStore } from './SequenceStore.js';
|
|
4
|
-
export { BoundaryStateTracker } from './BoundaryStateTracker.js';
|
|
5
|
-
export { KeyedRecorder } from './KeyedRecorder.js';
|
|
6
|
-
export { SequenceRecorder } from './SequenceRecorder.js';
|
|
7
4
|
export type { CombinedRecorder } from './CombinedRecorder.js';
|
|
8
5
|
export { hasEmitRecorderMethods, hasFlowRecorderMethods, hasRecorderMethods, isFlowEvent } from './CombinedRecorder.js';
|
|
9
6
|
export type { CompositeSnapshot } from './CompositeRecorder.js';
|
|
@@ -14,8 +14,11 @@
|
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
16
|
import type { CommitBundle } from '../memory/types.js';
|
|
17
|
-
import type { KeyedRecorder } from './KeyedRecorder.js';
|
|
18
17
|
import type { QualityEntry } from './QualityRecorder.js';
|
|
18
|
+
/** Minimal per-step quality lookup — satisfied by `QualityRecorder` (structural, decoupled). */
|
|
19
|
+
export interface QualityLookup {
|
|
20
|
+
getByKey(runtimeStageId: string): QualityEntry | undefined;
|
|
21
|
+
}
|
|
19
22
|
/** A single frame in the quality stack trace. */
|
|
20
23
|
export interface QualityFrame {
|
|
21
24
|
runtimeStageId: string;
|
|
@@ -44,7 +47,7 @@ export interface QualityStackTrace {
|
|
|
44
47
|
* @param startId runtimeStageId to start from
|
|
45
48
|
* @param maxDepth Maximum backtracking depth (default: 20)
|
|
46
49
|
*/
|
|
47
|
-
export declare function qualityTrace(commitLog: CommitBundle[], qualityRecorder:
|
|
50
|
+
export declare function qualityTrace(commitLog: CommitBundle[], qualityRecorder: QualityLookup, startId: string, maxDepth?: number): QualityStackTrace;
|
|
48
51
|
/**
|
|
49
52
|
* Format a QualityStackTrace as human-readable text.
|
|
50
53
|
*/
|
|
@@ -17,7 +17,7 @@ import type { CommitBundle, StageSnapshot } from '../memory/types.js';
|
|
|
17
17
|
export interface RecorderSnapshot {
|
|
18
18
|
id: string;
|
|
19
19
|
name: string;
|
|
20
|
-
/** ScopeRecorder type and pattern description (e.g., "Translator (
|
|
20
|
+
/** ScopeRecorder type and pattern description (e.g., "Translator (KeyedStore) — per-step token usage"). */
|
|
21
21
|
description?: string;
|
|
22
22
|
/** Preferred read-time operation — hints the UI about which view to show prominently. */
|
|
23
23
|
preferredOperation?: 'translate' | 'accumulate' | 'aggregate';
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* MetricRecorder — per-step timing and execution counts, keyed by runtimeStageId.
|
|
3
3
|
*
|
|
4
4
|
* Stores per-invocation data during traversal. Aggregated views computed on read.
|
|
5
|
-
*
|
|
5
|
+
* Composes a `KeyedStore<StepMetrics>` for O(1) lookup and standard operations
|
|
6
|
+
* (Convention 1 — one purpose per recorder: this is the ScopeRecorder; the store
|
|
7
|
+
* is the storage).
|
|
6
8
|
*
|
|
7
9
|
* @example
|
|
8
10
|
* ```typescript
|
|
@@ -20,7 +22,6 @@
|
|
|
20
22
|
* metric.accumulate((sum, m) => sum + m.duration, 0, visibleKeys);
|
|
21
23
|
* ```
|
|
22
24
|
*/
|
|
23
|
-
import { KeyedRecorder } from '../../recorder/KeyedRecorder.js';
|
|
24
25
|
import type { RecorderOperation } from '../../recorder/RecorderOperation.js';
|
|
25
26
|
import type { CommitEvent, PauseEvent, ReadEvent, ScopeRecorder, StageEvent, WriteEvent } from '../types.js';
|
|
26
27
|
/** Per-invocation metrics for a single execution step. */
|
|
@@ -67,10 +68,12 @@ export interface MetricRecorderOptions {
|
|
|
67
68
|
/** Preferred UI operation. Defaults to 'aggregate' (dashboard totals). */
|
|
68
69
|
preferredOperation?: RecorderOperation;
|
|
69
70
|
}
|
|
70
|
-
export declare class MetricRecorder
|
|
71
|
+
export declare class MetricRecorder implements ScopeRecorder {
|
|
71
72
|
private static _counter;
|
|
72
73
|
readonly id: string;
|
|
73
74
|
readonly preferredOperation: RecorderOperation;
|
|
75
|
+
/** 1:1 per-step storage (Convention 1 — composed, not inherited). */
|
|
76
|
+
private readonly store;
|
|
74
77
|
private stageStartTimes;
|
|
75
78
|
private currentRuntimeStageId;
|
|
76
79
|
private stageFilter?;
|
|
@@ -78,6 +81,18 @@ export declare class MetricRecorder extends KeyedRecorder<StepMetrics> implement
|
|
|
78
81
|
private shouldRecord;
|
|
79
82
|
/** Get or create the StepMetrics for the current stage. */
|
|
80
83
|
private current;
|
|
84
|
+
/** Translate: per-step metrics by runtimeStageId. */
|
|
85
|
+
getByKey(runtimeStageId: string): StepMetrics | undefined;
|
|
86
|
+
/** All per-step metrics as a read-only Map (insertion-ordered). */
|
|
87
|
+
getMap(): ReadonlyMap<string, StepMetrics>;
|
|
88
|
+
/** All per-step metrics (insertion-ordered). */
|
|
89
|
+
values(): StepMetrics[];
|
|
90
|
+
/** Number of recorded steps. */
|
|
91
|
+
get size(): number;
|
|
92
|
+
/** Aggregate: reduce ALL steps to a single value (dashboards, totals). */
|
|
93
|
+
aggregate<R>(fn: (acc: R, entry: StepMetrics, key: string) => R, initial: R): R;
|
|
94
|
+
/** Accumulate: reduce steps up to a slider position (progressive view). */
|
|
95
|
+
accumulate<R>(fn: (acc: R, entry: StepMetrics, key: string) => R, initial: R, keys?: ReadonlySet<string>): R;
|
|
81
96
|
onStageStart(event: StageEvent): void;
|
|
82
97
|
onRead(event: ReadEvent): void;
|
|
83
98
|
onWrite(event: WriteEvent): void;
|
package/dist/types/trace.d.ts
CHANGED
|
@@ -41,9 +41,6 @@ export { KeyedStore } from './lib/recorder/KeyedStore.js';
|
|
|
41
41
|
export { SequenceStore } from './lib/recorder/SequenceStore.js';
|
|
42
42
|
export type { RangeEntry, RangeToken } from './lib/recorder/CommitRangeIndex.js';
|
|
43
43
|
export { CommitRangeIndex } from './lib/recorder/CommitRangeIndex.js';
|
|
44
|
-
export { KeyedRecorder } from './lib/recorder/KeyedRecorder.js';
|
|
45
|
-
export { SequenceRecorder } from './lib/recorder/SequenceRecorder.js';
|
|
46
|
-
export { BoundaryStateTracker } from './lib/recorder/BoundaryStateTracker.js';
|
|
47
44
|
export type { Topology, TopologyEdge, TopologyIncomingKind, TopologyNode, TopologyRecorderOptions, } from './lib/recorder/TopologyRecorder.js';
|
|
48
45
|
export { TopologyRecorder, topologyRecorder } from './lib/recorder/TopologyRecorder.js';
|
|
49
46
|
export type { InOutEntry, InOutPhase, InOutRecorderOptions } from './lib/recorder/InOutRecorder.js';
|
package/package.json
CHANGED