@codyswann/lisa 2.39.3 → 2.41.1

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 (45) 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/agents/github-build-intake.md +2 -2
  5. package/plugins/lisa/agents/jira-build-intake.md +2 -2
  6. package/plugins/lisa/agents/linear-build-intake.md +2 -2
  7. package/plugins/lisa/rules/config-resolution.md +26 -4
  8. package/plugins/lisa/rules/leaf-only-lifecycle.md +24 -3
  9. package/plugins/lisa/rules/prd-lifecycle-rollup.md +109 -0
  10. package/plugins/lisa/skills/atlassian-access/SKILL.md +2 -0
  11. package/plugins/lisa/skills/git-submit-pr/SKILL.md +7 -0
  12. package/plugins/lisa/skills/github-build-intake/SKILL.md +14 -0
  13. package/plugins/lisa/skills/implement/SKILL.md +1 -1
  14. package/plugins/lisa/skills/jira-build-intake/SKILL.md +10 -3
  15. package/plugins/lisa/skills/linear-build-intake/SKILL.md +11 -4
  16. package/plugins/lisa/skills/tracker-build-intake/SKILL.md +15 -0
  17. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  19. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  21. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  23. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  25. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  29. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  31. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  33. package/plugins/src/base/agents/github-build-intake.md +2 -2
  34. package/plugins/src/base/agents/jira-build-intake.md +2 -2
  35. package/plugins/src/base/agents/linear-build-intake.md +2 -2
  36. package/plugins/src/base/rules/config-resolution.md +26 -4
  37. package/plugins/src/base/rules/leaf-only-lifecycle.md +24 -3
  38. package/plugins/src/base/rules/prd-lifecycle-rollup.md +109 -0
  39. package/plugins/src/base/skills/atlassian-access/SKILL.md +2 -0
  40. package/plugins/src/base/skills/git-submit-pr/SKILL.md +7 -0
  41. package/plugins/src/base/skills/github-build-intake/SKILL.md +14 -0
  42. package/plugins/src/base/skills/implement/SKILL.md +1 -1
  43. package/plugins/src/base/skills/jira-build-intake/SKILL.md +10 -3
  44. package/plugins/src/base/skills/linear-build-intake/SKILL.md +11 -4
  45. package/plugins/src/base/skills/tracker-build-intake/SKILL.md +15 -0
package/package.json CHANGED
@@ -82,7 +82,7 @@
82
82
  "lodash": ">=4.18.1"
83
83
  },
84
84
  "name": "@codyswann/lisa",
