@codyswann/lisa 4.28.3 → 4.28.5

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 (126) hide show
  1. package/README.md +3 -1
  2. package/all/copy-overwrite/scripts/lisa-postinstall.mjs +6 -2
  3. package/dist/cli/apply.d.ts +20 -0
  4. package/dist/cli/apply.d.ts.map +1 -1
  5. package/dist/cli/apply.js +70 -3
  6. package/dist/cli/apply.js.map +1 -1
  7. package/dist/cli/doctor-apply-freshness.d.ts.map +1 -1
  8. package/dist/cli/doctor-apply-freshness.js +10 -4
  9. package/dist/cli/doctor-apply-freshness.js.map +1 -1
  10. package/dist/cli/doctor-merge-drivers.d.ts.map +1 -1
  11. package/dist/cli/doctor-merge-drivers.js +4 -2
  12. package/dist/cli/doctor-merge-drivers.js.map +1 -1
  13. package/dist/cli/shared-options.d.ts +11 -4
  14. package/dist/cli/shared-options.d.ts.map +1 -1
  15. package/dist/cli/shared-options.js +8 -8
  16. package/dist/cli/shared-options.js.map +1 -1
  17. package/dist/core/apply-mode.d.ts +80 -29
  18. package/dist/core/apply-mode.d.ts.map +1 -1
  19. package/dist/core/apply-mode.js +23 -2
  20. package/dist/core/apply-mode.js.map +1 -1
  21. package/dist/core/apply-receipt.d.ts +5 -1
  22. package/dist/core/apply-receipt.d.ts.map +1 -1
  23. package/dist/core/apply-receipt.js.map +1 -1
  24. package/dist/core/config.d.ts +24 -12
  25. package/dist/core/config.d.ts.map +1 -1
  26. package/dist/core/config.js.map +1 -1
  27. package/dist/core/learnings.d.ts +1 -1
  28. package/dist/core/learnings.d.ts.map +1 -1
  29. package/dist/core/learnings.js +1 -1
  30. package/dist/core/learnings.js.map +1 -1
  31. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  32. package/dist/core/lisa-owned-hash-ledger.js +4 -0
  33. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  34. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  35. package/dist/core/project-config-learnings.d.ts +49 -0
  36. package/dist/core/project-config-learnings.d.ts.map +1 -0
  37. package/dist/core/project-config-learnings.js +87 -0
  38. package/dist/core/project-config-learnings.js.map +1 -0
  39. package/dist/core/project-config.d.ts +2 -18
  40. package/dist/core/project-config.d.ts.map +1 -1
  41. package/dist/core/project-config.js +4 -57
  42. package/dist/core/project-config.js.map +1 -1
  43. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  44. package/dist/core/upstream-evidence-manifest.js +11 -6
  45. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  46. package/dist/migrations/ensure-learnings-gitattributes.d.ts.map +1 -1
  47. package/dist/migrations/ensure-learnings-gitattributes.js +6 -5
  48. package/dist/migrations/ensure-learnings-gitattributes.js.map +1 -1
  49. package/dist/migrations/ensure-learnings-merge-driver.d.ts.map +1 -1
  50. package/dist/migrations/ensure-learnings-merge-driver.js +6 -3
  51. package/dist/migrations/ensure-learnings-merge-driver.js.map +1 -1
  52. package/dist/migrations/ensure-lisa-postinstall.d.ts +1 -1
  53. package/dist/migrations/ensure-lisa-postinstall.d.ts.map +1 -1
  54. package/dist/migrations/ensure-lisa-postinstall.js +28 -5
  55. package/dist/migrations/ensure-lisa-postinstall.js.map +1 -1
  56. package/dist/utils/postinstall-trampoline-source.js +10 -1
  57. package/dist/utils/postinstall-trampoline-source.js.map +1 -1
  58. package/package.json +3 -3
  59. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  61. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +7 -3
  62. package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/SKILL.md +1 -1
  63. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +7 -3
  64. package/plugins/lisa/skills/lisa-persist-learning/SKILL.md +1 -1
  65. package/plugins/lisa-agy/plugin.json +1 -1
  66. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +7 -3
  67. package/plugins/lisa-agy/skills/lisa-persist-learning/SKILL.md +1 -1
  68. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  71. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +7 -3
  75. package/plugins/lisa-copilot/skills/lisa-persist-learning/SKILL.md +1 -1
  76. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +7 -3
  78. package/plugins/lisa-cursor/skills/lisa-persist-learning/SKILL.md +1 -1
  79. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  81. package/plugins/lisa-expo-agy/plugin.json +1 -1
  82. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  86. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  87. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  91. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  92. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  96. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  97. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  99. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  101. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  102. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  103. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  106. package/plugins/lisa-rails-agy/plugin.json +1 -1
  107. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  110. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  111. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  112. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  113. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  114. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  115. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  116. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  117. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  118. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  119. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +7 -3
  120. package/plugins/src/base/skills/lisa-persist-learning/SKILL.md +1 -1
  121. package/scripts/install-claude-plugins.sh +7 -2
  122. package/scripts/verify-packed-learnings-contract.mjs +101 -2
  123. package/dist/core/learnings-merge-driver-config.d.ts +0 -7
  124. package/dist/core/learnings-merge-driver-config.d.ts.map +0 -1
  125. package/dist/core/learnings-merge-driver-config.js +0 -58
  126. package/dist/core/learnings-merge-driver-config.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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": "4.28.3",
