@codyswann/lisa 4.34.3 → 4.34.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 (85) hide show
  1. package/all/copy-overwrite/scripts/lisa-gates.mjs +40 -1
  2. package/all/copy-overwrite/scripts/lisa-work-item.mjs +14 -1
  3. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  4. package/dist/core/lisa-owned-hash-ledger.js +8 -0
  5. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  6. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  7. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  8. package/dist/core/upstream-evidence-manifest.js +10 -8
  9. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  10. package/package.json +5 -4
  11. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  13. package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +35 -0
  14. package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +35 -0
  15. package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +35 -0
  16. package/plugins/lisa/scripts/intake-blocker-reprobe.mjs +134 -0
  17. package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +35 -0
  18. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +35 -0
  19. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +35 -0
  20. package/plugins/lisa-agy/plugin.json +1 -1
  21. package/plugins/lisa-agy/scripts/intake-blocker-reprobe.mjs +134 -0
  22. package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +35 -0
  23. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +35 -0
  24. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +35 -0
  25. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  28. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-copilot/scripts/intake-blocker-reprobe.mjs +134 -0
  32. package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +35 -0
  33. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +35 -0
  34. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +35 -0
  35. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-cursor/scripts/intake-blocker-reprobe.mjs +134 -0
  37. package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +35 -0
  38. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +35 -0
  39. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +35 -0
  40. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa-expo-agy/plugin.json +1 -1
  43. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  48. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  53. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  58. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  63. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-rails-agy/plugin.json +1 -1
  68. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  72. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  73. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  77. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  78. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  80. package/plugins/src/base/scripts/intake-blocker-reprobe.mjs +134 -0
  81. package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +35 -0
  82. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +35 -0
  83. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +35 -0
  84. package/scripts/two-channel-couplings.json +1 -1
  85. package/typescript/copy-overwrite/.claude/hooks/worktree-create.sh +174 -12
