@codyswann/lisa 2.126.1 → 2.126.2

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 (84) 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 +1 -1
  5. package/plugins/lisa/rules/eager/leaf-only-lifecycle.md +5 -3
  6. package/plugins/lisa/rules/reference/leaf-only-lifecycle.md +13 -8
  7. package/plugins/lisa/skills/github-sync/SKILL.md +7 -6
  8. package/plugins/lisa/skills/jira-sync/SKILL.md +7 -6
  9. package/plugins/lisa/skills/linear-sync/SKILL.md +6 -5
  10. package/plugins/lisa/skills/repair-intake/SKILL.md +63 -29
  11. package/plugins/lisa/skills/tracker-sync/SKILL.md +5 -4
  12. package/plugins/lisa-agy/commands/repair-intake.md +1 -1
  13. package/plugins/lisa-agy/plugin.json +1 -1
  14. package/plugins/lisa-agy/skills/github-sync/SKILL.md +7 -6
  15. package/plugins/lisa-agy/skills/jira-sync/SKILL.md +7 -6
  16. package/plugins/lisa-agy/skills/linear-sync/SKILL.md +6 -5
  17. package/plugins/lisa-agy/skills/repair-intake/SKILL.md +63 -29
  18. package/plugins/lisa-agy/skills/tracker-sync/SKILL.md +5 -4
  19. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  21. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  22. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-copilot/commands/repair-intake.md +1 -1
  26. package/plugins/lisa-copilot/rules/eager/leaf-only-lifecycle.md +5 -3
  27. package/plugins/lisa-copilot/rules/reference/leaf-only-lifecycle.md +13 -8
  28. package/plugins/lisa-copilot/skills/github-sync/SKILL.md +7 -6
  29. package/plugins/lisa-copilot/skills/jira-sync/SKILL.md +7 -6
  30. package/plugins/lisa-copilot/skills/linear-sync/SKILL.md +6 -5
  31. package/plugins/lisa-copilot/skills/repair-intake/SKILL.md +63 -29
  32. package/plugins/lisa-copilot/skills/tracker-sync/SKILL.md +5 -4
  33. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-cursor/commands/repair-intake.md +1 -1
  35. package/plugins/lisa-cursor/rules/leaf-only-lifecycle-reference.mdc +13 -8
  36. package/plugins/lisa-cursor/rules/leaf-only-lifecycle.mdc +5 -3
  37. package/plugins/lisa-cursor/skills/github-sync/SKILL.md +7 -6
  38. package/plugins/lisa-cursor/skills/jira-sync/SKILL.md +7 -6
  39. package/plugins/lisa-cursor/skills/linear-sync/SKILL.md +6 -5
  40. package/plugins/lisa-cursor/skills/repair-intake/SKILL.md +63 -29
  41. package/plugins/lisa-cursor/skills/tracker-sync/SKILL.md +5 -4
  42. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  44. package/plugins/lisa-expo-agy/plugin.json +1 -1
  45. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  49. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  50. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  54. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  55. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  59. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  60. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  64. package/plugins/lisa-rails-agy/plugin.json +1 -1
  65. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  69. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  70. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  74. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  75. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  77. package/plugins/src/base/commands/repair-intake.md +1 -1
  78. package/plugins/src/base/rules/eager/leaf-only-lifecycle.md +5 -3
  79. package/plugins/src/base/rules/reference/leaf-only-lifecycle.md +13 -8
  80. package/plugins/src/base/skills/github-sync/SKILL.md +7 -6
  81. package/plugins/src/base/skills/jira-sync/SKILL.md +7 -6
  82. package/plugins/src/base/skills/linear-sync/SKILL.md +6 -5
  83. package/plugins/src/base/skills/repair-intake/SKILL.md +63 -29
  84. package/plugins/src/base/skills/tracker-sync/SKILL.md +5 -4
