@codyswann/lisa 2.31.0 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/skills/github-sync/SKILL.md +48 -2
- package/plugins/lisa/skills/jira-sync/SKILL.md +27 -1
- package/plugins/lisa/skills/linear-sync/SKILL.md +27 -2
- package/plugins/lisa/skills/tracker-sync/SKILL.md +23 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/src/base/skills/github-sync/SKILL.md +48 -2
- package/plugins/src/base/skills/jira-sync/SKILL.md +27 -1
- package/plugins/src/base/skills/linear-sync/SKILL.md +27 -2
- package/plugins/src/base/skills/tracker-sync/SKILL.md +23 -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.
|
|
85
|
+
"version": "2.32.0",
|
|
86
86
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
87
87
|
"main": "dist/index.js",
|
|
88
88
|
"exports": {
|
|
@@ -62,11 +62,57 @@ Based on the milestone, suggest (but do NOT automatically perform) a label trans
|
|
|
62
62
|
|
|
63
63
|
The actual `status:in-progress` flip is owned by `lisa:github-build-intake` (claim) and `lisa:github-agent`. The `status:code-review` flip is owned by `lisa:github-evidence`. The `status:done` flip is typically owned by merge automation or PM. This skill never relabels.
|
|
64
64
|
|
|
65
|
+
### Step 5: Parent Status Rollup (`--rollup`)
|
|
66
|
+
|
|
67
|
+
When invoked with `--rollup`, this skill **derives a parent/container issue's `status:*` label from the roll-up of its child sub-issues** instead of posting a milestone update on a leaf. This implements the GitHub sub-issue-completion arm of the **Parent status rollup (the state machine)** section of the `leaf-only-lifecycle` rule — cite that rule, do not restate the policy. It is the sync-side complement to the write-time labeling (`lisa:github-write-issue`), the validate-time S15 gate (`lisa:github-validate-issue`), and the claim-time gate (`lisa:github-build-intake`); all four cite the same rule so the classification never drifts.
|
|
68
|
+
|
|
69
|
+
**Resolve the child set the same way `lisa:github-read-issue` does** — native sub-issues via GraphQL, each with its `status:*` label and open/closed state:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
gh api graphql -f query='
|
|
73
|
+
query($owner:String!,$repo:String!,$number:Int!){
|
|
74
|
+
repository(owner:$owner,name:$repo){
|
|
75
|
+
issue(number:$number){
|
|
76
|
+
number title state
|
|
77
|
+
subIssues(first:100){ nodes { number state labels(first:20){ nodes { name } } } }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}' -F owner=<org> -F repo=<repo> -F number=<parent-number>
|
|
81
|
+
```
|
|
82
|
+
|
|
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
|
+
|
|
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:code-review`, `status:done`):
|
|
86
|
+
|
|
87
|
+
| If among the required child leaves… | Derived parent role | GitHub label |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| any child carries `status:blocked` (or is otherwise blocked) | `blocked` | `status:blocked` |
|
|
90
|
+
| else any child carries `status:in-progress` **or** `status:code-review` | `claimed` | `status:in-progress` |
|
|
91
|
+
| else **all** required children are terminal (closed / `status:done`) | `done` | the configured terminal `done` label |
|
|
92
|
+
| else (children exist, none started) | — | unchanged — parent keeps its non-ready container label |
|
|
93
|
+
|
|
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.
|
|
97
|
+
- **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
|
+
**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
|
+
|
|
101
|
+
**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
|
+
```bash
|
|
104
|
+
gh issue edit <parent-number> --repo <org>/<repo> \
|
|
105
|
+
--remove-label "<current status:*>" --add-label "<derived status:*>"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Safe default.** If rollup cannot be applied automatically (e.g. ambiguous required-set, GraphQL hierarchy unavailable, or a derived terminal that the env logic cannot resolve), this skill does **not** guess — it posts the derived suggestion as a comment and leaves the parent's label untouched. No unsafe transition is ever made.
|
|
109
|
+
|
|
65
110
|
## Important Notes
|
|
66
111
|
|
|
67
|
-
- **Never auto-transition labels** — always suggest and let the user / pipeline confirm.
|
|
68
|
-
- **Idempotent updates** — the `[claude-sync] <milestone>` prefix on the most-recent comment is the dedupe key
|
|
112
|
+
- **Never auto-transition labels** — always suggest and let the user / pipeline confirm. The one exception is the explicit `--rollup` parent derivation (Step 5), which moves a *parent's* `status:*` label as the `leaf-only-lifecycle` rule mandates — never a leaf's, and never to `status:ready`.
|
|
113
|
+
- **Idempotent updates** — the `[claude-sync] <milestone>` prefix on the most-recent comment is the dedupe key; the rollup path uses `[claude-sync] rollup`.
|
|
69
114
|
- **Comment format** — use GitHub-flavored markdown (`##` headings, fenced code blocks). The same template is used for the JIRA path (rendered as wiki markup there); keep the markdown source canonical.
|
|
115
|
+
- **Rollup cites the rule by slug** — parent state derivation follows the `leaf-only-lifecycle` rule's state machine; this skill does not restate the policy.
|
|
70
116
|
|
|
71
117
|
## Execution
|
|
72
118
|
|
|
@@ -58,11 +58,37 @@ Based on the milestone, suggest (but don't automatically perform) a status trans
|
|
|
58
58
|
| PR ready | "In Review" |
|
|
59
59
|
| PR merged | "Done" |
|
|
60
60
|
|
|
61
|
+
### Step 5: Parent Status Rollup (`--rollup`)
|
|
62
|
+
|
|
63
|
+
When invoked with `--rollup`, this skill **derives a parent/container ticket's status from the roll-up of its children** (Stories under an Epic; Sub-tasks under a Story/Task) instead of posting a milestone update on a leaf. This implements the JIRA child/subtask-status arm of the **Parent status rollup (the state machine)** section of the `leaf-only-lifecycle` rule — cite that rule, do not restate the policy.
|
|
64
|
+
|
|
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
|
+
|
|
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`):
|
|
68
|
+
|
|
69
|
+
| If among the required child leaves… | Derived parent role | JIRA status |
|
|
70
|
+
|---|---|---|
|
|
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 |
|
|
74
|
+
| else (children exist, none started) | — | unchanged — parent keeps its non-ready container status |
|
|
75
|
+
|
|
76
|
+
- **Blocked dominates** — a single blocked child surfaces `Blocked` on the parent even while siblings progress.
|
|
77
|
+
- **"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
|
+
- **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
|
+
|
|
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
|
+
|
|
84
|
+
**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
|
+
|
|
61
86
|
## Important Notes
|
|
62
87
|
|
|
63
|
-
- **Never auto-transition ticket status** — always suggest and let the user confirm
|
|
88
|
+
- **Never auto-transition ticket status** — always suggest and let the user confirm. The one exception is the explicit `--rollup` parent derivation (Step 5), which transitions a *parent's* status per the `leaf-only-lifecycle` rule — never a leaf's, and never to the build-ready status.
|
|
64
89
|
- **Idempotent updates** — running sync multiple times at the same milestone should not create duplicate comments
|
|
65
90
|
- **Comment format** — use JIRA markdown with clear headers and bullet points
|
|
91
|
+
- **Rollup cites the rule by slug** — parent state derivation follows the `leaf-only-lifecycle` rule's state machine; this skill does not restate the policy.
|
|
66
92
|
|
|
67
93
|
## Execution
|
|
68
94
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: linear-sync
|
|
3
3
|
description: "Syncs plan progress to a linked Linear Issue. Posts plan contents, progress updates, branch links, and PR links at key milestones. Use this skill throughout the plan lifecycle to keep Linear Issues in sync. The Linear counterpart of lisa:jira-sync and lisa:github-sync."
|
|
4
|
-
allowed-tools: ["Bash", "Skill", "mcp__linear-server__list_teams", "mcp__linear-server__get_issue", "mcp__linear-server__save_comment", "mcp__linear-server__list_issue_labels", "mcp__linear-server__create_issue_label", "mcp__linear-server__save_issue"]
|
|
4
|
+
allowed-tools: ["Bash", "Skill", "mcp__linear-server__list_teams", "mcp__linear-server__get_issue", "mcp__linear-server__list_issues", "mcp__linear-server__save_comment", "mcp__linear-server__list_issue_labels", "mcp__linear-server__create_issue_label", "mcp__linear-server__save_issue"]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Sync Plan to Linear: $ARGUMENTS
|
|
@@ -81,9 +81,34 @@ Verify exactly one `status:*` label remains after the update — having two simu
|
|
|
81
81
|
|
|
82
82
|
Without `--update-label`, this skill posts the comment only and does NOT touch labels.
|
|
83
83
|
|
|
84
|
+
## Phase 5 — Parent Status Rollup (`--rollup`)
|
|
85
|
+
|
|
86
|
+
When the caller passes `--rollup`, this skill **derives a parent/container's `status:*` label from the roll-up of its children** instead of acting on a leaf. A **Project** (the Epic equivalent) rolls up from its Issues; an **Issue** rolls up from its sub-Issues. This implements the Linear child-issue-status arm of the **Parent status rollup (the state machine)** section of the `leaf-only-lifecycle` rule — cite that rule, do not restate the policy.
|
|
87
|
+
|
|
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
|
+
|
|
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`):
|
|
91
|
+
|
|
92
|
+
| If among the required child leaves… | Derived parent role | Linear label |
|
|
93
|
+
|---|---|---|
|
|
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 |
|
|
97
|
+
| else (children exist, none started) | — | unchanged — parent keeps its non-ready container label |
|
|
98
|
+
|
|
99
|
+
- **Blocked dominates** — one blocked child surfaces `status:blocked` on the parent even while siblings progress.
|
|
100
|
+
- **"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
|
+
- **Never set the parent to `status:ready`** — `ready` is leaf-only. Rollup only moves the parent between non-ready container labels.
|
|
103
|
+
|
|
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
|
+
|
|
106
|
+
**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
|
+
|
|
84
108
|
## Rules
|
|
85
109
|
|
|
86
|
-
- Never auto-transition the native Linear `state` — only the label, and only when the caller explicitly asks.
|
|
110
|
+
- Never auto-transition the native Linear `state` — only the label, and only when the caller explicitly asks (`--update-label`, or `--rollup` for parent derivation per the `leaf-only-lifecycle` rule).
|
|
111
|
+
- Rollup derives a *parent's* `status:*` label from its children and never sets a parent to `status:ready`. It cites the `leaf-only-lifecycle` rule by slug rather than restating the state machine.
|
|
87
112
|
- Never post empty or minimal comments — if a milestone has no meaningful content, skip the post.
|
|
88
113
|
- Do not delete prior milestone comments. They are the audit trail.
|
|
89
114
|
- If `save_comment` fails, retry once. If it fails again, surface the error.
|
|
@@ -20,9 +20,32 @@ See the `config-resolution` rule for configuration and dispatch table.
|
|
|
20
20
|
- Anything else → stop and report `"Unknown tracker '<value>' in .lisa.config.json. Expected 'jira', 'github', or 'linear'."`
|
|
21
21
|
3. Pass through the output.
|
|
22
22
|
|
|
23
|
+
`$ARGUMENTS` is forwarded verbatim, including the optional `--rollup` flag (see "Parent status rollup" below) and `--update-label`. The shim never interprets these — the vendor skill does.
|
|
24
|
+
|
|
23
25
|
If `$ARGUMENTS` is empty, all vendor skills auto-detect a ticket reference from the active plan file (most recently modified `.md` in `plans/`).
|
|
24
26
|
|
|
27
|
+
## Parent status rollup (`--rollup`)
|
|
28
|
+
|
|
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
|
+
|
|
31
|
+
The state machine (first match wins, evaluated over the **required** leaves only):
|
|
32
|
+
|
|
33
|
+
| If among the required leaves… | …the parent rolls up to | Role |
|
|
34
|
+
|---|---|---|
|
|
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) |
|
|
38
|
+
| else (leaves exist, none started) | unchanged | — |
|
|
39
|
+
|
|
40
|
+
- **Blocked dominates** — one blocked leaf surfaces blocked on the parent even while others progress.
|
|
41
|
+
- **The parent never carries `ready`** — `ready` is a human "claim this leaf" signal; rollup only moves a parent between non-ready container states.
|
|
42
|
+
- **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 lifecycle collapses to `ready → claimed (in-progress) → review (code-review) → 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
|
+
**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
|
+
|
|
25
47
|
## Rules
|
|
26
48
|
|
|
27
49
|
- Idempotent updates — running sync at the same milestone twice should not produce duplicate comments. Vendor skills enforce this.
|
|
28
50
|
- Never auto-transition the underlying state. Linear's label-based transition (`status:*`) is the canonical signal and is updated only when the caller passes `--update-label`. Native states stay as suggestions.
|
|
51
|
+
- Parent rollup derives state from children per the `leaf-only-lifecycle` rule; it never sets a parent to `ready` and never resolves a dev/staging `done` in this single-environment repo.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.32.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.32.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -62,11 +62,57 @@ Based on the milestone, suggest (but do NOT automatically perform) a label trans
|
|
|
62
62
|
|
|
63
63
|
The actual `status:in-progress` flip is owned by `lisa:github-build-intake` (claim) and `lisa:github-agent`. The `status:code-review` flip is owned by `lisa:github-evidence`. The `status:done` flip is typically owned by merge automation or PM. This skill never relabels.
|
|
64
64
|
|
|
65
|
+
### Step 5: Parent Status Rollup (`--rollup`)
|
|
66
|
+
|
|
67
|
+
When invoked with `--rollup`, this skill **derives a parent/container issue's `status:*` label from the roll-up of its child sub-issues** instead of posting a milestone update on a leaf. This implements the GitHub sub-issue-completion arm of the **Parent status rollup (the state machine)** section of the `leaf-only-lifecycle` rule — cite that rule, do not restate the policy. It is the sync-side complement to the write-time labeling (`lisa:github-write-issue`), the validate-time S15 gate (`lisa:github-validate-issue`), and the claim-time gate (`lisa:github-build-intake`); all four cite the same rule so the classification never drifts.
|
|
68
|
+
|
|
69
|
+
**Resolve the child set the same way `lisa:github-read-issue` does** — native sub-issues via GraphQL, each with its `status:*` label and open/closed state:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
gh api graphql -f query='
|
|
73
|
+
query($owner:String!,$repo:String!,$number:Int!){
|
|
74
|
+
repository(owner:$owner,name:$repo){
|
|
75
|
+
issue(number:$number){
|
|
76
|
+
number title state
|
|
77
|
+
subIssues(first:100){ nodes { number state labels(first:20){ nodes { name } } } }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}' -F owner=<org> -F repo=<repo> -F number=<parent-number>
|
|
81
|
+
```
|
|
82
|
+
|
|
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
|
+
|
|
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:code-review`, `status:done`):
|
|
86
|
+
|
|
87
|
+
| If among the required child leaves… | Derived parent role | GitHub label |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| any child carries `status:blocked` (or is otherwise blocked) | `blocked` | `status:blocked` |
|
|
90
|
+
| else any child carries `status:in-progress` **or** `status:code-review` | `claimed` | `status:in-progress` |
|
|
91
|
+
| else **all** required children are terminal (closed / `status:done`) | `done` | the configured terminal `done` label |
|
|
92
|
+
| else (children exist, none started) | — | unchanged — parent keeps its non-ready container label |
|
|
93
|
+
|
|
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.
|
|
97
|
+
- **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
|
+
**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
|
+
|
|
101
|
+
**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
|
+
```bash
|
|
104
|
+
gh issue edit <parent-number> --repo <org>/<repo> \
|
|
105
|
+
--remove-label "<current status:*>" --add-label "<derived status:*>"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Safe default.** If rollup cannot be applied automatically (e.g. ambiguous required-set, GraphQL hierarchy unavailable, or a derived terminal that the env logic cannot resolve), this skill does **not** guess — it posts the derived suggestion as a comment and leaves the parent's label untouched. No unsafe transition is ever made.
|
|
109
|
+
|
|
65
110
|
## Important Notes
|
|
66
111
|
|
|
67
|
-
- **Never auto-transition labels** — always suggest and let the user / pipeline confirm.
|
|
68
|
-
- **Idempotent updates** — the `[claude-sync] <milestone>` prefix on the most-recent comment is the dedupe key
|
|
112
|
+
- **Never auto-transition labels** — always suggest and let the user / pipeline confirm. The one exception is the explicit `--rollup` parent derivation (Step 5), which moves a *parent's* `status:*` label as the `leaf-only-lifecycle` rule mandates — never a leaf's, and never to `status:ready`.
|
|
113
|
+
- **Idempotent updates** — the `[claude-sync] <milestone>` prefix on the most-recent comment is the dedupe key; the rollup path uses `[claude-sync] rollup`.
|
|
69
114
|
- **Comment format** — use GitHub-flavored markdown (`##` headings, fenced code blocks). The same template is used for the JIRA path (rendered as wiki markup there); keep the markdown source canonical.
|
|
115
|
+
- **Rollup cites the rule by slug** — parent state derivation follows the `leaf-only-lifecycle` rule's state machine; this skill does not restate the policy.
|
|
70
116
|
|
|
71
117
|
## Execution
|
|
72
118
|
|
|
@@ -58,11 +58,37 @@ Based on the milestone, suggest (but don't automatically perform) a status trans
|
|
|
58
58
|
| PR ready | "In Review" |
|
|
59
59
|
| PR merged | "Done" |
|
|
60
60
|
|
|
61
|
+
### Step 5: Parent Status Rollup (`--rollup`)
|
|
62
|
+
|
|
63
|
+
When invoked with `--rollup`, this skill **derives a parent/container ticket's status from the roll-up of its children** (Stories under an Epic; Sub-tasks under a Story/Task) instead of posting a milestone update on a leaf. This implements the JIRA child/subtask-status arm of the **Parent status rollup (the state machine)** section of the `leaf-only-lifecycle` rule — cite that rule, do not restate the policy.
|
|
64
|
+
|
|
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
|
+
|
|
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`):
|
|
68
|
+
|
|
69
|
+
| If among the required child leaves… | Derived parent role | JIRA status |
|
|
70
|
+
|---|---|---|
|
|
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 |
|
|
74
|
+
| else (children exist, none started) | — | unchanged — parent keeps its non-ready container status |
|
|
75
|
+
|
|
76
|
+
- **Blocked dominates** — a single blocked child surfaces `Blocked` on the parent even while siblings progress.
|
|
77
|
+
- **"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
|
+
- **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
|
+
|
|
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
|
+
|
|
84
|
+
**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
|
+
|
|
61
86
|
## Important Notes
|
|
62
87
|
|
|
63
|
-
- **Never auto-transition ticket status** — always suggest and let the user confirm
|
|
88
|
+
- **Never auto-transition ticket status** — always suggest and let the user confirm. The one exception is the explicit `--rollup` parent derivation (Step 5), which transitions a *parent's* status per the `leaf-only-lifecycle` rule — never a leaf's, and never to the build-ready status.
|
|
64
89
|
- **Idempotent updates** — running sync multiple times at the same milestone should not create duplicate comments
|
|
65
90
|
- **Comment format** — use JIRA markdown with clear headers and bullet points
|
|
91
|
+
- **Rollup cites the rule by slug** — parent state derivation follows the `leaf-only-lifecycle` rule's state machine; this skill does not restate the policy.
|
|
66
92
|
|
|
67
93
|
## Execution
|
|
68
94
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: linear-sync
|
|
3
3
|
description: "Syncs plan progress to a linked Linear Issue. Posts plan contents, progress updates, branch links, and PR links at key milestones. Use this skill throughout the plan lifecycle to keep Linear Issues in sync. The Linear counterpart of lisa:jira-sync and lisa:github-sync."
|
|
4
|
-
allowed-tools: ["Bash", "Skill", "mcp__linear-server__list_teams", "mcp__linear-server__get_issue", "mcp__linear-server__save_comment", "mcp__linear-server__list_issue_labels", "mcp__linear-server__create_issue_label", "mcp__linear-server__save_issue"]
|
|
4
|
+
allowed-tools: ["Bash", "Skill", "mcp__linear-server__list_teams", "mcp__linear-server__get_issue", "mcp__linear-server__list_issues", "mcp__linear-server__save_comment", "mcp__linear-server__list_issue_labels", "mcp__linear-server__create_issue_label", "mcp__linear-server__save_issue"]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Sync Plan to Linear: $ARGUMENTS
|
|
@@ -81,9 +81,34 @@ Verify exactly one `status:*` label remains after the update — having two simu
|
|
|
81
81
|
|
|
82
82
|
Without `--update-label`, this skill posts the comment only and does NOT touch labels.
|
|
83
83
|
|
|
84
|
+
## Phase 5 — Parent Status Rollup (`--rollup`)
|
|
85
|
+
|
|
86
|
+
When the caller passes `--rollup`, this skill **derives a parent/container's `status:*` label from the roll-up of its children** instead of acting on a leaf. A **Project** (the Epic equivalent) rolls up from its Issues; an **Issue** rolls up from its sub-Issues. This implements the Linear child-issue-status arm of the **Parent status rollup (the state machine)** section of the `leaf-only-lifecycle` rule — cite that rule, do not restate the policy.
|
|
87
|
+
|
|
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
|
+
|
|
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`):
|
|
91
|
+
|
|
92
|
+
| If among the required child leaves… | Derived parent role | Linear label |
|
|
93
|
+
|---|---|---|
|
|
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 |
|
|
97
|
+
| else (children exist, none started) | — | unchanged — parent keeps its non-ready container label |
|
|
98
|
+
|
|
99
|
+
- **Blocked dominates** — one blocked child surfaces `status:blocked` on the parent even while siblings progress.
|
|
100
|
+
- **"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
|
+
- **Never set the parent to `status:ready`** — `ready` is leaf-only. Rollup only moves the parent between non-ready container labels.
|
|
103
|
+
|
|
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
|
+
|
|
106
|
+
**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
|
+
|
|
84
108
|
## Rules
|
|
85
109
|
|
|
86
|
-
- Never auto-transition the native Linear `state` — only the label, and only when the caller explicitly asks.
|
|
110
|
+
- Never auto-transition the native Linear `state` — only the label, and only when the caller explicitly asks (`--update-label`, or `--rollup` for parent derivation per the `leaf-only-lifecycle` rule).
|
|
111
|
+
- Rollup derives a *parent's* `status:*` label from its children and never sets a parent to `status:ready`. It cites the `leaf-only-lifecycle` rule by slug rather than restating the state machine.
|
|
87
112
|
- Never post empty or minimal comments — if a milestone has no meaningful content, skip the post.
|
|
88
113
|
- Do not delete prior milestone comments. They are the audit trail.
|
|
89
114
|
- If `save_comment` fails, retry once. If it fails again, surface the error.
|
|
@@ -20,9 +20,32 @@ See the `config-resolution` rule for configuration and dispatch table.
|
|
|
20
20
|
- Anything else → stop and report `"Unknown tracker '<value>' in .lisa.config.json. Expected 'jira', 'github', or 'linear'."`
|
|
21
21
|
3. Pass through the output.
|
|
22
22
|
|
|
23
|
+
`$ARGUMENTS` is forwarded verbatim, including the optional `--rollup` flag (see "Parent status rollup" below) and `--update-label`. The shim never interprets these — the vendor skill does.
|
|
24
|
+
|
|
23
25
|
If `$ARGUMENTS` is empty, all vendor skills auto-detect a ticket reference from the active plan file (most recently modified `.md` in `plans/`).
|
|
24
26
|
|
|
27
|
+
## Parent status rollup (`--rollup`)
|
|
28
|
+
|
|
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
|
+
|
|
31
|
+
The state machine (first match wins, evaluated over the **required** leaves only):
|
|
32
|
+
|
|
33
|
+
| If among the required leaves… | …the parent rolls up to | Role |
|
|
34
|
+
|---|---|---|
|
|
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) |
|
|
38
|
+
| else (leaves exist, none started) | unchanged | — |
|
|
39
|
+
|
|
40
|
+
- **Blocked dominates** — one blocked leaf surfaces blocked on the parent even while others progress.
|
|
41
|
+
- **The parent never carries `ready`** — `ready` is a human "claim this leaf" signal; rollup only moves a parent between non-ready container states.
|
|
42
|
+
- **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 lifecycle collapses to `ready → claimed (in-progress) → review (code-review) → 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
|
+
**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
|
+
|
|
25
47
|
## Rules
|
|
26
48
|
|
|
27
49
|
- Idempotent updates — running sync at the same milestone twice should not produce duplicate comments. Vendor skills enforce this.
|
|
28
50
|
- Never auto-transition the underlying state. Linear's label-based transition (`status:*`) is the canonical signal and is updated only when the caller passes `--update-label`. Native states stay as suggestions.
|
|
51
|
+
- Parent rollup derives state from children per the `leaf-only-lifecycle` rule; it never sets a parent to `ready` and never resolves a dev/staging `done` in this single-environment repo.
|