@codyswann/lisa 2.239.0 → 2.240.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 (111) hide show
  1. package/dist/core/learnings-entry.d.ts +9 -0
  2. package/dist/core/learnings-entry.d.ts.map +1 -1
  3. package/dist/core/learnings-entry.js +4 -2
  4. package/dist/core/learnings-entry.js.map +1 -1
  5. package/dist/core/learnings-writer.d.ts +33 -0
  6. package/dist/core/learnings-writer.d.ts.map +1 -1
  7. package/dist/core/learnings-writer.js +75 -1
  8. package/dist/core/learnings-writer.js.map +1 -1
  9. package/package.json +1 -1
  10. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  11. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  12. package/plugins/lisa/.codex-plugin/skills/lisa-attribute-failure/SKILL.md +142 -0
  13. package/plugins/lisa/.codex-plugin/skills/lisa-attribute-failure/agents/openai.yaml +4 -0
  14. package/plugins/lisa/.codex-plugin/skills/lisa-doctor/SKILL.md +28 -77
  15. package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +16 -0
  16. package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +16 -0
  17. package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +16 -0
  18. package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/SKILL.md +61 -8
  19. package/plugins/lisa/commands/attribute-failure.md +6 -0
  20. package/plugins/lisa/commands/persist-learning.md +1 -1
  21. package/plugins/lisa/rules/reference/project-learnings.md +18 -0
  22. package/plugins/lisa/skills/lisa-attribute-failure/SKILL.md +142 -0
  23. package/plugins/lisa/skills/lisa-attribute-failure/agents/openai.yaml +4 -0
  24. package/plugins/lisa/skills/lisa-doctor/SKILL.md +28 -77
  25. package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +16 -0
  26. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +16 -0
  27. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +16 -0
  28. package/plugins/lisa/skills/lisa-persist-learning/SKILL.md +62 -9
  29. package/plugins/lisa-agy/commands/lisa/attribute-failure.md +6 -0
  30. package/plugins/lisa-agy/commands/lisa/persist-learning.md +1 -1
  31. package/plugins/lisa-agy/plugin.json +1 -1
  32. package/plugins/lisa-agy/skills/lisa-attribute-failure/SKILL.md +142 -0
  33. package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +28 -77
  34. package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +16 -0
  35. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +16 -0
  36. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +16 -0
  37. package/plugins/lisa-agy/skills/lisa-persist-learning/SKILL.md +62 -9
  38. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  41. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-copilot/commands/lisa/attribute-failure.md +6 -0
  45. package/plugins/lisa-copilot/commands/lisa/persist-learning.md +1 -1
  46. package/plugins/lisa-copilot/rules/reference/project-learnings.md +18 -0
  47. package/plugins/lisa-copilot/skills/lisa-attribute-failure/SKILL.md +142 -0
  48. package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +28 -77
  49. package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +16 -0
  50. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +16 -0
  51. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +16 -0
  52. package/plugins/lisa-copilot/skills/lisa-persist-learning/SKILL.md +62 -9
  53. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-cursor/commands/lisa/attribute-failure.md +6 -0
  55. package/plugins/lisa-cursor/commands/lisa/persist-learning.md +1 -1
  56. package/plugins/lisa-cursor/rules/project-learnings-reference.mdc +18 -0
  57. package/plugins/lisa-cursor/skills/lisa-attribute-failure/SKILL.md +142 -0
  58. package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +28 -77
  59. package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +16 -0
  60. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +16 -0
  61. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +16 -0
  62. package/plugins/lisa-cursor/skills/lisa-persist-learning/SKILL.md +62 -9
  63. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-expo-agy/plugin.json +1 -1
  66. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  71. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  75. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  76. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  80. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  81. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  85. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  86. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  90. package/plugins/lisa-rails-agy/plugin.json +1 -1
  91. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  95. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  96. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  99. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  100. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  101. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  103. package/plugins/src/base/commands/attribute-failure.md +6 -0
  104. package/plugins/src/base/commands/persist-learning.md +1 -1
  105. package/plugins/src/base/rules/reference/project-learnings.md +18 -0
  106. package/plugins/src/base/skills/lisa-attribute-failure/SKILL.md +142 -0
  107. package/plugins/src/base/skills/lisa-doctor/SKILL.md +28 -77
  108. package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +16 -0
  109. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +16 -0
  110. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +16 -0
  111. package/plugins/src/base/skills/lisa-persist-learning/SKILL.md +62 -9
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: lisa-attribute-failure
3
+ description: "Event-triggered root-cause attribution for an arbitrary failure: decide, with cited evidence, whether the defect is Lisa's fault or the project's. Accepts a failure event (defect description, implicated files, rule/skill/hook in play) and returns a verdict of lisa | project | ambiguous plus the evidence relied on. Read-only — it never files, writes, or remediates; callers (lisa-doctor findings, the learning judgment gate, rework triage) consume the verdict. Extracted from lisa-doctor's upstream Lisa change-history diagnosis (#1494) so the same attribution procedure can run on ANY failure event, not only doctor config-audit findings."
4
+ allowed-tools: ["Skill", "Bash", "Read", "Glob", "Grep"]
5
+ ---
6
+
7
+ # Attribute Failure
8
+
9
+ Attribute ONE failure event to Lisa or the project, with cited evidence. Failure event: $ARGUMENTS
10
+
11
+ Before a Lisa-caused failure can be routed upstream, something has to say — with evidence — "this was Lisa's fault, not the project's." A failure has two possible causes: the project drifted, or **Lisa itself changed or shipped the defect**. This skill must distinguish them instead of blaming the project by default, and equally must never blame Lisa without conclusive evidence. Wrong attribution in either direction is the failure mode; **inconclusive evidence is always `ambiguous`, and ambiguous stays local and low-confidence — never upstream.**
12
+
13
+ ## Input — the failure event
14
+
15
+ Accept the event as JSON or `key=value` fields. This procedure is **event-triggered, not version-window-keyed**: any failure can be attributed, not only a doctor config-audit finding.
16
+
17
+ - `defect` — what went wrong, in plain language (required)
18
+ - `implicated_files` — the file paths where the defect lives or manifests (required when known)
19
+ - `surface_in_play` — the Lisa rule / skill / hook / template / workflow involved, if any
20
+ - `installed_version` / `latest_version` — the Lisa version window, when the caller knows it; otherwise resolve it as signal 3 describes
21
+ - `failure_class` — a short slug naming the class of failure (used by downstream filing for the root-cause key)
22
+
23
+ ## Output — the verdict
24
+
25
+ Return exactly one verdict — `lisa` | `project` | `ambiguous` — plus the evidence it relied on:
26
+
27
+ ```text
28
+ ## Attribution: [lisa | project | ambiguous]
29
+
30
+ **Signal:** [managed-surface | shipped-artifact-behavior | upstream-history | none-conclusive]
31
+ **Lisa surface at fault:** [path or rule/skill/hook name | n/a]
32
+ **Cited evidence:**
33
+ - [each item names the signal it came from and the concrete citation: file path + ownership proof, shipped artifact text, or commit sha/subject/version]
34
+ **Confidence note:** [why the evidence is conclusive, or exactly what was inconclusive]
35
+ ```
36
+
37
+ Verdict rules:
38
+
39
+ - **`lisa`** — at least one signal conclusively pins the defect on a Lisa-shipped surface or upstream change, and the evidence names that surface. Never emit `lisa` from degraded, truncated, or unverified history.
40
+ - **`project`** — the implicated files are project-owned, no shipped Lisa artifact drove the behavior, and the upstream-history window shows no relevant Lisa change. Cite the absence explicitly (which paths were checked, which window showed no relevant change).
41
+ - **`ambiguous`** — anything else: unresolvable version window, unreachable history, partial ownership, conflicting signals. Ambiguous is treated as local and low-confidence; nothing is escalated upstream from it.
42
+
43
+ ## The three signals (evaluate in order)
44
+
45
+ ### Signal 1 — the defect lives in a Lisa-managed surface
46
+
47
+ Resolve ownership of each implicated file the way doctor's config audit does:
48
+
49
+ - **Copy-overwrite / managed**: the path is populated by `lisa apply` from a stack template (`typescript/`, `expo/`, `nestjs/`, `cdk/`, `harper-fabric/`, `rails/` copy-overwrite trees), is a plugin-distributed rule/skill/hook/agent/command surface, carries Lisa governance markers or a Lisa version stamp, or is governed by `package.lisa.json` `force` keys. A defect in the shipped content of such a surface is **Lisa's fault** — any local edit would be overwritten on the next `lisa apply`.
50
+ - **Create-only**: the local copy is project-owned after scaffolding, but if the defect is faithful to the template as shipped, the template is still Lisa's fault (every newly scaffolded repo inherits it). Cite the template origin, and note the local copy will not be overwritten.
51
+ - **Project-owned**: not from a Lisa template and not Lisa-managed — signal 1 is negative; continue.
52
+
53
+ If the project locally modified a managed surface and the defect lives in the local modification, that is project drift, not a Lisa defect — signal 1 is negative for `lisa` and positive evidence for `project`.
54
+
55
+ ### Signal 2 — a shipped Lisa rule/skill/hook drove the wrong behavior
56
+
57
+ Read the shipped artifact named in `surface_in_play` (the plugin-distributed rule, skill, hook, agent, or CI workflow as Lisa ships it — not a local fork). If following its instructions or configuration as shipped produces the observed defect, the verdict is `lisa`: cite the artifact path and the specific shipped text or configuration that drove the behavior. If the artifact is shipped correct and was misapplied locally, that is evidence for `project`.
58
+
59
+ ### Signal 3 — the upstream-history window shows Lisa changed the relevant contract
60
+
61
+ This is the #1494 doctor procedure, unchanged in behavior — here it is the third signal, not the trigger. Whenever signals 1-2 are not conclusive on their own — and always before a definitive `project` verdict — pull Lisa's own git history for the version window and read what actually changed:
62
+
63
+ 1. **Resolve the version window.** Use the caller-supplied `installed_version`/`latest_version` when present; otherwise determine the project's installed Lisa version (the `@codyswann/lisa` entry in `package.json`/lockfile, or the plugin version stamp on the active runtime) and the latest published version (`npm view @codyswann/lisa version`, or the update check's cached result). An unresolvable window makes this signal inconclusive — it can support `ambiguous`, never `lisa`.
64
+ 2. **Pull the upstream history for that window** (read-only; no clone required when `gh` is available):
65
+
66
+ ```bash
67
+ gh api "repos/CodySwannGT/lisa/compare/v<installed>...v<latest>" \
68
+ --paginate --slurp |
69
+ jq '{total_commits: .[0].total_commits, files: [.[0].files[]?.filename], commits: [.[].commits[]? | {sha, subject: (.commit.message | split("\n")[0]), api_url: .url, html_url, parents: [.parents[]?.sha]}]}'
70
+ ```
71
+
72
+ `--paginate` fetches every page of commits, and `--slurp` gathers those pages into a single
73
+ array before the external `jq` projection runs. GitHub CLI does not permit its built-in `--jq`
74
+ flag together with `--slurp`, so keep the pipe as shown; without `--slurp`, paginated responses
75
+ are not one merged input. `total_commits` and `files` only need the first page
76
+ (files are capped at 300 and not repeated on later pages); `commits` flattens across all pages
77
+ while retaining each commit SHA and URLs needed for accurate follow-up.
78
+
79
+ After path-scoping identifies a candidate commit, fetch its targeted file-level diff context by
80
+ the retained SHA rather than attributing from the subject alone:
81
+
82
+ ```bash
83
+ gh api "repos/CodySwannGT/lisa/commits/<sha>" \
84
+ --jq '{sha, files: [.files[]? | select(.filename == "<relevant-path>" or (.filename | startswith("<relevant-prefix>/"))) | {filename, status, additions, deletions, changes, patch}]}'
85
+ ```
86
+
87
+ Preserve the returned filename, status, counts, and available patch with the SHA in the
88
+ upstream-history projection. A missing or truncated `patch` is not proof of no relevant change;
89
+ use the compare diff fallback below or downgrade the attribution to `ambiguous` when
90
+ commit-level context cannot be established.
91
+
92
+ The compare endpoint paginates commits (250 without `--paginate`) and only lists changed files
93
+ on the first page, capped at 300 total — a large version window can silently drop commits or
94
+ files. If `total_commits` or the file count looks truncated, re-run with the
95
+ `application/vnd.github.diff` accept header (`gh api ... -H "Accept: application/vnd.github.diff"`)
96
+ to pull the full patch text, or fall back to the bounded-fetch `git log` below. When completeness
97
+ still can't be established, say so in the evidence and return `ambiguous` rather than attributing
98
+ with unverified confidence.
99
+
100
+ Fallbacks, in order: `gh api repos/CodySwannGT/lisa/commits?path=<template-path>` for a
101
+ path-scoped view — note this endpoint has no way to bound results to the `v<installed>..v<latest>`
102
+ window, so treat its output as best-effort context only, not authoritative attribution; a
103
+ finite-depth, explicit-tag fetch, which is bounded to the two version refs and should be preferred
104
+ for definitive attribution; or the local marketplace/plugin cache checkout when the runtime has
105
+ one. For the fetch fallback, use a temporary directory and a fixed history ceiling:
106
+
107
+ ```bash
108
+ lisa_history_dir="$(mktemp -d)"
109
+ git init "$lisa_history_dir"
110
+ git -C "$lisa_history_dir" remote add origin https://github.com/CodySwannGT/lisa.git
111
+ git -C "$lisa_history_dir" fetch --no-tags --filter=blob:none --depth=256 origin \
112
+ refs/tags/v<installed>:refs/tags/v<installed> \
113
+ refs/tags/v<latest>:refs/tags/v<latest>
114
+ git -C "$lisa_history_dir" merge-base --is-ancestor v<installed> v<latest>
115
+ git -C "$lisa_history_dir" log --format='%H%x09%s' v<installed>..v<latest> -- <paths>
116
+ git -C "$lisa_history_dir" show --format=fuller --stat --patch <relevant-sha> -- <paths>
117
+ ```
118
+
119
+ The explicit tag refspecs, `--no-tags`, and finite `--depth=256` make this fetch bounded. Do not
120
+ silently deepen beyond that ceiling. If `merge-base --is-ancestor` fails, the shallow window is
121
+ incomplete (or the tags are not on one ancestry line): do not make definitive attribution from
122
+ it. If none of the bounded sources are reachable, or only the unbounded path-scoped fallback is
123
+ reachable, report the gap in the evidence and return `ambiguous` — never fail the caller's flow
124
+ because history was unavailable or incomplete, and never let a degraded history produce `lisa`.
125
+ 3. **Scope the reading to what the failure touches.** Filter the commit list to the paths that
126
+ generate the failing surface: the detected stacks' template dirs (`typescript/`, `expo/`, …),
127
+ `plugins/src/base/` for skills/hooks/rules, `scripts/` for governance scripts, and the shipped
128
+ config factories (`src/configs/`). A failure about a lint rule reads the lint-config commits,
129
+ not the whole log.
130
+ 4. **Attribute.** When the upstream history shows Lisa changed the relevant contract (a tightened
131
+ lint rule, a renamed check context, a new required config key), the verdict is `lisa` — cite the
132
+ commit subject/version. When history shows no relevant upstream change and signals 1-2 are also
133
+ negative, the project drifted — the verdict is `project`, citing the absence of a relevant
134
+ upstream change as the evidence.
135
+
136
+ ## Rules
137
+
138
+ - **Read-only.** This skill reads the project and Lisa's repository; it never writes to either, never files issues, and never remediates. Filing the upstream ticket on a `lisa` verdict is the caller's flow (`lisa-persist-learning`, `handoff-upstream` disposition); doctor maps verdicts into its findings; remediation stays with whoever called.
139
+ - **Never block.** Attribution failure, missing tooling (`gh` unavailable), or degraded history degrades to `ambiguous` with the gap named in the evidence — it never raises an error that stops the caller's build or audit.
140
+ - **Every verdict cites its evidence.** An attribution without a named signal and concrete citation is invalid — return `ambiguous` instead. Evidence destined for upstream surfaces quotes ONLY Lisa-owned surface text (never host env values, tokens/credentials, PII, or proprietary host code — link the host issue instead of quoting it); the binding redaction procedure lives in `lisa-persist-learning`'s `handoff-upstream` step.
141
+ - **Ambiguous never escalates.** `ambiguous` is a terminal local verdict: low-confidence, no upstream filing, no durable local rule derived from it.
142
+ - **Headless-safe and idempotent**: no prompts, no side effects, same event in → same verdict out.
@@ -296,88 +296,39 @@ without turning the base doctor into a second `lisa-wiki-doctor`.
296
296
  A failing or warning check has two possible causes: the project drifted, or **Lisa itself changed
