Exploratory QA
Overview
Experience the product the way a brand-new end user would: drive its real consumer-facing interface and actually try to use it, then surface anything confusing, broken, or hard to understand. This is a usability/experience pass, not a test-coverage audit (for that, use e2e-coverage-gaps). Every finding is filed as a tracked work item so it enters the Lisa lifecycle — no static report file.
How you drive the product is owned by the use-the-product core skill. Invoke it first: it detects the product type (web / API / game / CLI / IaC), resolves the target environment and its mutation policy (so you never mutate production without an explicit, justified opt-in), and discovers the project's personas so you can explore as each one. This skill supplies the QA lens — what to look for and how to file it.
Parameters
target-url | env (first positional) — what to explore (passed through to use-the-product).
ready=true|false — the build-ready state for the tickets this pass creates.
ready=true → created build-ready, so lisa-intake / the build-intake scanner auto-picks them up.
ready=false (default) → created in the backlog for a human to review and promote.
1. Set up
- Invoke
use-the-product to detect the product type, resolve the environment + mutation policy, and discover personas/subagents. Everything about how to drive the product, where, and how much you may mutate comes from there — do not re-derive it.
- For a DOM web app whose resolved non-production environment has mutation policy
full, check the optional Kane provider with lisa kane probe. If ready, invoke lisa-kane-browser for self-contained journeys and use its screenshots, HAR/network, console, and normalized verdict as evidence. Keep the run on a directly controlled browser for read-only/forbidden environments. Treat tool_failed/timed_out as provider findings, never as product bugs.
- Confirm the tracker is configured. Findings are filed as tickets, so read
tracker from .lisa.config.json (local overrides global). If unset, stop and report that the tracker must be configured (/lisa:setup:jira / :github / :linear) before exploratory QA can file findings — do not silently fall back to a report file.
- Read the
ready flag (default false).
2. Arrive cold and use it like a human
- Start with no prior knowledge — do not pre-read the codebase to learn the intended flows; discover them the way a user would. Form a first impression: is it obvious what this product is, what to do first, and where to go next?
- Then actually attempt real tasks through the interface (per
use-the-product's per-type playbook), exercising representative controls/endpoints/commands — a first-time user explores, makes mistakes, and tries the obvious thing.
3. The QA lens — what to look for
Cover at least these dimensions unless the user narrows scope. Most are universal; the web-specific ones are marked and have per-type equivalents in §4.
- Comprehension & labeling: user-facing copy must read like something a normal first-time user understands. Flag machine-style/developer labels (raw IDs, enum keys,
snake_case, null/undefined, untranslated i18n keys), admin/database terms ("metadata", "rows", "record", "entity"), unexplained jargon, unclear button/menu names, and meaningless icons. If a label would make a non-technical user ask "what does that mean?", file a clarity ticket.
- Data usefulness & context: facts, metrics, and tables must help a person understand the surface, not just prove extraction happened. If a user can't tell what a number or field refers to without rereading the raw source, file a ticket to add context (excerpts, labels, grouping, provenance) or hide it.
- Data volume & trust: compare visible data to what the surface promises. A rich view with only a few items can read as broken/filtered/still-loading. File a finding when sparse data isn't explained (result counts, active filters, reset affordances, coverage status, sample labeling).
- Controls & mental model: controls must match what they do (sort ≠ filter; search ≠ facet; finite domains want selects/typeaheads, not blank text inputs). Flag controls that make users guess spelling/casing, hide the option universe, or use the wrong component.
- Flow completeness & expected counterparts: a screen that gates access or shows one side of a standard paired flow must offer the other side, or a clear path to it. A new user must never hit a dead end with no next step. Flag: sign-in with no sign-up (or vice-versa), no account recovery ("Forgot password?", resend verification), no exit from a state (no sign-out; a modal/wizard with no back/close/cancel), one-way actions (create with no edit/delete where both are expected), unreachable entry points (a feature only reachable by guessing a URL; an empty state with no primary action). When the missing counterpart makes a core task impossible for a class of users, file a
Bug; otherwise a usability Improvement.
- Action preconditions & incomplete end-states: an action that needs multiple inputs or a prerequisite (compare, merge, bulk-edit) should guide the user to satisfy it — disable/explain until met, collect inputs first, or give the destination an in-place control. Actually trigger these and watch where they land; flag when a primary action fires under-satisfied and strands the user, lands on an empty/partial end-state with no next step, or is offered where it cannot succeed. Left unable to finish →
Bug; works but confusing → Improvement.
- Navigation clarity: is it obvious how to get somewhere and back? Flag dead ends, hidden entry points, surprising redirects, broken links, no clear "home", and duplicated nav that competes for space without page-specific value.
- Behavior correctness: does the obvious action do what a user expects? Confusing errors, silent failures, disabled controls with no explanation, state that doesn't persist.
- Affordance clarity: can the user tell what is clickable, required, in-progress, or complete?
- Load & responsiveness: long/unclear loads, blank or skeleton-only shells, spinners with no progress. Flag surfaces that report "loaded" but where meaningful content arrives much later. Capture user-perceived timings (shell visible, first meaningful content, stable content); if the delay is noticeable, file it even when the eventual content is correct.
- Visual/layout quality (DOM web): cut-off/truncated text, overlap, cramped density, offscreen controls, accidental horizontal scroll. Do not judge by eyeballing a screenshot alone — confirm with the layout-integrity sweep in §4.
- Consistency / standard UX: components, spacing, terminology, and interaction patterns should be consistent and follow common conventions. Flag anything non-standard or screen-to-screen drift.
4. Type-specific depth
- DOM web app — breakpoints & layout integrity. Sweep a range of widths, including the in-between ones where clipping appears (e.g. 360, 390, 414, 600, 768, 834, 1024, 1280, 1440 plus ~900–1180 steps), and re-walk key paths at each. At every width, in addition to looking, take DOM measurements and treat as findings: container overflow (
documentElement.scrollWidth > clientWidth); clipped/offscreen controls (a control's getBoundingClientRect() falling outside the viewport or an overflow:hidden|clip|auto|scroll ancestor — e.g. a submit button cut off by its filter card); truncated meaningful text (scrollWidth > clientWidth / ellipsis on text that carries meaning); colliding controls (label overlapping an adjacent control with no gap). A primary/interactive control that is clipped, offscreen, or unreachable is a Bug, not an Improvement.
- HTTP / API backend. Judge the contract as a consumer experiences it: unclear/inconsistent status codes, error responses with no actionable message, payloads exposing internal shapes (raw enums, DB column names), missing pagination/among-counts, endpoints that 500 on obvious edge inputs. A broken/incorrect response is a
Bug; a confusing-but-working contract is an Improvement.
- Canvas game. Judge readability at gameplay scale, input responsiveness/latency, game-feel, unclear objectives, and silent state changes — not DOM breakpoints. A soft-lock or lost progress is a
Bug; unclear-but-playable friction is an Improvement.
- CLI / library. Judge help/output clarity, error messages, discoverability of commands, and surprising side effects. A wrong result / crash is a
Bug; confusing UX is an Improvement.
- IaC / CDK (read-only). From
cdk synth/diff: over-broad IAM, missing/opaque stack outputs, resources that don't serve their stated purpose, drift. A security-relevant misconfiguration is a Bug; unclear-but-correct infra is an Improvement.
5. Mutation
Whether you may create/edit/delete — and as which account — is set by the use-the-product mutation policy (read-only vs full, identity, production rules). Follow its Mutation Discipline when the policy is full (prefixed test data, identify + verify cleanup, record residue). Never mutate an env the policy marks read-only or forbidden; if a finding can only be confirmed by a forbidden mutation, file it as observed-and-blocked rather than escalating.
6. File findings as tracked work
No report file. Every finding becomes a leaf work item via lisa-tracker-write (the vendor-neutral writer — it dispatches to the configured tracker and runs the validation gate; never call a vendor *-write-* skill directly):
Kane never files the work item itself. Even when it marks a failure as a confirmed product bug, run Lisa's marker-based duplicate search, apply the QA lens, and route the finding through lisa-tracker-write exactly as for every other controller.
| Finding |
issue_type |
build_ready |
| User-visible bug (broken behavior) |
Bug |
the ready flag (default false) |
| Usability / UX / clarity issue |
Improvement |
the ready flag (default false) |
Each finding is a flat leaf, so build_ready applies directly — pass it explicitly on every create.
This skill is the named human-gate exception under ready-role-filing. Everywhere else in Lisa, a complete defect found during other work is filed with explicit build_ready: true so build-intake claims it next cycle. Exploratory QA is deliberately different, and that difference is ratified rather than drift: its findings are candidate defects and usability observations whose product significance is a human call, so auto-readying them would push judgment work into the build queue — precisely the failure the gate model exists to prevent. Its default ready=false is therefore an explicit human-gate marker, never a bare omission: every not-ready create passes human_gate: "exploratory finding — product significance is a human product call" alongside build_ready: false, and the writer stamps the auditable [lisa-human-gate] marker. An operator who wants a pass to feed the queue directly opts in with ready=true.
(The sibling e2e-coverage-gaps skills are the contrast: a missing automated test is not a product question, so they file build_ready: true by default.)
Each ticket MUST be a complete spec (the validator rejects thin tickets): a three-audience description; for a bug, exact reproduction steps, observed-vs-expected, the env / account / interface it occurred at, and evidence; for a usability issue, the observed friction, who it affects, where, and the proposed improvement; and Gherkin acceptance criteria for the fixed behavior.
Idempotency — don't spam duplicates
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:
- Open ticket carrying the marker → reference/update it instead; do not create a second.
- Closed as completed → does not suppress. A recurrence after a fix is a genuine regression, so file the finding.
- Closed as not planned (GitHub
stateReason == "not_planned"; the config-resolved won't-do/canceled equivalent on JIRA/Linear, including a Linear duplicate state) → 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.).
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:
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.
Output
No report file. Emit a concise in-session summary:
- Scope: product type, target env + mutation level, persona(s) explored as, tool, build/version if visible, date.
- First impression: could a new user tell what the product is and what to do first?
- Findings filed, bucketed by type — each with its created or referenced ticket ref and build-ready state.
- Observed but not filed: anything noticed but intentionally not ticketed (including forbidden-mutation blocks), with why.
Run outcome
As the registered exploratory-bugs automation loop, this pass conforms to the
automation-runbook-contract rule: every invocation ends in exactly one of the six run outcomes
and records it, so a quiet run and a broken run are never mistaken for each other.
| This cycle's exit path |
Run outcome |
Findings filed — one or more Bug / Improvement tickets created or referenced (§6) |
candidate-proposed |
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 |
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 |
The runbook's Retirement condition tripped — the trailing quiet window is empty AND this pass found nothing to file AND the project no longer ships an exploratory-qa surface — this row supersedes the nothing-needed row when it applies |
policy-obsolete |
| 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 |
Before invoking the run-record CLI, evaluate the Retirement condition first. If it applies,
select policy-obsolete as the sole outcome and do not record a prior nothing-needed result;
otherwise select the ordinary outcome from the table.
Record exactly one outcome per invocation through the run-record CLI, naming this loop's runbook
(the --summary is the operator-readable one-liner in the contract's exemplar voice — plain,
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.):
node "${CLAUDE_PLUGIN_ROOT:-node_modules/@codyswann/lisa/plugins/lisa}/scripts/automation-run-record.mjs" \
--loop-id exploratory-bugs --outcome candidate-proposed \
--summary "Explored 4 personas; filed 3 findings from the checkout flow — awaiting your flip to ready." \
--runbook .lisa/automations/exploratory-bugs.runbook.md [--ref <ticket-url>]...
The :- default in that command is load-bearing, not decoration. CLAUDE_PLUGIN_ROOT is not
exported into an agent's Bash tool environment, so the bare "${CLAUDE_PLUGIN_ROOT}/scripts/…"
form expands to an absolute /scripts/… and exits 1. The default names the installed package copy,
which resolves from the consumer repository root — which is where the recorder is actually
invoked. Never substitute a bare plugins/lisa/scripts/… or plugins/src/base/scripts/… path:
those are relative to the Lisa package root, not the repository you are standing in, and exit 1
from there. If recording still fails, degrade, never abort (per automation-runbook-contract):
note the recording failure in the run output and finish the cycle — a recording failure is a
degradation to report, never a reason to block the loop.
Retirement evaluation (every run). Evaluate this loop's runbook Retirement condition on
every run, exactly as the automation-runbook-contract rule's Retirement section defines it — this
skill conforms to that text and never restates or diverges from it. On top of the contract's two
conditions the runbook seeds a third domain conjunct — the project no longer ships an
exploratory-qa command surface to explore — which only tightens the test and never replaces it: a
quiet month on a product nobody broke is quality holding, not a reason to stop looking. Evaluate all
three. When all three hold, record policy-obsolete and file exactly ONE marker-deduped
teardown proposal through lisa-tracker-write (per tracked-work + integration-access-layer):
- Marker
<!-- [lisa-automation-retire] key=exploratory-bugs --> plus a visible prose line;
matched on the marker, never the title; searched open AND closed per rejection-detection's
Proposal rejection memory. Treat matches by close state: open suppresses another proposal;
Not planned suppresses another proposal unless new evidence postdates the rejection;
Completed means the prior approved action happened, so a later recurrence may be re-filed.
When an existing proposal suppresses filing, the run still records policy-obsolete and files
nothing — the outcome describes this run, while the ticket is filed exactly once.
- Labels
status:blocked + human-needed, carrying the contract's decision-ready packet. The
human-needed label marks the proposal human-owned: lisa-repair-intake recognizes it and never
re-dispatches it as stalled work.
- Evidence the date-filtered search result, this run's summary, the loop's current cadence
(the baseline an operator needs to choose a longer one), and a one-line summary of recent runs
read from
.lisa/automations/runs/exploratory-bugs.jsonl. Fill the rest of the packet the same
way every time: Work already attempted is the searches this run ran, and Risk of inaction is
that the loop keeps consuming schedule slots and tokens for nothing.
- How to answer names the three operator responses: approve — run
/lisa:tear-down-automations exploratory-bugs and only that loop registration goes away;
decline — close the proposal as
Not planned (closing it as Completed leaves a later re-file open) and the loop simply
continues; re-cadence — pick a longer cadence off that evidence and re-register with
/lisa:setup-automations instead of tearing down.
- Operator footer, verbatim, as on every loop-filed proposal (
rejection-detection):
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.
The loop keeps running at its normal cadence until a human acts, and never deletes its own
registration.
Quality bar
- Explore as a true first-time user — judge clarity, not whether you (who can read the code) can figure it out.
- Every ticket must stand alone for an implementer who was not in the session.
- Do not claim cleanup succeeded unless verified.
- File per the
ready flag (default: backlog for human triage).
- Route automated-coverage gaps to
e2e-coverage-gaps; preserve unrelated repo changes.
1---2name: lisa-exploratory-qa3description: First-time-user exploratory QA pass for ANY product type (DOM web app, HTTP/API backend, canvas game, CLI/library, IaC/CDK) that FEEDS THE LIFECYCLE. Use when asked to experience a product the way a brand-new end user would — driving its real consumer-facing interface via the `use-the-product` core (which detects the product type, resolves the per-environment mutation policy from .lisa.config.json so production data is never mutated by accident, and explores through the project's personas when it defines them) to find anything confusing, broken, or hard to use. Static route scans, HTTP fetches, screenshots alone, or console/network checks alone are not sufficient evidence. Instead of writing a report file, it files every finding as a tracked work item via lisa-tracker-write. A `ready` parameter controls whether those tickets are created build-ready (auto-picked-up by lisa-intake) or left in the backlog for human triage (default). For gaps in the automated test suite, use e2e-coverage-gaps instead.4---56# Exploratory QA78## Overview910Experience the product the way a **brand-new end user** would: drive its real consumer-facing interface and actually try to use it, then surface anything **confusing, broken, or hard to understand**. This is a usability/experience pass, **not** a test-coverage audit (for that, use `e2e-coverage-gaps`). Every finding is filed as a tracked work item so it enters the Lisa lifecycle — no static report file.1112**How you drive the product is owned by the `use-the-product` core skill.** Invoke it first: it detects the product type (web / API / game / CLI / IaC), resolves the target environment and its **mutation policy** (so you never mutate production without an explicit, justified opt-in), and discovers the project's **personas** so you can explore as each one. This skill supplies the **QA lens** — what to look for and how to file it.1314## Parameters1516- **`target-url | env`** (first positional) — what to explore (passed through to `use-the-product`).17- **`ready=true|false`** — the build-ready state for the tickets this pass creates.18 - `ready=true` → created build-ready, so `lisa-intake` / the build-intake scanner auto-picks them up.19 - `ready=false` (**default**) → created in the backlog for a human to review and promote.2021## 1. Set up2223- **Invoke `use-the-product`** to detect the product type, resolve the environment + mutation policy, and discover personas/subagents. Everything about *how* to drive the product, *where*, and *how much you may mutate* comes from there — do not re-derive it.24- For a DOM web app whose resolved non-production environment has mutation policy `full`, check the optional Kane provider with `lisa kane probe`. If ready, invoke `lisa-kane-browser` for self-contained journeys and use its screenshots, HAR/network, console, and normalized verdict as evidence. Keep the run on a directly controlled browser for `read-only`/`forbidden` environments. Treat `tool_failed`/`timed_out` as provider findings, never as product bugs.25- **Confirm the tracker is configured.** Findings are filed as tickets, so read `tracker` from `.lisa.config.json` (local overrides global). If unset, stop and report that the tracker must be configured (`/lisa:setup:jira` / `:github` / `:linear`) before exploratory QA can file findings — do not silently fall back to a report file.26- Read the `ready` flag (default `false`).2728## 2. Arrive cold and use it like a human2930- Start with **no prior knowledge** — do not pre-read the codebase to learn the intended flows; discover them the way a user would. Form a first impression: is it obvious what this product is, what to do first, and where to go next?31- Then actually attempt real tasks through the interface (per `use-the-product`'s per-type playbook), exercising representative controls/endpoints/commands — a first-time user explores, makes mistakes, and tries the obvious thing.3233## 3. The QA lens — what to look for3435Cover at least these dimensions unless the user narrows scope. Most are universal; the web-specific ones are marked and have per-type equivalents in §4.3637- **Comprehension & labeling:** user-facing copy must read like something a normal first-time user understands. Flag machine-style/developer labels (raw IDs, enum keys, `snake_case`, `null`/`undefined`, untranslated i18n keys), admin/database terms ("metadata", "rows", "record", "entity"), unexplained jargon, unclear button/menu names, and meaningless icons. If a label would make a non-technical user ask "what does that mean?", file a clarity ticket.38- **Data usefulness & context:** facts, metrics, and tables must help a person understand the surface, not just prove extraction happened. If a user can't tell what a number or field refers to without rereading the raw source, file a ticket to add context (excerpts, labels, grouping, provenance) or hide it.39- **Data volume & trust:** compare visible data to what the surface promises. A rich view with only a few items can read as broken/filtered/still-loading. File a finding when sparse data isn't explained (result counts, active filters, reset affordances, coverage status, sample labeling).40- **Controls & mental model:** controls must match what they do (sort ≠ filter; search ≠ facet; finite domains want selects/typeaheads, not blank text inputs). Flag controls that make users guess spelling/casing, hide the option universe, or use the wrong component.41- **Flow completeness & expected counterparts:** a screen that gates access or shows one side of a standard paired flow must offer the other side, or a clear path to it. A new user must never hit a dead end with no next step. Flag: **sign-in with no sign-up** (or vice-versa), **no account recovery** ("Forgot password?", resend verification), **no exit from a state** (no sign-out; a modal/wizard with no back/close/cancel), **one-way actions** (create with no edit/delete where both are expected), **unreachable entry points** (a feature only reachable by guessing a URL; an empty state with no primary action). When the missing counterpart makes a core task impossible for a class of users, file a `Bug`; otherwise a usability `Improvement`.42- **Action preconditions & incomplete end-states:** an action that needs multiple inputs or a prerequisite (compare, merge, bulk-edit) should guide the user to satisfy it — disable/explain until met, collect inputs first, or give the destination an in-place control. Actually trigger these and watch where they land; flag when a primary action fires under-satisfied and strands the user, lands on an empty/partial end-state with no next step, or is offered where it cannot succeed. Left unable to finish → `Bug`; works but confusing → `Improvement`.43- **Navigation clarity:** is it obvious how to get somewhere and back? Flag dead ends, hidden entry points, surprising redirects, broken links, no clear "home", and duplicated nav that competes for space without page-specific value.44- **Behavior correctness:** does the obvious action do what a user expects? Confusing errors, silent failures, disabled controls with no explanation, state that doesn't persist.45- **Affordance clarity:** can the user tell what is clickable, required, in-progress, or complete?46- **Load & responsiveness:** long/unclear loads, blank or skeleton-only shells, spinners with no progress. Flag surfaces that report "loaded" but where meaningful content arrives much later. Capture user-perceived timings (shell visible, first meaningful content, stable content); if the delay is noticeable, file it even when the eventual content is correct.47- **Visual/layout quality (DOM web):** cut-off/truncated text, overlap, cramped density, offscreen controls, accidental horizontal scroll. **Do not judge by eyeballing a screenshot alone** — confirm with the layout-integrity sweep in §4.48- **Consistency / standard UX:** components, spacing, terminology, and interaction patterns should be consistent and follow common conventions. Flag anything non-standard or screen-to-screen drift.4950## 4. Type-specific depth5152- **DOM web app — breakpoints & layout integrity.** Sweep a range of widths, including the *in-between* ones where clipping appears (e.g. 360, 390, 414, 600, 768, 834, 1024, 1280, 1440 plus ~900–1180 steps), and re-walk key paths at each. At every width, in addition to looking, take DOM measurements and treat as findings: **container overflow** (`documentElement.scrollWidth > clientWidth`); **clipped/offscreen controls** (a control's `getBoundingClientRect()` falling outside the viewport or an `overflow:hidden|clip|auto|scroll` ancestor — e.g. a submit button cut off by its filter card); **truncated meaningful text** (`scrollWidth > clientWidth` / ellipsis on text that carries meaning); **colliding controls** (label overlapping an adjacent control with no gap). A primary/interactive control that is clipped, offscreen, or unreachable is a **`Bug`**, not an Improvement.53- **HTTP / API backend.** Judge the *contract as a consumer experiences it*: unclear/inconsistent status codes, error responses with no actionable message, payloads exposing internal shapes (raw enums, DB column names), missing pagination/among-counts, endpoints that 500 on obvious edge inputs. A broken/incorrect response is a `Bug`; a confusing-but-working contract is an `Improvement`.54- **Canvas game.** Judge readability at gameplay scale, input responsiveness/latency, game-feel, unclear objectives, and silent state changes — not DOM breakpoints. A soft-lock or lost progress is a `Bug`; unclear-but-playable friction is an `Improvement`.55- **CLI / library.** Judge help/output clarity, error messages, discoverability of commands, and surprising side effects. A wrong result / crash is a `Bug`; confusing UX is an `Improvement`.56- **IaC / CDK (read-only).** From `cdk synth`/`diff`: over-broad IAM, missing/opaque stack outputs, resources that don't serve their stated purpose, drift. A security-relevant misconfiguration is a `Bug`; unclear-but-correct infra is an `Improvement`.5758## 5. Mutation5960Whether you may create/edit/delete — and as which account — is set by the `use-the-product` mutation policy (`read-only` vs `full`, `identity`, production rules). Follow its **Mutation Discipline** when the policy is `full` (prefixed test data, identify + verify cleanup, record residue). Never mutate an env the policy marks `read-only` or `forbidden`; if a finding can only be confirmed by a forbidden mutation, file it as observed-and-blocked rather than escalating.6162## 6. File findings as tracked work6364No report file. Every finding becomes a **leaf work item** via `lisa-tracker-write` (the vendor-neutral writer — it dispatches to the configured tracker and runs the validation gate; never call a vendor `*-write-*` skill directly):6566Kane never files the work item itself. Even when it marks a failure as a confirmed product bug, run Lisa's marker-based duplicate search, apply the QA lens, and route the finding through `lisa-tracker-write` exactly as for every other controller.6768| Finding | `issue_type` | `build_ready` |69|---|---|---|70| User-visible **bug** (broken behavior) | `Bug` | the `ready` flag (default `false`) |71| **Usability / UX / clarity issue** | `Improvement` | the `ready` flag (default `false`) |7273Each finding is a flat leaf, so `build_ready` applies directly — pass it explicitly on every create.7475**This skill is the named human-gate exception under `ready-role-filing`.** Everywhere else in Lisa, a complete defect found during other work is filed with explicit `build_ready: true` so build-intake claims it next cycle. Exploratory QA is deliberately different, and that difference is ratified rather than drift: its findings are *candidate* defects and usability observations whose **product significance is a human call**, so auto-readying them would push judgment work into the build queue — precisely the failure the gate model exists to prevent. Its default `ready=false` is therefore an **explicit human-gate marker, never a bare omission**: every not-ready create passes `human_gate: "exploratory finding — product significance is a human product call"` alongside `build_ready: false`, and the writer stamps the auditable `[lisa-human-gate]` marker. An operator who wants a pass to feed the queue directly opts in with `ready=true`.7677(The sibling `e2e-coverage-gaps` skills are the contrast: a missing automated test is not a product question, so they file `build_ready: true` by default.)7879Each ticket MUST be a complete spec (the validator rejects thin tickets): a **three-audience description**; for a **bug**, exact reproduction steps, observed-vs-expected, the env / account / interface it occurred at, and evidence; for a **usability issue**, the observed friction, who it affects, **where**, and the proposed improvement; and **Gherkin acceptance criteria** for the fixed behavior.8081### Idempotency — don't spam duplicates8283Re-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:8485- **Open** ticket carrying the marker → reference/update it instead; do not create a second.86- **Closed as _completed_** → does **not** suppress. A recurrence after a fix is a genuine **regression**, so file the finding.87- **Closed as _not planned_** (GitHub `stateReason == "not_planned"`; the config-resolved won't-do/canceled equivalent on JIRA/Linear, including a Linear `duplicate` state) → 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.`).8889Every 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:9091> 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.9293## Output9495No report file. Emit a concise in-session summary:9697- **Scope:** product type, target env + mutation level, persona(s) explored as, tool, build/version if visible, date.98- **First impression:** could a new user tell what the product is and what to do first?99- **Findings filed**, bucketed by type — each with its **created or referenced ticket ref** and **build-ready state**.100- **Observed but not filed:** anything noticed but intentionally not ticketed (including forbidden-mutation blocks), with why.101102## Run outcome103104As the registered `exploratory-bugs` automation loop, this pass conforms to the105`automation-runbook-contract` rule: every invocation ends in **exactly one** of the six run outcomes106and records it, so a quiet run and a broken run are never mistaken for each other.107108| This cycle's exit path | Run outcome |109|---|---|110| Findings filed — one or more `Bug` / `Improvement` tickets created or referenced (§6) | `candidate-proposed` |111| 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` |112| 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` |113| The runbook's **Retirement condition** tripped — the trailing quiet window is empty AND this pass found nothing to file AND the project no longer ships an exploratory-qa surface — this row supersedes the `nothing-needed` row when it applies | `policy-obsolete` |114| 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 |115116Before invoking the run-record CLI, evaluate the **Retirement condition** first. If it applies,117select `policy-obsolete` as the sole outcome and do not record a prior `nothing-needed` result;118otherwise select the ordinary outcome from the table.119120Record **exactly one** outcome per invocation through the run-record CLI, naming this loop's runbook121(the `--summary` is the operator-readable one-liner in the contract's exemplar voice — plain,122specific, 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.`):123124```bash125node "${CLAUDE_PLUGIN_ROOT:-node_modules/@codyswann/lisa/plugins/lisa}/scripts/automation-run-record.mjs" \126 --loop-id exploratory-bugs --outcome candidate-proposed \127 --summary "Explored 4 personas; filed 3 findings from the checkout flow — awaiting your flip to ready." \128 --runbook .lisa/automations/exploratory-bugs.runbook.md [--ref <ticket-url>]...129```130131The `:-` default in that command is load-bearing, not decoration. `CLAUDE_PLUGIN_ROOT` is **not132exported into an agent's Bash tool environment**, so the bare `"${CLAUDE_PLUGIN_ROOT}/scripts/…"`133form expands to an absolute `/scripts/…` and exits 1. The default names the installed package copy,134which resolves from the **consumer repository root** — which is where the recorder is actually135invoked. Never substitute a bare `plugins/lisa/scripts/…` or `plugins/src/base/scripts/…` path:136those are relative to the **Lisa package root**, not the repository you are standing in, and exit 1137from there. If recording still fails, **degrade, never abort** (per `automation-runbook-contract`):138note the recording failure in the run output and finish the cycle — a recording failure is a139degradation to report, never a reason to block the loop.140141**Retirement evaluation (every run).** Evaluate this loop's runbook **Retirement condition** on142every run, exactly as the `automation-runbook-contract` rule's Retirement section defines it — this143skill conforms to that text and never restates or diverges from it. On top of the contract's two144conditions the runbook seeds a third **domain conjunct** — the project no longer ships an145exploratory-qa command surface to explore — which only tightens the test and never replaces it: a146quiet month on a product nobody broke is quality holding, not a reason to stop looking. Evaluate all147three. When all three hold, record `policy-obsolete` and file **exactly ONE** marker-deduped148teardown proposal through `lisa-tracker-write` (per `tracked-work` + `integration-access-layer`):149150- **Marker** `<!-- [lisa-automation-retire] key=exploratory-bugs -->` plus a visible prose line;151 matched on the marker, never the title; searched **open AND closed** per `rejection-detection`'s152 **Proposal rejection memory**. Treat matches by close state: **open** suppresses another proposal;153 **Not planned** suppresses another proposal unless new evidence postdates the rejection;154 **Completed** means the prior approved action happened, so a later recurrence may be re-filed.155 When an existing proposal suppresses filing, **the run still records `policy-obsolete` and files156 nothing** — the outcome describes this run, while the ticket is filed exactly once.157- **Labels** `status:blocked` + `human-needed`, carrying the contract's decision-ready packet. The158 `human-needed` label marks the proposal human-owned: `lisa-repair-intake` recognizes it and never159 re-dispatches it as stalled work.160- **Evidence** the date-filtered search result, this run's summary, **the loop's current cadence**161 (the baseline an operator needs to choose a longer one), and a one-line summary of recent runs162 read from `.lisa/automations/runs/exploratory-bugs.jsonl`. Fill the rest of the packet the same163 way every time: *Work already attempted* is the searches this run ran, and *Risk of inaction* is164 that the loop keeps consuming schedule slots and tokens for nothing.165- **How to answer** names the three operator responses: **approve** — run166 `/lisa:tear-down-automations exploratory-bugs` and only that loop registration goes away;167 **decline** — close the proposal as168 **Not planned** (closing it as **Completed** leaves a later re-file open) and the loop simply169 continues; **re-cadence** — pick a longer cadence off that evidence and re-register with170 `/lisa:setup-automations` instead of tearing down.171- **Operator footer**, verbatim, as on every loop-filed proposal (`rejection-detection`):172 > 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.173174The loop **keeps running at its normal cadence** until a human acts, and never deletes its own175registration.176177## Quality bar178179- Explore as a true first-time user — judge clarity, not whether you (who can read the code) can figure it out.180- Every ticket must stand alone for an implementer who was not in the session.181- Do not claim cleanup succeeded unless verified.182- File per the `ready` flag (default: backlog for human triage).183- Route automated-coverage gaps to `e2e-coverage-gaps`; preserve unrelated repo changes.