footprintjs 3.0.0 → 3.0.2

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/AGENTS.md CHANGED
@@ -12,7 +12,7 @@ This is the footprint.js library — the flowchart pattern for backend code. Sel
12
12
  src/lib/
13
13
  ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
14
14
  ├── schema/ → Validation (Zod optional, duck-typed)
15
- ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart(), typedFlowChart())
15
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
16
16
  ├── scope/ → Per-stage facades + recorders + providers
17
17
  ├── reactive/ → TypedScope<T> deep Proxy (typed property access, $-methods)
18
18
  ├── decide/ → decide()/select() decision evidence capture
@@ -26,7 +26,7 @@ Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
26
26
  ## Key API — TypedScope (Recommended)
27
27
 
28
28
  ```typescript
29
- import { typedFlowChart, FlowChartExecutor, decide } from 'footprintjs';
29
+ import { flowChart, FlowChartExecutor, decide } from 'footprintjs';
30
30
 
31
31
  interface State {
32
32
  creditScore: number;
@@ -34,11 +34,10 @@ interface State {
34
34
  decision?: string;
35
35
  }
36
36
 
37
- const chart = typedFlowChart<State>('Intake', async (scope) => {
37
+ const chart = flowChart<State>('Intake', async (scope) => {
38
38
  scope.creditScore = 750; // typed write (no setValue needed)
39
39
  scope.riskTier = 'low'; // typed write
40
40
  }, 'intake')
41
- .setEnableNarrative()
42
41
  .addDeciderFunction('Route', (scope) => {
43
42
  return decide(scope, [
44
43
  { when: { riskTier: { eq: 'low' } }, then: 'approved', label: 'Low risk' },
@@ -54,7 +53,7 @@ const chart = typedFlowChart<State>('Intake', async (scope) => {
54
53
  .end()
55
54
  .build();
56
55
 
57
- const executor = new FlowChartExecutor(chart<State>());
56
+ const executor = new FlowChartExecutor(chart);
58
57
  await executor.run();
59
58
  executor.getNarrative(); // causal trace with decision evidence
60
59
  ```
