footprintjs 0.10.1 → 0.10.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 ADDED
@@ -0,0 +1,125 @@
1
+ # footprint.js — AI Agent Instructions
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.
8
+
9
+ ## Core Principle
10
+
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.
12
+
13
+ ## Architecture
14
+
15
+ ```
16
+ src/lib/
17
+ ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
18
+ ├── schema/ → Validation (Zod optional, duck-typed detection)
19
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
20
+ ├── scope/ → Per-stage facades + recorders + providers
21
+ ├── engine/ → DFS traversal + narrative + handlers
22
+ ├── runner/ → High-level executor (FlowChartExecutor)
23
+ └── contract/ → I/O schema + OpenAPI generation
24
+ ```
25
+
26
+ Two entry points:
27
+ - `import { ... } from 'footprintjs'` — public API
28
+ - `import { ... } from 'footprintjs/advanced'` — internals
29
+
30
+ ## Quick Start
31
+
32
+ ```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')
43
+ .setEnableNarrative()
44
+ .build();
45
+
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
70
+ ```
71
+
72
+ ## Stage Function Signature
73
+
74
+ ```typescript
75
+ (scope: ScopeFacade, breakPipeline: () => void, streamCallback?: StreamCallback) => void | Promise<void>
76
+ ```
77
+
78
+ ## ScopeFacade (per-stage state access)
79
+
80
+ ```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)
86
+ ```
87
+
88
+ ## Executor
89
+
90
+ ```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
+ })
101
+ ```
102
+
103
+ ## Two Observer Systems
104
+
105
+ **Scope Recorder** — fires DURING stage: `onRead`, `onWrite`, `onCommit`, `onStageStart/End`
106
+ **FlowRecorder** — fires AFTER stage: `onStageExecuted`, `onDecision`, `onFork`, `onNext`, `onLoop`, `onError`
107
+
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
113
+
114
+ - 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
+ ```
package/CLAUDE.md ADDED
@@ -0,0 +1,127 @@
1
+ # footprint.js — AI Coding Instructions
2
+
3
+ This is the footprint.js library — the flowchart pattern for backend code. Self-explainable systems that AI can reason about.
4
+
5
+ ## Core Principle
6
+
7
+ **Collect during traversal, never post-process.** All data collection (narrative, metrics, manifest, identity) happens as side effects of the single DFS traversal pass. Never walk the tree again after execution.
8
+
9
+ ## Architecture — Library of Libraries
10
+
11
+ ```
12
+ src/lib/
13
+ ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer, EventLog)
14
+ ├── schema/ → Validation abstraction (Zod optional, duck-typed detection)
15
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart(), DeciderList, SelectorFnList)
16
+ ├── scope/ → Per-stage facades + recorders + providers (largest module)
17
+ ├── engine/ → DFS traversal + narrative + 13 handlers
18
+ ├── runner/ → High-level executor (FlowChartExecutor)
19
+ └── contract/ → I/O schema + OpenAPI generation
20
+ ```
21
+
22
+ Dependency DAG: `memory ← scope ← engine ← runner`, `schema ← engine`, `builder (standalone) → engine`, `contract ← schema`
23
+
24
+ Two entry points:
25
+ - `import { ... } from 'footprintjs'` — public API
26
+ - `import { ... } from 'footprintjs/advanced'` — internals
27
+
28
+ ## Key API
29
+
30
+ ### Builder
31
+
32
+ ```typescript
33
+ import { flowChart, FlowChartBuilder } from 'footprintjs';
34
+
35
+ // flowChart(name, fn, id, buildTimeExtractor?, description?)
36
+ const chart = flowChart('Stage1', fn1, 'stage-1', undefined, 'Description')
37
+ .addFunction('Stage2', fn2, 'stage-2', 'Description')
38
+ .addDeciderFunction('Decide', deciderFn, 'decide', 'Route based on risk')
39
+ .addFunctionBranch('high', 'Reject', rejectFn)
40
+ .addFunctionBranch('low', 'Approve', approveFn)
41
+ .setDefault('high')
42
+ .end()
43
+ .setEnableNarrative()
44
+ .build();
45
+ ```
46
+
47
+ Methods: `start()`, `addFunction()`, `addStreamingFunction()`, `addDeciderFunction()`, `addSelectorFunction()`, `addListOfFunction()`, `addSubFlowChart()`, `addSubFlowChartNext()`, `loopTo()`, `setEnableNarrative()`, `setInputSchema()`, `setOutputSchema()`, `setOutputMapper()`, `build()`, `toSpec()`, `toMermaid()`
48
+
49
+ ### Stage Functions
50
+
51
+ ```typescript
52
+ type PipelineStageFunction = (
53
+ scope: ScopeFacade,
54
+ breakPipeline: () => void,
55
+ streamCallback?: StreamCallback,
56
+ ) => Promise<void> | void;
57
+ ```
58
+
59
+ ### ScopeFacade
60
+
61
+ ```typescript
62
+ scope.getValue('key') // tracked read (appears in narrative)
63
+ scope.setValue('key', value) // tracked write
64
+ scope.updateValue('key', partial) // deep merge
65
+ scope.deleteValue('key') // tracked delete
66
+ scope.getArgs<T>() // frozen readonly input (NOT tracked)
67
+ scope.attachRecorder(recorder) // plug observer
68
+ ```
69
+
70
+ **IMPORTANT:** `getValue`/`setValue` are tracked. `getArgs()` is NOT tracked and returns frozen input.
71
+
72
+ ### Executor
73
+
74
+ ```typescript
75
+ const executor = new FlowChartExecutor(chart);
76
+ await executor.run({ input: data, timeoutMs: 5000, signal: abortSignal });
77
+
78
+ executor.getNarrative() // combined flow + data narrative
79
+ executor.getNarrativeEntries() // structured entries with type/depth/stageName
80
+ executor.getFlowNarrative() // flow-only (no data ops)
81
+ executor.getSnapshot() // full memory state
82
+ executor.attachFlowRecorder(r) // plug flow observer
83
+ executor.setRedactionPolicy({}) // PII protection
84
+ ```
85
+
86
+ ## Two Observer Systems
87
+
88
+ Both use `{ id, hooks } → dispatcher → error isolation → attach/detach`. Intentionally NOT unified.
89
+
90
+ **Scope Recorder** (data ops — fires DURING stage execution):
91
+ - `onRead`, `onWrite`, `onCommit`, `onError`, `onStageStart`, `onStageEnd`
92
+ - Built-in: `NarrativeRecorder`, `MetricRecorder`, `DebugRecorder`
93
+
94
+ **FlowRecorder** (control flow — fires AFTER stage execution):
95
+ - `onStageExecuted`, `onNext`, `onDecision`, `onFork`, `onSelected`, `onSubflowEntry/Exit`, `onLoop`, `onBreak`, `onError`
96
+ - Built-in: 8 strategies (Narrative, Adaptive, Windowed, RLE, Milestone, Progressive, Separate, Manifest, Silent)
97
+
98
+ **CombinedNarrativeRecorder** implements BOTH interfaces. Auto-attached by `setEnableNarrative()`.
99
+
100
+ ## Event Ordering
101
+
102
+ ```
103
+ 1. Recorder.onStageStart — stage begins
104
+ 2. Recorder.onRead/onWrite — DURING execution (buffered per-stage)
105
+ 3. Recorder.onCommit — transaction flush
106
+ 4. Recorder.onStageEnd — stage completes
107
+ 5. FlowRecorder.onStageExecuted — CombinedNarrativeRecorder flushes buffered ops
108
+ 6. FlowRecorder.onNext/onDecision/onFork — control flow continues
109
+ ```
110
+
111
+ ## Anti-Patterns
112
+
113
+ - Never post-process the tree — use recorders
114
+ - Never use deprecated `CombinedNarrativeBuilder` — use `CombinedNarrativeRecorder`
115
+ - Don't extract shared base for Recorder/FlowRecorder — two instances = coincidence
116
+ - Don't use `getArgs()` for tracked data — use `getValue()`/`setValue()`
117
+ - Don't manually create `CombinedNarrativeRecorder` — `setEnableNarrative()` handles it
118
+
119
+ ## Build & Test
120
+
121
+ ```bash
122
+ npm run build # tsc (CJS) + tsc -p tsconfig.esm.json (ESM)
123
+ npm test # full suite
124
+ npm run test:unit
125
+ ```
126
+
127
+ Dual output: CommonJS (`dist/`) + ESM (`dist/esm/`) + types (`dist/types/`)
package/README.md CHANGED
@@ -537,6 +537,54 @@ src/lib/
537
537
 
538
538
  ---
539
539
 
540
+ ## AI Coding Tool Support
541
+
542
+ FootPrint ships with built-in instructions for every major AI coding assistant. Your AI tool understands the library's API, patterns, and anti-patterns out of the box.
543
+
544
+ ### Quick setup
545
+
546
+ ```bash
547
+ # After installing footprintjs, run:
548
+ npx footprintjs-setup
549
+ ```
550
+
551
+ This interactive installer copies the right instruction files for your tool(s):
552
+
553
+ | Tool | What gets installed | Format |
554
+ |------|-------------------|--------|
555
+ | **[Claude Code](https://claude.com/claude-code)** | `.claude/skills/footprint/SKILL.md` + `CLAUDE.md` | Interactive skill + project rules |
556
+ | **[OpenAI Codex](https://openai.com/index/codex/)** | `AGENTS.md` | Agent instructions |
557
+ | **[GitHub Copilot](https://github.com/features/copilot)** | `.github/copilot-instructions.md` | Custom instructions |
558
+ | **[Cursor](https://cursor.com)** | `.cursor/rules/footprint.md` | Project rules |
559
+ | **[Windsurf](https://windsurf.com)** | `.windsurfrules` | Project rules |
560
+ | **[Cline](https://github.com/cline/cline)** | `.clinerules` | Project rules |
561
+ | **[Kiro](https://kiro.dev)** | `.kiro/rules/footprint.md` | Project rules |
562
+
563
+ Or install manually:
564
+
565
+ ```bash
566
+ # Claude Code skill
567
+ mkdir -p .claude/skills/footprint
568
+ cp node_modules/footprintjs/ai-instructions/claude-code/SKILL.md .claude/skills/footprint/
569
+
570
+ # Cursor rules
571
+ mkdir -p .cursor/rules
572
+ cp node_modules/footprintjs/ai-instructions/cursor/footprint.md .cursor/rules/
573
+
574
+ # Any other tool — files are in node_modules/footprintjs/ai-instructions/
575
+ ```
576
+
577
+ Every file teaches the AI assistant:
578
+ - The **Builder**, **Executor**, and **ScopeFacade** APIs
579
+ - The **recorder system** (scope + flow, two observer layers)
580
+ - The **core principle**: collect during traversal, never post-process
581
+ - **Anti-patterns** to avoid (deprecated APIs, wrong state access patterns)
582
+ - **Event ordering** that makes inline narrative collection work
583
+
584
+ This means any AI coding tool can help you build flowchart pipelines correctly from day one.
585
+
586
+ ---
587
+
540
588
  ## License
541
589
 
542
590
  [MIT](./LICENSE) &copy; [Sanjay Krishna Anbalagan](https://github.com/sanjay1909)
@@ -0,0 +1,465 @@
1
+ ---
2
+ name: footprint
3
+ description: Use when building flowchart pipelines with footprintjs — stage functions, decider branches, selectors, subflows, loops, narrative traces, recorders, redaction, contracts, and LLM-ready output. Also use when someone asks how footprint.js works or wants to understand the library.
4
+ ---
5
+
6
+ # footprint.js — The Flowchart Pattern for Backend Code
7
+
8
+ footprint.js structures backend logic as a graph of named functions with transactional state. The code becomes self-explainable: every run auto-generates a causal trace of what happened and why.
9
+
10
+ **Core principle:** All data collection happens during the single DFS traversal pass — never post-process or walk the tree again.
11
+
12
+ ```bash
13
+ npm install footprintjs
14
+ ```
15
+
16
+ ---
17
+
18
+ ## Quick Start
19
+
20
+ ```typescript
21
+ import { flowChart, FlowChartExecutor } from 'footprintjs';
22
+
23
+ const chart = flowChart('ReceiveOrder', (scope) => {
24
+ scope.setValue('orderId', 'ORD-123');
25
+ scope.setValue('amount', 49.99);
26
+ }, 'receive-order', undefined, 'Receive and validate the incoming order')
27
+ .addFunction('ProcessPayment', (scope) => {
28
+ const amount = scope.getValue('amount');
29
+ scope.setValue('paymentStatus', amount < 100 ? 'approved' : 'review');
30
+ }, 'process-payment', 'Charge customer and record payment status')
31
+ .setEnableNarrative()
32
+ .build();
33
+
34
+ const executor = new FlowChartExecutor(chart);
35
+ await executor.run({ input: { orderId: 'ORD-123' } });
36
+
37
+ console.log(executor.getNarrative());
38
+ // Stage 1: The process began with ReceiveOrder.
39
+ // Step 1: Write orderId = "ORD-123"
40
+ // Step 2: Write amount = 49.99
41
+ // Stage 2: Next, it moved on to ProcessPayment.
42
+ // Step 1: Read amount = 49.99
43
+ // Step 2: Write paymentStatus = "approved"
44
+ ```
45
+
46
+ ---
47
+
48
+ ## FlowChartBuilder API
49
+
50
+ Always chain from `flowChart()` or `new FlowChartBuilder()`.
51
+
52
+ ### Linear Stages
53
+
54
+ ```typescript
55
+ import { flowChart } from 'footprintjs';
56
+
57
+ const chart = flowChart('StageA', fnA, 'stage-a', undefined, 'Description of A')
58
+ .addFunction('StageB', fnB, 'stage-b', 'Description of B')
59
+ .addFunction('StageC', fnC, 'stage-c', 'Description of C')
60
+ .build();
61
+ ```
62
+
63
+ **Parameters:** `(name: string, fn: PipelineStageFunction, id: string, description?: string)`
64
+
65
+ - `name` — human-readable label (used in narrative)
66
+ - `fn` — the stage function
67
+ - `id` — stable identifier (used for branching, visualization, loop targets)
68
+ - `description` — optional, appears in narrative and auto-generated tool descriptions
69
+
70
+ ### Stage Function Signature
71
+
72
+ ```typescript
73
+ type PipelineStageFunction = (
74
+ scope: ScopeFacade, // read/write transactional state
75
+ breakPipeline: () => void, // call to stop execution early
76
+ streamCallback?: StreamCallback, // for streaming stages
77
+ ) => Promise<void> | void;
78
+ ```
79
+
80
+ ### ScopeFacade — State Access
81
+
82
+ Every stage receives a `ScopeFacade` that tracks all reads and writes:
83
+
84
+ ```typescript
85
+ const myStage = (scope: ScopeFacade) => {
86
+ // Read (tracked — appears in narrative)
87
+ const name = scope.getValue('applicantName') as string;
88
+
89
+ // Write (tracked — appears in narrative)
90
+ scope.setValue('greeting', `Hello, ${name}!`);
91
+
92
+ // Update (deep merge for objects)
93
+ scope.updateValue('profile', { verified: true });
94
+
95
+ // Delete
96
+ scope.deleteValue('tempData');
97
+
98
+ // Readonly input (frozen, not tracked in narrative)
99
+ const config = scope.getArgs<{ maxRetries: number }>();
100
+ };
101
+ ```
102
+
103
+ **IMPORTANT:** `getValue`/`setValue` are tracked and produce narrative. `getArgs()` returns frozen readonly input and is NOT tracked.
104
+
105
+ ### Decider Branches (Single-Choice Conditional)
106
+
107
+ ```typescript
108
+ const chart = flowChart('Intake', intakeFn, 'intake')
109
+ .addDeciderFunction('AssessRisk', (scope) => {
110
+ const score = scope.getValue('riskScore') as number;
111
+ // Return the branch ID to take
112
+ return score > 70 ? 'high-risk' : 'low-risk';
113
+ }, 'assess-risk', 'Evaluate risk and route accordingly')
114
+ .addFunctionBranch('high-risk', 'RejectApplication', rejectFn, 'Reject due to high risk')
115
+ .addFunctionBranch('low-risk', 'ApproveApplication', approveFn, 'Approve the application')
116
+ .setDefault('high-risk') // fallback if branch ID doesn't match
117
+ .end()
118
+ .build();
119
+ ```
120
+
121
+ The decider function **returns a branch ID string**. The engine matches it to a child and executes that branch. The decision is recorded in the narrative.
122
+
123
+ ### Selector Branches (Multi-Choice Fan-Out)
124
+
125
+ ```typescript
126
+ const chart = flowChart('Intake', intakeFn, 'intake')
127
+ .addSelectorFunction('SelectChecks', (scope) => {
128
+ const checks = [];
129
+ if (scope.getValue('needsCredit')) checks.push('credit-check');
130
+ if (scope.getValue('needsIdentity')) checks.push('identity-check');
131
+ // Return array of branch IDs to execute in parallel
132
+ return checks;
133
+ }, 'select-checks')
134
+ .addFunctionBranch('credit-check', 'CreditCheck', creditFn)
135
+ .addFunctionBranch('identity-check', 'IdentityCheck', identityFn)
136
+ .end()
137
+ .build();
138
+ ```
139
+
140
+ ### Parallel Execution (Fork)
141
+
142
+ ```typescript
143
+ builder.addListOfFunction([
144
+ { id: 'check-a', name: 'CheckA', fn: checkAFn },
145
+ { id: 'check-b', name: 'CheckB', fn: checkBFn },
146
+ { id: 'check-c', name: 'CheckC', fn: checkCFn },
147
+ ], { failFast: true }); // reject on first error
148
+ ```
149
+
150
+ ### Subflows (Nested Flowcharts)
151
+
152
+ ```typescript
153
+ // Build a reusable sub-pipeline
154
+ const creditSubflow = flowChart('PullReport', pullReportFn, 'pull-report')
155
+ .addFunction('ScoreReport', scoreReportFn, 'score-report')
156
+ .build();
157
+
158
+ // Mount as linear continuation
159
+ builder.addSubFlowChartNext('credit-sub', creditSubflow, 'CreditCheck', {
160
+ inputMapper: (parentScope) => ({ ssn: parentScope.getValue('ssn') }),
161
+ outputMapper: (subOut, parentScope) => ({ creditScore: subOut.score }),
162
+ });
163
+
164
+ // Mount as decider branch
165
+ builder.addDeciderFunction('Route', routerFn, 'route')
166
+ .addSubFlowChartBranch('detailed', creditSubflow, 'DetailedCheck')
167
+ .addFunctionBranch('simple', 'SimpleCheck', simpleFn)
168
+ .end();
169
+ ```
170
+
171
+ ### Loops
172
+
173
+ ```typescript
174
+ builder
175
+ .addFunction('RetryPayment', async (scope) => {
176
+ const attempts = (scope.getValue('attempts') as number ?? 0) + 1;
177
+ scope.setValue('attempts', attempts);
178
+ if (attempts >= 3) return; // exit loop by not looping
179
+ }, 'retry-payment')
180
+ .loopTo('retry-payment'); // back-edge to this stage's ID
181
+ ```
182
+
183
+ ### Configuration
184
+
185
+ ```typescript
186
+ builder
187
+ .setEnableNarrative() // enable narrative recording
188
+ .setInputSchema(zodSchema) // validate input (Zod or JSON Schema)
189
+ .setOutputSchema(outputZodSchema) // declare output shape
190
+ .setOutputMapper((state) => ({ // map final state to response
191
+ decision: state.decision,
192
+ reason: state.reason,
193
+ }));
194
+ ```
195
+
196
+ ### Output
197
+
198
+ ```typescript
199
+ const chart = builder.build(); // FlowChart object (for executor)
200
+ const spec = builder.toSpec(); // JSON-safe structure (for visualization)
201
+ const mermaid = builder.toMermaid(); // Mermaid diagram string
202
+ ```
203
+
204
+ ---
205
+
206
+ ## FlowChartExecutor API
207
+
208
+ ```typescript
209
+ import { FlowChartExecutor } from 'footprintjs';
210
+
211
+ const executor = new FlowChartExecutor(chart);
212
+
213
+ // Run with input
214
+ const result = await executor.run({
215
+ input: { applicantName: 'Bob', income: 42000 },
216
+ timeoutMs: 5000, // optional auto-abort
217
+ signal: controller.signal, // optional AbortSignal
218
+ });
219
+
220
+ // Get narrative (combined flow + data operations)
221
+ const narrative: string[] = executor.getNarrative();
222
+ // ["Stage 1: The process began with ReceiveApplication.",
223
+ // " Step 1: Write app = {applicantName, income, ...}",
224
+ // "Stage 2: Next, it moved on to AssessRisk.",
225
+ // " Step 1: Read app = ...",
226
+ // " Step 2: Write riskTier = \"high\"",
227
+ // "[Condition]: A decision was made, path taken was RejectApplication."]
228
+
229
+ // Structured entries (for programmatic access)
230
+ const entries: CombinedNarrativeEntry[] = executor.getNarrativeEntries();
231
+ // [{ type: 'stage', text: '...', depth: 0, stageName: 'ReceiveApplication' },
232
+ // { type: 'step', text: 'Write app = ...', depth: 1, stageName: 'ReceiveApplication', stepNumber: 1 },
233
+ // { type: 'condition', text: '...', depth: 0 }]
234
+
235
+ // Full memory snapshot
236
+ const snapshot = executor.getSnapshot();
237
+ // { sharedState: { ... }, commitLog: [...], subflowResults: Map }
238
+
239
+ // Flow-only narrative (no data operations)
240
+ const flowOnly: string[] = executor.getFlowNarrative();
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Recorder System — Collect During Traversal
246
+
247
+ **The core innovation.** Two observer layers fire during the single DFS pass:
248
+
249
+ ### Scope Recorders (data operations)
250
+
251
+ Attached to `ScopeFacade`, fire during `getValue()`/`setValue()`:
252
+
253
+ ```typescript
254
+ import { MetricRecorder, DebugRecorder, NarrativeRecorder } from 'footprintjs';
255
+
256
+ // Built-in recorders
257
+ const metrics = new MetricRecorder();
258
+ const debug = new DebugRecorder('verbose'); // or 'minimal'
259
+
260
+ // Attach to scope (usually via scope factory)
261
+ scope.attachRecorder(metrics);
262
+ scope.attachRecorder(debug);
263
+
264
+ // After execution
265
+ metrics.getSummary(); // { totalReads: 12, totalWrites: 8, stages: {...} }
266
+ debug.getEntries(); // [{ type: 'read', key: 'app', value: {...}, stage: 'Intake' }, ...]
267
+ ```
268
+
269
+ ### FlowRecorders (control flow events)
270
+
271
+ Attached to executor, fire after each stage/decision/fork:
272
+
273
+ ```typescript
274
+ import { NarrativeFlowRecorder, AdaptiveNarrativeFlowRecorder } from 'footprintjs';
275
+
276
+ // Attach before run()
277
+ executor.attachFlowRecorder(new NarrativeFlowRecorder());
278
+
279
+ // 8 built-in strategies:
280
+ // NarrativeFlowRecorder — all events as sentences (default)
281
+ // AdaptiveNarrativeFlowRecorder — full detail then sampling for loops
282
+ // WindowedNarrativeFlowRecorder — keep last N iterations only
283
+ // RLENarrativeFlowRecorder — run-length encode repeated loops
284
+ // MilestoneNarrativeFlowRecorder — only decisions, errors, subflows
285
+ // ProgressiveNarrativeFlowRecorder — progress markers for streaming UIs
286
+ // SeparateNarrativeFlowRecorder — dual channels (main + loop detail)
287
+ // ManifestFlowRecorder — subflow tree + spec catalog for LLM exploration
288
+ ```
289
+
290
+ ### Custom FlowRecorder
291
+
292
+ ```typescript
293
+ import type { FlowRecorder, FlowStageEvent, FlowDecisionEvent } from 'footprintjs';
294
+
295
+ const myRecorder: FlowRecorder = {
296
+ id: 'my-recorder',
297
+ onStageExecuted(event: FlowStageEvent) {
298
+ console.log(`Executed: ${event.stageName}`);
299
+ },
300
+ onDecision(event: FlowDecisionEvent) {
301
+ console.log(`Decision at ${event.stageName}: chose ${event.chosen}`);
302
+ },
303
+ clear() {
304
+ // Reset state before each run
305
+ },
306
+ };
307
+
308
+ executor.attachFlowRecorder(myRecorder);
309
+ ```
310
+
311
+ ### CombinedNarrativeRecorder (the inline dual-channel recorder)
312
+
313
+ 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.
314
+
315
+ **You don't need to create this manually.** Calling `.setEnableNarrative()` on the builder auto-attaches it.
316
+
317
+ ---
318
+
319
+ ## Redaction (PII Protection)
320
+
321
+ ```typescript
322
+ executor.setRedactionPolicy({
323
+ keys: ['ssn', 'creditCardNumber'], // exact key names
324
+ patterns: [/password/i, /^secret.*/], // regex patterns
325
+ fields: { applicant: ['ssn', 'address.zip'] }, // nested field paths
326
+ });
327
+
328
+ await executor.run({ input: { ... } });
329
+
330
+ // Narrative shows: Write ssn = [REDACTED]
331
+ // Recorders receive scrubbed values
332
+ const report = executor.getRedactionReport();
333
+ // { redactedKeys: ['ssn'], patternsMatched: [...] } — never contains actual values
334
+ ```
335
+
336
+ ---
337
+
338
+ ## Contracts & OpenAPI
339
+
340
+ ```typescript
341
+ import { defineContract, generateOpenAPI } from 'footprintjs';
342
+ import { z } from 'zod';
343
+
344
+ const contract = defineContract(chart, {
345
+ inputSchema: z.object({
346
+ applicantName: z.string(),
347
+ income: z.number(),
348
+ }),
349
+ outputSchema: z.object({
350
+ decision: z.enum(['approved', 'rejected']),
351
+ reason: z.string(),
352
+ }),
353
+ outputMapper: (state) => ({
354
+ decision: state.decision,
355
+ reason: state.reason,
356
+ }),
357
+ });
358
+
359
+ const openApiSpec = generateOpenAPI(contract, {
360
+ title: 'Loan Underwriting API',
361
+ version: '1.0.0',
362
+ });
363
+ ```
364
+
365
+ ---
366
+
367
+ ## Event Ordering (Critical for Understanding)
368
+
369
+ When a stage executes, events fire in this exact order:
370
+
371
+ ```
372
+ 1. Recorder.onStageStart — stage begins
373
+ 2. Recorder.onRead — each getValue() call (DURING execution)
374
+ 3. Recorder.onWrite — each setValue() call (DURING execution)
375
+ 4. Recorder.onCommit — transaction buffer flushes to shared memory
376
+ 5. Recorder.onStageEnd — stage completes
377
+ 6. FlowRecorder.onStageExecuted — control flow records the stage
378
+ (CombinedNarrativeRecorder flushes buffered ops here)
379
+ 7. FlowRecorder.onNext — moving to next stage
380
+ OR FlowRecorder.onDecision — if this was a decider
381
+ OR FlowRecorder.onFork — if children execute in parallel
382
+ ```
383
+
384
+ **This ordering is what makes inline collection work.** Scope events buffer during execution, flow events trigger the flush.
385
+
386
+ ---
387
+
388
+ ## Anti-Patterns to Avoid
389
+
390
+ 1. **Never post-process the tree.** Don't walk the spec after execution to collect data. Use recorders.
391
+ 2. **Never use `CombinedNarrativeBuilder`** — it's deprecated. Use `CombinedNarrativeRecorder` (auto-attached by `setEnableNarrative()`).
392
+ 3. **Don't extract a shared base class** for Recorder and FlowRecorder. They look similar but serve different layers. Two instances = coincidence.
393
+ 4. **Don't call `getArgs()` for tracked data.** `getArgs()` returns frozen readonly input. Use `getValue()`/`setValue()` for state that should appear in the narrative.
394
+ 5. **Don't create scope recorders manually** unless building a custom recorder. `setEnableNarrative()` handles everything.
395
+
396
+ ---
397
+
398
+ ## Library Structure (for contributors)
399
+
400
+ ```
401
+ src/lib/
402
+ ├── memory/ → SharedMemory, StageContext, TransactionBuffer, EventLog (foundation)
403
+ ├── schema/ → detectSchema, validate, InputValidationError (foundation)
404
+ ├── builder/ → FlowChartBuilder, flowChart(), DeciderList, SelectorFnList (standalone)
405
+ ├── scope/ → ScopeFacade, recorders/, providers/, protection/ (depends: memory)
406
+ ├── engine/ → FlowchartTraverser, handlers/, narrative/ (depends: memory, scope, builder)
407
+ ├── runner/ → FlowChartExecutor, ExecutionRuntime (depends: engine, scope, schema)
408
+ └── contract/ → defineContract, generateOpenAPI (depends: schema)
409
+ ```
410
+
411
+ Two entry points:
412
+ - `import { ... } from 'footprintjs'` — public API
413
+ - `import { ... } from 'footprintjs/advanced'` — internals (memory, traverser, handlers)
414
+
415
+ ---
416
+
417
+ ## Common Patterns
418
+
419
+ ### Pipeline with decision + narrative
420
+
421
+ ```typescript
422
+ const chart = flowChart('Receive', receiveFn, 'receive')
423
+ .addFunction('Analyze', analyzeFn, 'analyze')
424
+ .addDeciderFunction('Decide', decideFn, 'decide')
425
+ .addFunctionBranch('approve', 'Approve', approveFn)
426
+ .addFunctionBranch('reject', 'Reject', rejectFn)
427
+ .setDefault('reject')
428
+ .end()
429
+ .setEnableNarrative()
430
+ .build();
431
+
432
+ const executor = new FlowChartExecutor(chart);
433
+ await executor.run({ input: data });
434
+ const trace = executor.getNarrative();
435
+ // Feed trace to LLM for grounded explanations
436
+ ```
437
+
438
+ ### Subflow with input/output mapping
439
+
440
+ ```typescript
441
+ const subflow = flowChart('SubStart', subStartFn, 'sub-start')
442
+ .addFunction('SubProcess', subProcessFn, 'sub-process')
443
+ .build();
444
+
445
+ const main = flowChart('Main', mainFn, 'main')
446
+ .addSubFlowChartNext('my-subflow', subflow, 'SubflowMount', {
447
+ inputMapper: (scope) => ({ key: scope.getValue('parentKey') }),
448
+ outputMapper: (subOut) => ({ result: subOut.processed }),
449
+ })
450
+ .build();
451
+ ```
452
+
453
+ ### Attach multiple recorders
454
+
455
+ ```typescript
456
+ import { ManifestFlowRecorder, MilestoneNarrativeFlowRecorder } from 'footprintjs';
457
+
458
+ executor.attachFlowRecorder(new ManifestFlowRecorder());
459
+ executor.attachFlowRecorder(new MilestoneNarrativeFlowRecorder());
460
+
461
+ await executor.run({ input: data });
462
+
463
+ const manifest = executor.getSubflowManifest(); // subflow catalog
464
+ const milestones = executor.getFlowNarrative(); // key events only
465
+ ```
@@ -0,0 +1,79 @@
1
+ # footprint.js — Cline Rules
2
+
3
+ This is the footprint.js library — the flowchart pattern for backend code. Self-explainable systems that AI can reason about.
4
+
5
+ ## Core Principle
6
+
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.
8
+
9
+ ## Architecture
10
+
11
+ ```
12
+ src/lib/
13
+ ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
14
+ ├── schema/ → Validation (Zod optional, duck-typed)
15
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
16
+ ├── scope/ → Per-stage facades + recorders + providers
17
+ ├── engine/ → DFS traversal + narrative + handlers
18
+ ├── runner/ → FlowChartExecutor
19
+ └── contract/ → I/O schema + OpenAPI
20
+ ```
21
+
22
+ Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
23
+
24
+ ## Builder API
25
+
26
+ ```typescript
27
+ flowChart(name, fn, id, extractor?, description?)
28
+ .addFunction(name, fn, id, description?)
29
+ .addDeciderFunction(name, fn, id, description?)
30
+ .addFunctionBranch(branchId, name, fn) / .setDefault(id) / .end()
31
+ .addSelectorFunction(name, fn, id, description?)
32
+ .addListOfFunction([...], { failFast? })
33
+ .addSubFlowChartNext(id, subflow, mount, { inputMapper?, outputMapper? })
34
+ .loopTo(stageId)
35
+ .setEnableNarrative()
36
+ .build() / .toSpec() / .toMermaid()
37
+ ```
38
+
39
+ ## Stage Functions
40
+
41
+ ```typescript
42
+ (scope: ScopeFacade, breakPipeline: () => void, streamCallback?: StreamCallback) => void | Promise<void>
43
+ ```
44
+
45
+ ## ScopeFacade
46
+
47
+ ```typescript
48
+ scope.getValue('key') // tracked read → narrative
49
+ scope.setValue('key', value) // tracked write → narrative
50
+ scope.updateValue('key', partial) // deep merge (tracked)
51
+ scope.deleteValue('key') // tracked delete
52
+ scope.getArgs<T>() // frozen readonly input (NOT tracked)
53
+ ```
54
+
55
+ ## Executor
56
+
57
+ ```typescript
58
+ const executor = new FlowChartExecutor(chart);
59
+ await executor.run({ input, timeoutMs?, signal? });
60
+ executor.getNarrative() // combined flow + data
61
+ executor.getNarrativeEntries() // structured entries
62
+ executor.getSnapshot() // memory state
63
+ executor.attachFlowRecorder(r) // plug flow observer
64
+ executor.setRedactionPolicy({ keys, patterns, fields })
65
+ ```
66
+
67
+ ## Observer Systems
68
+
69
+ - **Scope Recorder**: fires DURING stage (`onRead`, `onWrite`, `onCommit`)
70
+ - **FlowRecorder**: fires AFTER stage (`onStageExecuted`, `onDecision`, `onFork`, `onLoop`)
71
+ - 8 strategies: Narrative, Adaptive, Windowed, RLE, Milestone, Progressive, Separate, Manifest
72
+ - `CombinedNarrativeRecorder` implements both — auto-attached by `setEnableNarrative()`
73
+
74
+ ## Rules
75
+
76
+ - Never post-process the tree — use recorders
77
+ - `getValue()`/`setValue()` for tracked state; `getArgs()` for frozen input
78
+ - Don't use deprecated `CombinedNarrativeBuilder`
79
+ - `setEnableNarrative()` is all you need
@@ -0,0 +1,86 @@
1
+ # footprint.js — Copilot Instructions
2
+
3
+ This is the footprint.js library — the flowchart pattern for backend code.
4
+
5
+ ## What It Does
6
+
7
+ Structures backend logic as a graph of named functions with transactional state. Every run auto-generates a causal trace of what happened and why. LLMs read the trace for grounded explanations — no hallucination.
8
+
9
+ ## Core Principle
10
+
11
+ **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
+
13
+ ## Architecture
14
+
15
+ ```
16
+ src/lib/
17
+ ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
18
+ ├── schema/ → Validation (Zod optional, duck-typed)
19
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
20
+ ├── scope/ → Per-stage facades + recorders + providers
21
+ ├── engine/ → DFS traversal + narrative + handlers
22
+ ├── runner/ → FlowChartExecutor
23
+ └── contract/ → I/O schema + OpenAPI
24
+ ```
25
+
26
+ Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
27
+
28
+ ## Key API
29
+
30
+ ### Builder Chain
31
+
32
+ ```typescript
33
+ flowChart(name, fn, id, extractor?, description?)
34
+ .addFunction(name, fn, id, description?)
35
+ .addDeciderFunction(name, fn, id, description?)
36
+ .addFunctionBranch(branchId, name, fn) / .setDefault(id) / .end()
37
+ .addSelectorFunction(name, fn, id, description?)
38
+ .addListOfFunction([...], { failFast? })
39
+ .addSubFlowChartNext(id, subflow, mount, { inputMapper?, outputMapper? })
40
+ .loopTo(stageId)
41
+ .setEnableNarrative()
42
+ .setInputSchema(schema) / .setOutputSchema(schema) / .setOutputMapper(fn)
43
+ .build() / .toSpec() / .toMermaid()
44
+ ```
45
+
46
+ ### Stage Functions
47
+
48
+ ```typescript
49
+ (scope: ScopeFacade, breakPipeline: () => void, streamCallback?: StreamCallback) => void | Promise<void>
50
+ ```
51
+
52
+ ### ScopeFacade
53
+
54
+ ```typescript
55
+ scope.getValue('key') // tracked read
56
+ scope.setValue('key', value) // tracked write
57
+ scope.updateValue('key', partial) // deep merge
58
+ scope.deleteValue('key') // tracked delete
59
+ scope.getArgs<T>() // frozen readonly input (NOT tracked)
60
+ ```
61
+
62
+ ### Executor
63
+
64
+ ```typescript
65
+ const executor = new FlowChartExecutor(chart);
66
+ await executor.run({ input, timeoutMs?, signal? });
67
+ executor.getNarrative() // string[]
68
+ executor.getNarrativeEntries() // CombinedNarrativeEntry[]
69
+ executor.getSnapshot() // memory state
70
+ executor.attachFlowRecorder(r) // plug observer
71
+ executor.setRedactionPolicy({ keys, patterns, fields })
72
+ ```
73
+
74
+ ## Observer Systems
75
+
76
+ - **Scope Recorder**: fires DURING stage (`onRead`, `onWrite`, `onCommit`)
77
+ - **FlowRecorder**: fires AFTER stage (`onStageExecuted`, `onDecision`, `onFork`, `onLoop`)
78
+ - 8 built-in FlowRecorder strategies (Narrative, Adaptive, Windowed, RLE, Milestone, Progressive, Separate, Manifest)
79
+ - `setEnableNarrative()` auto-attaches `CombinedNarrativeRecorder` (implements both)
80
+
81
+ ## Rules
82
+
83
+ - Never post-process the tree — use recorders
84
+ - `getValue()`/`setValue()` for tracked state; `getArgs()` for frozen readonly input
85
+ - Don't use deprecated `CombinedNarrativeBuilder`
86
+ - `setEnableNarrative()` is all you need for narrative setup
@@ -0,0 +1,80 @@
1
+ # footprint.js — Cursor Rules
2
+
3
+ This is the footprint.js library — the flowchart pattern for backend code. Self-explainable systems that AI can reason about.
4
+
5
+ ## Core Principle
6
+
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.
8
+
9
+ ## Architecture
10
+
11
+ ```
12
+ src/lib/
13
+ ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
14
+ ├── schema/ → Validation (Zod optional, duck-typed)
15
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
16
+ ├── scope/ → Per-stage facades + recorders + providers
17
+ ├── engine/ → DFS traversal + narrative + handlers
18
+ ├── runner/ → FlowChartExecutor
19
+ └── contract/ → I/O schema + OpenAPI
20
+ ```
21
+
22
+ Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
23
+
24
+ ## Builder API
25
+
26
+ ```typescript
27
+ flowChart(name, fn, id, extractor?, description?)
28
+ .addFunction(name, fn, id, description?)
29
+ .addDeciderFunction(name, fn, id, description?)
30
+ .addFunctionBranch(branchId, name, fn) / .setDefault(id) / .end()
31
+ .addSelectorFunction(name, fn, id, description?)
32
+ .addListOfFunction([...], { failFast? })
33
+ .addSubFlowChartNext(id, subflow, mount, { inputMapper?, outputMapper? })
34
+ .loopTo(stageId)
35
+ .setEnableNarrative()
36
+ .build() / .toSpec() / .toMermaid()
37
+ ```
38
+
39
+ ## Stage Function Signature
40
+
41
+ ```typescript
42
+ (scope: ScopeFacade, breakPipeline: () => void, streamCallback?: StreamCallback) => void | Promise<void>
43
+ ```
44
+
45
+ ## ScopeFacade
46
+
47
+ ```typescript
48
+ scope.getValue('key') // tracked read → narrative
49
+ scope.setValue('key', value) // tracked write → narrative
50
+ scope.updateValue('key', partial) // deep merge (tracked)
51
+ scope.deleteValue('key') // tracked delete
52
+ scope.getArgs<T>() // frozen readonly input (NOT tracked)
53
+ ```
54
+
55
+ ## Executor
56
+
57
+ ```typescript
58
+ const executor = new FlowChartExecutor(chart);
59
+ await executor.run({ input, timeoutMs?, signal? });
60
+ executor.getNarrative() // combined flow + data
61
+ executor.getNarrativeEntries() // structured entries
62
+ executor.getSnapshot() // memory state
63
+ executor.attachFlowRecorder(r) // plug flow observer
64
+ executor.setRedactionPolicy({ keys, patterns, fields })
65
+ ```
66
+
67
+ ## Two Observer Systems (intentionally separate)
68
+
69
+ - **Scope Recorder**: `onRead`, `onWrite`, `onCommit`, `onStageStart/End` — fires DURING execution
70
+ - **FlowRecorder**: `onStageExecuted`, `onDecision`, `onFork`, `onNext`, `onLoop` — fires AFTER stage
71
+ - 8 strategies: Narrative, Adaptive, Windowed, RLE, Milestone, Progressive, Separate, Manifest
72
+ - `CombinedNarrativeRecorder` implements both — auto-attached by `setEnableNarrative()`
73
+
74
+ ## Rules
75
+
76
+ - Never post-process the tree — use recorders
77
+ - `getValue()`/`setValue()` for tracked state; `getArgs()` for frozen readonly input
78
+ - Don't use deprecated `CombinedNarrativeBuilder` — use `CombinedNarrativeRecorder`
79
+ - Don't extract shared base for Recorder/FlowRecorder — coincidence, not pattern
80
+ - `setEnableNarrative()` is all you need
@@ -0,0 +1,79 @@
1
+ # footprint.js — Kiro Rules
2
+
3
+ This is the footprint.js library — the flowchart pattern for backend code. Self-explainable systems that AI can reason about.
4
+
5
+ ## Core Principle
6
+
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.
8
+
9
+ ## Architecture
10
+
11
+ ```
12
+ src/lib/
13
+ ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
14
+ ├── schema/ → Validation (Zod optional, duck-typed)
15
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
16
+ ├── scope/ → Per-stage facades + recorders + providers
17
+ ├── engine/ → DFS traversal + narrative + handlers
18
+ ├── runner/ → FlowChartExecutor
19
+ └── contract/ → I/O schema + OpenAPI
20
+ ```
21
+
22
+ Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
23
+
24
+ ## Builder API
25
+
26
+ ```typescript
27
+ flowChart(name, fn, id, extractor?, description?)
28
+ .addFunction(name, fn, id, description?)
29
+ .addDeciderFunction(name, fn, id, description?)
30
+ .addFunctionBranch(branchId, name, fn) / .setDefault(id) / .end()
31
+ .addSelectorFunction(name, fn, id, description?)
32
+ .addListOfFunction([...], { failFast? })
33
+ .addSubFlowChartNext(id, subflow, mount, { inputMapper?, outputMapper? })
34
+ .loopTo(stageId)
35
+ .setEnableNarrative()
36
+ .build() / .toSpec() / .toMermaid()
37
+ ```
38
+
39
+ ## Stage Functions
40
+
41
+ ```typescript
42
+ (scope: ScopeFacade, breakPipeline: () => void, streamCallback?: StreamCallback) => void | Promise<void>
43
+ ```
44
+
45
+ ## ScopeFacade
46
+
47
+ ```typescript
48
+ scope.getValue('key') // tracked read → narrative
49
+ scope.setValue('key', value) // tracked write → narrative
50
+ scope.updateValue('key', partial) // deep merge (tracked)
51
+ scope.deleteValue('key') // tracked delete
52
+ scope.getArgs<T>() // frozen readonly input (NOT tracked)
53
+ ```
54
+
55
+ ## Executor
56
+
57
+ ```typescript
58
+ const executor = new FlowChartExecutor(chart);
59
+ await executor.run({ input, timeoutMs?, signal? });
60
+ executor.getNarrative() // combined flow + data
61
+ executor.getNarrativeEntries() // structured entries
62
+ executor.getSnapshot() // memory state
63
+ executor.attachFlowRecorder(r) // plug flow observer
64
+ executor.setRedactionPolicy({ keys, patterns, fields })
65
+ ```
66
+
67
+ ## Observer Systems
68
+
69
+ - **Scope Recorder**: fires DURING stage (`onRead`, `onWrite`, `onCommit`)
70
+ - **FlowRecorder**: fires AFTER stage (`onStageExecuted`, `onDecision`, `onFork`, `onLoop`)
71
+ - 8 strategies: Narrative, Adaptive, Windowed, RLE, Milestone, Progressive, Separate, Manifest
72
+ - `CombinedNarrativeRecorder` implements both — auto-attached by `setEnableNarrative()`
73
+
74
+ ## Rules
75
+
76
+ - Never post-process the tree — use recorders
77
+ - `getValue()`/`setValue()` for tracked state; `getArgs()` for frozen input
78
+ - Don't use deprecated `CombinedNarrativeBuilder`
79
+ - `setEnableNarrative()` is all you need
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env bash
2
+ # footprint.js — AI coding tool setup
3
+ # Installs instruction files for your preferred AI coding assistant.
4
+ #
5
+ # Usage:
6
+ # npx footprintjs-setup (after npm install footprintjs)
7
+ # bash node_modules/footprintjs/ai-instructions/setup.sh
8
+
9
+ set -euo pipefail
10
+
11
+ PKG_DIR="$(cd "$(dirname "$0")" && pwd)"
12
+ PROJECT_DIR="$(pwd)"
13
+
14
+ echo ""
15
+ echo " footprint.js — AI Coding Tool Setup"
16
+ echo " ──────────────────────────────────────"
17
+ echo ""
18
+ echo " This will copy instruction files so your AI coding"
19
+ echo " assistant understands the footprint.js API."
20
+ echo ""
21
+
22
+ install_claude_code() {
23
+ mkdir -p "$PROJECT_DIR/.claude/skills/footprint"
24
+ cp "$PKG_DIR/claude-code/SKILL.md" "$PROJECT_DIR/.claude/skills/footprint/SKILL.md"
25
+ # Also copy CLAUDE.md to project root if not present
26
+ if [ ! -f "$PROJECT_DIR/CLAUDE.md" ]; then
27
+ cp "$PKG_DIR/../CLAUDE.md" "$PROJECT_DIR/CLAUDE.md" 2>/dev/null || true
28
+ fi
29
+ echo " [ok] Claude Code — .claude/skills/footprint/SKILL.md"
30
+ }
31
+
32
+ install_codex() {
33
+ if [ ! -f "$PROJECT_DIR/AGENTS.md" ]; then
34
+ cp "$PKG_DIR/../AGENTS.md" "$PROJECT_DIR/AGENTS.md" 2>/dev/null || true
35
+ echo " [ok] OpenAI Codex — AGENTS.md"
36
+ else
37
+ echo " [skip] AGENTS.md already exists"
38
+ fi
39
+ }
40
+
41
+ install_copilot() {
42
+ mkdir -p "$PROJECT_DIR/.github"
43
+ if [ ! -f "$PROJECT_DIR/.github/copilot-instructions.md" ]; then
44
+ cp "$PKG_DIR/copilot-instructions.md" "$PROJECT_DIR/.github/copilot-instructions.md"
45
+ echo " [ok] GitHub Copilot — .github/copilot-instructions.md"
46
+ else
47
+ echo " [skip] .github/copilot-instructions.md already exists"
48
+ fi
49
+ }
50
+
51
+ install_cursor() {
52
+ mkdir -p "$PROJECT_DIR/.cursor/rules"
53
+ cp "$PKG_DIR/cursor/footprint.md" "$PROJECT_DIR/.cursor/rules/footprint.md"
54
+ echo " [ok] Cursor — .cursor/rules/footprint.md"
55
+ }
56
+
57
+ install_windsurf() {
58
+ if [ ! -f "$PROJECT_DIR/.windsurfrules" ]; then
59
+ cp "$PKG_DIR/windsurfrules" "$PROJECT_DIR/.windsurfrules"
60
+ echo " [ok] Windsurf — .windsurfrules"
61
+ else
62
+ # Append footprint rules to existing file
63
+ echo "" >> "$PROJECT_DIR/.windsurfrules"
64
+ cat "$PKG_DIR/windsurfrules" >> "$PROJECT_DIR/.windsurfrules"
65
+ echo " [ok] Windsurf — appended to .windsurfrules"
66
+ fi
67
+ }
68
+
69
+ install_cline() {
70
+ if [ ! -f "$PROJECT_DIR/.clinerules" ]; then
71
+ cp "$PKG_DIR/clinerules" "$PROJECT_DIR/.clinerules"
72
+ echo " [ok] Cline — .clinerules"
73
+ else
74
+ echo "" >> "$PROJECT_DIR/.clinerules"
75
+ cat "$PKG_DIR/clinerules" >> "$PROJECT_DIR/.clinerules"
76
+ echo " [ok] Cline — appended to .clinerules"
77
+ fi
78
+ }
79
+
80
+ install_kiro() {
81
+ mkdir -p "$PROJECT_DIR/.kiro/rules"
82
+ cp "$PKG_DIR/kiro/footprint.md" "$PROJECT_DIR/.kiro/rules/footprint.md"
83
+ echo " [ok] Kiro — .kiro/rules/footprint.md"
84
+ }
85
+
86
+ install_all() {
87
+ install_claude_code
88
+ install_codex
89
+ install_copilot
90
+ install_cursor
91
+ install_windsurf
92
+ install_cline
93
+ install_kiro
94
+ }
95
+
96
+ # Interactive menu
97
+ echo " Which tool(s) do you use?"
98
+ echo ""
99
+ echo " 1) Claude Code"
100
+ echo " 2) OpenAI Codex"
101
+ echo " 3) GitHub Copilot"
102
+ echo " 4) Cursor"
103
+ echo " 5) Windsurf"
104
+ echo " 6) Cline"
105
+ echo " 7) Kiro"
106
+ echo " a) All of the above"
107
+ echo " q) Quit"
108
+ echo ""
109
+ read -rp " Choose (e.g. 1,4 or a): " choice
110
+
111
+ echo ""
112
+
113
+ if [[ "$choice" == "q" ]]; then
114
+ echo " Cancelled."
115
+ exit 0
116
+ fi
117
+
118
+ if [[ "$choice" == "a" || "$choice" == "A" ]]; then
119
+ install_all
120
+ else
121
+ IFS=',' read -ra selections <<< "$choice"
122
+ for sel in "${selections[@]}"; do
123
+ sel="$(echo "$sel" | tr -d ' ')"
124
+ case "$sel" in
125
+ 1) install_claude_code ;;
126
+ 2) install_codex ;;
127
+ 3) install_copilot ;;
128
+ 4) install_cursor ;;
129
+ 5) install_windsurf ;;
130
+ 6) install_cline ;;
131
+ 7) install_kiro ;;
132
+ *) echo " [?] Unknown option: $sel" ;;
133
+ esac
134
+ done
135
+ fi
136
+
137
+ echo ""
138
+ echo " Done! Your AI assistant now knows the footprint.js API."
139
+ echo " Add the generated files to .gitignore if you don't want them in version control."
140
+ echo ""
@@ -0,0 +1,79 @@
1
+ # footprint.js — Windsurf Rules
2
+
3
+ This is the footprint.js library — the flowchart pattern for backend code. Self-explainable systems that AI can reason about.
4
+
5
+ ## Core Principle
6
+
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.
8
+
9
+ ## Architecture
10
+
11
+ ```
12
+ src/lib/
13
+ ├── memory/ → Transactional state (SharedMemory, StageContext, TransactionBuffer)
14
+ ├── schema/ → Validation (Zod optional, duck-typed)
15
+ ├── builder/ → Fluent DSL (FlowChartBuilder, flowChart())
16
+ ├── scope/ → Per-stage facades + recorders + providers
17
+ ├── engine/ → DFS traversal + narrative + handlers
18
+ ├── runner/ → FlowChartExecutor
19
+ └── contract/ → I/O schema + OpenAPI
20
+ ```
21
+
22
+ Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
23
+
24
+ ## Builder API
25
+
26
+ ```typescript
27
+ flowChart(name, fn, id, extractor?, description?)
28
+ .addFunction(name, fn, id, description?)
29
+ .addDeciderFunction(name, fn, id, description?)
30
+ .addFunctionBranch(branchId, name, fn) / .setDefault(id) / .end()
31
+ .addSelectorFunction(name, fn, id, description?)
32
+ .addListOfFunction([...], { failFast? })
33
+ .addSubFlowChartNext(id, subflow, mount, { inputMapper?, outputMapper? })
34
+ .loopTo(stageId)
35
+ .setEnableNarrative()
36
+ .build() / .toSpec() / .toMermaid()
37
+ ```
38
+
39
+ ## Stage Functions
40
+
41
+ ```typescript
42
+ (scope: ScopeFacade, breakPipeline: () => void, streamCallback?: StreamCallback) => void | Promise<void>
43
+ ```
44
+
45
+ ## ScopeFacade
46
+
47
+ ```typescript
48
+ scope.getValue('key') // tracked read → narrative
49
+ scope.setValue('key', value) // tracked write → narrative
50
+ scope.updateValue('key', partial) // deep merge (tracked)
51
+ scope.deleteValue('key') // tracked delete
52
+ scope.getArgs<T>() // frozen readonly input (NOT tracked)
53
+ ```
54
+
55
+ ## Executor
56
+
57
+ ```typescript
58
+ const executor = new FlowChartExecutor(chart);
59
+ await executor.run({ input, timeoutMs?, signal? });
60
+ executor.getNarrative() // combined flow + data
61
+ executor.getNarrativeEntries() // structured entries
62
+ executor.getSnapshot() // memory state
63
+ executor.attachFlowRecorder(r) // plug flow observer
64
+ executor.setRedactionPolicy({ keys, patterns, fields })
65
+ ```
66
+
67
+ ## Observer Systems
68
+
69
+ - **Scope Recorder**: fires DURING stage (`onRead`, `onWrite`, `onCommit`)
70
+ - **FlowRecorder**: fires AFTER stage (`onStageExecuted`, `onDecision`, `onFork`, `onLoop`)
71
+ - 8 built-in strategies (Narrative, Adaptive, Windowed, RLE, Milestone, Progressive, Separate, Manifest)
72
+ - `CombinedNarrativeRecorder` implements both — auto-attached by `setEnableNarrative()`
73
+
74
+ ## Rules
75
+
76
+ - Never post-process the tree — use recorders
77
+ - `getValue()`/`setValue()` for tracked state; `getArgs()` for frozen input
78
+ - Don't use deprecated `CombinedNarrativeBuilder`
79
+ - `setEnableNarrative()` is all you need for narrative
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "footprintjs",
3
- "version": "0.10.1",
3
+ "version": "0.10.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",
@@ -49,7 +49,10 @@
49
49
  "dist/**/*",
50
50
  "!dist/test",
51
51
  "!dist/types/test",
52
- "!dist/esm/types/test"
52
+ "!dist/esm/types/test",
53
+ "ai-instructions/**/*",
54
+ "CLAUDE.md",
55
+ "AGENTS.md"
53
56
  ],
54
57
  "main": "dist/index.js",
55
58
  "module": "dist/esm/index.js",
@@ -72,6 +75,9 @@
72
75
  "prettier --config .prettierrc.js --write"
73
76
  ]
74
77
  },
78
+ "bin": {
79
+ "footprintjs-setup": "ai-instructions/setup.sh"
80
+ },
75
81
  "sideEffects": false,
76
82
  "dependencies": {
77
83
  "lodash.get": "^4.4.2",