@codyswann/lisa 2.97.1 → 2.98.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) 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/commands/repair-intake.md +2 -2
  5. package/plugins/lisa/rules/leaf-only-lifecycle.md +7 -2
  6. package/plugins/lisa/rules/prd-lifecycle-rollup.md +3 -0
  7. package/plugins/lisa/skills/repair-intake/SKILL.md +155 -55
  8. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  9. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  10. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  11. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  12. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  14. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  16. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  18. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  20. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  22. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  24. package/plugins/src/base/commands/repair-intake.md +2 -2
  25. package/plugins/src/base/rules/leaf-only-lifecycle.md +7 -2
  26. package/plugins/src/base/rules/prd-lifecycle-rollup.md +3 -0
  27. package/plugins/src/base/skills/repair-intake/SKILL.md +155 -55
package/package.json CHANGED
@@ -82,7 +82,7 @@
82
82
  "lodash": ">=4.18.1"
83
83
  },
84
84
  "name": "@codyswann/lisa",
85
- "version": "2.97.1",
85
+ "version": "2.98.0",
86
86
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
87
87
  "main": "dist/index.js",
88
88
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
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.97.1",
3
+ "version": "2.98.0",
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
- description: "Repair counterpart to /lisa:intake. Vendor-agnostic batch scanner that finds stuck work — items left in `blocked` or stalled in an in-progress role (build `claimed`, PRD `in_review`) — across the same queues /lisa:intake serves (Notion / Confluence / Linear / GitHub PRDs; JIRA / GitHub / Linear build issues), and attempts to repair the first materially actionable one per cycle: resumes stalled in-progress work in place, re-validates blocked PRDs, and re-dispatches blocked build items whose blockers have cleared. One actionable repair per invocation; cron-safe. Designed as a /schedule target alongside /lisa:intake."
2
+ description: "Repair counterpart to /lisa:intake. Vendor-agnostic batch scanner that finds stuck or half-closed work — items left in `blocked`, stalled in an in-progress role (build `claimed`, PRD `in_review`), terminal-labeled items still natively open, and rollups whose children are all terminal — across the same queues /lisa:intake serves (Notion / Confluence / Linear / GitHub PRDs; JIRA / GitHub / Linear build issues). Repairs every materially actionable candidate inside the `max_candidates` cap: resumes stalled in-progress work in place, re-validates blocked PRDs, re-dispatches blocked build items whose blockers have cleared, performs terminal native closure, and closes out completed rollups. Cron-safe and bounded; default GitHub intake_mode is both and default max_candidates is 100."
3
3
  argument-hint: "<Notion-PRD-database-URL | Confluence-space-URL | Confluence-parent-page-URL | Linear-workspace-URL | Linear-team-URL | GitHub-repo-URL | org/repo | JIRA-project-key | JQL-filter> [intake_mode=prd|build|both] [stale_after=24h] [max_candidates=100] [force=true]"
4
4
  ---
5
5
 
