@codyswann/lisa 2.298.1 → 2.299.0

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.
Files changed (99) hide show
  1. package/all/copy-contents/.gitattributes +6 -0
  2. package/dist/cli/gate-commands.d.ts +8 -6
  3. package/dist/cli/gate-commands.d.ts.map +1 -1
  4. package/dist/cli/gate-commands.js +21 -0
  5. package/dist/cli/gate-commands.js.map +1 -1
  6. package/dist/cli/learnings-overflow-cmd.d.ts +27 -0
  7. package/dist/cli/learnings-overflow-cmd.d.ts.map +1 -0
  8. package/dist/cli/learnings-overflow-cmd.js +57 -0
  9. package/dist/cli/learnings-overflow-cmd.js.map +1 -0
  10. package/dist/core/learnings-alias.d.ts +156 -0
  11. package/dist/core/learnings-alias.d.ts.map +1 -0
  12. package/dist/core/learnings-alias.js +199 -0
  13. package/dist/core/learnings-alias.js.map +1 -0
  14. package/dist/core/learnings-document.d.ts +18 -0
  15. package/dist/core/learnings-document.d.ts.map +1 -1
  16. package/dist/core/learnings-document.js +23 -2
  17. package/dist/core/learnings-document.js.map +1 -1
  18. package/dist/core/learnings-merge-driver.d.ts +8 -33
  19. package/dist/core/learnings-merge-driver.d.ts.map +1 -1
  20. package/dist/core/learnings-merge-driver.js +15 -0
  21. package/dist/core/learnings-merge-driver.js.map +1 -1
  22. package/dist/core/learnings-overflow.d.ts +76 -0
  23. package/dist/core/learnings-overflow.d.ts.map +1 -0
  24. package/dist/core/learnings-overflow.js +212 -0
  25. package/dist/core/learnings-overflow.js.map +1 -0
  26. package/dist/core/learnings-writer.d.ts +6 -0
  27. package/dist/core/learnings-writer.d.ts.map +1 -1
  28. package/dist/core/learnings-writer.js +57 -8
  29. package/dist/core/learnings-writer.js.map +1 -1
  30. package/dist/core/learnings.d.ts +2 -0
  31. package/dist/core/learnings.d.ts.map +1 -1
  32. package/dist/core/learnings.js +2 -0
  33. package/dist/core/learnings.js.map +1 -1
  34. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  35. package/dist/core/upstream-evidence-manifest.js +11 -3
  36. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  37. package/package.json +1 -1
  38. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa/.codex-plugin/skills/lisa-learnings-audit/SKILL.md +42 -6
  41. package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/SKILL.md +7 -2
  42. package/plugins/lisa/skills/lisa-learnings-audit/SKILL.md +42 -6
  43. package/plugins/lisa/skills/lisa-persist-learning/SKILL.md +7 -2
  44. package/plugins/lisa-agy/plugin.json +1 -1
  45. package/plugins/lisa-agy/skills/lisa-learnings-audit/SKILL.md +42 -6
  46. package/plugins/lisa-agy/skills/lisa-persist-learning/SKILL.md +7 -2
  47. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  49. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  50. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-copilot/skills/lisa-learnings-audit/SKILL.md +42 -6
  54. package/plugins/lisa-copilot/skills/lisa-persist-learning/SKILL.md +7 -2
  55. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-cursor/skills/lisa-learnings-audit/SKILL.md +42 -6
  57. package/plugins/lisa-cursor/skills/lisa-persist-learning/SKILL.md +7 -2
  58. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-expo-agy/plugin.json +1 -1
  61. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  66. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  71. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  75. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  76. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  80. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  81. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  85. package/plugins/lisa-rails-agy/plugin.json +1 -1
  86. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  90. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  91. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  95. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  96. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  98. package/plugins/src/base/skills/lisa-learnings-audit/SKILL.md +42 -6
  99. package/plugins/src/base/skills/lisa-persist-learning/SKILL.md +7 -2
package/package.json CHANGED
@@ -115,7 +115,7 @@
115
115
  "brace-expansion": ">=5.0.6"
116
116
  },
117
117
  "name": "@codyswann/lisa",
118
- "version": "2.298.1",
118
+ "version": "2.299.0",
119
119
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
120
120
  "main": "dist/index.js",
121
121
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -40,17 +40,25 @@ memorized list:
40
40
  exemption from projection-only serving) plus `projectLearnings` for the
41
41
  bounded projection (to measure **budget pressure**: how many entries the
42
42
  projection omits). Never hand-parse or hand-edit the raw file.
43
- 2. **Rules trees** — the plugin `rules/eager/` + `rules/reference/` pairs AND
43
+ 2. **The overflow buffer** — captures the ledger had no budget to accept, held
44
+ in `PROJECT_LEARNINGS.overflow.md` beside the resolved ledger. Read it ONLY
45
+ through the executable contract: `bunx @codyswann/lisa learnings-overflow`,
46
+ which prints `{ file, pending, entries }` as JSON. Never hand-parse or
47
+ hand-edit it. These are judged-durable learnings with nowhere to live, so a
48
+ non-empty overflow is the single strongest budget-pressure evidence there is
49
+ — stronger than an omission count, because room was not merely tight, a real
50
+ capture was turned away. Drain it every run (see The audit cycle).
51
+ 3. **Rules trees** — the plugin `rules/eager/` + `rules/reference/` pairs AND
44
52
  the host project's `.claude/rules/` (e.g. `PROJECT_RULES.md`, which is
45
53
  human-authored only — its existing sections are still audit candidates;
46
54
  first-run candidates come from exactly there).
47
- 3. **Skills** — `.claude/skills/` and the plugin skill roots the runtime
55
+ 4. **Skills** — `.claude/skills/` and the plugin skill roots the runtime
48
56
  exposes (descriptions are eager context; bodies load on invoke).
