@codyswann/lisa 3.23.1 → 3.24.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 (109) hide show
  1. package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +2 -1
  2. package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +174 -0
  3. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  4. package/dist/core/lisa-owned-hash-ledger.js +4 -0
  5. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  6. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  7. package/dist/core/upstream-evidence-manifest.js +26 -68
  8. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  9. package/dist/migrations/ensure-quality-caller-scopes.d.ts.map +1 -1
  10. package/dist/migrations/ensure-quality-caller-scopes.js +1 -2
  11. package/dist/migrations/ensure-quality-caller-scopes.js.map +1 -1
  12. package/package.json +1 -1
  13. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  14. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  15. package/plugins/lisa/.codex-plugin/skills/lisa-doctor/SKILL.md +3 -5
  16. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +3 -3
  17. package/plugins/lisa/.codex-plugin/skills/lisa-sync-down/SKILL.md +9 -16
  18. package/plugins/lisa/hooks/block-managed-file-edits.sh +171 -0
  19. package/plugins/lisa/rules/reference/config-resolution.md +2 -2
  20. package/plugins/lisa/skills/lisa-doctor/SKILL.md +3 -5
  21. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +3 -3
  22. package/plugins/lisa/skills/lisa-sync-down/SKILL.md +10 -17
  23. package/plugins/lisa-agy/plugin.json +1 -1
  24. package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +3 -5
  25. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +3 -3
  26. package/plugins/lisa-agy/skills/lisa-sync-down/SKILL.md +10 -17
  27. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  29. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  30. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +171 -0
  34. package/plugins/lisa-copilot/rules/reference/config-resolution.md +2 -2
  35. package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +3 -5
  36. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +3 -3
  37. package/plugins/lisa-copilot/skills/lisa-sync-down/SKILL.md +10 -17
  38. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +171 -0
  40. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +2 -2
  41. package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +3 -5
  42. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +3 -3
  43. package/plugins/lisa-cursor/skills/lisa-sync-down/SKILL.md +10 -17
  44. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-expo-agy/plugin.json +1 -1
  47. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  52. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  56. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  57. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  61. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  62. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  67. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-rails-agy/plugin.json +1 -1
  72. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  76. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  77. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  81. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  82. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  84. package/plugins/src/base/hooks/block-managed-file-edits.sh +171 -0
  85. package/plugins/src/base/rules/reference/config-resolution.md +2 -2
  86. package/plugins/src/base/skills/lisa-doctor/SKILL.md +3 -5
  87. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +3 -3
  88. package/plugins/src/base/skills/lisa-sync-down/SKILL.md +10 -17
  89. package/rails/deletions.json +6 -1
  90. package/scripts/build-plugins.sh +2 -1
  91. package/scripts/detect-stale-workflow-inputs.mjs +7 -7
  92. package/scripts/generate-upstream-evidence-manifest.mjs +1 -0
  93. package/scripts/lisa-enforcement-fallback.sh +2 -1
  94. package/scripts/verify-health-deterministic-built.mjs +1 -1
  95. package/typescript/deletions.json +14 -1
  96. package/rails/create-only/.github/workflows/claude-code-review-response.yml +0 -31
  97. package/rails/create-only/.github/workflows/claude-nightly-code-complexity.yml +0 -22
  98. package/rails/create-only/.github/workflows/claude-nightly-test-coverage.yml +0 -30
  99. package/rails/create-only/.github/workflows/claude-nightly-test-improvement.yml +0 -33
  100. package/rails/create-only/.github/workflows/claude-sync-down-branches.yml +0 -46
  101. package/scripts/migrate-deploy-order.sh +0 -161
  102. package/typescript/create-only/.github/workflows/claude-ci-auto-fix.yml +0 -42
  103. package/typescript/create-only/.github/workflows/claude-code-review-response.yml +0 -31
  104. package/typescript/create-only/.github/workflows/claude-deploy-auto-fix.yml +0 -39
  105. package/typescript/create-only/.github/workflows/claude-nightly-code-complexity.yml +0 -24
  106. package/typescript/create-only/.github/workflows/claude-nightly-test-coverage.yml +0 -31
  107. package/typescript/create-only/.github/workflows/claude-nightly-test-improvement.yml +0 -34
  108. package/typescript/create-only/.github/workflows/claude-sync-down-branches.yml +0 -46
  109. package/typescript/create-only/.github/workflows/claude.yml +0 -37
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "3.23.1",
3
+ "version": "3.24.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": "3.23.1",
3
+ "version": "3.24.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": "3.23.1",
3
+ "version": "3.24.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": "3.23.1",
3
+ "version": "3.24.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": "3.23.1",
3
+ "version": "3.24.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": "3.23.1",
3
+ "version": "3.24.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": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
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.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.23.1",
3
+ "version": "3.24.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env bash
2
+ # PreToolUse hook: refuse agent writes to files Lisa overwrites on every apply.
3
+ #
4
+ # Lisa ships templates in three modes, and only one of them destroys host edits:
5
+ #
6
+ # copy-overwrite — replaced wholesale on every `lisa apply`. An edit here is
7
+ # gone at the next `bun install`, silently.
8
+ # copy-contents — Lisa APPENDS its lines; host content survives.
9
+ # create-only — skipped when the file exists; the host owns it outright.
10
+ #
11
+ # So this guard covers copy-overwrite and nothing else. Blocking the other two
12
+ # would stop agents editing files they are supposed to own, which is worse than
13
+ # the problem being solved.
14
+ #
15
+ # Measured, not hypothetical. Nothing enforced this, so downstream copies were
16
+ # edited and then silently diverged: `classify-maestro-failures.mjs` reached
17
+ # 36,061 bytes in one fleet repo against 29,586 shipped, and five gate files in
18
+ # another stopped receiving upstream fixes — one over roughly 138 bytes of
19
+ # cosmetic change. The edits were made in good faith; nothing told anyone the
20
+ # file was not theirs.
21
+ #
22
+ # ## Why the path, not a banner comment
23
+ #
24
+ # 103 of Lisa's 145 copy-overwrite files carry a "managed by Lisa" header, and
25
+ # the other 42 CANNOT: 30 are `.json` (no comment syntax), 8 are `.gitkeep` /
26
+ # `.keep` placeholders, and the rest are bare-value files like `.nvmrc`. A guard
27
+ # keyed on the banner would miss every one of them, so this resolves the path
28
+ # against the installed package instead and covers all 145 regardless of format.
29
+ #
30
+ # Blocked signatures:
31
+ # 1. Write / Edit / MultiEdit / NotebookEdit whose target resolves to a
32
+ # copy-overwrite template in the installed Lisa package;
33
+ # 2. Bash output redirection (`>`, `>>`, `>|`), `tee`, or `sed -i` aimed at
34
+ # one. Reads never fire.
35
+ #
36
+ # Exemptions (allowed):
37
+ # - `LISA_ALLOW_MANAGED_FILE_WRITE` set — the operator's explicit override,
38
+ # named in the refusal;
39
+ # - paths under `node_modules/` or `dist/` — vendored copies, not the host's;
40
+ # - the Lisa source repository itself, where these files are the originals and
41
+ # editing them is the entire point.
42
+ set -euo pipefail
43
+
44
+ input="$(cat)"
45
+
46
+ command -v jq >/dev/null 2>&1 || exit 0
47
+
48
+ # The operator's override, checked first so it is always the cheapest way out.
49
+ if [ -n "${LISA_ALLOW_MANAGED_FILE_WRITE:-}" ]; then
50
+ exit 0
51
+ fi
52
+
53
+ tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty')"
54
+ [ -n "$tool_name" ] || exit 0
55
+
56
+ project_root="${CLAUDE_PROJECT_DIR:-$PWD}"
57
+ package_root="$project_root/node_modules/@codyswann/lisa"
58
+
59
+ # No installed Lisa means nothing to classify against. Silent, because a project
60
+ # that has not installed Lisa is not doing anything wrong.
61
+ [ -d "$package_root" ] || exit 0
62
+
63
+ # In Lisa's own repository these files ARE the originals. Blocking here would
64
+ # make the templates uneditable by the only agents that should edit them.
65
+ if [ -f "$project_root/package.json" ] &&
66
+ grep -q '"name": *"@codyswann/lisa"' "$project_root/package.json" 2>/dev/null; then
67
+ exit 0
68
+ fi
69
+
70
+ # Whether a host-relative path is shipped as a copy-overwrite template.
71
+ #
72
+ # A few stat calls rather than an enumeration: the same relative path is probed
73
+ # under each stack's copy-overwrite tree, so cost does not grow with the 145
74
+ # templates.
75
+ managed_source() {
76
+ local candidate="$1"
77
+ case "$candidate" in
78
+ */node_modules/* | node_modules/* | */dist/* | dist/*) return 1 ;;
79
+ esac
80
+ # Normalise to a project-relative path so an absolute target still matches.
81
+ local rel="${candidate#"$project_root"/}"
82
+ rel="${rel#./}"
83
+ [ -n "$rel" ] || return 1
84
+ local stack
85
+ for stack in "$package_root"/*/copy-overwrite; do
86
+ [ -d "$stack" ] || continue
87
+ if [ -e "$stack/$rel" ]; then
88
+ printf '%s' "${stack#"$package_root"/}/$rel"
89
+ return 0
90
+ fi
91
+ done
92
+ return 1
93
+ }
94
+
95
+ refuse() {
96
+ local target="$1"
97
+ local source="$2"
98
+ cat >&2 <<EOF
99
+ BLOCKED: refusing to write \`$target\`.
100
+
101
+ WHY: this file is Lisa-managed. It is shipped as a **copy-overwrite** template
102
+ (\`$source\` in the installed package) and is replaced wholesale on every
103
+ \`lisa apply\` — which runs on every \`bun install\`. Your edit would survive
104
+ until the next install and then vanish, with nothing reporting that it had.
105
+
106
+ That is not hypothetical: downstream copies edited this way diverged silently
107
+ until they stopped receiving upstream fixes entirely.
108
+
109
+ WHERE IT GOES INSTEAD — take the first one that fits:
110
+
111
+ 1. The change should apply everywhere. Edit the template upstream in Lisa and
112
+ release it — \`/lisa:cross-pollinate\`, or an issue on CodySwannGT/lisa.
113
+ That is the only edit that survives an install.
114
+
115
+ 2. Only this project needs to differ. Look for the local escape hatch beside the
116
+ file — Lisa ships \`.local\` variants for the configs that support one
117
+ (\`eslint.config.local.ts\`, \`tsconfig.local.json\`, \`audit.ignore.local.json\`,
118
+ and others). Those are yours and are never overwritten.
119
+
120
+ 3. The behaviour is configurable rather than hardcoded. Check \`.lisa.config.json\`
121
+ — the \`gates\` block in particular decides which checks run and what command
122
+ proves each one, so a project can substitute its own task without touching a
123
+ shipped file.
124
+
125
+ 4. You believe this file should not be Lisa-managed at all. That is a real
126
+ argument and it belongs upstream, not in a local edit that will be erased.
127
+
128
+ If a human explicitly asked for this edit, they can re-run with
129
+ \`LISA_ALLOW_MANAGED_FILE_WRITE=1\` set, which bypasses this guard.
130
+ EOF
131
+ exit 2
132
+ }
133
+
134
+ case "$tool_name" in
135
+ Write | Edit | MultiEdit | NotebookEdit | Update)
136
+ paths="$(printf '%s' "$input" | jq -r '
137
+ [ .tool_input.file_path?,
138
+ .tool_input.path?,
139
+ .tool_input.notebook_path?,
140
+ (.tool_input.edits? // [] | .[].file_path?)
141
+ ] | map(select(. != null and . != "")) | .[]' 2>/dev/null || true)"
142
+ while IFS= read -r candidate; do
143
+ [ -n "$candidate" ] || continue
144
+ if source_path="$(managed_source "$candidate")"; then
145
+ refuse "$candidate" "$source_path"
146
+ fi
147
+ done <<EOF
148
+ $paths
149
+ EOF
150
+ ;;
151
+ Bash)
152
+ command_str="$(printf '%s' "$input" | jq -r '.tool_input.command // empty')"
153
+ [ -n "$command_str" ] || exit 0
154
+ # Write signatures only. A bare mention is a read and stays allowed — the
155
+ # path has to be the target of a redirection, a tee, or an in-place sed.
156
+ # `>>?\|?` covers `>`, `>>`, and the noclobber override `>|`.
157
+ while IFS= read -r token; do
158
+ [ -n "$token" ] || continue
159
+ if source_path="$(managed_source "$token")"; then
160
+ refuse "$token" "$source_path"
161
+ fi
162
+ done <<EOF
163
+ $(printf '%s' "$command_str" |
164
+ grep -Eo ">>?\|?[[:space:]]*['\"]?[^ |;&'\"]+|tee([[:space:]]+-[a-zA-Z]+)*[[:space:]]+['\"]?[^ |;&'\"]+|sed[[:space:]]+[^|;&]*-i[^|;&]*[[:space:]]['\"]?[^ |;&'\"]+" 2>/dev/null |
165
+ sed -E "s/^(>>?\|?|tee([[:space:]]+-[a-zA-Z]+)*|sed[[:space:]]+[^|;&]*-i[^|;&]*)[[:space:]]*//" |
166
+ tr -d "\"'" || true)
167
+ EOF
168
+ ;;
169
+ esac
170
+
171
+ exit 0
@@ -611,8 +611,8 @@ cannot: the relative rank of environments. `deploy.branches` is an unordered map
611
611
  so without `deploy.order` the rank of a custom env name (`preprod`, `qa`, …) is
612
612
  unknowable.
613
613
 
614
- The back-sync GitHub Action (`reusable-claude-sync-down-branches.yml`) consumes
615
- `deploy.order` to derive its source → target chain. It walks the order from the
614
+ The Lisa back-sync flow consumes `deploy.order` to derive its source → target
615
+ chain. It walks the order from the
616
616
  **highest** environment **down**, mapping each env's branch to the next-lower
617
617
  env's branch:
618
618
 
@@ -119,11 +119,9 @@ this order:
119
119
  single distinct branch, so there is nothing to back-sync, the chain is the empty no-op, and
120
120
  `deploy.order` is not required. Do not WARN.
121
121
  - `WARN` when `deploy.branches` resolves to **more than one distinct** branch but `deploy.order`
122
- is absent. Config-driven back-sync (`reusable-claude-sync-down-branches.yml`) cannot derive a
123
- source→target chain without the env ranking; the repo must either add `deploy.order`
124
- (low→high, e.g. `["dev","staging","production"]`) or pass an explicit `chain` in its
125
- `claude-sync-down-branches.yml` wrapper. WARN not FAIL because the explicit-chain override is
126
- a valid configuration.
122
+ is absent. Config-driven back-sync cannot derive a source→target chain without the env
123
+ ranking; the repo must add `deploy.order` (low→high, e.g.
124
+ `["dev","staging","production"]`).
127
125
  - `FAIL` when `deploy.order` is present but its env-name set does not exactly match the keys of
128
126
  `deploy.branches` (every env in one must appear in the other). A mismatch silently breaks the
129
127
  derived chain.
@@ -58,9 +58,9 @@ Use plain `gh` + `git` so Claude and Codex execute identically.
58
58
 
59
59
  ## 0. Take the babysitter lease
60
60
 
61
- This skill is the branch's owner while it runs. Declare that ownership so the
62
- CI auto-fix workflow (`reusable-claude-ci-auto-fix.yml`) stands down instead of
63
- pushing competing fixes to the same branch (the single-writer rule):
61
+ This skill is the branch's owner while it runs. Declare that ownership so any CI
62
+ repair automation stands down instead of pushing competing fixes to the same
63
+ branch (the single-writer rule):
64
64
 
65
65
  ```bash
66
66
  gh label create "lisa:babysitter-on-duty" \
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: lisa-sync-down
3
- description: This skill should be used to run a back-sync of an environment branch DOWN the deploy chain on demand — propagating merges (e.g. hotfixes) from a higher environment to every lower one. Given a source environment name or branch (e.g. `production`), it derives the source→target chain from `.lisa.config.json` `deploy.order` + `deploy.branches` (the same chain the `claude-sync-down-branches.yml` GitHub Action uses on PR merge), then for each downward hop creates a sync branch, merges, resolves conflicts, opens or updates a PR, and enables auto-merge. Runnable by a developer locally or by GitHub Actions.
3
+ description: This skill should be used to run a back-sync of an environment branch DOWN the deploy chain on demand — propagating merges (e.g. hotfixes) from a higher environment to every lower one. Given a source environment name or branch (e.g. `production`), it derives the source→target chain from `.lisa.config.json` `deploy.order` + `deploy.branches`, then for each downward hop creates a sync branch, merges, resolves conflicts, opens or updates a PR, and enables auto-merge. Runnable by a developer locally or by CI.
4
4
  allowed-tools: ["Bash", "Read", "Edit", "Write", "Grep", "Glob"]
5
5
  ---
6
6
 
@@ -8,9 +8,7 @@ allowed-tools: ["Bash", "Read", "Edit", "Write", "Grep", "Glob"]
8
8
 
9
9
  Back-sync a source environment branch DOWN the deploy chain, one hop at a time,
10
10
  all the way to the lowest environment. This is the on-demand, manual-or-CI
11
- counterpart to the `claude-sync-down-branches.yml` GitHub Action, which runs the
12
- same logic automatically when a PR is merged. Both derive their chain from the
13
- same config, so a manual run and an automatic run behave identically.
11
+ entrypoint for applying the config-derived sync chain.
14
12
 
15
13
  Argument (`$ARGUMENTS`): the **source** to start syncing from. Accepts:
16
14
 
@@ -156,16 +154,11 @@ needs human conflict resolution.
156
154
  ## Invocation
157
155
 
158
156
  - **Developer:** `/lisa:sync-down production` (or a branch: `/lisa:sync-down main`).
159
- - **GitHub Actions:** the PR-merge path is already covered by
160
- `claude-sync-down-branches.yml`. For an on-demand CI run, invoke this skill from a
161
- `workflow_dispatch` job via `anthropics/claude-code-action` with the prompt
162
- `/lisa:sync-down <env>` and `CLAUDE_CODE_OAUTH_TOKEN` — the same identity the
163
- Action uses so the resulting PRs trigger downstream CI.
164
-
165
- ## Relationship to the GitHub Action
166
-
167
- This skill and `reusable-claude-sync-down-branches.yml` are deliberately
168
- equivalent: same config-derived chain, same merge/conflict strategy, same
169
- deterministic sync-branch naming, same auto-merge behavior. The Action is the
170
- automatic (PR-merged) trigger; this skill is the manual/dispatch trigger. Keep
171
- their chain-derivation and conflict-resolution rules in sync when either changes.
157
+ - **CI:** invoke this skill from the runtime-native automation surface with
158
+ `/lisa:sync-down <env>` when an automatic sync is required.
159
+
160
+ ## Contract
161
+
162
+ This skill is the source of truth for back-sync behavior: config-derived chain,
163
+ merge/conflict strategy, deterministic sync-branch naming, and auto-merge
164
+ behavior all live here.
@@ -9,6 +9,11 @@
9
9
  ".claude/skills/plan-lower-code-complexity",
10
10
  ".claude/skills/plan-reduce-max-lines",
11
11
  ".claude/skills/plan-reduce-max-lines-per-function",
12
- ".claude/rules/rails-conventions.md"
12
+ ".claude/rules/rails-conventions.md",
13
+ ".github/workflows/claude-code-review-response.yml",
14
+ ".github/workflows/claude-nightly-code-complexity.yml",
15
+ ".github/workflows/claude-nightly-test-coverage.yml",
16
+ ".github/workflows/claude-nightly-test-improvement.yml",
17
+ ".github/workflows/claude-sync-down-branches.yml"
13
18
  ]
