@codyswann/lisa 4.53.7 → 4.54.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  2. package/dist/core/reusable-workflow-pin.d.ts +28 -0
  3. package/dist/core/reusable-workflow-pin.d.ts.map +1 -1
  4. package/dist/core/reusable-workflow-pin.js +49 -0
  5. package/dist/core/reusable-workflow-pin.js.map +1 -1
  6. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  7. package/dist/core/upstream-evidence-manifest.js +6 -3
  8. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  9. package/package.json +4 -4
  10. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  11. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  12. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +39 -1
  13. package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +8 -0
  14. package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +22 -0
  15. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +39 -1
  16. package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +8 -0
  17. package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +22 -0
  18. package/plugins/lisa-agy/plugin.json +1 -1
  19. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +39 -1
  20. package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +8 -0
  21. package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +22 -0
  22. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  24. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  25. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +39 -1
  29. package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +8 -0
  30. package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +22 -0
  31. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +39 -1
  33. package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +8 -0
  34. package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +22 -0
  35. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  37. package/plugins/lisa-expo-agy/plugin.json +1 -1
  38. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  43. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  48. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  53. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  58. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-rails-agy/plugin.json +1 -1
  63. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  68. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  72. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  73. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  75. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +39 -1
  76. package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +8 -0
  77. package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +22 -0
package/package.json CHANGED
@@ -181,7 +181,7 @@
181
181
  "zod-validation-error": "^4.0.0"
182
182
  },
183
183
  "name": "@codyswann/lisa",
184
- "version": "4.53.7",
184
+ "version": "4.54.1",
185
185
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
186
186
  "main": "dist/index.js",
187
187
  "exports": {
@@ -329,7 +329,7 @@
329
329
  "test": "tests"
330
330
  },
331
331
  "types": "./dist/index.d.ts",
332
- "lisaReleaseCommit": "f55a6d64cf60dc1317b8b45f27b82f8cfb0a6c2f",
333
- "gitHead": "f55a6d64cf60dc1317b8b45f27b82f8cfb0a6c2f",
334
- "lisaReleaseTag": "v4.53.7"
332
+ "lisaReleaseCommit": "c212a00cb49d283e91e50f22a11353a35e3d5c39",
333
+ "gitHead": "c212a00cb49d283e91e50f22a11353a35e3d5c39",
334
+ "lisaReleaseTag": "v4.54.1"
335
335
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -931,11 +931,49 @@ proxy for the other, so read them separately:
931
931
  wedged looking abandoned.
932
932
 
933
933
  Never dismiss on `reviewDecision` alone. Dismiss the stale (often bot) review
934
- where repo policy permits, else re-request review:
934
+ where repo policy permits:
935
935
  ```bash
936
936
  gh api -X PUT repos/<owner>/<repo>/pulls/<pr>/reviews/<review_id>/dismissals \
937
937
  -f message="Addressed; threads resolved." -f event=DISMISS
938
938
  ```
