@codyswann/lisa 2.239.1 → 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/core/learnings-entry.d.ts +9 -0
- package/dist/core/learnings-entry.d.ts.map +1 -1
- package/dist/core/learnings-entry.js +4 -2
- package/dist/core/learnings-entry.js.map +1 -1
- package/dist/core/learnings-writer.d.ts +33 -0
- package/dist/core/learnings-writer.d.ts.map +1 -1
- package/dist/core/learnings-writer.js +75 -1
- package/dist/core/learnings-writer.js.map +1 -1
- 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-attribute-failure/SKILL.md +142 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-attribute-failure/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-doctor/SKILL.md +28 -77
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +18 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +18 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +18 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/SKILL.md +61 -8
- package/plugins/lisa/commands/attribute-failure.md +6 -0
- package/plugins/lisa/commands/persist-learning.md +1 -1
- package/plugins/lisa/rules/eager/claim-archaeology.md +37 -0
- package/plugins/lisa/rules/reference/claim-archaeology.md +142 -0
- package/plugins/lisa/rules/reference/project-learnings.md +18 -0
- package/plugins/lisa/skills/lisa-attribute-failure/SKILL.md +142 -0
- package/plugins/lisa/skills/lisa-attribute-failure/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-doctor/SKILL.md +28 -77
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +18 -0
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +18 -0
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +18 -0
- package/plugins/lisa/skills/lisa-persist-learning/SKILL.md +62 -9
- package/plugins/lisa-agy/commands/lisa/attribute-failure.md +6 -0
- package/plugins/lisa-agy/commands/lisa/persist-learning.md +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-attribute-failure/SKILL.md +142 -0
- package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +28 -77
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +18 -0
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +18 -0
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +18 -0
- package/plugins/lisa-agy/skills/lisa-persist-learning/SKILL.md +62 -9
- 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/commands/lisa/attribute-failure.md +6 -0
- package/plugins/lisa-copilot/commands/lisa/persist-learning.md +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/rules/reference/project-learnings.md +18 -0
- package/plugins/lisa-copilot/skills/lisa-attribute-failure/SKILL.md +142 -0
- package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +28 -77
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +18 -0
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +18 -0
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +18 -0
- package/plugins/lisa-copilot/skills/lisa-persist-learning/SKILL.md +62 -9
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/attribute-failure.md +6 -0
- package/plugins/lisa-cursor/commands/lisa/persist-learning.md +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/rules/project-learnings-reference.mdc +18 -0
- package/plugins/lisa-cursor/skills/lisa-attribute-failure/SKILL.md +142 -0
- package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +28 -77
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +18 -0
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +18 -0
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +18 -0
- package/plugins/lisa-cursor/skills/lisa-persist-learning/SKILL.md +62 -9
- 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/commands/attribute-failure.md +6 -0
- package/plugins/src/base/commands/persist-learning.md +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/rules/reference/project-learnings.md +18 -0
- package/plugins/src/base/skills/lisa-attribute-failure/SKILL.md +142 -0
- package/plugins/src/base/skills/lisa-doctor/SKILL.md +28 -77
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +18 -0
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +18 -0
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +18 -0
- package/plugins/src/base/skills/lisa-persist-learning/SKILL.md +62 -9
|
@@ -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.
|
|
@@ -20,6 +20,24 @@ Each persisted entry has seven fields:
|
|
|
20
20
|
- `last_confirmed`
|
|
21
21
|
- `confidence`
|
|
22
22
|
|
|
23
|
+
## Claim-time confirmation (`last_confirmed`)
|
|
24
|
+
|
|
25
|
+
`last_confirmed` is advanced at claim time by the build-intake flows (step
|
|
26
|
+
3c.2 of `lisa-{jira,github,linear}-build-intake`) when an entry's rule
|
|
27
|
+
**demonstrably applied** during a claim — the rule was explicitly cited or
|
|
28
|
+
observably followed in the claim's plan or diff. Presence in the eagerly
|
|
29
|
+
loaded context is NOT application: every entry is present in every session,
|
|
30
|
+
so counting mere presence would confirm everything on every claim and defeat
|
|
31
|
+
decay entirely.
|
|
32
|
+
|
|
33
|
+
The bump goes only through `confirmLearningEntry` from
|
|
34
|
+
`@codyswann/lisa/learnings`: a surgical, lock-protected, atomic write that
|
|
35
|
+
advances only `last_confirmed` (re-validated against the
|
|
36
|
+
`>= first_learned` invariant), returns a structured no-op for a missing
|
|
37
|
+
entry or file instead of throwing, and is idempotent within a claim (a
|
|
38
|
+
same-date repeat returns `unchanged`). A failed bump is reported and never
|
|
39
|
+
blocks the build.
|
|
40
|
+
|
|
23
41
|
Only entries accepted by the executable contract may influence the session. A
|
|
24
42
|
missing file is expected and silent. Malformed Markdown, invalid JSONL, unsafe
|
|
25
43
|
paths, non-canonical content, or over-budget documents produce one readable
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-attribute-failure
|
|
3
|
+
description: "Event-triggered root-cause attribution for an arbitrary failure: decide, with cited evidence, whether the defect is Lisa's fault or the project's. Accepts a failure event (defect description, implicated files, rule/skill/hook in play) and returns a verdict of lisa | project | ambiguous plus the evidence relied on. Read-only — it never files, writes, or remediates; callers (lisa-doctor findings, the learning judgment gate, rework triage) consume the verdict. Extracted from lisa-doctor's upstream Lisa change-history diagnosis (#1494) so the same attribution procedure can run on ANY failure event, not only doctor config-audit findings."
|
|
4
|
+
allowed-tools: ["Skill", "Bash", "Read", "Glob", "Grep"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Attribute Failure
|
|
8
|
+
|
|
9
|
+
Attribute ONE failure event to Lisa or the project, with cited evidence. Failure event: $ARGUMENTS
|
|
10
|
+
|
|
11
|
+
Before a Lisa-caused failure can be routed upstream, something has to say — with evidence — "this was Lisa's fault, not the project's." A failure has two possible causes: the project drifted, or **Lisa itself changed or shipped the defect**. This skill must distinguish them instead of blaming the project by default, and equally must never blame Lisa without conclusive evidence. Wrong attribution in either direction is the failure mode; **inconclusive evidence is always `ambiguous`, and ambiguous stays local and low-confidence — never upstream.**
|
|
12
|
+
|
|
13
|
+
## Input — the failure event
|
|
14
|
+
|
|
15
|
+
Accept the event as JSON or `key=value` fields. This procedure is **event-triggered, not version-window-keyed**: any failure can be attributed, not only a doctor config-audit finding.
|
|
16
|
+
|
|
17
|
+
- `defect` — what went wrong, in plain language (required)
|
|
18
|
+
- `implicated_files` — the file paths where the defect lives or manifests (required when known)
|
|
19
|
+
- `surface_in_play` — the Lisa rule / skill / hook / template / workflow involved, if any
|
|
20
|
+
- `installed_version` / `latest_version` — the Lisa version window, when the caller knows it; otherwise resolve it as signal 3 describes
|
|
21
|
+
- `failure_class` — a short slug naming the class of failure (used by downstream filing for the root-cause key)
|
|
22
|
+
|
|
23
|
+
## Output — the verdict
|
|
24
|
+
|
|
25
|
+
Return exactly one verdict — `lisa` | `project` | `ambiguous` — plus the evidence it relied on:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
## Attribution: [lisa | project | ambiguous]
|
|
29
|
+
|
|
30
|
+
**Signal:** [managed-surface | shipped-artifact-behavior | upstream-history | none-conclusive]
|
|
31
|
+
**Lisa surface at fault:** [path or rule/skill/hook name | n/a]
|
|
32
|
+
**Cited evidence:**
|
|
33
|
+
- [each item names the signal it came from and the concrete citation: file path + ownership proof, shipped artifact text, or commit sha/subject/version]
|
|
34
|
+
**Confidence note:** [why the evidence is conclusive, or exactly what was inconclusive]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Verdict rules:
|
|
38
|
+
|
|
39
|
+
- **`lisa`** — at least one signal conclusively pins the defect on a Lisa-shipped surface or upstream change, and the evidence names that surface. Never emit `lisa` from degraded, truncated, or unverified history.
|
|
40
|
+
- **`project`** — the implicated files are project-owned, no shipped Lisa artifact drove the behavior, and the upstream-history window shows no relevant Lisa change. Cite the absence explicitly (which paths were checked, which window showed no relevant change).
|
|
41
|
+
- **`ambiguous`** — anything else: unresolvable version window, unreachable history, partial ownership, conflicting signals. Ambiguous is treated as local and low-confidence; nothing is escalated upstream from it.
|
|
42
|
+
|
|
43
|
+
## The three signals (evaluate in order)
|
|
44
|
+
|
|
45
|
+
### Signal 1 — the defect lives in a Lisa-managed surface
|
|
46
|
+
|
|
47
|
+
Resolve ownership of each implicated file the way doctor's config audit does:
|
|
48
|
+
|
|
49
|
+
- **Copy-overwrite / managed**: the path is populated by `lisa apply` from a stack template (`typescript/`, `expo/`, `nestjs/`, `cdk/`, `harper-fabric/`, `rails/` copy-overwrite trees), is a plugin-distributed rule/skill/hook/agent/command surface, carries Lisa governance markers or a Lisa version stamp, or is governed by `package.lisa.json` `force` keys. A defect in the shipped content of such a surface is **Lisa's fault** — any local edit would be overwritten on the next `lisa apply`.
|
|
50
|
+
- **Create-only**: the local copy is project-owned after scaffolding, but if the defect is faithful to the template as shipped, the template is still Lisa's fault (every newly scaffolded repo inherits it). Cite the template origin, and note the local copy will not be overwritten.
|
|
51
|
+
- **Project-owned**: not from a Lisa template and not Lisa-managed — signal 1 is negative; continue.
|
|
52
|
+
|
|
53
|
+
If the project locally modified a managed surface and the defect lives in the local modification, that is project drift, not a Lisa defect — signal 1 is negative for `lisa` and positive evidence for `project`.
|
|
54
|
+
|
|
55
|
+
### Signal 2 — a shipped Lisa rule/skill/hook drove the wrong behavior
|
|
56
|
+
|
|
57
|
+
Read the shipped artifact named in `surface_in_play` (the plugin-distributed rule, skill, hook, agent, or CI workflow as Lisa ships it — not a local fork). If following its instructions or configuration as shipped produces the observed defect, the verdict is `lisa`: cite the artifact path and the specific shipped text or configuration that drove the behavior. If the artifact is shipped correct and was misapplied locally, that is evidence for `project`.
|
|
58
|
+
|
|
59
|
+
### Signal 3 — the upstream-history window shows Lisa changed the relevant contract
|
|
60
|
+
|
|
61
|
+
This is the #1494 doctor procedure, unchanged in behavior — here it is the third signal, not the trigger. Whenever signals 1-2 are not conclusive on their own — and always before a definitive `project` verdict — pull Lisa's own git history for the version window and read what actually changed:
|
|
62
|
+
|
|
63
|
+
1. **Resolve the version window.** Use the caller-supplied `installed_version`/`latest_version` when present; otherwise determine the project's installed Lisa version (the `@codyswann/lisa` entry in `package.json`/lockfile, or the plugin version stamp on the active runtime) and the latest published version (`npm view @codyswann/lisa version`, or the update check's cached result). An unresolvable window makes this signal inconclusive — it can support `ambiguous`, never `lisa`.
|
|
64
|
+
2. **Pull the upstream history for that window** (read-only; no clone required when `gh` is available):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
gh api "repos/CodySwannGT/lisa/compare/v<installed>...v<latest>" \
|
|
68
|
+
--paginate --slurp |
|
|
69
|
+
jq '{total_commits: .[0].total_commits, files: [.[0].files[]?.filename], commits: [.[].commits[]? | {sha, subject: (.commit.message | split("\n")[0]), api_url: .url, html_url, parents: [.parents[]?.sha]}]}'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`--paginate` fetches every page of commits, and `--slurp` gathers those pages into a single
|
|
73
|
+
array before the external `jq` projection runs. GitHub CLI does not permit its built-in `--jq`
|
|
74
|
+
flag together with `--slurp`, so keep the pipe as shown; without `--slurp`, paginated responses
|
|
75
|
+
are not one merged input. `total_commits` and `files` only need the first page
|
|
76
|
+
(files are capped at 300 and not repeated on later pages); `commits` flattens across all pages
|
|
77
|
+
while retaining each commit SHA and URLs needed for accurate follow-up.
|
|
78
|
+
|
|
79
|
+
After path-scoping identifies a candidate commit, fetch its targeted file-level diff context by
|
|
80
|
+
the retained SHA rather than attributing from the subject alone:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
gh api "repos/CodySwannGT/lisa/commits/<sha>" \
|
|
84
|
+
--jq '{sha, files: [.files[]? | select(.filename == "<relevant-path>" or (.filename | startswith("<relevant-prefix>/"))) | {filename, status, additions, deletions, changes, patch}]}'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Preserve the returned filename, status, counts, and available patch with the SHA in the
|
|
88
|
+
upstream-history projection. A missing or truncated `patch` is not proof of no relevant change;
|
|
89
|
+
use the compare diff fallback below or downgrade the attribution to `ambiguous` when
|
|
90
|
+
commit-level context cannot be established.
|
|
91
|
+
|
|
92
|
+
The compare endpoint paginates commits (250 without `--paginate`) and only lists changed files
|
|
93
|
+
on the first page, capped at 300 total — a large version window can silently drop commits or
|
|
94
|
+
files. If `total_commits` or the file count looks truncated, re-run with the
|
|
95
|
+
`application/vnd.github.diff` accept header (`gh api ... -H "Accept: application/vnd.github.diff"`)
|
|
96
|
+
to pull the full patch text, or fall back to the bounded-fetch `git log` below. When completeness
|
|
97
|
+
still can't be established, say so in the evidence and return `ambiguous` rather than attributing
|
|
98
|
+
with unverified confidence.
|
|
99
|
+
|
|
100
|
+
Fallbacks, in order: `gh api repos/CodySwannGT/lisa/commits?path=<template-path>` for a
|
|
101
|
+
path-scoped view — note this endpoint has no way to bound results to the `v<installed>..v<latest>`
|
|
102
|
+
window, so treat its output as best-effort context only, not authoritative attribution; a
|
|
103
|
+
finite-depth, explicit-tag fetch, which is bounded to the two version refs and should be preferred
|
|
104
|
+
for definitive attribution; or the local marketplace/plugin cache checkout when the runtime has
|
|
105
|
+
one. For the fetch fallback, use a temporary directory and a fixed history ceiling:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
lisa_history_dir="$(mktemp -d)"
|
|
109
|
+
git init "$lisa_history_dir"
|
|
110
|
+
git -C "$lisa_history_dir" remote add origin https://github.com/CodySwannGT/lisa.git
|
|
111
|
+
git -C "$lisa_history_dir" fetch --no-tags --filter=blob:none --depth=256 origin \
|
|
112
|
+
refs/tags/v<installed>:refs/tags/v<installed> \
|
|
113
|
+
refs/tags/v<latest>:refs/tags/v<latest>
|
|
114
|
+
git -C "$lisa_history_dir" merge-base --is-ancestor v<installed> v<latest>
|
|
115
|
+
git -C "$lisa_history_dir" log --format='%H%x09%s' v<installed>..v<latest> -- <paths>
|
|
116
|
+
git -C "$lisa_history_dir" show --format=fuller --stat --patch <relevant-sha> -- <paths>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The explicit tag refspecs, `--no-tags`, and finite `--depth=256` make this fetch bounded. Do not
|
|
120
|
+
silently deepen beyond that ceiling. If `merge-base --is-ancestor` fails, the shallow window is
|
|
121
|
+
incomplete (or the tags are not on one ancestry line): do not make definitive attribution from
|
|
122
|
+
it. If none of the bounded sources are reachable, or only the unbounded path-scoped fallback is
|
|
123
|
+
reachable, report the gap in the evidence and return `ambiguous` — never fail the caller's flow
|
|
124
|
+
because history was unavailable or incomplete, and never let a degraded history produce `lisa`.
|
|
125
|
+
3. **Scope the reading to what the failure touches.** Filter the commit list to the paths that
|
|
126
|
+
generate the failing surface: the detected stacks' template dirs (`typescript/`, `expo/`, …),
|
|
127
|
+
`plugins/src/base/` for skills/hooks/rules, `scripts/` for governance scripts, and the shipped
|
|
128
|
+
config factories (`src/configs/`). A failure about a lint rule reads the lint-config commits,
|
|
129
|
+
not the whole log.
|
|
130
|
+
4. **Attribute.** When the upstream history shows Lisa changed the relevant contract (a tightened
|
|
131
|
+
lint rule, a renamed check context, a new required config key), the verdict is `lisa` — cite the
|
|
132
|
+
commit subject/version. When history shows no relevant upstream change and signals 1-2 are also
|
|
133
|
+
negative, the project drifted — the verdict is `project`, citing the absence of a relevant
|
|
134
|
+
upstream change as the evidence.
|
|
135
|
+
|
|
136
|
+
## Rules
|
|
137
|
+
|
|
138
|
+
- **Read-only.** This skill reads the project and Lisa's repository; it never writes to either, never files issues, and never remediates. Filing the upstream ticket on a `lisa` verdict is the caller's flow (`lisa-persist-learning`, `handoff-upstream` disposition); doctor maps verdicts into its findings; remediation stays with whoever called.
|
|
139
|
+
- **Never block.** Attribution failure, missing tooling (`gh` unavailable), or degraded history degrades to `ambiguous` with the gap named in the evidence — it never raises an error that stops the caller's build or audit.
|
|
140
|
+
- **Every verdict cites its evidence.** An attribution without a named signal and concrete citation is invalid — return `ambiguous` instead. Evidence destined for upstream surfaces quotes ONLY Lisa-owned surface text (never host env values, tokens/credentials, PII, or proprietary host code — link the host issue instead of quoting it); the binding redaction procedure lives in `lisa-persist-learning`'s `handoff-upstream` step.
|
|
141
|
+
- **Ambiguous never escalates.** `ambiguous` is a terminal local verdict: low-confidence, no upstream filing, no durable local rule derived from it.
|
|
142
|
+
- **Headless-safe and idempotent**: no prompts, no side effects, same event in → same verdict out.
|
|
@@ -296,88 +296,39 @@ without turning the base doctor into a second `lisa-wiki-doctor`.
|
|
|
296
296
|
A failing or warning check has two possible causes: the project drifted, or **Lisa itself changed
|
|
297
297
|
upstream** since this project last updated. Doctor must distinguish them instead of blaming the
|
|
298
298
|
project by default. Whenever findings need explanation — and always before proposing repairs —
|
|
299
|
-
|
|
299
|
+
attribute the finding with real evidence.
|
|
300
|
+
|
|
301
|
+
The attribution procedure itself is shared: it lives in the `lisa-attribute-failure` skill
|
|
302
|
+
(extracted from this section so any failure event can be attributed, not only doctor findings).
|
|
303
|
+
Doctor invokes it per finding:
|
|
300
304
|
|
|
301
305
|
1. **Resolve the version window.** Determine the project's installed Lisa version (the
|
|
302
306
|
`@codyswann/lisa` entry in `package.json`/lockfile, or the plugin version stamp on the active
|
|
303
307
|
runtime) and the latest published version (`npm view @codyswann/lisa version`, or the update
|
|
304
308
|
check's cached result).
|
|
305
|
-
2. **
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
Preserve the returned filename, status, counts, and available patch with the SHA in the
|
|
330
|
-
upstream-history projection. A missing or truncated `patch` is not proof of no relevant change;
|
|
331
|
-
use the compare diff fallback below or downgrade attribution to `WARN` when commit-level context
|
|
332
|
-
cannot be established.
|
|
333
|
-
|
|
334
|
-
The compare endpoint paginates commits (250 without `--paginate`) and only lists changed files
|
|
335
|
-
on the first page, capped at 300 total — a large version window can silently drop commits or
|
|
336
|
-
files. If `total_commits` or the file count looks truncated, re-run with the
|
|
337
|
-
`application/vnd.github.diff` accept header (`gh api ... -H "Accept: application/vnd.github.diff"`)
|
|
338
|
-
to pull the full patch text, or fall back to the bounded-fetch `git log` below. When completeness
|
|
339
|
-
still can't be established, say so in the finding and mark it `WARN` rather than attributing
|
|
340
|
-
drift with unverified confidence.
|
|
341
|
-
|
|
342
|
-
Fallbacks, in order: `gh api repos/CodySwannGT/lisa/commits?path=<template-path>` for a
|
|
343
|
-
path-scoped view — note this endpoint has no way to bound results to the `v<installed>..v<latest>`
|
|
344
|
-
window, so treat its output as best-effort context only, not authoritative attribution; a
|
|
345
|
-
finite-depth, explicit-tag fetch, which is bounded to the two version refs and should be preferred
|
|
346
|
-
for definitive attribution; or the local marketplace/plugin cache checkout when the runtime has
|
|
347
|
-
one. For the fetch fallback, use a temporary directory and a fixed history ceiling:
|
|
348
|
-
|
|
349
|
-
```bash
|
|
350
|
-
lisa_history_dir="$(mktemp -d)"
|
|
351
|
-
git init "$lisa_history_dir"
|
|
352
|
-
git -C "$lisa_history_dir" remote add origin https://github.com/CodySwannGT/lisa.git
|
|
353
|
-
git -C "$lisa_history_dir" fetch --no-tags --filter=blob:none --depth=256 origin \
|
|
354
|
-
refs/tags/v<installed>:refs/tags/v<installed> \
|
|
355
|
-
refs/tags/v<latest>:refs/tags/v<latest>
|
|
356
|
-
git -C "$lisa_history_dir" merge-base --is-ancestor v<installed> v<latest>
|
|
357
|
-
git -C "$lisa_history_dir" log --format='%H%x09%s' v<installed>..v<latest> -- <paths>
|
|
358
|
-
git -C "$lisa_history_dir" show --format=fuller --stat --patch <relevant-sha> -- <paths>
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
The explicit tag refspecs, `--no-tags`, and finite `--depth=256` make this fetch bounded. Do not
|
|
362
|
-
silently deepen beyond that ceiling. If `merge-base --is-ancestor` fails, the shallow window is
|
|
363
|
-
incomplete (or the tags are not on one ancestry line): do not make definitive attribution from
|
|
364
|
-
it. If none of the bounded sources are reachable, or only the unbounded path-scoped fallback is
|
|
365
|
-
reachable, report the gap as a `WARN`-level observability note — never fail the audit because
|
|
366
|
-
history was unavailable or incomplete.
|
|
367
|
-
3. **Scope the reading to what the finding touches.** Filter the commit list to the paths that
|
|
368
|
-
generate the failing surface: the detected stacks' template dirs (`typescript/`, `expo/`, …),
|
|
369
|
-
`plugins/src/base/` for skills/hooks/rules, `scripts/` for governance scripts, and the shipped
|
|
370
|
-
config factories (`src/configs/`). A finding about a lint rule failure reads the lint-config
|
|
371
|
-
commits, not the whole log.
|
|
372
|
-
4. **Attribute the finding.** When the upstream history shows Lisa changed the contract (a
|
|
373
|
-
tightened lint rule, a renamed check context, a new required config key), say so in `Observed:`
|
|
374
|
-
with the commit subject/version, and let `Remediation:` point at the sanctioned adoption path
|
|
375
|
-
(e.g. `lisa update` + re-apply, a documented config opt-out) rather than hand-editing managed
|
|
376
|
-
files. When history shows no relevant upstream change, the project drifted — remediate on the
|
|
377
|
-
project side.
|
|
378
|
-
|
|
379
|
-
This history pull is part of doctor's read-only contract: it reads Lisa's repository, never writes
|
|
380
|
-
to it, and repair suggestions stay suggestions.
|
|
309
|
+
2. **Invoke `lisa-attribute-failure`** with the finding as the failure event: the defect
|
|
310
|
+
description, the implicated template/config paths, the rule/skill/hook in play, and the
|
|
311
|
+
resolved version window. The skill evaluates its three ordered signals — Lisa-managed surface
|
|
312
|
+
ownership, shipped rule/skill/hook behavior, and the upstream change-history window (the
|
|
313
|
+
version-window compare procedure formerly documented inline here, preserved verbatim in that
|
|
314
|
+
skill: pagination/`--slurp` handling, targeted per-SHA diff context, truncation caveats, and
|
|
315
|
+
the bounded explicit-tag fetch fallback) — and returns `lisa` | `project` | `ambiguous` plus
|
|
316
|
+
the cited evidence.
|
|
317
|
+
3. **Map the verdict into the finding.**
|
|
318
|
+
- `lisa` — Lisa changed the contract (a tightened lint rule, a renamed check context, a new
|
|
319
|
+
required config key) or shipped the defective surface. Say so in `Observed:` with the cited
|
|
320
|
+
evidence (commit subject/version or managed-surface proof), and let `Remediation:` point at
|
|
321
|
+
the sanctioned adoption path (e.g. `lisa update` + re-apply, a documented config opt-out)
|
|
322
|
+
rather than hand-editing managed files.
|
|
323
|
+
- `project` — history shows no relevant upstream change and the surface is project-owned: the
|
|
324
|
+
project drifted, so remediate on the project side.
|
|
325
|
+
- `ambiguous` — history was unavailable, truncated, or otherwise inconclusive: report the gap
|
|
326
|
+
as a `WARN`-level observability note with the evidence gap named — never fail the audit
|
|
327
|
+
because history was unavailable or incomplete, and never attribute drift with unverified
|
|
328
|
+
confidence.
|
|
329
|
+
|
|
330
|
+
This diagnosis remains part of doctor's read-only contract: the attribution skill reads Lisa's
|
|
331
|
+
repository, never writes to it, and repair suggestions stay suggestions.
|
|
381
332
|
|
|
382
333
|
## Output contract
|
|
383
334
|
|
|
@@ -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;
|
|
@@ -325,6 +327,22 @@ If the canonical fix is merged but not yet present on the production branch, app
|
|
|
325
327
|
|
|
326
328
|
This path is distinct from `BLOCKED`: ambiguity, open blockers, and duplicate-of-open findings remain held for human action and must not be auto-closed.
|
|
327
329
|
|
|
330
|
+
#### 3c.2 Confirm applied learnings (last_confirmed bump)
|
|
331
|
+
|
|
332
|
+
Run this at the end of 3c, after the lifecycle outcome is recorded and before 3d. It keeps the decay pass safe: a genuinely useful learning that keeps applying stays fresh, while dead weight ages out.
|
|
333
|
+
|
|
334
|
+
1. **Identify which learnings demonstrably applied.** Resolve the learnings surface with `resolveProjectLearningsFile` and parse it with `parseLearningsFile` from `@codyswann/lisa/learnings` (never hardcode the path; a missing file skips this step silently). An entry counts as applied ONLY when its rule was explicitly cited or observably followed in this claim's plan, diff, or review responses — the plan quotes the rule or its id, or the diff does specifically what the rule mandates where the default behavior would have differed. **Presence in context is NOT application**: the ledger is loaded eagerly into every session, so counting "it was loaded" would confirm every entry on every claim and defeat decay entirely. A run that produced no plan or diff has nothing to confirm.
|
|
335
|
+
2. **Bump each applied entry exactly once** via the surgical writer:
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
node -e 'import("@codyswann/lisa/learnings").then(async m => { const r = await m.confirmLearningEntry(process.cwd(), process.argv[1], new Date().toISOString().slice(0, 10)); console.log(JSON.stringify(r)); }).catch(error => { console.log(JSON.stringify({ status: "error", id: process.argv[1], message: String(error) })); })' <entry-id> || true
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
The invocation is failure-safe by construction: a rejected import or write resolves to a structured `error` result instead of a crash, and the trailing `|| true` absorbs any remaining non-zero exit (missing `node`, unresolvable package). Record an `error` or non-zero outcome in the cycle summary and continue — the bump must never abort the lifecycle.
|
|
342
|
+
|
|
343
|
+
`confirmLearningEntry` advances ONLY `last_confirmed` — rule text, why, provenance, `first_learned`, and confidence are untouched — and is idempotent within a claim: a repeat same-date bump returns `unchanged`, so an entry that applied repeatedly during one claim is bumped once, not once per application.
|
|
344
|
+
3. **Never block on it.** A failed bump, an unwritable file, or a `not-found` result (the entry was pruned) is recorded under the cycle summary and the claim proceeds — shipping the issue always outranks confirming a learning about it.
|
|
345
|
+
|
|
328
346
|
#### 3d. Transition to $DONE (only after the PR is merged)
|
|
329
347
|
|
|
330
348
|
A `done` env state (`status:on-dev`, `status:on-stg`, or the terminal value) asserts that the code has actually reached that environment. Never set it for a PR that is merely open: auto-merge can be blocked indefinitely (a required rebase / `BEHIND` branch, failing checks, an unaddressed review), and the change may never land. Relabeling an issue `status:on-stg` on an open PR makes it *claim* a deploy that never happened. Transition only after confirming the PR merged.
|
|
@@ -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."`
|
|
@@ -257,6 +259,22 @@ If the canonical fix is merged but not yet present on the production branch, app
|
|
|
257
259
|
|
|
258
260
|
This path is distinct from `BLOCKED`: ambiguity, open blockers, and duplicate-of-open findings remain held for human action and must not be auto-closed.
|
|
259
261
|
|
|
262
|
+
#### 3c.2 Confirm applied learnings (last_confirmed bump)
|
|
263
|
+
|
|
264
|
+
Run this at the end of 3c, after the lifecycle outcome is recorded and before 3d. It keeps the decay pass safe: a genuinely useful learning that keeps applying stays fresh, while dead weight ages out.
|
|
265
|
+
|
|
266
|
+
1. **Identify which learnings demonstrably applied.** Resolve the learnings surface with `resolveProjectLearningsFile` and parse it with `parseLearningsFile` from `@codyswann/lisa/learnings` (never hardcode the path; a missing file skips this step silently). An entry counts as applied ONLY when its rule was explicitly cited or observably followed in this claim's plan, diff, or review responses — the plan quotes the rule or its id, or the diff does specifically what the rule mandates where the default behavior would have differed. **Presence in context is NOT application**: the ledger is loaded eagerly into every session, so counting "it was loaded" would confirm every entry on every claim and defeat decay entirely. A run that produced no plan or diff has nothing to confirm.
|
|
267
|
+
2. **Bump each applied entry exactly once** via the surgical writer:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
node -e 'import("@codyswann/lisa/learnings").then(async m => { const r = await m.confirmLearningEntry(process.cwd(), process.argv[1], new Date().toISOString().slice(0, 10)); console.log(JSON.stringify(r)); }).catch(error => { console.log(JSON.stringify({ status: "error", id: process.argv[1], message: String(error) })); })' <entry-id> || true
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The invocation is failure-safe by construction: a rejected import or write resolves to a structured `error` result instead of a crash, and the trailing `|| true` absorbs any remaining non-zero exit (missing `node`, unresolvable package). Record an `error` or non-zero outcome in the cycle summary and continue — the bump must never abort the lifecycle.
|
|
274
|
+
|
|
275
|
+
`confirmLearningEntry` advances ONLY `last_confirmed` — rule text, why, provenance, `first_learned`, and confidence are untouched — and is idempotent within a claim: a repeat same-date bump returns `unchanged`, so an entry that applied repeatedly during one claim is bumped once, not once per application.
|
|
276
|
+
3. **Never block on it.** A failed bump, an unwritable file, or a `not-found` result (the entry was pruned) is recorded under the cycle summary and the claim proceeds — shipping the ticket always outranks confirming a learning about it.
|
|
277
|
+
|
|
260
278
|
#### 3d. Transition to $DONE (only after the PR is merged)
|
|
261
279
|
|
|
262
280
|
A `done` env status (`On Dev`, `On Stg`, or the terminal value) asserts that the code has actually reached that environment. Never set it for a PR that is merely open: auto-merge can be blocked indefinitely (a required rebase / `BEHIND` branch, failing checks, an unaddressed review), and the change may never land. Setting `On Stg` on an open PR makes a ticket *claim* a deploy that never happened. Transition only after confirming the PR merged.
|
|
@@ -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.
|
|
@@ -250,6 +252,22 @@ If the canonical fix is merged but not yet present on the production branch, app
|
|
|
250
252
|
|
|
251
253
|
This path is distinct from `BLOCKED`: ambiguity, open blockers, and duplicate-of-open findings remain held for human action and must not be auto-closed.
|
|
252
254
|
|
|
255
|
+
#### 3c.2 Confirm applied learnings (last_confirmed bump)
|
|
256
|
+
|
|
257
|
+
Run this at the end of 3c, after the lifecycle outcome is recorded and before 3d. It keeps the decay pass safe: a genuinely useful learning that keeps applying stays fresh, while dead weight ages out.
|
|
258
|
+
|
|
259
|
+
1. **Identify which learnings demonstrably applied.** Resolve the learnings surface with `resolveProjectLearningsFile` and parse it with `parseLearningsFile` from `@codyswann/lisa/learnings` (never hardcode the path; a missing file skips this step silently). An entry counts as applied ONLY when its rule was explicitly cited or observably followed in this claim's plan, diff, or review responses — the plan quotes the rule or its id, or the diff does specifically what the rule mandates where the default behavior would have differed. **Presence in context is NOT application**: the ledger is loaded eagerly into every session, so counting "it was loaded" would confirm every entry on every claim and defeat decay entirely. A run that produced no plan or diff has nothing to confirm.
|
|
260
|
+
2. **Bump each applied entry exactly once** via the surgical writer:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
node -e 'import("@codyswann/lisa/learnings").then(async m => { const r = await m.confirmLearningEntry(process.cwd(), process.argv[1], new Date().toISOString().slice(0, 10)); console.log(JSON.stringify(r)); }).catch(error => { console.log(JSON.stringify({ status: "error", id: process.argv[1], message: String(error) })); })' <entry-id> || true
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The invocation is failure-safe by construction: a rejected import or write resolves to a structured `error` result instead of a crash, and the trailing `|| true` absorbs any remaining non-zero exit (missing `node`, unresolvable package). Record an `error` or non-zero outcome in the cycle summary and continue — the bump must never abort the lifecycle.
|
|
267
|
+
|
|
268
|
+
`confirmLearningEntry` advances ONLY `last_confirmed` — rule text, why, provenance, `first_learned`, and confidence are untouched — and is idempotent within a claim: a repeat same-date bump returns `unchanged`, so an entry that applied repeatedly during one claim is bumped once, not once per application.
|
|
269
|
+
3. **Never block on it.** A failed bump, an unwritable file, or a `not-found` result (the entry was pruned) is recorded under the cycle summary and the claim proceeds — shipping the Issue always outranks confirming a learning about it.
|
|
270
|
+
|
|
253
271
|
#### 3d. Relabel to $DONE (only after the PR is merged)
|
|
254
272
|
|
|
255
273
|
A `done` env state (`status:on-dev`, `status:on-stg`, or the terminal value) asserts that the code has actually reached that environment. Never set it for a PR that is merely open: auto-merge can be blocked indefinitely (a required rebase / `BEHIND` branch, failing checks, an unaddressed review), and the change may never land. Relabeling an Issue `status:on-stg` on an open PR makes it *claim* a deploy that never happened. Transition only after confirming the PR merged.
|