@codyswann/lisa 2.236.0 → 2.238.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 (104) hide show
  1. package/dist/cli/check-learnings-budget-cmd.d.ts +20 -0
  2. package/dist/cli/check-learnings-budget-cmd.d.ts.map +1 -0
  3. package/dist/cli/check-learnings-budget-cmd.js +59 -0
  4. package/dist/cli/check-learnings-budget-cmd.js.map +1 -0
  5. package/dist/cli/index.d.ts +3 -0
  6. package/dist/cli/index.d.ts.map +1 -1
  7. package/dist/cli/index.js +29 -1
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/core/learnings-budget-check.d.ts +42 -0
  10. package/dist/core/learnings-budget-check.d.ts.map +1 -0
  11. package/dist/core/learnings-budget-check.js +210 -0
  12. package/dist/core/learnings-budget-check.js.map +1 -0
  13. package/dist/core/learnings-document.js +6 -1
  14. package/dist/core/learnings-document.js.map +1 -1
  15. package/dist/core/learnings-writer.d.ts +22 -0
  16. package/dist/core/learnings-writer.d.ts.map +1 -1
  17. package/dist/core/learnings-writer.js +51 -8
  18. package/dist/core/learnings-writer.js.map +1 -1
  19. package/dist/core/learnings.d.ts +1 -1
  20. package/dist/core/learnings.d.ts.map +1 -1
  21. package/dist/core/learnings.js +1 -1
  22. package/dist/core/learnings.js.map +1 -1
  23. package/package.json +1 -1
  24. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  26. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  27. package/plugins/lisa/.codex-plugin/skills/lisa-git-submit-pr/SKILL.md +3 -2
  28. package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/SKILL.md +145 -0
  29. package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/agents/openai.yaml +4 -0
  30. package/plugins/lisa/agents/learning-judge.md +119 -0
  31. package/plugins/lisa/commands/persist-learning.md +6 -0
  32. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  33. package/plugins/lisa/skills/lisa-git-submit-pr/SKILL.md +3 -2
  34. package/plugins/lisa/skills/lisa-persist-learning/SKILL.md +145 -0
  35. package/plugins/lisa/skills/lisa-persist-learning/agents/openai.yaml +4 -0
  36. package/plugins/lisa-agy/agents/learning-judge.md +119 -0
  37. package/plugins/lisa-agy/commands/lisa/persist-learning.md +6 -0
  38. package/plugins/lisa-agy/plugin.json +1 -1
  39. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  40. package/plugins/lisa-agy/skills/lisa-git-submit-pr/SKILL.md +3 -2
  41. package/plugins/lisa-agy/skills/lisa-persist-learning/SKILL.md +145 -0
  42. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  44. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  45. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-copilot/agents/learning-judge.agent.md +119 -0
  49. package/plugins/lisa-copilot/commands/lisa/persist-learning.md +6 -0
  50. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  51. package/plugins/lisa-copilot/skills/lisa-git-submit-pr/SKILL.md +3 -2
  52. package/plugins/lisa-copilot/skills/lisa-persist-learning/SKILL.md +145 -0
  53. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-cursor/agents/learning-judge.md +119 -0
  55. package/plugins/lisa-cursor/commands/lisa/persist-learning.md +6 -0
  56. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  57. package/plugins/lisa-cursor/skills/lisa-git-submit-pr/SKILL.md +3 -2
  58. package/plugins/lisa-cursor/skills/lisa-persist-learning/SKILL.md +145 -0
  59. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  61. package/plugins/lisa-expo-agy/plugin.json +1 -1
  62. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  67. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  72. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  76. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  77. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  81. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  82. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  86. package/plugins/lisa-rails-agy/plugin.json +1 -1
  87. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  91. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  92. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  96. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  97. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  99. package/plugins/src/base/agents/learning-judge.md +119 -0
  100. package/plugins/src/base/commands/persist-learning.md +6 -0
  101. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  102. package/plugins/src/base/skills/lisa-git-submit-pr/SKILL.md +3 -2
  103. package/plugins/src/base/skills/lisa-persist-learning/SKILL.md +145 -0
  104. package/scripts/check-learnings-budget.ts +29 -163