@@ -1,6 +1,6 @@
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 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) — but for a stalled build it first diagnoses the PR/deploy state and, if the PR cannot merge (conflict, rebase-required, failing checks, unaddressed CodeRabbit/changes-requested) or a deploy failed, files a build-ready leaf fix ticket and moves the item to `blocked` (blocked by that ticket) rather than re-dispatching, 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."
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) — but for a stalled build it first diagnoses the PR/deploy state and, if the PR cannot merge (conflict, rebase-required, failing checks, unaddressed CodeRabbit/changes-requested) or a deploy failed, files a build-ready leaf fix ticket and moves the item to `blocked` (blocked by that ticket) rather than re-dispatching, 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, reconciles parent rollups to their derived state per leaf-only-lifecycle — including the intermediate-env case (e.g. all children at `On Stg` → parent `On Stg`) and a container wrongly stuck in `ready` — and closes out 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` leaves (a container wrongly carrying `ready` is the one exception — it is rolled up from its children, since `ready` on a parent is an invariant violation, not intake's claim signal). 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
 
@@ -24,9 +24,16 @@ close-out** roles and moves work *unstuck* or *fully closed*:
24
24
  research/waiting resolves the ambiguity that stopped it.
25
25
  - **Terminal-open drift** — an item already carrying its true terminal lifecycle role (for
26
26
  example GitHub `status:done`) but still open/active in the provider's native state.
27
- - **Completed rollup drift** — a parent/container item (Epic, Story, PRD, Linear Project, or
28
- equivalent) whose associated child set is fully terminal but whose own lifecycle/native state has
29
- not been closed out.
27
+ - **Rollup drift** — a parent/container item (Epic, Story, PRD, Linear Project, or equivalent)
28
+ whose own lifecycle state does not match the roll-up of its children's states per
29
+ `leaf-only-lifecycle`. This covers the *completed* case (all children terminal → close the parent
30
+ out) **and** the *intermediate-env* case (all children shipped to an env like `On Stg`, but the
31
+ parent never advanced — including a parent left stranded in a status it should never carry).
32
+ - **Stale-`ready` container** — a parent/container (open child work, or a childless
33
+ Epic/Story/Spike) wrongly carrying the build-ready role. This is a leaf-only-invariant violation
34
+ the build-intake claim gate deliberately leaves for a human; repair-intake reconciles it by
35
+ rolling the parent up from its children (with an audit note), so a container never sits in `ready`
36
+ indefinitely.
30
37
 
31
38
  This skill is the symmetric counterpart to `lisa:intake`. It reuses the same queue-detection,
32
39
  the same agent-team orchestration, the same "don't ask, just run" confirmation policy, and the
@@ -131,9 +138,9 @@ claim-and-advance). The essentials, inlined here so this skill is self-complete:
131
138
  | Confluence **parent page** URL/ID | PRD (Confluence, narrowed) | source=confluence | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
132
139
  | Linear **workspace** URL, **team** URL/key, or literal `linear` | PRD (Linear) | source=linear | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
133
140
  | GitHub **repo** URL / `org/repo` (PRD namespace) | PRD (GitHub) | source=github | `in_review`, `blocked`, terminal/open PRDs, all-terminal generated-work rollups |
134
- | GitHub **repo** URL / `org/repo` with `tracker = github` (build namespace) | Build (GitHub) | tracker=github | `claimed`, `blocked`, terminal/open issues, all-terminal parent rollups |
141
+ | GitHub **repo** URL / `org/repo` with `tracker = github` (build namespace) | Build (GitHub) | tracker=github | `claimed`, `blocked`, terminal/open issues, parent rollups (intermediate-env + all-terminal), stale-`ready` containers |
135
142
  | Literal `github` | GitHub; route by `intake_mode` (`prd` / `build` / `both`) | per lifecycle | per lifecycle above |
136
- | JIRA project key or full JQL | Build (JIRA) | tracker=jira | `claimed`, `blocked`, terminal/closure verification, all-terminal parent rollups |
143
+ | JIRA project key or full JQL | Build (JIRA) | tracker=jira | `claimed`, `blocked`, terminal/closure verification, parent rollups (intermediate-env + all-terminal), stale-`ready` containers |
137
144
 
138
145
  Disambiguation (same as `lisa:intake`): a `notion.so`/`notion.site` URL → Notion; an Atlassian
139
146
  `/wiki/spaces/<KEY>` URL → Confluence (with `/pages/<id>` → parent-page narrowing); a
@@ -355,24 +362,37 @@ native-open / active / unresolved:
355
362
  4. Post a compact `[lisa-repair-intake]` note only when the native close-out changed state or when
356
363
  an actionable setup error must be surfaced. Do not spam already-closed terminal items.
357
364
 
358
- ### Build rollup with all children terminal close out parent/container
365
+ ### Build parent rollup reconciliation (intermediate-env or terminal close-out)
359
366
 
360
- For each parent/container item (Epic, Story, Spike, Project, or any item with child work) whose
361
- required child set is fully terminal:
367
+ For each parent/container item (Epic, Story, Spike, Project, or any item with child work),
368
+ reconcile its lifecycle state with the roll-up of its children — **including the intermediate-env
369
+ case**, not only fully-terminal close-out. This is the recovery-side complement to the forward
370
+ rollup the `*-sync --rollup` skills perform; it catches a parent that was never rolled up (or was
371
+ left in a status it should not carry, including a stale build-ready `ready`).
362
372
 
363
373
  1. Read the child set using the vendor-native hierarchy first (GitHub sub-issues, JIRA
364
374
  Epic/parent/sub-task hierarchy, Linear project/parent/sub-issues), with the same fallbacks the
365
375
  vendor read/sync skills document.
366
- 2. Evaluate bottom-up per `leaf-only-lifecycle`: every required child must already be terminal.
376
+ 2. **Compute the derived parent state** bottom-up per the `leaf-only-lifecycle` **Parent status
377
+ rollup** state machine, evaluated over the env ladder `in-progress < dev < staging <
378
+ production` (the ordered keys of the env-keyed `done` map): any required child blocked →
379
+ `blocked`; else every required child shipped to some env → the **least-advanced** env among
380
+ them (e.g. all `On Stg` → `On Stg`); else any child started → `claimed`; else unchanged.
367
381
  Optional / won't-do / not-planned children are terminal-but-dropped and do not hold the parent
368
382
  open.
369
- 3. Apply the configured terminal rollup role to the parent/container, removing any stale build
370
- lifecycle role that conflicts.
371
- 4. Immediately perform terminal native closure where the provider supports it (GitHub close,
372
- Linear complete, JIRA resolved/closed). A completed rollup parent should not remain open in
373
- GitHub merely because no leaf agent touched it.
374
- 5. If any required child is incomplete, active, blocked, or inaccessible, leave the parent open and
375
- record it as `still_blocked` or `active` with the current child tally.
383
+ 3. **If the derived state differs from the parent's current state, apply it** via the vendor's
384
+ lifecycle write (JIRA transition, GitHub/Linear label swap keeping exactly one `status:*`),
385
+ removing any conflicting stale build lifecycle role **including a stale `ready`** the parent
386
+ should never carry. Post an idempotent `[lisa-repair-intake]` rollup note naming the derived
387
+ state and the child tally (honor the backoff window + fingerprint).
388
+ 4. **Perform native closure only at the true terminal `done`.** When and only when — the derived
389
+ env is the production/terminal value, finalize through the provider-native mechanism (GitHub
390
+ `gh issue close --reason completed`, Linear move to Done state, JIRA resolved/closed verified at
391
+ `statusCategory = Done`). An intermediate-env rollup (`On Dev`/`On Stg`) advances the parent's
392
+ status but **must not** close it — it is still open per `leaf-only-lifecycle`.
393
+ 5. If the derived state is `unchanged` (children exist but none started) or the required set is
394
+ ambiguous / inaccessible, leave the parent as-is and record it as `active` or `still_blocked`
395
+ with the current child tally; never guess a transition.
376
396
 
377
397
  ### PRD `in_review` (stalled in-progress) → re-run validate→route
378
398
 
@@ -483,8 +503,9 @@ cron tick.
483
503
  ## Lifecycle ownership guard
484
504
 
485
505
  repair-intake owns the repair surfaces needed to recover stuck work and close-out drift:
486
- build `claimed` / `blocked`, PRD `in_review` / `blocked`, terminal-labeled native-open items, and
487
- parent/container rollups whose child sets are already terminal. It MAY:
506
+ build `claimed` / `blocked`, PRD `in_review` / `blocked`, terminal-labeled native-open items,
507
+ parent/container rollups (intermediate-env *and* fully-terminal), and stale-`ready` containers.
508
+ It MAY:
488
509
 
489
510
  - Apply the build scanner's post-agent `claimed → done` on a successful resume (it is finishing
490
511
  the scanner's interrupted job), and move a dependency-cleared build item `blocked → claimed`.
@@ -492,8 +513,14 @@ parent/container rollups whose child sets are already terminal. It MAY:
492
513
  as the PRD intake does.
493
514
  - Close / complete / resolve build items that already carry the true terminal `done` role but are
494
515
  still natively open, per `leaf-only-lifecycle`.
495
- - Roll up a parent/container to the configured terminal state and close/complete/resolve it when
496
- all required children are terminal.
516
+ - Roll up a parent/container to its derived state per the `leaf-only-lifecycle` state machine —
517
+ **including an intermediate env value** (`On Dev`/`On Stg`) when all required children have
518
+ reached that env — and close/complete/resolve it **only** when the derived env is the true
519
+ terminal `done`.
520
+ - Reconcile a **container** wrongly carrying the build-ready `ready` role (a leaf-only-invariant
521
+ violation) by rolling it up from its children and removing the `ready`, with a
522
+ `[lisa-repair-intake]` audit note. This is the one `ready`-touching exception (see MUST NOT) and
523
+ applies only to containers, never to leaves.
497
524
  - Move a PRD with fully terminal generated work to `shipped` and close/archive the source artifact
498
525
  where the source vendor supports native close-out, per `prd-lifecycle-rollup`.
499
526
 
@@ -502,21 +529,27 @@ It MUST NOT:
502
529
  - Move a PRD out of `draft` or `verified` (those are product-owned), or set `verified` itself.
503
530
  - Apply a build `done` value other than via the env-resolution rules, or close a native item at
504
531
  any value other than the true terminal `done` (see `leaf-only-lifecycle`).
505
- - Touch `ready` items (that is `lisa:intake`'s lane).
532
+ - Touch `ready` **leaves** (that is `lisa:intake`'s lane). A container carrying `ready` is the
533
+ documented exception above — repair-intake reconciles it because `ready` on a parent is an
534
+ invariant violation, not the human "claim this leaf" signal intake owns.
506
535
 
507
536
  ## Cycle behavior
508
537
 
509
538
  1. **Resolve the queue** — detect vendor/lifecycle (Source dispatch); resolve stuck role names
510
539
  from config. For JIRA, confirm the needed transitions are reachable; stop on misconfig.
511
540
  2. **Enumerate repair candidates** — query in-progress role(s), `blocked` role(s), terminal/open
512
- items, and rollup parents/PRDs with child work for the detected lifecycle(s), up to
541
+ items, rollup parents/PRDs with child work, and **containers carrying the `ready` role** (a
542
+ leaf-only-invariant violation to reconcile), for the detected lifecycle(s), up to
513
543
  `max_candidates`, via the Access layer reads.
514
544
  3. **Order deterministically**, highest repair-confidence first:
515
545
  1. terminal-labeled items that only need native close / complete / resolve,
516
- 2. rollup parents/PRDs whose child sets are all terminal,
517
- 3. `blocked` items whose dependencies are now **cleared** (safe, high-value, one-cycle wins),
518
- 4. `blocked` items with **new clarifying answers**,
519
- 5. **stalled** in-progress items, oldest activity first.
546
+ 2. rollup parents/PRDs whose child sets are all terminal (close-out),
547
+ 3. rollup parents whose children have advanced to an intermediate env, or stale-`ready`
548
+ containers, that need their derived state applied (status-only reconciliation, no native
549
+ close),
550
+ 4. `blocked` items whose dependencies are now **cleared** (safe, high-value, one-cycle wins),
551
+ 5. `blocked` items with **new clarifying answers**,
552
+ 6. **stalled** in-progress items, oldest activity first.
520
553
  4. **Walk the ordered list**, evaluating each candidate (terminal close-out, rollup child tally,
521
554
  staleness, dependency, answer checks), and repair **every** candidate that is actionable inside
522
555
  the `max_candidates` cap. Continue after successful writes and after per-item errors.
@@ -539,8 +572,9 @@ Report outcomes in these buckets:
539
572
  `ticketed`.
540
573
  - `closed_out` — terminal-labeled items whose native open/active state was closed, completed,
541
574
  resolved, or archived.
542
- - `rolled_up` — parent/container/PRD rollups advanced because all associated children were
543
- terminal.
575
+ - `rolled_up` — parent/container/PRD rollups advanced to their derived state: an intermediate env
576
+ (e.g. all children at `On Stg` → parent `On Stg`), a fully-terminal close-out, or a stale-`ready`
577
+ container reconciled from its children.
544
578
  - `still_blocked` — examined and intentionally left `blocked`, with the active reason.
545
579
  - `active` — skipped because current work is not stale (or within backoff).
546
580
  - `errors` — items that failed evaluation, with the error.
@@ -28,19 +28,20 @@ If `$ARGUMENTS` is empty, all vendor skills auto-detect a ticket reference from
28
28
 
29
29
  When the caller passes `--rollup` after the milestone, the dispatch target additionally **derives the parent/container's lifecycle state from its children** instead of acting on the work item directly. This is the vendor-neutral implementation of the **Parent status rollup (the state machine)** section of the `leaf-only-lifecycle` rule — cite that rule, do not restate the policy here. The shim is dispatch only; the rollup mechanics live in the vendor sync skill (`lisa:github-sync`, `lisa:jira-sync`, `lisa:linear-sync`), which resolves child membership via its `*-read-*` skill and evaluates the state machine below.
30
30
 
31
- The state machine (first match wins, evaluated over the **required** leaves only):
31
+ The state machine (first match wins, evaluated over the **required** leaves only, on the env ladder `in-progress < dev < staging < production` — the ordered keys of the project's env-keyed `done` map):
32
32
 
33
33
  | If among the required leaves… | …the parent rolls up to | Role |
34
34
  |---|---|---|
35
35
  | any leaf is **blocked** | blocked / attention-needed | `blocked` |
36
- | else any leaf is **in progress** (claimed or in review) | active / in-progress | `claimed` |
37
- | else **all** required leaves are **terminal** | the configured rollup terminal | `done` (or `review` where supported) |
36
+ | else **every** required leaf has shipped to some env (each at a `done`-map value) | the **least-advanced** env among them | `done[min-env]` (terminal `done` at production) |
37
+ | else any leaf has **started** (claimed or in review, or shipped while a sibling has not) | active / in-progress | `claimed` (or `review` where supported) |
38
38
  | else (leaves exist, none started) | unchanged | — |
39
39
 
40
40
  - **Blocked dominates** — one blocked leaf surfaces blocked on the parent even while others progress.
41
+ - **Least-advanced env wins** — a parent reaches an env only once all required leaves have reached at least that env (all `On Stg` → `On Stg`; mixed dev/staging → the dev value). Native terminal closure fires only at the production `done`, never at an intermediate env.
41
42
  - **The parent never carries `ready`** — `ready` is a human "claim this leaf" signal; rollup only moves a parent between non-ready container states.
42
43
  - **Rollup is recursive** — an Epic rolls up from its Stories, each of which rolls up from its own leaves. Evaluate bottom-up.
43
- - **The terminal is the configured env-keyed `done`** — multi-env projects roll up to whichever `done` value matches the env their leaves shipped to (see `config-resolution` "Env-keyed `done`"). **Single-environment collapse (this repo):** `deploy.branches` declares only `production: main`, so `done` is a single value and the GitHub build lifecycle collapses to `ready → claimed (in-progress) → done`; the rollup terminal is simply `done` (or the PRD-side `ticketed` for PRD containers), with **no** dev/staging promotion hops and **no** env-keyed multi-entry chain to resolve.
44
+ - **The env rungs are the configured env-keyed `done`** — multi-env projects roll up to whichever `done` value (including intermediate `On Dev`/`On Stg`) their leaves have collectively reached (see `config-resolution` "Env-keyed `done`"). **Single-environment collapse (this repo):** `deploy.branches` declares only `production: main`, so `done` is a single value, the only env rung is production, and the GitHub build lifecycle collapses to `ready → claimed (in-progress) → done`; the rollup terminal is simply `done` (or the PRD-side `ticketed` for PRD containers), with **no** dev/staging promotion hops and **no** env-keyed multi-entry chain to resolve.
44
45
 
45
46
  **Safe-by-default when not yet supported.** A vendor sync path that has not implemented native rollup MUST be a documented no-op that surfaces the derived state as a suggestion/comment rather than guessing a transition — never an unsafe default. Without `--rollup`, the sync skills behave exactly as before (milestone comment on the work item; no parent derivation).
46
47
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.126.1",
3
+ "version": "2.126.2",
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.126.1",
3
+ "version": "2.126.2",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.126.1",
3
+ "version": "2.126.2",
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.126.1",
3
+ "version": "2.126.2",
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.126.1",
3
+ "version": "2.126.2",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.126.1",
3
+ "version": "2.126.2",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,5 +1,5 @@
1
1
  ---
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 — but for a stalled build it first diagnoses the PR/deploy state and, if the PR cannot merge (conflict, rebase, failing checks, unaddressed CodeRabbit/changes-requested) or a deploy failed, files a build-ready fix ticket and moves the item to `blocked` (blocked by it) instead of re-dispatching — 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."
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 — but for a stalled build it first diagnoses the PR/deploy state and, if the PR cannot merge (conflict, rebase, failing checks, unaddressed CodeRabbit/changes-requested) or a deploy failed, files a build-ready fix ticket and moves the item to `blocked` (blocked by it) instead of re-dispatching — re-validates blocked PRDs, re-dispatches blocked build items whose blockers have cleared, performs terminal native closure, reconciles parent rollups to their derived state (including the intermediate-env case — e.g. all children at `On Stg` → parent `On Stg` — and a container wrongly stuck in `ready`), 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=2h] [max_candidates=100] [force=true]"
4
4
  ---
5
5
 
@@ -19,12 +19,14 @@ A container *type* with no children is structurally a leaf — and may be build-
19
19
 
20
20
  ## Parent state rollup (priority order, first match wins)
21
21
 
22
+ Evaluate over the env ladder `in-progress < dev < staging < production` (the ordered keys of the project's env-keyed `done` map; single-env projects have only the production rung):
23
+
22
24
  1. Any leaf is **blocked** → parent rolls up to **blocked / attention-needed**.
23
- 2. Else any leaf is **claimed or in review** → parent is **in-progress**.
24
- 3. Else all required leaves are **terminal (`done`)** parent reaches the configured rollup terminal (env-keyed `done`).
25
+ 2. Else **every** required leaf has shipped to some env → parent rolls up to the **least-advanced** env among them (all `On Stg` → `On Stg`; mixed `On Dev`+`On Stg` → `On Dev`; all production → terminal `done`).
26
+ 3. Else any leaf has **started** (claimed/in review, or shipped while a sibling has not) → parent is **in-progress** (`claimed`).
25
27
  4. Else (leaves exist but none started) → parent unchanged.
26
28
 
27
- **Blocked dominates.** Optional/won't-do children do not hold a parent open. Rollup is recursive — bottom-up. The parent never carries `ready`.
29
+ **Blocked dominates.** A parent reaches an env only once all required leaves have reached at least that env. Intermediate-env rollup (`On Dev`/`On Stg`) happens, but native closure fires only at production `done`. Optional/won't-do children do not hold a parent open. Rollup is recursive — bottom-up. The parent never carries `ready`; a container found in `ready` is reconciled by rolling it up from its children.
28
30
 
29
31
  ## Terminal native closure
30
32
 
@@ -60,26 +60,31 @@ So the exception is narrow: childlessness *enables* build-ready only for types t
60
60
 
61
61
  A parent/container never sets its own lifecycle state; it **derives** it from the roll-up of its children's states. Rollup is evaluated whenever a child transitions (or when intake observes the child set). Using the canonical build-lifecycle roles from `config-resolution` (`ready`, `claimed`, `review`, `blocked`, `done`):
62
62
 
63
- Evaluate the children in priority order and take the **first** match:
63
+ Evaluate over the **env ladder** `in-progress < dev < staging < production` — the ordered keys of the project's env-keyed `done` map, with `claimed`/`review` as the rung below the first env (a single-environment project has only the `production` rung). Take the **first** match:
64
64
 
65
65
  | If among the required leaves… | …the parent rolls up to | Role |
66
66
  |---|---|---|
67
67
  | any leaf is **blocked** | blocked / attention-needed | `blocked` |
68
- | else any leaf is **in progress** (claimed or in review) | active / in-progress | `claimed` |
69
- | else **all** required leaves are **terminal** (`done`) | the configured rollup terminal state | `done` (or `review` where supported — see below) |
68
+ | else **every** required leaf has shipped to some env (each is at a `done`-map value) | the **least-advanced** env among them on the ladder | env-keyed `done[min-env]` (terminal `done` when that env is production) |
69
+ | else any leaf has **started** (claimed / in review, or shipped to some env while a sibling has not) | active / in-progress | `claimed` (or `review` where supported — see below) |
70
70
  | else (leaves exist but none started) | unchanged (parent stays in its non-ready container state) | — |
71
71
 
72
+ The middle two rungs are the same idea seen at two resolutions: a parent reaches an env only once **all** its required leaves have reached **at least** that env. So all leaves at `On Stg` → parent `On Stg`; a mix of `On Dev` and `On Stg` → parent `On Dev` (the set as a whole has only fully reached dev); any leaf still `claimed`/`review` (not yet shipped anywhere) holds the parent at `claimed`. In a single-environment project the only env rung is production, so this collapses to the familiar "all leaves `done` → parent `done`, else `claimed`."
73
+
72
74
  Notes:
73
75
 
74
76
  - **Blocked dominates.** A single blocked leaf surfaces blocked/attention on the parent even if other leaves are progressing, so a human sees the parent needs attention.
75
- - **"Required" leaves.** Optional or won't-do children do not hold a parent open; only the leaves that must ship for the parent to be complete are counted toward the all-terminal check.
76
- - **Rollup is recursive.** An Epic rolls up from its Stories, each of which rolls up from its own leaves. Evaluate bottom-up: a Story reaches `done` only when its leaves are all terminal; an Epic reaches `done` only when its Stories are all `done`.
77
+ - **"Required" leaves.** Optional or won't-do children do not hold a parent open; only the leaves that must ship for the parent to be complete are counted toward the env-rollup check.
78
+ - **Least-advanced env wins.** The parent reflects the env the whole required set has collectively reached never an env ahead of its laggard leaf. Native closure (below) fires only when the resolved env is the production/terminal value, never at an intermediate env (`On Dev`/`On Stg`).
79
+ - **Rollup is recursive.** An Epic rolls up from its Stories, each of which rolls up from its own leaves. Evaluate bottom-up: a Story reaches an env only when its leaves have all reached at least that env; an Epic reaches it only when its Stories have.
77
80
  - **Vendor support varies.** Apply the rollup state the vendor can express. Where a vendor has no native intermediate state, use the nearest configured role or a metadata/comment signal rather than forcing a non-existent status (PRD #522 non-goal: vendors need not expose identical states).
78
- - **The parent never carries `ready`.** `ready` is a *human* "this is buildable, claim it" signal and only ever lives on leaves. Rollup moves a parent between non-ready container states (in-progress / blocked / terminal); it never sets the parent to `ready`.
81
+ - **The parent never carries `ready`.** `ready` is a *human* "this is buildable, claim it" signal and only ever lives on leaves. Rollup moves a parent between non-ready container states (in-progress / per-env / blocked / terminal); it never sets the parent to `ready`. A container found carrying `ready` is a leaf-only-invariant violation — recompute its rolled state from its children and apply that (see `repair-intake`).
82
+
83
+ ### The rollup env states are the configured "done" map — multi-env capable
79
84
 
80
- ### The rollup terminal state is the configured "done" — multi-env capable
85
+ The env rungs are whatever the project configures for `done` — which is **env-keyed** (`config-resolution` "Env-keyed `done`"): a `done` map keyed by environment (`dev`, `staging`, `production`), each leaf's env resolved from its merged PR's base branch. This rule does **not** hardcode a `dev → staging → prod` promotion chain as required that is a project-specific deploy topology; the ladder is simply the ordered keys of the project's `done` map. A downstream project with dev/staging/prod environments rolls a parent up to the least-advanced env value its required leaves have collectively reached (an intermediate-env parent state, e.g. `On Stg`), and only to the production `done` value once every required leaf is at production. The rule stays generic and multi-env capable.
81
86
 
82
- The terminal rollup state is whatever the project configures for `done` which is **env-keyed** (`config-resolution` "Env-keyed `done`"): a `done` 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 as required — that is a project-specific deploy topology. A downstream project with dev/staging/prod environments rolls a parent up to whichever terminal `done` value matches the environment its leaves shipped to. The rule stays generic and multi-env capable.
87
+ Intermediate-env rollup and terminal native closure are distinct: a parent **rolls up to** an intermediate env (`On Dev`/`On Stg`) as its required leaves reach it, but native closure (next section) fires **only** at the production/terminal `done` value. A parent sitting at `On Stg` is correctly rolled up *and* still open.
83
88
 
84
89
  **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. For GitHub, the build lifecycle collapses to one chain: `ready → claimed (in-progress) → 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.