14
19
  }
@@ -121,7 +121,8 @@ if [ -d "$SRC_DIR/base/hooks" ]; then
121
121
  mkdir -p "$HOST_GUARD_DIR"
122
122
  fi
123
123
  for guard in block-no-verify parity-safety-net block-shell-json-parsing \
124
- block-instruction-file-edits block-direct-issue-create; do
124
+ block-instruction-file-edits block-direct-issue-create \
125
+ block-managed-file-edits; do
125
126
  if [ -f "$SRC_DIR/base/hooks/$guard.sh" ]; then
126
127
  materialize "$SRC_DIR/base/hooks/$guard.sh" "$HOST_GUARD_DIR/$guard.sh"
127
128
  chmod +x "$HOST_GUARD_DIR/$guard.sh"
@@ -2,14 +2,14 @@
2
2
  /**
3
3
  * Deterministic detector for stale reusable-workflow inputs (issue #1423).
4
4
  *
5
- * Context: a caller workflow (e.g. a consumer repo's `.github/workflows/claude.yml`)
5
+ * Context: a caller workflow (e.g. a consumer repo's `.github/workflows/example-agent.yml`)
6
6
  * invokes one of Lisa's reusable workflows via `uses: <owner>/<repo>/.github/workflows/
7
7
  * reusable-*.yml@<ref>` and passes a `with:` block. GitHub hard-fails the run at
8
8
  * startup if that `with:` block names an input the reusable workflow's
9
9
  * `workflow_call.inputs` no longer declares (e.g. an early-generation caller still
10
- * passing `package_manager`, which `reusable-claude.yml` never declared). Because
11
- * `claude.yml` is a create-only file Lisa never overwrites, an affected repo can
12
- * never self-heal — the drift has to be found and reported (or migrated) instead.
10
+ * passing `package_manager`, which `reusable-example.yml` never declared). Because
11
+ * create-only workflow callers are never overwritten, an affected repo cannot
12
+ * self-heal — the drift has to be found and reported (or migrated) instead.
13
13
  *
14
14
  * This script scans every workflow file directly under a project's
15
15
  * `.github/workflows/` for such callers, resolves each referenced reusable
@@ -17,8 +17,8 @@
17
17
  * `.github/workflows/`, the source of truth for the current contract), and reports
18
18
  * any caller `with:` key absent from the declared set as `stale`.
19
19
  *
20
- * It is intentionally NOT a general YAML parser like `migrate-deploy-order.sh`,
21
- * it does targeted, indentation-aware line scanning of the two shapes it cares
20
+ * It is intentionally NOT a general YAML parser. It does targeted,
21
+ * indentation-aware line scanning of the two shapes it cares
22
22
  * about (`uses:` + sibling `with:`, and `workflow_call: inputs:`). That is
23
23
  * sufficient for Lisa-authored workflow files, which have a stable, predictable
24
24
  * structure, and keeps the detector dependency-free (Node built-ins only, no
@@ -91,7 +91,7 @@ function isDirectory(target) {
91
91
  * @param {string} contractsRoot - directory holding the current reusable
92
92
  * workflow files (basename-addressed).
93
93
  * @param {string} reusableFile - basename of the referenced reusable
94
- * workflow file (e.g. `reusable-claude.yml`).
94
+ * workflow file (e.g. `reusable-example.yml`).
95
95
  * @returns {string[] | null} declared inputs, or `null` if the contract file
96
96
  * isn't present under `contractsRoot` (unresolvable, not necessarily stale).
97
97
  */
