@codyswann/lisa 2.176.6 → 2.176.8

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 (69) hide show
  1. package/package.json +1 -1
  2. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  3. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  4. package/plugins/lisa/rules/eager/repo-scope-split.md +5 -1
  5. package/plugins/lisa/rules/reference/repo-scope-split.md +15 -1
  6. package/plugins/lisa/skills/jira-build-intake/SKILL.md +27 -5
  7. package/plugins/lisa/skills/linear-build-intake/SKILL.md +2 -0
  8. package/plugins/lisa-agy/plugin.json +1 -1
  9. package/plugins/lisa-agy/skills/jira-build-intake/SKILL.md +27 -5
  10. package/plugins/lisa-agy/skills/linear-build-intake/SKILL.md +2 -0
  11. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  13. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  14. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  16. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-copilot/rules/eager/repo-scope-split.md +5 -1
  18. package/plugins/lisa-copilot/rules/reference/repo-scope-split.md +15 -1
  19. package/plugins/lisa-copilot/skills/jira-build-intake/SKILL.md +27 -5
  20. package/plugins/lisa-copilot/skills/linear-build-intake/SKILL.md +2 -0
  21. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-cursor/rules/repo-scope-split-reference.mdc +15 -1
  23. package/plugins/lisa-cursor/rules/repo-scope-split.mdc +5 -1
  24. package/plugins/lisa-cursor/skills/jira-build-intake/SKILL.md +27 -5
  25. package/plugins/lisa-cursor/skills/linear-build-intake/SKILL.md +2 -0
  26. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  28. package/plugins/lisa-expo-agy/plugin.json +1 -1
  29. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  33. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  34. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  38. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  39. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  43. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  44. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  48. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  49. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  53. package/plugins/lisa-rails-agy/plugin.json +1 -1
  54. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  58. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  59. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  63. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  64. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  66. package/plugins/src/base/rules/eager/repo-scope-split.md +5 -1
  67. package/plugins/src/base/rules/reference/repo-scope-split.md +15 -1
  68. package/plugins/src/base/skills/jira-build-intake/SKILL.md +27 -5
  69. package/plugins/src/base/skills/linear-build-intake/SKILL.md +2 -0
package/package.json CHANGED
@@ -91,7 +91,7 @@
91
91
  "ws": ">=8.20.1"
92
92
  },
93
93
  "name": "@codyswann/lisa",
94
- "version": "2.176.6",
94
+ "version": "2.176.8",
95
95
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
96
96
  "main": "dist/index.js",
97
97
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -30,7 +30,11 @@ Fall back to the standard BLOCK + reassign-to-Reporter path when:
30
30
 
31
31
  ## Claim-time repo scoping (build-intake)
32
32
 
33
- 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). For each ready candidate:
33
+ 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).
34
+
35
+ **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.
36
+
37
+ Then, for each ready candidate:
34
38
 
35
39
  1. **Read `repo:<name>` label.** Wrong repo → skip. Current repo → leaf-only gate + claim. Unlabeled → determine + stamp + re-apply.
36
40
  2. **Multi-repo leaf → split, never claim.** Each split sibling is created build-ready and stamped with its own `repo:<name>`.
@@ -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
- **Cost.** Only **unlabeled** candidates need content determination; once stamped, wrong-repo candidates are skipped by label alone. 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.
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
 
@@ -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: build JQL `project = <KEY> AND Status = "$READY" ORDER BY priority DESC, created ASC`.
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. Confirm the configured Atlassian site by invoking `lisa:atlassian-access` `operation: list-sites` (it enforces connection match against `.lisa.config.json`).
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). Keep the Phase 2 scan broad (it must still see unlabeled tickets so they can be determined and stamped); this gate orders/filters the results.
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.
@@ -311,9 +331,11 @@ If a ready-equivalent status does not exist in the JIRA project's workflow, this
311
331
  | `.lisa.config.json` `jira.workflow.claimed` | `In Progress` | The intermediate status the agent sets on pickup |
312
332
  | `.lisa.config.json` `jira.workflow.done` | env-keyed map (`dev`/`staging`/`production`) or string | The status set after a successful build; env-aware |
313
333
  | `.lisa.config.json` `deploy.branches` | — | Reverse-lookup map for env inference from PR base branch |
334
+ | `.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
335
 
315
336
  ## Rules
316
337
 
338
+ - **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
339
  - **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
340
  - Never transition a ticket the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
319
341
  - 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -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: build JQL `project = <KEY> AND Status = "$READY" ORDER BY priority DESC, created ASC`.
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. Confirm the configured Atlassian site by invoking `lisa:atlassian-access` `operation: list-sites` (it enforces connection match against `.lisa.config.json`).
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). Keep the Phase 2 scan broad (it must still see unlabeled tickets so they can be determined and stamped); this gate orders/filters the results.
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.
@@ -311,9 +331,11 @@ If a ready-equivalent status does not exist in the JIRA project's workflow, this
311
331
  | `.lisa.config.json` `jira.workflow.claimed` | `In Progress` | The intermediate status the agent sets on pickup |
312
332
  | `.lisa.config.json` `jira.workflow.done` | env-keyed map (`dev`/`staging`/`production`) or string | The status set after a successful build; env-aware |
313
333
  | `.lisa.config.json` `deploy.branches` | — | Reverse-lookup map for env inference from PR base branch |
334
+ | `.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
335
 
