@codyswann/lisa 2.237.0 → 2.239.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.
Files changed (133) hide show
  1. package/dist/core/learnings-writer.d.ts +22 -0
  2. package/dist/core/learnings-writer.d.ts.map +1 -1
  3. package/dist/core/learnings-writer.js +51 -8
  4. package/dist/core/learnings-writer.js.map +1 -1
  5. package/dist/core/learnings.d.ts +1 -1
  6. package/dist/core/learnings.d.ts.map +1 -1
  7. package/dist/core/learnings.js +1 -1
  8. package/dist/core/learnings.js.map +1 -1
  9. package/package.json +1 -1
  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-atlassian-access/SKILL.md +12 -1
  13. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  14. package/plugins/lisa/.codex-plugin/skills/lisa-git-submit-pr/SKILL.md +3 -2
  15. package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +5 -1
  16. package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +2 -0
  17. package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +5 -1
  18. package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +54 -0
  19. package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +5 -1
  20. package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/SKILL.md +145 -0
  21. package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/agents/openai.yaml +4 -0
  22. package/plugins/lisa/agents/learning-judge.md +119 -0
  23. package/plugins/lisa/commands/persist-learning.md +6 -0
  24. package/plugins/lisa/rules/eager/rejection-detection.md +40 -0
  25. package/plugins/lisa/rules/reference/rejection-detection.md +103 -0
  26. package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +12 -1
  27. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  28. package/plugins/lisa/skills/lisa-git-submit-pr/SKILL.md +3 -2
  29. package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +5 -1
  30. package/plugins/lisa/skills/lisa-implement/SKILL.md +2 -0
  31. package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +5 -1
  32. package/plugins/lisa/skills/lisa-linear-access/SKILL.md +54 -0
  33. package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +5 -1
  34. package/plugins/lisa/skills/lisa-persist-learning/SKILL.md +145 -0
  35. package/plugins/lisa/skills/lisa-persist-learning/agents/openai.yaml +4 -0
  36. package/plugins/lisa-agy/agents/learning-judge.md +119 -0
  37. package/plugins/lisa-agy/commands/lisa/persist-learning.md +6 -0
  38. package/plugins/lisa-agy/plugin.json +1 -1
  39. package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +12 -1
  40. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  41. package/plugins/lisa-agy/skills/lisa-git-submit-pr/SKILL.md +3 -2
  42. package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +5 -1
  43. package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +2 -0
  44. package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +5 -1
  45. package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +54 -0
  46. package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +5 -1
  47. package/plugins/lisa-agy/skills/lisa-persist-learning/SKILL.md +145 -0
  48. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  51. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-copilot/agents/learning-judge.agent.md +119 -0
  55. package/plugins/lisa-copilot/commands/lisa/persist-learning.md +6 -0
  56. package/plugins/lisa-copilot/rules/eager/rejection-detection.md +40 -0
  57. package/plugins/lisa-copilot/rules/reference/rejection-detection.md +103 -0
  58. package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +12 -1
  59. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  60. package/plugins/lisa-copilot/skills/lisa-git-submit-pr/SKILL.md +3 -2
  61. package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +5 -1
  62. package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +2 -0
  63. package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +5 -1
  64. package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +54 -0
  65. package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +5 -1
  66. package/plugins/lisa-copilot/skills/lisa-persist-learning/SKILL.md +145 -0
  67. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-cursor/agents/learning-judge.md +119 -0
  69. package/plugins/lisa-cursor/commands/lisa/persist-learning.md +6 -0
  70. package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +108 -0
  71. package/plugins/lisa-cursor/rules/rejection-detection.mdc +45 -0
  72. package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +12 -1
  73. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  74. package/plugins/lisa-cursor/skills/lisa-git-submit-pr/SKILL.md +3 -2
  75. package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +5 -1
  76. package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +2 -0
  77. package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +5 -1
  78. package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +54 -0
  79. package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +5 -1
  80. package/plugins/lisa-cursor/skills/lisa-persist-learning/SKILL.md +145 -0
  81. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  83. package/plugins/lisa-expo-agy/plugin.json +1 -1
  84. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  88. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  89. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  91. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  93. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  94. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  96. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  98. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  99. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  103. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  104. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  106. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  108. package/plugins/lisa-rails-agy/plugin.json +1 -1
  109. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  110. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  113. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  114. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  115. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  116. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  117. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  118. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  119. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  120. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  121. package/plugins/src/base/agents/learning-judge.md +119 -0
  122. package/plugins/src/base/commands/persist-learning.md +6 -0
  123. package/plugins/src/base/rules/eager/rejection-detection.md +40 -0
  124. package/plugins/src/base/rules/reference/rejection-detection.md +103 -0
  125. package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +12 -1
  126. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
  127. package/plugins/src/base/skills/lisa-git-submit-pr/SKILL.md +3 -2
  128. package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +5 -1
  129. package/plugins/src/base/skills/lisa-implement/SKILL.md +2 -0
  130. package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +5 -1
  131. package/plugins/src/base/skills/lisa-linear-access/SKILL.md +54 -0
  132. package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +5 -1
  133. package/plugins/src/base/skills/lisa-persist-learning/SKILL.md +145 -0
@@ -1,10 +1,32 @@
1
+ /** Write-time consolidation options for {@link persistConsolidatedLearning}. */
2
+ export interface ConsolidatedLearningOptions {
3
+ /** Ids of existing entries the new entry merges or replaces. */
4
+ readonly supersede?: readonly string[];
5
+ }
1
6
  /**
2
7
  * Persist one already-selected learning without generating or truncating it.
8
+ * Append-only: a duplicate id always throws. Kept as the stable back-compat
9
+ * entry point; consolidation-aware writers use
10
+ * {@link persistConsolidatedLearning} instead.
3
11
  * @param projectRoot - Absolute path to the host project root
4
12
  * @param candidate - Untrusted seven-field learning entry
5
13
  * @returns Absolute path to the persisted learnings file
6
14
  */
7
15
  export declare function persistLearningEntry(projectRoot: string, candidate: unknown): Promise<string>;
