Product Interview
Protocol
Input
- Feature: a feature name/description or an existing path.
- Match first: before writing, match the feature against existing folder slugs in . Never mint a new NNN before this check.
- Exactly one match: use that folder; Step 5 updates its spec in place.
- Several plausible matches: list them via ; never glob-and-pick.
- No match: resolve the next number plus a slug from the feature name as the new .
- Stable home: resolve here so a mid-interview call has a destination.
- Lazy creation: whichever writes first creates the folder — mockups at Step 2 or spec.md at Step 5.
Resumability — check before Step 0
On re-entry to an existing feature, read what exists on disk first; the spec encodes where a prior session stopped — resume there rather than re-interviewing settled ground:
- No folder / no (per the Input check) → nothing written; run Steps 0–1, start fresh.
- exists but core sections are missing or placeholder → an interrupted prior session; re-read what's there and rejoin the interview (Step 2) at the gaps — resume from the file, don't reconstruct from memory.
- Spec complete but the Gate anchor greps hit ( decisions / clarification markers) → a parked investigation, not damage; resume Step 2 at the open branches or markers only.
- present and plan.md's is set → keep the outline frozen; handle the change in the active session.
- present and plan.md is absent or its is unset → already designed on this WHAT; confirm the reopen, revise any affected locked decision per the template's Revising rule, and apply Step 5's header flip.
Step 0 still runs on every re-entry — the lens loads per session.
Step 0 — Load the job lens
Invoke the
skill via the Skill tool. Use its lens — the job-story format and job-fit judgment — to frame every product/scope question; skip its Steps and Job-frame output.
Step 1 — Read context first
Before asking anything, silently explore:
- Root and any product or design documents it says to read for this task
- Existing UX in the affected area (screens, flows, components)
- Related features and any prior spec this feature builds on
- Use shipped specs as decision history; verify current behavior in code
- A light possibility scan: what the target surface/platform allows at all, and what data the codebase already carries — possibility only, never how-to-build, never current code as a ceiling (see the Codebase is context rule)
Don't ask what current code or the active spec already answers. Treat project conventions as constraints to follow, not decisions to re-litigate.
Step 2 — Interview: product, then UX
Resolve the
product layer before the
UX layer
as the default, but treat them as one decision tree: when a UX branch blocks or would overturn a product choice, resolve that branch first (the dependency rule below governs). A UX answer that overturns an already-locked product choice follows the reversal rule below. Surface what the user is assuming, not just what they request. When the Step-1 read surfaces a load-bearing question the user didn't ask — an existing feature this overlaps, a UX pattern to reuse or deliberately diverge from — raise it, saying it came from the codebase; route technical finds to Open Questions tagged
. Draft the job-story as the product layer resolves — it anchors UX option scoring and the spec's Background. A slot you can't fill is a question to ask, not a blank to guess.
Manage the decision space as follows:
- Start the tree: once you can name two or more branches, show a compact nested list and ask, "Here's what I think we need to figure out — does this match?"
- Format each node:
- [branch] — [resolved: choice] | [open] | [deferred: why] | [blocked by branch]
, followed by when applicable.
- Order: list blocking branches first and resolve one branch at a time.
- Maintain: update the tree inline as branches split, collapse, or resolve.
- Finish: continue until every branch is resolved or explicitly deferred.
- Flat interview: for one or two flat questions, skip the tree and ask directly.
- Unknown branches: ask open-ended questions until you can name the branches; aim to show the tree within 2–3 rounds.
- Long interview: summarize current clarity and offer to continue or proceed.
Run each branch explore → stretch → verify (in order) — name the ideal before checking what's real, so a constraint never caps a choice the user hasn't reached for yet.
- Explore / stretch: for a non-trivial or ambiguous UX branch, name and score 2+ options by job-fit before locking; an obvious single-UX branch skips this. Escalate to parallel subagents (and any available design skills) only for high-stakes or high-ambiguity UX.
- Sketch, then gate visual review: for every feature that changes a user-facing screen, component, interaction, or visual state, sketch each UI branch or option in ASCII, then ask once via before Step 2 ends: "How should we validate this UI before locking the UX?" The user chooses; recommend one option and state why:
- Keep ASCII — use the sketches without a rendered artifact. Recommend this when one obvious direction follows an established pattern and the sketches make its behavior clear.
- Preview rendered mockups — render the one preferred direction across the relevant screens or states. Recommend this when the direction is settled but a new or complex UI is easier to judge visually.
- Compare rendered directions — render 2+ viable directions for one unresolved visual choice side by side, then ask the user to pick. Recommend this when the directions have meaningful layout, hierarchy, or interaction tradeoffs.
- Run mockups: on Preview or Compare, invoke the skill via the Skill tool with the matching PREVIEW or COMPARE intent, the resolved
meta/specs/NNN-slug/mockups/
path, and the design context you know. It derives High or Approximate fidelity after grounding; don't promise High fidelity in the question.
- Record the result: at Step 5, record , or the reviewed mockup link plus the approved preview or comparison result, in the UX section.
- Skip: omit this checkpoint only when the feature changes no user-facing UI.
- Verify (just-in-time):
- Trigger: when an unchecked claim could invalidate the agreed scope if false, dispatch a subagent to verify it against the source of truth (code, docs, or SDK). A claim that would only redirect the branch does not qualify.
- Scope: verify only whether the product surface can support the agreed behavior.
- Route: put implementation and capacity questions in Open Questions tagged .
- Hit a wall? Tag it. = outside our control (external SDK / platform) → law; stamp its assumption (e.g. "given the SDK has no programmatic redirect") so it reopens if the dependency changes. = cross-team, movable by request. = our code, we change freely. is a real constraint; / are guidance — if either forces a worse UX, challenge it (or flag the ask) first, then record the user's final call and move on.
- Don't lock a UI pattern whose surface feasibility is unverified — verify possibility first, or lock it "pending feasibility."
Completeness lens (verify nothing is missing — these are a lens, not a required structure):
- Product / scope — the job to be done, in the jtbd job-story format (loaded at Step 0); who it's for; what's in, what's out; success criteria.
- UX and behavior — happy path, error states, empty states, user flows, the surfaces/screens touched.
- Acceptance criteria — observable, testable conditions for "done."
- Constraints — compatibility, platform limits, dependencies, boundaries.
- Clarity — resolve remaining ambiguity or contradictions.
When a load-bearing assumption surfaces, test it once ("Does this constraint actually exist?" / "What's the simplest version still worth shipping?"). Challenge the framing, not the person. If a stated requirement seems materially wrong (product value, UX harm), say so with reasoning; record the user's final call, not yours.
If a later answer or feasibility finding overturns a choice the user already locked this session, re-confirm via
and record the overturned choice in the surviving decision's Rejected field, citing what killed it — the why must reach the record.
Record each resolved choice as a
decision block (see the Spec.md template — it defines the id format) with Status, Chosen, Rejected, Rationale. Classify anything unresolved by exactly one rule:
- A framed-but-unresolved decision → a block with .
- A blocking unknown inside any section → an inline
[NEEDS CLARIFICATION: ...]
marker.
- Non-blocking notes → Open Questions (these do NOT block the gate).
Deferring a decision is itself a decision: propose it, the user confirms, and it lands as a
locked whose Chosen is the deferral (alternatives marked
deferred in Rejected) — a confirmed deferral never blocks the gate.
The lock gate greps exactly two forms — see Gate anchors below. Anything blocking must carry one of them, or it will not block.
Step 3 — Pre-confirm verification gate
Once Step 2's branches are resolved or deferred, and before the Step-4 summary, run this required gate over the resolved UX elements that are load-bearing (an AC, another decision, or user-facing behavior rests on them) — two passes, two grains. The gate always runs; on a trivial feature it may be near-empty (nothing load-bearing beyond the Existing-patterns check) — record that and move on.
Pass 1 — per element. For each load-bearing element:
- States — error / empty / edge: what happens when data is missing, the call fails, or a value hits a boundary?
- Expectation-Fidelity — does what the element implies (its label, control, default, or placement) match what actually happens? Flag every mismatch.
- Surface-Obligations — any obligation the surface itself imposes (e.g. accessibility/compliance on a regulated surface), where it demands it.
Pass 2 — the assembled whole. Do the cleared elements coexist and work together on the real surface(s)? Check combination-possibility + cross-element interference, and re-verify the load-bearing facts the interview leaned on. Surface/platform possibility is validated here — launch subagents in parallel, one per applicable area below (Existing-patterns always runs), each carrying the flows it must validate:
| Area | When relevant | Verify |
|---|
| UI components | Feature uses specific components/libraries | Component exists, supports the interaction, composition constraints |
| External data/APIs | UX depends on external data | Data is available, fields exist |
| Platform constraints | Feature rides a platform (Shopify, extension, etc.) | The UX is permitted by the platform |
| Existing patterns | Always | The affected area's existing UX patterns and conventions |
Scope: possibility, not capacity. Constraint depth — rate limits, quotas, throughput, batch caps — is
's constraint recon (its Step 2B); don't duplicate it here.
Handle verification results in order:
- Receive: each subagent returns (yes/no), capabilities, gotchas, and
blocks: <the decision or flow it invalidates> | none
. is valid.
- Record: put each load-bearing possibility verdict in the relevant Rationale or the Constraints section, not only in conversation.
- Resolve: feed every Pass-1 miss or Pass-2 hit back into the tree. Re-explore it with the user; never narrow the UX silently.
- After one follow-up round: proceed with each resolved finding. Classify each remaining finding by Step 2's rule as an open decision, clarification marker, or Open Question, then move on.
Step 4 — Pre-write summary
Before writing, summarize the contract in chat in this exact shape — enough to spot a wrong turn without reproducing every AC:
**Contract summary (pre-write):**
- Scope: [one line]
- Decisions: D-NNN-XX [title] → [Chosen] (one line per decision)
- Visual review: [not applicable — no UI change | ASCII (user-approved) | mockups: path — review outcome]
- Constraints: [one line each]
- ACs: [n] ([x] code-gated, [y] human-gated)
- Step-3 gate: [clean | each finding and how it resolved]
**Assumptions I'm carrying (never discussed):**
- [assumption] — [what rests on it]
(Write
None — everything load-bearing was discussed
when the assumptions list is empty.) Then use
to collect the choice: "Write the draft" / "Adjust first" / "Find gaps first". Recommended: write the draft. The full verbatim contract — the numbered AC list with gating tags and every
block — belongs in the file, not chat: Step 5 writes it as
for the user to review. Reviewers verify diffs against that AC text, so it must be exact in the file.
On
Find gaps first — opt-in, at most once, for a complex feature or when you lack the domain depth to spot missing cases — invoke the
skill over the assembled contract. Absence-hunting only: its lens is
what the contract never mentions — missing scope, AC-coverage holes; never re-check verdicts the Step-3 gate settled (checkers verify what's written; find-gaps hunts what's not). Product/UX gaps only — not a technical-gap hunt (
's job); fence every lens to the WHAT layer and send technical gaps to Open Questions tagged
. Applied gaps re-enter Step 2; a new flow on an external surface re-runs the Step-3 gate on the delta. Then re-summarize and re-ask without the Find-gaps option — it runs at most once.
Step 5 — Write / update the spec
Write the spec in this order:
- Path: use
meta/specs/NNN-<topic-slug>/spec.md
with the resolved at Input. Create the folder unless a Step-2 mockup already created it.
- Existing spec: if Input resolved an existing spec, update it in place. Append or modify sections and revise decisions per the template's Revising rule.
- Counters: continue both counters. The next uses the highest existing across product and technical decisions plus 1; new ACs continue their counter likewise.
- Reopen: before plan.md's is set, a decision or AC edit on a spec with resets the header to . The existing outline was verified against the old WHAT, and the Draft header routes through a scoped redesign.
- Handoff: tell the user the path and ask them to review the file. The spec remains and uncommitted; Step 6 owns revisions and commit.
This skill writes the WHAT sections;
later appends technical Decisions + the Structure Outline (and appends to Constraints / Accepted risks what its recon proves);
appends the Completion record at ship. For the full file shape, see the
Spec.md template at the end of this file.
Step 6 — Review, commit, route
Stop here once every product/UX branch is resolved or deferred and the spec is written. Step 5 sent the user to read the file — this step turns that review into approval, then handles commit and routing as two tightly-coupled
rounds.
Q1 — Draft look right? If so, commit?
- Ask: offer "Commit now" (recommended) / "Skip commit for now" / "Adjust the draft first".
- Approve: either Commit or Skip approves the draft; proceed to Q2.
- Adjust: edit the Draft in place and re-ask Q1. If the edit overturns a locked decision or changes an AC on a spec with a populated outline, apply Step 5's header-flip rule.
- Commit: stage only spec.md; the commit is the durable trace of approval:
git add meta/specs/NNN-slug/spec.md && git commit -m "spec(NNN-slug): discovery — product/UX decisions + ACs"
- Folder guard: use the slug resolved at Input. If
git status --porcelain meta/specs/NNN-slug/
shows another changed file, leave it unstaged and tell the user.
- Skip commit: leave spec.md uncommitted and say so. Step 6 will stage it; re-offer the commit at every session boundary before then.
Q2 — Where next?
- — default: the WHAT is locked and the feature needs implementation decisions before sequencing.
- — if decisions or clarification markers remain, or a load-bearing assumption wasn't pressure-tested.
- directly — only for a trivial change with one obvious implementation.
The WHAT must be locked (both Gate anchor greps clean) before
will proceed.
Rules
- One question per round. Tightly coupled follow-ups are fine; shotgunning unrelated questions is not. Presenting/updating the tree counts as part of the round.
- Always use for questions with distinct choices — with your recommendation and why. Plain text only for genuinely open-ended questions.
- Product + UX only. Technical approach, data shapes, and file layout are 's job — route them to Open Questions tagged and move on — tech-design reads them at its discovery step.
- Codebase is context, not constraint. Existing code shows what IS, not what MUST BE; the user may intentionally diverge. A wall is law only when it's outside our control (external SDK / platform) — tag , stamp its assumption; anything we or a teammate can change ( our code, cross-team) is guidance — challenge it before it narrows the vision.
- Proportional effort — load-bearing only. Spend a subagent, verification, or UX-exploration round only where a decision rests on the answer; skip passing mentions and obvious single-UX branches. Match effort to stakes.
- Anchor questions in what you read. When a source raises a question, name its actual file or symbol and the claim that needs confirmation.
- Play back concrete scenarios, not abstract questions. Confirm behavior by walking one specific case in the shape
[trigger]: [what happens] — right?
("Save fails offline: the draft stays and a retry shows — right?") — a wrong detail draws the correction an abstract question won't.
- Keep project conventions out of the spec. Loaded project instructions own them; record a only for a feature-specific choice that is costly to reverse.
- The spec is the feature's build contract + record — it settles at ship; post-ship product/UX evolution belongs to future specs and durable docs, not retroactive edits here.
- Only spec.md and plan.md mint ids. spec.md mints /; plan.md mints (execute-plan's job; format in write-plan's Plan anchors). All other artifacts — interview notes, triage/backlog files, research — cite existing ids and never mint their own.
Spec.md template
Other skills inline only their own sections and point here:
markdown
# SPEC-NNN: [Feature name]
- **Status:** Draft <!-- Draft → Locked → Shipped. Set Draft: product-interview; → Locked: tech-design Step 6 (iff lock greps clean); → Shipped: execute-plan ship gate. The trivial route (product-interview → write-plan directly) skips tech-design and legitimately ships from Draft. Locked = zero open decisions and zero clarification markers. The lock gates grep per-decision markers, not this line; write-plan's stale-outline gate is the one gate that reads it (see Gate anchors). -->
- **Created:** [YYYY-MM-DD]
- **Source:** [origin — roadmap item, request, prior spec it builds on]
## Background
[Who needs this and why. The one-line job, in the jtbd job-story format (loaded at Step 0). The layer/scope boundary in a sentence. One short paragraph.]
## Requirements
[The WHAT, as observable rules — the densest, most load-bearing content. Enumerate edge cases per rule. No IDs: nothing downstream cites requirements — ACs are the citable contract.]
- [the rule] — edge cases: [list]
- [the rule] — edge cases: [list]
## UX
[Flows and states: happy path, error, empty. Surfaces/screens touched. Low fidelity is fine — ASCII mocks or bullet flows. Record the visual/structural options explored, not just the chosen one — keep each rejected layout/flow (mock or one line) with why it lost. When `generate-mockups` produced a page, link it (`mockups/…`) with any notes and stamp it *directional, not final*; the design system / `meta/DESIGN.md` wins if they disagree. Backend-only features: the externally observable contract — which fields/behavior a consumer sees; field types, nullability, and shapes belong to tech-design's outline, which takes precedence.]
## Out of scope
[Only Out-of-scope — don't restate In-scope (that duplicates Requirements). Annotate coupling.]
- [excluded item] — [coupling note]
## Acceptance Criteria
[Observable, testable "done" conditions — the contract an independent reviewer checks the diff against; the implementer never self-certifies.
**Numbering & rigor:** ids are `AC-NNN-XX` — `NNN` = this spec's folder number, `XX` = a zero-padded two-digit counter starting 01; plan tasks and tests cite the ids; scale rigor to scope.
**Gating tag (MANDATORY):** every AC carries exactly one — code-gated (machine-checkable against the diff) or human-gated with the concrete how (routed to Post-ship verification at ship). Tags are provisional at discovery; tech-design confirms or flips each once the approach is chosen — a tag-only edit, exempt from the supersession protocol.
**Revising:** ACs are the live contract — revise in place. While plan.md is absent or its Base SHA unset: a plain edit to final shape, no marker (if plan.md exists, the Revising rule's stale-plan route applies). Once Base SHA is set: add a trailing *(revised per D-NNN-XX)* marker — its presence always means changed mid-build; the why lives in the superseding decision.
**One physical line per AC:** ID, behavior, gating tag, and any *(revised per D-NNN-XX)* marker all on that line; the gates select by line.]
- **AC-NNN-01:** [observable behavior] — [code-gated]
- **AC-NNN-02:** [observable behavior] — [human-gated: how to verify, concretely]
## Decisions
[Inline, atomic `D-NNN-XX` blocks — the durable why.
**Id format:** `NNN` = this spec's folder number; `XX` = a zero-padded two-digit counter starting 01 — ONE counter per spec, shared by product (this skill) and tech (tech-design) decisions. Ids are unique across the repo (the folder number guarantees it) and never renumbered.
**Type marker:** each heading carries `[product]` or `[tech]` after the colon — advisory for readers and routing, no gate greps it.
**Citing:** cite the full id (`per D-NNN-07`), never a line number.
**Revising:** while plan.md is absent or its `Base SHA:` is unset (planning stage — anchor form in Plan anchors, `skills/write-plan/SKILL.md`), edit blocks in place to their final shape — fold an overturned Chosen into Rejected with what killed it, so the why survives in the one block; if plan.md already exists, tell the user the plan may be stale and route through write-plan's existing-plan guard. Once Base SHA is set (build started), supersede, never edit the body. Another spec's block is always superseded, never edited, at any stage.]
### D-NNN-01: [product] [decision title]
- **Status:** locked <!-- open | locked | superseded — lowercase, load-bearing (see Gate anchors). Unresolved decision = open; any open blocks downstream. -->
- **Chosen:** [the choice]
- **Rejected:** [alt — why it lost]; [alt — *deferred*, not rejected forever — why]
- **Rationale:** [the constraint that drove it; cite a verified fact if load-bearing]
- **Supersedes:** —
- **Superseded-by:** — <!-- set when Status flips to superseded; the ONLY edits ever made to a superseded block are Status + this line -->
## Structure Outline
<!-- WRITTEN BY tech-design — leave empty at discovery. Design snapshot: written `Status: Draft` for review, FROZEN once `Status: Locked`; before build, replaced only by a tech-design re-run. During build, deviations live as [Implementation] entries in plan.md's Execution Log; after ship, code is the source of truth for structure. -->
<!-- Section format lives in skills/tech-design/SKILL.md (Step 3); it ends with a "### Files touched" heading — load-bearing: write-plan's outline-present gate greps it (see Gate anchors below). -->
## Constraints
[Fixed boundaries: compatibility, performance, platform limits, dependencies. Append-by-both: discovery seeds it; tech-design appends the load-bearing numbers its recon proves. Tag each wall `[hard]` (outside our control — stamp the assumption) · `[ask]` (cross-team, movable) · `[ours]` (our code) — advisory annotations, no gate greps them.]
- [constraint] — `[hard|ask|ours]`
## Accepted risks (knowingly carried)
[Append-by-both: discovery seeds it; tech-design appends the risks the user accepts.]
- [risk we choose to live with] — [why acceptable]
## Open Questions
[NON-BLOCKING notes only — this section does not block the lock gate; blockers must be open-status decisions or inline clarification markers (see Gate anchors). Implementation questions for tech-design land here, tagged `(for tech-design)`. Omit this section entirely when empty.]
- [non-blocking question] — [why it can wait]
---
## Completion record
<!-- WRITTEN BY execute-plan at the ship gate — leave absent until then. Settles the spec: outcome stamped onto the contract. -->
**Shipped:** [date] · **Status:** Complete | Partial
### Criteria results
|---|---|
| AC-NNN-01 | PASS / PARTIAL / FAIL — [1-line evidence] |
### Post-ship verification
<!-- WRITTEN BY execute-plan at ship: manual test cases for the whole feature; each `- [ ]` as steps → expected result, human-gated ACs led by `AC-NNN-XX:`. "None — nothing manually observable" if none. -->
- [ ] [steps] → [expected result]
- [ ] AC-NNN-XX: [steps] → [expected result]
### Deferred / what this does NOT close
- [deferred debt or known limitation, with severity] — or "None"
### Review filter stats
<!-- one line aggregating the Wave Reviews tallies: review findings the fix-verify-loop pre-gate dropped + findings demoted, across all waves — so what the filter rejected stays visible -->
- [N dropped by pre-gate, M demoted, across all waves] — or "None"
Gate anchors (load-bearing — exact forms matter)
These live OUTSIDE the template so they are never copied into a spec instance. Downstream gates (tech-design Step 1, write-plan Step 1) block on:
grep -nE '^[[:space:]]*-[[:space:]]*\*\*Status:\*\*[[:space:]]*open' spec.md # any hit ⇒ blocked
grep -n '\[NEEDS CLARIFICATION:' spec.md # any hit ⇒ blocked
grep -n '^### Files touched' spec.md # write-plan Step 1 only: no hit ⇒ outline missing
grep -nE '^[[:space:]]*-[[:space:]]*\*\*Status:\*\*[[:space:]]*Draft' spec.md # write-plan Step 1 only: + outline present ⇒ stale outline (reopened)
Rules that keep these greps sound — breaking any of them silently breaks the pipeline:
- POSIX ERE only (, never ) — gates run through varying grep builds.
- Case split is load-bearing: header Status values are Capitalized (); decision Status values are lowercase (). That asymmetry is what keeps the header line out of the decision-gate regex. Never normalize one to the other.
- Clarification markers are always written with the colon (
[NEEDS CLARIFICATION: ...]
). The ban is by location, not intent: the colon form must NEVER appear in the canonical template body, or any text destined for a spec instance, where the gate would catch it; an illustrative placeholder in this rules block or interview prose, as here, is fine — the gate reads spec.md, never SKILL.md.
- Each AC is ONE physical line —
- **AC-NNN-XX:** behavior — [tag]
, keeping any marker on that same line (a long AC stays on one line; the gates care about line count, not length). Both AC selections (execute-plan Step 4 / Seat A code-gated, Step 5.3 human-gated) grep the AC line, then filter for the tag — a tag wrapped onto a continuation line silently drops the AC from review or post-ship verification.
- plan.md-side anchors (typed log tags, promotion marker, deferred tags) are defined beside the canonical plan template in
skills/write-plan/SKILL.md
.