49
- 4. **The wiki index** — `wiki/index.md` when the project has a wiki.
50
- 5. **Mechanical-control surfaces** — lint configs (ESLint/oxlint), ast-grep
57
+ 5. **The wiki index** — `wiki/index.md` when the project has a wiki.
58
+ 6. **Mechanical-control surfaces** — lint configs (ESLint/oxlint), ast-grep
51
59
  rules, git hooks, test suites, and `package.lisa.json` force sections:
52
60
  the surfaces that answer "does a mechanical owner already exist?".
53
- 6. **The tracker** — prior gardener tickets (open, done, and closed-rejected)
61
+ 7. **The tracker** — prior gardener tickets (open, done, and closed-rejected)
54
62
  are its memory; see Idempotency.
55
63
 
56
64
  ## Candidate-selection rules
@@ -64,7 +72,7 @@ Gather, per item, the five evidence axes:
64
72
  | **Staleness** | Does the item reference files, flags, versions, or tools that no longer exist? | Glob/Grep the referenced paths and configs. |
65
73
  | **Redundancy** | Does a mechanical owner already enforce the invariant? (The double-payment hunter.) | Check lint/ast-grep/hook/test/force surfaces for the same invariant. |
66
74
  | **Contradiction** | Does the item contradict another rule, skill, config, or observed current behavior? | Cross-reference the inventoried surfaces. |
67
- | **Budget pressure** | Is the ledger projection omitting entries, or the eager tier growing? | `projectLearnings` omission count; eager-tree token/size trend. |
75
+ | **Budget pressure** | Is the ledger projection omitting entries, captures landing in the overflow, or the eager tier growing? | `projectLearnings` omission count; `learnings-overflow` pending count; eager-tree token/size trend. |
68
76
 
69
77
  Selection outcomes per candidate: **PROMOTE** (up the ladder), **DEMOTE**
70
78
  (down the ladder), **CONFIRM** (evidence the entry demonstrably applied —
@@ -117,6 +125,34 @@ audits the auditor without re-running it.
117
125
  ## The audit cycle
118
126
 
119
127
  1. **Inventory** the sources of truth above.
128
+ 1.5. **Drain the overflow** — before anything else, because these captures are
129
+ the only knowledge in the system with no durable home at all. For each entry
130
+ `bunx @codyswann/lisa learnings-overflow` reports:
131
+
132
+ - **File its durable home first.** Route the entry through the ladder router
133
+ like any other candidate and file the resulting ticket, carrying the
134
+ entry's `rule`, `why`, `provenance`, and `confidence` verbatim so nothing
135
+ is paraphrased away. Use the ordinary `[lisa-gardener]` marker dedupe, so a
136
+ re-run never files the same overflowed capture twice.
137
+ - **Drain it only after that ticket exists**, one call naming exactly the ids
138
+ that now have a home:
139
+
140
+ ```bash
141
+ bunx @codyswann/lisa learnings-overflow --drain <id> [<id>...]
142
+ ```
143
+
144
+ Never drain first and file second, and never drain ids in bulk "to clean
145
+ up": the buffer is the only copy, so an entry removed before its ticket
146
+ exists is gone. Filing first makes a partial run — a rate limit, a network
147
+ failure — safely resumable, because everything unfiled is still in the
148
+ buffer for the next run. An id the command reports as `absent` was already
149
+ drained by an earlier run; that is expected, not an error.
150
+ - **Report the drain in the run proof**: how many were pending, which ids
151
+ were re-homed to which tickets, and how many remain.
152
+
153
+ A non-empty overflow also means the ledger turned a judged-durable capture
154
+ away, so treat it as top-priority budget-pressure evidence when selecting
155
+ RETIRE/PROMOTE candidates this run — the room has to come from somewhere.
120
156
  2. **Evidence** — gather the five axes per item; drop items with none.
121
157
  3. **Classify** — pass each candidate (`rule`, `why`, `provenance`,
122
158
  `evidence`) to the ladder router (the `skill-evaluator` agent). The router
@@ -187,8 +187,13 @@ No learning content is ever committed without a PR — there is no other write p
187
187
  Learnings ledger saturated — a durable capture was dropped for budget. The projection is now omitting entries; promote or retire a learning to reclaim room. This is an operator-visible budget-pressure signal, not itself a new learning.
188
188
  ```
189
189
 
190
- The `[lisa-ledger-saturated]` marker sits **outside the `[lisa-learning-*]` namespace** that the gardener (`lisa-learnings-audit`) auto-excludes from candidacy. The gardener already derives budget pressure independently — it measures the entries its `projectLearnings` projection has to omit — so this ticket does not feed that measurement mechanically; its job is an idempotent, operator-visible notification that saturation happened. Keeping it outside `[lisa-learning-*]` matters so the audit tooling does not silently filter it as learning-machinery noise before an operator sees it. **Do not reuse a `[lisa-learning-*]` marker here.** The candidate itself is still dropped; this signal records the saturation, it does not persist the rule.
191
- 4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings file; verify with `git diff --name-only` that the diff touches nothing else.
190
+ The `[lisa-ledger-saturated]` marker sits **outside the `[lisa-learning-*]` namespace** that the gardener (`lisa-learnings-audit`) auto-excludes from candidacy. The gardener already derives budget pressure independently — it measures the entries its `projectLearnings` projection has to omit — so this ticket does not feed that measurement mechanically; its job is an idempotent, operator-visible notification that saturation happened. Keeping it outside `[lisa-learning-*]` matters so the audit tooling does not silently filter it as learning-machinery noise before an operator sees it. **Do not reuse a `[lisa-learning-*]` marker here.** The signal records the saturation; it does not persist the rule.
191
+ - **The dropped capture is preserved for you — ship it.** The writer does this itself, so there is nothing to compose by hand: when the budget re-assertion fails, `persistConsolidatedLearning` / `persistLearningEntry` write the rejected entry to the **overflow buffer** — `PROJECT_LEARNINGS.overflow.md`, a sibling of the resolved ledger — and then re-raise a budget error naming that file. The thrown error is the confirmation; if it does not name the overflow, the capture was NOT preserved (the overflow is full too) and that is what the error says instead.
192
+
193
+ The overflow is git-tracked and bound to the same union merge driver as the ledger, so **still open the pull request on a budget-forced drop** — carrying the overflow file instead of the ledger. That is the whole point of preserving it: a capture that reaches only a disposable `learning/<fingerprint>` worktree is durable in name only, and the gardener draining `main` would never see it. Never hand-edit the overflow, and never delete an entry from it to make room; the gardener drains it.
194
+
195
+ So a budget-forced drop produces **both** halves and neither substitutes for the other: the `[lisa-ledger-saturated]` ticket (the signal that room must be reclaimed) and a pull request carrying the overflow file (the content itself). Report both in the run summary.
196
+ 4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings surface — the ledger, or the overflow buffer on a budget-forced drop; verify with `git diff --name-only` that the diff touches nothing else.
192
197
  5. **PR body.** Exactly one marker line plus the reviewable story:
193
198
 
194
199
  ```markdown
@@ -40,17 +40,25 @@ memorized list:
40
40
  exemption from projection-only serving) plus `projectLearnings` for the