@@ -19,17 +19,28 @@ merges" loop. Other skills delegate here instead of re-implementing it. Runs
19
19
  `chore(release): X.Y.Z [skip ci]` commits and breaks release promotion detection.
20
20
  - `verify_commit=<sha>` — the commit that MUST end up in the merged base (for the
21
21
  ancestry check). Default: the PR head at the time this skill starts.
22
+ - `auto_merge=<true|false>` — whether this skill is allowed to merge the PR at
23
+ all. Default `true` (existing behavior, byte-identical for every current
24
+ caller). With `auto_merge=false` the PR is deliberately left for a human:
25
+ skip the **entire** "## 1. Enable auto-merge" step — including its
26
+ direct-merge capability fallback — and never run any `gh pr merge` variant.
27
+ Still drive every blocker per `on_blocker` (green checks, resolved reviews,
28
+ synced branch), then stop at the `awaiting-human` terminal state below. A
29
+ green, open, un-merged PR is the *success* outcome of this mode, not a hang.
30
+ Used by learning-persistence flows whose low-confidence PRs must wait for a
31
+ human (`lisa-persist-learning`).
22
32
  - `on_blocker=<fix|report>` — what to do when a blocker needs code or review work.
23
33
  Default `fix`.
24
34
  - **`fix`** (the full loop): resolve conflicts, fix failing checks, address +
25
35
  resolve review comments, dismiss stale review gates — drive until merged.
26
36
  - **`report`** (diagnose & mechanically nudge only): perform just the safe,
27
- idempotent, non-destructive actions — ensure auto-merge is enabled and, if the
37
+ idempotent, non-destructive actions — ensure auto-merge is enabled (when
38
+ `auto_merge=true`) and, if the
28
39
  PR is `BEHIND` but otherwise clean, run `gh pr update-branch` only when the
29
40
  base branch requires strict up-to-date checks. For **anything** that would
30
41
  require editing code, resolving threads, or dismissing a review, **do not
31
42
  act** — stop and return a structured blocker classification
32
- (`merged` / `will-merge-after-resync` / `blocked:<conflict|checks|changes_requested|deploy>`)
43
+ (`merged` / `will-merge-after-resync` / `blocked:<conflict|checks|changes_requested|deploy|pending-auto-fix>`)
33
44
  so the caller applies its own policy. This is the mode `repair-intake` and the
34
45
  build-intake skills use to diagnose-and-route without fixing in place.
35
46
 
@@ -75,6 +86,31 @@ releases is why the TTL exists; do not rely on it as the normal release path.
75
86
 
76
87
  ## 1. Enable auto-merge
77
88
 