939
+
940
+ **When you cannot dismiss, re-requesting is NOT the automatic fallback.** This
941
+ file already forbids re-requesting to refresh a review, in as many words, and
942
+ that prohibition applies here too — the fallback used to contradict it, and an
943
+ agent reaching it was told to do the thing the same file calls one-way
944
+ destruction (CodySwannGT/lisa#3587).
945
+
946
+ Read the reviews before deciding, because the read is cheap and settles it:
947
+
948
+ ```bash
949
+ gh api "repos/<owner>/<repo>/pulls/<pr>/reviews" \
950
+ --jq '[.[] | {author: .user.login, state: .state, hasBody: (((.body // "") | length) > 0)}]'
951
+ ```
952
+
953
+ - **A human reviewer** — re-request freely. Re-requesting a person's review is
954
+ unremarkable and loses nothing.
955
+ - **A bot reviewer whose review is NOT the only substantive one** — re-request.
956
+ Something else still evidences that this head was read.
957
+ - **A bot reviewer whose review IS the only substantive one on the PR** — do
958
+ NOT re-request. **Escalate for a dismissal decision instead.** Under a
959
+ throttle or a spending cap the bot answers a new request with a status
960
+ carrying no review body, that hollow answer becomes its latest review, and
961
+ every effective-review computation — including the "latest non-dismissed
962
+ review per reviewer" rule immediately below — then reads the hollow one. The
963
+ substantive review is still retrievable from the reviews API; what is lost is
964
+ its standing as the reviewer's current review, which is the thing the merge
965
+ standard reads.
966
+
967
+ Deleting the fallback outright would over-correct: it is the only remedy a lane
968
+ without dismissal permission has, and that permission is not universal. This
969
+ narrows it to the case that costs nothing.
970
+
971
+ **Unverified, and stated as such rather than asserted:** that a bot's re-review
972
+ REPLACES its predecessor in the effective set is inferred from the observed
973
+ consequence, not measured against the vendor. It has not been tested here, and
974
+ testing it means re-requesting on a live pull request. The guidance is worth
975
+ following either way — the escalation costs one human decision, and the failure
976
+ it avoids is one-way.
939
977
  Some org rulesets allow 0 approvals yet a bot `CHANGES_REQUESTED` still blocks
940
978
  auto-merge — dismissing the stale review after resolving all threads is what
941
979
  unblocks it.
@@ -229,6 +229,14 @@ For each candidate, classify the relationship:
229
229
 
230
230
  Linear native relations are set on the Issue via `save_issue`'s `relations` field (or via a paired `save_issue_relation` call if available in the MCP). For Project-level (Epic) relationships, capture them in the description under `## Related Projects` since Linear doesn't model relations between Projects natively.
231
231
 
232
+ **Adding a relation to an already-linked pair may be a REPLACE, not an add.** Reported measured twice against the raw `issueRelationCreate` mutation: creating a `blocks` edge between two issues already carrying a `related` edge converted that edge in place — the source issue's outgoing relation count did not grow (CodySwannGT/lisa#3605). Relationship discovery is mandatory on the UPDATE path as well as creation, so this skill's own contract routinely points it at pairs that already have edges.
233
+
234
+ So on an update, read the pair's existing relations before writing one. Where an edge already exists and differs in type, surface it and let a human decide rather than writing over it — **an inherited relation is not a verified one**, and rewriting it silently removes the only moment anyone would check what it meant.
235
+
236
+ **The true undo is delete plus re-create with the original type.** A single delete removes the link entirely and does not restore what was there before, so any runbook recording "undo = delete relation X" has written down a plan that loses the prior relationship.
237
+
238
+ **Marked UNVERIFIED.** The observations were against the raw mutation; whether the MCP's `relations` field diffs before writing has not been tested, and testing it means writing a relation to a live workspace. Note that this does not make Lisa safe by default: `lisa-linear-access` resolves `LINEAR_API_KEY` + raw GraphQL as **tier 1, ahead of the MCP**, so the normal autonomous path is the one the observations were made against.
239
+
232
240
  ### 4c. Remote Links
233
241
 
234
242
  Identify and attach (Linear stores attachments / links on the Issue or in description body):
@@ -352,6 +352,28 @@ a PR that cannot merge or a deploy that failed — it just churns.
352
352
  ticket, and record it. The existing "Build `blocked` → unblock if cleared" path resumes this item on
353
353
  a later cycle once the fix ticket is terminal — a self-healing loop. Skip the resume steps below.
354
354
 
355
+ **On Linear, READ the pair's existing relations before writing that link.** Reported measured
356
+ twice: `issueRelationCreate` on a pair that already carries a `related` edge appears to CONVERT
357
+ that edge rather than add a second one — the outgoing count did not grow — and the undo is not
358
+ what it looks like, because deleting the new relation removes the link entirely instead of
359
+ restoring the original. Full reversal is delete **plus re-create with the original type**
360
+ (CodySwannGT/lisa#3605).
361
+
362
+ So: fetch the pair's relations first. If any edge already exists between the two items, do
363
+ **not** write — record the item under the run's escalations naming the existing edge and its
364
+ type, and leave both alone. A fix ticket freshly filed by this same pass has no prior edge by
365
+ construction, which is the ordinary case and stays a single write.
366
+
367
+ This matters here more than anywhere else Lisa writes a relation: this pass runs unattended on a
368
+ schedule, and `max_candidates` defaults to 100, so a converting write is not one lost edge but
369
+ up to a hundred — none of them logged, because nothing read the prior state.
370
+
371
+ **Marked UNVERIFIED, deliberately.** The conversion was observed against the raw GraphQL
372
+ mutation, and confirming it on Lisa's own path means writing a relation to a live Linear
373
+ workspace. That has not been done and must not be done to close this. The read costs one call
374
+ and is correct whether or not the semantics are what they appear to be — which is the whole
375
+ reason it is safe to adopt without settling the question.
376
+
355
377
  If the PR is healthy in-flight and no blocker is found, the work simply died mid-flight — run the **same per-item sequence
356
378
  the vendor build-intake runs**, skipping the claim transition (the item is already `claimed`):
357
379
 
@@ -931,11 +931,49 @@ proxy for the other, so read them separately:
931
931
  wedged looking abandoned.
932
932
 
933
933
  Never dismiss on `reviewDecision` alone. Dismiss the stale (often bot) review
934
- where repo policy permits, else re-request review:
934
+ where repo policy permits:
935
935
  ```bash
936
936
  gh api -X PUT repos/<owner>/<repo>/pulls/<pr>/reviews/<review_id>/dismissals \
937
937
  -f message="Addressed; threads resolved." -f event=DISMISS
938
938
  ```
939
+
940
+ **When you cannot dismiss, re-requesting is NOT the automatic fallback.** This
941
+ file already forbids re-requesting to refresh a review, in as many words, and
942
+ that prohibition applies here too — the fallback used to contradict it, and an
943
+ agent reaching it was told to do the thing the same file calls one-way
944
+ destruction (CodySwannGT/lisa#3587).
945
+
946
+ Read the reviews before deciding, because the read is cheap and settles it:
947
+
948
+ ```bash
949
+ gh api "repos/<owner>/<repo>/pulls/<pr>/reviews" \
950
+ --jq '[.[] | {author: .user.login, state: .state, hasBody: (((.body // "") | length) > 0)}]'
951
+ ```
952
+
953
+ - **A human reviewer** — re-request freely. Re-requesting a person's review is
954
+ unremarkable and loses nothing.
955
+ - **A bot reviewer whose review is NOT the only substantive one** — re-request.
956
+ Something else still evidences that this head was read.
957
+ - **A bot reviewer whose review IS the only substantive one on the PR** — do
958
+ NOT re-request. **Escalate for a dismissal decision instead.** Under a
959
+ throttle or a spending cap the bot answers a new request with a status
960
+ carrying no review body, that hollow answer becomes its latest review, and
961
+ every effective-review computation — including the "latest non-dismissed
962
+ review per reviewer" rule immediately below — then reads the hollow one. The
963
+ substantive review is still retrievable from the reviews API; what is lost is
964
+ its standing as the reviewer's current review, which is the thing the merge
965
+ standard reads.
966
+
967
+ Deleting the fallback outright would over-correct: it is the only remedy a lane
968
+ without dismissal permission has, and that permission is not universal. This
969
+ narrows it to the case that costs nothing.
970
+
971
+ **Unverified, and stated as such rather than asserted:** that a bot's re-review
972
+ REPLACES its predecessor in the effective set is inferred from the observed
973
+ consequence, not measured against the vendor. It has not been tested here, and
974
+ testing it means re-requesting on a live pull request. The guidance is worth
975
+ following either way — the escalation costs one human decision, and the failure
976
+ it avoids is one-way.
939
977
  Some org rulesets allow 0 approvals yet a bot `CHANGES_REQUESTED` still blocks
940
978
  auto-merge — dismissing the stale review after resolving all threads is what
941
979
  unblocks it.
@@ -229,6 +229,14 @@ For each candidate, classify the relationship:
229
229
 
230
230
  Linear native relations are set on the Issue via `save_issue`'s `relations` field (or via a paired `save_issue_relation` call if available in the MCP). For Project-level (Epic) relationships, capture them in the description under `## Related Projects` since Linear doesn't model relations between Projects natively.
231
231
 
232
+ **Adding a relation to an already-linked pair may be a REPLACE, not an add.** Reported measured twice against the raw `issueRelationCreate` mutation: creating a `blocks` edge between two issues already carrying a `related` edge converted that edge in place — the source issue's outgoing relation count did not grow (CodySwannGT/lisa#3605). Relationship discovery is mandatory on the UPDATE path as well as creation, so this skill's own contract routinely points it at pairs that already have edges.
233
+
234
+ So on an update, read the pair's existing relations before writing one. Where an edge already exists and differs in type, surface it and let a human decide rather than writing over it — **an inherited relation is not a verified one**, and rewriting it silently removes the only moment anyone would check what it meant.
235
+
236
+ **The true undo is delete plus re-create with the original type.** A single delete removes the link entirely and does not restore what was there before, so any runbook recording "undo = delete relation X" has written down a plan that loses the prior relationship.
237
+
238
+ **Marked UNVERIFIED.** The observations were against the raw mutation; whether the MCP's `relations` field diffs before writing has not been tested, and testing it means writing a relation to a live workspace. Note that this does not make Lisa safe by default: `lisa-linear-access` resolves `LINEAR_API_KEY` + raw GraphQL as **tier 1, ahead of the MCP**, so the normal autonomous path is the one the observations were made against.
239
+
232
240
  ### 4c. Remote Links
233
241
 
234
242
  Identify and attach (Linear stores attachments / links on the Issue or in description body):
@@ -352,6 +352,28 @@ a PR that cannot merge or a deploy that failed — it just churns.
352
352
  ticket, and record it. The existing "Build `blocked` → unblock if cleared" path resumes this item on
353
353
  a later cycle once the fix ticket is terminal — a self-healing loop. Skip the resume steps below.
354
354
 
355
+ **On Linear, READ the pair's existing relations before writing that link.** Reported measured
356
+ twice: `issueRelationCreate` on a pair that already carries a `related` edge appears to CONVERT
357
+ that edge rather than add a second one — the outgoing count did not grow — and the undo is not
358
+ what it looks like, because deleting the new relation removes the link entirely instead of
359
+ restoring the original. Full reversal is delete **plus re-create with the original type**
360
+ (CodySwannGT/lisa#3605).
361
+
362
+ So: fetch the pair's relations first. If any edge already exists between the two items, do
363
+ **not** write — record the item under the run's escalations naming the existing edge and its
364
+ type, and leave both alone. A fix ticket freshly filed by this same pass has no prior edge by
365
+ construction, which is the ordinary case and stays a single write.
366
+
367
+ This matters here more than anywhere else Lisa writes a relation: this pass runs unattended on a
368
+ schedule, and `max_candidates` defaults to 100, so a converting write is not one lost edge but
369
+ up to a hundred — none of them logged, because nothing read the prior state.
370
+
371
+ **Marked UNVERIFIED, deliberately.** The conversion was observed against the raw GraphQL
372
+ mutation, and confirming it on Lisa's own path means writing a relation to a live Linear
373
+ workspace. That has not been done and must not be done to close this. The read costs one call
374
+ and is correct whether or not the semantics are what they appear to be — which is the whole
375
+ reason it is safe to adopt without settling the question.
376
+
355
377
  If the PR is healthy in-flight and no blocker is found, the work simply died mid-flight — run the **same per-item sequence
356
378
  the vendor build-intake runs**, skipping the claim transition (the item is already `claimed`):
357
379
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -931,11 +931,49 @@ proxy for the other, so read them separately:
931
931
  wedged looking abandoned.
932
932
 
933
933
  Never dismiss on `reviewDecision` alone. Dismiss the stale (often bot) review
934
- where repo policy permits, else re-request review:
934
+ where repo policy permits:
935
935
  ```bash
936
936
  gh api -X PUT repos/<owner>/<repo>/pulls/<pr>/reviews/<review_id>/dismissals \
937
937
  -f message="Addressed; threads resolved." -f event=DISMISS
938
938
  ```
939
+
940
+ **When you cannot dismiss, re-requesting is NOT the automatic fallback.** This
941
+ file already forbids re-requesting to refresh a review, in as many words, and
942
+ that prohibition applies here too — the fallback used to contradict it, and an
943
+ agent reaching it was told to do the thing the same file calls one-way
944
+ destruction (CodySwannGT/lisa#3587).
945
+
946
+ Read the reviews before deciding, because the read is cheap and settles it:
947
+
948
+ ```bash
949
+ gh api "repos/<owner>/<repo>/pulls/<pr>/reviews" \
950
+ --jq '[.[] | {author: .user.login, state: .state, hasBody: (((.body // "") | length) > 0)}]'
951
+ ```
952
+
953
+ - **A human reviewer** — re-request freely. Re-requesting a person's review is
954
+ unremarkable and loses nothing.
955
+ - **A bot reviewer whose review is NOT the only substantive one** — re-request.
956
+ Something else still evidences that this head was read.
957
+ - **A bot reviewer whose review IS the only substantive one on the PR** — do
958
+ NOT re-request. **Escalate for a dismissal decision instead.** Under a
959
+ throttle or a spending cap the bot answers a new request with a status
960
+ carrying no review body, that hollow answer becomes its latest review, and
961
+ every effective-review computation — including the "latest non-dismissed
962
+ review per reviewer" rule immediately below — then reads the hollow one. The
963
+ substantive review is still retrievable from the reviews API; what is lost is
964
+ its standing as the reviewer's current review, which is the thing the merge
965
+ standard reads.
966
+
967
+ Deleting the fallback outright would over-correct: it is the only remedy a lane
968
+ without dismissal permission has, and that permission is not universal. This
969
+ narrows it to the case that costs nothing.
970
+
971
+ **Unverified, and stated as such rather than asserted:** that a bot's re-review
972
+ REPLACES its predecessor in the effective set is inferred from the observed
973
+ consequence, not measured against the vendor. It has not been tested here, and
974
+ testing it means re-requesting on a live pull request. The guidance is worth
975
+ following either way — the escalation costs one human decision, and the failure
976
+ it avoids is one-way.
939
977
  Some org rulesets allow 0 approvals yet a bot `CHANGES_REQUESTED` still blocks
940
978
  auto-merge — dismissing the stale review after resolving all threads is what
941
979
  unblocks it.
@@ -229,6 +229,14 @@ For each candidate, classify the relationship:
229
229
 
230
230
  Linear native relations are set on the Issue via `save_issue`'s `relations` field (or via a paired `save_issue_relation` call if available in the MCP). For Project-level (Epic) relationships, capture them in the description under `## Related Projects` since Linear doesn't model relations between Projects natively.
231
231
 
232
+ **Adding a relation to an already-linked pair may be a REPLACE, not an add.** Reported measured twice against the raw `issueRelationCreate` mutation: creating a `blocks` edge between two issues already carrying a `related` edge converted that edge in place — the source issue's outgoing relation count did not grow (CodySwannGT/lisa#3605). Relationship discovery is mandatory on the UPDATE path as well as creation, so this skill's own contract routinely points it at pairs that already have edges.
233
+
234
+ So on an update, read the pair's existing relations before writing one. Where an edge already exists and differs in type, surface it and let a human decide rather than writing over it — **an inherited relation is not a verified one**, and rewriting it silently removes the only moment anyone would check what it meant.
235
+
236
+ **The true undo is delete plus re-create with the original type.** A single delete removes the link entirely and does not restore what was there before, so any runbook recording "undo = delete relation X" has written down a plan that loses the prior relationship.
237
+
238
+ **Marked UNVERIFIED.** The observations were against the raw mutation; whether the MCP's `relations` field diffs before writing has not been tested, and testing it means writing a relation to a live workspace. Note that this does not make Lisa safe by default: `lisa-linear-access` resolves `LINEAR_API_KEY` + raw GraphQL as **tier 1, ahead of the MCP**, so the normal autonomous path is the one the observations were made against.
239
+
232
240
  ### 4c. Remote Links
233
241
 
234
242
  Identify and attach (Linear stores attachments / links on the Issue or in description body):
@@ -352,6 +352,28 @@ a PR that cannot merge or a deploy that failed — it just churns.
352
352
  ticket, and record it. The existing "Build `blocked` → unblock if cleared" path resumes this item on
353
353
  a later cycle once the fix ticket is terminal — a self-healing loop. Skip the resume steps below.
354
354
 
355
+ **On Linear, READ the pair's existing relations before writing that link.** Reported measured
356
+ twice: `issueRelationCreate` on a pair that already carries a `related` edge appears to CONVERT
357
+ that edge rather than add a second one — the outgoing count did not grow — and the undo is not
358
+ what it looks like, because deleting the new relation removes the link entirely instead of
359
+ restoring the original. Full reversal is delete **plus re-create with the original type**
360
+ (CodySwannGT/lisa#3605).
361
+
362
+ So: fetch the pair's relations first. If any edge already exists between the two items, do
363
+ **not** write — record the item under the run's escalations naming the existing edge and its
364
+ type, and leave both alone. A fix ticket freshly filed by this same pass has no prior edge by
365
+ construction, which is the ordinary case and stays a single write.
366
+
367
+ This matters here more than anywhere else Lisa writes a relation: this pass runs unattended on a
368
+ schedule, and `max_candidates` defaults to 100, so a converting write is not one lost edge but
369
+ up to a hundred — none of them logged, because nothing read the prior state.
370
+
371
+ **Marked UNVERIFIED, deliberately.** The conversion was observed against the raw GraphQL
372
+ mutation, and confirming it on Lisa's own path means writing a relation to a live Linear
373
+ workspace. That has not been done and must not be done to close this. The read costs one call
374
+ and is correct whether or not the semantics are what they appear to be — which is the whole
375
+ reason it is safe to adopt without settling the question.
376
+
355
377
  If the PR is healthy in-flight and no blocker is found, the work simply died mid-flight — run the **same per-item sequence
356
378
  the vendor build-intake runs**, skipping the claim transition (the item is already `claimed`):
357
379
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -931,11 +931,49 @@ proxy for the other, so read them separately:
931
931
  wedged looking abandoned.
932
932
 
933
933
  Never dismiss on `reviewDecision` alone. Dismiss the stale (often bot) review
934
- where repo policy permits, else re-request review:
934
+ where repo policy permits:
935
935
  ```bash
936
936
  gh api -X PUT repos/<owner>/<repo>/pulls/<pr>/reviews/<review_id>/dismissals \
937
937
  -f message="Addressed; threads resolved." -f event=DISMISS
938
938
  ```
939
+
940
+ **When you cannot dismiss, re-requesting is NOT the automatic fallback.** This
941
+ file already forbids re-requesting to refresh a review, in as many words, and
942
+ that prohibition applies here too — the fallback used to contradict it, and an
943
+ agent reaching it was told to do the thing the same file calls one-way
944
+ destruction (CodySwannGT/lisa#3587).
945
+
946
+ Read the reviews before deciding, because the read is cheap and settles it:
947
+
948
+ ```bash
949
+ gh api "repos/<owner>/<repo>/pulls/<pr>/reviews" \
950
+ --jq '[.[] | {author: .user.login, state: .state, hasBody: (((.body // "") | length) > 0)}]'
951
+ ```
952
+
953
+ - **A human reviewer** — re-request freely. Re-requesting a person's review is
954
+ unremarkable and loses nothing.
955
+ - **A bot reviewer whose review is NOT the only substantive one** — re-request.
956
+ Something else still evidences that this head was read.
957
+ - **A bot reviewer whose review IS the only substantive one on the PR** — do
958
+ NOT re-request. **Escalate for a dismissal decision instead.** Under a
959
+ throttle or a spending cap the bot answers a new request with a status
960
+ carrying no review body, that hollow answer becomes its latest review, and
961
+ every effective-review computation — including the "latest non-dismissed
962
+ review per reviewer" rule immediately below — then reads the hollow one. The
963
+ substantive review is still retrievable from the reviews API; what is lost is
964
+ its standing as the reviewer's current review, which is the thing the merge
965
+ standard reads.
966
+
967
+ Deleting the fallback outright would over-correct: it is the only remedy a lane
968
+ without dismissal permission has, and that permission is not universal. This
969
+ narrows it to the case that costs nothing.
970
+
971
+ **Unverified, and stated as such rather than asserted:** that a bot's re-review
972
+ REPLACES its predecessor in the effective set is inferred from the observed
973
+ consequence, not measured against the vendor. It has not been tested here, and
974
+ testing it means re-requesting on a live pull request. The guidance is worth
975
+ following either way — the escalation costs one human decision, and the failure
976
+ it avoids is one-way.
939
977
  Some org rulesets allow 0 approvals yet a bot `CHANGES_REQUESTED` still blocks
940
978
  auto-merge — dismissing the stale review after resolving all threads is what
941
979
  unblocks it.
@@ -229,6 +229,14 @@ For each candidate, classify the relationship:
229
229
 
230
230
  Linear native relations are set on the Issue via `save_issue`'s `relations` field (or via a paired `save_issue_relation` call if available in the MCP). For Project-level (Epic) relationships, capture them in the description under `## Related Projects` since Linear doesn't model relations between Projects natively.
231
231
 
232
+ **Adding a relation to an already-linked pair may be a REPLACE, not an add.** Reported measured twice against the raw `issueRelationCreate` mutation: creating a `blocks` edge between two issues already carrying a `related` edge converted that edge in place — the source issue's outgoing relation count did not grow (CodySwannGT/lisa#3605). Relationship discovery is mandatory on the UPDATE path as well as creation, so this skill's own contract routinely points it at pairs that already have edges.
233
+
234
+ So on an update, read the pair's existing relations before writing one. Where an edge already exists and differs in type, surface it and let a human decide rather than writing over it — **an inherited relation is not a verified one**, and rewriting it silently removes the only moment anyone would check what it meant.
235
+
236
+ **The true undo is delete plus re-create with the original type.** A single delete removes the link entirely and does not restore what was there before, so any runbook recording "undo = delete relation X" has written down a plan that loses the prior relationship.
237
+
238
+ **Marked UNVERIFIED.** The observations were against the raw mutation; whether the MCP's `relations` field diffs before writing has not been tested, and testing it means writing a relation to a live workspace. Note that this does not make Lisa safe by default: `lisa-linear-access` resolves `LINEAR_API_KEY` + raw GraphQL as **tier 1, ahead of the MCP**, so the normal autonomous path is the one the observations were made against.
239
+
232
240
  ### 4c. Remote Links
233
241
 
234
242
  Identify and attach (Linear stores attachments / links on the Issue or in description body):
@@ -352,6 +352,28 @@ a PR that cannot merge or a deploy that failed — it just churns.
352
352
  ticket, and record it. The existing "Build `blocked` → unblock if cleared" path resumes this item on
353
353
  a later cycle once the fix ticket is terminal — a self-healing loop. Skip the resume steps below.
354
354
 
355
+ **On Linear, READ the pair's existing relations before writing that link.** Reported measured
356
+ twice: `issueRelationCreate` on a pair that already carries a `related` edge appears to CONVERT
357
+ that edge rather than add a second one — the outgoing count did not grow — and the undo is not
358
+ what it looks like, because deleting the new relation removes the link entirely instead of
359
+ restoring the original. Full reversal is delete **plus re-create with the original type**
360
+ (CodySwannGT/lisa#3605).
361
+
362
+ So: fetch the pair's relations first. If any edge already exists between the two items, do
363
+ **not** write — record the item under the run's escalations naming the existing edge and its
364
+ type, and leave both alone. A fix ticket freshly filed by this same pass has no prior edge by
365
+ construction, which is the ordinary case and stays a single write.
366
+
367
+ This matters here more than anywhere else Lisa writes a relation: this pass runs unattended on a
368
+ schedule, and `max_candidates` defaults to 100, so a converting write is not one lost edge but
369
+ up to a hundred — none of them logged, because nothing read the prior state.
370
+
371
+ **Marked UNVERIFIED, deliberately.** The conversion was observed against the raw GraphQL
372
+ mutation, and confirming it on Lisa's own path means writing a relation to a live Linear
373
+ workspace. That has not been done and must not be done to close this. The read costs one call
374
+ and is correct whether or not the semantics are what they appear to be — which is the whole
375
+ reason it is safe to adopt without settling the question.
376
+
355
377
  If the PR is healthy in-flight and no blocker is found, the work simply died mid-flight — run the **same per-item sequence
356
378
  the vendor build-intake runs**, skipping the claim transition (the item is already `claimed`):
357
379
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -931,11 +931,49 @@ proxy for the other, so read them separately:
931
931
  wedged looking abandoned.
932
932
 
933
933
  Never dismiss on `reviewDecision` alone. Dismiss the stale (often bot) review
934
- where repo policy permits, else re-request review:
934
+ where repo policy permits:
935
935
  ```bash
936
936
  gh api -X PUT repos/<owner>/<repo>/pulls/<pr>/reviews/<review_id>/dismissals \
937
937
  -f message="Addressed; threads resolved." -f event=DISMISS
938
938
  ```
939
+
940
+ **When you cannot dismiss, re-requesting is NOT the automatic fallback.** This
941
+ file already forbids re-requesting to refresh a review, in as many words, and
942
+ that prohibition applies here too — the fallback used to contradict it, and an
943
+ agent reaching it was told to do the thing the same file calls one-way
944
+ destruction (CodySwannGT/lisa#3587).
945
+
946
+ Read the reviews before deciding, because the read is cheap and settles it:
947
+
948
+ ```bash
949
+ gh api "repos/<owner>/<repo>/pulls/<pr>/reviews" \
950
+ --jq '[.[] | {author: .user.login, state: .state, hasBody: (((.body // "") | length) > 0)}]'
951
+ ```
952
+
953
+ - **A human reviewer** — re-request freely. Re-requesting a person's review is
954
+ unremarkable and loses nothing.
955
+ - **A bot reviewer whose review is NOT the only substantive one** — re-request.
956
+ Something else still evidences that this head was read.
957
+ - **A bot reviewer whose review IS the only substantive one on the PR** — do
958
+ NOT re-request. **Escalate for a dismissal decision instead.** Under a
959
+ throttle or a spending cap the bot answers a new request with a status
960
+ carrying no review body, that hollow answer becomes its latest review, and
961
+ every effective-review computation — including the "latest non-dismissed
962
+ review per reviewer" rule immediately below — then reads the hollow one. The
963
+ substantive review is still retrievable from the reviews API; what is lost is
964
+ its standing as the reviewer's current review, which is the thing the merge
965
+ standard reads.
966
+
967
+ Deleting the fallback outright would over-correct: it is the only remedy a lane
968
+ without dismissal permission has, and that permission is not universal. This
969
+ narrows it to the case that costs nothing.
970
+
971
+ **Unverified, and stated as such rather than asserted:** that a bot's re-review
972
+ REPLACES its predecessor in the effective set is inferred from the observed
973
+ consequence, not measured against the vendor. It has not been tested here, and
974
+ testing it means re-requesting on a live pull request. The guidance is worth
975
+ following either way — the escalation costs one human decision, and the failure
976
+ it avoids is one-way.
939
977
  Some org rulesets allow 0 approvals yet a bot `CHANGES_REQUESTED` still blocks
940
978
  auto-merge — dismissing the stale review after resolving all threads is what
941
979
  unblocks it.
@@ -229,6 +229,14 @@ For each candidate, classify the relationship:
229
229
 
230
230
  Linear native relations are set on the Issue via `save_issue`'s `relations` field (or via a paired `save_issue_relation` call if available in the MCP). For Project-level (Epic) relationships, capture them in the description under `## Related Projects` since Linear doesn't model relations between Projects natively.
231
231
 
232
+ **Adding a relation to an already-linked pair may be a REPLACE, not an add.** Reported measured twice against the raw `issueRelationCreate` mutation: creating a `blocks` edge between two issues already carrying a `related` edge converted that edge in place — the source issue's outgoing relation count did not grow (CodySwannGT/lisa#3605). Relationship discovery is mandatory on the UPDATE path as well as creation, so this skill's own contract routinely points it at pairs that already have edges.
233
+
234
+ So on an update, read the pair's existing relations before writing one. Where an edge already exists and differs in type, surface it and let a human decide rather than writing over it — **an inherited relation is not a verified one**, and rewriting it silently removes the only moment anyone would check what it meant.
235
+
236
+ **The true undo is delete plus re-create with the original type.** A single delete removes the link entirely and does not restore what was there before, so any runbook recording "undo = delete relation X" has written down a plan that loses the prior relationship.
237
+
238
+ **Marked UNVERIFIED.** The observations were against the raw mutation; whether the MCP's `relations` field diffs before writing has not been tested, and testing it means writing a relation to a live workspace. Note that this does not make Lisa safe by default: `lisa-linear-access` resolves `LINEAR_API_KEY` + raw GraphQL as **tier 1, ahead of the MCP**, so the normal autonomous path is the one the observations were made against.
239
+
232
240
  ### 4c. Remote Links
233
241
 
234
242
  Identify and attach (Linear stores attachments / links on the Issue or in description body):
@@ -352,6 +352,28 @@ a PR that cannot merge or a deploy that failed — it just churns.
352
352
  ticket, and record it. The existing "Build `blocked` → unblock if cleared" path resumes this item on
353
353
  a later cycle once the fix ticket is terminal — a self-healing loop. Skip the resume steps below.
354
354
 
355
+ **On Linear, READ the pair's existing relations before writing that link.** Reported measured
356
+ twice: `issueRelationCreate` on a pair that already carries a `related` edge appears to CONVERT
357
+ that edge rather than add a second one — the outgoing count did not grow — and the undo is not
358
+ what it looks like, because deleting the new relation removes the link entirely instead of
359
+ restoring the original. Full reversal is delete **plus re-create with the original type**
360
+ (CodySwannGT/lisa#3605).
361
+
362
+ So: fetch the pair's relations first. If any edge already exists between the two items, do
363
+ **not** write — record the item under the run's escalations naming the existing edge and its
364
+ type, and leave both alone. A fix ticket freshly filed by this same pass has no prior edge by
365
+ construction, which is the ordinary case and stays a single write.
366
+
367
+ This matters here more than anywhere else Lisa writes a relation: this pass runs unattended on a
368
+ schedule, and `max_candidates` defaults to 100, so a converting write is not one lost edge but
369
+ up to a hundred — none of them logged, because nothing read the prior state.
370
+
371
+ **Marked UNVERIFIED, deliberately.** The conversion was observed against the raw GraphQL
372
+ mutation, and confirming it on Lisa's own path means writing a relation to a live Linear
373
+ workspace. That has not been done and must not be done to close this. The read costs one call
374
+ and is correct whether or not the semantics are what they appear to be — which is the whole
375
+ reason it is safe to adopt without settling the question.
376
+
355
377
  If the PR is healthy in-flight and no blocker is found, the work simply died mid-flight — run the **same per-item sequence
356
378
  the vendor build-intake runs**, skipping the claim transition (the item is already `claimed`):
357
379
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.53.7",
3
+ "version": "4.54.1",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"