315
336
  ## Rules
316
337
 
338
+ - **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
339
  - **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
340
  - Never transition a ticket the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
319
341
  - 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -30,7 +30,11 @@ Fall back to the standard BLOCK + reassign-to-Reporter path when:
30
30
 
31
31
  ## Claim-time repo scoping (build-intake)
32
32
 
33
- 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). For each ready candidate:
33
+ 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).
34
+
35
+ **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.
36
+
37
+ Then, for each ready candidate:
34
38
 
35
39
  1. **Read `repo:<name>` label.** Wrong repo → skip. Current repo → leaf-only gate + claim. Unlabeled → determine + stamp + re-apply.
36
40
  2. **Multi-repo leaf → split, never claim.** Each split sibling is created build-ready and stamped with its own `repo:<name>`.
@@ -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
- **Cost.** Only **unlabeled** candidates need content determination; once stamped, wrong-repo candidates are skipped by label alone. 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.
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
 
@@ -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: build JQL `project = <KEY> AND Status = "$READY" ORDER BY priority DESC, created ASC`.
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. Confirm the configured Atlassian site by invoking `lisa:atlassian-access` `operation: list-sites` (it enforces connection match against `.lisa.config.json`).
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). Keep the Phase 2 scan broad (it must still see unlabeled tickets so they can be determined and stamped); this gate orders/filters the results.
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.
@@ -311,9 +331,11 @@ If a ready-equivalent status does not exist in the JIRA project's workflow, this
311
331
  | `.lisa.config.json` `jira.workflow.claimed` | `In Progress` | The intermediate status the agent sets on pickup |
312
332
  | `.lisa.config.json` `jira.workflow.done` | env-keyed map (`dev`/`staging`/`production`) or string | The status set after a successful build; env-aware |
313
333
  | `.lisa.config.json` `deploy.branches` | — | Reverse-lookup map for env inference from PR base branch |
334
+ | `.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
335
 
315
336
  ## Rules
316
337
 
338
+ - **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
339
  - **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
340
  - Never transition a ticket the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
319
341
  - 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -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
- **Cost.** Only **unlabeled** candidates need content determination; once stamped, wrong-repo candidates are skipped by label alone. 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.
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). For each ready candidate:
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>`.
@@ -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: build JQL `project = <KEY> AND Status = "$READY" ORDER BY priority DESC, created ASC`.
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. Confirm the configured Atlassian site by invoking `lisa:atlassian-access` `operation: list-sites` (it enforces connection match against `.lisa.config.json`).
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). Keep the Phase 2 scan broad (it must still see unlabeled tickets so they can be determined and stamped); this gate orders/filters the results.
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.
@@ -311,9 +331,11 @@ If a ready-equivalent status does not exist in the JIRA project's workflow, this
311
331
  | `.lisa.config.json` `jira.workflow.claimed` | `In Progress` | The intermediate status the agent sets on pickup |
312
332
  | `.lisa.config.json` `jira.workflow.done` | env-keyed map (`dev`/`staging`/`production`) or string | The status set after a successful build; env-aware |
313
333
  | `.lisa.config.json` `deploy.branches` | — | Reverse-lookup map for env inference from PR base branch |
334
+ | `.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
335
 
315
336
  ## Rules
316
337
 
338
+ - **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
339
  - **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
340
  - Never transition a ticket the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
319
341
  - 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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": "2.176.6",
3
+ "version": "2.176.8",
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"
@@ -30,7 +30,11 @@ Fall back to the standard BLOCK + reassign-to-Reporter path when:
30
30
 
31
31
  ## Claim-time repo scoping (build-intake)
32
32
 
33
- 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). For each ready candidate:
33
+ 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).
34
+
35
+ **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.
36
+
37
+ Then, for each ready candidate:
34
38
 
35
39
  1. **Read `repo:<name>` label.** Wrong repo → skip. Current repo → leaf-only gate + claim. Unlabeled → determine + stamp + re-apply.
36
40
  2. **Multi-repo leaf → split, never claim.** Each split sibling is created build-ready and stamped with its own `repo:<name>`.
@@ -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
- **Cost.** Only **unlabeled** candidates need content determination; once stamped, wrong-repo candidates are skipped by label alone. 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.
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
 
@@ -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: build JQL `project = <KEY> AND Status = "$READY" ORDER BY priority DESC, created ASC`.
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. Confirm the configured Atlassian site by invoking `lisa:atlassian-access` `operation: list-sites` (it enforces connection match against `.lisa.config.json`).
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). Keep the Phase 2 scan broad (it must still see unlabeled tickets so they can be determined and stamped); this gate orders/filters the results.
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.
@@ -311,9 +331,11 @@ If a ready-equivalent status does not exist in the JIRA project's workflow, this
311
331
  | `.lisa.config.json` `jira.workflow.claimed` | `In Progress` | The intermediate status the agent sets on pickup |
312
332
  | `.lisa.config.json` `jira.workflow.done` | env-keyed map (`dev`/`staging`/`production`) or string | The status set after a successful build; env-aware |
313
333
  | `.lisa.config.json` `deploy.branches` | — | Reverse-lookup map for env inference from PR base branch |
334
+ | `.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
335
 
315
336
  ## Rules
316
337
 
338
+ - **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
339
  - **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
340
  - Never transition a ticket the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
319
341
  - 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