85
90
 
@@ -82,21 +82,22 @@ gh api graphql -f query='
82
82
 
83
83
  If the `subIssues` field is unavailable (older GHES), fall back to body parentage exactly as `lisa:github-read-issue` does. If the issue has **no** children it is a leaf, not a parent — rollup is N/A; behave as a normal milestone sync.
84
84
 
85
- **Evaluate the required children in priority order and take the first match** (canonical roles from `config-resolution`; the GitHub label map is `status:blocked`, `status:in-progress`, `status:done`):
85
+ **Evaluate the required children over the env ladder `in-progress < dev < staging < production` (the ordered keys of the GitHub env-keyed `done` map, e.g. `status:on-dev < status:on-stg < status:done`) and take the first match** (canonical roles from `config-resolution`; the GitHub label map is `status:blocked`, `status:in-progress`, env-keyed `done`):
86
86
 
87
87
  | If among the required child leaves… | Derived parent role | GitHub label |
88
88
  |---|---|---|
89
89
  | any child carries `status:blocked` (or is otherwise blocked) | `blocked` | `status:blocked` |
90
- | else any child carries `status:in-progress` | `claimed` | `status:in-progress` |
91
- | else **all** required children are terminal (closed / `status:done`) | `done` | the configured terminal `done` label |
90
+ | else **every** required child has shipped to some env (each at a `done`-map label, e.g. `status:on-dev`/`status:on-stg`/`status:done`) | `done[min-env]` | the **least-advanced** env label among them (all `status:on-stg` → `status:on-stg`; mixed dev+staging → `status:on-dev`; all production → `status:done`) |
91
+ | else any child has **started** (`status:in-progress`, or shipped to an env while a sibling has not) | `claimed` | `status:in-progress` |
92
92
  | else (children exist, none started) | — | unchanged — parent keeps its non-ready container label |