16
+ /**
17
+ * Persist one learning with mandatory write-time consolidation semantics:
18
+ * entries named in `supersede` are dropped from the document in the same
19
+ * atomic write that adds the new entry, so a related existing entry is merged
20
+ * or replaced instead of gaining a near-duplicate sibling. The rendered
21
+ * document is re-validated against the shared entry and file budgets after
22
+ * consolidation. Without `supersede` this is exactly the append-only
23
+ * {@link persistLearningEntry} behavior, including the duplicate-id throw.
24
+ * @param projectRoot - Absolute path to the host project root
25
+ * @param candidate - Untrusted seven-field learning entry
26
+ * @param options - Optional consolidation directives
27
+ * @returns Absolute path to the persisted learnings file
28
+ */
29
+ export declare function persistConsolidatedLearning(projectRoot: string, candidate: unknown, options?: ConsolidatedLearningOptions): Promise<string>;
8
30
  export { parseLearningsFile, renderLearningsFile, } from "./learnings-document.js";
9
31
  export { validateLearningEntry } from "./learnings-entry.js";
10
32
  //# sourceMappingURL=learnings-writer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"learnings-writer.d.ts","sourceRoot":"","sources":["../../src/core/learnings-writer.ts"],"names":[],"mappings":"AAsBA;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAED,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"learnings-writer.d.ts","sourceRoot":"","sources":["../../src/core/learnings-writer.ts"],"names":[],"mappings":"AAsBA,gFAAgF;AAChF,MAAM,WAAW,2BAA2B;IAC1C,gEAAgE;IAChE,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,2BAA2B,CAC/C,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,OAAO,EAClB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -9,25 +9,44 @@ import { withLearningTargetLock } from "./learnings-lock.js";
9
9
  import { readProjectConfig, resolveProjectLearningsFile, } from "./project-config.js";
10
10
  /**
11
11
  * Persist one already-selected learning without generating or truncating it.
12
+ * Append-only: a duplicate id always throws. Kept as the stable back-compat
13
+ * entry point; consolidation-aware writers use
14
+ * {@link persistConsolidatedLearning} instead.
12
15
  * @param projectRoot - Absolute path to the host project root
13
16
  * @param candidate - Untrusted seven-field learning entry
14
17
  * @returns Absolute path to the persisted learnings file
15
18
  */
