@codyswann/lisa 3.69.4 → 3.69.6

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 (71) hide show
  1. package/all/copy-overwrite/scripts/lisa-gates.mjs +200 -15
  2. package/dist/cli/doctor-skip-jobs-migration.d.ts.map +1 -1
  3. package/dist/cli/doctor-skip-jobs-migration.js +6 -0
  4. package/dist/cli/doctor-skip-jobs-migration.js.map +1 -1
  5. package/dist/cli/gate-report-sections.d.ts.map +1 -1
  6. package/dist/cli/gate-report-sections.js +2 -0
  7. package/dist/cli/gate-report-sections.js.map +1 -1
  8. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  9. package/dist/core/lisa-owned-hash-ledger.js +11 -0
  10. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  11. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  12. package/dist/core/upstream-evidence-manifest.js +13 -8
  13. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  14. package/expo/copy-overwrite/scripts/bdd/contract.mjs +41 -2
  15. package/expo/copy-overwrite/scripts/bdd/envelope.mjs +18 -68
  16. package/expo/copy-overwrite/scripts/check-bdd-coverage.mjs +135 -206
  17. package/expo/create-only/bdd/coverage-map.json +1 -7
  18. package/package.json +1 -1
  19. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  21. package/plugins/lisa-agy/plugin.json +1 -1
  22. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  24. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  25. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  31. package/plugins/lisa-expo-agy/plugin.json +1 -1
  32. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  36. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  37. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  41. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  42. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  47. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  52. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  56. package/plugins/lisa-rails-agy/plugin.json +1 -1
  57. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  61. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  62. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  67. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  69. package/scripts/check-learnings-budget.ts +93 -18
  70. package/typescript/create-only/.github/workflows/required-checks-drift.yml +10 -4
  71. package/typescript/github-rulesets/quality-checks.json +4 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.69.4",
3
+ "version": "3.69.6",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.69.4",
3
+ "version": "3.69.6",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.69.4",
3
+ "version": "3.69.6",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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": "3.69.4",
3
+ "version": "3.69.6",
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,5 +1,27 @@
1
- /** CI gate for the canonical project learnings document and its hard budgets. */
2
- import { existsSync } from "node:fs";
1
+ /**
2
+ * CI gate for the canonical project learnings document and its hard budgets.
3
+ *
4
+ * With ONE argument it checks that file and nothing else, which is what the
5
+ * `📚 Learnings Budget` job's consumer path uses.
6
+ *
7
+ * With ZERO arguments it checks TWO files: the shipped template, and this
8
+ * repository's own ledger resolved the way the contract resolves it. It used to
9
+ * check only the template — 0 entries, so it passed unconditionally — which
10
+ * made `bun run check:learnings-budget` look like a gate on the ledger and act
11
+ * like a gate on nothing. The quality job worked around that by passing the
12
+ * resolved path explicitly, and the comment there said so out loud: "passing
13
+ * the real path explicitly is what keeps this a gate and not a silent no-op."
14
+ * A default whose only safe use is not using it is a trap, so the default now
15
+ * covers both surfaces and the workaround is no longer load-bearing (#2932).
16
+ *
17
+ * The two surfaces are genuinely different and both matter: the template is
18
+ * what every adopting project starts from, and the ledger is what this
19
+ * repository's agents actually read. A missing template is a failure — it is
20
+ * committed here and always exists. A missing ledger is a pass, because a
21
+ * project that has never recorded a learning has no file.
22
+ * @module scripts/check-learnings-budget
23
+ */
24
+ import { existsSync, readFileSync } from "node:fs";
3
25
  import * as path from "node:path";
4
26
  import { pathToFileURL } from "node:url";
5
27
  import type * as BudgetCheckModule from "../src/core/learnings-budget-check.js";
@@ -9,15 +31,44 @@ type BudgetChecker = Pick<
9
31
  "checkLearningsBudget" | "formatDiagnosticPath"
10
32
  >;
11
33
 