41
41
  bounded projection (to measure **budget pressure**: how many entries the
42
42
  projection omits). Never hand-parse or hand-edit the raw file.
43
- 2. **Rules trees** — the plugin `rules/eager/` + `rules/reference/` pairs AND
43
+ 2. **The overflow buffer** — captures the ledger had no budget to accept, held
44
+ in `PROJECT_LEARNINGS.overflow.md` beside the resolved ledger. Read it ONLY
45
+ through the executable contract: `bunx @codyswann/lisa learnings-overflow`,
46
+ which prints `{ file, pending, entries }` as JSON. Never hand-parse or
47
+ hand-edit it. These are judged-durable learnings with nowhere to live, so a
48
+ non-empty overflow is the single strongest budget-pressure evidence there is
49
+ — stronger than an omission count, because room was not merely tight, a real
50
+ capture was turned away. Drain it every run (see The audit cycle).
51
+ 3. **Rules trees** — the plugin `rules/eager/` + `rules/reference/` pairs AND
44
52
  the host project's `.claude/rules/` (e.g. `PROJECT_RULES.md`, which is
45
53
  human-authored only — its existing sections are still audit candidates;
46
54
  first-run candidates come from exactly there).
47
- 3. **Skills** — `.claude/skills/` and the plugin skill roots the runtime
55
+ 4. **Skills** — `.claude/skills/` and the plugin skill roots the runtime
48
56
  exposes (descriptions are eager context; bodies load on invoke).
49
- 4. **The wiki index** — `wiki/index.md` when the project has a wiki.
50
- 5. **Mechanical-control surfaces** — lint configs (ESLint/oxlint), ast-grep
57
+ 5. **The wiki index** — `wiki/index.md` when the project has a wiki.
58
+ 6. **Mechanical-control surfaces** — lint configs (ESLint/oxlint), ast-grep
51
59
  rules, git hooks, test suites, and `package.lisa.json` force sections:
52
60
  the surfaces that answer "does a mechanical owner already exist?".
53
- 6. **The tracker** — prior gardener tickets (open, done, and closed-rejected)
61
+ 7. **The tracker** — prior gardener tickets (open, done, and closed-rejected)
54
62
  are its memory; see Idempotency.
55
63
 
56
64
  ## Candidate-selection rules
@@ -64,7 +72,7 @@ Gather, per item, the five evidence axes:
64
72
  | **Staleness** | Does the item reference files, flags, versions, or tools that no longer exist? | Glob/Grep the referenced paths and configs. |
65
73
  | **Redundancy** | Does a mechanical owner already enforce the invariant? (The double-payment hunter.) | Check lint/ast-grep/hook/test/force surfaces for the same invariant. |
66
74
  | **Contradiction** | Does the item contradict another rule, skill, config, or observed current behavior? | Cross-reference the inventoried surfaces. |
67
- | **Budget pressure** | Is the ledger projection omitting entries, or the eager tier growing? | `projectLearnings` omission count; eager-tree token/size trend. |
75
+ | **Budget pressure** | Is the ledger projection omitting entries, captures landing in the overflow, or the eager tier growing? | `projectLearnings` omission count; `learnings-overflow` pending count; eager-tree token/size trend. |
68
76
 
69
77
  Selection outcomes per candidate: **PROMOTE** (up the ladder), **DEMOTE**
