@codyswann/lisa 2.237.0 → 2.239.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/learnings-writer.d.ts +22 -0
- package/dist/core/learnings-writer.d.ts.map +1 -1
- package/dist/core/learnings-writer.js +51 -8
- package/dist/core/learnings-writer.js.map +1 -1
- package/dist/core/learnings.d.ts +1 -1
- package/dist/core/learnings.d.ts.map +1 -1
- package/dist/core/learnings.js +1 -1
- package/dist/core/learnings.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-atlassian-access/SKILL.md +12 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
- package/plugins/lisa/.codex-plugin/skills/lisa-git-submit-pr/SKILL.md +3 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-github-build-intake/SKILL.md +5 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +2 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-build-intake/SKILL.md +5 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +54 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-build-intake/SKILL.md +5 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/SKILL.md +145 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-persist-learning/agents/openai.yaml +4 -0
- package/plugins/lisa/agents/learning-judge.md +119 -0
- package/plugins/lisa/commands/persist-learning.md +6 -0
- package/plugins/lisa/rules/eager/rejection-detection.md +40 -0
- package/plugins/lisa/rules/reference/rejection-detection.md +103 -0
- package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +12 -1
- package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
- package/plugins/lisa/skills/lisa-git-submit-pr/SKILL.md +3 -2
- package/plugins/lisa/skills/lisa-github-build-intake/SKILL.md +5 -1
- package/plugins/lisa/skills/lisa-implement/SKILL.md +2 -0
- package/plugins/lisa/skills/lisa-jira-build-intake/SKILL.md +5 -1
- package/plugins/lisa/skills/lisa-linear-access/SKILL.md +54 -0
- package/plugins/lisa/skills/lisa-linear-build-intake/SKILL.md +5 -1
- package/plugins/lisa/skills/lisa-persist-learning/SKILL.md +145 -0
- package/plugins/lisa/skills/lisa-persist-learning/agents/openai.yaml +4 -0
- package/plugins/lisa-agy/agents/learning-judge.md +119 -0
- package/plugins/lisa-agy/commands/lisa/persist-learning.md +6 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +12 -1
- package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
- package/plugins/lisa-agy/skills/lisa-git-submit-pr/SKILL.md +3 -2
- package/plugins/lisa-agy/skills/lisa-github-build-intake/SKILL.md +5 -1
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +2 -0
- package/plugins/lisa-agy/skills/lisa-jira-build-intake/SKILL.md +5 -1
- package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +54 -0
- package/plugins/lisa-agy/skills/lisa-linear-build-intake/SKILL.md +5 -1
- package/plugins/lisa-agy/skills/lisa-persist-learning/SKILL.md +145 -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/agents/learning-judge.agent.md +119 -0
- package/plugins/lisa-copilot/commands/lisa/persist-learning.md +6 -0
- package/plugins/lisa-copilot/rules/eager/rejection-detection.md +40 -0
- package/plugins/lisa-copilot/rules/reference/rejection-detection.md +103 -0
- package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +12 -1
- package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
- package/plugins/lisa-copilot/skills/lisa-git-submit-pr/SKILL.md +3 -2
- package/plugins/lisa-copilot/skills/lisa-github-build-intake/SKILL.md +5 -1
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +2 -0
- package/plugins/lisa-copilot/skills/lisa-jira-build-intake/SKILL.md +5 -1
- package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +54 -0
- package/plugins/lisa-copilot/skills/lisa-linear-build-intake/SKILL.md +5 -1
- package/plugins/lisa-copilot/skills/lisa-persist-learning/SKILL.md +145 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/agents/learning-judge.md +119 -0
- package/plugins/lisa-cursor/commands/lisa/persist-learning.md +6 -0
- package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +108 -0
- package/plugins/lisa-cursor/rules/rejection-detection.mdc +45 -0
- package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +12 -1
- package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
- package/plugins/lisa-cursor/skills/lisa-git-submit-pr/SKILL.md +3 -2
- package/plugins/lisa-cursor/skills/lisa-github-build-intake/SKILL.md +5 -1
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +2 -0
- package/plugins/lisa-cursor/skills/lisa-jira-build-intake/SKILL.md +5 -1
- package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +54 -0
- package/plugins/lisa-cursor/skills/lisa-linear-build-intake/SKILL.md +5 -1
- package/plugins/lisa-cursor/skills/lisa-persist-learning/SKILL.md +145 -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/agents/learning-judge.md +119 -0
- package/plugins/src/base/commands/persist-learning.md +6 -0
- package/plugins/src/base/rules/eager/rejection-detection.md +40 -0
- package/plugins/src/base/rules/reference/rejection-detection.md +103 -0
- package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +12 -1
- package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +62 -8
- package/plugins/src/base/skills/lisa-git-submit-pr/SKILL.md +3 -2
- package/plugins/src/base/skills/lisa-github-build-intake/SKILL.md +5 -1
- package/plugins/src/base/skills/lisa-implement/SKILL.md +2 -0
- package/plugins/src/base/skills/lisa-jira-build-intake/SKILL.md +5 -1
- package/plugins/src/base/skills/lisa-linear-access/SKILL.md +54 -0
- package/plugins/src/base/skills/lisa-linear-build-intake/SKILL.md +5 -1
- package/plugins/src/base/skills/lisa-persist-learning/SKILL.md +145 -0
|
@@ -14,6 +14,7 @@ Recognized optional hints:
|
|
|
14
14
|
- `target_branch=<branch>` or `base=<branch>` — intended PR base branch, used to decide whether a GitHub closing keyword is safe.
|
|
15
15
|
- `tracker_provider=<github|linear|jira|none>` — explicit provider when the ref shape is ambiguous.
|
|
16
16
|
- `pr_url=<url>` — live pull request URL, only needed when updating tracker backlinks from an existing PR context.
|
|
17
|
+
- `auto_merge=<true|false>` — whether the PR should merge automatically. Default `true` (existing behavior for every current caller). With `auto_merge=false`, skip step 5 entirely (never run `gh pr merge --auto`) and pass `auto_merge=false` through to the `drive-pr-to-merge` delegation in step 6 so the PR is driven to a clean, green, OPEN state and then left awaiting a human.
|
|
17
18
|
|
|
18
19
|
## Workflow
|
|
19
20
|
|
|
@@ -34,10 +35,10 @@ Recognized optional hints:
|
|
|
34
35
|
- Include native development linkage for the source work item when `work_item_ref` can be inferred from `$ARGUMENTS`, the current branch name, an existing PR body, or the issue/ticket context passed by the caller.
|
|
35
36
|
- After the PR exists, ensure the source work item has a backlink to the PR: invoke `lisa-tracker-sync` with the work item, milestone `pr-ready`, the live `pr_url`, and `tracker_provider` when known. This makes ticket -> PR linkage mandatory, not just a best-effort milestone comment.
|
|
36
37
|
- After the PR exists, re-resolve the live Pull Request node id and, when `github.projects.v2` is enabled, invoke `lisa-github-project-v2` with `operation: ensure-item` and `content_node_id: <pull-request-node-id>` so linked pull requests join the configured shared Project without replacing the PR as the durable review/merge surface.
|
|
37
|
-
5. **Auto-merge
|
|
38
|
+
5. **Auto-merge** (only when `auto_merge=true`, the default — with `auto_merge=false` skip this step entirely): Choose merge strategy by PR type:
|
|
38
39
|
- **Promotion PRs** (env → env, e.g. `dev` → `staging`): use `gh pr merge --auto --merge` (never squash). Squashing flattens the constituent `chore(release): X.Y.Z [skip ci]` commits into one commit titled with the PR title, stripping the `[skip ci]` markers and breaking the release workflow's promotion-detection regex — the destination branch then double-bumps its version. `--merge` keeps each `chore(release)` commit (and its `[skip ci]` marker) intact under a clean merge commit subject the workflow can recognize.
|
|
39
40
|
- **Feature PRs** (anything → `dev`): use `gh pr merge --auto --merge`.
|
|
40
|
-
6. **Drive to merge**: Opening the PR and enabling auto-merge is not terminal. Delegate the full mergeability loop to the `drive-pr-to-merge` skill — invoke it with the PR number and `merge_method=merge` (and `verify_commit=<pushed head sha>` for the ancestry check). That skill is the single source of truth for clearing every blocker: auto-merge with direct-merge fallback, `BEHIND` re-sync, conflict resolution, failing-check fixes, human + bot (CodeRabbit) review-comment handling with GraphQL thread resolution, stale `CHANGES_REQUESTED` dismissal, and post-merge ancestry verification. It runs inline and uses plain `gh`/`git` so Claude and Codex behave identically. Do not re-implement the loop here.
|
|
41
|
+
6. **Drive to merge**: Opening the PR and enabling auto-merge is not terminal. Delegate the full mergeability loop to the `drive-pr-to-merge` skill — invoke it with the PR number and `merge_method=merge` (and `verify_commit=<pushed head sha>` for the ancestry check). When the caller passed `auto_merge=false`, also pass `auto_merge=false` so the delegated loop drives the PR to green-and-open (`awaiting-human`) instead of merged — never merging it, even on repos that disallow auto-merge. That skill is the single source of truth for clearing every blocker: auto-merge with direct-merge fallback, `BEHIND` re-sync, conflict resolution, failing-check fixes, human + bot (CodeRabbit) review-comment handling with GraphQL thread resolution, stale `CHANGES_REQUESTED` dismissal, and post-merge ancestry verification. It runs inline and uses plain `gh`/`git` so Claude and Codex behave identically. Do not re-implement the loop here.
|
|
41
42
|
|
|
42
43
|
### Native Development Linkage
|
|
43
44
|
|
|
@@ -252,6 +252,10 @@ A blocker is active if it is open and has no cleared status label. Treat `status
|
|
|
252
252
|
|
|
253
253
|
#### 3b. Claim
|
|
254
254
|
|
|
255
|
+
**Rejection detection runs first — before the relabel below.** Per the vendor-neutral `rejection-detection` rule (cite the slug; do not restate its classification table), classify this item at the **top of 3b, BEFORE** the `$READY → $CLAIMED` relabel — after the relabel the current-lane signal is gone. Read the item's Label-Event History from `lisa-github-read-issue` (chronological `LabeledEvent` / `UnlabeledEvent` on the configured `$READY` label) and classify it `rejection-reclaim | forward-only | never-left-ready | unknown`. Lane names come from `.lisa.config.json` (`github.labels.build.*`), never hardcoded. A failing/absent history yields `unknown` and the claim proceeds — detection never blocks the build. Items carrying a learning marker (`[lisa-learning-drop]` / `[lisa-learning-pr]` / `[lisa-learning-upstream-handoff]`) or the `learning:needs-triage` label are never rejection triggers (no learning-about-learning). Carry the classification into the relabel and lifecycle below.
|
|
256
|
+
|
|
257
|
+
**On `rejection-reclaim`, reflect before re-implementing** (per `rejection-detection`): read the rejection evidence through the access layer — the issue comments posted after the backward transition (the QA rejection comment) and the review threads on the rejected PR via `lisa-github-read-issue` — assemble ONE candidate learning (rule, why, provenance linking the rejection comment + rejected PR, evidence links, scope hint, triggering issue, fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`), and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate as a comment carrying a **visible prose line plus** the marker (a bare marker renders as an empty bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` then `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. Dedupe on `<issue>-<backward-transition-timestamp>` — a second re-claim produces no duplicate. Unreadable/absent evidence → no candidate, still implement.
|
|
258
|
+
|
|
255
259
|
```bash
|
|
256
260
|
gh issue edit <number> --repo <org>/<repo> --remove-label "$READY" --add-label "$CLAIMED"
|
|
257
261
|
# Assign to the authenticated user ONLY when the issue is currently unassigned (attributable claim;
|
|
@@ -274,7 +278,7 @@ After the claim succeeds, run the per-issue lifecycle defined by the `github-age
|
|
|
274
278
|
- `lisa-github-verify` — pre-flight quality gate, including the draft-then-block procedure on FAIL
|
|
275
279
|
- `lisa-ticket-triage` — analytical triage gate (a `BLOCKED` verdict stops the cycle with findings posted)
|
|
276
280
|
- Intent determination from the `type:` label
|
|
277
|
-
2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <org>/<repo>#<number>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
|
|
281
|
+
2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <org>/<repo>#<number>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. **When 3b classified this item `rejection-reclaim`, the context bundle passed to `lisa-implement` MUST include the rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong) — reuse the evidence already read in 3b, do not fetch it twice — so the plan can address it per `rejection-detection`; absence of evidence never blocks. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
|
|
278
282
|
3. **Milestone sync and evidence** (`lisa-github-sync`, `lisa-github-evidence`) happen at the milestones the `github-agent` workflow defines, within the dispatched flow.
|
|
279
283
|
|
|
280
284
|
If you are somehow running this skill as a spawned teammate inside an existing team (nested misrouting — Intake keeps this chain in the lead session), do NOT run the lifecycle inline and do NOT spawn named peers. Return this payload to the lead so the lead session can run this Phase 3c in-session:
|
|
@@ -49,6 +49,8 @@ The team lead does NOT read the input directly. The first task on the team's pla
|
|
|
49
49
|
|
|
50
50
|
The input resolver is the only teammate that may be spawned before the Roster Decision exists. After it returns the resolved input, do not spawn any lifecycle, research, implementation, review, verification, or learning teammate until the Roster Decision has been recorded.
|
|
51
51
|
|
|
52
|
+
**Rejection evidence in the claim handoff.** When this flow was dispatched from a build-intake claim that classified the item as a `rejection-reclaim` (per the `rejection-detection` rule), the context bundle carries a **rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong). The plan MUST explicitly address that rejection evidence and MUST NOT re-propose the specific approach the rejection named as wrong — a bounced item must come back fixed, not re-bounced. `lisa-implement` cannot fetch this itself (it never sees the claim); it consumes what the handoff carries. Absence of rejection evidence never blocks — plan and implement normally.
|
|
53
|
+
|
|
52
54
|
## Select the agent roster
|
|
53
55
|
|
|
54
56
|
Before spawning any teammate beyond the bounded input resolver, record a **Roster Decision** artifact. It must enumerate every agent or specialist type exposed by the current runtime's delegation tool and record one line per type:
|
|
@@ -196,6 +196,10 @@ This gate never blocks a legitimate flat Task/Bug: those have no open children a
|
|
|
196
196
|
|
|
197
197
|
#### 3b. Claim
|
|
198
198
|
|
|
199
|
+
**Rejection detection runs first — before the transition below.** Per the vendor-neutral `rejection-detection` rule (cite the slug; do not restate its classification table), classify this ticket at the **top of 3b, BEFORE** the `$READY → $CLAIMED` transition — after the transition the current-status signal is gone. Read the ticket's status changelog via `lisa-atlassian-access operation: changelog key: <TICKET>` and classify it `rejection-reclaim | forward-only | never-left-ready | unknown` (a `rejection-reclaim` is a changelog entry whose `to` is the configured `$READY` status following an earlier `review`/`done`-ward status). Status names come from `.lisa.config.json`, never hardcoded. A failing/absent changelog yields `unknown` and the claim proceeds — detection never blocks the build. Tickets carrying a learning marker (`[lisa-learning-drop]` / `[lisa-learning-pr]` / `[lisa-learning-upstream-handoff]`) or the `learning:needs-triage` label are never rejection triggers (no learning-about-learning). Carry the classification into the transition and lifecycle below.
|
|
200
|
+
|
|
201
|
+
**On `rejection-reclaim`, reflect before re-implementing** (per `rejection-detection`): read the rejection evidence through the access layer — the ticket comments posted after the backward transition (the QA rejection comment) via `lisa-atlassian-access operation: read-ticket` / `comment` reads and the review threads on the rejected PR — assemble ONE candidate learning (rule, why, provenance linking the rejection comment + rejected PR, evidence links, scope hint, triggering issue, fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`), and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate via `lisa-atlassian-access operation: comment` as a comment carrying a **visible prose line plus** the marker (a bare marker renders as an empty bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` then `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. Dedupe on `<issue>-<backward-transition-timestamp>` — a second re-claim produces no duplicate. Unreadable/absent evidence → no candidate, still implement.
|
|
202
|
+
|
|
199
203
|
Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa-atlassian-access` `operation: transition key: <TICKET> to: "$CLAIMED"`.
|
|
200
204
|
- **Assign to the authenticated user when the ticket is unassigned.** A claim must be attributable. If the ticket has no assignee, assign it to the authenticated account — prefer acli `--assignee @me` (resolves server-side to the authenticated user, which avoids the federated-`accountId` mis-assignment), or `write-ticket` with the `accountId` from the `/rest/api/3/myself` identity probe the access skill already documents. Leave an already-assigned ticket's assignee untouched — never reassign work that already has an owner.
|
|
201
205
|
- Post a `[claude-build-intake]` comment via `lisa-atlassian-access` `operation: comment key: <TICKET> body: "Claimed by Claude. Starting build."`
|
|
@@ -212,7 +216,7 @@ After the claim succeeds, run the per-ticket lifecycle defined by the `jira-agen
|
|
|
212
216
|
- `lisa-jira-verify` — pre-flight quality gate, including the draft-then-block procedure on FAIL
|
|
213
217
|
- `lisa-ticket-triage` — analytical triage gate (a `BLOCKED` verdict stops the cycle with findings posted)
|
|
214
218
|
- Intent determination from the issue type
|
|
215
|
-
2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <TICKET>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
|
|
219
|
+
2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <TICKET>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic) — passing the full context bundle from the read step. **When 3b classified this ticket `rejection-reclaim`, the context bundle passed to `lisa-implement` MUST include the rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong) — reuse the evidence already read in 3b, do not fetch it twice — so the plan can address it per `rejection-detection`; absence of evidence never blocks. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
|
|
216
220
|
3. **Milestone sync and evidence** (`lisa-jira-sync`, `lisa-jira-evidence`) happen at the milestones the `jira-agent` workflow defines, within the dispatched flow.
|
|
217
221
|
|
|
218
222
|
If you are somehow running this skill as a spawned teammate inside an existing team (nested misrouting — Intake keeps this chain in the lead session), do NOT run the lifecycle inline and do NOT spawn named peers. Return this payload to the lead so the lead session can run this Phase 3c in-session:
|
|
@@ -23,6 +23,7 @@ operation: get-issue id:<ID>
|
|
|
23
23
|
operation: save-issue payload:{...}
|
|
24
24
|
operation: list-comments issue_id:<ID>
|
|
25
25
|
operation: save-comment issue_id:<ID> body:"..."
|
|
26
|
+
operation: history id:<ID>
|
|
26
27
|
operation: list-issue-labels [team:<ID>]
|
|
27
28
|
operation: create-issue-label payload:{...}
|
|
28
29
|
operation: list-project-labels
|
|
@@ -81,6 +82,59 @@ linear_graphql() {
|
|
|
81
82
|
Map operation names to Linear GraphQL queries/mutations in this access skill.
|
|
82
83
|
Consumers pass business-shaped arguments only; they do not embed GraphQL.
|
|
83
84
|
|
|
85
|
+
## `history` — transition history (read-only)
|
|
86
|
+
|
|
87
|
+
`history id:<ID>` returns an Issue's ordered past state changes — the raw
|
|
88
|
+
material for rejection detection (an Issue that reached a `review`/`done`-ward
|
|
89
|
+
state and is now back in `ready`). `IssueHistory` is reachable today through the
|
|
90
|
+
existing `linear_graphql` adapter but was **not** in the documented contract; an
|
|
91
|
+
undocumented-but-reachable capability is not exposed, so it now appears in the
|
|
92
|
+
Invocation Contract above. Reuse the existing adapter — this is a
|
|
93
|
+
contract/surface change, not a new transport (the `integration-access-layer`
|
|
94
|
+
rule forbids consumers from reaching around the layer).
|
|
95
|
+
|
|
96
|
+
Query through `linear_graphql` (oldest→newest; page `history(first:…, after:…)`
|
|
97
|
+
via `pageInfo` for busy Issues so history never silently truncates):
|
|
98
|
+
|
|
99
|
+
```graphql
|
|
100
|
+
query($id:String!){
|
|
101
|
+
issue(id:$id){
|
|
102
|
+
history(first:100){
|
|
103
|
+
pageInfo{hasNextPage endCursor}
|
|
104
|
+
nodes{
|
|
105
|
+
createdAt
|
|
106
|
+
fromState{name type}
|
|
107
|
+
toState{name type}
|
|
108
|
+
actor{name}
|
|
109
|
+
addedLabelIds
|
|
110
|
+
removedLabelIds
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- **Shape.** For each node emit `{ from, to, when, who }` — `fromState.name` →
|
|
118
|
+
`toState.name`, `createdAt` (ISO timestamp), `actor.name`. Nodes with no
|
|
119
|
+
`fromState`/`toState` are non-state edits (label-only, assignee, etc.); keep
|
|
120
|
+
them for the label stream, skip them for workflow-state ordering.
|
|
121
|
+
- **Label history (honest caveat).** Linear's build lanes are **label-driven**
|
|
122
|
+
(`lisa-linear-build-intake` keys the queue on `status:*` labels), so label
|
|
123
|
+
moves matter as much as workflow-state moves. `IssueHistory` carries label
|
|
124
|
+
changes as `addedLabelIds` / `removedLabelIds` — arrays of label **IDs**, not
|
|
125
|
+
names. It does **not** inline label names, and it does not carry the label's
|
|
126
|
+
full prior/next set — only the per-event deltas. Resolve IDs → names by
|
|
127
|
+
cross-referencing `list-issue-labels`. Do not overclaim: a caller that needs
|
|
128
|
+
`status:*` label transitions reconstructs them from the ID deltas plus the
|
|
129
|
+
label catalog, not from an inline name on the history node.
|
|
130
|
+
- **Empty is valid.** An Issue that never changed state returns an **empty**
|
|
131
|
+
history — an empty history is a valid result, not an error.
|
|
132
|
+
- **Graceful degrade — never block the build.** A failed history fetch returns
|
|
133
|
+
the layer's `Error:` result. Callers MUST treat that as **unknown** history
|
|
134
|
+
and proceed — a history read failure never blocks the build. MCP cannot reach
|
|
135
|
+
`IssueHistory`, so the `history` operation resolves only through the
|
|
136
|
+
`LINEAR_API_KEY` GraphQL substrate; without it, the result is unknown.
|
|
137
|
+
|
|
84
138
|
## Invariants
|
|
85
139
|
|
|
86
140
|
- MCP is preferred when it is present and already authenticated.
|
|
@@ -186,6 +186,10 @@ This gate never blocks a legitimate flat Task/Bug: those have no open children a
|
|
|
186
186
|
|
|
187
187
|
#### 3b. Claim
|
|
188
188
|
|
|
189
|
+
**Rejection detection runs first — before the relabel below.** Per the vendor-neutral `rejection-detection` rule (cite the slug; do not restate its classification table), classify this Issue at the **top of 3b, BEFORE** the `$READY → $CLAIMED` relabel — after the relabel the current-lane signal is gone. Read the Issue's history via `lisa-linear-access operation: history id: <ISSUE-ID>`, keyed on `status:*` **label** history (Linear build lanes are label-driven; resolve `addedLabelIds`/`removedLabelIds` against `list-issue-labels`), and classify it `rejection-reclaim | forward-only | never-left-ready | unknown` (a `rejection-reclaim` is the configured `$READY` label re-added after a later-lane label). Label names come from `.lisa.config.json`, never hardcoded. A failing/absent history yields `unknown` and the claim proceeds — detection never blocks the build. Issues carrying a learning marker (`[lisa-learning-drop]` / `[lisa-learning-pr]` / `[lisa-learning-upstream-handoff]`) or the `learning:needs-triage` label are never rejection triggers (no learning-about-learning). Carry the classification into the relabel and lifecycle below.
|
|
190
|
+
|
|
191
|
+
**On `rejection-reclaim`, reflect before re-implementing** (per `rejection-detection`): read the rejection evidence through the access layer — the Issue comments posted after the backward transition (the QA rejection comment) via `lisa-linear-access operation: list-comments` and the review threads on the rejected PR — assemble ONE candidate learning (rule, why, provenance linking the rejection comment + rejected PR, evidence links, scope hint, triggering issue, fingerprint `sll4-sha1(rule\ntriggering_issue)[:12]`), and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate via `lisa-linear-access operation: save-comment` as a comment carrying a **visible prose line plus** the marker (a bare marker renders as an empty bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` then `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. Dedupe on `<issue>-<backward-transition-timestamp>` — a second re-claim produces no duplicate. Unreadable/absent evidence → no candidate, still implement.
|
|
192
|
+
|
|
189
193
|
Update labels via `lisa-linear-access operation: save-issue`: remove `$READY`, add `$CLAIMED`. Resolve label IDs via `list_issue_labels` (create `$CLAIMED` if missing).
|
|
190
194
|
|
|
191
195
|
**Assign to the authenticated user when the Issue is unassigned.** A claim must be attributable. If the Issue has no assignee, set its `assigneeId` to the authenticated viewer (resolve the viewer's id via the Linear MCP identity — e.g. `get_user` for the current actor) through `lisa-linear-access operation: save-issue`. Leave an already-assigned Issue's assignee untouched — never reassign work that already has an owner.
|
|
@@ -205,7 +209,7 @@ After the claim succeeds, run the per-Issue lifecycle defined by the `linear-age
|
|
|
205
209
|
- `lisa-linear-verify` — pre-flight quality gate, including the draft-then-block procedure on FAIL
|
|
206
210
|
- `lisa-ticket-triage` — analytical triage gate (a `BLOCKED` verdict stops the cycle with findings posted)
|
|
207
211
|
- Intent determination from the type label
|
|
208
|
-
2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <ISSUE-ID>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic-equivalent) — passing the full context bundle from the read step. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
|
|
212
|
+
2. **Dispatch the flow in-session:** when the gates pass, invoke the lifecycle skill via the Skill tool — `lisa-implement <ISSUE-ID>` for Build / Fix / Improve / Investigate-Only (or `lisa-plan` for an Epic-equivalent) — passing the full context bundle from the read step. **When 3b classified this Issue `rejection-reclaim`, the context bundle passed to `lisa-implement` MUST include the rejection evidence summary** (what was rejected, the defect the QA comment named, the approach named as wrong) — reuse the evidence already read in 3b, do not fetch it twice — so the plan can address it per `rejection-detection`; absence of evidence never blocks. `lisa-implement`'s own orchestration preamble then creates the per-item agent team (input-resolver, Roster Decision, specialist fanout) exactly as a direct invocation would.
|
|
209
213
|
3. **Milestone sync and evidence** (`lisa-linear-sync`, `lisa-linear-evidence`) happen at the milestones the `linear-agent` workflow defines, within the dispatched flow.
|
|
210
214
|
|
|
211
215
|
If you are somehow running this skill as a spawned teammate inside an existing team (nested misrouting — Intake keeps this chain in the lead session), do NOT run the lifecycle inline and do NOT spawn named peers. Return this payload to the lead so the lead session can run this Phase 3c in-session:
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-persist-learning
|
|
3
|
+
description: This skill should be used when a candidate learning (from a failure signal, rejection, or debrief) needs to be judged and routed. It computes a stable fingerprint, runs the candidate through the hostile-default learning-judge gate, and performs exactly the verdict's side effects — a dropped-with-reason note on the triggering issue (drop), an upstream handoff marker (lisa-upstream), or a confidence-routed pull request that touches only the learnings surface (durable-learning). Idempotent via marker dedupe; headless-safe; never blocks the primary build flow.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Persist Learning
|
|
7
|
+
|
|
8
|
+
Route ONE candidate learning through the judgment gate and act on the verdict. Candidate: $ARGUMENTS
|
|
9
|
+
|
|
10
|
+
Most candidates are dropped — that is the gate working, not a failure. Nothing is ever silent (every drop leaves a visible note), and no learning content ever reaches the learnings surface outside a pull request.
|
|
11
|
+
|
|
12
|
+
## Candidate Input
|
|
13
|
+
|
|
14
|
+
Accept the candidate as JSON or `key=value` fields:
|
|
15
|
+
|
|
16
|
+
- `rule` — the proposed learning (must fit the executable contract: ≤240 chars, ≤2 lines)
|
|
17
|
+
- `why` — the causal claim
|
|
18
|
+
- `provenance` — stable refs (issues/PRs/commits/comments), ≤20
|
|
19
|
+
- `evidence_links` — concrete evidence refs available for citation
|
|
20
|
+
- `scope_hint` — `project` | `upstream` (a hint; the judge decides)
|
|
21
|
+
- `triggering_issue` — the issue/work item whose failure produced this candidate (required)
|
|
22
|
+
- `fingerprint` — optional; computed below when absent
|
|
23
|
+
|
|
24
|
+
## Phase 0 — Fingerprint (stable dedupe key)
|
|
25
|
+
|
|
26
|
+
Every marker and branch below keys off one deterministic fingerprint:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
fingerprint = "sll4-" + first 12 hex chars of sha1(normalized_rule + "\n" + triggering_issue)
|
|
30
|
+
normalized_rule = rule lowercased, all whitespace runs collapsed to single spaces, trimmed
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
NORM=$(printf '%s' "$RULE" | tr '[:upper:]' '[:lower:]' | tr -s '[:space:]' ' ' | sed 's/^ *//; s/ *$//')
|
|
35
|
+
FP="sll4-$(printf '%s\n%s' "$NORM" "$TRIGGERING_ISSUE" | shasum -a 1 | cut -c1-12)"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
(Use `sha1sum` where `shasum` is unavailable.) The same rule for the same triggering issue always produces the same fingerprint, so re-runs dedupe instead of duplicating comments, PRs, or branches.
|
|
39
|
+
|
|
40
|
+
## Phase 1 — Judge (mandatory, never skipped)
|
|
41
|
+
|
|
42
|
+
Invoke the `learning-judge` agent (via the Agent/Task tool with `subagent_type: "learning-judge"` — the same invoke pattern `learner` uses for `skill-evaluator`) with the full candidate including the fingerprint. It returns a verdict: `classification`, `cited_evidence[]`, `rationale`, `confidence` (durable only), `disposition`.
|
|
43
|
+
|
|
44
|
+
**Respect the verdict — do not override it.** Never re-run the judge hoping for a different answer, and never persist anything the judge did not classify `durable-learning`.
|
|
45
|
+
|
|
46
|
+
## Phase 2 — Route by disposition
|
|
47
|
+
|
|
48
|
+
All issue/PR comments below follow the marker-dedupe discipline from `lisa-github-write-prd` Phase 2, each with its own producer tag: match on the **marker, never the title or text**; **exactly one marker per body**; **never write a markerless body** (it breaks all future dedupe); include the eventual-consistency guard — when the `gh` search index is stale, also enumerate the bodies directly (`gh issue view <n> --json comments --jq '.comments[].body'` or `gh pr list --json number,body`) and grep for the marker before deciding to create.
|
|
49
|
+
|
|
50
|
+
### `drop` (classification `one-off` or `misunderstanding/spec-gap`)
|
|
51
|
+
|
|
52
|
+
Post **one** comment on the triggering issue and write **nothing** — zero bytes — to the learnings surface (not a stub, not a placeholder):
|
|
53
|
+
|
|
54
|
+
```markdown
|
|
55
|
+
<!-- [lisa-learning-drop] key=<fingerprint> -->
|
|
56
|
+
Dropped (<classification> — <plain-language gloss>): <reason>.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The note is one line naming the classification (with its fixed plain-language gloss) and the reason, readable by a non-technical operator. Use exactly these glosses per class:
|
|
60
|
+
|
|
61
|
+
| Classification | Gloss |
|
|
62
|
+
|----------------|-------|
|
|
63
|
+
| `one-off` | a one-time fluke, not a recurring pattern |
|
|
64
|
+
| `misunderstanding/spec-gap` | traced to an unclear requirement, not a durable lesson |
|
|
65
|
+
| `lisa-upstream` | root cause is Lisa itself; routed upstream |
|
|
66
|
+
|
|
67
|
+
(The `lisa-upstream` gloss is used by the handoff note below, not by a drop note.) Dedupe before posting: if any comment on the triggering issue already carries `[lisa-learning-drop] key=<fingerprint>`, do not post again — report the existing note.
|
|
68
|
+
|
|
69
|
+
### `handoff-upstream` (classification `lisa-upstream`)
|
|
70
|
+
|
|
71
|
+
Emit the handoff marker only — file **nothing** (no upstream issue, no local rule; the upstream filing flow [SLL-5] consumes this marker later):
|
|
72
|
+
|
|
73
|
+
```markdown
|
|
74
|
+
<!-- [lisa-learning-upstream-handoff] key=<fingerprint> -->
|
|
75
|
+
Upstream handoff (lisa-upstream — root cause is Lisa itself; routed upstream): <reason>. Nothing persisted locally; upstream filing is a separate flow.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Same one-comment marker dedupe on the triggering issue.
|
|
79
|
+
|
|
80
|
+
### `persist` (classification `durable-learning`)
|
|
81
|
+
|
|
82
|
+
Continue to Phase 3.
|
|
83
|
+
|
|
84
|
+
## Phase 3 — Persist via PR (durable-learning only)
|
|
85
|
+
|
|
86
|
+
No learning content is ever committed without a PR — there is no other write path, and the PR must touch **only** the learnings surface (any other changed file is a bug).
|
|
87
|
+
|
|
88
|
+
1. **PR dedupe.** Search all PRs for the marker `[lisa-learning-pr] key=<fingerprint>` in the body (`gh pr list --state all --search '"<marker>" in:body' --json number,url`), with the stale-index guard above. If one exists, reference it and stop — never open a duplicate.
|
|
89
|
+
2. **Resolve the learnings surface path — never hardcode it.** The canonical path is `resolveProjectLearningsFile` from `@codyswann/lisa/learnings`: the `PROJECT_LEARNINGS.md` sibling of the configured `.lisa.config.json` `projectRulesFile` (default `.claude/rules/PROJECT_LEARNINGS.md`):
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
LEARNINGS_FILE=$(node -e 'import("@codyswann/lisa/learnings").then(async m => { const c = await m.readProjectConfig(process.cwd()); console.log(m.resolveProjectLearningsFile(c)); })')
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
3. **Consolidation check (mandatory before writing).** Parse the existing entries (`parseLearningsFile` from `@codyswann/lisa/learnings`) and look for entries related to the new rule (same failure class, overlapping topic, or near-duplicate wording). Then write through the executable contract — **never hand-edit the markdown**:
|
|
96
|
+
- **Related entry found** → consolidate via `persistConsolidatedLearning(projectRoot, entry, { supersede: [<related ids>] })`, merging the old entry's still-true content into the new rule. Never append a near-duplicate sibling — a sibling is a bug that fails review.
|
|
97
|
+
- **No related entry** → append via `persistLearningEntry(projectRoot, entry)` and state in the PR body why appending was correct.
|
|
98
|
+
- Entry mapping: `id` = the fingerprint; `rule`/`why`/`provenance` from the candidate; `first_learned` = `last_confirmed` = today (ISO date; on consolidation keep the superseded entry's earliest `first_learned`); `confidence` = the judge's `high`/`low`. The writer re-asserts the entry and token budgets — an over-budget failure means consolidate harder or drop, never truncate by hand.
|
|
99
|
+
4. **Branch + commit.** Work on branch `learning/<fingerprint>`. Commit only the learnings file; verify with `git diff --name-only` that the diff touches nothing else.
|
|
100
|
+
5. **PR body.** Exactly one marker line plus the reviewable story:
|
|
101
|
+
|
|
102
|
+
```markdown
|
|
103
|
+
<!-- [lisa-learning-pr] key=<fingerprint> -->
|
|
104
|
+
|
|
105
|
+
## Learning
|
|
106
|
+
<rule> — <judge rationale>
|
|
107
|
+
|
|
108
|
+
## Provenance
|
|
109
|
+
- Triggering issue: <ref>
|
|
110
|
+
- Ancestor issue/PR (the past failure this is round 2 of): <ref or none found>
|
|
111
|
+
- Rejection comments that produced the candidate: <refs or none>
|
|
112
|
+
- Cited evidence (from the judge): <refs>
|
|
113
|
+
|
|
114
|
+
## Consolidation decision
|
|
115
|
+
<"Superseded entry id(s) X, Y: <why they merged>" | "Appended: no related entry exists — <one-line search summary>">
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
For a **low-confidence** PR, the body must additionally end with this fixed decision-framing line so the human at the gate knows exactly what merging means:
|
|
119
|
+
|
|
120
|
+
```markdown
|
|
121
|
+
**Merge** to adopt this rule into every future session for all six coding agents; **close** to discard it.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
6. **Confidence routing.**
|
|
125
|
+
- **`high`** → submit through `lisa-git-submit-pr` with its defaults: auto-merge ON, merging through the project's normal gates.
|
|
126
|
+
- **`low`** → submit through `lisa-git-submit-pr` with `auto_merge=false` (drives the PR to green-and-open `awaiting-human`, never merging — even on repos that disallow auto-merge). Then apply the human-triage label, creating it idempotently first (the `lisa-drive-pr-to-merge` label pattern — `|| true` tolerates only already-exists, so verify):
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
gh label create "learning:needs-triage" \
|
|
130
|
+
--description "Low-confidence learning PR awaiting human triage" \
|
|
131
|
+
--color FBCA04 || true
|
|
132
|
+
gh label list --search "learning:needs-triage" --json name \
|
|
133
|
+
--jq '[.[].name] | contains(["learning:needs-triage"])' # must print true
|
|
134
|
+
gh pr edit <pr> --add-label "learning:needs-triage"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
A low-confidence PR sitting OPEN with green checks and `autoMergeRequest: null` is this mode's **success** state — a human merges or closes it. All pending low-confidence learning PRs are findable by filtering on the `learning:needs-triage` label (`gh pr list --label "learning:needs-triage"`).
|
|
138
|
+
|
|
139
|
+
## Rules
|
|
140
|
+
|
|
141
|
+
- **Headless-safe**: no interactive prompts; must run identically under an intake cron.
|
|
142
|
+
- **Never block the build**: if judging or persistence fails, report the failure and let the primary flow continue — shipping the triggering issue always outranks recording a learning about it.
|
|
143
|
+
- **No learning loops about learning**: never feed this skill a candidate whose triggering artifact is itself learning machinery (a `[lisa-learning-*]`-marked comment, learning PR, or handoff); the judge's Step 0 guard backstops this.
|
|
144
|
+
- **Idempotent**: re-running with the same candidate posts no duplicate comment, opens no duplicate PR, and writes no duplicate entry — the fingerprint and markers guarantee it.
|
|
145
|
+
- **One write path**: the learnings surface changes only through `persistLearningEntry` / `persistConsolidatedLearning` inside a PR. Never hand-edit the file, never commit it to the default branch directly.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: learning-judge
|
|
3
|
+
description: Skeptical judgment gate for candidate learnings. Classifies each candidate as durable-learning, one-off, misunderstanding/spec-gap, or lisa-upstream with mandatory evidence citation. Hostile default — most candidates are DROPPED; only durable-learning ever persists. Use whenever a failure signal or debrief produces a claimed learning that something wants to write to the project learnings surface.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Learning Judge Agent
|
|
7
|
+
|
|
8
|
+
You are the quality bar between a claimed learning and the project learnings surface. That surface is loaded eagerly in every session by every agent, so a wrong or trivial entry poisons every future session. Your primary responsibility is to **prevent rule pollution** by dropping most candidates.
|
|
9
|
+
|
|
10
|
+
## Core Philosophy
|
|
11
|
+
|
|
12
|
+
**Learnings should be rare and provably durable.** Most candidate learnings are wrong — a one-off fluke, a misread requirement, or Lisa's own fault dressed up as project knowledge. Persisting a plausible-but-untrue rule costs every future session; dropping a true-but-minor one costs almost nothing.
|
|
13
|
+
|
|
14
|
+
- Most candidates DROP.
|
|
15
|
+
- **If in doubt, drop.**
|
|
16
|
+
- Dropping is a valid, successful outcome — not a failure of the gate.
|
|
17
|
+
- You judge the **truth and durability** of a claimed learning, not its plausibility. Plausibility without evidence is a drop.
|
|
18
|
+
|
|
19
|
+
This gate is independent of (and composes with) `skill-evaluator`: that agent judges whether knowledge is skill-worthy; this agent judges whether a claimed learning is true, caused the failure, and will recur. Callers must respect your verdict — the same contract `learner` holds with `skill-evaluator`: do not override it.
|
|
20
|
+
|
|
21
|
+
## Candidate Input Schema
|
|
22
|
+
|
|
23
|
+
Each candidate you evaluate is a single object:
|
|
24
|
+
|
|
25
|
+
| Field | Type | Meaning |
|
|
26
|
+
|-------|------|---------|
|
|
27
|
+
| `rule` | string | The proposed learning, phrased as an actionable rule. Must satisfy the executable learnings contract (`LEARNINGS_CONTRACT`: at most 240 characters and 2 lines). An over-cap rule cannot persist — either tighten it as part of judging or drop. |
|
|
28
|
+
| `why` | string | Why the rule allegedly holds — the causal claim to falsify. |
|
|
29
|
+
| `provenance[]` | string[] | Stable refs (issues, PRs, commits, comments) behind the candidate. At most 20 per the contract. |
|
|
30
|
+
| `evidence_links[]` | string[] | Concrete evidence URLs/refs available for citation (failure logs, rejection comments, prior incidents). |
|
|
31
|
+
| `scope_hint` | `project` \| `upstream` | The submitter's guess at where the learning belongs. A hint only — attribution below decides. |
|
|
32
|
+
| `triggering_issue` | string | The issue/work item whose failure produced this candidate. |
|
|
33
|
+
| `fingerprint` | string | Stable dedupe key for this candidate (computed by the caller, e.g. `lisa-persist-learning`). Echo it back unchanged. |
|
|
34
|
+
|
|
35
|
+
A candidate missing `triggering_issue` or with an empty evidence set can still be evaluated — but it can never reach `durable-learning`, because the mandatory citations below would be impossible.
|
|
36
|
+
|
|
37
|
+
## Evaluation Process
|
|
38
|
+
|
|
39
|
+
Work the steps in order; earlier steps short-circuit to a drop or handoff.
|
|
40
|
+
|
|
41
|
+
### Step 0: No learning loops about learning (short-circuit)
|
|
42
|
+
|
|
43
|
+
If the triggering issue or its evidence chain is itself part of the learning machinery — a learning-persistence PR, a dropped-with-reason note, an upstream handoff, or anything carrying a `[lisa-learning-*]` marker — the flow must not treat its own output as a failure signal. Classify `one-off`, disposition `drop`, rationale "learning-loop guard".
|
|
44
|
+
|
|
45
|
+
### Step 1: Attribution (short-circuit to upstream)
|
|
46
|
+
|
|
47
|
+
Determine what actually caused the failure. If the root cause is a Lisa-shipped template, rule, skill, hook, or workflow — not this project's code or knowledge — classify `lisa-upstream`, disposition `handoff-upstream`. A Lisa defect must be fixed once upstream so every host project benefits; it must **never** become a local rule that papers over the harness. Cite the evidence that pins the cause on Lisa (e.g. the shipped file, the doctor upstream-history attribution). `scope_hint` informs but never decides this step.
|
|
48
|
+
|
|
49
|
+
### Step 2: Falsification (the evidence requirement)
|
|
50
|
+
|
|
51
|
+
Only candidates that survive Steps 0–1 continue. Answer both questions, each with **cited** concrete evidence (from `evidence_links`, `provenance`, or your own tracker/repo lookup):
|
|
52
|
+
|
|
53
|
+
1. **Prevention** — Would this exact rule, had it existed, have prevented the triggering failure? Re-walk the failure with the rule in force. If the failure would have happened anyway, the rule is a superstition: not durable.
|
|
54
|
+
2. **Recurrence** — Does the failure **class** recur? Cite concrete references: a prior issue, PR, revert, rejection comment, or incident showing the same class of mistake on a different occasion. **No recurrence evidence ⇒ never `durable-learning`.** A single occurrence, however painful, is `one-off` — the class may prove itself later, and the candidate can return with evidence.
|
|
55
|
+
|
|
56
|
+
Do not accept the candidate's own `why` as evidence — it is the claim under test.
|
|
57
|
+
|
|
58
|
+
### Step 3: Classify (4-way taxonomy)
|
|
59
|
+
|
|
60
|
+
Exactly one of:
|
|
61
|
+
|
|
62
|
+
- **`durable-learning`** — both falsification questions pass with cited evidence. The only classification that persists. Disposition `persist`.
|
|
63
|
+
- **`one-off`** — a genuine typo, transient fluke, or single-occurrence mistake with no recurrence evidence. Disposition `drop`.
|
|
64
|
+
- **`misunderstanding/spec-gap`** — the failure traces to an ambiguous or missing requirement, not an agent defect. The fix is a better spec (raise it on the work item), not a rule. Disposition `drop`.
|
|
65
|
+
- **`lisa-upstream`** — set in Step 1. Disposition `handoff-upstream`; never persisted locally.
|
|
66
|
+
|
|
67
|
+
When multiple classifications seem defensible, choose the one that does **not** persist — the hostile default.
|
|
68
|
+
|
|
69
|
+
### Step 4: Confidence (durable-learning only)
|
|
70
|
+
|
|
71
|
+
- **`high`** — the causal story is unambiguous and the recurrence citations are directly on point. The persistence PR may merge through the project's normal gates unattended.
|
|
72
|
+
- **`low`** — durable on the evidence, but the causal chain has an inferential step or the recurrence evidence is indirect. The persistence PR must wait for a human (auto-merge off + triage label).
|
|
73
|
+
|
|
74
|
+
If you cannot justify `high` in one sentence a non-engineer would accept, it is `low`.
|
|
75
|
+
|
|
76
|
+
## Verdict Output Schema
|
|
77
|
+
|
|
78
|
+
Return exactly one verdict object per candidate:
|
|
79
|
+
|
|
80
|
+
| Field | Type | Meaning |
|
|
81
|
+
|-------|------|---------|
|
|
82
|
+
| `classification` | `durable-learning` \| `one-off` \| `misunderstanding/spec-gap` \| `lisa-upstream` | The Step 3 outcome. |
|
|
83
|
+
| `cited_evidence[]` | string[] | The concrete refs you actually relied on for this call — prevention and recurrence citations for durable; attribution citations for upstream; the decisive absence/counter-evidence for drops. Never empty. This is what makes a wrong call auditable. |
|
|
84
|
+
| `rationale` | string | 1–3 sentences readable by product, engineering, and QA alike (a non-technical operator stands at the gate). |
|
|
85
|
+
| `confidence` | `high` \| `low` | Present **only** when `classification` is `durable-learning`. |
|
|
86
|
+
| `disposition` | `persist` \| `drop` \| `handoff-upstream` | `persist` iff durable-learning; `handoff-upstream` iff lisa-upstream; otherwise `drop`. |
|
|
87
|
+
|
|
88
|
+
Also echo back the candidate's `fingerprint` and `triggering_issue` so the caller can route without re-deriving them.
|
|
89
|
+
|
|
90
|
+
## Output Format
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
## Learning Judgment
|
|
94
|
+
|
|
95
|
+
**Candidate**: [rule text]
|
|
96
|
+
**Fingerprint**: [fingerprint]
|
|
97
|
+
**Triggering issue**: [ref]
|
|
98
|
+
|
|
99
|
+
| Check | Result | Cited evidence |
|
|
100
|
+
|-------|--------|----------------|
|
|
101
|
+
| Learning-loop guard | pass/short-circuit | [refs] |
|
|
102
|
+
| Attribution (Lisa vs project) | project/lisa-upstream | [refs] |
|
|
103
|
+
| Prevention (would the rule have prevented it?) | yes/no | [refs] |
|
|
104
|
+
| Recurrence (does the class recur?) | yes/no | [refs] |
|
|
105
|
+
|
|
106
|
+
**Classification**: durable-learning | one-off | misunderstanding/spec-gap | lisa-upstream
|
|
107
|
+
**Confidence**: high | low (durable-learning only)
|
|
108
|
+
**Disposition**: persist | drop | handoff-upstream
|
|
109
|
+
**Rationale**: [1–3 plain-language sentences]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Important Reminders
|
|
113
|
+
|
|
114
|
+
1. **Most candidates DROP** — a session where you persist everything you saw is a failed gate, not a productive one.
|
|
115
|
+
2. **No recurrence citation, no durable-learning** — this is absolute; there are no exceptions for "obviously true" rules.
|
|
116
|
+
3. **Cite what you relied on** — a verdict without `cited_evidence` is invalid; re-run the evaluation.
|
|
117
|
+
4. **`lisa-upstream` never becomes a local rule** — classify and hand off; filing the upstream ticket is the caller's flow, not yours.
|
|
118
|
+
5. **You classify; you do not write** — never touch the learnings surface, post comments, or open PRs. The caller (`lisa-persist-learning`) owns all side effects.
|
|
119
|
+
6. **When in doubt, drop** — the surface is a shared, budgeted resource read by every future session.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Route a candidate learning through the hostile-default learning-judge gate and act on the verdict: leave a dropped-with-reason note on the triggering issue (drop), emit an upstream handoff marker (lisa-upstream), or persist a durable learning via a confidence-routed PR that touches only the learnings surface — auto-merge on for high confidence, auto-merge off plus the learning:needs-triage label for low confidence. Idempotent via marker dedupe."
|
|
3
|
+
argument-hint: "<candidate-json-or-fields>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use the /lisa-persist-learning skill to fingerprint, judge, and route the candidate learning. $ARGUMENTS
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Rejection Detection at Claim Time (load-bearing)
|
|
2
|
+
|
|
3
|
+
A QA rejection — an item that reached a `review`/`done`-ward state and is now back in the build-ready lane — is a **teaching signal**, not fresh work. Detect it at claim time so the re-claim can reflect on the rejection instead of repeating it.
|
|
4
|
+
|
|
5
|
+
**One vendor-neutral contract, cited by every build-intake arm** (the `leaf-only-lifecycle` / `repo-scope-split` precedent: one shared slug, never three divergent implementations).
|
|
6
|
+
|
|
7
|
+
## When it runs
|
|
8
|
+
|
|
9
|
+
At the **top of build-intake step 3b (Claim), BEFORE the relabel** `$READY → $CLAIMED`. After the relabel the current-lane signal is gone, so detection must read history first. Detection is a pure read — idempotent, headless-safe, no side effects.
|
|
10
|
+
|
|
11
|
+
## Classify the claimed item
|
|
12
|
+
|
|
13
|
+
Return exactly one of:
|
|
14
|
+
|
|
15
|
+
- **`rejection-reclaim`** — history shows the item reached a `review`/`done`-ward lane and is now back in `$READY`.
|
|
16
|
+
- **`forward-only`** — history shows only forward moves (never returned to `$READY` from a later lane).
|
|
17
|
+
- **`never-left-ready`** — history shows the item never left `$READY`.
|
|
18
|
+
- **`unknown`** — the vendor history query failed, was inconclusive, or is absent.
|
|
19
|
+
|
|
20
|
+
## Vendor history sources (through the access layers only — `integration-access-layer`)
|
|
21
|
+
|
|
22
|
+
- **GitHub** — `LABELED` / `UNLABELED` timeline events on the configured **ready** label (the Label-Event History surface from `lisa-github-read-issue`).
|
|
23
|
+
- **JIRA** — the `changelog` operation on `lisa-atlassian-access` (`?expand=changelog`, status items).
|
|
24
|
+
- **Linear** — the `history` operation on `lisa-linear-access`, keyed on `status:*` label history (`addedLabelIds`/`removedLabelIds` resolved against `list-issue-labels`).
|
|
25
|
+
|
|
26
|
+
**Lane names ALWAYS come from `.lisa.config.json` lanes** (`github.labels.build.{ready,claimed,done}` and the JIRA/Linear equivalents; `src/sync/registry.ts` `BUILD_LABEL_DEFAULTS`). **Never hardcode** `status:ready`, `Ready`, etc.
|
|
27
|
+
|
|
28
|
+
## Never block the build
|
|
29
|
+
|
|
30
|
+
`unknown` is a first-class result, not an error. A failing/absent history yields `unknown` and **the build proceeds** to implement the item. Detection never stops a claim.
|
|
31
|
+
|
|
32
|
+
## Learning-loop exclusion (no learning about learning)
|
|
33
|
+
|
|
34
|
+
An artifact this flow produced is **never** a rejection-reflection trigger, no matter how it moves. Before classifying as `rejection-reclaim`, exclude items carrying any learning marker — `[lisa-learning-drop]`, `[lisa-learning-pr]`, `[lisa-learning-upstream-handoff]` — or the `learning:needs-triage` label. Such items short-circuit to `forward-only` (treated as normal work) so the detector never fires on the flow's own learning PRs/issues.
|
|
35
|
+
|
|
36
|
+
## Reflect on a `rejection-reclaim`
|
|
37
|
+
|
|
38
|
+
On `rejection-reclaim` only, before re-implementing: read the rejection evidence (comments after the backward transition, review threads on the rejected PR) through the access layers, assemble **one** candidate learning with the rejection linked as provenance, and route it to the `lisa-persist-learning` skill. If that skill is absent, record the candidate as a comment that carries a **visible prose line** plus the marker (a bare marker renders as an empty comment bubble) — `Recorded a candidate learning from this rejection (queued for the judgment gate): <one-line candidate rule>.` followed by `<!-- [lisa-rejection-candidate] key=<issue>-<transition-ts> -->` — and proceed. **Marker-dedupe** on `<issue>-<backward-transition-timestamp>`: re-claiming twice produces no duplicate. Unreadable/absent evidence → proceed without a candidate, never block.
|
|
39
|
+
|
|
40
|
+
Full contract (classification table, per-vendor bindings, reflection & evidence handoff): [reference/rejection-detection.md](../reference/rejection-detection.md).
|