@codyswann/lisa 2.41.1 → 2.43.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 CHANGED
@@ -82,7 +82,7 @@
82
82
  "lodash": ">=4.18.1"
83
83
  },
84
84
  "name": "@codyswann/lisa",
85
- "version": "2.41.1",
85
+ "version": "2.43.0",
86
86
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
87
87
  "main": "dist/index.js",
88
88
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -39,7 +39,13 @@ Pass `$ARGUMENTS` as a single JSON-style block:
39
39
  - `linear` → Linear MCP update
40
40
  - `github` → `gh issue edit --body`
41
41
  - `file` → `Edit` (preferred) or `Write` (full rewrite if needed)
42
- 5. **Return** the rendered section (so the caller can include it in its own report) and the source URL of the updated PRD.
42
+ 5. **Record the PRD→child relationship in the source tool's native hierarchy** where the source supports it and the destination tracker is the same system. Each supported source has its own native-linking section, all governed by the same generated-top-level-work contract, child-ref idempotency, and graceful-degradation discipline from the `prd-lifecycle-rollup` rule:
43
+ - `github` (same repo as the created tickets) → native GitHub **sub-issue** of the PRD issue — see [Native parent linking (GitHub)](#native-parent-linking-github).
44
+ - `linear` (PRD also lives in Linear) → native **parent / project** relationship — see [Native parent linking (Linear)](#native-parent-linking-linear).
45
+ - `jira` (PRD also lives in the same JIRA/Atlassian instance) → native **parent / Epic link** or a documented **issue-link type** — see [Native parent linking (JIRA)](#native-parent-linking-jira).
46
+
47
+ The documented `## Tickets` section from step 3 is always written regardless; native linking is **in addition to** it, not a replacement (the documented section is the cross-vendor and older-host fallback per the `prd-lifecycle-rollup` rule). Sources without native issue hierarchy (Notion, Confluence, file) rely on the documented section alone, as does any cross-vendor combination (e.g. a Notion PRD with a JIRA tracker).
48
+ 6. **Return** the rendered section (so the caller can include it in its own report) and the source URL of the updated PRD.
43
49
 
44
50
  ## Format
45
51
 
@@ -75,6 +81,144 @@ Rendering rules:
75
81
  - Sort epics by key (lexical). Sort stories within an epic by key. Sort sub-tasks within a story by key. Sort the unparented list by `(type, key)`.
76
82
  - The line `_Generated by ..._` is fixed text — does not include a timestamp. A timestamp would defeat the diff-equality check Debrief relies on.
77
83
 
84
+ ## Native parent linking (GitHub)
85
+
86
+ When `source_type: github` **and** the PRD issue lives in the same repository as the created tickets, make the PRD the structural parent of the work it generated by linking each generated top-level work item as a native GitHub **sub-issue** of the PRD issue. This is the GitHub leg of the PRD→child native-hierarchy requirement in the `prd-lifecycle-rollup` rule (cite it by slug; do not restate its taxonomy here). The documented `## Tickets` section is still written either way — native linking is the first-class relationship; the documented section is the durable fallback for cross-vendor and older hosts.
87
+
88
+ This section is GitHub-only. The Linear and JIRA native parents have their own sections below ([Linear](#native-parent-linking-linear), [JIRA](#native-parent-linking-jira)); the documented-section-only fallback for Notion / Confluence / file / cross-vendor sources is governed by the `prd-lifecycle-rollup` rule and is out of scope for this GitHub section.
89
+
90
+ ### What gets linked — generated top-level work only
91
+
92
+ Per the `prd-lifecycle-rollup` "generated top-level work" contract, the PRD owns **only** its generated top-level work as direct children:
93
+
94
+ - A ticket is **top-level** when its `parent_key` is null/empty — these are the created Epic(s) and any top-level Story created directly under the PRD. Link these as sub-issues of the PRD.
95
+ - A ticket with a non-null `parent_key` is a **descendant** (a Story under an Epic, or a leaf Sub-task) — it is owned by its own top-level parent, **never** linked directly to the PRD. Leaf Sub-tasks are explicitly NOT direct PRD children (PRD #525 non-goal).
96
+
97
+ So if the PRD generated `Epic E1 → Story S1 → Sub-task T1`, only `E1` becomes a sub-issue of the PRD; `S1` is a sub-issue of `E1` and `T1` of `S1` (those links were already made by the write path), and neither `S1` nor `T1` is a direct child of the PRD.
98
+
99
+ ### Same-repo guard
100
+
101
+ Native sub-issue linking only applies when the PRD and the work item are in the same repository. Parse `owner/repo` from `source_ref` (the PRD URL or `<org>/<repo>#<n>` token) and from each top-level ticket's `key`/`url`. Skip native linking for any ticket whose repo differs from the PRD's repo (cross-repo or cross-vendor) — record those in the documented section only. The cross-vendor case (e.g. a GitHub PRD with a JIRA/Linear tracker) never reaches this path because the ticket is not a GitHub issue.
102
+
103
+ ### Idempotency — dedupe by child-ref
104
+
105
+ The dedupe key is **child-ref identity** (`owner/repo#number`), per the `prd-lifecycle-rollup` idempotency rule. Before adding any link, read the PRD's current sub-issues and skip any child already linked, so re-running `prd-backlink` never creates duplicate sub-issue links and is a no-op when everything is already linked.
106
+
107
+ 1. **Read the PRD's existing sub-issues** (same GraphQL `subIssues` query `lisa:github-read-issue` Phase 3 uses), and build the set of already-linked child refs:
108
+
109
+ ```bash
110
+ gh api graphql -f query='query($org:String!,$repo:String!,$number:Int!){repository(owner:$org,name:$repo){issue(number:$number){subIssues(first:100){nodes{number repository{nameWithOwner}}}}}}' \
111
+ -F org=<prd_org> -F repo=<prd_repo> -F number=<prd_number> \
112
+ --jq '.data.repository.issue.subIssues.nodes[] | "\(.repository.nameWithOwner)#\(.number)"'
113
+ ```
114
+
115
+ The resulting `owner/repo#number` strings are the existing child-ref set.
116
+
117
+ 2. **For each generated top-level ticket** in the same repo whose child-ref is **not** already in that set, resolve node IDs and call `addSubIssue` (the same mutation `lisa:github-write-issue` Phase 6 step 3 uses):
118
+
119
+ ```bash
120
+ prd_id=$(gh api graphql -f query='query($org:String!,$repo:String!,$number:Int!){repository(owner:$org,name:$repo){issue(number:$number){id}}}' -F org=<prd_org> -F repo=<prd_repo> -F number=<prd_number> --jq '.data.repository.issue.id')
121
+ child_id=$(gh api graphql -f query='query($org:String!,$repo:String!,$number:Int!){repository(owner:$org,name:$repo){issue(number:$number){id}}}' -F org=<org> -F repo=<repo> -F number=<child_number> --jq '.data.repository.issue.id')
122
+ gh api graphql -f query='mutation($parentId:ID!,$childId:ID!){addSubIssue(input:{issueId:$parentId,subIssueId:$childId}){issue{number}subIssue{number}}}' -F parentId="$prd_id" -F childId="$child_id"
123
+ ```
124
+
125
+ A child already in the existing-sub-issue set is a no-op — do not call the mutation for it.
126
+
127
+ ### Graceful degradation
128
+
129
+ - **Already linked.** Covered by the dedupe read above (no mutation issued). If a concurrent run linked it between the read and the mutation, GitHub rejects the duplicate — treat that rejection as success (the desired end state already holds), not a failure.
130
+ - **Mutation unavailable** (older GHES, sub-issues feature off, or the `addSubIssue`/`subIssues` fields are missing). Fall back to the documented `## Tickets` section only and surface a warning to the caller (`native sub-issue linking unavailable — documented section written`). Never silently drop the relationship and never abort the run — the documented section is the contract that always lands.
131
+
132
+ ## Native parent linking (Linear)
133
+
134
+ When `source_type: linear` **and** the PRD also lives in Linear (same workspace as the created work items), make the PRD the structural parent of the work it generated using Linear's native **parent / project** relationship. This is the Linear leg of the PRD→child native-hierarchy requirement in the `prd-lifecycle-rollup` rule (cite it by slug; do not restate its taxonomy here). The documented `## Tickets` section is still written either way — native linking is the first-class relationship; the documented section is the durable fallback for cross-vendor and older hosts.
135
+
136
+ This section is Linear-only. The GitHub and JIRA native parents have their own sections; the documented-section-only fallback for Notion / Confluence / file / cross-vendor sources is out of scope here.
137
+
138
+ ### Which Linear primitive — PRD shape decides
139
+
140
+ Linear models top-level grouping two ways, and the PRD's own entity type decides which native relationship to use:
141
+
142
+ - **PRD is a Linear Project** (the common case — a PRD project groups its generated work). Generated top-level **Issues** are attached to the PRD by setting their `projectId` to the PRD Project's id. The Project *is* the native parent of its Issues; no separate parent-issue link is needed.
143
+ - **PRD is a Linear Issue** (a parent issue, not a project). Generated top-level Issues are attached as native **sub-Issues** of the PRD Issue by setting their `parentId` to the PRD Issue's id.
144
+
145
+ Either way the write surface is `mcp__linear-server__save_issue` with the single relevant field (`projectId` or `parentId`) — the same primitive `lisa:linear-write-issue` uses to set a Story's `projectId` (Epic Project) and a Sub-task's `parentId` (Story Issue). Send **only** that field on update; Linear treats `save_issue` as a full overwrite of the fields named (`lisa:linear-write-issue` Phase 6 UPDATE), so resending other fields would clobber them.
146
+
147
+ ### What gets linked — generated top-level work only
148
+
149
+ Per the `prd-lifecycle-rollup` "generated top-level work" contract, the PRD owns **only** its generated top-level work as direct children:
150
+
151
+ - A work item is **top-level** when its `parent_key` is null/empty — these are the created Epic(s) (Linear Projects) and any top-level Story (Linear Issue) created directly under the PRD. Attach these to the PRD via `projectId`/`parentId`.
152
+ - A work item with a non-null `parent_key` is a **descendant** (a Story under an Epic Project, or a leaf Sub-task) — it is owned by its own top-level parent, **never** linked directly to the PRD. Leaf Sub-tasks are explicitly NOT direct PRD children (PRD #525 non-goal); their `parentId` already points at their Story (set by the write path), and overwriting it to point at the PRD would break that hierarchy.
153
+
154
+ So if the PRD generated `Epic E1 (Project) → Story S1 (Issue) → Sub-task T1 (sub-Issue)`, only the top-level unit is attached to the PRD; `S1` already carries `projectId = E1` and `T1` already carries `parentId = S1` (set by the write path), and neither `S1` nor `T1` is a direct child of the PRD.
155
+
156
+ ### Idempotency — dedupe by child-ref
157
+
158
+ The dedupe key is **child-ref identity** — the Linear issue/project identifier (e.g. `TEAM-123`) or its UUID — per the `prd-lifecycle-rollup` idempotency rule. Before attaching any child, read the PRD's current children and skip any child already attached, so re-running `prd-backlink` never duplicates a relationship and is a no-op when everything is already attached.
159
+
160
+ 1. **Read the PRD's existing children** (the same reads `lisa:linear-read-issue` uses for Project members and sub-Issues):
161
+ - PRD-as-Project → `mcp__linear-server__list_issues({project: <prd_project_id>})` and collect each member Issue's identifier/UUID.
162
+ - PRD-as-Issue → `mcp__linear-server__get_issue({id: <prd_issue_id>})` and collect its sub-Issue identifiers/UUIDs.
163
+
164
+ The resulting identifiers/UUIDs are the existing child-ref set.
165
+
166
+ 2. **For each generated top-level item** whose child-ref is **not** already in that set, attach it with a single-field `save_issue`:
167
+ - PRD-as-Project → `mcp__linear-server__save_issue({id: <child_id>, projectId: <prd_project_id>})`.
168
+ - PRD-as-Issue → `mcp__linear-server__save_issue({id: <child_id>, parentId: <prd_issue_id>})`.
169
+
170
+ A child already in the existing-children set is a no-op — do not issue the `save_issue` call for it. Reading the child's current `projectId`/`parentId` and finding it already equal to the PRD's id is the same no-op (attaching to the value it already holds changes nothing).
171
+
172
+ ### Graceful degradation
173
+
174
+ - **Already attached.** Covered by the dedupe read above (no write issued). If a concurrent run attached it between the read and the write, re-setting the same `projectId`/`parentId` is harmless (the desired end state already holds) — treat it as success, not a failure.
175
+ - **No native hierarchy / cross-vendor.** If the PRD is not itself a Linear entity (cross-vendor — e.g. a Notion PRD with a Linear tracker, which reaches this skill as `source_type: notion`, not `linear`), or the workspace/team does not expose the relationship, this is a clean **no-op**: fall back to the documented `## Tickets` section only and surface a warning to the caller (`native Linear parent linking unavailable — documented section written`). Never silently drop the relationship and never abort the run — the documented section is the contract that always lands.
176
+
177
+ ## Native parent linking (JIRA)
178
+
179
+ When `source_type: jira` **and** the PRD also lives in the same JIRA/Atlassian instance as the created tickets, make the PRD the structural parent of the work it generated using JIRA's native **parent / Epic link**, or — where the instance does not allow an Epic/Story to be a child of the PRD's issue type — a documented **issue-link type** (e.g. `relates to`). This is the JIRA leg of the PRD→child native-hierarchy requirement in the `prd-lifecycle-rollup` rule (cite it by slug; do not restate its taxonomy here). The documented `## Tickets` section is still written either way — native linking is the first-class relationship; the documented section is the durable fallback for cross-vendor and older hosts.
180
+
181
+ This section is JIRA-only. The GitHub and Linear native parents have their own sections; the documented-section-only fallback for Notion / Confluence / file / cross-vendor sources is out of scope here.
182
+
183
+ ### Which JIRA primitive — native parent first, issue-link fallback
184
+
185
+ JIRA's hierarchy support varies by instance and issue-type scheme, so prefer the strongest relationship the instance allows:
186
+
187
+ 1. **Native parent / Epic link (preferred).** If the PRD's issue type can be a parent of the generated top-level type in the instance's hierarchy (e.g. the PRD is a parent-level issue and the generated work is Epics/Stories beneath it), set the generated top-level issue's **parent** to the PRD — the same parent/Epic-link surface `lisa:jira-write-ticket` uses to set a Story's epic parent (Phase 4a + Phase 6 CREATE). Use `lisa:atlassian-access` `operation: write-ticket payload: {key: <child>, parent: <prd_key>}` (send only the parent field on update so other fields are preserved — `lisa:jira-write-ticket` Phase 6 UPDATE).
188
+ 2. **Documented issue-link type (fallback).** If the instance's hierarchy does not allow the PRD to parent the generated type (common when both are Epics, or the PRD is itself an Epic and Epics cannot nest), create a native **issue link** of a documented type instead — `lisa:atlassian-access` `operation: link from: <prd_key> to: <child_key> type: "<link-type>"` — the same link surface `lisa:jira-write-ticket` Phase 6 step 3 uses for `blocks` / `relates to` / etc. Use the project's configured PRD→work link type (default `relates to`); surface an error if an unknown type is passed rather than inventing one.
189
+
190
+ Choosing between (1) and (2) is per the instance's capability — never invent a parent relationship the issue-type scheme rejects, and never silently downgrade without recording which relationship was used.
191
+
192
+ ### What gets linked — generated top-level work only
193
+
194
+ Per the `prd-lifecycle-rollup` "generated top-level work" contract, the PRD owns **only** its generated top-level work as direct children:
195
+
196
+ - A ticket is **top-level** when its `parent_key` is null/empty — these are the created Epic(s) and any top-level Story created directly under the PRD. Attach these to the PRD via the native parent or the documented issue-link type.
197
+ - A ticket with a non-null `parent_key` is a **descendant** (a Story under an Epic, or a leaf Sub-task) — it is owned by its own top-level parent, **never** linked directly to the PRD. Leaf Sub-tasks are explicitly NOT direct PRD children (PRD #525 non-goal); their parent already points at their Story/Epic (set by the write path), and re-parenting them to the PRD would break that hierarchy.
198
+
199
+ So if the PRD generated `Epic E1 → Story S1 → Sub-task T1`, only `E1` is attached to the PRD; `S1` already carries its epic parent `E1` and `T1` its parent `S1` (set by the write path), and neither `S1` nor `T1` is a direct child of the PRD.
200
+
201
+ ### Idempotency — dedupe by child-ref
202
+
203
+ The dedupe key is **child-ref identity** — the JIRA issue key (e.g. `PROJ-123`) — per the `prd-lifecycle-rollup` idempotency rule. Before attaching any child, read the PRD's current children/links and skip any child already attached, so re-running `prd-backlink` never duplicates a parent assignment or issue link and is a no-op when everything is already attached.
204
+
205
+ 1. **Read the PRD's existing children and links** (the same reads `lisa:jira-read-ticket` uses):
206
+ - Native-parent path → enumerate the PRD's children via the epic-link/parent JQL `lisa:jira-read-ticket` Phase 5 uses (`"Epic Link" = <PRD-KEY>` or `parent = <PRD-KEY>`), and collect their keys.
207
+ - Issue-link path → read the PRD's `issuelinks` (`lisa:jira-read-ticket` Phase 4) and collect the keys already linked with the configured PRD→work link type.
208
+
209
+ The resulting issue keys are the existing child-ref set.
210
+
211
+ 2. **For each generated top-level ticket** whose key is **not** already in that set, attach it via the chosen primitive:
212
+ - Native-parent → `lisa:atlassian-access` `operation: write-ticket payload: {key: <child>, parent: <prd_key>}`.
213
+ - Issue-link → `lisa:atlassian-access` `operation: link from: <prd_key> to: <child> type: "<link-type>"`.
214
+
215
+ A child already in the existing-children/links set is a no-op — do not issue the write/link for it.
216
+
217
+ ### Graceful degradation
218
+
219
+ - **Already attached.** Covered by the dedupe read above (no write issued). If a concurrent run attached it between the read and the write, JIRA either no-ops the identical parent assignment or rejects the duplicate link — treat that as success (the desired end state already holds), not a failure.
220
+ - **No native hierarchy / cross-vendor.** If the PRD is not itself a JIRA issue (cross-vendor — e.g. a Notion or Confluence PRD with a JIRA tracker, which reaches this skill as `source_type: notion`/`confluence`, not `jira`), or neither a native parent nor a documented issue-link type is available in the instance, this is a clean **no-op**: fall back to the documented `## Tickets` section only and surface a warning to the caller (`native JIRA parent linking unavailable — documented section written`). Never silently drop the relationship and never abort the run — the documented section is the contract that always lands.
221
+
78
222
  ## Failures
79
223
 
80
224
  - **Source unreachable / permission denied.** Stop and report. Do not silently swallow.
@@ -86,4 +230,16 @@ Rendering rules:
86
230
  ```text
87
231
  PRD back-link updated: <source_url>
88
232
  Section: ## Tickets — <n> epics, <n> stories, <n> sub-tasks, <n> unparented (<bugs/spikes>)
233
+ Native parent links: <n> linked, <n> already linked (<vendor> native hierarchy; documented section is the fallback)
89
234
  ```
235
+
236
+ The `Native parent links` line reports whichever vendor's native linking ran:
237
+ - **GitHub** (same-repo PRD) → sub-issues via `addSubIssue`.
238
+ - **Linear** (PRD in Linear) → `projectId`/`parentId` attachments via `save_issue`.
239
+ - **JIRA** (PRD in JIRA) → native parent / Epic link, or documented issue-link type.
240
+
241
+ Omit the `Native parent links` line when no native linking applies (Notion / Confluence / file source, or any cross-vendor combination — documented section only). If native linking was attempted but unavailable for the vendor, replace the counts with the degradation warning from that vendor's Graceful degradation subsection.
242
+
243
+ ## Related rules
244
+
245
+ - `prd-lifecycle-rollup` — the vendor-neutral source of truth for PRD→generated-top-level-work ownership, the per-vendor terminal predicate, PRD `shipped` rollup, and the child-ref idempotency dedupe key. This skill implements the GitHub, Linear, and JIRA native-linking legs of that rule; it cites the rule by slug rather than restating its taxonomy.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.41.1",
3
+ "version": "2.43.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.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.41.1",
3
+ "version": "2.43.0",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -39,7 +39,13 @@ Pass `$ARGUMENTS` as a single JSON-style block:
39
39
  - `linear` → Linear MCP update
40
40
  - `github` → `gh issue edit --body`
41
41
  - `file` → `Edit` (preferred) or `Write` (full rewrite if needed)
42
- 5. **Return** the rendered section (so the caller can include it in its own report) and the source URL of the updated PRD.
42
+ 5. **Record the PRD→child relationship in the source tool's native hierarchy** where the source supports it and the destination tracker is the same system. Each supported source has its own native-linking section, all governed by the same generated-top-level-work contract, child-ref idempotency, and graceful-degradation discipline from the `prd-lifecycle-rollup` rule:
43
+ - `github` (same repo as the created tickets) → native GitHub **sub-issue** of the PRD issue — see [Native parent linking (GitHub)](#native-parent-linking-github).
44
+ - `linear` (PRD also lives in Linear) → native **parent / project** relationship — see [Native parent linking (Linear)](#native-parent-linking-linear).
45
+ - `jira` (PRD also lives in the same JIRA/Atlassian instance) → native **parent / Epic link** or a documented **issue-link type** — see [Native parent linking (JIRA)](#native-parent-linking-jira).
46
+
47
+ The documented `## Tickets` section from step 3 is always written regardless; native linking is **in addition to** it, not a replacement (the documented section is the cross-vendor and older-host fallback per the `prd-lifecycle-rollup` rule). Sources without native issue hierarchy (Notion, Confluence, file) rely on the documented section alone, as does any cross-vendor combination (e.g. a Notion PRD with a JIRA tracker).
48
+ 6. **Return** the rendered section (so the caller can include it in its own report) and the source URL of the updated PRD.
43
49
 
44
50
  ## Format
45
51
 
@@ -75,6 +81,144 @@ Rendering rules:
75
81
  - Sort epics by key (lexical). Sort stories within an epic by key. Sort sub-tasks within a story by key. Sort the unparented list by `(type, key)`.
76
82
  - The line `_Generated by ..._` is fixed text — does not include a timestamp. A timestamp would defeat the diff-equality check Debrief relies on.
77
83
 
84
+ ## Native parent linking (GitHub)
85
+
86
+ When `source_type: github` **and** the PRD issue lives in the same repository as the created tickets, make the PRD the structural parent of the work it generated by linking each generated top-level work item as a native GitHub **sub-issue** of the PRD issue. This is the GitHub leg of the PRD→child native-hierarchy requirement in the `prd-lifecycle-rollup` rule (cite it by slug; do not restate its taxonomy here). The documented `## Tickets` section is still written either way — native linking is the first-class relationship; the documented section is the durable fallback for cross-vendor and older hosts.
87
+
88
+ This section is GitHub-only. The Linear and JIRA native parents have their own sections below ([Linear](#native-parent-linking-linear), [JIRA](#native-parent-linking-jira)); the documented-section-only fallback for Notion / Confluence / file / cross-vendor sources is governed by the `prd-lifecycle-rollup` rule and is out of scope for this GitHub section.
89
+
90
+ ### What gets linked — generated top-level work only
91
+
92
+ Per the `prd-lifecycle-rollup` "generated top-level work" contract, the PRD owns **only** its generated top-level work as direct children:
93
+
94
+ - A ticket is **top-level** when its `parent_key` is null/empty — these are the created Epic(s) and any top-level Story created directly under the PRD. Link these as sub-issues of the PRD.
95
+ - A ticket with a non-null `parent_key` is a **descendant** (a Story under an Epic, or a leaf Sub-task) — it is owned by its own top-level parent, **never** linked directly to the PRD. Leaf Sub-tasks are explicitly NOT direct PRD children (PRD #525 non-goal).
96
+
97
+ So if the PRD generated `Epic E1 → Story S1 → Sub-task T1`, only `E1` becomes a sub-issue of the PRD; `S1` is a sub-issue of `E1` and `T1` of `S1` (those links were already made by the write path), and neither `S1` nor `T1` is a direct child of the PRD.
98
+
99
+ ### Same-repo guard
100
+
101
+ Native sub-issue linking only applies when the PRD and the work item are in the same repository. Parse `owner/repo` from `source_ref` (the PRD URL or `<org>/<repo>#<n>` token) and from each top-level ticket's `key`/`url`. Skip native linking for any ticket whose repo differs from the PRD's repo (cross-repo or cross-vendor) — record those in the documented section only. The cross-vendor case (e.g. a GitHub PRD with a JIRA/Linear tracker) never reaches this path because the ticket is not a GitHub issue.
102
+
103
+ ### Idempotency — dedupe by child-ref
104
+
105
+ The dedupe key is **child-ref identity** (`owner/repo#number`), per the `prd-lifecycle-rollup` idempotency rule. Before adding any link, read the PRD's current sub-issues and skip any child already linked, so re-running `prd-backlink` never creates duplicate sub-issue links and is a no-op when everything is already linked.
106
+
107
+ 1. **Read the PRD's existing sub-issues** (same GraphQL `subIssues` query `lisa:github-read-issue` Phase 3 uses), and build the set of already-linked child refs:
108
+
109
+ ```bash
110
+ gh api graphql -f query='query($org:String!,$repo:String!,$number:Int!){repository(owner:$org,name:$repo){issue(number:$number){subIssues(first:100){nodes{number repository{nameWithOwner}}}}}}' \
111
+ -F org=<prd_org> -F repo=<prd_repo> -F number=<prd_number> \
112
+ --jq '.data.repository.issue.subIssues.nodes[] | "\(.repository.nameWithOwner)#\(.number)"'
113
+ ```
114
+
115
+ The resulting `owner/repo#number` strings are the existing child-ref set.
116
+
117
+ 2. **For each generated top-level ticket** in the same repo whose child-ref is **not** already in that set, resolve node IDs and call `addSubIssue` (the same mutation `lisa:github-write-issue` Phase 6 step 3 uses):
118
+
119
+ ```bash
120
+ prd_id=$(gh api graphql -f query='query($org:String!,$repo:String!,$number:Int!){repository(owner:$org,name:$repo){issue(number:$number){id}}}' -F org=<prd_org> -F repo=<prd_repo> -F number=<prd_number> --jq '.data.repository.issue.id')
121
+ child_id=$(gh api graphql -f query='query($org:String!,$repo:String!,$number:Int!){repository(owner:$org,name:$repo){issue(number:$number){id}}}' -F org=<org> -F repo=<repo> -F number=<child_number> --jq '.data.repository.issue.id')
122
+ gh api graphql -f query='mutation($parentId:ID!,$childId:ID!){addSubIssue(input:{issueId:$parentId,subIssueId:$childId}){issue{number}subIssue{number}}}' -F parentId="$prd_id" -F childId="$child_id"
123
+ ```
124
+
125
+ A child already in the existing-sub-issue set is a no-op — do not call the mutation for it.
126
+
127
+ ### Graceful degradation
128
+
129
+ - **Already linked.** Covered by the dedupe read above (no mutation issued). If a concurrent run linked it between the read and the mutation, GitHub rejects the duplicate — treat that rejection as success (the desired end state already holds), not a failure.
130
+ - **Mutation unavailable** (older GHES, sub-issues feature off, or the `addSubIssue`/`subIssues` fields are missing). Fall back to the documented `## Tickets` section only and surface a warning to the caller (`native sub-issue linking unavailable — documented section written`). Never silently drop the relationship and never abort the run — the documented section is the contract that always lands.
131
+
132
+ ## Native parent linking (Linear)
133
+
134
+ When `source_type: linear` **and** the PRD also lives in Linear (same workspace as the created work items), make the PRD the structural parent of the work it generated using Linear's native **parent / project** relationship. This is the Linear leg of the PRD→child native-hierarchy requirement in the `prd-lifecycle-rollup` rule (cite it by slug; do not restate its taxonomy here). The documented `## Tickets` section is still written either way — native linking is the first-class relationship; the documented section is the durable fallback for cross-vendor and older hosts.
135
+
136
+ This section is Linear-only. The GitHub and JIRA native parents have their own sections; the documented-section-only fallback for Notion / Confluence / file / cross-vendor sources is out of scope here.
137
+
138
+ ### Which Linear primitive — PRD shape decides
139
+
140
+ Linear models top-level grouping two ways, and the PRD's own entity type decides which native relationship to use:
141
+
142
+ - **PRD is a Linear Project** (the common case — a PRD project groups its generated work). Generated top-level **Issues** are attached to the PRD by setting their `projectId` to the PRD Project's id. The Project *is* the native parent of its Issues; no separate parent-issue link is needed.
143
+ - **PRD is a Linear Issue** (a parent issue, not a project). Generated top-level Issues are attached as native **sub-Issues** of the PRD Issue by setting their `parentId` to the PRD Issue's id.
144
+
145
+ Either way the write surface is `mcp__linear-server__save_issue` with the single relevant field (`projectId` or `parentId`) — the same primitive `lisa:linear-write-issue` uses to set a Story's `projectId` (Epic Project) and a Sub-task's `parentId` (Story Issue). Send **only** that field on update; Linear treats `save_issue` as a full overwrite of the fields named (`lisa:linear-write-issue` Phase 6 UPDATE), so resending other fields would clobber them.
146
+
147
+ ### What gets linked — generated top-level work only
148
+
149
+ Per the `prd-lifecycle-rollup` "generated top-level work" contract, the PRD owns **only** its generated top-level work as direct children:
150
+
151
+ - A work item is **top-level** when its `parent_key` is null/empty — these are the created Epic(s) (Linear Projects) and any top-level Story (Linear Issue) created directly under the PRD. Attach these to the PRD via `projectId`/`parentId`.
152
+ - A work item with a non-null `parent_key` is a **descendant** (a Story under an Epic Project, or a leaf Sub-task) — it is owned by its own top-level parent, **never** linked directly to the PRD. Leaf Sub-tasks are explicitly NOT direct PRD children (PRD #525 non-goal); their `parentId` already points at their Story (set by the write path), and overwriting it to point at the PRD would break that hierarchy.
153
+
154
+ So if the PRD generated `Epic E1 (Project) → Story S1 (Issue) → Sub-task T1 (sub-Issue)`, only the top-level unit is attached to the PRD; `S1` already carries `projectId = E1` and `T1` already carries `parentId = S1` (set by the write path), and neither `S1` nor `T1` is a direct child of the PRD.
155
+
156
+ ### Idempotency — dedupe by child-ref
157
+
158
+ The dedupe key is **child-ref identity** — the Linear issue/project identifier (e.g. `TEAM-123`) or its UUID — per the `prd-lifecycle-rollup` idempotency rule. Before attaching any child, read the PRD's current children and skip any child already attached, so re-running `prd-backlink` never duplicates a relationship and is a no-op when everything is already attached.
159
+
160
+ 1. **Read the PRD's existing children** (the same reads `lisa:linear-read-issue` uses for Project members and sub-Issues):
161
+ - PRD-as-Project → `mcp__linear-server__list_issues({project: <prd_project_id>})` and collect each member Issue's identifier/UUID.
162
+ - PRD-as-Issue → `mcp__linear-server__get_issue({id: <prd_issue_id>})` and collect its sub-Issue identifiers/UUIDs.
163
+
164
+ The resulting identifiers/UUIDs are the existing child-ref set.
165
+
166
+ 2. **For each generated top-level item** whose child-ref is **not** already in that set, attach it with a single-field `save_issue`:
167
+ - PRD-as-Project → `mcp__linear-server__save_issue({id: <child_id>, projectId: <prd_project_id>})`.
168
+ - PRD-as-Issue → `mcp__linear-server__save_issue({id: <child_id>, parentId: <prd_issue_id>})`.
169
+
170
+ A child already in the existing-children set is a no-op — do not issue the `save_issue` call for it. Reading the child's current `projectId`/`parentId` and finding it already equal to the PRD's id is the same no-op (attaching to the value it already holds changes nothing).
171
+
172
+ ### Graceful degradation
173
+
174
+ - **Already attached.** Covered by the dedupe read above (no write issued). If a concurrent run attached it between the read and the write, re-setting the same `projectId`/`parentId` is harmless (the desired end state already holds) — treat it as success, not a failure.
175
+ - **No native hierarchy / cross-vendor.** If the PRD is not itself a Linear entity (cross-vendor — e.g. a Notion PRD with a Linear tracker, which reaches this skill as `source_type: notion`, not `linear`), or the workspace/team does not expose the relationship, this is a clean **no-op**: fall back to the documented `## Tickets` section only and surface a warning to the caller (`native Linear parent linking unavailable — documented section written`). Never silently drop the relationship and never abort the run — the documented section is the contract that always lands.
176
+
177
+ ## Native parent linking (JIRA)
178
+
179
+ When `source_type: jira` **and** the PRD also lives in the same JIRA/Atlassian instance as the created tickets, make the PRD the structural parent of the work it generated using JIRA's native **parent / Epic link**, or — where the instance does not allow an Epic/Story to be a child of the PRD's issue type — a documented **issue-link type** (e.g. `relates to`). This is the JIRA leg of the PRD→child native-hierarchy requirement in the `prd-lifecycle-rollup` rule (cite it by slug; do not restate its taxonomy here). The documented `## Tickets` section is still written either way — native linking is the first-class relationship; the documented section is the durable fallback for cross-vendor and older hosts.
180
+
181
+ This section is JIRA-only. The GitHub and Linear native parents have their own sections; the documented-section-only fallback for Notion / Confluence / file / cross-vendor sources is out of scope here.
182
+
183
+ ### Which JIRA primitive — native parent first, issue-link fallback
184
+
185
+ JIRA's hierarchy support varies by instance and issue-type scheme, so prefer the strongest relationship the instance allows:
186
+
187
+ 1. **Native parent / Epic link (preferred).** If the PRD's issue type can be a parent of the generated top-level type in the instance's hierarchy (e.g. the PRD is a parent-level issue and the generated work is Epics/Stories beneath it), set the generated top-level issue's **parent** to the PRD — the same parent/Epic-link surface `lisa:jira-write-ticket` uses to set a Story's epic parent (Phase 4a + Phase 6 CREATE). Use `lisa:atlassian-access` `operation: write-ticket payload: {key: <child>, parent: <prd_key>}` (send only the parent field on update so other fields are preserved — `lisa:jira-write-ticket` Phase 6 UPDATE).
188
+ 2. **Documented issue-link type (fallback).** If the instance's hierarchy does not allow the PRD to parent the generated type (common when both are Epics, or the PRD is itself an Epic and Epics cannot nest), create a native **issue link** of a documented type instead — `lisa:atlassian-access` `operation: link from: <prd_key> to: <child_key> type: "<link-type>"` — the same link surface `lisa:jira-write-ticket` Phase 6 step 3 uses for `blocks` / `relates to` / etc. Use the project's configured PRD→work link type (default `relates to`); surface an error if an unknown type is passed rather than inventing one.
189
+
190
+ Choosing between (1) and (2) is per the instance's capability — never invent a parent relationship the issue-type scheme rejects, and never silently downgrade without recording which relationship was used.
191
+
192
+ ### What gets linked — generated top-level work only
193
+
194
+ Per the `prd-lifecycle-rollup` "generated top-level work" contract, the PRD owns **only** its generated top-level work as direct children:
195
+
196
+ - A ticket is **top-level** when its `parent_key` is null/empty — these are the created Epic(s) and any top-level Story created directly under the PRD. Attach these to the PRD via the native parent or the documented issue-link type.
197
+ - A ticket with a non-null `parent_key` is a **descendant** (a Story under an Epic, or a leaf Sub-task) — it is owned by its own top-level parent, **never** linked directly to the PRD. Leaf Sub-tasks are explicitly NOT direct PRD children (PRD #525 non-goal); their parent already points at their Story/Epic (set by the write path), and re-parenting them to the PRD would break that hierarchy.
198
+
199
+ So if the PRD generated `Epic E1 → Story S1 → Sub-task T1`, only `E1` is attached to the PRD; `S1` already carries its epic parent `E1` and `T1` its parent `S1` (set by the write path), and neither `S1` nor `T1` is a direct child of the PRD.
200
+
201
+ ### Idempotency — dedupe by child-ref
202
+
203
+ The dedupe key is **child-ref identity** — the JIRA issue key (e.g. `PROJ-123`) — per the `prd-lifecycle-rollup` idempotency rule. Before attaching any child, read the PRD's current children/links and skip any child already attached, so re-running `prd-backlink` never duplicates a parent assignment or issue link and is a no-op when everything is already attached.
204
+
205
+ 1. **Read the PRD's existing children and links** (the same reads `lisa:jira-read-ticket` uses):
206
+ - Native-parent path → enumerate the PRD's children via the epic-link/parent JQL `lisa:jira-read-ticket` Phase 5 uses (`"Epic Link" = <PRD-KEY>` or `parent = <PRD-KEY>`), and collect their keys.
207
+ - Issue-link path → read the PRD's `issuelinks` (`lisa:jira-read-ticket` Phase 4) and collect the keys already linked with the configured PRD→work link type.
208
+
209
+ The resulting issue keys are the existing child-ref set.
210
+
211
+ 2. **For each generated top-level ticket** whose key is **not** already in that set, attach it via the chosen primitive:
212
+ - Native-parent → `lisa:atlassian-access` `operation: write-ticket payload: {key: <child>, parent: <prd_key>}`.
213
+ - Issue-link → `lisa:atlassian-access` `operation: link from: <prd_key> to: <child> type: "<link-type>"`.
214
+
215
+ A child already in the existing-children/links set is a no-op — do not issue the write/link for it.
216
+
217
+ ### Graceful degradation
218
+
219
+ - **Already attached.** Covered by the dedupe read above (no write issued). If a concurrent run attached it between the read and the write, JIRA either no-ops the identical parent assignment or rejects the duplicate link — treat that as success (the desired end state already holds), not a failure.
220
+ - **No native hierarchy / cross-vendor.** If the PRD is not itself a JIRA issue (cross-vendor — e.g. a Notion or Confluence PRD with a JIRA tracker, which reaches this skill as `source_type: notion`/`confluence`, not `jira`), or neither a native parent nor a documented issue-link type is available in the instance, this is a clean **no-op**: fall back to the documented `## Tickets` section only and surface a warning to the caller (`native JIRA parent linking unavailable — documented section written`). Never silently drop the relationship and never abort the run — the documented section is the contract that always lands.
221
+
78
222
  ## Failures
79
223
 
80
224
  - **Source unreachable / permission denied.** Stop and report. Do not silently swallow.
@@ -86,4 +230,16 @@ Rendering rules:
86
230
  ```text
87
231
  PRD back-link updated: <source_url>
88
232
  Section: ## Tickets — <n> epics, <n> stories, <n> sub-tasks, <n> unparented (<bugs/spikes>)
233
+ Native parent links: <n> linked, <n> already linked (<vendor> native hierarchy; documented section is the fallback)
89
234
  ```
235
+
236
+ The `Native parent links` line reports whichever vendor's native linking ran:
237
+ - **GitHub** (same-repo PRD) → sub-issues via `addSubIssue`.
238
+ - **Linear** (PRD in Linear) → `projectId`/`parentId` attachments via `save_issue`.
239
+ - **JIRA** (PRD in JIRA) → native parent / Epic link, or documented issue-link type.
240
+
241
+ Omit the `Native parent links` line when no native linking applies (Notion / Confluence / file source, or any cross-vendor combination — documented section only). If native linking was attempted but unavailable for the vendor, replace the counts with the degradation warning from that vendor's Graceful degradation subsection.
242
+
243
+ ## Related rules
244
+
245
+ - `prd-lifecycle-rollup` — the vendor-neutral source of truth for PRD→generated-top-level-work ownership, the per-vendor terminal predicate, PRD `shipped` rollup, and the child-ref idempotency dedupe key. This skill implements the GitHub, Linear, and JIRA native-linking legs of that rule; it cites the rule by slug rather than restating its taxonomy.