89
+ **Gate: only when `auto_merge=true` (the default).** When `auto_merge=false`,
90
+ skip the enable step and its capability fallback — do not enable auto-merge,
91
+ and do **not** use the capability fallback below: on a repo that disallows
92
+ auto-merge, an `auto_merge=false` PR must stay OPEN for human triage, never be
93
+ silently direct-merged.
94
+
95
+ With `auto_merge=false`, also **disarm any pre-existing auto-merge latch**
96
+ before entering the watch loop — skipping the enable step is not enough when a
97
+ prior session (or `lisa-git-submit-pr`'s default path) already armed the PR,
98
+ because an armed latch would still merge the instant checks go green:
99
+
100
+ ```bash
101
+ armed=$(gh pr view <pr> --json autoMergeRequest -q .autoMergeRequest)
102
+ if [ "$armed" != "null" ] && [ -n "$armed" ]; then
103
+ gh pr merge <pr> --disable-auto
104
+ fi
105
+ gh pr view <pr> --json autoMergeRequest -q .autoMergeRequest # must print null
106
+ ```
107
+
108
+ If the disarm fails or the re-read still shows an armed `autoMergeRequest`,
109
+ **fail closed**: treat the PR as a hard block (section 4) and report that the
110
+ `awaiting-human` state was NOT reached — never proceed to a state in which the
111
+ PR could merge without a human. Once disarmed (or already unarmed), proceed
112
+ straight to the watch loop (section 2).
113
+
78
114
  Before enabling auto-merge, capture the live PR head and compare it to
79
115
  `verify_commit`:
80
116
 
@@ -98,9 +134,11 @@ started, then re-enable auto-merge. Do not leave auto-merge armed while a
98
134
  required fix, CodeRabbit follow-up, generated artifact update, or CI auto-fix is
99
135
  still in flight.
100
136
 
101
- - **Capability fallback**: if the repo disallows auto-merge, do not fail. Keep
102
- watching; once checks are green, the review gate is clear, and `mergeable == MERGEABLE`,
103
- run `gh pr merge <pr> --<merge_method>` directly.
137
+ - **Capability fallback** (`auto_merge=true` only): if the repo disallows
138
+ auto-merge, do not fail. Keep watching; once checks are green, the review gate
139
+ is clear, and `mergeable == MERGEABLE`, run `gh pr merge <pr> --<merge_method>`
140
+ directly. This fallback lives inside the gated section above — with
141
+ `auto_merge=false` it never fires; the PR remains open awaiting a human.
104
142
 
105
143
  ## 2. The watch loop
106
144
 
@@ -114,9 +152,17 @@ Handle every blocker class; after any fix, re-poll and continue. Do not stop whi
114
152
  the PR is still open and progress is possible. On each iteration, refresh the
115
153
  babysitter lease if its last stamp is older than ~30 minutes (section 0).
116
154
 
155
+ With **`auto_merge=false`**, the loop's goal changes from "merged" to "clean and
156
+ waiting": drive blockers exactly the same, but exit successfully at
157
+ `awaiting-human` (section 4) once the PR is open with green checks, a clear
158
+ review gate, and `mergeable == MERGEABLE`. Never enable auto-merge or merge
159
+ directly in this mode.
160
+
117
161
  In **`on_blocker=report`** mode, only the mechanical step (a) and auto-merge enabling
118
- apply; for any of (b)–(e) do not act — classify the blocker and return per the input
119
- contract above.
162
+ (when `auto_merge=true`) apply; for any of (b)–(f) do not act — classify the blocker
163
+ and return per the input contract above. That includes (f): adjudicating a pending
164
+ auto-fix PR (merging, closing, or deleting its branch) is destructive work, not
165
+ diagnosis — return its classification (`blocked:pending-auto-fix`) instead.
120
166
 
121
167
  ### a. Branch behind base (`mergeStateStatus == BEHIND`)
122
168
  Before proactively syncing a clean `BEHIND` PR, check whether the base branch
@@ -196,7 +242,9 @@ needed, otherwise close it and delete the side branch. Never leave it dangling
196
242
  — it represents a competing writer's pending work. Merging it mutates the
197
243
  driven branch, so treat it like any other push: disarm auto-merge first,
198
244
  re-read `headRefOid`, reset `verify_commit` to the merged head, wait for that
199
- head's checks to start, then re-enable auto-merge (section 1).
245
+ head's checks to start, then re-enable auto-merge (section 1). In
246
+ `on_blocker=report` mode this whole step is off-limits (diagnose-only): do not
247
+ merge, close, or delete anything — return `blocked:pending-auto-fix`.
200
248
 
201
249
  ## 3. Merge and verify it actually shipped (ancestry check)
202
250
 
@@ -220,6 +268,12 @@ failed drive-to-merge outcome, not a successful closeout.
220
268
  Loop until one of:
221
269
 
222
270
  - **`MERGED`** and the ancestry check passes → success.
271
+ - **`awaiting-human`** (`auto_merge=false` only) → success. The PR is `OPEN`,
272
+ required checks are green, the review gate is clear, and
273
+ `mergeable == MERGEABLE`, with auto-merge deliberately not enabled
274
+ (`gh pr view <pr> --json autoMergeRequest` shows `null`). Report the PR URL
275
+ and state — a human decides whether it merges. This is the intended outcome
276
+ of auto-merge-off mode, not a stall; do not keep looping for `MERGED`.
223
277
  - **`CLOSED`** → report (PR was closed without merge).
224
278
  - **Hard block needing a human**: an unresolvable conflict, a failing check that
225
279
  needs design input, or genuine unresolved human objection (not a bot gate). Stop
@@ -14,6 +14,7 @@ Recognized optional hints:
14
14
  - `target_branch=<branch>` or `base=<branch>` — intended PR base branch, used to decide whether a GitHub closing keyword is safe.
15
15
  - `tracker_provider=<github|linear|jira|none>` — explicit provider when the ref shape is ambiguous.
16
16
  - `pr_url=<url>` — live pull request URL, only needed when updating tracker backlinks from an existing PR context.
17
+ - `auto_merge=<true|false>` — whether the PR should merge automatically. Default `true` (existing behavior for every current caller). With `auto_merge=false`, skip step 5 entirely (never run `gh pr merge --auto`) and pass `auto_merge=false` through to the `drive-pr-to-merge` delegation in step 6 so the PR is driven to a clean, green, OPEN state and then left awaiting a human.
17
18
 
18
19
  ## Workflow
19
20
 
@@ -34,10 +35,10 @@ Recognized optional hints:
34
35
  - Include native development linkage for the source work item when `work_item_ref` can be inferred from `$ARGUMENTS`, the current branch name, an existing PR body, or the issue/ticket context passed by the caller.
35
36
  - After the PR exists, ensure the source work item has a backlink to the PR: invoke `lisa-tracker-sync` with the work item, milestone `pr-ready`, the live `pr_url`, and `tracker_provider` when known. This makes ticket -> PR linkage mandatory, not just a best-effort milestone comment.
36
37
  - After the PR exists, re-resolve the live Pull Request node id and, when `github.projects.v2` is enabled, invoke `lisa-github-project-v2` with `operation: ensure-item` and `content_node_id: <pull-request-node-id>` so linked pull requests join the configured shared Project without replacing the PR as the durable review/merge surface.
37
- 5. **Auto-merge**: Choose merge strategy by PR type:
38
+ 5. **Auto-merge** (only when `auto_merge=true`, the default — with `auto_merge=false` skip this step entirely): Choose merge strategy by PR type:
38
39
  - **Promotion PRs** (env → env, e.g. `dev` → `staging`): use `gh pr merge --auto --merge` (never squash). Squashing flattens the constituent `chore(release): X.Y.Z [skip ci]` commits into one commit titled with the PR title, stripping the `[skip ci]` markers and breaking the release workflow's promotion-detection regex — the destination branch then double-bumps its version. `--merge` keeps each `chore(release)` commit (and its `[skip ci]` marker) intact under a clean merge commit subject the workflow can recognize.
39
40
  - **Feature PRs** (anything → `dev`): use `gh pr merge --auto --merge`.
40
- 6. **Drive to merge**: Opening the PR and enabling auto-merge is not terminal. Delegate the full mergeability loop to the `drive-pr-to-merge` skill — invoke it with the PR number and `merge_method=merge` (and `verify_commit=<pushed head sha>` for the ancestry check). That skill is the single source of truth for clearing every blocker: auto-merge with direct-merge fallback, `BEHIND` re-sync, conflict resolution, failing-check fixes, human + bot (CodeRabbit) review-comment handling with GraphQL thread resolution, stale `CHANGES_REQUESTED` dismissal, and post-merge ancestry verification. It runs inline and uses plain `gh`/`git` so Claude and Codex behave identically. Do not re-implement the loop here.
41
+ 6. **Drive to merge**: Opening the PR and enabling auto-merge is not terminal. Delegate the full mergeability loop to the `drive-pr-to-merge` skill — invoke it with the PR number and `merge_method=merge` (and `verify_commit=<pushed head sha>` for the ancestry check). When the caller passed `auto_merge=false`, also pass `auto_merge=false` so the delegated loop drives the PR to green-and-open (`awaiting-human`) instead of merged — never merging it, even on repos that disallow auto-merge. That skill is the single source of truth for clearing every blocker: auto-merge with direct-merge fallback, `BEHIND` re-sync, conflict resolution, failing-check fixes, human + bot (CodeRabbit) review-comment handling with GraphQL thread resolution, stale `CHANGES_REQUESTED` dismissal, and post-merge ancestry verification. It runs inline and uses plain `gh`/`git` so Claude and Codex behave identically. Do not re-implement the loop here.
41
42
 
42
43
  ### Native Development Linkage
43
44
 
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: lisa-persist-learning
3
+ description: This skill should be used when a candidate learning (from a failure signal, rejection, or debrief) needs to be judged and routed. It computes a stable fingerprint, runs the candidate through the hostile-default learning-judge gate, and performs exactly the verdict's side effects — a dropped-with-reason note on the triggering issue (drop), an upstream handoff marker (lisa-upstream), or a confidence-routed pull request that touches only the learnings surface (durable-learning). Idempotent via marker dedupe; headless-safe; never blocks the primary build flow.
4
+ ---
5
+
6
+ # Persist Learning
7
+
8
+ Route ONE candidate learning through the judgment gate and act on the verdict. Candidate: $ARGUMENTS
9
+
10
+ Most candidates are dropped — that is the gate working, not a failure. Nothing is ever silent (every drop leaves a visible note), and no learning content ever reaches the learnings surface outside a pull request.
11
+
12
+ ## Candidate Input
13
+
14
+ Accept the candidate as JSON or `key=value` fields:
15
+
16
+ - `rule` — the proposed learning (must fit the executable contract: ≤240 chars, ≤2 lines)
17
+ - `why` — the causal claim
18
+ - `provenance` — stable refs (issues/PRs/commits/comments), ≤20
19
+ - `evidence_links` — concrete evidence refs available for citation
20
+ - `scope_hint` — `project` | `upstream` (a hint; the judge decides)
21
+ - `triggering_issue` — the issue/work item whose failure produced this candidate (required)
22
+ - `fingerprint` — optional; computed below when absent
23
+
24
+ ## Phase 0 — Fingerprint (stable dedupe key)
25
+
26
+ Every marker and branch below keys off one deterministic fingerprint:
27
+
28
+ ```text
29
+ fingerprint = "sll4-" + first 12 hex chars of sha1(normalized_rule + "\n" + triggering_issue)
30
+ normalized_rule = rule lowercased, all whitespace runs collapsed to single spaces, trimmed
31
+ ```
32
+
33
+ ```bash
34
+ NORM=$(printf '%s' "$RULE" | tr '[:upper:]' '[:lower:]' | tr -s '[:space:]' ' ' | sed 's/^ *//; s/ *$//')
35
+ FP="sll4-$(printf '%s\n%s' "$NORM" "$TRIGGERING_ISSUE" | shasum -a 1 | cut -c1-12)"
36
+ ```
37
+
38
+ (Use `sha1sum` where `shasum` is unavailable.) The same rule for the same triggering issue always produces the same fingerprint, so re-runs dedupe instead of duplicating comments, PRs, or branches.
39
+
40
+ ## Phase 1 — Judge (mandatory, never skipped)
41
+
42
+ Invoke the `learning-judge` agent (via the Agent/Task tool with `subagent_type: "learning-judge"` — the same invoke pattern `learner` uses for `skill-evaluator`) with the full candidate including the fingerprint. It returns a verdict: `classification`, `cited_evidence[]`, `rationale`, `confidence` (durable only), `disposition`.
43
+
44
+ **Respect the verdict — do not override it.** Never re-run the judge hoping for a different answer, and never persist anything the judge did not classify `durable-learning`.
45
+
46
+ ## Phase 2 — Route by disposition
47
+
48
+ All issue/PR comments below follow the marker-dedupe discipline from `lisa-github-write-prd` Phase 2, each with its own producer tag: match on the **marker, never the title or text**; **exactly one marker per body**; **never write a markerless body** (it breaks all future dedupe); include the eventual-consistency guard — when the `gh` search index is stale, also enumerate the bodies directly (`gh issue view <n> --json comments --jq '.comments[].body'` or `gh pr list --json number,body`) and grep for the marker before deciding to create.
49
+
50
+ ### `drop` (classification `one-off` or `misunderstanding/spec-gap`)
51
+
52
+ Post **one** comment on the triggering issue and write **nothing** — zero bytes — to the learnings surface (not a stub, not a placeholder):
53
+
54
+ ```markdown
55
+ <!-- [lisa-learning-drop] key=<fingerprint> -->
56
+ Dropped (<classification> — <plain-language gloss>): <reason>.
57
+ ```
58
+
59
+ The note is one line naming the classification (with its fixed plain-language gloss) and the reason, readable by a non-technical operator. Use exactly these glosses per class:
60
+
61
+ | Classification | Gloss |
62
+ |----------------|-------|
63
+ | `one-off` | a one-time fluke, not a recurring pattern |
64
+ | `misunderstanding/spec-gap` | traced to an unclear requirement, not a durable lesson |
65
+ | `lisa-upstream` | root cause is Lisa itself; routed upstream |
66
+
67
+ (The `lisa-upstream` gloss is used by the handoff note below, not by a drop note.) Dedupe before posting: if any comment on the triggering issue already carries `[lisa-learning-drop] key=<fingerprint>`, do not post again — report the existing note.
68
+
69
+ ### `handoff-upstream` (classification `lisa-upstream`)
70
+
71
+ Emit the handoff marker only — file **nothing** (no upstream issue, no local rule; the upstream filing flow [SLL-5] consumes this marker later):
72
+
73
+ ```markdown
74
+ <!-- [lisa-learning-upstream-handoff] key=<fingerprint> -->
75
+ Upstream handoff (lisa-upstream — root cause is Lisa itself; routed upstream): <reason>. Nothing persisted locally; upstream filing is a separate flow.
76
+ ```
77
+
78
+ Same one-comment marker dedupe on the triggering issue.
79
+
80
+ ### `persist` (classification `durable-learning`)
81
+
82
+ Continue to Phase 3.
83
+
84
+ ## Phase 3 — Persist via PR (durable-learning only)
85
+
86
+ No learning content is ever committed without a PR — there is no other write path, and the PR must touch **only** the learnings surface (any other changed file is a bug).
87
+
88
+ 1. **PR dedupe.** Search all PRs for the marker `[lisa-learning-pr] key=<fingerprint>` in the body (`gh pr list --state all --search '"<marker>" in:body' --json number,url`), with the stale-index guard above. If one exists, reference it and stop — never open a duplicate.
89
+ 2. **Resolve the learnings surface path — never hardcode it.** The canonical path is `resolveProjectLearningsFile` from `@codyswann/lisa/learnings`: the `PROJECT_LEARNINGS.md` sibling of the configured `.lisa.config.json` `projectRulesFile` (default `.claude/rules/PROJECT_LEARNINGS.md`):
90
+
91
+ ```bash
92
+ LEARNINGS_FILE=$(node -e 'import("@codyswann/lisa/learnings").then(async m => { const c = await m.readProjectConfig(process.cwd()); console.log(m.resolveProjectLearningsFile(c)); })')
93
+ ```
94
+
95
+ 3. **Consolidation check (mandatory before writing).** Parse the existing entries (`parseLearningsFile` from `@codyswann/lisa/learnings`) and look for entries related to the new rule (same failure class, overlapping topic, or near-duplicate wording). Then write through the executable contract — **never hand-edit the markdown**:
96
+ - **Related entry found** → consolidate via `persistConsolidatedLearning(projectRoot, entry, { supersede: [<related ids>] })`, merging the old entry's still-true content into the new rule. Never append a near-duplicate sibling — a sibling is a bug that fails review.
97
+ - **No related entry** → append via `persistLearningEntry(projectRoot, entry)` and state in the PR body why appending was correct.
98
+ - Entry mapping: `id` = the fingerprint; `rule`/`why`/`provenance` from the candidate; `first_learned` = `last_confirmed` = today (ISO date; on consolidation keep the superseded entry's earliest `first_learned`); `confidence` = the judge's `high`/`low`. The writer re-asserts the entry and token budgets — an over-budget failure means consolidate harder or drop, never truncate by hand.
99
+ 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.
100
+ 5. **PR body.** Exactly one marker line plus the reviewable story:
101
+
102
+ ```markdown
103
+ <!-- [lisa-learning-pr] key=<fingerprint> -->
104
+
105
+ ## Learning
106
+ <rule> — <judge rationale>
107
+
108
+ ## Provenance
109
+ - Triggering issue: <ref>
110
+ - Ancestor issue/PR (the past failure this is round 2 of): <ref or none found>
111
+ - Rejection comments that produced the candidate: <refs or none>
112
+ - Cited evidence (from the judge): <refs>
113
+
114
+ ## Consolidation decision
115
+ <"Superseded entry id(s) X, Y: <why they merged>" | "Appended: no related entry exists — <one-line search summary>">
116
+ ```
117
+
118
+ For a **low-confidence** PR, the body must additionally end with this fixed decision-framing line so the human at the gate knows exactly what merging means:
119
+
120
+ ```markdown
121
+ **Merge** to adopt this rule into every future session for all six coding agents; **close** to discard it.
122
+ ```
123
+
124
+ 6. **Confidence routing.**
125
+ - **`high`** → submit through `lisa-git-submit-pr` with its defaults: auto-merge ON, merging through the project's normal gates.
126
+ - **`low`** → submit through `lisa-git-submit-pr` with `auto_merge=false` (drives the PR to green-and-open `awaiting-human`, never merging — even on repos that disallow auto-merge). Then apply the human-triage label, creating it idempotently first (the `lisa-drive-pr-to-merge` label pattern — `|| true` tolerates only already-exists, so verify):
127
+
128
+ ```bash
129
+ gh label create "learning:needs-triage" \
130
+ --description "Low-confidence learning PR awaiting human triage" \
131
+ --color FBCA04 || true
132
+ gh label list --search "learning:needs-triage" --json name \
133
+ --jq '[.[].name] | contains(["learning:needs-triage"])' # must print true
134
+ gh pr edit <pr> --add-label "learning:needs-triage"
135
+ ```
136
+
137
+ A low-confidence PR sitting OPEN with green checks and `autoMergeRequest: null` is this mode's **success** state — a human merges or closes it. All pending low-confidence learning PRs are findable by filtering on the `learning:needs-triage` label (`gh pr list --label "learning:needs-triage"`).
138
+
139
+ ## Rules
140
+
141
+ - **Headless-safe**: no interactive prompts; must run identically under an intake cron.
142
+ - **Never block the build**: if judging or persistence fails, report the failure and let the primary flow continue — shipping the triggering issue always outranks recording a learning about it.
143
+ - **No learning loops about learning**: never feed this skill a candidate whose triggering artifact is itself learning machinery (a `[lisa-learning-*]`-marked comment, learning PR, or handoff); the judge's Step 0 guard backstops this.
144
+ - **Idempotent**: re-running with the same candidate posts no duplicate comment, opens no duplicate PR, and writes no duplicate entry — the fingerprint and markers guarantee it.
145
+ - **One write path**: the learnings surface changes only through `persistLearningEntry` / `persistConsolidatedLearning` inside a PR. Never hand-edit the file, never commit it to the default branch directly.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.236.0",
3
+ "version": "2.238.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.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Expo and 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.236.0",
3
+ "version": "2.238.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.236.0",
3
+ "version": "2.238.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.236.0",
3
+ "version": "2.238.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-harper-fabric",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.236.0",
3
+ "version": "2.238.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"