@codyswann/lisa 2.240.0 → 2.241.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sync/registry.d.ts.map +1 -1
- package/dist/sync/registry.js +7 -0
- package/dist/sync/registry.js.map +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +2 -0
- package/plugins/lisa/rules/eager/claim-archaeology.md +37 -0
- package/plugins/lisa/rules/reference/claim-archaeology.md +142 -0
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +2 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +2 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/rules/eager/claim-archaeology.md +37 -0
- package/plugins/lisa-copilot/rules/reference/claim-archaeology.md +142 -0
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +2 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/rules/claim-archaeology-reference.mdc +147 -0
- package/plugins/lisa-cursor/rules/claim-archaeology.mdc +42 -0
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +2 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/rules/eager/claim-archaeology.md +37 -0
- package/plugins/src/base/rules/reference/claim-archaeology.md +142 -0
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +2 -0
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +2 -0
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +2 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Claim-Time Archaeology
|
|
2
|
+
|
|
3
|
+
Lisa lifecycles are ONE-WAY — a done issue never reopens, so residual failures come back as NEW issues, and the causal link "issue B exists because issue A was done wrong" is invisible unless someone digs. Reopening terminal issues is out of scope, so claim-time archaeology is the only way to recover that link: at claim time, determine whether the item being claimed is round 2 of a past failure, and if so, what specifically went wrong the first time.
|
|
4
|
+
|
|
5
|
+
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 in its claim step rather than growing its own archaeology, exactly as the arms cite `leaf-only-lifecycle`, `repo-scope-split`, and `rejection-detection`. One slug is what keeps an ancestor found on JIRA from being missed on Linear.
|
|
6
|
+
|
|
7
|
+
## Seam and sequencing — after rejection detection, before the claim transition
|
|
8
|
+
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
Within `3b`, the pre-transition window runs two passes in a fixed order:
|
|
12
|
+
|
|
13
|
+
1. **`rejection-detection` runs first** (top of `3b`, before the relabel — it needs the current-lane signal that the relabel destroys).
|
|
14
|
+
2. **Archaeology runs second** — after the rejection classification exists, still **before the relabel/transition** `$READY → $CLAIMED`.
|
|
15
|
+
|
|
16
|
+
The ordering is load-bearing: rejection-detection's classification is an **input** to archaeology's. A `rejection-reclaim` detected in pass 1 flows straight into archaeology's classification — it is reused, **not re-derived**. Archaeology never re-reads transition history to second-guess the rejection detector; forking that signal would guarantee drift between the two passes.
|
|
17
|
+
|
|
18
|
+
**`lisa-implement` is NOT the seam** — it never sees the claim. Archaeology belongs to the build-intake claim phase, like the two gates before it.
|
|
19
|
+
|
|
20
|
+
## Ancestry signals
|
|
21
|
+
|
|
22
|
+
Three signal sources, tried in order of cheapness. Every query counts against the cost budget below.
|
|
23
|
+
|
|
24
|
+
### 1. Tracker metadata (typed relations)
|
|
25
|
+
|
|
26
|
+
The cheapest and most reliable signal: the relations the vendor read skills already parse. Read them from the context bundle the intake flow already fetched — do not re-fetch:
|
|
27
|
+
|
|
28
|
+
- The typed relation lines — `Blocks` / `Blocked by` / `Relates to` / `Duplicates` / `Cloned from` — that `lisa-github-read-issue`, `lisa-jira-read-ticket`, and `lisa-linear-read-issue` parse into the relations table of their context bundles.
|
|
29
|
+
- GitHub's native `closingIssuesReferences` (PR↔issue closure links) and timeline cross-references, surfaced by the same `lisa-github-read-issue` GraphQL read. JIRA issue links and Linear native relations (`blocks` / `blocked_by` / `relates_to` / `duplicates`) are the vendor equivalents, read through the access layers (`integration-access-layer`) — never a direct vendor API call.
|
|
30
|
+
|
|
31
|
+
A relation pointing at a **closed, done** issue whose shipped work plausibly covers this issue's surface is an ancestor candidate. An "introduced by"-shaped link (this issue references the PR or issue that shipped the defect) is the strongest form.
|
|
32
|
+
|
|
33
|
+
### 2. Text similarity (bounded, lexical)
|
|
34
|
+
|
|
35
|
+
**The honest bound, stated plainly: no embedding machinery exists in Lisa, and none is introduced here. This signal is lexical overlap over tracker search primitives — not semantic similarity — and it will miss paraphrased descriptions.** That is acceptable: it exists to catch the common case of a new issue describing a defect in something recently shipped, using roughly the words the shipping issue used.
|
|
36
|
+
|
|
37
|
+
Scope: **recently-closed** issues (closed within the recent window the budget affords, newest first) **touching the same implicated files** where file paths are named or inferable, ranked by **title/label overlap** with the issue being claimed. The primitives:
|
|
38
|
+
|
|
39
|
+
- **GitHub** — `gh search issues "<key terms>" --repo <org>/<repo> --state closed --sort updated` (and `--label` narrowing where labels overlap).
|
|
40
|
+
- **JIRA** — `lisa-atlassian-access operation: search-issues jql: "project = <P> AND statusCategory = Done AND resolved >= -30d AND text ~ \"<key terms>\" ORDER BY resolved DESC"`.
|
|
41
|
+
- **Linear** — `lisa-linear-access operation: list-issues` filtered to completed state types, matched client-side on title/label overlap.
|
|
42
|
+
|
|
43
|
+
A hit is a candidate only when the overlap is specific (shared distinctive terms, same component labels, same files named) — generic word overlap alone never promotes an ancestor.
|
|
44
|
+
|
|
45
|
+
### 3. Git ancestry (deterministic, machine-readable)
|
|
46
|
+
|
|
47
|
+
For the files the issue implicates (named in the body, or inferred from the similarity hits), answer "which PR last shipped this file" with **direct deterministic git commands**:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git log --follow --format='%H %aI %s' -n 5 -- <file> # last commits touching the file
|
|
51
|
+
git blame -L <range> --line-porcelain <file> # who last shipped the implicated lines
|
|
52
|
+
# The PR that shipped the file. --full-history is required: path-limited git log
|
|
53
|
+
# simplifies away merge commits by default, silently dropping the merge-PR answer.
|
|
54
|
+
# Two --grep patterns (OR'd) cover both merge conventions: classic merge commits
|
|
55
|
+
# ("Merge pull request #<n>") and squash/rebase merges (subject ending "(#<n>)").
|
|
56
|
+
# POSIX BRE only — GNU-only \+ silently matches nothing on BSD/macOS git.
|
|
57
|
+
git log --full-history --grep "Merge pull request #" --grep "(#[0-9][0-9]*)" --format='%H %aI %s' -n 5 -- <file>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Keep the result **parseable**: a `{file, sha, pr, date}` tuple per implicated file (PR number extracted from the subject — `Merge pull request #<n>` for merge commits, the trailing `(#<n>)` for squash/rebase merges; empty when the history matches neither convention). The PR maps back to its issue via `closingIssuesReferences` / the PR body's issue reference.
|
|
61
|
+
|
|
62
|
+
**Do NOT delegate this to the `git-history-analyzer` agent.** That agent can answer the question, but it returns a **prose report with no machine-readable contract** (nothing downstream can reliably parse it), it is explicitly forbidden from judging past decisions, and it reads the local repo only. For programmatic claim-time archaeology, run the deterministic query directly and keep the `{file, sha, pr, date}` result.
|
|
63
|
+
|
|
64
|
+
## Learning-loop exclusion (scan-side — a learning artifact is never an ancestor)
|
|
65
|
+
|
|
66
|
+
This flow produces learning PRs, candidate comments, and upstream handoffs. Those artifacts touch the same files and reference the same issues as the failures they describe — which makes them **near-perfect false-positive ancestors**. Without an explicit exclusion the flow learns from itself, recursively.
|
|
67
|
+
|
|
68
|
+
Before any candidate is promoted to ancestor, exclude every artifact carrying any of these markers or labels — such an artifact is **never an ancestor**, no matter how strong its other signals:
|
|
69
|
+
|
|
70
|
+
- `[lisa-learning-drop]`
|
|
71
|
+
- `[lisa-learning-pr]`
|
|
72
|
+
- `[lisa-learning-upstream-handoff]`
|
|
73
|
+
- `[lisa-rejection-candidate]`
|
|
74
|
+
- `[lisa-archaeology-candidate]` (this rule's own producer tag — archaeology's output must not seed the next claim's input)
|
|
75
|
+
- the `learning:needs-triage` label
|
|
76
|
+
|
|
77
|
+
This is the **scan-side** half of the no-learning-loops guard; `rejection-detection` carries the symmetric **trigger-side** half ("a learning artifact is never a rejection-reflection trigger").
|
|
78
|
+
|
|
79
|
+
## Classification
|
|
80
|
+
|
|
81
|
+
Exactly one of three states:
|
|
82
|
+
|
|
83
|
+
| Classification | Condition |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `rejection-reclaim` | The `rejection-detection` pass classified this claim `rejection-reclaim`. Taken directly from that result — reused, never re-derived here. Its reflection path (the `[lisa-rejection-candidate]` candidate) already covers the learning; archaeology adds nothing on top. |
|
|
86
|
+
| `retry-of-done-issue` | Not a rejection-reclaim, AND an ancestry signal (§ above, post-exclusion) names a closed done issue whose shipped work this issue exists to fix. |
|
|
87
|
+
| `fresh` | Everything else: no ancestor, weak/inconclusive signals, budget exhausted, or the pass errored. |
|
|
88
|
+
|
|
89
|
+
Classification itself is **stateless** — a pure function of the signals read this pass, holding no cache or stored state between claims. Re-running it on the same inputs yields the same answer; idempotency of the *side effect* (the candidate) is carried by marker dedupe below.
|
|
90
|
+
|
|
91
|
+
## Candidate derivation (`retry-of-done-issue` only)
|
|
92
|
+
|
|
93
|
+
An ancestor alone teaches nothing — "B relates to A" is trivia. The learning lives in the **delta**: the gap between what agent A actually did and what issue B proves was actually needed.
|
|
94
|
+
|
|
95
|
+
1. **Reconstruct what the ancestor shipped**, through the access layers: its merged PR (diff, description), the review threads on that PR, and the evidence comments on the ancestor issue.
|
|
96
|
+
2. **Derive ONE candidate learning citing the delta** — **what was done** versus **what this issue proves was needed**. The shape is "A shipped X; B proves Y was required; the mistake was assuming X sufficed" — never "A had a bug". A **vague summary** that does not name the specific mistake is worthless and must be rejected (produce nothing rather than noise).
|
|
97
|
+
3. **Route it to `lisa-persist-learning`** exactly like the rejection-reflection path: candidate fields (rule, why, provenance linking the ancestor issue + its PR + this issue, evidence links, scope hint, triggering issue) with fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`.
|
|
98
|
+
|
|
99
|
+
### Graceful degrade — `lisa-persist-learning` unavailable
|
|
100
|
+
|
|
101
|
+
Same fallback pattern as the rejection path, with this rule's own distinct marker. Record the candidate as a comment on the claimed item carrying a **visible prose line** plus the marker (a bare marker renders as an empty comment bubble):
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
Recorded a candidate learning from this retry's ancestry (queued for the judgment gate): <one-line candidate rule>.
|
|
105
|
+
<!-- [lisa-archaeology-candidate] key=<issue>::<ancestor> -->
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The marker line is verbatim — the dedupe contract keys on it, not on the prose.
|
|
109
|
+
|
|
110
|
+
### Idempotency — marker dedupe
|
|
111
|
+
|
|
112
|
+
The key is `<issue>::<ancestor>` (the claimed item's ref, `::`, the ancestor's ref — the `::` separator keeps the key unambiguous when vendor refs themselves contain hyphens, e.g. `PROJ-123`; it is stable across re-claims of the same pair). Before producing a candidate, search for an existing `[lisa-archaeology-candidate]` comment/artifact carrying this exact key — match on the **marker, never the title** (the `lisa-github-write-prd` Phase 2 discipline). Dedupe is per **(issue, ancestor) pair**: re-claiming an issue whose archaeology already resolved the same ancestor finds the marker and short-circuits — no duplicate candidate for that pair. A re-claim that resolves a **different** ancestor is new evidence and may legitimately produce a second candidate under its own key — that is intended, not a dedupe failure.
|
|
113
|
+
|
|
114
|
+
### `fresh` produces silence
|
|
115
|
+
|
|
116
|
+
A `fresh` classification produces **no candidate and zero comments**. Silence is the correct output — emitting a low-value candidate on every claim is precisely the rule-pollution failure mode the learning loop names as its existential risk.
|
|
117
|
+
|
|
118
|
+
## Cost budget — enforced here, configured in one place
|
|
119
|
+
|
|
120
|
+
Archaeology is speculative digging on the critical path of every claim. The budget is what makes that safe.
|
|
121
|
+
|
|
122
|
+
- **`archaeology.maxSteps`** — the maximum number of tracker/git queries one archaeology pass may spend, read from `.lisa.config.json`:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
MAX_STEPS=$(jq -r '.archaeology.maxSteps // 8' .lisa.config.json 2>/dev/null || echo 8)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The conservative default is **8** — enough for the metadata read (free, already fetched), one or two similarity searches, and git ancestry over a handful of implicated files, and small enough that a fruitless dig on a large repo ends quickly. `lisa sync` seeds the key (registry default), and **this rule pair is the single documented place** for what the budget means — do not restate its semantics in the vendor skills.
|
|
129
|
+
- **`archaeology.maxSeconds`** — optional wall-clock ceiling for the whole pass, read the same way (`jq -r '.archaeology.maxSeconds // empty'`); unset means steps alone bound the pass.
|
|
130
|
+
|
|
131
|
+
**Budget exhaustion is a NORMAL outcome, not an error.** When the pass hits either ceiling with no confident ancestor, it classifies `fresh` and the claim proceeds immediately — no retry, no escalation, no blocking warning.
|
|
132
|
+
|
|
133
|
+
## Never block the claim
|
|
134
|
+
|
|
135
|
+
The invariant everything above hangs on: **archaeology never blocks the claim**. By construction:
|
|
136
|
+
|
|
137
|
+
- Weak or inconclusive signals → degrade to `fresh`, claim proceeds.
|
|
138
|
+
- Budget exhausted → degrade to `fresh`, claim proceeds.
|
|
139
|
+
- The pass throws or errors (tracker outage, malformed history, missing config) → the exception is caught, classification degrades to `fresh`, and the **claim still proceeds** — a crash inside a speculative bonus feature must never strand a ready issue in the queue.
|
|
140
|
+
- Unreadable ancestor evidence on a genuine retry → no candidate produced, the item is still implemented — degraded, not stopped.
|
|
141
|
+
|
|
142
|
+
Headless-safe throughout: no interactive prompts, safe under intake crons.
|
|
@@ -256,6 +256,8 @@ A blocker is active if it is open and has no cleared status label. Treat `status
|
|
|
256
256
|
|
|
257
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
258
|
|
|
259
|
+
**Claim-time archaeology runs second — after rejection detection, still before the relabel below.** Classify this item per the vendor-neutral `claim-archaeology` rule, with the rejection classification above as its input. All shared semantics — ancestry signals, classification, learning-loop exclusion, cost budget, candidate derivation, marker dedupe, and the never-block degrade — live in that one slug; change them there, never here. GitHub wiring only: the typed relations and `closingIssuesReferences` are already in the read bundle; text-similarity searches use `gh search issues` over recently-closed issues; the fallback candidate comment is posted with `gh issue comment`.
|
|
260
|
+
|
|
259
261
|
```bash
|
|
260
262
|
gh issue edit <number> --repo <org>/<repo> --remove-label "$READY" --add-label "$CLAIMED"
|
|
261
263
|
# Assign to the authenticated user ONLY when the issue is currently unassigned (attributable claim;
|
|
@@ -200,6 +200,8 @@ This gate never blocks a legitimate flat Task/Bug: those have no open children a
|
|
|
200
200
|
|
|
201
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
202
|
|
|
203
|
+
**Claim-time archaeology runs second — after rejection detection, still before the transition below.** Classify this ticket per the vendor-neutral `claim-archaeology` rule, with the rejection classification above as its input. All shared semantics — ancestry signals, classification, learning-loop exclusion, cost budget, candidate derivation, marker dedupe, and the never-block degrade — live in that one slug; change them there, never here. JIRA wiring only: the typed relations are already in the read bundle; text-similarity searches run through `lisa-atlassian-access operation: search-issues jql:` over recently-closed tickets; the fallback candidate comment is posted via `lisa-atlassian-access operation: comment`.
|
|
204
|
+
|
|
203
205
|
Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa-atlassian-access` `operation: transition key: <TICKET> to: "$CLAIMED"`.
|
|
204
206
|
- **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.
|
|
205
207
|
- Post a `[claude-build-intake]` comment via `lisa-atlassian-access` `operation: comment key: <TICKET> body: "Claimed by Claude. Starting build."`
|
|
@@ -190,6 +190,8 @@ This gate never blocks a legitimate flat Task/Bug: those have no open children a
|
|
|
190
190
|
|
|
191
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
192
|
|
|
193
|
+
**Claim-time archaeology runs second — after rejection detection, still before the relabel below.** Classify this Issue per the vendor-neutral `claim-archaeology` rule, with the rejection classification above as its input. All shared semantics — ancestry signals, classification, learning-loop exclusion, cost budget, candidate derivation, marker dedupe, and the never-block degrade — live in that one slug; change them there, never here. Linear wiring only: the native relations are already in the read bundle; text-similarity searches run through `lisa-linear-access operation: list-issues` filtered to recently-closed Issues; the fallback candidate comment is posted via `lisa-linear-access operation: save-comment`.
|
|
194
|
+
|
|
193
195
|
Update labels via `lisa-linear-access operation: save-issue`: remove `$READY`, add `$CLAIMED`. Resolve label IDs via `list_issue_labels` (create `$CLAIMED` if missing).
|
|
194
196
|
|
|
195
197
|
**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.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Claim-Time Archaeology"
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claim-Time Archaeology
|
|
7
|
+
|
|
8
|
+
Lisa lifecycles are ONE-WAY — a done issue never reopens, so residual failures come back as NEW issues, and the causal link "issue B exists because issue A was done wrong" is invisible unless someone digs. Reopening terminal issues is out of scope, so claim-time archaeology is the only way to recover that link: at claim time, determine whether the item being claimed is round 2 of a past failure, and if so, what specifically went wrong the first time.
|
|
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 in its claim step rather than growing its own archaeology, exactly as the arms cite `leaf-only-lifecycle`, `repo-scope-split`, and `rejection-detection`. One slug is what keeps an ancestor found on JIRA from being missed on Linear.
|
|
11
|
+
|
|
12
|
+
## Seam and sequencing — after rejection detection, before the claim transition
|
|
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
|
+
Within `3b`, the pre-transition window runs two passes in a fixed order:
|
|
17
|
+
|
|
18
|
+
1. **`rejection-detection` runs first** (top of `3b`, before the relabel — it needs the current-lane signal that the relabel destroys).
|
|
19
|
+
2. **Archaeology runs second** — after the rejection classification exists, still **before the relabel/transition** `$READY → $CLAIMED`.
|
|
20
|
+
|
|
21
|
+
The ordering is load-bearing: rejection-detection's classification is an **input** to archaeology's. A `rejection-reclaim` detected in pass 1 flows straight into archaeology's classification — it is reused, **not re-derived**. Archaeology never re-reads transition history to second-guess the rejection detector; forking that signal would guarantee drift between the two passes.
|
|
22
|
+
|
|
23
|
+
**`lisa-implement` is NOT the seam** — it never sees the claim. Archaeology belongs to the build-intake claim phase, like the two gates before it.
|
|
24
|
+
|
|
25
|
+
## Ancestry signals
|
|
26
|
+
|
|
27
|
+
Three signal sources, tried in order of cheapness. Every query counts against the cost budget below.
|
|
28
|
+
|
|
29
|
+
### 1. Tracker metadata (typed relations)
|
|
30
|
+
|
|
31
|
+
The cheapest and most reliable signal: the relations the vendor read skills already parse. Read them from the context bundle the intake flow already fetched — do not re-fetch:
|
|
32
|
+
|
|
33
|
+
- The typed relation lines — `Blocks` / `Blocked by` / `Relates to` / `Duplicates` / `Cloned from` — that `lisa-github-read-issue`, `lisa-jira-read-ticket`, and `lisa-linear-read-issue` parse into the relations table of their context bundles.
|
|
34
|
+
- GitHub's native `closingIssuesReferences` (PR↔issue closure links) and timeline cross-references, surfaced by the same `lisa-github-read-issue` GraphQL read. JIRA issue links and Linear native relations (`blocks` / `blocked_by` / `relates_to` / `duplicates`) are the vendor equivalents, read through the access layers (`integration-access-layer`) — never a direct vendor API call.
|
|
35
|
+
|
|
36
|
+
A relation pointing at a **closed, done** issue whose shipped work plausibly covers this issue's surface is an ancestor candidate. An "introduced by"-shaped link (this issue references the PR or issue that shipped the defect) is the strongest form.
|
|
37
|
+
|
|
38
|
+
### 2. Text similarity (bounded, lexical)
|
|
39
|
+
|
|
40
|
+
**The honest bound, stated plainly: no embedding machinery exists in Lisa, and none is introduced here. This signal is lexical overlap over tracker search primitives — not semantic similarity — and it will miss paraphrased descriptions.** That is acceptable: it exists to catch the common case of a new issue describing a defect in something recently shipped, using roughly the words the shipping issue used.
|
|
41
|
+
|
|
42
|
+
Scope: **recently-closed** issues (closed within the recent window the budget affords, newest first) **touching the same implicated files** where file paths are named or inferable, ranked by **title/label overlap** with the issue being claimed. The primitives:
|
|
43
|
+
|
|
44
|
+
- **GitHub** — `gh search issues "<key terms>" --repo <org>/<repo> --state closed --sort updated` (and `--label` narrowing where labels overlap).
|
|
45
|
+
- **JIRA** — `lisa-atlassian-access operation: search-issues jql: "project = <P> AND statusCategory = Done AND resolved >= -30d AND text ~ \"<key terms>\" ORDER BY resolved DESC"`.
|
|
46
|
+
- **Linear** — `lisa-linear-access operation: list-issues` filtered to completed state types, matched client-side on title/label overlap.
|
|
47
|
+
|
|
48
|
+
A hit is a candidate only when the overlap is specific (shared distinctive terms, same component labels, same files named) — generic word overlap alone never promotes an ancestor.
|
|
49
|
+
|
|
50
|
+
### 3. Git ancestry (deterministic, machine-readable)
|
|
51
|
+
|
|
52
|
+
For the files the issue implicates (named in the body, or inferred from the similarity hits), answer "which PR last shipped this file" with **direct deterministic git commands**:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
git log --follow --format='%H %aI %s' -n 5 -- <file> # last commits touching the file
|
|
56
|
+
git blame -L <range> --line-porcelain <file> # who last shipped the implicated lines
|
|
57
|
+
# The PR that shipped the file. --full-history is required: path-limited git log
|
|
58
|
+
# simplifies away merge commits by default, silently dropping the merge-PR answer.
|
|
59
|
+
# Two --grep patterns (OR'd) cover both merge conventions: classic merge commits
|
|
60
|
+
# ("Merge pull request #<n>") and squash/rebase merges (subject ending "(#<n>)").
|
|
61
|
+
# POSIX BRE only — GNU-only \+ silently matches nothing on BSD/macOS git.
|
|
62
|
+
git log --full-history --grep "Merge pull request #" --grep "(#[0-9][0-9]*)" --format='%H %aI %s' -n 5 -- <file>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Keep the result **parseable**: a `{file, sha, pr, date}` tuple per implicated file (PR number extracted from the subject — `Merge pull request #<n>` for merge commits, the trailing `(#<n>)` for squash/rebase merges; empty when the history matches neither convention). The PR maps back to its issue via `closingIssuesReferences` / the PR body's issue reference.
|
|
66
|
+
|
|
67
|
+
**Do NOT delegate this to the `git-history-analyzer` agent.** That agent can answer the question, but it returns a **prose report with no machine-readable contract** (nothing downstream can reliably parse it), it is explicitly forbidden from judging past decisions, and it reads the local repo only. For programmatic claim-time archaeology, run the deterministic query directly and keep the `{file, sha, pr, date}` result.
|
|
68
|
+
|
|
69
|
+
## Learning-loop exclusion (scan-side — a learning artifact is never an ancestor)
|
|
70
|
+
|
|
71
|
+
This flow produces learning PRs, candidate comments, and upstream handoffs. Those artifacts touch the same files and reference the same issues as the failures they describe — which makes them **near-perfect false-positive ancestors**. Without an explicit exclusion the flow learns from itself, recursively.
|
|
72
|
+
|
|
73
|
+
Before any candidate is promoted to ancestor, exclude every artifact carrying any of these markers or labels — such an artifact is **never an ancestor**, no matter how strong its other signals:
|
|
74
|
+
|
|
75
|
+
- `[lisa-learning-drop]`
|
|
76
|
+
- `[lisa-learning-pr]`
|
|
77
|
+
- `[lisa-learning-upstream-handoff]`
|
|
78
|
+
- `[lisa-rejection-candidate]`
|
|
79
|
+
- `[lisa-archaeology-candidate]` (this rule's own producer tag — archaeology's output must not seed the next claim's input)
|
|
80
|
+
- the `learning:needs-triage` label
|
|
81
|
+
|
|
82
|
+
This is the **scan-side** half of the no-learning-loops guard; `rejection-detection` carries the symmetric **trigger-side** half ("a learning artifact is never a rejection-reflection trigger").
|
|
83
|
+
|
|
84
|
+
## Classification
|
|
85
|
+
|
|
86
|
+
Exactly one of three states:
|
|
87
|
+
|
|
88
|
+
| Classification | Condition |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `rejection-reclaim` | The `rejection-detection` pass classified this claim `rejection-reclaim`. Taken directly from that result — reused, never re-derived here. Its reflection path (the `[lisa-rejection-candidate]` candidate) already covers the learning; archaeology adds nothing on top. |
|
|
91
|
+
| `retry-of-done-issue` | Not a rejection-reclaim, AND an ancestry signal (§ above, post-exclusion) names a closed done issue whose shipped work this issue exists to fix. |
|
|
92
|
+
| `fresh` | Everything else: no ancestor, weak/inconclusive signals, budget exhausted, or the pass errored. |
|
|
93
|
+
|
|
94
|
+
Classification itself is **stateless** — a pure function of the signals read this pass, holding no cache or stored state between claims. Re-running it on the same inputs yields the same answer; idempotency of the *side effect* (the candidate) is carried by marker dedupe below.
|
|
95
|
+
|
|
96
|
+
## Candidate derivation (`retry-of-done-issue` only)
|
|
97
|
+
|
|
98
|
+
An ancestor alone teaches nothing — "B relates to A" is trivia. The learning lives in the **delta**: the gap between what agent A actually did and what issue B proves was actually needed.
|
|
99
|
+
|
|
100
|
+
1. **Reconstruct what the ancestor shipped**, through the access layers: its merged PR (diff, description), the review threads on that PR, and the evidence comments on the ancestor issue.
|
|
101
|
+
2. **Derive ONE candidate learning citing the delta** — **what was done** versus **what this issue proves was needed**. The shape is "A shipped X; B proves Y was required; the mistake was assuming X sufficed" — never "A had a bug". A **vague summary** that does not name the specific mistake is worthless and must be rejected (produce nothing rather than noise).
|
|
102
|
+
3. **Route it to `lisa-persist-learning`** exactly like the rejection-reflection path: candidate fields (rule, why, provenance linking the ancestor issue + its PR + this issue, evidence links, scope hint, triggering issue) with fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`.
|
|
103
|
+
|
|
104
|
+
### Graceful degrade — `lisa-persist-learning` unavailable
|
|
105
|
+
|
|
106
|
+
Same fallback pattern as the rejection path, with this rule's own distinct marker. Record the candidate as a comment on the claimed item carrying a **visible prose line** plus the marker (a bare marker renders as an empty comment bubble):
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
Recorded a candidate learning from this retry's ancestry (queued for the judgment gate): <one-line candidate rule>.
|
|
110
|
+
<!-- [lisa-archaeology-candidate] key=<issue>::<ancestor> -->
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The marker line is verbatim — the dedupe contract keys on it, not on the prose.
|
|
114
|
+
|
|
115
|
+
### Idempotency — marker dedupe
|
|
116
|
+
|
|
117
|
+
The key is `<issue>::<ancestor>` (the claimed item's ref, `::`, the ancestor's ref — the `::` separator keeps the key unambiguous when vendor refs themselves contain hyphens, e.g. `PROJ-123`; it is stable across re-claims of the same pair). Before producing a candidate, search for an existing `[lisa-archaeology-candidate]` comment/artifact carrying this exact key — match on the **marker, never the title** (the `lisa-github-write-prd` Phase 2 discipline). Dedupe is per **(issue, ancestor) pair**: re-claiming an issue whose archaeology already resolved the same ancestor finds the marker and short-circuits — no duplicate candidate for that pair. A re-claim that resolves a **different** ancestor is new evidence and may legitimately produce a second candidate under its own key — that is intended, not a dedupe failure.
|
|
118
|
+
|
|
119
|
+
### `fresh` produces silence
|
|
120
|
+
|
|
121
|
+
A `fresh` classification produces **no candidate and zero comments**. Silence is the correct output — emitting a low-value candidate on every claim is precisely the rule-pollution failure mode the learning loop names as its existential risk.
|
|
122
|
+
|
|
123
|
+
## Cost budget — enforced here, configured in one place
|
|
124
|
+
|
|
125
|
+
Archaeology is speculative digging on the critical path of every claim. The budget is what makes that safe.
|
|
126
|
+
|
|
127
|
+
- **`archaeology.maxSteps`** — the maximum number of tracker/git queries one archaeology pass may spend, read from `.lisa.config.json`:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
MAX_STEPS=$(jq -r '.archaeology.maxSteps // 8' .lisa.config.json 2>/dev/null || echo 8)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The conservative default is **8** — enough for the metadata read (free, already fetched), one or two similarity searches, and git ancestry over a handful of implicated files, and small enough that a fruitless dig on a large repo ends quickly. `lisa sync` seeds the key (registry default), and **this rule pair is the single documented place** for what the budget means — do not restate its semantics in the vendor skills.
|
|
134
|
+
- **`archaeology.maxSeconds`** — optional wall-clock ceiling for the whole pass, read the same way (`jq -r '.archaeology.maxSeconds // empty'`); unset means steps alone bound the pass.
|
|
135
|
+
|
|
136
|
+
**Budget exhaustion is a NORMAL outcome, not an error.** When the pass hits either ceiling with no confident ancestor, it classifies `fresh` and the claim proceeds immediately — no retry, no escalation, no blocking warning.
|
|
137
|
+
|
|
138
|
+
## Never block the claim
|
|
139
|
+
|
|
140
|
+
The invariant everything above hangs on: **archaeology never blocks the claim**. By construction:
|
|
141
|
+
|
|
142
|
+
- Weak or inconclusive signals → degrade to `fresh`, claim proceeds.
|
|
143
|
+
- Budget exhausted → degrade to `fresh`, claim proceeds.
|
|
144
|
+
- The pass throws or errors (tracker outage, malformed history, missing config) → the exception is caught, classification degrades to `fresh`, and the **claim still proceeds** — a crash inside a speculative bonus feature must never strand a ready issue in the queue.
|
|
145
|
+
- Unreadable ancestor evidence on a genuine retry → no candidate produced, the item is still implemented — degraded, not stopped.
|
|
146
|
+
|
|
147
|
+
Headless-safe throughout: no interactive prompts, safe under intake crons.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Claim-Time Archaeology (load-bearing)"
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claim-Time Archaeology (load-bearing)
|
|
7
|
+
|
|
8
|
+
Lisa lifecycles are one-way — a done issue never reopens, so a residual failure comes back as a **new** issue with no visible link to the issue that shipped it. Archaeology recovers that link at claim time: the claiming agent learns it is working on round 2 of a past failure, and what specifically went wrong the first time.
|
|
9
|
+
|
|
10
|
+
**One vendor-neutral contract, cited by every build-intake arm** (the `leaf-only-lifecycle` / `repo-scope-split` / `rejection-detection` precedent: one shared slug, never three divergent implementations).
|
|
11
|
+
|
|
12
|
+
## When it runs
|
|
13
|
+
|
|
14
|
+
In build-intake step 3b, **AFTER the rejection-detection classification and BEFORE the relabel/transition** `$READY → $CLAIMED`. Rejection detection runs first; its classification is an **input** to archaeology — a detected `rejection-reclaim` passes straight through, never re-derived.
|
|
15
|
+
|
|
16
|
+
## Classify the claimed item
|
|
17
|
+
|
|
18
|
+
Return exactly one of:
|
|
19
|
+
|
|
20
|
+
- **`rejection-reclaim`** — taken directly from the `rejection-detection` result. Reuse it; do not re-derive.
|
|
21
|
+
- **`retry-of-done-issue`** — an ancestry signal names a closed done issue whose shipped work this issue exists to fix.
|
|
22
|
+
- **`fresh`** — no ancestor found, signals weak/inconclusive, budget exhausted, or the pass errored. The default and the safe degrade.
|
|
23
|
+
|
|
24
|
+
## Ancestry signals (summary — full bindings in the reference body)
|
|
25
|
+
|
|
26
|
+
1. **Tracker metadata** — the typed relations the read skills already parse (Blocks / Blocked by / Relates to / Duplicates / Cloned from, `closingIssuesReferences`, cross-references).
|
|
27
|
+
2. **Text similarity** — tracker search primitives over recently-closed issues touching the same implicated files, ranked by title/label overlap. Lexical only; no embedding machinery exists.
|
|
28
|
+
3. **Git ancestry** — deterministic `git log --follow` / `git blame` / merge-commit queries yielding a parseable `{file, sha, pr, date}` result. Never delegate this to the prose-report `git-history-analyzer` agent.
|
|
29
|
+
|
|
30
|
+
## Learning-loop exclusion (scan-side)
|
|
31
|
+
|
|
32
|
+
An artifact this flow produced is **never** an ancestor. Exclude anything carrying `[lisa-learning-drop]`, `[lisa-learning-pr]`, `[lisa-learning-upstream-handoff]`, `[lisa-rejection-candidate]`, or `[lisa-archaeology-candidate]` markers, or the `learning:needs-triage` label.
|
|
33
|
+
|
|
34
|
+
## Cost budget — never block the claim
|
|
35
|
+
|
|
36
|
+
The pass runs inside a hard budget: `.lisa.config.json` `archaeology.maxSteps` (default **8** tracker/git queries; optional `archaeology.maxSeconds`). Budget exhausted, weak signals, or an exception → classify `fresh` and proceed. Archaeology is a bonus layered on the claim; it **never blocks the claim**. Exceeding the budget degrades to `fresh` — a normal outcome, not an error.
|
|
37
|
+
|
|
38
|
+
## On `retry-of-done-issue`
|
|
39
|
+
|
|
40
|
+
Reconstruct what the ancestor's PR shipped (merged PR, review threads, evidence comments) and derive **ONE** candidate learning citing the **delta** between what was done and what this issue proves was needed — routed to `lisa-persist-learning` exactly like the rejection-reflection path. Fallback when that skill is absent: a comment with a visible prose line plus `<!-- [lisa-archaeology-candidate] key=<issue>::<ancestor> -->` (marker-dedupe; re-claims produce no duplicate). `fresh` → no candidate, zero comments.
|
|
41
|
+
|
|
42
|
+
Full contract (signal bindings, classification table, candidate derivation, budget mechanics): [reference/claim-archaeology.md](claim-archaeology-reference.mdc).
|
|
@@ -256,6 +256,8 @@ A blocker is active if it is open and has no cleared status label. Treat `status
|
|
|
256
256
|
|
|
257
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
258
|
|
|
259
|
+
**Claim-time archaeology runs second — after rejection detection, still before the relabel below.** Classify this item per the vendor-neutral `claim-archaeology` rule, with the rejection classification above as its input. All shared semantics — ancestry signals, classification, learning-loop exclusion, cost budget, candidate derivation, marker dedupe, and the never-block degrade — live in that one slug; change them there, never here. GitHub wiring only: the typed relations and `closingIssuesReferences` are already in the read bundle; text-similarity searches use `gh search issues` over recently-closed issues; the fallback candidate comment is posted with `gh issue comment`.
|
|
260
|
+
|
|
259
261
|
```bash
|
|
260
262
|
gh issue edit <number> --repo <org>/<repo> --remove-label "$READY" --add-label "$CLAIMED"
|
|
261
263
|
# Assign to the authenticated user ONLY when the issue is currently unassigned (attributable claim;
|
|
@@ -200,6 +200,8 @@ This gate never blocks a legitimate flat Task/Bug: those have no open children a
|
|
|
200
200
|
|
|
201
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
202
|
|
|
203
|
+
**Claim-time archaeology runs second — after rejection detection, still before the transition below.** Classify this ticket per the vendor-neutral `claim-archaeology` rule, with the rejection classification above as its input. All shared semantics — ancestry signals, classification, learning-loop exclusion, cost budget, candidate derivation, marker dedupe, and the never-block degrade — live in that one slug; change them there, never here. JIRA wiring only: the typed relations are already in the read bundle; text-similarity searches run through `lisa-atlassian-access operation: search-issues jql:` over recently-closed tickets; the fallback candidate comment is posted via `lisa-atlassian-access operation: comment`.
|
|
204
|
+
|
|
203
205
|
Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa-atlassian-access` `operation: transition key: <TICKET> to: "$CLAIMED"`.
|
|
204
206
|
- **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.
|
|
205
207
|
- Post a `[claude-build-intake]` comment via `lisa-atlassian-access` `operation: comment key: <TICKET> body: "Claimed by Claude. Starting build."`
|
|
@@ -190,6 +190,8 @@ This gate never blocks a legitimate flat Task/Bug: those have no open children a
|
|
|
190
190
|
|
|
191
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
192
|
|
|
193
|
+
**Claim-time archaeology runs second — after rejection detection, still before the relabel below.** Classify this Issue per the vendor-neutral `claim-archaeology` rule, with the rejection classification above as its input. All shared semantics — ancestry signals, classification, learning-loop exclusion, cost budget, candidate derivation, marker dedupe, and the never-block degrade — live in that one slug; change them there, never here. Linear wiring only: the native relations are already in the read bundle; text-similarity searches run through `lisa-linear-access operation: list-issues` filtered to recently-closed Issues; the fallback candidate comment is posted via `lisa-linear-access operation: save-comment`.
|
|
194
|
+
|
|
193
195
|
Update labels via `lisa-linear-access operation: save-issue`: remove `$READY`, add `$CLAIMED`. Resolve label IDs via `list_issue_labels` (create `$CLAIMED` if missing).
|
|
194
196
|
|
|
195
197
|
**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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.241.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.241.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.241.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.241.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|