70
78
  (down the ladder), **CONFIRM** (evidence the entry demonstrably applied —
@@ -117,6 +125,34 @@ audits the auditor without re-running it.
117
125
  ## The audit cycle
118
126
 
119
127
  1. **Inventory** the sources of truth above.
128
+ 1.5. **Drain the overflow** — before anything else, because these captures are
129
+ the only knowledge in the system with no durable home at all. For each entry
130
+ `bunx @codyswann/lisa learnings-overflow` reports:
131
+
132
+ - **File its durable home first.** Route the entry through the ladder router
133
+ like any other candidate and file the resulting ticket, carrying the
134
+ entry's `rule`, `why`, `provenance`, and `confidence` verbatim so nothing
135
+ is paraphrased away. Use the ordinary `[lisa-gardener]` marker dedupe, so a
136
+ re-run never files the same overflowed capture twice.
137
+ - **Drain it only after that ticket exists**, one call naming exactly the ids
138
+ that now have a home:
139
+
140
+ ```bash
141
+ bunx @codyswann/lisa learnings-overflow --drain <id> [<id>...]
142
+ ```
143
+
144
+ Never drain first and file second, and never drain ids in bulk "to clean
145
+ up": the buffer is the only copy, so an entry removed before its ticket
146
+ exists is gone. Filing first makes a partial run — a rate limit, a network
147
+ failure — safely resumable, because everything unfiled is still in the
148
+ buffer for the next run. An id the command reports as `absent` was already
149
+ drained by an earlier run; that is expected, not an error.
150
+ - **Report the drain in the run proof**: how many were pending, which ids
151
+ were re-homed to which tickets, and how many remain.
152
+
153
+ A non-empty overflow also means the ledger turned a judged-durable capture
154
+ away, so treat it as top-priority budget-pressure evidence when selecting
155
+ RETIRE/PROMOTE candidates this run — the room has to come from somewhere.
120
156
  2. **Evidence** — gather the five axes per item; drop items with none.
121
157
  3. **Classify** — pass each candidate (`rule`, `why`, `provenance`,
122
158
  `evidence`) to the ladder router (the `skill-evaluator` agent). The router
@@ -187,8 +187,13 @@ No learning content is ever committed without a PR — there is no other write p
187
187
  Learnings ledger saturated — a durable capture was dropped for budget. The projection is now omitting entries; promote or retire a learning to reclaim room. This is an operator-visible budget-pressure signal, not itself a new learning.
188
188
  ```
189
189
 
190
- The `[lisa-ledger-saturated]` marker sits **outside the `[lisa-learning-*]` namespace** that the gardener (`lisa-learnings-audit`) auto-excludes from candidacy. The gardener already derives budget pressure independently — it measures the entries its `projectLearnings` projection has to omit — so this ticket does not feed that measurement mechanically; its job is an idempotent, operator-visible notification that saturation happened. Keeping it outside `[lisa-learning-*]` matters so the audit tooling does not silently filter it as learning-machinery noise before an operator sees it. **Do not reuse a `[lisa-learning-*]` marker here.** The candidate itself is still dropped; this signal records the saturation, it does not persist the rule.
191
- 4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings file; verify with `git diff --name-only` that the diff touches nothing else.
190
+ The `[lisa-ledger-saturated]` marker sits **outside the `[lisa-learning-*]` namespace** that the gardener (`lisa-learnings-audit`) auto-excludes from candidacy. The gardener already derives budget pressure independently — it measures the entries its `projectLearnings` projection has to omit — so this ticket does not feed that measurement mechanically; its job is an idempotent, operator-visible notification that saturation happened. Keeping it outside `[lisa-learning-*]` matters so the audit tooling does not silently filter it as learning-machinery noise before an operator sees it. **Do not reuse a `[lisa-learning-*]` marker here.** The signal records the saturation; it does not persist the rule.
191
+ - **The dropped capture is preserved for you — ship it.** The writer does this itself, so there is nothing to compose by hand: when the budget re-assertion fails, `persistConsolidatedLearning` / `persistLearningEntry` write the rejected entry to the **overflow buffer** — `PROJECT_LEARNINGS.overflow.md`, a sibling of the resolved ledger — and then re-raise a budget error naming that file. The thrown error is the confirmation; if it does not name the overflow, the capture was NOT preserved (the overflow is full too) and that is what the error says instead.
192
+
193
+ The overflow is git-tracked and bound to the same union merge driver as the ledger, so **still open the pull request on a budget-forced drop** — carrying the overflow file instead of the ledger. That is the whole point of preserving it: a capture that reaches only a disposable `learning/<fingerprint>` worktree is durable in name only, and the gardener draining `main` would never see it. Never hand-edit the overflow, and never delete an entry from it to make room; the gardener drains it.
194
+
195
+ So a budget-forced drop produces **both** halves and neither substitutes for the other: the `[lisa-ledger-saturated]` ticket (the signal that room must be reclaimed) and a pull request carrying the overflow file (the content itself). Report both in the run summary.
196
+ 4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings surface — the ledger, or the overflow buffer on a budget-forced drop; verify with `git diff --name-only` that the diff touches nothing else.
192
197
  5. **PR body.** Exactly one marker line plus the reviewable story:
193
198
 
194
199
  ```markdown
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -40,17 +40,25 @@ memorized list:
40
40
  exemption from projection-only serving) plus `projectLearnings` for the
41
41
  bounded projection (to measure **budget pressure**: how many entries the
42
42
  projection omits). Never hand-parse or hand-edit the raw file.
43
- 2. **Rules trees** — the plugin `rules/eager/` + `rules/reference/` pairs AND
43
+ 2. **The overflow buffer** — captures the ledger had no budget to accept, held
44
+ in `PROJECT_LEARNINGS.overflow.md` beside the resolved ledger. Read it ONLY
45
+ through the executable contract: `bunx @codyswann/lisa learnings-overflow`,
46
+ which prints `{ file, pending, entries }` as JSON. Never hand-parse or
47
+ hand-edit it. These are judged-durable learnings with nowhere to live, so a
48
+ non-empty overflow is the single strongest budget-pressure evidence there is
49
+ — stronger than an omission count, because room was not merely tight, a real
50
+ capture was turned away. Drain it every run (see The audit cycle).
51
+ 3. **Rules trees** — the plugin `rules/eager/` + `rules/reference/` pairs AND
44
52
  the host project's `.claude/rules/` (e.g. `PROJECT_RULES.md`, which is
45
53
  human-authored only — its existing sections are still audit candidates;
46
54
  first-run candidates come from exactly there).
47
- 3. **Skills** — `.claude/skills/` and the plugin skill roots the runtime
55
+ 4. **Skills** — `.claude/skills/` and the plugin skill roots the runtime
48
56
  exposes (descriptions are eager context; bodies load on invoke).
49
- 4. **The wiki index** — `wiki/index.md` when the project has a wiki.
50
- 5. **Mechanical-control surfaces** — lint configs (ESLint/oxlint), ast-grep
57
+ 5. **The wiki index** — `wiki/index.md` when the project has a wiki.
58
+ 6. **Mechanical-control surfaces** — lint configs (ESLint/oxlint), ast-grep
51
59
  rules, git hooks, test suites, and `package.lisa.json` force sections:
52
60
  the surfaces that answer "does a mechanical owner already exist?".
53
- 6. **The tracker** — prior gardener tickets (open, done, and closed-rejected)
61
+ 7. **The tracker** — prior gardener tickets (open, done, and closed-rejected)
54
62
  are its memory; see Idempotency.
