@codyswann/lisa 2.256.1 → 2.257.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/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-exploratory-qa/SKILL.md +12 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-learnings-audit/SKILL.md +9 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-monitor/SKILL.md +6 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-project-ideation/SKILL.md +21 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-repair-intake/SKILL.md +17 -7
- package/plugins/lisa/rules/eager/rejection-detection.md +12 -1
- package/plugins/lisa/rules/reference/observability-audit.md +5 -1
- package/plugins/lisa/rules/reference/rejection-detection.md +61 -0
- package/plugins/lisa/skills/lisa-exploratory-qa/SKILL.md +12 -4
- package/plugins/lisa/skills/lisa-learnings-audit/SKILL.md +9 -0
- package/plugins/lisa/skills/lisa-monitor/SKILL.md +6 -4
- package/plugins/lisa/skills/lisa-project-ideation/SKILL.md +21 -4
- package/plugins/lisa/skills/lisa-repair-intake/SKILL.md +17 -7
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-exploratory-qa/SKILL.md +12 -4
- package/plugins/lisa-agy/skills/lisa-learnings-audit/SKILL.md +9 -0
- package/plugins/lisa-agy/skills/lisa-monitor/SKILL.md +6 -4
- package/plugins/lisa-agy/skills/lisa-project-ideation/SKILL.md +21 -4
- package/plugins/lisa-agy/skills/lisa-repair-intake/SKILL.md +17 -7
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/rules/eager/rejection-detection.md +12 -1
- package/plugins/lisa-copilot/rules/reference/observability-audit.md +5 -1
- package/plugins/lisa-copilot/rules/reference/rejection-detection.md +61 -0
- package/plugins/lisa-copilot/skills/lisa-exploratory-qa/SKILL.md +12 -4
- package/plugins/lisa-copilot/skills/lisa-learnings-audit/SKILL.md +9 -0
- package/plugins/lisa-copilot/skills/lisa-monitor/SKILL.md +6 -4
- package/plugins/lisa-copilot/skills/lisa-project-ideation/SKILL.md +21 -4
- package/plugins/lisa-copilot/skills/lisa-repair-intake/SKILL.md +17 -7
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/rules/observability-audit-reference.mdc +5 -1
- package/plugins/lisa-cursor/rules/rejection-detection-reference.mdc +61 -0
- package/plugins/lisa-cursor/rules/rejection-detection.mdc +12 -1
- package/plugins/lisa-cursor/skills/lisa-exploratory-qa/SKILL.md +12 -4
- package/plugins/lisa-cursor/skills/lisa-learnings-audit/SKILL.md +9 -0
- package/plugins/lisa-cursor/skills/lisa-monitor/SKILL.md +6 -4
- package/plugins/lisa-cursor/skills/lisa-project-ideation/SKILL.md +21 -4
- package/plugins/lisa-cursor/skills/lisa-repair-intake/SKILL.md +17 -7
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/rules/eager/rejection-detection.md +12 -1
- package/plugins/src/base/rules/reference/observability-audit.md +5 -1
- package/plugins/src/base/rules/reference/rejection-detection.md +61 -0
- package/plugins/src/base/skills/lisa-exploratory-qa/SKILL.md +12 -4
- package/plugins/src/base/skills/lisa-learnings-audit/SKILL.md +9 -0
- package/plugins/src/base/skills/lisa-monitor/SKILL.md +6 -4
- package/plugins/src/base/skills/lisa-project-ideation/SKILL.md +21 -4
- package/plugins/src/base/skills/lisa-repair-intake/SKILL.md +17 -7
package/package.json
CHANGED
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"form-data": ">=4.0.6"
|
|
103
103
|
},
|
|
104
104
|
"name": "@codyswann/lisa",
|
|
105
|
-
"version": "2.
|
|
105
|
+
"version": "2.257.0",
|
|
106
106
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
107
107
|
"main": "dist/index.js",
|
|
108
108
|
"exports": {
|
|
@@ -74,7 +74,15 @@ Each finding is a flat leaf, so `build_ready` applies directly — pass it expli
|
|
|
74
74
|
|
|
75
75
|
### Idempotency — don't spam duplicates
|
|
76
76
|
|
|
77
|
-
Re-running a pass must not refile the same finding. Before creating a ticket, search the tracker for
|
|
77
|
+
Re-running a pass must not refile the same finding. Before creating a ticket, search the tracker for a ticket carrying a stable marker `[lisa-exploratory-qa] <finding-key>` in its body (the `<finding-key>` is a stable slug of surface + symptom, e.g. `settings-modal/horizontal-overflow@tablet`). Per the `rejection-detection` rule's **Proposal rejection memory** section, that marker search MUST cover **open AND closed** tickets (with a body-enumeration fallback on search-index lag), and **match by the marker, never by title.** Then split on how any prior ticket closed:
|
|
78
|
+
|
|
79
|
+
- **Open** ticket carrying the marker → reference/update it instead; do not create a second.
|
|
80
|
+
- **Closed as _completed_** → does **not** suppress. A recurrence after a fix is a genuine **regression**, so file the finding.
|
|
81
|
+
- **Closed as _not planned_** (GitHub `stateReason == "not_planned"`; the config-resolved won't-do/canceled equivalent on JIRA/Linear) → a human **declined** this finding, so **suppress it**. Re-file only with evidence that **postdates the decline**, carrying BOTH the machine token (`declined <date>; recurred <date> in <ref>`) and a human acknowledgment sentence (`You declined this on <date>. It has recurred (<date>, <ref>), so we're raising it once more for your review.`).
|
|
82
|
+
|
|
83
|
+
Every filed finding ticket MUST end with the `rejection-detection` **operator footer** as a visible prose line so the operator knows which close-reason silences it:
|
|
84
|
+
|
|
85
|
+
> To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.
|
|
78
86
|
|
|
79
87
|
## Output
|
|
80
88
|
|
|
@@ -94,13 +102,13 @@ and records it, so a quiet run and a broken run are never mistaken for each othe
|
|
|
94
102
|
| This cycle's exit path | Run outcome |
|
|
95
103
|
|---|---|
|
|
96
104
|
| Findings filed — one or more `Bug` / `Improvement` tickets created or referenced (§6) | `candidate-proposed` |
|
|
97
|
-
| Clean pass — explored the personas and surfaces, nothing worth filing | `nothing-needed` |
|
|
98
|
-
| Tracker unconfigured — the §1 stop path; findings cannot be filed | `recovery-required` |
|
|
105
|
+
| Clean pass — explored the personas and surfaces, nothing worth filing — **or** every candidate was suppressed by a prior decline (`rejection-detection` **Proposal rejection memory**): the summary MUST name the suppression count | `nothing-needed` |
|
|
106
|
+
| Tracker unconfigured — the §1 stop path; findings cannot be filed — **or** the open-and-closed rejection-memory marker search could not run (tracker unreachable / credentials revoked): a memory check that could not run is a broken loop, never a silent `nothing-needed` | `recovery-required` |
|
|
99
107
|
| A degradation that still let the pass explore (e.g. Kane unavailable, one persona unreachable) | the outcome it actually reached above, with the summary **leading with the degradation** — degradation never mints a seventh token |
|
|
100
108
|
|
|
101
109
|
Record **exactly one** outcome per invocation through the run-record CLI, naming this loop's runbook
|
|
102
110
|
(the `--summary` is the operator-readable one-liner in the contract's exemplar voice — plain,
|
|
103
|
-
specific, actionable, e.g. `Explored 4 personas; nothing confusing to file.` for `nothing-needed`):
|
|
111
|
+
specific, actionable, e.g. `Explored 4 personas; nothing confusing to file.` — or, when a decline suppressed the only candidates, `Explored 4 personas; 2 candidates suppressed by a prior decline — nothing new to propose.` — for `nothing-needed`; and for a `recovery-required` from an unreadable decline check, `Tracker unreachable during the decline check — restore credentials; nothing was filed this run.`):
|
|
104
112
|
|
|
105
113
|
```bash
|
|
106
114
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/automation-run-record.mjs" \
|
|
@@ -248,6 +248,15 @@ resolved by reading the ticket, never by filing a sibling blind. Then:
|
|
|
248
248
|
|
|
249
249
|
Re-runs are quiet no-ops: same surfaces + same evidence ⇒ zero new tickets.
|
|
250
250
|
|
|
251
|
+
This open-and-closed marker search plus the "declined `<date>`; recurred `<date>` in `<ref>`"
|
|
252
|
+
re-file discipline is the shipped precedent for the **Proposal rejection memory** section of the
|
|
253
|
+
`rejection-detection` rule — the shared contract every proposing loop now consults so a
|
|
254
|
+
closed-as-not-planned proposal is never re-filed. The gardener already conforms; it is cited there,
|
|
255
|
+
not re-implemented. Like every proposing loop, each gardener-filed ticket MUST carry the
|
|
256
|
+
`rejection-detection` **operator footer** as a visible prose line so the human knows which
|
|
257
|
+
close-reason silences it: `To stop this from being raised again, close it as **Not planned**. Close
|
|
258
|
+
it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.`
|
|
259
|
+
|
|
251
260
|
## Autonomous-vs-approval boundary
|
|
252
261
|
|
|
253
262
|
**Everything is human-gated at v1 — this skill only files tickets.** The
|
|
@@ -130,7 +130,8 @@ After report, file what was found — **only when run standalone**, never under
|
|
|
130
130
|
|
|
131
131
|
- **Anomalies** (live signals over the conservative bar) → `Bug` leaves. **Gaps** (in-scope MISSING rubric dimensions) → `Task`/`Improvement` leaves.
|
|
132
132
|
- Every ticket is filed via the vendor-neutral `lisa-tracker-write` shim with `build_ready: true` (never a vendor write skill directly), as a **single-repo leaf** stamped `repo:<current>`, with a real three-audience description, Gherkin AC, Target Backend Environment, and a Validation Journey + typed `[EVIDENCE: <artifact-type>: <name>]` marker (e.g. `[EVIDENCE: log-snippet: alert-cleared]`) so it passes the `tracker-validate` gates.
|
|
133
|
-
- **
|
|
133
|
+
- **Operator footer (required):** every filed ticket ends with the `rejection-detection` **operator footer** as a visible prose line — `To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.` — so the operator knows which close-reason silences the finding.
|
|
134
|
+
- **Idempotent + decline-aware:** embed the `<!-- lisa:monitor-finding: <fingerprint> -->` sentinel and follow the `observability-audit` rule's current fingerprint/idempotency contract for open-and-closed search, prior-decline suppression, recurrence evidence, completed-item regression handling, and tracker-read failure outcomes. Never duplicate a live or just-resolved finding.
|
|
134
135
|
- **Capped** at `max_candidates` (default 20), `core`/high-severity first; report how many were filed vs dropped.
|
|
135
136
|
- **`--dry-run`** previews would-file tickets and creates nothing. **`--all-gaps`** widens gap filing to `recommended` tiers.
|
|
136
137
|
|
|
@@ -149,8 +150,8 @@ so a quiet monitoring run and a broken one never look identical.
|
|
|
149
150
|
| This cycle's exit path | Run outcome |
|
|
150
151
|
|---|---|
|
|
151
152
|
| Anomalies or in-scope gaps filed — one or more `Bug` / `Task` / `Improvement` leaves created or referenced | `candidate-proposed` |
|
|
152
|
-
| Clean sweep — health/audit ran end to end, nothing over the bar and no in-scope gaps | `nothing-needed` |
|
|
153
|
-
| Provider/threshold resolution failure — threshold collection fails (a present-but-uninspectable config, an invalid configured threshold) or a signal provider is unreachable so the sweep could not run | `recovery-required` |
|
|
153
|
+
| Clean sweep — health/audit ran end to end, nothing over the bar and no in-scope gaps — **or** every finding was suppressed by a prior decline (`rejection-detection` **Proposal rejection memory**): the summary MUST name the suppression count | `nothing-needed` |
|
|
154
|
+
| Provider/threshold resolution failure — threshold collection fails (a present-but-uninspectable config, an invalid configured threshold) or a signal provider is unreachable so the sweep could not run — **or** the open-and-closed rejection-memory search could not read the tracker: a memory check that could not run is a broken loop, never a silent `nothing-needed` | `recovery-required` |
|
|
154
155
|
| A degradation that still let the sweep run (an optional `ops-specialist` overlay absent, Kane unavailable) | the outcome it actually reached above, with the summary **leading with the degradation** — degradation never mints a seventh token |
|
|
155
156
|
|
|
156
157
|
Only the **standalone** run records. The nested report-only modes do their own job and do not file or
|
|
@@ -161,7 +162,8 @@ invocations.
|
|
|
161
162
|
Record **exactly one** outcome per standalone invocation through the run-record CLI, naming this
|
|
162
163
|
loop's runbook (the `--summary` is the operator-readable one-liner in the contract's exemplar voice —
|
|
163
164
|
plain, specific, actionable, e.g. `Health green; audit clean — nothing to propose.` for
|
|
164
|
-
`nothing-needed`
|
|
165
|
+
`nothing-needed`; and for a `recovery-required` from an unreadable decline check, `Tracker
|
|
166
|
+
unreachable during the decline check — restore credentials; nothing was filed this run.`):
|
|
165
167
|
|
|
166
168
|
```bash
|
|
167
169
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/automation-run-record.mjs" \
|
|
@@ -241,10 +241,26 @@ Each created PRD carries the marker `[lisa-project-ideation] idea=<stable-key>`.
|
|
|
241
241
|
`<stable-key>` deterministically from: repo identity (configured repo or git remote + repo-root
|
|
242
242
|
basename) + a normalized slug of the idea name + the normalized persona key(s) + the existing-fit
|
|
243
243
|
anchor. **Do not** include rank, date, confidence, or the generated PRD title (they change across
|
|
244
|
-
runs). `lisa-prd-source-write` searches the source for
|
|
244
|
+
runs). `lisa-prd-source-write` searches the source for a PRD carrying this marker before
|
|
245
245
|
creating — matching by marker, never by title — so re-running ideation updates/references the
|
|
246
246
|
existing PRD rather than duplicating it.
|
|
247
247
|
|
|
248
|
+
Per the `rejection-detection` rule's **Proposal rejection memory** section, that marker search MUST
|
|
249
|
+
cover **open AND closed** PRDs (with a body-enumeration fallback on search-index lag), and a PRD
|
|
250
|
+
**closed as _not planned_** (GitHub `stateReason == "not_planned"`; the config-resolved won't-do/
|
|
251
|
+
canceled equivalent on JIRA/Linear — never a hardcoded lane string) is a **durable human decline**
|
|
252
|
+
that **suppresses** re-proposing that idea. Re-propose only with evidence that **postdates the
|
|
253
|
+
decline**, and state it in the new PRD as BOTH the machine token (`declined <date>; recurred <date>
|
|
254
|
+
in <ref>`) and a human acknowledgment sentence (`You declined this on <date>. It has recurred
|
|
255
|
+
(<date>, <ref>), so we're raising it once more for your review.`). A PRD closed as _completed_ is
|
|
256
|
+
not a decline. This is tracker-side memory; the advisory ideation memory ledger stays advisory and
|
|
257
|
+
never overrides it.
|
|
258
|
+
|
|
259
|
+
Every created PRD MUST carry the `rejection-detection` **operator footer** as a visible prose line
|
|
260
|
+
so the operator knows which close-reason silences it:
|
|
261
|
+
|
|
262
|
+
> To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.
|
|
263
|
+
|
|
248
264
|
## Step 7 — Output (no report file)
|
|
249
265
|
|
|
250
266
|
Emit two distinct in-session sections (do not write a report file):
|
|
@@ -284,9 +300,9 @@ so a quiet ideation run and a broken one are never confused.
|
|
|
284
300
|
| This run's exit path | Run outcome |
|
|
285
301
|
|---|---|
|
|
286
302
|
| PRD(s) created or reused this run (Step 6/7 **PRDs Created**) | `candidate-proposed` |
|
|
287
|
-
| Nothing to ideate — no Practical Idea cleared the bar; nothing created | `nothing-needed` |
|
|
303
|
+
| Nothing to ideate — no Practical Idea cleared the bar; nothing created — **or** every idea was suppressed by a prior decline (`rejection-detection` **Proposal rejection memory**): the summary MUST name the suppression count | `nothing-needed` |
|
|
288
304
|
| The Step 5.5 **PRD-queue-pressure gate** blocked auto-ready creation — a human must drain the queue before another auto-ready PRD is added | `approval-requested` |
|
|
289
|
-
| The loop itself could not run — the PRD source reader failed or the queue is misconfigured (a source-reader failure snapshot, not queue pressure) | `recovery-required` |
|
|
305
|
+
| The loop itself could not run — the PRD source reader failed or the queue is misconfigured (a source-reader failure snapshot, not queue pressure) — **or** the open-and-closed rejection-memory marker search could not read the source: a memory check that could not run is a broken loop, never a silent `nothing-needed` | `recovery-required` |
|
|
290
306
|
| A degradation that still let ideation run (optional Codex automation memory unavailable, an inspiration source unreachable) | the outcome it actually reached above, with the summary **leading with the degradation** — degradation never mints a seventh token |
|
|
291
307
|
|
|
292
308
|
The pressure gate is `approval-requested`, **not** `recovery-required`: the loop ran fine and
|
|
@@ -296,7 +312,8 @@ broken machine. `recovery-required` is reserved for the loop failing to run at a
|
|
|
296
312
|
Record **exactly one** outcome per invocation through the run-record CLI, naming this loop's runbook
|
|
297
313
|
(the `--summary` is the operator-readable one-liner in the contract's exemplar voice — plain,
|
|
298
314
|
specific, actionable, e.g. `Reviewed evidence; no practical idea cleared the bar — nothing to
|
|
299
|
-
propose.` for `nothing-needed`
|
|
315
|
+
propose.` for `nothing-needed`; and for a `recovery-required` from an unreadable decline check,
|
|
316
|
+
`Tracker unreachable during the decline check — restore credentials; nothing was filed this run.`):
|
|
300
317
|
|
|
301
318
|
```bash
|
|
302
319
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/automation-run-record.mjs" \
|
|
@@ -456,7 +456,9 @@ re-issued against an unchanged conflicting head.
|
|
|
456
456
|
follow-up, `build_ready: true` so it auto-builds. The ticket MUST name: the blocked item + its
|
|
457
457
|
PR/deploy URL, the exact blocker (conflict / which checks failed with their logs link / which
|
|
458
458
|
change requests / which deploy run), three-audience description, and Gherkin acceptance criteria
|
|
459
|
-
for "PR is mergeable / deploy is green."
|
|
459
|
+
for "PR is mergeable / deploy is green." Every created blocker fix ticket MUST end with the
|
|
460
|
+
`rejection-detection` **operator footer** as a visible prose line:
|
|
461
|
+
`To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.`
|
|
460
462
|
2. **Transition the stalled item `claimed → blocked`** and add an **`is blocked by`** link to the
|
|
461
463
|
new fix ticket (vendor-native: JIRA issue link `is blocked by`; GitHub/Linear `Blocked by:` line
|
|
462
464
|
+ label). Post a `[lisa-repair-intake]` note naming what it is blocked by and why. This block is
|
|
@@ -470,11 +472,19 @@ The item now sits in `blocked`; once the fix ticket reaches a terminal state, th
|
|
|
470
472
|
`blocked` → unblock if cleared** path (next section) detects the cleared `is blocked by`
|
|
471
473
|
dependency and resumes the original in place — a self-healing loop.
|
|
472
474
|
|
|
473
|
-
**Idempotency.** Before filing, check for
|
|
475
|
+
**Idempotency.** Before filing, check for a fix ticket already carrying the marker
|
|
474
476
|
`[lisa-repair-intake] blocker:<item-ref>/<blocker-key>` (blocker-key is a stable slug of the
|
|
475
|
-
blocker, e.g. `pr-1234/merge-conflict` or `pr-1234/checks-failing`).
|
|
476
|
-
|
|
477
|
-
|
|
477
|
+
blocker, e.g. `pr-1234/merge-conflict` or `pr-1234/checks-failing`). Per the `rejection-detection`
|
|
478
|
+
rule's **Proposal rejection memory** section, that marker search MUST cover **open AND closed**
|
|
479
|
+
tickets (body-enumeration fallback on search-index lag): an **open** match → reference it and ensure
|
|
480
|
+
the `is blocked by` link is present rather than creating a duplicate; a match **closed as _not
|
|
481
|
+
planned_** (GitHub `stateReason == "not_planned"`; the config-resolved equivalent on JIRA/Linear) is
|
|
482
|
+
a **human decline** of that fix ticket — do **not** re-file it unless evidence **postdates the
|
|
483
|
+
decline**, and the re-filed ticket MUST carry BOTH the machine token (`declined <date>; recurred
|
|
484
|
+
<date> in <ref>`) and the human acknowledgment sentence (`You declined this on <date>. It has
|
|
485
|
+
recurred (<date>, <ref>), so we're raising it once more for your review.`); a match closed as
|
|
486
|
+
_completed_ is a regression path, not a decline. Honor the backoff window and state fingerprint
|
|
487
|
+
(Loop prevention) so re-runs over the same unchanged blocker are no-ops.
|
|
478
488
|
|
|
479
489
|
### Build `blocked` → re-evaluate, unblock if cleared
|
|
480
490
|
|
|
@@ -985,11 +995,11 @@ to each item* — the two never merge in the one-line summary.
|
|
|
985
995
|
|
|
986
996
|
| This cycle's exit path | Run outcome |
|
|
987
997
|
|---|---|
|
|
988
|
-
| Nothing actionable — the idle case (walk step 5): examined N, all active or in backoff | `nothing-needed` |
|
|
998
|
+
| Nothing actionable — the idle case (walk step 5): examined N, all active or in backoff — including a fix ticket suppressed by a prior decline (`rejection-detection` **Proposal rejection memory**), which the summary names in its suppression count | `nothing-needed` |
|
|
989
999
|
| Repairs applied **and confirmed** this cycle — `resumed` / `resynced` / `recovered` / `unblocked` / `closed_out` / `rolled_up` / `relinked` / `normalized_ready` | `change-proved` |
|
|
990
1000
|
| Repair produced new work for a human to pick up — e.g. an unmergeable PR or failed deploy filed as a **build-ready fix ticket** and left `blocked` | `candidate-proposed` |
|
|
991
1001
|
| A repair reached an autonomy boundary needing a human (a protected-deploy approval before it can proceed) | `approval-requested` |
|
|
992
|
-
| The loop itself could not run — the queue is unreadable
|
|
1002
|
+
| The loop itself could not run — the queue is unreadable, tracker credentials are revoked, or an open-and-closed rejection-memory / blocker-marker search is unreadable and therefore must not fall through to `nothing-needed` | `recovery-required` |
|
|
993
1003
|
|
|
994
1004
|
Record **exactly one** outcome per invocation through the run-record CLI, naming this loop's runbook
|
|
995
1005
|
(the `--summary` is the operator-readable one-liner in the contract's exemplar voice — plain,
|
|
@@ -37,4 +37,15 @@ An artifact this flow produced is **never** a rejection-reflection trigger, no m
|
|
|
37
37
|
|
|
38
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
39
|
|
|
40
|
-
|
|
40
|
+
## Proposal rejection memory (proposal-side — orthogonal to the reclaim path above)
|
|
41
|
+
|
|
42
|
+
A **loop-proposed** item that a human **closed as _not planned_** is a durable decline. Every *proposing* loop (`lisa-exploratory-qa`, `lisa-project-ideation`, `lisa-monitor`, `lisa-repair-intake`; `lisa-learnings-audit` is the shipped precedent) MUST consult it **before filing a candidate**. This is orthogonal to the `rejection-reclaim` path above — that classification and its reflection are untouched. The memory lives in the **tracker, not a new state file**.
|
|
43
|
+
|
|
44
|
+
- **Signal:** the prior item is closed *not planned* (GitHub `stateReason == "not_planned"`; the JIRA/Linear equivalent resolved via `config-resolution` — never hardcode). The GitHub compare is **case-insensitive** — raw `gh --json stateReason` returns `NOT_PLANNED` UPPERCASE, so normalize to lowercase (or read via `lisa-github-read-issue`) before comparing, never string-match raw `gh` JSON. Closed as **completed** is NOT a decline — a recurrence after a completed fix is a regression and may file.
|
|
45
|
+
- **Marker:** `<!-- [<loop-marker>] key=<candidate-key> -->`, key computed deterministically `printf '%s' "$normalized" | shasum -a 256 | cut -c1-12`. Search **open AND closed** items for the marker before filing (body-enumeration fallback on search-index lag); **match on the marker, never the title.**
|
|
46
|
+
- **Rule:** a hit on a closed-**not-planned** item **suppresses** the proposal. Re-file **only** with evidence postdating the decline, carrying BOTH the machine token (`declined <date>; recurred <date> in <ref>`) and a human acknowledgment sentence (`You declined this on <date>. It has recurred (<date>, <ref>), so we're raising it once more for your review.`).
|
|
47
|
+
- **Operator footer:** every loop-filed proposal ticket MUST carry, as visible prose, `To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.` — so the operator knows which close-reason produces which outcome.
|
|
48
|
+
- **Outcomes:** an all-suppressed cycle ends `nothing-needed` naming the **suppression count**; a memory check that cannot read the tracker ends `recovery-required`, never a silent `nothing-needed` (e.g. `Tracker unreachable during the decline check — restore credentials; nothing was filed this run.`).
|
|
49
|
+
- **Concurrency:** search-then-write = convergence, not mutual exclusion — a transient duplicate is closed by the next run; no cross-run lock.
|
|
50
|
+
|
|
51
|
+
Full contract (classification table, per-vendor bindings, reflection & evidence handoff, Proposal rejection memory): [reference/rejection-detection.md](../reference/rejection-detection.md).
|
|
@@ -94,7 +94,11 @@ A manual re-run next week must not duplicate last week's tickets. Mirror the `re
|
|
|
94
94
|
- Anomaly: `sha1("<source>:<stable-signature>:<CURRENT_REPO>")` (12 hex chars). `stable-signature` is the Sentry issue short-id (or culprit), the CloudWatch alarm name, or `errorType@location` — something that survives between runs, **never** the human title or occurrence count.
|
|
95
95
|
- Gap: the literal `gap:<dimension>:<CURRENT_REPO>` (a dimension is missing-or-not; no hash needed).
|
|
96
96
|
2. **Sentinel in the body.** Embed `<!-- lisa:monitor-finding: <fingerprint> -->` in every filed ticket.
|
|
97
|
-
3. **Search before create** (this IS gate S13). Before filing, search the tracker for the fingerprint string. The search **MUST include closed/resolved tickets** (`gh issue list --search <fp> --state all` for GitHub; JQL with no status filter for JIRA; all states for Linear) — otherwise a just-closed match is invisible and the backoff below can't fire. If an **open** ticket carries the fingerprint → skip (optionally link).
|
|
97
|
+
3. **Search before create** (this IS gate S13). Before filing, search the tracker for the fingerprint string. The search **MUST include closed/resolved tickets** (`gh issue list --search <fp> --state all` for GitHub; JQL with no status filter for JIRA; all states for Linear) — otherwise a just-closed match is invisible and the backoff and decline checks below can't fire. If an **open** ticket carries the fingerprint → skip (optionally link). Otherwise classify a closed match by **how** it closed:
|
|
98
|
+
- **Closed as _not planned_ — a permanent decline** (`rejection-detection` **Proposal rejection memory**). GitHub `stateReason == "not_planned"`; the config-resolved won't-do/canceled equivalent on JIRA/Linear (never a hardcoded lane string). This is a durable human "no", not a time-boxed guard: **suppress the finding regardless of age**, and re-file only with evidence that **postdates the decline**. The new ticket MUST carry BOTH the machine token (`declined <date>; recurred <date> in <ref>`) and the human acknowledgment sentence (`You declined this on <date>. It has recurred (<date>, <ref>), so we're raising it once more for your review.`). This layers *on top of* the backoff below — a not_planned close outlives any window.
|
|
99
|
+
- **Closed as _completed_ within the recently-resolved backoff window** (`monitor.backoffHours`, default **24h** — matching the 24h Sentry event window; this is **not** the 2h `intake.repair.staleAfterHours`) → skip, to avoid re-filing a just-fixed regression before its signal has drained. A completed close **older** than the window is not a decline — a genuine recurrence is a regression and may file.
|
|
100
|
+
|
|
101
|
+
Only file when no live match, no recently-resolved completed match, and no prior not_planned decline exists.
|
|
98
102
|
|
|
99
103
|
## The cap
|
|
100
104
|
|
|
@@ -101,3 +101,64 @@ Mining a learning is not enough — if the agent then rebuilds the same rejected
|
|
|
101
101
|
- **The evidence summary** names: what was rejected, why (the defect the QA comment named), and the specific approach the rejection named as wrong.
|
|
102
102
|
- **The plan must reckon with it.** On a `rejection-reclaim`, the re-implementation plan MUST explicitly address the rejection evidence and MUST NOT re-propose the specific approach the rejection named as wrong.
|
|
103
103
|
- **Absence never blocks.** If the rejection evidence is unreadable or absent, the agent still implements the item — degraded, not stopped.
|
|
104
|
+
|
|
105
|
+
## Proposal rejection memory (a loop-proposed item closed as *not planned*)
|
|
106
|
+
|
|
107
|
+
Everything above is **claim-time** memory — a work item that was bounced *backward* through the lanes. This section is its orthogonal twin: **proposal-time** memory — an item a **loop itself proposed** that a human then **closed as _not planned_**. The two never touch. The `rejection-reclaim` classification, its reflection path, and its evidence handoff above are **unchanged**; this section adds a second, independent contract that every *proposing* loop consults **before it files a candidate**.
|
|
108
|
+
|
|
109
|
+
The most corrosive failure of an unattended fleet is nagging: a human declines a proposal and the loop cheerfully files it again next cycle, until operators stop reading the tracker. The cure is to make a decline durable. The memory lives in the **tracker, not a new state file** — a closed-as-not-planned proposal *is* the record of the human's "no", matched by a stable marker key. This generalizes `lisa-learnings-audit`'s already-shipped idempotency discipline to every proposing loop: `lisa-exploratory-qa`, `lisa-project-ideation`, `lisa-monitor`, and `lisa-repair-intake` cite this section; the gardener (`lisa-learnings-audit`) is the shipped precedent it was modeled on, not a second implementation.
|
|
110
|
+
|
|
111
|
+
### Rejection signal — "not planned", resolved from config
|
|
112
|
+
|
|
113
|
+
A proposal is **declined** when its prior item is **closed as _not planned_**:
|
|
114
|
+
|
|
115
|
+
- **GitHub** — `stateReason == "not_planned"` (surfaced by `lisa-github-read-issue`, which renders `State: closed (not_planned)`). **The comparison is case-insensitive:** raw `gh issue view --json stateReason` returns the value UPPERCASE (`NOT_PLANNED` / `COMPLETED`), so a literal string-compare against `not_planned` on raw `gh` output would false-negative a genuine decline. Normalize the value to lowercase before comparing, or consume it through `lisa-github-read-issue`'s already-lowercased rendering — never string-match raw `gh` JSON directly.
|
|
116
|
+
- **JIRA / Linear** — the configured *not-planned* equivalent resolved per the `config-resolution` rule (a JIRA won't-do resolution, a Linear canceled state), **never a hardcoded lane string** — exactly as the claim-time lane names above always come from `.lisa.config.json`, never a literal.
|
|
117
|
+
|
|
118
|
+
**Closed as _completed_ is NOT a decline.** A completed fix whose underlying problem later recurs is a **regression**, and the loop **may file** it — the recurrence is genuinely new work, not a re-proposal of the declined thing. Only *not planned* is the durable "no".
|
|
119
|
+
|
|
120
|
+
### Marker discipline — deterministic key, open AND closed search
|
|
121
|
+
|
|
122
|
+
Every loop-proposed item carries a **visible prose line** plus a marker (a bare HTML marker renders as an empty comment bubble):
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
<!-- [<loop-marker>] key=<candidate-key> -->
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`<loop-marker>` is the proposing loop's own marker (`[lisa-exploratory-qa]`, `[lisa-project-ideation]`, `lisa:monitor-finding`, `[lisa-repair-intake]`, `[lisa-gardener]`). `<candidate-key>` is computed **deterministically — never estimated by the model** (the gardener's exact formula):
|
|
129
|
+
|
|
130
|
+
1. **Normalize** the candidate's stable identity (surface + symptom, idea key, or finding signature): trim, collapse every internal whitespace run to a single space, lowercase.
|
|
131
|
+
2. **Hash** in Bash: `printf '%s' "$normalized" | shasum -a 256 | cut -c1-12`.
|
|
132
|
+
|
|
133
|
+
The same candidate therefore always produces the same key across runs, regardless of which session computes it. **Before filing anything, every loop searches the tracker for that marker across open AND closed items** — keyed on the stable prefix first, the hash as disambiguation only — plus a **body-enumeration fallback for search-index lag** (`gh issue list --state all --json number,body` and grep the bodies; `gh search issues` already covers open and closed by default — `--state all` is NOT a valid `gh search` flag). **Match on the marker, never the title.**
|
|
134
|
+
|
|
135
|
+
### Operator footer — teach the close-reason where the operator reads it
|
|
136
|
+
|
|
137
|
+
The whole mechanism hinges on a distinction the operator cannot see unless we say it: closing a proposal **Not planned** silences it for good, while closing it **Completed** leaves the door open to a regression re-file. So every loop-filed proposal ticket MUST carry this footer verbatim as its own visible prose line (not only the HTML marker), so the human at the gate knows exactly which close-reason produces which outcome:
|
|
138
|
+
|
|
139
|
+
> To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.
|
|
140
|
+
|
|
141
|
+
This footer is **required on every proposing loop's filed ticket** (`lisa-exploratory-qa`, `lisa-monitor`, `lisa-project-ideation`, `lisa-learnings-audit`, and any other loop that files a proposal). It is what makes the tracker-as-memory legible to a non-technical operator — the close-button becomes a documented control, not a guess.
|
|
142
|
+
|
|
143
|
+
### The rule
|
|
144
|
+
|
|
145
|
+
A marker hit on a **closed-as-not-planned** item **suppresses the proposal** — regardless of title similarity. Re-proposal is permitted **only** with evidence that **postdates the decline**, and the re-filed item MUST carry BOTH of the following:
|
|
146
|
+
|
|
147
|
+
1. **A machine token** for the dedupe/audit trail — generalizing the gardener's wording:
|
|
148
|
+
|
|
149
|
+
> declined `<decline-date>`; recurred `<recurrence-date>` in `<recurrence-ref>`
|
|
150
|
+
|
|
151
|
+
2. **A human acknowledgment sentence** in plain prose, so re-raising reads as a respectful return to an answered question rather than a machine nagging:
|
|
152
|
+
|
|
153
|
+
> You declined this on `<decline-date>`. It has recurred (`<recurrence-date>`, `<recurrence-ref>`), so we're raising it once more for your review.
|
|
154
|
+
|
|
155
|
+
Naming the decline date, the recurrence date, and the recurrence reference in both forms is what turns "raising it again" into an auditable, evidence-backed, operator-legible act rather than nagging. A marker hit on a closed-as-**completed** item is not a decline — it falls under the regression path above.
|
|
156
|
+
|
|
157
|
+
### Interaction with run outcomes
|
|
158
|
+
|
|
159
|
+
- **Every candidate suppressed** — a cycle whose only candidates were each suppressed by a prior decline terminates **`nothing-needed`**, and its one-line summary **names the suppression count** (e.g. `Explored 4 personas; 2 candidates suppressed by a prior decline — nothing new to propose.`). Respecting a decline is thereby *visible* in the run record — never indistinguishable from finding nothing.
|
|
160
|
+
- **Tracker unreadable during the memory check** — if the open-and-closed marker search cannot run (tracker unreachable, credentials revoked, the substrate broken), the loop terminates **`recovery-required`**, **never** a silent `nothing-needed`. A memory check that could not run is a broken loop, not a quiet one — this is the exact failure mode the contract exists to exclude, so "I saw no candidates" must never be conflated with "I could not look". The one-line summary names the broken check and the fix in operator language, e.g. `Tracker unreachable during the decline check — restore credentials; nothing was filed this run.`
|
|
161
|
+
|
|
162
|
+
### Concurrency honesty
|
|
163
|
+
|
|
164
|
+
The search-then-write here is **not** an atomic claim: two truly concurrent runs can each miss the other's in-flight proposal and file a transient duplicate. It therefore guarantees **convergence, not mutual exclusion** — the duplicate is found and closed by the next run's marker search, and because the key is a stable content hash, a duplicate never multiplies. State this rather than implying a lock; a cross-run locking protocol would be disproportionate to the risk.
|
|
@@ -74,7 +74,15 @@ Each finding is a flat leaf, so `build_ready` applies directly — pass it expli
|
|
|
74
74
|
|
|
75
75
|
### Idempotency — don't spam duplicates
|
|
76
76
|
|
|
77
|
-
Re-running a pass must not refile the same finding. Before creating a ticket, search the tracker for
|
|
77
|
+
Re-running a pass must not refile the same finding. Before creating a ticket, search the tracker for a ticket carrying a stable marker `[lisa-exploratory-qa] <finding-key>` in its body (the `<finding-key>` is a stable slug of surface + symptom, e.g. `settings-modal/horizontal-overflow@tablet`). Per the `rejection-detection` rule's **Proposal rejection memory** section, that marker search MUST cover **open AND closed** tickets (with a body-enumeration fallback on search-index lag), and **match by the marker, never by title.** Then split on how any prior ticket closed:
|
|
78
|
+
|
|
79
|
+
- **Open** ticket carrying the marker → reference/update it instead; do not create a second.
|
|
80
|
+
- **Closed as _completed_** → does **not** suppress. A recurrence after a fix is a genuine **regression**, so file the finding.
|
|
81
|
+
- **Closed as _not planned_** (GitHub `stateReason == "not_planned"`; the config-resolved won't-do/canceled equivalent on JIRA/Linear) → a human **declined** this finding, so **suppress it**. Re-file only with evidence that **postdates the decline**, carrying BOTH the machine token (`declined <date>; recurred <date> in <ref>`) and a human acknowledgment sentence (`You declined this on <date>. It has recurred (<date>, <ref>), so we're raising it once more for your review.`).
|
|
82
|
+
|
|
83
|
+
Every filed finding ticket MUST end with the `rejection-detection` **operator footer** as a visible prose line so the operator knows which close-reason silences it:
|
|
84
|
+
|
|
85
|
+
> To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.
|
|
78
86
|
|
|
79
87
|
## Output
|
|
80
88
|
|
|
@@ -94,13 +102,13 @@ and records it, so a quiet run and a broken run are never mistaken for each othe
|
|
|
94
102
|
| This cycle's exit path | Run outcome |
|
|
95
103
|
|---|---|
|
|
96
104
|
| Findings filed — one or more `Bug` / `Improvement` tickets created or referenced (§6) | `candidate-proposed` |
|
|
97
|
-
| Clean pass — explored the personas and surfaces, nothing worth filing | `nothing-needed` |
|
|
98
|
-
| Tracker unconfigured — the §1 stop path; findings cannot be filed | `recovery-required` |
|
|
105
|
+
| Clean pass — explored the personas and surfaces, nothing worth filing — **or** every candidate was suppressed by a prior decline (`rejection-detection` **Proposal rejection memory**): the summary MUST name the suppression count | `nothing-needed` |
|
|
106
|
+
| Tracker unconfigured — the §1 stop path; findings cannot be filed — **or** the open-and-closed rejection-memory marker search could not run (tracker unreachable / credentials revoked): a memory check that could not run is a broken loop, never a silent `nothing-needed` | `recovery-required` |
|
|
99
107
|
| A degradation that still let the pass explore (e.g. Kane unavailable, one persona unreachable) | the outcome it actually reached above, with the summary **leading with the degradation** — degradation never mints a seventh token |
|
|
100
108
|
|
|
101
109
|
Record **exactly one** outcome per invocation through the run-record CLI, naming this loop's runbook
|
|
102
110
|
(the `--summary` is the operator-readable one-liner in the contract's exemplar voice — plain,
|
|
103
|
-
specific, actionable, e.g. `Explored 4 personas; nothing confusing to file.` for `nothing-needed`):
|
|
111
|
+
specific, actionable, e.g. `Explored 4 personas; nothing confusing to file.` — or, when a decline suppressed the only candidates, `Explored 4 personas; 2 candidates suppressed by a prior decline — nothing new to propose.` — for `nothing-needed`; and for a `recovery-required` from an unreadable decline check, `Tracker unreachable during the decline check — restore credentials; nothing was filed this run.`):
|
|
104
112
|
|
|
105
113
|
```bash
|
|
106
114
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/automation-run-record.mjs" \
|
|
@@ -248,6 +248,15 @@ resolved by reading the ticket, never by filing a sibling blind. Then:
|
|
|
248
248
|
|
|
249
249
|
Re-runs are quiet no-ops: same surfaces + same evidence ⇒ zero new tickets.
|
|
250
250
|
|
|
251
|
+
This open-and-closed marker search plus the "declined `<date>`; recurred `<date>` in `<ref>`"
|
|
252
|
+
re-file discipline is the shipped precedent for the **Proposal rejection memory** section of the
|
|
253
|
+
`rejection-detection` rule — the shared contract every proposing loop now consults so a
|
|
254
|
+
closed-as-not-planned proposal is never re-filed. The gardener already conforms; it is cited there,
|
|
255
|
+
not re-implemented. Like every proposing loop, each gardener-filed ticket MUST carry the
|
|
256
|
+
`rejection-detection` **operator footer** as a visible prose line so the human knows which
|
|
257
|
+
close-reason silences it: `To stop this from being raised again, close it as **Not planned**. Close
|
|
258
|
+
it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.`
|
|
259
|
+
|
|
251
260
|
## Autonomous-vs-approval boundary
|
|
252
261
|
|
|
253
262
|
**Everything is human-gated at v1 — this skill only files tickets.** The
|
|
@@ -130,7 +130,8 @@ After report, file what was found — **only when run standalone**, never under
|
|
|
130
130
|
|
|
131
131
|
- **Anomalies** (live signals over the conservative bar) → `Bug` leaves. **Gaps** (in-scope MISSING rubric dimensions) → `Task`/`Improvement` leaves.
|
|
132
132
|
- Every ticket is filed via the vendor-neutral `lisa-tracker-write` shim with `build_ready: true` (never a vendor write skill directly), as a **single-repo leaf** stamped `repo:<current>`, with a real three-audience description, Gherkin AC, Target Backend Environment, and a Validation Journey + typed `[EVIDENCE: <artifact-type>: <name>]` marker (e.g. `[EVIDENCE: log-snippet: alert-cleared]`) so it passes the `tracker-validate` gates.
|
|
133
|
-
- **
|
|
133
|
+
- **Operator footer (required):** every filed ticket ends with the `rejection-detection` **operator footer** as a visible prose line — `To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.` — so the operator knows which close-reason silences the finding.
|
|
134
|
+
- **Idempotent + decline-aware:** embed the `<!-- lisa:monitor-finding: <fingerprint> -->` sentinel and follow the `observability-audit` rule's current fingerprint/idempotency contract for open-and-closed search, prior-decline suppression, recurrence evidence, completed-item regression handling, and tracker-read failure outcomes. Never duplicate a live or just-resolved finding.
|
|
134
135
|
- **Capped** at `max_candidates` (default 20), `core`/high-severity first; report how many were filed vs dropped.
|
|
135
136
|
- **`--dry-run`** previews would-file tickets and creates nothing. **`--all-gaps`** widens gap filing to `recommended` tiers.
|
|
136
137
|
|
|
@@ -149,8 +150,8 @@ so a quiet monitoring run and a broken one never look identical.
|
|
|
149
150
|
| This cycle's exit path | Run outcome |
|
|
150
151
|
|---|---|
|
|
151
152
|
| Anomalies or in-scope gaps filed — one or more `Bug` / `Task` / `Improvement` leaves created or referenced | `candidate-proposed` |
|
|
152
|
-
| Clean sweep — health/audit ran end to end, nothing over the bar and no in-scope gaps | `nothing-needed` |
|
|
153
|
-
| Provider/threshold resolution failure — threshold collection fails (a present-but-uninspectable config, an invalid configured threshold) or a signal provider is unreachable so the sweep could not run | `recovery-required` |
|
|
153
|
+
| Clean sweep — health/audit ran end to end, nothing over the bar and no in-scope gaps — **or** every finding was suppressed by a prior decline (`rejection-detection` **Proposal rejection memory**): the summary MUST name the suppression count | `nothing-needed` |
|
|
154
|
+
| Provider/threshold resolution failure — threshold collection fails (a present-but-uninspectable config, an invalid configured threshold) or a signal provider is unreachable so the sweep could not run — **or** the open-and-closed rejection-memory search could not read the tracker: a memory check that could not run is a broken loop, never a silent `nothing-needed` | `recovery-required` |
|
|
154
155
|
| A degradation that still let the sweep run (an optional `ops-specialist` overlay absent, Kane unavailable) | the outcome it actually reached above, with the summary **leading with the degradation** — degradation never mints a seventh token |
|
|
155
156
|
|
|
156
157
|
Only the **standalone** run records. The nested report-only modes do their own job and do not file or
|
|
@@ -161,7 +162,8 @@ invocations.
|
|
|
161
162
|
Record **exactly one** outcome per standalone invocation through the run-record CLI, naming this
|
|
162
163
|
loop's runbook (the `--summary` is the operator-readable one-liner in the contract's exemplar voice —
|
|
163
164
|
plain, specific, actionable, e.g. `Health green; audit clean — nothing to propose.` for
|
|
164
|
-
`nothing-needed`
|
|
165
|
+
`nothing-needed`; and for a `recovery-required` from an unreadable decline check, `Tracker
|
|
166
|
+
unreachable during the decline check — restore credentials; nothing was filed this run.`):
|
|
165
167
|
|
|
166
168
|
```bash
|
|
167
169
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/automation-run-record.mjs" \
|
|
@@ -241,10 +241,26 @@ Each created PRD carries the marker `[lisa-project-ideation] idea=<stable-key>`.
|
|
|
241
241
|
`<stable-key>` deterministically from: repo identity (configured repo or git remote + repo-root
|
|
242
242
|
basename) + a normalized slug of the idea name + the normalized persona key(s) + the existing-fit
|
|
243
243
|
anchor. **Do not** include rank, date, confidence, or the generated PRD title (they change across
|
|
244
|
-
runs). `lisa-prd-source-write` searches the source for
|
|
244
|
+
runs). `lisa-prd-source-write` searches the source for a PRD carrying this marker before
|
|
245
245
|
creating — matching by marker, never by title — so re-running ideation updates/references the
|
|
246
246
|
existing PRD rather than duplicating it.
|
|
247
247
|
|
|
248
|
+
Per the `rejection-detection` rule's **Proposal rejection memory** section, that marker search MUST
|
|
249
|
+
cover **open AND closed** PRDs (with a body-enumeration fallback on search-index lag), and a PRD
|
|
250
|
+
**closed as _not planned_** (GitHub `stateReason == "not_planned"`; the config-resolved won't-do/
|
|
251
|
+
canceled equivalent on JIRA/Linear — never a hardcoded lane string) is a **durable human decline**
|
|
252
|
+
that **suppresses** re-proposing that idea. Re-propose only with evidence that **postdates the
|
|
253
|
+
decline**, and state it in the new PRD as BOTH the machine token (`declined <date>; recurred <date>
|
|
254
|
+
in <ref>`) and a human acknowledgment sentence (`You declined this on <date>. It has recurred
|
|
255
|
+
(<date>, <ref>), so we're raising it once more for your review.`). A PRD closed as _completed_ is
|
|
256
|
+
not a decline. This is tracker-side memory; the advisory ideation memory ledger stays advisory and
|
|
257
|
+
never overrides it.
|
|
258
|
+
|
|
259
|
+
Every created PRD MUST carry the `rejection-detection` **operator footer** as a visible prose line
|
|
260
|
+
so the operator knows which close-reason silences it:
|
|
261
|
+
|
|
262
|
+
> To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.
|
|
263
|
+
|
|
248
264
|
## Step 7 — Output (no report file)
|
|
249
265
|
|
|
250
266
|
Emit two distinct in-session sections (do not write a report file):
|
|
@@ -284,9 +300,9 @@ so a quiet ideation run and a broken one are never confused.
|
|
|
284
300
|
| This run's exit path | Run outcome |
|
|
285
301
|
|---|---|
|
|
286
302
|
| PRD(s) created or reused this run (Step 6/7 **PRDs Created**) | `candidate-proposed` |
|
|
287
|
-
| Nothing to ideate — no Practical Idea cleared the bar; nothing created | `nothing-needed` |
|
|
303
|
+
| Nothing to ideate — no Practical Idea cleared the bar; nothing created — **or** every idea was suppressed by a prior decline (`rejection-detection` **Proposal rejection memory**): the summary MUST name the suppression count | `nothing-needed` |
|
|
288
304
|
| The Step 5.5 **PRD-queue-pressure gate** blocked auto-ready creation — a human must drain the queue before another auto-ready PRD is added | `approval-requested` |
|
|
289
|
-
| The loop itself could not run — the PRD source reader failed or the queue is misconfigured (a source-reader failure snapshot, not queue pressure) | `recovery-required` |
|
|
305
|
+
| The loop itself could not run — the PRD source reader failed or the queue is misconfigured (a source-reader failure snapshot, not queue pressure) — **or** the open-and-closed rejection-memory marker search could not read the source: a memory check that could not run is a broken loop, never a silent `nothing-needed` | `recovery-required` |
|
|
290
306
|
| A degradation that still let ideation run (optional Codex automation memory unavailable, an inspiration source unreachable) | the outcome it actually reached above, with the summary **leading with the degradation** — degradation never mints a seventh token |
|
|
291
307
|
|
|
292
308
|
The pressure gate is `approval-requested`, **not** `recovery-required`: the loop ran fine and
|
|
@@ -296,7 +312,8 @@ broken machine. `recovery-required` is reserved for the loop failing to run at a
|
|
|
296
312
|
Record **exactly one** outcome per invocation through the run-record CLI, naming this loop's runbook
|
|
297
313
|
(the `--summary` is the operator-readable one-liner in the contract's exemplar voice — plain,
|
|
298
314
|
specific, actionable, e.g. `Reviewed evidence; no practical idea cleared the bar — nothing to
|
|
299
|
-
propose.` for `nothing-needed`
|
|
315
|
+
propose.` for `nothing-needed`; and for a `recovery-required` from an unreadable decline check,
|
|
316
|
+
`Tracker unreachable during the decline check — restore credentials; nothing was filed this run.`):
|
|
300
317
|
|
|
301
318
|
```bash
|
|
302
319
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/automation-run-record.mjs" \
|
|
@@ -456,7 +456,9 @@ re-issued against an unchanged conflicting head.
|
|
|
456
456
|
follow-up, `build_ready: true` so it auto-builds. The ticket MUST name: the blocked item + its
|
|
457
457
|
PR/deploy URL, the exact blocker (conflict / which checks failed with their logs link / which
|
|
458
458
|
change requests / which deploy run), three-audience description, and Gherkin acceptance criteria
|
|
459
|
-
for "PR is mergeable / deploy is green."
|
|
459
|
+
for "PR is mergeable / deploy is green." Every created blocker fix ticket MUST end with the
|
|
460
|
+
`rejection-detection` **operator footer** as a visible prose line:
|
|
461
|
+
`To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.`
|
|
460
462
|
2. **Transition the stalled item `claimed → blocked`** and add an **`is blocked by`** link to the
|
|
461
463
|
new fix ticket (vendor-native: JIRA issue link `is blocked by`; GitHub/Linear `Blocked by:` line
|
|
462
464
|
+ label). Post a `[lisa-repair-intake]` note naming what it is blocked by and why. This block is
|
|
@@ -470,11 +472,19 @@ The item now sits in `blocked`; once the fix ticket reaches a terminal state, th
|
|
|
470
472
|
`blocked` → unblock if cleared** path (next section) detects the cleared `is blocked by`
|
|
471
473
|
dependency and resumes the original in place — a self-healing loop.
|
|
472
474
|
|
|
473
|
-
**Idempotency.** Before filing, check for
|
|
475
|
+
**Idempotency.** Before filing, check for a fix ticket already carrying the marker
|
|
474
476
|
`[lisa-repair-intake] blocker:<item-ref>/<blocker-key>` (blocker-key is a stable slug of the
|
|
475
|
-
blocker, e.g. `pr-1234/merge-conflict` or `pr-1234/checks-failing`).
|
|
476
|
-
|
|
477
|
-
|
|
477
|
+
blocker, e.g. `pr-1234/merge-conflict` or `pr-1234/checks-failing`). Per the `rejection-detection`
|
|
478
|
+
rule's **Proposal rejection memory** section, that marker search MUST cover **open AND closed**
|
|
479
|
+
tickets (body-enumeration fallback on search-index lag): an **open** match → reference it and ensure
|
|
480
|
+
the `is blocked by` link is present rather than creating a duplicate; a match **closed as _not
|
|
481
|
+
planned_** (GitHub `stateReason == "not_planned"`; the config-resolved equivalent on JIRA/Linear) is
|
|
482
|
+
a **human decline** of that fix ticket — do **not** re-file it unless evidence **postdates the
|
|
483
|
+
decline**, and the re-filed ticket MUST carry BOTH the machine token (`declined <date>; recurred
|
|
484
|
+
<date> in <ref>`) and the human acknowledgment sentence (`You declined this on <date>. It has
|
|
485
|
+
recurred (<date>, <ref>), so we're raising it once more for your review.`); a match closed as
|
|
486
|
+
_completed_ is a regression path, not a decline. Honor the backoff window and state fingerprint
|
|
487
|
+
(Loop prevention) so re-runs over the same unchanged blocker are no-ops.
|
|
478
488
|
|
|
479
489
|
### Build `blocked` → re-evaluate, unblock if cleared
|
|
480
490
|
|
|
@@ -985,11 +995,11 @@ to each item* — the two never merge in the one-line summary.
|
|
|
985
995
|
|
|
986
996
|
| This cycle's exit path | Run outcome |
|
|
987
997
|
|---|---|
|
|
988
|
-
| Nothing actionable — the idle case (walk step 5): examined N, all active or in backoff | `nothing-needed` |
|
|
998
|
+
| Nothing actionable — the idle case (walk step 5): examined N, all active or in backoff — including a fix ticket suppressed by a prior decline (`rejection-detection` **Proposal rejection memory**), which the summary names in its suppression count | `nothing-needed` |
|
|
989
999
|
| Repairs applied **and confirmed** this cycle — `resumed` / `resynced` / `recovered` / `unblocked` / `closed_out` / `rolled_up` / `relinked` / `normalized_ready` | `change-proved` |
|
|
990
1000
|
| Repair produced new work for a human to pick up — e.g. an unmergeable PR or failed deploy filed as a **build-ready fix ticket** and left `blocked` | `candidate-proposed` |
|
|
991
1001
|
| A repair reached an autonomy boundary needing a human (a protected-deploy approval before it can proceed) | `approval-requested` |
|
|
992
|
-
| The loop itself could not run — the queue is unreadable
|
|
1002
|
+
| The loop itself could not run — the queue is unreadable, tracker credentials are revoked, or an open-and-closed rejection-memory / blocker-marker search is unreadable and therefore must not fall through to `nothing-needed` | `recovery-required` |
|
|
993
1003
|
|
|
994
1004
|
Record **exactly one** outcome per invocation through the run-record CLI, naming this loop's runbook
|
|
995
1005
|
(the `--summary` is the operator-readable one-liner in the contract's exemplar voice — plain,
|
|
@@ -74,7 +74,15 @@ Each finding is a flat leaf, so `build_ready` applies directly — pass it expli
|
|
|
74
74
|
|
|
75
75
|
### Idempotency — don't spam duplicates
|
|
76
76
|
|
|
77
|
-
Re-running a pass must not refile the same finding. Before creating a ticket, search the tracker for
|
|
77
|
+
Re-running a pass must not refile the same finding. Before creating a ticket, search the tracker for a ticket carrying a stable marker `[lisa-exploratory-qa] <finding-key>` in its body (the `<finding-key>` is a stable slug of surface + symptom, e.g. `settings-modal/horizontal-overflow@tablet`). Per the `rejection-detection` rule's **Proposal rejection memory** section, that marker search MUST cover **open AND closed** tickets (with a body-enumeration fallback on search-index lag), and **match by the marker, never by title.** Then split on how any prior ticket closed:
|
|
78
|
+
|
|
79
|
+
- **Open** ticket carrying the marker → reference/update it instead; do not create a second.
|
|
80
|
+
- **Closed as _completed_** → does **not** suppress. A recurrence after a fix is a genuine **regression**, so file the finding.
|
|
81
|
+
- **Closed as _not planned_** (GitHub `stateReason == "not_planned"`; the config-resolved won't-do/canceled equivalent on JIRA/Linear) → a human **declined** this finding, so **suppress it**. Re-file only with evidence that **postdates the decline**, carrying BOTH the machine token (`declined <date>; recurred <date> in <ref>`) and a human acknowledgment sentence (`You declined this on <date>. It has recurred (<date>, <ref>), so we're raising it once more for your review.`).
|
|
82
|
+
|
|
83
|
+
Every filed finding ticket MUST end with the `rejection-detection` **operator footer** as a visible prose line so the operator knows which close-reason silences it:
|
|
84
|
+
|
|
85
|
+
> To stop this from being raised again, close it as **Not planned**. Close it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.
|
|
78
86
|
|
|
79
87
|
## Output
|
|
80
88
|
|
|
@@ -94,13 +102,13 @@ and records it, so a quiet run and a broken run are never mistaken for each othe
|
|
|
94
102
|
| This cycle's exit path | Run outcome |
|
|
95
103
|
|---|---|
|
|
96
104
|
| Findings filed — one or more `Bug` / `Improvement` tickets created or referenced (§6) | `candidate-proposed` |
|
|
97
|
-
| Clean pass — explored the personas and surfaces, nothing worth filing | `nothing-needed` |
|
|
98
|
-
| Tracker unconfigured — the §1 stop path; findings cannot be filed | `recovery-required` |
|
|
105
|
+
| Clean pass — explored the personas and surfaces, nothing worth filing — **or** every candidate was suppressed by a prior decline (`rejection-detection` **Proposal rejection memory**): the summary MUST name the suppression count | `nothing-needed` |
|
|
106
|
+
| Tracker unconfigured — the §1 stop path; findings cannot be filed — **or** the open-and-closed rejection-memory marker search could not run (tracker unreachable / credentials revoked): a memory check that could not run is a broken loop, never a silent `nothing-needed` | `recovery-required` |
|
|
99
107
|
| A degradation that still let the pass explore (e.g. Kane unavailable, one persona unreachable) | the outcome it actually reached above, with the summary **leading with the degradation** — degradation never mints a seventh token |
|
|
100
108
|
|
|
101
109
|
Record **exactly one** outcome per invocation through the run-record CLI, naming this loop's runbook
|
|
102
110
|
(the `--summary` is the operator-readable one-liner in the contract's exemplar voice — plain,
|
|
103
|
-
specific, actionable, e.g. `Explored 4 personas; nothing confusing to file.` for `nothing-needed`):
|
|
111
|
+
specific, actionable, e.g. `Explored 4 personas; nothing confusing to file.` — or, when a decline suppressed the only candidates, `Explored 4 personas; 2 candidates suppressed by a prior decline — nothing new to propose.` — for `nothing-needed`; and for a `recovery-required` from an unreadable decline check, `Tracker unreachable during the decline check — restore credentials; nothing was filed this run.`):
|
|
104
112
|
|
|
105
113
|
```bash
|
|
106
114
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/automation-run-record.mjs" \
|
|
@@ -248,6 +248,15 @@ resolved by reading the ticket, never by filing a sibling blind. Then:
|
|
|
248
248
|
|
|
249
249
|
Re-runs are quiet no-ops: same surfaces + same evidence ⇒ zero new tickets.
|
|
250
250
|
|
|
251
|
+
This open-and-closed marker search plus the "declined `<date>`; recurred `<date>` in `<ref>`"
|
|
252
|
+
re-file discipline is the shipped precedent for the **Proposal rejection memory** section of the
|
|
253
|
+
`rejection-detection` rule — the shared contract every proposing loop now consults so a
|
|
254
|
+
closed-as-not-planned proposal is never re-filed. The gardener already conforms; it is cited there,
|
|
255
|
+
not re-implemented. Like every proposing loop, each gardener-filed ticket MUST carry the
|
|
256
|
+
`rejection-detection` **operator footer** as a visible prose line so the human knows which
|
|
257
|
+
close-reason silences it: `To stop this from being raised again, close it as **Not planned**. Close
|
|
258
|
+
it as **Completed** if it was fixed — a later recurrence may be re-filed as a regression.`
|
|
259
|
+
|
|
251
260
|
## Autonomous-vs-approval boundary
|
|
252
261
|
|
|
253
262
|
**Everything is human-gated at v1 — this skill only files tickets.** The
|