85
- "version": "2.39.3",
85
+ "version": "2.41.1",
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.39.3",
3
+ "version": "2.41.1",
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.39.3",
3
+ "version": "2.41.1",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -49,14 +49,14 @@ If the cycle errored before processing any issues (e.g. label namespace not adop
49
49
 
50
50
  ### 4. Suggest next actions when warranted
51
51
 
52
- After a successful cycle, if any issues ended in the configured `done` label, mention that the next phase (QA, deploy, or downstream verification) is owned by either humans or a future intake skill. This skill does not own anything past `done`.
52
+ After a successful cycle, if any issues ended in the configured `done` label, mention whether the vendor skill also performed terminal native closure. This skill does not own anything past `done`, except that `github-build-intake` closes the GitHub issue when `done` is the true terminal value per `leaf-only-lifecycle`.
53
53
 
54
54
  If any issues ended in `Blocked` (pre-flight verify failed) or `Held` (triage found ambiguities), point that out so the caller knows which issues need human attention before they can be re-claimed. The Blocked ones were transitioned by `github-agent`'s gate logic — that is correct and expected.
55
55
 
56
56
  ## Rules
57
57
 
58
58
  - **Never run a cycle without an explicit repo.** Side effects too high to default.
59
- - **Never modify the lifecycle**: only the configured `ready → claimed → done` transitions. Never touch terminal-`done` or any other label. (Exception: `github-agent` may relabel to the configured `blocked` label as part of its pre-flight gate — that's its job, not yours.)
59
+ - **Never modify the lifecycle**: only the configured `ready → claimed → done` transitions and terminal-only native issue closure. Never touch any other label. (Exception: `github-agent` may relabel to the configured `blocked` label as part of its pre-flight gate — that's its job, not yours.)
60
60
  - **Never bypass `github-agent` to do build work directly.** The intake skill dispatches; `github-agent` builds. Skipping the dispatch produces broken work.
61
61
  - **Never invent labels.** The label namespace lives in `.lisa.config.json` `github.labels.build.*` (canonical) — the setup skill writes it. Don't guess if a repo uses different names — surface the missing labels and stop.
62
62
  - **Never start a second cycle while one is in flight against the same repo.** Serial execution. Scheduling layer (when added) is responsible for not double-firing.
@@ -49,14 +49,14 @@ If the cycle errored before processing any tickets (e.g. workflow misconfigured
49
49
 
50
50
  ### 4. Suggest next actions when warranted
51
51
 
52
- After a successful cycle, if any tickets ended in the configured `done` status, mention that the next phase (QA, deploy, or downstream verification depending on the project workflow) is owned by either humans or a future intake skill. This skill does not own anything past `done`.
52
+ After a successful cycle, if any tickets ended in the configured `done` status, mention whether terminal native resolution was verified. This skill does not own anything past `done`, except that `jira-build-intake` verifies the JIRA ticket is natively resolved / closed when `done` is the true terminal value per `leaf-only-lifecycle`.
53
53
 
54
54
  If any tickets ended in `Blocked` (pre-flight verify failed) or `Held` (triage found ambiguities), point that out so the caller knows which tickets need human attention before they can be re-claimed. The Blocked ones were transitioned by `jira-agent`'s gate logic — that is correct and expected.
55
55
 
56
56
  ## Rules
57
57
 
58
58
  - **Never run a cycle without an explicit query.** Side effects too high to default.
59
- - **Never modify the lifecycle**: only the configured `ready → claimed → done` transitions. Never touch `TODO`, post-`done` statuses, or any other status. (Exception: `jira-agent` may transition to `Blocked` as part of its pre-flight gate — that's its job, not yours.)
59
+ - **Never modify the lifecycle**: only the configured `ready → claimed → done` transitions and terminal-only native resolution verification. Never touch `TODO`, post-`done` statuses, or any other status. (Exception: `jira-agent` may transition to `Blocked` as part of its pre-flight gate — that's its job, not yours.)
60
60
  - **Never bypass `jira-agent` to do build work directly.** The intake skill dispatches; `jira-agent` builds. Skipping the dispatch produces broken work.
61
61
  - **Never invent transitions.** If a project's workflow uses different status names, they are configured in `.lisa.config.json` `jira.workflow.*` (canonical) — the setup skill writes them. Per-invocation `$ARGUMENTS` overrides are a secondary escape hatch. Don't guess.
62
62
  - **Never start a second cycle while one is in flight against an overlapping query.** Serial execution. Scheduling layer (when added) is responsible for not double-firing.
@@ -49,14 +49,14 @@ If the cycle errored before processing any Issues (e.g. label convention not ado
49
49
 
50
50
  ### 4. Suggest next actions when warranted
51
51
 
52
- After a successful cycle, if any Issues ended at the configured `done` label, mention that the next phase (QA, deploy, or downstream verification) is owned by humans or a future intake skill. This skill does not own anything past `done`.
52
+ After a successful cycle, if any Issues ended at the configured `done` label, mention whether the vendor skill also performed terminal native completion. This skill does not own anything past `done`, except that `linear-build-intake` moves the native Issue state to Done / Completed when `done` is the true terminal value per `leaf-only-lifecycle`.
53
53
 
54
54
  If any Issues ended at the configured `blocked` label (pre-flight verify failed) or `Held` (triage found ambiguities), point that out so the caller knows which Issues need human attention before they can be re-claimed. The blocked ones were transitioned by `linear-agent`'s gate logic — that is correct and expected.
55
55
 
56
56
  ## Rules
57
57
 
58
58
  - **Never run a cycle without an explicit query or configured `linear.teamKey`.** Side effects too high to default.
59
- - **Never modify the lifecycle**: only the configured `ready → claimed → done` transitions. Never touch terminal-`done`, the configured `blocked` label (owned by `linear-agent`), or any other label. (Exception: the configured `review` label is set by `linear-evidence` mid-flow — that's not your concern.)
59
+ - **Never modify the lifecycle**: only the configured `ready → claimed → done` transitions and terminal-only native Issue completion. Never touch the configured `blocked` label (owned by `linear-agent`) or any other label. (Exception: the configured `review` label is set by `linear-evidence` mid-flow — that's not your concern.)
60
60
  - **Never bypass `linear-agent` to do build work directly.** The intake skill dispatches; `linear-agent` builds. Skipping the dispatch produces broken work.
61
61
  - **Never invent labels.** Names live in `.lisa.config.json` `linear.labels.build.*` (canonical) — the setup skill writes them. If a team hasn't adopted them yet, the skill exits with an adoption hint. Don't guess label names.
62
62
  - **Never start a second cycle while one is in flight against an overlapping team.** Serial execution. Scheduling layer (when added) is responsible for not double-firing.
@@ -57,7 +57,8 @@ fi
57
57
  "shipped": "<page-id>"
58
58
  },
59
59
  "dashboardPageId": "<page-id>",
60
- "feedbackPageId": "<page-id>"
60
+ "feedbackPageId": "<page-id>",
61
+ "rollup": { "closeOnShipped": false }
61
62
  },
62
63
  "github": {
63
64
  "org": "<org-or-user>",
@@ -75,7 +76,8 @@ fi
75
76
  "ready": "prd-ready", "in_review": "prd-in-review",
76
77
  "blocked": "prd-blocked", "ticketed": "prd-ticketed",
77
78
  "shipped": "prd-shipped",
78
- "sentinel": "prd-intake-feedback"
79
+ "sentinel": "prd-intake-feedback",
80
+ "rollup": { "closeOnShipped": false }
79
81
  }
80
82
  }
81
83
  },
@@ -86,7 +88,8 @@ fi
86
88
  "values": {
87
89
  "draft": "Draft", "ready": "Ready", "in_review": "In Review",
88
90
  "blocked": "Blocked", "ticketed": "Ticketed", "shipped": "Shipped"
89
- }
91
+ },
92
+ "rollup": { "closeOnShipped": false }
90
93
  },
91
94
  "linear": {
92
95
  "workspace": "<workspace-slug>",
@@ -104,7 +107,8 @@ fi
104
107
  "ready": "prd-ready", "in_review": "prd-in-review",
105
108
  "blocked": "prd-blocked", "ticketed": "prd-ticketed",
106
109
  "shipped": "prd-shipped",
107
- "sentinel": "prd-intake-feedback"
110
+ "sentinel": "prd-intake-feedback",
111
+ "rollup": { "closeOnShipped": false }
108
112
  }
109
113
  }
110
114
  },
@@ -208,6 +212,18 @@ Every lifecycle skill operates on a fixed set of **roles** (`ready`, `claimed`,
208
212
  | `shipped` | All child tickets shipped | `Shipped` (status) | `prd-shipped` (label) |
209
213
  | `sentinel` | (PRD-intake feedback issue marker, GitHub/Linear self-host only) | — | `prd-intake-feedback` |
210
214
 
215
+ ### PRD rollup config (`prd.rollup`)
216
+
217
+ PRD lifecycle completion is **derived** from the PRD's generated top-level work, not set independently — see the `prd-lifecycle-rollup` rule for the full contract (generated-top-level-work definition, per-vendor terminal-state predicate, the `shipped` transition, and the child-ref idempotency key). When all required generated top-level children are terminal, rollup transitions the PRD to its `shipped` role; the `prd.rollup` block configures the optional close/archive step that follows.
218
+
219
+ The `rollup` object lives in each PRD-source vendor section (`github.labels.prd.rollup`, `linear.labels.prd.rollup`, `notion.rollup`, `confluence.rollup`):
220
+
221
+ | Key | Required | Default | Notes |
222
+ |-----|----------|---------|-------|
223
+ | `closeOnShipped` | no | `false` | When `true`, rollup closes/archives the PRD after the `shipped` transition (GitHub: close the issue; Linear: move to a closed/archived state; JIRA: transition to Done; Confluence/Notion: archive where supported). When `false` (the default), the PRD is set to `shipped` but left open for a human to close. Closure never happens before all generated top-level work is terminal. |
224
+
225
+ Like every other vocabulary key, `prd.rollup` is **optional** — a missing block inherits `closeOnShipped: false`. The `shipped` transition itself is unconditional on the all-terminal condition; only the close/archive step is gated by this flag.
226
+
211
227
  ### Env-keyed `done`
212
228
 
213
229
  The `done` role is special: the terminal status/label depends on which environment a PR was merged into. A hotfix to staging ends at `On Stg`; a production hotfix ends at `Done`. So `done` is a **map** keyed by env name (`dev`, `staging`, `production`).
@@ -220,6 +236,12 @@ Skills that transition to `done` MUST resolve the env first:
220
236
 
221
237
  If a project's terminal state is the same regardless of env, set `done` to a string instead of a map (lifecycle skills accept either shape).
222
238
 
239
+ The true terminal `done` value is also the only value that triggers provider-native closure / resolution per `leaf-only-lifecycle`:
240
+
241
+ - If `done` is a string, that value is terminal.
242
+ - If `done` is an env-keyed map, the production / final environment's value is terminal. The conventional key is `production`; project-specific final env names must be explicit in deploy config or the lifecycle skill must fail rather than guessing.
243
+ - Intermediate env values (`dev`, `staging`, or configured equivalents) are deployment waypoints. Applying them must not close / resolve / complete the native tracker item.
244
+
223
245
  ### What's configurable, what's not
224
246
 
225
247
  - **Status / label NAMES** are configurable per project — that's the point of the vocabulary maps.
@@ -1,11 +1,12 @@
1
- # Leaf-Only Build-Ready Invariant & Parent Status Rollup
1
+ # Leaf-Only Build-Ready Invariant, Parent Status Rollup & Terminal Native Closure
2
2
 
3
- This is the single vendor-neutral source of truth for two coupled lifecycle rules. Every `*-to-tracker`, `*-write-*`, `*-validate-*`, and `*-build-intake` skill cites this rule rather than restating it, so per-vendor logic does not drift.
3
+ This is the single vendor-neutral source of truth for three coupled lifecycle rules. Every `*-to-tracker`, `*-write-*`, `*-validate-*`, and `*-build-intake` skill cites this rule rather than restating it, so per-vendor logic does not drift.
4
4
 
5
5
  1. **Leaf-only invariant** — only an independently implementable **leaf work unit** may carry the build-ready role. A parent/container with child work is never directly build-ready.
6
6
  2. **Parent status rollup** — a parent/container's lifecycle state is *derived* from its children, never set independently.
7
+ 3. **Terminal native closure** — when a leaf work unit reaches the configured terminal `done` role, Lisa also closes / resolves / completes it using the provider's native mechanism where one exists. Intermediate done-like environment states stay open.
7
8
 
8
- The two are the same idea seen from opposite ends: a parent never enters the build queue as work; it only ever *reflects* the state of the leaves underneath it.
9
+ The first two are the same idea seen from opposite ends: a parent never enters the build queue as work; it only ever *reflects* the state of the leaves underneath it. The third keeps the provider's native open/closed signal aligned with Lisa's terminal lifecycle state so finished work does not linger as open.
9
10
 
10
11
  ## Why this exists
11
12
 
@@ -82,6 +83,25 @@ The terminal rollup state is whatever the project configures for `done` — whic
82
83
 
83
84
  **Single-environment collapse (this repo).** Lisa's own deploy has only `main`/`production` (no dev/staging), so `done` is a single value, not a map, and the build lifecycle collapses to one chain: `ready → claimed (in-progress) → review (code-review) → done`. The rollup terminal state is simply `done`. This is the *collapsed* case of the generic rule, not a different rule — projects with more environments keep the env-keyed map.
84
85
 
86
+ ## Terminal native closure
87
+
88
+ The configured terminal `done` role is not just another label or status. Once a **leaf work unit** reaches the true terminal `done` value, Lisa must also finalize the item through the tracker's native completion mechanism when the tracker supports one:
89
+
90
+ | Tracker | Terminal native action |
91
+ |---|---|
92
+ | GitHub Issues | `gh issue close <number> --reason completed` after applying the terminal `done` label |
93
+ | Linear | move the Issue's native workflow `state` to the team's configured Done / Completed state after applying the terminal `done` label |
94
+ | JIRA | transition to the configured terminal Done / Resolved / Closed status and verify the resulting issue is in `statusCategory = Done` with a resolution when the workflow requires one |
95
+ | Provider without a close / archive concept | no-op; the terminal lifecycle role is sufficient |
96
+
97
+ This action is **terminal-only**:
98
+
99
+ - Intermediate env-keyed states such as `status:on-dev`, `status:on-stg`, `On Dev`, or `On Stg` remain open / unresolved / active. They are deployment waypoints, not terminal completion.
100
+ - A single-environment project whose `done` resolves to one value treats that value as terminal. In this repo, `production: main` means `status:done` / `Done` is terminal.
101
+ - A multi-environment project treats only the production / final environment's `done` value as terminal unless the project explicitly configures `done` as a single string. Do not close native work items at lower environments.
102
+ - The native finalization must be idempotent. If the item is already closed / completed / resolved, report that and continue.
103
+ - If a provider exposes no native close / archive operation, or a project has not configured the native Done state, record a capability-aware no-op or setup error according to the vendor skill. Do not invent a state name.
104
+
85
105
  ## Citation
86
106
 
87
107
  Skills that enforce this invariant or perform rollup cite this rule by slug (the `leaf-only-lifecycle` rule) instead of restating it:
@@ -90,5 +110,6 @@ Skills that enforce this invariant or perform rollup cite this rule by slug (the
90
110
  - **Validate** (`*-validate-*`) — FAIL a container carrying the build-ready role; FAIL a childless Epic/Story/Spike marked build-ready.
91
111
  - **Build intake** (`*-build-intake`, `tracker-build-intake`) — claim leaves only; skip or safe-block containers with stale build-ready roles.
92
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.
93
114
 
94
115
  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.
@@ -0,0 +1,109 @@
1
+ # PRD Lifecycle Rollup & Generated-Top-Level-Work Contract
2
+
3
+ This is the single vendor-neutral source of truth for how a PRD owns the work it generates and how its lifecycle rolls up to `shipped` from that work. Every PRD-source and PRD-intake skill (`prd-backlink`, `prd-ticket-coverage`, `*-prd-intake`, `*-to-tracker`) cites this rule by slug rather than restating it, so PRD→child linking and PRD closure rollup behave identically across GitHub, Linear, JIRA/Atlassian, Confluence, and Notion instead of drifting per vendor.
4
+
5
+ It defines four coupled things:
6
+
7
+ 1. **Generated top-level work** — what a PRD owns as children (its created Epics / top-level Stories), explicitly **excluding** leaf Sub-tasks.
8
+ 2. **Per-vendor terminal-state predicate** — how "this generated child is done" is decided for each source/tracker.
9
+ 3. **PRD `shipped` transition + config-gated close/archive** — when and how a PRD rolls up to its terminal state and is closed/archived.
10
+ 4. **Idempotency dedupe key** — the child-ref identity that makes linking and rollup safe to re-run.
11
+
12
+ This is the PRD-level companion to `leaf-only-lifecycle`: that rule governs the *build* lifecycle of leaf work units and how a **parent container** rolls up from its leaf children; this rule governs the *PRD* lifecycle and how the **PRD** rolls up from its generated top-level children. The two share the rollup shape (terminal children → parent advances) and the multi-env terminal handling, applied at different levels of the hierarchy.
13
+
14
+ ## Why this exists
15
+
16
+ PRD intake currently comments back with created ticket links, but the PRD is not necessarily the structural parent of the work it generated. That makes lifecycle rollup weaker than it should be: humans cannot always navigate from PRD to generated top-level work using the host tool's hierarchy UI, agents must parse free-form comments instead of reading a first-class relationship, and PRD shipped/closed state is not automatically derived from whether its generated work is done. Worst of all, cross-vendor implementations drift in how they record the PRD-to-work relationship and when they close a PRD.
17
+
18
+ The desired model is vendor-neutral: **the PRD is the source-of-truth parent for the top-level work it generated, and PRD lifecycle completion rolls up from that child set.** That belongs here, in a cross-vendor rule, and every backlink / intake / coverage path enforces it.
19
+
20
+ This surfaced in real GitHub-backed PRD intake (`CodySwannGT/advisory-rankings`): PRD issue #64 generated top-level Epic #65 plus child Stories/Sub-tasks. #65 should be a child/sub-issue of #64, and #64 should eventually transition/close when #65 and its descendants are complete — not before, and not twice.
21
+
22
+ ## Generated top-level work (the contract)
23
+
24
+ **Generated top-level work** is the set of work units a PRD's intake/decomposition *directly created at the top of the hierarchy* and now owns as children:
25
+
26
+ | Class | Examples by type | Owned by the PRD as a child? |
27
+ |---|---|---|
28
+ | **Generated top-level work** | the created **Epic(s)**, and any **top-level Story** created directly under the PRD (a Story with no parent Epic) | **Yes** — these are the PRD's children for rollup |
29
+ | **Descendant work** | **Sub-tasks**, and **Stories that hang under a generated Epic**, and any leaf (Bug/Task/Improvement) under a top-level unit | **No** — owned by their top-level parent, not by the PRD directly |
30
+
31
+ The boundary is **structural, mirroring `leaf-only-lifecycle`'s container/leaf taxonomy but one level up**:
32
+
33
+ - A PRD owns its **top-level** generated units. Those top-level units own their own descendants (per `leaf-only-lifecycle`'s parent-status-rollup state machine).
34
+ - Leaf Sub-tasks are **never** direct children of the PRD when a top-level Epic/Story hierarchy exists (PRD #525 non-goal: "Do not make leaf implementation tasks direct children of the PRD when a top-level Epic/Story hierarchy exists"). The PRD owns top-level generated work; those top-level work units own their descendants.
35
+ - When a PRD decomposes into a flat set with no Epic (e.g. a few top-level Stories or a single leaf Task and nothing under it), the **top-level** items it created are its generated top-level work, whatever their type. The rule is "what the PRD created at the top," not "only Epics."
36
+
37
+ ### How each vendor records the PRD→child relationship
38
+
39
+ The relationship is recorded with the source tool's **native hierarchy first**, falling back to a **durable documented section** in the PRD where native hierarchy is unavailable or the source and destination are different systems (PRD #525: vendors need not all expose identical native hierarchy):
40
+
41
+ - **GitHub Issues** — native **sub-issues**: the generated Epic issue becomes a sub-issue of the PRD issue when the repo supports sub-issues and source and tracker are the same repo. Otherwise a documented `## Tickets` / generated-work section.
42
+ - **Linear** — native **parent / project** relationships: a generated top-level Issue uses `parentId`, or a generated Project groups generated Issues; the PRD's relationship to its top-level Project/Issues is recorded natively where the PRD also lives in Linear.
43
+ - **JIRA** — native **Epic link / parent field** or a documented issue-link type for the PRD→Epic relationship where available.
44
+ - **Confluence / Notion** — no native issue hierarchy for tracker work: the PRD page carries a stable, machine-readable **generated-work section** (e.g. `## Tickets` / `## Generated Work`) listing the top-level refs and URLs.
45
+ - **Cross-vendor** (e.g. Notion PRD → JIRA tracker) — the destination ticket cannot become a native child of the PRD, so the relationship is **always** recorded in the PRD source artifact's documented section.
46
+
47
+ The documented-section fallback is always written so the generated child set is readable later without relying only on free-form comments. Comment summaries are still useful human-facing audit trails and are not removed (PRD #525 non-goal).
48
+
49
+ ## Per-vendor terminal-state predicate
50
+
51
+ A generated top-level child is **terminal** (counts as done for rollup) when it reaches the source/tracker's done/shipped state. The predicate is vendor-specific; the *semantics* ("this child has shipped") are not:
52
+
53
+ | Vendor | A generated top-level child is terminal when… |
54
+ |---|---|
55
+ | **GitHub Issues** | the issue is **closed** *and* (where the build-status label is used) carries the resolved `done` role label (`status:done` by default — env-keyed; see below). A closed-as-not-planned issue is terminal-but-dropped: it does not hold the PRD open but is excluded from "shipped" (treated like a won't-do leaf). |
56
+ | **Linear** | the Issue/Project is in a **completed** workflow state (`done`-category), **or** a **canceled** state (terminal-but-dropped, like not-planned — does not hold the PRD open, excluded from shipped). |
57
+ | **JIRA** | the issue's status is in the **Done status category** (`statusCategory.key == "done"`). |
58
+ | **Confluence / Notion** | the documented generated-work entry is marked **done** in the PRD's machine-readable section (the durable equivalent of a closed ticket, since these sources have no native ticket state). |
59
+
60
+ Notes:
61
+
62
+ - **Required vs. optional children.** Only the generated top-level children that must ship for the PRD to be complete are counted toward the all-terminal check. Won't-do / canceled / not-planned children are terminal-but-dropped: they do not hold the PRD open and are excluded from the shipped set. This mirrors `leaf-only-lifecycle`'s "required leaves" qualifier.
63
+ - **Recursion is delegated, not duplicated.** A generated Epic is terminal only when *it* has rolled up to its own terminal state per `leaf-only-lifecycle`'s parent-status-rollup (all its required Stories/Sub-tasks terminal). This rule does not re-derive an Epic's state from its leaves — it reads the top-level child's own resolved state and trusts `leaf-only-lifecycle` to have rolled that up bottom-up.
64
+ - **Blocked dominates at the report level.** If any required generated top-level child is blocked or incomplete, rollup leaves the PRD open and reports the incomplete child set (PRD #525: "rollup leaving a PRD open when at least one generated top-level child is incomplete"). The PRD is only advanced when **all** required top-level children are terminal.
65
+
66
+ ### The terminal `done` is the configured, env-keyed value — multi-env capable
67
+
68
+ Where a vendor's terminal predicate references the build-status `done` role (GitHub/Linear labels), that value is the project's configured `done` — which is **env-keyed** (`config-resolution` "Env-keyed `done`"): a map keyed by environment (`dev`, `staging`, `production`), resolved from the merged PR's base branch. This rule does **not** hardcode a `dev → staging → prod` promotion chain; a multi-env project counts a child as terminal when it reaches whichever `done` value matches the environment it shipped to.
69
+
70
+ **Single-environment collapse (this repo).** Lisa's own deploy has only `main`/`production` (`deploy.branches = production: main`, no dev/staging), so `done` is a single value, not a map, and the build lifecycle collapses to one chain: `ready → claimed (in-progress) → review (code-review) → done`. A generated top-level child is terminal when it reaches the single `status:done`; rollup never resolves a `dev` or `staging` `done` in this repo. This is the *collapsed* case of the generic rule, not a different rule — projects with more environments keep the env-keyed map.
71
+
72
+ ## PRD `shipped` transition + config-gated close/archive
73
+
74
+ When **all required** generated top-level children are terminal, the PRD rolls up to its terminal PRD-lifecycle state and, where configured, is closed/archived:
75
+
76
+ 1. **Transition to `shipped`.** Set the PRD to the configured `shipped` role (`config-resolution` PRD-lifecycle roles: `prd-shipped` label for GitHub/Linear, `Shipped` status for Notion, the shipped parent page for Confluence). The PRD lifecycle is `ready → in_review → (blocked | ticketed) → shipped`; rollup performs the `ticketed → shipped` hop only, and only on the all-terminal condition.
77
+ 2. **Config-gated close/archive.** When the source tool supports closure/archival *and* the project configures it, also close (GitHub: close the issue; Linear: move to a closed/archived state; JIRA: transition to Done; Confluence/Notion: archive where supported). Closure is **gated on configuration** via `prd.rollup.closeOnShipped` (`config-resolution`): when false (the default), the PRD is set to `shipped` but left open for a human to close; when true, rollup closes/archives it after the `shipped` transition. Never close a PRD before all generated top-level work is terminal (PRD #525 non-goal).
78
+ 3. **Partial completion is a no-op + report.** If only some required children are terminal, leave the PRD in its current state and report the incomplete/blocked child set. Do not advance, do not close.
79
+
80
+ The PRD never advances to `shipped` on its own authority — it is **derived** from the generated-top-level-child set, exactly as a container's state is derived from its leaves in `leaf-only-lifecycle`.
81
+
82
+ ## Idempotency dedupe key
83
+
84
+ Linking and rollup MUST be safe to re-run: re-running intake, backlink, or rollup never produces duplicate child links, duplicate sub-issues, duplicate generated-work entries, or a double `shipped` transition (PRD #525: "Re-running backlink or rollup is idempotent").
85
+
86
+ The dedupe key is **child-ref identity** — the stable, vendor-native identifier of each generated top-level child:
87
+
88
+ - **GitHub** — `owner/repo#number` (the issue ref).
89
+ - **Linear** — the issue/project identifier (e.g. `TEAM-123`) or its UUID.
90
+ - **JIRA** — the issue key (e.g. `PROJ-123`).
91
+ - **Confluence / Notion** — the destination ticket ref recorded in the generated-work entry (the entry is keyed by that ref, not by list position).
92
+
93
+ Apply it as follows:
94
+
95
+ - **Linking** is keyed by child-ref: before adding a native child link or a documented generated-work entry, check whether that exact child-ref is already linked/listed; if so, it's a no-op. The documented generated-work section is **regenerated** from the current child set on each run rather than appended, so re-planning never accumulates stale or duplicate entries (the same regenerate-don't-append discipline `prd-backlink` already uses for its `## Tickets` section).
96
+ - **Rollup** is keyed by the PRD's current state: a PRD already in `shipped` (and closed, when closure is configured) is a no-op — rollup does not re-transition or re-close it. Computing the all-terminal condition over the child-ref set is itself idempotent (a pure function of the children's current states).
97
+
98
+ ## Citation
99
+
100
+ Skills that link generated work to a PRD or roll a PRD up cite this rule by slug (the `prd-lifecycle-rollup` rule) instead of restating it:
101
+
102
+ - **PRD backlink / native linking** (`prd-backlink`) — record generated top-level work as native PRD children where supported; always write the documented generated-work fallback; dedupe by child-ref. *(LPC-1.1 #580, LPC-1.2 #582)*
103
+ - **PRD coverage** (`prd-ticket-coverage`) — read the generated top-level child set deterministically from the recorded relationship, not from free-form comments.
104
+ - **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)*
105
+ - **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)*
106
+ - **Idempotency** (all of the above) — dedupe-by-ref linking and no-op already-shipped rollup. *(LPC-1.4 #585)*
107
+ - **Vendor matrix documentation** — describe native-hierarchy vs documented-link fallback per supported vendor against this contract. *(LPC-1.5 #586)*
108
+
109
+ This is the PRD-level companion to `leaf-only-lifecycle` (which governs the build lifecycle and parent-from-leaves rollup); together they define the full hierarchy: a PRD owns its generated top-level work, which owns its leaves, and terminal state rolls up bottom-to-top — leaf → top-level unit → PRD.
@@ -261,6 +261,8 @@ Substrate column meanings:
261
261
 
262
262
  **acli flag note:** acli's `--output` flag does not exist; the correct flag is `--json`. List commands require `--paginate` or `--limit` (no implicit fetch-all). Several documented adapters are nominal — verify against `acli <subcmd> --help` before relying on them. When acli's adapter is broken or missing for a specific op, fall through to MCP (if identity-matched) then curl per the tier ordering.
263
263
 
264
+ **JIRA terminal-resolution note:** when a caller marks a transition as terminal per `leaf-only-lifecycle`, the substrate must not treat a Done-named status as sufficient by name alone. After `transition key:<K> to:<S>`, re-read the issue and verify `statusCategory = Done`; if the workflow requires a resolution, verify `resolution` is set. If the transition screen requires a resolution value, pass the configured default resolution when available; otherwise return a setup error so the build-intake skill can report the workflow gap instead of silently leaving an unresolved ticket in a Done-looking status.
265
+
264
266
  Operations not in this table are unsupported — add an adapter row before using them. Adapters MUST return a structured response (parse `acli`'s `--json`; jq-process curl's raw JSON).
265
267
 
266
268
  ### Payload conventions
@@ -27,6 +27,13 @@ Push current branch and create or update a pull request. Optional hint: $ARGUMEN
27
27
  5. **Auto-merge**: Choose merge strategy by PR type:
28
28
  - **Promotion PRs** (env → env, e.g. `dev` → `staging`): use `gh pr merge --auto --merge` (never squash). Squashing flattens the constituent `chore(release): X.Y.Z [skip ci]` commits into one commit titled with the PR title, stripping the `[skip ci]` markers and breaking the release workflow's promotion-detection regex — the destination branch then double-bumps its version. `--merge` keeps each `chore(release)` commit (and its `[skip ci]` marker) intact under a clean merge commit subject the workflow can recognize.
29
29
  - **Feature PRs** (anything → `dev`): use `gh pr merge --auto --merge`.
30
+ 6. **Drive to merge**: Opening the PR and enabling auto-merge is not terminal. Continue monitoring until the PR is actually `MERGED`, `CLOSED`, or a required check/review gate fails and must be fixed.
31
+ - **Auto-merge capability fallback**: If `gh pr merge --auto --merge` fails because the repository does not allow auto-merge, keep watching the required checks and review decision. Once checks are green, the review gate is clear, and the PR is mergeable, run `gh pr merge --merge` directly. Never fall back to squash.
32
+ - **BEHIND re-sync**: Poll `gh pr view <pr> --json mergeStateStatus,statusCheckRollup,reviewDecision,mergeable,state`. If `mergeStateStatus == BEHIND` after required checks are green, run `gh pr update-branch <pr>` and continue watching the new head while checks rerun.
33
+ - **Sync conflict path**: If `gh pr update-branch` reports a conflict or cannot update the branch, surface the conflicting files, fetch the base branch locally, merge the base into the PR branch, resolve conflicts, run the relevant checks, and push. If the conflict needs broader design input, escalate through the agent-team fix-task pattern with the file list and current merge state. Do not leave a PR silently stalled in an ambiguous sync state.
34
+ - **Review-gate stall**: If `reviewDecision == CHANGES_REQUESTED`, inspect unresolved review threads and blocking reviews. After the requested changes are addressed and threads are resolved, clear stale bot review blocks by dismissing the obsolete `CHANGES_REQUESTED` review where repository policy permits, or re-request review when dismissal is not allowed. A later `COMMENTED` review does not clear GitHub's prior `CHANGES_REQUESTED` state by itself.
35
+ - **Loop terminal states**: Continue the loop until the PR is `MERGED`, is `CLOSED`, checks fail, the review gate remains blocked by unresolved human feedback, or a sync conflict cannot be resolved locally. On check failures, use the existing PR watch/fix path: inspect logs, fix, commit, push, and resume the loop.
36
+ - **Harness-agnostic commands**: Use plain `gh` and `git` commands for this loop so Claude and Codex agents execute the same behavior from the shared skill.
30
37
 
31
38
  ### PR Description Format
32
39
 
@@ -215,12 +215,24 @@ Wait for `lisa:github-agent` to return. Capture its outcome:
215
215
  If `lisa:github-agent` returned Success:
216
216
 
217
217
  1. Resolve `$DONE` for this issue's PR base branch using the Workflow resolution algorithm above. If env can't be resolved and `done` is env-keyed, record an Error and skip this transition — never guess.
218
+ 2. Determine whether `$DONE` is the true terminal done value per the `leaf-only-lifecycle` rule's Terminal native closure section:
219
+ - If `github.labels.build.done` is a string, that string is terminal.
220
+ - If `github.labels.build.done` is an object, only the production/final environment value is terminal (default: `status:done`). Intermediate env values such as `status:on-dev` and `status:on-stg` are not terminal and must stay open.
221
+ - If the project uses a different final environment name, resolve it from the configured deployment topology; if ambiguous, record an Error and do not close.
218
222
 
219
223
  ```bash
220
224
  gh issue edit <number> --repo <org>/<repo> --remove-label "$CLAIMED" --add-label "$DONE"
221
225
  gh issue comment <number> --repo <org>/<repo> --body "[claude-build-intake] Build complete. PR <URL>. Transitioned to $DONE."
222
226
  ```
223
227
 
228
+ If `$DONE` is terminal, immediately close the native GitHub issue:
229
+
230
+ ```bash
231
+ gh issue close <number> --repo <org>/<repo> --reason completed
232
+ ```
233
+
234
+ This close is idempotent: if the issue is already closed, record that native closure was already satisfied and continue. If `$DONE` is an intermediate env state, leave the issue open by design.
235
+
224
236
  For any non-Success outcome, do NOT transition. The issue sits in `$CLAIMED` (or wherever `lisa:github-agent` left it) — humans take it from there.
225
237
 
226
238
  #### 3e. Continue
@@ -256,6 +268,7 @@ Total PRs opened: <n>
256
268
  - **Leaf-only claim gate runs first**: Phase 3a classifies each candidate before any claim; a container with open child work (or a childless Epic/Story/Spike) is skipped/safe-blocked, never claimed. The safe-block comment is idempotent — a re-entrant cycle does not re-post it.
257
269
  - **Claim-first ordering**: `$CLAIMED` set BEFORE `lisa:github-agent` invocation — no double-pickup.
258
270
  - **No writes outside the lifecycle**: this skill only relabels `$READY → $CLAIMED` and `$CLAIMED → $DONE`. Every other label change is owned by `lisa:github-agent`.
271
+ - **Terminal native closure**: after `$CLAIMED → $DONE`, close the GitHub issue only when `$DONE` is the true terminal done value per `leaf-only-lifecycle`; intermediate env labels stay open.
259
272
  - **Failure isolation**: per-issue exceptions caught and recorded; the cycle continues.
260
273
  - **Single cycle per repo**: do not run two `lisa:github-build-intake` cycles in parallel against the same repo — concurrent claims could race. The scheduling layer is responsible for serialization.
261
274
  - **Single-label invariant**: after every transition, verify exactly one `status:*` label is present on the issue. If two are present (rare race), surface as an Error and skip — do NOT auto-resolve.
@@ -281,6 +294,7 @@ If the repo has not adopted the `status:*` label namespace, this skill cannot ru
281
294
  - Never relabel an issue the cycle didn't claim. The `$CLAIMED` label is the signature of cycle ownership.
282
295
  - Never bypass `lisa:github-agent` to do build work directly. `lisa:github-agent` owns the per-issue lifecycle.
283
296
  - Never auto-transition past `$DONE`. Downstream labels (terminal `status:done`, etc.) are owned by QA / PM / merge automation.
297
+ - Never close a GitHub issue at intermediate env states (`status:on-dev`, `status:on-stg`, or configured equivalents). Native close happens only at the terminal `done` value.
284
298
  - If the issue has no Validation Journey or no sign-in credentials, `lisa:github-agent`'s pre-flight verify will catch it — **don't try to fix the issue from here**.
285
299
  - On any unexpected response from `lisa:github-agent` (status it doesn't claim, missing PR URL on success), record as Error and surface — never assume.
286
300
  - Never pick an arbitrary env for `$DONE` resolution. If `done` is a map and env is ambiguous, fail loudly.
@@ -116,7 +116,7 @@ Before shutting down the team, execute the Verify flow:
116
116
  4. Commit ALL outstanding changes in logical batches on the branch (minus sensitive data/information) — not just changes made by the agent team. This includes pre-existing uncommitted changes that were on the branch before the plan started. Do NOT filter commits to only "task-related" files. If it shows up in git status, it gets committed (unless it contains secrets).
117
117
  5. Push the changes - if any pre-push hook blocks you, create a task for the agent team to fix the error/problem whether it was pre-existing or not
118
118
  6. Open a pull request with auto-merge on
119
- 7. PR Watch Loop: Monitor the PR. Create a task for the agent team to resolve any code review comments by either implementing the suggestions or commenting why they should not be implemented and close the comment. Fix any failing checks and repush. Continue until all checks pass.
119
+ 7. PR Watch Loop: Monitor the PR using `git-submit-pr`'s drive-to-merge behavior. Create a task for the agent team to resolve any code review comments by either implementing the suggestions or commenting why they should not be implemented and close the comment. Fix any failing checks and repush. If the PR is `BEHIND`, blocked by stale review state, or cannot enable auto-merge, follow the harness-agnostic `git-submit-pr` re-sync, review-gate, and direct-merge fallback loop until the PR is actually merged or a blocking failure is surfaced.
120
120
  8. Merge the PR
121
121
  9. Monitor the deploy action that triggers automatically from the successful merge
122
122
  10. If deploy fails, create a task for the agent team to fix the failure, open a new PR and then go back to step 7
@@ -189,8 +189,13 @@ Wait for `lisa:jira-agent` to return. Capture its outcome:
189
189
 
190
190
  If `lisa:jira-agent` returned Success:
191
191
  1. Resolve `$DONE` for this ticket's PR base branch using the Workflow resolution algorithm above. If env can't be resolved and `done` is env-keyed, record an Error and skip this transition — never guess.
192
- 2. Invoke `lisa:atlassian-access` `operation: transition key: <TICKET> to: "$DONE"`.
193
- 3. Post a `[claude-build-intake]` comment via `lisa:atlassian-access` `operation: comment key: <TICKET> body: "Build complete. PR <URL>. Transitioned to $DONE."`
192
+ 2. Determine whether `$DONE` is the true terminal done value per the `leaf-only-lifecycle` rule's Terminal native closure section:
193
+ - If `jira.workflow.done` is a string, that status is terminal.
194
+ - If `jira.workflow.done` is an object, only the production/final environment value is terminal (default: `Done`). Intermediate env statuses such as `On Dev` and `On Stg` are not terminal and must remain unresolved / open.
195
+ - If the project uses a different final environment name, resolve it from the configured deployment topology; if ambiguous, record an Error and do not finalize native resolution.
196
+ 3. Invoke `lisa:atlassian-access` `operation: transition key: <TICKET> to: "$DONE"`.
197
+ 4. If `$DONE` is terminal, verify the resulting JIRA issue is natively closed/resolved: status category is `Done`, and resolution is set when the project's workflow requires one. If the transition screen requires an explicit resolution, use the configured default resolution if present; otherwise record an Error naming the missing workflow setup rather than silently landing in an unresolved Done-named status.
198
+ 5. Post a `[claude-build-intake]` comment via `lisa:atlassian-access` `operation: comment key: <TICKET> body: "Build complete. PR <URL>. Transitioned to $DONE."` Include whether terminal native resolution was verified, already satisfied, skipped for an intermediate env, or blocked by workflow setup.
194
199
 
195
200
  For any non-Success outcome, do NOT transition. The ticket sits in `$CLAIMED` (or wherever `lisa:jira-agent` left it for the Blocked case) — the cycle's job is done; humans take it from there.
196
201
 
@@ -226,7 +231,8 @@ Total PRs opened: <n>
226
231
 
227
232
  - **Leaf-only claim gate runs first**: Phase 3a classifies each candidate before any claim; a container with open child work (or a childless Epic/Story/Spike) is skipped/safe-blocked, never claimed (the `leaf-only-lifecycle` rule's claim-time arm). The safe-block comment is idempotent — a re-entrant cycle does not re-post it.
228
233
  - **Claim-first ordering**: `$CLAIMED` set BEFORE `lisa:jira-agent` invocation — no double-pickup.
229
- - **No writes outside the lifecycle**: this skill only transitions `$READY → $CLAIMED` and `$CLAIMED → $DONE`. Every other status change is owned by `lisa:jira-agent` (which suggests transitions but only auto-transitions on the verify-FAIL path).
234
+ - **No writes outside the lifecycle**: this skill only transitions `$READY → $CLAIMED` and `$CLAIMED → $DONE`, then verifies terminal native resolution when `$DONE` is the true terminal state per `leaf-only-lifecycle`. Every other status change is owned by `lisa:jira-agent` (which suggests transitions but only auto-transitions on the verify-FAIL path).
235
+ - **Terminal native closure**: for terminal `$DONE`, the resulting JIRA issue must be in a resolved / closed state (`statusCategory = Done` and resolution set when required). Intermediate env statuses stay unresolved / open.
230
236
  - **Failure isolation**: per-ticket exceptions caught and recorded; the cycle continues.
231
237
  - **Single cycle per query**: do not run two `lisa:jira-build-intake` cycles in parallel against overlapping queries — concurrent claims could race. The scheduling layer (when added) is responsible for serialization.
232
238
  - **Never invent a transition**: if `$CLAIMED` or `$DONE` aren't valid transitions in the project's workflow, stop and report rather than guessing alternative names.
@@ -261,6 +267,7 @@ If a ready-equivalent status does not exist in the JIRA project's workflow, this
261
267
  - Never transition a ticket the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
262
268
  - 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.
263
269
  - Never auto-transition past `$DONE`. Downstream statuses are owned by QA / product / a future verification-intake skill — not this one.
270
+ - Never resolve / close a JIRA ticket at intermediate env statuses (`On Dev`, `On Stg`, or configured equivalents). Native resolution is terminal-only.
264
271
  - If the ticket has no Validation Journey or no sign-in credentials in its description, `lisa:jira-agent`'s pre-flight verify will catch it and transition to `Blocked` — **don't try to fix the ticket from here**. Pre-flight gating is `lisa:jira-agent`'s job; running build work on a thin ticket produces broken work.
265
272
  - On any unexpected response from `lisa:jira-agent` (status it doesn't claim, missing PR URL on success, etc.), record as Error and surface — never assume.
266
273
  - Never pick an arbitrary env for `$DONE` resolution. If `done` is a map and env is ambiguous, fail loudly.
@@ -71,7 +71,7 @@ In prose below, the role names refer to the resolved labels: e.g. "the `ready` l
71
71
 
72
72
  ## Why labels, not native states
73
73
 
74
- Linear's per-team workflow state names vary (`Todo` / `Backlog` / `Up Next` / etc.). Labels are workspace-scoped or team-scoped and stable across teams, so we drive the build queue off labels rather than chasing renamed native states. The native `state` field is informational only for this skill.
74
+ Linear's per-team workflow state names vary (`Todo` / `Backlog` / `Up Next` / etc.). Labels are workspace-scoped or team-scoped and stable across teams, so we drive the build queue off labels rather than chasing renamed native states. The native `state` field is informational until terminal completion; at the true terminal `done` value, the `leaf-only-lifecycle` rule requires native closure by moving the Issue to a configured Done / Completed workflow state when one exists.
75
75
 
76
76
  ## Configuration
77
77
 
@@ -199,8 +199,13 @@ Wait for the agent to return. Capture its outcome:
199
199
 
200
200
  If `lisa:linear-agent` returned Success:
201
201
  1. Resolve `$DONE` for this issue's PR base branch using the Workflow resolution algorithm above. If env can't be resolved and `done` is env-keyed, record an Error and skip this transition — never guess.
202
- 2. Update labels via `mcp__linear-server__save_issue`: remove `$CLAIMED` (or `$REVIEW` if `lisa:linear-evidence` already moved it forward), add `$DONE`.
203
- 3. Post a `[claude-build-intake]` comment: `"Build complete. PR <URL>. Transitioned to $DONE."`
202
+ 2. Determine whether `$DONE` is the true terminal done value per the `leaf-only-lifecycle` rule's Terminal native closure section:
203
+ - If `linear.labels.build.done` is a string, that string is terminal.
204
+ - If `linear.labels.build.done` is an object, only the production/final environment value is terminal (default: `status:done`). Intermediate env values such as `status:on-dev` and `status:on-stg` are not terminal and must keep the native Issue open.
205
+ - If the project uses a different final environment name, resolve it from the configured deployment topology; if ambiguous, record an Error and do not change the native state.
206
+ 3. Update labels via `mcp__linear-server__save_issue`: remove `$CLAIMED` (or `$REVIEW` if `lisa:linear-evidence` already moved it forward), add `$DONE`.
207
+ 4. If `$DONE` is terminal, move the native Linear Issue state to the configured Done / Completed state. Resolve that state from project configuration if present; otherwise inspect the team workflow for a terminal state with `state.type = "completed"` and a name such as `Done` or `Completed`. If no terminal state can be resolved, record an Error and leave the labels as the source of truth — do not invent a state name.
208
+ 5. Post a `[claude-build-intake]` comment: `"Build complete. PR <URL>. Transitioned to $DONE."` Include whether native closure was applied, already satisfied, skipped for an intermediate env, or unavailable for setup reasons.
204
209
 
205
210
  For any non-Success outcome, do NOT transition. The Issue sits where the agent left it — humans take it from there.
206
211
 
@@ -236,7 +241,8 @@ Total PRs opened: <n>
236
241
 
237
242
  - **Leaf-only claim gate runs first**: Phase 3a classifies each candidate before any claim; a container with open child work (or a childless Epic/Story/Spike) is skipped/safe-blocked, never claimed (the `leaf-only-lifecycle` rule's claim-time arm). The safe-block comment is idempotent — a re-entrant cycle does not re-post it.
238
243
  - **Claim-first ordering**: `$CLAIMED` set BEFORE agent invocation — no double-pickup.
239
- - **No writes outside the lifecycle**: this skill only adds/removes `$READY`, `$CLAIMED`, `$DONE`. Every other label change (and the native state) is owned by the agent or `lisa:linear-evidence`.
244
+ - **No writes outside the lifecycle**: this skill only adds/removes `$READY`, `$CLAIMED`, `$DONE`, plus terminal-only native state completion required by `leaf-only-lifecycle`. Every other label change (and non-terminal native state change) is owned by the agent or `lisa:linear-evidence`.
245
+ - **Terminal native closure**: after the `$DONE` label is applied, move the Linear Issue to a native completed state only when `$DONE` is the true terminal done value; intermediate env labels stay open / active.
240
246
  - **Failure isolation**: per-Issue exceptions caught and recorded; the cycle continues.
241
247
  - **Single cycle per team**: do not run two concurrent cycles against the same team — concurrent claims could race.
242
248
  - **Single-label invariant**: after every transition, verify exactly one `status:*` label is present. Two simultaneously breaks the build queue.
@@ -258,6 +264,7 @@ If the team hasn't adopted these labels, the first run exits with an adoption hi
258
264
  - Never relabel an Issue the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
259
265
  - Never bypass `lisa:linear-agent` to do build work directly. The agent owns the per-Issue lifecycle.
260
266
  - Never auto-transition past `$DONE`. Downstream labels are owned by QA / product / a future verification-intake skill.
267
+ - Never move the native Linear state to Done / Completed for intermediate env states (`status:on-dev`, `status:on-stg`, or configured equivalents). Native completion happens only at the terminal `done` value.
261
268
  - If the Issue has no Validation Journey or no sign-in credentials in its description, `lisa:linear-agent`'s pre-flight verify will catch it and relabel to `status:blocked` — don't try to fix the Issue from here.
262
269
  - On any unexpected response from `lisa:linear-agent` (label it doesn't claim, missing PR URL on success, etc.), record as Error and surface — never assume.
263
270
  - Never pick an arbitrary env for `$DONE` resolution. If `done` is a map and env is ambiguous, fail loudly.
@@ -10,6 +10,8 @@ Thin dispatcher. Resolves the configured destination tracker and delegates to th
10
10
 
11
11
  See the `config-resolution` rule for configuration and dispatch table.
12
12
 
13
+ The vendor scanners also own the terminal native-closure step from `leaf-only-lifecycle`: after a leaf reaches the true terminal `done` value, they close / resolve / complete the native tracker item where supported, while leaving intermediate env states open.
14
+
13
15
  ## Workflow
14
16
 
15
17
  1. Resolve tracker config (same logic as `lisa:tracker-write`).
@@ -37,9 +39,22 @@ This is the claim-time arm of the rule. Its siblings are the write-time labeling
37
39
 
38
40
  The shim never needs to inspect the item itself — it forwards `$ARGUMENTS` verbatim and the resolved vendor scanner runs its Phase 3a gate before any claim.
39
41
 
42
+ ## Terminal native-closure contract (forwarded to every vendor)
43
+
44
+ This shim also forwards the `leaf-only-lifecycle` terminal native-closure contract. It does not decide whether a `done` value is terminal; the vendor scanner resolves that from its own config and deployment topology after the per-item agent succeeds.
45
+
46
+ | Tracker | Vendor scanner behavior at true terminal `done` |
47
+ |---|---|
48
+ | `github` | apply the terminal `done` label, then `gh issue close --reason completed` |
49
+ | `jira` | transition to the configured terminal status and verify native resolved / closed state |
50
+ | `linear` | apply the terminal `done` label, then move the Issue to the configured completed workflow state |
51
+
52
+ Intermediate env states are not native closure. A vendor scanner that resolves `On Dev`, `On Stg`, `status:on-dev`, `status:on-stg`, or a configured equivalent leaves the item open / unresolved.
53
+
40
54
  ## Rules
41
55
 
42
56
  - Single cycle per invocation — the vendor skill processes the current `Ready` set and exits.
43
57
  - The vendor skills run their own pre-flight checks (JIRA workflow transitions for the JIRA path; label namespace adoption for the GitHub and Linear paths) before processing items. Never bypass.
44
58
  - **Leaf-only claim, every vendor.** Per the `leaf-only-lifecycle` rule, each vendor scanner claims leaf work units only and skips / safe-blocks a container (open child work, or a childless Epic/Story/Spike) carrying a stale build-ready role. This shim does not re-implement the gate — it relies on the vendor scanner's Phase 3a — but the contract is uniform across `jira`, `github`, and `linear` so behavior never drifts by tracker.
59
+ - **Terminal native closure, every capable vendor.** Per the same rule, each vendor scanner finalizes native open/closed state only at the true terminal `done` value. This shim never performs native closure itself, but callers can rely on the dispatched vendor scanner to apply the contract.
45
60
  - Never run two intake cycles concurrently against overlapping queues — the scheduling layer is responsible for serialization.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.39.3",
3
+ "version": "2.41.1",
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.39.3",
3
+ "version": "2.41.1",
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.39.3",
3
+ "version": "2.41.1",
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.39.3",
3
+ "version": "2.41.1",
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.39.3",
3
+ "version": "2.41.1",
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.39.3",
3
+ "version": "2.41.1",
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.39.3",
3
+ "version": "2.41.1",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"