55
63
 
56
64
  ## Candidate-selection rules
@@ -64,7 +72,7 @@ Gather, per item, the five evidence axes:
64
72
  | **Staleness** | Does the item reference files, flags, versions, or tools that no longer exist? | Glob/Grep the referenced paths and configs. |
65
73
  | **Redundancy** | Does a mechanical owner already enforce the invariant? (The double-payment hunter.) | Check lint/ast-grep/hook/test/force surfaces for the same invariant. |
66
74
  | **Contradiction** | Does the item contradict another rule, skill, config, or observed current behavior? | Cross-reference the inventoried surfaces. |
67
- | **Budget pressure** | Is the ledger projection omitting entries, or the eager tier growing? | `projectLearnings` omission count; eager-tree token/size trend. |
75
+ | **Budget pressure** | Is the ledger projection omitting entries, captures landing in the overflow, or the eager tier growing? | `projectLearnings` omission count; `learnings-overflow` pending count; eager-tree token/size trend. |
68
76
 
69
77
  Selection outcomes per candidate: **PROMOTE** (up the ladder), **DEMOTE**
70
78
  (down the ladder), **CONFIRM** (evidence the entry demonstrably applied —
@@ -117,6 +125,34 @@ audits the auditor without re-running it.
117
125
  ## The audit cycle
118
126
 
119
127
  1. **Inventory** the sources of truth above.
128
+ 1.5. **Drain the overflow** — before anything else, because these captures are
129
+ the only knowledge in the system with no durable home at all. For each entry
130
+ `bunx @codyswann/lisa learnings-overflow` reports:
131
+
132
+ - **File its durable home first.** Route the entry through the ladder router
133
+ like any other candidate and file the resulting ticket, carrying the
134
+ entry's `rule`, `why`, `provenance`, and `confidence` verbatim so nothing
135
+ is paraphrased away. Use the ordinary `[lisa-gardener]` marker dedupe, so a
136
+ re-run never files the same overflowed capture twice.
137
+ - **Drain it only after that ticket exists**, one call naming exactly the ids
138
+ that now have a home:
139
+
140
+ ```bash
141
+ bunx @codyswann/lisa learnings-overflow --drain <id> [<id>...]
142
+ ```
143
+
144
+ Never drain first and file second, and never drain ids in bulk "to clean
145
+ up": the buffer is the only copy, so an entry removed before its ticket
146
+ exists is gone. Filing first makes a partial run — a rate limit, a network
147
+ failure — safely resumable, because everything unfiled is still in the
148
+ buffer for the next run. An id the command reports as `absent` was already
149
+ drained by an earlier run; that is expected, not an error.
150
+ - **Report the drain in the run proof**: how many were pending, which ids
151
+ were re-homed to which tickets, and how many remain.
152
+
153
+ A non-empty overflow also means the ledger turned a judged-durable capture
154
+ away, so treat it as top-priority budget-pressure evidence when selecting
155
+ RETIRE/PROMOTE candidates this run — the room has to come from somewhere.
120
156
  2. **Evidence** — gather the five axes per item; drop items with none.
121
157
  3. **Classify** — pass each candidate (`rule`, `why`, `provenance`,
122
158
  `evidence`) to the ladder router (the `skill-evaluator` agent). The router
@@ -187,8 +187,13 @@ No learning content is ever committed without a PR — there is no other write p
187
187
  Learnings ledger saturated — a durable capture was dropped for budget. The projection is now omitting entries; promote or retire a learning to reclaim room. This is an operator-visible budget-pressure signal, not itself a new learning.
188
188
  ```
189
189
 
190
- The `[lisa-ledger-saturated]` marker sits **outside the `[lisa-learning-*]` namespace** that the gardener (`lisa-learnings-audit`) auto-excludes from candidacy. The gardener already derives budget pressure independently — it measures the entries its `projectLearnings` projection has to omit — so this ticket does not feed that measurement mechanically; its job is an idempotent, operator-visible notification that saturation happened. Keeping it outside `[lisa-learning-*]` matters so the audit tooling does not silently filter it as learning-machinery noise before an operator sees it. **Do not reuse a `[lisa-learning-*]` marker here.** The candidate itself is still dropped; this signal records the saturation, it does not persist the rule.
191
- 4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings file; verify with `git diff --name-only` that the diff touches nothing else.
190
+ The `[lisa-ledger-saturated]` marker sits **outside the `[lisa-learning-*]` namespace** that the gardener (`lisa-learnings-audit`) auto-excludes from candidacy. The gardener already derives budget pressure independently — it measures the entries its `projectLearnings` projection has to omit — so this ticket does not feed that measurement mechanically; its job is an idempotent, operator-visible notification that saturation happened. Keeping it outside `[lisa-learning-*]` matters so the audit tooling does not silently filter it as learning-machinery noise before an operator sees it. **Do not reuse a `[lisa-learning-*]` marker here.** The signal records the saturation; it does not persist the rule.
191
+ - **The dropped capture is preserved for you — ship it.** The writer does this itself, so there is nothing to compose by hand: when the budget re-assertion fails, `persistConsolidatedLearning` / `persistLearningEntry` write the rejected entry to the **overflow buffer** — `PROJECT_LEARNINGS.overflow.md`, a sibling of the resolved ledger — and then re-raise a budget error naming that file. The thrown error is the confirmation; if it does not name the overflow, the capture was NOT preserved (the overflow is full too) and that is what the error says instead.
192
+
193
+ The overflow is git-tracked and bound to the same union merge driver as the ledger, so **still open the pull request on a budget-forced drop** — carrying the overflow file instead of the ledger. That is the whole point of preserving it: a capture that reaches only a disposable `learning/<fingerprint>` worktree is durable in name only, and the gardener draining `main` would never see it. Never hand-edit the overflow, and never delete an entry from it to make room; the gardener drains it.
194
+
195
+ So a budget-forced drop produces **both** halves and neither substitutes for the other: the `[lisa-ledger-saturated]` ticket (the signal that room must be reclaimed) and a pull request carrying the overflow file (the content itself). Report both in the run summary.
196
+ 4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings surface — the ledger, or the overflow buffer on a budget-forced drop; verify with `git diff --name-only` that the diff touches nothing else.
192
197
  5. **PR body.** Exactly one marker line plus the reviewable story:
193
198
 
194
199
  ```markdown
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -40,17 +40,25 @@ memorized list:
40
40
  exemption from projection-only serving) plus `projectLearnings` for the
