@codyswann/lisa 2.176.7 → 2.176.9
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.
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/rules/eager/repo-scope-split.md +5 -1
- package/plugins/lisa/rules/reference/repo-scope-split.md +15 -1
- package/plugins/lisa/skills/github-build-intake/SKILL.md +25 -3
- package/plugins/lisa/skills/jira-build-intake/SKILL.md +52 -8
- package/plugins/lisa/skills/linear-build-intake/SKILL.md +27 -3
- package/plugins/lisa/skills/repair-intake/SKILL.md +5 -2
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/github-build-intake/SKILL.md +25 -3
- package/plugins/lisa-agy/skills/jira-build-intake/SKILL.md +52 -8
- package/plugins/lisa-agy/skills/linear-build-intake/SKILL.md +27 -3
- package/plugins/lisa-agy/skills/repair-intake/SKILL.md +5 -2
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/rules/eager/repo-scope-split.md +5 -1
- package/plugins/lisa-copilot/rules/reference/repo-scope-split.md +15 -1
- package/plugins/lisa-copilot/skills/github-build-intake/SKILL.md +25 -3
- package/plugins/lisa-copilot/skills/jira-build-intake/SKILL.md +52 -8
- package/plugins/lisa-copilot/skills/linear-build-intake/SKILL.md +27 -3
- package/plugins/lisa-copilot/skills/repair-intake/SKILL.md +5 -2
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/rules/repo-scope-split-reference.mdc +15 -1
- package/plugins/lisa-cursor/rules/repo-scope-split.mdc +5 -1
- package/plugins/lisa-cursor/skills/github-build-intake/SKILL.md +25 -3
- package/plugins/lisa-cursor/skills/jira-build-intake/SKILL.md +52 -8
- package/plugins/lisa-cursor/skills/linear-build-intake/SKILL.md +27 -3
- package/plugins/lisa-cursor/skills/repair-intake/SKILL.md +5 -2
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/rules/eager/repo-scope-split.md +5 -1
- package/plugins/src/base/rules/reference/repo-scope-split.md +15 -1
- package/plugins/src/base/skills/github-build-intake/SKILL.md +25 -3
- package/plugins/src/base/skills/jira-build-intake/SKILL.md +52 -8
- package/plugins/src/base/skills/linear-build-intake/SKILL.md +27 -3
- package/plugins/src/base/skills/repair-intake/SKILL.md +5 -2
|
@@ -45,7 +45,21 @@ Resolve the current repo per the `config-resolution` "Repo scoping" section (con
|
|
|
45
45
|
2. **Multi-repo leaf → split, never claim.** If determination finds the leaf touches more than one repo, run the **work-time split procedure** below to break it into single-repo siblings — each created **build-ready** (`build_ready: true`, so the build queue auto-claims it) and stamped with its own `repo:<name>`. After the split, the current repo's sibling (if any) becomes a normal current-repo candidate; the others are separate single-repo `ready` leaves for their repos. A multi-repo leaf is never claimed as-is.
|
|
46
46
|
3. **Wrong repo → skip.** A single-repo leaf whose `repo:<name>` ≠ the current repo is left `ready` (and labeled) and skipped; intake moves on until it finds a claimable current-repo leaf, then stops (one item per cycle).
|
|
47
47
|
|
|
48
|
-
**
|
|
48
|
+
**Query-time pre-filter (the cheapest arm — apply before the per-candidate walk).** When the queue is queryable, scope the candidate **query itself** to the current repo so sibling-repo tickets never even enter the set — instead of pulling the whole project's ready tickets and skipping the wrong ones one-by-one (a full wasted scan when none belong to the current repo, e.g. a JIRA project shared across `frontend`/`backend`/`infrastructure`). On JIRA, append to the JQL:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
AND (labels = "repo:<current>" OR labels IS EMPTY)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This pre-applies **only** the unambiguous *"labeled for another repo → skip"* arm at query time, while `labels IS EMPTY` keeps **unlabeled** tickets in the set so the determine + stamp arm still works. It does not change any outcome — it just moves the cheap skips off the per-candidate walk. The JIRA **component** alias and any rarer residual cases are not expressed in the pre-filter and remain with the authoritative per-candidate gate above. Skip the pre-filter (fall back to the broad scan) when the current repo can't be resolved, or when the caller's query already constrains repo (a `repo:` label term or a `component =` term) — never fail the cycle just because the pre-filter couldn't be built.
|
|
55
|
+
|
|
56
|
+
Apply it **only where the query layer can express `OR labels IS EMPTY` without dropping unlabeled candidates** — otherwise the pre-filter would hide the very tickets the determine + stamp arm exists to handle:
|
|
57
|
+
|
|
58
|
+
- **JIRA (multi-repo project):** JQL expresses it directly — `lisa:jira-build-intake` applies it in Phase 1. This is the case the optimization is for.
|
|
59
|
+
- **GitHub:** issues are inherently single-repo, so the scanner is already repo-scoped — no pre-filter needed.
|
|
60
|
+
- **Linear (multi-repo team):** the `list_issues` label filter is an AND-of-labels and cannot express "current-repo **or** unlabeled" in one query, so `lisa:linear-build-intake` keeps the broad `$READY` query and relies on the per-candidate 3a.0 gate — a narrowing label filter there would strand unlabeled Issues.
|
|
61
|
+
|
|
62
|
+
**Cost.** Only **unlabeled** candidates need content determination; once stamped, wrong-repo candidates are skipped by label alone — and with the query-time pre-filter, sibling-repo tickets are not even fetched. Prefer candidates already labeled `repo:<current>` first (cheap claim), falling through to unlabeled candidates (determine + stamp) only when no pre-labeled current-repo leaf is ready.
|
|
49
63
|
|
|
50
64
|
A container (an Epic, or any item with open child work) is handled by the leaf-only gate, not here — containers may span repos, may keep multiple `repo:<name>` labels for visibility, and are never claimed/built directly. Only a leaf work unit — including a now-childless Story/Spike that the leaf-only gate treats as a leaf — is split or skipped by repo scope.
|
|
51
65
|
|
|
@@ -256,9 +256,31 @@ This is the idempotency lock — a re-entrant cycle's `--label $READY` filter wi
|
|
|
256
256
|
|
|
257
257
|
If the relabel fails (permission, race), log under "Errors" in the cycle summary and skip this issue. **Do not invoke the build flow on an issue you didn't successfully claim.**
|
|
258
258
|
|
|
259
|
-
#### 3c.
|
|
259
|
+
#### 3c. Return or run the build delegation
|
|
260
|
+
|
|
261
|
+
After the claim succeeds, the per-issue build must run under `lisa:github-agent` (the per-issue lifecycle agent), but a teammate running this skill must not spawn that named peer itself. Claude teams are flat: only the lead can add a named teammate. Therefore:
|
|
262
|
+
|
|
263
|
+
- If you are the team lead/root agent, spawn or invoke `lisa:github-agent` with the issue ref and wait for its structured result.
|
|
264
|
+
- If you are a teammate, stop this skill's direct work and return a structured `delegation-request` to the lead instead of calling `Agent` with `name` or otherwise spawning `github-agent` as a named peer.
|
|
265
|
+
- A private anonymous helper is allowed only when the helper is not a roster peer and the `Agent` call omits `name`; it must not replace this `github-agent` delegation.
|
|
266
|
+
|
|
267
|
+
Return this payload shape to the lead:
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
{
|
|
271
|
+
"type": "delegation-request",
|
|
272
|
+
"agent": "github-agent",
|
|
273
|
+
"workItem": "<org>/<repo>#<number>",
|
|
274
|
+
"context": {
|
|
275
|
+
"claimedLabel": "$CLAIMED",
|
|
276
|
+
"doneResolution": "Resolve $DONE from the PR base branch per this skill's Workflow resolution section"
|
|
277
|
+
},
|
|
278
|
+
"onSuccess": "Confirm the returned PR is merged, then apply Phase 3d and Phase 3d.1",
|
|
279
|
+
"onBlockedOrError": "Leave the issue where github-agent left it and record the surfaced outcome"
|
|
280
|
+
}
|
|
281
|
+
```
|
|
260
282
|
|
|
261
|
-
|
|
283
|
+
`lisa:github-agent` owns:
|
|
262
284
|
- Reading the full issue graph (`lisa:github-read-issue`)
|
|
263
285
|
- Running its own pre-flight quality gate (`lisa:github-verify`)
|
|
264
286
|
- Running issue triage (`lisa:ticket-triage`)
|
|
@@ -266,7 +288,7 @@ Invoke `lisa:github-agent` (the per-issue lifecycle agent) with the issue ref. `
|
|
|
266
288
|
- Posting progress comments via `lisa:github-sync`
|
|
267
289
|
- Posting evidence via `lisa:github-evidence`
|
|
268
290
|
|
|
269
|
-
|
|
291
|
+
The lead waits for `lisa:github-agent` to return, then resumes this scanner with the returned outcome:
|
|
270
292
|
|
|
271
293
|
- **Success** — the build flow completed and a PR exists; evidence posted. The PR may already be **merged** or still **open** (auto-merge enabled, awaiting checks/merge). "Success" means the build work is sound — it does **not** assert the change reached an environment. The env transition in 3d gates on the PR actually being merged; an open PR does not advance the issue to a `done` env status.
|
|
272
294
|
- **Blocked by github-verify pre-flight gate** — `lisa:github-agent` itself relabels the issue to `status:blocked` (or removes `$CLAIMED` and reassigns to the original author). This is correct and expected — let it stand. Record and move on.
|
|
@@ -11,7 +11,7 @@ All Atlassian operations in this skill go through `lisa:atlassian-access`. Do no
|
|
|
11
11
|
`$ARGUMENTS` is one of:
|
|
12
12
|
|
|
13
13
|
1. A JIRA project key (e.g. `SE`) — scans that project for tickets in the configured `ready` status.
|
|
14
|
-
2. A full JQL filter (e.g. `project = SE AND component = "frontend" AND Status = Ready`) — used as-is. The skill will not append a `Status = <ready>` clause if the JQL already names a status, so callers can intentionally widen.
|
|
14
|
+
2. A full JQL filter (e.g. `project = SE AND component = "frontend" AND Status = Ready`) — used as-is. The skill will not append a `Status = <ready>` clause if the JQL already names a status, so callers can intentionally widen. It also auto-scopes the query to the current repo (Phase 1 step 2) unless the JQL already constrains repo (a `repo:` label or `component =` term), so a multi-repo project / forwarded assignee filter is not widened to sibling repos by accident.
|
|
15
15
|
|
|
16
16
|
Run one build-intake cycle. The first eligible ready ticket is claimed, built via the `lisa:jira-agent` flow, transitioned to the configured `done` status (env-aware — see below), then the cycle exits. Remaining ready tickets stay queued for later scheduler invocations.
|
|
17
17
|
|
|
@@ -105,10 +105,30 @@ This skill ONLY transitions `$READY → $CLAIMED` on claim, and `$CLAIMED → $D
|
|
|
105
105
|
|
|
106
106
|
### Phase 1 — Resolve the query
|
|
107
107
|
|
|
108
|
-
1. Parse `$ARGUMENTS
|
|
109
|
-
- Project key:
|
|
108
|
+
1. Parse `$ARGUMENTS` into a base JQL:
|
|
109
|
+
- Project key: `project = <KEY> AND Status = "$READY"`.
|
|
110
110
|
- Full JQL: use as-is. If it does not include a `Status` clause, append `AND Status = "$READY"`.
|
|
111
|
-
2.
|
|
111
|
+
2. **Repo-scope pre-filter (query-time arm of `repo-scope-split`).** A JIRA project can oversee multiple repos (`frontend` / `backend` / `infrastructure`), so an unscoped `project = <KEY> AND Status = $READY` — or an `assignee = … AND Status = $READY` filter forwarded by `lisa:intake` — pulls in **every** sibling repo's ready tickets and forces the claim-time gate (3a.0) to skip them one-by-one: a full wasted scan when none belong to the current repo. Narrow the candidate set at query time so other-repo tickets never enter it:
|
|
112
|
+
1. **Resolve the current repo** per `config-resolution` "Repo scoping" (`.repo` → `.github.repo` → `git remote get-url origin` basename) — the same resolution 3a.0 uses.
|
|
113
|
+
2. **If the base JQL already constrains repo** — it contains a `repo:` label term or a `component = "<repo>"` term — the caller has already scoped (or intentionally widened) it; leave it untouched.
|
|
114
|
+
3. **Else, if the current repo resolved**, append (before the `ORDER BY`):
|
|
115
|
+
```text
|
|
116
|
+
AND (labels = "repo:<current>" OR labels IS EMPTY)
|
|
117
|
+
```
|
|
118
|
+
This drops tickets explicitly stamped for a **sibling** repo up front, while still surfacing **unlabeled** tickets (`labels IS EMPTY`) so the claim-time gate can determine + stamp them — i.e. it pre-applies only the unambiguous "wrong-repo → skip" arm of 3a.0 and never hides work the stamping path must see. The JIRA **component** alias and any rarer residual cases stay with the authoritative claim-time gate in 3a.0.
|
|
119
|
+
4. **If the current repo cannot be resolved**, skip this augmentation and fall back to the broad scan (3a.0 still enforces scoping). Do not fail the cycle solely because the pre-filter could not be built.
|
|
120
|
+
3. Append the ordering: `ORDER BY priority DESC, created ASC`.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# CURRENT_REPO resolved per config-resolution "Repo scoping" (see 3a.0).
|
|
124
|
+
# Append a repo pre-filter only when the JQL does not already constrain repo.
|
|
125
|
+
if [ -n "$CURRENT_REPO" ] && ! printf '%s' "$BASE_JQL" | grep -qiE 'repo:|component[[:space:]]*='; then
|
|
126
|
+
BASE_JQL="${BASE_JQL} AND (labels = \"repo:${CURRENT_REPO}\" OR labels IS EMPTY)"
|
|
127
|
+
fi
|
|
128
|
+
JQL="${BASE_JQL} ORDER BY priority DESC, created ASC"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
4. Confirm the configured Atlassian site by invoking `lisa:atlassian-access` `operation: list-sites` (it enforces connection match against `.lisa.config.json`).
|
|
112
132
|
|
|
113
133
|
### Phase 2 — Find ready tickets
|
|
114
134
|
|
|
@@ -123,7 +143,7 @@ If empty, report `"No tickets with Status=$READY. Nothing to do."` and exit. Thi
|
|
|
123
143
|
A JIRA project can oversee multiple repos (`frontend` / `backend` / `infrastructure`). This skill claims only tickets 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).
|
|
124
144
|
|
|
125
145
|
1. **Resolve the current repo** per `config-resolution` "Repo scoping" (`.repo` → `.github.repo` → `git remote get-url origin` basename). If unresolvable, stop and report — do not claim tickets you cannot scope.
|
|
126
|
-
2. **Cheap path first.** Prefer candidates already carrying `repo:<current>` — a JIRA **label**, or a **component** equal to the repo name (accepted as an alias)
|
|
146
|
+
2. **Cheap path first.** Phase 1's query-time pre-filter has already dropped tickets explicitly stamped for a sibling repo, so the Phase 2 result set is current-repo-labeled + unlabeled tickets. Prefer candidates already carrying `repo:<current>` — a JIRA **label**, or a **component** equal to the repo name (accepted as an alias); the pre-filter is label-only, so a ticket scoped solely by a sibling-repo **component** can still appear and is skipped here. The result set still includes unlabeled tickets so they can be determined and stamped; this gate orders/filters what remains.
|
|
127
147
|
3. **Per candidate, apply the repo-scope decision (`repo-scope-split`):**
|
|
128
148
|
- Carries `repo:<other>` (label or component) → **skip** (leave it `ready` for that repo's own intake); next candidate.
|
|
129
149
|
- **Unlabeled** → determine the target repo(s) from the ticket (description, AC, technical approach) confirmed against the code surfaces, then **stamp** `repo:<name>` via `lisa:atlassian-access` `operation: write-ticket` (add the label / set the component) so later cycles filter cheaply; re-apply with the now-known repo.
|
|
@@ -183,9 +203,31 @@ Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa:atlassian-ac
|
|
|
183
203
|
|
|
184
204
|
If the transition fails (permission, missing transition, race), log under "Errors" in the cycle summary and skip this ticket. **Do not invoke the build flow on a ticket you didn't successfully claim.**
|
|
185
205
|
|
|
186
|
-
#### 3c.
|
|
206
|
+
#### 3c. Return or run the build delegation
|
|
207
|
+
|
|
208
|
+
After the claim succeeds, the per-ticket build must run under `lisa:jira-agent` (the per-ticket lifecycle agent), but a teammate running this skill must not spawn that named peer itself. Claude teams are flat: only the lead can add a named teammate. Therefore:
|
|
209
|
+
|
|
210
|
+
- If you are the team lead/root agent, spawn or invoke `lisa:jira-agent` with the ticket key and wait for its structured result.
|
|
211
|
+
- If you are a teammate, stop this skill's direct work and return a structured `delegation-request` to the lead instead of calling `Agent` with `name` or otherwise spawning `jira-agent` as a named peer.
|
|
212
|
+
- A private anonymous helper is allowed only when the helper is not a roster peer and the `Agent` call omits `name`; it must not replace this `jira-agent` delegation.
|
|
213
|
+
|
|
214
|
+
Return this payload shape to the lead:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"type": "delegation-request",
|
|
219
|
+
"agent": "jira-agent",
|
|
220
|
+
"workItem": "<TICKET>",
|
|
221
|
+
"context": {
|
|
222
|
+
"claimedStatus": "$CLAIMED",
|
|
223
|
+
"doneResolution": "Resolve $DONE from the PR base branch per this skill's Workflow resolution section"
|
|
224
|
+
},
|
|
225
|
+
"onSuccess": "Confirm the returned PR is merged, then apply Phase 3d and Phase 3d.1",
|
|
226
|
+
"onBlockedOrError": "Leave the ticket where jira-agent left it and record the surfaced outcome"
|
|
227
|
+
}
|
|
228
|
+
```
|
|
187
229
|
|
|
188
|
-
|
|
230
|
+
`lisa:jira-agent` owns:
|
|
189
231
|
- Reading the full ticket graph (`lisa:jira-read-ticket`)
|
|
190
232
|
- Running its own pre-flight quality gate (`lisa:jira-verify`)
|
|
191
233
|
- Running ticket triage (`lisa:ticket-triage`)
|
|
@@ -193,7 +235,7 @@ Invoke the `lisa:jira-agent` (existing per-ticket lifecycle agent) with the tick
|
|
|
193
235
|
- Posting progress comments via `lisa:jira-sync`
|
|
194
236
|
- Posting evidence via `lisa:jira-evidence`
|
|
195
237
|
|
|
196
|
-
|
|
238
|
+
The lead waits for `lisa:jira-agent` to return, then resumes this scanner with the returned outcome:
|
|
197
239
|
- **Success** — the build flow completed and a PR exists; evidence posted. The PR may already be **merged** or still **open** (auto-merge enabled, awaiting checks/merge). "Success" means the build work is sound — it does **not** assert the change reached an environment. The env transition in 3d gates on the PR actually being merged; an open PR does not advance the ticket to a `done` env status.
|
|
198
240
|
- **Blocked by jira-verify pre-flight gate** — `lisa:jira-agent` itself transitions the ticket to `Blocked` and reassigns to Reporter. This is correct and expected — let it stand. Record the outcome and move on.
|
|
199
241
|
- **Duplicate already fixed** — `lisa:jira-agent` / `lisa:ticket-triage` returned `DUPLICATE_ALREADY_FIXED` with a canonical ticket reference and empirical base-branch evidence. Post the triage finding, ensure the native `duplicates <canonical>` link exists, transition to the terminal `$DONE` status with resolution `Duplicate`, and do not open a PR. If the canonical fix is merged but not yet on the production branch, the close comment must say the production error can recur until the canonical ticket promotes and that recurrence is tracked by the canonical ticket; do not reopen this duplicate for that recurrence.
|
|
@@ -311,9 +353,11 @@ If a ready-equivalent status does not exist in the JIRA project's workflow, this
|
|
|
311
353
|
| `.lisa.config.json` `jira.workflow.claimed` | `In Progress` | The intermediate status the agent sets on pickup |
|
|
312
354
|
| `.lisa.config.json` `jira.workflow.done` | env-keyed map (`dev`/`staging`/`production`) or string | The status set after a successful build; env-aware |
|
|
313
355
|
| `.lisa.config.json` `deploy.branches` | — | Reverse-lookup map for env inference from PR base branch |
|
|
356
|
+
| `.lisa.config.json` `repo` / `github.repo` (or git remote basename) | (git remote basename) | Current repo for the Phase 1 query-time repo pre-filter and the 3a.0 claim-time gate |
|
|
314
357
|
|
|
315
358
|
## Rules
|
|
316
359
|
|
|
360
|
+
- **Scope the query to the current repo.** Per `repo-scope-split`, append the repo pre-filter (Phase 1 step 2) so a multi-repo JIRA project — or a forwarded `assignee` filter — never pulls sibling repos' ready tickets into the candidate set. It is the cheap query-time arm of the same rule whose authoritative claim-time arm is the 3a.0 gate; the two must agree on how the current repo is resolved. Skip the augmentation (do not fail) only when the current repo can't be resolved or the JQL already constrains repo.
|
|
317
361
|
- **Claim leaves only.** Per the `leaf-only-lifecycle` rule, never claim a container — a ticket with open child work, or a childless Epic — even if it carries the build-ready status. Skip or safe-block it (Phase 3a); never silently implement a container.
|
|
318
362
|
- Never transition a ticket the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
|
|
319
363
|
- Never bypass `lisa:jira-agent` to do build work directly. `lisa:jira-agent` owns the per-ticket lifecycle (read, verify, triage, route, sync, evidence). This skill is the dispatcher, not the builder.
|
|
@@ -124,6 +124,8 @@ Query: `mcp__linear-server__list_issues({team: <teamId>, label: "$READY"})`.
|
|
|
124
124
|
|
|
125
125
|
Capture each Issue's: identifier, title, type label, priority, assignee, project, labels, description summary.
|
|
126
126
|
|
|
127
|
+
> **No query-time repo pre-filter here (by design).** Unlike `lisa:jira-build-intake`, which narrows its JQL with `AND (labels = "repo:<current>" OR labels IS EMPTY)` (the query-time arm of `repo-scope-split`), the Linear `list_issues` label filter is an AND-of-labels and cannot express "current-repo **or** unlabeled" in one query. Adding `repo:<current>` to this query would strand unlabeled Issues the determine + stamp path must see. So the Linear scanner keeps this query broad and relies on the per-candidate 3a.0 gate below for repo scoping.
|
|
128
|
+
|
|
127
129
|
If empty, report `"No Linear Issues labeled $READY. Nothing to do."` and exit. Common idle case.
|
|
128
130
|
|
|
129
131
|
### Phase 3 — Process the first eligible ready Issue
|
|
@@ -194,9 +196,31 @@ This is the idempotency lock — a re-entrant cycle's `label: $READY` filter wil
|
|
|
194
196
|
|
|
195
197
|
If the relabel fails (permission, race), record under "Errors" and skip. **Do not invoke the build flow on an Issue you didn't successfully claim.**
|
|
196
198
|
|
|
197
|
-
#### 3c.
|
|
199
|
+
#### 3c. Return or run the build delegation
|
|
200
|
+
|
|
201
|
+
After the claim succeeds, the per-Issue build must run under `lisa:linear-agent` (the per-Issue lifecycle agent), but a teammate running this skill must not spawn that named peer itself. Claude teams are flat: only the lead can add a named teammate. Therefore:
|
|
202
|
+
|
|
203
|
+
- If you are the team lead/root agent, spawn or invoke `lisa:linear-agent` with the Issue identifier and wait for its structured result.
|
|
204
|
+
- If you are a teammate, stop this skill's direct work and return a structured `delegation-request` to the lead instead of calling `Agent` with `name` or otherwise spawning `linear-agent` as a named peer.
|
|
205
|
+
- A private anonymous helper is allowed only when the helper is not a roster peer and the `Agent` call omits `name`; it must not replace this `linear-agent` delegation.
|
|
206
|
+
|
|
207
|
+
Return this payload shape to the lead:
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"type": "delegation-request",
|
|
212
|
+
"agent": "linear-agent",
|
|
213
|
+
"workItem": "<ISSUE-ID>",
|
|
214
|
+
"context": {
|
|
215
|
+
"claimedLabel": "$CLAIMED",
|
|
216
|
+
"doneResolution": "Resolve $DONE from the PR base branch per this skill's Workflow resolution section"
|
|
217
|
+
},
|
|
218
|
+
"onSuccess": "Confirm the returned PR is merged, then apply Phase 3d and Phase 3d.1",
|
|
219
|
+
"onBlockedOrError": "Leave the Issue where linear-agent left it and record the surfaced outcome"
|
|
220
|
+
}
|
|
221
|
+
```
|
|
198
222
|
|
|
199
|
-
|
|
223
|
+
`lisa:linear-agent` owns:
|
|
200
224
|
- Reading the full Issue graph (`lisa:linear-read-issue`)
|
|
201
225
|
- Running its own pre-flight quality gate (`lisa:linear-verify`)
|
|
202
226
|
- Running ticket triage (`lisa:ticket-triage`)
|
|
@@ -204,7 +228,7 @@ Invoke `lisa:linear-agent` (per-Issue lifecycle agent) with the Issue identifier
|
|
|
204
228
|
- Posting progress comments via `lisa:linear-sync`
|
|
205
229
|
- Posting evidence via `lisa:linear-evidence`
|
|
206
230
|
|
|
207
|
-
|
|
231
|
+
The lead waits for `lisa:linear-agent` to return, then resumes this scanner with the returned outcome:
|
|
208
232
|
- **Success** — the build flow completed and a PR exists; evidence posted. The PR may already be **merged** or still **open** (auto-merge enabled, awaiting checks/merge). "Success" means the build work is sound — it does **not** assert the change reached an environment. The env transition in 3d gates on the PR actually being merged; an open PR does not advance the Issue to a `done` env status.
|
|
209
233
|
- **Blocked by linear-verify pre-flight gate** — `lisa:linear-agent` itself relabels to `status:blocked` and assigns to creator. Let it stand. Record and move on.
|
|
210
234
|
- **Duplicate already fixed** — `lisa:linear-agent` / `lisa:ticket-triage` returned `DUPLICATE_ALREADY_FIXED` with a canonical Issue reference and empirical base-branch evidence. Post the triage finding, ensure the native `duplicates <canonical>` relationship exists when Linear exposes it (otherwise leave an explicit relation/comment reference), apply the terminal `$DONE` label, move the native Issue to the configured canceled-as-duplicate or completed terminal state, and do not open a PR. If the canonical fix is merged but not yet on the production branch, the close comment must say the production error can recur until the canonical Issue promotes and that recurrence is tracked by the canonical Issue; do not reopen this duplicate for that recurrence.
|
|
@@ -334,8 +334,11 @@ If the PR is healthy in-flight and no blocker is found, the work simply died mid
|
|
|
334
334
|
the vendor build-intake runs**, skipping the claim transition (the item is already `claimed`):
|
|
335
335
|
|
|
336
336
|
1. Dispatch the item to the vendor agent — `lisa:jira-agent` / `lisa:github-agent` /
|
|
337
|
-
`lisa:linear-agent` (matching the queue's tracker) — with the item ref.
|
|
338
|
-
|
|
337
|
+
`lisa:linear-agent` (matching the queue's tracker) — with the item ref. If repair-intake is
|
|
338
|
+
running as a teammate rather than the lead/root agent, return a structured `delegation-request`
|
|
339
|
+
to the lead instead of spawning that named peer yourself; only the lead can add named teammates
|
|
340
|
+
in Claude's flat roster. This resumes the work in place, preserving its existing branch/PR and
|
|
341
|
+
prior comments.
|
|
339
342
|
2. **On agent success**, apply the scanner's post-agent transition yourself: `claimed → done`,
|
|
340
343
|
where `done` is **env-resolved** exactly as `lisa:<tracker>-build-intake` resolves it (per
|
|
341
344
|
`config-resolution` env-keyed `done`: explicit `target_env` arg wins; else reverse-lookup the
|
|
@@ -50,7 +50,21 @@ Resolve the current repo per the `config-resolution` "Repo scoping" section (con
|
|
|
50
50
|
2. **Multi-repo leaf → split, never claim.** If determination finds the leaf touches more than one repo, run the **work-time split procedure** below to break it into single-repo siblings — each created **build-ready** (`build_ready: true`, so the build queue auto-claims it) and stamped with its own `repo:<name>`. After the split, the current repo's sibling (if any) becomes a normal current-repo candidate; the others are separate single-repo `ready` leaves for their repos. A multi-repo leaf is never claimed as-is.
|
|
51
51
|
3. **Wrong repo → skip.** A single-repo leaf whose `repo:<name>` ≠ the current repo is left `ready` (and labeled) and skipped; intake moves on until it finds a claimable current-repo leaf, then stops (one item per cycle).
|
|
52
52
|
|
|
53
|
-
**
|
|
53
|
+
**Query-time pre-filter (the cheapest arm — apply before the per-candidate walk).** When the queue is queryable, scope the candidate **query itself** to the current repo so sibling-repo tickets never even enter the set — instead of pulling the whole project's ready tickets and skipping the wrong ones one-by-one (a full wasted scan when none belong to the current repo, e.g. a JIRA project shared across `frontend`/`backend`/`infrastructure`). On JIRA, append to the JQL:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
AND (labels = "repo:<current>" OR labels IS EMPTY)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This pre-applies **only** the unambiguous *"labeled for another repo → skip"* arm at query time, while `labels IS EMPTY` keeps **unlabeled** tickets in the set so the determine + stamp arm still works. It does not change any outcome — it just moves the cheap skips off the per-candidate walk. The JIRA **component** alias and any rarer residual cases are not expressed in the pre-filter and remain with the authoritative per-candidate gate above. Skip the pre-filter (fall back to the broad scan) when the current repo can't be resolved, or when the caller's query already constrains repo (a `repo:` label term or a `component =` term) — never fail the cycle just because the pre-filter couldn't be built.
|
|
60
|
+
|
|
61
|
+
Apply it **only where the query layer can express `OR labels IS EMPTY` without dropping unlabeled candidates** — otherwise the pre-filter would hide the very tickets the determine + stamp arm exists to handle:
|
|
62
|
+
|
|
63
|
+
- **JIRA (multi-repo project):** JQL expresses it directly — `lisa:jira-build-intake` applies it in Phase 1. This is the case the optimization is for.
|
|
64
|
+
- **GitHub:** issues are inherently single-repo, so the scanner is already repo-scoped — no pre-filter needed.
|
|
65
|
+
- **Linear (multi-repo team):** the `list_issues` label filter is an AND-of-labels and cannot express "current-repo **or** unlabeled" in one query, so `lisa:linear-build-intake` keeps the broad `$READY` query and relies on the per-candidate 3a.0 gate — a narrowing label filter there would strand unlabeled Issues.
|
|
66
|
+
|
|
67
|
+
**Cost.** Only **unlabeled** candidates need content determination; once stamped, wrong-repo candidates are skipped by label alone — and with the query-time pre-filter, sibling-repo tickets are not even fetched. Prefer candidates already labeled `repo:<current>` first (cheap claim), falling through to unlabeled candidates (determine + stamp) only when no pre-labeled current-repo leaf is ready.
|
|
54
68
|
|
|
55
69
|
A container (an Epic, or any item with open child work) is handled by the leaf-only gate, not here — containers may span repos, may keep multiple `repo:<name>` labels for visibility, and are never claimed/built directly. Only a leaf work unit — including a now-childless Story/Spike that the leaf-only gate treats as a leaf — is split or skipped by repo scope.
|
|
56
70
|
|
|
@@ -35,7 +35,11 @@ Fall back to the standard BLOCK + reassign-to-Reporter path when:
|
|
|
35
35
|
|
|
36
36
|
## Claim-time repo scoping (build-intake)
|
|
37
37
|
|
|
38
|
-
A tracker can oversee multiple repos. Build-intake claims only current-repo tickets. Resolve current repo per `config-resolution` (config `repo` → `github.repo` → git remote basename).
|
|
38
|
+
A tracker can oversee multiple repos. Build-intake claims only current-repo tickets. Resolve current repo per `config-resolution` (config `repo` → `github.repo` → git remote basename).
|
|
39
|
+
|
|
40
|
+
**Query-time pre-filter (do this first, where expressible).** Scope the candidate query to the current repo so sibling-repo tickets never enter the set — on JIRA, append `AND (labels = "repo:<current>" OR labels IS EMPTY)`. This pre-applies only the unambiguous wrong-repo → skip arm; `labels IS EMPTY` keeps unlabeled tickets visible so the per-candidate gate below can still determine + stamp them. Skip it (broad scan) when the current repo can't be resolved or the query already constrains repo. Apply only where the query layer can express `OR labels IS EMPTY`: JIRA does (applied); GitHub issues are inherently single-repo (not needed); Linear's label filter can't, so it keeps the broad query and relies on the per-candidate gate.
|
|
41
|
+
|
|
42
|
+
Then, for each ready candidate:
|
|
39
43
|
|
|
40
44
|
1. **Read `repo:<name>` label.** Wrong repo → skip. Current repo → leaf-only gate + claim. Unlabeled → determine + stamp + re-apply.
|
|
41
45
|
2. **Multi-repo leaf → split, never claim.** Each split sibling is created build-ready and stamped with its own `repo:<name>`.
|
|
@@ -256,9 +256,31 @@ This is the idempotency lock — a re-entrant cycle's `--label $READY` filter wi
|
|
|
256
256
|
|
|
257
257
|
If the relabel fails (permission, race), log under "Errors" in the cycle summary and skip this issue. **Do not invoke the build flow on an issue you didn't successfully claim.**
|
|
258
258
|
|
|
259
|
-
#### 3c.
|
|
259
|
+
#### 3c. Return or run the build delegation
|
|
260
|
+
|
|
261
|
+
After the claim succeeds, the per-issue build must run under `lisa:github-agent` (the per-issue lifecycle agent), but a teammate running this skill must not spawn that named peer itself. Claude teams are flat: only the lead can add a named teammate. Therefore:
|
|
262
|
+
|
|
263
|
+
- If you are the team lead/root agent, spawn or invoke `lisa:github-agent` with the issue ref and wait for its structured result.
|
|
264
|
+
- If you are a teammate, stop this skill's direct work and return a structured `delegation-request` to the lead instead of calling `Agent` with `name` or otherwise spawning `github-agent` as a named peer.
|
|
265
|
+
- A private anonymous helper is allowed only when the helper is not a roster peer and the `Agent` call omits `name`; it must not replace this `github-agent` delegation.
|
|
266
|
+
|
|
267
|
+
Return this payload shape to the lead:
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
{
|
|
271
|
+
"type": "delegation-request",
|
|
272
|
+
"agent": "github-agent",
|
|
273
|
+
"workItem": "<org>/<repo>#<number>",
|
|
274
|
+
"context": {
|
|
275
|
+
"claimedLabel": "$CLAIMED",
|
|
276
|
+
"doneResolution": "Resolve $DONE from the PR base branch per this skill's Workflow resolution section"
|
|
277
|
+
},
|
|
278
|
+
"onSuccess": "Confirm the returned PR is merged, then apply Phase 3d and Phase 3d.1",
|
|
279
|
+
"onBlockedOrError": "Leave the issue where github-agent left it and record the surfaced outcome"
|
|
280
|
+
}
|
|
281
|
+
```
|
|
260
282
|
|
|
261
|
-
|
|
283
|
+
`lisa:github-agent` owns:
|
|
262
284
|
- Reading the full issue graph (`lisa:github-read-issue`)
|
|
263
285
|
- Running its own pre-flight quality gate (`lisa:github-verify`)
|
|
264
286
|
- Running issue triage (`lisa:ticket-triage`)
|
|
@@ -266,7 +288,7 @@ Invoke `lisa:github-agent` (the per-issue lifecycle agent) with the issue ref. `
|
|
|
266
288
|
- Posting progress comments via `lisa:github-sync`
|
|
267
289
|
- Posting evidence via `lisa:github-evidence`
|
|
268
290
|
|
|
269
|
-
|
|
291
|
+
The lead waits for `lisa:github-agent` to return, then resumes this scanner with the returned outcome:
|
|
270
292
|
|
|
271
293
|
- **Success** — the build flow completed and a PR exists; evidence posted. The PR may already be **merged** or still **open** (auto-merge enabled, awaiting checks/merge). "Success" means the build work is sound — it does **not** assert the change reached an environment. The env transition in 3d gates on the PR actually being merged; an open PR does not advance the issue to a `done` env status.
|
|
272
294
|
- **Blocked by github-verify pre-flight gate** — `lisa:github-agent` itself relabels the issue to `status:blocked` (or removes `$CLAIMED` and reassigns to the original author). This is correct and expected — let it stand. Record and move on.
|
|
@@ -11,7 +11,7 @@ All Atlassian operations in this skill go through `lisa:atlassian-access`. Do no
|
|
|
11
11
|
`$ARGUMENTS` is one of:
|
|
12
12
|
|
|
13
13
|
1. A JIRA project key (e.g. `SE`) — scans that project for tickets in the configured `ready` status.
|
|
14
|
-
2. A full JQL filter (e.g. `project = SE AND component = "frontend" AND Status = Ready`) — used as-is. The skill will not append a `Status = <ready>` clause if the JQL already names a status, so callers can intentionally widen.
|
|
14
|
+
2. A full JQL filter (e.g. `project = SE AND component = "frontend" AND Status = Ready`) — used as-is. The skill will not append a `Status = <ready>` clause if the JQL already names a status, so callers can intentionally widen. It also auto-scopes the query to the current repo (Phase 1 step 2) unless the JQL already constrains repo (a `repo:` label or `component =` term), so a multi-repo project / forwarded assignee filter is not widened to sibling repos by accident.
|
|
15
15
|
|
|
16
16
|
Run one build-intake cycle. The first eligible ready ticket is claimed, built via the `lisa:jira-agent` flow, transitioned to the configured `done` status (env-aware — see below), then the cycle exits. Remaining ready tickets stay queued for later scheduler invocations.
|
|
17
17
|
|
|
@@ -105,10 +105,30 @@ This skill ONLY transitions `$READY → $CLAIMED` on claim, and `$CLAIMED → $D
|
|
|
105
105
|
|
|
106
106
|
### Phase 1 — Resolve the query
|
|
107
107
|
|
|
108
|
-
1. Parse `$ARGUMENTS
|
|
109
|
-
- Project key:
|
|
108
|
+
1. Parse `$ARGUMENTS` into a base JQL:
|
|
109
|
+
- Project key: `project = <KEY> AND Status = "$READY"`.
|
|
110
110
|
- Full JQL: use as-is. If it does not include a `Status` clause, append `AND Status = "$READY"`.
|
|
111
|
-
2.
|
|
111
|
+
2. **Repo-scope pre-filter (query-time arm of `repo-scope-split`).** A JIRA project can oversee multiple repos (`frontend` / `backend` / `infrastructure`), so an unscoped `project = <KEY> AND Status = $READY` — or an `assignee = … AND Status = $READY` filter forwarded by `lisa:intake` — pulls in **every** sibling repo's ready tickets and forces the claim-time gate (3a.0) to skip them one-by-one: a full wasted scan when none belong to the current repo. Narrow the candidate set at query time so other-repo tickets never enter it:
|
|
112
|
+
1. **Resolve the current repo** per `config-resolution` "Repo scoping" (`.repo` → `.github.repo` → `git remote get-url origin` basename) — the same resolution 3a.0 uses.
|
|
113
|
+
2. **If the base JQL already constrains repo** — it contains a `repo:` label term or a `component = "<repo>"` term — the caller has already scoped (or intentionally widened) it; leave it untouched.
|
|
114
|
+
3. **Else, if the current repo resolved**, append (before the `ORDER BY`):
|
|
115
|
+
```text
|
|
116
|
+
AND (labels = "repo:<current>" OR labels IS EMPTY)
|
|
117
|
+
```
|
|
118
|
+
This drops tickets explicitly stamped for a **sibling** repo up front, while still surfacing **unlabeled** tickets (`labels IS EMPTY`) so the claim-time gate can determine + stamp them — i.e. it pre-applies only the unambiguous "wrong-repo → skip" arm of 3a.0 and never hides work the stamping path must see. The JIRA **component** alias and any rarer residual cases stay with the authoritative claim-time gate in 3a.0.
|
|
119
|
+
4. **If the current repo cannot be resolved**, skip this augmentation and fall back to the broad scan (3a.0 still enforces scoping). Do not fail the cycle solely because the pre-filter could not be built.
|
|
120
|
+
3. Append the ordering: `ORDER BY priority DESC, created ASC`.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# CURRENT_REPO resolved per config-resolution "Repo scoping" (see 3a.0).
|
|
124
|
+
# Append a repo pre-filter only when the JQL does not already constrain repo.
|
|
125
|
+
if [ -n "$CURRENT_REPO" ] && ! printf '%s' "$BASE_JQL" | grep -qiE 'repo:|component[[:space:]]*='; then
|
|
126
|
+
BASE_JQL="${BASE_JQL} AND (labels = \"repo:${CURRENT_REPO}\" OR labels IS EMPTY)"
|
|
127
|
+
fi
|
|
128
|
+
JQL="${BASE_JQL} ORDER BY priority DESC, created ASC"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
4. Confirm the configured Atlassian site by invoking `lisa:atlassian-access` `operation: list-sites` (it enforces connection match against `.lisa.config.json`).
|
|
112
132
|
|
|
113
133
|
### Phase 2 — Find ready tickets
|
|
114
134
|
|
|
@@ -123,7 +143,7 @@ If empty, report `"No tickets with Status=$READY. Nothing to do."` and exit. Thi
|
|
|
123
143
|
A JIRA project can oversee multiple repos (`frontend` / `backend` / `infrastructure`). This skill claims only tickets 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).
|
|
124
144
|
|
|
125
145
|
1. **Resolve the current repo** per `config-resolution` "Repo scoping" (`.repo` → `.github.repo` → `git remote get-url origin` basename). If unresolvable, stop and report — do not claim tickets you cannot scope.
|
|
126
|
-
2. **Cheap path first.** Prefer candidates already carrying `repo:<current>` — a JIRA **label**, or a **component** equal to the repo name (accepted as an alias)
|
|
146
|
+
2. **Cheap path first.** Phase 1's query-time pre-filter has already dropped tickets explicitly stamped for a sibling repo, so the Phase 2 result set is current-repo-labeled + unlabeled tickets. Prefer candidates already carrying `repo:<current>` — a JIRA **label**, or a **component** equal to the repo name (accepted as an alias); the pre-filter is label-only, so a ticket scoped solely by a sibling-repo **component** can still appear and is skipped here. The result set still includes unlabeled tickets so they can be determined and stamped; this gate orders/filters what remains.
|
|
127
147
|
3. **Per candidate, apply the repo-scope decision (`repo-scope-split`):**
|
|
128
148
|
- Carries `repo:<other>` (label or component) → **skip** (leave it `ready` for that repo's own intake); next candidate.
|
|
129
149
|
- **Unlabeled** → determine the target repo(s) from the ticket (description, AC, technical approach) confirmed against the code surfaces, then **stamp** `repo:<name>` via `lisa:atlassian-access` `operation: write-ticket` (add the label / set the component) so later cycles filter cheaply; re-apply with the now-known repo.
|
|
@@ -183,9 +203,31 @@ Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa:atlassian-ac
|
|
|
183
203
|
|
|
184
204
|
If the transition fails (permission, missing transition, race), log under "Errors" in the cycle summary and skip this ticket. **Do not invoke the build flow on a ticket you didn't successfully claim.**
|
|
185
205
|
|
|
186
|
-
#### 3c.
|
|
206
|
+
#### 3c. Return or run the build delegation
|
|
207
|
+
|
|
208
|
+
After the claim succeeds, the per-ticket build must run under `lisa:jira-agent` (the per-ticket lifecycle agent), but a teammate running this skill must not spawn that named peer itself. Claude teams are flat: only the lead can add a named teammate. Therefore:
|
|
209
|
+
|
|
210
|
+
- If you are the team lead/root agent, spawn or invoke `lisa:jira-agent` with the ticket key and wait for its structured result.
|
|
211
|
+
- If you are a teammate, stop this skill's direct work and return a structured `delegation-request` to the lead instead of calling `Agent` with `name` or otherwise spawning `jira-agent` as a named peer.
|
|
212
|
+
- A private anonymous helper is allowed only when the helper is not a roster peer and the `Agent` call omits `name`; it must not replace this `jira-agent` delegation.
|
|
213
|
+
|
|
214
|
+
Return this payload shape to the lead:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"type": "delegation-request",
|
|
219
|
+
"agent": "jira-agent",
|
|
220
|
+
"workItem": "<TICKET>",
|
|
221
|
+
"context": {
|
|
222
|
+
"claimedStatus": "$CLAIMED",
|
|
223
|
+
"doneResolution": "Resolve $DONE from the PR base branch per this skill's Workflow resolution section"
|
|
224
|
+
},
|
|
225
|
+
"onSuccess": "Confirm the returned PR is merged, then apply Phase 3d and Phase 3d.1",
|
|
226
|
+
"onBlockedOrError": "Leave the ticket where jira-agent left it and record the surfaced outcome"
|
|
227
|
+
}
|
|
228
|
+
```
|
|
187
229
|
|
|
188
|
-
|
|
230
|
+
`lisa:jira-agent` owns:
|
|
189
231
|
- Reading the full ticket graph (`lisa:jira-read-ticket`)
|
|
190
232
|
- Running its own pre-flight quality gate (`lisa:jira-verify`)
|
|
191
233
|
- Running ticket triage (`lisa:ticket-triage`)
|
|
@@ -193,7 +235,7 @@ Invoke the `lisa:jira-agent` (existing per-ticket lifecycle agent) with the tick
|
|
|
193
235
|
- Posting progress comments via `lisa:jira-sync`
|
|
194
236
|
- Posting evidence via `lisa:jira-evidence`
|
|
195
237
|
|
|
196
|
-
|
|
238
|
+
The lead waits for `lisa:jira-agent` to return, then resumes this scanner with the returned outcome:
|
|
197
239
|
- **Success** — the build flow completed and a PR exists; evidence posted. The PR may already be **merged** or still **open** (auto-merge enabled, awaiting checks/merge). "Success" means the build work is sound — it does **not** assert the change reached an environment. The env transition in 3d gates on the PR actually being merged; an open PR does not advance the ticket to a `done` env status.
|
|
198
240
|
- **Blocked by jira-verify pre-flight gate** — `lisa:jira-agent` itself transitions the ticket to `Blocked` and reassigns to Reporter. This is correct and expected — let it stand. Record the outcome and move on.
|
|
199
241
|
- **Duplicate already fixed** — `lisa:jira-agent` / `lisa:ticket-triage` returned `DUPLICATE_ALREADY_FIXED` with a canonical ticket reference and empirical base-branch evidence. Post the triage finding, ensure the native `duplicates <canonical>` link exists, transition to the terminal `$DONE` status with resolution `Duplicate`, and do not open a PR. If the canonical fix is merged but not yet on the production branch, the close comment must say the production error can recur until the canonical ticket promotes and that recurrence is tracked by the canonical ticket; do not reopen this duplicate for that recurrence.
|
|
@@ -311,9 +353,11 @@ If a ready-equivalent status does not exist in the JIRA project's workflow, this
|
|
|
311
353
|
| `.lisa.config.json` `jira.workflow.claimed` | `In Progress` | The intermediate status the agent sets on pickup |
|
|
312
354
|
| `.lisa.config.json` `jira.workflow.done` | env-keyed map (`dev`/`staging`/`production`) or string | The status set after a successful build; env-aware |
|
|
313
355
|
| `.lisa.config.json` `deploy.branches` | — | Reverse-lookup map for env inference from PR base branch |
|
|
356
|
+
| `.lisa.config.json` `repo` / `github.repo` (or git remote basename) | (git remote basename) | Current repo for the Phase 1 query-time repo pre-filter and the 3a.0 claim-time gate |
|
|
314
357
|
|
|
315
358
|
## Rules
|
|
316
359
|
|
|
360
|
+
- **Scope the query to the current repo.** Per `repo-scope-split`, append the repo pre-filter (Phase 1 step 2) so a multi-repo JIRA project — or a forwarded `assignee` filter — never pulls sibling repos' ready tickets into the candidate set. It is the cheap query-time arm of the same rule whose authoritative claim-time arm is the 3a.0 gate; the two must agree on how the current repo is resolved. Skip the augmentation (do not fail) only when the current repo can't be resolved or the JQL already constrains repo.
|
|
317
361
|
- **Claim leaves only.** Per the `leaf-only-lifecycle` rule, never claim a container — a ticket with open child work, or a childless Epic — even if it carries the build-ready status. Skip or safe-block it (Phase 3a); never silently implement a container.
|
|
318
362
|
- Never transition a ticket the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
|
|
319
363
|
- Never bypass `lisa:jira-agent` to do build work directly. `lisa:jira-agent` owns the per-ticket lifecycle (read, verify, triage, route, sync, evidence). This skill is the dispatcher, not the builder.
|
|
@@ -124,6 +124,8 @@ Query: `mcp__linear-server__list_issues({team: <teamId>, label: "$READY"})`.
|
|
|
124
124
|
|
|
125
125
|
Capture each Issue's: identifier, title, type label, priority, assignee, project, labels, description summary.
|
|
126
126
|
|
|
127
|
+
> **No query-time repo pre-filter here (by design).** Unlike `lisa:jira-build-intake`, which narrows its JQL with `AND (labels = "repo:<current>" OR labels IS EMPTY)` (the query-time arm of `repo-scope-split`), the Linear `list_issues` label filter is an AND-of-labels and cannot express "current-repo **or** unlabeled" in one query. Adding `repo:<current>` to this query would strand unlabeled Issues the determine + stamp path must see. So the Linear scanner keeps this query broad and relies on the per-candidate 3a.0 gate below for repo scoping.
|
|
128
|
+
|
|
127
129
|
If empty, report `"No Linear Issues labeled $READY. Nothing to do."` and exit. Common idle case.
|
|
128
130
|
|
|
129
131
|
### Phase 3 — Process the first eligible ready Issue
|
|
@@ -194,9 +196,31 @@ This is the idempotency lock — a re-entrant cycle's `label: $READY` filter wil
|
|
|
194
196
|
|
|
195
197
|
If the relabel fails (permission, race), record under "Errors" and skip. **Do not invoke the build flow on an Issue you didn't successfully claim.**
|
|
196
198
|
|
|
197
|
-
#### 3c.
|
|
199
|
+
#### 3c. Return or run the build delegation
|
|
200
|
+
|
|
201
|
+
After the claim succeeds, the per-Issue build must run under `lisa:linear-agent` (the per-Issue lifecycle agent), but a teammate running this skill must not spawn that named peer itself. Claude teams are flat: only the lead can add a named teammate. Therefore:
|
|
202
|
+
|
|
203
|
+
- If you are the team lead/root agent, spawn or invoke `lisa:linear-agent` with the Issue identifier and wait for its structured result.
|
|
204
|
+
- If you are a teammate, stop this skill's direct work and return a structured `delegation-request` to the lead instead of calling `Agent` with `name` or otherwise spawning `linear-agent` as a named peer.
|
|
205
|
+
- A private anonymous helper is allowed only when the helper is not a roster peer and the `Agent` call omits `name`; it must not replace this `linear-agent` delegation.
|
|
206
|
+
|
|
207
|
+
Return this payload shape to the lead:
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"type": "delegation-request",
|
|
212
|
+
"agent": "linear-agent",
|
|
213
|
+
"workItem": "<ISSUE-ID>",
|
|
214
|
+
"context": {
|
|
215
|
+
"claimedLabel": "$CLAIMED",
|
|
216
|
+
"doneResolution": "Resolve $DONE from the PR base branch per this skill's Workflow resolution section"
|
|
217
|
+
},
|
|
218
|
+
"onSuccess": "Confirm the returned PR is merged, then apply Phase 3d and Phase 3d.1",
|
|
219
|
+
"onBlockedOrError": "Leave the Issue where linear-agent left it and record the surfaced outcome"
|
|
220
|
+
}
|
|
221
|
+
```
|
|
198
222
|
|
|
199
|
-
|
|
223
|
+
`lisa:linear-agent` owns:
|
|
200
224
|
- Reading the full Issue graph (`lisa:linear-read-issue`)
|
|
201
225
|
- Running its own pre-flight quality gate (`lisa:linear-verify`)
|
|
202
226
|
- Running ticket triage (`lisa:ticket-triage`)
|
|
@@ -204,7 +228,7 @@ Invoke `lisa:linear-agent` (per-Issue lifecycle agent) with the Issue identifier
|
|
|
204
228
|
- Posting progress comments via `lisa:linear-sync`
|
|
205
229
|
- Posting evidence via `lisa:linear-evidence`
|
|
206
230
|
|
|
207
|
-
|
|
231
|
+
The lead waits for `lisa:linear-agent` to return, then resumes this scanner with the returned outcome:
|
|
208
232
|
- **Success** — the build flow completed and a PR exists; evidence posted. The PR may already be **merged** or still **open** (auto-merge enabled, awaiting checks/merge). "Success" means the build work is sound — it does **not** assert the change reached an environment. The env transition in 3d gates on the PR actually being merged; an open PR does not advance the Issue to a `done` env status.
|
|
209
233
|
- **Blocked by linear-verify pre-flight gate** — `lisa:linear-agent` itself relabels to `status:blocked` and assigns to creator. Let it stand. Record and move on.
|
|
210
234
|
- **Duplicate already fixed** — `lisa:linear-agent` / `lisa:ticket-triage` returned `DUPLICATE_ALREADY_FIXED` with a canonical Issue reference and empirical base-branch evidence. Post the triage finding, ensure the native `duplicates <canonical>` relationship exists when Linear exposes it (otherwise leave an explicit relation/comment reference), apply the terminal `$DONE` label, move the native Issue to the configured canceled-as-duplicate or completed terminal state, and do not open a PR. If the canonical fix is merged but not yet on the production branch, the close comment must say the production error can recur until the canonical Issue promotes and that recurrence is tracked by the canonical Issue; do not reopen this duplicate for that recurrence.
|
|
@@ -334,8 +334,11 @@ If the PR is healthy in-flight and no blocker is found, the work simply died mid
|
|
|
334
334
|
the vendor build-intake runs**, skipping the claim transition (the item is already `claimed`):
|
|
335
335
|
|
|
336
336
|
1. Dispatch the item to the vendor agent — `lisa:jira-agent` / `lisa:github-agent` /
|
|
337
|
-
`lisa:linear-agent` (matching the queue's tracker) — with the item ref.
|
|
338
|
-
|
|
337
|
+
`lisa:linear-agent` (matching the queue's tracker) — with the item ref. If repair-intake is
|
|
338
|
+
running as a teammate rather than the lead/root agent, return a structured `delegation-request`
|
|
339
|
+
to the lead instead of spawning that named peer yourself; only the lead can add named teammates
|
|
340
|
+
in Claude's flat roster. This resumes the work in place, preserving its existing branch/PR and
|
|
341
|
+
prior comments.
|
|
339
342
|
2. **On agent success**, apply the scanner's post-agent transition yourself: `claimed → done`,
|
|
340
343
|
where `done` is **env-resolved** exactly as `lisa:<tracker>-build-intake` resolves it (per
|
|
341
344
|
`config-resolution` env-keyed `done`: explicit `target_env` arg wins; else reverse-lookup the
|