footprintjs 1.0.0 → 1.0.1

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
@@ -1,125 +1,119 @@
1
- # footprint.js — AI Agent Instructions
1
+ # footprint.js — Agent Instructions
2
2
 
3
- > The flowchart pattern for backend code — self-explainable systems that AI can reason about.
4
-
5
- ## What This Library Does
6
-
7
- footprint.js structures backend logic as a graph of named functions with transactional state. Every run auto-generates a causal trace showing what happened and why. An LLM reads the trace and explains decisions accurately — no hallucination.
3
+ This is the footprint.js library — the flowchart pattern for backend code. Self-explainable systems that AI can reason about.
8
4
 
9
5
  ## Core Principle
10
6
 
11
- **Collect during traversal, never post-process.** All data collection (narrative, metrics, manifest) happens as side effects of the single DFS traversal pass. Never walk the tree again after execution.
7
+ **Collect during traversal, never post-process.** All data collection happens as side effects of the single DFS traversal. Never walk the tree after execution.
12
8
 
13
9
  ## Architecture
14
10
 
15
11
  ```
16
12
  src/lib/
17
13
  ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
18
- ├── schema/ → Validation (Zod optional, duck-typed detection)
19
- ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
14
+ ├── schema/ → Validation (Zod optional, duck-typed)
15
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart(), typedFlowChart())
20
16
  ├── scope/ → Per-stage facades + recorders + providers
17
+ ├── reactive/ → TypedScope<T> deep Proxy (typed property access, $-methods)
18
+ ├── decide/ → decide()/select() decision evidence capture
21
19
  ├── engine/ → DFS traversal + narrative + handlers
22
- ├── runner/ → High-level executor (FlowChartExecutor)
23
- └── contract/ → I/O schema + OpenAPI generation
20
+ ├── runner/ → FlowChartExecutor
21
+ └── contract/ → I/O schema + OpenAPI
24
22
  ```
25
23
 
26
- Two entry points:
27
- - `import { ... } from 'footprintjs'` — public API
28
- - `import { ... } from 'footprintjs/advanced'` — internals
24
+ Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
29
25
 
30
- ## Quick Start
26
+ ## Key API — TypedScope (Recommended)
31
27
 
32
28
  ```typescript
33
- import { flowChart, FlowChartExecutor } from 'footprintjs';
34
-
35
- const chart = flowChart('ReceiveOrder', (scope) => {
36
- scope.setValue('orderId', 'ORD-123');
37
- scope.setValue('amount', 49.99);
38
- }, 'receive-order', undefined, 'Receive the incoming order')
39
- .addFunction('ProcessPayment', (scope) => {
40
- const amount = scope.getValue('amount');
41
- scope.setValue('paymentStatus', amount < 100 ? 'approved' : 'review');
42
- }, 'process-payment', 'Charge customer')
29
+ import { typedFlowChart, createTypedScopeFactory, FlowChartExecutor, decide } from 'footprintjs';
30
+
31
+ interface State {
32
+ creditScore: number;
33
+ riskTier: string;
34
+ decision?: string;
35
+ }
36
+
37
+ const chart = typedFlowChart<State>('Intake', async (scope) => {
38
+ scope.creditScore = 750; // typed write (no setValue needed)
39
+ scope.riskTier = 'low'; // typed write
40
+ }, 'intake')
43
41
  .setEnableNarrative()
42
+ .addDeciderFunction('Route', (scope) => {
43
+ return decide(scope, [
44
+ { when: { riskTier: { eq: 'low' } }, then: 'approved', label: 'Low risk' },
45
+ ], 'rejected');
46
+ }, 'route', 'Route based on risk')
47
+ .addFunctionBranch('approved', 'Approve', async (scope) => {
48
+ scope.decision = 'Approved';
49
+ })
50
+ .addFunctionBranch('rejected', 'Reject', async (scope) => {
51
+ scope.decision = 'Rejected';
52
+ })
53
+ .setDefault('rejected')
54
+ .end()
44
55
  .build();
45
56
 
46
- const executor = new FlowChartExecutor(chart);
47
- await executor.run({ input: { orderId: 'ORD-123' } });
48
- console.log(executor.getNarrative());
49
- ```
50
-
51
- ## Builder API
52
-
53
- ```typescript
54
- flowChart(name, fn, id, extractor?, description?) // start chain
55
- .addFunction(name, fn, id, description?) // linear stage
56
- .addDeciderFunction(name, fn, id, description?) // single-choice branch
57
- .addFunctionBranch(branchId, name, fn) // branch option
58
- .addSubFlowChartBranch(branchId, subflow) // subflow branch
59
- .setDefault(branchId) // fallback
60
- .end() // close decider
61
- .addSelectorFunction(name, fn, id, description?) // multi-choice fan-out
62
- .addListOfFunction([...], { failFast? }) // parallel fork
63
- .addSubFlowChartNext(id, subflow, mount, opts?) // mount subflow inline
64
- .loopTo(stageId) // back-edge loop
65
- .setEnableNarrative() // enable narrative
66
- .setInputSchema(schema) // Zod or JSON Schema
67
- .build() // compile
68
- .toSpec() // JSON structure
69
- .toMermaid() // diagram
57
+ const executor = new FlowChartExecutor(chart, createTypedScopeFactory<State>());
58
+ await executor.run();
59
+ executor.getNarrative(); // causal trace with decision evidence
70
60
  ```