3
+ "version": "4.28.5",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.28.3",
3
+ "version": "4.28.5",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.28.3",
3
+ "version": "4.28.5",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.28.3",
3
+ "version": "4.28.5",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.28.3",
3
+ "version": "4.28.5",
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"
@@ -296,7 +296,8 @@ commit, and push. Only escalate to a human if the conflict needs design input
296
296
  surface the file list and merge state.
297
297
 
298
298
  **The project learnings ledger is a special case.** It is bound to the
299
- `lisa-learnings` union merge driver in `.gitattributes`, which merges concurrent
299
+ `lisa-learnings` union merge driver in `.gitattributes`, which is enabled by default
300
+ and merges concurrent
300
301
  learning branches by entry id. When that driver runs and still fails, it exits
301
302
  non-zero WITHOUT writing conflict markers — so the ledger on disk looks clean
302
303
  but contains **OUR SIDE ONLY**. Staging it as-is (`git add`, `git checkout
@@ -304,8 +305,11 @@ but contains **OUR SIDE ONLY**. Staging it as-is (`git add`, `git checkout
304
305
  learnings. On any conflict touching the ledger: re-union both sides by keeping
305
306
  every distinct entry id, honouring supersessions from either side, and re-run
306
307
  `lisa check-learnings-budget` before committing. If the driver was never
307
- registered you will instead see real conflict markers — run
308
- `lisa install-merge-driver .` and redo the merge rather than hand-editing them.
308
+ registered you will instead see real conflict markers. Only an exact
309
+ `learnings.mergeDriver: false` declines installation, and changing that setting
310
+ does not uninstall an existing registration or remove the committed mapping. Run
311
+ `lisa install-merge-driver .` and redo the merge rather than hand-editing markers
312
+ unless the project explicitly opted out.
309
313
 
310
314
  ### c. Failing CI / deploy checks (`statusCheckRollup` has FAILURE)
311
315
  Inspect the failing check's logs (`gh pr checks <pr>`, `gh run view <run> --log-failed`).
@@ -240,4 +240,4 @@ No learning content is ever committed without a PR — there is no other write p
240
240
  - **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.
241
241
  - **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.
242
242
  - **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.
243
- - **Never resolve a ledger conflict by taking one side.** Concurrent passes each work on their own `learning/<fingerprint>` branch, so two PRs routinely touch the same JSONL block. The `lisa-learnings` union merge driver reconciles them by entry id, but when it cannot it exits non-zero **without writing conflict markers** — leaving a clean-looking file that holds **OUR SIDE ONLY**. `git add`-ing it, or running `git checkout --ours`, silently deletes the other branch's learnings. Re-union both sides instead: keep every distinct entry id, honour supersessions from either side, then re-run `lisa check-learnings-budget`. Literal conflict markers in the ledger mean the driver was not registered — run `lisa install-merge-driver .` and redo the merge.
243
+ - **Never resolve a ledger conflict by taking one side.** Concurrent passes each work on their own `learning/<fingerprint>` branch, so two PRs routinely touch the same JSONL block. The `lisa-learnings` union merge driver reconciles them by entry id, but when it cannot it exits non-zero **without writing conflict markers** — leaving a clean-looking file that holds **OUR SIDE ONLY**. Driver installation is enabled by default; only an exact `learnings.mergeDriver: false` declines it. That opt-out is non-destructive: changing the setting does not uninstall an existing registration or remove the committed mapping. `git add`-ing the failed result, or running `git checkout --ours`, silently deletes the other branch's learnings. Re-union both sides instead: keep every distinct entry id, honour supersessions from either side, then re-run `lisa check-learnings-budget`. Literal conflict markers in the ledger mean the driver was not registered — run `lisa install-merge-driver .` and redo the merge unless the project explicitly opted out.
@@ -144,7 +144,12 @@ fi
144
144
 
145
145
  # Apply Lisa templates non-interactively (init when missing, update when present),
146
146
  # EXCEPT in CI. --skip-git-check bypasses the dirty working directory check since
147
- # package.json and the lockfile are always uncommitted during postinstall.
147
+ # package.json and the lockfile are always uncommitted during postinstall, and
148
+ # LISA_POSTINSTALL=1 declares the install-lifecycle context that selects the
149
+ # reduced postinstall-safe apply. The two used to be one flag; separating them
150
+ # is CodySwannGT/lisa#3066, and this call site keeps BOTH so its behaviour is
151
+ # unchanged. Whether a package-side bootstrap apply should instead run in full
152
+ # is a separate decision, deliberately not taken here.
148
153
  #
149
154
  # CI skip: matches the established Lisa philosophy (see ensure-lisa-postinstall
150
155
  # migration and tests/unit/config/postinstall-ci-guard.test.ts) — in CI the
@@ -176,7 +181,7 @@ HOST_HAS_LISA_POSTINSTALL="$(node -e "
176
181
  if [ "$IS_LISA_SELF" != "true" ] && [ -z "${CI:-}" ]; then
177
182
  if [ "$HOST_HAS_LISA_POSTINSTALL" = "true" ]; then
178
183
  echo "Lisa apply deferred to the host project's own postinstall script."
179
- elif ! LISA_BOOTSTRAP=1 node "$LISA_DIR/dist/index.js" --yes --skip-git-check "$PROJECT_ROOT"; then
184
+ elif ! LISA_BOOTSTRAP=1 LISA_POSTINSTALL=1 node "$LISA_DIR/dist/index.js" --yes --skip-git-check "$PROJECT_ROOT"; then
180
185
  # Loud but non-fatal, for the same reason as the host-side bootstrap: a
181
186
  # postinstall that exits non-zero aborts the whole install. The durable
182
187
  # signal is the apply receipt this failed run did NOT write, which
@@ -2,7 +2,14 @@
2
2
  /** Empirical proof that the packed public learnings API preserves v2 semantics. */
3
3
  import assert from "node:assert/strict";
4
4
  import { execFile } from "node:child_process";
5
- import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
5
+ import {
6
+ mkdir,
7
+ mkdtemp,
8
+ readFile,
9
+ rm,
10
+ symlink,
11
+ writeFile,
12
+ } from "node:fs/promises";
6
13
  import path from "node:path";
7
14
  import { pathToFileURL } from "node:url";
8
15
  import { promisify } from "node:util";
@@ -10,6 +17,8 @@ import { promisify } from "node:util";
10
17
  const executeFile = promisify(execFile);
11
18
  const root = path.resolve(import.meta.dirname, "..");
12
19
  const temporary = await mkdtemp(path.join(root, ".packed-learnings-proof-"));
20
+ const packedConfigFile = ".lisa.config.json";
21
+ const packedLearningsFile = "docs/knowledge/PROJECT_LEARNINGS.md";
13
22
 
14
23
  try {
15
24
  const archive = path.join(temporary, "lisa-packed.tgz");
@@ -29,6 +38,96 @@ try {
29
38
  assert.equal(learnings.MAX_STABLE_TOKEN_BYTES, 128);
30
39
  assert.equal(learnings.LEARNINGS_CONTRACT.maxTokens, 14_900);
31
40
 
41
+ const configProject = path.join(temporary, "config-consumer");
42
+ await mkdir(configProject);
43
+ await writeFile(
44
+ path.join(configProject, packedConfigFile),
45
+ `${JSON.stringify({
46
+ learnings: {
47
+ file: packedLearningsFile,
48
+ mergeDriver: false,
49
+ },
50
+ })}\n`,
51
+ "utf8"
52
+ );
53
+ const packedConfig = await learnings.readProjectConfig(configProject);
54
+ assert.deepEqual(packedConfig.learnings, {
55
+ file: packedLearningsFile,
56
+ mergeDriver: false,
57
+ });
58
+ assert.deepEqual(learnings.resolveLearningsSettings(packedConfig), {
59
+ learningsFile: packedLearningsFile,
60
+ mergeDriverEnabled: false,
61
+ });
62
+ await writeFile(
63
+ path.join(configProject, packedConfigFile),
64
+ '{"learnings":{"mergeDriver":"false"}}\n',
65
+ "utf8"
66
+ );
67
+ await assert.rejects(
68
+ learnings.readProjectConfig(configProject),
69
+ /learnings\.mergeDriver.*expected boolean.*received "false"/iu
70
+ );
71
+
72
+ const typeConsumer = path.join(temporary, "type-consumer");
73
+ await mkdir(path.join(typeConsumer, "node_modules", "@codyswann"), {
74
+ recursive: true,
75
+ });
76
+ await symlink(
77
+ packedRoot,
78
+ path.join(typeConsumer, "node_modules", "@codyswann", "lisa"),
79
+ "dir"
80
+ );
81
+ await writeFile(
82
+ path.join(typeConsumer, "tsconfig.json"),
83
+ `${JSON.stringify(
84
+ {
85
+ compilerOptions: {
86
+ module: "NodeNext",
87
+ moduleResolution: "NodeNext",
88
+ noEmit: true,
89
+ skipLibCheck: true,
90
+ strict: true,
91
+ target: "ES2022",
92
+ },
93
+ include: ["consumer.ts"],
94
+ },
95
+ null,
96
+ 2
97
+ )}\n`,
98
+ "utf8"
99
+ );
100
+ await writeFile(
101
+ path.join(typeConsumer, "consumer.ts"),
102
+ `import { resolveLearningsSettings, type LearningsConfig, type ProjectConfig } from "@codyswann/lisa/learnings";
103
+
104
+ const absent: LearningsConfig = {};
105
+ const explicitTrue: LearningsConfig = { mergeDriver: true };
106
+ const explicitFalse: LearningsConfig = { mergeDriver: false };
107
+ const withFile: LearningsConfig = { file: "docs/LEARNINGS.md", mergeDriver: false };
108
+ const configs: ProjectConfig[] = [
109
+ { learnings: absent },
110
+ { learnings: explicitTrue },
111
+ { learnings: explicitFalse },
112
+ { learnings: withFile },
113
+ ];
114
+ for (const config of configs) resolveLearningsSettings(config);
115
+ // @ts-expect-error mergeDriver is a boolean opt-out, never a string
116
+ const invalid: LearningsConfig = { mergeDriver: "false" };
117
+ void invalid;
118
+ `,
119
+ "utf8"
120
+ );
121
+ await executeFile(
122
+ process.execPath,
123
+ [
124
+ path.join(root, "node_modules", "typescript", "bin", "tsc"),
125
+ "-p",
126
+ typeConsumer,
127
+ ],
128
+ { cwd: typeConsumer }
129
+ );
130
+
32
131
  const packedVictim = "packed-victim";
33
132
  const packedProvenance = "issue:#2015";
34
133
  const packedDate = "2026-08-26";
@@ -261,7 +360,7 @@ try {
261
360
  assert.equal(await readFile(overflow.file, "utf8"), beforeOverflowCollision);
262
361
 
263
362
  console.log(
264
- "[EVIDENCE: packed-learnings-contract] v1=migratable legacy=bounded entries=hardened stamps=hardened race=9 stale=8 chain=stable collision=atomic overflow=fork merge=2 projection=bounded"
363
+ "[EVIDENCE: packed-learnings-contract] config=validated merge-driver=opt-out types=closed v1=migratable legacy=bounded entries=hardened stamps=hardened race=9 stale=8 chain=stable collision=atomic overflow=fork merge=2 projection=bounded"
265
364
  );
266
365
  } finally {
267
366
  await rm(temporary, { recursive: true, force: true });
@@ -1,7 +0,0 @@
1
- /**
2
- * Whether this project accepts the merge-driver registration.
3
- * @param projectRoot - Host project directory
4
- * @returns False only when `learnings.mergeDriver` is explicitly `false`
5
- */
6
- export declare function isLearningsMergeDriverEnabled(projectRoot: string): Promise<boolean>;
7
- //# sourceMappingURL=learnings-merge-driver-config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"learnings-merge-driver-config.d.ts","sourceRoot":"","sources":["../../src/core/learnings-merge-driver-config.ts"],"names":[],"mappings":"AA6CA;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CAQlB"}
@@ -1,58 +0,0 @@
1
- /**
2
- * Host opt-out for the project-learnings union merge driver.
3
- *
4
- * `lisa apply` registers the driver automatically, and the TypeScript stack
5
- * wires `lisa apply` into `postinstall` — so an ordinary `npm install` writes
6
- * `merge.lisa-learnings.driver` into `.git/config` with no separate human step.
7
- * That is not an escalation over `npm install` itself, which already runs
8
- * arbitrary code, but it does persist an executable hook that fires on ordinary
9
- * git operations for as long as the clone exists, outside npm's lifecycle. A
10
- * host that would rather not carry that hook needs a way to say so.
11
- *
12
- * This reads `.lisa.config.json` directly rather than extending the validated
13
- * `ProjectConfig` schema: `project-config.ts` sits exactly at its 300-line lint
14
- * cap, and forcing an extraction of its cross-cutting learnings validators to
15
- * make room would be a far larger change than this flag warrants. Keeping the
16
- * flag beside the feature it governs also means the driver's opt-out is
17
- * readable in one place.
18
- *
19
- * Opt-out only, and strictly `=== false`: absent, malformed, or non-boolean
20
- * values all leave the driver enabled, matching the zero-configuration default
21
- * every other Lisa guardrail uses. A malformed config must not silently disable
22
- * a data-integrity guard.
23
- * @module core/learnings-merge-driver-config
24
- */
25
- import { readFile } from "node:fs/promises";
26
- import * as path from "node:path";
27
- const CONFIG_FILE = ".lisa.config.json";
28
- /**
29
- * Read one inert own property without invoking an accessor.
30
- * @param candidate - Value to inspect
31
- * @param key - Property name to read
32
- * @returns The own data value, when present
33
- */
34
- function readOwn(candidate, key) {
35
- if (candidate === null || typeof candidate !== "object") {
36
- return undefined;
37
- }
38
- const descriptor = Object.getOwnPropertyDescriptor(candidate, key);
39
- return descriptor !== undefined && "value" in descriptor
40
- ? descriptor.value
41
- : undefined;
42
- }
43
- /**
44
- * Whether this project accepts the merge-driver registration.
45
- * @param projectRoot - Host project directory
46
- * @returns False only when `learnings.mergeDriver` is explicitly `false`
47
- */
48
- export async function isLearningsMergeDriverEnabled(projectRoot) {
49
- try {
50
- const raw = await readFile(path.join(projectRoot, CONFIG_FILE), "utf8");
51
- const learnings = readOwn(JSON.parse(raw), "learnings");
52
- return readOwn(learnings, "mergeDriver") !== false;
53
- }
54
- catch {
55
- return true;
56
- }
57
- }
58
- //# sourceMappingURL=learnings-merge-driver-config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"learnings-merge-driver-config.js","sourceRoot":"","sources":["../../src/core/learnings-merge-driver-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,MAAM,WAAW,GAAG,mBAAmB,CAAC;AAExC;;;;;GAKG;AACH,SAAS,OAAO,CAAC,SAAkB,EAAE,GAAW;IAC9C,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QACxD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,CAAC,wBAAwB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IACnE,OAAO,UAAU,KAAK,SAAS,IAAI,OAAO,IAAI,UAAU;QACtD,CAAC,CAAC,UAAU,CAAC,KAAK;QAClB,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,WAAmB;IAEnB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC;QACxE,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,EAAE,WAAW,CAAC,CAAC;QACnE,OAAO,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,KAAK,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}