93
93
 
94
94
  - **Blocked dominates** — a single blocked child surfaces `status:blocked` on the parent even while siblings progress, so a human sees the parent needs attention.
95
- - **"Required" children only** — a child labelled won't-do / optional does not hold the parent open; only leaves that must ship count toward the all-terminal check.
96
- - **Recursive** — a parent reaches `done` only when its children are all terminal; an Epic reaches `done` only when its Stories have themselves rolled up to `done`. Evaluate bottom-up.
95
+ - **Least-advanced env wins** — the parent reaches an env only when every required child has reached at least that env; it never sits ahead of its laggard child. Native closure (`gh issue close --reason completed`) fires only when the resolved env is the production `status:done`, never at `status:on-dev`/`status:on-stg`.
96
+ - **"Required" children only** — a child labelled won't-do / optional does not hold the parent open; only leaves that must ship count toward the env-rollup check.
97
+ - **Recursive** — a parent reaches an env only when its children have all reached at least that env; an Epic reaches it only when its Stories have themselves rolled up to it. Evaluate bottom-up.
97
98
  - **Never set the parent to `status:ready`** — `ready` is leaf-only (the human "claim this" signal). Rollup only moves the parent between non-ready container labels.
98
99
 
99
- **Single-environment collapse (this repo).** `.lisa.config.json` `deploy.branches` declares only `production: main`, so the terminal `done` resolves to the single label `status:done` — there is no `status:on-dev` / `status:on-stg` and **no dev → staging → prod promotion chain**. Resolve the terminal via the env-keyed `done` logic in `config-resolution`, but in the single-environment case it collapses to the one `status:done` value; the rollup never attempts to resolve a dev or staging `done`. Projects that DO have multiple environments keep the env-keyed map and roll the parent up to whichever `done` matches the environment its leaves shipped to.
100
+ **Single-environment collapse (this repo).** `.lisa.config.json` `deploy.branches` declares only `production: main`, so the env-keyed `done` resolves to the single label `status:done` — there is no `status:on-dev` / `status:on-stg` and **no dev → staging → prod promotion chain**. Resolve the env rungs via the env-keyed `done` logic in `config-resolution`, but in the single-environment case the only rung is production and it collapses to the one `status:done` value; the rollup never attempts to resolve a dev or staging `done`. Projects that DO have multiple environments keep the env-keyed map and roll the parent up to whichever `done` (including intermediate `status:on-dev`/`status:on-stg`) its leaves have collectively reached.
100
101
 