297
297
  upstream** since this project last updated. Doctor must distinguish them instead of blaming the
298
298
  project by default. Whenever findings need explanation — and always before proposing repairs —
299
- pull Lisa's own git history and read what actually changed:
299
+ attribute the finding with real evidence.
300
+
301
+ The attribution procedure itself is shared: it lives in the `lisa-attribute-failure` skill
302
+ (extracted from this section so any failure event can be attributed, not only doctor findings).
303
+ Doctor invokes it per finding:
300
304
 
301
305
  1. **Resolve the version window.** Determine the project's installed Lisa version (the
302
306
  `@codyswann/lisa` entry in `package.json`/lockfile, or the plugin version stamp on the active
303
307
  runtime) and the latest published version (`npm view @codyswann/lisa version`, or the update
304
308
  check's cached result).
305
- 2. **Pull the upstream history for that window** (read-only; no clone required when `gh` is
306
- available):
307
-
308
- ```bash
309
- gh api "repos/CodySwannGT/lisa/compare/v<installed>...v<latest>" \
310
- --paginate --slurp |
311
- jq '{total_commits: .[0].total_commits, files: [.[0].files[]?.filename], commits: [.[].commits[]? | {sha, subject: (.commit.message | split("\n")[0]), api_url: .url, html_url, parents: [.parents[]?.sha]}]}'
312
- ```
313
-
314
- `--paginate` fetches every page of commits, and `--slurp` gathers those pages into a single
315
- array before the external `jq` projection runs. GitHub CLI does not permit its built-in `--jq`
316
- flag together with `--slurp`, so keep the pipe as shown; without `--slurp`, paginated responses
317
- are not one merged input. `total_commits` and `files` only need the first page
318
- (files are capped at 300 and not repeated on later pages); `commits` flattens across all pages
319
- while retaining each commit SHA and URLs needed for accurate follow-up.
320
-
321
- After path-scoping identifies a candidate commit, fetch its targeted file-level diff context by
322
- the retained SHA rather than attributing from the subject alone:
323
-
324
- ```bash
325
- gh api "repos/CodySwannGT/lisa/commits/<sha>" \
326
- --jq '{sha, files: [.files[]? | select(.filename == "<relevant-path>" or (.filename | startswith("<relevant-prefix>/"))) | {filename, status, additions, deletions, changes, patch}]}'
327
- ```
328
-
329
- Preserve the returned filename, status, counts, and available patch with the SHA in the
330
- upstream-history projection. A missing or truncated `patch` is not proof of no relevant change;
331
- use the compare diff fallback below or downgrade attribution to `WARN` when commit-level context
332
- cannot be established.
333
-
334
- The compare endpoint paginates commits (250 without `--paginate`) and only lists changed files
335
- on the first page, capped at 300 total — a large version window can silently drop commits or
336
- files. If `total_commits` or the file count looks truncated, re-run with the
337
- `application/vnd.github.diff` accept header (`gh api ... -H "Accept: application/vnd.github.diff"`)
338
- to pull the full patch text, or fall back to the bounded-fetch `git log` below. When completeness
339
- still can't be established, say so in the finding and mark it `WARN` rather than attributing
340
- drift with unverified confidence.
341
-
342
- Fallbacks, in order: `gh api repos/CodySwannGT/lisa/commits?path=<template-path>` for a
343
- path-scoped view — note this endpoint has no way to bound results to the `v<installed>..v<latest>`
344
- window, so treat its output as best-effort context only, not authoritative attribution; a
345
- finite-depth, explicit-tag fetch, which is bounded to the two version refs and should be preferred
346
- for definitive attribution; or the local marketplace/plugin cache checkout when the runtime has
347
- one. For the fetch fallback, use a temporary directory and a fixed history ceiling:
348
-
349
- ```bash
350
- lisa_history_dir="$(mktemp -d)"
351
- git init "$lisa_history_dir"
352
- git -C "$lisa_history_dir" remote add origin https://github.com/CodySwannGT/lisa.git
353
- git -C "$lisa_history_dir" fetch --no-tags --filter=blob:none --depth=256 origin \
354
- refs/tags/v<installed>:refs/tags/v<installed> \
355
- refs/tags/v<latest>:refs/tags/v<latest>
356
- git -C "$lisa_history_dir" merge-base --is-ancestor v<installed> v<latest>
357
- git -C "$lisa_history_dir" log --format='%H%x09%s' v<installed>..v<latest> -- <paths>
358
- git -C "$lisa_history_dir" show --format=fuller --stat --patch <relevant-sha> -- <paths>
359
- ```
360
-
361
- The explicit tag refspecs, `--no-tags`, and finite `--depth=256` make this fetch bounded. Do not
362
- silently deepen beyond that ceiling. If `merge-base --is-ancestor` fails, the shallow window is
363
- incomplete (or the tags are not on one ancestry line): do not make definitive attribution from
364
- it. If none of the bounded sources are reachable, or only the unbounded path-scoped fallback is
365
- reachable, report the gap as a `WARN`-level observability note — never fail the audit because
366
- history was unavailable or incomplete.
367
- 3. **Scope the reading to what the finding touches.** Filter the commit list to the paths that
368
- generate the failing surface: the detected stacks' template dirs (`typescript/`, `expo/`, …),
369
- `plugins/src/base/` for skills/hooks/rules, `scripts/` for governance scripts, and the shipped
370
- config factories (`src/configs/`). A finding about a lint rule failure reads the lint-config
371
- commits, not the whole log.
372
- 4. **Attribute the finding.** When the upstream history shows Lisa changed the contract (a
373
- tightened lint rule, a renamed check context, a new required config key), say so in `Observed:`
374
- with the commit subject/version, and let `Remediation:` point at the sanctioned adoption path
375
- (e.g. `lisa update` + re-apply, a documented config opt-out) rather than hand-editing managed
376
- files. When history shows no relevant upstream change, the project drifted — remediate on the
377
- project side.
378
-
379
- This history pull is part of doctor's read-only contract: it reads Lisa's repository, never writes
380
- to it, and repair suggestions stay suggestions.
309
+ 2. **Invoke `lisa-attribute-failure`** with the finding as the failure event: the defect
310
+ description, the implicated template/config paths, the rule/skill/hook in play, and the
311
+ resolved version window. The skill evaluates its three ordered signals — Lisa-managed surface
312
+ ownership, shipped rule/skill/hook behavior, and the upstream change-history window (the
313
+ version-window compare procedure formerly documented inline here, preserved verbatim in that
314
+ skill: pagination/`--slurp` handling, targeted per-SHA diff context, truncation caveats, and
315
+ the bounded explicit-tag fetch fallback) and returns `lisa` | `project` | `ambiguous` plus
316
+ the cited evidence.
317
+ 3. **Map the verdict into the finding.**
318
+ - `lisa` Lisa changed the contract (a tightened lint rule, a renamed check context, a new
319
+ required config key) or shipped the defective surface. Say so in `Observed:` with the cited
320
+ evidence (commit subject/version or managed-surface proof), and let `Remediation:` point at
321
+ the sanctioned adoption path (e.g. `lisa update` + re-apply, a documented config opt-out)
322
+ rather than hand-editing managed files.
323
+ - `project` history shows no relevant upstream change and the surface is project-owned: the
324
+ project drifted, so remediate on the project side.
325
+ - `ambiguous` history was unavailable, truncated, or otherwise inconclusive: report the gap
326
+ as a `WARN`-level observability note with the evidence gap named never fail the audit
327
+ because history was unavailable or incomplete, and never attribute drift with unverified
328
+ confidence.
329
+
330
+ This diagnosis remains part of doctor's read-only contract: the attribution skill reads Lisa's
331
+ repository, never writes to it, and repair suggestions stay suggestions.
381
332
 
382
333
  ## Output contract
383
334
 
@@ -325,6 +325,22 @@ If the canonical fix is merged but not yet present on the production branch, app
325
325
 
326
326
  This path is distinct from `BLOCKED`: ambiguity, open blockers, and duplicate-of-open findings remain held for human action and must not be auto-closed.
327
327
 
328
+ #### 3c.2 Confirm applied learnings (last_confirmed bump)
329
+
330
+ Run this at the end of 3c, after the lifecycle outcome is recorded and before 3d. It keeps the decay pass safe: a genuinely useful learning that keeps applying stays fresh, while dead weight ages out.
331
+
332
+ 1. **Identify which learnings demonstrably applied.** Resolve the learnings surface with `resolveProjectLearningsFile` and parse it with `parseLearningsFile` from `@codyswann/lisa/learnings` (never hardcode the path; a missing file skips this step silently). An entry counts as applied ONLY when its rule was explicitly cited or observably followed in this claim's plan, diff, or review responses — the plan quotes the rule or its id, or the diff does specifically what the rule mandates where the default behavior would have differed. **Presence in context is NOT application**: the ledger is loaded eagerly into every session, so counting "it was loaded" would confirm every entry on every claim and defeat decay entirely. A run that produced no plan or diff has nothing to confirm.
333
+ 2. **Bump each applied entry exactly once** via the surgical writer:
334
+
335
+ ```bash
336
+ node -e 'import("@codyswann/lisa/learnings").then(async m => { const r = await m.confirmLearningEntry(process.cwd(), process.argv[1], new Date().toISOString().slice(0, 10)); console.log(JSON.stringify(r)); }).catch(error => { console.log(JSON.stringify({ status: "error", id: process.argv[1], message: String(error) })); })' <entry-id> || true
337
+ ```
338
+
339
+ The invocation is failure-safe by construction: a rejected import or write resolves to a structured `error` result instead of a crash, and the trailing `|| true` absorbs any remaining non-zero exit (missing `node`, unresolvable package). Record an `error` or non-zero outcome in the cycle summary and continue — the bump must never abort the lifecycle.
340
+
341
+ `confirmLearningEntry` advances ONLY `last_confirmed` — rule text, why, provenance, `first_learned`, and confidence are untouched — and is idempotent within a claim: a repeat same-date bump returns `unchanged`, so an entry that applied repeatedly during one claim is bumped once, not once per application.
342
+ 3. **Never block on it.** A failed bump, an unwritable file, or a `not-found` result (the entry was pruned) is recorded under the cycle summary and the claim proceeds — shipping the issue always outranks confirming a learning about it.
343
+
328
344
  #### 3d. Transition to $DONE (only after the PR is merged)
329
345
 
330
346
  A `done` env state (`status:on-dev`, `status:on-stg`, or the terminal value) asserts that the code has actually reached that environment. Never set it for a PR that is merely open: auto-merge can be blocked indefinitely (a required rebase / `BEHIND` branch, failing checks, an unaddressed review), and the change may never land. Relabeling an issue `status:on-stg` on an open PR makes it *claim* a deploy that never happened. Transition only after confirming the PR merged.
@@ -257,6 +257,22 @@ If the canonical fix is merged but not yet present on the production branch, app
257
257
 
258
258
  This path is distinct from `BLOCKED`: ambiguity, open blockers, and duplicate-of-open findings remain held for human action and must not be auto-closed.
259
259
 
260
+ #### 3c.2 Confirm applied learnings (last_confirmed bump)
261
+
262
+ Run this at the end of 3c, after the lifecycle outcome is recorded and before 3d. It keeps the decay pass safe: a genuinely useful learning that keeps applying stays fresh, while dead weight ages out.
263
+
264
+ 1. **Identify which learnings demonstrably applied.** Resolve the learnings surface with `resolveProjectLearningsFile` and parse it with `parseLearningsFile` from `@codyswann/lisa/learnings` (never hardcode the path; a missing file skips this step silently). An entry counts as applied ONLY when its rule was explicitly cited or observably followed in this claim's plan, diff, or review responses — the plan quotes the rule or its id, or the diff does specifically what the rule mandates where the default behavior would have differed. **Presence in context is NOT application**: the ledger is loaded eagerly into every session, so counting "it was loaded" would confirm every entry on every claim and defeat decay entirely. A run that produced no plan or diff has nothing to confirm.
265
+ 2. **Bump each applied entry exactly once** via the surgical writer:
266
+
267
+ ```bash
268
+ node -e 'import("@codyswann/lisa/learnings").then(async m => { const r = await m.confirmLearningEntry(process.cwd(), process.argv[1], new Date().toISOString().slice(0, 10)); console.log(JSON.stringify(r)); }).catch(error => { console.log(JSON.stringify({ status: "error", id: process.argv[1], message: String(error) })); })' <entry-id> || true
269
+ ```
270
+
271
+ The invocation is failure-safe by construction: a rejected import or write resolves to a structured `error` result instead of a crash, and the trailing `|| true` absorbs any remaining non-zero exit (missing `node`, unresolvable package). Record an `error` or non-zero outcome in the cycle summary and continue — the bump must never abort the lifecycle.
272
+
273
+ `confirmLearningEntry` advances ONLY `last_confirmed` — rule text, why, provenance, `first_learned`, and confidence are untouched — and is idempotent within a claim: a repeat same-date bump returns `unchanged`, so an entry that applied repeatedly during one claim is bumped once, not once per application.
274
+ 3. **Never block on it.** A failed bump, an unwritable file, or a `not-found` result (the entry was pruned) is recorded under the cycle summary and the claim proceeds — shipping the ticket always outranks confirming a learning about it.
275
+
260
276
  #### 3d. Transition to $DONE (only after the PR is merged)
261
277
 
262
278
  A `done` env status (`On Dev`, `On Stg`, or the terminal value) asserts that the code has actually reached that environment. Never set it for a PR that is merely open: auto-merge can be blocked indefinitely (a required rebase / `BEHIND` branch, failing checks, an unaddressed review), and the change may never land. Setting `On Stg` on an open PR makes a ticket *claim* a deploy that never happened. Transition only after confirming the PR merged.
@@ -250,6 +250,22 @@ If the canonical fix is merged but not yet present on the production branch, app
250
250
 
251
251
  This path is distinct from `BLOCKED`: ambiguity, open blockers, and duplicate-of-open findings remain held for human action and must not be auto-closed.
252
252
 
253
+ #### 3c.2 Confirm applied learnings (last_confirmed bump)
254
+
255
+ Run this at the end of 3c, after the lifecycle outcome is recorded and before 3d. It keeps the decay pass safe: a genuinely useful learning that keeps applying stays fresh, while dead weight ages out.
256
+
257
+ 1. **Identify which learnings demonstrably applied.** Resolve the learnings surface with `resolveProjectLearningsFile` and parse it with `parseLearningsFile` from `@codyswann/lisa/learnings` (never hardcode the path; a missing file skips this step silently). An entry counts as applied ONLY when its rule was explicitly cited or observably followed in this claim's plan, diff, or review responses — the plan quotes the rule or its id, or the diff does specifically what the rule mandates where the default behavior would have differed. **Presence in context is NOT application**: the ledger is loaded eagerly into every session, so counting "it was loaded" would confirm every entry on every claim and defeat decay entirely. A run that produced no plan or diff has nothing to confirm.
258
+ 2. **Bump each applied entry exactly once** via the surgical writer:
259
+
260
+ ```bash
261
+ node -e 'import("@codyswann/lisa/learnings").then(async m => { const r = await m.confirmLearningEntry(process.cwd(), process.argv[1], new Date().toISOString().slice(0, 10)); console.log(JSON.stringify(r)); }).catch(error => { console.log(JSON.stringify({ status: "error", id: process.argv[1], message: String(error) })); })' <entry-id> || true
262
+ ```
263
+
264
+ The invocation is failure-safe by construction: a rejected import or write resolves to a structured `error` result instead of a crash, and the trailing `|| true` absorbs any remaining non-zero exit (missing `node`, unresolvable package). Record an `error` or non-zero outcome in the cycle summary and continue — the bump must never abort the lifecycle.
265
+
266
+ `confirmLearningEntry` advances ONLY `last_confirmed` — rule text, why, provenance, `first_learned`, and confidence are untouched — and is idempotent within a claim: a repeat same-date bump returns `unchanged`, so an entry that applied repeatedly during one claim is bumped once, not once per application.
267
+ 3. **Never block on it.** A failed bump, an unwritable file, or a `not-found` result (the entry was pruned) is recorded under the cycle summary and the claim proceeds — shipping the Issue always outranks confirming a learning about it.
268
+
253
269
  #### 3d. Relabel to $DONE (only after the PR is merged)
254
270
 
255
271
  A `done` env state (`status:on-dev`, `status:on-stg`, or the terminal value) asserts that the code has actually reached that environment. Never set it for a PR that is merely open: auto-merge can be blocked indefinitely (a required rebase / `BEHIND` branch, failing checks, an unaddressed review), and the change may never land. Relabeling an Issue `status:on-stg` on an open PR makes it *claim* a deploy that never happened. Transition only after confirming the PR merged.
@@ -1,6 +1,6 @@
1
1
  ---
2
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.
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 automatically filed upstream Lisa ticket with marker dedupe, evidence, and a per-run cap (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
4
  ---
5
5
 
6
6
  # Persist Learning
@@ -62,20 +62,73 @@ The note is one line naming the classification (with its fixed plain-language gl
62
62
  |----------------|-------|
63
63
  | `one-off` | a one-time fluke, not a recurring pattern |
64
64
  | `misunderstanding/spec-gap` | traced to an unclear requirement, not a durable lesson |
65
- | `lisa-upstream` | root cause is Lisa itself; routed upstream |
65
+ | `lisa-upstream` | root cause suspected in Lisa; routed for upstream attribution |
66
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.
67
+ (A `lisa-upstream` classification never produces a drop note — it routes through the `handoff-upstream` flow below, whose step-1 note uses this pre-attribution wording because filing only happens after attribution confirms the Lisa surface.) 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
68
 
69
69
  ### `handoff-upstream` (classification `lisa-upstream`)
70
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):
71
+ This disposition completes the SLL-5 loop (#1583): on a Lisa-attributed failure the upstream Lisa ticket is filed **automatically**. Filing lives here — not in `lisa-attribute-failure` — because that skill is deliberately read-only (doctor delegates to it inside its own read-only contract), while this skill already owns exactly the verdict's side effects and the marker-dedupe discipline. Never persist a local rule for a Lisa-attributed failure; the host project's only durable trace is the brief linking note in step 6.
72
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
- ```
73
+ 1. **Post the handoff marker** on the triggering issue (same one-comment marker dedupe; the marker key is unchanged). The visible line must not claim a filing that has not happened yet — attribution and filing come after this step:
74
+
75
+ ```markdown
76
+ <!-- [lisa-learning-upstream-handoff] key=<fingerprint> -->
77
+ Candidate routed for upstream attribution (root cause suspected in Lisa): <reason>.
78
+ ```
79
+
80
+ 2. **Require a confirmed `lisa` verdict from `lisa-attribute-failure` — always.** Run the `lisa-attribute-failure` skill on the failure event before any filing. The judge's `cited_evidence` seeds the event (implicated files, surface in play, failure class) but never substitutes for the verdict — a path or commit reference alone is not attribution. File **only** when the skill returns a conclusive `lisa` verdict that names the Lisa surface with cited evidence. Any other outcome — `ambiguous`, `project`, or a verdict that cannot name a concrete Lisa surface — files **NOTHING** upstream: the candidate stays local and low-confidence, the run summary says attribution was inconclusive, and the step-1 note is resolved with one corrective follow-up comment on the triggering issue (marker-deduped so re-runs never repeat it; the suffix is distinct from the filing-failure marker in step 8 so one outcome never suppresses the other):
81
+
82
+ ```markdown
83
+ <!-- [lisa-learning-upstream-handoff] key=<fingerprint>-inconclusive -->
84
+ Attribution was inconclusive — nothing was filed upstream and nothing was persisted locally.
85
+ ```
86
+
87
+ 3. **Derive the root-cause key from the LISA SURFACE, never the host project or the local issue.** Two projects hitting the same Lisa bug MUST collide on the same key — that collision is the design (update, not duplicate):
88
+
89
+ ```text
90
+ root-cause-key = <lisa-surface>#<failure-class>
91
+ ```
92
+
93
+ - `<lisa-surface>` — the Lisa-relative path of the surface at fault (e.g. `plugins/src/base/skills/lisa-doctor/SKILL.md`, `typescript/copy-overwrite/.github/workflows/quality.yml`), or the canonical rule/skill/hook name when no single file applies (e.g. `lisa-doctor`, `block-no-verify`).
94
+ - `<failure-class>` — a short lowercase hyphen-slug for the class of failure (e.g. `pagination-truncation`, `stale-artifact-overwrite`).
95
+ - Normalize: lowercase, trim, collapse every whitespace run to a single `-`. The key must contain no host-project name, no local issue number, and no fingerprint — those vary per project and would defeat fleet-wide dedupe.
96
+
97
+ 4. **Enforce the per-run cap.** Resolve `hardening.maxUpstreamFilingsPerRun` from `.lisa.config.json` (default `5` — a conservative bound modeled on `lisa-repair-intake`'s `max_candidates` precedent). Count every upstream create **and** update this run performs; once the cap is reached, drop the remaining candidates and **note each dropped candidate visibly** (in the run summary, naming its root-cause key) — never queue a spam burst and never drop silently. A later run picks the dropped candidates up idempotently.
98
+
99
+ 5. **Evidence redaction (binding).** The upstream repo is PUBLIC by default (`hardening.upstreamRepo` → `CodySwannGT/lisa`) and this filing runs headless on crons — treat every drafted upstream body and comment as world-readable:
77
100
 
78
- Same one-comment marker dedupe on the triggering issue.
101
+ - Quote ONLY Lisa-owned surface text: template/rule/skill/hook excerpts and upstream commit references. The reproduction must be REDACTED — generic placeholders, never the host project's real values.
102
+ - Never paste host environment values, tokens/credentials, connection strings, API keys, PII (names, emails, customer data), or proprietary host code/payloads.
103
+ - The evidence chain names the Lisa surface and the failure class — never project payloads. When host context is essential to understand the failure, LINK the host-project issue instead of quoting it.
104
+ - Before filing or commenting, scan the drafted body for common secret shapes — `key=value` pairs with high-entropy values, token prefixes (`AKIA`, `ghp_`, `xox`), email addresses — and strip on match. When in doubt, leave it out: a thinner upstream ticket is recoverable; a leaked secret is not.
105
+
106
+ 6. **Dedupe by marker, then file or update.** The upstream marker is:
107
+
108
+ ```markdown
109
+ <!-- [lisa-upstream-attribution] key=<root-cause-key> -->
110
+ ```
111
+
112
+ Resolve the upstream repo from `.lisa.config.json` `hardening.upstreamRepo` (default `CodySwannGT/lisa`). Search **all issue states** for an existing issue carrying the marker — a closed marker-bearing ticket still owns this root cause, and searching only open issues would mint a duplicate the moment the original closes. Match on the **MARKER, never the title** — with the same eventual-consistency guard as above (`gh issue list -R <upstream> --state all --search '"<marker>" in:body' --json number,state,url`, and when the search index returns nothing, also `gh issue list -R <upstream> --state all --json number,state,body` and grep the bodies for the marker before concluding no ticket exists).
113
+
114
+ - **No existing ticket** → file via `lisa-github-write-issue` targeting the upstream repo, following the `lisa-rework-triage` "Filing upstream" discipline: a three-audience description (what failed for the operator, what the harness did wrong, what to change), the redacted evidence chain (Lisa-owned text only, per step 5: defect → Lisa surface → attribution evidence → redacted reproduction), the affected project named, and the `self-hardening` label. The body carries **exactly one** dedupe marker; **never write a markerless body** — it permanently breaks all future dedupe.
115
+ - **Existing ticket (open or closed)** → this is a repeat encounter: comment the new occurrence on the existing issue with this project's evidence, marker-deduped per occurrence via `<!-- [lisa-upstream-attribution-occurrence] key=<fingerprint> -->` so re-runs never duplicate the occurrence comment. Never open a second issue, and never match on the title — evidence compounds on one ticket. When the match is **CLOSED**, still comment the occurrence there and reference it in the local trace instead of filing a duplicate; do not reopen it yourself — recurrence evidence on a closed ticket signals the shipped fix may not cover this case, and reopening is the upstream maintainer's call.
116
+
117
+ 7. **Leave the local trace — a note, never a rule.** Post one follow-up comment on the triggering issue linking the upstream ticket:
118
+
119
+ ```markdown
120
+ <!-- [lisa-upstream-filed] key=<fingerprint> -->
121
+ Upstream ticket: <url> (root-cause key `<root-cause-key>`). No local rule persisted — the fix ships fleet-wide through Lisa.
122
+ ```
123
+
124
+ The learnings surface gains **no durable local rule** for a Lisa-attributed failure. Agents avoid the trap via the upstream ticket link until the fix ships.
125
+
126
+ 8. **Degrade gracefully.** If filing fails (auth, rate limit, network), report the failure in the run summary and continue shipping the host issue — a later run retries idempotently. Never block the primary build flow. So the step-1 note is not left dangling, post a marker-deduped corrective follow-up on the triggering issue — with its own suffix, distinct from step 2's `-inconclusive`, so an earlier inconclusive note can never suppress a filing-failure note (or vice versa):
127
+
128
+ ```markdown
129
+ <!-- [lisa-learning-upstream-handoff] key=<fingerprint>-filing-failed -->
130
+ Upstream filing did not complete — nothing was filed upstream and nothing was persisted locally; a later run retries.
131
+ ```
79
132
 
80
133
  ### `persist` (classification `durable-learning`)
81
134
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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.239.0",
3
+ "version": "2.240.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"