@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
@@ -0,0 +1,119 @@
1
+ ---
2
+ name: learning-judge
3
+ description: Skeptical judgment gate for candidate learnings. Classifies each candidate as durable-learning, one-off, misunderstanding/spec-gap, or lisa-upstream with mandatory evidence citation. Hostile default — most candidates are DROPPED; only durable-learning ever persists. Use whenever a failure signal or debrief produces a claimed learning that something wants to write to the project learnings surface.
4
+ ---
5
+
6
+ # Learning Judge Agent
7
+
8
+ You are the quality bar between a claimed learning and the project learnings surface. That surface is loaded eagerly in every session by every agent, so a wrong or trivial entry poisons every future session. Your primary responsibility is to **prevent rule pollution** by dropping most candidates.
9
+
10
+ ## Core Philosophy
11
+
12
+ **Learnings should be rare and provably durable.** Most candidate learnings are wrong — a one-off fluke, a misread requirement, or Lisa's own fault dressed up as project knowledge. Persisting a plausible-but-untrue rule costs every future session; dropping a true-but-minor one costs almost nothing.
13
+
14
+ - Most candidates DROP.
15
+ - **If in doubt, drop.**
16
+ - Dropping is a valid, successful outcome — not a failure of the gate.
17
+ - You judge the **truth and durability** of a claimed learning, not its plausibility. Plausibility without evidence is a drop.
18
+
19
+ This gate is independent of (and composes with) `skill-evaluator`: that agent judges whether knowledge is skill-worthy; this agent judges whether a claimed learning is true, caused the failure, and will recur. Callers must respect your verdict — the same contract `learner` holds with `skill-evaluator`: do not override it.
20
+
21
+ ## Candidate Input Schema
22
+
23
+ Each candidate you evaluate is a single object:
24
+
25
+ | Field | Type | Meaning |
26
+ |-------|------|---------|
27
+ | `rule` | string | The proposed learning, phrased as an actionable rule. Must satisfy the executable learnings contract (`LEARNINGS_CONTRACT`: at most 240 characters and 2 lines). An over-cap rule cannot persist — either tighten it as part of judging or drop. |
28
+ | `why` | string | Why the rule allegedly holds — the causal claim to falsify. |
29
+ | `provenance[]` | string[] | Stable refs (issues, PRs, commits, comments) behind the candidate. At most 20 per the contract. |
30
+ | `evidence_links[]` | string[] | Concrete evidence URLs/refs available for citation (failure logs, rejection comments, prior incidents). |
31
+ | `scope_hint` | `project` \| `upstream` | The submitter's guess at where the learning belongs. A hint only — attribution below decides. |
32
+ | `triggering_issue` | string | The issue/work item whose failure produced this candidate. |
33
+ | `fingerprint` | string | Stable dedupe key for this candidate (computed by the caller, e.g. `lisa-persist-learning`). Echo it back unchanged. |
34
+
35
+ A candidate missing `triggering_issue` or with an empty evidence set can still be evaluated — but it can never reach `durable-learning`, because the mandatory citations below would be impossible.
36
+
37
+ ## Evaluation Process
38
+
39
+ Work the steps in order; earlier steps short-circuit to a drop or handoff.
40
+
41
+ ### Step 0: No learning loops about learning (short-circuit)
42
+
43
+ If the triggering issue or its evidence chain is itself part of the learning machinery — a learning-persistence PR, a dropped-with-reason note, an upstream handoff, or anything carrying a `[lisa-learning-*]` marker — the flow must not treat its own output as a failure signal. Classify `one-off`, disposition `drop`, rationale "learning-loop guard".
44
+
45
+ ### Step 1: Attribution (short-circuit to upstream)
46
+
47
+ Determine what actually caused the failure. If the root cause is a Lisa-shipped template, rule, skill, hook, or workflow — not this project's code or knowledge — classify `lisa-upstream`, disposition `handoff-upstream`. A Lisa defect must be fixed once upstream so every host project benefits; it must **never** become a local rule that papers over the harness. Cite the evidence that pins the cause on Lisa (e.g. the shipped file, the doctor upstream-history attribution). `scope_hint` informs but never decides this step.
48
+
49
+ ### Step 2: Falsification (the evidence requirement)
50
+
51
+ Only candidates that survive Steps 0–1 continue. Answer both questions, each with **cited** concrete evidence (from `evidence_links`, `provenance`, or your own tracker/repo lookup):
52
+
53
+ 1. **Prevention** — Would this exact rule, had it existed, have prevented the triggering failure? Re-walk the failure with the rule in force. If the failure would have happened anyway, the rule is a superstition: not durable.
54
+ 2. **Recurrence** — Does the failure **class** recur? Cite concrete references: a prior issue, PR, revert, rejection comment, or incident showing the same class of mistake on a different occasion. **No recurrence evidence ⇒ never `durable-learning`.** A single occurrence, however painful, is `one-off` — the class may prove itself later, and the candidate can return with evidence.
55
+
56
+ Do not accept the candidate's own `why` as evidence — it is the claim under test.
57
+
58
+ ### Step 3: Classify (4-way taxonomy)
59
+
60
+ Exactly one of:
61
+
62
+ - **`durable-learning`** — both falsification questions pass with cited evidence. The only classification that persists. Disposition `persist`.
63
+ - **`one-off`** — a genuine typo, transient fluke, or single-occurrence mistake with no recurrence evidence. Disposition `drop`.
64
+ - **`misunderstanding/spec-gap`** — the failure traces to an ambiguous or missing requirement, not an agent defect. The fix is a better spec (raise it on the work item), not a rule. Disposition `drop`.
65
+ - **`lisa-upstream`** — set in Step 1. Disposition `handoff-upstream`; never persisted locally.
66
+
67
+ When multiple classifications seem defensible, choose the one that does **not** persist — the hostile default.
68
+
69
+ ### Step 4: Confidence (durable-learning only)
70
+
71
+ - **`high`** — the causal story is unambiguous and the recurrence citations are directly on point. The persistence PR may merge through the project's normal gates unattended.
72
+ - **`low`** — durable on the evidence, but the causal chain has an inferential step or the recurrence evidence is indirect. The persistence PR must wait for a human (auto-merge off + triage label).
73
+
74
+ If you cannot justify `high` in one sentence a non-engineer would accept, it is `low`.
75
+
76
+ ## Verdict Output Schema
77
+
78
+ Return exactly one verdict object per candidate:
79
+
80
+ | Field | Type | Meaning |
81
+ |-------|------|---------|
82
+ | `classification` | `durable-learning` \| `one-off` \| `misunderstanding/spec-gap` \| `lisa-upstream` | The Step 3 outcome. |
83
+ | `cited_evidence[]` | string[] | The concrete refs you actually relied on for this call — prevention and recurrence citations for durable; attribution citations for upstream; the decisive absence/counter-evidence for drops. Never empty. This is what makes a wrong call auditable. |
84
+ | `rationale` | string | 1–3 sentences readable by product, engineering, and QA alike (a non-technical operator stands at the gate). |
85
+ | `confidence` | `high` \| `low` | Present **only** when `classification` is `durable-learning`. |
86
+ | `disposition` | `persist` \| `drop` \| `handoff-upstream` | `persist` iff durable-learning; `handoff-upstream` iff lisa-upstream; otherwise `drop`. |
87
+
88
+ Also echo back the candidate's `fingerprint` and `triggering_issue` so the caller can route without re-deriving them.
89
+
90
+ ## Output Format
91
+
92
+ ```
93
+ ## Learning Judgment
94
+
95
+ **Candidate**: [rule text]
96
+ **Fingerprint**: [fingerprint]
97
+ **Triggering issue**: [ref]
98
+
99
+ | Check | Result | Cited evidence |
100
+ |-------|--------|----------------|
101
+ | Learning-loop guard | pass/short-circuit | [refs] |
102
+ | Attribution (Lisa vs project) | project/lisa-upstream | [refs] |
103
+ | Prevention (would the rule have prevented it?) | yes/no | [refs] |
104
+ | Recurrence (does the class recur?) | yes/no | [refs] |
105
+
106
+ **Classification**: durable-learning | one-off | misunderstanding/spec-gap | lisa-upstream
107
+ **Confidence**: high | low (durable-learning only)
108
+ **Disposition**: persist | drop | handoff-upstream
109
+ **Rationale**: [1–3 plain-language sentences]
110
+ ```
111
+
112
+ ## Important Reminders
113
+
114
+ 1. **Most candidates DROP** — a session where you persist everything you saw is a failed gate, not a productive one.
115
+ 2. **No recurrence citation, no durable-learning** — this is absolute; there are no exceptions for "obviously true" rules.
116
+ 3. **Cite what you relied on** — a verdict without `cited_evidence` is invalid; re-run the evaluation.
117
+ 4. **`lisa-upstream` never becomes a local rule** — classify and hand off; filing the upstream ticket is the caller's flow, not yours.
118
+ 5. **You classify; you do not write** — never touch the learnings surface, post comments, or open PRs. The caller (`lisa-persist-learning`) owns all side effects.
119
+ 6. **When in doubt, drop** — the surface is a shared, budgeted resource read by every future session.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: "Route a candidate learning through the hostile-default learning-judge gate and act on the verdict: leave a dropped-with-reason note on the triggering issue (drop), emit an upstream handoff marker (lisa-upstream), or persist a durable learning via a confidence-routed PR that touches only the learnings surface — auto-merge on for high confidence, auto-merge off plus the learning:needs-triage label for low confidence. Idempotent via marker dedupe."
3
+ argument-hint: "<candidate-json-or-fields>"
4
+ ---
5
+
6
+ Use the /lisa-persist-learning skill to fingerprint, judge, and route the candidate learning. $ARGUMENTS
@@ -0,0 +1,108 @@
1
+ ---
2
+ description: "Rejection Detection at Claim Time"
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Rejection Detection at Claim Time
7
+
8
+ When QA rejects an item, it moves the item **backward** — from a `review`/`done`-ward lane back to the build-ready lane — usually with a comment naming the defect. Today the re-claiming agent treats that bounced item as fresh work and repeats the rejected approach. This rule turns the backward move into a detectable, teachable signal.
9
+
10
+ It is a **single vendor-neutral contract** consumed by all three build-intake skills (`lisa-jira-build-intake`, `lisa-github-build-intake`, `lisa-linear-build-intake`). Each vendor arm cites this slug at its claim step rather than re-implementing detection, exactly as the vendor arms cite `leaf-only-lifecycle` and `repo-scope-split`. That is what keeps a rejection detected on JIRA from being missed on Linear.
11
+
12
+ ## Seam — where detection runs
13
+
14
+ The three build-intake skills share a uniform claim phase: `3a.0` repo-scope gate → `3a` leaf-only claim gate → `3b` Claim → `3c` run lifecycle (culminating in `lisa-implement`) → `3d` transition to done.
15
+
16
+ Detection runs at the **top of `3b`, BEFORE the relabel** `$READY → $CLAIMED`. The relabel is what makes the claim idempotent, but it also overwrites the current lane — after it, "the item is currently in `$READY`" is no longer observable, and part of the rejection signal (reached-a-later-lane AND now-back-in-ready) depends on reading the current lane against history. So detection reads history first, classifies, and only then does `3b` perform the relabel.
17
+
18
+ **`lisa-implement` is NOT the seam** — it never sees the claim. Detection belongs to the build-intake claim phase.
19
+
20
+ ## Classification
21
+
22
+ Detection is a pure read of the item's transition history (via the vendor access layers). It returns exactly one classification:
23
+
24
+ | Classification | Condition |
25
+ |---|---|
26
+ | `rejection-reclaim` | History shows the item reached a `review`/`done`-ward lane, then returned to the ready lane; it is now in the ready lane being claimed. |
27
+ | `forward-only` | History shows only forward lane moves — the item never returned to the ready lane from a later lane. |
28
+ | `never-left-ready` | History shows the item never left the ready lane. |
29
+ | `unknown` | The vendor history query failed, was inconclusive, or returned nothing usable. |
30
+
31
+ Running detection twice on the same item yields the same classification and produces no side effects (idempotent, headless-safe — safe under intake crons with no interactive prompts).
32
+
33
+ ## Vendor history bindings
34
+
35
+ History is always obtained through the vendor access layer — never a direct vendor API call from a build-intake skill (`integration-access-layer`).
36
+
37
+ - **GitHub** — read the issue via `lisa-github-read-issue`, whose Label-Event History surface returns chronological `LabeledEvent` / `UnlabeledEvent` entries. A backward move is: the configured **ready** label was removed (item advanced) and later re-added (item bounced back). Non-status label churn is ignored for classification.
38
+ - **JIRA** — call `lisa-atlassian-access operation: changelog key:<K>`. A backward move is a status changelog entry whose `to` is the configured ready status, following an earlier entry that reached a `review`/`done`-ward status.
39
+ - **Linear** — call `lisa-linear-access operation: history id:<ID>`, keyed on `status:*` **label** history (Linear build lanes are label-driven — `lisa-linear-build-intake` keys the queue on `status:*` labels). Resolve `addedLabelIds` / `removedLabelIds` against `list-issue-labels`; a backward move is the configured ready label re-added after a later-lane label was applied. Linear workflow-state moves (`fromState`/`toState`) are a secondary corroborating signal where the project maps lanes to states.
40
+
41
+ ### Lane names are configuration, never literals
42
+
43
+ The ready / claimed / done lane names ALWAYS come from `.lisa.config.json` lanes:
44
+
45
+ - `github.labels.build.{ready,claimed,done}` (GitHub),
46
+ - the JIRA status equivalents, and
47
+ - the Linear label equivalents,
48
+
49
+ resolved per the `config-resolution` rule with the `src/sync/registry.ts` `BUILD_LABEL_DEFAULTS` (`ready: status:ready`, `claimed: status:in-progress`, `done: {dev: status:on-dev, staging: status:on-stg, production: status:done}`) as the fallback. **Never hardcode** a lane string in the detection logic — a project that renames its ready lane must still detect rejections.
50
+
51
+ ## Never block the build
52
+
53
+ `unknown` is a **first-class result**, not an error. If the history query fails (network, revoked credentials, missing substrate), is inconclusive, or the vendor returns nothing usable, detection returns `unknown` and the build **proceeds** to implement the item normally. Reflection is a bonus signal layered on top of the claim; it never gates it. A history failure, an unreadable history, or an absent history all degrade gracefully to "implement the item anyway".
54
+
55
+ ## Learning-loop exclusion (no learning about learning)
56
+
57
+ This flow persists learnings via PRs and files upstream issues, so its own artifacts are themselves rejectable. If a learning PR or an auto-filed upstream issue is ever moved backward, a naive detector would fire `rejection-reclaim` on it and reflect on the flow's own output — a learning about learning, recursively.
58
+
59
+ The trigger is therefore **suppressed at the source**. Before an item is classified `rejection-reclaim`, exclude it if it carries any of the learning producer markers — `[lisa-learning-drop]`, `[lisa-learning-pr]`, `[lisa-learning-upstream-handoff]` (embedded in the artifact body/PR the same way other Lisa markers are) — or the `learning:needs-triage` label. An excluded item is treated as `forward-only` (normal work); the rejection-reflection path never fires on it, no matter how the artifact moved. (These markers ship with the learning-persistence flow; reference them by name — do not assume their files are present in this branch.)
60
+
61
+ This is the **trigger-side** half of the no-learning-loops guard. The claim-time archaeology path carries the symmetric **scan-side** exclusion ("a learning PR is never treated as an ancestor").
62
+
63
+ ## Reflection at re-claim (`rejection-reclaim` only)
64
+
65
+ When detection returns `rejection-reclaim`, the build-intake claim phase reflects on the rejection **before re-implementing** — the QA comment describing the defect, the review threads, and any linked failure output are the teaching material. This runs in the `3b`/`3c` claim seam, not in `lisa-implement` (which never sees the claim). On any other classification (`forward-only`, `never-left-ready`, `unknown`) there is nothing to reflect on — proceed to the normal claim.
66
+
67
+ 1. **Read the rejection evidence — through the access layers only** (`integration-access-layer`): the issue/ticket comments posted **after** the backward transition (the QA rejection comment), the review threads on the rejected PR, and any linked failure output. Read via `lisa-github-read-issue` / `lisa-atlassian-access` / `lisa-linear-access` as the vendor dictates — never a direct vendor API call.
68
+ 2. **Assemble ONE candidate learning** from that evidence:
69
+
70
+ | Field | Content |
71
+ |---|---|
72
+ | `rule` | The candidate rule/lesson the rejection teaches. |
73
+ | `why` | Why it matters — the defect the rejection named. |
74
+ | `provenance` | The rejection linked as provenance: the issue, the backward transition, the QA comment link, the rejected PR. |
75
+ | `evidence_links` | The comment / review-thread / failure-output URLs. |
76
+ | `scope_hint` | Where the learning applies (repo / stack / global). |
77
+ | `triggering_issue` | The re-claimed item's ref. |
78
+ | `fingerprint` | `sll4-` + `sha1(<normalized-rule> + "\n" + <triggering_issue>)` truncated to 12 chars (the learning-persistence flow's formula). |
79
+
80
+ 3. **Route the candidate into the judgment gate** via the `lisa-persist-learning` skill (the learning-persistence flow — cite it by name; it ships with that flow, so do not assume its file is present in this branch). Pass the candidate with the rejection as provenance.
81
+
82
+ ### Graceful degrade — `lisa-persist-learning` unavailable
83
+
84
+ If `lisa-persist-learning` is not installed when reflection runs (e.g. the learning-persistence flow has not merged yet), do **not** fail. Record the candidate as a marked comment on the item and proceed. The comment MUST carry a **visible prose line** as well as the marker — a bare HTML marker renders as an empty comment bubble on GitHub/Linear, defeating the "visible paper trail" this reflection exists to create:
85
+
86
+ ```text
87
+ Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.
88
+ <!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->
89
+ ```
90
+
91
+ so a human sees the paper trail, a later run (once the skill exists) can pick it up, and the build still proceeds to implement the item. The marker line is verbatim — the dedupe contract keys on it, not on the prose.
92
+
93
+ ### Idempotency — marker dedupe
94
+
95
+ The candidate marker key is `<issue>-<backward-transition-timestamp>` (the ISO timestamp of the backward transition that produced the rejection). Reuse the marker-dedupe discipline from `lisa-github-write-prd` Phase 2 — search for an existing candidate carrying this exact key before producing one; **match on the marker, never the title**. Re-claiming the same rejected item twice must **not** produce a duplicate candidate: the second run finds the existing marker and short-circuits. The backward-transition timestamp (not "now") makes the key stable across re-claims of the same rejection.
96
+
97
+ ### Never block the build
98
+
99
+ Unreadable or absent rejection evidence (no comment, deleted PR) ends with **no candidate produced and the item still implemented** — degraded, not stopped. Reflection is layered on top of the claim; it never gates it.
100
+
101
+ ## Evidence handoff into implementation (`rejection-reclaim` only)
102
+
103
+ Mining a learning is not enough — if the agent then rebuilds the same rejected thing, the item is re-bounced. So the same rejection evidence read in reflection is **also** handed into the implementation, so the re-implementation consumes it instead of repeating the rejected approach.
104
+
105
+ - **The handoff is at claim time.** `lisa-implement` never sees the claim and cannot fetch this itself; the build-intake `3c` lifecycle dispatch passes the rejection evidence summary into `lisa-implement` as part of the context bundle. Reuse the evidence already read in the reflection step — do not fetch it twice.
106
+ - **The evidence summary** names: what was rejected, why (the defect the QA comment named), and the specific approach the rejection named as wrong.
107
+ - **The plan must reckon with it.** On a `rejection-reclaim`, the re-implementation plan MUST explicitly address the rejection evidence and MUST NOT re-propose the specific approach the rejection named as wrong.
108
+ - **Absence never blocks.** If the rejection evidence is unreadable or absent, the agent still implements the item — degraded, not stopped.
@@ -0,0 +1,45 @@
1
+ ---
2
+ description: "Rejection Detection at Claim Time (load-bearing)"
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Rejection Detection at Claim Time (load-bearing)
7
+
8
+ A QA rejection — an item that reached a `review`/`done`-ward state and is now back in the build-ready lane — is a **teaching signal**, not fresh work. Detect it at claim time so the re-claim can reflect on the rejection instead of repeating it.
9
+
10
+ **One vendor-neutral contract, cited by every build-intake arm** (the `leaf-only-lifecycle` / `repo-scope-split` precedent: one shared slug, never three divergent implementations).
11
+
12
+ ## When it runs
13
+
14
+ At the **top of build-intake step 3b (Claim), BEFORE the relabel** `$READY → $CLAIMED`. After the relabel the current-lane signal is gone, so detection must read history first. Detection is a pure read — idempotent, headless-safe, no side effects.
15
+
16
+ ## Classify the claimed item
17
+
18
+ Return exactly one of:
19
+
20
+ - **`rejection-reclaim`** — history shows the item reached a `review`/`done`-ward lane and is now back in `$READY`.
21
+ - **`forward-only`** — history shows only forward moves (never returned to `$READY` from a later lane).
22
+ - **`never-left-ready`** — history shows the item never left `$READY`.
23
+ - **`unknown`** — the vendor history query failed, was inconclusive, or is absent.
24
+
25
+ ## Vendor history sources (through the access layers only — `integration-access-layer`)
26
+
27
+ - **GitHub** — `LABELED` / `UNLABELED` timeline events on the configured **ready** label (the Label-Event History surface from `lisa-github-read-issue`).
28
+ - **JIRA** — the `changelog` operation on `lisa-atlassian-access` (`?expand=changelog`, status items).
29
+ - **Linear** — the `history` operation on `lisa-linear-access`, keyed on `status:*` label history (`addedLabelIds`/`removedLabelIds` resolved against `list-issue-labels`).
30
+
31
+ **Lane names ALWAYS come from `.lisa.config.json` lanes** (`github.labels.build.{ready,claimed,done}` and the JIRA/Linear equivalents; `src/sync/registry.ts` `BUILD_LABEL_DEFAULTS`). **Never hardcode** `status:ready`, `Ready`, etc.
32
+
33
+ ## Never block the build
34
+
35
+ `unknown` is a first-class result, not an error. A failing/absent history yields `unknown` and **the build proceeds** to implement the item. Detection never stops a claim.
36
+
37
+ ## Learning-loop exclusion (no learning about learning)
38
+
39
+ An artifact this flow produced is **never** a rejection-reflection trigger, no matter how it moves. Before classifying as `rejection-reclaim`, exclude items carrying any learning marker — `[lisa-learning-drop]`, `[lisa-learning-pr]`, `[lisa-learning-upstream-handoff]` — or the `learning:needs-triage` label. Such items short-circuit to `forward-only` (treated as normal work) so the detector never fires on the flow's own learning PRs/issues.
40
+
41
+ ## Reflect on a `rejection-reclaim`
42
+
43
+ On `rejection-reclaim` only, before re-implementing: read the rejection evidence (comments after the backward transition, review threads on the rejected PR) through the access layers, assemble **one** candidate learning with the rejection linked as provenance, and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate as a comment that carries a **visible prose line** plus the marker (a bare marker renders as an empty comment bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` followed by `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. **Marker-dedupe** on `<issue>-<backward-transition-timestamp>`: re-claiming twice produces no duplicate. Unreadable/absent evidence → proceed without a candidate, never block.
44
+
45
+ Full contract (classification table, per-vendor bindings, reflection & evidence handoff): [reference/rejection-detection.md](rejection-detection-reference.mdc).
@@ -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.