71
61
 
72
- ## Stage Function Signature
62
+ ### TypedScope $-methods (escape hatches)
73
63
 
74
64
  ```typescript
75
- (scope: ScopeFacade, breakPipeline: () => void, streamCallback?: StreamCallback) => void | Promise<void>
65
+ scope.$getArgs<T>() // frozen readonly input
66
+ scope.$getEnv() // execution environment (signal, timeoutMs, traceId)
67
+ scope.$break() // stop pipeline
68
+ scope.$debug(key, value) // debug info
69
+ scope.$metric(name, value) // metrics
76
70
  ```
77
71
 
78
- ## ScopeFacade (per-stage state access)
72
+ ### decide() / select()
79
73
 
80
74
  ```typescript
81
- scope.getValue('key') // tracked read appears in narrative
82
- scope.setValue('key', value) // tracked write → appears in narrative
83
- scope.updateValue('key', partial) // deep merge (tracked)
84
- scope.deleteValue('key') // tracked delete
85
- scope.getArgs<T>() // frozen readonly input (NOT tracked)
75
+ // Filter syntax captures operators + thresholds
76
+ decide(scope, [
77
+ { when: { creditScore: { gt: 700 }, dti: { lt: 0.43 } }, then: 'approved', label: 'Good credit' },
78
+ ], 'rejected');
79
+
80
+ // Function syntax — captures which keys were read
81
+ decide(scope, [
82
+ { when: (s) => s.creditScore > 700, then: 'approved' },
83
+ ], 'rejected');
84
+
85
+ // select() — all matching branches (not first-match)
86
+ select(scope, [
87
+ { when: { glucose: { gt: 100 } }, then: 'diabetes' },
88
+ { when: { bmi: { gt: 30 } }, then: 'obesity' },
89
+ ]);
86
90
  ```
87
91
 
88
- ## Executor
92
+ ### Executor
89
93
 
90
94
  ```typescript
91
- const executor = new FlowChartExecutor(chart);
92
- await executor.run({ input, timeoutMs?, signal? });
93
-
94
- executor.getNarrative() // string[] — combined flow + data
95
- executor.getNarrativeEntries() // CombinedNarrativeEntry[] — structured
96
- executor.getSnapshot() // full memory state
97
- executor.attachFlowRecorder(r) // plug observer before run()
98
- executor.setRedactionPolicy({ // PII protection
99
- keys: ['ssn'], patterns: [/password/i]
100
- })
95
+ const executor = new FlowChartExecutor(chart, createTypedScopeFactory<State>());
96
+ await executor.run({ input, env: { traceId: 'req-123' } });
97
+ executor.getNarrative() // string[]
98
+ executor.getNarrativeEntries() // CombinedNarrativeEntry[]
99
+ executor.getSnapshot() // memory state
100
+ executor.attachRecorder(recorder) // scope observer
101
+ executor.attachFlowRecorder(r) // flow observer
102
+ executor.setRedactionPolicy({ keys, patterns, fields })
101
103
  ```
102
104
 
103
- ## Two Observer Systems
105
+ ## Observer Systems
104
106
 
105
- **Scope Recorder** fires DURING stage: `onRead`, `onWrite`, `onCommit`, `onStageStart/End`
106
- **FlowRecorder** fires AFTER stage: `onStageExecuted`, `onDecision`, `onFork`, `onNext`, `onLoop`, `onError`
107
+ - **Scope Recorder**: fires DURING stage (`onRead`, `onWrite`, `onCommit`)
108
+ - **FlowRecorder**: fires AFTER stage (`onStageExecuted`, `onDecision`, `onFork`, `onLoop`)
109
+ - 8 built-in FlowRecorder strategies
110
+ - `setEnableNarrative()` auto-attaches `CombinedNarrativeRecorder`
107
111
 
108
- 8 built-in FlowRecorder strategies: Narrative, Adaptive, Windowed, RLE, Milestone, Progressive, Separate, Manifest, Silent.
109
-
110
- `CombinedNarrativeRecorder` implements BOTH — auto-attached by `setEnableNarrative()`.
111
-
112
- ## Anti-Patterns
112
+ ## Rules
113
113
 
114
+ - Use `typedFlowChart<T>()` + `createTypedScopeFactory<T>()` (not flowChart + ScopeFacade)
115
+ - Use `decide()` / `select()` in decider/selector functions
116
+ - Use typed property access (not getValue/setValue)
117
+ - Use `$getArgs()` for input, `$getEnv()` for environment
114
118
  - Never post-process the tree — use recorders
115
- - Never use deprecated `CombinedNarrativeBuilder`
116
- - Don't use `getArgs()` for tracked data — use `getValue()`/`setValue()`
117
- - Don't manually create `CombinedNarrativeRecorder` — `setEnableNarrative()` handles it
118
-
119
- ## Build
120
-
121
- ```bash
122
- npm install footprintjs
123
- npm run build # CJS + ESM dual output
124
- npm test
125
- ```
119
+ - `setEnableNarrative()` is all you need for narrative setup