@@ -104,6 +104,7 @@ const tracked = execFileSync("git", ["ls-files", "-z"], {
104
104
  const explicitlyIncluded = ["scripts/generate-upstream-evidence-manifest.mjs"];
105
105
  const publicSurfaceMembers = [...new Set([...tracked, ...explicitlyIncluded])]
106
106
  .filter(file => file !== "src/core/upstream-evidence-manifest.ts")
107
+ .filter(file => existsSync(path.join(repoRoot, file)))
107
108
  .filter(file => {
108
109
  const segments = file.split("/");
109
110
  const basename = segments.at(-1) ?? "";
@@ -67,7 +67,8 @@ repo_root="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null)}"
67
67
  # `plugins/` directory to fall back on.
68
68
  status=0
69
69
  for guard in block-no-verify parity-safety-net block-shell-json-parsing \
70
- block-instruction-file-edits block-direct-issue-create; do
70
+ block-instruction-file-edits block-direct-issue-create \
71
+ block-managed-file-edits; do
71
72
  script=""
72
73
  for candidate in \
73
74
  "$repo_root/scripts/lisa-hooks/$guard.sh" \
@@ -278,7 +278,7 @@ try {
278
278
  "offline starter inspection remains an explicit warning"
279
279
  );
280
280
 
281
- const callerPath = path.join(project, ".github", "workflows", "claude.yml");
281
+ const callerPath = path.join(project, ".github", "workflows", "ci.yml");
282
282
  const callerOriginal = await readFile(callerPath, "utf8");
283
283
  await writeFile(
284
284
  callerPath,
@@ -33,7 +33,20 @@
33
33
  ".github/workflows/reusable-claude-nightly-code-complexity.yml",
34
34
  ".github/workflows/reusable-claude-nightly-test-coverage.yml",
35
35
  ".github/workflows/reusable-claude-nightly-test-improvement.yml",
36
- ".github/workflows/load-test.yml"
36
+ ".github/workflows/load-test.yml",
37
+ ".github/workflows/claude-ci-auto-fix.yml",
38
+ ".github/workflows/claude-code-review-response.yml",
39
+ ".github/workflows/claude-deploy-auto-fix.yml",
40
+ ".github/workflows/claude-nightly-code-complexity.yml",
41
+ ".github/workflows/claude-nightly-test-coverage.yml",
42
+ ".github/workflows/claude-nightly-test-improvement.yml",
43
+ ".github/workflows/claude-sync-down-branches.yml",
44
+ ".github/workflows/claude.yml",
45
+ ".github/workflows/reusable-claude-sync-down-branches.yml",
46
+ ".github/workflows/reusable-claude-nightly-code-complexity-rails.yml",
47
+ ".github/workflows/reusable-claude-nightly-jira-triage.yml",
48
+ ".github/workflows/reusable-claude-nightly-test-coverage-rails.yml",
49
+ ".github/workflows/reusable-claude-nightly-test-improvement-rails.yml"
37
50
  ],
38
51
  "keep": []
39
52
  }