16
19
  export async function persistLearningEntry(projectRoot, candidate) {
20
+ return persistConsolidatedLearning(projectRoot, candidate);
21
+ }
22
+ /**
23
+ * Persist one learning with mandatory write-time consolidation semantics:
24
+ * entries named in `supersede` are dropped from the document in the same
25
+ * atomic write that adds the new entry, so a related existing entry is merged
26
+ * or replaced instead of gaining a near-duplicate sibling. The rendered
27
+ * document is re-validated against the shared entry and file budgets after
28
+ * consolidation. Without `supersede` this is exactly the append-only
29
+ * {@link persistLearningEntry} behavior, including the duplicate-id throw.
30
+ * @param projectRoot - Absolute path to the host project root
31
+ * @param candidate - Untrusted seven-field learning entry
32
+ * @param options - Optional consolidation directives
33
+ * @returns Absolute path to the persisted learnings file
34
+ */
35
+ export async function persistConsolidatedLearning(projectRoot, candidate, options = {}) {
17
36
  const entry = validateLearningEntry(candidate);
18
- const initialDocument = buildNextDocument([], entry);
37
+ const supersede = validateSupersedeIds(options.supersede);
19
38
  const config = await readProjectConfig(projectRoot);
20
39
  const relativeFile = resolveProjectLearningsFile(config);
21
40
  const { root, target } = resolveSafeLearningTarget(projectRoot, relativeFile);
41
+ // Fast budget fail on the lone entry before any directory is created.
42
+ buildNextDocument([], entry, []);
22
43
  await assertSafeLearningParents(root, path.dirname(target));
23
44
  await fse.ensureDir(path.dirname(target));
24
45
  return withLearningTargetLock(target, async () => {
25
46
  await assertSafeLearningParents(root, path.dirname(target));
26
47
  const existing = await readExistingLearnings(target);
27
48
  const entries = existing === undefined ? [] : parseLearningsFile(existing);
28
- const rendered = existing === undefined
29
- ? initialDocument
30
- : buildNextDocument(entries, entry);
49
+ const rendered = buildNextDocument(entries, entry, supersede);
31
50
  const temporary = path.join(path.dirname(target), `.${path.basename(target)}.${process.pid}.${crypto.randomUUID()}.tmp`);
32
51
  try {
33
52
  await writeFile(temporary, rendered, { encoding: "utf8", flag: "wx" });
@@ -43,18 +62,42 @@ export async function persistLearningEntry(projectRoot, candidate) {
43
62
  export { parseLearningsFile, renderLearningsFile, } from "./learnings-document.js";
44
63
  export { validateLearningEntry } from "./learnings-entry.js";
45
64
  /**
46
- * Build and budget-check the next canonical document.
65
+ * Build and budget-check the next canonical document, dropping superseded
66
+ * entries before the new entry is added so consolidation and the budget
67
+ * re-assertion happen in one deterministic step.
47
68
  * @param entries - Existing validated entries
48
69
  * @param entry - New validated entry
70
+ * @param supersede - Ids of existing entries the new entry replaces
49
71
  * @returns Next canonical document
50
72
  */
51
- function buildNextDocument(entries, entry) {
52
- if (entries.some(current => current.id === entry.id)) {
73
+ function buildNextDocument(entries, entry, supersede) {
74
+ const missing = supersede.filter(id => !entries.some(current => current.id === id));
75
+ if (missing.length > 0) {
76
+ throw new Error(`Cannot supersede unknown learning id(s): ${missing.join(", ")}`);
77
+ }
78
+ const supersededIds = new Set(supersede);
79
+ const retained = entries.filter(current => !supersededIds.has(current.id));
80
+ if (retained.some(current => current.id === entry.id)) {
53
81
  throw new Error(`Duplicate learning id: ${entry.id}`);
54
82
  }
55
- const nextEntries = [...entries, entry].sort((left, right) => left.id.localeCompare(right.id));
83
+ const nextEntries = [...retained, entry].sort((left, right) => left.id.localeCompare(right.id));
56
84
  const rendered = renderLearningsFile(nextEntries);
57
85
  assertDocumentBudget(rendered, nextEntries.length, "Learnings file");
58
86
  return rendered;
59
87
  }
88
+ /**
89
+ * Reject malformed supersede directives before any filesystem work.
90
+ * @param supersede - Caller-supplied (possibly untrusted) supersede ids
91
+ * @returns Deduplicated list of validated supersede ids
92
+ */
93
+ function validateSupersedeIds(supersede) {
94
+ if (supersede === undefined) {
95
+ return [];
96
+ }
97
+ if (!Array.isArray(supersede) ||
98
+ supersede.some(id => typeof id !== "string" || id.trim() === "")) {
99
+ throw new Error("Invalid supersede option: expected non-empty learning id strings");
100
+ }
101
+ return [...new Set(supersede)];
102
+ }
60
103
  //# sourceMappingURL=learnings-writer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"learnings-writer.js","sourceRoot":"","sources":["../../src/core/learnings-writer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"learnings-writer.js","sourceRoot":"","sources":["../../src/core/learnings-writer.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACL,iBAAiB,EACjB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAQ7B;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,EACnB,SAAkB;IAElB,OAAO,2BAA2B,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,WAAmB,EACnB,SAAkB,EAClB,UAAuC,EAAE;IAEzC,MAAM,KAAK,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,2BAA2B,CAAC,MAAM,CAAC,CAAC;IACzD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,yBAAyB,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAC9E,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IACjC,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,OAAO,sBAAsB,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAC3E,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CACzB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EACpB,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,MAAM,CACtE,CAAC;QACF,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACvE,MAAM,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YAC5D,MAAM,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAClC,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;;;;GAQG;AACH,SAAS,iBAAiB,CACxB,OAAiC,EACjC,KAAoB,EACpB,SAA4B;IAE5B,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAC9B,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAClD,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,4CAA4C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACjE,CAAC;IACJ,CAAC;IACD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3E,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,WAAW,GAAG,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC5D,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAChC,CAAC;IACF,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAClD,oBAAoB,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACrE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAC3B,SAAwC;IAExC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;QACzB,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAChE,CAAC;QACD,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;AACjC,CAAC"}
@@ -1,4 +1,4 @@
1
1
  export * from "./learnings-contract.js";
2
2
  export * from "./learnings-writer.js";
3
- export { DEFAULT_PROJECT_RULES_FILE, PROJECT_LEARNINGS_FILENAME, resolveProjectLearningsFile, resolveProjectRulesFile, type ProjectConfig, } from "./project-config.js";
3
+ export { DEFAULT_PROJECT_RULES_FILE, PROJECT_LEARNINGS_FILENAME, readProjectConfig, resolveProjectLearningsFile, resolveProjectRulesFile, type ProjectConfig, } from "./project-config.js";
4
4
  //# sourceMappingURL=learnings.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"learnings.d.ts","sourceRoot":"","sources":["../../src/core/learnings.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,2BAA2B,EAC3B,uBAAuB,EACvB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"learnings.d.ts","sourceRoot":"","sources":["../../src/core/learnings.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,iBAAiB,EACjB,2BAA2B,EAC3B,uBAAuB,EACvB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC"}
@@ -1,4 +1,4 @@
1
1
  export * from "./learnings-contract.js";
2
2
  export * from "./learnings-writer.js";
3
- export { DEFAULT_PROJECT_RULES_FILE, PROJECT_LEARNINGS_FILENAME, resolveProjectLearningsFile, resolveProjectRulesFile, } from "./project-config.js";
3
+ export { DEFAULT_PROJECT_RULES_FILE, PROJECT_LEARNINGS_FILENAME, readProjectConfig, resolveProjectLearningsFile, resolveProjectRulesFile, } from "./project-config.js";
4
4
  //# sourceMappingURL=learnings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"learnings.js","sourceRoot":"","sources":["../../src/core/learnings.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,2BAA2B,EAC3B,uBAAuB,GAExB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"learnings.js","sourceRoot":"","sources":["../../src/core/learnings.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,iBAAiB,EACjB,2BAA2B,EAC3B,uBAAuB,GAExB,MAAM,qBAAqB,CAAC"}
package/package.json CHANGED
@@ -102,7 +102,7 @@
102
102
  "form-data": ">=4.0.6"
103
103
  },
104
104
  "name": "@codyswann/lisa",
105
- "version": "2.237.0",
105
+ "version": "2.239.0",
106
106
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
107
107
  "main": "dist/index.js",
108
108
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.237.0",
3
+ "version": "2.239.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.237.0",
3
+ "version": "2.239.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -291,7 +291,8 @@ Substrate column meanings:
291
291
  | `write-ticket payload:<P>` (create) | guarded fallback only: `acli jira workitem create --from-json <P>` + response tenant assertion | `mcp__plugin_atlassian_atlassian__createJiraIssue` | `POST https://api.atlassian.com/ex/jira/<CLOUDID>/rest/api/3/issue` body=`<P>` |
292
292
  | `write-ticket payload:<P>` (edit) | guarded fallback only: `acli jira workitem edit <K> --from-json <P>` + response tenant assertion | `mcp__plugin_atlassian_atlassian__editJiraIssue` | `PUT https://api.atlassian.com/ex/jira/<CLOUDID>/rest/api/3/issue/<K>` body=`<P>` |
293
293
  | `transition key:<K> to:<S>` | guarded fallback only: `acli jira workitem transition --key <K> --status "<S>" --yes` + post-read tenant assertion | `mcp__plugin_atlassian_atlassian__transitionJiraIssue` | resolve transition id then `POST https://api.atlassian.com/ex/jira/<CLOUDID>/rest/api/3/issue/<K>/transitions` |
294
- | `transitions key:<K>` | (not exposed) | `mcp__plugin_atlassian_atlassian__getTransitionsForJiraIssue` | `GET https://<SITE>/rest/api/3/issue/<K>/transitions` |
294
+ | `transitions key:<K>` — **false friend:** available transitions from current status, **NOT** past history; for history use `changelog` | (not exposed) | `mcp__plugin_atlassian_atlassian__getTransitionsForJiraIssue` | `GET https://<SITE>/rest/api/3/issue/<K>/transitions` |
295
+ | `changelog key:<K>` (read; ordered past status transitions) | (not exposed) | (not exposed) | `GET https://<SITE>/rest/api/3/issue/<K>?expand=changelog` |
295
296
  | `comment key:<K> body:<B>` | guarded fallback only: `acli jira workitem comment add --key <K> --body "<B>"` + post-read tenant assertion | `mcp__plugin_atlassian_atlassian__addCommentToJiraIssue` | `POST https://api.atlassian.com/ex/jira/<CLOUDID>/rest/api/3/issue/<K>/comment` |
296
297
  | `link from:<K> to:<K2> type:<T>` | guarded fallback only: `acli jira workitem link create --in <K> --out <K2> --type "<T>" --yes` + direction and tenant assertion (see direction note) | `mcp__plugin_atlassian_atlassian__createJiraIssueLink` | `POST https://api.atlassian.com/ex/jira/<CLOUDID>/rest/api/3/issueLink` |
297
298
  | `remote-links key:<K>` | (not exposed) | `mcp__plugin_atlassian_atlassian__getJiraIssueRemoteIssueLinks` | `GET https://<SITE>/rest/api/3/issue/<K>/remotelink` |
@@ -338,6 +339,16 @@ Operations not in this table are unsupported — add an adapter row before using
338
339
  - `write-page` payload: supports a label-only mutation form — `{ "id": "<I>", "labels": { "add": [...], "remove": [...] } }` — so callers transitioning PRD lifecycle labels do not need to resend the page body. Full create/update payloads also accepted.
339
340
  - `comment-page` `kind: inline` requires `anchor` (the highlighted text the comment attaches to). `kind: footer` ignores `anchor`.
340
341
 
342
+ ### `changelog` — transition history (read-only)
343
+
344
+ `changelog key:<K>` returns the ordered past status transitions of a JIRA issue — the raw material for rejection detection (an issue that reached `review`/`done`-ward and is now back in `ready`). It is distinct from `transitions`, which is a false friend: `transitions` lists the *available* next transitions from the current status, never past ones. `read-ticket` uses `fields=*all`, which does **not** include the changelog — the expansion must be requested explicitly with `?expand=changelog`.
345
+
346
+ - **Substrate.** The only substrate that exposes the changelog is JIRA REST via the `?expand=changelog` query parameter (a read, so the `<SITE>` gateway is allowed after the token account check). Neither `acli jira workitem view` (a field-projection tool; the changelog is an `expand`, not a field) nor the Atlassian MCP surfaces a changelog expansion, so both are marked `(not exposed)` — do not invent a separate transport, and do not try to reconstruct history from `transitions`.
347
+ - **Shape.** Walk `changelog.histories[].items[]` and keep entries where `field == "status"`; for each emit `{ from, to, when, author }` — `items[].fromString` → `items[].toString`, `histories[].created` (ISO timestamp), `histories[].author.displayName`/`accountId`. Preserve JIRA's oldest→newest ordering.
348
+ - **Empty is valid.** An issue that never transitioned returns an **empty** history — an empty history is a valid result, not an error. Callers treat empty as "never left its initial status".
349
+ - **Pagination / truncation.** The issue-resource changelog (`?expand=changelog`) truncates busy issues (`changelog.maxResults`/`total`/`startAt`). When `total` exceeds what the issue resource returned, page the dedicated endpoint `GET https://<SITE>/rest/api/3/issue/<K>/changelog?startAt=<n>` until `startAt + maxResults >= total`, preserving order across pages. A silently truncated history is a correctness bug for detection.
350
+ - **Graceful degrade — never block the build.** A failed changelog fetch (network, auth, missing substrate) returns the substrate contract's `Error:` result. Callers MUST treat that as **unknown** history and proceed — a history read failure never blocks the build.
351
+
341
352
  ### Step 4 — Return result
342
353
 
343
354
  Emit either:
@@ -19,17 +19,28 @@ merges" loop. Other skills delegate here instead of re-implementing it. Runs
19
19
  `chore(release): X.Y.Z [skip ci]` commits and breaks release promotion detection.
20
20
  - `verify_commit=<sha>` — the commit that MUST end up in the merged base (for the
21
21
  ancestry check). Default: the PR head at the time this skill starts.
22
+ - `auto_merge=<true|false>` — whether this skill is allowed to merge the PR at
23
+ all. Default `true` (existing behavior, byte-identical for every current
24
+ caller). With `auto_merge=false` the PR is deliberately left for a human:
25
+ skip the **entire** "## 1. Enable auto-merge" step — including its
26
+ direct-merge capability fallback — and never run any `gh pr merge` variant.
27
+ Still drive every blocker per `on_blocker` (green checks, resolved reviews,
28
+ synced branch), then stop at the `awaiting-human` terminal state below. A
29
+ green, open, un-merged PR is the *success* outcome of this mode, not a hang.
30
+ Used by learning-persistence flows whose low-confidence PRs must wait for a
31
+ human (`lisa-persist-learning`).
22
32
  - `on_blocker=<fix|report>` — what to do when a blocker needs code or review work.
23
33
  Default `fix`.
24
34
  - **`fix`** (the full loop): resolve conflicts, fix failing checks, address +
25
35
  resolve review comments, dismiss stale review gates — drive until merged.
26
36
  - **`report`** (diagnose & mechanically nudge only): perform just the safe,
27
- idempotent, non-destructive actions — ensure auto-merge is enabled and, if the
37
+ idempotent, non-destructive actions — ensure auto-merge is enabled (when
38
+ `auto_merge=true`) and, if the
28
39
  PR is `BEHIND` but otherwise clean, run `gh pr update-branch` only when the
29
40
  base branch requires strict up-to-date checks. For **anything** that would
30
41
  require editing code, resolving threads, or dismissing a review, **do not
31
42
  act** — stop and return a structured blocker classification
32
- (`merged` / `will-merge-after-resync` / `blocked:<conflict|checks|changes_requested|deploy>`)
43
+ (`merged` / `will-merge-after-resync` / `blocked:<conflict|checks|changes_requested|deploy|pending-auto-fix>`)
33
44
  so the caller applies its own policy. This is the mode `repair-intake` and the
34
45
  build-intake skills use to diagnose-and-route without fixing in place.
35
46
 
@@ -75,6 +86,31 @@ releases is why the TTL exists; do not rely on it as the normal release path.
75
86
 
76
87
  ## 1. Enable auto-merge
77
88
 
89
+ **Gate: only when `auto_merge=true` (the default).** When `auto_merge=false`,
90
+ skip the enable step and its capability fallback — do not enable auto-merge,
91
+ and do **not** use the capability fallback below: on a repo that disallows
92
+ auto-merge, an `auto_merge=false` PR must stay OPEN for human triage, never be
93
+ silently direct-merged.
94
+
95
+ With `auto_merge=false`, also **disarm any pre-existing auto-merge latch**
96
+ before entering the watch loop — skipping the enable step is not enough when a
97
+ prior session (or `lisa-git-submit-pr`'s default path) already armed the PR,
98
+ because an armed latch would still merge the instant checks go green:
99
+
100
+ ```bash
101
+ armed=$(gh pr view <pr> --json autoMergeRequest -q .autoMergeRequest)
102
+ if [ "$armed" != "null" ] && [ -n "$armed" ]; then
103
+ gh pr merge <pr> --disable-auto
104
+ fi
105
+ gh pr view <pr> --json autoMergeRequest -q .autoMergeRequest # must print null
106
+ ```
107
+
108
+ If the disarm fails or the re-read still shows an armed `autoMergeRequest`,
109
+ **fail closed**: treat the PR as a hard block (section 4) and report that the
110
+ `awaiting-human` state was NOT reached — never proceed to a state in which the
111
+ PR could merge without a human. Once disarmed (or already unarmed), proceed
112
+ straight to the watch loop (section 2).
113
+
78
114
  Before enabling auto-merge, capture the live PR head and compare it to
79
115
  `verify_commit`:
80
116
 
@@ -98,9 +134,11 @@ started, then re-enable auto-merge. Do not leave auto-merge armed while a
98
134
  required fix, CodeRabbit follow-up, generated artifact update, or CI auto-fix is
99
135
  still in flight.
100
136
 
101
- - **Capability fallback**: if the repo disallows auto-merge, do not fail. Keep
102
- watching; once checks are green, the review gate is clear, and `mergeable == MERGEABLE`,
103
- run `gh pr merge <pr> --<merge_method>` directly.
137
+ - **Capability fallback** (`auto_merge=true` only): if the repo disallows
138
+ auto-merge, do not fail. Keep watching; once checks are green, the review gate
139
+ is clear, and `mergeable == MERGEABLE`, run `gh pr merge <pr> --<merge_method>`
140
+ directly. This fallback lives inside the gated section above — with
141
+ `auto_merge=false` it never fires; the PR remains open awaiting a human.
104
142
 
105
143
  ## 2. The watch loop
106
144
 
@@ -114,9 +152,17 @@ Handle every blocker class; after any fix, re-poll and continue. Do not stop whi
114
152
  the PR is still open and progress is possible. On each iteration, refresh the
115
153
  babysitter lease if its last stamp is older than ~30 minutes (section 0).
116
154
 
155
+ With **`auto_merge=false`**, the loop's goal changes from "merged" to "clean and
156
+ waiting": drive blockers exactly the same, but exit successfully at
157
+ `awaiting-human` (section 4) once the PR is open with green checks, a clear
158
+ review gate, and `mergeable == MERGEABLE`. Never enable auto-merge or merge
159
+ directly in this mode.
160
+
117
161
  In **`on_blocker=report`** mode, only the mechanical step (a) and auto-merge enabling
118
- apply; for any of (b)–(e) do not act — classify the blocker and return per the input
119
- contract above.
162
+ (when `auto_merge=true`) apply; for any of (b)–(f) do not act — classify the blocker
163
+ and return per the input contract above. That includes (f): adjudicating a pending
164
+ auto-fix PR (merging, closing, or deleting its branch) is destructive work, not
165
+ diagnosis — return its classification (`blocked:pending-auto-fix`) instead.
120
166
 
121
167
  ### a. Branch behind base (`mergeStateStatus == BEHIND`)
122
168
  Before proactively syncing a clean `BEHIND` PR, check whether the base branch
@@ -196,7 +242,9 @@ needed, otherwise close it and delete the side branch. Never leave it dangling
196
242
  — it represents a competing writer's pending work. Merging it mutates the
197
243
  driven branch, so treat it like any other push: disarm auto-merge first,
198
244
  re-read `headRefOid`, reset `verify_commit` to the merged head, wait for that
199
- head's checks to start, then re-enable auto-merge (section 1).
245
+ head's checks to start, then re-enable auto-merge (section 1). In
246
+ `on_blocker=report` mode this whole step is off-limits (diagnose-only): do not
247
+ merge, close, or delete anything — return `blocked:pending-auto-fix`.
200
248
 
201
249
  ## 3. Merge and verify it actually shipped (ancestry check)
202
250
 
@@ -220,6 +268,12 @@ failed drive-to-merge outcome, not a successful closeout.
220
268
  Loop until one of:
221
269
 
222
270
  - **`MERGED`** and the ancestry check passes → success.
271
+ - **`awaiting-human`** (`auto_merge=false` only) → success. The PR is `OPEN`,
272
+ required checks are green, the review gate is clear, and
273
+ `mergeable == MERGEABLE`, with auto-merge deliberately not enabled
274
+ (`gh pr view <pr> --json autoMergeRequest` shows `null`). Report the PR URL
275
+ and state — a human decides whether it merges. This is the intended outcome
276
+ of auto-merge-off mode, not a stall; do not keep looping for `MERGED`.
223
277
  - **`CLOSED`** → report (PR was closed without merge).
224
278
  - **Hard block needing a human**: an unresolvable conflict, a failing check that
225
279
  needs design input, or genuine unresolved human objection (not a bot gate). Stop
@@ -14,6 +14,7 @@ Recognized optional hints:
14
14
  - `target_branch=<branch>` or `base=<branch>` — intended PR base branch, used to decide whether a GitHub closing keyword is safe.
15
15
  - `tracker_provider=<github|linear|jira|none>` — explicit provider when the ref shape is ambiguous.
16
16
  - `pr_url=<url>` — live pull request URL, only needed when updating tracker backlinks from an existing PR context.
17
+ - `auto_merge=<true|false>` — whether the PR should merge automatically. Default `true` (existing behavior for every current caller). With `auto_merge=false`, skip step 5 entirely (never run `gh pr merge --auto`) and pass `auto_merge=false` through to the `drive-pr-to-merge` delegation in step 6 so the PR is driven to a clean, green, OPEN state and then left awaiting a human.
17
18
 
18
19
  ## Workflow
19
20
 
@@ -34,10 +35,10 @@ Recognized optional hints:
34
35
  - Include native development linkage for the source work item when `work_item_ref` can be inferred from `$ARGUMENTS`, the current branch name, an existing PR body, or the issue/ticket context passed by the caller.
35
36
  - After the PR exists, ensure the source work item has a backlink to the PR: invoke `lisa-tracker-sync` with the work item, milestone `pr-ready`, the live `pr_url`, and `tracker_provider` when known. This makes ticket -> PR linkage mandatory, not just a best-effort milestone comment.
36
37
  - After the PR exists, re-resolve the live Pull Request node id and, when `github.projects.v2` is enabled, invoke `lisa-github-project-v2` with `operation: ensure-item` and `content_node_id: <pull-request-node-id>` so linked pull requests join the configured shared Project without replacing the PR as the durable review/merge surface.
37
- 5. **Auto-merge**: Choose merge strategy by PR type:
38
+ 5. **Auto-merge** (only when `auto_merge=true`, the default — with `auto_merge=false` skip this step entirely): Choose merge strategy by PR type:
38
39
  - **Promotion PRs** (env → env, e.g. `dev` → `staging`): use `gh pr merge --auto --merge` (never squash). Squashing flattens the constituent `chore(release): X.Y.Z [skip ci]` commits into one commit titled with the PR title, stripping the `[skip ci]` markers and breaking the release workflow's promotion-detection regex — the destination branch then double-bumps its version. `--merge` keeps each `chore(release)` commit (and its `[skip ci]` marker) intact under a clean merge commit subject the workflow can recognize.
39
40
  - **Feature PRs** (anything → `dev`): use `gh pr merge --auto --merge`.
40
- 6. **Drive to merge**: Opening the PR and enabling auto-merge is not terminal. Delegate the full mergeability loop to the `drive-pr-to-merge` skill — invoke it with the PR number and `merge_method=merge` (and `verify_commit=<pushed head sha>` for the ancestry check). That skill is the single source of truth for clearing every blocker: auto-merge with direct-merge fallback, `BEHIND` re-sync, conflict resolution, failing-check fixes, human + bot (CodeRabbit) review-comment handling with GraphQL thread resolution, stale `CHANGES_REQUESTED` dismissal, and post-merge ancestry verification. It runs inline and uses plain `gh`/`git` so Claude and Codex behave identically. Do not re-implement the loop here.
41
+ 6. **Drive to merge**: Opening the PR and enabling auto-merge is not terminal. Delegate the full mergeability loop to the `drive-pr-to-merge` skill — invoke it with the PR number and `merge_method=merge` (and `verify_commit=<pushed head sha>` for the ancestry check). When the caller passed `auto_merge=false`, also pass `auto_merge=false` so the delegated loop drives the PR to green-and-open (`awaiting-human`) instead of merged — never merging it, even on repos that disallow auto-merge. That skill is the single source of truth for clearing every blocker: auto-merge with direct-merge fallback, `BEHIND` re-sync, conflict resolution, failing-check fixes, human + bot (CodeRabbit) review-comment handling with GraphQL thread resolution, stale `CHANGES_REQUESTED` dismissal, and post-merge ancestry verification. It runs inline and uses plain `gh`/`git` so Claude and Codex behave identically. Do not re-implement the loop here.
41
42
 
42
43
  ### Native Development Linkage
43
44
 
@@ -252,6 +252,10 @@ A blocker is active if it is open and has no cleared status label. Treat `status
252
252
 
253
253
  #### 3b. Claim
254
254
 
255
+ **Rejection detection runs first — before the relabel below.** Per the vendor-neutral `rejection-detection` rule (cite the slug; do not restate its classification table), classify this item at the **top of 3b, BEFORE** the `$READY → $CLAIMED` relabel — after the relabel the current-lane signal is gone. Read the item's Label-Event History from `lisa-github-read-issue` (chronological `LabeledEvent` / `UnlabeledEvent` on the configured `$READY` label) and classify it `rejection-reclaim | forward-only | never-left-ready | unknown`. Lane names come from `.lisa.config.json` (`github.labels.build.*`), never hardcoded. A failing/absent history yields `unknown` and the claim proceeds — detection never blocks the build. Items carrying a learning marker (`[lisa-learning-drop]` / `[lisa-learning-pr]` / `[lisa-learning-upstream-handoff]`) or the `learning:needs-triage` label are never rejection triggers (no learning-about-learning). Carry the classification into the relabel and lifecycle below.
256
+
257
+ **On `rejection-reclaim`, reflect before re-implementing** (per `rejection-detection`): read the rejection evidence through the access layer — the issue comments posted after the backward transition (the QA rejection comment) and the review threads on the rejected PR via `lisa-github-read-issue` — assemble ONE candidate learning (rule, why, provenance linking the rejection comment + rejected PR, evidence links, scope hint, triggering issue, fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`), and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate as a comment carrying a **visible prose line plus** the marker (a bare marker renders as an empty bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` then `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. Dedupe on `<issue>-<backward-transition-timestamp>` — a second re-claim produces no duplicate. Unreadable/absent evidence → no candidate, still implement.
258
+
255
259
  ```bash
256
260
  gh issue edit <number> --repo <org>/<repo> --remove-label "$READY" --add-label "$CLAIMED"
257
261
  # Assign to the authenticated user ONLY when the issue is currently unassigned (attributable claim;
@@ -274,7 +278,7 @@ After the claim succeeds, run the per-issue lifecycle defined by the `github-age
274
278
  - `lisa-github-verify` — pre-flight quality gate, including the draft-then-block procedure on FAIL
275
279
  - `lisa-ticket-triage` — analytical triage gate (a `BLOCKED` verdict stops the cycle with findings posted)
276
280
  - Intent determination from the `type:` label
277
- 2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <org>/<repo>#<number>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
281
+ 2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <org>/<repo>#<number>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. **When 3b classified this item `rejection-reclaim`, the context bundle passed to `lisa-implement` MUST include the rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong) — reuse the evidence already read in 3b, do not fetch it twice — so the plan can address it per `rejection-detection`; absence of evidence never blocks. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
278
282
  3. **Milestone sync and evidence** (`lisa-github-sync`, `lisa-github-evidence`) happen at the milestones the `github-agent` workflow defines, within the dispatched flow.
279
283
 
280
284
  If you are somehow running this skill as a spawned teammate inside an existing team (nested misrouting — Intake keeps this chain in the lead session), do NOT run the lifecycle inline and do NOT spawn named peers. Return this payload to the lead so the lead session can run this Phase 3c in-session:
@@ -49,6 +49,8 @@ The team lead does NOT read the input directly. The first task on the team's pla
49
49
 
50
50
  The input resolver is the only teammate that may be spawned before the Roster Decision exists. After it returns the resolved input, do not spawn any lifecycle, research, implementation, review, verification, or learning teammate until the Roster Decision has been recorded.
51
51
 
52
+ **Rejection evidence in the claim handoff.** When this flow was dispatched from a build-intake claim that classified the item as a `rejection-reclaim` (per the `rejection-detection` rule), the context bundle carries a **rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong). The plan MUST explicitly address that rejection evidence and MUST NOT re-propose the specific approach the rejection named as wrong — a bounced item must come back fixed, not re-bounced. `lisa-implement` cannot fetch this itself (it never sees the claim); it consumes what the handoff carries. Absence of rejection evidence never blocks — plan and implement normally.
53
+
52
54
  ## Select the agent roster
53
55
 
54
56
  Before spawning any teammate beyond the bounded input resolver, record a **Roster Decision** artifact. It must enumerate every agent or specialist type exposed by the current runtime's delegation tool and record one line per type:
@@ -196,6 +196,10 @@ This gate never blocks a legitimate flat Task/Bug: those have no open children a
196
196
 
197
197
  #### 3b. Claim
198
198
 
199
+ **Rejection detection runs first — before the transition below.** Per the vendor-neutral `rejection-detection` rule (cite the slug; do not restate its classification table), classify this ticket at the **top of 3b, BEFORE** the `$READY → $CLAIMED` transition — after the transition the current-status signal is gone. Read the ticket's status changelog via `lisa-atlassian-access operation: changelog key: <TICKET>` and classify it `rejection-reclaim | forward-only | never-left-ready | unknown` (a `rejection-reclaim` is a changelog entry whose `to` is the configured `$READY` status following an earlier `review`/`done`-ward status). Status names come from `.lisa.config.json`, never hardcoded. A failing/absent changelog yields `unknown` and the claim proceeds — detection never blocks the build. Tickets carrying a learning marker (`[lisa-learning-drop]` / `[lisa-learning-pr]` / `[lisa-learning-upstream-handoff]`) or the `learning:needs-triage` label are never rejection triggers (no learning-about-learning). Carry the classification into the transition and lifecycle below.
200
+
201
+ **On `rejection-reclaim`, reflect before re-implementing** (per `rejection-detection`): read the rejection evidence through the access layer — the ticket comments posted after the backward transition (the QA rejection comment) via `lisa-atlassian-access operation: read-ticket` / `comment` reads and the review threads on the rejected PR — assemble ONE candidate learning (rule, why, provenance linking the rejection comment + rejected PR, evidence links, scope hint, triggering issue, fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`), and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate via `lisa-atlassian-access operation: comment` as a comment carrying a **visible prose line plus** the marker (a bare marker renders as an empty bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` then `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. Dedupe on `<issue>-<backward-transition-timestamp>` — a second re-claim produces no duplicate. Unreadable/absent evidence → no candidate, still implement.
202
+
199
203
  Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa-atlassian-access` `operation: transition key: <TICKET> to: "$CLAIMED"`.
200
204
  - **Assign to the authenticated user when the ticket is unassigned.** A claim must be attributable. If the ticket has no assignee, assign it to the authenticated account — prefer acli `--assignee @me` (resolves server-side to the authenticated user, which avoids the federated-`accountId` mis-assignment), or `write-ticket` with the `accountId` from the `/rest/api/3/myself` identity probe the access skill already documents. Leave an already-assigned ticket's assignee untouched — never reassign work that already has an owner.
201
205
  - Post a `[claude-build-intake]` comment via `lisa-atlassian-access` `operation: comment key: <TICKET> body: "Claimed by Claude. Starting build."`
@@ -212,7 +216,7 @@ After the claim succeeds, run the per-ticket lifecycle defined by the `jira-agen
212
216
  - `lisa-jira-verify` — pre-flight quality gate, including the draft-then-block procedure on FAIL
213
217
  - `lisa-ticket-triage` — analytical triage gate (a `BLOCKED` verdict stops the cycle with findings posted)
214
218
  - Intent determination from the issue type
215
- 2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <TICKET>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
219
+ 2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <TICKET>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. **When 3b classified this ticket `rejection-reclaim`, the context bundle passed to `lisa-implement` MUST include the rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong) — reuse the evidence already read in 3b, do not fetch it twice — so the plan can address it per `rejection-detection`; absence of evidence never blocks. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
216
220
  3. **Milestone sync and evidence** (`lisa-jira-sync`, `lisa-jira-evidence`) happen at the milestones the `jira-agent` workflow defines, within the dispatched flow.
217
221
 
218
222
  If you are somehow running this skill as a spawned teammate inside an existing team (nested misrouting — Intake keeps this chain in the lead session), do NOT run the lifecycle inline and do NOT spawn named peers. Return this payload to the lead so the lead session can run this Phase 3c in-session:
@@ -23,6 +23,7 @@ operation: get-issue id:<ID>
23
23
  operation: save-issue payload:{...}
24
24
  operation: list-comments issue_id:<ID>
25
25
  operation: save-comment issue_id:<ID> body:"..."
26
+ operation: history id:<ID>
26
27
  operation: list-issue-labels [team:<ID>]
27
28
  operation: create-issue-label payload:{...}
28
29
  operation: list-project-labels
@@ -81,6 +82,59 @@ linear_graphql() {
81
82
  Map operation names to Linear GraphQL queries/mutations in this access skill.
82
83
  Consumers pass business-shaped arguments only; they do not embed GraphQL.
83
84
 
85
+ ## `history` — transition history (read-only)
86
+
87
+ `history id:<ID>` returns an Issue's ordered past state changes — the raw
88
+ material for rejection detection (an Issue that reached a `review`/`done`-ward
89
+ state and is now back in `ready`). `IssueHistory` is reachable today through the
90
+ existing `linear_graphql` adapter but was **not** in the documented contract; an
91
+ undocumented-but-reachable capability is not exposed, so it now appears in the
92
+ Invocation Contract above. Reuse the existing adapter — this is a
93
+ contract/surface change, not a new transport (the `integration-access-layer`
94
+ rule forbids consumers from reaching around the layer).
95
+
96
+ Query through `linear_graphql` (oldest→newest; page `history(first:…, after:…)`
97
+ via `pageInfo` for busy Issues so history never silently truncates):
98
+
99
+ ```graphql
100
+ query($id:String!){
101
+ issue(id:$id){
102
+ history(first:100){
103
+ pageInfo{hasNextPage endCursor}
104
+ nodes{
105
+ createdAt
106
+ fromState{name type}
107
+ toState{name type}
108
+ actor{name}
109
+ addedLabelIds
110
+ removedLabelIds
111
+ }
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ - **Shape.** For each node emit `{ from, to, when, who }` — `fromState.name` →
118
+ `toState.name`, `createdAt` (ISO timestamp), `actor.name`. Nodes with no
119
+ `fromState`/`toState` are non-state edits (label-only, assignee, etc.); keep
120
+ them for the label stream, skip them for workflow-state ordering.
121
+ - **Label history (honest caveat).** Linear's build lanes are **label-driven**
122
+ (`lisa-linear-build-intake` keys the queue on `status:*` labels), so label
123
+ moves matter as much as workflow-state moves. `IssueHistory` carries label
124
+ changes as `addedLabelIds` / `removedLabelIds` — arrays of label **IDs**, not
125
+ names. It does **not** inline label names, and it does not carry the label's
126
+ full prior/next set — only the per-event deltas. Resolve IDs → names by
127
+ cross-referencing `list-issue-labels`. Do not overclaim: a caller that needs
128
+ `status:*` label transitions reconstructs them from the ID deltas plus the
129
+ label catalog, not from an inline name on the history node.
130
+ - **Empty is valid.** An Issue that never changed state returns an **empty**
131
+ history — an empty history is a valid result, not an error.
132
+ - **Graceful degrade — never block the build.** A failed history fetch returns
133
+ the layer's `Error:` result. Callers MUST treat that as **unknown** history
134
+ and proceed — a history read failure never blocks the build. MCP cannot reach
135
+ `IssueHistory`, so the `history` operation resolves only through the
136
+ `LINEAR_API_KEY` GraphQL substrate; without it, the result is unknown.
137
+
84
138
  ## Invariants
85
139
 
86
140
  - MCP is preferred when it is present and already authenticated.
@@ -186,6 +186,10 @@ This gate never blocks a legitimate flat Task/Bug: those have no open children a
186
186
 
187
187
  #### 3b. Claim
188
188
 
189
+ **Rejection detection runs first — before the relabel below.** Per the vendor-neutral `rejection-detection` rule (cite the slug; do not restate its classification table), classify this Issue at the **top of 3b, BEFORE** the `$READY → $CLAIMED` relabel — after the relabel the current-lane signal is gone. Read the Issue's history via `lisa-linear-access operation: history id: <ISSUE-ID>`, keyed on `status:*` **label** history (Linear build lanes are label-driven; resolve `addedLabelIds`/`removedLabelIds` against `list-issue-labels`), and classify it `rejection-reclaim | forward-only | never-left-ready | unknown` (a `rejection-reclaim` is the configured `$READY` label re-added after a later-lane label). Label names come from `.lisa.config.json`, never hardcoded. A failing/absent history yields `unknown` and the claim proceeds — detection never blocks the build. Issues carrying a learning marker (`[lisa-learning-drop]` / `[lisa-learning-pr]` / `[lisa-learning-upstream-handoff]`) or the `learning:needs-triage` label are never rejection triggers (no learning-about-learning). Carry the classification into the relabel and lifecycle below.
190
+
191
+ **On `rejection-reclaim`, reflect before re-implementing** (per `rejection-detection`): read the rejection evidence through the access layer — the Issue comments posted after the backward transition (the QA rejection comment) via `lisa-linear-access operation: list-comments` and the review threads on the rejected PR — assemble ONE candidate learning (rule, why, provenance linking the rejection comment + rejected PR, evidence links, scope hint, triggering issue, fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`), and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate via `lisa-linear-access operation: save-comment` as a comment carrying a **visible prose line plus** the marker (a bare marker renders as an empty bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` then `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. Dedupe on `<issue>-<backward-transition-timestamp>` — a second re-claim produces no duplicate. Unreadable/absent evidence → no candidate, still implement.
192
+
189
193
  Update labels via `lisa-linear-access operation: save-issue`: remove `$READY`, add `$CLAIMED`. Resolve label IDs via `list_issue_labels` (create `$CLAIMED` if missing).
190
194
 
191
195
  **Assign to the authenticated user when the Issue is unassigned.** A claim must be attributable. If the Issue has no assignee, set its `assigneeId` to the authenticated viewer (resolve the viewer's id via the Linear MCP identity — e.g. `get_user` for the current actor) through `lisa-linear-access operation: save-issue`. Leave an already-assigned Issue's assignee untouched — never reassign work that already has an owner.
@@ -205,7 +209,7 @@ After the claim succeeds, run the per-Issue lifecycle defined by the `linear-age
205
209
  - `lisa-linear-verify` — pre-flight quality gate, including the draft-then-block procedure on FAIL
206
210
  - `lisa-ticket-triage` — analytical triage gate (a `BLOCKED` verdict stops the cycle with findings posted)
207
211
  - Intent determination from the type label
208
- 2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <ISSUE-ID>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic-equivalent) — passing the full context bundle from the read step. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
212
+ 2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <ISSUE-ID>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic-equivalent) — passing the full context bundle from the read step. **When 3b classified this Issue `rejection-reclaim`, the context bundle passed to `lisa-implement` MUST include the rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong) — reuse the evidence already read in 3b, do not fetch it twice — so the plan can address it per `rejection-detection`; absence of evidence never blocks. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
209
213
  3. **Milestone sync and evidence** (`lisa-linear-sync`, `lisa-linear-evidence`) happen at the milestones the `linear-agent` workflow defines, within the dispatched flow.
210
214
 
211
215
  If you are somehow running this skill as a spawned teammate inside an existing team (nested misrouting — Intake keeps this chain in the lead session), do NOT run the lifecycle inline and do NOT spawn named peers. Return this payload to the lead so the lead session can run this Phase 3c in-session: