@albinocrabs/feynman 0.2.0 → 0.2.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.
@@ -0,0 +1,317 @@
1
+ # Lint Rules Reference
2
+
3
+ feynman includes a linter for ASCII diagrams in markdown files.
4
+ Eight rules (L01-L08) enforce structural correctness.
5
+
6
+ Run: `npx @albinocrabs/feynman lint <file.md>` or `feynman lint <file.md>`
7
+
8
+ ---
9
+
10
+ ## L01: Box Closure (severity: error)
11
+
12
+ **What:** Every `┌` corner must have a matching `└` at the same column position;
13
+ every `┐` must have a matching `┘` at the same column.
14
+
15
+ **Why:** An unclosed frame box is visually deceptive — it implies containment
16
+ it doesn't deliver, and the unclosed edge is often invisible at a glance.
17
+
18
+ **Source:** [`lib/lint/rules.js#L66`](../lib/lint/rules.js)
19
+
20
+ ### Valid
21
+
22
+ ```
23
+ ┌─ Status ──────┐
24
+ │ item-a done │
25
+ │ item-b wait │
26
+ └───────────────┘
27
+ ```
28
+
29
+ ### Invalid
30
+
31
+ ```text
32
+ ┌─ Status ─────────────┐
33
+ │ item-a done │
34
+ │ item-b in progress│
35
+ ```
36
+
37
+ Output:
38
+ ```text
39
+ file:2:1: L01 Unclosed box: '┌' at line 2, col 1 has no matching '└' at same column
40
+ ```
41
+
42
+ ---
43
+
44
+ ## L02: Tree Chars (severity: error)
45
+
46
+ **What:** The last child in each group of siblings must use `└──`, not `├──`.
47
+
48
+ **Why:** `├──` means "more siblings follow"; `└──` means "last child." Using
49
+ `├──` for the last item misleads the reader about the list's structure.
50
+
51
+ **Source:** [`lib/lint/rules.js#L156`](../lib/lint/rules.js)
52
+
53
+ ### Valid
54
+
55
+ ```
56
+ project
57
+ ├── src
58
+ │ ├── components
59
+ │ └── utils
60
+ └── package.json
61
+ ```
62
+
63
+ ### Invalid
64
+
65
+ ```text
66
+ project
67
+ ├── src
68
+ │ ├── components
69
+ │ ├── utils
70
+ ├── tests
71
+ ├── package.json
72
+ ```
73
+
74
+ Output:
75
+ ```text
76
+ file:6:1: L02 Last tree child uses '├──' but should use '└──'
77
+ ```
78
+
79
+ ---
80
+
81
+ ## L03: Arrow Style (severity: error)
82
+
83
+ **What:** Only one arrow style is allowed per diagram.
84
+
85
+ Recognized styles:
86
+
87
+ ```text
88
+
89
+ -->
90
+ ─→
91
+ ──>
92
+ ```
93
+
94
+ **Why:** Mixed arrow styles force the reader to verify that the styles are
95
+ equivalent rather than distinct, adding cognitive overhead without information.
96
+
97
+ **Source:** [`lib/lint/rules.js#L227`](../lib/lint/rules.js)
98
+
99
+ ### Valid
100
+
101
+ ```
102
+ [Step A] --> [Step B] --> [Step C]
103
+ ```
104
+
105
+ ### Invalid
106
+
107
+ ```text
108
+ [Step A] --> [Step B] → [Step C] --> [Step D]
109
+ ```
110
+
111
+ Output:
112
+ ```text
113
+ file:1:1: L03 Mixed arrow styles: diagram uses '-->' and '→' — pick one style
114
+ ```
115
+
116
+ ---
117
+
118
+ ## L04: Column Widths (severity: error)
119
+
120
+ **What:** All rows in a markdown table must have the same column count.
121
+ The separator row (`|---|---|`) must match the header column count.
122
+
123
+ **Why:** Mismatched column counts break the visual grid — the reader must
124
+ count pipes instead of reading structure.
125
+
126
+ **Source:** [`lib/lint/rules.js#L272`](../lib/lint/rules.js)
127
+
128
+ ### Valid
129
+
130
+ ```
131
+ Option A | Option B | Option C
132
+ ------------|-------------|----------
133
+ fast | slow | medium
134
+ stateless | persistent | persistent
135
+ ```
136
+
137
+ ### Invalid
138
+
139
+ ```text
140
+ Option A | Option B
141
+ ------------|-------------|----------
142
+ fast | slow | medium
143
+ ```
144
+
145
+ Output:
146
+ ```text
147
+ file:2:1: L04 Table separator has 3 columns but header has 2 columns
148
+ ```
149
+
150
+ ---
151
+
152
+ ## L05: Flow Integrity (severity: error)
153
+
154
+ **What:** When two or more `[Box]` tokens appear on the same line, an arrow
155
+ must exist between each consecutive pair.
156
+
157
+ **Why:** Adjacent boxes without arrows are ambiguous — are they sequential
158
+ steps, parallel options, or unrelated elements? An arrow makes the
159
+ relationship explicit.
160
+
161
+ **Source:** [`lib/lint/rules.js#L349`](../lib/lint/rules.js)
162
+
163
+ Note: boxes separated by three or more spaces are treated as parallel layout
164
+ (e.g. side-by-side comparison columns) and do not require an arrow.
165
+
166
+ ### Valid
167
+
168
+ ```
169
+ [Auth] --> [Handler] --> [Response]
170
+ ```
171
+
172
+ ### Invalid
173
+
174
+ ```text
175
+ [Auth] [Handler] [Response]
176
+ ```
177
+
178
+ Output:
179
+ ```text
180
+ file:1:1: L05 3 boxes on same line with no arrow between them: [Auth], [Handler], [Response]
181
+ ```
182
+
183
+ ---
184
+
185
+ ## L06: Priority Scale (severity: warn)
186
+
187
+ **What:** When `▲` appears in a diagram, `▼` must also appear, and vice versa.
188
+
189
+ **Why:** A priority scale with only one end is not a scale — the reader
190
+ cannot determine whether the listed items are near the top or bottom of
191
+ the full range.
192
+
193
+ **Source:** [`lib/lint/rules.js#L399`](../lib/lint/rules.js)
194
+
195
+ ### Valid
196
+
197
+ ```
198
+ ▲ high
199
+ critical-bug
200
+ security-patch
201
+ ▼ low
202
+ cosmetic-fix
203
+ ```
204
+
205
+ ### Invalid
206
+
207
+ ```text
208
+ ▲ high
209
+ critical-bug
210
+ security-patch
211
+ performance-fix
212
+ ```
213
+
214
+ Output:
215
+ ```text
216
+ file:1:1: L06 Priority scale has '▲' but missing '▼' — scales require both ends
217
+ ```
218
+
219
+ ---
220
+
221
+ ## L07: No Mermaid + ASCII Mix (severity: warn)
222
+
223
+ **What:** A document must not contain both a Mermaid fenced block and ASCII
224
+ diagram characters such as box corners, arrows, or tree branches.
225
+
226
+ **Why:** Mixing two diagram encoding systems forces the reader to context-switch
227
+ between visual vocabularies. It also signals that the response lacks a coherent
228
+ visual strategy.
229
+
230
+ **Source:** [`lib/lint/rules.js#L441`](../lib/lint/rules.js)
231
+
232
+ ### Valid
233
+
234
+ A file with only ASCII diagrams:
235
+
236
+ ```
237
+ [A] --> [B] --> [C]
238
+ ```
239
+
240
+ ### Invalid
241
+
242
+ A file with both a Mermaid block and ASCII art (shown as `text` fences to
243
+ avoid triggering the rule):
244
+
245
+ ```text
246
+ [A] --> [B]
247
+
248
+ ` ``mermaid
249
+ graph TD
250
+ A --> B
251
+ ` ``
252
+ ```
253
+
254
+ Output:
255
+ ```text
256
+ file:4:1: L07 Response mixes Mermaid (line 4) and ASCII diagrams — use one format only
257
+ ```
258
+
259
+ ---
260
+
261
+ ## L08: Frame Width Discipline (severity: error)
262
+
263
+ **What:** All rows inside a `┌─ … ─┐` frame block must have the same
264
+ display width (measured in terminal columns).
265
+
266
+ **Why:** Ragged right edges in a frame add visual noise without information.
267
+ Consistent width makes the frame a clean visual container.
268
+
269
+ **Source:** [`lib/lint/rules.js#L476`](../lib/lint/rules.js)
270
+
271
+ Display width counts each Unicode character as 1 column (box-drawing
272
+ characters, Latin, and Cyrillic are single-width; CJK characters are
273
+ double-width).
274
+
275
+ ### Valid
276
+
277
+ ```
278
+ ┌─ Status ──────┐
279
+ │ item-a done │
280
+ │ item-b wait │
281
+ │ item-c ok │
282
+ └───────────────┘
283
+ ```
284
+
285
+ ### Invalid
286
+
287
+ ```text
288
+ ┌─ Status ─────┐
289
+ │ item-a done │
290
+ │ item-b in progress - this line is much wider than the frame header
291
+ └─────────────────────────────┘
292
+ ```
293
+
294
+ Output:
295
+ ```text
296
+ file:3:1: L08 Frame row width 68 differs from frame header width 16 (line 1)
297
+ ```
298
+
299
+ ---
300
+
301
+ ## `language: text` Convention
302
+
303
+ To include intentionally-invalid diagram examples in documentation (as this
304
+ file does), wrap them in fences with the `text` language tag:
305
+
306
+ ````markdown
307
+ ```text
308
+ [A] [B] ← no arrow — intentionally invalid for L05
309
+ ```
310
+ ````
311
+
312
+ The parser recognizes fenced code blocks but only lints blocks with no
313
+ language tag or with diagram-relevant languages. Blocks tagged `text` are
314
+ skipped.
315
+
316
+ This is the recommended way to document "bad" examples without causing
317
+ lint failures in your documentation files.
@@ -0,0 +1,281 @@
1
+ # Self-Improvement Loop Design
2
+
3
+ This document designs a future feedback loop for feynman rules. It is a
4
+ research spec only. Implementation is deferred to v0.3.0 under BENCH-V3-02.
5
+
6
+ The goal is not automatic rule rewriting. The goal is a reviewable trail from
7
+ lint failures to human-approved rule changes.
8
+
9
+ ---
10
+
11
+ ## Scope
12
+
13
+ In scope:
14
+
15
+ - Failure log schema for `feynman-lint`
16
+ - Aggregation step for recurring failures
17
+ - Rule-update proposal format
18
+ - Human review gate
19
+ - Rollout cadence
20
+ - Kill-switch
21
+ - Open questions for v0.3.0
22
+
23
+ Out of scope:
24
+
25
+ - Automatic edits to `rules/feynman-activate.md`
26
+ - Telemetry upload or remote collection
27
+ - Background daemon
28
+ - Model-generated pull requests
29
+ - Per-user analytics dashboard
30
+
31
+ ---
32
+
33
+ ## Loop Overview
34
+
35
+ ```
36
+ [Claude response] --> [feynman-lint]
37
+ |
38
+ pass + fail
39
+ | |
40
+ v v
41
+ [silent] [local failure log]
42
+ |
43
+ v
44
+ [aggregate patterns]
45
+ |
46
+ v
47
+ [proposal file]
48
+ |
49
+ v
50
+ [human review]
51
+ |
52
+ accept + reject
53
+ | |
54
+ v v
55
+ [rule PR] [archive reason]
56
+ ```
57
+
58
+ The loop stays local-first. A user can inspect every captured failure before
59
+ anything affects rules.
60
+
61
+ ---
62
+
63
+ ## Failure Log Schema
64
+
65
+ Each failed lint run writes one JSONL row. The path is proposed for v0.3.0:
66
+ `~/.claude/.feynman/failures.jsonl`.
67
+
68
+ ```json
69
+ {
70
+ "schema": 1,
71
+ "timestamp": "2026-05-06T21:10:00Z",
72
+ "feynmanVersion": "0.2.0",
73
+ "source": "stop-hook",
74
+ "rulesetIntensity": "full",
75
+ "documentHash": "sha256:...",
76
+ "issues": [
77
+ {
78
+ "rule": "L05",
79
+ "severity": "error",
80
+ "line": 12,
81
+ "column": 1,
82
+ "message": "2 boxes on same line with no arrow between them",
83
+ "diagramShape": "flow",
84
+ "snippetHash": "sha256:..."
85
+ }
86
+ ],
87
+ "context": {
88
+ "fileKind": "response",
89
+ "hasCodeFence": true,
90
+ "diagramChars": 42,
91
+ "lineCount": 9
92
+ }
93
+ }
94
+ ```
95
+
96
+ Privacy rule: raw response text is not logged by default. Hashes and structural
97
+ metadata are enough to find repeated failure patterns without storing content.
98
+
99
+ Optional debug mode can store redacted snippets, but only behind an explicit
100
+ local config flag.
101
+
102
+ ---
103
+
104
+ ## Aggregation Step
105
+
106
+ Aggregation groups failures by rule, diagram shape, and structural signature.
107
+ It runs manually, not in the hook path.
108
+
109
+ ```
110
+ [failures.jsonl]
111
+ |
112
+ v
113
+ [group by rule + shape + signature]
114
+ |
115
+ v
116
+ [rank by frequency + recency + severity]
117
+ |
118
+ v
119
+ [candidate patterns]
120
+ ```
121
+
122
+ Candidate score:
123
+
124
+ ```text
125
+ score = (error_count * 3) + warn_count + recent_count - ignored_count
126
+ ```
127
+
128
+ Aggregation output is a local markdown report:
129
+ `~/.claude/.feynman/proposals/YYYY-MM.md`.
130
+
131
+ ---
132
+
133
+ ## Rule-Update Proposal Format
134
+
135
+ Each proposal must be small enough for review in one PR.
136
+
137
+ ```markdown
138
+ # Proposal: tighten L05 flow spacing guidance
139
+
140
+ ## Trigger
141
+
142
+ - Rule: L05
143
+ - Pattern: parallel boxes with one or two spaces
144
+ - Count: 18 failures in 30 days
145
+
146
+ ## Evidence
147
+
148
+ - Structural signature: multibox_same_line_no_arrow
149
+ - Common shape: branch output rows
150
+ - Existing docs affected: examples/api-flow.md, examples/deploy-pipeline.md
151
+
152
+ ## Proposed Rule Change
153
+
154
+ Change prose in `rules/feynman-activate.md` to state that side-by-side
155
+ parallel boxes use three or more spaces.
156
+
157
+ ## Test Plan
158
+
159
+ - Add one valid fixture for parallel layout
160
+ - Add one invalid fixture for adjacent boxes
161
+ - Run `npm test`
162
+ - Run `feynman-lint` over docs/examples
163
+
164
+ ## Rollout
165
+
166
+ - Merge behind normal release process
167
+ - Mention in changelog
168
+ - No runtime migration
169
+ ```
170
+
171
+ Proposal files are advisory. They do not modify rules by themselves.
172
+
173
+ ---
174
+
175
+ ## Human Review Gate
176
+
177
+ Every proposal goes through a maintainer decision.
178
+
179
+ ```
180
+ [proposal]
181
+ |
182
+ v
183
+ [maintainer review]
184
+ |
185
+ +--> [accept] --> [normal PR]
186
+ |
187
+ +--> [revise] --> [proposal update]
188
+ |
189
+ +--> [reject] --> [archive with reason]
190
+ ```
191
+
192
+ Review checklist:
193
+
194
+ - Does the failure repeat across multiple prompts or files?
195
+ - Is the current rule actually wrong, or is the diagram wrong?
196
+ - Would the rule change increase unwanted diagrams?
197
+ - Does it preserve declarative phrasing?
198
+ - Does it keep the rule block under the size cap?
199
+ - Does it add or update golden tests?
200
+
201
+ No proposal is accepted without tests and docs/examples self-lint.
202
+
203
+ ---
204
+
205
+ ## Rollout Cadence
206
+
207
+ Recommended cadence for v0.3.0:
208
+
209
+ ```text
210
+ weekly collect local failures, no action
211
+ monthly generate proposal report
212
+ release merge accepted rule changes
213
+ hotfix only for false positives that block normal use
214
+ ```
215
+
216
+ Rule changes should batch by release unless a linter bug causes high-noise
217
+ false positives.
218
+
219
+ ---
220
+
221
+ ## Kill-Switch
222
+
223
+ The loop needs a hard local off switch before implementation.
224
+
225
+ Proposed config:
226
+
227
+ ```json
228
+ {
229
+ "selfImprovement": {
230
+ "enabled": false,
231
+ "logFailures": false,
232
+ "storeSnippets": false,
233
+ "proposalCadence": "manual"
234
+ }
235
+ }
236
+ ```
237
+
238
+ Default is off. If enabled, logging remains local. If disabled, the Stop hook
239
+ does not write failure logs.
240
+
241
+ Emergency behavior:
242
+
243
+ ```
244
+ [selfImprovement.enabled=false] --> [no logs]
245
+ [logFailures=false] --> [no logs]
246
+ [corrupt config] --> [fail closed: no logs]
247
+ [write error] --> [silent; never block Claude]
248
+ ```
249
+
250
+ The hook must never fail a user prompt because logging failed.
251
+
252
+ ---
253
+
254
+ ## Data Retention
255
+
256
+ Suggested defaults:
257
+
258
+ - Keep aggregate reports until deleted by user.
259
+ - Keep raw JSONL failure rows for 30 days.
260
+ - Never write raw response text unless debug mode is explicitly enabled.
261
+ - Provide `feynman self-improve purge` in v0.3.0 if the feature ships.
262
+
263
+ ---
264
+
265
+ ## Open Questions
266
+
267
+ - Should this live behind `feynman doctor` checks or a separate subcommand?
268
+ - Should proposal generation be part of `feynman lint --report`?
269
+ - Should snippet redaction exist, or should raw snippets be disallowed entirely?
270
+ - How should proposal scores account for false positives fixed in the same
271
+ release?
272
+ - Should team-level aggregation exist, or is local-only the permanent boundary?
273
+ - Can rule proposals remain useful without storing prompt context?
274
+
275
+ ---
276
+
277
+ ## Deferred Implementation
278
+
279
+ Implementation is explicitly deferred to v0.3.0 (BENCH-V3-02). v0.2.0 ships
280
+ only this design document. No runtime code, CLI command, hook behavior, config
281
+ schema, or logging path is added in this milestone.