6
- Use the /lisa:repair-intake skill to scan the queue for stuck items (blocked, or stalled in an in-progress role) and repair the first materially actionable one. $ARGUMENTS
6
+ Use the /lisa:repair-intake skill to scan the queue for stuck or half-closed items and repair every materially actionable candidate inside `max_candidates` (default 100). For GitHub queues, default `intake_mode` to `both` when the caller omits it. $ARGUMENTS
@@ -109,7 +109,12 @@ Skills that enforce this invariant or perform rollup cite this rule by slug (the
109
109
  - **Decomposition / write** (`*-to-tracker`, `*-write-*`) — apply the `ready` role to leaves only; never to containers.
110
110
  - **Validate** (`*-validate-*`) — FAIL a container carrying the build-ready role; FAIL a childless Epic/Story/Spike marked build-ready.
111
111
  - **Build intake** (`*-build-intake`, `tracker-build-intake`) — dispatch leaves only; move or safe-block containers with stale build-ready roles according to vendor lifecycle semantics.
112
- - **Rollup** — derive parent state from children per the state machine above.
113
- - **Terminal native closure** (`*-build-intake`, terminal helpers) — after a leaf reaches the true terminal `done` role, finalize it through the provider's native close / complete / resolve mechanism where available; never do this for intermediate env states.
112
+ - **Rollup** — derive parent state from children per the state machine above. `repair-intake`
113
+ also uses this rule to close out parent/container rollups that were left open after every
114
+ required child became terminal.
115
+ - **Terminal native closure** (`*-build-intake`, `repair-intake`, terminal helpers) — after a leaf
116
+ or all-terminal rollup parent reaches the true terminal `done` role, finalize it through the
117
+ provider's native close / complete / resolve mechanism where available; never do this for
118
+ intermediate env states.
114
119
 
115
120
  This is the inverse-direction companion to `repo-scope-split` (which governs a leaf's *repo* scope); together they define what a build-ready leaf work unit is: directly implementable, single-repo, childless-or-leaf-typed.
@@ -146,6 +146,9 @@ Skills that link generated work to a PRD or roll a PRD up cite this rule by slug
146
146
  - **PRD coverage** (`prd-ticket-coverage`) — read the generated top-level child set deterministically from the recorded relationship, not from free-form comments.
147
147
  - **GitHub PRD closure rollup** (`github-prd-intake`) — detect terminal/incomplete/blocked child sets, transition to `prd-shipped`, and close the PRD issue when `prd.rollup.closeOnShipped` is configured. *(LPC-1.3 #583)*
148
148
  - **Linear / Confluence / Notion PRD rollup** (`linear-prd-intake`, `confluence-prd-intake`, `notion-prd-intake`) — mirror the GitHub closure rollup with each vendor's terminal predicate and close/archive support. *(LPC-1.3 #584)*
149
+ - **Repair close-out** (`repair-intake`) — re-run the same generated-top-level-work terminal
150
+ predicate to close out PRDs that were left open after all associated child work became terminal,
151
+ without setting the product-owned `verified` role.
149
152
  - **Idempotency** (all of the above) — dedupe-by-ref linking and no-op already-shipped rollup. *(LPC-1.4 #585)*
150
153
  - **Vendor matrix documentation** — describe native-hierarchy vs documented-link fallback per supported vendor against this contract. *(LPC-1.5 #586)*
151
154
 
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: repair-intake
3
- description: "Vendor-agnostic repair scanner — the recovery counterpart to lisa:intake. Where intake claims `ready` work, repair-intake finds work that got stuck: items left in `blocked`, or stalled in an in-progress role (build `claimed`, PRD `in_review`) after a processing cycle died and nobody picked it back up. Scans the same queues lisa:intake serves (Notion / Confluence / Linear / GitHub PRD databases; JIRA / GitHub / Linear build queues), enumerates stuck candidates, and repairs the first materially actionable one per cycle: resumes stalled in-progress work IN PLACE (build → the vendor agent + the scanner's post-agent transition; PRD → the source `*-to-tracker` dry-run validate→route pipeline), re-validates blocked PRDs when new clarifying answers exist, and re-dispatches blocked build items whose `is blocked by` dependencies have since closed. One actionable repair per invocation, idempotent, loop-protected via a [lisa-repair-intake] marker + state fingerprint + backoff. Never mutates product-owned states (`draft`, `shipped`, `verified`) and never closes PRDs. Designed as a /schedule cron target running alongside lisa:intake."
3
+ description: "Vendor-agnostic repair scanner — the recovery counterpart to lisa:intake. Where intake claims `ready` work, repair-intake finds work that got stuck or was left half-closed: items left in `blocked`, stalled in an in-progress role (build `claimed`, PRD `in_review`), terminal-labeled items that are still natively open, and rollup/container items whose children are all terminal but whose parent is not closed out. Scans the same queues lisa:intake serves (Notion / Confluence / Linear / GitHub PRD databases; JIRA / GitHub / Linear build queues), enumerates candidates up to `max_candidates`, and repairs every materially actionable one in that bounded set: resumes stalled in-progress work IN PLACE (build → the vendor agent + the scanner's post-agent transition; PRD → the source `*-to-tracker` dry-run validate→route pipeline), re-validates blocked PRDs when new clarifying answers exist, re-dispatches blocked build items whose `is blocked by` dependencies have since closed, performs terminal native closure for terminal-labeled items, and closes rollups whose associated child work is fully terminal. Idempotent, loop-protected via a [lisa-repair-intake] marker + state fingerprint + backoff. Never mutates product-owned states (`draft`, `verified`) and never touches `ready` items. Designed as a /schedule cron target running alongside lisa:intake."
4
4
  allowed-tools: ["Skill", "Bash", "Read", "Write", "Edit", "mcp__linear-server__list_teams", "mcp__linear-server__list_projects", "mcp__linear-server__get_project", "mcp__linear-server__save_project", "mcp__linear-server__list_project_labels", "mcp__linear-server__list_issues", "mcp__linear-server__get_issue", "mcp__linear-server__save_issue", "mcp__linear-server__list_comments", "mcp__linear-server__save_comment", "mcp__linear-server__list_issue_labels", "mcp__linear-server__create_issue_label"]
5
5
  ---
6
6
 
7
7
  # Repair Intake: $ARGUMENTS
8
8
 
9
9
  Run one batch-**repair** cycle against the queue identified by `$ARGUMENTS`. Where `lisa:intake`
10
- scans the `ready` role and moves work *forward*, repair-intake scans the **stuck** roles and
11
- moves work *unstuck*:
10
+ scans the `ready` role and moves work *forward*, repair-intake scans the **stuck and
11
+ close-out** roles and moves work *unstuck* or *fully closed*:
12
12
 
13
13
  - **Stalled in-progress** — an item left in an in-progress role (build `claimed`, PRD
14
14
  `in_review`) whose processing cycle died. It is technically "being worked" but nothing is
@@ -18,12 +18,19 @@ moves work *unstuck*:
18
18
  - **Recoverable blocked** — an item in `blocked` whose blocker may now be gone: an
19
19
  `is blocked by` dependency has since closed, clarifying questions have been answered, or
20
20
  research/waiting resolves the ambiguity that stopped it.
21
+ - **Terminal-open drift** — an item already carrying its true terminal lifecycle role (for
22
+ example GitHub `status:done`) but still open/active in the provider's native state.
23
+ - **Completed rollup drift** — a parent/container item (Epic, Story, PRD, Linear Project, or
24
+ equivalent) whose associated child set is fully terminal but whose own lifecycle/native state has
25
+ not been closed out.
21
26
 
22
27
  This skill is the symmetric counterpart to `lisa:intake`. It reuses the same queue-detection,
23
28
  the same agent-team orchestration, the same "don't ask, just run" confirmation policy, and the
24
29
  same per-item surfaces the vendor intakes use (`lisa:<source>-to-tracker` dry-run for PRDs;
25
- `lisa:<tracker>-agent` + the scanner's lifecycle transitions for build) — it differs only in
26
- *which roles it scans* and *that it skips the claim step* (the item is already claimed/blocked).
30
+ `lisa:<tracker>-agent` + the scanner's lifecycle transitions for build) — it differs in *which
31
+ roles it scans* and, for stalled/blocked work, *that it skips the claim step* (the item is already
32
+ claimed/blocked). Close-out candidates do not dispatch agents; they only reconcile terminal
33
+ lifecycle state with provider-native closure and rollup state.
27
34
 
28
35
  ## Public contract
29
36
 
@@ -34,9 +41,9 @@ same per-item surfaces the vendor intakes use (`lisa:<source>-to-tracker` dry-ru
34
41
  | Token | Meaning | Default |
35
42
  |-------|---------|---------|
36
43
  | `<queue>` | Same queue identifier `lisa:intake` accepts (see Source dispatch). Required. | — |
37
- | `intake_mode` | `prd` \| `build` \| `both`. Only meaningful for a GitHub `org/repo` (or bare `github`) that hosts both PRD and build label namespaces. `both` is unique to repair — a repair sweep usefully covers both lifecycles in one schedule. Absent → prefer PRD when both namespaces exist, else whichever exists (matches `lisa:intake`). | (infer) |
44
+ | `intake_mode` | `prd` \| `build` \| `both`. Only meaningful for a GitHub `org/repo` (or bare `github`) that hosts both PRD and build label namespaces. `both` is unique to repair — a repair sweep usefully covers both lifecycles in one schedule. Absent → `both` when both namespaces exist, else whichever lifecycle exists. | `both` for dual GitHub queues; otherwise infer |
38
45
  | `stale_after` | How long with no observable activity before an in-progress item counts as stalled. Accepts `24h`, `90m`, `2d`, or `0` (treat any in-progress item as stalled — manual recovery, also the only way to resume work on a provider that exposes no reliable timestamp). Overrides config. | `24h` |
39
- | `max_candidates` | Cap on how many stuck items to enumerate while looking for the first actionable one. Bounds scan cost. Overrides config. | `100` |
46
+ | `max_candidates` | Cap on how many stuck/close-out candidates to enumerate and evaluate. Repair every materially actionable candidate within this bounded set, then stop. Overrides config. | `100` |
40
47
  | `force` | `true` bypasses the loop-prevention backoff window (so a manual re-run re-attempts items even if their fingerprint is unchanged). It does **not** change the staleness rule — use `stale_after=0` for that. | `false` |
41
48
 
42
49
  ## Confirmation policy
@@ -54,8 +61,9 @@ Specifically forbidden:
54
61
  - Pausing because many items are stuck, an item looks complex, or a repair is likely to land
55
62
  the item back in `blocked`. Returning an item to `blocked` with a current, accurate note is a
56
63
  valid outcome of the repair lifecycle, not a failure.
57
- - Pausing because a re-dispatch looks expensive. The cost of one cycle is bounded (one
58
- actionable repair); the cost of stalling a scheduled cron waiting on a human is unbounded.
64
+ - Pausing because a re-dispatch looks expensive. The cost of one cycle is bounded by
65
+ `max_candidates` and the actionable subset inside that cap; the cost of stalling a scheduled
66
+ cron waiting on a human is unbounded.
59
67
 
60
68
  The only legitimate reasons to stop early:
61
69
 
@@ -63,7 +71,8 @@ The only legitimate reasons to stop early:
63
71
  missing value and exit.
64
72
  - The queue itself is misconfigured (Status property missing expected values, JIRA workflow
65
73
  can't reach required transitions). Surface and exit.
66
- - No stuck items, or none actionable this cycle. Exit cleanly with the idle-case summary.
74
+ - No stuck/close-out candidates, or none actionable this cycle. Exit cleanly with the idle-case
75
+ summary.
67
76
 
68
77
  ## Orchestration: agent team
69
78
 
@@ -107,16 +116,16 @@ rules as `lisa:intake`** — read that skill's "Source dispatch" section for the
107
116
  table; the detection is identical and only the per-item action changes (repair instead of
108
117
  claim-and-advance). The essentials, inlined here so this skill is self-complete:
109
118
 
110
- | If `$ARGUMENTS` is... | Queue / lifecycle | Source/tracker key | Stuck roles repaired |
119
+ | If `$ARGUMENTS` is... | Queue / lifecycle | Source/tracker key | Candidates repaired |
111
120
  |------------------------|-------------------|--------------------|----------------------|
112
- | Notion **database** URL/ID | PRD (Notion) | source=notion | `in_review`, `blocked` |
113
- | Confluence **space** URL/key | PRD (Confluence) | source=confluence | `in_review`, `blocked` (parent-page roles) |
114
- | Confluence **parent page** URL/ID | PRD (Confluence, narrowed) | source=confluence | `in_review`, `blocked` |
115
- | Linear **workspace** URL, **team** URL/key, or literal `linear` | PRD (Linear) | source=linear | `in_review`, `blocked` (project labels) |
116
- | GitHub **repo** URL / `org/repo` (PRD namespace) | PRD (GitHub) | source=github | `in_review`, `blocked` (PRD labels) |
117
- | GitHub **repo** URL / `org/repo` with `tracker = github` (build namespace) | Build (GitHub) | tracker=github | `claimed`, `blocked` (build labels) |
121
+ | Notion **database** URL/ID | PRD (Notion) | source=notion | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
122
+ | Confluence **space** URL/key | PRD (Confluence) | source=confluence | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
123
+ | Confluence **parent page** URL/ID | PRD (Confluence, narrowed) | source=confluence | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
124
+ | Linear **workspace** URL, **team** URL/key, or literal `linear` | PRD (Linear) | source=linear | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
125
+ | GitHub **repo** URL / `org/repo` (PRD namespace) | PRD (GitHub) | source=github | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
126
+ | GitHub **repo** URL / `org/repo` with `tracker = github` (build namespace) | Build (GitHub) | tracker=github | `claimed`, `blocked`, terminal/open issues, all-terminal parent rollups |
118
127
  | Literal `github` | GitHub; route by `intake_mode` (`prd` / `build` / `both`) | per lifecycle | per lifecycle above |
119
- | JIRA project key or full JQL | Build (JIRA) | tracker=jira | `claimed`, `blocked` (statuses) |
128
+ | JIRA project key or full JQL | Build (JIRA) | tracker=jira | `claimed`, `blocked`, terminal/closure verification, all-terminal parent rollups |
120
129
 
121
130
  Disambiguation (same as `lisa:intake`): a `notion.so`/`notion.site` URL → Notion; an Atlassian
122
131
  `/wiki/spaces/<KEY>` URL → Confluence (with `/pages/<id>` → parent-page narrowing); a
@@ -127,17 +136,17 @@ URL is out of scope** — this skill is batch-only; repair one item by hand via
127
136
  (build) or by re-running `lisa:<source>-to-tracker` (PRD).
128
137
 
129
138
  Role names for every vendor are resolved from `.lisa.config.json` per the `config-resolution`
130
- rule — never hardcode status/label strings. The relevant stuck roles:
131
-
132
- | Lifecycle | Vendor | In-progress role key | Blocked role key |
133
- |-----------|--------|----------------------|------------------|
134
- | Build | JIRA | `jira.workflow.claimed` (`In Progress`) | `jira.workflow.blocked` (`Blocked`) |
135
- | Build | GitHub | `github.labels.build.claimed` (`status:in-progress`) | `github.labels.build.blocked` (`status:blocked`) |
136
- | Build | Linear | `linear.labels.build.claimed` (`status:in-progress`) | `linear.labels.build.blocked` (`status:blocked`) |
137
- | PRD | Notion | `notion.values.in_review` (`In Review`) | `notion.values.blocked` (`Blocked`) |
138
- | PRD | GitHub | `github.labels.prd.in_review` (`prd-in-review`) | `github.labels.prd.blocked` (`prd-blocked`) |
139
- | PRD | Linear | `linear.labels.prd.in_review` (`prd-in-review`) | `linear.labels.prd.blocked` (`prd-blocked`) |
140
- | PRD | Confluence | `confluence.parents.in_review` (page id) | `confluence.parents.blocked` (page id) |
139
+ rule — never hardcode status/label strings. The relevant repair roles:
140
+
141
+ | Lifecycle | Vendor | In-progress role key | Blocked role key | Terminal / rollup role key |
142
+ |-----------|--------|----------------------|------------------|----------------------------|
143
+ | Build | JIRA | `jira.workflow.claimed` (`In Progress`) | `jira.workflow.blocked` (`Blocked`) | env-resolved `jira.workflow.done` |
144
+ | Build | GitHub | `github.labels.build.claimed` (`status:in-progress`) | `github.labels.build.blocked` (`status:blocked`) | env-resolved `github.labels.build.done` (`status:done`) |
145
+ | Build | Linear | `linear.labels.build.claimed` (`status:in-progress`) | `linear.labels.build.blocked` (`status:blocked`) | env-resolved `linear.labels.build.done` (`status:done`) |
146
+ | PRD | Notion | `notion.values.in_review` (`In Review`) | `notion.values.blocked` (`Blocked`) | `notion.values.shipped` (`Shipped`) |
147
+ | PRD | GitHub | `github.labels.prd.in_review` (`prd-in-review`) | `github.labels.prd.blocked` (`prd-blocked`) | `github.labels.prd.shipped` (`prd-shipped`) |
148
+ | PRD | Linear | `linear.labels.prd.in_review` (`prd-in-review`) | `linear.labels.prd.blocked` (`prd-blocked`) | `linear.labels.prd.shipped` (`prd-shipped`) |
149
+ | PRD | Confluence | `confluence.parents.in_review` (page id) | `confluence.parents.blocked` (page id) | `confluence.parents.shipped` (page id) |
141
150
 
142
151
  Resolve with the standard role-read pattern (local overrides global, default fallback):
143
152
 
@@ -159,13 +168,13 @@ BLOCKED=$(read_role '.github.labels.build.blocked' 'status:blocked')
159
168
  repair-intake stays vendor-neutral; concrete reads/writes go through the same layers the vendor
160
169
  intakes use. Never call Atlassian MCP or `acli` directly — go through `lisa:atlassian-access`.
161
170
 
162
- | Vendor | Reads (scan / comments / links) | Writes (transition / comment) | Re-dispatch / re-validate |
171
+ | Vendor | Reads (scan / comments / links) | Writes (transition / comment / close-out) | Re-dispatch / re-validate |
163
172
  |--------|---------------------------------|-------------------------------|---------------------------|
164
173
  | JIRA (build) | `lisa:atlassian-access` `search-issues` / `lisa:jira-read-ticket` | `lisa:atlassian-access` `transition` / `comment` | `lisa:jira-agent` |
165
- | GitHub (build) | `gh issue list` / `gh issue view --json` / `gh pr list` | `gh issue edit` (labels) / `gh issue comment` | `lisa:github-agent` |
174
+ | GitHub (build) | `gh issue list` / `gh issue view --json` / `gh pr list` / GraphQL sub-issues | `gh issue edit` (labels) / `gh issue comment` / `gh issue close --reason completed` | `lisa:github-agent` |
166
175
  | Linear (build) | Linear MCP `list_issues` / `get_issue` / `list_comments` | Linear MCP `save_issue` (labels) / `save_comment` | `lisa:linear-agent` |
167
176
  | Notion (PRD) | `lisa:notion-access` (`query`, page comments) | `lisa:notion-access` `write-page` (status) / page comment | `lisa:notion-to-tracker` (dry-run) |
168
- | GitHub (PRD) | `gh issue list/view` (PRD labels) | `gh issue edit` / `gh issue comment` | `lisa:github-to-tracker` (dry-run) |
177
+ | GitHub (PRD) | `gh issue list/view` (PRD labels) / GraphQL sub-issues / generated-work section | `gh issue edit` / `gh issue comment` / `gh issue close --reason completed` | `lisa:github-to-tracker` (dry-run) |
169
178
  | Linear (PRD) | Linear MCP `list_projects` / `get_project` (+ sentinel feedback issue) | Linear MCP `save_project` (labels) / `save_comment` | `lisa:linear-to-tracker` (dry-run) |
170
179
  | Confluence (PRD) | `lisa:atlassian-access` CQL | `lisa:atlassian-access` page `parentId` update / comment | `lisa:confluence-to-tracker` (dry-run) |
171
180
 
@@ -207,9 +216,10 @@ proceeds — it is judged on blocker state, not time.)
207
216
 
208
217
  ## Repair decision tree
209
218
 
210
- Apply per candidate. Stop the cycle after the **first** candidate that triggers a write
211
- (lifecycle transition, re-dispatch, or refreshed note). Everything examined before it is
212
- recorded read-only.
219
+ Apply per candidate. Continue through the ordered list until every candidate inside the
220
+ `max_candidates` cap has been evaluated. Each candidate may trigger a write (lifecycle transition,
221
+ native close/archive/complete, re-dispatch, or refreshed note), be recorded read-only, or be
222
+ recorded under Errors. Do not stop after the first write; the cap is the batch boundary.
213
223
 
214
224
  ### Build `claimed` (stalled in-progress) → resume in place
215
225
 
@@ -244,6 +254,45 @@ skipping the claim transition (the item is already `claimed`):
244
254
  4. Else → still blocked. Refresh the note with the current reason (Loop prevention) and leave it
245
255
  `blocked`.
246
256
 
257
+ ### Build terminal-open → native close / complete / resolve
258
+
259
+ For each build item that already carries the env-resolved true terminal `done` role but is still
260
+ native-open / active / unresolved:
261
+
262
+ 1. Verify the item is a **leaf** or a **rollup parent whose all required children are terminal**.
263
+ If it is a parent with incomplete children, do not close it; refresh a `[lisa-repair-intake]`
264
+ note naming the incomplete child set.
265
+ 2. Verify the terminal `done` role is the true final value per `leaf-only-lifecycle` and
266
+ `config-resolution` env-keyed `done`. Intermediate env labels (for example `status:on-dev` or
267
+ `status:on-stg`) are not terminal and must stay open.
268
+ 3. Perform the provider-native terminal action idempotently:
269
+ - GitHub: `gh issue close <number> --repo <org>/<repo> --reason completed`.
270
+ - Linear: move the issue to the configured Done / Completed native workflow state if available;
271
+ otherwise record the missing native state as a setup error.
272
+ - JIRA: verify it is resolved / closed (`statusCategory = Done`, resolution set if required);
273
+ if not, transition through the configured terminal workflow path or report the missing setup.
274
+ 4. Post a compact `[lisa-repair-intake]` note only when the native close-out changed state or when
275
+ an actionable setup error must be surfaced. Do not spam already-closed terminal items.
276
+
277
+ ### Build rollup with all children terminal → close out parent/container
278
+
279
+ For each parent/container item (Epic, Story, Spike, Project, or any item with child work) whose
280
+ required child set is fully terminal:
281
+
282
+ 1. Read the child set using the vendor-native hierarchy first (GitHub sub-issues, JIRA
283
+ Epic/parent/sub-task hierarchy, Linear project/parent/sub-issues), with the same fallbacks the
284
+ vendor read/sync skills document.
285
+ 2. Evaluate bottom-up per `leaf-only-lifecycle`: every required child must already be terminal.
286
+ Optional / won't-do / not-planned children are terminal-but-dropped and do not hold the parent
287
+ open.
288
+ 3. Apply the configured terminal rollup role to the parent/container, removing any stale build
289
+ lifecycle role that conflicts.
290
+ 4. Immediately perform terminal native closure where the provider supports it (GitHub close,
291
+ Linear complete, JIRA resolved/closed). A completed rollup parent should not remain open in
292
+ GitHub merely because no leaf agent touched it.
293
+ 5. If any required child is incomplete, active, blocked, or inaccessible, leave the parent open and
294
+ record it as `still_blocked` or `active` with the current child tally.
295
+
247
296
  ### PRD `in_review` (stalled in-progress) → re-run validate→route
248
297
 
249
298
  After the staleness gate passes, run the **same dry-run validate→route pipeline the vendor PRD
@@ -276,6 +325,40 @@ intake runs per item**, targeted at this single PRD and **skipping the claim** (
276
325
  3. If no new answers and no dependency change → leave `blocked` untouched (subject to the
277
326
  backoff window — do not re-post an identical note).
278
327
 
328
+ ### PRD terminal-open → close / archive source artifact
329
+
330
+ For each PRD source artifact that already carries the configured terminal source role (`shipped`
331
+ for generated-work completion, or a source-specific terminal role that the configured PRD source
332
+ declares closed-out) but is still native-open / active:
333
+
334
+ 1. Verify the PRD's generated top-level work is terminal per `prd-lifecycle-rollup`, unless the
335
+ source artifact is already in a stronger product-owned terminal role that explicitly permits
336
+ closure. Do not move a PRD out of `draft` or `verified`.
337
+ 2. Close or archive through the source vendor's native mechanism where one exists:
338
+ - GitHub: close the PRD issue with `--reason completed`.
339
+ - Linear: archive/close the PRD project through Linear MCP when supported by the workspace.
340
+ - Confluence/Notion: archive the page only when the access layer exposes a supported archival
341
+ action; otherwise record a capability-aware no-op.
342
+ 3. Never set `verified`; `/lisa:verify-prd` remains the only automated writer of the verified
343
+ role. This path only reconciles an already-terminal PRD with native closure.
344
+
345
+ ### PRD rollup with all generated work terminal → ship and close out
346
+
347
+ For each PRD in `ticketed` or another non-product-owned open PRD role whose generated top-level
348
+ work is fully terminal:
349
+
350
+ 1. Read the generated top-level child set exactly as `prd-ticket-coverage` / the vendor PRD intake
351
+ does: native PRD children where supported, plus the durable generated-work section fallback.
352
+ 2. Evaluate terminal state using `prd-lifecycle-rollup`'s vendor predicate. A generated Epic or
353
+ Story is terminal only when it has itself rolled up and closed out; do not re-derive its leaf
354
+ descendants directly when its own state is still open.
355
+ 3. Transition the PRD to the configured `shipped` role.
356
+ 4. Close/archive the PRD source artifact through the vendor-native close-out mechanism where
357
+ supported. This repair path is the explicit close-out sweep for PRDs whose child work is done;
358
+ it does not set `verified` and does not run `/lisa:verify-prd`.
359
+ 5. If generated work is missing, ambiguous, or partially incomplete, leave the PRD open and report
360
+ the incomplete child set. Never close a PRD on partial completion.
361
+
279
362
  ## Dependency clearing (conservative, vendor-specific extraction)
280
363
 
281
364
  `lisa:tracker-read` is a thin dispatcher that returns each vendor's bundle **verbatim** — there
@@ -308,7 +391,7 @@ cron tick.
308
391
 
309
392
  - Every note this skill writes is prefixed `[lisa-repair-intake]` and carries a compact **state
310
393
  fingerprint**: the lifecycle role, the set of blocker refs + their observed states, the
311
- validation verdict (PASS/FAIL), and a timestamp.
394
+ validation verdict (PASS/FAIL), terminal/open state, rollup child tally, and a timestamp.
312
395
  - Before writing a note or re-attempting a `blocked` item, compute the current fingerprint. If
313
396
  an identical fingerprint was already posted within the **backoff window**, skip the item
314
397
  silently (record as `still_blocked` / `active`, no write).
@@ -318,18 +401,24 @@ cron tick.
318
401
 
319
402
  ## Lifecycle ownership guard
320
403
 
321
- repair-intake owns ONLY the four stuck roles: build `claimed` / `blocked` and PRD `in_review` /
322
- `blocked`, plus the transitions a stuck item legitimately moves through during recovery. It MAY:
404
+ repair-intake owns the repair surfaces needed to recover stuck work and close-out drift:
405
+ build `claimed` / `blocked`, PRD `in_review` / `blocked`, terminal-labeled native-open items, and
406
+ parent/container rollups whose child sets are already terminal. It MAY:
323
407
 
324
408
  - Apply the build scanner's post-agent `claimed → done` on a successful resume (it is finishing
325
409
  the scanner's interrupted job), and move a dependency-cleared build item `blocked → claimed`.
326
410
  - Move a re-validated PRD `in_review`/`blocked → ticketed` (PASS) or `→ blocked` (FAIL), exactly
327
411
  as the PRD intake does.
412
+ - Close / complete / resolve build items that already carry the true terminal `done` role but are
413
+ still natively open, per `leaf-only-lifecycle`.
414
+ - Roll up a parent/container to the configured terminal state and close/complete/resolve it when
415
+ all required children are terminal.
416
+ - Move a PRD with fully terminal generated work to `shipped` and close/archive the source artifact
417
+ where the source vendor supports native close-out, per `prd-lifecycle-rollup`.
328
418
 
329
419
  It MUST NOT:
330
420
 
331
- - Move a PRD out of `draft`, `shipped`, or `verified`, or close/archive any PRD (those are
332
- product- and rollup-owned — see `prd-lifecycle-rollup`).
421
+ - Move a PRD out of `draft` or `verified` (those are product-owned), or set `verified` itself.
333
422
  - Apply a build `done` value other than via the env-resolution rules, or close a native item at
334
423
  any value other than the true terminal `done` (see `leaf-only-lifecycle`).
335
424
  - Touch `ready` items (that is `lisa:intake`'s lane).
@@ -338,23 +427,27 @@ It MUST NOT:
338
427
 
339
428
  1. **Resolve the queue** — detect vendor/lifecycle (Source dispatch); resolve stuck role names
340
429
  from config. For JIRA, confirm the needed transitions are reachable; stop on misconfig.
341
- 2. **Enumerate stuck candidates** — query the in-progress role(s) and the `blocked` role for the
342
- detected lifecycle(s), up to `max_candidates`, via the Access layer reads.
430
+ 2. **Enumerate repair candidates** — query in-progress role(s), `blocked` role(s), terminal/open
431
+ items, and rollup parents/PRDs with child work for the detected lifecycle(s), up to
432
+ `max_candidates`, via the Access layer reads.
343
433
  3. **Order deterministically**, highest repair-confidence first:
344
- 1. `blocked` items whose dependencies are now **cleared** (safe, high-value, one-cycle wins),
345
- 2. `blocked` items with **new clarifying answers**,
346
- 3. **stalled** in-progress items, oldest activity first.
347
- 4. **Walk the ordered list**, evaluating each read-only (staleness, dependency, answer checks),
348
- and **repair the first candidate that is actionable** per the decision tree. Stop after that
349
- one write-producing repair.
434
+ 1. terminal-labeled items that only need native close / complete / resolve,
435
+ 2. rollup parents/PRDs whose child sets are all terminal,
436
+ 3. `blocked` items whose dependencies are now **cleared** (safe, high-value, one-cycle wins),
437
+ 4. `blocked` items with **new clarifying answers**,
438
+ 5. **stalled** in-progress items, oldest activity first.
439
+ 4. **Walk the ordered list**, evaluating each candidate (terminal close-out, rollup child tally,
440
+ staleness, dependency, answer checks), and repair **every** candidate that is actionable inside
441
+ the `max_candidates` cap. Continue after successful writes and after per-item errors.
350
442
  5. **Empty / nothing actionable** → exit cleanly:
351
443
  `"No stuck items actionable this cycle (examined N, all active or in backoff)."`
352
444
  6. **Failure isolation** — if evaluating one candidate errors, record it under Errors and
353
445
  continue to the next; one bad item never aborts the cycle.
354
446
 
355
- Process **at most one materially actionable repair per invocation** — scan many, repair one,
356
- exit. This matches `lisa:intake`'s one-item-per-cycle contract: bounded cost, low race risk, no
357
- surprise burst of PRs. Throughput is the scheduler's job, not one invocation's.
447
+ Process **all materially actionable repairs among the enumerated candidates** — scan up to
448
+ `max_candidates`, repair the actionable subset, then exit. This intentionally differs from
449
+ `lisa:intake`'s one-ready-item claim contract because repair work is bounded by an explicit cap and
450
+ often consists of cheap close-out reconciliation that should drain in one cron pass.
358
451
 
359
452
  ## Summary report
360
453
 
@@ -363,11 +456,16 @@ Report outcomes in these buckets:
363
456
  - `resumed` — stalled in-progress work re-dispatched in place.
364
457
  - `unblocked` — blocker cleared (or answers resolved); re-dispatched or transitioned to
365
458
  `ticketed`.
459
+ - `closed_out` — terminal-labeled items whose native open/active state was closed, completed,
460
+ resolved, or archived.
461
+ - `rolled_up` — parent/container/PRD rollups advanced because all associated children were
462
+ terminal.
366
463
  - `still_blocked` — examined and intentionally left `blocked`, with the active reason.
367
464
  - `active` — skipped because current work is not stale (or within backoff).
368
465
  - `errors` — items that failed evaluation, with the error.
369
466
 
370
- State the single item repaired this cycle (if any) and what action was taken.
467
+ State every item repaired this cycle and the action taken. If the output would be long, group by
468
+ bucket and show compact refs plus counts.
371
469
 
372
470
  ## Schedule examples
373
471
 
@@ -388,13 +486,15 @@ stuck work accumulates), or interleaved on a longer cadence.
388
486
 
389
487
  - Never run a cycle without an explicit queue. Side effects too high to default.
390
488
  - Never reset stalled in-progress items to `ready` — resume in place (decision tree).
391
- - Never mutate product-owned states (`draft`, `shipped`, `verified`) or close PRDs (Lifecycle
392
- ownership guard).
489
+ - Never mutate product-owned states (`draft`, `verified`) or set `verified`; PRD rollup close-out
490
+ may move open generated-work PRDs to `shipped` and close/archive them only after all associated
491
+ child work is terminal.
393
492
  - Apply build `done` ONLY via the env-resolution rules, and trigger native closure only at the
394
493
  true terminal `done` value (`leaf-only-lifecycle`).
395
494
  - Never re-dispatch a `blocked` build item unless every parsed blocker is cleared (conservative
396
495
  dependency clearing).
397
- - One materially actionable repair per cycle. Stop after the first write-producing repair.
496
+ - Repair every materially actionable candidate inside the `max_candidates` cap; default cap is 100.
497
+ - Default GitHub `intake_mode` is `both` when both PRD and build namespaces exist.
398
498
  - Honor the backoff window — never re-post an identical `[lisa-repair-intake]` note within it
399
499
  (unless `force=true`).
400
500
  - Never run two repair cycles concurrently against overlapping queues, and never run
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
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.97.1",
3
+ "version": "2.98.0",
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-expo",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
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.97.1",
3
+ "version": "2.98.0",
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-harper-fabric",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
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.97.1",
3
+ "version": "2.98.0",
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-nestjs",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
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-openclaw",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint 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.97.1",
3
+ "version": "2.98.0",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.97.1",
3
+ "version": "2.98.0",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  ---
2
- description: "Repair counterpart to /lisa:intake. Vendor-agnostic batch scanner that finds stuck work — items left in `blocked` or stalled in an in-progress role (build `claimed`, PRD `in_review`) — across the same queues /lisa:intake serves (Notion / Confluence / Linear / GitHub PRDs; JIRA / GitHub / Linear build issues), and attempts to repair the first materially actionable one per cycle: resumes stalled in-progress work in place, re-validates blocked PRDs, and re-dispatches blocked build items whose blockers have cleared. One actionable repair per invocation; cron-safe. Designed as a /schedule target alongside /lisa:intake."
2
+ description: "Repair counterpart to /lisa:intake. Vendor-agnostic batch scanner that finds stuck or half-closed work — items left in `blocked`, stalled in an in-progress role (build `claimed`, PRD `in_review`), terminal-labeled items still natively open, and rollups whose children are all terminal — across the same queues /lisa:intake serves (Notion / Confluence / Linear / GitHub PRDs; JIRA / GitHub / Linear build issues). Repairs every materially actionable candidate inside the `max_candidates` cap: resumes stalled in-progress work in place, re-validates blocked PRDs, re-dispatches blocked build items whose blockers have cleared, performs terminal native closure, and closes out completed rollups. Cron-safe and bounded; default GitHub intake_mode is both and default max_candidates is 100."
3
3
  argument-hint: "<Notion-PRD-database-URL | Confluence-space-URL | Confluence-parent-page-URL | Linear-workspace-URL | Linear-team-URL | GitHub-repo-URL | org/repo | JIRA-project-key | JQL-filter> [intake_mode=prd|build|both] [stale_after=24h] [max_candidates=100] [force=true]"
4
4
  ---
5
5
 
6
- Use the /lisa:repair-intake skill to scan the queue for stuck items (blocked, or stalled in an in-progress role) and repair the first materially actionable one. $ARGUMENTS
6
+ Use the /lisa:repair-intake skill to scan the queue for stuck or half-closed items and repair every materially actionable candidate inside `max_candidates` (default 100). For GitHub queues, default `intake_mode` to `both` when the caller omits it. $ARGUMENTS
@@ -109,7 +109,12 @@ Skills that enforce this invariant or perform rollup cite this rule by slug (the
109
109
  - **Decomposition / write** (`*-to-tracker`, `*-write-*`) — apply the `ready` role to leaves only; never to containers.
110
110
  - **Validate** (`*-validate-*`) — FAIL a container carrying the build-ready role; FAIL a childless Epic/Story/Spike marked build-ready.
111
111
  - **Build intake** (`*-build-intake`, `tracker-build-intake`) — dispatch leaves only; move or safe-block containers with stale build-ready roles according to vendor lifecycle semantics.
112
- - **Rollup** — derive parent state from children per the state machine above.
113
- - **Terminal native closure** (`*-build-intake`, terminal helpers) — after a leaf reaches the true terminal `done` role, finalize it through the provider's native close / complete / resolve mechanism where available; never do this for intermediate env states.
112
+ - **Rollup** — derive parent state from children per the state machine above. `repair-intake`
113
+ also uses this rule to close out parent/container rollups that were left open after every
114
+ required child became terminal.
115
+ - **Terminal native closure** (`*-build-intake`, `repair-intake`, terminal helpers) — after a leaf
116
+ or all-terminal rollup parent reaches the true terminal `done` role, finalize it through the
117
+ provider's native close / complete / resolve mechanism where available; never do this for
118
+ intermediate env states.
114
119
 
115
120
  This is the inverse-direction companion to `repo-scope-split` (which governs a leaf's *repo* scope); together they define what a build-ready leaf work unit is: directly implementable, single-repo, childless-or-leaf-typed.
@@ -146,6 +146,9 @@ Skills that link generated work to a PRD or roll a PRD up cite this rule by slug
146
146
  - **PRD coverage** (`prd-ticket-coverage`) — read the generated top-level child set deterministically from the recorded relationship, not from free-form comments.
147
147
  - **GitHub PRD closure rollup** (`github-prd-intake`) — detect terminal/incomplete/blocked child sets, transition to `prd-shipped`, and close the PRD issue when `prd.rollup.closeOnShipped` is configured. *(LPC-1.3 #583)*
148
148
  - **Linear / Confluence / Notion PRD rollup** (`linear-prd-intake`, `confluence-prd-intake`, `notion-prd-intake`) — mirror the GitHub closure rollup with each vendor's terminal predicate and close/archive support. *(LPC-1.3 #584)*
149
+ - **Repair close-out** (`repair-intake`) — re-run the same generated-top-level-work terminal
150
+ predicate to close out PRDs that were left open after all associated child work became terminal,
151
+ without setting the product-owned `verified` role.
149
152
  - **Idempotency** (all of the above) — dedupe-by-ref linking and no-op already-shipped rollup. *(LPC-1.4 #585)*
150
153
  - **Vendor matrix documentation** — describe native-hierarchy vs documented-link fallback per supported vendor against this contract. *(LPC-1.5 #586)*
151
154
 
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: repair-intake
3
- description: "Vendor-agnostic repair scanner — the recovery counterpart to lisa:intake. Where intake claims `ready` work, repair-intake finds work that got stuck: items left in `blocked`, or stalled in an in-progress role (build `claimed`, PRD `in_review`) after a processing cycle died and nobody picked it back up. Scans the same queues lisa:intake serves (Notion / Confluence / Linear / GitHub PRD databases; JIRA / GitHub / Linear build queues), enumerates stuck candidates, and repairs the first materially actionable one per cycle: resumes stalled in-progress work IN PLACE (build → the vendor agent + the scanner's post-agent transition; PRD → the source `*-to-tracker` dry-run validate→route pipeline), re-validates blocked PRDs when new clarifying answers exist, and re-dispatches blocked build items whose `is blocked by` dependencies have since closed. One actionable repair per invocation, idempotent, loop-protected via a [lisa-repair-intake] marker + state fingerprint + backoff. Never mutates product-owned states (`draft`, `shipped`, `verified`) and never closes PRDs. Designed as a /schedule cron target running alongside lisa:intake."
3
+ description: "Vendor-agnostic repair scanner — the recovery counterpart to lisa:intake. Where intake claims `ready` work, repair-intake finds work that got stuck or was left half-closed: items left in `blocked`, stalled in an in-progress role (build `claimed`, PRD `in_review`), terminal-labeled items that are still natively open, and rollup/container items whose children are all terminal but whose parent is not closed out. Scans the same queues lisa:intake serves (Notion / Confluence / Linear / GitHub PRD databases; JIRA / GitHub / Linear build queues), enumerates candidates up to `max_candidates`, and repairs every materially actionable one in that bounded set: resumes stalled in-progress work IN PLACE (build → the vendor agent + the scanner's post-agent transition; PRD → the source `*-to-tracker` dry-run validate→route pipeline), re-validates blocked PRDs when new clarifying answers exist, re-dispatches blocked build items whose `is blocked by` dependencies have since closed, performs terminal native closure for terminal-labeled items, and closes rollups whose associated child work is fully terminal. Idempotent, loop-protected via a [lisa-repair-intake] marker + state fingerprint + backoff. Never mutates product-owned states (`draft`, `verified`) and never touches `ready` items. Designed as a /schedule cron target running alongside lisa:intake."
4
4
  allowed-tools: ["Skill", "Bash", "Read", "Write", "Edit", "mcp__linear-server__list_teams", "mcp__linear-server__list_projects", "mcp__linear-server__get_project", "mcp__linear-server__save_project", "mcp__linear-server__list_project_labels", "mcp__linear-server__list_issues", "mcp__linear-server__get_issue", "mcp__linear-server__save_issue", "mcp__linear-server__list_comments", "mcp__linear-server__save_comment", "mcp__linear-server__list_issue_labels", "mcp__linear-server__create_issue_label"]
5
5
  ---
6
6
 
7
7
  # Repair Intake: $ARGUMENTS
8
8
 
9
9
  Run one batch-**repair** cycle against the queue identified by `$ARGUMENTS`. Where `lisa:intake`
10
- scans the `ready` role and moves work *forward*, repair-intake scans the **stuck** roles and
11
- moves work *unstuck*:
10
+ scans the `ready` role and moves work *forward*, repair-intake scans the **stuck and
11
+ close-out** roles and moves work *unstuck* or *fully closed*:
12
12
 
13
13
  - **Stalled in-progress** — an item left in an in-progress role (build `claimed`, PRD
14
14
  `in_review`) whose processing cycle died. It is technically "being worked" but nothing is
@@ -18,12 +18,19 @@ moves work *unstuck*:
18
18
  - **Recoverable blocked** — an item in `blocked` whose blocker may now be gone: an
19
19
  `is blocked by` dependency has since closed, clarifying questions have been answered, or
20
20
  research/waiting resolves the ambiguity that stopped it.
21
+ - **Terminal-open drift** — an item already carrying its true terminal lifecycle role (for
22
+ example GitHub `status:done`) but still open/active in the provider's native state.
23
+ - **Completed rollup drift** — a parent/container item (Epic, Story, PRD, Linear Project, or
24
+ equivalent) whose associated child set is fully terminal but whose own lifecycle/native state has
25
+ not been closed out.
21
26
 
22
27
  This skill is the symmetric counterpart to `lisa:intake`. It reuses the same queue-detection,
23
28
  the same agent-team orchestration, the same "don't ask, just run" confirmation policy, and the
24
29
  same per-item surfaces the vendor intakes use (`lisa:<source>-to-tracker` dry-run for PRDs;
25
- `lisa:<tracker>-agent` + the scanner's lifecycle transitions for build) — it differs only in
26
- *which roles it scans* and *that it skips the claim step* (the item is already claimed/blocked).
30
+ `lisa:<tracker>-agent` + the scanner's lifecycle transitions for build) — it differs in *which
31
+ roles it scans* and, for stalled/blocked work, *that it skips the claim step* (the item is already
32
+ claimed/blocked). Close-out candidates do not dispatch agents; they only reconcile terminal
33
+ lifecycle state with provider-native closure and rollup state.
27
34
 
28
35
  ## Public contract
29
36
 
@@ -34,9 +41,9 @@ same per-item surfaces the vendor intakes use (`lisa:<source>-to-tracker` dry-ru
34
41
  | Token | Meaning | Default |
35
42
  |-------|---------|---------|
36
43
  | `<queue>` | Same queue identifier `lisa:intake` accepts (see Source dispatch). Required. | — |
37
- | `intake_mode` | `prd` \| `build` \| `both`. Only meaningful for a GitHub `org/repo` (or bare `github`) that hosts both PRD and build label namespaces. `both` is unique to repair — a repair sweep usefully covers both lifecycles in one schedule. Absent → prefer PRD when both namespaces exist, else whichever exists (matches `lisa:intake`). | (infer) |
44
+ | `intake_mode` | `prd` \| `build` \| `both`. Only meaningful for a GitHub `org/repo` (or bare `github`) that hosts both PRD and build label namespaces. `both` is unique to repair — a repair sweep usefully covers both lifecycles in one schedule. Absent → `both` when both namespaces exist, else whichever lifecycle exists. | `both` for dual GitHub queues; otherwise infer |
38
45
  | `stale_after` | How long with no observable activity before an in-progress item counts as stalled. Accepts `24h`, `90m`, `2d`, or `0` (treat any in-progress item as stalled — manual recovery, also the only way to resume work on a provider that exposes no reliable timestamp). Overrides config. | `24h` |
39
- | `max_candidates` | Cap on how many stuck items to enumerate while looking for the first actionable one. Bounds scan cost. Overrides config. | `100` |
46
+ | `max_candidates` | Cap on how many stuck/close-out candidates to enumerate and evaluate. Repair every materially actionable candidate within this bounded set, then stop. Overrides config. | `100` |
40
47
  | `force` | `true` bypasses the loop-prevention backoff window (so a manual re-run re-attempts items even if their fingerprint is unchanged). It does **not** change the staleness rule — use `stale_after=0` for that. | `false` |
41
48
 
42
49
  ## Confirmation policy
@@ -54,8 +61,9 @@ Specifically forbidden:
54
61
  - Pausing because many items are stuck, an item looks complex, or a repair is likely to land
55
62
  the item back in `blocked`. Returning an item to `blocked` with a current, accurate note is a
56
63
  valid outcome of the repair lifecycle, not a failure.
57
- - Pausing because a re-dispatch looks expensive. The cost of one cycle is bounded (one
58
- actionable repair); the cost of stalling a scheduled cron waiting on a human is unbounded.
64
+ - Pausing because a re-dispatch looks expensive. The cost of one cycle is bounded by
65
+ `max_candidates` and the actionable subset inside that cap; the cost of stalling a scheduled
66
+ cron waiting on a human is unbounded.
59
67
 
60
68
  The only legitimate reasons to stop early:
61
69
 
@@ -63,7 +71,8 @@ The only legitimate reasons to stop early:
63
71
  missing value and exit.
64
72
  - The queue itself is misconfigured (Status property missing expected values, JIRA workflow
65
73
  can't reach required transitions). Surface and exit.
66
- - No stuck items, or none actionable this cycle. Exit cleanly with the idle-case summary.
74
+ - No stuck/close-out candidates, or none actionable this cycle. Exit cleanly with the idle-case
75
+ summary.
67
76
 
68
77
  ## Orchestration: agent team
69
78
 
@@ -107,16 +116,16 @@ rules as `lisa:intake`** — read that skill's "Source dispatch" section for the
107
116
  table; the detection is identical and only the per-item action changes (repair instead of
108
117
  claim-and-advance). The essentials, inlined here so this skill is self-complete:
109
118
 
110
- | If `$ARGUMENTS` is... | Queue / lifecycle | Source/tracker key | Stuck roles repaired |
119
+ | If `$ARGUMENTS` is... | Queue / lifecycle | Source/tracker key | Candidates repaired |
111
120
  |------------------------|-------------------|--------------------|----------------------|
112
- | Notion **database** URL/ID | PRD (Notion) | source=notion | `in_review`, `blocked` |
113
- | Confluence **space** URL/key | PRD (Confluence) | source=confluence | `in_review`, `blocked` (parent-page roles) |
114
- | Confluence **parent page** URL/ID | PRD (Confluence, narrowed) | source=confluence | `in_review`, `blocked` |
115
- | Linear **workspace** URL, **team** URL/key, or literal `linear` | PRD (Linear) | source=linear | `in_review`, `blocked` (project labels) |
116
- | GitHub **repo** URL / `org/repo` (PRD namespace) | PRD (GitHub) | source=github | `in_review`, `blocked` (PRD labels) |
117
- | GitHub **repo** URL / `org/repo` with `tracker = github` (build namespace) | Build (GitHub) | tracker=github | `claimed`, `blocked` (build labels) |
121
+ | Notion **database** URL/ID | PRD (Notion) | source=notion | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
122
+ | Confluence **space** URL/key | PRD (Confluence) | source=confluence | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
123
+ | Confluence **parent page** URL/ID | PRD (Confluence, narrowed) | source=confluence | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
124
+ | Linear **workspace** URL, **team** URL/key, or literal `linear` | PRD (Linear) | source=linear | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
125
+ | GitHub **repo** URL / `org/repo` (PRD namespace) | PRD (GitHub) | source=github | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
126
+ | GitHub **repo** URL / `org/repo` with `tracker = github` (build namespace) | Build (GitHub) | tracker=github | `claimed`, `blocked`, terminal/open issues, all-terminal parent rollups |
118
127
  | Literal `github` | GitHub; route by `intake_mode` (`prd` / `build` / `both`) | per lifecycle | per lifecycle above |
119
- | JIRA project key or full JQL | Build (JIRA) | tracker=jira | `claimed`, `blocked` (statuses) |
128
+ | JIRA project key or full JQL | Build (JIRA) | tracker=jira | `claimed`, `blocked`, terminal/closure verification, all-terminal parent rollups |
120
129
 
121
130
  Disambiguation (same as `lisa:intake`): a `notion.so`/`notion.site` URL → Notion; an Atlassian
122
131
  `/wiki/spaces/<KEY>` URL → Confluence (with `/pages/<id>` → parent-page narrowing); a
@@ -127,17 +136,17 @@ URL is out of scope** — this skill is batch-only; repair one item by hand via
127
136
  (build) or by re-running `lisa:<source>-to-tracker` (PRD).
128
137
 
129
138
  Role names for every vendor are resolved from `.lisa.config.json` per the `config-resolution`
130
- rule — never hardcode status/label strings. The relevant stuck roles:
131
-
132
- | Lifecycle | Vendor | In-progress role key | Blocked role key |
133
- |-----------|--------|----------------------|------------------|
134
- | Build | JIRA | `jira.workflow.claimed` (`In Progress`) | `jira.workflow.blocked` (`Blocked`) |
135
- | Build | GitHub | `github.labels.build.claimed` (`status:in-progress`) | `github.labels.build.blocked` (`status:blocked`) |
136
- | Build | Linear | `linear.labels.build.claimed` (`status:in-progress`) | `linear.labels.build.blocked` (`status:blocked`) |
137
- | PRD | Notion | `notion.values.in_review` (`In Review`) | `notion.values.blocked` (`Blocked`) |
138
- | PRD | GitHub | `github.labels.prd.in_review` (`prd-in-review`) | `github.labels.prd.blocked` (`prd-blocked`) |
139
- | PRD | Linear | `linear.labels.prd.in_review` (`prd-in-review`) | `linear.labels.prd.blocked` (`prd-blocked`) |
140
- | PRD | Confluence | `confluence.parents.in_review` (page id) | `confluence.parents.blocked` (page id) |
139
+ rule — never hardcode status/label strings. The relevant repair roles:
140
+
141
+ | Lifecycle | Vendor | In-progress role key | Blocked role key | Terminal / rollup role key |
142
+ |-----------|--------|----------------------|------------------|----------------------------|
143
+ | Build | JIRA | `jira.workflow.claimed` (`In Progress`) | `jira.workflow.blocked` (`Blocked`) | env-resolved `jira.workflow.done` |
144
+ | Build | GitHub | `github.labels.build.claimed` (`status:in-progress`) | `github.labels.build.blocked` (`status:blocked`) | env-resolved `github.labels.build.done` (`status:done`) |
145
+ | Build | Linear | `linear.labels.build.claimed` (`status:in-progress`) | `linear.labels.build.blocked` (`status:blocked`) | env-resolved `linear.labels.build.done` (`status:done`) |
146
+ | PRD | Notion | `notion.values.in_review` (`In Review`) | `notion.values.blocked` (`Blocked`) | `notion.values.shipped` (`Shipped`) |
147
+ | PRD | GitHub | `github.labels.prd.in_review` (`prd-in-review`) | `github.labels.prd.blocked` (`prd-blocked`) | `github.labels.prd.shipped` (`prd-shipped`) |
148
+ | PRD | Linear | `linear.labels.prd.in_review` (`prd-in-review`) | `linear.labels.prd.blocked` (`prd-blocked`) | `linear.labels.prd.shipped` (`prd-shipped`) |
149
+ | PRD | Confluence | `confluence.parents.in_review` (page id) | `confluence.parents.blocked` (page id) | `confluence.parents.shipped` (page id) |
141
150
 
142
151
  Resolve with the standard role-read pattern (local overrides global, default fallback):
143
152
 
@@ -159,13 +168,13 @@ BLOCKED=$(read_role '.github.labels.build.blocked' 'status:blocked')
159
168
  repair-intake stays vendor-neutral; concrete reads/writes go through the same layers the vendor
160
169
  intakes use. Never call Atlassian MCP or `acli` directly — go through `lisa:atlassian-access`.
161
170
 
162
- | Vendor | Reads (scan / comments / links) | Writes (transition / comment) | Re-dispatch / re-validate |
171
+ | Vendor | Reads (scan / comments / links) | Writes (transition / comment / close-out) | Re-dispatch / re-validate |
163
172
  |--------|---------------------------------|-------------------------------|---------------------------|
164
173
  | JIRA (build) | `lisa:atlassian-access` `search-issues` / `lisa:jira-read-ticket` | `lisa:atlassian-access` `transition` / `comment` | `lisa:jira-agent` |
165
- | GitHub (build) | `gh issue list` / `gh issue view --json` / `gh pr list` | `gh issue edit` (labels) / `gh issue comment` | `lisa:github-agent` |
174
+ | GitHub (build) | `gh issue list` / `gh issue view --json` / `gh pr list` / GraphQL sub-issues | `gh issue edit` (labels) / `gh issue comment` / `gh issue close --reason completed` | `lisa:github-agent` |
166
175
  | Linear (build) | Linear MCP `list_issues` / `get_issue` / `list_comments` | Linear MCP `save_issue` (labels) / `save_comment` | `lisa:linear-agent` |
167
176
  | Notion (PRD) | `lisa:notion-access` (`query`, page comments) | `lisa:notion-access` `write-page` (status) / page comment | `lisa:notion-to-tracker` (dry-run) |
168
- | GitHub (PRD) | `gh issue list/view` (PRD labels) | `gh issue edit` / `gh issue comment` | `lisa:github-to-tracker` (dry-run) |
177
+ | GitHub (PRD) | `gh issue list/view` (PRD labels) / GraphQL sub-issues / generated-work section | `gh issue edit` / `gh issue comment` / `gh issue close --reason completed` | `lisa:github-to-tracker` (dry-run) |
169
178
  | Linear (PRD) | Linear MCP `list_projects` / `get_project` (+ sentinel feedback issue) | Linear MCP `save_project` (labels) / `save_comment` | `lisa:linear-to-tracker` (dry-run) |
170
179
  | Confluence (PRD) | `lisa:atlassian-access` CQL | `lisa:atlassian-access` page `parentId` update / comment | `lisa:confluence-to-tracker` (dry-run) |
171
180
 
@@ -207,9 +216,10 @@ proceeds — it is judged on blocker state, not time.)
207
216
 
208
217
  ## Repair decision tree
209
218
 
210
- Apply per candidate. Stop the cycle after the **first** candidate that triggers a write
211
- (lifecycle transition, re-dispatch, or refreshed note). Everything examined before it is
212
- recorded read-only.
219
+ Apply per candidate. Continue through the ordered list until every candidate inside the
220
+ `max_candidates` cap has been evaluated. Each candidate may trigger a write (lifecycle transition,
221
+ native close/archive/complete, re-dispatch, or refreshed note), be recorded read-only, or be
222
+ recorded under Errors. Do not stop after the first write; the cap is the batch boundary.
213
223
 
214
224
  ### Build `claimed` (stalled in-progress) → resume in place
215
225
 
@@ -244,6 +254,45 @@ skipping the claim transition (the item is already `claimed`):
244
254
  4. Else → still blocked. Refresh the note with the current reason (Loop prevention) and leave it
245
255
  `blocked`.
246
256
 
257
+ ### Build terminal-open → native close / complete / resolve
258
+
259
+ For each build item that already carries the env-resolved true terminal `done` role but is still
260
+ native-open / active / unresolved:
261
+
262
+ 1. Verify the item is a **leaf** or a **rollup parent whose all required children are terminal**.
263
+ If it is a parent with incomplete children, do not close it; refresh a `[lisa-repair-intake]`
264
+ note naming the incomplete child set.
265
+ 2. Verify the terminal `done` role is the true final value per `leaf-only-lifecycle` and
266
+ `config-resolution` env-keyed `done`. Intermediate env labels (for example `status:on-dev` or
267
+ `status:on-stg`) are not terminal and must stay open.
268
+ 3. Perform the provider-native terminal action idempotently:
269
+ - GitHub: `gh issue close <number> --repo <org>/<repo> --reason completed`.
270
+ - Linear: move the issue to the configured Done / Completed native workflow state if available;
271
+ otherwise record the missing native state as a setup error.
272
+ - JIRA: verify it is resolved / closed (`statusCategory = Done`, resolution set if required);
273
+ if not, transition through the configured terminal workflow path or report the missing setup.
274
+ 4. Post a compact `[lisa-repair-intake]` note only when the native close-out changed state or when
275
+ an actionable setup error must be surfaced. Do not spam already-closed terminal items.
276
+
277
+ ### Build rollup with all children terminal → close out parent/container
278
+
279
+ For each parent/container item (Epic, Story, Spike, Project, or any item with child work) whose
280
+ required child set is fully terminal:
281
+
282
+ 1. Read the child set using the vendor-native hierarchy first (GitHub sub-issues, JIRA
283
+ Epic/parent/sub-task hierarchy, Linear project/parent/sub-issues), with the same fallbacks the
284
+ vendor read/sync skills document.
285
+ 2. Evaluate bottom-up per `leaf-only-lifecycle`: every required child must already be terminal.
286
+ Optional / won't-do / not-planned children are terminal-but-dropped and do not hold the parent
287
+ open.
288
+ 3. Apply the configured terminal rollup role to the parent/container, removing any stale build
289
+ lifecycle role that conflicts.
290
+ 4. Immediately perform terminal native closure where the provider supports it (GitHub close,
291
+ Linear complete, JIRA resolved/closed). A completed rollup parent should not remain open in
292
+ GitHub merely because no leaf agent touched it.
293
+ 5. If any required child is incomplete, active, blocked, or inaccessible, leave the parent open and
294
+ record it as `still_blocked` or `active` with the current child tally.
295
+
247
296
  ### PRD `in_review` (stalled in-progress) → re-run validate→route
248
297
 
249
298
  After the staleness gate passes, run the **same dry-run validate→route pipeline the vendor PRD
@@ -276,6 +325,40 @@ intake runs per item**, targeted at this single PRD and **skipping the claim** (
276
325
  3. If no new answers and no dependency change → leave `blocked` untouched (subject to the
277
326
  backoff window — do not re-post an identical note).
278
327
 
328
+ ### PRD terminal-open → close / archive source artifact
329
+
330
+ For each PRD source artifact that already carries the configured terminal source role (`shipped`
331
+ for generated-work completion, or a source-specific terminal role that the configured PRD source
332
+ declares closed-out) but is still native-open / active:
333
+
334
+ 1. Verify the PRD's generated top-level work is terminal per `prd-lifecycle-rollup`, unless the
335
+ source artifact is already in a stronger product-owned terminal role that explicitly permits
336
+ closure. Do not move a PRD out of `draft` or `verified`.
337
+ 2. Close or archive through the source vendor's native mechanism where one exists:
338
+ - GitHub: close the PRD issue with `--reason completed`.
339
+ - Linear: archive/close the PRD project through Linear MCP when supported by the workspace.
340
+ - Confluence/Notion: archive the page only when the access layer exposes a supported archival
341
+ action; otherwise record a capability-aware no-op.
342
+ 3. Never set `verified`; `/lisa:verify-prd` remains the only automated writer of the verified
343
+ role. This path only reconciles an already-terminal PRD with native closure.
344
+
345
+ ### PRD rollup with all generated work terminal → ship and close out
346
+
347
+ For each PRD in `ticketed` or another non-product-owned open PRD role whose generated top-level
348
+ work is fully terminal:
349
+
350
+ 1. Read the generated top-level child set exactly as `prd-ticket-coverage` / the vendor PRD intake
351
+ does: native PRD children where supported, plus the durable generated-work section fallback.
352
+ 2. Evaluate terminal state using `prd-lifecycle-rollup`'s vendor predicate. A generated Epic or
353
+ Story is terminal only when it has itself rolled up and closed out; do not re-derive its leaf
354
+ descendants directly when its own state is still open.
355
+ 3. Transition the PRD to the configured `shipped` role.
356
+ 4. Close/archive the PRD source artifact through the vendor-native close-out mechanism where
357
+ supported. This repair path is the explicit close-out sweep for PRDs whose child work is done;
358
+ it does not set `verified` and does not run `/lisa:verify-prd`.
359
+ 5. If generated work is missing, ambiguous, or partially incomplete, leave the PRD open and report
360
+ the incomplete child set. Never close a PRD on partial completion.
361
+
279
362
  ## Dependency clearing (conservative, vendor-specific extraction)
280
363
 
281
364
  `lisa:tracker-read` is a thin dispatcher that returns each vendor's bundle **verbatim** — there
@@ -308,7 +391,7 @@ cron tick.
308
391
 
309
392
  - Every note this skill writes is prefixed `[lisa-repair-intake]` and carries a compact **state
310
393
  fingerprint**: the lifecycle role, the set of blocker refs + their observed states, the
311
- validation verdict (PASS/FAIL), and a timestamp.
394
+ validation verdict (PASS/FAIL), terminal/open state, rollup child tally, and a timestamp.
312
395
  - Before writing a note or re-attempting a `blocked` item, compute the current fingerprint. If
313
396
  an identical fingerprint was already posted within the **backoff window**, skip the item
314
397
  silently (record as `still_blocked` / `active`, no write).
@@ -318,18 +401,24 @@ cron tick.
318
401
 
319
402
  ## Lifecycle ownership guard
320
403
 
321
- repair-intake owns ONLY the four stuck roles: build `claimed` / `blocked` and PRD `in_review` /
322
- `blocked`, plus the transitions a stuck item legitimately moves through during recovery. It MAY:
404
+ repair-intake owns the repair surfaces needed to recover stuck work and close-out drift:
405
+ build `claimed` / `blocked`, PRD `in_review` / `blocked`, terminal-labeled native-open items, and
406
+ parent/container rollups whose child sets are already terminal. It MAY:
323
407
 
324
408
  - Apply the build scanner's post-agent `claimed → done` on a successful resume (it is finishing
325
409
  the scanner's interrupted job), and move a dependency-cleared build item `blocked → claimed`.
326
410
  - Move a re-validated PRD `in_review`/`blocked → ticketed` (PASS) or `→ blocked` (FAIL), exactly
327
411
  as the PRD intake does.
412
+ - Close / complete / resolve build items that already carry the true terminal `done` role but are
413
+ still natively open, per `leaf-only-lifecycle`.
414
+ - Roll up a parent/container to the configured terminal state and close/complete/resolve it when
415
+ all required children are terminal.
416
+ - Move a PRD with fully terminal generated work to `shipped` and close/archive the source artifact
417
+ where the source vendor supports native close-out, per `prd-lifecycle-rollup`.
328
418
 
329
419
  It MUST NOT:
330
420
 
331
- - Move a PRD out of `draft`, `shipped`, or `verified`, or close/archive any PRD (those are
332
- product- and rollup-owned — see `prd-lifecycle-rollup`).
421
+ - Move a PRD out of `draft` or `verified` (those are product-owned), or set `verified` itself.
333
422
  - Apply a build `done` value other than via the env-resolution rules, or close a native item at
334
423
  any value other than the true terminal `done` (see `leaf-only-lifecycle`).
335
424
  - Touch `ready` items (that is `lisa:intake`'s lane).
@@ -338,23 +427,27 @@ It MUST NOT:
338
427
 
339
428
  1. **Resolve the queue** — detect vendor/lifecycle (Source dispatch); resolve stuck role names
340
429
  from config. For JIRA, confirm the needed transitions are reachable; stop on misconfig.
341
- 2. **Enumerate stuck candidates** — query the in-progress role(s) and the `blocked` role for the
342
- detected lifecycle(s), up to `max_candidates`, via the Access layer reads.
430
+ 2. **Enumerate repair candidates** — query in-progress role(s), `blocked` role(s), terminal/open
431
+ items, and rollup parents/PRDs with child work for the detected lifecycle(s), up to
432
+ `max_candidates`, via the Access layer reads.
343
433
  3. **Order deterministically**, highest repair-confidence first:
344
- 1. `blocked` items whose dependencies are now **cleared** (safe, high-value, one-cycle wins),
345
- 2. `blocked` items with **new clarifying answers**,
346
- 3. **stalled** in-progress items, oldest activity first.
347
- 4. **Walk the ordered list**, evaluating each read-only (staleness, dependency, answer checks),
348
- and **repair the first candidate that is actionable** per the decision tree. Stop after that
349
- one write-producing repair.
434
+ 1. terminal-labeled items that only need native close / complete / resolve,
435
+ 2. rollup parents/PRDs whose child sets are all terminal,
436
+ 3. `blocked` items whose dependencies are now **cleared** (safe, high-value, one-cycle wins),
437
+ 4. `blocked` items with **new clarifying answers**,
438
+ 5. **stalled** in-progress items, oldest activity first.
439
+ 4. **Walk the ordered list**, evaluating each candidate (terminal close-out, rollup child tally,
440
+ staleness, dependency, answer checks), and repair **every** candidate that is actionable inside
441
+ the `max_candidates` cap. Continue after successful writes and after per-item errors.
350
442
  5. **Empty / nothing actionable** → exit cleanly:
351
443
  `"No stuck items actionable this cycle (examined N, all active or in backoff)."`
352
444
  6. **Failure isolation** — if evaluating one candidate errors, record it under Errors and
353
445
  continue to the next; one bad item never aborts the cycle.
354
446
 
355
- Process **at most one materially actionable repair per invocation** — scan many, repair one,
356
- exit. This matches `lisa:intake`'s one-item-per-cycle contract: bounded cost, low race risk, no
357
- surprise burst of PRs. Throughput is the scheduler's job, not one invocation's.
447
+ Process **all materially actionable repairs among the enumerated candidates** — scan up to
448
+ `max_candidates`, repair the actionable subset, then exit. This intentionally differs from
449
+ `lisa:intake`'s one-ready-item claim contract because repair work is bounded by an explicit cap and
450
+ often consists of cheap close-out reconciliation that should drain in one cron pass.
358
451
 
359
452
  ## Summary report
360
453
 
@@ -363,11 +456,16 @@ Report outcomes in these buckets:
363
456
  - `resumed` — stalled in-progress work re-dispatched in place.
364
457
  - `unblocked` — blocker cleared (or answers resolved); re-dispatched or transitioned to
365
458
  `ticketed`.
459
+ - `closed_out` — terminal-labeled items whose native open/active state was closed, completed,
460
+ resolved, or archived.
461
+ - `rolled_up` — parent/container/PRD rollups advanced because all associated children were
462
+ terminal.
366
463
  - `still_blocked` — examined and intentionally left `blocked`, with the active reason.
367
464
  - `active` — skipped because current work is not stale (or within backoff).
368
465
  - `errors` — items that failed evaluation, with the error.
369
466
 
370
- State the single item repaired this cycle (if any) and what action was taken.
467
+ State every item repaired this cycle and the action taken. If the output would be long, group by
468
+ bucket and show compact refs plus counts.
371
469
 
372
470
  ## Schedule examples
373
471
 
@@ -388,13 +486,15 @@ stuck work accumulates), or interleaved on a longer cadence.
388
486
 
389
487
  - Never run a cycle without an explicit queue. Side effects too high to default.
390
488
  - Never reset stalled in-progress items to `ready` — resume in place (decision tree).
391
- - Never mutate product-owned states (`draft`, `shipped`, `verified`) or close PRDs (Lifecycle
392
- ownership guard).
489
+ - Never mutate product-owned states (`draft`, `verified`) or set `verified`; PRD rollup close-out
490
+ may move open generated-work PRDs to `shipped` and close/archive them only after all associated
491
+ child work is terminal.
393
492
  - Apply build `done` ONLY via the env-resolution rules, and trigger native closure only at the
394
493
  true terminal `done` value (`leaf-only-lifecycle`).
395
494
  - Never re-dispatch a `blocked` build item unless every parsed blocker is cleared (conservative
396
495
  dependency clearing).
397
- - One materially actionable repair per cycle. Stop after the first write-producing repair.
496
+ - Repair every materially actionable candidate inside the `max_candidates` cap; default cap is 100.
497
+ - Default GitHub `intake_mode` is `both` when both PRD and build namespaces exist.
398
498
  - Honor the backoff window — never re-post an identical `[lisa-repair-intake]` note within it
399
499
  (unless `force=true`).
400
500
  - Never run two repair cycles concurrently against overlapping queues, and never run