41
41
  bounded projection (to measure **budget pressure**: how many entries the
42
42
  projection omits). Never hand-parse or hand-edit the raw file.
43
- 2. **Rules trees** — the plugin `rules/eager/` + `rules/reference/` pairs AND
43
+ 2. **The overflow buffer** — captures the ledger had no budget to accept, held
44
+ in `PROJECT_LEARNINGS.overflow.md` beside the resolved ledger. Read it ONLY
45
+ through the executable contract: `bunx @codyswann/lisa learnings-overflow`,
46
+ which prints `{ file, pending, entries }` as JSON. Never hand-parse or
47
+ hand-edit it. These are judged-durable learnings with nowhere to live, so a
48
+ non-empty overflow is the single strongest budget-pressure evidence there is
49
+ — stronger than an omission count, because room was not merely tight, a real
50
+ capture was turned away. Drain it every run (see The audit cycle).
51
+ 3. **Rules trees** — the plugin `rules/eager/` + `rules/reference/` pairs AND
44
52
  the host project's `.claude/rules/` (e.g. `PROJECT_RULES.md`, which is
45
53
  human-authored only — its existing sections are still audit candidates;
46
54
  first-run candidates come from exactly there).
47
- 3. **Skills** — `.claude/skills/` and the plugin skill roots the runtime
55
+ 4. **Skills** — `.claude/skills/` and the plugin skill roots the runtime
48
56
  exposes (descriptions are eager context; bodies load on invoke).
49
- 4. **The wiki index** — `wiki/index.md` when the project has a wiki.
50
- 5. **Mechanical-control surfaces** — lint configs (ESLint/oxlint), ast-grep
57
+ 5. **The wiki index** — `wiki/index.md` when the project has a wiki.
58
+ 6. **Mechanical-control surfaces** — lint configs (ESLint/oxlint), ast-grep
51
59
  rules, git hooks, test suites, and `package.lisa.json` force sections:
52
60
  the surfaces that answer "does a mechanical owner already exist?".
53
- 6. **The tracker** — prior gardener tickets (open, done, and closed-rejected)
61
+ 7. **The tracker** — prior gardener tickets (open, done, and closed-rejected)
54
62
  are its memory; see Idempotency.
55
63
 
56
64
  ## Candidate-selection rules
@@ -64,7 +72,7 @@ Gather, per item, the five evidence axes:
64
72
  | **Staleness** | Does the item reference files, flags, versions, or tools that no longer exist? | Glob/Grep the referenced paths and configs. |
65
73
  | **Redundancy** | Does a mechanical owner already enforce the invariant? (The double-payment hunter.) | Check lint/ast-grep/hook/test/force surfaces for the same invariant. |
66
74
  | **Contradiction** | Does the item contradict another rule, skill, config, or observed current behavior? | Cross-reference the inventoried surfaces. |
67
- | **Budget pressure** | Is the ledger projection omitting entries, or the eager tier growing? | `projectLearnings` omission count; eager-tree token/size trend. |
75
+ | **Budget pressure** | Is the ledger projection omitting entries, captures landing in the overflow, or the eager tier growing? | `projectLearnings` omission count; `learnings-overflow` pending count; eager-tree token/size trend. |
68
76
 
69
77
  Selection outcomes per candidate: **PROMOTE** (up the ladder), **DEMOTE**
