/**
* 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;