101
102
  **Apply the derived label** (only when it differs from the parent's current `status:*`): remove the parent's existing `status:*` label and add the derived one, keeping exactly one `status:*` label so the build-queue invariant holds. Post an idempotent `[claude-sync] rollup` comment naming the derived state and the child tally (e.g. `3/4 leaves terminal, 1 blocked → status:blocked`); skip the comment if an identical one is already the most recent rollup comment.
102
103
 
@@ -64,22 +64,23 @@ When invoked with `--rollup`, this skill **derives a parent/container ticket's s
64
64
 
65
65
  **Resolve the child set the same way `lisa:jira-read-ticket` does** — the native Epic → Story → Sub-task hierarchy (Epic link / parent field for Stories, the subtask relationship for Sub-tasks), each with its current status. Fetch via `lisa:atlassian-access` (`operation: read-ticket` / `search-issues` with the parent's `"Epic Link" = <KEY>` or `parent = <KEY>` JQL). If the ticket has **no** children it is a leaf — rollup is N/A; behave as a normal milestone sync.
66
66
 
67
- **Evaluate the required children in priority order and take the first match** (canonical roles from `config-resolution`; the JIRA status map defaults to `Blocked`, `In Progress`, `Code Review`, env-keyed `done`):
67
+ **Evaluate the required children over the env ladder `in-progress < dev < staging < production` (the ordered keys of the JIRA env-keyed `done` map, e.g. `On Dev < On Stg < Done`) and take the first match** (canonical roles from `config-resolution`; the JIRA status map defaults to `Blocked`, `In Progress`, `Code Review`, env-keyed `done`):
68
68
 
69
69
  | If among the required child leaves… | Derived parent role | JIRA status |
70
70
  |---|---|---|
71
71
  | any child is **blocked** | `blocked` | `Blocked` |
72
- | else any child is **in progress** (`In Progress` or `Code Review`) | `claimed` | `In Progress` |
73
- | else **all** required children are terminal (`Done`) | `done` | the configured terminal `done` status |
72
+ | else **every** required child has shipped to some env (each at a `done`-map value, e.g. `On Dev`/`On Stg`/`Done`) | `done[min-env]` | the **least-advanced** env status among them (all `On Stg` → `On Stg`; mixed `On Dev`+`On Stg` → `On Dev`; all production → `Done`) |
73
+ | else any child has **started** (`In Progress` / `Code Review`, or shipped to an env while a sibling has not) | `claimed` | `In Progress` |
74
74
  | else (children exist, none started) | — | unchanged — parent keeps its non-ready container status |
75
75
 
76
76
  - **Blocked dominates** — a single blocked child surfaces `Blocked` on the parent even while siblings progress.
77
+ - **Least-advanced env wins** — the parent reaches an env only when every required child has reached at least that env; it never sits ahead of its laggard child. Apply native terminal resolution (the `leaf-only-lifecycle` Terminal native closure) only when the resolved env is the production `Done`, never at `On Dev`/`On Stg`.
77
78
  - **"Required" children only** — won't-do / optional children do not hold the parent open.
78
- - **Recursive** — an Epic reaches `Done` only when its Stories have themselves rolled up to `Done`; a Story reaches `Done` only when its Sub-tasks are all terminal. Evaluate bottom-up.
79
+ - **Recursive** — an Epic reaches an env only when its Stories have themselves rolled up to at least that env; a Story reaches it only when its Sub-tasks have. Evaluate bottom-up.
79
80
  - **Never set the parent to the build-ready status** — `ready` is leaf-only. Rollup only moves the parent between non-ready container statuses.
80
- - **`review` is optional for JIRA** (`config-resolution`) — a project that omits `Code Review` keeps the parent in `In Progress` until terminal; skip the intermediate rollup hop rather than forcing a non-existent status (a `leaf-only-lifecycle` "vendor support varies" note).
81
+ - **`review` is optional for JIRA** (`config-resolution`) — a project that omits `Code Review` keeps the parent in `In Progress` until it shifts to an env; skip the intermediate review hop rather than forcing a non-existent status (a `leaf-only-lifecycle` "vendor support varies" note).
81
82
 
82
- **Single-environment collapse (this repo).** The terminal `done` resolves via the env-keyed `done` logic in `config-resolution`. In this repo `deploy.branches` declares only `production: main`, so `done` collapses to a single status and the lifecycle is `Ready → In Progress → Code Review → Done` with **no** dev/staging promotion hops; the rollup never resolves a dev or staging `done`. Multi-environment projects keep the env-keyed map.
83
+ **Single-environment collapse (this repo).** The env rungs resolve via the env-keyed `done` logic in `config-resolution`. In this repo `deploy.branches` declares only `production: main`, so `done` collapses to a single status, the only env rung is production, and the lifecycle is `Ready → In Progress → Code Review → Done` with **no** dev/staging promotion hops; the rollup never resolves a dev or staging `done`. Multi-environment projects keep the env-keyed map and roll a parent up to intermediate env statuses (`On Dev`/`On Stg`).
83
84
 
84
85
  **Apply the derived status** (only when it differs from the parent's current status) via `lisa:atlassian-access` `operation: transition`, and post an idempotent rollup comment naming the derived state and the child tally. **Safe default:** if the derived terminal cannot be resolved (ambiguous required-set or unresolvable env `done`), do not guess — post the derived suggestion as a comment and leave the parent's status untouched.
85
86
 
@@ -87,21 +87,22 @@ When the caller passes `--rollup`, this skill **derives a parent/container's `st
87
87
 
88
88
  **Resolve the child set the same way `lisa:linear-read-issue` does** — `mcp__linear-server__list_issues({project: <id>})` for a Project's Issues, or `mcp__linear-server__get_issue` per child for an Issue's sub-Issues (via `parentId`). Capture each child's `status:*` label. If the item has **no** children it is a leaf — rollup is N/A; behave as a normal milestone sync.
89
89
 
90
- **Evaluate the required children in priority order and take the first match** (canonical roles from `config-resolution`; Linear label map is `status:blocked`, `status:in-progress`, `status:code-review`, `status:done`):
90
+ **Evaluate the required children over the env ladder `in-progress < dev < staging < production` (the ordered keys of the Linear env-keyed `done` map, e.g. `status:on-dev < status:on-stg < status:done`) and take the first match** (canonical roles from `config-resolution`; Linear label map is `status:blocked`, `status:in-progress`, `status:code-review`, env-keyed `done`):
91
91
 
92
92
  | If among the required child leaves… | Derived parent role | Linear label |
93
93
  |---|---|---|
94
94
  | any child carries `status:blocked` | `blocked` | `status:blocked` |
95
- | else any child carries `status:in-progress` **or** `status:code-review` | `claimed` | `status:in-progress` |
96
- | else **all** required children carry `status:done` | `done` | the configured terminal `done` label |
95
+ | else **every** required child has shipped to some env (each at a `done`-map label, e.g. `status:on-dev`/`status:on-stg`/`status:done`) | `done[min-env]` | the **least-advanced** env label among them (all `status:on-stg` `status:on-stg`; mixed dev+staging → `status:on-dev`; all production → `status:done`) |
96
+ | else any child has **started** (`status:in-progress` / `status:code-review`, or shipped to an env while a sibling has not) | `claimed` | `status:in-progress` |
97
97
  | else (children exist, none started) | — | unchanged — parent keeps its non-ready container label |
98
98
 
99
99
  - **Blocked dominates** — one blocked child surfaces `status:blocked` on the parent even while siblings progress.
100
+ - **Least-advanced env wins** — the parent reaches an env only when every required child has reached at least that env; it never sits ahead of its laggard child. Native completion (moving the workflow `state` to Done) fires only when the resolved env is the production `status:done`, never at `status:on-dev`/`status:on-stg`.
100
101
  - **"Required" children only** — won't-do / optional (e.g. `Canceled`) children do not hold the parent open.
101
- - **Recursive** — a Project reaches `status:done` only when its Issues have themselves rolled up to `status:done`. Evaluate bottom-up.
102
+ - **Recursive** — a Project reaches an env only when its Issues have themselves rolled up to at least that env. Evaluate bottom-up.
102
103
  - **Never set the parent to `status:ready`** — `ready` is leaf-only. Rollup only moves the parent between non-ready container labels.
103
104
 
104
- **Single-environment collapse (this repo).** The terminal `done` resolves via the env-keyed `done` logic in `config-resolution`. In this repo `deploy.branches` declares only `production: main`, so `done` collapses to the single `status:done` label and the lifecycle is `status:ready → status:in-progress → status:code-review → status:done` with **no** dev/staging promotion hops; the rollup never resolves a dev or staging `done`. Multi-environment projects keep the env-keyed map.
105
+ **Single-environment collapse (this repo).** The env rungs resolve via the env-keyed `done` logic in `config-resolution`. In this repo `deploy.branches` declares only `production: main`, so `done` collapses to the single `status:done` label, the only env rung is production, and the lifecycle is `status:ready → status:in-progress → status:code-review → status:done` with **no** dev/staging promotion hops; the rollup never resolves a dev or staging `done`. Multi-environment projects keep the env-keyed map and roll a parent up to intermediate env labels (`status:on-dev`/`status:on-stg`).
105
106
 
106
107
  **Apply the derived label** via `mcp__linear-server__save_issue` (Project or Issue), removing the parent's existing `status:*` and adding the derived one so exactly one `status:*` label remains. Post an idempotent rollup comment naming the derived state and the child tally. The native Linear `state` is **not** auto-transitioned — only the `status:*` label, mirroring the `--update-label` rule. **Safe default:** if the derived terminal cannot be resolved (ambiguous required-set or unresolvable env `done`), do not guess — post the derived suggestion as a comment and leave the parent's label untouched.
107
108