@@ -199,6 +199,41 @@ classifyPreWorkCandidate({ laneType, labels, body, humanNeededLabel, statedBlock
199
199
 
200
200
  ### Phase 3 — Process the first eligible ready Issue
201
201
 
202
+ #### 3.0 Human-hold gate (absolute, and it runs before every other gate)
203
+
204
+ A person parks an item by putting `[lisa-human-gate]` in its description. That marker used to be
205
+ read only for candidates **outside** `$READY` (Phase 2's blocker re-probe), so an item already in
206
+ the ready lane was claimed with the hold never consulted — one was dispatched and fully implemented
207
+ before a human vetoed the merge. The check was correct; it was unreachable from the path that
208
+ matters.
209
+
210
+ Run this **first**, ahead of the repo-scope gate (3a.0) and the leaf-only gate (3a), for every ready
211
+ candidate. Ordering is load-bearing for the same reason it is inside `classifyPreWorkCandidate`: no
212
+ other gate's verdict — however conclusive — may promote an item a person parked.
213
+
214
+ 1. **Classify with `classifyReadyCandidate(...)`** from `scripts/intake-blocker-reprobe.mjs`. It
215
+ shares the gate test with the pre-work classifier deliberately — two copies of a substring test
216
+ drift, and a drifted gate fails *silently*, by quietly ceasing to match. Do **not** re-implement
217
+ the test here, and do **not** key it on `reason=`: markers in the wild carry no `reason=` key at
218
+ all and sit anywhere in the body, so a structured parse would miss them while appearing to work
219
+ on every item that happens to have one.
220
+ 2. **On `claimable: false` with reason `human-gate`, do not claim and do not dispatch.**
221
+ 3. **Reconcile the lane; do not merely skip.** Skipping alone leaves the item in `$READY`, re-judged
222
+ and re-rejected every cycle forever and seen by nothing — `lisa-repair-intake` sweeps items that
223
+ are **not** in the ready role and excludes gated ones outright, so a ready-and-gated item falls
224
+ outside its filter twice over. Call
225
+ `planHumanGateReconciliation({ labels, body, humanNeededLabel, readyLabel, alreadyNotified })`
226
+ and apply exactly the actions it returns: remove `$READY`, add the configured human-needed
227
+ marker, and post `formatHumanGateNote()` once. The planner is idempotent by state, so an item
228
+ already out of the lane and already marked yields no second mutation and no second comment. This
229
+ is the same repair the leaf-only gate already performs for a ready item that must not be
230
+ dispatched.
231
+ 4. **Name it in the cycle summary** via `summarizeHumanGateHolds([...])`, so the record
232
+ distinguishes "nothing was eligible" from "something eligible was held for a person". A lane
233
+ mutation nobody can see afterwards is the same class of problem this gate exists to fix.
234
+ 5. **Continue to the next candidate.** A held item does not end the cycle.
235
+
236
+
202
237
  #### 3a.0 Repo-scope gate (claim only current-repo Issues)
203
238
 
204
239
  A Linear team can oversee multiple repos (`frontend` / `backend` / `infrastructure`). This skill claims only Issues for the repo it is running in. Run this gate **before** the leaf-only gate (3a) and the claim (3b), per the `repo-scope-split` rule's "Claim-time repo scoping" section (cite it by slug; do not restate its decision table).
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "inspected": {
7
7
  "workflows": 22,
8
- "steps": 708,
8
+ "steps": 714,
9
9
  "couplings": 33,
10
10
  "inventory": 69
11
11
  },
@@ -10,17 +10,28 @@
10
10
  # so every git command redirects its output off stdout, and progress goes to
11
11
  # stderr (NOT /dev/tty, which is "not configured" in headless `-p`/CI sessions).
12
12
  #
13
- # It mirrors Claude's default layout — <cwd>/.claude/worktrees/<name> on a fresh
14
- # `worktree-<name>` branch — so behavior is unchanged; the value is clean,
15
- # pre-TUI output and an explicit, durable creation path. The plugin bootstrap
16
- # itself is done by the `post-checkout` hook that `git worktree add` fires, so
17
- # `claude -w` and standalone `cd && claude` sessions converge on the same state.
13
+ # It mirrors Claude's default layout — <cwd>/.claude/worktrees/<name> — but the
14
+ # new branch is cut from the repository's INTEGRATION branch, not from whatever
15
+ # the invoking checkout happened to be standing on. See "Choosing the base".
16
+ # The plugin bootstrap itself is done by the `post-checkout` hook that
17
+ # `git worktree add` fires, so `claude -w` and standalone `cd && claude`
18
+ # sessions converge on the same state.
18
19
  #
19
20
  # Payload (observed, Claude Code stdin):
20
21
  # { "name": "<worktree>", "cwd": "<project root>", "hook_event_name": ... }
22
+ #
23
+ # Agent parity: `WorktreeCreate` is a Claude Code hook event and has no
24
+ # counterpart on Codex, Cursor, OpenCode, Copilot or Antigravity — none of them
25
+ # delegate worktree creation to the host project. There is deliberately no
26
+ # projection of this file for those agents; on them, worktrees are created by
27
+ # whatever invokes git directly, and the base-selection rules below do not
28
+ # apply. This is a genuine surface gap, not a parity omission.
21
29
 
22
30
  _log() { echo "$*" >&2; }
23
31
 
32
+ # The remote whose default branch defines "the integration branch" here.
33
+ _remote="origin"
34
+
24
35
  _payload="$(cat)"
25
36
 
26
37
  if command -v jq >/dev/null 2>&1; then
@@ -43,7 +54,21 @@ case "$_name" in
43
54
  ;;
44
55
  esac
45
56
 
46
- _root="${_cwd:-$(pwd)}"
57
+ # Which REPOSITORY this hook operates on is decided entirely by the payload's
58
+ # `cwd`. This hook cannot validate that choice — it is handed `{ name, cwd }`
59
+ # and nothing that names the intended repository, so it has no way to tell a
60
+ # correct `cwd` from one that drifted. What it can do is refuse to be silent
61
+ # about falling back to ambient process state, which is the one part of the
62
+ # repository choice that happens HERE rather than upstream.
63
+ if [ -n "${_cwd:-}" ]; then
64
+ _root="$_cwd"
65
+ else
66
+ _root="$(pwd)"
67
+ _log "WorktreeCreate: WARNING — payload carried no 'cwd'; falling back to the"
68
+ _log " process working directory: $_root"
69
+ _log " If that is not the repository you meant, this worktree is in the wrong one."
70
+ fi
71
+
47
72
  _base="$_root/.claude/worktrees"
48
73
  _path="$_base/$_name"
49
74
  _wt_branch="worktree-$_name"
@@ -55,23 +80,160 @@ if [ -d "$_path" ]; then
55
80
  exit 0
56
81
  fi
57
82
 
83
+ # ---------------------------------------------------------------------------
84
+ # Choosing the base
85
+ #
86
+ # This used to be a literal `HEAD`, which made the base of every new worktree a
87
+ # property of ambient session state rather than of the work being handed over: a
88
+ # checkout parked on an unmerged feature branch silently handed each new
89
+ # worktree that other work item's commits, and a PR opened from it presented
90
+ # them as its own. Nothing errored, and no identity check catches it — the
91
+ # directory, the branch name and the binding are all exactly what was asked for.
92
+ # Only the base was wrong, and nothing inspects a base.
93
+ #
94
+ # Resolution is OFFLINE ONLY. This hook runs before a session starts and must
95
+ # never stall on the network, so it reads the LOCAL ref store and nothing else.
96
+ # Precedence, most explicit first:
97
+ #
98
+ # 1. $LISA_WORKTREE_BASE — operator override; wrong here is fatal
99
+ # 2. refs/remotes/<remote>/HEAD — the remote's own declared default branch
100
+ # 3. .lisa.config.json — deploy.branches.production, as a hint
101
+ # 4. HEAD — fallback, and never a silent one
102
+ #
103
+ # origin/HEAD outranks the config hint deliberately. `deploy.branches.production`
104
+ # names a DEPLOY target, which in a dev/staging/prod repository is not the branch
105
+ # work integrates onto; origin/HEAD is that repository's own statement of its
106
+ # default branch, which is.
107
+
108
+ # Fully qualified remote default-branch ref, or nothing when it cannot be
109
+ # resolved offline. Mirrors `remoteDefaultRef` in lisa-work-item.mjs: the symref
110
+ # must point INSIDE refs/remotes/<remote>/ and the ref it names must exist, so a
111
+ # crafted symref cannot redirect the base somewhere arbitrary.
112
+ _resolve_remote_head() {
113
+ _sym="$(git -C "$_root" symbolic-ref --quiet "refs/remotes/$_remote/HEAD" 2>/dev/null)" || return 1
114
+ [ -n "$_sym" ] || return 1
115
+ case "$_sym" in
116
+ "refs/remotes/$_remote/"*) ;;
117
+ *) return 1 ;;
118
+ esac
119
+ git -C "$_root" rev-parse -q --verify "$_sym" >/dev/null 2>&1 || return 1
120
+ printf '%s' "$_sym"
121
+ }
122
+
123
+ # Lisa's configured production branch, resolved to a real ref — remote-tracking
124
+ # first, then local. A configured NAME that resolves to no ref is not usable.
125
+ _resolve_config_base() {
126
+ _cfg="$_root/.lisa.config.json"
127
+ [ -f "$_cfg" ] || return 1
128
+ _cfg_branch="$(jq -r '.deploy.branches.production // empty' <"$_cfg" 2>/dev/null)" || return 1
129
+ [ -n "$_cfg_branch" ] || return 1
130
+ for _cand in "refs/remotes/$_remote/$_cfg_branch" "refs/heads/$_cfg_branch"; do
131
+ if git -C "$_root" rev-parse -q --verify "$_cand" >/dev/null 2>&1; then
132
+ printf '%s' "$_cand"
133
+ return 0
134
+ fi
135
+ done
136
+ return 1
137
+ }
138
+
139
+ _base_ref=""
140
+ _base_source=""
141
+
142
+ if [ -n "${LISA_WORKTREE_BASE:-}" ]; then
143
+ # An explicit override that does not resolve is a configuration error the
144
+ # operator can see and fix. Quietly ignoring it would reintroduce this very
145
+ # bug — a base silently different from the one that was asked for.
146
+ if git -C "$_root" rev-parse -q --verify "$LISA_WORKTREE_BASE" >/dev/null 2>&1; then
147
+ _base_ref="$LISA_WORKTREE_BASE"
148
+ _base_source="LISA_WORKTREE_BASE"
149
+ else
150
+ _log "WorktreeCreate: LISA_WORKTREE_BASE='$LISA_WORKTREE_BASE' does not resolve"
151
+ _log " to a ref in $_root; refusing rather than silently using a different base."
152
+ exit 1
153
+ fi
154
+ fi
155
+
156
+ if [ -z "$_base_ref" ]; then
157
+ _base_ref="$(_resolve_remote_head)" && _base_source="$_remote/HEAD"
158
+ fi
159
+
160
+ if [ -z "$_base_ref" ] && command -v jq >/dev/null 2>&1; then
161
+ _base_ref="$(_resolve_config_base)" && _base_source="deploy.branches.production"
162
+ fi
163
+
164
+ if [ -z "$_base_ref" ]; then
165
+ _base_ref="HEAD"
166
+ _base_source="fallback"
167
+ fi
168
+
58
169
  mkdir -p "$_base" 2>/dev/null || true
59
- _log "Creating worktree $_path (branch: $_wt_branch)…"
60
170
 
61
- # Reuse the worktree branch if it already exists, else create it from HEAD.
62
- # All git output is kept off stdout (its "Preparing worktree…" line would
63
- # corrupt the path contract and hang Claude).
171
+ # Announce what was resolved, so a wrong repository or a wrong base is visible
172
+ # AT CREATION instead of being reconstructed hours later from a confused agent.
173
+ # The origin URL can embed a credential (https://user:token@host/...), so strip
174
+ # any userinfo before it reaches a log.
175
+ _origin_url="$(git -C "$_root" remote get-url "$_remote" 2>/dev/null | sed -e 's#://[^/@]*@#://#')"
176
+ _base_sha="$(git -C "$_root" rev-parse --short "$_base_ref" 2>/dev/null)"
177
+
178
+ _log "Creating worktree $_path"
179
+ _log " repository : $_root"
180
+ _log " origin : ${_origin_url:-<none>}"
181
+ _log " branch : $_wt_branch"
182
+ _log " base : $_base_ref @ ${_base_sha:-<unknown>} ($_base_source)"
183
+
184
+ if [ "$_base_source" = "fallback" ]; then
185
+ _log "WorktreeCreate: WARNING — no integration branch could be resolved for this"
186
+ _log " repository, so this worktree is based on whatever branch that checkout was"
187
+ _log " standing on. It may carry another work item's unmerged commits. Check with:"
188
+ _log " git -C $_path log --oneline -5"
189
+ fi
190
+
191
+ # Reuse the worktree branch if it already exists, else create it from the
192
+ # resolved base. All git output is kept off stdout (its "Preparing worktree…"
193
+ # line would corrupt the path contract and hang Claude); stderr is captured so a
194
+ # failure can say WHY instead of just that it happened.
195
+ #
196
+ # --no-track matters: without it, branching from refs/remotes/<remote>/<default>
197
+ # sets the integration branch as this branch's upstream, and a later bare
198
+ # `git push` from the worktree would target it.
64
199
  if git -C "$_root" show-ref --verify --quiet "refs/heads/$_wt_branch" 2>/dev/null; then
65
- git -C "$_root" worktree add "$_path" "$_wt_branch" >/dev/null 2>&1
200
+ _reused=1
201
+ _add_err="$(git -C "$_root" worktree add "$_path" "$_wt_branch" 2>&1 >/dev/null)"
66
202
  else
67
- git -C "$_root" worktree add -b "$_wt_branch" "$_path" HEAD >/dev/null 2>&1
203
+ _reused=0
204
+ _add_err="$(git -C "$_root" worktree add --no-track -b "$_wt_branch" "$_path" "$_base_ref" 2>&1 >/dev/null)"
68
205
  fi
69
206
 
70
207
  if [ ! -d "$_path" ]; then
71
208
  _log "WorktreeCreate: git worktree add failed for $_path."
209
+ [ -n "$_add_err" ] && _log " git: $_add_err"
72
210
  exit 1
73
211
  fi
74
212
 
213
+ if [ "$_reused" = "1" ]; then
214
+ # A reused branch legitimately carries its own prior work, so its commits
215
+ # ahead of the integration branch are expected. Report, do not refuse.
216
+ _log " reused existing branch $_wt_branch"
217
+ elif [ "$_base_source" != "fallback" ]; then
218
+ # Post-condition. A freshly cut branch is AT its base, so this is 0 by
219
+ # construction — which is exactly why it is worth asserting: if base selection
220
+ # ever regresses, this is what catches it, loudly, instead of handing back a
221
+ # plausible-looking tree with someone else's commits in it.
222
+ _ahead="$(git -C "$_path" rev-list --left-right --count "$_base_ref...HEAD" 2>/dev/null | awk '{print $2}')"
223
+ case "${_ahead:-0}" in
224
+ "" | 0) ;;
225
+ *[!0-9]*) ;;
226
+ *)
227
+ _log "WorktreeCreate: REFUSING — the new worktree carries $_ahead commit(s) that"
228
+ _log " are not on $_base_ref. That means it inherited another work item's"
229
+ _log " unmerged work. Removing it rather than handing back a wrong tree."
230
+ git -C "$_root" worktree remove --force "$_path" >/dev/null 2>&1
231
+ git -C "$_root" branch -D "$_wt_branch" >/dev/null 2>&1
232
+ exit 1
233
+ ;;
234
+ esac
235
+ fi
236
+
75
237
  # THE ONLY thing on stdout: the absolute worktree path.
76
238
  printf '%s\n' "$_path"
77
239
  exit 0