/** * The 6Ws Interview — SINGLE SOURCE. * * The canonical question registry for filling a .faf by asking a human (or an * agent asking a human). faf-cli OWNS it; consumers (claude-faf-mcp's faf_go, * siblings, UIs) IMPORT it — never reimplement, never copy. Decided * 2026-06-10: the CLI exports, CFM consumes, zero drift. (CFM's previous * hand-maintained registry had already drifted — e.g. its `how` asked "How * should AI assist?" while the canonical slot semantic is "How is it built / * used?". This file is the arbiter; slots.ts is its spine.) * * Shape carries what interactive consumers need: question text, a short * header chip, input type, required flag, select options where a closed-ish * vocabulary exists. Question SEMANTICS align to core/slots.ts descriptions — * the slot is the meaning, the question is just its interview voice. * * Authoring doctrine baked into the prompts: 6Ws answers are terse LABELS * (3-4 words, hard cap <6) — a scannable spec card, not prose. */ import type { SeededContextDetailed } from '../detect/relentless.js'; /** Bump when questions/options change — consumers can pin or report it. */ export declare const INTERVIEW_VERSION = "faf-interview/1"; export interface InterviewOption { label: string; value: string; description: string; } export interface InterviewQuestion

{ /** Canonical slot path (core/slots.ts is the spine). */ path: P; question: string; /** Short chip/header label (max ~12 chars) for option-UI consumers. */ header: string; type: 'text' | 'select'; required: boolean; options?: InterviewOption[]; } /** * The human/sourced boundary, as TYPES — the load-bearing line in FAF. * * HumanSlotPath = the 8 things only a human knows (name + goal + the 6Ws). * SourcedSlotPath = the slots detection fills (language + the stack). * * They are DISJOINT by construction. Typing the two interviews against these * makes it a COMPILE error to put a sourced slot in the human interview (or * vice-versa) — the "Interview-16" drift becomes unrepresentable at the Truth, * not merely caught by a downstream test. "I'll remember" is not a fix; a type * is. (claude-faf-mcp's wjttc-faf-go-boundary test is the belt; this is the * braces — the source itself can no longer drift.) */ export type HumanSlotPath = 'project.name' | 'project.goal' | 'human_context.who' | 'human_context.what' | 'human_context.why' | 'human_context.where' | 'human_context.when' | 'human_context.how'; export type SourcedSlotPath = 'project.main_language' | 'stack.frontend' | 'stack.backend' | 'stack.database' | 'stack.runtime' | 'stack.hosting' | 'stack.build' | 'stack.cicd'; /** * THE 8-Q 6Ws INTERVIEW — the core. Project identity (name + goal) plus the * six Ws. Language is deliberately NOT here: detection finds it; humans are * only asked what machines cannot derive. The 6Ws are the underivable half. */ export declare const SIX_WS_INTERVIEW: InterviewQuestion[]; /** * Stack interview — asked only for slots ACTIVE for the app_type and still * empty. Selects where a common vocabulary exists; every select includes * Other (specify) and None where absence is legitimate. */ export declare const STACK_INTERVIEW: InterviewQuestion[]; /** The full ordered registry: the 8-Q core first, then stack. */ export declare const INTERVIEW: InterviewQuestion[]; /** Lookup by slot path. */ export declare const INTERVIEW_BY_PATH: Map>; /** * Plain-object companion to INTERVIEW_BY_PATH. A Map JSON-serializes to `{}`, * which reads as "the export shipped empty" to any consumer that crosses a * serialization boundary (caught by the CFM compose handoff, 2026-06-12). * Bridge consumers that serialize should use THIS; in-process consumers can * use either. */ export declare const INTERVIEW_PATHS: Record; /** * Interview voice for ANY slot: the registry question when one exists, * otherwise derived from the slot's canonical description — so slot-driven * flows (faf go) and registry-driven flows (faf_go) speak the same language. */ export declare function questionForSlot(path: string): string; /** * The questions still worth asking for a given .faf: registry order, empty * (or placeholder) slots only, slotignored skipped — the What-Not is never * interviewed. `data` is the parsed .faf object. */ export declare function interviewForMissing(data: Record, isEmpty: (value: unknown) => boolean): InterviewQuestion[]; export interface GoalSeed { what?: string; where?: string; who?: string; } /** * Seed the human-context slots from the Goal sentence — verbatim facts only. * Returns a partial { what?, where?, who? }; absent keys = "no fact, ask the * human". Never returns why/when/how (not verbatim-extractable from a goal). */ export declare function seedSixWsFromGoal(goal: string): GoalSeed; export type BoxStatus = 'filled' | 'seeded' | 'empty'; export interface TableOf8Row { n: number; path: string; header: string; question: string; value: string; status: BoxStatus; seeded: boolean; /** Provenance for a SEEDED row — where the suggestion was sourced from, so the * human confirms informed: 'project goal' or a README locus ('README:## Why'). * Absent on filled/empty rows. */ source?: string; /** 0..1 confidence for a seeded row, by source quality. Absent otherwise. */ confidence?: number; } export interface TableOf8 { version: string; rows: TableOf8Row[]; filledCount: number; seededCount: number; emptyCount: number; complete: boolean; } /** * Build the Table-of-8 from a .faf object (any/empty) + an optional goal and an * optional sourced README extraction (relentlessContextDetailed). The goal seeds * WHO/WHAT/WHERE via seedSixWsFromGoal (facts only, source 'project goal'); the * detailed README form covers WHY/WHEN/HOW and any slot the goal didn't reach, * each carrying its own provenance. Rows the .faf already fills are 'filled'; * seeded ones carry `source`+`confidence` so the human confirms informed; the * rest are 'empty' (ask the human). Goal wins over README where both have a * fact (the deliberate sentence beats the extraction). Pure. */ export declare function buildTableOf8(faf: Record, opts?: { goal?: string; detailed?: SeededContextDetailed; }): TableOf8;