@codyswann/lisa 2.31.0 → 2.33.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/confluence-to-tracker/SKILL.md +6 -0
- 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/linear-to-tracker/SKILL.md +6 -0
- package/plugins/lisa/skills/notion-to-tracker/SKILL.md +6 -0
- 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/confluence-to-tracker/SKILL.md +6 -0
- 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/linear-to-tracker/SKILL.md +6 -0
- package/plugins/src/base/skills/notion-to-tracker/SKILL.md +6 -0
- 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.33.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": {
|
|
@@ -214,6 +214,8 @@ For each PRD epic, **invoke the `lisa:tracker-write` skill** (do not invoke `lis
|
|
|
214
214
|
- `artifacts`: the full Phase 1.5 artifact list — every artifact, regardless of domain. The epic is the canonical hub. No filtering at the epic level.
|
|
215
215
|
- `priority`, `labels`, `components`, `fix_version`: as appropriate
|
|
216
216
|
|
|
217
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: an Epic is a container, not a leaf work unit. Do NOT mark it build-ready — `lisa:tracker-write` must not be passed `status:ready` for an Epic, and the Epic's lifecycle state rolls up from its children. The build-ready label is applied only in Phase 5.
|
|
218
|
+
|
|
217
219
|
Capture the returned epic key — Phase 4 needs it as the parent for stories.
|
|
218
220
|
|
|
219
221
|
### Phase 4: Create Stories
|
|
@@ -242,6 +244,8 @@ For each story, **invoke `lisa:tracker-write`** with:
|
|
|
242
244
|
| Infrastructure | `ops`, `reference` |
|
|
243
245
|
| Mixed / setup ("X.0") | All domains |
|
|
244
246
|
|
|
247
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: a Story is a container (it has child Sub-tasks), not a leaf work unit. Do NOT mark it build-ready — never pass `status:ready` to `lisa:tracker-write` for a Story. Its lifecycle state rolls up from its Sub-tasks. The build-ready label is applied only in Phase 5.
|
|
248
|
+
|
|
245
249
|
Capture each returned story key — Phase 5 needs it as the parent for sub-tasks.
|
|
246
250
|
|
|
247
251
|
### Phase 5: Create Sub-tasks
|
|
@@ -254,6 +258,8 @@ Each sub-task MUST:
|
|
|
254
258
|
1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]`
|
|
255
259
|
2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
|
|
256
260
|
|
|
261
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa:tracker-write` applies `status:ready` here so downstream build intake (`lisa:tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa:tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
|
|
262
|
+
|
|
257
263
|
Sub-tasks inherit their parent story's artifacts by reference (the parent link). Do not pass the same artifact list to every sub-task.
|
|
258
264
|
|
|
259
265
|
### Phase 5.5: Artifact Preservation Gate (mandatory)
|
|
@@ -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.
|
|
@@ -195,6 +195,8 @@ For each epic identified in Phase 1, **invoke the `lisa:tracker-write` skill** (
|
|
|
195
195
|
- `artifacts`: the full Phase 1.5 artifact list — every artifact, regardless of domain. The epic is the canonical hub. No filtering at the epic level.
|
|
196
196
|
- `priority`, `labels`, `components`, `fix_version`: as appropriate
|
|
197
197
|
|
|
198
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: an Epic is a container, not a leaf work unit. Do NOT mark it build-ready — `lisa:tracker-write` must not be passed `status:ready` for an Epic, and the Epic's lifecycle state rolls up from its children. The build-ready label is applied only in Phase 5.
|
|
199
|
+
|
|
198
200
|
Capture the returned epic key — Phase 4 needs it as the parent for stories.
|
|
199
201
|
|
|
200
202
|
### Phase 4: Create Stories
|
|
@@ -223,6 +225,8 @@ For each story, **invoke `lisa:tracker-write`** with:
|
|
|
223
225
|
| Infrastructure | `ops`, `reference` |
|
|
224
226
|
| Mixed / setup ("X.0") | All domains |
|
|
225
227
|
|
|
228
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: a Story is a container (it has child Sub-tasks), not a leaf work unit. Do NOT mark it build-ready — never pass `status:ready` to `lisa:tracker-write` for a Story. Its lifecycle state rolls up from its Sub-tasks. The build-ready label is applied only in Phase 5.
|
|
229
|
+
|
|
226
230
|
Capture each returned story key — Phase 5 needs it as the parent for sub-tasks.
|
|
227
231
|
|
|
228
232
|
### Phase 5: Create Sub-tasks
|
|
@@ -235,6 +239,8 @@ Each sub-task MUST:
|
|
|
235
239
|
1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]`
|
|
236
240
|
2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
|
|
237
241
|
|
|
242
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa:tracker-write` applies `status:ready` here so downstream build intake (`lisa:tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa:tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
|
|
243
|
+
|
|
238
244
|
Sub-tasks inherit their parent story's artifacts by reference (the parent link). Do not pass the same artifact list to every sub-task.
|
|
239
245
|
|
|
240
246
|
### Phase 5.5: Artifact Preservation Gate (mandatory)
|
|
@@ -186,6 +186,8 @@ For each PRD epic, **invoke the `lisa:tracker-write` skill** (do not call `creat
|
|
|
186
186
|
- `artifacts`: the full Phase 1.5 artifact list — every artifact, regardless of domain. The epic is the canonical hub, and anyone working on the epic or its descendants must be able to reach the full set from one place. No filtering at the epic level. `lisa:tracker-write` Phase 4c attaches them as remote links.
|
|
187
187
|
- `priority`, `labels`, `components`, `fix_version`: as appropriate
|
|
188
188
|
|
|
189
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: an Epic is a container, not a leaf work unit. Do NOT mark it build-ready — `lisa:tracker-write` must not be passed `status:ready` for an Epic, and the Epic's lifecycle state rolls up from its children. The build-ready label is applied only in Phase 5.
|
|
190
|
+
|
|
189
191
|
Capture the returned epic key — Phase 4 needs it as the parent for stories.
|
|
190
192
|
|
|
191
193
|
### Phase 4: Create Stories
|
|
@@ -216,6 +218,8 @@ For each story, **invoke `lisa:tracker-write`** with:
|
|
|
216
218
|
- `artifacts`: the Phase 1.5 artifacts filtered by domain per the inheritance table below — `lisa:tracker-write` Phase 4c attaches them as remote links
|
|
217
219
|
- `priority`, `labels`, `components`, `fix_version`: as appropriate
|
|
218
220
|
|
|
221
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: a Story is a container (it has child Sub-tasks), not a leaf work unit. Do NOT mark it build-ready — never pass `status:ready` to `lisa:tracker-write` for a Story. Its lifecycle state rolls up from its Sub-tasks. The build-ready label is applied only in Phase 5.
|
|
222
|
+
|
|
219
223
|
Capture each returned story key — Phase 5 needs it as the parent for sub-tasks.
|
|
220
224
|
|
|
221
225
|
**Inherit domain-matching artifacts as story remote links**. For each story, the artifact set passed to `lisa:tracker-write` should be the Phase 1.5 artifacts whose domain matches the story's scope:
|
|
@@ -239,6 +243,8 @@ Each sub-task MUST:
|
|
|
239
243
|
1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]`. `lisa:tracker-write` enforces single-repo scope on Sub-task; cross-repo sub-tasks will be rejected and must be split before delegation.
|
|
240
244
|
2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
|
|
241
245
|
|
|
246
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa:tracker-write` applies `status:ready` here so downstream build intake (`lisa:tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa:tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
|
|
247
|
+
|
|
242
248
|
**Verification plan examples by stack:**
|
|
243
249
|
- **Backend APIs**: curl GraphQL/REST calls with auth token, database queries, checking audit entries
|
|
244
250
|
- **Frontend web**: Playwright browser tests (login with test user, navigate, interact, screenshot)
|
|
@@ -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.33.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.33.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"
|
|
@@ -214,6 +214,8 @@ For each PRD epic, **invoke the `lisa:tracker-write` skill** (do not invoke `lis
|
|
|
214
214
|
- `artifacts`: the full Phase 1.5 artifact list — every artifact, regardless of domain. The epic is the canonical hub. No filtering at the epic level.
|
|
215
215
|
- `priority`, `labels`, `components`, `fix_version`: as appropriate
|
|
216
216
|
|
|
217
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: an Epic is a container, not a leaf work unit. Do NOT mark it build-ready — `lisa:tracker-write` must not be passed `status:ready` for an Epic, and the Epic's lifecycle state rolls up from its children. The build-ready label is applied only in Phase 5.
|
|
218
|
+
|
|
217
219
|
Capture the returned epic key — Phase 4 needs it as the parent for stories.
|
|
218
220
|
|
|
219
221
|
### Phase 4: Create Stories
|
|
@@ -242,6 +244,8 @@ For each story, **invoke `lisa:tracker-write`** with:
|
|
|
242
244
|
| Infrastructure | `ops`, `reference` |
|
|
243
245
|
| Mixed / setup ("X.0") | All domains |
|
|
244
246
|
|
|
247
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: a Story is a container (it has child Sub-tasks), not a leaf work unit. Do NOT mark it build-ready — never pass `status:ready` to `lisa:tracker-write` for a Story. Its lifecycle state rolls up from its Sub-tasks. The build-ready label is applied only in Phase 5.
|
|
248
|
+
|
|
245
249
|
Capture each returned story key — Phase 5 needs it as the parent for sub-tasks.
|
|
246
250
|
|
|
247
251
|
### Phase 5: Create Sub-tasks
|
|
@@ -254,6 +258,8 @@ Each sub-task MUST:
|
|
|
254
258
|
1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]`
|
|
255
259
|
2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
|
|
256
260
|
|
|
261
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa:tracker-write` applies `status:ready` here so downstream build intake (`lisa:tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa:tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
|
|
262
|
+
|
|
257
263
|
Sub-tasks inherit their parent story's artifacts by reference (the parent link). Do not pass the same artifact list to every sub-task.
|
|
258
264
|
|
|
259
265
|
### Phase 5.5: Artifact Preservation Gate (mandatory)
|
|
@@ -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.
|
|
@@ -195,6 +195,8 @@ For each epic identified in Phase 1, **invoke the `lisa:tracker-write` skill** (
|
|
|
195
195
|
- `artifacts`: the full Phase 1.5 artifact list — every artifact, regardless of domain. The epic is the canonical hub. No filtering at the epic level.
|
|
196
196
|
- `priority`, `labels`, `components`, `fix_version`: as appropriate
|
|
197
197
|
|
|
198
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: an Epic is a container, not a leaf work unit. Do NOT mark it build-ready — `lisa:tracker-write` must not be passed `status:ready` for an Epic, and the Epic's lifecycle state rolls up from its children. The build-ready label is applied only in Phase 5.
|
|
199
|
+
|
|
198
200
|
Capture the returned epic key — Phase 4 needs it as the parent for stories.
|
|
199
201
|
|
|
200
202
|
### Phase 4: Create Stories
|
|
@@ -223,6 +225,8 @@ For each story, **invoke `lisa:tracker-write`** with:
|
|
|
223
225
|
| Infrastructure | `ops`, `reference` |
|
|
224
226
|
| Mixed / setup ("X.0") | All domains |
|
|
225
227
|
|
|
228
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: a Story is a container (it has child Sub-tasks), not a leaf work unit. Do NOT mark it build-ready — never pass `status:ready` to `lisa:tracker-write` for a Story. Its lifecycle state rolls up from its Sub-tasks. The build-ready label is applied only in Phase 5.
|
|
229
|
+
|
|
226
230
|
Capture each returned story key — Phase 5 needs it as the parent for sub-tasks.
|
|
227
231
|
|
|
228
232
|
### Phase 5: Create Sub-tasks
|
|
@@ -235,6 +239,8 @@ Each sub-task MUST:
|
|
|
235
239
|
1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]`
|
|
236
240
|
2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
|
|
237
241
|
|
|
242
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa:tracker-write` applies `status:ready` here so downstream build intake (`lisa:tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa:tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
|
|
243
|
+
|
|
238
244
|
Sub-tasks inherit their parent story's artifacts by reference (the parent link). Do not pass the same artifact list to every sub-task.
|
|
239
245
|
|
|
240
246
|
### Phase 5.5: Artifact Preservation Gate (mandatory)
|
|
@@ -186,6 +186,8 @@ For each PRD epic, **invoke the `lisa:tracker-write` skill** (do not call `creat
|
|
|
186
186
|
- `artifacts`: the full Phase 1.5 artifact list — every artifact, regardless of domain. The epic is the canonical hub, and anyone working on the epic or its descendants must be able to reach the full set from one place. No filtering at the epic level. `lisa:tracker-write` Phase 4c attaches them as remote links.
|
|
187
187
|
- `priority`, `labels`, `components`, `fix_version`: as appropriate
|
|
188
188
|
|
|
189
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: an Epic is a container, not a leaf work unit. Do NOT mark it build-ready — `lisa:tracker-write` must not be passed `status:ready` for an Epic, and the Epic's lifecycle state rolls up from its children. The build-ready label is applied only in Phase 5.
|
|
190
|
+
|
|
189
191
|
Capture the returned epic key — Phase 4 needs it as the parent for stories.
|
|
190
192
|
|
|
191
193
|
### Phase 4: Create Stories
|
|
@@ -216,6 +218,8 @@ For each story, **invoke `lisa:tracker-write`** with:
|
|
|
216
218
|
- `artifacts`: the Phase 1.5 artifacts filtered by domain per the inheritance table below — `lisa:tracker-write` Phase 4c attaches them as remote links
|
|
217
219
|
- `priority`, `labels`, `components`, `fix_version`: as appropriate
|
|
218
220
|
|
|
221
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: a Story is a container (it has child Sub-tasks), not a leaf work unit. Do NOT mark it build-ready — never pass `status:ready` to `lisa:tracker-write` for a Story. Its lifecycle state rolls up from its Sub-tasks. The build-ready label is applied only in Phase 5.
|
|
222
|
+
|
|
219
223
|
Capture each returned story key — Phase 5 needs it as the parent for sub-tasks.
|
|
220
224
|
|
|
221
225
|
**Inherit domain-matching artifacts as story remote links**. For each story, the artifact set passed to `lisa:tracker-write` should be the Phase 1.5 artifacts whose domain matches the story's scope:
|
|
@@ -239,6 +243,8 @@ Each sub-task MUST:
|
|
|
239
243
|
1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]`. `lisa:tracker-write` enforces single-repo scope on Sub-task; cross-repo sub-tasks will be rejected and must be split before delegation.
|
|
240
244
|
2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
|
|
241
245
|
|
|
246
|
+
**Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa:tracker-write` applies `status:ready` here so downstream build intake (`lisa:tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa:tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
|
|
247
|
+
|
|
242
248
|
**Verification plan examples by stack:**
|
|
243
249
|
- **Backend APIs**: curl GraphQL/REST calls with auth token, database queries, checking audit entries
|
|
244
250
|
- **Frontend web**: Playwright browser tests (login with test user, navigate, interact, screenshot)
|
|
@@ -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.
|