| name | flow-next-interview |
| description | Interview user in-depth about a spec, task, or spec file to extract complete implementation details. Use when user wants to flesh out a spec, refine requirements, or clarify a feature before building. Triggers on /flow-next:interview with Flow IDs (fn-1-add-oauth, fn-1-add-oauth.2, or legacy fn-1, fn-1.2, fn-1-xxx, fn-1-xxx.2) or file paths. |
| user-invocable | false |
Flow interview
Conduct an extremely thorough interview about a task/spec and write refined details back.
.flow/ is the only task tracker. A run that recorded task state in a markdown TODO, a plan file, TodoWrite, or any other tracker has broken this — all task state is read and written via flowctl.
Chart boundary (fn-135)
Existing-spec clarification stays primary. Interview refines a valid spec with unresolved judgment questions. Do not reopen discovery as /flow-next:chart unless the answers reveal that the effort itself is not yet specifiable - only then route backward to chart. Clear work that never needed a chart stays out of chart. Unsure of the hop: /flow-next:guide.
Preamble
CRITICAL: flowctl is BUNDLED — NOT installed globally. which flowctl will fail (expected). Define once; subsequent blocks use $FLOWCTL:
FLOWCTL="${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}/scripts/flowctl"
[ -x "$FLOWCTL" ] || FLOWCTL="<plugin-root>/scripts/flowctl"
[ -x "$FLOWCTL" ] || FLOWCTL=".flow/bin/flowctl"
Role: technical interviewer, spec refiner
Goal: extract complete implementation details through deep questioning (40+ questions typical)
Input
Full request: $ARGUMENTS
Accepts a Flow spec ID, a Flow task ID, a resolvable tracker handle, a file path, or nothing — recognition rules and the fetch command per type are in "Detect Input Type" below (single copy; the write-back command per type is in references/write-back.md).
Examples:
/flow-next:interview fn-1-add-oauth
/flow-next:interview fn-1-add-oauth.3
/flow-next:interview fn-1 (legacy formats fn-1, fn-1-xxx still supported)
/flow-next:interview docs/oauth-spec.md
If empty, ask: "What should I interview you about? Give me a Flow ID (e.g., fn-1-add-oauth) or file path (e.g., docs/spec.md)"
Setup
Parse --scope=business|technical|both (fn-44.1 plumbing)
Token-safe parsing for --scope / --biz / --tech lives in flowctl scope resolve — never re-implement inline. The subcommand strips scope tokens, preserves every other token in order (Flow IDs, paths, --docs, --strategy, ...), and emits the resolved scope plus a defaulted flag. The resolver's fallback when no scope flag is passed is technical (1.0.2 backward-compat) — but the skill does NOT silently run it: when defaulted == true, ask the user which pass to run after Detect Input Type (see "Scope selection when no flag passed" below). technical applies only when that question cannot be asked.
RESOLVED_JSON=$("$FLOWCTL" scope resolve --json --raw "$ARGUMENTS")
SCOPE=$(printf '%s' "$RESOLVED_JSON" | jq -r '.scope')
SCOPE_DEFAULTED=$(printf '%s' "$RESOLVED_JSON" | jq -r '.defaulted // false')
ARGUMENTS=$(printf '%s' "$RESOLVED_JSON" | jq -r '.remaining_args | join(" ")')
Scope parsing, write policy, and bank selection come from flowctl scope resolve / scope write-policy / scope bank. A skill that re-implements the tokenizer, the section-ownership rules, or the bank mapping inline has broken this — the two copies drift and the inline one wins silently.
Parse --docs / --no-docs / --strategy / --no-strategy flags
The four doc-aware override flags must be stripped from $ARGUMENTS before input-type detection so they don't get confused for a Flow ID or path. Two force variables carry the result — "" = autodetect, "on" = forced on, "off" = forced off:
RAW_ARGS="$ARGUMENTS"
DOC_AWARE_FORCE=""
STRATEGY_AWARE_FORCE=""
When the invocation carried ANY of --docs / --no-docs / --strategy / --no-strategy, STOP and read references/doc-aware.md § Flag parsing before proceeding — it holds the strip block (both pairs mutually exclusive, negation wins on conflict), the cascade rules, the flag matrix that is the contract for each combination, and the scope × doc/strategy interaction table. A bare invocation skips it: no flag token is present, so RAW_ARGS is $ARGUMENTS unchanged (whitespace-normalized) and both force variables stay empty (autodetect).
Doc-aware autodetect
Decide whether doc-aware mode activates. DOC_AWARE controls glossary + decisions; STRATEGY_AWARE controls the strategy-conflict behavior independently. Each has three paths (forced-on / forced-off / autodetect) per the flag matrix.
The default-autodetect rule is: doc-aware mode activates when any of three conditions has signal — glossary.total_terms > 0 (a) OR a decision entry exists (b) OR strategy.sections_filled >= 1 (c). The two flag pairs override (a)+(b) and (c) independently. Counting populated entries (rather than [[ -f <file> ]]) is deliberate — see references/doc-aware.md § Why counts, not file presence.
DOC_AWARE=0
if [[ "$DOC_AWARE_FORCE" == "on" ]]; then
DOC_AWARE=1
elif [[ "$DOC_AWARE_FORCE" == "off" ]]; then
DOC_AWARE=0
else
GLOSSARY_RAW="$("$FLOWCTL" glossary list --json 2>/dev/null)" || DOC_AWARE=1
DECISIONS_RAW="$("$FLOWCTL" memory list --track knowledge --category decisions --json 2>/dev/null)" || DOC_AWARE=1
if [ "$DOC_AWARE" = "0" ]; then
TERMS="$(printf '%s' "$GLOSSARY_RAW" | jq -r '.total_terms // 0' 2>/dev/null)" || DOC_AWARE=1
DECS="$(printf '%s' "$DECISIONS_RAW" | jq -r '.entries | length // 0' 2>/dev/null)" || DOC_AWARE=1
fi
if [ "$DOC_AWARE" = "0" ] && { [ "${TERMS:-0}" -gt 0 ] || [ "${DECS:-0}" -gt 0 ]; }; then
DOC_AWARE=1
fi
fi
STRATEGY_AWARE=0
if [[ "$STRATEGY_AWARE_FORCE" == "on" ]]; then
STRATEGY_AWARE=1
elif [[ "$STRATEGY_AWARE_FORCE" == "off" ]]; then
STRATEGY_AWARE=0
else
STRATEGY_RAW="$("$FLOWCTL" strategy status --json 2>/dev/null)" || STRATEGY_AWARE=1
if [ "$STRATEGY_AWARE" = "0" ]; then
STRAT_FILLED="$(printf '%s' "$STRATEGY_RAW" | jq -r '.sections_filled // 0' 2>/dev/null)" || STRATEGY_AWARE=1
fi
if [ "$STRATEGY_AWARE" = "0" ] && [ "${STRAT_FILLED:-0}" -ge 1 ]; then
STRATEGY_AWARE=1
fi
fi
if [ "$DOC_AWARE" = "1" ] || [ "$STRATEGY_AWARE" = "1" ]; then
echo "DOC-AWARE GATE ACTIVE — STOP. Read references/doc-aware.md before drafting the first question."
fi
When the sentinel prints, STOP and read references/doc-aware.md before any further step, then apply its behaviors — Phase-zero glossary scan (a), fuzzy-term sharpening (b), code-versus-assertion contradiction (c), decision-record write (d), and code-vs-strategy contradiction (e). On the default no-docs path (DOC_AWARE=0 and STRATEGY_AWARE=0) the interview proceeds exactly as today — do not read the file.
Detect Input Type
Handle-recognition rule (R16): do NOT gate on a hard "must start with fn-" check. Before treating a single-token arg as a file path or freeform, route it through $FLOWCTL show <arg> --json — flowctl's widened resolver (fn-52.10) maps a tracker key (wor-17 / wor-17.M) to its linked spec/task, so a resolvable handle is the existing spec/task, never a new idea. Patterns 1-2 below are the common case; pattern 3 generalizes them to any resolvable handle.
-
Flow spec ID pattern: matches fn-\d+(-[a-z0-9-]+)? (e.g., fn-1-add-oauth, fn-12, fn-2-fix-login-bug)
- Fetch:
$FLOWCTL show <id> --json
- Read spec:
$FLOWCTL cat <id>
-
Flow task ID pattern: matches fn-\d+(-[a-z0-9-]+)?\.\d+ (e.g., fn-1-add-oauth.3, fn-12.5)
- Fetch:
$FLOWCTL show <id> --json
- Read spec:
$FLOWCTL cat <id>
- Also get parent spec context:
$FLOWCTL cat <spec-id>
-
Resolvable tracker handle: any single-token arg (not an .md path) that $FLOWCTL show <arg> --json resolves — e.g. a Linear key wor-17 (spec) or wor-17.3 (task). Use the canonical id from the JSON; a .-containing handle is a task (fetch parent spec too), otherwise a spec. Treat exactly like patterns 1-2; never re-create.
-
File path: a path-like token / .md extension that does NOT resolve via flowctl show
- Read file contents
- If file doesn't exist, ask user to provide valid path
Done when: the argument is classified as exactly one of the four patterns, every non-.md single-token arg was routed through $FLOWCTL show <arg> --json before that classification, and the target's content (spec body, task + parent spec, or file) is in hand for the scope recommendation below.
Scope selection when no flag passed
Fires ONLY when SCOPE_DEFAULTED=true (no --scope / --biz / --tech in the invocation). An explicit scope flag always wins and skips this section entirely.
Runs AFTER Detect Input Type — the spec/file content is in hand, so the recommendation is informed. Ask ONE AskUserQuestion (same blocking primitive as every interview question; the tool-unreachable fallback under "Question Format" applies):
- header:
Interview scope
- body:
Which interview pass should run? business = product framing (goal, users, boundaries, outcome AC — never decides architecture, stack, or APIs); technical = implementation details (architecture, API contracts, edge cases); both = business first, then technical. Recommended: <X> — <one-sentence rationale from the target's current state>. Confidence: [judgment-call].
- options (frozen):
business, technical, both
Derive the recommendation from the target's current state:
- Biz sections empty AND tech sections empty (new idea, fresh spec, bare file) → recommend
both — ground the product framing before any technical decision.
- Biz sections populated, tech sections empty or placeholder-only → recommend
technical — the business layer exists; fill the technical layer.
- Tech sections populated, biz sections absent (1.0.2-shape solo spec) → recommend
technical — refine in place.
Set SCOPE to the answer and proceed exactly as if the flag had been passed — write-policy, question bank, and pass behavior all follow the chosen scope. If the question genuinely cannot be asked (tool unreachable and no plain-text answer), fall back to technical and say so in the interview opener.
Why this exists: a PM invoking /flow-next:interview <spec-id> bare used to get a silent technical interrogation — stack/API questions they don't own, with skipped answers at risk of becoming rails-derived defaults. The scope question makes the business pass discoverable at the exact moment it matters.
Interview Process
CRITICAL REQUIREMENT: You MUST use the AskUserQuestion tool for every question.
- DO NOT output questions as text
- DO NOT list questions in your response
- ONLY ask questions via AskUserQuestion tool calls
- Ask in rounds: each round carries the whole frontier (see Question Order below), split across AskUserQuestion calls of up to 4 questions each
- Expect 40+ questions total for complex specs
Anti-pattern (WRONG):
Question 1: What database should we use?
Options: a) PostgreSQL b) SQLite c) MongoDB
Correct pattern: Call AskUserQuestion tool with question and options.
Question Format: Lead with Recommendation
Every AskUserQuestion body must include the agent's recommended option AND a confidence tier. Mirrors the canonical phrasing in flow-next-audit/SKILL.md:64 ("Lead with the recommended option and a one-sentence rationale"). Call ToolSearch with select:AskUserQuestion first if its schema isn't loaded. Fall back to numbered options in plain text only when the tool is unreachable.
Pattern:
question.body: ". . Recommended: — . Confidence: [high | judgment-call | your-call]."
question.options: neutral labels (no "(recommended)" markers — recommendation goes in the body; neutral options reduce anchoring)
Plain-language question contract (fn-90-adjacent field feedback, eval-validated)
Applies to EVERY question, both scopes. The interviewee must be able to read a question once and answer it confidently without asking what it means — field feedback showed jargon-dense questions disempower exactly the people the interview exists to hear (baseline legibility scored 4/10 for a second-language PM; this contract scores 7.5+ at ~30% fewer tokens).
- Open the body with ONE sentence of stakes: what this question decides, in the audience's words.
- Write for the audience in everyday words; prefer the common word over the term of art. A term of art you genuinely need gets a plain-word gloss in ≤1 clause at first use (e.g. "counter-metrics — things we'd hate to make worse").
- No unexplained acronyms or tool/repo shorthand. In business scope, no implementation vocabulary (no schemas, endpoints, config keys).
- Every option description states its consequence in plain words: "Choose this if…" / "This means…".
- Gloss referenced acceptance criteria. When a question cites a spec R-ID, attach a short plain-words gist at first mention — "R3 (the audit line's required fields)" — never a bare "R3" the interviewee must open the spec to decode. Gist, not quote: pasting full criterion text bloats the question body.
Required content and trim order (priorities — NOT a length cap; never trade required content for brevity):
- ALWAYS keep, in this order: the stakes sentence; the recommendation + its one-sentence rationale; the confidence tier; the gloss for any term of art used; each option's consequence.
- TRIM FIRST, until the question reads in one pass: repetition between body and options, secondary background, hedging, restated option lists.
- Target shape (calibration, not a ceiling): a body around 40-60 words with option descriptions around a dozen words each is what "done" usually looks like — reach it by trimming the trim-first list, never by dropping required content.
Confidence tiers (mandatory — pick one per question):
[high] — strong codebase signal or convention match. Recommendation is load-bearing; user can usually accept.
[judgment-call] — slight lean but reasonable people disagree. User's call carries weight.