70
78
  (down the ladder), **CONFIRM** (evidence the entry demonstrably applied —
@@ -117,6 +125,34 @@ audits the auditor without re-running it.
117
125
  ## The audit cycle
118
126
 
119
127
  1. **Inventory** the sources of truth above.
128
+ 1.5. **Drain the overflow** — before anything else, because these captures are
129
+ the only knowledge in the system with no durable home at all. For each entry
130
+ `bunx @codyswann/lisa learnings-overflow` reports:
131
+
132
+ - **File its durable home first.** Route the entry through the ladder router
133
+ like any other candidate and file the resulting ticket, carrying the
134
+ entry's `rule`, `why`, `provenance`, and `confidence` verbatim so nothing
135
+ is paraphrased away. Use the ordinary `[lisa-gardener]` marker dedupe, so a
136
+ re-run never files the same overflowed capture twice.
137
+ - **Drain it only after that ticket exists**, one call naming exactly the ids
138
+ that now have a home:
139
+
140
+ ```bash
141
+ bunx @codyswann/lisa learnings-overflow --drain <id> [<id>...]
142
+ ```
143
+
144
+ Never drain first and file second, and never drain ids in bulk "to clean
145
+ up": the buffer is the only copy, so an entry removed before its ticket
146
+ exists is gone. Filing first makes a partial run — a rate limit, a network
147
+ failure — safely resumable, because everything unfiled is still in the
148
+ buffer for the next run. An id the command reports as `absent` was already
149
+ drained by an earlier run; that is expected, not an error.
150
+ - **Report the drain in the run proof**: how many were pending, which ids
151
+ were re-homed to which tickets, and how many remain.
152
+
153
+ A non-empty overflow also means the ledger turned a judged-durable capture
154
+ away, so treat it as top-priority budget-pressure evidence when selecting
155
+ RETIRE/PROMOTE candidates this run — the room has to come from somewhere.
120
156
  2. **Evidence** — gather the five axes per item; drop items with none.
121
157
  3. **Classify** — pass each candidate (`rule`, `why`, `provenance`,
122
158
  `evidence`) to the ladder router (the `skill-evaluator` agent). The router
@@ -187,8 +187,13 @@ No learning content is ever committed without a PR — there is no other write p
187
187
  Learnings ledger saturated — a durable capture was dropped for budget. The projection is now omitting entries; promote or retire a learning to reclaim room. This is an operator-visible budget-pressure signal, not itself a new learning.
188
188
  ```
189
189
 
190
- The `[lisa-ledger-saturated]` marker sits **outside the `[lisa-learning-*]` namespace** that the gardener (`lisa-learnings-audit`) auto-excludes from candidacy. The gardener already derives budget pressure independently — it measures the entries its `projectLearnings` projection has to omit — so this ticket does not feed that measurement mechanically; its job is an idempotent, operator-visible notification that saturation happened. Keeping it outside `[lisa-learning-*]` matters so the audit tooling does not silently filter it as learning-machinery noise before an operator sees it. **Do not reuse a `[lisa-learning-*]` marker here.** The candidate itself is still dropped; this signal records the saturation, it does not persist the rule.
191
- 4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings file; verify with `git diff --name-only` that the diff touches nothing else.
190
+ The `[lisa-ledger-saturated]` marker sits **outside the `[lisa-learning-*]` namespace** that the gardener (`lisa-learnings-audit`) auto-excludes from candidacy. The gardener already derives budget pressure independently — it measures the entries its `projectLearnings` projection has to omit — so this ticket does not feed that measurement mechanically; its job is an idempotent, operator-visible notification that saturation happened. Keeping it outside `[lisa-learning-*]` matters so the audit tooling does not silently filter it as learning-machinery noise before an operator sees it. **Do not reuse a `[lisa-learning-*]` marker here.** The signal records the saturation; it does not persist the rule.
191
+ - **The dropped capture is preserved for you — ship it.** The writer does this itself, so there is nothing to compose by hand: when the budget re-assertion fails, `persistConsolidatedLearning` / `persistLearningEntry` write the rejected entry to the **overflow buffer** — `PROJECT_LEARNINGS.overflow.md`, a sibling of the resolved ledger — and then re-raise a budget error naming that file. The thrown error is the confirmation; if it does not name the overflow, the capture was NOT preserved (the overflow is full too) and that is what the error says instead.
192
+
193
+ The overflow is git-tracked and bound to the same union merge driver as the ledger, so **still open the pull request on a budget-forced drop** — carrying the overflow file instead of the ledger. That is the whole point of preserving it: a capture that reaches only a disposable `learning/<fingerprint>` worktree is durable in name only, and the gardener draining `main` would never see it. Never hand-edit the overflow, and never delete an entry from it to make room; the gardener drains it.
194
+
195
+ So a budget-forced drop produces **both** halves and neither substitutes for the other: the `[lisa-ledger-saturated]` ticket (the signal that room must be reclaimed) and a pull request carrying the overflow file (the content itself). Report both in the run summary.
196
+ 4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings surface — the ledger, or the overflow buffer on a budget-forced drop; verify with `git diff --name-only` that the diff touches nothing else.
192
197
  5. **PR body.** Exactly one marker line plus the reviewable story:
193
198
 
194
199
  ```markdown
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -40,17 +40,25 @@ memorized list:
40
40
  exemption from projection-only serving) plus `projectLearnings` for the
41
41
  bounded projection (to measure **budget pressure**: how many entries the
42
42
  projection omits). Never hand-parse or hand-edit the raw file.
43
- 2. **Rules trees** — the plugin `rules/eager/` + `rules/reference/` pairs AND
43
+ 2. **The overflow buffer** — captures the ledger had no budget to accept, held
44
+ in `PROJECT_LEARNINGS.overflow.md` beside the resolved ledger. Read it ONLY
45
+ through the executable contract: `bunx @codyswann/lisa learnings-overflow`,
46
+ which prints `{ file, pending, entries }` as JSON. Never hand-parse or
47
+ hand-edit it. These are judged-durable learnings with nowhere to live, so a
48
+ non-empty overflow is the single strongest budget-pressure evidence there is
49
+ — stronger than an omission count, because room was not merely tight, a real
50
+ capture was turned away. Drain it every run (see The audit cycle).
51
+ 3. **Rules trees** — the plugin `rules/eager/` + `rules/reference/` pairs AND
44
52
  the host project's `.claude/rules/` (e.g. `PROJECT_RULES.md`, which is
45
53
  human-authored only — its existing sections are still audit candidates;
46
54
  first-run candidates come from exactly there).
47
- 3. **Skills** — `.claude/skills/` and the plugin skill roots the runtime
55
+ 4. **Skills** — `.claude/skills/` and the plugin skill roots the runtime
48
56
  exposes (descriptions are eager context; bodies load on invoke).
49
- 4. **The wiki index** — `wiki/index.md` when the project has a wiki.
50
- 5. **Mechanical-control surfaces** — lint configs (ESLint/oxlint), ast-grep
57
+ 5. **The wiki index** — `wiki/index.md` when the project has a wiki.
58
+ 6. **Mechanical-control surfaces** — lint configs (ESLint/oxlint), ast-grep
51
59
  rules, git hooks, test suites, and `package.lisa.json` force sections:
52
60
  the surfaces that answer "does a mechanical owner already exist?".
53
- 6. **The tracker** — prior gardener tickets (open, done, and closed-rejected)
61
+ 7. **The tracker** — prior gardener tickets (open, done, and closed-rejected)
54
62
  are its memory; see Idempotency.
55
63
 
56
64
  ## Candidate-selection rules
@@ -64,7 +72,7 @@ Gather, per item, the five evidence axes:
64
72
  | **Staleness** | Does the item reference files, flags, versions, or tools that no longer exist? | Glob/Grep the referenced paths and configs. |
65
73
  | **Redundancy** | Does a mechanical owner already enforce the invariant? (The double-payment hunter.) | Check lint/ast-grep/hook/test/force surfaces for the same invariant. |
