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 +81 -87
- package/ai-instructions/claude-code/SKILL.md +204 -91
- package/ai-instructions/clinerules +71 -31
- package/ai-instructions/copilot-instructions.md +67 -34
- package/ai-instructions/cursor/footprint.md +73 -34
- package/ai-instructions/kiro/footprint.md +70 -30
- package/ai-instructions/windsurfrules +71 -31
- package/dist/esm/lib/reactive/createTypedScope.js +28 -6
- package/dist/esm/lib/runner/FlowChartExecutor.js +10 -1
- package/dist/esm/lib/scope/ScopeFacade.js +12 -4
- package/dist/lib/reactive/createTypedScope.js +28 -6
- package/dist/lib/runner/FlowChartExecutor.js +10 -1
- package/dist/lib/scope/ScopeFacade.js +12 -4
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +9 -0
- package/dist/types/lib/scope/ScopeFacade.d.ts +11 -3
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -1,125 +1,119 @@
|
|
|
1
|
-
# footprint.js —
|
|
1
|
+
# footprint.js — Agent Instructions
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
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
|
|
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/ →
|
|
23
|
-
└── contract/ → I/O schema + OpenAPI
|
|
20
|
+
├── runner/ → FlowChartExecutor
|
|
21
|
+
└── contract/ → I/O schema + OpenAPI
|
|
24
22
|
```
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
- `import { ... } from 'footprintjs'` — public API
|
|
28
|
-
- `import { ... } from 'footprintjs/advanced'` — internals
|
|
24
|
+
Entry points: `footprintjs` (public) and `footprintjs/advanced` (internals).
|
|
29
25
|
|
|
30
|
-
##
|
|
26
|
+
## Key API — TypedScope (Recommended)
|
|
31
27
|
|
|
32
28
|
```typescript
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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(
|
|
48
|
-
|
|
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
|
-
|
|
62
|
+
### TypedScope $-methods (escape hatches)
|
|
73
63
|
|
|
74
64
|
```typescript
|
|
75
|
-
|
|
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
|
-
|
|
72
|
+
### decide() / select()
|
|
79
73
|
|
|
80
74
|
```typescript
|
|
81
|
-
|
|
82
|
-
scope
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
92
|
+
### Executor
|
|
89
93
|
|
|
90
94
|
```typescript
|
|
91
|
-
const executor = new FlowChartExecutor(chart);
|
|
92
|
-
await executor.run({ input,
|
|
93
|
-
|
|
94
|
-
executor.
|
|
95
|
-
executor.
|
|
96
|
-
executor.
|
|
97
|
-
executor.attachFlowRecorder(r)
|
|
98
|
-
executor.setRedactionPolicy({
|
|
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
|
-
##
|
|
105
|
+
## Observer Systems
|
|
104
106
|
|
|
105
|
-
**Scope Recorder
|
|
106
|
-
**FlowRecorder
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|