12
- const DEFAULT_LEARNINGS_FILE = path.resolve(
13
- import.meta.dir,
14
- "..",
34
+ const REPO_ROOT = path.resolve(import.meta.dir, "..");
35
+
36
+ const TEMPLATE_LEARNINGS_FILE = path.resolve(
37
+ REPO_ROOT,
15
38
  "all",
16
39
  "create-only",
17
40
  ".lisa",
18
41
  "PROJECT_LEARNINGS.md"
19
42
  );
20
43
 
44
+ /** Where the contract puts a project's ledger when nothing overrides it. */
45
+ const DEFAULT_LEDGER_RELATIVE = path.join(".lisa", "PROJECT_LEARNINGS.md");
46
+
47
+ /**
48
+ * This repository's own ledger, resolved the way the contract resolves it.
49
+ *
50
+ * Reads `learnings.file` from `.lisa.config.json` and falls back to the
51
+ * documented default. Resolving it here rather than writing the path into a
52
+ * workflow is what stops the two drifting: an override would otherwise move the
53
+ * ledger and leave every caller checking a file that no longer exists.
54
+ * @returns Absolute path to the ledger, whether or not it exists
55
+ */
56
+ function resolveLedger(): string {
57
+ const configPath = path.join(REPO_ROOT, ".lisa.config.json");
58
+ const override = existsSync(configPath)
59
+ ? (
60
+ JSON.parse(readFileSync(configPath, "utf8")) as {
61
+ learnings?: { file?: unknown };
62
+ }
63
+ ).learnings?.file
64
+ : undefined;
65
+ const relative =
66
+ typeof override === "string" && override.trim() !== ""
67
+ ? override
68
+ : DEFAULT_LEDGER_RELATIVE;
69
+ return path.resolve(REPO_ROOT, relative);
70
+ }
71
+
21
72
  /** Run the package-facing checker with zero or one explicit file path. */
22
73
  async function main(): Promise<void> {
23
74
  const arguments_ = process.argv.slice(2);
@@ -25,25 +76,49 @@ async function main(): Promise<void> {
25
76
  fail("Usage: bun run check:learnings-budget -- [PROJECT_LEARNINGS.md]");
26
77
  }
27
78
 
28
- const file =
29
- arguments_.length === 0
30
- ? DEFAULT_LEARNINGS_FILE
31
- : path.resolve(process.cwd(), arguments_[0] as string);
79
+ const checker = await loadBudgetChecker();
80
+ if (arguments_.length === 1) {
81
+ await check(checker, path.resolve(process.cwd(), arguments_[0] as string), {
82
+ absenceIsFailure: true,
83
+ });
84
+ return;
85
+ }
86
+
87
+ await check(checker, TEMPLATE_LEARNINGS_FILE, { absenceIsFailure: true });
88
+ const ledger = resolveLedger();
89
+ if (!existsSync(ledger)) {
90
+ console.log(`${checker.formatDiagnosticPath(ledger)}: no learnings file`);
91
+ return;
92
+ }
93
+ await check(checker, ledger, { absenceIsFailure: true });
94
+ }
32
95
 
33
- const { checkLearningsBudget, formatDiagnosticPath } =
34
- await loadBudgetChecker();
35
- const result = await checkLearningsBudget(file);
96
+ /**
97
+ * Check one file and report, failing the process on any violation.
98
+ * @param checker - The loaded budget checker
99
+ * @param file - Absolute path to the document to check
100
+ * @param options - Whether a missing file is a violation
101
+ * @param options.absenceIsFailure - True when the file must exist
102
+ */
103
+ async function check(
104
+ checker: BudgetChecker,
105
+ file: string,
106
+ options: { absenceIsFailure: boolean }
107
+ ): Promise<void> {
108
+ const result = await checker.checkLearningsBudget(file);
36
109
  if (result.kind === "ok") {
37
110
  console.log(
38
- `${formatDiagnosticPath(file)}: learnings budget passed (${result.entryCount}/${result.maxEntries} entries, ${result.measuredTokens}/${result.maxTokens} maxTokens)`
111
+ `${checker.formatDiagnosticPath(file)}: learnings budget passed (${result.entryCount}/${result.maxEntries} entries, ${result.measuredTokens}/${result.maxTokens} maxTokens)`
39
112
  );
40
113
  return;
41
114
  }
42
- // This package script targets Lisa's own committed learnings template, which
43
- // always exists — so a missing file is as much a failure here as any other
44
- // violation. Host projects use the `lisa check-learnings-budget` CLI
45
- // subcommand instead, which treats a missing file as an expected pass.
46
- fail(`${formatDiagnosticPath(file)}: ${result.detail}`);
115
+ // Both files this script checks are committed here, so a missing one is as
116
+ // much a failure as any other violation. Host projects use the
117
+ // `lisa check-learnings-budget` CLI subcommand instead, which treats a
118
+ // missing file as an expected pass.
119
+ if (options.absenceIsFailure) {
120
+ fail(`${checker.formatDiagnosticPath(file)}: ${result.detail}`);
121
+ }
47
122
  }
48
123
 
49
124
  /**
@@ -67,13 +67,19 @@ jobs:
67
67
  GH_TOKEN: ${{ secrets.RULESET_READ_TOKEN || github.token }}
68
68
  run: |
69
69
  set -euo pipefail
70
+ # An absent input is a FAILURE, never a skip — the same flip #2933
71
+ # made to the `🔒 Skipped Required Checks` job in quality.yml, for the
72
+ # same reason: this workflow is installed by the same `lisa apply`
73
+ # that installs the prover and the seed, so neither can be absent
74
+ # without the install being broken, and a weekly run that reports
75
+ # success having compared nothing is the drift it exists to catch.
70
76
  if [ ! -f scripts/check-skipped-required-checks.mjs ]; then
71
- echo "scripts/check-skipped-required-checks.mjs not present project not yet on this template; skipping."
72
- exit 0
77
+ echo "::error title=Skipped-required-check prover missing::scripts/check-skipped-required-checks.mjs does not exist, so the committed snapshot was compared against nothing. Run \`lisa apply\` to install the Lisa-managed script."
78
+ exit 1
73
79
  fi
74
80
  if [ ! -f .github/required-checks.json ]; then
75
- echo "No .github/required-checks.json nothing to compare. Lisa ships a seed; run \`lisa apply\` to get it."
76
- exit 0
81
+ echo "::error title=Required-checks declaration missing::.github/required-checks.json does not exist, so there is no committed snapshot to compare against the live ruleset. It is create-only and Lisa ships a seed run \`lisa apply\` to get it."
82
+ exit 1
77
83
  fi
78
84
  configured="$(node -e "const d=require('./.github/required-checks.json');process.stdout.write(String(Boolean(d.ruleset&&d.ruleset.repo&&Array.isArray(d.ruleset.ids)&&d.ruleset.ids.length)))")"
79
85
  if [ "$configured" != "true" ]; then
@@ -75,6 +75,10 @@
75
75
  {
76
76
  "context": "🔍 Quality Checks / 🗑️ Dead Code Detection",
77
77
  "integration_id": 15368
78
+ },
79
+ {
80
+ "context": "🔍 Quality Checks / 📚 Learnings Budget",
81
+ "integration_id": 15368
78
82
  }
79
83
  ]
80
84
  }