@@ -92,7 +91,7 @@ select(scope, [
92
91
  ### Executor
93
92
 
94
93
  ```typescript
95
- const executor = new FlowChartExecutor(chart<State>());
94
+ const executor = new FlowChartExecutor(chart);
96
95
  await executor.run({ input, env: { traceId: 'req-123' } });
97
96
  executor.getNarrative() // string[]
98
97
  executor.getNarrativeEntries() // CombinedNarrativeEntry[]
@@ -107,13 +106,13 @@ executor.setRedactionPolicy({ keys, patterns, fields })
107
106
  - **Scope Recorder**: fires DURING stage (`onRead`, `onWrite`, `onCommit`)
108
107
  - **FlowRecorder**: fires AFTER stage (`onStageExecuted`, `onDecision`, `onFork`, `onLoop`)
109
108
  - 8 built-in FlowRecorder strategies
110
- - `setEnableNarrative()` auto-attaches `CombinedNarrativeRecorder`
109
+ - Narrative via `executor.recorder(narrative())` at runtime
111
110
 
112
111
  ## Rules
113
112
 
114
- - Use `typedFlowChart<T>()` — scopeFactory is auto-embedded, no `createTypedScopeFactory` needed
113
+ - Use `flowChart<T>()` — scopeFactory is auto-embedded
115
114
  - Use `decide()` / `select()` in decider/selector functions
116
115
  - Use typed property access (not getValue/setValue)
117
116
  - Use `$getArgs()` for input, `$getEnv()` for environment
118
117
  - Never post-process the tree — use recorders
119
- - `setEnableNarrative()` is all you need for narrative setup
118
+ - Use `.recorder(narrative())` at runtime for narrative setup
package/CLAUDE.md CHANGED
@@ -32,7 +32,7 @@ Two entry points:
32
32
  ### TypedScope (Recommended)
33
33
 
34
34
  ```typescript
35
- import { typedFlowChart, createTypedScopeFactory, FlowChartExecutor } from 'footprintjs';
35
+ import { flowChart, FlowChartExecutor } from 'footprintjs';
36
36
 
37
37
  interface LoanState {
38
38
  creditTier: string;
@@ -42,7 +42,7 @@ interface LoanState {
42
42
  approved?: boolean;
43
43
  }
44
44
 
45
- const chart = typedFlowChart<LoanState>('Intake', async (scope) => {
45
+ const chart = flowChart<LoanState>('Intake', async (scope) => {
46
46
  scope.creditTier = 'A'; // typed write
47
47
  scope.amount = 50000; // typed write
48
48
  scope.customer.address.zip = '90210'; // deep write (updateValue)
@@ -56,10 +56,9 @@ const chart = typedFlowChart<LoanState>('Intake', async (scope) => {
56
56
  const env = scope.$getEnv();
57
57
  scope.$break(); // stop pipeline
58
58
  }, 'intake')
59
- .setEnableNarrative()
60
59
  .build();
61
60
 
62
- const executor = new FlowChartExecutor(chart, createTypedScopeFactory<LoanState>());
61
+ const executor = new FlowChartExecutor(chart);
63
62
  await executor.run({ input: { requestId: 'req-123' } });
64
63
  ```
65
64
 
@@ -91,11 +90,10 @@ const chart = flowChart('Stage1', fn1, 'stage-1', undefined, 'Description')
91
90
  .addFunctionBranch('low', 'Approve', approveFn)
92
91
  .setDefault('high')
93
92
  .end()
94
- .setEnableNarrative()
95
93
  .build();
96
94
  ```
97
95
 
98
- Methods: `start()`, `addFunction()`, `addStreamingFunction()`, `addDeciderFunction()`, `addSelectorFunction()`, `addListOfFunction()`, `addSubFlowChart()`, `addSubFlowChartNext()`, `loopTo()`, `setEnableNarrative()`, `setInputSchema()`, `setOutputSchema()`, `setOutputMapper()`, `build()`, `toSpec()`, `toMermaid()`
96
+ Methods: `start()`, `addFunction()`, `addStreamingFunction()`, `addDeciderFunction()`, `addSelectorFunction()`, `addListOfFunction()`, `addSubFlowChart()`, `addSubFlowChartNext()`, `loopTo()`, `contract()`, `build()`, `toSpec()`, `toMermaid()`
99
97
 
100
98
  ### ScopeFacade (Internal — use TypedScope for new code)
101
99
 
@@ -114,7 +112,7 @@ scope.getEnv() // frozen execution environment (NOT tracked)
114
112
  ### Executor
115
113
 
116
114
  ```typescript
117
- const executor = new FlowChartExecutor(chart, createTypedScopeFactory<State>());
115
+ const executor = new FlowChartExecutor(chart);
118
116
  await executor.run({ input: data, env: { traceId: 'req-123' } });
119
117
 
120
118
  executor.attachRecorder(recorder) // plug scope observer
@@ -150,7 +148,7 @@ Both use `{ id, hooks } -> dispatcher -> error isolation -> attach/detach`. Inte
150
148
  - `onDecision`/`onSelected` carry optional `evidence` from decide()/select()
151
149
  - Built-in: 8 strategies (Narrative, Adaptive, Windowed, RLE, Milestone, Progressive, Separate, Manifest, Silent)
152
150
 
153
- **CombinedNarrativeRecorder** implements BOTH interfaces. Auto-attached by `setEnableNarrative()`.
151
+ **CombinedNarrativeRecorder** implements BOTH interfaces. Attached via `executor.recorder(narrative())` at runtime.
154
152
 
155
153
  ## Event Ordering
156
154
 
@@ -172,7 +170,7 @@ Both use `{ id, hooks } -> dispatcher -> error isolation -> attach/detach`. Inte
172
170
  - Don't extract shared base for Recorder/FlowRecorder — two instances = coincidence
173
171
  - Don't use `getArgs()` for tracked data — use typed scope properties
174
172
  - Don't put infrastructure data in `getArgs()` — use `getEnv()` via `run({ env })`
175
- - Don't manually create `CombinedNarrativeRecorder` — `setEnableNarrative()` handles it
173
+ - Don't manually create `CombinedNarrativeRecorder` — `executor.recorder(narrative())` handles it
176
174
 
177
175
  ## Build & Test
178
176
 
package/README.md CHANGED
@@ -73,7 +73,7 @@ That answer came from the trace &mdash; not from the LLM's imagination.
73
73
  ## Quick Start
74
74
 
75
75
  ```typescript
76
- import { typedFlowChart, FlowChartExecutor, decide } from 'footprintjs';
76
+ import { flowChart, FlowChartExecutor, decide } from 'footprintjs';
77
77
 
78
78
  interface State {
79
79
  user: { name: string; tier: string };
@@ -81,7 +81,7 @@ interface State {
81
81
  lane: string;
82
82
  }
83
83
 
84
- const chart = typedFlowChart<State>('FetchUser', async (scope) => {
84
+ const chart = flowChart<State>('FetchUser', async (scope) => {
85
85
  scope.user = { name: 'Alice', tier: 'premium' };
86
86
  }, 'fetch-user')
87
87
  .addFunction('ApplyDiscount', async (scope) => {
@@ -100,7 +100,6 @@ const chart = typedFlowChart<State>('FetchUser', async (scope) => {
100
100
  })
101
101
  .setDefault('standard')
102
102
  .end()
103
- .setEnableNarrative()
104
103
  .build();
105
104
 
106
105
  const executor = new FlowChartExecutor(chart);
@@ -18,7 +18,7 @@ npm install footprintjs
18
18
  ## Quick Start
19
19
 
20
20
  ```typescript
21
- import { typedFlowChart, FlowChartExecutor } from 'footprintjs';
21
+ import { flowChart, FlowChartExecutor } from 'footprintjs';
22
22
 
23
23
  interface OrderState {
24
24
  orderId: string;
@@ -26,7 +26,7 @@ interface OrderState {
26
26
  paymentStatus: string;
27
27
  }
28
28
 
29
- const chart = typedFlowChart<OrderState>('ReceiveOrder', (scope) => {
29
+ const chart = flowChart<OrderState>('ReceiveOrder', (scope) => {
30
30
  scope.orderId = 'ORD-123';
31
31
  scope.amount = 49.99;
32
32
  }, 'receive-order', undefined, 'Receive and validate the incoming order')
@@ -34,10 +34,9 @@ const chart = typedFlowChart<OrderState>('ReceiveOrder', (scope) => {
34
34
  const amount = scope.amount;
35
35
  scope.paymentStatus = amount < 100 ? 'approved' : 'review';
36
36
  }, 'process-payment', 'Charge customer and record payment status')
37
- .setEnableNarrative()
38
37
  .build();
39
38
 
40
- const executor = new FlowChartExecutor(chart<OrderState>());
39
+ const executor = new FlowChartExecutor(chart);
41
40
  await executor.run({ input: { orderId: 'ORD-123' } });
42
41
 
43
42
  console.log(executor.getNarrative());
@@ -53,12 +52,12 @@ console.log(executor.getNarrative());
53
52
 
54
53
  ## FlowChartBuilder API
55
54
 
56
- Always chain from `typedFlowChart<T>()` (recommended) or `flowChart()`.
55
+ Always chain from `flowChart<T>()` (recommended) or `flowChart()`.
57
56
 
58
57
  ### Linear Stages
59
58
 
60
59
  ```typescript
61
- import { typedFlowChart } from 'footprintjs';
60
+ import { flowChart } from 'footprintjs';
62
61
 
63
62
  interface MyState {
64
63
  valueA: string;
@@ -66,7 +65,7 @@ interface MyState {
66
65
  valueC: boolean;
67
66
  }
68
67
 
69
- const chart = typedFlowChart<MyState>('StageA', fnA, 'stage-a', undefined, 'Description of A')
68
+ const chart = flowChart<MyState>('StageA', fnA, 'stage-a', undefined, 'Description of A')
70
69
  .addFunction('StageB', fnB, 'stage-b', 'Description of B')
71
70
  .addFunction('StageC', fnC, 'stage-c', 'Description of C')
72
71
  .build();
@@ -81,7 +80,7 @@ const chart = typedFlowChart<MyState>('StageA', fnA, 'stage-a', undefined, 'Desc
81
80
 
82
81
  ### Stage Function Signature (TypedScope)
83
82
 
84
- With `typedFlowChart<T>()`, stage functions receive a `TypedScope<T>` proxy. All reads and writes use typed property access:
83
+ With `flowChart<T>()`, stage functions receive a `TypedScope<T>` proxy. All reads and writes use typed property access:
85
84
 
86
85
  ```typescript
87
86
  interface LoanState {
@@ -133,7 +132,7 @@ interface RiskState {
133
132
  riskTier: string;
134
133
  }
135
134
 
136
- const chart = typedFlowChart<RiskState>('Intake', intakeFn, 'intake')
135
+ const chart = flowChart<RiskState>('Intake', intakeFn, 'intake')
137
136
  .addDeciderFunction('AssessRisk', (scope) => {
138
137
  // decide() captures filter evidence automatically
139
138
  return decide(scope, [
@@ -168,7 +167,7 @@ interface CheckState {
168
167
  needsIdentity: boolean;
169
168
  }
170
169
 
171
- const chart = typedFlowChart<CheckState>('Intake', intakeFn, 'intake')
170
+ const chart = flowChart<CheckState>('Intake', intakeFn, 'intake')
172
171
  .addSelectorFunction('SelectChecks', (scope) => {
173
172
  return select(scope, [
174
173
  { when: { needsCredit: { eq: true } }, then: 'credit-check', label: 'Credit required' },
@@ -196,7 +195,7 @@ builder.addListOfFunction([
196
195
 
197
196
  ```typescript
198
197
  // Build a reusable sub-pipeline
199
- const creditSubflow = typedFlowChart<CreditState>('PullReport', pullReportFn, 'pull-report')
198
+ const creditSubflow = flowChart<CreditState>('PullReport', pullReportFn, 'pull-report')
200
199
  .addFunction('ScoreReport', scoreReportFn, 'score-report')
201
200
  .build();
202
201
 
@@ -233,13 +232,14 @@ builder
233
232
 
234
233
  ```typescript
235
234
  builder
236
- .setEnableNarrative() // enable narrative recording
237
- .setInputSchema(zodSchema) // validate input (Zod or JSON Schema)
238
- .setOutputSchema(outputZodSchema) // declare output shape
239
- .setOutputMapper((state) => ({ // map final state to response
240
- decision: state.decision,
241
- reason: state.reason,
242
- }));
235
+ .contract({ // I/O schemas + output mapper
236
+ input: zodSchema, // validate input (Zod or JSON Schema)
237
+ output: outputZodSchema, // declare output shape
238
+ mapper: (state) => ({ // map final state to response
239
+ decision: state.decision,
240
+ reason: state.reason,
241
+ }),
242
+ });
243
243
  ```
244
244
 
245
245
  ### Output
@@ -264,7 +264,7 @@ interface AppState {
264
264
  decision?: string;
265
265
  }
266
266
 
267
- const executor = new FlowChartExecutor(chart<AppState>());
267
+ const executor = new FlowChartExecutor(chart);
268
268
 
269
269
  // Run with input and optional execution environment
270
270
  const result = await executor.run({
@@ -373,7 +373,7 @@ executor.attachFlowRecorder(myRecorder);
373
373
 
374
374
  This is what powers `getNarrative()` and `getNarrativeEntries()`. It implements BOTH `Recorder` (scope) and `FlowRecorder` (engine) interfaces. It buffers scope ops per-stage, then flushes when the flow event arrives — producing merged entries in a single pass.
375
375
 
376
- **You don't need to create this manually.** Calling `.setEnableNarrative()` on the builder auto-attaches it.
376
+ **You don't need to create this manually.** Use `executor.recorder(narrative())` at runtime to attach it.
377
377
 
378
378
  ---
379
379
 
@@ -399,25 +399,28 @@ const report = executor.getRedactionReport();
399
399
  ## Contracts & OpenAPI
400
400
 
401
401
  ```typescript
402
- import { defineContract, generateOpenAPI } from 'footprintjs';
402
+ import { flowChart } from 'footprintjs';
403
403
  import { z } from 'zod';
404
404
 
405
- const contract = defineContract(chart, {
406
- inputSchema: z.object({
407
- applicantName: z.string(),
408
- income: z.number(),
409
- }),
410
- outputSchema: z.object({
411
- decision: z.enum(['approved', 'rejected']),
412
- reason: z.string(),
413
- }),
414
- outputMapper: (state) => ({
415
- decision: state.decision,
416
- reason: state.reason,
417
- }),
418
- });
405
+ const chart = flowChart('ProcessLoan', receiveFn)
406
+ .addFunction('Assess', assessFn)
407
+ .contract({
408
+ input: z.object({
409
+ applicantName: z.string(),
410
+ income: z.number(),
411
+ }),
412
+ output: z.object({
413
+ decision: z.enum(['approved', 'rejected']),
414
+ reason: z.string(),
415
+ }),
416
+ mapper: (state) => ({
417
+ decision: state.decision,
418
+ reason: state.reason,
419
+ }),
420
+ })
421
+ .build();
419
422
 
420
- const openApiSpec = generateOpenAPI(contract, {
423
+ const openApiSpec = chart.toOpenAPI({
421
424
  title: 'Loan Underwriting API',
422
425
  version: '1.0.0',
423
426
  });
@@ -452,11 +455,11 @@ When a stage executes, events fire in this exact order:
452
455
  1. **Never post-process the tree.** Don't walk the spec after execution to collect data. Use recorders.
453
456
  2. **Don't use `getValue()`/`setValue()` in TypedScope stages.** Use typed property access (`scope.amount = 50000`). The old ScopeFacade API is internal only.
454
457
  3. **Don't use `$`-prefixed state keys** (e.g., `$break` as a property name) — they collide with TypedScope's `$`-prefixed escape hatches (`$getArgs`, `$getEnv`, `$break`, `$debug`, `$metric`).
455
- 4. **Never use `CombinedNarrativeBuilder`** — it's deprecated. Use `CombinedNarrativeRecorder` (auto-attached by `setEnableNarrative()`).
458
+ 4. **Never use `CombinedNarrativeBuilder`** — it's deprecated. Use `CombinedNarrativeRecorder` (attached via `executor.recorder(narrative())`).
456
459
  5. **Don't extract a shared base class** for Recorder and FlowRecorder. They look similar but serve different layers. Two instances = coincidence.
457
460
  6. **Don't call `$getArgs()` for tracked data.** `$getArgs()` returns frozen readonly input. Use typed scope properties for state that should appear in the narrative.
458
461
  7. **Don't put infrastructure data in `$getArgs()`.** Use `$getEnv()` via `run({ env })` for signals, timeouts, and trace IDs.
459
- 8. **Don't create scope recorders manually** unless building a custom recorder. `setEnableNarrative()` handles everything.
462
+ 8. **Don't create scope recorders manually** unless building a custom recorder. `executor.recorder(narrative())` handles everything.
460
463
 
461
464
  ---
462
465
 
@@ -466,13 +469,13 @@ When a stage executes, events fire in this exact order:
466
469
  src/lib/
467
470
  ├── memory/ → SharedMemory, StageContext, TransactionBuffer, EventLog (foundation)
468
471
  ├── schema/ → detectSchema, validate, InputValidationError (foundation)
469
- ├── builder/ → FlowChartBuilder, flowChart(), typedFlowChart(), DeciderList, SelectorFnList (standalone)
472
+ ├── builder/ → FlowChartBuilder, flowChart(), DeciderList, SelectorFnList (standalone)
470
473
  ├── scope/ → ScopeFacade, recorders/, providers/, protection/ (depends: memory)
471
474
  ├── reactive/ → TypedScope<T> deep Proxy, typed property access, $-methods, cycle-safe (depends: scope)
472
475
  ├── decide/ → decide()/select() decision evidence capture, filter + function when formats (depends: scope)
473
476
  ├── engine/ → FlowchartTraverser, handlers/, narrative/ (depends: memory, scope, reactive, builder)
474
477
  ├── runner/ → FlowChartExecutor, ExecutionRuntime (depends: engine, scope, schema)
475
- └── contract/ → defineContract, generateOpenAPI (depends: schema)
478
+ └── contract/ → I/O schema + OpenAPI generation (depends: schema)
476
479
  ```
477
480
 
478
481
  Dependency DAG: `memory <- scope <- reactive <- engine <- runner`, `schema <- engine`, `builder (standalone) -> engine`, `contract <- schema`, `decide -> scope`
@@ -488,7 +491,7 @@ Two entry points:
488
491
  ### Pipeline with decide() + narrative
489
492
 
490
493
  ```typescript
491
- import { typedFlowChart, FlowChartExecutor, decide } from 'footprintjs';
494
+ import { flowChart, FlowChartExecutor, decide } from 'footprintjs';
492
495
 
493
496
  interface LoanState {
494
497
  applicantName: string;
@@ -499,7 +502,7 @@ interface LoanState {
499
502
  reason?: string;
500
503
  }
501
504
 
502
- const chart = typedFlowChart<LoanState>('Receive', (scope) => {
505
+ const chart = flowChart<LoanState>('Receive', (scope) => {
503
506
  const args = scope.$getArgs<{ applicantName: string; income: number }>();
504
507
  scope.applicantName = args.applicantName;
505
508
  scope.income = args.income;
@@ -524,10 +527,9 @@ const chart = typedFlowChart<LoanState>('Receive', (scope) => {
524
527
  })
525
528
  .setDefault('reject')
526
529
  .end()
527
- .setEnableNarrative()
528
530
  .build();
529
531
 
530
- const executor = new FlowChartExecutor(chart<LoanState>());
532
+ const executor = new FlowChartExecutor(chart);
531
533
  await executor.run({ input: { applicantName: 'Bob', income: 42000 } });
532
534
  const trace = executor.getNarrative();
533
535
  // Feed trace to LLM for grounded explanations
@@ -547,11 +549,11 @@ interface MainState {
547
549
  creditScore?: number;
548
550
  }
549
551
 
550
- const subflow = typedFlowChart<SubState>('SubStart', subStartFn, 'sub-start')
552
+ const subflow = flowChart<SubState>('SubStart', subStartFn, 'sub-start')
551
553
  .addFunction('SubProcess', subProcessFn, 'sub-process')
552
554
  .build();
553
555
 
554
- const main = typedFlowChart<MainState>('Main', mainFn, 'main')
556
+ const main = flowChart<MainState>('Main', mainFn, 'main')
555
557
  .addSubFlowChartNext('my-subflow', subflow, 'SubflowMount', {
556
558
  inputMapper: (scope) => ({ ssn: scope.ssn }),
557
559
  outputMapper: (subOut) => ({ creditScore: subOut.score }),
@@ -12,7 +12,7 @@ This is the footprint.js library — the flowchart pattern for backend code. Sel
12
12
  src/lib/
13
13
  ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
14
14
  ├── schema/ → Validation (Zod optional, duck-typed)
15
- ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart(), typedFlowChart())
15
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
16
16
  ├── scope/ → Per-stage facades + recorders + providers
17
17
  ├── reactive/ → TypedScope<T> deep Proxy (typed property access, $-methods)
18
18
  ├── decide/ → decide()/select() decision evidence capture
@@ -26,7 +26,7 @@ Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
26
26
  ## Key API — TypedScope (Recommended)
27
27
 
28
28
  ```typescript
29
- import { typedFlowChart, FlowChartExecutor, decide } from 'footprintjs';
29
+ import { flowChart, FlowChartExecutor, decide } from 'footprintjs';
30
30
 
31
31
  interface State {
32
32
  creditScore: number;
@@ -34,11 +34,10 @@ interface State {
34
34
  decision?: string;
35
35
  }
36
36
 
37
- const chart = typedFlowChart<State>('Intake', async (scope) => {
37
+ const chart = flowChart<State>('Intake', async (scope) => {
38
38
  scope.creditScore = 750; // typed write (no setValue needed)
39
39
  scope.riskTier = 'low'; // typed write
40
40
  }, 'intake')
41
- .setEnableNarrative()
42
41
  .addDeciderFunction('Route', (scope) => {
43
42
  return decide(scope, [
44
43
  { when: { riskTier: { eq: 'low' } }, then: 'approved', label: 'Low risk' },
@@ -54,7 +53,7 @@ const chart = typedFlowChart<State>('Intake', async (scope) => {
54
53
  .end()
55
54
  .build();
56
55
 
57
- const executor = new FlowChartExecutor(chart<State>());
56
+ const executor = new FlowChartExecutor(chart);
58
57
  await executor.run();
59
58
  executor.getNarrative(); // causal trace with decision evidence
60
59
  ```
@@ -92,7 +91,7 @@ select(scope, [
92
91
  ### Executor
93
92
 
94
93
  ```typescript
95
- const executor = new FlowChartExecutor(chart<State>());
94
+ const executor = new FlowChartExecutor(chart);
96
95
  await executor.run({ input, env: { traceId: 'req-123' } });
97
96
  executor.getNarrative() // string[]
98
97
  executor.getNarrativeEntries() // CombinedNarrativeEntry[]
@@ -107,13 +106,13 @@ executor.setRedactionPolicy({ keys, patterns, fields })
107
106
  - **Scope Recorder**: fires DURING stage (`onRead`, `onWrite`, `onCommit`)
108
107
  - **FlowRecorder**: fires AFTER stage (`onStageExecuted`, `onDecision`, `onFork`, `onLoop`)
109
108
  - 8 built-in FlowRecorder strategies
110
- - `setEnableNarrative()` auto-attaches `CombinedNarrativeRecorder`
109
+ - Narrative via `executor.recorder(narrative())` at runtime
111
110
 
112
111
  ## Rules
113
112
 
114
- - Use `typedFlowChart<T>()` — scopeFactory is auto-embedded, no `createTypedScopeFactory` needed
113
+ - Use `flowChart<T>()` — scopeFactory is auto-embedded
115
114
  - Use `decide()` / `select()` in decider/selector functions
116
115
  - Use typed property access (not getValue/setValue)
117
116
  - Use `$getArgs()` for input, `$getEnv()` for environment
118
117
  - Never post-process the tree — use recorders
119
- - `setEnableNarrative()` is all you need for narrative setup
118
+ - Use `.recorder(narrative())` at runtime for narrative setup
@@ -12,7 +12,7 @@ This is the footprint.js library — the flowchart pattern for backend code. Sel
12
12
  src/lib/
13
13
  ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
14
14
  ├── schema/ → Validation (Zod optional, duck-typed)
15
- ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart(), typedFlowChart())
15
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
16
16
  ├── scope/ → Per-stage facades + recorders + providers
17
17
  ├── reactive/ → TypedScope<T> deep Proxy (typed property access, $-methods)
18
18
  ├── decide/ → decide()/select() decision evidence capture
@@ -26,7 +26,7 @@ Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
26
26
  ## Key API — TypedScope (Recommended)
27
27
 
28
28
  ```typescript
29
- import { typedFlowChart, FlowChartExecutor, decide } from 'footprintjs';
29
+ import { flowChart, FlowChartExecutor, decide } from 'footprintjs';
30
30
 
31
31
  interface State {
32
32
  creditScore: number;
@@ -34,11 +34,10 @@ interface State {
34
34
  decision?: string;
35
35
  }
36
36
 
37
- const chart = typedFlowChart<State>('Intake', async (scope) => {
37
+ const chart = flowChart<State>('Intake', async (scope) => {
38
38
  scope.creditScore = 750; // typed write (no setValue needed)
39
39
  scope.riskTier = 'low'; // typed write
40
40
  }, 'intake')
41
- .setEnableNarrative()
42
41
  .addDeciderFunction('Route', (scope) => {
43
42
  return decide(scope, [
44
43
  { when: { riskTier: { eq: 'low' } }, then: 'approved', label: 'Low risk' },
@@ -54,7 +53,7 @@ const chart = typedFlowChart<State>('Intake', async (scope) => {
54
53
  .end()
55
54
  .build();
56
55
 
57
- const executor = new FlowChartExecutor(chart<State>());
56
+ const executor = new FlowChartExecutor(chart);
58
57
  await executor.run();
59
58
  executor.getNarrative(); // causal trace with decision evidence
60
59
  ```
@@ -92,7 +91,7 @@ select(scope, [
92
91
  ### Executor
93
92
 
94
93
  ```typescript
95
- const executor = new FlowChartExecutor(chart<State>());
94
+ const executor = new FlowChartExecutor(chart);
96
95
  await executor.run({ input, env: { traceId: 'req-123' } });
97
96
  executor.getNarrative() // string[]
98
97
  executor.getNarrativeEntries() // CombinedNarrativeEntry[]
@@ -107,13 +106,13 @@ executor.setRedactionPolicy({ keys, patterns, fields })
107
106
  - **Scope Recorder**: fires DURING stage (`onRead`, `onWrite`, `onCommit`)
108
107
  - **FlowRecorder**: fires AFTER stage (`onStageExecuted`, `onDecision`, `onFork`, `onLoop`)
109
108
  - 8 built-in FlowRecorder strategies
110
- - `setEnableNarrative()` auto-attaches `CombinedNarrativeRecorder`
109
+ - Narrative via `executor.recorder(narrative())` at runtime
111
110
 
112
111
  ## Rules
113
112
 
114
- - Use `typedFlowChart<T>()` — scopeFactory is auto-embedded, no `createTypedScopeFactory` needed
113
+ - Use `flowChart<T>()` — scopeFactory is auto-embedded
115
114
  - Use `decide()` / `select()` in decider/selector functions
116
115
  - Use typed property access (not getValue/setValue)
117
116
  - Use `$getArgs()` for input, `$getEnv()` for environment
118
117
  - Never post-process the tree — use recorders
119
- - `setEnableNarrative()` is all you need for narrative setup
118
+ - Use `.recorder(narrative())` at runtime for narrative setup
@@ -12,7 +12,7 @@ This is the footprint.js library — the flowchart pattern for backend code. Sel
12
12
  src/lib/
13
13
  ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
14
14
  ├── schema/ → Validation (Zod optional, duck-typed)
15
- ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart(), typedFlowChart())
15
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
16
16
  ├── scope/ → Per-stage facades + recorders + providers
17
17
  ├── reactive/ → TypedScope<T> deep Proxy (typed property access, $-methods)
18
18
  ├── decide/ → decide()/select() decision evidence capture
@@ -26,7 +26,7 @@ Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
26
26
  ## Key API — TypedScope (Recommended)
27
27
 
28
28
  ```typescript
29
- import { typedFlowChart, FlowChartExecutor, decide } from 'footprintjs';
29
+ import { flowChart, FlowChartExecutor, decide } from 'footprintjs';
30
30
 
31
31
  interface State {
32
32
  creditScore: number;
@@ -34,11 +34,10 @@ interface State {
34
34
  decision?: string;
35
35
  }
36
36
 
37
- const chart = typedFlowChart<State>('Intake', async (scope) => {
37
+ const chart = flowChart<State>('Intake', async (scope) => {
38
38
  scope.creditScore = 750; // typed write (no setValue needed)
39
39
  scope.riskTier = 'low'; // typed write
40
40
  }, 'intake')
41
- .setEnableNarrative()
42
41
  .addDeciderFunction('Route', (scope) => {
43
42
  return decide(scope, [
44
43
  { when: { riskTier: { eq: 'low' } }, then: 'approved', label: 'Low risk' },
@@ -54,7 +53,7 @@ const chart = typedFlowChart<State>('Intake', async (scope) => {
54
53
  .end()
55
54
  .build();
56
55
 
57
- const executor = new FlowChartExecutor(chart<State>());
56
+ const executor = new FlowChartExecutor(chart);
58
57
  await executor.run();
59
58
  executor.getNarrative(); // causal trace with decision evidence
60
59
  ```
@@ -92,7 +91,7 @@ select(scope, [
92
91
  ### Executor
93
92
 
94
93
  ```typescript
95
- const executor = new FlowChartExecutor(chart<State>());
94
+ const executor = new FlowChartExecutor(chart);
96
95
  await executor.run({ input, env: { traceId: 'req-123' } });
97
96
  executor.getNarrative() // string[]
98
97
  executor.getNarrativeEntries() // CombinedNarrativeEntry[]
@@ -107,13 +106,13 @@ executor.setRedactionPolicy({ keys, patterns, fields })
107
106
  - **Scope Recorder**: fires DURING stage (`onRead`, `onWrite`, `onCommit`)
108
107
  - **FlowRecorder**: fires AFTER stage (`onStageExecuted`, `onDecision`, `onFork`, `onLoop`)
109
108
  - 8 built-in FlowRecorder strategies
110
- - `setEnableNarrative()` auto-attaches `CombinedNarrativeRecorder`
109
+ - Narrative via `executor.recorder(narrative())` at runtime
111
110
 
112
111
  ## Rules
113
112
 
114
- - Use `typedFlowChart<T>()` — scopeFactory is auto-embedded, no `createTypedScopeFactory` needed
113
+ - Use `flowChart<T>()` — scopeFactory is auto-embedded
115
114
  - Use `decide()` / `select()` in decider/selector functions
116
115
  - Use typed property access (not getValue/setValue)
117
116
  - Use `$getArgs()` for input, `$getEnv()` for environment
118
117
  - Never post-process the tree — use recorders
119
- - `setEnableNarrative()` is all you need for narrative setup
118
+ - Use `.recorder(narrative())` at runtime for narrative setup
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "footprintjs",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Turn your whiteboard flowchart into running code — with automatic causal traces for LLM reasoning",
5
5
  "license": "MIT",
6
6
  "author": "Sanjay Krishna Anbalagan",