66
74
  | **Contradiction** | Does the item contradict another rule, skill, config, or observed current behavior? | Cross-reference the inventoried surfaces. |
67
- | **Budget pressure** | Is the ledger projection omitting entries, or the eager tier growing? | `projectLearnings` omission count; eager-tree token/size trend. |
75
+ | **Budget pressure** | Is the ledger projection omitting entries, captures landing in the overflow, or the eager tier growing? | `projectLearnings` omission count; `learnings-overflow` pending count; eager-tree token/size trend. |
68
76
 
69
77
  Selection outcomes per candidate: **PROMOTE** (up the ladder), **DEMOTE**
70
78
  (down the ladder), **CONFIRM** (evidence the entry demonstrably applied —
@@ -117,6 +125,34 @@ audits the auditor without re-running it.
117
125
  ## The audit cycle
118
126
 
119
127
  1. **Inventory** the sources of truth above.
128
+ 1.5. **Drain the overflow** — before anything else, because these captures are
129
+ the only knowledge in the system with no durable home at all. For each entry
130
+ `bunx @codyswann/lisa learnings-overflow` reports:
131
+
132
+ - **File its durable home first.** Route the entry through the ladder router
133
+ like any other candidate and file the resulting ticket, carrying the
134
+ entry's `rule`, `why`, `provenance`, and `confidence` verbatim so nothing
135
+ is paraphrased away. Use the ordinary `[lisa-gardener]` marker dedupe, so a
136
+ re-run never files the same overflowed capture twice.
137
+ - **Drain it only after that ticket exists**, one call naming exactly the ids
138
+ that now have a home:
139
+
140
+ ```bash
141
+ bunx @codyswann/lisa learnings-overflow --drain <id> [<id>...]
142
+ ```
143
+
144
+ Never drain first and file second, and never drain ids in bulk "to clean
145
+ up": the buffer is the only copy, so an entry removed before its ticket
146
+ exists is gone. Filing first makes a partial run — a rate limit, a network
147
+ failure — safely resumable, because everything unfiled is still in the
148
+ buffer for the next run. An id the command reports as `absent` was already
149
+ drained by an earlier run; that is expected, not an error.
150
+ - **Report the drain in the run proof**: how many were pending, which ids
151
+ were re-homed to which tickets, and how many remain.
152
+
153
+ A non-empty overflow also means the ledger turned a judged-durable capture
154
+ away, so treat it as top-priority budget-pressure evidence when selecting
155
+ RETIRE/PROMOTE candidates this run — the room has to come from somewhere.
120
156
  2. **Evidence** — gather the five axes per item; drop items with none.
121
157
  3. **Classify** — pass each candidate (`rule`, `why`, `provenance`,
122
158
  `evidence`) to the ladder router (the `skill-evaluator` agent). The router
@@ -187,8 +187,13 @@ No learning content is ever committed without a PR — there is no other write p
187
187
  Learnings ledger saturated — a durable capture was dropped for budget. The projection is now omitting entries; promote or retire a learning to reclaim room. This is an operator-visible budget-pressure signal, not itself a new learning.
188
188
  ```
189
189
 
190
- The `[lisa-ledger-saturated]` marker sits **outside the `[lisa-learning-*]` namespace** that the gardener (`lisa-learnings-audit`) auto-excludes from candidacy. The gardener already derives budget pressure independently — it measures the entries its `projectLearnings` projection has to omit — so this ticket does not feed that measurement mechanically; its job is an idempotent, operator-visible notification that saturation happened. Keeping it outside `[lisa-learning-*]` matters so the audit tooling does not silently filter it as learning-machinery noise before an operator sees it. **Do not reuse a `[lisa-learning-*]` marker here.** The candidate itself is still dropped; this signal records the saturation, it does not persist the rule.
191
- 4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings file; verify with `git diff --name-only` that the diff touches nothing else.
190
+ The `[lisa-ledger-saturated]` marker sits **outside the `[lisa-learning-*]` namespace** that the gardener (`lisa-learnings-audit`) auto-excludes from candidacy. The gardener already derives budget pressure independently — it measures the entries its `projectLearnings` projection has to omit — so this ticket does not feed that measurement mechanically; its job is an idempotent, operator-visible notification that saturation happened. Keeping it outside `[lisa-learning-*]` matters so the audit tooling does not silently filter it as learning-machinery noise before an operator sees it. **Do not reuse a `[lisa-learning-*]` marker here.** The signal records the saturation; it does not persist the rule.
191
+ - **The dropped capture is preserved for you — ship it.** The writer does this itself, so there is nothing to compose by hand: when the budget re-assertion fails, `persistConsolidatedLearning` / `persistLearningEntry` write the rejected entry to the **overflow buffer** — `PROJECT_LEARNINGS.overflow.md`, a sibling of the resolved ledger — and then re-raise a budget error naming that file. The thrown error is the confirmation; if it does not name the overflow, the capture was NOT preserved (the overflow is full too) and that is what the error says instead.
192
+
193
+ The overflow is git-tracked and bound to the same union merge driver as the ledger, so **still open the pull request on a budget-forced drop** — carrying the overflow file instead of the ledger. That is the whole point of preserving it: a capture that reaches only a disposable `learning/<fingerprint>` worktree is durable in name only, and the gardener draining `main` would never see it. Never hand-edit the overflow, and never delete an entry from it to make room; the gardener drains it.
194
+
195
+ So a budget-forced drop produces **both** halves and neither substitutes for the other: the `[lisa-ledger-saturated]` ticket (the signal that room must be reclaimed) and a pull request carrying the overflow file (the content itself). Report both in the run summary.
196
+ 4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings surface — the ledger, or the overflow buffer on a budget-forced drop; verify with `git diff --name-only` that the diff touches nothing else.
192
197
  5. **PR body.** Exactly one marker line plus the reviewable story:
193
198
 
194
199
  ```markdown
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.298.1",
3
+ "version": "2.299.0",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"